[Pkg-javascript-commits] [node-mocha] 04/10: Imported Upstream version 2.2.5

Leo Iannacone l3on-guest at moszumanska.debian.org
Sat May 30 15:58:24 UTC 2015


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 aeb656ee3cdf0494032c2737a9d675fb7a9a065b
Author: Leo Iannacone <info at leoiannacone.com>
Date:   Sat May 30 17:25:26 2015 +0200

    Imported Upstream version 2.2.5
---
 .editorconfig                                      |   18 +
 .mailmap                                           |    1 +
 .travis.yml                                        |   16 +-
 CONTRIBUTING.md                                    |   49 +
 History.md => HISTORY.md                           |  144 ++-
 LICENSE                                            |    2 +-
 Makefile                                           |   53 +-
 README.md                                          |   11 +
 Readme.md                                          |  203 ----
 bin/_mocha                                         |  112 +-
 bin/mocha                                          |   26 +-
 bin/options.js                                     |   36 +
 bower.json                                         |   30 +-
 component.json                                     |   14 +-
 index.js                                           |    3 +-
 lib/browser/debug.js                               |    1 -
 lib/browser/escape-string-regexp.js                |    8 +-
 lib/browser/events.js                              |    3 +-
 lib/browser/progress.js                            |   12 +-
 lib/browser/tty.js                                 |    1 -
 lib/context.js                                     |   13 +-
 lib/hook.js                                        |    1 -
 lib/interfaces/bdd.js                              |   39 +-
 lib/interfaces/common.js                           |   58 ++
 lib/interfaces/exports.js                          |    1 -
 lib/interfaces/index.js                            |    1 -
 lib/interfaces/qunit.js                            |   45 +-
 lib/interfaces/tdd.js                              |   46 +-
 lib/mocha.js                                       |   69 +-
 lib/pending.js                                     |   16 +
 lib/reporters/base.js                              |   57 +-
 lib/reporters/doc.js                               |    1 -
 lib/reporters/dot.js                               |    3 +-
 lib/reporters/html-cov.js                          |    3 +-
 lib/reporters/html.js                              |   24 +-
 lib/reporters/index.js                             |    1 -
 lib/reporters/json-cov.js                          |    1 -
 lib/reporters/json-stream.js                       |    7 +-
 lib/reporters/json.js                              |    1 -
 lib/reporters/landing.js                           |    7 +-
 lib/reporters/list.js                              |    1 -
 lib/reporters/markdown.js                          |   21 +-
 lib/reporters/min.js                               |    1 -
 lib/reporters/nyan.js                              |   26 +-
 lib/reporters/spec.js                              |    7 +-
 lib/reporters/tap.js                               |    1 -
 lib/reporters/templates/menu.jade                  |    2 +-
 lib/reporters/templates/style.html                 |    8 +-
 lib/reporters/xunit.js                             |   51 +-
 lib/runnable.js                                    |   32 +-
 lib/runner.js                                      |  119 ++-
 lib/suite.js                                       |   15 +-
 lib/test.js                                        |    1 -
 lib/utils.js                                       |  408 +++++++-
 media/logo.svg                                     |    3 +-
 mocha.js                                           | 1093 ++++++++++++++------
 package.json                                       |   28 +-
 support/compile.js                                 |    1 -
 support/foot.js                                    |    2 +-
 support/tail.js                                    |    3 +-
 test.js                                            |    9 +
 test/acceptance/context.js                         |    1 -
 test/acceptance/diffs.js                           |   87 --
 test/acceptance/duration.js                        |    1 -
 test/acceptance/failing/timeout.js                 |   17 -
 test/acceptance/fs.js                              |    1 -
 test/acceptance/glob/glob.js                       |    1 -
 test/acceptance/globals.js                         |    1 -
 test/acceptance/http.js                            |    3 +-
 test/acceptance/interfaces/bdd.js                  |    1 -
 test/acceptance/interfaces/exports.js              |    1 -
 test/acceptance/interfaces/qunit.js                |    3 +-
 test/acceptance/interfaces/tdd.js                  |    1 -
 test/acceptance/misc/asyncOnly.js                  |   10 -
 test/acceptance/misc/bail.js                       |   21 -
 test/acceptance/misc/cascade.js                    |   58 --
 test/acceptance/misc/exit.js                       |    5 +
 test/acceptance/misc/grep.js                       |   22 -
 test/acceptance/misc/many.js                       |    2 +-
 test/acceptance/misc/nontty.js                     |    1 -
 test/acceptance/misc/only/bdd.js                   |    3 +-
 test/acceptance/misc/only/qunit.js                 |    3 +-
 test/acceptance/misc/only/tdd.js                   |    3 +-
 test/acceptance/multiple.done.js                   |   16 -
 test/acceptance/pending.js                         |    4 -
 test/acceptance/require/require.js                 |    1 -
 test/acceptance/root.js                            |    3 +-
 test/acceptance/test.coffee                        |    2 +-
 test/acceptance/throw.js                           |  111 ++
 test/acceptance/timeout.js                         |    1 -
 test/acceptance/uncaught.js                        |   17 -
 test/acceptance/utils.js                           |  278 ++++-
 test/browser/grep.html                             |   51 +
 test/browser/grep.js                               |  108 ++
 test/browser/large.js                              |    3 +-
 test/browser/stack-trace.html                      |   24 +
 test/browser/stack-trace.js                        |   20 +
 test/color.js                                      |   17 +
 test/grep.js                                       |   18 +-
 test/hook.async.js                                 |    1 -
 test/hook.err.js                                   |    6 +-
 test/hook.sync.js                                  |    3 +-
 test/hook.sync.nested.js                           |    1 -
 test/hook.timeout.js                               |    3 +-
 test/http.meta.2.js                                |   19 +-
 test/http.meta.js                                  |   23 +-
 test/integration/diffs.js                          |   45 +
 test/integration/fixtures/cascade.js               |   57 +
 .../fixtures/diffs/diffs.css.in}                   |    2 +-
 .../fixtures/diffs/diffs.css.out}                  |    2 +-
 test/integration/fixtures/diffs/diffs.js           |   84 ++
 test/integration/fixtures/diffs/output             |   91 ++
 test/integration/fixtures/multiple.done.js         |   18 +
 .../fixtures/options/async-only.async.js           |    3 +
 .../fixtures/options/async-only.sync.js            |    1 +
 test/integration/fixtures/options/bail.js          |   25 +
 test/integration/fixtures/options/delay.js         |   19 +
 test/integration/fixtures/options/grep.js          |   12 +
 .../fixtures/options/sort.alpha.js}                |    2 +-
 .../fixtures/options/sort.beta.js}                 |    2 +-
 .../fixtures/pending/skip.sync.before.js           |   13 +
 .../fixtures/pending/skip.sync.beforeEach.js       |   13 +
 .../integration/fixtures/pending/skip.sync.spec.js |   10 +
 test/integration/fixtures/pending/spec.js          |    3 +
 test/integration/fixtures/regression/issue-1327.js |   15 +
 test/integration/fixtures/timeout.js               |   17 +
 test/integration/fixtures/uncaught.hook.js         |   15 +
 test/integration/fixtures/uncaught.js              |   26 +
 test/integration/helpers.js                        |  142 +++
 test/integration/hooks.js                          |   44 +
 test/integration/multiple.done.js                  |   28 +
 test/integration/options.js                        |  140 +++
 test/integration/pending.js                        |   63 ++
 test/integration/regression.js                     |   24 +
 test/integration/timeout.js                        |   18 +
 test/integration/uncaught.js                       |   37 +
 test/jsapi/index.js                                |    2 -
 test/mocha.js                                      |   33 +
 test/ms.js                                         |   82 ++
 test/regression/issue1327/case.js                  |   14 -
 test/regression/issue1327/control.js               |   10 -
 test/reporters/base.js                             |  178 +++-
 test/reporters/json.js                             |    7 +-
 test/reporters/nyan.js                             |    2 +-
 test/runnable.js                                   |    6 +-
 test/runner.js                                     |  101 +-
 test/suite.js                                      |    1 -
 test/utils.js                                      |  150 ++-
 148 files changed, 4139 insertions(+), 1363 deletions(-)

diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..e3a4859
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,18 @@
+# This file is for unifying the coding style for different editors and IDEs
+# editorconfig.org
+
+root = true
+
+[*]
+end_of_line = lf
+charset = utf-8
+insert_final_newline = true
+trim_trailing_whitespace = true
+indent_style = space
+indent_size = 2
+
+[Makefile]
+indent_style = tab
+
+[*.md]
+trim_trailing_whitespace = false
diff --git a/.mailmap b/.mailmap
new file mode 100644
index 0000000..7040131
--- /dev/null
+++ b/.mailmap
@@ -0,0 +1 @@
+TJ Holowaychuk <tj at vision-media.ca>
\ No newline at end of file
diff --git a/.travis.yml b/.travis.yml
index ed05f88..2789c51 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,6 +1,14 @@
 language: node_js
+script: travis_retry npm test
 node_js:
-  - "0.11"
-  - "0.10"
-  - "0.8"
-  - "0.6"
+- 'iojs'
+- '0.12'
+- '0.11'
+- '0.10'
+- '0.8'
+sudo: false
+notifications:
+  urls:
+    - secure: "EmycFlk48bUvOQ07mxnbcn6T+n3G4wVWuXgrDsUUp6EhJ9866wQK9i+qVhqogWuSEQrGoVaS/vpc/NEYxDP30BD/+nqgvOhVhLVEgtziI85imyV/oUVpArdW3AUmuzCrx7rQCivygjpBmWV9ZcUT5Km4qp1iQhuumy2WbplxrZ4="
+  on_success: change
+  on_failure: always
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..6d172f0
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,49 @@
+# Contributing to Mocha
+
+Hi!  We could use your help.  Let us help you help us.  Or something.
+
+## General
+
+1. If you are looking for a place to begin, **please send PRs for bugfixes instead of new features**, and/or **look for issues labeled `PR PLEASE`.**
+
+2.  **Help with documentation and the wiki is always appreciated**.
+
+3.  Please **be courteous and constructive** when commenting on issues, commits, and pull requests.
+
+## Bug Reports & Issues
+
+1.  When reporting a bug, please **provide steps to reproduce**.  If possible, show code.
+  
+2.  Please **show all code in JavaScript**.  We don't all read `<insert-language-that-compiles-to-JavaScript-here>`.  If you do not, you will be asked to.
+
+3.  Because Mocha works with many third-party libraries and tools, **ensure the bug you are reporting is actually within Mocha**.
+
+4.  If you report a bug, and it is inactive for a significant amount of time, it may be closed.  **Please respond promptly to requests for more information**.
+
+## Pull Requests
+
+1. Before sending a large PR, it's recommended to **create an issue to propose the change**.  Nobody wants to write a book of code and throw it away.
+
+2.  Because Mocha should be kept as maintainable as possible, its codebase must be kept slim.  Historically, *most PRs for new features are not merged*.  New features inevitably increase the size of the codebase, and thus reduce maintainability.  Only features *deemed essential* are likely to be merged--this is at the discretion of the maintainer(s).  If your PR for a feature is not merged, this doesn't necessarily mean your PR was a bad idea, wouldn't be used, or otherwise sucks.  It ju [...]
+
+3.  Due to the above, before creating a PR for a new feature, **create an issue to propose the feature.**
+
+4.  Please **respect existing coding conventions**, whatever those may be.
+
+5.  If your PR has been waiting in limbo for some time, it's very helpful to **rebase against master**, which will make it easier to merge.
+
+6.  Please **add tests for new code**.
+
+7.  **Always run `npm test` before sending a PR.**  If you break the tests, your PR will not be accepted until they are fixed.
+
+## Source Control
+
+1. Please **squash your commits** when sending a pull request.  If you are unfamiliar with this process, see [this guide](https://help.github.com/articles/about-git-rebase/).  If you have already pushed your changesets and are squashing thereafter, this may necessitate the use of a "force push".  Please [read the docs](http://git-scm.com/docs/git-push) before you attempt this. 
+ 
+2. Please **follow the commit message conventions [outlined here](https://medium.com/code-adventures/git-conventions-a940ee20862d).**
+
+## TL;DR
+
+**Be kind, be diligent, look before you leap into a PR, and follow common community conventions**.
+
+*- The Mocha Team*
diff --git a/History.md b/HISTORY.md
similarity index 79%
rename from History.md
rename to HISTORY.md
index 9a63be6..9f7db23 100644
--- a/History.md
+++ b/HISTORY.md
@@ -1,4 +1,146 @@
 
+2.2.5 / 2015-05-14
+==================
+
+  * Merge pull request #1699 from nylen/upgrade/jsdiff
+  * Upgrade jsdiff to v1.4.0
+  * Merge pull request #1648 from nylen/fix/diff-colors
+  * Merge pull request #1686 from danielstjules/1327-regression
+  * Sanity check: update fixtures/regression/issue-1327.js to be closer to orig test
+  * Fix diff colors
+  * Merge pull request #1675 from danielstjules/integration-tests
+  * Merge pull request #1682 from kemitchell/spdx-license
+  * use a valid SPDX license identifier
+  * Add integration tests
+  * Merge pull request #1655 from a8m/fix-issue-1241
+  * Merge pull request #1661 from a8m/fix-issue-1660
+  * fix(utils/stringify): fix issue #1660
+  * fix(reporter/base): issue #1241
+
+2.2.4 / 2015-04-08
+==================
+
+  * Load mocha.opts in _mocha for now (close #1645)
+
+2.2.3 / 2015-04-07
+==================
+
+  * fix(reporter/base): string diff - issue #1241
+  * fix(reporter/base): string diff - issue #1241 
+  * fix(reporter/base): don't show diffs for errors without expectation 
+  * fix(reporter/base): don't assume error message is first line of stack 
+  * improve: dry up reporter/base test 
+  * fix(reporter/base): explicitly ignore showDiff #1614 
+  * Add iojs to travis build 
+  * Pass `--allow-natives-syntax` flag to node. 
+  * Support --harmony_classes flag for io.js 
+  * Fix 1556: Update utils.clean to handle newlines in func declarations 
+  * Fix 1606: fix err handling in IE <= 8 and non-ES5 browsers 
+  * Fix 1585: make _mocha executable again 
+  * chore(package.json): add a8m as a contributor 
+  * Fixed broken link on html-cov reporter 
+  * support --es_staging flag 
+  * fix issue where menu overlaps content. 
+  * update contributors in package.json 
+  * Remove trailing whitespace from reporter output 
+  * Remove contributors list from readme 
+  * log third-party reporter errors 
+  * [Fix] Exclude not own properties when looping on options 
+  * fix: support node args in mocha.opts (close #1573) 
+  * fix(reporter/base): string diff - issue #1241
+
+2.2.1 / 2015-03-09
+==================
+
+  * Fix passing of args intended for node/iojs.
+
+2.2.0 / 2015-03-06
+==================
+
+  * Update mocha.js
+  * Add --fgrep. Use grep for RegExp, fgrep for str
+  * Ignore async global errors after spec resolution
+  * Fixing errors that prevent mocha.js from loading in the browser - fixes #1558
+  * fix(utils): issue #1558 + make
+  * add ability to delay root suite; closes #362, closes #1124
+  * fix insanity in http tests
+  * update travis: add node 0.12, add gitter, remove slack
+  * building
+  * resolve #1548: ensure the environment's "node" executable is used
+  * reporters/base: use supports-color to detect colorable term
+  * travis: use docker containers
+  * small fix: commander option for --expose-gc
+  * Ignore asynchronous errors after global failure
+  * Improve error output when a test fails with a non-error
+  * updated travis badge, uses svg instead of img
+  * Allow skip from test context for #332
+  * [JSHINT] Unnecessary semicolon fixed in bin/_mocha
+  * Added a reminder about the done() callback to test timeout error messages
+  * fixes #1496, in Mocha.run(fn), check if fn exists before executing it, added tests too
+  * Add Harmony Proxy flag for iojs
+  * test(utils|ms|*): test existing units
+  * add support for some iojs flags
+  * fix(utils.stringify): issue #1229, diff viewer
+  * Remove slack link
+  * Prevent multiple 'grep=' querystring params in html reporter
+  * Use grep as regexp (close #1381)
+  * utils.stringify should handle objects without an Object prototype
+  * in runnable test, comparing to undefined error's message rather than a literal
+  * Fix test running output truncation on async STDIO
+  * ammended for deprecated customFds option in child_process
+
+2.1.0 / 2014-12-23
+==================
+
+ * showDiff: don’t stringify strings
+ * Clean up unused module dependencies.
+ * Filter zero-length strings from mocha.opts
+ * only write to stdout in reporters
+ * Revert "only write to stdout in reporters"
+ * Print colored output only to a tty
+ * update summary in README.md
+ * rename Readme.md/History.md to README.md/HISTORY.md because neurotic
+ * add .mailmap to fix "git shortlog" or "git summary" output
+ * fixes #1461: nyan-reporter now respects Base.useColors, fixed bug where Base.color would not return a string when str wasn't a string.
+ * Use existing test URL builder in failed replay links
+ * modify .travis.yml: use travis_retry; closes #1449
+ * fix -t 0 behavior; closes #1446
+ * fix tests (whoops)
+ * improve diff behavior
+ * Preserve pathname when linking to individual tests
+ * Fix test
+ * Tiny typo in comments fixed
+ * after hooks now being called on failed tests when using bail, fixes #1093
+ * fix throwing undefined/null now makes tests fail, fixes #1395
+ * compiler extensions are added as watched extensions, removed non-standard extensions from watch regex, resolves #1221
+ * prefix/namespace for suite titles in markdown reporter, fixes #554
+ * fix more bad markdown in CONTRIBUTING.md
+ * fix bad markdown in CONTRIBUTING.md
+ * add setImmediate/clearImmediate to globals; closes #1435
+ * Fix buffer diffs (closes #1132, closes #1433)
+ * add a CONTRIBUTING.md.  closes #882
+ * fix intermittent build failures (maybe). closes #1407
+ * add Slack notification to .travis.yml
+ * Fix slack link
+ * Add slack room to readme
+ * Update maintainers
+ * update maintainers and contributors
+ * resolves #1393: kill children with more effort on SIGINT
+ * xunit reporter support for optionally writing to a file
+ * if a reporter has a .done method, call it before exiting
+ * add support for reporter options
+ * only write to stdout in reporters
+
+2.0.0 / 2014-10-21
+==================
+
+ * remove: support for node 0.6.x, 0.4.x
+ * fix: landing reporter with non ansi characters (#211)
+ * fix: html reporter - preserve query params when navigating to suites/tests (#1358)
+ * fix: json stream reporter add error message to failed test
+ * fix: fixes for visionmedia -> mochajs
+ * fix: use stdio, fixes node deprecation warnings (#1391)
+
 1.21.5 / 2014-10-11
 ==================
 
@@ -17,7 +159,7 @@
  * fix: ability to disable syntax highlighting (#1329)
  * fix: added empty object to errorJSON() call to catch when no error is present
  * fix: never time out after calling enableTimeouts(false)
- * fix: timeout(0) will work at suite level (#1300) 
+ * fix: timeout(0) will work at suite level (#1300)
  * Fix for --watch+only() issue (#888 )
  * fix: respect err.showDiff, add Base reporter test (#810)
 
diff --git a/LICENSE b/LICENSE
index 1c5d7fa..ca47f26 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
 (The MIT License)
 
-Copyright (c) 2011-2014 TJ Holowaychuk <tj at vision-media.ca>
+Copyright (c) 2011-2015 TJ Holowaychuk <tj at vision-media.ca>
 
 Permission is hereby granted, free of charge, to any person obtaining
 a copy of this software and associated documentation files (the
diff --git a/Makefile b/Makefile
index 72c4814..cde1648 100644
--- a/Makefile
+++ b/Makefile
@@ -36,7 +36,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-failing test-regression
+test-all: test-bdd test-tdd test-qunit test-exports test-unit test-integration test-jsapi test-compilers test-glob test-requires test-reporters test-only
 
 test-jsapi:
 	@node test/jsapi
@@ -48,25 +48,10 @@ test-unit:
 		--growl \
 		test/*.js
 
-test-regression: test-outputs/issue1327/case-out.json
+test-integration:
 	@./bin/mocha \
 		--reporter $(REPORTER) \
-		test/regression/issue*/control.js
-
-test-outputs/issue1327/case-out.json: test/regression/issue1327/case.js
-	@mkdir -p $(dir $@) || true
-	@./bin/mocha --reporter json $< > $@ || true
-
-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/integration/*.js
 
 test-compilers:
 	@./bin/mocha \
@@ -109,31 +94,6 @@ test-exports:
 		--ui exports \
 		test/acceptance/interfaces/exports
 
-test-grep:
-	@./bin/mocha \
-	  --reporter $(REPORTER) \
-	  --grep fast \
-	  test/acceptance/misc/grep
-
-test-invert:
-	@./bin/mocha \
-	  --reporter $(REPORTER) \
-	  --grep slow \
-	  --invert \
-	  test/acceptance/misc/grep
-
-test-bail:
-	@./bin/mocha \
-		--reporter $(REPORTER) \
-		--bail \
-		test/acceptance/misc/bail
-
-test-async-only:
-	@./bin/mocha \
-	  --reporter $(REPORTER) \
-	  --async-only \
-	  test/acceptance/misc/asyncOnly
-
 test-glob:
 	@./test/acceptance/glob/glob.sh
 
@@ -158,11 +118,10 @@ test-only:
 		--ui qunit \
 		test/acceptance/misc/only/qunit
 
-test-sort:
+test-mocha:
 	@./bin/mocha \
 		--reporter $(REPORTER) \
-		--sort \
-		test/acceptance/sort
+		test/mocha
 
 non-tty:
 	@./bin/mocha \
@@ -189,4 +148,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 test-failing tm clean
+.PHONY: test-cov test-jsapi test-compilers watch test test-all test-bdd test-tdd test-qunit test-exports test-unit test-integration non-tty tm clean
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..44692d3
--- /dev/null
+++ b/README.md
@@ -0,0 +1,11 @@
+[![Build Status](https://api.travis-ci.org/mochajs/mocha.svg?branch=master)](http://travis-ci.org/mochajs/mocha) [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/mochajs/mocha?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
+
+  [![Mocha test framework](http://f.cl.ly/items/3l1k0n2A1U3M1I1L210p/Screen%20Shot%202012-02-24%20at%202.21.43%20PM.png)](http://mochajs.org)
+
+  Mocha is a simple, flexible, fun JavaScript test framework for node.js and the browser. For more information view the [documentation](http://mochajs.org).
+
+## Links
+
+  - [Google Group](http://groups.google.com/group/mochajs)
+  - [Wiki](https://github.com/mochajs/mocha/wiki)
+  - Mocha [Extensions and reporters](https://github.com/mochajs/mocha/wiki)
diff --git a/Readme.md b/Readme.md
deleted file mode 100644
index b84bb05..0000000
--- a/Readme.md
+++ /dev/null
@@ -1,203 +0,0 @@
- [![Build Status](https://secure.travis-ci.org/visionmedia/mocha.png)](http://travis-ci.org/visionmedia/mocha)
-
-  [![Mocha test framework](http://f.cl.ly/items/3l1k0n2A1U3M1I1L210p/Screen%20Shot%202012-02-24%20at%202.21.43%20PM.png)](http://visionmedia.github.io/mocha)
-
-  Mocha is a simple, flexible, fun JavaScript test framework for node.js and the browser. For more information view the [documentation](http://visionmedia.github.io/mocha).
-
-## Contributors
-
-```
-
- project  : mocha
- repo age : 2 years, 11 months
- active   : 433 days
- commits  : 1424
- files    : 143
- authors  :
-   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
-
-  - [Google Group](http://groups.google.com/group/mochajs)
-  - [Wiki](https://github.com/visionmedia/mocha/wiki)
-  - Mocha [Extensions and reporters](https://github.com/visionmedia/mocha/wiki)
diff --git a/bin/_mocha b/bin/_mocha
index 3032690..5b3c0c5 100755
--- a/bin/_mocha
+++ b/bin/_mocha
@@ -4,20 +4,17 @@
  * Module dependencies.
  */
 
-var program = require('commander')
-  , sprintf = require('util').format
-  , path = require('path')
-  , fs = require('fs')
-  , glob = require('glob')
-  , resolve = path.resolve
-  , exists = fs.existsSync || path.existsSync
-  , Mocha = require('../')
-  , utils = Mocha.utils
-  , interfaces = Mocha.interfaces
-  , join = path.join
-  , basename = path.basename
-  , cwd = process.cwd()
-  , mocha = new Mocha;
+var program = require('commander'),
+  path = require('path'),
+  fs = require('fs'),
+  resolve = path.resolve,
+  exists = fs.existsSync || path.existsSync,
+  Mocha = require('../'),
+  utils = Mocha.utils,
+  join = path.join,
+  cwd = process.cwd(),
+  getOptions = require('./options'),
+  mocha = new Mocha;
 
 /**
  * Save timer references to avoid Sinon interfering (see GH-237).
@@ -65,26 +62,34 @@ program
   .option('-c, --colors', 'force enabling of colors')
   .option('-C, --no-colors', 'force disabling of colors')
   .option('-G, --growl', 'enable growl notification support')
+  .option('-O, --reporter-options <k=v,k2=v2,...>', 'reporter-specific options')
   .option('-R, --reporter <name>', 'specify the reporter to use', 'spec')
   .option('-S, --sort', "sort test files")
   .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('-f, --fgrep <string>', 'only run tests containing <string>')
+  .option('-gc, --expose-gc', 'expose gc extension')
+  .option('-i, --invert', 'inverts --grep and --fgrep 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('--full-trace', 'display the full stack trace')
   .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('--es_staging', 'enable all staged features')
   .option('--harmony-collections', 'enable harmony collections (sets, maps, and weak maps)')
   .option('--harmony-generators', 'enable harmony generators')
   .option('--harmony-proxies', 'enable harmony proxies')
+  .option('--harmony_shipping', 'enable all shipped harmony fetaures (iojs)')
+  .option('--harmony_arrow_functions', 'enable "harmony arrow functions" (iojs)')
+  .option('--harmony_proxies', 'enable "harmony proxies" (iojs)')
+  .option('--harmony_classes', 'enable "harmony classes" (iojs)')
   .option('--inline-diffs', 'display actual/expected differences inline within each string')
   .option('--interfaces', 'display available interfaces')
   .option('--no-deprecation', 'silence deprecation warnings')
@@ -98,6 +103,7 @@ program
   .option('--trace', 'trace function calls')
   .option('--trace-deprecation', 'show stack traces on deprecations')
   .option('--watch-extensions <ext>,...', 'additional extensions to monitor with --watch', list, [])
+  .option('--delay', 'wait for async suite definition')
 
 program.name = 'mocha';
 
@@ -170,23 +176,9 @@ program.on('require', function(mod){
   requires.push(mod);
 });
 
-// --opts
+// load mocha.opts into process.argv
 
-var optsPath = process.argv.indexOf('--opts') !== -1
-    ? process.argv[process.argv.indexOf('--opts') + 1]
-    : 'test/mocha.opts';
-
-try {
-  var opts = fs.readFileSync(optsPath, 'utf8')
-    .trim()
-    .split(/\s+/);
-
-  process.argv = process.argv
-    .slice(0, 2)
-    .concat(opts.concat(process.argv.slice(2)));
-} catch (err) {
-  // ignore
-}
+getOptions();
 
 // parse args
 
@@ -196,9 +188,22 @@ program.parse(process.argv);
 
 Error.stackTraceLimit = Infinity; // TODO: config
 
+// reporter options
+
+var reporterOptions = {};
+if (program.reporterOptions !== undefined) {
+    program.reporterOptions.split(",").forEach(function(opt) {
+        var L = opt.split("=");
+        if (L.length != 2) {
+            throw new Error("invalid reporter option '" + opt + "'");
+        }
+        reporterOptions[L[0]] = L[1];
+    });
+}
+
 // reporter
 
-mocha.reporter(program.reporter);
+mocha.reporter(program.reporter, reporterOptions);
 
 // interface
 
@@ -251,6 +256,10 @@ mocha.suite.bail(program.bail);
 
 if (program.grep) mocha.grep(new RegExp(program.grep));
 
+// --fgrep
+
+if (program.fgrep) mocha.grep(program.fgrep);
+
 // --invert
 
 if (program.invert) mocha.invert();
@@ -259,6 +268,10 @@ if (program.invert) mocha.invert();
 
 if (program.checkLeaks) mocha.checkLeaks();
 
+// --stack-trace
+
+if(program.fullTrace) mocha.fullTrace();
+
 // --growl
 
 if (program.growl) mocha.growl();
@@ -267,6 +280,10 @@ if (program.growl) mocha.growl();
 
 if (program.asyncOnly) mocha.asyncOnly();
 
+// --delay
+
+if (program.delay) mocha.delay();
+
 // --globals
 
 mocha.globals(globals);
@@ -282,6 +299,7 @@ program.compilers.forEach(function(c) {
   if (mod[0] == '.') mod = join(process.cwd(), mod);
   require(mod);
   extensions.push(ext);
+  program.watchExtensions.push(ext);
 });
 
 // requires
@@ -325,7 +343,7 @@ if (program.watch) {
   });
 
 
-  var watchFiles = utils.files(cwd, [ 'js', 'coffee', 'litcoffee', 'coffee.md' ].concat(program.watchExtensions));
+  var watchFiles = utils.files(cwd, [ 'js' ].concat(program.watchExtensions));
   var runAgain = false;
 
   function loadAndRun() {
@@ -377,12 +395,32 @@ if (program.watch) {
 // load
 
 mocha.files = files;
-runner = mocha.run(program.exit ? process.exit : exitLater);
+runner = mocha.run(program.exit ? exit : exitLater);
 
 function exitLater(code) {
   process.on('exit', function() { process.exit(code) })
 }
 
+function exit(code) {
+  // flush output for Node.js Windows pipe bug
+  // https://github.com/joyent/node/issues/6247 is just one bug example
+  // https://github.com/visionmedia/mocha/issues/333 has a good discussion
+  function done() {
+    if (!(draining--)) process.exit(code);
+  }
+
+  var draining = 0;
+  var streams = [process.stdout, process.stderr];
+
+  streams.forEach(function(stream){
+    // submit empty write request and wait for completion
+    draining += 1;
+    stream.write('', done);
+  });
+
+  done();
+}
+
 process.on('SIGINT', function() { runner.abort(); })
 
 // enable growl notifications
@@ -419,7 +457,7 @@ function list(str) {
 
 function hideCursor(){
   process.stdout.write('\u001b[?25l');
-};
+}
 
 /**
  * Show the cursor.
@@ -427,7 +465,7 @@ function hideCursor(){
 
 function showCursor(){
   process.stdout.write('\u001b[?25h');
-};
+}
 
 /**
  * Stop play()ing.
diff --git a/bin/mocha b/bin/mocha
index b5dbc72..daa8fd9 100755
--- a/bin/mocha
+++ b/bin/mocha
@@ -5,8 +5,15 @@
  * when found, before invoking the "real" _mocha(1) executable.
  */
 
-var spawn = require('child_process').spawn
-  , args = [ __dirname + '/_mocha' ];
+var spawn = require('child_process').spawn,
+  path = require('path'),
+  fs = require('fs'),
+  args = [path.join(__dirname, '_mocha')],
+  getOptions = require('./options');
+
+// load mocha.opts into process.argv
+
+getOptions();
 
 process.argv.slice(2).forEach(function(arg){
   var flag = arg.split('=')[0];
@@ -28,13 +35,19 @@ process.argv.slice(2).forEach(function(arg){
       break;
     case '--gc-global':
     case '--harmony':
+    case '--es_staging':
     case '--harmony-proxies':
     case '--harmony-collections':
     case '--harmony-generators':
+    case '--harmony_shipping':
+    case '--harmony_arrow_functions':
+    case '--harmony_proxies':
+    case '--harmony_classes':
     case '--no-deprecation':
     case '--prof':
     case '--throw-deprecation':
     case '--trace-deprecation':
+    case '--allow-natives-syntax':
       args.unshift(arg);
       break;
     default:
@@ -44,7 +57,7 @@ process.argv.slice(2).forEach(function(arg){
   }
 });
 
-var proc = spawn(process.argv[0], args, { customFds: [0,1,2] });
+var proc = spawn(process.execPath, args, { stdio: 'inherit' });
 proc.on('exit', function (code, signal) {
   process.on('exit', function(){
     if (signal) {
@@ -54,3 +67,10 @@ proc.on('exit', function (code, signal) {
     }
   });
 });
+
+// terminate children.
+process.on('SIGINT', function () {
+  proc.kill('SIGINT'); // calls runner.abort()
+  proc.kill('SIGTERM'); // if that didn't work, we're probably in an infinite loop, so make it die.
+  process.kill(process.pid, 'SIGINT');
+});
diff --git a/bin/options.js b/bin/options.js
new file mode 100644
index 0000000..3b31798
--- /dev/null
+++ b/bin/options.js
@@ -0,0 +1,36 @@
+/**
+ * Dependencies.
+ */
+
+var fs = require('fs');
+
+/**
+ * Export `getOptions`.
+ */
+
+module.exports = getOptions;
+
+/**
+ * Get options.
+ */
+
+function getOptions() {
+  var optsPath = process.argv.indexOf('--opts') !== -1
+        ? process.argv[process.argv.indexOf('--opts') + 1]
+        : 'test/mocha.opts';
+
+  try {
+    var opts = fs.readFileSync(optsPath, 'utf8')
+          .trim()
+          .split(/\s+/)
+          .filter(function(value) {
+            return value ? true : false;
+          });
+
+    process.argv = process.argv
+      .slice(0, 2)
+      .concat(opts.concat(process.argv.slice(2)));
+  } catch (err) {
+    // ignore
+  }
+}
diff --git a/bower.json b/bower.json
index 839191b..f03b076 100644
--- a/bower.json
+++ b/bower.json
@@ -1,6 +1,22 @@
 {
   "name": "mocha",
-  "version": "1.21.5",
+  "version": "2.2.5",
+  "homepage": "http://mocha.github.io/mocha",
+  "description": "simple, flexible, fun test framework",
+  "authors": [
+    "TJ Holowaychuk <tj at vision-media.ca>",
+    "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/mochajs/mocha.git"
+  },
   "main": [
     "mocha.js",
     "mocha.css"
@@ -19,5 +35,13 @@
     "index.js",
     "Makefile",
     "package.json"
-  ]
-}
+  ],
+  "keywords": [
+    "mocha",
+    "test",
+    "bdd",
+    "tdd",
+    "tap"
+  ],
+  "license": "MIT"
+}
\ No newline at end of file
diff --git a/component.json b/component.json
index eb7cdb8..5da3daa 100644
--- a/component.json
+++ b/component.json
@@ -1,7 +1,7 @@
 {
   "name": "mocha",
-  "version": "1.21.5",
-  "repo": "visionmedia/mocha",
+  "version": "2.2.5",
+  "repo": "mochajs/mocha",
   "description": "simple, flexible, fun test framework",
   "keywords": [
     "mocha",
@@ -11,6 +11,10 @@
     "tap"
   ],
   "main": "mocha.js",
-  "scripts": ["mocha.js"],
-  "styles": ["mocha.css"]
-}
+  "scripts": [
+    "mocha.js"
+  ],
+  "styles": [
+    "mocha.css"
+  ]
+}
\ No newline at end of file
diff --git a/index.js b/index.js
index 507566f..169b271 100644
--- a/index.js
+++ b/index.js
@@ -1,4 +1,3 @@
-
 module.exports = process.env.COV
   ? require('./lib-cov/mocha')
-  : require('./lib/mocha');
\ No newline at end of file
+  : require('./lib/mocha');
diff --git a/lib/browser/debug.js b/lib/browser/debug.js
index 03cf592..0d939e5 100644
--- a/lib/browser/debug.js
+++ b/lib/browser/debug.js
@@ -1,4 +1,3 @@
-
 module.exports = function(type){
   return function(){
   }
diff --git a/lib/browser/escape-string-regexp.js b/lib/browser/escape-string-regexp.js
index ac6572c..21a9566 100644
--- a/lib/browser/escape-string-regexp.js
+++ b/lib/browser/escape-string-regexp.js
@@ -3,9 +3,9 @@
 var matchOperatorsRe = /[|\\{}()[\]^$+*?.]/g;
 
 module.exports = function (str) {
-	if (typeof str !== 'string') {
-		throw new TypeError('Expected a string');
-	}
+  if (typeof str !== 'string') {
+    throw new TypeError('Expected a string');
+  }
 
-	return str.replace(matchOperatorsRe,  '\\$&');
+  return str.replace(matchOperatorsRe,  '\\$&');
 };
diff --git a/lib/browser/events.js b/lib/browser/events.js
index cfbd072..f708260 100644
--- a/lib/browser/events.js
+++ b/lib/browser/events.js
@@ -1,4 +1,3 @@
-
 /**
  * Module exports.
  */
@@ -175,4 +174,4 @@ EventEmitter.prototype.emit = function (name) {
   }
 
   return true;
-};
\ No newline at end of file
+};
diff --git a/lib/browser/progress.js b/lib/browser/progress.js
index 90526f7..b30e517 100644
--- a/lib/browser/progress.js
+++ b/lib/browser/progress.js
@@ -94,28 +94,28 @@ Progress.prototype.draw = function(ctx){
       , y = half
       , rad = half - 1
       , fontSize = this._fontSize;
-  
+
     ctx.font = fontSize + 'px ' + this._font;
-  
+
     var angle = Math.PI * 2 * (percent / 100);
     ctx.clearRect(0, 0, size, size);
-  
+
     // outer circle
     ctx.strokeStyle = '#9f9f9f';
     ctx.beginPath();
     ctx.arc(x, y, rad, 0, angle, false);
     ctx.stroke();
-  
+
     // inner circle
     ctx.strokeStyle = '#eee';
     ctx.beginPath();
     ctx.arc(x, y, rad - 1, 0, angle, true);
     ctx.stroke();
-  
+
     // text
     var text = this._text || (percent | 0) + '%'
       , w = ctx.measureText(text).width;
-  
+
     ctx.fillText(
         text
       , x - w / 2 + 1
diff --git a/lib/browser/tty.js b/lib/browser/tty.js
index 6f5f079..eab6388 100644
--- a/lib/browser/tty.js
+++ b/lib/browser/tty.js
@@ -1,4 +1,3 @@
-
 exports.isatty = function(){
   return true;
 };
diff --git a/lib/context.js b/lib/context.js
index 84440be..3885218 100644
--- a/lib/context.js
+++ b/lib/context.js
@@ -1,4 +1,3 @@
-
 /**
  * Expose `Context`.
  */
@@ -69,6 +68,18 @@ Context.prototype.slow = function(ms){
 };
 
 /**
+ * Mark a test as skipped.
+ *
+ * @return {Context} self
+ * @api private
+ */
+
+Context.prototype.skip = function(){
+    this.runnable().skip();
+    return this;
+};
+
+/**
  * Inspect the context void of `._runnable`.
  *
  * @return {String}
diff --git a/lib/hook.js b/lib/hook.js
index 814e7b6..c2dc346 100644
--- a/lib/hook.js
+++ b/lib/hook.js
@@ -1,4 +1,3 @@
-
 /**
  * Module dependencies.
  */
diff --git a/lib/interfaces/bdd.js b/lib/interfaces/bdd.js
index ac16d72..f9f08a3 100644
--- a/lib/interfaces/bdd.js
+++ b/lib/interfaces/bdd.js
@@ -1,4 +1,3 @@
-
 /**
  * Module dependencies.
  */
@@ -30,38 +29,13 @@ module.exports = function(suite){
 
   suite.on('pre-require', function(context, file, mocha){
 
-    /**
-     * Execute before running tests.
-     */
-
-    context.before = function(name, fn){
-      suites[0].beforeAll(name, fn);
-    };
-
-    /**
-     * Execute after running tests.
-     */
-
-    context.after = function(name, fn){
-      suites[0].afterAll(name, fn);
-    };
-
-    /**
-     * Execute before each test case.
-     */
-
-    context.beforeEach = function(name, fn){
-      suites[0].beforeEach(name, fn);
-    };
-
-    /**
-     * Execute after each test case.
-     */
-
-    context.afterEach = function(name, fn){
-      suites[0].afterEach(name, fn);
-    };
+    var common = require('./common')(suites, context);
 
+    context.before = common.before;
+    context.after = common.after;
+    context.beforeEach = common.beforeEach;
+    context.afterEach = common.afterEach;
+    context.run = mocha.options.delay && common.runWithSuite(suite);
     /**
      * Describe a "suite" with the given `title`
      * and callback `fn` containing nested suites
@@ -136,5 +110,6 @@ module.exports = function(suite){
     context.it.skip = function(title){
       context.it(title);
     };
+
   });
 };
diff --git a/lib/interfaces/common.js b/lib/interfaces/common.js
new file mode 100644
index 0000000..1ccd339
--- /dev/null
+++ b/lib/interfaces/common.js
@@ -0,0 +1,58 @@
+/**
+ * Functions common to more than one interface
+ * @module lib/interfaces/common
+ */
+
+'use strict';
+
+module.exports = function (suites, context) {
+
+  return {
+    /**
+     * This is only present if flag --delay is passed into Mocha.  It triggers
+     * root suite execution.  Returns a function which runs the root suite.
+     */
+    runWithSuite: function runWithSuite(suite) {
+      return function run() {
+        suite.run();
+      };
+    },
+
+    /**
+     * Execute before running tests.
+     */
+    before: function (name, fn) {
+      suites[0].beforeAll(name, fn);
+    },
+
+    /**
+     * Execute after running tests.
+     */
+    after: function (name, fn) {
+      suites[0].afterAll(name, fn);
+    },
+
+    /**
+     * Execute before each test case.
+     */
+    beforeEach: function (name, fn) {
+      suites[0].beforeEach(name, fn);
+    },
+
+    /**
+     * Execute after each test case.
+     */
+    afterEach: function (name, fn) {
+      suites[0].afterEach(name, fn);
+    },
+
+    test: {
+      /**
+       * Pending test case.
+       */
+      skip: function (title) {
+        context.test(title);
+      }
+    }
+  }
+};
diff --git a/lib/interfaces/exports.js b/lib/interfaces/exports.js
index cedb905..95e8a07 100644
--- a/lib/interfaces/exports.js
+++ b/lib/interfaces/exports.js
@@ -1,4 +1,3 @@
-
 /**
  * Module dependencies.
  */
diff --git a/lib/interfaces/index.js b/lib/interfaces/index.js
index f7b2655..4f825d1 100644
--- a/lib/interfaces/index.js
+++ b/lib/interfaces/index.js
@@ -1,4 +1,3 @@
-
 exports.bdd = require('./bdd');
 exports.tdd = require('./tdd');
 exports.qunit = require('./qunit');
diff --git a/lib/interfaces/qunit.js b/lib/interfaces/qunit.js
index 0a22641..6668967 100644
--- a/lib/interfaces/qunit.js
+++ b/lib/interfaces/qunit.js
@@ -1,4 +1,3 @@
-
 /**
  * Module dependencies.
  */
@@ -38,38 +37,13 @@ module.exports = function(suite){
 
   suite.on('pre-require', function(context, file, mocha){
 
-    /**
-     * Execute before running tests.
-     */
-
-    context.before = function(name, fn){
-      suites[0].beforeAll(name, fn);
-    };
-
-    /**
-     * Execute after running tests.
-     */
-
-    context.after = function(name, fn){
-      suites[0].afterAll(name, fn);
-    };
-
-    /**
-     * Execute before each test case.
-     */
-
-    context.beforeEach = function(name, fn){
-      suites[0].beforeEach(name, fn);
-    };
-
-    /**
-     * Execute after each test case.
-     */
-
-    context.afterEach = function(name, fn){
-      suites[0].afterEach(name, fn);
-    };
+    var common = require('./common')(suites, context);
 
+    context.before = common.before;
+    context.after = common.after;
+    context.beforeEach = common.beforeEach;
+    context.afterEach = common.afterEach;
+    context.run = mocha.options.delay && common.runWithSuite(suite);
     /**
      * Describe a "suite" with the given `title`.
      */
@@ -114,12 +88,7 @@ module.exports = function(suite){
       mocha.grep(new RegExp(reString));
     };
 
-    /**
-     * Pending test case.
-     */
+    context.test.skip = common.test.skip;
 
-    context.test.skip = function(title){
-      context.test(title);
-    };
   });
 };
diff --git a/lib/interfaces/tdd.js b/lib/interfaces/tdd.js
index dc43e41..13bc2a3 100644
--- a/lib/interfaces/tdd.js
+++ b/lib/interfaces/tdd.js
@@ -1,4 +1,3 @@
-
 /**
  * Module dependencies.
  */
@@ -38,38 +37,13 @@ module.exports = function(suite){
 
   suite.on('pre-require', function(context, file, mocha){
 
-    /**
-     * Execute before each test case.
-     */
-
-    context.setup = function(name, fn){
-      suites[0].beforeEach(name, fn);
-    };
-
-    /**
-     * Execute after each test case.
-     */
-
-    context.teardown = function(name, fn){
-      suites[0].afterEach(name, fn);
-    };
-
-    /**
-     * Execute before the suite.
-     */
-
-    context.suiteSetup = function(name, fn){
-      suites[0].beforeAll(name, fn);
-    };
-
-    /**
-     * Execute after the suite.
-     */
-
-    context.suiteTeardown = function(name, fn){
-      suites[0].afterAll(name, fn);
-    };
+    var common = require('./common')(suites, context);
 
+    context.setup = common.beforeEach;
+    context.teardown = common.afterEach;
+    context.suiteSetup = common.before;
+    context.suiteTeardown = common.after;
+    context.run = mocha.options.delay && common.runWithSuite(suite);
     /**
      * Describe a "suite" with the given `title`
      * and callback `fn` containing nested suites
@@ -130,12 +104,6 @@ module.exports = function(suite){
       mocha.grep(new RegExp(reString));
     };
 
-    /**
-     * Pending test case.
-     */
-
-    context.test.skip = function(title){
-      context.test(title);
-    };
+    context.test.skip = common.test.skip;
   });
 };
diff --git a/lib/mocha.js b/lib/mocha.js
index 73bdaf9..8a11124 100644
--- a/lib/mocha.js
+++ b/lib/mocha.js
@@ -66,6 +66,7 @@ function image(name) {
  *   - `bail` bail on the first test failure
  *   - `slow` milliseconds to wait before considering a test slow
  *   - `ignoreLeaks` ignore global leaks
+ *   - `fullTrace` display the full stack-trace on failing
  *   - `grep` string or regexp to filter tests with
  *
  * @param {Object} options
@@ -76,13 +77,14 @@ function Mocha(options) {
   options = options || {};
   this.files = [];
   this.options = options;
-  this.grep(options.grep);
+  if (options.grep) this.grep(new RegExp(options.grep));
+  if (options.fgrep) this.grep(options.fgrep);
   this.suite = new exports.Suite('', new exports.Context);
   this.ui(options.ui);
   this.bail(options.bail);
-  this.reporter(options.reporter);
+  this.reporter(options.reporter, options.reporterOptions);
   if (null != options.timeout) this.timeout(options.timeout);
-  this.useColors(options.useColors)
+  this.useColors(options.useColors);
   if (options.enableTimeouts !== null) this.enableTimeouts(options.enableTimeouts);
   if (options.slow) this.slow(options.slow);
 
@@ -99,6 +101,7 @@ function Mocha(options) {
     exports.suite = context.suite || context.describe;
     exports.teardown = context.teardown || context.afterEach;
     exports.test = context.test || context.it;
+    exports.run = context.run;
   });
 }
 
@@ -131,17 +134,21 @@ Mocha.prototype.addFile = function(file){
  * Set reporter to `reporter`, defaults to "spec".
  *
  * @param {String|Function} reporter name or constructor
+ * @param {Object} reporterOptions optional options
  * @api public
  */
-
-Mocha.prototype.reporter = function(reporter){
+Mocha.prototype.reporter = function(reporter, reporterOptions){
   if ('function' == typeof reporter) {
     this._reporter = reporter;
   } else {
     reporter = reporter || 'spec';
     var _reporter;
-    try { _reporter = require('./reporters/' + reporter); } catch (err) {};
-    if (!_reporter) try { _reporter = require(reporter); } catch (err) {};
+    try { _reporter = require('./reporters/' + reporter); } catch (err) {}
+    if (!_reporter) try { _reporter = require(reporter); } catch (err) {
+      err.message.indexOf('Cannot find module') !== -1
+        ? console.warn('"' + reporter + '" reporter not found')
+        : console.warn('"' + reporter + '" reporter blew up with error:\n' + err.stack);
+    }
     if (!_reporter && reporter === 'teamcity')
       console.warn('The Teamcity reporter was moved to a package named ' +
         'mocha-teamcity-reporter ' +
@@ -149,6 +156,7 @@ Mocha.prototype.reporter = function(reporter){
     if (!_reporter) throw new Error('invalid reporter "' + reporter + '"');
     this._reporter = _reporter;
   }
+  this.options.reporterOptions = reporterOptions;
   return this;
 };
 
@@ -162,7 +170,7 @@ Mocha.prototype.reporter = function(reporter){
 Mocha.prototype.ui = function(name){
   name = name || 'bdd';
   this._ui = exports.interfaces[name];
-  if (!this._ui) try { this._ui = require(name); } catch (err) {};
+  if (!this._ui) try { this._ui = require(name); } catch (err) {}
   if (!this._ui) throw new Error('invalid interface "' + name + '"');
   this._ui = this._ui(this.suite);
   return this;
@@ -264,6 +272,18 @@ Mocha.prototype.checkLeaks = function(){
 };
 
 /**
+ * Display long stack-trace on failing
+ *
+ * @return {Mocha}
+ * @api public
+ */
+
+Mocha.prototype.fullTrace = function() {
+  this.options.fullStackTrace = true;
+  return this;
+};
+
+/**
  * Enable growl support.
  *
  * @return {Mocha}
@@ -297,9 +317,9 @@ Mocha.prototype.globals = function(globals){
  */
 
 Mocha.prototype.useColors = function(colors){
-  this.options.useColors = arguments.length && colors != undefined
-    ? colors
-    : true;
+  if (colors !== undefined) {
+    this.options.useColors = colors;
+  }
   return this;
 };
 
@@ -382,26 +402,45 @@ Mocha.prototype.noHighlighting = function() {
 };
 
 /**
+ * Delay root suite execution.
+ * @returns {Mocha}
+ * @api public
+ */
+Mocha.prototype.delay = function delay() {
+  this.options.delay = true;
+  return this;
+};
+
+/**
  * Run tests and invoke `fn()` when complete.
  *
  * @param {Function} fn
  * @return {Runner}
  * @api public
  */
-
 Mocha.prototype.run = function(fn){
   if (this.files.length) this.loadFiles();
   var suite = this.suite;
   var options = this.options;
   options.files = this.files;
-  var runner = new exports.Runner(suite);
+  var runner = new exports.Runner(suite, options.delay);
   var reporter = new this._reporter(runner, options);
   runner.ignoreLeaks = false !== options.ignoreLeaks;
+  runner.fullStackTrace = options.fullStackTrace;
   runner.asyncOnly = options.asyncOnly;
   if (options.grep) runner.grep(options.grep, options.invert);
   if (options.globals) runner.globals(options.globals);
   if (options.growl) this._growl(runner, reporter);
-  exports.reporters.Base.useColors = options.useColors;
+  if (options.useColors !== undefined) {
+    exports.reporters.Base.useColors = options.useColors;
+  }
   exports.reporters.Base.inlineDiffs = options.useInlineDiffs;
-  return runner.run(fn);
+
+  function done(failures) {
+      if (reporter.done) {
+          reporter.done(failures, fn);
+      } else fn && fn(failures);
+  }
+
+  return runner.run(done);
 };
diff --git a/lib/pending.js b/lib/pending.js
new file mode 100644
index 0000000..265ec73
--- /dev/null
+++ b/lib/pending.js
@@ -0,0 +1,16 @@
+
+/**
+ * Expose `Pending`.
+ */
+
+module.exports = Pending;
+
+/**
+ * Initialize a new `Pending` error with the given message.
+ *
+ * @param {String} message
+ */
+
+function Pending(message) {
+    this.message = message;
+}
diff --git a/lib/reporters/base.js b/lib/reporters/base.js
index 00c94de..9b719a8 100644
--- a/lib/reporters/base.js
+++ b/lib/reporters/base.js
@@ -1,4 +1,3 @@
-
 /**
  * Module dependencies.
  */
@@ -6,7 +5,8 @@
 var tty = require('tty')
   , diff = require('diff')
   , ms = require('../ms')
-  , utils = require('../utils');
+  , utils = require('../utils')
+  , supportsColor = process.env ? require('supports-color') : null;
 
 /**
  * Save timer references to avoid Sinon interfering (see GH-237).
@@ -31,10 +31,12 @@ var isatty = tty.isatty(1) && tty.isatty(2);
 exports = module.exports = Base;
 
 /**
- * Enable coloring by default.
+ * Enable coloring by default, except in the browser interface.
  */
 
-exports.useColors = isatty || (process.env.MOCHA_COLORS !== undefined);
+exports.useColors = process.env
+  ? (supportsColor || (process.env.MOCHA_COLORS !== undefined))
+  : false;
 
 /**
  * Inline diffs instead of +/-
@@ -64,8 +66,8 @@ exports.colors = {
   , 'green': 32
   , 'light': 90
   , 'diff gutter': 90
-  , 'diff added': 42
-  , 'diff removed': 41
+  , 'diff added': 32
+  , 'diff removed': 31
 };
 
 /**
@@ -98,7 +100,7 @@ if ('win32' == process.platform) {
  */
 
 var color = exports.color = function(type, str) {
-  if (!exports.useColors) return str;
+  if (!exports.useColors) return String(str);
   return '\u001b[' + exports.colors[type] + 'm' + str + '\u001b[0m';
 };
 
@@ -155,7 +157,7 @@ exports.cursor = {
  */
 
 exports.list = function(failures){
-  console.error();
+  console.log();
   failures.forEach(function(test, i){
     // format
     var fmt = color('error title', '  %s) %s:\n')
@@ -166,26 +168,33 @@ exports.list = function(failures){
     var err = test.err
       , message = err.message || ''
       , stack = err.stack || message
-      , index = stack.indexOf(message) + message.length
-      , msg = stack.slice(0, index)
+      , index = stack.indexOf(message)
       , actual = err.actual
       , expected = err.expected
       , escape = true;
+    if (index === -1) {
+      msg = message;
+    } else {
+      index += message.length;
+      msg = stack.slice(0, index);
+      // remove msg from stack
+      stack = stack.slice(index + 1);
+    }
 
     // uncaught
     if (err.uncaught) {
       msg = 'Uncaught ' + msg;
     }
-
     // explicitly show diff
-    if (err.showDiff && sameType(actual, expected)) {
+    if (err.showDiff !== false && sameType(actual, expected)
+        && expected !== undefined) {
+
       escape = false;
-      err.actual = actual = utils.stringify(actual);
-      err.expected = expected = utils.stringify(expected);
-    }
+      if (!(utils.isString(actual) && utils.isString(expected))) {
+        err.actual = actual = utils.stringify(actual);
+        err.expected = expected = utils.stringify(expected);
+      }
 
-    // actual / expected diff
-    if (err.showDiff && 'string' == typeof actual && 'string' == typeof expected) {
       fmt = color('error title', '  %s) %s:\n%s') + color('error stack', '\n%s\n');
       var match = message.match(/^([^:]+): expected/);
       msg = '\n      ' + color('error message', match ? match[1] : msg);
@@ -197,11 +206,10 @@ exports.list = function(failures){
       }
     }
 
-    // indent stack trace without msg
-    stack = stack.slice(index ? index + 1 : index)
-      .replace(/^/gm, '  ');
+    // indent stack trace
+    stack = stack.replace(/^/gm, '  ');
 
-    console.error(fmt, (i + 1), test.fullTitle(), msg, stack);
+    console.log(fmt, (i + 1), test.fullTitle(), msg, stack);
   });
 };
 
@@ -306,11 +314,10 @@ Base.prototype.epilogue = function(){
   if (stats.failures) {
     fmt = color('fail', '  %d failing');
 
-    console.error(fmt,
-      stats.failures);
+    console.log(fmt, stats.failures);
 
     Base.list(this.failures);
-    console.error();
+    console.log();
   }
 
   console.log();
@@ -388,7 +395,7 @@ function unifiedDiff(err, escape) {
   function notBlank(line) {
     return line != null;
   }
-  msg = diff.createPatch('string', err.actual, err.expected);
+  var msg = diff.createPatch('string', err.actual, err.expected);
   var lines = msg.split('\n').splice(4);
   return '\n      '
          + colorLines('diff added',   '+ expected') + ' '
diff --git a/lib/reporters/doc.js b/lib/reporters/doc.js
index eab72e5..d194eb0 100644
--- a/lib/reporters/doc.js
+++ b/lib/reporters/doc.js
@@ -1,4 +1,3 @@
-
 /**
  * Module dependencies.
  */
diff --git a/lib/reporters/dot.js b/lib/reporters/dot.js
index e200468..42a45ee 100644
--- a/lib/reporters/dot.js
+++ b/lib/reporters/dot.js
@@ -1,4 +1,3 @@
-
 /**
  * Module dependencies.
  */
@@ -28,7 +27,7 @@ function Dot(runner) {
     , n = -1;
 
   runner.on('start', function(){
-    process.stdout.write('\n  ');
+    process.stdout.write('\n');
   });
 
   runner.on('pending', function(test){
diff --git a/lib/reporters/html-cov.js b/lib/reporters/html-cov.js
index bfb27ff..74b46ad 100644
--- a/lib/reporters/html-cov.js
+++ b/lib/reporters/html-cov.js
@@ -1,4 +1,3 @@
-
 /**
  * Module dependencies.
  */
@@ -48,4 +47,4 @@ function coverageClass(n) {
   if (n >= 50) return 'medium';
   if (n >= 25) return 'low';
   return 'terrible';
-}
\ No newline at end of file
+}
diff --git a/lib/reporters/html.js b/lib/reporters/html.js
index 8b44e3e..aec2af0 100644
--- a/lib/reporters/html.js
+++ b/lib/reporters/html.js
@@ -1,4 +1,3 @@
-
 /**
  * Module dependencies.
  */
@@ -136,7 +135,7 @@ function HTML(runner) {
     } else if (test.pending) {
       var el = fragment('<li class="test pass pending"><h2>%e</h2></li>', test.title);
     } else {
-      var el = fragment('<li class="test fail"><h2>%e <a href="?grep=%e" class="replay">‣</a></h2></li>', test.title, encodeURIComponent(test.fullTitle()));
+      var el = fragment('<li class="test fail"><h2>%e <a href="%e" class="replay">‣</a></h2></li>', test.title, self.testURL(test));
       var str = test.err.stack || test.err.toString();
 
       // FF / Opera do not add the message
@@ -178,13 +177,28 @@ function HTML(runner) {
 }
 
 /**
+ * Makes a URL, preserving querystring ("search") parameters.
+ * @param {string} s
+ * @returns {string} your new URL
+ */
+var makeUrl = function makeUrl(s) {
+  var search = window.location.search;
+
+  // Remove previous grep query parameter if present
+  if (search) {
+    search = search.replace(/[?&]grep=[^&\s]*/g, '').replace(/^&/, '?');
+  }
+
+  return window.location.pathname + (search ? search + '&' : '?' ) + 'grep=' + encodeURIComponent(s);
+};
+
+/**
  * Provide suite URL
  *
  * @param {Object} [suite]
  */
-
 HTML.prototype.suiteURL = function(suite){
-  return '?grep=' + encodeURIComponent(suite.fullTitle());
+  return makeUrl(suite.fullTitle());
 };
 
 /**
@@ -194,7 +208,7 @@ HTML.prototype.suiteURL = function(suite){
  */
 
 HTML.prototype.testURL = function(test){
-  return '?grep=' + encodeURIComponent(test.fullTitle());
+  return makeUrl(test.fullTitle());
 };
 
 /**
diff --git a/lib/reporters/index.js b/lib/reporters/index.js
index 1c4fccf..87b76d9 100644
--- a/lib/reporters/index.js
+++ b/lib/reporters/index.js
@@ -1,4 +1,3 @@
-
 exports.Base = require('./base');
 exports.Dot = require('./dot');
 exports.Doc = require('./doc');
diff --git a/lib/reporters/json-cov.js b/lib/reporters/json-cov.js
index 83e57f4..309c0ef 100644
--- a/lib/reporters/json-cov.js
+++ b/lib/reporters/json-cov.js
@@ -1,4 +1,3 @@
-
 /**
  * Module dependencies.
  */
diff --git a/lib/reporters/json-stream.js b/lib/reporters/json-stream.js
index 7cb8fbe..f7c05a8 100644
--- a/lib/reporters/json-stream.js
+++ b/lib/reporters/json-stream.js
@@ -1,4 +1,3 @@
-
 /**
  * Module dependencies.
  */
@@ -35,7 +34,9 @@ function List(runner) {
   });
 
   runner.on('fail', function(test, err){
-    console.log(JSON.stringify(['fail', clean(test)]));
+    test = clean(test);
+    test.err = err.message;
+    console.log(JSON.stringify(['fail', test]));
   });
 
   runner.on('end', function(){
@@ -58,4 +59,4 @@ function clean(test) {
     , fullTitle: test.fullTitle()
     , duration: test.duration
   }
-}
\ No newline at end of file
+}
diff --git a/lib/reporters/json.js b/lib/reporters/json.js
index 4ec9e12..f565506 100644
--- a/lib/reporters/json.js
+++ b/lib/reporters/json.js
@@ -1,4 +1,3 @@
-
 /**
  * Module dependencies.
  */
diff --git a/lib/reporters/landing.js b/lib/reporters/landing.js
index bf064f6..ee004a2 100644
--- a/lib/reporters/landing.js
+++ b/lib/reporters/landing.js
@@ -1,4 +1,3 @@
-
 /**
  * Module dependencies.
  */
@@ -56,7 +55,7 @@ function Landing(runner) {
   }
 
   runner.on('start', function(){
-    stream.write('\n  ');
+    stream.write('\n\n\n  ');
     cursor.hide();
   });
 
@@ -73,7 +72,7 @@ function Landing(runner) {
     }
 
     // render landing strip
-    stream.write('\u001b[4F\n\n');
+    stream.write('\u001b['+(width+1)+'D\u001b[2A');
     stream.write(runway());
     stream.write('\n  ');
     stream.write(color('runway', Array(col).join('⋅')));
@@ -94,4 +93,4 @@ function Landing(runner) {
  * Inherit from `Base.prototype`.
  */
 
-Landing.prototype.__proto__ = Base.prototype;
\ No newline at end of file
+Landing.prototype.__proto__ = Base.prototype;
diff --git a/lib/reporters/list.js b/lib/reporters/list.js
index 3328e15..f64367a 100644
--- a/lib/reporters/list.js
+++ b/lib/reporters/list.js
@@ -1,4 +1,3 @@
-
 /**
  * Module dependencies.
  */
diff --git a/lib/reporters/markdown.js b/lib/reporters/markdown.js
index 6383a64..e14174c 100644
--- a/lib/reporters/markdown.js
+++ b/lib/reporters/markdown.js
@@ -6,6 +6,12 @@ var Base = require('./base')
   , utils = require('../utils');
 
 /**
+ * Constants
+ */
+
+var SUITE_PREFIX = '$';
+
+/**
  * Expose `Markdown`.
  */
 
@@ -35,8 +41,9 @@ function Markdown(runner) {
   }
 
   function mapTOC(suite, obj) {
-    var ret = obj;
-    obj = obj[suite.title] = obj[suite.title] || { suite: suite };
+    var ret = obj,
+        key = SUITE_PREFIX + suite.title;
+    obj = obj[key] = obj[key] || { suite: suite };
     suite.suites.forEach(function(suite){
       mapTOC(suite, obj);
     });
@@ -49,11 +56,13 @@ function Markdown(runner) {
     var link;
     for (var key in obj) {
       if ('suite' == key) continue;
-      if (key) link = ' - [' + key + '](#' + utils.slug(obj[key].suite.fullTitle()) + ')\n';
-      if (key) buf += Array(level).join('  ') + link;
+      if (key !== SUITE_PREFIX) {
+        link = ' - [' + key.substring(1) + ']';
+        link += '(#' + utils.slug(obj[key].suite.fullTitle()) + ')\n';
+        buf += Array(level).join('  ') + link;
+      }
       buf += stringifyTOC(obj[key], level);
     }
-    --level;
     return buf;
   }
 
@@ -88,4 +97,4 @@ function Markdown(runner) {
     process.stdout.write(generateTOC(runner.suite));
     process.stdout.write(buf);
   });
-}
\ No newline at end of file
+}
diff --git a/lib/reporters/min.js b/lib/reporters/min.js
index 1b6117d..ce1a3fe 100644
--- a/lib/reporters/min.js
+++ b/lib/reporters/min.js
@@ -1,4 +1,3 @@
-
 /**
  * Module dependencies.
  */
diff --git a/lib/reporters/nyan.js b/lib/reporters/nyan.js
index a8d43bf..63056b1 100644
--- a/lib/reporters/nyan.js
+++ b/lib/reporters/nyan.js
@@ -2,8 +2,7 @@
  * Module dependencies.
  */
 
-var Base = require('./base')
-  , color = Base.color;
+var Base = require('./base');
 
 /**
  * Expose `Dot`.
@@ -80,17 +79,16 @@ NyanCat.prototype.draw = function(){
 
 NyanCat.prototype.drawScoreboard = function(){
   var stats = this.stats;
-  var colors = Base.colors;
 
-  function draw(color, n) {
+  function draw(type, n) {
     write(' ');
-    write('\u001b[' + color + 'm' + n + '\u001b[0m');
+    write(Base.color(type, n));
     write('\n');
   }
 
-  draw(colors.green, stats.passes);
-  draw(colors.fail, stats.failures);
-  draw(colors.pending, stats.pending);
+  draw('green', stats.passes);
+  draw('fail', stats.failures);
+  draw('pending', stats.pending);
   write('\n');
 
   this.cursorUp(this.numberOfLines);
@@ -140,26 +138,26 @@ NyanCat.prototype.drawRainbow = function(){
 NyanCat.prototype.drawNyanCat = function() {
   var self = this;
   var startWidth = this.scoreboardWidth + this.trajectories[0].length;
-  var color = '\u001b[' + startWidth + 'C';
+  var dist = '\u001b[' + startWidth + 'C';
   var padding = '';
 
-  write(color);
+  write(dist);
   write('_,------,');
   write('\n');
 
-  write(color);
+  write(dist);
   padding = self.tick ? '  ' : '   ';
   write('_|' + padding + '/\\_/\\ ');
   write('\n');
 
-  write(color);
+  write(dist);
   padding = self.tick ? '_' : '__';
   var tail = self.tick ? '~' : '^';
   var face;
   write(tail + '|' + padding + this.face() + ' ');
   write('\n');
 
-  write(color);
+  write(dist);
   padding = self.tick ? ' ' : '  ';
   write(padding + '""  "" ');
   write('\n');
@@ -240,6 +238,8 @@ NyanCat.prototype.generateColors = function(){
  */
 
 NyanCat.prototype.rainbowify = function(str){
+  if (!Base.useColors)
+    return str;
   var color = this.rainbowColors[this.colorIndex % this.rainbowColors.length];
   this.colorIndex += 1;
   return '\u001b[38;5;' + color + 'm' + str + '\u001b[0m';
diff --git a/lib/reporters/spec.js b/lib/reporters/spec.js
index ada25c3..3debffe 100644
--- a/lib/reporters/spec.js
+++ b/lib/reporters/spec.js
@@ -1,4 +1,3 @@
-
 /**
  * Module dependencies.
  */
@@ -55,14 +54,14 @@ function Spec(runner) {
     if ('fast' == test.speed) {
       var fmt = indent()
         + color('checkmark', '  ' + Base.symbols.ok)
-        + color('pass', ' %s ');
+        + color('pass', ' %s');
       cursor.CR();
       console.log(fmt, test.title);
     } else {
       var fmt = indent()
         + color('checkmark', '  ' + Base.symbols.ok)
-        + color('pass', ' %s ')
-        + color(test.speed, '(%dms)');
+        + color('pass', ' %s')
+        + color(test.speed, ' (%dms)');
       cursor.CR();
       console.log(fmt, test.title, test.duration);
     }
diff --git a/lib/reporters/tap.js b/lib/reporters/tap.js
index 2bcd995..01a92eb 100644
--- a/lib/reporters/tap.js
+++ b/lib/reporters/tap.js
@@ -1,4 +1,3 @@
-
 /**
  * Module dependencies.
  */
diff --git a/lib/reporters/templates/menu.jade b/lib/reporters/templates/menu.jade
index e9ba464..c682e3f 100644
--- a/lib/reporters/templates/menu.jade
+++ b/lib/reporters/templates/menu.jade
@@ -10,4 +10,4 @@
         if segments.length
           span.dirname= segments.join('/') + '/'
         span.basename= basename
-  a#logo(href='http://visionmedia.github.io/mocha/') m
+  a#logo(href='http://mochajs.org/') m
diff --git a/lib/reporters/templates/style.html b/lib/reporters/templates/style.html
index 643c0ab..4c9c37c 100644
--- a/lib/reporters/templates/style.html
+++ b/lib/reporters/templates/style.html
@@ -8,7 +8,7 @@ body {
 }
 
 #coverage {
-  padding: 60px;
+  padding: 60px 400px 60px 60px;
 }
 
 h1 a {
@@ -124,6 +124,10 @@ footer span {
   padding: 15px 0;
   text-align: right;
   border-left: 1px solid #eee;
+  max-width: 400px;
+  overflow: auto;
+  white-space: nowrap;
+  
   -moz-box-shadow: 0 0 2px #888
      , inset 5px 0 20px rgba(0,0,0,.5)
      , inset 5px 0 3px rgba(0,0,0,.3);
@@ -317,4 +321,4 @@ code .init { color: #2F6FAD }
 code .string { color: #5890AD }
 code .keyword { color: #8A6343 }
 code .number { color: #2F6FAD }
-</style>
\ No newline at end of file
+</style>
diff --git a/lib/reporters/xunit.js b/lib/reporters/xunit.js
index 3506a07..77cd347 100644
--- a/lib/reporters/xunit.js
+++ b/lib/reporters/xunit.js
@@ -1,10 +1,10 @@
-
 /**
  * Module dependencies.
  */
 
 var Base = require('./base')
   , utils = require('../utils')
+  , fs = require('fs')
   , escape = utils.escape;
 
 /**
@@ -30,12 +30,19 @@ exports = module.exports = XUnit;
  * @api public
  */
 
-function XUnit(runner) {
+function XUnit(runner, options) {
   Base.call(this, runner);
   var stats = this.stats
     , tests = []
     , self = this;
 
+  if (options.reporterOptions && options.reporterOptions.output) {
+      if (! fs.createWriteStream) {
+          throw new Error('file output not supported in browser');
+      }
+      self.fileStream = fs.createWriteStream(options.reporterOptions.output);
+  }
+
   runner.on('pending', function(test){
     tests.push(test);
   });
@@ -49,7 +56,7 @@ function XUnit(runner) {
   });
 
   runner.on('end', function(){
-    console.log(tag('testsuite', {
+    self.write(tag('testsuite', {
         name: 'Mocha Tests'
       , tests: stats.tests
       , failures: stats.failures
@@ -59,22 +66,46 @@ function XUnit(runner) {
       , time: (stats.duration / 1000) || 0
     }, false));
 
-    tests.forEach(test);
-    console.log('</testsuite>');
+    tests.forEach(function(t) { self.test(t); });
+    self.write('</testsuite>');
   });
 }
 
 /**
+ * Override done to close the stream (if it's a file).
+ */
+XUnit.prototype.done = function(failures, fn) {
+    if (this.fileStream) {
+        this.fileStream.end(function() {
+            fn(failures);
+        });
+    } else {
+        fn(failures);
+    }
+};
+
+/**
  * Inherit from `Base.prototype`.
  */
 
 XUnit.prototype.__proto__ = Base.prototype;
 
 /**
+ * Write out the given line
+ */
+XUnit.prototype.write = function(line) {
+    if (this.fileStream) {
+        this.fileStream.write(line + '\n');
+    } else {
+        console.log(line);
+    }
+};
+
+/**
  * Output tag for the given `test.`
  */
 
-function test(test) {
+XUnit.prototype.test = function(test, ostream) {
   var attrs = {
       classname: test.parent.fullTitle()
     , name: test.title
@@ -83,13 +114,13 @@ function test(test) {
 
   if ('failed' == test.state) {
     var err = test.err;
-    console.log(tag('testcase', attrs, false, tag('failure', {}, false, cdata(escape(err.message) + "\n" + err.stack))));
+    this.write(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)));
+    this.write(tag('testcase', attrs, false, tag('skipped', {}, true)));
   } else {
-    console.log(tag('testcase', attrs, true) );
+    this.write(tag('testcase', attrs, true) );
   }
-}
+};
 
 /**
  * HTML tag helper.
diff --git a/lib/runnable.js b/lib/runnable.js
index 9409007..6e4803f 100644
--- a/lib/runnable.js
+++ b/lib/runnable.js
@@ -1,11 +1,12 @@
-
 /**
  * Module dependencies.
  */
 
 var EventEmitter = require('events').EventEmitter
   , debug = require('debug')('mocha:runnable')
-  , milliseconds = require('./ms');
+  , Pending = require('./pending')
+  , milliseconds = require('./ms')
+  , utils = require('./utils');
 
 /**
  * Save timer references to avoid Sinon interfering (see GH-237).
@@ -46,6 +47,7 @@ function Runnable(title, fn) {
   this._slow = 75;
   this._enableTimeouts = true;
   this.timedOut = false;
+  this._trace = new Error('done() called multiple times')
 }
 
 /**
@@ -104,6 +106,16 @@ Runnable.prototype.enableTimeouts = function(enabled){
 };
 
 /**
+ * Halt and mark as pending.
+ *
+ * @api private
+ */
+
+Runnable.prototype.skip = function(){
+    throw new Pending();
+};
+
+/**
  * Return the full title generated by recursively
  * concatenating the parent's full title.
  *
@@ -155,7 +167,7 @@ Runnable.prototype.resetTimeout = function(){
   this.clearTimeout();
   this.timer = setTimeout(function(){
     if (!self._enableTimeouts) return;
-    self.callback(new Error('timeout of ' + ms + 'ms exceeded'));
+    self.callback(new Error('timeout of ' + ms + 'ms exceeded. Ensure the done() callback is being called in this test.'));
     self.timedOut = true;
   }, ms);
 };
@@ -191,18 +203,22 @@ Runnable.prototype.run = function(fn){
   function multiple(err) {
     if (emitted) return;
     emitted = true;
-    self.emit('error', err || new Error('done() called multiple times'));
+    self.emit('error', err || new Error('done() called multiple times; stacktrace may be inaccurate'));
   }
 
   // finished
   function done(err) {
     var ms = self.timeout();
     if (self.timedOut) return;
-    if (finished) return multiple(err);
+    if (finished) return multiple(err || self._trace);
+
+    // Discard the resolution if this test has already failed asynchronously
+    if (self.state) return;
+
     self.clearTimeout();
     self.duration = new Date - start;
     finished = true;
-    if (!err && self.duration > ms && self._enableTimeouts) err = new Error('timeout of ' + ms + 'ms exceeded');
+    if (!err && self.duration > ms && self._enableTimeouts) err = new Error('timeout of ' + ms + 'ms exceeded. Ensure the done() callback is being called in this test.');
     fn(err);
   }
 
@@ -226,7 +242,7 @@ Runnable.prototype.run = function(fn){
         done();
       });
     } catch (err) {
-      done(err);
+      done(utils.getError(err));
     }
     return;
   }
@@ -243,7 +259,7 @@ Runnable.prototype.run = function(fn){
       callFn(this.fn);
     }
   } catch (err) {
-    done(err);
+    done(utils.getError(err));
   }
 
   function callFn(fn) {
diff --git a/lib/runner.js b/lib/runner.js
index b5a6d49..3199dc9 100644
--- a/lib/runner.js
+++ b/lib/runner.js
@@ -4,10 +4,14 @@
 
 var EventEmitter = require('events').EventEmitter
   , debug = require('debug')('mocha:runner')
+  , Pending = require('./pending')
   , Test = require('./test')
   , utils = require('./utils')
   , filter = utils.filter
-  , keys = utils.keys;
+  , keys = utils.keys
+  , type = utils.type
+  , stringify = utils.stringify
+  , stackFilter = utils.stackTraceFilter();
 
 /**
  * Non-enumerable globals.
@@ -19,7 +23,9 @@ var globals = [
   'setInterval',
   'clearInterval',
   'XMLHttpRequest',
-  'Date'
+  'Date',
+  'setImmediate',
+  'clearImmediate'
 ];
 
 /**
@@ -45,13 +51,17 @@ module.exports = Runner;
  *   - `fail`  (test, err) test failed
  *   - `pending`  (test) test pending
  *
+ * @param {Suite} suite Root suite
+ * @param {boolean} [delay] Whether or not to delay execution of root suite
+ *   until ready.
  * @api public
  */
 
-function Runner(suite) {
+function Runner(suite, delay) {
   var self = this;
   this._globals = [];
   this._abort = false;
+  this._delay = delay;
   this.suite = suite;
   this.total = suite.total();
   this.failures = 0;
@@ -188,14 +198,18 @@ Runner.prototype.checkGlobals = function(test){
  * @api private
  */
 
-Runner.prototype.fail = function(test, err){
+Runner.prototype.fail = function(test, err) {
   ++this.failures;
   test.state = 'failed';
 
-  if ('string' == typeof err) {
-    err = new Error('the string "' + err + '" was thrown, throw an Error :)');
+  if (!(err instanceof Error)) {
+    err = new Error('the ' + type(err) + ' ' + stringify(err) + ' was thrown, throw an Error :)');
   }
 
+  err.stack = (this.fullStackTrace || !err.stack)
+    ? err.stack
+    : stackFilter(err.stack);
+
   this.emit('fail', test, err);
 };
 
@@ -244,7 +258,6 @@ Runner.prototype.hook = function(name, fn){
   function next(i) {
     var hook = hooks[i];
     if (!hook) return fn();
-    if (self.failures && suite.bail()) return fn();
     self.currentRunnable = hook;
 
     hook.ctx.currentTest = self.test;
@@ -260,10 +273,14 @@ Runner.prototype.hook = function(name, fn){
       var testError = hook.error();
       if (testError) self.fail(self.test, testError);
       if (err) {
-        self.failHook(hook, err);
+        if (err instanceof Pending) {
+          suite.pending = true;
+        } else {
+          self.failHook(hook, err);
 
-        // stop executing hooks, notify callee of hook err
-        return fn(err);
+          // stop executing hooks, notify callee of hook err
+          return fn(err);
+        }
       }
       self.emit('hook end', hook);
       delete hook.ctx.currentTest;
@@ -445,6 +462,11 @@ Runner.prototype.runTests = function(suite, fn){
     self.emit('test', self.test = test);
     self.hookDown('beforeEach', function(err, errSuite){
 
+      if (suite.pending) {
+        self.emit('pending', test);
+        self.emit('test end', test);
+        return next();
+      }
       if (err) return hookErr(err, errSuite, false);
 
       self.currentRunnable = self.test;
@@ -452,8 +474,17 @@ Runner.prototype.runTests = function(suite, fn){
         test = self.test;
 
         if (err) {
-          self.fail(test, err);
+          if (err instanceof Pending) {
+            self.emit('pending', test);
+          } else {
+            self.fail(test, err);
+          }
           self.emit('test end', test);
+
+          if (err instanceof Pending) {
+            return next();
+          }
+
           return self.hookUp('afterEach', next);
         }
 
@@ -538,19 +569,18 @@ Runner.prototype.uncaught = function(err){
     }.call(err) ? err : ( err.message || err ));
   } else {
     debug('uncaught undefined exception');
-    err = new Error('Caught undefined error, did you throw without specifying what?');
+    err = utils.undefinedError();
   }
   err.uncaught = true;
 
   var runnable = this.currentRunnable;
   if (!runnable) return;
 
-  var wasAlreadyDone = runnable.state;
-  this.fail(runnable, err);
-
   runnable.clearTimeout();
 
-  if (wasAlreadyDone) return;
+  // Ignore errors if complete
+  if (runnable.state) return;
+  this.fail(runnable, err);
 
   // recover from test
   if ('test' == runnable.type) {
@@ -573,13 +603,23 @@ Runner.prototype.uncaught = function(err){
  */
 
 Runner.prototype.run = function(fn){
-  var self = this
-    , fn = fn || function(){};
+  var self = this,
+    rootSuite = this.suite;
+
+  fn = fn || function(){};
 
   function uncaught(err){
     self.uncaught(err);
   }
 
+  function start() {
+    self.emit('start');
+    self.runSuite(rootSuite, function(){
+      debug('finished running');
+      self.emit('end');
+    });
+  }
+
   debug('start');
 
   // callback
@@ -589,16 +629,19 @@ Runner.prototype.run = function(fn){
     fn(self.failures);
   });
 
-  // run suites
-  this.emit('start');
-  this.runSuite(this.suite, function(){
-    debug('finished running');
-    self.emit('end');
-  });
-
   // uncaught exception
   process.on('uncaughtException', uncaught);
 
+  if (this._delay) {
+    // for reporters, I guess.
+    // might be nice to debounce some dots while we wait.
+    this.emit('waiting', rootSuite);
+    rootSuite.once('run', start);
+  }
+  else {
+    start();
+  }
+
   return this;
 };
 
@@ -654,20 +697,20 @@ function filterLeaks(ok, globals) {
  * @api private
  */
 
- function extraGlobals() {
-  if (typeof(process) === 'object' &&
-      typeof(process.version) === 'string') {
+function extraGlobals() {
+ if (typeof(process) === 'object' &&
+     typeof(process.version) === 'string') {
 
-    var nodeVersion = process.version.split('.').reduce(function(a, v) {
-      return a << 8 | v;
-    });
-
-    // 'errno' was renamed to process._errno in v0.9.11.
+   var nodeVersion = process.version.split('.').reduce(function(a, v) {
+     return a << 8 | v;
+   });
 
-    if (nodeVersion < 0x00090B) {
-      return ['errno'];
-    }
-  }
+   // 'errno' was renamed to process._errno in v0.9.11.
 
-  return [];
+   if (nodeVersion < 0x00090B) {
+     return ['errno'];
+   }
  }
+
+ return [];
+}
diff --git a/lib/suite.js b/lib/suite.js
index e8696f4..edc820e 100644
--- a/lib/suite.js
+++ b/lib/suite.js
@@ -1,4 +1,3 @@
-
 /**
  * Module dependencies.
  */
@@ -63,6 +62,7 @@ function Suite(title, parentContext) {
   this._enableTimeouts = true;
   this._slow = 75;
   this._bail = false;
+  this.delayed = false;
 }
 
 /**
@@ -99,7 +99,7 @@ Suite.prototype.clone = function(){
 
 Suite.prototype.timeout = function(ms){
   if (0 == arguments.length) return this._timeout;
-  if (ms === 0) this._enableTimeouts = false;
+  if (ms.toString() === '0') this._enableTimeouts = false;
   if ('string' == typeof ms) ms = milliseconds(ms);
   debug('timeout %d', ms);
   this._timeout = parseInt(ms, 10);
@@ -140,7 +140,7 @@ Suite.prototype.slow = function(ms){
 /**
  * Sets whether to bail after first error.
  *
- * @parma {Boolean} bail
+ * @param {Boolean} bail
  * @return {Suite|Number} for chaining
  * @api private
  */
@@ -344,3 +344,12 @@ Suite.prototype.eachTest = function(fn){
   });
   return this;
 };
+
+/**
+ * This will run the root suite if we happen to be running in delayed mode.
+ */
+Suite.prototype.run = function run() {
+  if (this.root) {
+    this.emit('run');
+  }
+};
diff --git a/lib/test.js b/lib/test.js
index 11773e0..4a4cf63 100644
--- a/lib/test.js
+++ b/lib/test.js
@@ -1,4 +1,3 @@
-
 /**
  * Module dependencies.
  */
diff --git a/lib/utils.js b/lib/utils.js
index ecc0a14..845f84c 100644
--- a/lib/utils.js
+++ b/lib/utils.js
@@ -47,6 +47,17 @@ exports.forEach = function(arr, fn, scope){
 };
 
 /**
+ * Test if the given obj is type of string
+ *
+ * @param {Object} obj
+ * @returns Boolean
+ */
+
+exports.isString = function(obj) {
+  return 'string' === typeof obj;
+};
+
+/**
  * Array#map (<=IE8)
  *
  * @param {Array} array
@@ -58,7 +69,7 @@ exports.forEach = function(arr, fn, scope){
 exports.map = function(arr, fn, scope){
   var result = [];
   for (var i = 0, l = arr.length; i < l; i++)
-    result.push(fn.call(scope, arr[i], i));
+    result.push(fn.call(scope, arr[i], i, arr));
   return result;
 };
 
@@ -127,7 +138,7 @@ exports.filter = function(arr, fn){
 
 exports.keys = Object.keys || function(obj) {
   var keys = []
-    , has = Object.prototype.hasOwnProperty // for `window` on <=IE8
+    , has = Object.prototype.hasOwnProperty; // for `window` on <=IE8
 
   for (var key in obj) {
     if (has.call(obj, key)) {
@@ -158,6 +169,28 @@ exports.watch = function(files, fn){
 };
 
 /**
+ * Array.isArray (<=IE8)
+ *
+ * @param {Object} obj
+ * @return {Boolean}
+ * @api private
+ */
+var isArray = Array.isArray || function (obj) {
+  return '[object Array]' == {}.toString.call(obj);
+};
+
+/**
+ * @description
+ * Buffer.prototype.toJSON polyfill
+ * @type {Function}
+ */
+if(typeof Buffer !== 'undefined' && Buffer.prototype) {
+  Buffer.prototype.toJSON = Buffer.prototype.toJSON || function () {
+    return Array.prototype.slice.call(this, 0);
+  };
+}
+
+/**
  * Ignored files.
  */
 
@@ -179,15 +212,15 @@ exports.files = function(dir, ext, ret){
   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, ext, ret);
-    } else if (path.match(re)) {
-      ret.push(path);
-    }
-  });
+    .filter(ignored)
+    .forEach(function(path){
+      path = join(dir, path);
+      if (fs.statSync(path).isDirectory()) {
+        exports.files(path, ext, ret);
+      } else if (path.match(re)) {
+        ret.push(path);
+      }
+    });
 
   return ret;
 };
@@ -215,7 +248,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 *\(.*\)\s*{|\(.*\) *=> *{?/, '')
     .replace(/\s+\}$/, '');
 
   var spaces = str.match(/^\n?( *)/)[1].length
@@ -292,54 +325,256 @@ exports.highlightTags = function(name) {
   }
 };
 
+/**
+ * If a value could have properties, and has none, this function is called, which returns
+ * a string representation of the empty value.
+ *
+ * Functions w/ no properties return `'[Function]'`
+ * Arrays w/ length === 0 return `'[]'`
+ * Objects w/ no properties return `'{}'`
+ * All else: return result of `value.toString()`
+ *
+ * @param {*} value Value to inspect
+ * @param {string} [type] The type of the value, if known.
+ * @returns {string}
+ */
+var emptyRepresentation = function emptyRepresentation(value, type) {
+  type = type || exports.type(value);
+
+  switch(type) {
+    case 'function':
+      return '[Function]';
+    case 'object':
+      return '{}';
+    case 'array':
+      return '[]';
+    default:
+      return value.toString();
+  }
+};
+
+/**
+ * Takes some variable and asks `{}.toString()` what it thinks it is.
+ * @param {*} value Anything
+ * @example
+ * type({}) // 'object'
+ * type([]) // 'array'
+ * type(1) // 'number'
+ * type(false) // 'boolean'
+ * type(Infinity) // 'number'
+ * type(null) // 'null'
+ * type(new Date()) // 'date'
+ * type(/foo/) // 'regexp'
+ * type('type') // 'string'
+ * type(global) // 'global'
+ * @api private
+ * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/toString
+ * @returns {string}
+ */
+exports.type = function type(value) {
+  if (typeof Buffer !== 'undefined' && Buffer.isBuffer(value)) {
+    return 'buffer';
+  }
+  return Object.prototype.toString.call(value)
+    .replace(/^\[.+\s(.+?)\]$/, '$1')
+    .toLowerCase();
+};
 
 /**
- * Stringify `obj`.
+ * @summary Stringify `value`.
+ * @description Different behavior depending on type of value.
+ * - If `value` is undefined or null, return `'[undefined]'` or `'[null]'`, respectively.
+ * - If `value` is not an object, function or array, return result of `value.toString()` wrapped in double-quotes.
+ * - If `value` is an *empty* object, function, or array, return result of function
+ *   {@link emptyRepresentation}.
+ * - If `value` has properties, call {@link exports.canonicalize} on it, then return result of
+ *   JSON.stringify().
  *
- * @param {Object} obj
- * @return {String}
+ * @see exports.type
+ * @param {*} value
+ * @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');
+exports.stringify = function(value) {
+  var type = exports.type(value);
+
+  if (!~exports.indexOf(['object', 'array', 'function'], type)) {
+    if(type != 'buffer') {
+      return jsonStringify(value);
+    }
+    var json = value.toJSON();
+    // Based on the toJSON result
+    return jsonStringify(json.data && json.type ? json.data : json, 2)
+      .replace(/,(\n|$)/g, '$1');
+  }
+
+  for (var prop in value) {
+    if (Object.prototype.hasOwnProperty.call(value, prop)) {
+      return jsonStringify(exports.canonicalize(value), 2).replace(/,(\n|$)/g, '$1');
+    }
+  }
+
+  return emptyRepresentation(value, type);
 };
 
 /**
- * Return a new object that has the keys in sorted order.
- * @param {Object} obj
- * @param {Array} [stack]
- * @return {Object}
+ * @description
+ * like JSON.stringify but more sense.
+ * @param {Object}  object
+ * @param {Number=} spaces
+ * @param {number=} depth
+ * @returns {*}
+ * @private
+ */
+function jsonStringify(object, spaces, depth) {
+  if(typeof spaces == 'undefined') return _stringify(object);  // primitive types
+
+  depth = depth || 1;
+  var space = spaces * depth
+    , str = isArray(object) ? '[' : '{'
+    , end = isArray(object) ? ']' : '}'
+    , length = object.length || exports.keys(object).length
+    , repeat = function(s, n) { return new Array(n).join(s); }; // `.repeat()` polyfill
+
+  function _stringify(val) {
+    switch (exports.type(val)) {
+      case 'null':
+      case 'undefined':
+        val = '[' + val + ']';
+        break;
+      case 'array':
+      case 'object':
+        val = jsonStringify(val, spaces, depth + 1);
+        break;
+      case 'boolean':
+      case 'regexp':
+      case 'number':
+        val = val === 0 && (1/val) === -Infinity // `-0`
+          ? '-0'
+          : val.toString();
+        break;
+      case 'date':
+        var sDate = isNaN(val.getTime())        // Invalid date
+          ? val.toString()
+          : val.toISOString();
+        val = '[Date: ' + sDate + ']';
+        break;
+      case 'buffer':
+        var json = val.toJSON();
+        // Based on the toJSON result
+        json = json.data && json.type ? json.data : json;
+        val = '[Buffer: ' + jsonStringify(json, 2, depth + 1) + ']';
+        break;
+      default:
+        val = (val == '[Function]' || val == '[Circular]')
+          ? val
+          : JSON.stringify(val); //string
+    }
+    return val;
+  }
+
+  for(var i in object) {
+    if(!object.hasOwnProperty(i)) continue;        // not my business
+    --length;
+    str += '\n ' + repeat(' ', space)
+      + (isArray(object) ? '' : '"' + i + '": ') // key
+      +  _stringify(object[i])                   // value
+      + (length ? ',' : '');                     // comma
+  }
+
+  return str + (str.length != 1                    // [], {}
+    ? '\n' + repeat(' ', --space) + end
+    : end);
+}
+
+/**
+ * Return if obj is a Buffer
+ * @param {Object} arg
+ * @return {Boolean}
  * @api private
  */
+exports.isBuffer = function (arg) {
+  return typeof Buffer !== 'undefined' && Buffer.isBuffer(arg);
+};
 
-exports.canonicalize = function(obj, stack) {
-  stack = stack || [];
+/**
+ * @summary Return a new Thing that has the keys in sorted order.  Recursive.
+ * @description If the Thing...
+ * - has already been seen, return string `'[Circular]'`
+ * - is `undefined`, return string `'[undefined]'`
+ * - is `null`, return value `null`
+ * - is some other primitive, return the value
+ * - is not a primitive or an `Array`, `Object`, or `Function`, return the value of the Thing's `toString()` method
+ * - is a non-empty `Array`, `Object`, or `Function`, return the result of calling this function again.
+ * - is an empty `Array`, `Object`, or `Function`, return the result of calling `emptyRepresentation()`
+ *
+ * @param {*} value Thing to inspect.  May or may not have properties.
+ * @param {Array} [stack=[]] Stack of seen values
+ * @return {(Object|Array|Function|string|undefined)}
+ * @see {@link exports.stringify}
+ * @api private
+ */
 
-  if (exports.indexOf(stack, obj) !== -1) return '[Circular]';
+exports.canonicalize = function(value, stack) {
+  var canonicalizedObj,
+    type = exports.type(value),
+    prop,
+    withStack = function withStack(value, fn) {
+      stack.push(value);
+      fn();
+      stack.pop();
+    };
 
-  var canonicalizedObj;
+  stack = stack || [];
 
-  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;
+  if (exports.indexOf(stack, value) !== -1) {
+    return '[Circular]';
+  }
+
+  switch(type) {
+    case 'undefined':
+    case 'buffer':
+    case 'null':
+      canonicalizedObj = value;
+      break;
+    case 'array':
+      withStack(value, function () {
+        canonicalizedObj = exports.map(value, function (item) {
+          return exports.canonicalize(item, stack);
+        });
+      });
+      break;
+    case 'function':
+      for (prop in value) {
+        canonicalizedObj = {};
+        break;
+      }
+      if (!canonicalizedObj) {
+        canonicalizedObj = emptyRepresentation(value, type);
+        break;
+      }
+    /* falls through */
+    case 'object':
+      canonicalizedObj = canonicalizedObj || {};
+      withStack(value, function () {
+        exports.forEach(exports.keys(value).sort(), function (key) {
+          canonicalizedObj[key] = exports.canonicalize(value[key], stack);
+        });
+      });
+      break;
+    case 'date':
+    case 'number':
+    case 'regexp':
+    case 'boolean':
+      canonicalizedObj = value;
+      break;
+    default:
+      canonicalizedObj = value.toString();
   }
 
   return canonicalizedObj;
- };
+};
 
 /**
  * Lookup file names at the given `path`.
@@ -366,7 +601,7 @@ exports.lookupFiles = function lookupFiles(path, extensions, recursive) {
     return;
   }
 
-  fs.readdirSync(path).forEach(function(file){
+  fs.readdirSync(path).forEach(function(file) {
     file = join(path, file);
     try {
       var stat = fs.statSync(file);
@@ -386,3 +621,90 @@ exports.lookupFiles = function lookupFiles(path, extensions, recursive) {
 
   return files;
 };
+
+/**
+ * Generate an undefined error with a message warning the user.
+ *
+ * @return {Error}
+ */
+
+exports.undefinedError = function() {
+  return new Error('Caught undefined error, did you throw without specifying what?');
+};
+
+/**
+ * Generate an undefined error if `err` is not defined.
+ *
+ * @param {Error} err
+ * @return {Error}
+ */
+
+exports.getError = function(err) {
+  return err || exports.undefinedError();
+};
+
+
+/**
+ * @summary
+ * This Filter based on `mocha-clean` module.(see: `github.com/rstacruz/mocha-clean`)
+ * @description
+ * When invoking this function you get a filter function that get the Error.stack as an input,
+ * and return a prettify output.
+ * (i.e: strip Mocha, node_modules, bower and componentJS from stack trace).
+ * @returns {Function}
+ */
+
+exports.stackTraceFilter = function() {
+  var slash = '/'
+    , is = typeof document === 'undefined'
+      ? { node: true }
+      : { browser: true }
+    , cwd = is.node
+      ? process.cwd() + slash
+      : location.href.replace(/\/[^\/]*$/, '/');
+
+  function isNodeModule (line) {
+    return (~line.indexOf('node_modules'));
+  }
+
+  function isMochaInternal (line) {
+    return (~line.indexOf('node_modules' + slash + 'mocha'))  ||
+      (~line.indexOf('components' + slash + 'mochajs'))       ||
+      (~line.indexOf('components' + slash + 'mocha'));
+  }
+
+  // node_modules, bower, componentJS
+  function isBrowserModule(line) {
+    return (~line.indexOf('node_modules')) ||
+      (~line.indexOf('components'));
+  }
+
+  function isNodeInternal (line) {
+    return (~line.indexOf('(timers.js:')) ||
+      (~line.indexOf('(events.js:'))      ||
+      (~line.indexOf('(node.js:'))        ||
+      (~line.indexOf('(module.js:'))      ||
+      (~line.indexOf('GeneratorFunctionPrototype.next (native)')) ||
+      false
+  }
+
+  return function(stack) {
+    stack = stack.split('\n');
+
+    stack = exports.reduce(stack, function(list, line) {
+      if (is.node && (isNodeModule(line) ||
+        isMochaInternal(line) ||
+        isNodeInternal(line)))
+        return list;
+
+      if (is.browser && (isBrowserModule(line)))
+        return list;
+
+      // Clean up cwd(absolute)
+      list.push(line.replace(cwd, ''));
+      return list;
+    }, []);
+
+    return stack.join('\n');
+  }
+};
\ No newline at end of file
diff --git a/media/logo.svg b/media/logo.svg
index 88d3713..1bed0ce 100644
--- a/media/logo.svg
+++ b/media/logo.svg
@@ -1,8 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!-- Generator: Adobe Illustrator 17.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
 <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
-<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
-	 width="612px" height="792px" viewBox="0 0 612 792" enable-background="new 0 0 612 792" xml:space="preserve">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="612px" height="792px" viewBox="0 0 612 792" enable-background="new 0 0 612 792" xml:space="preserve">
 <circle fill="#8A6343" cx="306" cy="396" r="306"/>
 <text transform="matrix(1 0 0 1 72.1431 424.7633)" fill="#FFFFFF" font-family="'HelveticaNeue'" font-size="153">mocha</text>
 </svg>
diff --git a/mocha.js b/mocha.js
index e8bee79..5ff1385 100644
--- a/mocha.js
+++ b/mocha.js
@@ -48,7 +48,6 @@ require.relative = function (parent) {
 
 
 require.register("browser/debug.js", function(module, exports, require){
-
 module.exports = function(type){
   return function(){
   }
@@ -435,17 +434,16 @@ require.register("browser/escape-string-regexp.js", function(module, exports, re
 var matchOperatorsRe = /[|\\{}()[\]^$+*?.]/g;
 
 module.exports = function (str) {
-	if (typeof str !== 'string') {
-		throw new TypeError('Expected a string');
-	}
+  if (typeof str !== 'string') {
+    throw new TypeError('Expected a string');
+  }
 
-	return str.replace(matchOperatorsRe,  '\\$&');
+  return str.replace(matchOperatorsRe,  '\\$&');
 };
 
 }); // module: browser/escape-string-regexp.js
 
 require.register("browser/events.js", function(module, exports, require){
-
 /**
  * Module exports.
  */
@@ -623,6 +621,7 @@ EventEmitter.prototype.emit = function (name) {
 
   return true;
 };
+
 }); // module: browser/events.js
 
 require.register("browser/fs.js", function(module, exports, require){
@@ -734,28 +733,28 @@ Progress.prototype.draw = function(ctx){
       , y = half
       , rad = half - 1
       , fontSize = this._fontSize;
-  
+
     ctx.font = fontSize + 'px ' + this._font;
-  
+
     var angle = Math.PI * 2 * (percent / 100);
     ctx.clearRect(0, 0, size, size);
-  
+
     // outer circle
     ctx.strokeStyle = '#9f9f9f';
     ctx.beginPath();
     ctx.arc(x, y, rad, 0, angle, false);
     ctx.stroke();
-  
+
     // inner circle
     ctx.strokeStyle = '#eee';
     ctx.beginPath();
     ctx.arc(x, y, rad - 1, 0, angle, true);
     ctx.stroke();
-  
+
     // text
     var text = this._text || (percent | 0) + '%'
       , w = ctx.measureText(text).width;
-  
+
     ctx.fillText(
         text
       , x - w / 2 + 1
@@ -767,7 +766,6 @@ Progress.prototype.draw = function(ctx){
 }); // module: browser/progress.js
 
 require.register("browser/tty.js", function(module, exports, require){
-
 exports.isatty = function(){
   return true;
 };
@@ -784,7 +782,6 @@ exports.getWindowSize = function(){
 }); // module: browser/tty.js
 
 require.register("context.js", function(module, exports, require){
-
 /**
  * Expose `Context`.
  */
@@ -855,6 +852,18 @@ Context.prototype.slow = function(ms){
 };
 
 /**
+ * Mark a test as skipped.
+ *
+ * @return {Context} self
+ * @api private
+ */
+
+Context.prototype.skip = function(){
+    this.runnable().skip();
+    return this;
+};
+
+/**
  * Inspect the context void of `._runnable`.
  *
  * @return {String}
@@ -872,7 +881,6 @@ Context.prototype.inspect = function(){
 }); // module: context.js
 
 require.register("hook.js", function(module, exports, require){
-
 /**
  * Module dependencies.
  */
@@ -929,7 +937,6 @@ Hook.prototype.error = function(err){
 }); // module: hook.js
 
 require.register("interfaces/bdd.js", function(module, exports, require){
-
 /**
  * Module dependencies.
  */
@@ -961,38 +968,13 @@ module.exports = function(suite){
 
   suite.on('pre-require', function(context, file, mocha){
 
-    /**
-     * Execute before running tests.
-     */
-
-    context.before = function(name, fn){
-      suites[0].beforeAll(name, fn);
-    };
-
-    /**
-     * Execute after running tests.
-     */
-
-    context.after = function(name, fn){
-      suites[0].afterAll(name, fn);
-    };
-
-    /**
-     * Execute before each test case.
-     */
-
-    context.beforeEach = function(name, fn){
-      suites[0].beforeEach(name, fn);
-    };
-
-    /**
-     * Execute after each test case.
-     */
-
-    context.afterEach = function(name, fn){
-      suites[0].afterEach(name, fn);
-    };
+    var common = require('./common')(suites, context);
 
+    context.before = common.before;
+    context.after = common.after;
+    context.beforeEach = common.beforeEach;
+    context.afterEach = common.afterEach;
+    context.run = mocha.options.delay && common.runWithSuite(suite);
     /**
      * Describe a "suite" with the given `title`
      * and callback `fn` containing nested suites
@@ -1067,13 +1049,75 @@ module.exports = function(suite){
     context.it.skip = function(title){
       context.it(title);
     };
+
   });
 };
 
 }); // module: interfaces/bdd.js
 
-require.register("interfaces/exports.js", function(module, exports, require){
+require.register("interfaces/common.js", function(module, exports, require){
+/**
+ * Functions common to more than one interface
+ * @module lib/interfaces/common
+ */
+
+'use strict';
+
+module.exports = function (suites, context) {
+
+  return {
+    /**
+     * This is only present if flag --delay is passed into Mocha.  It triggers
+     * root suite execution.  Returns a function which runs the root suite.
+     */
+    runWithSuite: function runWithSuite(suite) {
+      return function run() {
+        suite.run();
+      };
+    },
+
+    /**
+     * Execute before running tests.
+     */
+    before: function (name, fn) {
+      suites[0].beforeAll(name, fn);
+    },
 
+    /**
+     * Execute after running tests.
+     */
+    after: function (name, fn) {
+      suites[0].afterAll(name, fn);
+    },
+
+    /**
+     * Execute before each test case.
+     */
+    beforeEach: function (name, fn) {
+      suites[0].beforeEach(name, fn);
+    },
+
+    /**
+     * Execute after each test case.
+     */
+    afterEach: function (name, fn) {
+      suites[0].afterEach(name, fn);
+    },
+
+    test: {
+      /**
+       * Pending test case.
+       */
+      skip: function (title) {
+        context.test(title);
+      }
+    }
+  }
+};
+
+}); // module: interfaces/common.js
+
+require.register("interfaces/exports.js", function(module, exports, require){
 /**
  * Module dependencies.
  */
@@ -1139,7 +1183,6 @@ module.exports = function(suite){
 }); // module: interfaces/exports.js
 
 require.register("interfaces/index.js", function(module, exports, require){
-
 exports.bdd = require('./bdd');
 exports.tdd = require('./tdd');
 exports.qunit = require('./qunit');
@@ -1148,7 +1191,6 @@ exports.exports = require('./exports');
 }); // module: interfaces/index.js
 
 require.register("interfaces/qunit.js", function(module, exports, require){
-
 /**
  * Module dependencies.
  */
@@ -1188,38 +1230,13 @@ module.exports = function(suite){
 
   suite.on('pre-require', function(context, file, mocha){
 
-    /**
-     * Execute before running tests.
-     */
-
-    context.before = function(name, fn){
-      suites[0].beforeAll(name, fn);
-    };
-
-    /**
-     * Execute after running tests.
-     */
-
-    context.after = function(name, fn){
-      suites[0].afterAll(name, fn);
-    };
-
-    /**
-     * Execute before each test case.
-     */
-
-    context.beforeEach = function(name, fn){
-      suites[0].beforeEach(name, fn);
-    };
-
-    /**
-     * Execute after each test case.
-     */
-
-    context.afterEach = function(name, fn){
-      suites[0].afterEach(name, fn);
-    };
+    var common = require('./common')(suites, context);
 
+    context.before = common.before;
+    context.after = common.after;
+    context.beforeEach = common.beforeEach;
+    context.afterEach = common.afterEach;
+    context.run = mocha.options.delay && common.runWithSuite(suite);
     /**
      * Describe a "suite" with the given `title`.
      */
@@ -1264,20 +1281,14 @@ module.exports = function(suite){
       mocha.grep(new RegExp(reString));
     };
 
-    /**
-     * Pending test case.
-     */
+    context.test.skip = common.test.skip;
 
-    context.test.skip = function(title){
-      context.test(title);
-    };
   });
 };
 
 }); // module: interfaces/qunit.js
 
 require.register("interfaces/tdd.js", function(module, exports, require){
-
 /**
  * Module dependencies.
  */
@@ -1317,38 +1328,13 @@ module.exports = function(suite){
 
   suite.on('pre-require', function(context, file, mocha){
 
-    /**
-     * Execute before each test case.
-     */
-
-    context.setup = function(name, fn){
-      suites[0].beforeEach(name, fn);
-    };
-
-    /**
-     * Execute after each test case.
-     */
-
-    context.teardown = function(name, fn){
-      suites[0].afterEach(name, fn);
-    };
-
-    /**
-     * Execute before the suite.
-     */
-
-    context.suiteSetup = function(name, fn){
-      suites[0].beforeAll(name, fn);
-    };
-
-    /**
-     * Execute after the suite.
-     */
-
-    context.suiteTeardown = function(name, fn){
-      suites[0].afterAll(name, fn);
-    };
+    var common = require('./common')(suites, context);
 
+    context.setup = common.beforeEach;
+    context.teardown = common.afterEach;
+    context.suiteSetup = common.before;
+    context.suiteTeardown = common.after;
+    context.run = mocha.options.delay && common.runWithSuite(suite);
     /**
      * Describe a "suite" with the given `title`
      * and callback `fn` containing nested suites
@@ -1409,13 +1395,7 @@ module.exports = function(suite){
       mocha.grep(new RegExp(reString));
     };
 
-    /**
-     * Pending test case.
-     */
-
-    context.test.skip = function(title){
-      context.test(title);
-    };
+    context.test.skip = common.test.skip;
   });
 };
 
@@ -1490,6 +1470,7 @@ function image(name) {
  *   - `bail` bail on the first test failure
  *   - `slow` milliseconds to wait before considering a test slow
  *   - `ignoreLeaks` ignore global leaks
+ *   - `fullTrace` display the full stack-trace on failing
  *   - `grep` string or regexp to filter tests with
  *
  * @param {Object} options
@@ -1500,13 +1481,14 @@ function Mocha(options) {
   options = options || {};
   this.files = [];
   this.options = options;
-  this.grep(options.grep);
+  if (options.grep) this.grep(new RegExp(options.grep));
+  if (options.fgrep) this.grep(options.fgrep);
   this.suite = new exports.Suite('', new exports.Context);
   this.ui(options.ui);
   this.bail(options.bail);
-  this.reporter(options.reporter);
+  this.reporter(options.reporter, options.reporterOptions);
   if (null != options.timeout) this.timeout(options.timeout);
-  this.useColors(options.useColors)
+  this.useColors(options.useColors);
   if (options.enableTimeouts !== null) this.enableTimeouts(options.enableTimeouts);
   if (options.slow) this.slow(options.slow);
 
@@ -1523,6 +1505,7 @@ function Mocha(options) {
     exports.suite = context.suite || context.describe;
     exports.teardown = context.teardown || context.afterEach;
     exports.test = context.test || context.it;
+    exports.run = context.run;
   });
 }
 
@@ -1555,17 +1538,21 @@ Mocha.prototype.addFile = function(file){
  * Set reporter to `reporter`, defaults to "spec".
  *
  * @param {String|Function} reporter name or constructor
+ * @param {Object} reporterOptions optional options
  * @api public
  */
-
-Mocha.prototype.reporter = function(reporter){
+Mocha.prototype.reporter = function(reporter, reporterOptions){
   if ('function' == typeof reporter) {
     this._reporter = reporter;
   } else {
     reporter = reporter || 'spec';
     var _reporter;
-    try { _reporter = require('./reporters/' + reporter); } catch (err) {};
-    if (!_reporter) try { _reporter = require(reporter); } catch (err) {};
+    try { _reporter = require('./reporters/' + reporter); } catch (err) {}
+    if (!_reporter) try { _reporter = require(reporter); } catch (err) {
+      err.message.indexOf('Cannot find module') !== -1
+        ? console.warn('"' + reporter + '" reporter not found')
+        : console.warn('"' + reporter + '" reporter blew up with error:\n' + err.stack);
+    }
     if (!_reporter && reporter === 'teamcity')
       console.warn('The Teamcity reporter was moved to a package named ' +
         'mocha-teamcity-reporter ' +
@@ -1573,6 +1560,7 @@ Mocha.prototype.reporter = function(reporter){
     if (!_reporter) throw new Error('invalid reporter "' + reporter + '"');
     this._reporter = _reporter;
   }
+  this.options.reporterOptions = reporterOptions;
   return this;
 };
 
@@ -1586,7 +1574,7 @@ Mocha.prototype.reporter = function(reporter){
 Mocha.prototype.ui = function(name){
   name = name || 'bdd';
   this._ui = exports.interfaces[name];
-  if (!this._ui) try { this._ui = require(name); } catch (err) {};
+  if (!this._ui) try { this._ui = require(name); } catch (err) {}
   if (!this._ui) throw new Error('invalid interface "' + name + '"');
   this._ui = this._ui(this.suite);
   return this;
@@ -1688,6 +1676,18 @@ Mocha.prototype.checkLeaks = function(){
 };
 
 /**
+ * Display long stack-trace on failing
+ *
+ * @return {Mocha}
+ * @api public
+ */
+
+Mocha.prototype.fullTrace = function() {
+  this.options.fullStackTrace = true;
+  return this;
+};
+
+/**
  * Enable growl support.
  *
  * @return {Mocha}
@@ -1721,9 +1721,9 @@ Mocha.prototype.globals = function(globals){
  */
 
 Mocha.prototype.useColors = function(colors){
-  this.options.useColors = arguments.length && colors != undefined
-    ? colors
-    : true;
+  if (colors !== undefined) {
+    this.options.useColors = colors;
+  }
   return this;
 };
 
@@ -1806,28 +1806,47 @@ Mocha.prototype.noHighlighting = function() {
 };
 
 /**
+ * Delay root suite execution.
+ * @returns {Mocha}
+ * @api public
+ */
+Mocha.prototype.delay = function delay() {
+  this.options.delay = true;
+  return this;
+};
+
+/**
  * Run tests and invoke `fn()` when complete.
  *
  * @param {Function} fn
  * @return {Runner}
  * @api public
  */
-
 Mocha.prototype.run = function(fn){
   if (this.files.length) this.loadFiles();
   var suite = this.suite;
   var options = this.options;
   options.files = this.files;
-  var runner = new exports.Runner(suite);
+  var runner = new exports.Runner(suite, options.delay);
   var reporter = new this._reporter(runner, options);
   runner.ignoreLeaks = false !== options.ignoreLeaks;
+  runner.fullStackTrace = options.fullStackTrace;
   runner.asyncOnly = options.asyncOnly;
   if (options.grep) runner.grep(options.grep, options.invert);
   if (options.globals) runner.globals(options.globals);
   if (options.growl) this._growl(runner, reporter);
-  exports.reporters.Base.useColors = options.useColors;
+  if (options.useColors !== undefined) {
+    exports.reporters.Base.useColors = options.useColors;
+  }
   exports.reporters.Base.inlineDiffs = options.useInlineDiffs;
-  return runner.run(fn);
+
+  function done(failures) {
+      if (reporter.done) {
+          reporter.done(failures, fn);
+      } else fn && fn(failures);
+  }
+
+  return runner.run(done);
 };
 
 }); // module: mocha.js
@@ -1945,8 +1964,27 @@ function plural(ms, n, name) {
 
 }); // module: ms.js
 
-require.register("reporters/base.js", function(module, exports, require){
+require.register("pending.js", function(module, exports, require){
+
+/**
+ * Expose `Pending`.
+ */
 
+module.exports = Pending;
+
+/**
+ * Initialize a new `Pending` error with the given message.
+ *
+ * @param {String} message
+ */
+
+function Pending(message) {
+    this.message = message;
+}
+
+}); // module: pending.js
+
+require.register("reporters/base.js", function(module, exports, require){
 /**
  * Module dependencies.
  */
@@ -1954,7 +1992,8 @@ require.register("reporters/base.js", function(module, exports, require){
 var tty = require('browser/tty')
   , diff = require('browser/diff')
   , ms = require('../ms')
-  , utils = require('../utils');
+  , utils = require('../utils')
+  , supportsColor = process.env ? require('supports-color') : null;
 
 /**
  * Save timer references to avoid Sinon interfering (see GH-237).
@@ -1979,10 +2018,12 @@ var isatty = tty.isatty(1) && tty.isatty(2);
 exports = module.exports = Base;
 
 /**
- * Enable coloring by default.
+ * Enable coloring by default, except in the browser interface.
  */
 
-exports.useColors = isatty || (process.env.MOCHA_COLORS !== undefined);
+exports.useColors = process.env
+  ? (supportsColor || (process.env.MOCHA_COLORS !== undefined))
+  : false;
 
 /**
  * Inline diffs instead of +/-
@@ -2046,7 +2087,7 @@ if ('win32' == process.platform) {
  */
 
 var color = exports.color = function(type, str) {
-  if (!exports.useColors) return str;
+  if (!exports.useColors) return String(str);
   return '\u001b[' + exports.colors[type] + 'm' + str + '\u001b[0m';
 };
 
@@ -2103,7 +2144,7 @@ exports.cursor = {
  */
 
 exports.list = function(failures){
-  console.error();
+  console.log();
   failures.forEach(function(test, i){
     // format
     var fmt = color('error title', '  %s) %s:\n')
@@ -2114,26 +2155,33 @@ exports.list = function(failures){
     var err = test.err
       , message = err.message || ''
       , stack = err.stack || message
-      , index = stack.indexOf(message) + message.length
-      , msg = stack.slice(0, index)
+      , index = stack.indexOf(message)
       , actual = err.actual
       , expected = err.expected
       , escape = true;
+    if (index === -1) {
+      msg = message;
+    } else {
+      index += message.length;
+      msg = stack.slice(0, index);
+      // remove msg from stack
+      stack = stack.slice(index + 1);
+    }
 
     // uncaught
     if (err.uncaught) {
       msg = 'Uncaught ' + msg;
     }
-
     // explicitly show diff
-    if (err.showDiff && sameType(actual, expected)) {
-      escape = false;
-      err.actual = actual = utils.stringify(actual);
-      err.expected = expected = utils.stringify(expected);
-    }
+    if (err.showDiff !== false && sameType(actual, expected)
+        && expected !== undefined) {
+
+      if ('string' !== typeof actual) {
+        escape = false;
+        err.actual = actual = utils.stringify(actual);
+        err.expected = expected = utils.stringify(expected);
+      }
 
-    // actual / expected diff
-    if (err.showDiff && 'string' == typeof actual && 'string' == typeof expected) {
       fmt = color('error title', '  %s) %s:\n%s') + color('error stack', '\n%s\n');
       var match = message.match(/^([^:]+): expected/);
       msg = '\n      ' + color('error message', match ? match[1] : msg);
@@ -2145,11 +2193,10 @@ exports.list = function(failures){
       }
     }
 
-    // indent stack trace without msg
-    stack = stack.slice(index ? index + 1 : index)
-      .replace(/^/gm, '  ');
+    // indent stack trace
+    stack = stack.replace(/^/gm, '  ');
 
-    console.error(fmt, (i + 1), test.fullTitle(), msg, stack);
+    console.log(fmt, (i + 1), test.fullTitle(), msg, stack);
   });
 };
 
@@ -2254,11 +2301,10 @@ Base.prototype.epilogue = function(){
   if (stats.failures) {
     fmt = color('fail', '  %d failing');
 
-    console.error(fmt,
-      stats.failures);
+    console.log(fmt, stats.failures);
 
     Base.list(this.failures);
-    console.error();
+    console.log();
   }
 
   console.log();
@@ -2336,7 +2382,7 @@ function unifiedDiff(err, escape) {
   function notBlank(line) {
     return line != null;
   }
-  msg = diff.createPatch('string', err.actual, err.expected);
+  var msg = diff.createPatch('string', err.actual, err.expected);
   var lines = msg.split('\n').splice(4);
   return '\n      '
          + colorLines('diff added',   '+ expected') + ' '
@@ -2409,7 +2455,6 @@ function sameType(a, b) {
 }); // module: reporters/base.js
 
 require.register("reporters/doc.js", function(module, exports, require){
-
 /**
  * Module dependencies.
  */
@@ -2476,7 +2521,6 @@ function Doc(runner) {
 }); // module: reporters/doc.js
 
 require.register("reporters/dot.js", function(module, exports, require){
-
 /**
  * Module dependencies.
  */
@@ -2506,7 +2550,7 @@ function Dot(runner) {
     , n = -1;
 
   runner.on('start', function(){
-    process.stdout.write('\n  ');
+    process.stdout.write('\n');
   });
 
   runner.on('pending', function(test){
@@ -2547,7 +2591,6 @@ Dot.prototype.constructor = Dot;
 }); // module: reporters/dot.js
 
 require.register("reporters/html-cov.js", function(module, exports, require){
-
 /**
  * Module dependencies.
  */
@@ -2598,10 +2641,10 @@ function coverageClass(n) {
   if (n >= 25) return 'low';
   return 'terrible';
 }
+
 }); // module: reporters/html-cov.js
 
 require.register("reporters/html.js", function(module, exports, require){
-
 /**
  * Module dependencies.
  */
@@ -2739,7 +2782,7 @@ function HTML(runner) {
     } else if (test.pending) {
       var el = fragment('<li class="test pass pending"><h2>%e</h2></li>', test.title);
     } else {
-      var el = fragment('<li class="test fail"><h2>%e <a href="?grep=%e" class="replay">‣</a></h2></li>', test.title, encodeURIComponent(test.fullTitle()));
+      var el = fragment('<li class="test fail"><h2>%e <a href="%e" class="replay">‣</a></h2></li>', test.title, self.testURL(test));
       var str = test.err.stack || test.err.toString();
 
       // FF / Opera do not add the message
@@ -2781,13 +2824,28 @@ function HTML(runner) {
 }
 
 /**
+ * Makes a URL, preserving querystring ("search") parameters.
+ * @param {string} s
+ * @returns {string} your new URL
+ */
+var makeUrl = function makeUrl(s) {
+  var search = window.location.search;
+
+  // Remove previous grep query parameter if present
+  if (search) {
+    search = search.replace(/[?&]grep=[^&\s]*/g, '').replace(/^&/, '?');
+  }
+
+  return window.location.pathname + (search ? search + '&' : '?' ) + 'grep=' + encodeURIComponent(s);
+};
+
+/**
  * Provide suite URL
  *
  * @param {Object} [suite]
  */
-
 HTML.prototype.suiteURL = function(suite){
-  return '?grep=' + encodeURIComponent(suite.fullTitle());
+  return makeUrl(suite.fullTitle());
 };
 
 /**
@@ -2797,7 +2855,7 @@ HTML.prototype.suiteURL = function(suite){
  */
 
 HTML.prototype.testURL = function(test){
-  return '?grep=' + encodeURIComponent(test.fullTitle());
+  return makeUrl(test.fullTitle());
 };
 
 /**
@@ -2878,7 +2936,6 @@ function on(el, event, fn) {
 }); // module: reporters/html.js
 
 require.register("reporters/index.js", function(module, exports, require){
-
 exports.Base = require('./base');
 exports.Dot = require('./dot');
 exports.Doc = require('./doc');
@@ -2900,7 +2957,6 @@ exports.JSONStream = require('./json-stream');
 }); // module: reporters/index.js
 
 require.register("reporters/json-cov.js", function(module, exports, require){
-
 /**
  * Module dependencies.
  */
@@ -3057,7 +3113,6 @@ function clean(test) {
 }); // module: reporters/json-cov.js
 
 require.register("reporters/json-stream.js", function(module, exports, require){
-
 /**
  * Module dependencies.
  */
@@ -3094,7 +3149,9 @@ function List(runner) {
   });
 
   runner.on('fail', function(test, err){
-    console.log(JSON.stringify(['fail', clean(test)]));
+    test = clean(test);
+    test.err = err.message;
+    console.log(JSON.stringify(['fail', test]));
   });
 
   runner.on('end', function(){
@@ -3118,10 +3175,10 @@ function clean(test) {
     , duration: test.duration
   }
 }
+
 }); // module: reporters/json-stream.js
 
 require.register("reporters/json.js", function(module, exports, require){
-
 /**
  * Module dependencies.
  */
@@ -3218,7 +3275,6 @@ function errorJSON(err) {
 }); // module: reporters/json.js
 
 require.register("reporters/landing.js", function(module, exports, require){
-
 /**
  * Module dependencies.
  */
@@ -3276,7 +3332,7 @@ function Landing(runner) {
   }
 
   runner.on('start', function(){
-    stream.write('\n  ');
+    stream.write('\n\n\n  ');
     cursor.hide();
   });
 
@@ -3293,7 +3349,7 @@ function Landing(runner) {
     }
 
     // render landing strip
-    stream.write('\u001b[4F\n\n');
+    stream.write('\u001b['+(width+1)+'D\u001b[2A');
     stream.write(runway());
     stream.write('\n  ');
     stream.write(color('runway', Array(col).join('⋅')));
@@ -3319,10 +3375,10 @@ F.prototype = Base.prototype;
 Landing.prototype = new F;
 Landing.prototype.constructor = Landing;
 
+
 }); // module: reporters/landing.js
 
 require.register("reporters/list.js", function(module, exports, require){
-
 /**
  * Module dependencies.
  */
@@ -3402,6 +3458,12 @@ var Base = require('./base')
   , utils = require('../utils');
 
 /**
+ * Constants
+ */
+
+var SUITE_PREFIX = '$';
+
+/**
  * Expose `Markdown`.
  */
 
@@ -3431,8 +3493,9 @@ function Markdown(runner) {
   }
 
   function mapTOC(suite, obj) {
-    var ret = obj;
-    obj = obj[suite.title] = obj[suite.title] || { suite: suite };
+    var ret = obj,
+        key = SUITE_PREFIX + suite.title;
+    obj = obj[key] = obj[key] || { suite: suite };
     suite.suites.forEach(function(suite){
       mapTOC(suite, obj);
     });
@@ -3445,11 +3508,13 @@ function Markdown(runner) {
     var link;
     for (var key in obj) {
       if ('suite' == key) continue;
-      if (key) link = ' - [' + key + '](#' + utils.slug(obj[key].suite.fullTitle()) + ')\n';
-      if (key) buf += Array(level).join('  ') + link;
+      if (key !== SUITE_PREFIX) {
+        link = ' - [' + key.substring(1) + ']';
+        link += '(#' + utils.slug(obj[key].suite.fullTitle()) + ')\n';
+        buf += Array(level).join('  ') + link;
+      }
       buf += stringifyTOC(obj[key], level);
     }
-    --level;
     return buf;
   }
 
@@ -3485,10 +3550,10 @@ function Markdown(runner) {
     process.stdout.write(buf);
   });
 }
+
 }); // module: reporters/markdown.js
 
 require.register("reporters/min.js", function(module, exports, require){
-
 /**
  * Module dependencies.
  */
@@ -3538,8 +3603,7 @@ require.register("reporters/nyan.js", function(module, exports, require){
  * Module dependencies.
  */
 
-var Base = require('./base')
-  , color = Base.color;
+var Base = require('./base');
 
 /**
  * Expose `Dot`.
@@ -3616,17 +3680,16 @@ NyanCat.prototype.draw = function(){
 
 NyanCat.prototype.drawScoreboard = function(){
   var stats = this.stats;
-  var colors = Base.colors;
 
-  function draw(color, n) {
+  function draw(type, n) {
     write(' ');
-    write('\u001b[' + color + 'm' + n + '\u001b[0m');
+    write(Base.color(type, n));
     write('\n');
   }
 
-  draw(colors.green, stats.passes);
-  draw(colors.fail, stats.failures);
-  draw(colors.pending, stats.pending);
+  draw('green', stats.passes);
+  draw('fail', stats.failures);
+  draw('pending', stats.pending);
   write('\n');
 
   this.cursorUp(this.numberOfLines);
@@ -3676,26 +3739,26 @@ NyanCat.prototype.drawRainbow = function(){
 NyanCat.prototype.drawNyanCat = function() {
   var self = this;
   var startWidth = this.scoreboardWidth + this.trajectories[0].length;
-  var color = '\u001b[' + startWidth + 'C';
+  var dist = '\u001b[' + startWidth + 'C';
   var padding = '';
 
-  write(color);
+  write(dist);
   write('_,------,');
   write('\n');
 
-  write(color);
+  write(dist);
   padding = self.tick ? '  ' : '   ';
   write('_|' + padding + '/\\_/\\ ');
   write('\n');
 
-  write(color);
+  write(dist);
   padding = self.tick ? '_' : '__';
   var tail = self.tick ? '~' : '^';
   var face;
   write(tail + '|' + padding + this.face() + ' ');
   write('\n');
 
-  write(color);
+  write(dist);
   padding = self.tick ? ' ' : '  ';
   write(padding + '""  "" ');
   write('\n');
@@ -3776,6 +3839,8 @@ NyanCat.prototype.generateColors = function(){
  */
 
 NyanCat.prototype.rainbowify = function(str){
+  if (!Base.useColors)
+    return str;
   var color = this.rainbowColors[this.colorIndex % this.rainbowColors.length];
   this.colorIndex += 1;
   return '\u001b[38;5;' + color + 'm' + str + '\u001b[0m';
@@ -3902,7 +3967,6 @@ Progress.prototype.constructor = Progress;
 }); // module: reporters/progress.js
 
 require.register("reporters/spec.js", function(module, exports, require){
-
 /**
  * Module dependencies.
  */
@@ -3959,14 +4023,14 @@ function Spec(runner) {
     if ('fast' == test.speed) {
       var fmt = indent()
         + color('checkmark', '  ' + Base.symbols.ok)
-        + color('pass', ' %s ');
+        + color('pass', ' %s');
       cursor.CR();
       console.log(fmt, test.title);
     } else {
       var fmt = indent()
         + color('checkmark', '  ' + Base.symbols.ok)
-        + color('pass', ' %s ')
-        + color(test.speed, '(%dms)');
+        + color('pass', ' %s')
+        + color(test.speed, ' (%dms)');
       cursor.CR();
       console.log(fmt, test.title, test.duration);
     }
@@ -3993,7 +4057,6 @@ Spec.prototype.constructor = Spec;
 }); // module: reporters/spec.js
 
 require.register("reporters/tap.js", function(module, exports, require){
-
 /**
  * Module dependencies.
  */
@@ -4070,13 +4133,13 @@ function title(test) {
 }); // module: reporters/tap.js
 
 require.register("reporters/xunit.js", function(module, exports, require){
-
 /**
  * Module dependencies.
  */
 
 var Base = require('./base')
   , utils = require('../utils')
+  , fs = require('browser/fs')
   , escape = utils.escape;
 
 /**
@@ -4102,12 +4165,19 @@ exports = module.exports = XUnit;
  * @api public
  */
 
-function XUnit(runner) {
+function XUnit(runner, options) {
   Base.call(this, runner);
   var stats = this.stats
     , tests = []
     , self = this;
 
+  if (options.reporterOptions && options.reporterOptions.output) {
+      if (! fs.createWriteStream) {
+          throw new Error('file output not supported in browser');
+      }
+      self.fileStream = fs.createWriteStream(options.reporterOptions.output);
+  }
+
   runner.on('pending', function(test){
     tests.push(test);
   });
@@ -4121,7 +4191,7 @@ function XUnit(runner) {
   });
 
   runner.on('end', function(){
-    console.log(tag('testsuite', {
+    self.write(tag('testsuite', {
         name: 'Mocha Tests'
       , tests: stats.tests
       , failures: stats.failures
@@ -4131,12 +4201,25 @@ function XUnit(runner) {
       , time: (stats.duration / 1000) || 0
     }, false));
 
-    tests.forEach(test);
-    console.log('</testsuite>');
+    tests.forEach(function(t) { self.test(t); });
+    self.write('</testsuite>');
   });
 }
 
 /**
+ * Override done to close the stream (if it's a file).
+ */
+XUnit.prototype.done = function(failures, fn) {
+    if (this.fileStream) {
+        this.fileStream.end(function() {
+            fn(failures);
+        });
+    } else {
+        fn(failures);
+    }
+};
+
+/**
  * Inherit from `Base.prototype`.
  */
 
@@ -4147,10 +4230,21 @@ XUnit.prototype.constructor = XUnit;
 
 
 /**
+ * Write out the given line
+ */
+XUnit.prototype.write = function(line) {
+    if (this.fileStream) {
+        this.fileStream.write(line + '\n');
+    } else {
+        console.log(line);
+    }
+};
+
+/**
  * Output tag for the given `test.`
  */
 
-function test(test) {
+XUnit.prototype.test = function(test, ostream) {
   var attrs = {
       classname: test.parent.fullTitle()
     , name: test.title
@@ -4159,13 +4253,13 @@ function test(test) {
 
   if ('failed' == test.state) {
     var err = test.err;
-    console.log(tag('testcase', attrs, false, tag('failure', {}, false, cdata(escape(err.message) + "\n" + err.stack))));
+    this.write(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)));
+    this.write(tag('testcase', attrs, false, tag('skipped', {}, true)));
   } else {
-    console.log(tag('testcase', attrs, true) );
+    this.write(tag('testcase', attrs, true) );
   }
-}
+};
 
 /**
  * HTML tag helper.
@@ -4196,14 +4290,15 @@ function cdata(str) {
 }); // module: reporters/xunit.js
 
 require.register("runnable.js", function(module, exports, require){
-
 /**
  * Module dependencies.
  */
 
 var EventEmitter = require('browser/events').EventEmitter
   , debug = require('browser/debug')('mocha:runnable')
-  , milliseconds = require('./ms');
+  , Pending = require('./pending')
+  , milliseconds = require('./ms')
+  , utils = require('./utils');
 
 /**
  * Save timer references to avoid Sinon interfering (see GH-237).
@@ -4244,6 +4339,7 @@ function Runnable(title, fn) {
   this._slow = 75;
   this._enableTimeouts = true;
   this.timedOut = false;
+  this._trace = new Error('done() called multiple times')
 }
 
 /**
@@ -4306,6 +4402,16 @@ Runnable.prototype.enableTimeouts = function(enabled){
 };
 
 /**
+ * Halt and mark as pending.
+ *
+ * @api private
+ */
+
+Runnable.prototype.skip = function(){
+    throw new Pending();
+};
+
+/**
  * Return the full title generated by recursively
  * concatenating the parent's full title.
  *
@@ -4357,7 +4463,7 @@ Runnable.prototype.resetTimeout = function(){
   this.clearTimeout();
   this.timer = setTimeout(function(){
     if (!self._enableTimeouts) return;
-    self.callback(new Error('timeout of ' + ms + 'ms exceeded'));
+    self.callback(new Error('timeout of ' + ms + 'ms exceeded. Ensure the done() callback is being called in this test.'));
     self.timedOut = true;
   }, ms);
 };
@@ -4393,18 +4499,22 @@ Runnable.prototype.run = function(fn){
   function multiple(err) {
     if (emitted) return;
     emitted = true;
-    self.emit('error', err || new Error('done() called multiple times'));
+    self.emit('error', err || new Error('done() called multiple times; stacktrace may be inaccurate'));
   }
 
   // finished
   function done(err) {
     var ms = self.timeout();
     if (self.timedOut) return;
-    if (finished) return multiple(err);
+    if (finished) return multiple(err || self._trace);
+
+    // Discard the resolution if this test has already failed asynchronously
+    if (self.state) return;
+
     self.clearTimeout();
     self.duration = new Date - start;
     finished = true;
-    if (!err && self.duration > ms && self._enableTimeouts) err = new Error('timeout of ' + ms + 'ms exceeded');
+    if (!err && self.duration > ms && self._enableTimeouts) err = new Error('timeout of ' + ms + 'ms exceeded. Ensure the done() callback is being called in this test.');
     fn(err);
   }
 
@@ -4428,7 +4538,7 @@ Runnable.prototype.run = function(fn){
         done();
       });
     } catch (err) {
-      done(err);
+      done(utils.getError(err));
     }
     return;
   }
@@ -4445,7 +4555,7 @@ Runnable.prototype.run = function(fn){
       callFn(this.fn);
     }
   } catch (err) {
-    done(err);
+    done(utils.getError(err));
   }
 
   function callFn(fn) {
@@ -4474,10 +4584,14 @@ require.register("runner.js", function(module, exports, require){
 
 var EventEmitter = require('browser/events').EventEmitter
   , debug = require('browser/debug')('mocha:runner')
+  , Pending = require('./pending')
   , Test = require('./test')
   , utils = require('./utils')
   , filter = utils.filter
-  , keys = utils.keys;
+  , keys = utils.keys
+  , type = utils.type
+  , stringify = utils.stringify
+  , stackFilter = utils.stackTraceFilter();
 
 /**
  * Non-enumerable globals.
@@ -4489,7 +4603,9 @@ var globals = [
   'setInterval',
   'clearInterval',
   'XMLHttpRequest',
-  'Date'
+  'Date',
+  'setImmediate',
+  'clearImmediate'
 ];
 
 /**
@@ -4515,13 +4631,17 @@ module.exports = Runner;
  *   - `fail`  (test, err) test failed
  *   - `pending`  (test) test pending
  *
+ * @param {Suite} suite Root suite
+ * @param {boolean} [delay] Whether or not to delay execution of root suite
+ *   until ready.
  * @api public
  */
 
-function Runner(suite) {
+function Runner(suite, delay) {
   var self = this;
   this._globals = [];
   this._abort = false;
+  this._delay = delay;
   this.suite = suite;
   this.total = suite.total();
   this.failures = 0;
@@ -4662,14 +4782,18 @@ Runner.prototype.checkGlobals = function(test){
  * @api private
  */
 
-Runner.prototype.fail = function(test, err){
+Runner.prototype.fail = function(test, err) {
   ++this.failures;
   test.state = 'failed';
 
-  if ('string' == typeof err) {
-    err = new Error('the string "' + err + '" was thrown, throw an Error :)');
+  if (!(err instanceof Error)) {
+    err = new Error('the ' + type(err) + ' ' + stringify(err) + ' was thrown, throw an Error :)');
   }
 
+  err.stack = (this.fullStackTrace || !err.stack)
+    ? err.stack
+    : stackFilter(err.stack);
+
   this.emit('fail', test, err);
 };
 
@@ -4718,7 +4842,6 @@ Runner.prototype.hook = function(name, fn){
   function next(i) {
     var hook = hooks[i];
     if (!hook) return fn();
-    if (self.failures && suite.bail()) return fn();
     self.currentRunnable = hook;
 
     hook.ctx.currentTest = self.test;
@@ -4734,10 +4857,14 @@ Runner.prototype.hook = function(name, fn){
       var testError = hook.error();
       if (testError) self.fail(self.test, testError);
       if (err) {
-        self.failHook(hook, err);
+        if (err instanceof Pending) {
+          suite.pending = true;
+        } else {
+          self.failHook(hook, err);
 
-        // stop executing hooks, notify callee of hook err
-        return fn(err);
+          // stop executing hooks, notify callee of hook err
+          return fn(err);
+        }
       }
       self.emit('hook end', hook);
       delete hook.ctx.currentTest;
@@ -4919,6 +5046,11 @@ Runner.prototype.runTests = function(suite, fn){
     self.emit('test', self.test = test);
     self.hookDown('beforeEach', function(err, errSuite){
 
+      if (suite.pending) {
+        self.emit('pending', test);
+        self.emit('test end', test);
+        return next();
+      }
       if (err) return hookErr(err, errSuite, false);
 
       self.currentRunnable = self.test;
@@ -4926,8 +5058,17 @@ Runner.prototype.runTests = function(suite, fn){
         test = self.test;
 
         if (err) {
-          self.fail(test, err);
+          if (err instanceof Pending) {
+            self.emit('pending', test);
+          } else {
+            self.fail(test, err);
+          }
           self.emit('test end', test);
+
+          if (err instanceof Pending) {
+            return next();
+          }
+
           return self.hookUp('afterEach', next);
         }
 
@@ -5012,19 +5153,18 @@ Runner.prototype.uncaught = function(err){
     }.call(err) ? err : ( err.message || err ));
   } else {
     debug('uncaught undefined exception');
-    err = new Error('Caught undefined error, did you throw without specifying what?');
+    err = utils.undefinedError();
   }
   err.uncaught = true;
 
   var runnable = this.currentRunnable;
   if (!runnable) return;
 
-  var wasAlreadyDone = runnable.state;
-  this.fail(runnable, err);
-
   runnable.clearTimeout();
 
-  if (wasAlreadyDone) return;
+  // Ignore errors if complete
+  if (runnable.state) return;
+  this.fail(runnable, err);
 
   // recover from test
   if ('test' == runnable.type) {
@@ -5047,13 +5187,23 @@ Runner.prototype.uncaught = function(err){
  */
 
 Runner.prototype.run = function(fn){
-  var self = this
-    , fn = fn || function(){};
+  var self = this,
+    rootSuite = this.suite;
+
+  fn = fn || function(){};
 
   function uncaught(err){
     self.uncaught(err);
   }
 
+  function start() {
+    self.emit('start');
+    self.runSuite(rootSuite, function(){
+      debug('finished running');
+      self.emit('end');
+    });
+  }
+
   debug('start');
 
   // callback
@@ -5063,16 +5213,19 @@ Runner.prototype.run = function(fn){
     fn(self.failures);
   });
 
-  // run suites
-  this.emit('start');
-  this.runSuite(this.suite, function(){
-    debug('finished running');
-    self.emit('end');
-  });
-
   // uncaught exception
   process.on('uncaughtException', uncaught);
 
+  if (this._delay) {
+    // for reporters, I guess.
+    // might be nice to debounce some dots while we wait.
+    this.emit('waiting', rootSuite);
+    rootSuite.once('run', start);
+  }
+  else {
+    start();
+  }
+
   return this;
 };
 
@@ -5128,28 +5281,27 @@ function filterLeaks(ok, globals) {
  * @api private
  */
 
- function extraGlobals() {
-  if (typeof(process) === 'object' &&
-      typeof(process.version) === 'string') {
+function extraGlobals() {
+ if (typeof(process) === 'object' &&
+     typeof(process.version) === 'string') {
 
-    var nodeVersion = process.version.split('.').reduce(function(a, v) {
-      return a << 8 | v;
-    });
+   var nodeVersion = process.version.split('.').reduce(function(a, v) {
+     return a << 8 | v;
+   });
 
-    // 'errno' was renamed to process._errno in v0.9.11.
+   // 'errno' was renamed to process._errno in v0.9.11.
 
-    if (nodeVersion < 0x00090B) {
-      return ['errno'];
-    }
-  }
-
-  return [];
+   if (nodeVersion < 0x00090B) {
+     return ['errno'];
+   }
  }
 
+ return [];
+}
+
 }); // module: runner.js
 
 require.register("suite.js", function(module, exports, require){
-
 /**
  * Module dependencies.
  */
@@ -5214,6 +5366,7 @@ function Suite(title, parentContext) {
   this._enableTimeouts = true;
   this._slow = 75;
   this._bail = false;
+  this.delayed = false;
 }
 
 /**
@@ -5254,7 +5407,7 @@ Suite.prototype.clone = function(){
 
 Suite.prototype.timeout = function(ms){
   if (0 == arguments.length) return this._timeout;
-  if (ms === 0) this._enableTimeouts = false;
+  if (ms.toString() === '0') this._enableTimeouts = false;
   if ('string' == typeof ms) ms = milliseconds(ms);
   debug('timeout %d', ms);
   this._timeout = parseInt(ms, 10);
@@ -5295,7 +5448,7 @@ Suite.prototype.slow = function(ms){
 /**
  * Sets whether to bail after first error.
  *
- * @parma {Boolean} bail
+ * @param {Boolean} bail
  * @return {Suite|Number} for chaining
  * @api private
  */
@@ -5500,10 +5653,18 @@ Suite.prototype.eachTest = function(fn){
   return this;
 };
 
+/**
+ * This will run the root suite if we happen to be running in delayed mode.
+ */
+Suite.prototype.run = function run() {
+  if (this.root) {
+    this.emit('run');
+  }
+};
+
 }); // module: suite.js
 
 require.register("test.js", function(module, exports, require){
-
 /**
  * Module dependencies.
  */
@@ -5603,7 +5764,7 @@ exports.forEach = function(arr, fn, scope){
 exports.map = function(arr, fn, scope){
   var result = [];
   for (var i = 0, l = arr.length; i < l; i++)
-    result.push(fn.call(scope, arr[i], i));
+    result.push(fn.call(scope, arr[i], i, arr));
   return result;
 };
 
@@ -5672,7 +5833,7 @@ exports.filter = function(arr, fn){
 
 exports.keys = Object.keys || function(obj) {
   var keys = []
-    , has = Object.prototype.hasOwnProperty // for `window` on <=IE8
+    , has = Object.prototype.hasOwnProperty; // for `window` on <=IE8
 
   for (var key in obj) {
     if (has.call(obj, key)) {
@@ -5703,6 +5864,28 @@ exports.watch = function(files, fn){
 };
 
 /**
+ * Array.isArray (<=IE8)
+ *
+ * @param {Object} obj
+ * @return {Boolean}
+ * @api private
+ */
+var isArray = Array.isArray || function (obj) {
+  return '[object Array]' == {}.toString.call(obj);
+};
+
+/**
+ * @description
+ * Buffer.prototype.toJSON polyfill
+ * @type {Function}
+ */
+if(typeof Buffer !== 'undefined' && Buffer.prototype) {
+  Buffer.prototype.toJSON = Buffer.prototype.toJSON || function () {
+    return Array.prototype.slice.call(this, 0);
+  };
+}
+
+/**
  * Ignored files.
  */
 
@@ -5724,15 +5907,15 @@ exports.files = function(dir, ext, ret){
   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, ext, ret);
-    } else if (path.match(re)) {
-      ret.push(path);
-    }
-  });
+    .filter(ignored)
+    .forEach(function(path){
+      path = join(dir, path);
+      if (fs.statSync(path).isDirectory()) {
+        exports.files(path, ext, ret);
+      } else if (path.match(re)) {
+        ret.push(path);
+      }
+    });
 
   return ret;
 };
@@ -5760,7 +5943,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 *\(.*\)\s*{|\(.*\) *=> *{?/, '')
     .replace(/\s+\}$/, '');
 
   var spaces = str.match(/^\n?( *)/)[1].length
@@ -5837,54 +6020,253 @@ exports.highlightTags = function(name) {
   }
 };
 
+/**
+ * If a value could have properties, and has none, this function is called, which returns
+ * a string representation of the empty value.
+ *
+ * Functions w/ no properties return `'[Function]'`
+ * Arrays w/ length === 0 return `'[]'`
+ * Objects w/ no properties return `'{}'`
+ * All else: return result of `value.toString()`
+ *
+ * @param {*} value Value to inspect
+ * @param {string} [type] The type of the value, if known.
+ * @returns {string}
+ */
+var emptyRepresentation = function emptyRepresentation(value, type) {
+  type = type || exports.type(value);
+
+  switch(type) {
+    case 'function':
+      return '[Function]';
+    case 'object':
+      return '{}';
+    case 'array':
+      return '[]';
+    default:
+      return value.toString();
+  }
+};
+
+/**
+ * Takes some variable and asks `{}.toString()` what it thinks it is.
+ * @param {*} value Anything
+ * @example
+ * type({}) // 'object'
+ * type([]) // 'array'
+ * type(1) // 'number'
+ * type(false) // 'boolean'
+ * type(Infinity) // 'number'
+ * type(null) // 'null'
+ * type(new Date()) // 'date'
+ * type(/foo/) // 'regexp'
+ * type('type') // 'string'
+ * type(global) // 'global'
+ * @api private
+ * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/toString
+ * @returns {string}
+ */
+exports.type = function type(value) {
+  if (typeof Buffer !== 'undefined' && Buffer.isBuffer(value)) {
+    return 'buffer';
+  }
+  return Object.prototype.toString.call(value)
+    .replace(/^\[.+\s(.+?)\]$/, '$1')
+    .toLowerCase();
+};
 
 /**
- * Stringify `obj`.
+ * @summary Stringify `value`.
+ * @description Different behavior depending on type of value.
+ * - If `value` is undefined or null, return `'[undefined]'` or `'[null]'`, respectively.
+ * - If `value` is not an object, function or array, return result of `value.toString()` wrapped in double-quotes.
+ * - If `value` is an *empty* object, function, or array, return result of function
+ *   {@link emptyRepresentation}.
+ * - If `value` has properties, call {@link exports.canonicalize} on it, then return result of
+ *   JSON.stringify().
  *
- * @param {Object} obj
- * @return {String}
+ * @see exports.type
+ * @param {*} value
+ * @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');
+exports.stringify = function(value) {
+  var type = exports.type(value);
+
+  if (!~exports.indexOf(['object', 'array', 'function'], type)) {
+    if(type != 'buffer') {
+      return jsonStringify(value);
+    }
+    var json = value.toJSON();
+    // Based on the toJSON result
+    return jsonStringify(json.data && json.type ? json.data : json, 2)
+      .replace(/,(\n|$)/g, '$1');
+  }
+
+  for (var prop in value) {
+    if (Object.prototype.hasOwnProperty.call(value, prop)) {
+      return jsonStringify(exports.canonicalize(value), 2).replace(/,(\n|$)/g, '$1');
+    }
+  }
+
+  return emptyRepresentation(value, type);
+};
+
+/**
+ * @description
+ * like JSON.stringify but more sense.
+ * @param {Object}  object
+ * @param {Number=} spaces
+ * @param {number=} depth
+ * @returns {*}
+ * @private
+ */
+function jsonStringify(object, spaces, depth) {
+  if(typeof spaces == 'undefined') return _stringify(object);  // primitive types
+
+  depth = depth || 1;
+  var space = spaces * depth
+    , str = isArray(object) ? '[' : '{'
+    , end = isArray(object) ? ']' : '}'
+    , length = object.length || exports.keys(object).length
+    , repeat = function(s, n) { return new Array(n).join(s); }; // `.repeat()` polyfill
+
+  function _stringify(val) {
+    switch (exports.type(val)) {
+      case 'null':
+      case 'undefined':
+        val = '[' + val + ']';
+        break;
+      case 'array':
+      case 'object':
+        val = jsonStringify(val, spaces, depth + 1);
+        break;
+      case 'boolean':
+      case 'regexp':
+      case 'number':
+        val = val === 0 && (1/val) === -Infinity // `-0`
+          ? '-0'
+          : val.toString();
+        break;
+      case 'date':
+        val = '[Date: ' + val.toISOString() + ']';
+        break;
+      case 'buffer':
+        var json = val.toJSON();
+        // Based on the toJSON result
+        json = json.data && json.type ? json.data : json;
+        val = '[Buffer: ' + jsonStringify(json, 2, depth + 1) + ']';
+        break;
+      default:
+        val = (val == '[Function]' || val == '[Circular]')
+          ? val
+          : '"' + val + '"'; //string
+    }
+    return val;
+  }
+
+  for(var i in object) {
+    if(!object.hasOwnProperty(i)) continue;        // not my business
+    --length;
+    str += '\n ' + repeat(' ', space)
+      + (isArray(object) ? '' : '"' + i + '": ') // key
+      +  _stringify(object[i])                   // value
+      + (length ? ',' : '');                     // comma
+  }
+
+  return str + (str.length != 1                    // [], {}
+    ? '\n' + repeat(' ', --space) + end
+    : end);
+}
+
+/**
+ * Return if obj is a Buffer
+ * @param {Object} arg
+ * @return {Boolean}
+ * @api private
+ */
+exports.isBuffer = function (arg) {
+  return typeof Buffer !== 'undefined' && Buffer.isBuffer(arg);
 };
 
 /**
- * Return a new object that has the keys in sorted order.
- * @param {Object} obj
- * @param {Array} [stack]
- * @return {Object}
+ * @summary Return a new Thing that has the keys in sorted order.  Recursive.
+ * @description If the Thing...
+ * - has already been seen, return string `'[Circular]'`
+ * - is `undefined`, return string `'[undefined]'`
+ * - is `null`, return value `null`
+ * - is some other primitive, return the value
+ * - is not a primitive or an `Array`, `Object`, or `Function`, return the value of the Thing's `toString()` method
+ * - is a non-empty `Array`, `Object`, or `Function`, return the result of calling this function again.
+ * - is an empty `Array`, `Object`, or `Function`, return the result of calling `emptyRepresentation()`
+ *
+ * @param {*} value Thing to inspect.  May or may not have properties.
+ * @param {Array} [stack=[]] Stack of seen values
+ * @return {(Object|Array|Function|string|undefined)}
+ * @see {@link exports.stringify}
  * @api private
  */
 
-exports.canonicalize = function(obj, stack) {
-  stack = stack || [];
+exports.canonicalize = function(value, stack) {
+  var canonicalizedObj,
+    type = exports.type(value),
+    prop,
+    withStack = function withStack(value, fn) {
+      stack.push(value);
+      fn();
+      stack.pop();
+    };
 
-  if (exports.indexOf(stack, obj) !== -1) return '[Circular]';
+  stack = stack || [];
 
-  var canonicalizedObj;
+  if (exports.indexOf(stack, value) !== -1) {
+    return '[Circular]';
+  }
 
-  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;
+  switch(type) {
+    case 'undefined':
+    case 'buffer':
+    case 'null':
+      canonicalizedObj = value;
+      break;
+    case 'array':
+      withStack(value, function () {
+        canonicalizedObj = exports.map(value, function (item) {
+          return exports.canonicalize(item, stack);
+        });
+      });
+      break;
+    case 'function':
+      for (prop in value) {
+        canonicalizedObj = {};
+        break;
+      }
+      if (!canonicalizedObj) {
+        canonicalizedObj = emptyRepresentation(value, type);
+        break;
+      }
+    /* falls through */
+    case 'object':
+      canonicalizedObj = canonicalizedObj || {};
+      withStack(value, function () {
+        exports.forEach(exports.keys(value).sort(), function (key) {
+          canonicalizedObj[key] = exports.canonicalize(value[key], stack);
+        });
+      });
+      break;
+    case 'date':
+    case 'number':
+    case 'regexp':
+    case 'boolean':
+      canonicalizedObj = value;
+      break;
+    default:
+      canonicalizedObj = value.toString();
   }
 
   return canonicalizedObj;
- };
+};
 
 /**
  * Lookup file names at the given `path`.
@@ -5911,7 +6293,7 @@ exports.lookupFiles = function lookupFiles(path, extensions, recursive) {
     return;
   }
 
-  fs.readdirSync(path).forEach(function(file){
+  fs.readdirSync(path).forEach(function(file) {
     file = join(path, file);
     try {
       var stat = fs.statSync(file);
@@ -5932,6 +6314,92 @@ exports.lookupFiles = function lookupFiles(path, extensions, recursive) {
   return files;
 };
 
+/**
+ * Generate an undefined error with a message warning the user.
+ *
+ * @return {Error}
+ */
+
+exports.undefinedError = function() {
+  return new Error('Caught undefined error, did you throw without specifying what?');
+};
+
+/**
+ * Generate an undefined error if `err` is not defined.
+ *
+ * @param {Error} err
+ * @return {Error}
+ */
+
+exports.getError = function(err) {
+  return err || exports.undefinedError();
+};
+
+
+/**
+ * @summary
+ * This Filter based on `mocha-clean` module.(see: `github.com/rstacruz/mocha-clean`)
+ * @description
+ * When invoking this function you get a filter function that get the Error.stack as an input,
+ * and return a prettify output.
+ * (i.e: strip Mocha, node_modules, bower and componentJS from stack trace).
+ * @returns {Function}
+ */
+
+exports.stackTraceFilter = function() {
+  var slash = '/'
+    , is = typeof document === 'undefined'
+      ? { node: true }
+      : { browser: true }
+    , cwd = is.node
+      ? process.cwd() + slash
+      : location.href.replace(/\/[^\/]*$/, '/');
+
+  function isNodeModule (line) {
+    return (~line.indexOf('node_modules'));
+  }
+
+  function isMochaInternal (line) {
+    return (~line.indexOf('node_modules' + slash + 'mocha'))  ||
+      (~line.indexOf('components' + slash + 'mochajs'))       ||
+      (~line.indexOf('components' + slash + 'mocha'));
+  }
+
+  // node_modules, bower, componentJS
+  function isBrowserModule(line) {
+    return (~line.indexOf('node_modules')) ||
+      (~line.indexOf('components'));
+  }
+
+  function isNodeInternal (line) {
+    return (~line.indexOf('(timers.js:')) ||
+      (~line.indexOf('(events.js:'))      ||
+      (~line.indexOf('(node.js:'))        ||
+      (~line.indexOf('(module.js:'))      ||
+      (~line.indexOf('GeneratorFunctionPrototype.next (native)')) ||
+      false
+  }
+
+  return function(stack) {
+    stack = stack.split('\n');
+
+    stack = exports.reduce(stack, function(list, line) {
+      if (is.node && (isNodeModule(line) ||
+        isMochaInternal(line) ||
+        isNodeInternal(line)))
+        return list;
+
+      if (is.browser && (isBrowserModule(line)))
+        return list;
+
+      // Clean up cwd(absolute)
+      list.push(line.replace(cwd, ''));
+      return list;
+    }, []);
+
+    return stack.join('\n');
+  }
+};
 }); // module: utils.js
 // The global object is "self" in Web Workers.
 var global = (function() { return this; })();
@@ -6074,7 +6542,8 @@ mocha.run = function(fn){
   mocha.globals('location');
 
   var query = Mocha.utils.parseQuery(global.location.search || '');
-  if (query.grep) mocha.grep(query.grep);
+  if (query.grep) mocha.grep(new RegExp(query.grep));
+  if (query.fgrep) mocha.grep(query.fgrep);
   if (query.invert) mocha.invert();
 
   return Mocha.prototype.run.call(mocha, function(err){
@@ -6092,4 +6561,4 @@ mocha.run = function(fn){
  */
 
 Mocha.process = process;
-})();
\ No newline at end of file
+})();
diff --git a/package.json b/package.json
index ce702d2..28e16ff 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "mocha",
-  "version": "1.21.5",
+  "version": "2.2.5",
   "description": "simple, flexible, fun test framework",
   "keywords": [
     "mocha",
@@ -12,17 +12,24 @@
   "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>"
+    "Daniel St. Jules <danielst.jules at gmail.com>",
+    "David da Silva Contín <dasilvacontin at gmail.com>",
+    "Ariel Mashraki <ariel at mashraki.co.il>",
+    "Pawel Kozlowski <pkozlowski.opensource at gmail.com>"
   ],
+  "license": "MIT",
   "repository": {
     "type": "git",
-    "url": "git://github.com/visionmedia/mocha.git"
+    "url": "git://github.com/mochajs/mocha.git"
   },
+  "maintainers": [
+    "travisjeffery <tj at travisjeffery.com>",
+    "jbnicolai <joshua at jbna.nl>",
+    "boneskull <chiller at badwing.com>"
+  ],
   "main": "./index",
   "browser": "./mocha.js",
   "bin": {
@@ -30,7 +37,7 @@
     "_mocha": "./bin/_mocha"
   },
   "engines": {
-    "node": ">= 0.4.x"
+    "node": ">= 0.8.x"
   },
   "scripts": {
     "test": "make test-all"
@@ -38,12 +45,13 @@
   "dependencies": {
     "commander": "2.3.0",
     "debug": "2.0.0",
-    "diff": "1.0.8",
+    "diff": "1.4.0",
     "escape-string-regexp": "1.0.2",
     "glob": "3.2.3",
     "growl": "1.8.1",
     "jade": "0.26.3",
-    "mkdirp": "0.5.0"
+    "mkdirp": "0.5.0",
+    "supports-color": "~1.2.0"
   },
   "devDependencies": {
     "coffee-script": "~1.8.0",
@@ -61,7 +69,7 @@
   "licenses": [
     {
       "type": "MIT",
-      "url": "https://raw.github.com/visionmedia/mocha/master/LICENSE"
+      "url": "https://raw.github.com/mochajs/mocha/master/LICENSE"
     }
   ]
-}
+}
\ No newline at end of file
diff --git a/support/compile.js b/support/compile.js
index a0a94d2..bea017a 100644
--- a/support/compile.js
+++ b/support/compile.js
@@ -1,4 +1,3 @@
-
 /**
  * Module dependencies.
  */
diff --git a/support/foot.js b/support/foot.js
index 158693a..0319a0f 100644
--- a/support/foot.js
+++ b/support/foot.js
@@ -1 +1 @@
-})();
\ No newline at end of file
+})();
diff --git a/support/tail.js b/support/tail.js
index e9c0c0d..030177a 100644
--- a/support/tail.js
+++ b/support/tail.js
@@ -139,7 +139,8 @@ mocha.run = function(fn){
   mocha.globals('location');
 
   var query = Mocha.utils.parseQuery(global.location.search || '');
-  if (query.grep) mocha.grep(query.grep);
+  if (query.grep) mocha.grep(new RegExp(query.grep));
+  if (query.fgrep) mocha.grep(query.fgrep);
   if (query.invert) mocha.invert();
 
   return Mocha.prototype.run.call(mocha, function(err){
diff --git a/test.js b/test.js
new file mode 100644
index 0000000..47de23e
--- /dev/null
+++ b/test.js
@@ -0,0 +1,9 @@
+function usedToBeAsync (cb) {
+  cb()
+}
+
+it('test', function() {
+  this.timeout(4294967296);
+  usedToBeAsync(done)
+});
+
diff --git a/test/acceptance/context.js b/test/acceptance/context.js
index add011b..47c2db7 100644
--- a/test/acceptance/context.js
+++ b/test/acceptance/context.js
@@ -1,4 +1,3 @@
-
 describe('Context', function(){
   beforeEach(function(){
     this.calls = ['before'];
diff --git a/test/acceptance/diffs.js b/test/acceptance/diffs.js
deleted file mode 100644
index b0b6eae..0000000
--- a/test/acceptance/diffs.js
+++ /dev/null
@@ -1,87 +0,0 @@
-
-var fs = require('fs')
-  , cssin = fs.readFileSync('test/acceptance/fixtures/css.in', 'ascii')
-  , cssout = fs.readFileSync('test/acceptance/fixtures/css.out', 'ascii');
-
-describe('diffs', function(){
-  // uncomment the assertions, and run with different params to check the output
-  // ex: --color, --no-color, --unified-diff
-
-  it('should display a diff for small strings', function(){
-    var expected = 'foo bar baz'
-      , actual = 'foo rar baz';
-
-    // expected.should.eql(actual);
-  });
-
-  it('should display a diff of canonicalized objects', function(){
-    var actual = { name: 'travis j', age: 23 }
-      , expected = { age: 23, name: 'travis' };
-
-      // actual.should.eql(expected);
-  });
-
-  it('should display a diff for medium strings', function(){
-    var expected = 'foo bar baz\nfoo bar baz\nfoo bar baz'
-      , actual = 'foo bar baz\nfoo rar baz\nfoo bar raz';
-
-    // expected.should.eql(actual);
-  });
-
-  it('should display a diff for entire object dumps', function(){
-     var expected = { name: 'joe',  age: 30, address: {city: 'new york', country: 'us'  }}
-      , actual   = { name: 'joel', age: 30, address: {city: 'new york', country: 'usa' }};
-
-      // actual.should.eql(expected);
-  });
-
-  it('should display a diff for multi-line strings', function(){
-    var expected = 'one two three\nfour five six\nseven eight nine';
-    var actual   = 'one two three\nfour zzzz six\nseven eight nine';
-
-    // actual.should.eql(expected);
-  });
-
-  it('should display a diff for entire object dumps', function(){
-    var expected = { name: 'joe',  age: 30, address: {city: 'new york', country: 'us'  }}
-    var actual   = { name: 'joel', age: 30, address: {city: 'new york', country: 'usa' }};
-
-    // actual.should.eql(expected);
-  });
-
-  it('should display a full-comparison with escaped special characters', function(){
-    var expected = 'one\ttab\ntwo\t\ttabs';
-    var actual   = 'one\ttab\ntwo\t\t\ttabs';
-
-    //actual.should.equal(expected);
-  });
-
-  it('should display a word diff for large strings', function(){
-    // cssin.should.equal(cssout);
-  });
-
-  it('should work with objects', function(){
-    var tobi = {
-      name: 'tobi',
-      species: 'ferret',
-      color: 'white',
-      age: 2
-    };
-
-    var loki = {
-      name: 'loki',
-      species: 'ferret',
-      color: 'brown',
-      age: 2
-    };
-
-    // 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/duration.js b/test/acceptance/duration.js
index 1f4c5c8..4f319b8 100644
--- a/test/acceptance/duration.js
+++ b/test/acceptance/duration.js
@@ -1,4 +1,3 @@
-
 describe('durations', function(){
   describe('when slow', function(){
     it('should highlight in red', function(done){
diff --git a/test/acceptance/failing/timeout.js b/test/acceptance/failing/timeout.js
deleted file mode 100644
index e52dde4..0000000
--- a/test/acceptance/failing/timeout.js
+++ /dev/null
@@ -1,17 +0,0 @@
-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/fs.js b/test/acceptance/fs.js
index 102b8e1..cdd3216 100644
--- a/test/acceptance/fs.js
+++ b/test/acceptance/fs.js
@@ -1,4 +1,3 @@
-
 var fs = require('fs');
 
 describe('fs.readFile()', function(){
diff --git a/test/acceptance/glob/glob.js b/test/acceptance/glob/glob.js
index b1127a2..3029ae8 100644
--- a/test/acceptance/glob/glob.js
+++ b/test/acceptance/glob/glob.js
@@ -1,4 +1,3 @@
-
 describe('globbing test', function(){
   it('should find this test', function(){
     // see glob.sh for details
diff --git a/test/acceptance/globals.js b/test/acceptance/globals.js
index 815eaee..f8ef804 100644
--- a/test/acceptance/globals.js
+++ b/test/acceptance/globals.js
@@ -1,4 +1,3 @@
-
 describe('global leaks', function(){
   before(function(){
     // uncomment to test
diff --git a/test/acceptance/http.js b/test/acceptance/http.js
index 00406e6..1dfa914 100644
--- a/test/acceptance/http.js
+++ b/test/acceptance/http.js
@@ -1,4 +1,3 @@
-
 var http = require('http');
 
 var server = http.createServer(function(req, res){
@@ -14,4 +13,4 @@ describe('http', function(){
       done();
     })
   })
-})
\ No newline at end of file
+})
diff --git a/test/acceptance/interfaces/bdd.js b/test/acceptance/interfaces/bdd.js
index f536aa0..bea1db2 100644
--- a/test/acceptance/interfaces/bdd.js
+++ b/test/acceptance/interfaces/bdd.js
@@ -1,4 +1,3 @@
-
 describe('Array', function(){
   describe('#indexOf()', function(){
     it('should return -1 when the value is not present', function(){
diff --git a/test/acceptance/interfaces/exports.js b/test/acceptance/interfaces/exports.js
index c93bf28..38093d5 100644
--- a/test/acceptance/interfaces/exports.js
+++ b/test/acceptance/interfaces/exports.js
@@ -1,4 +1,3 @@
-
 var calls = [];
 
 exports.Array = {
diff --git a/test/acceptance/interfaces/qunit.js b/test/acceptance/interfaces/qunit.js
index dbdc5bb..48aa21d 100644
--- a/test/acceptance/interfaces/qunit.js
+++ b/test/acceptance/interfaces/qunit.js
@@ -1,4 +1,3 @@
-
 function ok(expr, msg) {
   if (!expr) throw new Error(msg);
 }
@@ -21,4 +20,4 @@ suite('String');
 
 test('#length', function(){
   ok('foo'.length == 3);
-});
\ No newline at end of file
+});
diff --git a/test/acceptance/interfaces/tdd.js b/test/acceptance/interfaces/tdd.js
index 33311dd..1c6885e 100644
--- a/test/acceptance/interfaces/tdd.js
+++ b/test/acceptance/interfaces/tdd.js
@@ -1,4 +1,3 @@
-
 suite('Array', function(){
   suite('#indexOf()', function(){
     var initialValue = 32;
diff --git a/test/acceptance/misc/asyncOnly.js b/test/acceptance/misc/asyncOnly.js
deleted file mode 100644
index e03251f..0000000
--- a/test/acceptance/misc/asyncOnly.js
+++ /dev/null
@@ -1,10 +0,0 @@
-
-describe('asyncOnly', function(){
-  it('should display an error', function(){
-
-  })
-
-  it('should pass', function(done){
-    done();
-  })
-})
diff --git a/test/acceptance/misc/bail.js b/test/acceptance/misc/bail.js
deleted file mode 100644
index 0628b2f..0000000
--- a/test/acceptance/misc/bail.js
+++ /dev/null
@@ -1,21 +0,0 @@
-
-describe('bail', function(){
-  it('should only display this error', function(done){
-    throw new Error('this should be displayed');
-  })
-
-  it('should not display this error', function(done){
-    throw new Error('this should not be displayed');
-  })
-})
-
-describe('bail-2', function(){
-
-	before(function(done){
-    throw new Error('this hook should not be displayed');
-	})
-
-  it('should not display this error', function(done){
-    throw new Error('this should not be displayed');
-  })
-})
diff --git a/test/acceptance/misc/cascade.js b/test/acceptance/misc/cascade.js
deleted file mode 100644
index 0f884a0..0000000
--- a/test/acceptance/misc/cascade.js
+++ /dev/null
@@ -1,58 +0,0 @@
-
-describe('one', function(){
-  before(function(){
-    console.log('before one');
-  })
-
-  after(function(){
-    console.log('after one');
-  })
-
-  beforeEach(function(){
-    console.log('  before each one');
-  })
-
-  afterEach(function(){
-    console.log('  after each one');
-  })
-
-  describe('two', function(){
-    before(function(){
-      console.log('  before two');
-    })
-
-    after(function(){
-      console.log('  after two');
-    })
-
-    beforeEach(function(){
-      console.log('    before each two');
-    })
-
-    afterEach(function(){
-      console.log('    after each two');
-    })
-
-    describe('three', function(){
-      before(function(){
-        console.log('    before three');
-      })
-
-      after(function(){
-        console.log('    after three');
-      })
-
-      beforeEach(function(){
-        console.log('    before each three');
-      })
-
-      afterEach(function(){
-        console.log('    after each three');
-      })
-
-      it('should three', function(){
-        console.log('      TEST three');
-    })
-    })
-  })
-})
diff --git a/test/acceptance/misc/exit.js b/test/acceptance/misc/exit.js
index 4cace0b..113e392 100644
--- a/test/acceptance/misc/exit.js
+++ b/test/acceptance/misc/exit.js
@@ -10,4 +10,9 @@ describe('exit', function(){
       console.log('all done');
     }, 2500)
   })
+
+  it('should kill all processes when SIGINT received', function () {
+    // uncomment to test
+    //while (true) {}
+  });
 })
diff --git a/test/acceptance/misc/grep.js b/test/acceptance/misc/grep.js
deleted file mode 100644
index 9bfe0c5..0000000
--- a/test/acceptance/misc/grep.js
+++ /dev/null
@@ -1,22 +0,0 @@
-
-describe('grep', function(){
-  describe('fast', function(){
-    it('should run fast', function(){
-
-    })
-
-    it('should run fast again', function(){
-
-    })
-  })
-
-  describe('slow', function(){
-    it('should run slow', function(done){
-      setTimeout(done, 1000);
-    })
-
-    it('should run slow again', function(done){
-      setTimeout(done, 1000);
-    })
-  })
-})
diff --git a/test/acceptance/misc/many.js b/test/acceptance/misc/many.js
index c479ae4..26538bc 100644
--- a/test/acceptance/misc/many.js
+++ b/test/acceptance/misc/many.js
@@ -24,4 +24,4 @@ describe('a load of tests', function(){
     addTest();
   }
 
-})
\ No newline at end of file
+})
diff --git a/test/acceptance/misc/nontty.js b/test/acceptance/misc/nontty.js
index cd96f1f..2372a66 100644
--- a/test/acceptance/misc/nontty.js
+++ b/test/acceptance/misc/nontty.js
@@ -1,4 +1,3 @@
-
 describe('tests for non-tty', function(){
   it('should pass', function(){
 
diff --git a/test/acceptance/misc/only/bdd.js b/test/acceptance/misc/only/bdd.js
index 56627ab..ff14dcd 100644
--- a/test/acceptance/misc/only/bdd.js
+++ b/test/acceptance/misc/only/bdd.js
@@ -1,4 +1,3 @@
-
 describe('should only run .only test in this bdd suite', function() {
   it('should not run this test', function() {
     var zero = 0;
@@ -12,4 +11,4 @@ describe('should only run .only test in this bdd suite', function() {
     var zero = 0;
     zero.should.equal(1, 'this test should have been skipped');
   });
-});
\ No newline at end of file
+});
diff --git a/test/acceptance/misc/only/qunit.js b/test/acceptance/misc/only/qunit.js
index e1d9ac1..07c240f 100644
--- a/test/acceptance/misc/only/qunit.js
+++ b/test/acceptance/misc/only/qunit.js
@@ -1,4 +1,3 @@
-
 function ok(expr, msg) {
   if (!expr) throw new Error(msg);
 }
@@ -13,4 +12,4 @@ test.only('should run this test', function() {
 });
 test('should run this test, not (includes the title of the .only test)', function() {
   ok(0 === 1, 'this test should have been skipped');
-});
\ No newline at end of file
+});
diff --git a/test/acceptance/misc/only/tdd.js b/test/acceptance/misc/only/tdd.js
index ed7e906..cb6429a 100644
--- a/test/acceptance/misc/only/tdd.js
+++ b/test/acceptance/misc/only/tdd.js
@@ -1,4 +1,3 @@
-
 suite('should only run .only test in this tdd suite', function() {
   test('should not run this test', function() {
     var zero = 0;
@@ -12,4 +11,4 @@ suite('should only run .only test in this tdd suite', function() {
     var zero = 0;
     zero.should.equal(1, 'this test should have been skipped');
   });
-});
\ No newline at end of file
+});
diff --git a/test/acceptance/multiple.done.js b/test/acceptance/multiple.done.js
deleted file mode 100644
index aeb8845..0000000
--- a/test/acceptance/multiple.done.js
+++ /dev/null
@@ -1,16 +0,0 @@
-
-describe('multiple calls to done()', function(){
-  beforeEach(function(done){
-    done()
-    // uncomment
-    // done()
-  })
-
-  it('should fail in a test-case', function(done){
-    process.nextTick(function(){
-      done();
-      // uncomment
-      // done();
-    });
-  })
-})
\ No newline at end of file
diff --git a/test/acceptance/pending.js b/test/acceptance/pending.js
deleted file mode 100644
index cf738b2..0000000
--- a/test/acceptance/pending.js
+++ /dev/null
@@ -1,4 +0,0 @@
-
-describe('pending', function(){
-  it('should be allowed')
-})
\ No newline at end of file
diff --git a/test/acceptance/require/require.js b/test/acceptance/require/require.js
index 74dd0eb..20f3e6d 100644
--- a/test/acceptance/require/require.js
+++ b/test/acceptance/require/require.js
@@ -1,4 +1,3 @@
-
 describe('require test', function(){
   it('should require args in order', function(){
     var req = global.required;
diff --git a/test/acceptance/root.js b/test/acceptance/root.js
index 49a1e7f..1773830 100644
--- a/test/acceptance/root.js
+++ b/test/acceptance/root.js
@@ -1,4 +1,3 @@
-
 var calls = [];
 
 before(function(){
@@ -9,4 +8,4 @@ describe('root', function(){
   it('should be a valid suite', function(){
     calls.should.eql(['before']);
   })
-})
\ No newline at end of file
+})
diff --git a/test/acceptance/test.coffee b/test/acceptance/test.coffee
index b1c470a..8260940 100644
--- a/test/acceptance/test.coffee
+++ b/test/acceptance/test.coffee
@@ -3,4 +3,4 @@ obj = foo: 'bar'
 
 describe 'coffeescript', ->
   it 'should work', ->
-    obj.should.eql foo: 'bar'
\ No newline at end of file
+    obj.should.eql foo: 'bar'
diff --git a/test/acceptance/throw.js b/test/acceptance/throw.js
new file mode 100644
index 0000000..ac74f22
--- /dev/null
+++ b/test/acceptance/throw.js
@@ -0,0 +1,111 @@
+var mocha = require('../../')
+  , Suite = mocha.Suite
+  , Runner = mocha.Runner
+  , Test = mocha.Test;
+
+describe('a test that throws', function () {
+  var suite, runner;
+
+  beforeEach(function(){
+    suite = new Suite(null, 'root');
+    runner = new Runner(suite);
+  })
+
+  this.timeout(50);
+
+  describe('undefined', function (){
+    it('should not pass if throwing sync and test is sync', function(done) {
+      var test = new Test('im sync and throw undefined sync', function(){
+        throw undefined;
+      });
+      suite.addTest(test);
+      runner = new Runner(suite);
+      runner.on('end', function(){
+        runner.failures.should.equal(1);
+        test.state.should.equal('failed');
+        done();
+      });
+      runner.run();
+    })
+
+    it('should not pass if throwing sync and test is async', function(done){
+      var test = new Test('im async and throw undefined sync', function(done2){
+        throw undefined;
+        process.nexTick(done2);
+      });
+      suite.addTest(test);
+      runner = new Runner(suite);
+      runner.on('end', function(){
+        runner.failures.should.equal(1);
+        test.state.should.equal('failed');
+        done();
+      });
+      runner.run();
+    });
+
+    it('should not pass if throwing async and test is async', function(done){
+      var test = new Test('im async and throw undefined async', function(done2){
+        process.nexTick(function(){
+          throw undefined;
+          done2();
+        });
+      });
+      suite.addTest(test);
+      runner = new Runner(suite);
+      runner.on('end', function(){
+        runner.failures.should.equal(1);
+        test.state.should.equal('failed');
+        done();
+      });
+      runner.run();
+    })
+  })
+
+  describe('null', function (){
+    it('should not pass if throwing sync and test is sync', function(done) {
+      var test = new Test('im sync and throw null sync', function(){
+        throw null;
+      });
+      suite.addTest(test);
+      runner = new Runner(suite);
+      runner.on('end', function(){
+        runner.failures.should.equal(1);
+        test.state.should.equal('failed');
+        done();
+      });
+      runner.run();
+    })
+
+    it('should not pass if throwing sync and test is async', function(done){
+      var test = new Test('im async and throw null sync', function(done2){
+        throw null;
+        process.nexTick(done2);
+      });
+      suite.addTest(test);
+      runner = new Runner(suite);
+      runner.on('end', function(){
+        runner.failures.should.equal(1);
+        test.state.should.equal('failed');
+        done();
+      });
+      runner.run();
+    });
+
+    it('should not pass if throwing async and test is async', function(done){
+      var test = new Test('im async and throw null async', function(done2){
+        process.nexTick(function(){
+          throw null;
+          done2();
+        });
+      });
+      suite.addTest(test);
+      runner = new Runner(suite);
+      runner.on('end', function(){
+        runner.failures.should.equal(1);
+        test.state.should.equal('failed');
+        done();
+      });
+      runner.run();
+    })
+  })
+})
\ No newline at end of file
diff --git a/test/acceptance/timeout.js b/test/acceptance/timeout.js
index a12f2fb..f61c3ba 100644
--- a/test/acceptance/timeout.js
+++ b/test/acceptance/timeout.js
@@ -1,4 +1,3 @@
-
 describe('timeouts', function(){
   beforeEach(function(done){
     // uncomment
diff --git a/test/acceptance/uncaught.js b/test/acceptance/uncaught.js
deleted file mode 100644
index 7349595..0000000
--- a/test/acceptance/uncaught.js
+++ /dev/null
@@ -1,17 +0,0 @@
-
-describe('uncaught', function(){
-  beforeEach(function(done){
-    process.nextTick(function(){
-      // throw new Error('oh noes');
-      done();
-    });
-  })
-
-  it('should report properly', function(done){
-    process.nextTick(function(){
-      // if you uncomment this :)
-      // throw new Error("I'm uncaught!");
-      done();
-    })
-  })
-})
\ No newline at end of file
diff --git a/test/acceptance/utils.js b/test/acceptance/utils.js
index 4bb52e3..e67250b 100644
--- a/test/acceptance/utils.js
+++ b/test/acceptance/utils.js
@@ -35,8 +35,8 @@ describe('lib/utils', function () {
     it("should format functions saved in windows style - spaces", function () {
       var fn = [
         "function (one) {"
-        , "   do {",
-        , '    "nothing";',
+        , "   do {"
+        , '    "nothing";'
         , "   } while (false);"
         , ' }'
       ].join("\r\n");
@@ -72,18 +72,280 @@ describe('lib/utils', function () {
   });
 
   describe('stringify', function(){
-    it('should canoncalize the object', function(){
+
+    var stringify = utils.stringify;
+
+    it('should return Buffer with .toJSON representation', function() {
+      stringify(new Buffer([0x01])).should.equal('[\n  1\n]');
+      stringify(new Buffer([0x01, 0x02])).should.equal('[\n  1\n  2\n]');
+
+      stringify(new Buffer('ABCD')).should.equal('[\n  65\n  66\n  67\n  68\n]');
+    });
+
+    it('should return Date object with .toISOString() + string prefix', function() {
+      stringify(new Date(0)).should.equal('[Date: ' + new Date(0).toISOString() + ']');
+
+      var date = new Date(); // now
+      stringify(date).should.equal('[Date: ' + date.toISOString() + ']');
+    });
+
+    it('should return invalid Date object with .toString() + string prefix', function() {
+      stringify(new Date('')).should.equal('[Date: ' + new Date('').toString() + ']');
+    });
+
+    describe('#Number', function() {
+      it('should show the handle -0 situations', function() {
+        stringify(-0).should.eql('-0');
+        stringify(0).should.eql('0');
+        stringify('-0').should.eql('"-0"');
+      });
+
+      it('should work well with `NaN` and `Infinity`', function() {
+        stringify(NaN).should.equal('NaN');
+        stringify(Infinity).should.equal('Infinity');
+        stringify(-Infinity).should.equal('-Infinity');
+      });
+
+      it('floats and ints', function() {
+        stringify(1).should.equal('1');
+        stringify(1.2).should.equal('1.2');
+        stringify(1e9).should.equal('1000000000');
+      });
+    });
+
+    describe('canonicalize example', function() {
+      it('should represent the actual full result', function() {
+        var expected = {
+          str: 'string',
+          int: 90,
+          float: 9.99,
+          boolean: false,
+          nil: null,
+          undef: undefined,
+          regex: /^[a-z|A-Z]/,
+          date: new Date(0),
+          func: function() {},
+          infi: Infinity,
+          nan: NaN,
+          zero: -0,
+          buffer: new Buffer([0x01, 0x02]),
+          array: [1,2,3],
+          empArr: [],
+          matrix: [[1], [2,3,4] ],
+          object: { a: 1, b: 2 },
+          canObj: { a: { b: 1, c: 2 }, b: {} },
+          empObj: {}
+        };
+        expected.circular = expected; // Make `Circular` situation
+        var actual = ['{'
+          , '  "array": ['
+          , '    1'
+          , '    2'
+          , '    3'
+          , '  ]'
+          , '  "boolean": false'
+          , '  "buffer": [Buffer: ['
+          , '    1'
+          , '    2'
+          , '  ]]'
+          , '  "canObj": {'
+          , '    "a": {'
+          , '      "b": 1'
+          , '      "c": 2'
+          , '    }'
+          , '    "b": {}'
+          , '  }'
+          , '  "circular": [Circular]'
+          , '  "date": [Date: 1970-01-01T00:00:00.000Z]'
+          , '  "empArr": []'
+          , '  "empObj": {}'
+          , '  "float": 9.99'
+          , '  "func": [Function]'
+          , '  "infi": Infinity'
+          , '  "int": 90'
+          , '  "matrix": ['
+          , '    ['
+          , '      1'
+          , '    ]'
+          , '    ['
+          , '      2'
+          , '      3'
+          , '      4'
+          , '    ]'
+          , '  ]'
+          , '  "nan": NaN'
+          , '  "nil": [null]'
+          , '  "object": {'
+          , '    "a": 1'
+          , '    "b": 2'
+          , '  }'
+          , '  "regex": /^[a-z|A-Z]/'
+          , '  "str": "string"'
+          , '  "undef": [undefined]'
+          , '  "zero": -0'
+          , '}'].join('\n');
+        stringify(expected).should.equal(actual);
+      });
+    });
+
+    it('should canonicalize the object', function(){
       var travis = { name: 'travis', age: 24 };
       var travis2 = { age: 24, name: 'travis' };
 
-      utils.stringify(travis).should.equal(utils.stringify(travis2));
+      stringify(travis).should.equal(stringify(travis2));
     });
 
-    it('should handle circular structures', function(){
+    it('should handle circular structures in objects', function(){
       var travis = { name: 'travis' };
       travis.whoami = travis;
 
-      utils.stringify(travis).should.equal('{\n  "name": "travis"\n  "whoami": "[Circular]"\n}');
+      stringify(travis).should.equal('{\n  "name": "travis"\n  "whoami": [Circular]\n}');
+    });
+
+    it('should handle circular structures in arrays', function(){
+      var travis = ['travis'];
+      travis.push(travis);
+
+      stringify(travis).should.equal('[\n  "travis"\n  [Circular]\n]');
+    });
+
+    it('should handle circular structures in functions', function(){
+      var travis = function () {};
+      travis.fn = travis;
+
+      stringify(travis).should.equal('{\n  "fn": [Circular]\n}');
+    });
+
+
+    it('should handle various non-undefined, non-null, non-object, non-array, non-date, and non-function values', function () {
+      var regexp = new RegExp("(?:)"),
+        regExpObj = { regexp: regexp },
+        regexpString = '/(?:)/';
+
+      stringify(regExpObj).should.equal('{\n  "regexp": ' + regexpString + '\n}');
+      stringify(regexp).should.equal(regexpString);
+
+      var number = 1,
+        numberObj = { number: number },
+        numberString = '1';
+
+      stringify(numberObj).should.equal('{\n  "number": ' + number + '\n}');
+      stringify(number).should.equal(numberString);
+
+      var boolean = false,
+        booleanObj = { boolean: boolean },
+        booleanString = 'false';
+
+      stringify(booleanObj).should.equal('{\n  "boolean": ' + boolean + '\n}');
+      stringify(boolean).should.equal(booleanString);
+
+      var string = 'sneepy',
+        stringObj = { string: string };
+
+      stringify(stringObj).should.equal('{\n  "string": "' + string + '"\n}');
+      stringify(string).should.equal(JSON.stringify(string));
+
+      var nullValue = null,
+        nullObj = { 'null': null },
+        nullString = '[null]';
+
+      stringify(nullObj).should.equal('{\n  "null": [null]\n}');
+      stringify(nullValue).should.equal(nullString);
+    });
+
+    it('should handle arrays', function () {
+      var array = ['dave', 'dave', 'dave', 'dave'],
+        arrayObj = {array: array},
+        arrayString = array.map(function () {
+          return '    "dave"';
+        }).join('\n');
+
+      stringify(arrayObj).should.equal('{\n  "array": [\n' + arrayString + '\n  ]\n}');
+      stringify(array).should.equal('[' + arrayString.replace(/\s+/g, '\n  ') + '\n]');
+    });
+
+    it('should handle functions', function () {
+      var fn = function() {},
+        fnObj = {fn: fn},
+        fnString = '[Function]';
+
+      stringify(fnObj).should.equal('{\n  "fn": ' + fnString + '\n}');
+      stringify(fn).should.equal('[Function]');
+    });
+
+    it('should handle empty objects', function () {
+      stringify({}).should.equal('{}');
+      stringify({foo: {}}).should.equal('{\n  "foo": {}\n}');
+    });
+
+    it('should handle empty arrays', function () {
+      stringify([]).should.equal('[]');
+      stringify({foo: []}).should.equal('{\n  "foo": []\n}');
+    });
+
+    it('should handle non-empty arrays', function () {
+      stringify(['a', 'b', 'c']).should.equal('[\n  "a"\n  "b"\n  "c"\n]')
+    });
+
+    it('should handle empty functions (with no properties)', function () {
+      stringify(function(){}).should.equal('[Function]');
+      stringify({foo: function() {}}).should.equal('{\n  "foo": [Function]\n}');
+      stringify({foo: function() {}, bar: 'baz'}).should.equal('{\n  "bar": "baz"\n  "foo": [Function]\n}');
+    });
+
+    it('should handle functions w/ properties', function () {
+      var fn = function(){};
+      fn.bar = 'baz';
+      stringify(fn).should.equal('{\n  "bar": "baz"\n}');
+      stringify({foo: fn}).should.equal('{\n  "foo": {\n    "bar": "baz"\n  }\n}');
+    });
+
+    it('should handle undefined values', function () {
+      stringify({foo: undefined}).should.equal('{\n  "foo": [undefined]\n}');
+      stringify({foo: 'bar', baz: undefined}).should.equal('{\n  "baz": [undefined]\n  "foo": "bar"\n}');
+      stringify().should.equal('[undefined]');
+    });
+
+    it('should recurse', function () {
+      stringify({foo: {bar: {baz: {quux: {herp: 'derp'}}}}}).should.equal('{\n  "foo": {\n    "bar": {\n      "baz": {\n        "quux": {\n          "herp": "derp"\n        }\n      }\n    }\n  }\n}');
+    });
+
+    it('might get confusing', function () {
+      stringify(null).should.equal('[null]');
+    });
+
+    it('should not freak out if it sees a primitive twice', function () {
+      stringify({foo: null, bar: null}).should.equal('{\n  "bar": [null]\n  "foo": [null]\n}');
+      stringify({foo: 1, bar: 1}).should.equal('{\n  "bar": 1\n  "foo": 1\n}');
+    });
+
+    it('should stringify dates', function () {
+      var date = new Date(0);
+      stringify(date).should.equal('[Date: 1970-01-01T00:00:00.000Z]');
+      stringify({date: date}).should.equal('{\n  "date": [Date: 1970-01-01T00:00:00.000Z]\n}');
+    });
+
+    it('should handle object without an Object prototype', function () {
+      var a = Object.create(null);
+      a.foo = 1;
+
+      stringify(a).should.equal('{\n  "foo": 1\n}');
+    });
+  });
+
+  describe('type', function () {
+    var type = utils.type;
+    it('should recognize various types', function () {
+      type({}).should.equal('object');
+      type([]).should.equal('array');
+      type(1).should.equal('number');
+      type(Infinity).should.equal('number');
+      type(null).should.equal('null');
+      type(new Date()).should.equal('date');
+      type(/foo/).should.equal('regexp');
+      type('type').should.equal('string');
+      type(global).should.equal('global');
+      type(true).should.equal('boolean');
     });
   });
 
@@ -103,8 +365,8 @@ describe('lib/utils', function () {
         .and.containEql('/tmp/mocha-utils.js')
         .and.have.lengthOf(2);
       existsSync('/tmp/mocha-utils-link.js').should.be.true;
-      fs.rename('/tmp/mocha-utils.js', '/tmp/bob');
-      existsSync('/tmp/mocha-utils-link.js').should.be.true;
+      fs.renameSync('/tmp/mocha-utils.js', '/tmp/bob');
+      existsSync('/tmp/mocha-utils-link.js').should.be.false;
       utils.lookupFiles('/tmp', ['js'], false).should.eql([]);
     });
 
diff --git a/test/browser/grep.html b/test/browser/grep.html
new file mode 100644
index 0000000..0ba47c8
--- /dev/null
+++ b/test/browser/grep.html
@@ -0,0 +1,51 @@
+<html>
+  <head>
+    <title>Mocha</title>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <link rel="stylesheet" href="../../mocha.css" />
+    <script src="../../mocha.js"></script>
+    <script>mocha.setup('bdd')</script>
+    <script>
+      function assert(expr, msg) {
+        if (!expr) throw new Error(msg || 'failed');
+      }
+    </script>
+    <script src="grep.js"></script>
+  </head>
+  <body>
+    <div id="mocha"></div>
+  <script>
+      (function(window) {
+        var location = window.location;
+        mocha.checkLeaks();
+        var runner = mocha.run();
+        setTimeout(run, 1000);
+
+        function run() {
+          var regex = [
+              '.*',                                // All
+              'm{2}',                              // 'mm...m'
+              '\\d',                               // Contains number
+              '^\\d{2}(?=\\s\\d$)',                // Start with 2 numbers and end with one
+              '^@.*(?=\\(\\)$)',                   // Run @Array and @Function suite, but only function
+              '^@(?!.*\\)$)',                      // Run @Array and @Function suite, but only properties
+              '^co',                               // Start with 'co'
+              'first$',                            // Ends with 'first'
+              '^co.*(?=second$)',                  // Starts with 'co', ends with 'second'
+              '^Date:\\s01\/0(?:[1-4])\/2015$',    // Run all tests between '01/[01-04]/2015'
+              encodeURIComponent('^#'),            // Run encoded => start with '#'
+              encodeURIComponent('^[^a-z|0-9]+$')  // Run encoded => only uppercase suites(include `it` fns), e.g: CONSTANTS
+            ]
+            , qs = location.search.replace('?grep=', '')
+            , re = ~qs.indexOf('%') ? qs : decodeURIComponent(qs)
+            , grep = regex[regex.indexOf(re) + 1];
+
+          return grep
+            ? location.search = 'grep=' + grep
+            : false;
+        }
+      })(window);
+  </script>
+  </body>
+</html>
diff --git a/test/browser/grep.js b/test/browser/grep.js
new file mode 100644
index 0000000..9531b87
--- /dev/null
+++ b/test/browser/grep.js
@@ -0,0 +1,108 @@
+// numbers
+describe('21', function() {
+  it('1', function() {
+    assert(true);
+  });
+  it('2', function() {
+    assert(true);
+  });
+});
+// symbols
+describe('@Array', function() {
+  it('.pop()', function() {
+    assert(true);
+  });
+  it('.push()', function() {
+    assert(true);
+  });
+  it('.length', function() {
+    assert(true);
+  });
+});
+
+describe('@Function', function() {
+  it('.call()', function() {
+    assert(true);
+  });
+  it('.apply()', function() {
+    assert(true);
+  });
+  it('.length', function() {
+    assert(true);
+  });
+  it('.name', function() {
+    assert(true);
+  });
+  it('.prototype', function() {
+    assert(true);
+  });
+});
+
+//url with hashtags
+describe('#Services',function() {
+  describe('#http', function() {
+    it('.createClient()', function() {
+      assert(true);
+    });
+    it('.Server()', function() {
+      assert(true);
+    });
+  });
+  describe('#crypto', function() {
+    it('.randomBytes()', function() {
+      assert(true);
+    });
+    it('.Hmac()', function() {
+      assert(true);
+    });
+  });
+});
+
+// Uppercase
+describe('CONSTANTS', function() {
+  it('.STATUS_CODES', function() {
+    assert(true);
+  });
+});
+
+// Dates
+describe('Date:', function() {
+  it('01/02/2015', function() {
+    assert(true);
+  });
+  it('01/03/2015', function() {
+    assert(true);
+  });
+  it('01/06/2015', function() {
+    assert(true);
+  });
+});
+
+// etc..
+describe('booking/summary', function() {
+  it('should be run last', function() {
+    assert(true);
+  });
+});
+
+describe('component/booking/summary', function() {
+  it('should be run second', function() {
+    assert(true);
+  });
+});
+
+describe('component/booking/intro', function() {
+  it('should be run first', function() {
+    assert(true);
+  });
+});
+
+describe('contains numbers', function() {
+  it('should run if the number 92 matching', function() {
+    assert(true);
+  });
+
+  it('should run if the number 8 matching', function() {
+    assert(true);
+  });
+});
\ No newline at end of file
diff --git a/test/browser/large.js b/test/browser/large.js
index 1250aba..56757ea 100644
--- a/test/browser/large.js
+++ b/test/browser/large.js
@@ -1,4 +1,3 @@
-
 var n = 30;
 while (n--) {
   describe('Array ' + n, function(){
@@ -46,4 +45,4 @@ describe('something', function(){
       done();
     }, 1);
   })
-})
\ No newline at end of file
+})
diff --git a/test/browser/stack-trace.html b/test/browser/stack-trace.html
new file mode 100644
index 0000000..0f267da
--- /dev/null
+++ b/test/browser/stack-trace.html
@@ -0,0 +1,24 @@
+<html>
+<head>
+    <title>Mocha</title>
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <link rel="stylesheet" href="../../mocha.css" />
+    <script src="../../mocha.js"></script>
+    <script>mocha.setup('bdd')</script>
+    <script>
+        function assert(expr, err) {
+            if (!expr) throw err;
+        }
+    </script>
+    <script src="stack-trace.js"></script>
+    <script>
+        onload = function() {
+            mocha.run();
+        };
+    </script>
+</head>
+<body>
+<div id="mocha"></div>
+</body>
+</html>
diff --git a/test/browser/stack-trace.js b/test/browser/stack-trace.js
new file mode 100644
index 0000000..b39944d
--- /dev/null
+++ b/test/browser/stack-trace.js
@@ -0,0 +1,20 @@
+'use strict';
+describe('Stack trace', function() {
+  it('should prettify the stack-trace', function() {
+    var err = new Error();
+    // We do this fake stack-trace because we under development,
+    // and our root isn't `node_modules`, `bower` or `components`
+    err.stack = ['Error: failed'
+      , 'at assert (stack-trace.html:11:30)'
+      , 'at Context.<anonymous> (stack-trace.js:5:5)'
+      , 'at callFn (http://localhost:63342/node_modules/mocha.js:4546:21)'
+      , 'at Test.require.register.Runnable.run (http://localhost:63342/node_modules/mocha.js:4539:7)'
+      , 'at Runner.require.register.Runner.runTest (http://localhost:63342/node_modules/mocha.js:4958:10)'
+      , 'at http://localhost:63342/bower_components/mocha.js:5041:12'
+      , 'at next (http://localhost:63342/bower_components/mocha.js:4883:14)'
+      , 'at http://localhost:63342/bower_components/mocha.js:4893:7'
+      , 'at next (http://localhost:63342/bower_components/mocha.js:4828:23)'
+      , 'at http://localhost:63342/bower_components/mocha.js:4860:5'].join('\n');
+    assert(false, err);
+  });
+});
\ No newline at end of file
diff --git a/test/color.js b/test/color.js
new file mode 100644
index 0000000..8c7167b
--- /dev/null
+++ b/test/color.js
@@ -0,0 +1,17 @@
+var assert = require('assert');
+var child_process = require('child_process');
+
+describe('Mocha', function() {
+  this.timeout(1000);
+
+  it('should not output colors to pipe', function(cb) {
+    var command = 'bin/mocha --grep missing-test';
+    child_process.exec(command, function(err, stdout, stderr) {
+      if (err) return cb(err);
+
+      assert(stdout.indexOf('[90m') === -1);
+
+      cb(null);
+    });
+  });
+});
diff --git a/test/grep.js b/test/grep.js
index b4be371..633aab4 100644
--- a/test/grep.js
+++ b/test/grep.js
@@ -1,16 +1,22 @@
-
 var Mocha = require('../');
 
 describe('Mocha', function(){
   describe('"grep" option', function(){
     it('should add a RegExp to the mocha.options object', function(){
-      var mocha = new Mocha({ grep: /foo/ });
-      mocha.options.grep.toString().should.equal('/foo/');
+      var mocha = new Mocha({ grep: /foo.*/ });
+      mocha.options.grep.toString().should.equal('/foo.*/');
     })
 
-    it('should convert grep string to a RegExp', function(){
-      var mocha = new Mocha({ grep: 'foo' });
-      mocha.options.grep.toString().should.equal('/foo/');
+    it('should convert string to a RegExp', function(){
+      var mocha = new Mocha({ grep: 'foo.*' });
+      mocha.options.grep.toString().should.equal('/foo.*/');
+    })
+  })
+
+  describe('"fgrep" option', function(){
+    it('should escape and convert string to a RegExp', function(){
+      var mocha = new Mocha({ fgrep: 'foo.*' });
+      mocha.options.grep.toString().should.equal('/foo\\.\\*/');
     })
   })
 
diff --git a/test/hook.async.js b/test/hook.async.js
index fd62932..ae6e642 100644
--- a/test/hook.async.js
+++ b/test/hook.async.js
@@ -1,4 +1,3 @@
-
 describe('async', function(){
   var calls = [];
 
diff --git a/test/hook.err.js b/test/hook.err.js
index f28ecdd..d7e6c2e 100644
--- a/test/hook.err.js
+++ b/test/hook.err.js
@@ -195,7 +195,7 @@ describe('hook error handling', function(){
         after(function(){
           calls.push("1.2 after");
         });
-      });  
+      });
 
       afterEach(function() {
         calls.push('1 after each')
@@ -244,7 +244,7 @@ describe('hook error handling', function(){
         after(function(){
           calls.push("2.2 after");
         });
-      });  
+      });
 
       afterEach(function() {
         calls.push('2 after each')
@@ -293,4 +293,4 @@ describe('hook error handling', function(){
     });
   })
 
-})
\ No newline at end of file
+})
diff --git a/test/hook.sync.js b/test/hook.sync.js
index c446c6a..1d40f5d 100644
--- a/test/hook.sync.js
+++ b/test/hook.sync.js
@@ -1,4 +1,3 @@
-
 describe('serial', function(){
   var calls = [];
 
@@ -95,4 +94,4 @@ describe('serial', function(){
         , 'parent after']);
     })
   })
-})
\ No newline at end of file
+})
diff --git a/test/hook.sync.nested.js b/test/hook.sync.nested.js
index 94c00da..b87c222 100644
--- a/test/hook.sync.nested.js
+++ b/test/hook.sync.nested.js
@@ -1,4 +1,3 @@
-
 describe('serial', function(){
   describe('nested', function(){
     var calls = [];
diff --git a/test/hook.timeout.js b/test/hook.timeout.js
index 5b158e2..155c1e9 100644
--- a/test/hook.timeout.js
+++ b/test/hook.timeout.js
@@ -1,4 +1,3 @@
-
 before(function(done){
   this.timeout(100);
   setTimeout(done, 50);
@@ -6,4 +5,4 @@ before(function(done){
 
 it('should work', function(done) {
   done();
-});
\ No newline at end of file
+});
diff --git a/test/http.meta.2.js b/test/http.meta.2.js
index bcc6b6b..fe9662b 100644
--- a/test/http.meta.2.js
+++ b/test/http.meta.2.js
@@ -1,6 +1,7 @@
-
 var http = require('http');
 
+var PORT = 8899;
+
 var server = http.createServer(function(req, res){
   var accept = req.headers.accept || ''
     , json = ~accept.indexOf('json');
@@ -19,15 +20,13 @@ var server = http.createServer(function(req, res){
   }
 })
 
-server.listen(8899);
-
 function get(url) {
   var fields
     , expected
     , header = {};
 
   function request(done) {
-    http.get({ path: url, port: 8899, headers: header }, function(res){
+    http.get({ path: url, port: PORT, headers: header }, function(res){
       var buf = '';
       res.should.have.property('statusCode', 200);
       res.setEncoding('utf8');
@@ -53,6 +52,7 @@ function get(url) {
 
         expected = body;
         describe('GET ' + url, function(){
+          this.timeout(500);
           if (fields) {
             describe('when given ' + fields, function(){
               it('should respond with "' + body + '"', request);
@@ -67,6 +67,15 @@ function get(url) {
 }
 
 describe('http server', function(){
+
+  before(function(done) {
+    server.listen(PORT, done);
+  });
+
+  after(function() {
+    server.close();
+  });
+
   get('/')
     .should
     .respond('hello')
@@ -79,4 +88,4 @@ describe('http server', function(){
     .set('Accept', 'application/json')
     .should
     .respond('["tobi","loki","jane"]')
-})
\ No newline at end of file
+})
diff --git a/test/http.meta.js b/test/http.meta.js
index 9ae90d8..5d1fb67 100644
--- a/test/http.meta.js
+++ b/test/http.meta.js
@@ -1,6 +1,7 @@
-
 var http = require('http');
 
+var PORT = 8889;
+
 var server = http.createServer(function(req, res){
   var accept = req.headers.accept || ''
     , json = ~accept.indexOf('json');
@@ -17,13 +18,12 @@ var server = http.createServer(function(req, res){
       }
       break;
   }
-})
+});
 
-server.listen(8889);
 
 function get(url, body, header) {
   return function(done){
-    http.get({ path: url, port: 8889, headers: header }, function(res){
+    http.get({ path: url, port: PORT, headers: header || {}}, function(res){
       var buf = '';
       res.should.have.property('statusCode', 200);
       res.setEncoding('utf8');
@@ -36,8 +36,17 @@ function get(url, body, header) {
   }
 }
 
-describe('http requests', function(){
-  describe('GET /', function(){
+describe('http requests', function () {
+
+  before(function(done) {
+    server.listen(PORT, done);
+  });
+
+  after(function() {
+    server.close();
+  });
+
+  describe('GET /', function () {
     it('should respond with hello',
       get('/', 'hello'))
   })
@@ -49,4 +58,4 @@ describe('http requests', function(){
     it('should respond with users',
       get('/users', '["tobi","loki","jane"]', { Accept: 'application/json' }))
   })
-})
\ No newline at end of file
+})
diff --git a/test/integration/diffs.js b/test/integration/diffs.js
new file mode 100644
index 0000000..d8aebba
--- /dev/null
+++ b/test/integration/diffs.js
@@ -0,0 +1,45 @@
+var assert   = require('assert');
+var helpers  = require('./helpers');
+var run      = helpers.runMocha;
+var fs       = require('fs');
+var getDiffs = helpers.getDiffs;
+
+function getExpectedOutput() {
+  var output = fs.readFileSync('test/integration/fixtures/diffs/output', 'UTF8');
+
+  // Diffs are delimited in file by "// DIFF"
+  return output.split(/\s*\/\/ DIFF/).slice(1).map(function(diff) {
+    return diff.split('\n').filter(Boolean).join('\n');
+  });
+}
+
+describe('diffs', function() {
+  var diffs, expected;
+  this.timeout(1000);
+
+  before(function(done) {
+    run('diffs/diffs.js', ['-C'], function(err, res) {
+      expected = getExpectedOutput();
+      diffs = getDiffs(res.output);
+      done(err);
+    });
+  });
+
+  [
+    'should display a diff for small strings',
+    'should display a diff of canonicalized objects',
+    'should display a diff for medium strings',
+    'should display a diff for entire object dumps',
+    'should display a diff for multi-line strings',
+    'should display a diff for entire object dumps',
+    'should display a full-comparison with escaped special characters',
+    'should display a word diff for large strings',
+    'should work with objects',
+    'should show value diffs and not be affected by commas',
+    'should display diff by data and not like an objects'
+  ].forEach(function(title, i) {
+    it(title, function() {
+      assert.equal(diffs[i], expected[i]);
+    });
+  });
+});
diff --git a/test/integration/fixtures/cascade.js b/test/integration/fixtures/cascade.js
new file mode 100644
index 0000000..10b1c37
--- /dev/null
+++ b/test/integration/fixtures/cascade.js
@@ -0,0 +1,57 @@
+describe('one', function() {
+  before(function() {
+    console.log('before one');
+  });
+
+  after(function() {
+    console.log('after one');
+  });
+
+  beforeEach(function() {
+    console.log('  before each one');
+  });
+
+  afterEach(function() {
+    console.log('  after each one');
+  });
+
+  describe('two', function() {
+    before(function() {
+      console.log('  before two');
+    });
+
+    after(function() {
+      console.log('  after two');
+    });
+
+    beforeEach(function() {
+      console.log('    before each two');
+    });
+
+    afterEach(function() {
+      console.log('    after each two');
+    });
+
+    describe('three', function() {
+      before(function() {
+        console.log('    before three');
+      });
+
+      after(function() {
+        console.log('    after three');
+      });
+
+      beforeEach(function() {
+        console.log('    before each three');
+      });
+
+      afterEach(function() {
+        console.log('    after each three');
+      });
+
+      it('should three', function() {
+        console.log('      TEST three');
+      });
+    });
+  });
+});
diff --git a/test/acceptance/fixtures/css.in b/test/integration/fixtures/diffs/diffs.css.in
similarity index 98%
rename from test/acceptance/fixtures/css.in
rename to test/integration/fixtures/diffs/diffs.css.in
index 29fb274..09a3ca5 100644
--- a/test/acceptance/fixtures/css.in
+++ b/test/integration/fixtures/diffs/diffs.css.in
@@ -6,4 +6,4 @@ body {
 
 a {
   color: blue
-}
\ No newline at end of file
+}
diff --git a/test/acceptance/fixtures/css.out b/test/integration/fixtures/diffs/diffs.css.out
similarity index 98%
rename from test/acceptance/fixtures/css.out
rename to test/integration/fixtures/diffs/diffs.css.out
index 643692a..53b3ec9 100644
--- a/test/acceptance/fixtures/css.out
+++ b/test/integration/fixtures/diffs/diffs.css.out
@@ -10,4 +10,4 @@ a {
 
 foo {
   bar: 'baz';
-}
\ No newline at end of file
+}
diff --git a/test/integration/fixtures/diffs/diffs.js b/test/integration/fixtures/diffs/diffs.js
new file mode 100644
index 0000000..cf538fd
--- /dev/null
+++ b/test/integration/fixtures/diffs/diffs.js
@@ -0,0 +1,84 @@
+var fs     = require('fs');
+var assert = require('assert');
+var cssin  = fs.readFileSync('test/integration/fixtures/diffs/diffs.css.in', 'ascii');
+var cssout = fs.readFileSync('test/integration/fixtures/diffs/diffs.css.out', 'ascii');
+
+describe('diffs', function() {
+  var actual, expected;
+
+  it('should display a diff for small strings', function() {
+    actual = 'foo rar baz';
+    expected = 'foo bar baz';
+    actual.should.equal(expected);
+  });
+
+  it('should display a diff of canonicalized objects', function() {
+    actual = { name: 'travis j', age: 23 };
+    expected = { age: 23, name: 'travis' };
+    actual.should.equal(expected);
+  });
+
+  it('should display a diff for medium strings', function() {
+    actual = 'foo bar baz\nfoo rar baz\nfoo bar raz';
+    expected = 'foo bar baz\nfoo bar baz\nfoo bar baz';
+    actual.should.equal(expected);
+  });
+
+  it('should display a diff for entire object dumps', function() {
+    actual   = { name: 'joel', age: 30, address: {city: 'new york', country: 'usa' }};
+    expected = { name: 'joe',  age: 30, address: {city: 'new york', country: 'us'  }};
+    actual.should.equal(expected);
+  });
+
+  it('should display a diff for multi-line strings', function() {
+    actual   = 'one two three\nfour zzzz six\nseven eight nine';
+    expected = 'one two three\nfour five six\nseven eight nine';
+    actual.should.equal(expected);
+  });
+
+  it('should display a diff for entire object dumps', function() {
+    actual   = { name: 'joel', age: 30, address: {city: 'new york', country: 'usa' }};
+    expected = { name: 'joe',  age: 30, address: {city: 'new york', country: 'us'  }}
+    actual.should.equal(expected);
+  });
+
+  it('should display a full-comparison with escaped special characters', function() {
+    actual   = 'one\ttab\ntwo\t\t\ttabs';
+    expected = 'one\ttab\ntwo\t\ttabs';
+    actual.should.equal(expected);
+  });
+
+  it('should display a word diff for large strings', function() {
+    cssin.should.equal(cssout);
+  });
+
+  it('should work with objects', function() {
+    actual = {
+      name: 'tobi',
+      species: 'ferret',
+      color: 'white',
+      age: 2
+    };
+
+    expected = {
+      name: 'loki',
+      species: 'ferret',
+      color: 'brown',
+      age: 2
+    };
+
+    actual.should.eql(expected);
+  });
+
+  it('should show value diffs and not be affected by commas', function() {
+    actual = { a: 123 };
+    expected = { a: 123, b: 456 };
+    actual.should.equal(expected);
+  });
+
+  it('should display diff by data and not like an objects', function() {
+    actual = new Buffer([0x01]);
+    expected = new Buffer([0x02]);
+    actual.should.equal(expected);
+  });
+});
diff --git a/test/integration/fixtures/diffs/output b/test/integration/fixtures/diffs/output
new file mode 100644
index 0000000..bf56a96
--- /dev/null
+++ b/test/integration/fixtures/diffs/output
@@ -0,0 +1,91 @@
+// DIFF
+      -foo rar baz
+      +foo bar baz
+
+// DIFF
+       {
+         "age": 23
+      -  "name": "travis j"
+      +  "name": "travis"
+       }
+
+// DIFF
+       foo bar baz
+      -foo rar baz
+      -foo bar raz
+      +foo bar baz
+      +foo bar baz
+
+// DIFF
+       {
+         "address": {
+           "city": "new york"
+      -    "country": "usa"
+      +    "country": "us"
+         }
+         "age": 30
+      -  "name": "joel"
+      +  "name": "joe"
+       }
+
+// DIFF
+       one two three
+      -four zzzz six
+      +four five six
+       seven eight nine
+
+// DIFF
+       {
+         "address": {
+           "city": "new york"
+      -    "country": "usa"
+      +    "country": "us"
+         }
+         "age": 30
+      -  "name": "joel"
+      +  "name": "joe"
+       }
+
+// DIFF
+       one	tab
+      -two			tabs
+      +two		tabs
+
+// DIFF
+       body {
+         font: "Helvetica Neue", Helvetica, arial, sans-serif;
+         background: black;
+      -  color: white;
+      +  color: #fff;
+       }
+       
+       a {
+      -  color: blue
+      +  color: blue;
+       }
+      +
+      +foo {
+      +  bar: 'baz';
+      +}
+
+// DIFF
+       {
+         "age": 2
+      -  "color": "white"
+      -  "name": "tobi"
+      +  "color": "brown"
+      +  "name": "loki"
+         "species": "ferret"
+       }
+
+// DIFF
+       {
+         "a": 123
+      +  "b": 456
+       }
+
+// DIFF
+       [
+      -  1
+      +  2
+       ]
diff --git a/test/integration/fixtures/multiple.done.js b/test/integration/fixtures/multiple.done.js
new file mode 100644
index 0000000..63a7040
--- /dev/null
+++ b/test/integration/fixtures/multiple.done.js
@@ -0,0 +1,18 @@
+// The suite below should result in an additional error, but does
+// not. Uncomment once this bug is resolved.
+
+// describe('suite', function() {
+//   beforeEach(function(done) {
+//     done();
+//     done();
+//   });
+
+//   it('test', function() {});
+// });
+
+it('should fail in a test-case', function(done) {
+  process.nextTick(function(){
+    done();
+    done();
+  });
+});
diff --git a/test/integration/fixtures/options/async-only.async.js b/test/integration/fixtures/options/async-only.async.js
new file mode 100644
index 0000000..5387d16
--- /dev/null
+++ b/test/integration/fixtures/options/async-only.async.js
@@ -0,0 +1,3 @@
+it('should pass', function(done){
+  done();
+});
diff --git a/test/integration/fixtures/options/async-only.sync.js b/test/integration/fixtures/options/async-only.sync.js
new file mode 100644
index 0000000..d0dd9fa
--- /dev/null
+++ b/test/integration/fixtures/options/async-only.sync.js
@@ -0,0 +1 @@
+it('throws an error', function() {});
diff --git a/test/integration/fixtures/options/bail.js b/test/integration/fixtures/options/bail.js
new file mode 100644
index 0000000..bb8ad13
--- /dev/null
+++ b/test/integration/fixtures/options/bail.js
@@ -0,0 +1,25 @@
+describe('suite1', function() {
+  it('should display this spec', function() {});
+
+  it('should only display this error', function(done) {
+    throw new Error('this should be displayed');
+  });
+
+  it('should not display this error', function(done) {
+    throw new Error('this should not be displayed');
+  });
+});
+
+describe('suite2', function() {
+  // TODO: When uncommented, the hook below is ran and throws an exception
+  // despite the previous failure with the bail flag. This is a bug. Uncomment
+  // once resolved
+
+  // before(function(done) {
+  //   throw new Error('this hook should not be displayed');
+  // });
+
+  it('should not display this error', function(done) {
+    throw new Error('this should not be displayed');
+  });
+});
diff --git a/test/integration/fixtures/options/delay.js b/test/integration/fixtures/options/delay.js
new file mode 100644
index 0000000..32b8f99
--- /dev/null
+++ b/test/integration/fixtures/options/delay.js
@@ -0,0 +1,19 @@
+var assert = require('assert');
+var delay  = 500;
+var start  = new Date().getTime();
+
+setTimeout(function() {
+  describe('delayed execution', function() {
+    it('should have waited ' + delay + 'ms to run this suite', function() {
+      assert(new Date().getTime() - delay >= start);
+    });
+
+    it('should have no effect if attempted twice in the same suite', function() {
+      assert(true);
+      run();
+      assert(true);
+    });
+  });
+
+  run();
+}, delay);
diff --git a/test/integration/fixtures/options/grep.js b/test/integration/fixtures/options/grep.js
new file mode 100644
index 0000000..5e163f5
--- /dev/null
+++ b/test/integration/fixtures/options/grep.js
@@ -0,0 +1,12 @@
+describe('grep', function() {
+  describe('match', function() {
+    it('should run', function(){});
+    it('should also run', function() {});
+  });
+
+  describe('fail', function(){
+    it('should not be ran', function() {
+      throw new Error('Spec should not run');
+    });
+  });
+});
diff --git a/test/acceptance/sort/alpha.js b/test/integration/fixtures/options/sort.alpha.js
similarity index 97%
rename from test/acceptance/sort/alpha.js
rename to test/integration/fixtures/options/sort.alpha.js
index 365f39c..7a53029 100644
--- a/test/acceptance/sort/alpha.js
+++ b/test/integration/fixtures/options/sort.alpha.js
@@ -4,4 +4,4 @@ describe('alpha', function(){
       throw new Error('alpha was not executed first');
     }
   });
-});
\ No newline at end of file
+});
diff --git a/test/acceptance/sort/beta.js b/test/integration/fixtures/options/sort.beta.js
similarity index 96%
rename from test/acceptance/sort/beta.js
rename to test/integration/fixtures/options/sort.beta.js
index 3de3e0c..0951f49 100644
--- a/test/acceptance/sort/beta.js
+++ b/test/integration/fixtures/options/sort.beta.js
@@ -2,4 +2,4 @@ describe('beta', function(){
   it('should be executed second', function(){
     global.beta = 1;
   });
-});
\ No newline at end of file
+});
diff --git a/test/integration/fixtures/pending/skip.sync.before.js b/test/integration/fixtures/pending/skip.sync.before.js
new file mode 100644
index 0000000..35152e2
--- /dev/null
+++ b/test/integration/fixtures/pending/skip.sync.before.js
@@ -0,0 +1,13 @@
+describe('skip in before', function() {
+  before(function() {
+    this.skip();
+  });
+
+  it('should never run this test', function() {
+    throw new Error('never thrown');
+  });
+
+  it('should never run this test', function() {
+    throw new Error('never thrown');
+  });
+});
diff --git a/test/integration/fixtures/pending/skip.sync.beforeEach.js b/test/integration/fixtures/pending/skip.sync.beforeEach.js
new file mode 100644
index 0000000..8d1c442
--- /dev/null
+++ b/test/integration/fixtures/pending/skip.sync.beforeEach.js
@@ -0,0 +1,13 @@
+describe('skip in beforeEach', function() {
+  beforeEach(function() {
+    this.skip();
+  });
+
+  it('should never run this test', function() {
+    throw new Error('never thrown');
+  });
+
+  it('should never run this test', function() {
+    throw new Error('never thrown');
+  });
+});
diff --git a/test/integration/fixtures/pending/skip.sync.spec.js b/test/integration/fixtures/pending/skip.sync.spec.js
new file mode 100644
index 0000000..e2bbb73
--- /dev/null
+++ b/test/integration/fixtures/pending/skip.sync.spec.js
@@ -0,0 +1,10 @@
+describe('skip in test', function() {
+  it('should skip immediately', function() {
+    this.skip();
+    throw new Error('never thrown');
+  });
+
+  it('should run other tests in the suite', function() {
+    // Do nothing
+  });
+});
diff --git a/test/integration/fixtures/pending/spec.js b/test/integration/fixtures/pending/spec.js
new file mode 100644
index 0000000..084dd33
--- /dev/null
+++ b/test/integration/fixtures/pending/spec.js
@@ -0,0 +1,3 @@
+describe('suite', function() {
+  it('pending spec');
+});
diff --git a/test/integration/fixtures/regression/issue-1327.js b/test/integration/fixtures/regression/issue-1327.js
new file mode 100644
index 0000000..43d5553
--- /dev/null
+++ b/test/integration/fixtures/regression/issue-1327.js
@@ -0,0 +1,15 @@
+it('test 1', function() {
+  console.log('testbody1');
+  process.nextTick(function() {
+    throw 'Too bad';
+  });
+});
+
+it('test 2', function() {
+  console.log('testbody2');
+});
+
+it('test 3', function() {
+  console.log('testbody3');
+  throw new Error('OUCH');
+});
diff --git a/test/integration/fixtures/timeout.js b/test/integration/fixtures/timeout.js
new file mode 100644
index 0000000..d8c99b2
--- /dev/null
+++ b/test/integration/fixtures/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());
+  }
+});
diff --git a/test/integration/fixtures/uncaught.hook.js b/test/integration/fixtures/uncaught.hook.js
new file mode 100644
index 0000000..9adcb3f
--- /dev/null
+++ b/test/integration/fixtures/uncaught.hook.js
@@ -0,0 +1,15 @@
+describe('uncaught', function() {
+  beforeEach(function(done) {
+    process.nextTick(function() {
+      throw new Error('oh noes');
+      done();
+    });
+  });
+
+  it('test', function(done) {
+    process.nextTick(function() {
+      throw new Error("I'm uncaught!");
+      done();
+    });
+  });
+});
diff --git a/test/integration/fixtures/uncaught.js b/test/integration/fixtures/uncaught.js
new file mode 100644
index 0000000..054d1d8
--- /dev/null
+++ b/test/integration/fixtures/uncaught.js
@@ -0,0 +1,26 @@
+'use strict';
+
+/**
+ * This file should only generate one failure per spec despite the fact that
+ * Mocha is capable of detecting two distinct exceptions during test execution.
+ */
+
+it('fails exactly once when a global error is thrown first', function(done) {
+  setTimeout(function() {
+    throw new Error('global error');
+
+    setTimeout(function() {
+      done(new Error('test error'));
+    }, 0);
+  }, 0);
+});
+
+it('fails exactly once when a global error is thrown second', function(done) {
+  setTimeout(function() {
+    done(new Error('test error'));
+  }, 0);
+
+  setTimeout(function() {
+    throw new Error('global error');
+  }, 0);
+});
diff --git a/test/integration/helpers.js b/test/integration/helpers.js
new file mode 100644
index 0000000..eea8b18
--- /dev/null
+++ b/test/integration/helpers.js
@@ -0,0 +1,142 @@
+var spawn = require('child_process').spawn;
+var path  = require('path');
+
+module.exports = {
+  /**
+   * Invokes the mocha binary for the given fixture with color output disabled.
+   * Accepts an array of additional command line args to pass. The callback is
+   * invoked with a summary of the run, in addition to its output. The summary
+   * includes the number of passing, pending, and failing tests, as well as the
+   * exit code. Useful for testing different reporters.
+   *
+   * Example response:
+   * {
+   *   pending: 0,
+   *   passing: 0,
+   *   failing: 1,
+   *   code:    1,
+   *   output:  '...'
+   * }
+   *
+   * @param {string}   fixturePath
+   * @param {array}    args
+   * @param {function} fn
+   */
+  runMocha: function(fixturePath, args, fn) {
+    var path;
+
+    path = resolveFixturePath(fixturePath);
+    args = args || [];
+
+    invokeMocha(args.concat(['-C', path]), function(err, res) {
+      if (err) return fn(err);
+
+      fn(null, getSummary(res));
+    });
+  },
+
+  /**
+   * Invokes the mocha binary for the given fixture using the JSON reporter,
+   * returning the parsed output, as well as exit code.
+   *
+   * @param {string}   fixturePath
+   * @param {array}    args
+   * @param {function} fn
+   */
+  runMochaJSON: function(fixturePath, args, fn) {
+    var path;
+
+    path = resolveFixturePath(fixturePath);
+    args = args || [];
+
+    invokeMocha(args.concat(['--reporter', 'json', path]), function(err, res) {
+      if (err) return fn(err);
+
+      try {
+        var result = JSON.parse(res.output);
+        result.code = res.code;
+      } catch (err) {
+        return fn(err);
+      }
+
+      fn(null, result);
+    });
+  },
+
+  /**
+   * Returns an array of diffs corresponding to exceptions thrown from specs,
+   * given the plaintext output (-C) of a mocha run.
+   *
+   * @param  {string}   output
+   * returns {string[]}
+   */
+  getDiffs: function(output) {
+    var diffs, i, inDiff, inStackTrace;
+
+    diffs = [];
+    output.split('\n').forEach(function(line) {
+      if (line.match(/^  \d+\)/)) {
+        // New spec, e.g. "1) spec title"
+        diffs.push([]);
+        i = diffs.length - 1;
+        inStackTrace = false;
+        inDiff = false;
+      } else if (!diffs.length || inStackTrace) {
+        // Haven't encountered a spec yet
+        // or we're in the middle of a stack trace
+        return;
+      } else if (line.indexOf('+ expected - actual') !== -1) {
+        inDiff = true;
+      } else if (line.match(/at Context/)) {
+        // At the start of a stack trace
+        inStackTrace = true;
+        inDiff = false;
+      } else if (inDiff) {
+        diffs[i].push(line);
+      }
+    });
+
+    // Ignore empty lines before/after diff
+    return diffs.map(function(diff) {
+      return diff.slice(1, -1).join('\n');
+    });
+  }
+};
+
+function invokeMocha(args, fn) {
+  var output, mocha, listener;
+
+  output = '';
+  mocha = spawn('./bin/mocha', args);
+
+  listener = function(data) {
+    output += data;
+  };
+
+  mocha.stdout.on('data', listener);
+  mocha.stderr.on('data', listener);
+  mocha.on('error', fn);
+
+  mocha.on('close', function(code) {
+    fn(null, {
+      output: output.split('\n').join('\n'),
+      code: code
+    });
+  });
+}
+
+function resolveFixturePath(fixture) {
+  return path.join('./test/integration/fixtures', fixture);
+}
+
+function getSummary(res) {
+  return ['passing', 'pending', 'failing'].reduce(function(summary, type) {
+    var pattern, match;
+
+    pattern = new RegExp('  (\\d+) ' + type + '\\s');
+    match = pattern.exec(res.output);
+    summary[type] = (match) ? parseInt(match, 10) : 0;
+
+    return summary;
+  }, res);
+}
diff --git a/test/integration/hooks.js b/test/integration/hooks.js
new file mode 100644
index 0000000..4236c7f
--- /dev/null
+++ b/test/integration/hooks.js
@@ -0,0 +1,44 @@
+var assert = require('assert');
+var run    = require('./helpers').runMocha;
+var args   = [];
+
+describe('hooks', function() {
+  this.timeout(1000);
+
+  it('are ran in correct order', function(done) {
+    run('cascade.js', args, function(err, res) {
+      var lines, expected;
+
+      assert(!err);
+
+      lines = res.output.split(/[\n․]+/).map(function(line) {
+        return line.trim();
+      }).filter(function(line) {
+        return line.length;
+      }).slice(0, -1);
+
+      expected = [
+        'before one',
+        'before two',
+        'before three',
+        'before each one',
+        'before each two',
+        'before each three',
+        'TEST three',
+        'after each three',
+        'after each two',
+        'after each one',
+        'after three',
+        'after two',
+        'after one'
+      ];
+
+      expected.forEach(function(line, i) {
+        assert.equal(lines[i], line);
+      });
+
+      assert.equal(res.code, 0);
+      done();
+    });
+  });
+});
diff --git a/test/integration/multiple.done.js b/test/integration/multiple.done.js
new file mode 100644
index 0000000..8f9a3cd
--- /dev/null
+++ b/test/integration/multiple.done.js
@@ -0,0 +1,28 @@
+var assert = require('assert');
+var run    = require('./helpers').runMochaJSON;
+var args   = [];
+
+describe('multiple calls to done()', function() {
+  var res;
+
+  this.timeout(1000);
+
+  before(function(done) {
+    run('multiple.done.js', args, function(err, result) {
+      res = result;
+      done(err);
+    });
+  });
+
+  it('results in failures', function() {
+    assert.equal(res.stats.pending, 0);
+    assert.equal(res.stats.passes, 1);
+    assert.equal(res.stats.failures, 1);
+    assert.equal(res.code, 1);
+  });
+
+  it('throws a descriptive error', function() {
+    assert.equal(res.failures[0].err.message,
+      'done() called multiple times');
+  });
+});
diff --git a/test/integration/options.js b/test/integration/options.js
new file mode 100644
index 0000000..db37109
--- /dev/null
+++ b/test/integration/options.js
@@ -0,0 +1,140 @@
+var assert = require('assert');
+var run    = require('./helpers').runMochaJSON;
+var args   = [];
+
+describe('options', function() {
+  this.timeout(2000);
+
+  describe('--async-only', function() {
+
+    before(function() {
+      args = ['--async-only'];
+    });
+
+    it('should fail synchronous specs', function(done) {
+      run('options/async-only.sync.js', args, function(err, res) {
+        assert(!err);
+        assert.equal(res.stats.pending, 0);
+        assert.equal(res.stats.passes, 0);
+        assert.equal(res.stats.failures, 1);
+
+        assert.equal(res.failures[0].title, 'throws an error');
+        assert.equal(res.code, 1);
+        done();
+      });
+    });
+
+    it('should allow asynchronous specs', function(done) {
+      run('options/async-only.async.js', args, function(err, res) {
+        assert(!err);
+        assert.equal(res.stats.pending, 0);
+        assert.equal(res.stats.passes, 1);
+        assert.equal(res.stats.failures, 0);
+
+        assert.equal(res.passes[0].title, 'should pass');
+        assert.equal(res.code, 0);
+        done();
+      });
+    });
+  });
+
+  describe('--bail', function() {
+    before(function() {
+      args = ['--bail'];
+    });
+
+    it('should stop after the first error', function(done) {
+      run('options/bail.js', args, function(err, res) {
+        assert(!err);
+        assert.equal(res.stats.pending, 0);
+        assert.equal(res.stats.passes, 1);
+        assert.equal(res.stats.failures, 1);
+
+        assert.equal(res.passes[0].title, 'should display this spec');
+        assert.equal(res.failures[0].title, 'should only display this error');
+        assert.equal(res.code, 1);
+        done();
+      });
+    });
+  });
+
+  describe('--sort', function() {
+    before(function() {
+      args = ['--sort'];
+    });
+
+    it('should sort tests in alphabetical order', function(done) {
+      run('options/sort*', args, function(err, res) {
+        assert(!err);
+        assert.equal(res.stats.pending, 0);
+        assert.equal(res.stats.passes, 2);
+        assert.equal(res.stats.failures, 0);
+
+        assert.equal(res.passes[0].fullTitle,
+          'alpha should be executed first');
+        assert.equal(res.code, 0);
+        done();
+      });
+    });
+  });
+
+  describe('--delay', function() {
+    before(function() {
+      args = ['--delay'];
+    });
+
+    it('should run the generated test suite', function(done) {
+      run('options/delay.js', args, function(err, res) {
+        assert(!err);
+        assert.equal(res.stats.pending, 0);
+        assert.equal(res.stats.passes, 2);
+        assert.equal(res.stats.failures, 0);
+
+        assert.equal(res.passes[0].title,
+          'should have waited 500ms to run this suite');
+        assert.equal(res.code, 0);
+        done();
+      });
+    });
+  });
+
+  describe('--grep', function() {
+    it('runs specs matching a string', function(done) {
+      args = ['--grep', 'match'];
+      run('options/grep.js', args, function(err, res) {
+        assert(!err);
+        assert.equal(res.stats.pending, 0);
+        assert.equal(res.stats.passes, 2);
+        assert.equal(res.stats.failures, 0);
+        assert.equal(res.code, 0);
+        done();
+      });
+    });
+
+    it('runs specs matching a RegExp', function(done) {
+      args = ['--grep', '.*'];
+      run('options/grep.js', args, function(err, res) {
+        assert(!err);
+        assert.equal(res.stats.pending, 0);
+        assert.equal(res.stats.passes, 2);
+        assert.equal(res.stats.failures, 1);
+        assert.equal(res.code, 1);
+        done();
+      });
+    });
+
+    describe('with --invert', function() {
+      it('runs specs that do not match the pattern', function(done) {
+        args = ['--grep', 'fail', '--invert'];
+        run('options/grep.js', args, function(err, res) {
+          assert(!err);
+          assert.equal(res.stats.pending, 0);
+          assert.equal(res.stats.passes, 2);
+          assert.equal(res.stats.failures, 0);
+          assert.equal(res.code, 0);
+          done();
+        });
+      });
+    });
+  });
+});
diff --git a/test/integration/pending.js b/test/integration/pending.js
new file mode 100644
index 0000000..01ebbdf
--- /dev/null
+++ b/test/integration/pending.js
@@ -0,0 +1,63 @@
+var assert = require('assert');
+var run    = require('./helpers').runMochaJSON;
+var args   = [];
+
+describe('pending', function() {
+  describe('pending specs', function() {
+    this.timeout(1000);
+
+    it('should be created by omitting a function', function(done) {
+      run('pending/spec.js', args, function(err, res) {
+        assert(!err);
+        assert.equal(res.stats.pending, 1);
+        assert.equal(res.stats.passes, 0);
+        assert.equal(res.stats.failures, 0);
+        assert.equal(res.code, 0);
+        done();
+      });
+    });
+  });
+
+  describe('synchronous skip()', function() {
+    this.timeout(1000);
+
+    describe('in spec', function() {
+      it('should immediately skip the spec and run all others', function(done) {
+        run('pending/skip.sync.spec.js', args, function(err, res) {
+          assert(!err);
+          assert.equal(res.stats.pending, 1);
+          assert.equal(res.stats.passes, 1);
+          assert.equal(res.stats.failures, 0);
+          assert.equal(res.code, 0);
+          done();
+        });
+      });
+    });
+
+    describe('in before', function() {
+      it('should skip all suite specs', function(done) {
+        run('pending/skip.sync.before.js', args, function(err, res) {
+          assert(!err);
+          assert.equal(res.stats.pending, 2);
+          assert.equal(res.stats.passes, 0);
+          assert.equal(res.stats.failures, 0);
+          assert.equal(res.code, 0);
+          done();
+        });
+      });
+    });
+
+    describe('in beforeEach', function() {
+      it('should skip all suite specs', function(done) {
+        run('pending/skip.sync.beforeEach.js', args, function(err, res) {
+          assert(!err);
+          assert.equal(res.stats.pending, 2);
+          assert.equal(res.stats.passes, 0);
+          assert.equal(res.stats.failures, 0);
+          assert.equal(res.code, 0);
+          done();
+        });
+      });
+    });
+  });
+});
diff --git a/test/integration/regression.js b/test/integration/regression.js
new file mode 100644
index 0000000..564e922
--- /dev/null
+++ b/test/integration/regression.js
@@ -0,0 +1,24 @@
+var assert = require('assert');
+var run    = require('./helpers').runMocha;
+
+describe('regressions', function() {
+  this.timeout(1000);
+
+  it('issue-1327: should run all 3 specs exactly once', function(done) {
+    var args = [];
+    run('regression/issue-1327.js', args, function(err, res) {
+      var occurences = function(str) {
+        var pattern = new RegExp(str, 'g');
+        return (res.output.match(pattern) || []).length;
+      };
+
+      assert(!err);
+      assert.equal(occurences('testbody1'), 1);
+      assert.equal(occurences('testbody2'), 1);
+      assert.equal(occurences('testbody3'), 1);
+
+      assert.equal(res.code, 1);
+      done();
+    });
+  });
+});
diff --git a/test/integration/timeout.js b/test/integration/timeout.js
new file mode 100644
index 0000000..9f0985c
--- /dev/null
+++ b/test/integration/timeout.js
@@ -0,0 +1,18 @@
+var assert = require('assert');
+var run    = require('./helpers').runMochaJSON;
+var args   = [];
+
+describe('this.timeout()', function() {
+  this.timeout(1000);
+
+  it('is respected by sync and async suites', function(done) {
+    run('timeout.js', args, function(err, res) {
+      assert(!err);
+      assert.equal(res.stats.pending, 0);
+      assert.equal(res.stats.passes, 0);
+      assert.equal(res.stats.failures, 2);
+      assert.equal(res.code, 2);
+      done();
+    });
+  });
+});
diff --git a/test/integration/uncaught.js b/test/integration/uncaught.js
new file mode 100644
index 0000000..cddc52b
--- /dev/null
+++ b/test/integration/uncaught.js
@@ -0,0 +1,37 @@
+var assert = require('assert');
+var run    = require('./helpers').runMochaJSON;
+var args   = [];
+
+describe('uncaught exceptions', function() {
+  this.timeout(1000);
+
+  it('handles uncaught exceptions from hooks', function(done) {
+    run('uncaught.hook.js', args, function(err, res) {
+      assert(!err);
+      assert.equal(res.stats.pending, 0);
+      assert.equal(res.stats.passes, 0);
+      assert.equal(res.stats.failures, 1);
+
+      assert.equal(res.failures[0].fullTitle,
+        'uncaught "before each" hook');
+      assert.equal(res.code, 1);
+      done();
+    });
+  });
+
+  it('handles uncaught exceptions from async specs', function(done) {
+    run('uncaught.js', args, function(err, res) {
+      assert(!err);
+      assert.equal(res.stats.pending, 0);
+      assert.equal(res.stats.passes, 0);
+      assert.equal(res.stats.failures, 2);
+
+      assert.equal(res.failures[0].title,
+        'fails exactly once when a global error is thrown first');
+      assert.equal(res.failures[1].title,
+        'fails exactly once when a global error is thrown second');
+      assert.equal(res.code, 2);
+      done();
+    });
+  });
+});
diff --git a/test/jsapi/index.js b/test/jsapi/index.js
index 5b5c169..2dbbcb0 100644
--- a/test/jsapi/index.js
+++ b/test/jsapi/index.js
@@ -1,4 +1,3 @@
-
 var Mocha = require('../../')
   , path = require('path');
 
@@ -21,7 +20,6 @@ mocha.addFile('test/hook.async.js');
 mocha.addFile('test/acceptance/duration.js');
 mocha.addFile('test/acceptance/fs.js');
 mocha.addFile('test/acceptance/globals.js');
-mocha.addFile('test/acceptance/pending.js');
 mocha.addFile('test/acceptance/timeout.js');
 
 mocha.run(function(){
diff --git a/test/mocha.js b/test/mocha.js
new file mode 100644
index 0000000..46411fc
--- /dev/null
+++ b/test/mocha.js
@@ -0,0 +1,33 @@
+var Mocha = require('../');
+var Test = Mocha.Test;
+
+describe('Mocha', function(){
+  var blankOpts = { reporter: function(){} }; // no output
+
+  describe('.run(fn)', function(){
+    it('should not raise errors if callback was not provided', function(){
+      var mocha = new Mocha(blankOpts);
+      mocha.run();
+    })
+
+    it('should execute the callback when complete', function(done) {
+      var mocha = new Mocha(blankOpts);
+      mocha.run(function(){
+        done();
+      })
+    })
+
+    it('should execute the callback with the number of failures '+
+      'as parameter', function(done) {
+      var mocha = new Mocha(blankOpts);
+      var failingTest = new Test('failing test', function(){
+        throw new Error('such fail');
+      });
+      mocha.suite.addTest(failingTest);
+      mocha.run(function(failures) {
+        failures.should.equal(1);
+        done();
+      });
+    })
+  })
+})
diff --git a/test/ms.js b/test/ms.js
new file mode 100644
index 0000000..1fd9740
--- /dev/null
+++ b/test/ms.js
@@ -0,0 +1,82 @@
+'use strict';
+var ms = require('../lib/ms');
+
+describe('.ms()', function() {
+  // Helpers
+  var time = {
+    minutes: function(n) { return n * 60 * 1000; },
+    hours: function (n) { return n * this.minutes(60); },
+    days: function(n) { return n * this.hours(24); },
+    years: function(n) { return n * this.days(365.25); }
+  };
+  describe('get a value that less than 1 second', function() {
+    it('should return milliseconds representation', function() {
+      ms(200).should.equal('200ms');
+      ms(30).should.equal('30ms');
+      ms(2000).should.not.equal('2000ms');
+    });
+  });
+
+  describe('seconds representation', function() {
+    it('should return short format', function() {
+      ms(2000).should.equal('2s');
+    });
+
+    it('should return long format', function() {
+      ms(2000, { long: true }).should.equal('2 seconds');
+      ms(1000, { long: true }).should.equal('1 second');
+      ms(1010, { long: true }).should.equal('1 second');
+    });
+  });
+
+  describe('minutess representation', function() {
+    it('should return short format', function() {
+      ms(time.minutes(1)).should.equal('1m');
+    });
+
+    it('should return long format', function() {
+      ms(time.minutes(1), { long: true }).should.equal('1 minute');
+      ms(time.minutes(3), { long: true }).should.equal('3 minutes');
+    });
+  });
+
+  describe('hours representation', function() {
+    it('should return short format', function() {
+      ms(time.hours(1)).should.equal('1h');
+    });
+
+    it('should return long format', function() {
+      ms(time.hours(1), { long: true }).should.equal('1 hour');
+      ms(time.hours(3), { long: true }).should.equal('3 hours');
+    });
+  });
+
+  describe('days representation', function() {
+    it('should return short format', function() {
+      ms(time.days(1)).should.equal('1d');
+    });
+
+    it('should return long format', function() {
+      ms(time.days(1), { long: true }).should.equal('1 day');
+      ms(time.days(3), { long: true }).should.equal('3 days');
+    });
+  });
+
+  describe('Getting string value', function() {
+    it('should return the milliseconds representation(Number)', function() {
+      ms('1 second').should.equal(1000);
+      
+      ms('1 minute').should.equal(time.minutes(1));
+      ms('6 minutes').should.equal(time.minutes(6));
+
+      ms('1 hour').should.equal(time.hours(1));
+      ms('5 hours').should.equal(time.hours(5));
+
+      ms('1 day').should.equal(time.days(1));
+      ms('3 days').should.equal(time.days(3));
+
+      ms('1 year').should.equal(time.years(1));
+      ms('2 years').should.equal(time.years(2));
+    });
+  });
+});
diff --git a/test/regression/issue1327/case.js b/test/regression/issue1327/case.js
deleted file mode 100644
index 295ec12..0000000
--- a/test/regression/issue1327/case.js
+++ /dev/null
@@ -1,14 +0,0 @@
-var debug = require('debug')('mocha:issue1327');
-it("test 1", function() {
-    debug("This runs only once.");
-    process.nextTick(function() {
-        throw "Too bad";
-    });
-});
-it("test 2", function() {
-    debug("This should run once - Previously wasn't called at all.");
-});
-it("test 3", function() {
-    debug("This used to run twice.");
-    throw new Error("OUCH");
-});
diff --git a/test/regression/issue1327/control.js b/test/regression/issue1327/control.js
deleted file mode 100644
index b77555d..0000000
--- a/test/regression/issue1327/control.js
+++ /dev/null
@@ -1,10 +0,0 @@
-var assert = require("assert"),
-    fs = require("fs");
-
-describe("GitHub issue #1327: expected behavior of case.js", function() {
-    it("should have run 3 tests", function() {
-        var results = JSON.parse(fs.readFileSync(
-            "test-outputs/issue1327/case-out.json"));
-        results.stats.tests.should.equal(3);
-    });
-});
diff --git a/test/reporters/base.js b/test/reporters/base.js
index e8fe1e6..5276979 100644
--- a/test/reporters/base.js
+++ b/test/reporters/base.js
@@ -1,68 +1,160 @@
-var Base = require('../../lib/reporters/base');
+var Base   = require('../../lib/reporters/base')
+  , Assert = require('assert').AssertionError;
+
+function makeTest(err) {
+  return {
+    err: err,
+    fullTitle: function () {
+      return 'test title';
+    }
+  };
+}
 
 describe('Base reporter', function () {
+  var stdout
+    , stdoutWrite
+    , useColors;
+
+  beforeEach(function () {
+    stdout = [];
+    stdoutWrite = process.stdout.write;
+    process.stdout.write = function (string) {
+      stdout.push(string);
+    };
+    useColors = Base.useColors;
+    Base.useColors = false;
+  });
 
-  it('should show diffs with showDiff property set', function () {
-    var err = new Error('test'),
-      stderr = [],
-      stderrWrite = process.stderr.write,
-      errOut;
+  afterEach(function () {
+    process.stdout.write = stdoutWrite;
+    Base.useColors = useColors;
+  });
 
-    err.actual = "a1";
-    err.expected = "e1";
-    err.showDiff = true;
-    var test = {
-      err: err,
-      fullTitle: function () {
-        return 'title';
-      }
-    };
+  describe('showDiff', function() {
+    it('should show diffs by default', function () {
+      var err = new Assert({ actual: 'foo', expected: 'bar' })
+        , errOut;
 
-    process.stderr.write = function (string) {
-      stderr.push(string);
-    };
+      var test = makeTest(err);
 
-    Base.list([test]);
+      Base.list([test]);
 
-    process.stderr.write = stderrWrite;
+      errOut = stdout.join('\n');
+      errOut.should.match(/\- actual/);
+      errOut.should.match(/\+ expected/);
+    });
 
-    errOut = stderr.join('\n');
+    it('should show diffs if property set to `true`', function () {
+      var err = new Assert({ actual: 'foo', expected: 'bar' })
+        , errOut;
 
-    errOut.should.match(/test/);
-    errOut.should.match(/actual/);
-    errOut.should.match(/expected/);
+      err.showDiff = true;
+      var test = makeTest(err);
+
+
+      Base.list([test]);
+
+      errOut = stdout.join('\n');
+      errOut.should.match(/\- actual/);
+      errOut.should.match(/\+ expected/);
+    });
+
+    it('should not show diffs when showDiff property set to `false`', function () {
+      var err = new Assert({ actual: 'foo', expected: 'bar' })
+        , errOut;
+
+      err.showDiff = false;
+      var test = makeTest(err);
+
+      Base.list([test]);
+
+      errOut = stdout.join('\n');
+      errOut.should.not.match(/\- actual/);
+      errOut.should.not.match(/\+ expected/);
+    });
+
+    it('should not show diffs when expected is not defined', function () {
+      var err = new Error('ouch')
+        , errOut;
+
+      var test = makeTest(err);
+
+      Base.list([test]);
 
+      errOut = stdout.join('\n');
+      errOut.should.not.match(/\- actual/);
+      errOut.should.not.match(/\+ expected/);
+    });
+
+  });
+
+  describe('Getting two strings', function() {
+    // Fix regression V1.2.1(see: issue #1241)
+    it('should show strings diff as is', function () {
+      var err = new Error('test'),
+        errOut;
+
+      err.actual = 'foo\nbar';
+      err.expected = 'foo\nbaz';
+      err.showDiff = true;
+      var test = makeTest(err);
+
+      Base.list([test]);
+
+      errOut = stdout.join('\n');
+
+      errOut.should.not.match(/"foo\\nbar"/);
+      errOut.should.match(/foo/).and.match(/bar/);
+      errOut.should.match(/test/);
+      errOut.should.match(/actual/);
+      errOut.should.match(/expected/);
+    });
   });
 
-  it('should not show diffs when showDiff property set', function () {
+  it('should stringify objects', function () {
     var err = new Error('test'),
-      stderr = [],
-      stderrWrite = process.stderr.write,
       errOut;
 
-    err.actual = "a1";
-    err.expected = "e1";
-    err.showDiff = false;
-    var test = {
-      err: err,
-      fullTitle: function () {
-        return 'title';
-      }
-    };
+    err.actual = {key:"a1"};
+    err.expected = {key:"e1"};
+    err.showDiff = true;
+    var test = makeTest(err);
 
-    process.stderr.write = function (string) {
-      stderr.push(string);
+    Base.list([test]);
+
+    errOut = stdout.join('\n');
+    errOut.should.match(/"key"/);
+    errOut.should.match(/test/);
+    errOut.should.match(/\- actual/);
+    errOut.should.match(/\+ expected/);
+  });
+
+  it('should remove message from stack', function () {
+    var err = {
+      message: 'Error',
+      stack: 'Error\nfoo\nbar',
+      showDiff: false
     };
+    var test = makeTest(err);
 
     Base.list([test]);
 
-    process.stderr.write = stderrWrite;
+    var errOut = stdout.join('\n').trim();
+    errOut.should.equal('1) test title:\n     Error\n  foo\n  bar')
+  });
 
-    errOut = stderr.join('\n');
+  it('should not modify stack if it does not contain message', function () {
+    var err = {
+      message: 'Error',
+      stack: 'foo\nbar',
+      showDiff: false
+    };
+    var test = makeTest(err);
 
-    errOut.should.match(/test/);
-    errOut.should.not.match(/actual/);
-    errOut.should.not.match(/expected/);
+    Base.list([test]);
 
+    var errOut = stdout.join('\n').trim();
+    errOut.should.equal('1) test title:\n     Error\n  foo\n  bar')
   });
+
 });
diff --git a/test/reporters/json.js b/test/reporters/json.js
index 56173bf..e7b8955 100644
--- a/test/reporters/json.js
+++ b/test/reporters/json.js
@@ -1,4 +1,3 @@
-
 var Mocha = require('../../')
   , Suite = Mocha.Suite
   , Runner = Mocha.Runner
@@ -14,7 +13,7 @@ describe('json reporter', function(){
     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';
@@ -38,7 +37,7 @@ describe('json reporter', function(){
 
        done();
      });
-  })
+  });
 
   it('should have 1 test pending', function(done) {
     var testTitle = 'json test 1';
@@ -59,4 +58,4 @@ describe('json reporter', function(){
      });
   })
 
-})
+});
diff --git a/test/reporters/nyan.js b/test/reporters/nyan.js
index 8a54458..e574808 100644
--- a/test/reporters/nyan.js
+++ b/test/reporters/nyan.js
@@ -27,4 +27,4 @@ describe('nyan face', function () {
     nyanCat.face.call(nyanCat).should.equal('( - .-)');
     done();
   });
-})
+});
diff --git a/test/runnable.js b/test/runnable.js
index 6127b4e..2508a5f 100644
--- a/test/runnable.js
+++ b/test/runnable.js
@@ -1,5 +1,5 @@
-
 var mocha = require('../')
+  , utils = mocha.utils
   , Runnable = mocha.Runnable
   , EventEmitter = require('events').EventEmitter;
 
@@ -233,9 +233,7 @@ describe('Runnable(title, fn)', function(){
           });
 
           test.run(function(err) {
-            if (err !== null) {
-              throw new should.AssertionError('err should be null');
-            }
+            err.message.should.equal(utils.undefinedError().message);
             done();
           })
         });
diff --git a/test/runner.js b/test/runner.js
index f5ee8cc..a2ce512 100644
--- a/test/runner.js
+++ b/test/runner.js
@@ -1,4 +1,3 @@
-
 var mocha = require('../')
   , Suite = mocha.Suite
   , Runner = mocha.Runner
@@ -182,6 +181,20 @@ describe('Runner', function(){
     })
   })
 
+  describe('.hook(name, fn)', function(){
+    it('should execute hooks after failed test if suite bail is true', function(done){
+      runner.fail({});
+      suite.bail(true);
+      suite.afterEach(function(){
+        suite.afterAll(function() {
+          done();
+        })
+      });
+      runner.hook('afterEach', function(){});
+      runner.hook('afterAll', function(){});
+    })
+  })
+
   describe('.fail(test, err)', function(){
     it('should increment .failures', function(){
       runner.failures.should.equal(0);
@@ -206,6 +219,42 @@ describe('Runner', function(){
       });
       runner.fail(test, err);
     })
+
+    it('should emit a helpful message when failed with a string', function(done){
+      var test = {}, err = 'string';
+      runner.on('fail', function(test, err){
+        err.message.should.equal('the string "string" was thrown, throw an Error :)');
+        done();
+      });
+      runner.fail(test, err);
+    })
+
+    it('should emit a the error when failed with an Error', function(done){
+      var test = {}, err = new Error('an error message');
+      runner.on('fail', function(test, err){
+        err.message.should.equal('an error message');
+        done();
+      });
+      runner.fail(test, err);
+    })
+
+    it('should emit a helpful message when failed with an Object', function(done){
+      var test = {}, err = { x: 1 };
+      runner.on('fail', function(test, err){
+        err.message.should.equal('the object {\n  "x": 1\n} was thrown, throw an Error :)');
+        done();
+      });
+      runner.fail(test, err);
+    })
+
+    it('should emit a helpful message when failed with an Array', function(done){
+      var test = {}, err = [1,2];
+      runner.on('fail', function(test, err){
+        err.message.should.equal('the array [\n  1\n  2\n] was thrown, throw an Error :)');
+        done();
+      });
+      runner.fail(test, err);
+    })
   })
 
   describe('.failHook(hook, err)', function(){
@@ -241,5 +290,51 @@ describe('Runner', function(){
       runner.failHook(hook, err);
       done();
     })
-  })
-})
+  });
+
+  describe('stackTrace', function() {
+    var stack = [ 'AssertionError: foo bar'
+      , 'at EventEmitter.<anonymous> (/usr/local/dev/test.js:16:12)'
+      , 'at Context.<anonymous> (/usr/local/dev/test.js:19:5)'
+      , 'Test.Runnable.run (/usr/local/lib/node_modules/mocha/lib/runnable.js:244:7)'
+      , 'Runner.runTest (/usr/local/lib/node_modules/mocha/lib/runner.js:374:10)'
+      , '/usr/local/lib/node_modules/mocha/lib/runner.js:452:12'
+      , 'next (/usr/local/lib/node_modules/mocha/lib/runner.js:299:14)'
+      , '/usr/local/lib/node_modules/mocha/lib/runner.js:309:7'
+      , 'next (/usr/local/lib/node_modules/mocha/lib/runner.js:248:23)'
+      , 'Immediate._onImmediate (/usr/local/lib/node_modules/mocha/lib/runner.js:276:5)'
+      , 'at processImmediate [as _immediateCallback] (timers.js:321:17)'];
+
+    describe('shortStackTrace', function() {
+      it('should prettify the stack-trace', function(done) {
+        var hook = {},
+            err = new Error();
+        // Fake stack-trace
+        err.stack = stack.join('\n');
+
+        runner.on('fail', function(hook, err){
+          err.stack.should.equal(stack.slice(0,3).join('\n'));
+          done();
+        });
+        runner.failHook(hook, err);
+      });
+    });
+
+    describe('longStackTrace', function() {
+      it('should display the full stack-trace', function(done) {
+        var hook = {},
+            err = new Error();
+        // Fake stack-trace
+        err.stack = stack.join('\n');
+        // Add --stack-trace option
+        runner.fullStackTrace = true;
+
+        runner.on('fail', function(hook, err){
+          err.stack.should.equal(stack.join('\n'));
+          done();
+        });
+        runner.failHook(hook, err);
+      });
+    });
+  });
+});
diff --git a/test/suite.js b/test/suite.js
index e457d64..011b3fb 100644
--- a/test/suite.js
+++ b/test/suite.js
@@ -1,4 +1,3 @@
-
 var mocha = require('../')
   , Context = mocha.Context
   , Suite = mocha.Suite
diff --git a/test/utils.js b/test/utils.js
index f5ea9dc..524a0e0 100644
--- a/test/utils.js
+++ b/test/utils.js
@@ -1,20 +1,148 @@
-
 var mocha = require('..');
 var utils = mocha.utils;
-var clean = utils.clean;
 
-describe('utils', function(){
-  describe('.clean()', function(){
-    it('should remove the wrapping function declaration', function(){
+describe('utils', function() {
+  describe('.clean()', function() {
+    var clean = utils.clean;
+    it('should remove the wrapping function declaration', function() {
       clean('function  (one, two, three)  {\n//code\n}').should.equal('//code');
-    })
+    });
 
-    it('should remove space character indentation from the function body', function(){
+    it('should handle newlines in the function declaration', function() {
+      clean('function  (one, two, three)\n  {\n//code\n}').should.equal('//code');
+    });
+
+    it('should remove space character indentation from the function body', function() {
       clean('  //line1\n    //line2').should.equal('//line1\n  //line2');
-    })
+    });
 
-    it('should remove tab character indentation from the function body', function(){
+    it('should remove tab character indentation from the function body', function() {
       clean('\t//line1\n\t\t//line2').should.equal('//line1\n\t//line2');
+    });
+  });
+
+  describe('.isBuffer()', function() {
+    var isBuffer = utils.isBuffer;
+    it('should test if object is a Buffer', function() {
+      isBuffer(new Buffer([0x01])).should.equal(true);
+      isBuffer({}).should.equal(false);
+    })
+  });
+
+  describe('.map()', function() {
+    var map = utils.map;
+    it('should behave same as Array.prototype.map', function() {
+      var arr = [1, 2, 3];
+      map(arr, JSON.stringify).should.eql(arr.map(JSON.stringify));
+    });
+
+    it('should call the callback with 3 arguments[currentValue, index, array]', function() {
+      var index = 0;
+      map([1, 2, 3], function(e, i, arr) {
+        e.should.equal(arr[index]);
+        i.should.equal(index++);
+      })
+    });
+
+    it('should apply with the given scope', function() {
+      var scope = {};
+      map(['a', 'b', 'c'], function() {
+        this.should.equal(scope);
+      }, scope);
+    });
+  });
+
+  describe('.parseQuery()', function() {
+    var parseQuery = utils.parseQuery;
+    it('should get queryString and return key-value object', function() {
+      parseQuery('?foo=1&bar=2&baz=3').should.eql({
+        foo: 1,
+        bar: 2,
+        baz: 3
+      });
+
+      parseQuery('?r1=^@(?!.*\\)$)&r2=m{2}&r3=^co.*').should.eql({
+        r1: '^@(?!.*\\)$)',
+        r2: 'm{2}',
+        r3: '^co.*'
+      });
     })
-  })
-})
+  });
+
+  describe('.stackTraceFilter()', function() {
+    describe('on node', function() {
+      var filter = utils.stackTraceFilter();
+      it('should get a stack-trace as a string and prettify it', function() {
+        var stack = [ 'AssertionError: foo bar'
+            , 'at EventEmitter.<anonymous> (/usr/local/dev/test.js:16:12)'
+            , 'at Context.<anonymous> (/usr/local/dev/test.js:19:5)'
+            , 'Test.Runnable.run (/usr/local/lib/node_modules/mocha/lib/runnable.js:244:7)'
+            , 'Runner.runTest (/usr/local/lib/node_modules/mocha/lib/runner.js:374:10)'
+            , '/usr/local/lib/node_modules/mocha/lib/runner.js:452:12'
+            , 'next (/usr/local/lib/node_modules/mocha/lib/runner.js:299:14)'
+            , '/usr/local/lib/node_modules/mocha/lib/runner.js:309:7'
+            , 'next (/usr/local/lib/node_modules/mocha/lib/runner.js:248:23)'
+            , 'Immediate._onImmediate (/usr/local/lib/node_modules/mocha/lib/runner.js:276:5)'
+            , 'at processImmediate [as _immediateCallback] (timers.js:321:17)'];
+        filter(stack.join('\n')).should.equal(stack.slice(0,3).join('\n'));
+
+        stack = [ 'AssertionError: bar baz'
+          , 'at /usr/local/dev/some-test-file.js:25:8'
+          , 'at tryCatcher (/usr/local/dev/own/tmp/node_modules/bluebird/js/main/util.js:24:31)'
+          , 'at Promise._resolveFromResolver (/usr/local/dev/own/tmp/node_modules/bluebird/js/main/promise.js:439:31)'
+          , 'at new Promise (/usr/local/dev/own/tmp/node_modules/bluebird/js/main/promise.js:53:37)'
+          , 'at yourFunction (/usr/local/dev/own/tmp/test1.js:24:13)'
+          , 'at Context.<anonymous> (/usr/local/dev/some-test-file:30:4)'
+          , 'Test.Runnable.run (/usr/local/lib/node_modules/mocha/lib/runnable.js:218:15)'
+          , 'next (/usr/local/lib/node_modules/mocha/lib/runner.js:248:23)'
+          , 'Immediate._onImmediate (/usr/local/lib/node_modules/mocha/lib/runner.js:276:5)'
+          , 'at processImmediate [as _immediateCallback] (timers.js:321:17)'];
+        filter(stack.join('\n')).should.equal(stack.slice(0,2).concat(stack.slice(5,7)).join('\n'));
+      });
+
+      it('should ignore bower and components files', function() {
+        var stack = ['Error: failed'
+          , 'at assert (index.html:11:26)'
+          , 'at Context.<anonymous> (test.js:17:18)'
+          , 'at bower_components/should/should.js:4827:7'
+          , 'at next (file:///.../bower_components/should/should.js:4766:23)'
+          , 'at components/should/5.0.0/should.js:4827:7'
+          , 'at next (file:///.../components/should/5.0.0/should.js:4766:23)'
+          , 'at file:///.../bower_components/mocha/mocha.js:4794:5'
+          , 'at timeslice (.../components/mocha/mocha.js:6218:27)'
+          , 'at Test.require.register.Runnable.run (file:///.../components/mochajs/mocha/2.1.0/mocha.js:4463:15)'
+          , 'at Runner.require.register.Runner.runTest (file:///.../components/mochajs/mocha/2.1.0/mocha.js:4892:10)'
+          , 'at file:///.../components/mochajs/mocha/2.1.0/mocha.js:4970:12'
+          , 'at next (file:///.../components/mochajs/mocha/2.1.0/mocha.js:4817:14)'];
+        filter(stack.join('\n')).should.equal(stack.slice(0,7).join('\n'));
+      });
+    });
+
+    describe('on browser', function() {
+      var filter;
+      before(function() {
+        global.document = true;
+        global.location = { href: 'localhost:3000/foo/bar/index.html' };
+        filter = utils.stackTraceFilter();
+      });
+      it('should strip out bower and components too', function() {
+        var stack = ['Error: failed'
+          , 'at assert (index.html:11:26)'
+          , 'at Context.<anonymous> (test.js:17:18)'
+          , 'at bower_components/should/should.js:4827:7'
+          , 'at next (localhost:3000/foo/bar/bower_components/should/should.js:4766:23)'
+          , 'at components/should/5.0.0/should.js:4827:7'
+          , 'at next (localhost:3000/foo/bar/components/should/5.0.0/should.js:4766:23)'
+          , 'at Runner.require.register.Runner.runTest (localhost:3000/foo/bar/node_modules/mocha.js:4892:10)'
+          , 'at localhost:3000/foo/bar/node_modules/mocha.js:4970:12'
+          , 'at next (localhost:3000/foo/bar/node_modules/mocha.js:4817:14)'];
+        filter(stack.join('\n')).should.equal(stack.slice(0,3).join('\n'));
+      });
+
+      after(function() {
+        delete global.document;
+        delete global.location;
+      });
+    });
+  });
+});

-- 
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