[Pkg-javascript-commits] [node-mocha] 01/12: Imported Upstream version 1.21.4

Leo Iannacone l3on-guest at moszumanska.debian.org
Wed Oct 15 18:03:13 UTC 2014


This is an automated email from the git hooks/post-receive script.

l3on-guest pushed a commit to branch master
in repository node-mocha.

commit 739f5fe55e94a2bcddaa540b51078f403da29a1c
Author: Leo Iannacone <l3on at ubuntu.com>
Date:   Sat Oct 11 18:50:56 2014 +0200

    Imported Upstream version 1.21.4
---
 .travis.yml                        |   5 +-
 History.md                         |  36 +++-
 Makefile                           |  19 ++-
 Readme.md                          | 337 ++++++++++++++++++++-----------------
 bin/_mocha                         |  61 ++++---
 bin/mocha                          |   5 +
 bower.json                         |   2 +-
 component.json                     |   2 +-
 lib/context.js                     |  15 ++
 lib/mocha.js                       |  32 +++-
 lib/reporters/base.js              |  52 +-----
 lib/reporters/doc.js               |   7 +
 lib/reporters/dot.js               |   5 +-
 lib/reporters/json.js              |  18 +-
 lib/reporters/progress.js          |  10 +-
 lib/reporters/xunit.js             |   3 +-
 lib/runnable.js                    |  40 ++++-
 lib/runner.js                      |   8 +-
 lib/suite.js                       |  23 +++
 lib/utils.js                       |  59 ++++++-
 mocha.js                           | 309 +++++++++++++++++++++++++---------
 package.json                       |  15 +-
 support/tail.js                    |  11 +-
 test/acceptance/context.js         |   6 +
 test/acceptance/diffs.js           |   7 +
 test/acceptance/failing/timeout.js |  17 ++
 test/acceptance/timeout.js         |  14 ++
 test/acceptance/utils.js           |  32 +++-
 test/reporters/json.js             |  43 +++++
 test/runnable.js                   |  67 ++++++++
 30 files changed, 916 insertions(+), 344 deletions(-)

diff --git a/.travis.yml b/.travis.yml
index 09d3ef3..51f9bdd 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,4 +1,5 @@
 language: node_js
 node_js:
-  - 0.8
-  - 0.10
+  - "0.11"
+  - "0.8"
+  - "0.10"
diff --git a/History.md b/History.md
index 7ab50bd..358f45f 100644
--- a/History.md
+++ b/History.md
@@ -1,7 +1,41 @@
+1.22.1-3 / 2014-07-27
+==================
+
+  * fix: disabling timeouts with this.timeout(0) (#1301)
+
+1.22.1-3 / 2014-07-27
+==================
+
+  * fix: local uis and reporters (#1288)
+  * fix: building 1.21.0's changes in the browser (#1284)
+
+1.21.0 / 2014-07-23
+==================
+
+  * add: --no-timeouts option (#1262, #1268)
+  * add: --*- deprecation node flags (#1217)
+  * add: --watch-extensions argument (#1247)
+  * change: spec reporter is default (#1228)
+  * fix: diff output showing incorrect +/- (#1182)
+  * fix: diffs of circular structures (#1179)
+  * fix: re-render the progress bar when progress has changed only (#1151)
+  * fix support for environments with global and window (#1159)
+  * fix: reverting to previously defined onerror handler (#1178)
+  * fix: stringify non error objects passed to done() (#1270)
+  * fix: using local ui, reporters (#1267)
+  * fix: cleaning es6 arrows (#1176)
+  * fix: don't include attrs in failure tag for xunit (#1244)
+  * fix: fail tests that return a promise if promise is rejected w/o a reason (#1224)
+  * fix: showing failed tests in doc reporter (#1117)
+  * fix: dot reporter dots being off (#1204)
+  * fix: catch empty throws (#1219)
+  * fix: honoring timeout for sync operations (#1242)
+  * update: growl to 1.8.0
+
 1.20.1 / 2014-06-03
 ==================
 
-  * update: should dependency to ~4.0.0 (#1231)
+  * update: should dev dependency to ~4.0.0 (#1231)
 
 1.20.0 / 2014-05-28
 ==================
diff --git a/Makefile b/Makefile
index 6c55501..77efec7 100644
--- a/Makefile
+++ b/Makefile
@@ -32,7 +32,7 @@ lib-cov:
 
 test: test-unit
 
-test-all: test-bdd test-tdd test-qunit test-exports test-unit test-grep test-jsapi test-compilers test-sort test-glob test-requires test-reporters test-only
+test-all: test-bdd test-tdd test-qunit test-exports test-unit test-grep test-jsapi test-compilers test-sort test-glob test-requires test-reporters test-only test-failing
 
 test-jsapi:
 	@node test/jsapi
@@ -44,17 +44,28 @@ test-unit:
 		--growl \
 		test/*.js
 
+test-failing:
+	@./bin/mocha \
+		--reporter $(REPORTER) \
+		test/acceptance/failing/timeout.js > /dev/null 2>&1 ; \
+		failures="$$?" ; \
+		if [ "$$failures" != '2' ] ; then \
+			echo 'test-failing:' ; \
+			echo "  expected 2 failing tests but saw $$failures" ; \
+			exit 1 ; \
+		fi
+
 test-compilers:
 	@./bin/mocha \
 		--reporter $(REPORTER) \
-		--compilers coffee:coffee-script,foo:./test/compiler/foo \
+		--compilers coffee:coffee-script/register,foo:./test/compiler/foo \
 		test/acceptance/test.coffee \
 		test/acceptance/test.foo
 
 test-requires:
 	@./bin/mocha \
 		--reporter $(REPORTER) \
-		--compilers coffee:coffee-script \
+		--compilers coffee:coffee-script/register \
 		--require test/acceptance/require/a.js \
 		--require test/acceptance/require/b.coffee \
 		--require test/acceptance/require/c.js \
@@ -165,4 +176,4 @@ non-tty:
 tm:
 	@open editors/$(TM_BUNDLE)
 
-.PHONY: test-cov test-jsapi test-compilers watch test test-all test-bdd test-tdd test-qunit test-exports test-unit non-tty test-grep tm clean
+.PHONY: test-cov test-jsapi test-compilers watch test test-all test-bdd test-tdd test-qunit test-exports test-unit non-tty test-grep test-failing tm clean
diff --git a/Readme.md b/Readme.md
index 9340cac..b84bb05 100644
--- a/Readme.md
+++ b/Readme.md
@@ -9,160 +9,191 @@
 ```
 
  project  : mocha
- repo age : 2 years, 4 months ago
- commits  : 1314
- active   : 372 days
- files    : 141
+ repo age : 2 years, 11 months
+ active   : 433 days
+ commits  : 1424
+ files    : 143
  authors  :
-   582    TJ Holowaychuk          44.3%
-   389    Tj Holowaychuk          29.6%
-    46    Travis Jeffery          3.5%
-    31    Guillermo Rauch         2.4%
-    13    Attila Domokos          1.0%
-    10    John Firebaugh          0.8%
-     8    Jo Liss                 0.6%
-     7    Nathan Rajlich          0.5%
-     6    Mike Pennisi            0.5%
-     6    James Carr              0.5%
-     6    Brendan Nee             0.5%
-     5    Aaron Heckmann          0.4%
-     5    Ryunosuke SATO          0.4%
-     4    hokaccha                0.3%
-     4    Joshua Krall            0.3%
-     4    Xavier Antoviaque       0.3%
-     3    Jesse Dailey            0.2%
-     3    Forbes Lindesay         0.2%
-     3    Sindre Sorhus           0.2%
-     3    Cory Thomas             0.2%
-     3    Fredrik Enestad         0.2%
-     3    Ben Lindsey             0.2%
-     3    Tyson Tate              0.2%
-     3    Mathieu Desvé          0.2%
-     3    Valentin Agachi         0.2%
-     3    Wil Moore III           0.2%
-     3    Merrick Christensen     0.2%
-     3    eiji.ienaga             0.2%
-     3    fool2fish               0.2%
-     3    Nathan Bowser           0.2%
-     3    Paul Miller             0.2%
-     2    Juzer Ali               0.2%
-     2    Pete Hawkins            0.2%
-     2    Jonas Westerlund        0.2%
-     2    Arian Stolwijk          0.2%
-     2    Quang Van               0.2%
-     2    Glen Mailer             0.2%
-     2    Justin DuJardin         0.2%
-     2    FARKAS Máté           0.2%
-     2    Raynos                  0.2%
-     2    Michael Riley           0.2%
-     2    Michael Schoonmaker     0.2%
-     2    Domenic Denicola        0.2%
-     2    Simon Gaeremynck        0.2%
-     2    Konstantin Käfer      0.2%
-     2    domenic                 0.2%
-     2    Paul Armstrong          0.2%
-     2    fcrisci                 0.2%
-     2    Alexander Early         0.2%
-     2    Shawn Krisman           0.2%
-     2    Brian Beck              0.2%
-     2    Nathan Alderson         0.2%
-     2    David Henderson         0.2%
-     2    Timo Tijhof             0.2%
-     2    Ian Storm Taylor        0.2%
-     2    travis jeffery          0.2%
-     1    Matt Smith              0.1%
-     1    Matthew Shanley         0.1%
-     1    Nathan Black            0.1%
-     1    Phil Sung               0.1%
-     1    R56                     0.1%
-     1    Refael Ackermann        0.1%
-     1    Richard Dingwall        0.1%
-     1    Romain Prieto           0.1%
-     1    Roman Neuhauser         0.1%
-     1    Roman Shtylman          0.1%
-     1    Russ Bradberry          0.1%
-     1    Russell Munson          0.1%
-     1    Rustem Mustafin         0.1%
-     1    Salehen Shovon Rahman   0.1%
-     1    Sasha Koss              0.1%
-     1    Seiya Konno             0.1%
-     1    Simon Goumaz            0.1%
-     1    Standa Opichal          0.1%
-     1    Stephen Mathieson       0.1%
-     1    Steve Mason             0.1%
-     1    Tapiwa Kelvin           0.1%
-     1    Teddy Zeenny            0.1%
-     1    Tim Ehat                0.1%
-     1    Vadim Nikitin           0.1%
-     1    Victor Costan           0.1%
-     1    Will Langstroth         0.1%
-     1    Yanis Wang              0.1%
-     1    Yuest Wang              0.1%
-     1    abrkn                   0.1%
-     1    airportyh               0.1%
-     1    badunk                  0.1%
-     1    fengmk2                 0.1%
-     1    grasGendarme            0.1%
-     1    lodr                    0.1%
-     1    tgautier at yahoo.com      0.1%
-     1    traleig1                0.1%
-     1    vlad                    0.1%
-     1    yuitest                 0.1%
-     1    Adam Crabtree           0.1%
-     1    Andreas Brekken         0.1%
-     1    Andreas Lind Petersen   0.1%
-     1    Andrew Nesbitt          0.1%
-     1    Andrey Popp             0.1%
-     1    Arnaud Brousseau        0.1%
-     1    Atsuya Takagi           0.1%
-     1    Austin Birch            0.1%
-     1    Bjørge Næss           0.1%
-     1    Brian Lalor             0.1%
-     1    Brian M. Carlson        0.1%
-     1    Brian Moore             0.1%
-     1    Bryan Donovan           0.1%
-     1    Casey Foster            0.1%
-     1    ChrisWren               0.1%
-     1    Corey Butler            0.1%
-     1    Daniel Stockman         0.1%
-     1    Dave McKenna            0.1%
-     1    Di Wu                   0.1%
-     1    Dmitry Shirokov         0.1%
-     1    Fedor Indutny           0.1%
-     1    Florian Margaine        0.1%
-     1    Frederico Silva         0.1%
-     1    Fredrik Lindin          0.1%
-     1    Gareth Murphy           0.1%
-     1    Gavin Mogan             0.1%
-     1    Glen Huang              0.1%
-     1    Greg Perkins            0.1%
-     1    Harry Brundage          0.1%
-     1    Herman Junge            0.1%
-     1    Ian Young               0.1%
-     1    Ivan                    0.1%
-     1    JP Bochi                0.1%
-     1    Jaakko Salonen          0.1%
-     1    Jakub Nešetřil      0.1%
-     1    James Bowes             0.1%
-     1    James Lal               0.1%
-     1    Jason Barry             0.1%
-     1    Javier Aranda           0.1%
-     1    Jeff Kunkle             0.1%
-     1    Jeremy Martin           0.1%
-     1    Jimmy Cuadra            0.1%
-     1    Jonathan Creamer        0.1%
-     1    Jussi Virtanen          0.1%
-     1    Katie Gengler           0.1%
-     1    Kazuhito Hokamura       0.1%
-     1    Kirill Korolyov         0.1%
-     1    Koen Punt               0.1%
-     1    Laszlo Bacsi            0.1%
-     1    Liam Newman             0.1%
-     1    László Bácsi         0.1%
-     1    Maciej Małecki         0.1%
-     1    Mal Graty               0.1%
-     1    Marc Kuo                0.1%
-     1    Matt Robenolt           0.1%
+   588	TJ Holowaychuk         41.3%
+   389	Tj Holowaychuk         27.3%
+    98	Travis Jeffery         6.9%
+    31	Guillermo Rauch        2.2%
+    13	Attila Domokos         0.9%
+    10	John Firebaugh         0.7%
+     8	Jo Liss                0.6%
+     7	Joshua Appelman        0.5%
+     7	Nathan Rajlich         0.5%
+     6	Brendan Nee            0.4%
+     6	James Carr             0.4%
+     6	Mike Pennisi           0.4%
+     5	Raynos                 0.4%
+     5	Aaron Heckmann         0.4%
+     5	Ryunosuke SATO         0.4%
+     4	Forbes Lindesay        0.3%
+     4	Domenic Denicola       0.3%
+     4	Xavier Antoviaque      0.3%
+     4	hokaccha               0.3%
+     4	Jonathan Ong           0.3%
+     4	Joshua Krall           0.3%
+     3	Ben Lindsey            0.2%
+     3	Benjie Gillam          0.2%
+     3	Fredrik Enestad        0.2%
+     3	Sindre Sorhus          0.2%
+     3	Cory Thomas            0.2%
+     3	Mathieu Desvé          0.2%
+     3	Tyson Tate             0.2%
+     3	Valentin Agachi        0.2%
+     3	Wil Moore III          0.2%
+     3	Jesse Dailey           0.2%
+     3	Merrick Christensen    0.2%
+     3	eiji.ienaga            0.2%
+     3	fool2fish              0.2%
+     3	Nathan Bowser          0.2%
+     3	Paul Miller            0.2%
+     2	FARKAS Máté            0.1%
+     2	Shawn Krisman          0.1%
+     2	Jacob Wejendorp        0.1%
+     2	Jonas Westerlund       0.1%
+     2	Paul Armstrong         0.1%
+     2	Konstantin Käfer       0.1%
+     2	Michael Riley          0.1%
+     2	Michael Schoonmaker    0.1%
+     2	Andreas Lind Petersen  0.1%
+     2	domenic                0.1%
+     2	Quang Van              0.1%
+     2	fcrisci                0.1%
+     2	Nathan Alderson        0.1%
+     2	travis jeffery         0.1%
+     2	Juzer Ali              0.1%
+     2	Pete Hawkins           0.1%
+     2	Justin DuJardin        0.1%
+     2	David Henderson        0.1%
+     2	jsdevel                0.1%
+     2	Timo Tijhof            0.1%
+     2	Brian Beck             0.1%
+     2	Simon Gaeremynck       0.1%
+     2	Ian Storm Taylor       0.1%
+     2	Arian Stolwijk         0.1%
+     2	Alexander Early        0.1%
+     2	Ben Bradley            0.1%
+     2	Glen Mailer            0.1%
+     1	Maciej Małecki         0.1%
+     1	Mal Graty              0.1%
+     1	Marc Kuo               0.1%
+     1	Matija Marohnić        0.1%
+     1	Matt Robenolt          0.1%
+     1	Matt Smith             0.1%
+     1	Matthew Shanley        0.1%
+     1	Mattias Tidlund        0.1%
+     1	Michael Jackson        0.1%
+     1	Nathan Black           0.1%
+     1	Nick Fitzgerald        0.1%
+     1	Noshir Patel           0.1%
+     1	Panu Horsmalahti       0.1%
+     1	Phil Sung              0.1%
+     1	R56                    0.1%
+     1	Refael Ackermann       0.1%
+     1	Richard Dingwall       0.1%
+     1	Romain Prieto          0.1%
+     1	Roman Neuhauser        0.1%
+     1	Roman Shtylman         0.1%
+     1	Russ Bradberry         0.1%
+     1	Russell Munson         0.1%
+     1	Rustem Mustafin        0.1%
+     1	Salehen Shovon Rahman  0.1%
+     1	Sasha Koss             0.1%
+     1	Seiya Konno            0.1%
+     1	Shaine Hatch           0.1%
+     1	Simon Goumaz           0.1%
+     1	Standa Opichal         0.1%
+     1	Stephen Mathieson      0.1%
+     1	Steve Mason            0.1%
+     1	Tapiwa Kelvin          0.1%
+     1	Teddy Zeenny           0.1%
+     1	Tim Ehat               0.1%
+     1	Vadim Nikitin          0.1%
+     1	Victor Costan          0.1%
+     1	Will Langstroth        0.1%
+     1	Yanis Wang             0.1%
+     1	Yuest Wang             0.1%
+     1	Zsolt Takács           0.1%
+     1	abrkn                  0.1%
+     1	airportyh              0.1%
+     1	badunk                 0.1%
+     1	claudyus               0.1%
+     1	fengmk2                0.1%
+     1	gaye                   0.1%
+     1	grasGendarme           0.1%
+     1	lakmeer                0.1%
+     1	lodr                   0.1%
+     1	qiuzuhui               0.1%
+     1	sebv                   0.1%
+     1	tgautier at yahoo.com     0.1%
+     1	traleig1               0.1%
+     1	vlad                   0.1%
+     1	yuitest                0.1%
+     1	Adam Crabtree          0.1%
+     1	Andreas Brekken        0.1%
+     1	Andrew Nesbitt         0.1%
+     1	Andrey Popp            0.1%
+     1	Arnaud Brousseau       0.1%
+     1	Atsuya Takagi          0.1%
+     1	Austin Birch           0.1%
+     1	Ben Noordhuis          0.1%
+     1	Bjørge Næss            0.1%
+     1	Brian Lalor            0.1%
+     1	Brian M. Carlson       0.1%
+     1	Brian Moore            0.1%
+     1	Bryan Donovan          0.1%
+     1	Casey Foster           0.1%
+     1	ChrisWren              0.1%
+     1	Christopher Hiller     0.1%
+     1	Corey Butler           0.1%
+     1	Daniel Stockman        0.1%
+     1	Dave McKenna           0.1%
+     1	Denis Bardadym         0.1%
+     1	Devin Weaver           0.1%
+     1	Di Wu                  0.1%
+     1	Dmitry Shirokov        0.1%
+     1	Fedor Indutny          0.1%
+     1	Florian Margaine       0.1%
+     1	Frederico Silva        0.1%
+     1	Fredrik Lindin         0.1%
+     1	Gareth Aye             0.1%
+     1	Gareth Murphy          0.1%
+     1	Gavin Mogan            0.1%
+     1	Giovanni Bassi         0.1%
+     1	Glen Huang             0.1%
+     1	Greg Perkins           0.1%
+     1	Harish                 0.1%
+     1	Harry Brundage         0.1%
+     1	Herman Junge           0.1%
+     1	Ian Young              0.1%
+     1	Ivan                   0.1%
+     1	JP Bochi               0.1%
+     1	Jaakko Salonen         0.1%
+     1	Jakub Nešetřil         0.1%
+     1	James Bowes            0.1%
+     1	James Lal              0.1%
+     1	Jan Kopriva            0.1%
+     1	Jason Barry            0.1%
+     1	Javier Aranda          0.1%
+     1	Jean Ponchon           0.1%
+     1	Jeff Kunkle            0.1%
+     1	Jeremy Martin          0.1%
+     1	Jimmy Cuadra           0.1%
+     1	John Doty              0.1%
+     1	Jonathan Creamer       0.1%
+     1	Jonathan Park          0.1%
+     1	Jussi Virtanen         0.1%
+     1	Katie Gengler          0.1%
+     1	Kazuhito Hokamura      0.1%
+     1	Kirill Korolyov        0.1%
+     1	Koen Punt              0.1%
+     1	Laszlo Bacsi           0.1%
+     1	Liam Newman            0.1%
+     1	Linus Unnebäck         0.1%
+     1	László Bácsi           0.1%
 ```
 
 ## Links
diff --git a/bin/_mocha b/bin/_mocha
index cc9b06c..7c39c6c 100755
--- a/bin/_mocha
+++ b/bin/_mocha
@@ -61,37 +61,43 @@ var images = {
 program
   .version(JSON.parse(fs.readFileSync(__dirname + '/../package.json', 'utf8')).version)
   .usage('[debug] [options] [files]')
-  .option('-r, --require <name>', 'require the given module')
-  .option('-R, --reporter <name>', 'specify the reporter to use', 'dot')
-  .option('-u, --ui <name>', 'specify user-interface (bdd|tdd|exports)', 'bdd')
-  .option('-g, --grep <pattern>', 'only run tests matching <pattern>')
-  .option('-i, --invert', 'inverts --grep matches')
-  .option('-t, --timeout <ms>', 'set test-case timeout in milliseconds [2000]')
-  .option('-s, --slow <ms>', '"slow" test threshold in milliseconds [75]')
-  .option('-w, --watch', 'watch files for changes')
+  .option('-A, --async-only', "force all tests to take a callback (async)")
   .option('-c, --colors', 'force enabling of colors')
   .option('-C, --no-colors', 'force disabling of colors')
   .option('-G, --growl', 'enable growl notification support')
-  .option('-d, --debug', "enable node's debugger, synonym for node --debug")
-  .option('-b, --bail', "bail after first test failure")
-  .option('-A, --async-only', "force all tests to take a callback (async)")
+  .option('-R, --reporter <name>', 'specify the reporter to use', 'spec')
   .option('-S, --sort', "sort test files")
-  .option('--recursive', 'include sub directories')
+  .option('-b, --bail', "bail after first test failure")
+  .option('-d, --debug', "enable node's debugger, synonym for node --debug")
+  .option('-g, --grep <pattern>', 'only run tests matching <pattern>')
+  .option('-gc', '--expose-gc', 'expose gc extension')
+  .option('-i, --invert', 'inverts --grep matches')
+  .option('-r, --require <name>', 'require the given module')
+  .option('-s, --slow <ms>', '"slow" test threshold in milliseconds [75]')
+  .option('-t, --timeout <ms>', 'set test-case timeout in milliseconds [2000]')
+  .option('-u, --ui <name>', 'specify user-interface (bdd|tdd|exports)', 'bdd')
+  .option('-w, --watch', 'watch files for changes')
+  .option('--check-leaks', 'check for global variable leaks')
+  .option('--compilers <ext>:<module>,...', 'use the given module(s) to compile files', list, [])
   .option('--debug-brk', "enable node's debugger breaking on the first line")
   .option('--globals <names>', 'allow the given comma-delimited global [names]', list, [])
   .option('--harmony', 'enable all harmony features (except typeof)')
-  .option('--harmony-proxies', 'enable harmony proxies')
   .option('--harmony-collections', 'enable harmony collections (sets, maps, and weak maps)')
   .option('--harmony-generators', 'enable harmony generators')
-  .option('--prof', 'log statistical profiling information')
-  .option('-gc', '--expose-gc', 'expose gc extension')
-  .option('--trace', 'trace function calls')
-  .option('--check-leaks', 'check for global variable leaks')
-  .option('--interfaces', 'display available interfaces')
-  .option('--reporters', 'display available reporters')
-  .option('--compilers <ext>:<module>,...', 'use the given module(s) to compile files', list, [])
+  .option('--harmony-proxies', 'enable harmony proxies')
   .option('--inline-diffs', 'display actual/expected differences inline within each string')
+  .option('--interfaces', 'display available interfaces')
+  .option('--no-deprecation', 'silence deprecation warnings')
   .option('--no-exit', 'require a clean shutdown of the event loop: mocha will not call process.exit')
+  .option('--no-timeouts', 'disables timeouts, given implicitly with --debug')
+  .option('--opts <path>', 'specify opts path', 'test/mocha.opts')
+  .option('--prof', 'log statistical profiling information')
+  .option('--recursive', 'include sub directories')
+  .option('--reporters', 'display available reporters')
+  .option('--throw-deprecation', 'throw an exception anytime a deprecated function is used')
+  .option('--trace', 'trace function calls')
+  .option('--trace-deprecation', 'show stack traces on deprecations')
+  .option('--watch-extensions <ext>,...', 'additional extensions to monitor with --watch', list, [])
 
 program.name = 'mocha';
 
@@ -164,10 +170,14 @@ program.on('require', function(mod){
   requires.push(mod);
 });
 
-// mocha.opts support
+// --opts
+
+var optsPath = process.argv.indexOf('--opts') !== -1
+    ? process.argv[process.argv.indexOf('--opts') + 1]
+    : 'test/mocha.opts';
 
 try {
-  var opts = fs.readFileSync('test/mocha.opts', 'utf8')
+  var opts = fs.readFileSync(optsPath, 'utf8')
     .trim()
     .split(/\s+/);
 
@@ -225,6 +235,10 @@ if (program.inlineDiffs) mocha.useInlineDiffs(true);
 
 if (program.slow) mocha.suite.slow(program.slow);
 
+// --no-timeouts
+
+if (!program.timeouts) mocha.enableTimeouts(false);
+
 // --timeout
 
 if (program.timeout) mocha.suite.timeout(program.timeout);
@@ -313,7 +327,8 @@ if (program.watch) {
     process.exit();
   });
 
-  var watchFiles = utils.files(cwd);
+
+  var watchFiles = utils.files(cwd, [ 'js', 'coffee', 'litcoffee', 'coffee.md' ].concat(program.watchExtensions));
   var runAgain = false;
 
   function loadAndRun() {
diff --git a/bin/mocha b/bin/mocha
index 742d607..b5dbc72 100755
--- a/bin/mocha
+++ b/bin/mocha
@@ -14,11 +14,13 @@ process.argv.slice(2).forEach(function(arg){
   switch (flag) {
     case '-d':
       args.unshift('--debug');
+      args.push('--no-timeouts');
       break;
     case 'debug':
     case '--debug':
     case '--debug-brk':
       args.unshift(arg);
+      args.push('--no-timeouts');
       break;
     case '-gc':
     case '--expose-gc':
@@ -29,7 +31,10 @@ process.argv.slice(2).forEach(function(arg){
     case '--harmony-proxies':
     case '--harmony-collections':
     case '--harmony-generators':
+    case '--no-deprecation':
     case '--prof':
+    case '--throw-deprecation':
+    case '--trace-deprecation':
       args.unshift(arg);
       break;
     default:
diff --git a/bower.json b/bower.json
index b6e3fd7..0305ec8 100644
--- a/bower.json
+++ b/bower.json
@@ -1,6 +1,6 @@
 {
   "name": "mocha",
-  "version": "1.20.1",
+  "version": "1.21.4",
   "main": [
     "mocha.js",
     "mocha.css"
diff --git a/component.json b/component.json
index 59820ac..8ad5826 100644
--- a/component.json
+++ b/component.json
@@ -1,6 +1,6 @@
 {
   "name": "mocha",
-  "version": "1.20.1",
+  "version": "1.21.4",
   "repo": "visionmedia/mocha",
   "description": "simple, flexible, fun test framework",
   "keywords": [
diff --git a/lib/context.js b/lib/context.js
index 6d6422a..84440be 100644
--- a/lib/context.js
+++ b/lib/context.js
@@ -36,11 +36,26 @@ Context.prototype.runnable = function(runnable){
  */
 
 Context.prototype.timeout = function(ms){
+  if (arguments.length === 0) return this.runnable().timeout();
   this.runnable().timeout(ms);
   return this;
 };
 
 /**
+ * Set test timeout `enabled`.
+ *
+ * @param {Boolean} enabled
+ * @return {Context} self
+ * @api private
+ */
+
+Context.prototype.enableTimeouts = function (enabled) {
+  this.runnable().enableTimeouts(enabled);
+  return this;
+};
+
+
+/**
  * Set test slowness threshold `ms`.
  *
  * @param {Number} ms
diff --git a/lib/mocha.js b/lib/mocha.js
index dabb409..8e57487 100644
--- a/lib/mocha.js
+++ b/lib/mocha.js
@@ -18,6 +18,16 @@ var path = require('path')
 exports = module.exports = Mocha;
 
 /**
+ * To require local UIs and reporters when running in node.
+ */
+
+if (typeof process !== 'undefined' && typeof process.cwd === 'function') {
+  var join = path.join
+    , cwd = process.cwd();
+  module.paths.push(cwd, join(cwd, 'node_modules'));
+}
+
+/**
  * Expose internals.
  */
 
@@ -49,7 +59,7 @@ function image(name) {
  * Options:
  *
  *   - `ui` name "bdd", "tdd", "exports" etc
- *   - `reporter` reporter instance, defaults to `mocha.reporters.Dot`
+ *   - `reporter` reporter instance, defaults to `mocha.reporters.spec`
  *   - `globals` array of accepted globals
  *   - `timeout` timeout in milliseconds
  *   - `bail` bail on the first test failure
@@ -72,6 +82,7 @@ function Mocha(options) {
   this.reporter(options.reporter);
   if (null != options.timeout) this.timeout(options.timeout);
   this.useColors(options.useColors)
+  if (options.enableTimeouts !== null) this.enableTimeouts(options.enableTimeouts);
   if (options.slow) this.slow(options.slow);
 
   this.suite.on('pre-require', function (context) {
@@ -116,7 +127,7 @@ Mocha.prototype.addFile = function(file){
 };
 
 /**
- * Set reporter to `reporter`, defaults to "dot".
+ * Set reporter to `reporter`, defaults to "spec".
  *
  * @param {String|Function} reporter name or constructor
  * @api public
@@ -126,7 +137,7 @@ Mocha.prototype.reporter = function(reporter){
   if ('function' == typeof reporter) {
     this._reporter = reporter;
   } else {
-    reporter = reporter || 'dot';
+    reporter = reporter || 'spec';
     var _reporter;
     try { _reporter = require('./reporters/' + reporter); } catch (err) {};
     if (!_reporter) try { _reporter = require(reporter); } catch (err) {};
@@ -333,6 +344,21 @@ Mocha.prototype.slow = function(slow){
 };
 
 /**
+ * Enable timeouts.
+ *
+ * @param {Boolean} enabled
+ * @return {Mocha}
+ * @api public
+ */
+
+Mocha.prototype.enableTimeouts = function(enabled) {
+  this.suite.enableTimeouts(arguments.length && enabled !== undefined
+    ? enabled
+    : true);
+  return this
+};
+
+/**
  * Makes all tests async (accepting a callback)
  *
  * @return {Mocha}
diff --git a/lib/reporters/base.js b/lib/reporters/base.js
index 0754fe1..2690fa1 100644
--- a/lib/reporters/base.js
+++ b/lib/reporters/base.js
@@ -180,8 +180,8 @@ exports.list = function(failures){
     // explicitly show diff
     if (err.showDiff && sameType(actual, expected)) {
       escape = false;
-      err.actual = actual = stringify(canonicalize(actual));
-      err.expected = expected = stringify(canonicalize(expected));
+      err.actual = actual = utils.stringify(actual);
+      err.expected = expected = utils.stringify(expected);
     }
 
     // actual / expected diff
@@ -444,53 +444,6 @@ function colorLines(name, str) {
 }
 
 /**
- * Stringify `obj`.
- *
- * @param {Object} obj
- * @return {String}
- * @api private
- */
-
-function stringify(obj) {
-  if (obj instanceof RegExp) return obj.toString();
-  return JSON.stringify(obj, null, 2);
-}
-
-/**
- * Return a new object that has the keys in sorted order.
- * @param {Object} obj
- * @return {Object}
- * @api private
- */
-
- function canonicalize(obj, stack) {
-   stack = stack || [];
-
-   if (utils.indexOf(stack, obj) !== -1) return obj;
-
-   var canonicalizedObj;
-
-   if ('[object Array]' == {}.toString.call(obj)) {
-     stack.push(obj);
-     canonicalizedObj = utils.map(obj, function(item) {
-       return canonicalize(item, stack);
-     });
-     stack.pop();
-   } else if (typeof obj === 'object' && obj !== null) {
-     stack.push(obj);
-     canonicalizedObj = {};
-     utils.forEach(utils.keys(obj).sort(), function(key) {
-       canonicalizedObj[key] = canonicalize(obj[key], stack);
-     });
-     stack.pop();
-   } else {
-     canonicalizedObj = obj;
-   }
-
-   return canonicalizedObj;
- }
-
-/**
  * Check that a / b have the same type.
  *
  * @param {Object} a
@@ -504,4 +457,3 @@ function sameType(a, b) {
   b = Object.prototype.toString.call(b);
   return a == b;
 }
-
diff --git a/lib/reporters/doc.js b/lib/reporters/doc.js
index 2e5bf57..eab72e5 100644
--- a/lib/reporters/doc.js
+++ b/lib/reporters/doc.js
@@ -53,4 +53,11 @@ function Doc(runner) {
     var code = utils.escape(utils.clean(test.fn.toString()));
     console.log('%s  <dd><pre><code>%s</code></pre></dd>', indent(), code);
   });
+
+  runner.on('fail', function(test, err){
+    console.log('%s  <dt class="error">%s</dt>', indent(), utils.escape(test.title));
+    var code = utils.escape(utils.clean(test.fn.toString()));
+    console.log('%s  <dd class="error"><pre><code>%s</code></pre></dd>', indent(), code);
+    console.log('%s  <dd class="error">%s</dd>', indent(), utils.escape(err));
+  });
 }
diff --git a/lib/reporters/dot.js b/lib/reporters/dot.js
index 0c298ba..e200468 100644
--- a/lib/reporters/dot.js
+++ b/lib/reporters/dot.js
@@ -25,13 +25,14 @@ function Dot(runner) {
   var self = this
     , stats = this.stats
     , width = Base.window.width * .75 | 0
-    , n = 0;
+    , n = -1;
 
   runner.on('start', function(){
     process.stdout.write('\n  ');
   });
 
   runner.on('pending', function(test){
+    if (++n % width == 0) process.stdout.write('\n  ');
     process.stdout.write(color('pending', Base.symbols.dot));
   });
 
@@ -59,4 +60,4 @@ function Dot(runner) {
  * Inherit from `Base.prototype`.
  */
 
-Dot.prototype.__proto__ = Base.prototype;
\ No newline at end of file
+Dot.prototype.__proto__ = Base.prototype;
diff --git a/lib/reporters/json.js b/lib/reporters/json.js
index e202692..d037120 100644
--- a/lib/reporters/json.js
+++ b/lib/reporters/json.js
@@ -48,6 +48,8 @@ function JSONReporter(runner) {
       passes: passes.map(clean)
     };
 
+    runner.testResults = obj;
+
     process.stdout.write(JSON.stringify(obj, null, 2));
   });
 }
@@ -66,6 +68,20 @@ function clean(test) {
     title: test.title,
     fullTitle: test.fullTitle(),
     duration: test.duration,
-    err: test.err
+    err: errorJSON(test.err)
   }
 }
+
+/**
+ * Transform `error` into a JSON object.
+ * @param {Error} err
+ * @return {Object}
+ */
+
+function errorJSON(err) {
+  var res = {};
+  Object.getOwnPropertyNames(err).forEach(function(key) {
+    res[key] = err[key];
+  }, err);
+  return res;
+}
diff --git a/lib/reporters/progress.js b/lib/reporters/progress.js
index 5953638..2debb94 100644
--- a/lib/reporters/progress.js
+++ b/lib/reporters/progress.js
@@ -1,4 +1,3 @@
-
 /**
  * Module dependencies.
  */
@@ -36,7 +35,8 @@ function Progress(runner, options) {
     , width = Base.window.width * .50 | 0
     , total = runner.total
     , complete = 0
-    , max = Math.max;
+    , max = Math.max
+    , lastN = -1;
 
   // default chars
   options.open = options.open || '[';
@@ -59,6 +59,12 @@ function Progress(runner, options) {
       , n = width * percent | 0
       , i = width - n;
 
+    if (lastN === n && !options.verbose) {
+      // Don't re-render the line if it hasn't changed
+      return;
+    }
+    lastN = n;
+
     cursor.CR();
     process.stdout.write('\u001b[J');
     process.stdout.write(color('progress', '  ' + options.open));
diff --git a/lib/reporters/xunit.js b/lib/reporters/xunit.js
index e956380..3506a07 100644
--- a/lib/reporters/xunit.js
+++ b/lib/reporters/xunit.js
@@ -83,8 +83,7 @@ function test(test) {
 
   if ('failed' == test.state) {
     var err = test.err;
-    attrs.message = escape(err.message);
-    console.log(tag('testcase', attrs, false, tag('failure', attrs, false, cdata(err.stack))));
+    console.log(tag('testcase', attrs, false, tag('failure', {}, false, cdata(escape(err.message) + "\n" + err.stack))));
   } else if (test.pending) {
     console.log(tag('testcase', attrs, false, tag('skipped', {}, true)));
   } else {
diff --git a/lib/runnable.js b/lib/runnable.js
index 6940bb7..2696293 100644
--- a/lib/runnable.js
+++ b/lib/runnable.js
@@ -44,6 +44,7 @@ function Runnable(title, fn) {
   this.sync = ! this.async;
   this._timeout = 2000;
   this._slow = 75;
+  this._enableTimeouts = true;
   this.timedOut = false;
 }
 
@@ -63,6 +64,7 @@ Runnable.prototype.__proto__ = EventEmitter.prototype;
 
 Runnable.prototype.timeout = function(ms){
   if (0 == arguments.length) return this._timeout;
+  if (ms === 0) this._enableTimeouts = false;
   if ('string' == typeof ms) ms = milliseconds(ms);
   debug('timeout %d', ms);
   this._timeout = ms;
@@ -87,6 +89,21 @@ Runnable.prototype.slow = function(ms){
 };
 
 /**
+ * Set and & get timeout `enabled`.
+ *
+ * @param {Boolean} enabled
+ * @return {Runnable|Boolean} enabled or self
+ * @api private
+ */
+
+Runnable.prototype.enableTimeouts = function(enabled){
+  if (arguments.length === 0) return this._enableTimeouts;
+  debug('enableTimeouts %s', enabled);
+  this._enableTimeouts = enabled;
+  return this;
+};
+
+/**
  * Return the full title generated by recursively
  * concatenating the parent's full title.
  *
@@ -134,6 +151,7 @@ Runnable.prototype.resetTimeout = function(){
   var self = this;
   var ms = this.timeout() || 1e9;
 
+  if (!this._enableTimeouts) return;
   this.clearTimeout();
   this.timer = setTimeout(function(){
     self.callback(new Error('timeout of ' + ms + 'ms exceeded'));
@@ -160,13 +178,13 @@ Runnable.prototype.globals = function(arr){
 
 Runnable.prototype.run = function(fn){
   var self = this
-    , ms = this.timeout()
     , start = new Date
     , ctx = this.ctx
     , finished
     , emitted;
 
-  if (ctx) ctx.runnable(this);
+  // Some times the ctx exists but it is not runnable
+  if (ctx && ctx.runnable) ctx.runnable(this);
 
   // called multiple times
   function multiple(err) {
@@ -177,11 +195,13 @@ Runnable.prototype.run = function(fn){
 
   // finished
   function done(err) {
+    var ms = self.timeout();
     if (self.timedOut) return;
     if (finished) return multiple(err);
     self.clearTimeout();
     self.duration = new Date - start;
     finished = true;
+    if (!err && self.duration > ms && self._enableTimeouts) err = new Error('timeout of ' + ms + 'ms exceeded');
     fn(err);
   }
 
@@ -195,7 +215,13 @@ Runnable.prototype.run = function(fn){
     try {
       this.fn.call(ctx, function(err){
         if (err instanceof Error || toString.call(err) === "[object Error]") return done(err);
-        if (null != err) return done(new Error('done() invoked with non-Error: ' + err));
+        if (null != err) {
+          if (Object.prototype.toString.call(err) === '[object Object]') {
+            return done(new Error('done() invoked with non-Error: ' + JSON.stringify(err)));
+          } else {
+            return done(new Error('done() invoked with non-Error: ' + err));
+          }
+        }
         done();
       });
     } catch (err) {
@@ -223,7 +249,13 @@ Runnable.prototype.run = function(fn){
     var result = fn.call(ctx);
     if (result && typeof result.then === 'function') {
       self.resetTimeout();
-      result.then(function(){ done() }, done);
+      result
+        .then(function() {
+          done()
+        },
+        function(reason) {
+          done(reason || new Error('Promise rejected with no or falsy reason'))
+        });
     } else {
       done();
     }
diff --git a/lib/runner.js b/lib/runner.js
index a56e530..61fc27a 100644
--- a/lib/runner.js
+++ b/lib/runner.js
@@ -532,7 +532,13 @@ Runner.prototype.runSuite = function(suite, fn){
  */
 
 Runner.prototype.uncaught = function(err){
-  debug('uncaught exception %s', err.message);
+  if (err) {
+    debug('uncaught exception %s', err.message);
+  } else {
+    debug('uncaught undefined exception');
+    err = new Error('Catched undefined error, did you throw without specifying what?');
+  }
+  
   var runnable = this.currentRunnable;
   if (!runnable || 'failed' == runnable.state) return;
   runnable.clearTimeout();
diff --git a/lib/suite.js b/lib/suite.js
index b06645b..ee5983e 100644
--- a/lib/suite.js
+++ b/lib/suite.js
@@ -60,6 +60,7 @@ function Suite(title, parentContext) {
   this._afterAll = [];
   this.root = !title;
   this._timeout = 2000;
+  this._enableTimeouts = true;
   this._slow = 75;
   this._bail = false;
 }
@@ -82,6 +83,7 @@ Suite.prototype.clone = function(){
   debug('clone');
   suite.ctx = this.ctx;
   suite.timeout(this.timeout());
+  suite.enableTimeouts(this.enableTimeouts());
   suite.slow(this.slow());
   suite.bail(this.bail());
   return suite;
@@ -104,6 +106,21 @@ Suite.prototype.timeout = function(ms){
 };
 
 /**
+  * Set timeout `enabled`.
+  *
+  * @param {Boolean} enabled
+  * @return {Suite|Boolean} self or enabled
+  * @api private
+  */
+
+Suite.prototype.enableTimeouts = function(enabled){
+  if (arguments.length === 0) return this._enableTimeouts;
+  debug('enableTimeouts %s', enabled);
+  this._enableTimeouts = enabled;
+  return this;
+}
+
+/**
  * Set slow `ms` or short-hand such as "2s".
  *
  * @param {Number|String} ms
@@ -153,6 +170,7 @@ Suite.prototype.beforeAll = function(title, fn){
   var hook = new Hook(title, fn);
   hook.parent = this;
   hook.timeout(this.timeout());
+  hook.enableTimeouts(this.enableTimeouts());
   hook.slow(this.slow());
   hook.ctx = this.ctx;
   this._beforeAll.push(hook);
@@ -179,6 +197,7 @@ Suite.prototype.afterAll = function(title, fn){
   var hook = new Hook(title, fn);
   hook.parent = this;
   hook.timeout(this.timeout());
+  hook.enableTimeouts(this.enableTimeouts());
   hook.slow(this.slow());
   hook.ctx = this.ctx;
   this._afterAll.push(hook);
@@ -205,6 +224,7 @@ Suite.prototype.beforeEach = function(title, fn){
   var hook = new Hook(title, fn);
   hook.parent = this;
   hook.timeout(this.timeout());
+  hook.enableTimeouts(this.enableTimeouts());
   hook.slow(this.slow());
   hook.ctx = this.ctx;
   this._beforeEach.push(hook);
@@ -231,6 +251,7 @@ Suite.prototype.afterEach = function(title, fn){
   var hook = new Hook(title, fn);
   hook.parent = this;
   hook.timeout(this.timeout());
+  hook.enableTimeouts(this.enableTimeouts());
   hook.slow(this.slow());
   hook.ctx = this.ctx;
   this._afterEach.push(hook);
@@ -249,6 +270,7 @@ Suite.prototype.afterEach = function(title, fn){
 Suite.prototype.addSuite = function(suite){
   suite.parent = this;
   suite.timeout(this.timeout());
+  suite.enableTimeouts(this.enableTimeouts());
   suite.slow(this.slow());
   suite.bail(this.bail());
   this.suites.push(suite);
@@ -267,6 +289,7 @@ Suite.prototype.addSuite = function(suite){
 Suite.prototype.addTest = function(test){
   test.parent = this;
   test.timeout(this.timeout());
+  test.enableTimeouts(this.enableTimeouts());
   test.slow(this.slow());
   test.ctx = this.ctx;
   this.tests.push(test);
diff --git a/lib/utils.js b/lib/utils.js
index b0f67fe..bd84083 100644
--- a/lib/utils.js
+++ b/lib/utils.js
@@ -169,16 +169,19 @@ function ignored(path){
  * @api private
  */
 
-exports.files = function(dir, ret){
+exports.files = function(dir, ext, ret){
   ret = ret || [];
+  ext = ext || ['js'];
+
+  var re = new RegExp('\\.(' + ext.join('|') + ')$');
 
   fs.readdirSync(dir)
   .filter(ignored)
   .forEach(function(path){
     path = join(dir, path);
     if (fs.statSync(path).isDirectory()) {
-      exports.files(path, ret);
-    } else if (path.match(/\.(js|coffee|litcoffee|coffee.md)$/)) {
+      exports.files(path, ext, ret);
+    } else if (path.match(re)) {
       ret.push(path);
     }
   });
@@ -209,7 +212,7 @@ exports.slug = function(str){
 exports.clean = function(str) {
   str = str
     .replace(/\r\n?|[\n\u2028\u2029]/g, "\n").replace(/^\uFEFF/, '')
-    .replace(/^function *\(.*\) *{/, '')
+    .replace(/^function *\(.*\) *{|\(.*\) *=> *{?/, '')
     .replace(/\s+\}$/, '');
 
   var spaces = str.match(/^\n?( *)/)[1].length
@@ -297,3 +300,51 @@ exports.highlightTags = function(name) {
     code[i].innerHTML = highlight(code[i].innerHTML);
   }
 };
+
+
+/**
+ * Stringify `obj`.
+ *
+ * @param {Object} obj
+ * @return {String}
+ * @api private
+ */
+
+exports.stringify = function(obj) {
+  if (obj instanceof RegExp) return obj.toString();
+  return JSON.stringify(exports.canonicalize(obj), null, 2).replace(/,(\n|$)/g, '$1');
+}
+
+/**
+ * Return a new object that has the keys in sorted order.
+ * @param {Object} obj
+ * @return {Object}
+ * @api private
+ */
+
+exports.canonicalize = function(obj, stack) {
+   stack = stack || [];
+
+   if (exports.indexOf(stack, obj) !== -1) return '[Circular]';
+
+   var canonicalizedObj;
+
+   if ({}.toString.call(obj) === '[object Array]') {
+     stack.push(obj);
+     canonicalizedObj = exports.map(obj, function(item) {
+       return exports.canonicalize(item, stack);
+     });
+     stack.pop();
+   } else if (typeof obj === 'object' && obj !== null) {
+     stack.push(obj);
+     canonicalizedObj = {};
+     exports.forEach(exports.keys(obj).sort(), function(key) {
+       canonicalizedObj[key] = exports.canonicalize(obj[key], stack);
+     });
+     stack.pop();
+   } else {
+     canonicalizedObj = obj;
+   }
+
+   return canonicalizedObj;
+ }
diff --git a/mocha.js b/mocha.js
index 19edc87..079aa58 100644
--- a/mocha.js
+++ b/mocha.js
@@ -788,11 +788,26 @@ Context.prototype.runnable = function(runnable){
  */
 
 Context.prototype.timeout = function(ms){
+  if (arguments.length === 0) return this.runnable().timeout();
   this.runnable().timeout(ms);
   return this;
 };
 
 /**
+ * Set test timeout `enabled`.
+ *
+ * @param {Boolean} enabled
+ * @return {Context} self
+ * @api private
+ */
+
+Context.prototype.enableTimeouts = function (enabled) {
+  this.runnable().enableTimeouts(enabled);
+  return this;
+};
+
+
+/**
  * Set test slowness threshold `ms`.
  *
  * @param {Number} ms
@@ -951,6 +966,7 @@ module.exports = function(suite){
 
     context.describe = context.context = function(title, fn){
       var suite = Suite.create(suites[0], title);
+      suite.file = file;
       suites.unshift(suite);
       fn.call(suite);
       suites.shift();
@@ -991,6 +1007,7 @@ module.exports = function(suite){
       var suite = suites[0];
       if (suite.pending) var fn = null;
       var test = new Test(title, fn);
+      test.file = file;
       suite.addTest(test);
       return test;
     };
@@ -1051,7 +1068,7 @@ module.exports = function(suite){
 
   suite.on('require', visit);
 
-  function visit(obj) {
+  function visit(obj, file) {
     var suite;
     for (var key in obj) {
       if ('function' == typeof obj[key]) {
@@ -1070,7 +1087,9 @@ module.exports = function(suite){
             suites[0].afterEach(fn);
             break;
           default:
-            suites[0].addTest(new Test(key, fn));
+            var test = new Test(key, fn);
+            test.file = file;
+            suites[0].addTest(test);
         }
       } else {
         var suite = Suite.create(suites[0], key);
@@ -1172,6 +1191,7 @@ module.exports = function(suite){
     context.suite = function(title){
       if (suites.length > 1) suites.shift();
       var suite = Suite.create(suites[0], title);
+      suite.file = file;
       suites.unshift(suite);
       return suite;
     };
@@ -1193,6 +1213,7 @@ module.exports = function(suite){
 
     context.test = function(title, fn){
       var test = new Test(title, fn);
+      test.file = file;
       suites[0].addTest(test);
       return test;
     };
@@ -1299,6 +1320,7 @@ module.exports = function(suite){
 
     context.suite = function(title, fn){
       var suite = Suite.create(suites[0], title);
+      suite.file = file;
       suites.unshift(suite);
       fn.call(suite);
       suites.shift();
@@ -1335,6 +1357,7 @@ module.exports = function(suite){
       var suite = suites[0];
       if (suite.pending) var fn = null;
       var test = new Test(title, fn);
+      test.file = file;
       suite.addTest(test);
       return test;
     };
@@ -1382,6 +1405,16 @@ var path = require('browser/path')
 exports = module.exports = Mocha;
 
 /**
+ * To require local UIs and reporters when running in node.
+ */
+
+if (typeof process !== 'undefined' && typeof process.cwd === 'function') {
+  var join = path.join
+    , cwd = process.cwd();
+  module.paths.push(cwd, join(cwd, 'node_modules'));
+}
+
+/**
  * Expose internals.
  */
 
@@ -1413,7 +1446,7 @@ function image(name) {
  * Options:
  *
  *   - `ui` name "bdd", "tdd", "exports" etc
- *   - `reporter` reporter instance, defaults to `mocha.reporters.Dot`
+ *   - `reporter` reporter instance, defaults to `mocha.reporters.spec`
  *   - `globals` array of accepted globals
  *   - `timeout` timeout in milliseconds
  *   - `bail` bail on the first test failure
@@ -1436,6 +1469,7 @@ function Mocha(options) {
   this.reporter(options.reporter);
   if (null != options.timeout) this.timeout(options.timeout);
   this.useColors(options.useColors)
+  if (options.enableTimeouts !== null) this.enableTimeouts(options.enableTimeouts);
   if (options.slow) this.slow(options.slow);
 
   this.suite.on('pre-require', function (context) {
@@ -1480,7 +1514,7 @@ Mocha.prototype.addFile = function(file){
 };
 
 /**
- * Set reporter to `reporter`, defaults to "dot".
+ * Set reporter to `reporter`, defaults to "spec".
  *
  * @param {String|Function} reporter name or constructor
  * @api public
@@ -1490,7 +1524,7 @@ Mocha.prototype.reporter = function(reporter){
   if ('function' == typeof reporter) {
     this._reporter = reporter;
   } else {
-    reporter = reporter || 'dot';
+    reporter = reporter || 'spec';
     var _reporter;
     try { _reporter = require('./reporters/' + reporter); } catch (err) {};
     if (!_reporter) try { _reporter = require(reporter); } catch (err) {};
@@ -1697,6 +1731,21 @@ Mocha.prototype.slow = function(slow){
 };
 
 /**
+ * Enable timeouts.
+ *
+ * @param {Boolean} enabled
+ * @return {Mocha}
+ * @api public
+ */
+
+Mocha.prototype.enableTimeouts = function(enabled) {
+  this.suite.enableTimeouts(arguments.length && enabled !== undefined
+    ? enabled
+    : true);
+  return this
+};
+
+/**
  * Makes all tests async (accepting a callback)
  *
  * @return {Mocha}
@@ -2031,8 +2080,8 @@ exports.list = function(failures){
     // explicitly show diff
     if (err.showDiff && sameType(actual, expected)) {
       escape = false;
-      err.actual = actual = stringify(canonicalize(actual));
-      err.expected = expected = stringify(canonicalize(expected));
+      err.actual = actual = utils.stringify(actual);
+      err.expected = expected = utils.stringify(expected);
     }
 
     // actual / expected diff
@@ -2295,53 +2344,6 @@ function colorLines(name, str) {
 }
 
 /**
- * Stringify `obj`.
- *
- * @param {Object} obj
- * @return {String}
- * @api private
- */
-
-function stringify(obj) {
-  if (obj instanceof RegExp) return obj.toString();
-  return JSON.stringify(obj, null, 2);
-}
-
-/**
- * Return a new object that has the keys in sorted order.
- * @param {Object} obj
- * @return {Object}
- * @api private
- */
-
- function canonicalize(obj, stack) {
-   stack = stack || [];
-
-   if (utils.indexOf(stack, obj) !== -1) return obj;
-
-   var canonicalizedObj;
-
-   if ('[object Array]' == {}.toString.call(obj)) {
-     stack.push(obj);
-     canonicalizedObj = utils.map(obj, function(item) {
-       return canonicalize(item, stack);
-     });
-     stack.pop();
-   } else if (typeof obj === 'object' && obj !== null) {
-     stack.push(obj);
-     canonicalizedObj = {};
-     utils.forEach(utils.keys(obj).sort(), function(key) {
-       canonicalizedObj[key] = canonicalize(obj[key], stack);
-     });
-     stack.pop();
-   } else {
-     canonicalizedObj = obj;
-   }
-
-   return canonicalizedObj;
- }
-
-/**
  * Check that a / b have the same type.
  *
  * @param {Object} a
@@ -2356,7 +2358,6 @@ function sameType(a, b) {
   return a == b;
 }
 
-
 }); // module: reporters/base.js
 
 require.register("reporters/doc.js", function(module, exports, require){
@@ -2415,6 +2416,13 @@ function Doc(runner) {
     var code = utils.escape(utils.clean(test.fn.toString()));
     console.log('%s  <dd><pre><code>%s</code></pre></dd>', indent(), code);
   });
+
+  runner.on('fail', function(test, err){
+    console.log('%s  <dt class="error">%s</dt>', indent(), utils.escape(test.title));
+    var code = utils.escape(utils.clean(test.fn.toString()));
+    console.log('%s  <dd class="error"><pre><code>%s</code></pre></dd>', indent(), code);
+    console.log('%s  <dd class="error">%s</dd>', indent(), utils.escape(err));
+  });
 }
 
 }); // module: reporters/doc.js
@@ -2447,13 +2455,14 @@ function Dot(runner) {
   var self = this
     , stats = this.stats
     , width = Base.window.width * .75 | 0
-    , n = 0;
+    , n = -1;
 
   runner.on('start', function(){
     process.stdout.write('\n  ');
   });
 
   runner.on('pending', function(test){
+    if (++n % width == 0) process.stdout.write('\n  ');
     process.stdout.write(color('pending', Base.symbols.dot));
   });
 
@@ -2486,6 +2495,7 @@ F.prototype = Base.prototype;
 Dot.prototype = new F;
 Dot.prototype.constructor = Dot;
 
+
 }); // module: reporters/dot.js
 
 require.register("reporters/html-cov.js", function(module, exports, require){
@@ -3101,17 +3111,22 @@ function JSONReporter(runner) {
     passes.push(test);
   });
 
-  runner.on('fail', function(test){
+  runner.on('fail', function(test, err){
     failures.push(test);
+    if (err === Object(err)) {
+      test.errMsg = err.message;
+      test.errStack = err.stack;
+    }
   });
 
   runner.on('end', function(){
     var obj = {
-        stats: self.stats
-      , tests: tests.map(clean)
-      , failures: failures.map(clean)
-      , passes: passes.map(clean)
+      stats: self.stats,
+      tests: tests.map(clean),
+      failures: failures.map(clean),
+      passes: passes.map(clean)
     };
+    runner.testResults = obj;
 
     process.stdout.write(JSON.stringify(obj, null, 2));
   });
@@ -3128,11 +3143,15 @@ function JSONReporter(runner) {
 
 function clean(test) {
   return {
-      title: test.title
-    , fullTitle: test.fullTitle()
-    , duration: test.duration
+    title: test.title,
+    fullTitle: test.fullTitle(),
+    duration: test.duration,
+    err: test.err,
+    errStack: test.err.stack,
+    errMessage: test.err.message
   }
 }
+
 }); // module: reporters/json.js
 
 require.register("reporters/landing.js", function(module, exports, require){
@@ -3720,7 +3739,6 @@ NyanCat.prototype.constructor = NyanCat;
 }); // module: reporters/nyan.js
 
 require.register("reporters/progress.js", function(module, exports, require){
-
 /**
  * Module dependencies.
  */
@@ -3758,7 +3776,8 @@ function Progress(runner, options) {
     , width = Base.window.width * .50 | 0
     , total = runner.total
     , complete = 0
-    , max = Math.max;
+    , max = Math.max
+    , lastN = -1;
 
   // default chars
   options.open = options.open || '[';
@@ -3781,6 +3800,12 @@ function Progress(runner, options) {
       , n = width * percent | 0
       , i = width - n;
 
+    if (lastN === n && !options.verbose) {
+      // Don't re-render the line if it hasn't changed
+      return;
+    }
+    lastN = n;
+
     cursor.CR();
     process.stdout.write('\u001b[J');
     process.stdout.write(color('progress', '  ' + options.open));
@@ -4071,8 +4096,7 @@ function test(test) {
 
   if ('failed' == test.state) {
     var err = test.err;
-    attrs.message = escape(err.message);
-    console.log(tag('testcase', attrs, false, tag('failure', attrs, false, cdata(err.stack))));
+    console.log(tag('testcase', attrs, false, tag('failure', {}, false, cdata(escape(err.message) + "\n" + err.stack))));
   } else if (test.pending) {
     console.log(tag('testcase', attrs, false, tag('skipped', {}, true)));
   } else {
@@ -4155,6 +4179,7 @@ function Runnable(title, fn) {
   this.sync = ! this.async;
   this._timeout = 2000;
   this._slow = 75;
+  this._enableTimeouts = true;
   this.timedOut = false;
 }
 
@@ -4202,6 +4227,21 @@ Runnable.prototype.slow = function(ms){
 };
 
 /**
+ * Set and & get timeout `enabled`.
+ *
+ * @param {Boolean} enabled
+ * @return {Runnable|Boolean} enabled or self
+ * @api private
+ */
+
+Runnable.prototype.enableTimeouts = function(enabled){
+  if (arguments.length === 0) return this._enableTimeouts;
+  debug('enableTimeouts %s', enabled);
+  this._enableTimeouts = enabled;
+  return this;
+};
+
+/**
  * Return the full title generated by recursively
  * concatenating the parent's full title.
  *
@@ -4249,6 +4289,7 @@ Runnable.prototype.resetTimeout = function(){
   var self = this;
   var ms = this.timeout() || 1e9;
 
+  if (!this._enableTimeouts) return;
   this.clearTimeout();
   this.timer = setTimeout(function(){
     self.callback(new Error('timeout of ' + ms + 'ms exceeded'));
@@ -4275,13 +4316,13 @@ Runnable.prototype.globals = function(arr){
 
 Runnable.prototype.run = function(fn){
   var self = this
-    , ms = this.timeout()
     , start = new Date
     , ctx = this.ctx
     , finished
     , emitted;
 
-  if (ctx) ctx.runnable(this);
+  // Some times the ctx exists but it is not runnable
+  if (ctx && ctx.runnable) ctx.runnable(this);
 
   // called multiple times
   function multiple(err) {
@@ -4292,11 +4333,13 @@ Runnable.prototype.run = function(fn){
 
   // finished
   function done(err) {
+    var ms = self.timeout();
     if (self.timedOut) return;
     if (finished) return multiple(err);
     self.clearTimeout();
     self.duration = new Date - start;
     finished = true;
+    if (!err && self.duration > ms && self._enableTimeouts) err = new Error('timeout of ' + ms + 'ms exceeded');
     fn(err);
   }
 
@@ -4310,7 +4353,13 @@ Runnable.prototype.run = function(fn){
     try {
       this.fn.call(ctx, function(err){
         if (err instanceof Error || toString.call(err) === "[object Error]") return done(err);
-        if (null != err) return done(new Error('done() invoked with non-Error: ' + err));
+        if (null != err) {
+          if (Object.prototype.toString.call(err) === '[object Object]') {
+            return done(new Error('done() invoked with non-Error: ' + JSON.stringify(err)));
+          } else {
+            return done(new Error('done() invoked with non-Error: ' + err));
+          }
+        }
         done();
       });
     } catch (err) {
@@ -4338,7 +4387,13 @@ Runnable.prototype.run = function(fn){
     var result = fn.call(ctx);
     if (result && typeof result.then === 'function') {
       self.resetTimeout();
-      result.then(function(){ done() }, done);
+      result
+        .then(function() {
+          done()
+        },
+        function(reason) {
+          done(reason || new Error('Promise rejected with no or falsy reason'))
+        });
     } else {
       done();
     }
@@ -4515,7 +4570,6 @@ Runner.prototype.checkGlobals = function(test){
   var ok = this._globals;
 
   var globals = this.globalProps();
-  var isNode = process.kill;
   var leaks;
 
   if (test) {
@@ -4887,7 +4941,13 @@ Runner.prototype.runSuite = function(suite, fn){
  */
 
 Runner.prototype.uncaught = function(err){
-  debug('uncaught exception %s', err.message);
+  if (err) {
+    debug('uncaught exception %s', err.message);
+  } else {
+    debug('uncaught undefined exception');
+    err = new Error('Catched undefined error, did you throw without specifying what?');
+  }
+  
   var runnable = this.currentRunnable;
   if (!runnable || 'failed' == runnable.state) return;
   runnable.clearTimeout();
@@ -5067,7 +5127,7 @@ exports.create = function(parent, title){
 
 function Suite(title, parentContext) {
   this.title = title;
-  var context = function () {};
+  var context = function() {};
   context.prototype = parentContext;
   this.ctx = new context();
   this.suites = [];
@@ -5079,6 +5139,7 @@ function Suite(title, parentContext) {
   this._afterAll = [];
   this.root = !title;
   this._timeout = 2000;
+  this._enableTimeouts = true;
   this._slow = 75;
   this._bail = false;
 }
@@ -5105,6 +5166,7 @@ Suite.prototype.clone = function(){
   debug('clone');
   suite.ctx = this.ctx;
   suite.timeout(this.timeout());
+  suite.enableTimeouts(this.enableTimeouts());
   suite.slow(this.slow());
   suite.bail(this.bail());
   return suite;
@@ -5127,6 +5189,21 @@ Suite.prototype.timeout = function(ms){
 };
 
 /**
+  * Set timeout `enabled`.
+  *
+  * @param {Boolean} enabled
+  * @return {Suite|Boolean} self or enabled
+  * @api private
+  */
+
+Suite.prototype.enableTimeouts = function(enabled){
+  if (arguments.length === 0) return this._enableTimeouts;
+  debug('enableTimeouts %s', enabled);
+  this._enableTimeouts = enabled;
+  return this;
+}
+
+/**
  * Set slow `ms` or short-hand such as "2s".
  *
  * @param {Number|String} ms
@@ -5176,6 +5253,7 @@ Suite.prototype.beforeAll = function(title, fn){
   var hook = new Hook(title, fn);
   hook.parent = this;
   hook.timeout(this.timeout());
+  hook.enableTimeouts(this.enableTimeouts());
   hook.slow(this.slow());
   hook.ctx = this.ctx;
   this._beforeAll.push(hook);
@@ -5202,6 +5280,7 @@ Suite.prototype.afterAll = function(title, fn){
   var hook = new Hook(title, fn);
   hook.parent = this;
   hook.timeout(this.timeout());
+  hook.enableTimeouts(this.enableTimeouts());
   hook.slow(this.slow());
   hook.ctx = this.ctx;
   this._afterAll.push(hook);
@@ -5228,6 +5307,7 @@ Suite.prototype.beforeEach = function(title, fn){
   var hook = new Hook(title, fn);
   hook.parent = this;
   hook.timeout(this.timeout());
+  hook.enableTimeouts(this.enableTimeouts());
   hook.slow(this.slow());
   hook.ctx = this.ctx;
   this._beforeEach.push(hook);
@@ -5254,6 +5334,7 @@ Suite.prototype.afterEach = function(title, fn){
   var hook = new Hook(title, fn);
   hook.parent = this;
   hook.timeout(this.timeout());
+  hook.enableTimeouts(this.enableTimeouts());
   hook.slow(this.slow());
   hook.ctx = this.ctx;
   this._afterEach.push(hook);
@@ -5272,6 +5353,7 @@ Suite.prototype.afterEach = function(title, fn){
 Suite.prototype.addSuite = function(suite){
   suite.parent = this;
   suite.timeout(this.timeout());
+  suite.enableTimeouts(this.enableTimeouts());
   suite.slow(this.slow());
   suite.bail(this.bail());
   this.suites.push(suite);
@@ -5290,6 +5372,7 @@ Suite.prototype.addSuite = function(suite){
 Suite.prototype.addTest = function(test){
   test.parent = this;
   test.timeout(this.timeout());
+  test.enableTimeouts(this.enableTimeouts());
   test.slow(this.slow());
   test.ctx = this.ctx;
   this.tests.push(test);
@@ -5558,16 +5641,19 @@ function ignored(path){
  * @api private
  */
 
-exports.files = function(dir, ret){
+exports.files = function(dir, ext, ret){
   ret = ret || [];
+  ext = ext || ['js'];
+
+  var re = new RegExp('\\.(' + ext.join('|') + ')$');
 
   fs.readdirSync(dir)
   .filter(ignored)
   .forEach(function(path){
     path = join(dir, path);
     if (fs.statSync(path).isDirectory()) {
-      exports.files(path, ret);
-    } else if (path.match(/\.(js|coffee|litcoffee|coffee.md)$/)) {
+      exports.files(path, ext, ret);
+    } else if (path.match(re)) {
       ret.push(path);
     }
   });
@@ -5598,7 +5684,7 @@ exports.slug = function(str){
 exports.clean = function(str) {
   str = str
     .replace(/\r\n?|[\n\u2028\u2029]/g, "\n").replace(/^\uFEFF/, '')
-    .replace(/^function *\(.*\) *{/, '')
+    .replace(/^function *\(.*\) *{|\(.*\) *=> *{?/, '')
     .replace(/\s+\}$/, '');
 
   var spaces = str.match(/^\n?( *)/)[1].length
@@ -5669,7 +5755,7 @@ function highlight(js) {
     .replace(/('.*?')/gm, '<span class="string">$1</span>')
     .replace(/(\d+\.\d+)/gm, '<span class="number">$1</span>')
     .replace(/(\d+)/gm, '<span class="number">$1</span>')
-    .replace(/\bnew *(\w+)/gm, '<span class="keyword">new</span> <span class="init">$1</span>')
+    .replace(/\bnew[ \t]+(\w+)/gm, '<span class="keyword">new</span> <span class="init">$1</span>')
     .replace(/\b(function|new|throw|return|var|if|else)\b/gm, '<span class="keyword">$1</span>')
 }
 
@@ -5687,9 +5773,57 @@ exports.highlightTags = function(name) {
   }
 };
 
+
+/**
+ * Stringify `obj`.
+ *
+ * @param {Object} obj
+ * @return {String}
+ * @api private
+ */
+
+exports.stringify = function(obj) {
+  if (obj instanceof RegExp) return obj.toString();
+  return JSON.stringify(exports.canonicalize(obj), null, 2).replace(/,(\n|$)/g, '$1');
+}
+
+/**
+ * Return a new object that has the keys in sorted order.
+ * @param {Object} obj
+ * @return {Object}
+ * @api private
+ */
+
+exports.canonicalize = function(obj, stack) {
+   stack = stack || [];
+
+   if (exports.indexOf(stack, obj) !== -1) return '[Circular]';
+
+   var canonicalizedObj;
+
+   if ({}.toString.call(obj) === '[object Array]') {
+     stack.push(obj);
+     canonicalizedObj = exports.map(obj, function(item) {
+       return exports.canonicalize(item, stack);
+     });
+     stack.pop();
+   } else if (typeof obj === 'object' && obj !== null) {
+     stack.push(obj);
+     canonicalizedObj = {};
+     exports.forEach(exports.keys(obj).sort(), function(key) {
+       canonicalizedObj[key] = exports.canonicalize(obj[key], stack);
+     });
+     stack.pop();
+   } else {
+     canonicalizedObj = obj;
+   }
+
+   return canonicalizedObj;
+ }
+
 }); // module: utils.js
 // The global object is "self" in Web Workers.
-global = (function() { return this; })();
+var global = (function() { return this; })();
 
 /**
  * Save timer references to avoid Sinon interfering (see GH-237).
@@ -5716,13 +5850,20 @@ process.stdout = {};
 
 var uncaughtExceptionHandlers = [];
 
+var originalOnerrorHandler = global.onerror;
+
 /**
  * Remove uncaughtException listener.
+ * Revert to original onerror handler if previously defined.
  */
 
 process.removeListener = function(e, fn){
   if ('uncaughtException' == e) {
-    global.onerror = function() {};
+    if (originalOnerrorHandler) {
+      global.onerror = originalOnerrorHandler;
+    } else {
+      global.onerror = function() {};
+    }
     var i = Mocha.utils.indexOf(uncaughtExceptionHandlers, fn);
     if (i != -1) { uncaughtExceptionHandlers.splice(i, 1); }
   }
@@ -5825,12 +5966,12 @@ mocha.run = function(fn){
   if (query.grep) mocha.grep(query.grep);
   if (query.invert) mocha.invert();
 
-  return Mocha.prototype.run.call(mocha, function(){
+  return Mocha.prototype.run.call(mocha, function(err){
     // The DOM Document is not available in Web Workers.
     if (global.document) {
       Mocha.utils.highlightTags('code');
     }
-    if (fn) fn();
+    if (fn) fn(err);
   });
 };
 
diff --git a/package.json b/package.json
index 89770c8..a8dd6a2 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "mocha",
-  "version": "1.20.1",
+  "version": "1.21.4",
   "description": "simple, flexible, fun test framework",
   "keywords": [
     "mocha",
@@ -10,6 +10,15 @@
     "tap"
   ],
   "author": "TJ Holowaychuk <tj at vision-media.ca>",
+  "contributors": [
+    "Joshua Appelman <joshua at jbna.nl>",
+    "Oleg Gaidarenko <markelog at gmail.com>",
+    "Christoffer Hallas <christoffer.hallas at gmail.com>",
+    "Christopher Hiller <chiller at badwing.com>",
+    "Travis Jeffery <tj at travisjeffery.com>",
+    "Johnathan Ong <me at jongleberry.com>",
+    "Guillermo Rauch <rauchg at gmail.com>"
+  ],
   "repository": {
     "type": "git",
     "url": "git://github.com/visionmedia/mocha.git"
@@ -28,7 +37,7 @@
   },
   "dependencies": {
     "commander": "2.0.0",
-    "growl": "1.7.x",
+    "growl": "1.8.x",
     "jade": "0.26.3",
     "diff": "1.0.7",
     "debug": "*",
@@ -36,7 +45,7 @@
     "glob": "3.2.3"
   },
   "devDependencies": {
-    "coffee-script": "1.2",
+    "coffee-script": "~1.7.1",
     "should": "~4.0.0"
   },
   "files": [
diff --git a/support/tail.js b/support/tail.js
index 999d38d..16be67c 100644
--- a/support/tail.js
+++ b/support/tail.js
@@ -1,5 +1,5 @@
 // The global object is "self" in Web Workers.
-global = (function() { return this; })();
+var global = (function() { return this; })();
 
 /**
  * Save timer references to avoid Sinon interfering (see GH-237).
@@ -26,13 +26,20 @@ process.stdout = {};
 
 var uncaughtExceptionHandlers = [];
 
+var originalOnerrorHandler = global.onerror;
+
 /**
  * Remove uncaughtException listener.
+ * Revert to original onerror handler if previously defined.
  */
 
 process.removeListener = function(e, fn){
   if ('uncaughtException' == e) {
-    global.onerror = function() {};
+    if (originalOnerrorHandler) {
+      global.onerror = originalOnerrorHandler;
+    } else {
+      global.onerror = function() {};
+    }
     var i = Mocha.utils.indexOf(uncaughtExceptionHandlers, fn);
     if (i != -1) { uncaughtExceptionHandlers.splice(i, 1); }
   }
diff --git a/test/acceptance/context.js b/test/acceptance/context.js
index 0d39f14..add011b 100644
--- a/test/acceptance/context.js
+++ b/test/acceptance/context.js
@@ -65,3 +65,9 @@ describe('Context Siblings', function(){
   })
 
 })
+
+describe('timeout()', function(){
+  it('should return the timeout', function(){
+    this.timeout().should.equal(200);
+  });
+});
diff --git a/test/acceptance/diffs.js b/test/acceptance/diffs.js
index 17c3dfa..b0b6eae 100644
--- a/test/acceptance/diffs.js
+++ b/test/acceptance/diffs.js
@@ -77,4 +77,11 @@ describe('diffs', function(){
 
     // tobi.should.eql(loki);
   });
+
+  it('should show value diffs and not be affected by commas', function(){
+    var obj1 = { a: 123 };
+    var obj2 = { a: 123, b: 456 };
+
+    // obj1.should.equal(obj2);
+  });
 });
diff --git a/test/acceptance/failing/timeout.js b/test/acceptance/failing/timeout.js
new file mode 100644
index 0000000..e52dde4
--- /dev/null
+++ b/test/acceptance/failing/timeout.js
@@ -0,0 +1,17 @@
+describe('timeout', function(){
+  this.timeout(1);
+
+  it('should be honored with sync suites', function(){
+    sleep(2);
+  });
+
+  it('should be honored with async suites', function(done){
+    sleep(2);
+    done();
+  });
+
+  function sleep(ms){
+    var start = Date.now();
+    while(start + ms > Date.now())continue;
+  }
+});
diff --git a/test/acceptance/timeout.js b/test/acceptance/timeout.js
index 109808b..3e865f9 100644
--- a/test/acceptance/timeout.js
+++ b/test/acceptance/timeout.js
@@ -18,4 +18,18 @@ describe('timeouts', function(){
       done();
     }, 300);
   })
+
+  describe('disabling', function(){
+    it('should allow overriding per-test', function(done){
+      this.enableTimeouts(false);
+      this.timeout(1);
+      setTimeout(done, 2);
+    });
+    
+    it('should work with timeout(0)', function(done) {
+      this.timeout(0);
+      setTimeout(done, 1);
+    })
+  });
+  
 })
diff --git a/test/acceptance/utils.js b/test/acceptance/utils.js
index 9737368..9c900a9 100644
--- a/test/acceptance/utils.js
+++ b/test/acceptance/utils.js
@@ -55,5 +55,35 @@ describe('lib/utils', function () {
       ].join("\r\n");
       utils.clean(fn).should.equal("if (false) {\n\tvar json = {\n\t\tone : 1\n\t};\n}");
     });
+
+    it("should format es6 arrow functions", function () {
+      var fn = [
+        "() => {",
+        "  var a = 1;",
+        "}"
+      ].join("\n");
+      utils.clean(fn).should.equal("var a = 1;");
+    });
+
+    it("should format es6 arrow functions with implicit return", function () {
+      var fn = "() => foo()";
+      utils.clean(fn).should.equal("foo()");
+    });
+  });
+
+  describe('stringify', function(){
+    it('should canoncalize the object', function(){
+      var travis = { name: 'travis', age: 24 };
+      var travis2 = { age: 24, name: 'travis' };
+
+      utils.stringify(travis).should.equal(utils.stringify(travis2));
+    });
+
+    it('should handle circular structures', function(){
+      var travis = { name: 'travis' };
+      travis.whoami = travis;
+
+      utils.stringify(travis).should.equal('{\n  "name": "travis"\n  "whoami": "[Circular]"\n}');
+    });
   });
-});
\ No newline at end of file
+});
diff --git a/test/reporters/json.js b/test/reporters/json.js
new file mode 100644
index 0000000..e5c1a0a
--- /dev/null
+++ b/test/reporters/json.js
@@ -0,0 +1,43 @@
+
+var Mocha = require('../../')
+  , Suite = Mocha.Suite
+  , Runner = Mocha.Runner
+  , Test = Mocha.Test;
+
+describe('json reporter', function(){
+  var suite, runner;
+
+  beforeEach(function(){
+    var mocha = new Mocha({
+      reporter: 'json'
+    });
+    suite = new Suite('JSON suite', 'root');
+    runner = new Runner(suite);
+    var mochaReporter = new mocha._reporter(runner);
+  })
+
+   it('should have 1 test failure', function(done){
+     var testTitle = 'json test 1';
+     var error = { message: 'oh shit' };
+
+     suite.addTest(new Test(testTitle, function (done) {
+       done(new Error(error.message));
+     }));
+
+     runner.run(function(failureCount) {
+       failureCount.should.be.exactly(1);
+       runner.should.have.property('testResults');
+       runner.testResults.should.have.property('failures');
+       runner.testResults.failures.should.be.an.instanceOf(Array);
+       runner.testResults.failures.should.have.a.lengthOf(1);
+
+       var failure = runner.testResults.failures[0];
+       failure.should.have.property('title', testTitle);
+       failure.err.message.should.equal(error.message);
+       failure.should.have.properties('err');
+
+       done();
+     });
+  })
+
+})
diff --git a/test/runnable.js b/test/runnable.js
index 79b6ffb..ee20c8a 100644
--- a/test/runnable.js
+++ b/test/runnable.js
@@ -41,6 +41,14 @@ describe('Runnable(title, fn)', function(){
     })
   })
 
+  describe('#enableTimeouts(enabled)', function(){
+    it('should set enabled', function(){
+      var run = new Runnable;
+      run.enableTimeouts(false);
+      run.enableTimeouts().should.equal(false);
+    });
+  });
+
   describe('#slow(ms)', function(){
     it('should set the slow threshold', function(){
       var run = new Runnable;
@@ -126,6 +134,17 @@ describe('Runnable(title, fn)', function(){
       })
     })
 
+    describe('when timeouts are disabled', function() {
+      it('should not error with timeout', function(done) {
+        var test = new Runnable('foo', function(done){
+          setTimeout(process.nextTick.bind(undefined, done), 2);
+        });
+        test.timeout(1);
+        test.enableTimeouts(false);
+        test.run(done);
+      });
+    });
+
     describe('when async', function(){
       describe('without error', function(){
         it('should invoke the callback', function(done){
@@ -222,6 +241,32 @@ describe('Runnable(title, fn)', function(){
         })
       })
 
+      describe('when done() is invoked with a non-Error object', function(){
+        it('should invoke the callback', function(done){
+          var test = new Runnable('foo', function(done){
+            done({ error: 'Test error' });
+          });
+
+          test.run(function(err){
+            err.message.should.equal('done() invoked with non-Error: {"error":"Test error"}');
+            done();
+          });
+        })
+      })
+
+      describe('when done() is invoked with a string', function(){
+        it('should invoke the callback', function(done){
+          var test = new Runnable('foo', function(done){
+            done('Test error');
+          });
+
+          test.run(function(err){
+            err.message.should.equal('done() invoked with non-Error: Test error');
+            done();
+          });
+        })
+      })
+
       it('should allow updating the timeout', function(done){
         var callCount = 0;
         var increment = function() {
@@ -299,6 +344,28 @@ describe('Runnable(title, fn)', function(){
         })
       })
 
+      describe('when the promise is rejected without a reason', function(){
+        var expectedErr = new Error('Promise rejected with no or falsy reason');
+        var rejectedPromise = {
+          then: function (fulfilled, rejected) {
+            process.nextTick(function () {
+              rejected();
+            });
+          }
+        };
+
+        it('should invoke the callback', function(done){
+          var test = new Runnable('foo', function(){
+            return rejectedPromise;
+          });
+
+          test.run(function(err){
+            err.should.eql(expectedErr);
+            done();
+          });
+        })
+      })
+
       describe('when the promise takes too long to settle', function(){
         var foreverPendingPromise = {
           then: function () { }

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-javascript/node-mocha.git



More information about the Pkg-javascript-commits mailing list