├── .gitignore ├── .travis.yml ├── History.md ├── LICENSE ├── Makefile ├── Readme.md ├── bin ├── _mocha └── mocha ├── bower.json ├── component.json ├── editors └── JavaScript mocha.tmbundle │ ├── Snippets │ ├── bdd - after each.tmSnippet │ ├── bdd - after.tmSnippet │ ├── bdd - before each.tmSnippet │ ├── bdd - before.tmSnippet │ ├── bdd - describe.tmSnippet │ ├── bdd - it.tmSnippet │ ├── tdd - assert.tmSnippet │ ├── tdd - assert_deepEqual.tmSnippet │ ├── tdd - assert_equal.tmSnippet │ ├── tdd - assert_fail.tmSnippet │ ├── tdd - assert_isFunction.tmSnippet │ ├── tdd - setup.tmSnippet │ ├── tdd - suite.tmSnippet │ ├── tdd - teardown.tmSnippet │ └── tdd - test.tmSnippet │ └── info.plist ├── images ├── error.png └── ok.png ├── index.js ├── lib ├── browser │ ├── debug.js │ ├── events.js │ ├── fs.js │ ├── path.js │ ├── progress.js │ └── tty.js ├── context.js ├── hook.js ├── interfaces │ ├── bdd.js │ ├── exports.js │ ├── index.js │ ├── qunit.js │ └── tdd.js ├── mocha.js ├── ms.js ├── reporters │ ├── base.js │ ├── doc.js │ ├── dot.js │ ├── html-cov.js │ ├── html.js │ ├── index.js │ ├── json-cov.js │ ├── json-stream.js │ ├── json.js │ ├── landing.js │ ├── list.js │ ├── markdown.js │ ├── min.js │ ├── nyan.js │ ├── progress.js │ ├── spec.js │ ├── tap.js │ ├── templates │ │ ├── coverage.jade │ │ ├── menu.jade │ │ ├── script.html │ │ └── style.html │ └── xunit.js ├── runnable.js ├── runner.js ├── suite.js ├── template.html ├── test.js └── utils.js ├── media └── logo.svg ├── mocha.css ├── mocha.js ├── package.json ├── support ├── compile.js ├── foot.js ├── head.js └── tail.js └── test ├── acceptance ├── context.js ├── diffs.js ├── duration.js ├── fixtures │ ├── css.in │ └── css.out ├── fs.js ├── glob │ ├── glob.js │ └── glob.sh ├── globals.js ├── http.js ├── interfaces │ ├── bdd.js │ ├── exports.js │ ├── qunit.js │ └── tdd.js ├── misc │ ├── asyncOnly.js │ ├── bail.js │ ├── cascade.js │ ├── exit.js │ ├── grep.js │ ├── many.js │ ├── nontty.js │ └── only │ │ ├── bdd.js │ │ ├── qunit.js │ │ └── tdd.js ├── multiple.done.js ├── pending.js ├── require │ ├── a.js │ ├── b.coffee │ ├── c.js │ ├── d.coffee │ └── require.js ├── required-tokens.js ├── root.js ├── sort │ ├── alpha.js │ └── beta.js ├── test.coffee ├── test.foo ├── timeout.js ├── uncaught.js └── utils.js ├── browser ├── array.js ├── index.html ├── large.html ├── large.js ├── opts.html └── opts.js ├── compiler └── foo.js ├── grep.js ├── hook.async.js ├── hook.err.js ├── hook.sync.js ├── hook.sync.nested.js ├── hook.timeout.js ├── http.meta.2.js ├── http.meta.js ├── jsapi └── index.js ├── mocha.opts ├── reporters └── nyan.js ├── runnable.js ├── runner.js ├── suite.js └── utils.js /.gitignore: -------------------------------------------------------------------------------- 1 | coverage.html 2 | lib-cov 3 | .DS_Store 4 | node_modules 5 | *.sock 6 | testing 7 | _mocha.js 8 | my-reporter.js 9 | *.sw* 10 | lib/browser/diff.js 11 | .idea 12 | *.iml 13 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 0.8 4 | - 0.10 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | (The MIT License) 2 | 3 | Copyright (c) 2011-2013 TJ Holowaychuk 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | 'Software'), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 21 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 22 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | REPORTER ?= dot 3 | TM_BUNDLE = JavaScript\ mocha.tmbundle 4 | SRC = $(shell find lib -name "*.js" -type f | sort) 5 | SUPPORT = $(wildcard support/*.js) 6 | 7 | all: mocha.js 8 | 9 | lib/browser/diff.js: node_modules/diff/diff.js 10 | cp node_modules/diff/diff.js lib/browser/diff.js 11 | 12 | mocha.js: $(SRC) $(SUPPORT) lib/browser/diff.js 13 | @node support/compile $(SRC) 14 | @cat \ 15 | support/head.js \ 16 | _mocha.js \ 17 | support/tail.js \ 18 | support/foot.js \ 19 | > mocha.js 20 | 21 | clean: 22 | rm -f mocha.js 23 | rm -fr lib-cov 24 | rm -f coverage.html 25 | 26 | test-cov: lib-cov 27 | @COV=1 $(MAKE) test REPORTER=html-cov > coverage.html 28 | 29 | lib-cov: 30 | @rm -fr ./$@ 31 | @jscoverage lib $@ 32 | 33 | test: test-unit 34 | 35 | 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 36 | 37 | test-jsapi: 38 | @node test/jsapi 39 | 40 | test-unit: 41 | @./bin/mocha \ 42 | --reporter $(REPORTER) \ 43 | test/acceptance/*.js \ 44 | --growl \ 45 | test/*.js 46 | 47 | test-compilers: 48 | @./bin/mocha \ 49 | --reporter $(REPORTER) \ 50 | --compilers coffee:coffee-script,foo:./test/compiler/foo \ 51 | test/acceptance/test.coffee \ 52 | test/acceptance/test.foo 53 | 54 | test-requires: 55 | @./bin/mocha \ 56 | --reporter $(REPORTER) \ 57 | --compilers coffee:coffee-script \ 58 | --require test/acceptance/require/a.js \ 59 | --require test/acceptance/require/b.coffee \ 60 | --require test/acceptance/require/c.js \ 61 | --require test/acceptance/require/d.coffee \ 62 | test/acceptance/require/require.js 63 | 64 | test-bdd: 65 | @./bin/mocha \ 66 | --reporter $(REPORTER) \ 67 | --ui bdd \ 68 | test/acceptance/interfaces/bdd 69 | 70 | test-tdd: 71 | @./bin/mocha \ 72 | --reporter $(REPORTER) \ 73 | --ui tdd \ 74 | test/acceptance/interfaces/tdd 75 | 76 | test-qunit: 77 | @./bin/mocha \ 78 | --reporter $(REPORTER) \ 79 | --ui qunit \ 80 | test/acceptance/interfaces/qunit 81 | 82 | test-exports: 83 | @./bin/mocha \ 84 | --reporter $(REPORTER) \ 85 | --ui exports \ 86 | test/acceptance/interfaces/exports 87 | 88 | test-grep: 89 | @./bin/mocha \ 90 | --reporter $(REPORTER) \ 91 | --grep fast \ 92 | test/acceptance/misc/grep 93 | 94 | test-invert: 95 | @./bin/mocha \ 96 | --reporter $(REPORTER) \ 97 | --grep slow \ 98 | --invert \ 99 | test/acceptance/misc/grep 100 | 101 | test-bail: 102 | @./bin/mocha \ 103 | --reporter $(REPORTER) \ 104 | --bail \ 105 | test/acceptance/misc/bail 106 | 107 | test-async-only: 108 | @./bin/mocha \ 109 | --reporter $(REPORTER) \ 110 | --async-only \ 111 | test/acceptance/misc/asyncOnly 112 | 113 | test-glob: 114 | @./test/acceptance/glob/glob.sh 115 | 116 | test-reporters: 117 | @./bin/mocha \ 118 | --reporter $(REPORTER) \ 119 | test/reporters/*.js 120 | 121 | test-only: 122 | @./bin/mocha \ 123 | --reporter $(REPORTER) \ 124 | --ui tdd \ 125 | test/acceptance/misc/only/tdd 126 | 127 | @./bin/mocha \ 128 | --reporter $(REPORTER) \ 129 | --ui bdd \ 130 | test/acceptance/misc/only/bdd 131 | 132 | @./bin/mocha \ 133 | --reporter $(REPORTER) \ 134 | --ui qunit \ 135 | test/acceptance/misc/only/qunit 136 | 137 | test-sort: 138 | @./bin/mocha \ 139 | --reporter $(REPORTER) \ 140 | --sort \ 141 | test/acceptance/sort 142 | 143 | non-tty: 144 | @./bin/mocha \ 145 | --reporter dot \ 146 | test/acceptance/interfaces/bdd 2>&1 > /tmp/dot.out 147 | 148 | @echo dot: 149 | @cat /tmp/dot.out 150 | 151 | @./bin/mocha \ 152 | --reporter list \ 153 | test/acceptance/interfaces/bdd 2>&1 > /tmp/list.out 154 | 155 | @echo list: 156 | @cat /tmp/list.out 157 | 158 | @./bin/mocha \ 159 | --reporter spec \ 160 | test/acceptance/interfaces/bdd 2>&1 > /tmp/spec.out 161 | 162 | @echo spec: 163 | @cat /tmp/spec.out 164 | 165 | tm: 166 | @open editors/$(TM_BUNDLE) 167 | 168 | .PHONY: test-cov test-jsapi test-compilers watch test test-all test-bdd test-tdd test-qunit test-exports test-unit non-tty test-grep tm clean 169 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | ______ 2 | 3 | A slightly modified version of mocha that uses [co](https://github.com/visionmedia/co) to provide generator function support. 4 | 5 | ```JavaScript 6 | describe("New customer", function() { 7 | var business; 8 | var customer; 9 | 10 | before(function*() { 11 | yield setup(); 12 | business = yield Account.create("FooBar Inc"); 13 | customer = yield business.addCustomer("Mr. Baz"); 14 | }); 15 | 16 | it("should be the only customer", function*() { 17 | var count = yield Customer.count({ businessID: business.id }); 18 | assert.equal(count, 1); 19 | }); 20 | 21 | after(function*() { 22 | yield teardown(); 23 | }); 24 | }); 25 | ``` 26 | 27 | ## Installation 28 | 29 | ``` 30 | npm install mocha-co 31 | ``` 32 | 33 | If you plan to install it globally, please be sure to uninstall any existing mocha aready installed with 34 | 35 | ``` 36 | sudo npm uninstall -g mocha 37 | ``` 38 | 39 | and then install mocha-co 40 | 41 | ``` 42 | sudo npm install -g mocha-co 43 | ``` 44 | 45 | Then use it as usual. 46 | 47 | Original idea: https://labnotes.org/yield-to-the-test-using-mocha-with-es6-generators/ 48 | 49 | Upstream README below 50 | 51 | ______ 52 | 53 | ## Description 54 | 55 | Mocha is a simple, flexible, fun JavaScript test framework for node.js and the browser. For more information view the [documentation](http://mochajs.org/). 56 | 57 | ## Contributors 58 | 59 | ``` 60 | 61 | project : mocha 62 | repo age : 2 years, 4 months ago 63 | commits : 1314 64 | active : 372 days 65 | files : 141 66 | authors : 67 | 582 TJ Holowaychuk 44.3% 68 | 389 Tj Holowaychuk 29.6% 69 | 46 Travis Jeffery 3.5% 70 | 31 Guillermo Rauch 2.4% 71 | 13 Attila Domokos 1.0% 72 | 10 John Firebaugh 0.8% 73 | 8 Jo Liss 0.6% 74 | 7 Nathan Rajlich 0.5% 75 | 6 Mike Pennisi 0.5% 76 | 6 James Carr 0.5% 77 | 6 Brendan Nee 0.5% 78 | 5 Aaron Heckmann 0.4% 79 | 5 Ryunosuke SATO 0.4% 80 | 4 hokaccha 0.3% 81 | 4 Joshua Krall 0.3% 82 | 4 Xavier Antoviaque 0.3% 83 | 3 Jesse Dailey 0.2% 84 | 3 Forbes Lindesay 0.2% 85 | 3 Sindre Sorhus 0.2% 86 | 3 Cory Thomas 0.2% 87 | 3 Fredrik Enestad 0.2% 88 | 3 Ben Lindsey 0.2% 89 | 3 Tyson Tate 0.2% 90 | 3 Mathieu Desvé 0.2% 91 | 3 Valentin Agachi 0.2% 92 | 3 Wil Moore III 0.2% 93 | 3 Merrick Christensen 0.2% 94 | 3 eiji.ienaga 0.2% 95 | 3 fool2fish 0.2% 96 | 3 Nathan Bowser 0.2% 97 | 3 Paul Miller 0.2% 98 | 2 Juzer Ali 0.2% 99 | 2 Pete Hawkins 0.2% 100 | 2 Jonas Westerlund 0.2% 101 | 2 Arian Stolwijk 0.2% 102 | 2 Quang Van 0.2% 103 | 2 Glen Mailer 0.2% 104 | 2 Justin DuJardin 0.2% 105 | 2 FARKAS Máté 0.2% 106 | 2 Raynos 0.2% 107 | 2 Michael Riley 0.2% 108 | 2 Michael Schoonmaker 0.2% 109 | 2 Domenic Denicola 0.2% 110 | 2 Simon Gaeremynck 0.2% 111 | 2 Konstantin Käfer 0.2% 112 | 2 domenic 0.2% 113 | 2 Paul Armstrong 0.2% 114 | 2 fcrisci 0.2% 115 | 2 Alexander Early 0.2% 116 | 2 Shawn Krisman 0.2% 117 | 2 Brian Beck 0.2% 118 | 2 Nathan Alderson 0.2% 119 | 2 David Henderson 0.2% 120 | 2 Timo Tijhof 0.2% 121 | 2 Ian Storm Taylor 0.2% 122 | 2 travis jeffery 0.2% 123 | 1 Matt Smith 0.1% 124 | 1 Matthew Shanley 0.1% 125 | 1 Nathan Black 0.1% 126 | 1 Phil Sung 0.1% 127 | 1 R56 0.1% 128 | 1 Refael Ackermann 0.1% 129 | 1 Richard Dingwall 0.1% 130 | 1 Romain Prieto 0.1% 131 | 1 Roman Neuhauser 0.1% 132 | 1 Roman Shtylman 0.1% 133 | 1 Russ Bradberry 0.1% 134 | 1 Russell Munson 0.1% 135 | 1 Rustem Mustafin 0.1% 136 | 1 Salehen Shovon Rahman 0.1% 137 | 1 Sasha Koss 0.1% 138 | 1 Seiya Konno 0.1% 139 | 1 Simon Goumaz 0.1% 140 | 1 Standa Opichal 0.1% 141 | 1 Stephen Mathieson 0.1% 142 | 1 Steve Mason 0.1% 143 | 1 Tapiwa Kelvin 0.1% 144 | 1 Teddy Zeenny 0.1% 145 | 1 Tim Ehat 0.1% 146 | 1 Vadim Nikitin 0.1% 147 | 1 Victor Costan 0.1% 148 | 1 Will Langstroth 0.1% 149 | 1 Yanis Wang 0.1% 150 | 1 Yuest Wang 0.1% 151 | 1 abrkn 0.1% 152 | 1 airportyh 0.1% 153 | 1 badunk 0.1% 154 | 1 fengmk2 0.1% 155 | 1 grasGendarme 0.1% 156 | 1 lodr 0.1% 157 | 1 tgautier@yahoo.com 0.1% 158 | 1 traleig1 0.1% 159 | 1 vlad 0.1% 160 | 1 yuitest 0.1% 161 | 1 Adam Crabtree 0.1% 162 | 1 Andreas Brekken 0.1% 163 | 1 Andreas Lind Petersen 0.1% 164 | 1 Andrew Nesbitt 0.1% 165 | 1 Andrey Popp 0.1% 166 | 1 Arnaud Brousseau 0.1% 167 | 1 Atsuya Takagi 0.1% 168 | 1 Austin Birch 0.1% 169 | 1 Bjørge Næss 0.1% 170 | 1 Brian Lalor 0.1% 171 | 1 Brian M. Carlson 0.1% 172 | 1 Brian Moore 0.1% 173 | 1 Bryan Donovan 0.1% 174 | 1 Casey Foster 0.1% 175 | 1 ChrisWren 0.1% 176 | 1 Corey Butler 0.1% 177 | 1 Daniel Stockman 0.1% 178 | 1 Dave McKenna 0.1% 179 | 1 Di Wu 0.1% 180 | 1 Dmitry Shirokov 0.1% 181 | 1 Fedor Indutny 0.1% 182 | 1 Florian Margaine 0.1% 183 | 1 Frederico Silva 0.1% 184 | 1 Fredrik Lindin 0.1% 185 | 1 Gareth Murphy 0.1% 186 | 1 Gavin Mogan 0.1% 187 | 1 Glen Huang 0.1% 188 | 1 Greg Perkins 0.1% 189 | 1 Harry Brundage 0.1% 190 | 1 Herman Junge 0.1% 191 | 1 Ian Young 0.1% 192 | 1 Ivan 0.1% 193 | 1 JP Bochi 0.1% 194 | 1 Jaakko Salonen 0.1% 195 | 1 Jakub Nešetřil 0.1% 196 | 1 James Bowes 0.1% 197 | 1 James Lal 0.1% 198 | 1 Jason Barry 0.1% 199 | 1 Javier Aranda 0.1% 200 | 1 Jeff Kunkle 0.1% 201 | 1 Jeremy Martin 0.1% 202 | 1 Jimmy Cuadra 0.1% 203 | 1 Jonathan Creamer 0.1% 204 | 1 Jussi Virtanen 0.1% 205 | 1 Katie Gengler 0.1% 206 | 1 Kazuhito Hokamura 0.1% 207 | 1 Kirill Korolyov 0.1% 208 | 1 Koen Punt 0.1% 209 | 1 Laszlo Bacsi 0.1% 210 | 1 Liam Newman 0.1% 211 | 1 László Bácsi 0.1% 212 | 1 Maciej Małecki 0.1% 213 | 1 Mal Graty 0.1% 214 | 1 Marc Kuo 0.1% 215 | 1 Matt Robenolt 0.1% 216 | ``` 217 | 218 | ## Links 219 | 220 | - [Google Group](http://groups.google.com/group/mochajs) 221 | - [Wiki](https://github.com/visionmedia/mocha/wiki) 222 | - Mocha [Extensions and reporters](https://github.com/visionmedia/mocha/wiki) 223 | -------------------------------------------------------------------------------- /bin/mocha: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /** 4 | * This tiny wrapper file checks for known node flags and appends them 5 | * when found, before invoking the "real" _mocha(1) executable. 6 | */ 7 | 8 | var spawn = require('child_process').spawn 9 | , args = [ __dirname + '/_mocha' ]; 10 | 11 | process.argv.slice(2).forEach(function(arg){ 12 | var flag = arg.split('=')[0]; 13 | 14 | switch (flag) { 15 | case '-d': 16 | args.unshift('--debug'); 17 | break; 18 | case 'debug': 19 | case '--debug': 20 | case '--debug-brk': 21 | args.unshift(arg); 22 | break; 23 | case '-gc': 24 | case '--expose-gc': 25 | args.unshift('--expose-gc'); 26 | break; 27 | case '--gc-global': 28 | case '--harmony': 29 | case '--harmony-proxies': 30 | case '--harmony-collections': 31 | case '--harmony-generators': 32 | case '--prof': 33 | args.unshift(arg); 34 | break; 35 | default: 36 | if (0 == arg.indexOf('--trace')) args.unshift(arg); 37 | else args.push(arg); 38 | break; 39 | } 40 | }); 41 | 42 | var proc = spawn(process.argv[0], args, { customFds: [0,1,2] }); 43 | proc.on('exit', function (code, signal) { 44 | process.on('exit', function(){ 45 | if (signal) { 46 | process.kill(process.pid, signal); 47 | } else { 48 | process.exit(code); 49 | } 50 | }); 51 | }); 52 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mocha", 3 | "version": "1.17.1", 4 | "main": "mocha.js", 5 | "ignore": [ 6 | "bin", 7 | "editors", 8 | "images", 9 | "lib", 10 | "support", 11 | "test", 12 | ".gitignore", 13 | ".npmignore", 14 | ".travis.yml", 15 | "component.json", 16 | "index.js", 17 | "Makefile", 18 | "package.json" 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /component.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mocha", 3 | "version": "1.17.1", 4 | "repo": "visionmedia/mocha", 5 | "description": "simple, flexible, fun test framework", 6 | "keywords": [ 7 | "mocha", 8 | "test", 9 | "bdd", 10 | "tdd", 11 | "tap" 12 | ], 13 | "main": "mocha.js", 14 | "scripts": ["mocha.js"], 15 | "styles": ["mocha.css"] 16 | } 17 | -------------------------------------------------------------------------------- /editors/JavaScript mocha.tmbundle/Snippets/bdd - after each.tmSnippet: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | content 6 | afterEach(function(){ 7 | $0 8 | }) 9 | name 10 | bdd - after each 11 | scope 12 | source.js 13 | tabTrigger 14 | ae 15 | uuid 16 | 7B4DA8F4-2064-468B-B252-054148419B4B 17 | 18 | 19 | -------------------------------------------------------------------------------- /editors/JavaScript mocha.tmbundle/Snippets/bdd - after.tmSnippet: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | content 6 | after(function(){ 7 | $0 8 | }) 9 | name 10 | bdd - after 11 | scope 12 | source.js 13 | tabTrigger 14 | a 15 | uuid 16 | A49A87F9-399E-4D74-A489-C535BB06D487 17 | 18 | 19 | -------------------------------------------------------------------------------- /editors/JavaScript mocha.tmbundle/Snippets/bdd - before each.tmSnippet: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | content 6 | beforeEach(function(){ 7 | $0 8 | }) 9 | name 10 | bdd - before each 11 | scope 12 | source.js 13 | tabTrigger 14 | be 15 | uuid 16 | 7AB064E3-EFBB-4FA7-98CA-9E87C10CC04E 17 | 18 | 19 | -------------------------------------------------------------------------------- /editors/JavaScript mocha.tmbundle/Snippets/bdd - before.tmSnippet: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | content 6 | before(function(){ 7 | $0 8 | }) 9 | name 10 | bdd - before 11 | scope 12 | source.js 13 | tabTrigger 14 | b 15 | uuid 16 | DF6F1F42-F80A-4A24-AF78-376F19070C4C 17 | 18 | 19 | -------------------------------------------------------------------------------- /editors/JavaScript mocha.tmbundle/Snippets/bdd - describe.tmSnippet: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | content 6 | describe('$1', function(){ 7 | $0 8 | }) 9 | name 10 | bdd - describe 11 | scope 12 | source.js 13 | tabTrigger 14 | des 15 | uuid 16 | 4AA1FB50-9BB9-400E-A140-D61C39BDFDF5 17 | 18 | 19 | -------------------------------------------------------------------------------- /editors/JavaScript mocha.tmbundle/Snippets/bdd - it.tmSnippet: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | content 6 | it('should $1', function(){ 7 | $0 8 | }) 9 | name 10 | bdd - it 11 | scope 12 | source.js 13 | tabTrigger 14 | it 15 | uuid 16 | 591AE071-95E4-4E1E-B0F3-A7DAF41595EE 17 | 18 | 19 | -------------------------------------------------------------------------------- /editors/JavaScript mocha.tmbundle/Snippets/tdd - assert.tmSnippet: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | content 6 | assert($0); 7 | name 8 | tdd - assert 9 | scope 10 | source.js 11 | tabTrigger 12 | as 13 | uuid 14 | 9D920EC2-6A72-4108-B5A0-591AFA61C740 15 | 16 | 17 | -------------------------------------------------------------------------------- /editors/JavaScript mocha.tmbundle/Snippets/tdd - assert_deepEqual.tmSnippet: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | content 6 | assert.deepEqual($1, $2); 7 | name 8 | tdd - assert.deepEqual 9 | scope 10 | source.js 11 | tabTrigger 12 | deq 13 | uuid 14 | 7D21FF16-E2E1-46BB-AD6B-82AD767A0822 15 | 16 | 17 | -------------------------------------------------------------------------------- /editors/JavaScript mocha.tmbundle/Snippets/tdd - assert_equal.tmSnippet: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | content 6 | assert.equal($1, $2); 7 | name 8 | tdd - assert.equal 9 | scope 10 | source.js 11 | tabTrigger 12 | eq 13 | uuid 14 | 4868D5C0-075D-44A8-B41B-A14E8350C0F8 15 | 16 | 17 | -------------------------------------------------------------------------------- /editors/JavaScript mocha.tmbundle/Snippets/tdd - assert_fail.tmSnippet: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | content 6 | assert.fail($0); 7 | name 8 | tdd - assert.fail 9 | scope 10 | source.js 11 | tabTrigger 12 | fail 13 | uuid 14 | 11756F3A-5F08-445C-B5B6-30605838BC6F 15 | 16 | 17 | -------------------------------------------------------------------------------- /editors/JavaScript mocha.tmbundle/Snippets/tdd - assert_isFunction.tmSnippet: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | content 6 | assert.isFunction($0); 7 | name 8 | tdd - assert.isFunction 9 | scope 10 | source.js 11 | tabTrigger 12 | isf 13 | uuid 14 | 83DFFD36-2C46-4C92-8950-64665D32DC8E 15 | 16 | 17 | -------------------------------------------------------------------------------- /editors/JavaScript mocha.tmbundle/Snippets/tdd - setup.tmSnippet: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | content 6 | setup(function() { 7 | $0 8 | }); 9 | name 10 | tdd - setup 11 | scope 12 | source.js 13 | tabTrigger 14 | setup 15 | uuid 16 | DCEE796A-900D-4922-BAF0-39C9B91A15E1 17 | 18 | 19 | -------------------------------------------------------------------------------- /editors/JavaScript mocha.tmbundle/Snippets/tdd - suite.tmSnippet: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | content 6 | suite('$1', function() { 7 | $0 8 | }); 9 | name 10 | tdd - suite 11 | scope 12 | source.js 13 | tabTrigger 14 | suite 15 | uuid 16 | 2E65C1BE-A568-4868-95BF-E8C958EA5BA5 17 | 18 | 19 | -------------------------------------------------------------------------------- /editors/JavaScript mocha.tmbundle/Snippets/tdd - teardown.tmSnippet: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | content 6 | teardown(function() { 7 | $0 8 | }); 9 | name 10 | tdd - teardown 11 | scope 12 | source.js 13 | tabTrigger 14 | teardown 15 | uuid 16 | 9472B167-BEC1-45FD-BA1C-68A99961D8FF 17 | 18 | 19 | -------------------------------------------------------------------------------- /editors/JavaScript mocha.tmbundle/Snippets/tdd - test.tmSnippet: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | content 6 | test('$1', function() { 7 | $0 8 | }); 9 | name 10 | tdd - test 11 | scope 12 | source.js 13 | tabTrigger 14 | test 15 | uuid 16 | F6E3DF27-3458-4ADD-91A3-B6EF11E1D81E 17 | 18 | 19 | -------------------------------------------------------------------------------- /editors/JavaScript mocha.tmbundle/info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | name 6 | JavaScript mocha 7 | ordering 8 | 9 | 4AA1FB50-9BB9-400E-A140-D61C39BDFDF5 10 | 591AE071-95E4-4E1E-B0F3-A7DAF41595EE 11 | DF6F1F42-F80A-4A24-AF78-376F19070C4C 12 | A49A87F9-399E-4D74-A489-C535BB06D487 13 | 7AB064E3-EFBB-4FA7-98CA-9E87C10CC04E 14 | 7B4DA8F4-2064-468B-B252-054148419B4B 15 | 16 | uuid 17 | 094ACE33-0C0E-422A-B3F7-5B919F5B1239 18 | 19 | 20 | -------------------------------------------------------------------------------- /images/error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ilkkao/co-mocha/c8eee586706f15f4f77922a8a5e05dd21389c984/images/error.png -------------------------------------------------------------------------------- /images/ok.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ilkkao/co-mocha/c8eee586706f15f4f77922a8a5e05dd21389c984/images/ok.png -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = process.env.COV 3 | ? require('./lib-cov/mocha') 4 | : require('./lib/mocha'); -------------------------------------------------------------------------------- /lib/browser/debug.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = function(type){ 3 | return function(){ 4 | } 5 | }; 6 | -------------------------------------------------------------------------------- /lib/browser/events.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Module exports. 4 | */ 5 | 6 | exports.EventEmitter = EventEmitter; 7 | 8 | /** 9 | * Check if `obj` is an array. 10 | */ 11 | 12 | function isArray(obj) { 13 | return '[object Array]' == {}.toString.call(obj); 14 | } 15 | 16 | /** 17 | * Event emitter constructor. 18 | * 19 | * @api public 20 | */ 21 | 22 | function EventEmitter(){}; 23 | 24 | /** 25 | * Adds a listener. 26 | * 27 | * @api public 28 | */ 29 | 30 | EventEmitter.prototype.on = function (name, fn) { 31 | if (!this.$events) { 32 | this.$events = {}; 33 | } 34 | 35 | if (!this.$events[name]) { 36 | this.$events[name] = fn; 37 | } else if (isArray(this.$events[name])) { 38 | this.$events[name].push(fn); 39 | } else { 40 | this.$events[name] = [this.$events[name], fn]; 41 | } 42 | 43 | return this; 44 | }; 45 | 46 | EventEmitter.prototype.addListener = EventEmitter.prototype.on; 47 | 48 | /** 49 | * Adds a volatile listener. 50 | * 51 | * @api public 52 | */ 53 | 54 | EventEmitter.prototype.once = function (name, fn) { 55 | var self = this; 56 | 57 | function on () { 58 | self.removeListener(name, on); 59 | fn.apply(this, arguments); 60 | }; 61 | 62 | on.listener = fn; 63 | this.on(name, on); 64 | 65 | return this; 66 | }; 67 | 68 | /** 69 | * Removes a listener. 70 | * 71 | * @api public 72 | */ 73 | 74 | EventEmitter.prototype.removeListener = function (name, fn) { 75 | if (this.$events && this.$events[name]) { 76 | var list = this.$events[name]; 77 | 78 | if (isArray(list)) { 79 | var pos = -1; 80 | 81 | for (var i = 0, l = list.length; i < l; i++) { 82 | if (list[i] === fn || (list[i].listener && list[i].listener === fn)) { 83 | pos = i; 84 | break; 85 | } 86 | } 87 | 88 | if (pos < 0) { 89 | return this; 90 | } 91 | 92 | list.splice(pos, 1); 93 | 94 | if (!list.length) { 95 | delete this.$events[name]; 96 | } 97 | } else if (list === fn || (list.listener && list.listener === fn)) { 98 | delete this.$events[name]; 99 | } 100 | } 101 | 102 | return this; 103 | }; 104 | 105 | /** 106 | * Removes all listeners for an event. 107 | * 108 | * @api public 109 | */ 110 | 111 | EventEmitter.prototype.removeAllListeners = function (name) { 112 | if (name === undefined) { 113 | this.$events = {}; 114 | return this; 115 | } 116 | 117 | if (this.$events && this.$events[name]) { 118 | this.$events[name] = null; 119 | } 120 | 121 | return this; 122 | }; 123 | 124 | /** 125 | * Gets all listeners for a certain event. 126 | * 127 | * @api public 128 | */ 129 | 130 | EventEmitter.prototype.listeners = function (name) { 131 | if (!this.$events) { 132 | this.$events = {}; 133 | } 134 | 135 | if (!this.$events[name]) { 136 | this.$events[name] = []; 137 | } 138 | 139 | if (!isArray(this.$events[name])) { 140 | this.$events[name] = [this.$events[name]]; 141 | } 142 | 143 | return this.$events[name]; 144 | }; 145 | 146 | /** 147 | * Emits an event. 148 | * 149 | * @api public 150 | */ 151 | 152 | EventEmitter.prototype.emit = function (name) { 153 | if (!this.$events) { 154 | return false; 155 | } 156 | 157 | var handler = this.$events[name]; 158 | 159 | if (!handler) { 160 | return false; 161 | } 162 | 163 | var args = [].slice.call(arguments, 1); 164 | 165 | if ('function' == typeof handler) { 166 | handler.apply(this, args); 167 | } else if (isArray(handler)) { 168 | var listeners = handler.slice(); 169 | 170 | for (var i = 0, l = listeners.length; i < l; i++) { 171 | listeners[i].apply(this, args); 172 | } 173 | } else { 174 | return false; 175 | } 176 | 177 | return true; 178 | }; -------------------------------------------------------------------------------- /lib/browser/fs.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ilkkao/co-mocha/c8eee586706f15f4f77922a8a5e05dd21389c984/lib/browser/fs.js -------------------------------------------------------------------------------- /lib/browser/path.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ilkkao/co-mocha/c8eee586706f15f4f77922a8a5e05dd21389c984/lib/browser/path.js -------------------------------------------------------------------------------- /lib/browser/progress.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Expose `Progress`. 3 | */ 4 | 5 | module.exports = Progress; 6 | 7 | /** 8 | * Initialize a new `Progress` indicator. 9 | */ 10 | 11 | function Progress() { 12 | this.percent = 0; 13 | this.size(0); 14 | this.fontSize(11); 15 | this.font('helvetica, arial, sans-serif'); 16 | } 17 | 18 | /** 19 | * Set progress size to `n`. 20 | * 21 | * @param {Number} n 22 | * @return {Progress} for chaining 23 | * @api public 24 | */ 25 | 26 | Progress.prototype.size = function(n){ 27 | this._size = n; 28 | return this; 29 | }; 30 | 31 | /** 32 | * Set text to `str`. 33 | * 34 | * @param {String} str 35 | * @return {Progress} for chaining 36 | * @api public 37 | */ 38 | 39 | Progress.prototype.text = function(str){ 40 | this._text = str; 41 | return this; 42 | }; 43 | 44 | /** 45 | * Set font size to `n`. 46 | * 47 | * @param {Number} n 48 | * @return {Progress} for chaining 49 | * @api public 50 | */ 51 | 52 | Progress.prototype.fontSize = function(n){ 53 | this._fontSize = n; 54 | return this; 55 | }; 56 | 57 | /** 58 | * Set font `family`. 59 | * 60 | * @param {String} family 61 | * @return {Progress} for chaining 62 | */ 63 | 64 | Progress.prototype.font = function(family){ 65 | this._font = family; 66 | return this; 67 | }; 68 | 69 | /** 70 | * Update percentage to `n`. 71 | * 72 | * @param {Number} n 73 | * @return {Progress} for chaining 74 | */ 75 | 76 | Progress.prototype.update = function(n){ 77 | this.percent = n; 78 | return this; 79 | }; 80 | 81 | /** 82 | * Draw on `ctx`. 83 | * 84 | * @param {CanvasRenderingContext2d} ctx 85 | * @return {Progress} for chaining 86 | */ 87 | 88 | Progress.prototype.draw = function(ctx){ 89 | try { 90 | var percent = Math.min(this.percent, 100) 91 | , size = this._size 92 | , half = size / 2 93 | , x = half 94 | , y = half 95 | , rad = half - 1 96 | , fontSize = this._fontSize; 97 | 98 | ctx.font = fontSize + 'px ' + this._font; 99 | 100 | var angle = Math.PI * 2 * (percent / 100); 101 | ctx.clearRect(0, 0, size, size); 102 | 103 | // outer circle 104 | ctx.strokeStyle = '#9f9f9f'; 105 | ctx.beginPath(); 106 | ctx.arc(x, y, rad, 0, angle, false); 107 | ctx.stroke(); 108 | 109 | // inner circle 110 | ctx.strokeStyle = '#eee'; 111 | ctx.beginPath(); 112 | ctx.arc(x, y, rad - 1, 0, angle, true); 113 | ctx.stroke(); 114 | 115 | // text 116 | var text = this._text || (percent | 0) + '%' 117 | , w = ctx.measureText(text).width; 118 | 119 | ctx.fillText( 120 | text 121 | , x - w / 2 + 1 122 | , y + fontSize / 2 - 1); 123 | } catch (ex) {} //don't fail if we can't render progress 124 | return this; 125 | }; 126 | -------------------------------------------------------------------------------- /lib/browser/tty.js: -------------------------------------------------------------------------------- 1 | 2 | exports.isatty = function(){ 3 | return true; 4 | }; 5 | 6 | exports.getWindowSize = function(){ 7 | if ('innerHeight' in global) { 8 | return [global.innerHeight, global.innerWidth]; 9 | } else { 10 | // In a Web Worker, the DOM Window is not available. 11 | return [640, 480]; 12 | } 13 | }; 14 | -------------------------------------------------------------------------------- /lib/context.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Expose `Context`. 4 | */ 5 | 6 | module.exports = Context; 7 | 8 | /** 9 | * Initialize a new `Context`. 10 | * 11 | * @api private 12 | */ 13 | 14 | function Context(){} 15 | 16 | /** 17 | * Set or get the context `Runnable` to `runnable`. 18 | * 19 | * @param {Runnable} runnable 20 | * @return {Context} 21 | * @api private 22 | */ 23 | 24 | Context.prototype.runnable = function(runnable){ 25 | if (0 == arguments.length) return this._runnable; 26 | this.test = this._runnable = runnable; 27 | return this; 28 | }; 29 | 30 | /** 31 | * Set test timeout `ms`. 32 | * 33 | * @param {Number} ms 34 | * @return {Context} self 35 | * @api private 36 | */ 37 | 38 | Context.prototype.timeout = function(ms){ 39 | this.runnable().timeout(ms); 40 | return this; 41 | }; 42 | 43 | /** 44 | * Set test slowness threshold `ms`. 45 | * 46 | * @param {Number} ms 47 | * @return {Context} self 48 | * @api private 49 | */ 50 | 51 | Context.prototype.slow = function(ms){ 52 | this.runnable().slow(ms); 53 | return this; 54 | }; 55 | 56 | /** 57 | * Inspect the context void of `._runnable`. 58 | * 59 | * @return {String} 60 | * @api private 61 | */ 62 | 63 | Context.prototype.inspect = function(){ 64 | return JSON.stringify(this, function(key, val){ 65 | if ('_runnable' == key) return; 66 | if ('test' == key) return; 67 | return val; 68 | }, 2); 69 | }; 70 | -------------------------------------------------------------------------------- /lib/hook.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Module dependencies. 4 | */ 5 | 6 | var Runnable = require('./runnable'); 7 | 8 | /** 9 | * Expose `Hook`. 10 | */ 11 | 12 | module.exports = Hook; 13 | 14 | /** 15 | * Initialize a new `Hook` with the given `title` and callback `fn`. 16 | * 17 | * @param {String} title 18 | * @param {Function} fn 19 | * @api private 20 | */ 21 | 22 | function Hook(title, fn) { 23 | Runnable.call(this, title, fn); 24 | this.type = 'hook'; 25 | } 26 | 27 | /** 28 | * Inherit from `Runnable.prototype`. 29 | */ 30 | 31 | Hook.prototype.__proto__ = Runnable.prototype; 32 | 33 | /** 34 | * Get or set the test `err`. 35 | * 36 | * @param {Error} err 37 | * @return {Error} 38 | * @api public 39 | */ 40 | 41 | Hook.prototype.error = function(err){ 42 | if (0 == arguments.length) { 43 | var err = this._error; 44 | this._error = null; 45 | return err; 46 | } 47 | 48 | this._error = err; 49 | }; 50 | -------------------------------------------------------------------------------- /lib/interfaces/bdd.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Module dependencies. 4 | */ 5 | 6 | var Suite = require('../suite') 7 | , Test = require('../test') 8 | , utils = require('../utils'); 9 | 10 | /** 11 | * BDD-style interface: 12 | * 13 | * describe('Array', function(){ 14 | * describe('#indexOf()', function(){ 15 | * it('should return -1 when not present', function(){ 16 | * 17 | * }); 18 | * 19 | * it('should return the index when present', function(){ 20 | * 21 | * }); 22 | * }); 23 | * }); 24 | * 25 | */ 26 | 27 | module.exports = function(suite){ 28 | var suites = [suite]; 29 | 30 | suite.on('pre-require', function(context, file, mocha){ 31 | 32 | /** 33 | * Execute before running tests. 34 | */ 35 | 36 | context.before = function(fn){ 37 | suites[0].beforeAll(fn); 38 | }; 39 | 40 | /** 41 | * Execute after running tests. 42 | */ 43 | 44 | context.after = function(fn){ 45 | suites[0].afterAll(fn); 46 | }; 47 | 48 | /** 49 | * Execute before each test case. 50 | */ 51 | 52 | context.beforeEach = function(fn){ 53 | suites[0].beforeEach(fn); 54 | }; 55 | 56 | /** 57 | * Execute after each test case. 58 | */ 59 | 60 | context.afterEach = function(fn){ 61 | suites[0].afterEach(fn); 62 | }; 63 | 64 | /** 65 | * Describe a "suite" with the given `title` 66 | * and callback `fn` containing nested suites 67 | * and/or tests. 68 | */ 69 | 70 | context.describe = context.context = function(title, fn){ 71 | var suite = Suite.create(suites[0], title); 72 | suites.unshift(suite); 73 | fn.call(suite); 74 | suites.shift(); 75 | return suite; 76 | }; 77 | 78 | /** 79 | * Pending describe. 80 | */ 81 | 82 | context.xdescribe = 83 | context.xcontext = 84 | context.describe.skip = function(title, fn){ 85 | var suite = Suite.create(suites[0], title); 86 | suite.pending = true; 87 | suites.unshift(suite); 88 | fn.call(suite); 89 | suites.shift(); 90 | }; 91 | 92 | /** 93 | * Exclusive suite. 94 | */ 95 | 96 | context.describe.only = function(title, fn){ 97 | var suite = context.describe(title, fn); 98 | mocha.grep(suite.fullTitle()); 99 | return suite; 100 | }; 101 | 102 | /** 103 | * Describe a specification or test-case 104 | * with the given `title` and callback `fn` 105 | * acting as a thunk. 106 | */ 107 | 108 | context.it = context.specify = function(title, fn){ 109 | var suite = suites[0]; 110 | if (suite.pending) var fn = null; 111 | var test = new Test(title, fn); 112 | suite.addTest(test); 113 | return test; 114 | }; 115 | 116 | /** 117 | * Exclusive test-case. 118 | */ 119 | 120 | context.it.only = function(title, fn){ 121 | var test = context.it(title, fn); 122 | var reString = '^' + utils.escapeRegexp(test.fullTitle()) + '$'; 123 | mocha.grep(new RegExp(reString)); 124 | return test; 125 | }; 126 | 127 | /** 128 | * Pending test case. 129 | */ 130 | 131 | context.xit = 132 | context.xspecify = 133 | context.it.skip = function(title){ 134 | context.it(title); 135 | }; 136 | }); 137 | }; 138 | -------------------------------------------------------------------------------- /lib/interfaces/exports.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Module dependencies. 4 | */ 5 | 6 | var Suite = require('../suite') 7 | , Test = require('../test'); 8 | 9 | /** 10 | * TDD-style interface: 11 | * 12 | * exports.Array = { 13 | * '#indexOf()': { 14 | * 'should return -1 when the value is not present': function(){ 15 | * 16 | * }, 17 | * 18 | * 'should return the correct index when the value is present': function(){ 19 | * 20 | * } 21 | * } 22 | * }; 23 | * 24 | */ 25 | 26 | module.exports = function(suite){ 27 | var suites = [suite]; 28 | 29 | suite.on('require', visit); 30 | 31 | function visit(obj) { 32 | var suite; 33 | for (var key in obj) { 34 | if ('function' == typeof obj[key]) { 35 | var fn = obj[key]; 36 | switch (key) { 37 | case 'before': 38 | suites[0].beforeAll(fn); 39 | break; 40 | case 'after': 41 | suites[0].afterAll(fn); 42 | break; 43 | case 'beforeEach': 44 | suites[0].beforeEach(fn); 45 | break; 46 | case 'afterEach': 47 | suites[0].afterEach(fn); 48 | break; 49 | default: 50 | suites[0].addTest(new Test(key, fn)); 51 | } 52 | } else { 53 | var suite = Suite.create(suites[0], key); 54 | suites.unshift(suite); 55 | visit(obj[key]); 56 | suites.shift(); 57 | } 58 | } 59 | } 60 | }; 61 | -------------------------------------------------------------------------------- /lib/interfaces/index.js: -------------------------------------------------------------------------------- 1 | 2 | exports.bdd = require('./bdd'); 3 | exports.tdd = require('./tdd'); 4 | exports.qunit = require('./qunit'); 5 | exports.exports = require('./exports'); 6 | -------------------------------------------------------------------------------- /lib/interfaces/qunit.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Module dependencies. 4 | */ 5 | 6 | var Suite = require('../suite') 7 | , Test = require('../test') 8 | , utils = require('../utils'); 9 | 10 | /** 11 | * QUnit-style interface: 12 | * 13 | * suite('Array'); 14 | * 15 | * test('#length', function(){ 16 | * var arr = [1,2,3]; 17 | * ok(arr.length == 3); 18 | * }); 19 | * 20 | * test('#indexOf()', function(){ 21 | * var arr = [1,2,3]; 22 | * ok(arr.indexOf(1) == 0); 23 | * ok(arr.indexOf(2) == 1); 24 | * ok(arr.indexOf(3) == 2); 25 | * }); 26 | * 27 | * suite('String'); 28 | * 29 | * test('#length', function(){ 30 | * ok('foo'.length == 3); 31 | * }); 32 | * 33 | */ 34 | 35 | module.exports = function(suite){ 36 | var suites = [suite]; 37 | 38 | suite.on('pre-require', function(context, file, mocha){ 39 | 40 | /** 41 | * Execute before running tests. 42 | */ 43 | 44 | context.before = function(fn){ 45 | suites[0].beforeAll(fn); 46 | }; 47 | 48 | /** 49 | * Execute after running tests. 50 | */ 51 | 52 | context.after = function(fn){ 53 | suites[0].afterAll(fn); 54 | }; 55 | 56 | /** 57 | * Execute before each test case. 58 | */ 59 | 60 | context.beforeEach = function(fn){ 61 | suites[0].beforeEach(fn); 62 | }; 63 | 64 | /** 65 | * Execute after each test case. 66 | */ 67 | 68 | context.afterEach = function(fn){ 69 | suites[0].afterEach(fn); 70 | }; 71 | 72 | /** 73 | * Describe a "suite" with the given `title`. 74 | */ 75 | 76 | context.suite = function(title){ 77 | if (suites.length > 1) suites.shift(); 78 | var suite = Suite.create(suites[0], title); 79 | suites.unshift(suite); 80 | return suite; 81 | }; 82 | 83 | /** 84 | * Exclusive test-case. 85 | */ 86 | 87 | context.suite.only = function(title, fn){ 88 | var suite = context.suite(title, fn); 89 | mocha.grep(suite.fullTitle()); 90 | }; 91 | 92 | /** 93 | * Describe a specification or test-case 94 | * with the given `title` and callback `fn` 95 | * acting as a thunk. 96 | */ 97 | 98 | context.test = function(title, fn){ 99 | var test = new Test(title, fn); 100 | suites[0].addTest(test); 101 | return test; 102 | }; 103 | 104 | /** 105 | * Exclusive test-case. 106 | */ 107 | 108 | context.test.only = function(title, fn){ 109 | var test = context.test(title, fn); 110 | var reString = '^' + utils.escapeRegexp(test.fullTitle()) + '$'; 111 | mocha.grep(new RegExp(reString)); 112 | }; 113 | 114 | /** 115 | * Pending test case. 116 | */ 117 | 118 | context.test.skip = function(title){ 119 | context.test(title); 120 | }; 121 | }); 122 | }; 123 | -------------------------------------------------------------------------------- /lib/interfaces/tdd.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Module dependencies. 4 | */ 5 | 6 | var Suite = require('../suite') 7 | , Test = require('../test') 8 | , utils = require('../utils');; 9 | 10 | /** 11 | * TDD-style interface: 12 | * 13 | * suite('Array', function(){ 14 | * suite('#indexOf()', function(){ 15 | * suiteSetup(function(){ 16 | * 17 | * }); 18 | * 19 | * test('should return -1 when not present', function(){ 20 | * 21 | * }); 22 | * 23 | * test('should return the index when present', function(){ 24 | * 25 | * }); 26 | * 27 | * suiteTeardown(function(){ 28 | * 29 | * }); 30 | * }); 31 | * }); 32 | * 33 | */ 34 | 35 | module.exports = function(suite){ 36 | var suites = [suite]; 37 | 38 | suite.on('pre-require', function(context, file, mocha){ 39 | 40 | /** 41 | * Execute before each test case. 42 | */ 43 | 44 | context.setup = function(fn){ 45 | suites[0].beforeEach(fn); 46 | }; 47 | 48 | /** 49 | * Execute after each test case. 50 | */ 51 | 52 | context.teardown = function(fn){ 53 | suites[0].afterEach(fn); 54 | }; 55 | 56 | /** 57 | * Execute before the suite. 58 | */ 59 | 60 | context.suiteSetup = function(fn){ 61 | suites[0].beforeAll(fn); 62 | }; 63 | 64 | /** 65 | * Execute after the suite. 66 | */ 67 | 68 | context.suiteTeardown = function(fn){ 69 | suites[0].afterAll(fn); 70 | }; 71 | 72 | /** 73 | * Describe a "suite" with the given `title` 74 | * and callback `fn` containing nested suites 75 | * and/or tests. 76 | */ 77 | 78 | context.suite = function(title, fn){ 79 | var suite = Suite.create(suites[0], title); 80 | suites.unshift(suite); 81 | fn.call(suite); 82 | suites.shift(); 83 | return suite; 84 | }; 85 | 86 | /** 87 | * Pending suite. 88 | */ 89 | context.suite.skip = function(title, fn) { 90 | var suite = Suite.create(suites[0], title); 91 | suite.pending = true; 92 | suites.unshift(suite); 93 | fn.call(suite); 94 | suites.shift(); 95 | }; 96 | 97 | /** 98 | * Exclusive test-case. 99 | */ 100 | 101 | context.suite.only = function(title, fn){ 102 | var suite = context.suite(title, fn); 103 | mocha.grep(suite.fullTitle()); 104 | }; 105 | 106 | /** 107 | * Describe a specification or test-case 108 | * with the given `title` and callback `fn` 109 | * acting as a thunk. 110 | */ 111 | 112 | context.test = function(title, fn){ 113 | var suite = suites[0]; 114 | if (suite.pending) var fn = null; 115 | var test = new Test(title, fn); 116 | suite.addTest(test); 117 | return test; 118 | }; 119 | 120 | /** 121 | * Exclusive test-case. 122 | */ 123 | 124 | context.test.only = function(title, fn){ 125 | var test = context.test(title, fn); 126 | var reString = '^' + utils.escapeRegexp(test.fullTitle()) + '$'; 127 | mocha.grep(new RegExp(reString)); 128 | }; 129 | 130 | /** 131 | * Pending test case. 132 | */ 133 | 134 | context.test.skip = function(title){ 135 | context.test(title); 136 | }; 137 | }); 138 | }; 139 | -------------------------------------------------------------------------------- /lib/mocha.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * mocha 3 | * Copyright(c) 2011 TJ Holowaychuk 4 | * MIT Licensed 5 | */ 6 | 7 | /** 8 | * Module dependencies. 9 | */ 10 | 11 | var path = require('path') 12 | , utils = require('./utils'); 13 | 14 | /** 15 | * Expose `Mocha`. 16 | */ 17 | 18 | exports = module.exports = Mocha; 19 | 20 | /** 21 | * Expose internals. 22 | */ 23 | 24 | exports.utils = utils; 25 | exports.interfaces = require('./interfaces'); 26 | exports.reporters = require('./reporters'); 27 | exports.Runnable = require('./runnable'); 28 | exports.Context = require('./context'); 29 | exports.Runner = require('./runner'); 30 | exports.Suite = require('./suite'); 31 | exports.Hook = require('./hook'); 32 | exports.Test = require('./test'); 33 | 34 | /** 35 | * Return image `name` path. 36 | * 37 | * @param {String} name 38 | * @return {String} 39 | * @api private 40 | */ 41 | 42 | function image(name) { 43 | return __dirname + '/../images/' + name + '.png'; 44 | } 45 | 46 | /** 47 | * Setup mocha with `options`. 48 | * 49 | * Options: 50 | * 51 | * - `ui` name "bdd", "tdd", "exports" etc 52 | * - `reporter` reporter instance, defaults to `mocha.reporters.Dot` 53 | * - `globals` array of accepted globals 54 | * - `timeout` timeout in milliseconds 55 | * - `bail` bail on the first test failure 56 | * - `slow` milliseconds to wait before considering a test slow 57 | * - `ignoreLeaks` ignore global leaks 58 | * - `grep` string or regexp to filter tests with 59 | * 60 | * @param {Object} options 61 | * @api public 62 | */ 63 | 64 | function Mocha(options) { 65 | options = options || {}; 66 | this.files = []; 67 | this.options = options; 68 | this.grep(options.grep); 69 | this.suite = new exports.Suite('', new exports.Context); 70 | this.ui(options.ui); 71 | this.bail(options.bail); 72 | this.reporter(options.reporter); 73 | if (null != options.timeout) this.timeout(options.timeout); 74 | this.useColors(options.useColors) 75 | if (options.slow) this.slow(options.slow); 76 | 77 | this.suite.on('pre-require', function (context) { 78 | exports.afterEach = context.afterEach || context.teardown; 79 | exports.after = context.after || context.suiteTeardown; 80 | exports.beforeEach = context.beforeEach || context.setup; 81 | exports.before = context.before || context.suiteSetup; 82 | exports.describe = context.describe || context.suite; 83 | exports.it = context.it || context.test; 84 | exports.setup = context.setup || context.beforeEach; 85 | exports.suiteSetup = context.suiteSetup || context.before; 86 | exports.suiteTeardown = context.suiteTeardown || context.after; 87 | exports.suite = context.suite || context.describe; 88 | exports.teardown = context.teardown || context.afterEach; 89 | exports.test = context.test || context.it; 90 | }); 91 | } 92 | 93 | /** 94 | * Enable or disable bailing on the first failure. 95 | * 96 | * @param {Boolean} [bail] 97 | * @api public 98 | */ 99 | 100 | Mocha.prototype.bail = function(bail){ 101 | if (0 == arguments.length) bail = true; 102 | this.suite.bail(bail); 103 | return this; 104 | }; 105 | 106 | /** 107 | * Add test `file`. 108 | * 109 | * @param {String} file 110 | * @api public 111 | */ 112 | 113 | Mocha.prototype.addFile = function(file){ 114 | this.files.push(file); 115 | return this; 116 | }; 117 | 118 | /** 119 | * Set reporter to `reporter`, defaults to "dot". 120 | * 121 | * @param {String|Function} reporter name or constructor 122 | * @api public 123 | */ 124 | 125 | Mocha.prototype.reporter = function(reporter){ 126 | if ('function' == typeof reporter) { 127 | this._reporter = reporter; 128 | } else { 129 | reporter = reporter || 'dot'; 130 | var _reporter; 131 | try { _reporter = require('./reporters/' + reporter); } catch (err) {}; 132 | if (!_reporter) try { _reporter = require(reporter); } catch (err) {}; 133 | if (!_reporter && reporter === 'teamcity') 134 | console.warn('The Teamcity reporter was moved to a package named ' + 135 | 'mocha-teamcity-reporter ' + 136 | '(https://npmjs.org/package/mocha-teamcity-reporter).'); 137 | if (!_reporter) throw new Error('invalid reporter "' + reporter + '"'); 138 | this._reporter = _reporter; 139 | } 140 | return this; 141 | }; 142 | 143 | /** 144 | * Set test UI `name`, defaults to "bdd". 145 | * 146 | * @param {String} bdd 147 | * @api public 148 | */ 149 | 150 | Mocha.prototype.ui = function(name){ 151 | name = name || 'bdd'; 152 | this._ui = exports.interfaces[name]; 153 | if (!this._ui) try { this._ui = require(name); } catch (err) {}; 154 | if (!this._ui) throw new Error('invalid interface "' + name + '"'); 155 | this._ui = this._ui(this.suite); 156 | return this; 157 | }; 158 | 159 | /** 160 | * Load registered files. 161 | * 162 | * @api private 163 | */ 164 | 165 | Mocha.prototype.loadFiles = function(fn){ 166 | var self = this; 167 | var suite = this.suite; 168 | var pending = this.files.length; 169 | this.files.forEach(function(file){ 170 | file = path.resolve(file); 171 | suite.emit('pre-require', global, file, self); 172 | suite.emit('require', require(file), file, self); 173 | suite.emit('post-require', global, file, self); 174 | --pending || (fn && fn()); 175 | }); 176 | }; 177 | 178 | /** 179 | * Enable growl support. 180 | * 181 | * @api private 182 | */ 183 | 184 | Mocha.prototype._growl = function(runner, reporter) { 185 | var notify = require('growl'); 186 | 187 | runner.on('end', function(){ 188 | var stats = reporter.stats; 189 | if (stats.failures) { 190 | var msg = stats.failures + ' of ' + runner.total + ' tests failed'; 191 | notify(msg, { name: 'mocha', title: 'Failed', image: image('error') }); 192 | } else { 193 | notify(stats.passes + ' tests passed in ' + stats.duration + 'ms', { 194 | name: 'mocha' 195 | , title: 'Passed' 196 | , image: image('ok') 197 | }); 198 | } 199 | }); 200 | }; 201 | 202 | /** 203 | * Add regexp to grep, if `re` is a string it is escaped. 204 | * 205 | * @param {RegExp|String} re 206 | * @return {Mocha} 207 | * @api public 208 | */ 209 | 210 | Mocha.prototype.grep = function(re){ 211 | this.options.grep = 'string' == typeof re 212 | ? new RegExp(utils.escapeRegexp(re)) 213 | : re; 214 | return this; 215 | }; 216 | 217 | /** 218 | * Invert `.grep()` matches. 219 | * 220 | * @return {Mocha} 221 | * @api public 222 | */ 223 | 224 | Mocha.prototype.invert = function(){ 225 | this.options.invert = true; 226 | return this; 227 | }; 228 | 229 | /** 230 | * Ignore global leaks. 231 | * 232 | * @param {Boolean} ignore 233 | * @return {Mocha} 234 | * @api public 235 | */ 236 | 237 | Mocha.prototype.ignoreLeaks = function(ignore){ 238 | this.options.ignoreLeaks = !!ignore; 239 | return this; 240 | }; 241 | 242 | /** 243 | * Enable global leak checking. 244 | * 245 | * @return {Mocha} 246 | * @api public 247 | */ 248 | 249 | Mocha.prototype.checkLeaks = function(){ 250 | this.options.ignoreLeaks = false; 251 | return this; 252 | }; 253 | 254 | /** 255 | * Enable growl support. 256 | * 257 | * @return {Mocha} 258 | * @api public 259 | */ 260 | 261 | Mocha.prototype.growl = function(){ 262 | this.options.growl = true; 263 | return this; 264 | }; 265 | 266 | /** 267 | * Ignore `globals` array or string. 268 | * 269 | * @param {Array|String} globals 270 | * @return {Mocha} 271 | * @api public 272 | */ 273 | 274 | Mocha.prototype.globals = function(globals){ 275 | this.options.globals = (this.options.globals || []).concat(globals); 276 | return this; 277 | }; 278 | 279 | /** 280 | * Emit color output. 281 | * 282 | * @param {Boolean} colors 283 | * @return {Mocha} 284 | * @api public 285 | */ 286 | 287 | Mocha.prototype.useColors = function(colors){ 288 | this.options.useColors = arguments.length && colors != undefined 289 | ? colors 290 | : true; 291 | return this; 292 | }; 293 | 294 | /** 295 | * Use inline diffs rather than +/-. 296 | * 297 | * @param {Boolean} inlineDiffs 298 | * @return {Mocha} 299 | * @api public 300 | */ 301 | 302 | Mocha.prototype.useInlineDiffs = function(inlineDiffs) { 303 | this.options.useInlineDiffs = arguments.length && inlineDiffs != undefined 304 | ? inlineDiffs 305 | : false; 306 | return this; 307 | }; 308 | 309 | /** 310 | * Set the timeout in milliseconds. 311 | * 312 | * @param {Number} timeout 313 | * @return {Mocha} 314 | * @api public 315 | */ 316 | 317 | Mocha.prototype.timeout = function(timeout){ 318 | this.suite.timeout(timeout); 319 | return this; 320 | }; 321 | 322 | /** 323 | * Set slowness threshold in milliseconds. 324 | * 325 | * @param {Number} slow 326 | * @return {Mocha} 327 | * @api public 328 | */ 329 | 330 | Mocha.prototype.slow = function(slow){ 331 | this.suite.slow(slow); 332 | return this; 333 | }; 334 | 335 | /** 336 | * Makes all tests async (accepting a callback) 337 | * 338 | * @return {Mocha} 339 | * @api public 340 | */ 341 | 342 | Mocha.prototype.asyncOnly = function(){ 343 | this.options.asyncOnly = true; 344 | return this; 345 | }; 346 | 347 | /** 348 | * Run tests and invoke `fn()` when complete. 349 | * 350 | * @param {Function} fn 351 | * @return {Runner} 352 | * @api public 353 | */ 354 | 355 | Mocha.prototype.run = function(fn){ 356 | if (this.files.length) this.loadFiles(); 357 | var suite = this.suite; 358 | var options = this.options; 359 | var runner = new exports.Runner(suite); 360 | var reporter = new this._reporter(runner); 361 | runner.ignoreLeaks = false !== options.ignoreLeaks; 362 | runner.asyncOnly = options.asyncOnly; 363 | if (options.grep) runner.grep(options.grep, options.invert); 364 | if (options.globals) runner.globals(options.globals); 365 | if (options.growl) this._growl(runner, reporter); 366 | exports.reporters.Base.useColors = options.useColors; 367 | exports.reporters.Base.inlineDiffs = options.useInlineDiffs; 368 | return runner.run(fn); 369 | }; 370 | -------------------------------------------------------------------------------- /lib/ms.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Helpers. 3 | */ 4 | 5 | var s = 1000; 6 | var m = s * 60; 7 | var h = m * 60; 8 | var d = h * 24; 9 | var y = d * 365.25; 10 | 11 | /** 12 | * Parse or format the given `val`. 13 | * 14 | * Options: 15 | * 16 | * - `long` verbose formatting [false] 17 | * 18 | * @param {String|Number} val 19 | * @param {Object} options 20 | * @return {String|Number} 21 | * @api public 22 | */ 23 | 24 | module.exports = function(val, options){ 25 | options = options || {}; 26 | if ('string' == typeof val) return parse(val); 27 | return options.long ? longFormat(val) : shortFormat(val); 28 | }; 29 | 30 | /** 31 | * Parse the given `str` and return milliseconds. 32 | * 33 | * @param {String} str 34 | * @return {Number} 35 | * @api private 36 | */ 37 | 38 | function parse(str) { 39 | var match = /^((?:\d+)?\.?\d+) *(ms|seconds?|s|minutes?|m|hours?|h|days?|d|years?|y)?$/i.exec(str); 40 | if (!match) return; 41 | var n = parseFloat(match[1]); 42 | var type = (match[2] || 'ms').toLowerCase(); 43 | switch (type) { 44 | case 'years': 45 | case 'year': 46 | case 'y': 47 | return n * y; 48 | case 'days': 49 | case 'day': 50 | case 'd': 51 | return n * d; 52 | case 'hours': 53 | case 'hour': 54 | case 'h': 55 | return n * h; 56 | case 'minutes': 57 | case 'minute': 58 | case 'm': 59 | return n * m; 60 | case 'seconds': 61 | case 'second': 62 | case 's': 63 | return n * s; 64 | case 'ms': 65 | return n; 66 | } 67 | } 68 | 69 | /** 70 | * Short format for `ms`. 71 | * 72 | * @param {Number} ms 73 | * @return {String} 74 | * @api private 75 | */ 76 | 77 | function shortFormat(ms) { 78 | if (ms >= d) return Math.round(ms / d) + 'd'; 79 | if (ms >= h) return Math.round(ms / h) + 'h'; 80 | if (ms >= m) return Math.round(ms / m) + 'm'; 81 | if (ms >= s) return Math.round(ms / s) + 's'; 82 | return ms + 'ms'; 83 | } 84 | 85 | /** 86 | * Long format for `ms`. 87 | * 88 | * @param {Number} ms 89 | * @return {String} 90 | * @api private 91 | */ 92 | 93 | function longFormat(ms) { 94 | return plural(ms, d, 'day') 95 | || plural(ms, h, 'hour') 96 | || plural(ms, m, 'minute') 97 | || plural(ms, s, 'second') 98 | || ms + ' ms'; 99 | } 100 | 101 | /** 102 | * Pluralization helper. 103 | */ 104 | 105 | function plural(ms, n, name) { 106 | if (ms < n) return; 107 | if (ms < n * 1.5) return Math.floor(ms / n) + ' ' + name; 108 | return Math.ceil(ms / n) + ' ' + name + 's'; 109 | } 110 | -------------------------------------------------------------------------------- /lib/reporters/doc.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Module dependencies. 4 | */ 5 | 6 | var Base = require('./base') 7 | , utils = require('../utils'); 8 | 9 | /** 10 | * Expose `Doc`. 11 | */ 12 | 13 | exports = module.exports = Doc; 14 | 15 | /** 16 | * Initialize a new `Doc` reporter. 17 | * 18 | * @param {Runner} runner 19 | * @api public 20 | */ 21 | 22 | function Doc(runner) { 23 | Base.call(this, runner); 24 | 25 | var self = this 26 | , stats = this.stats 27 | , total = runner.total 28 | , indents = 2; 29 | 30 | function indent() { 31 | return Array(indents).join(' '); 32 | } 33 | 34 | runner.on('suite', function(suite){ 35 | if (suite.root) return; 36 | ++indents; 37 | console.log('%s
', indent()); 38 | ++indents; 39 | console.log('%s

%s

', indent(), utils.escape(suite.title)); 40 | console.log('%s
', indent()); 41 | }); 42 | 43 | runner.on('suite end', function(suite){ 44 | if (suite.root) return; 45 | console.log('%s
', indent()); 46 | --indents; 47 | console.log('%s
', indent()); 48 | --indents; 49 | }); 50 | 51 | runner.on('pass', function(test){ 52 | console.log('%s
%s
', indent(), utils.escape(test.title)); 53 | var code = utils.escape(utils.clean(test.fn.toString())); 54 | console.log('%s
%s
', indent(), code); 55 | }); 56 | } 57 | -------------------------------------------------------------------------------- /lib/reporters/dot.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Module dependencies. 4 | */ 5 | 6 | var Base = require('./base') 7 | , color = Base.color; 8 | 9 | /** 10 | * Expose `Dot`. 11 | */ 12 | 13 | exports = module.exports = Dot; 14 | 15 | /** 16 | * Initialize a new `Dot` matrix test reporter. 17 | * 18 | * @param {Runner} runner 19 | * @api public 20 | */ 21 | 22 | function Dot(runner) { 23 | Base.call(this, runner); 24 | 25 | var self = this 26 | , stats = this.stats 27 | , width = Base.window.width * .75 | 0 28 | , n = 0; 29 | 30 | runner.on('start', function(){ 31 | process.stdout.write('\n '); 32 | }); 33 | 34 | runner.on('pending', function(test){ 35 | process.stdout.write(color('pending', Base.symbols.dot)); 36 | }); 37 | 38 | runner.on('pass', function(test){ 39 | if (++n % width == 0) process.stdout.write('\n '); 40 | if ('slow' == test.speed) { 41 | process.stdout.write(color('bright yellow', Base.symbols.dot)); 42 | } else { 43 | process.stdout.write(color(test.speed, Base.symbols.dot)); 44 | } 45 | }); 46 | 47 | runner.on('fail', function(test, err){ 48 | if (++n % width == 0) process.stdout.write('\n '); 49 | process.stdout.write(color('fail', Base.symbols.dot)); 50 | }); 51 | 52 | runner.on('end', function(){ 53 | console.log(); 54 | self.epilogue(); 55 | }); 56 | } 57 | 58 | /** 59 | * Inherit from `Base.prototype`. 60 | */ 61 | 62 | Dot.prototype.__proto__ = Base.prototype; -------------------------------------------------------------------------------- /lib/reporters/html-cov.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Module dependencies. 4 | */ 5 | 6 | var JSONCov = require('./json-cov') 7 | , fs = require('fs'); 8 | 9 | /** 10 | * Expose `HTMLCov`. 11 | */ 12 | 13 | exports = module.exports = HTMLCov; 14 | 15 | /** 16 | * Initialize a new `JsCoverage` reporter. 17 | * 18 | * @param {Runner} runner 19 | * @api public 20 | */ 21 | 22 | function HTMLCov(runner) { 23 | var jade = require('jade') 24 | , file = __dirname + '/templates/coverage.jade' 25 | , str = fs.readFileSync(file, 'utf8') 26 | , fn = jade.compile(str, { filename: file }) 27 | , self = this; 28 | 29 | JSONCov.call(this, runner, false); 30 | 31 | runner.on('end', function(){ 32 | process.stdout.write(fn({ 33 | cov: self.cov 34 | , coverageClass: coverageClass 35 | })); 36 | }); 37 | } 38 | 39 | /** 40 | * Return coverage class for `n`. 41 | * 42 | * @return {String} 43 | * @api private 44 | */ 45 | 46 | function coverageClass(n) { 47 | if (n >= 75) return 'high'; 48 | if (n >= 50) return 'medium'; 49 | if (n >= 25) return 'low'; 50 | return 'terrible'; 51 | } -------------------------------------------------------------------------------- /lib/reporters/html.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Module dependencies. 4 | */ 5 | 6 | var Base = require('./base') 7 | , utils = require('../utils') 8 | , Progress = require('../browser/progress') 9 | , escape = utils.escape; 10 | 11 | /** 12 | * Save timer references to avoid Sinon interfering (see GH-237). 13 | */ 14 | 15 | var Date = global.Date 16 | , setTimeout = global.setTimeout 17 | , setInterval = global.setInterval 18 | , clearTimeout = global.clearTimeout 19 | , clearInterval = global.clearInterval; 20 | 21 | /** 22 | * Expose `HTML`. 23 | */ 24 | 25 | exports = module.exports = HTML; 26 | 27 | /** 28 | * Stats template. 29 | */ 30 | 31 | var statsTemplate = ''; 37 | 38 | /** 39 | * Initialize a new `HTML` reporter. 40 | * 41 | * @param {Runner} runner 42 | * @api public 43 | */ 44 | 45 | function HTML(runner, root) { 46 | Base.call(this, runner); 47 | 48 | var self = this 49 | , stats = this.stats 50 | , total = runner.total 51 | , stat = fragment(statsTemplate) 52 | , items = stat.getElementsByTagName('li') 53 | , passes = items[1].getElementsByTagName('em')[0] 54 | , passesLink = items[1].getElementsByTagName('a')[0] 55 | , failures = items[2].getElementsByTagName('em')[0] 56 | , failuresLink = items[2].getElementsByTagName('a')[0] 57 | , duration = items[3].getElementsByTagName('em')[0] 58 | , canvas = stat.getElementsByTagName('canvas')[0] 59 | , report = fragment('') 60 | , stack = [report] 61 | , progress 62 | , ctx 63 | 64 | root = root || document.getElementById('mocha'); 65 | 66 | if (canvas.getContext) { 67 | var ratio = window.devicePixelRatio || 1; 68 | canvas.style.width = canvas.width; 69 | canvas.style.height = canvas.height; 70 | canvas.width *= ratio; 71 | canvas.height *= ratio; 72 | ctx = canvas.getContext('2d'); 73 | ctx.scale(ratio, ratio); 74 | progress = new Progress; 75 | } 76 | 77 | if (!root) return error('#mocha div missing, add it to your document'); 78 | 79 | // pass toggle 80 | on(passesLink, 'click', function(){ 81 | unhide(); 82 | var name = /pass/.test(report.className) ? '' : ' pass'; 83 | report.className = report.className.replace(/fail|pass/g, '') + name; 84 | if (report.className.trim()) hideSuitesWithout('test pass'); 85 | }); 86 | 87 | // failure toggle 88 | on(failuresLink, 'click', function(){ 89 | unhide(); 90 | var name = /fail/.test(report.className) ? '' : ' fail'; 91 | report.className = report.className.replace(/fail|pass/g, '') + name; 92 | if (report.className.trim()) hideSuitesWithout('test fail'); 93 | }); 94 | 95 | root.appendChild(stat); 96 | root.appendChild(report); 97 | 98 | if (progress) progress.size(40); 99 | 100 | runner.on('suite', function(suite){ 101 | if (suite.root) return; 102 | 103 | // suite 104 | var url = self.suiteURL(suite); 105 | var el = fragment('
  • %s

  • ', url, escape(suite.title)); 106 | 107 | // container 108 | stack[0].appendChild(el); 109 | stack.unshift(document.createElement('ul')); 110 | el.appendChild(stack[0]); 111 | }); 112 | 113 | runner.on('suite end', function(suite){ 114 | if (suite.root) return; 115 | stack.shift(); 116 | }); 117 | 118 | runner.on('fail', function(test, err){ 119 | if ('hook' == test.type) runner.emit('test end', test); 120 | }); 121 | 122 | runner.on('test end', function(test){ 123 | // TODO: add to stats 124 | var percent = stats.tests / this.total * 100 | 0; 125 | if (progress) progress.update(percent).draw(ctx); 126 | 127 | // update stats 128 | var ms = new Date - stats.start; 129 | text(passes, stats.passes); 130 | text(failures, stats.failures); 131 | text(duration, (ms / 1000).toFixed(2)); 132 | 133 | // test 134 | if ('passed' == test.state) { 135 | var url = self.testURL(test); 136 | var el = fragment('
  • %e%ems

  • ', test.speed, test.title, test.duration, url); 137 | } else if (test.pending) { 138 | var el = fragment('
  • %e

  • ', test.title); 139 | } else { 140 | var el = fragment('
  • %e

  • ', test.title, encodeURIComponent(test.fullTitle())); 141 | var str = test.err.stack || test.err.toString(); 142 | 143 | // FF / Opera do not add the message 144 | if (!~str.indexOf(test.err.message)) { 145 | str = test.err.message + '\n' + str; 146 | } 147 | 148 | // <=IE7 stringifies to [Object Error]. Since it can be overloaded, we 149 | // check for the result of the stringifying. 150 | if ('[object Error]' == str) str = test.err.message; 151 | 152 | // Safari doesn't give you a stack. Let's at least provide a source line. 153 | if (!test.err.stack && test.err.sourceURL && test.err.line !== undefined) { 154 | str += "\n(" + test.err.sourceURL + ":" + test.err.line + ")"; 155 | } 156 | 157 | el.appendChild(fragment('
    %e
    ', str)); 158 | } 159 | 160 | // toggle code 161 | // TODO: defer 162 | if (!test.pending) { 163 | var h2 = el.getElementsByTagName('h2')[0]; 164 | 165 | on(h2, 'click', function(){ 166 | pre.style.display = 'none' == pre.style.display 167 | ? 'block' 168 | : 'none'; 169 | }); 170 | 171 | var pre = fragment('
    %e
    ', utils.clean(test.fn.toString())); 172 | el.appendChild(pre); 173 | pre.style.display = 'none'; 174 | } 175 | 176 | // Don't call .appendChild if #mocha-report was already .shift()'ed off the stack. 177 | if (stack[0]) stack[0].appendChild(el); 178 | }); 179 | } 180 | 181 | /** 182 | * Provide suite URL 183 | * 184 | * @param {Object} [suite] 185 | */ 186 | 187 | HTML.prototype.suiteURL = function(suite){ 188 | return '?grep=' + encodeURIComponent(suite.fullTitle()); 189 | }; 190 | 191 | /** 192 | * Provide test URL 193 | * 194 | * @param {Object} [test] 195 | */ 196 | 197 | HTML.prototype.testURL = function(test){ 198 | return '?grep=' + encodeURIComponent(test.fullTitle()); 199 | }; 200 | 201 | /** 202 | * Display error `msg`. 203 | */ 204 | 205 | function error(msg) { 206 | document.body.appendChild(fragment('
    %s
    ', msg)); 207 | } 208 | 209 | /** 210 | * Return a DOM fragment from `html`. 211 | */ 212 | 213 | function fragment(html) { 214 | var args = arguments 215 | , div = document.createElement('div') 216 | , i = 1; 217 | 218 | div.innerHTML = html.replace(/%([se])/g, function(_, type){ 219 | switch (type) { 220 | case 's': return String(args[i++]); 221 | case 'e': return escape(args[i++]); 222 | } 223 | }); 224 | 225 | return div.firstChild; 226 | } 227 | 228 | /** 229 | * Check for suites that do not have elements 230 | * with `classname`, and hide them. 231 | */ 232 | 233 | function hideSuitesWithout(classname) { 234 | var suites = document.getElementsByClassName('suite'); 235 | for (var i = 0; i < suites.length; i++) { 236 | var els = suites[i].getElementsByClassName(classname); 237 | if (0 == els.length) suites[i].className += ' hidden'; 238 | } 239 | } 240 | 241 | /** 242 | * Unhide .hidden suites. 243 | */ 244 | 245 | function unhide() { 246 | var els = document.getElementsByClassName('suite hidden'); 247 | for (var i = 0; i < els.length; ++i) { 248 | els[i].className = els[i].className.replace('suite hidden', 'suite'); 249 | } 250 | } 251 | 252 | /** 253 | * Set `el` text to `str`. 254 | */ 255 | 256 | function text(el, str) { 257 | if (el.textContent) { 258 | el.textContent = str; 259 | } else { 260 | el.innerText = str; 261 | } 262 | } 263 | 264 | /** 265 | * Listen on `event` with callback `fn`. 266 | */ 267 | 268 | function on(el, event, fn) { 269 | if (el.addEventListener) { 270 | el.addEventListener(event, fn, false); 271 | } else { 272 | el.attachEvent('on' + event, fn); 273 | } 274 | } 275 | -------------------------------------------------------------------------------- /lib/reporters/index.js: -------------------------------------------------------------------------------- 1 | 2 | exports.Base = require('./base'); 3 | exports.Dot = require('./dot'); 4 | exports.Doc = require('./doc'); 5 | exports.TAP = require('./tap'); 6 | exports.JSON = require('./json'); 7 | exports.HTML = require('./html'); 8 | exports.List = require('./list'); 9 | exports.Min = require('./min'); 10 | exports.Spec = require('./spec'); 11 | exports.Nyan = require('./nyan'); 12 | exports.XUnit = require('./xunit'); 13 | exports.Markdown = require('./markdown'); 14 | exports.Progress = require('./progress'); 15 | exports.Landing = require('./landing'); 16 | exports.JSONCov = require('./json-cov'); 17 | exports.HTMLCov = require('./html-cov'); 18 | exports.JSONStream = require('./json-stream'); 19 | -------------------------------------------------------------------------------- /lib/reporters/json-cov.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Module dependencies. 4 | */ 5 | 6 | var Base = require('./base'); 7 | 8 | /** 9 | * Expose `JSONCov`. 10 | */ 11 | 12 | exports = module.exports = JSONCov; 13 | 14 | /** 15 | * Initialize a new `JsCoverage` reporter. 16 | * 17 | * @param {Runner} runner 18 | * @param {Boolean} output 19 | * @api public 20 | */ 21 | 22 | function JSONCov(runner, output) { 23 | var self = this 24 | , output = 1 == arguments.length ? true : output; 25 | 26 | Base.call(this, runner); 27 | 28 | var tests = [] 29 | , failures = [] 30 | , passes = []; 31 | 32 | runner.on('test end', function(test){ 33 | tests.push(test); 34 | }); 35 | 36 | runner.on('pass', function(test){ 37 | passes.push(test); 38 | }); 39 | 40 | runner.on('fail', function(test){ 41 | failures.push(test); 42 | }); 43 | 44 | runner.on('end', function(){ 45 | var cov = global._$jscoverage || {}; 46 | var result = self.cov = map(cov); 47 | result.stats = self.stats; 48 | result.tests = tests.map(clean); 49 | result.failures = failures.map(clean); 50 | result.passes = passes.map(clean); 51 | if (!output) return; 52 | process.stdout.write(JSON.stringify(result, null, 2 )); 53 | }); 54 | } 55 | 56 | /** 57 | * Map jscoverage data to a JSON structure 58 | * suitable for reporting. 59 | * 60 | * @param {Object} cov 61 | * @return {Object} 62 | * @api private 63 | */ 64 | 65 | function map(cov) { 66 | var ret = { 67 | instrumentation: 'node-jscoverage' 68 | , sloc: 0 69 | , hits: 0 70 | , misses: 0 71 | , coverage: 0 72 | , files: [] 73 | }; 74 | 75 | for (var filename in cov) { 76 | var data = coverage(filename, cov[filename]); 77 | ret.files.push(data); 78 | ret.hits += data.hits; 79 | ret.misses += data.misses; 80 | ret.sloc += data.sloc; 81 | } 82 | 83 | ret.files.sort(function(a, b) { 84 | return a.filename.localeCompare(b.filename); 85 | }); 86 | 87 | if (ret.sloc > 0) { 88 | ret.coverage = (ret.hits / ret.sloc) * 100; 89 | } 90 | 91 | return ret; 92 | }; 93 | 94 | /** 95 | * Map jscoverage data for a single source file 96 | * to a JSON structure suitable for reporting. 97 | * 98 | * @param {String} filename name of the source file 99 | * @param {Object} data jscoverage coverage data 100 | * @return {Object} 101 | * @api private 102 | */ 103 | 104 | function coverage(filename, data) { 105 | var ret = { 106 | filename: filename, 107 | coverage: 0, 108 | hits: 0, 109 | misses: 0, 110 | sloc: 0, 111 | source: {} 112 | }; 113 | 114 | data.source.forEach(function(line, num){ 115 | num++; 116 | 117 | if (data[num] === 0) { 118 | ret.misses++; 119 | ret.sloc++; 120 | } else if (data[num] !== undefined) { 121 | ret.hits++; 122 | ret.sloc++; 123 | } 124 | 125 | ret.source[num] = { 126 | source: line 127 | , coverage: data[num] === undefined 128 | ? '' 129 | : data[num] 130 | }; 131 | }); 132 | 133 | ret.coverage = ret.hits / ret.sloc * 100; 134 | 135 | return ret; 136 | } 137 | 138 | /** 139 | * Return a plain-object representation of `test` 140 | * free of cyclic properties etc. 141 | * 142 | * @param {Object} test 143 | * @return {Object} 144 | * @api private 145 | */ 146 | 147 | function clean(test) { 148 | return { 149 | title: test.title 150 | , fullTitle: test.fullTitle() 151 | , duration: test.duration 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /lib/reporters/json-stream.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Module dependencies. 4 | */ 5 | 6 | var Base = require('./base') 7 | , color = Base.color; 8 | 9 | /** 10 | * Expose `List`. 11 | */ 12 | 13 | exports = module.exports = List; 14 | 15 | /** 16 | * Initialize a new `List` test reporter. 17 | * 18 | * @param {Runner} runner 19 | * @api public 20 | */ 21 | 22 | function List(runner) { 23 | Base.call(this, runner); 24 | 25 | var self = this 26 | , stats = this.stats 27 | , total = runner.total; 28 | 29 | runner.on('start', function(){ 30 | console.log(JSON.stringify(['start', { total: total }])); 31 | }); 32 | 33 | runner.on('pass', function(test){ 34 | console.log(JSON.stringify(['pass', clean(test)])); 35 | }); 36 | 37 | runner.on('fail', function(test, err){ 38 | console.log(JSON.stringify(['fail', clean(test)])); 39 | }); 40 | 41 | runner.on('end', function(){ 42 | process.stdout.write(JSON.stringify(['end', self.stats])); 43 | }); 44 | } 45 | 46 | /** 47 | * Return a plain-object representation of `test` 48 | * free of cyclic properties etc. 49 | * 50 | * @param {Object} test 51 | * @return {Object} 52 | * @api private 53 | */ 54 | 55 | function clean(test) { 56 | return { 57 | title: test.title 58 | , fullTitle: test.fullTitle() 59 | , duration: test.duration 60 | } 61 | } -------------------------------------------------------------------------------- /lib/reporters/json.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Module dependencies. 4 | */ 5 | 6 | var Base = require('./base') 7 | , cursor = Base.cursor 8 | , color = Base.color; 9 | 10 | /** 11 | * Expose `JSON`. 12 | */ 13 | 14 | exports = module.exports = JSONReporter; 15 | 16 | /** 17 | * Initialize a new `JSON` reporter. 18 | * 19 | * @param {Runner} runner 20 | * @api public 21 | */ 22 | 23 | function JSONReporter(runner) { 24 | var self = this; 25 | Base.call(this, runner); 26 | 27 | var tests = [] 28 | , failures = [] 29 | , passes = []; 30 | 31 | runner.on('test end', function(test){ 32 | tests.push(test); 33 | }); 34 | 35 | runner.on('pass', function(test){ 36 | passes.push(test); 37 | }); 38 | 39 | runner.on('fail', function(test){ 40 | failures.push(test); 41 | }); 42 | 43 | runner.on('end', function(){ 44 | var obj = { 45 | stats: self.stats 46 | , tests: tests.map(clean) 47 | , failures: failures.map(clean) 48 | , passes: passes.map(clean) 49 | }; 50 | 51 | process.stdout.write(JSON.stringify(obj, null, 2)); 52 | }); 53 | } 54 | 55 | /** 56 | * Return a plain-object representation of `test` 57 | * free of cyclic properties etc. 58 | * 59 | * @param {Object} test 60 | * @return {Object} 61 | * @api private 62 | */ 63 | 64 | function clean(test) { 65 | return { 66 | title: test.title 67 | , fullTitle: test.fullTitle() 68 | , duration: test.duration 69 | } 70 | } -------------------------------------------------------------------------------- /lib/reporters/landing.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Module dependencies. 4 | */ 5 | 6 | var Base = require('./base') 7 | , cursor = Base.cursor 8 | , color = Base.color; 9 | 10 | /** 11 | * Expose `Landing`. 12 | */ 13 | 14 | exports = module.exports = Landing; 15 | 16 | /** 17 | * Airplane color. 18 | */ 19 | 20 | Base.colors.plane = 0; 21 | 22 | /** 23 | * Airplane crash color. 24 | */ 25 | 26 | Base.colors['plane crash'] = 31; 27 | 28 | /** 29 | * Runway color. 30 | */ 31 | 32 | Base.colors.runway = 90; 33 | 34 | /** 35 | * Initialize a new `Landing` reporter. 36 | * 37 | * @param {Runner} runner 38 | * @api public 39 | */ 40 | 41 | function Landing(runner) { 42 | Base.call(this, runner); 43 | 44 | var self = this 45 | , stats = this.stats 46 | , width = Base.window.width * .75 | 0 47 | , total = runner.total 48 | , stream = process.stdout 49 | , plane = color('plane', '✈') 50 | , crashed = -1 51 | , n = 0; 52 | 53 | function runway() { 54 | var buf = Array(width).join('-'); 55 | return ' ' + color('runway', buf); 56 | } 57 | 58 | runner.on('start', function(){ 59 | stream.write('\n '); 60 | cursor.hide(); 61 | }); 62 | 63 | runner.on('test end', function(test){ 64 | // check if the plane crashed 65 | var col = -1 == crashed 66 | ? width * ++n / total | 0 67 | : crashed; 68 | 69 | // show the crash 70 | if ('failed' == test.state) { 71 | plane = color('plane crash', '✈'); 72 | crashed = col; 73 | } 74 | 75 | // render landing strip 76 | stream.write('\u001b[4F\n\n'); 77 | stream.write(runway()); 78 | stream.write('\n '); 79 | stream.write(color('runway', Array(col).join('⋅'))); 80 | stream.write(plane) 81 | stream.write(color('runway', Array(width - col).join('⋅') + '\n')); 82 | stream.write(runway()); 83 | stream.write('\u001b[0m'); 84 | }); 85 | 86 | runner.on('end', function(){ 87 | cursor.show(); 88 | console.log(); 89 | self.epilogue(); 90 | }); 91 | } 92 | 93 | /** 94 | * Inherit from `Base.prototype`. 95 | */ 96 | 97 | Landing.prototype.__proto__ = Base.prototype; -------------------------------------------------------------------------------- /lib/reporters/list.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Module dependencies. 4 | */ 5 | 6 | var Base = require('./base') 7 | , cursor = Base.cursor 8 | , color = Base.color; 9 | 10 | /** 11 | * Expose `List`. 12 | */ 13 | 14 | exports = module.exports = List; 15 | 16 | /** 17 | * Initialize a new `List` test reporter. 18 | * 19 | * @param {Runner} runner 20 | * @api public 21 | */ 22 | 23 | function List(runner) { 24 | Base.call(this, runner); 25 | 26 | var self = this 27 | , stats = this.stats 28 | , n = 0; 29 | 30 | runner.on('start', function(){ 31 | console.log(); 32 | }); 33 | 34 | runner.on('test', function(test){ 35 | process.stdout.write(color('pass', ' ' + test.fullTitle() + ': ')); 36 | }); 37 | 38 | runner.on('pending', function(test){ 39 | var fmt = color('checkmark', ' -') 40 | + color('pending', ' %s'); 41 | console.log(fmt, test.fullTitle()); 42 | }); 43 | 44 | runner.on('pass', function(test){ 45 | var fmt = color('checkmark', ' '+Base.symbols.dot) 46 | + color('pass', ' %s: ') 47 | + color(test.speed, '%dms'); 48 | cursor.CR(); 49 | console.log(fmt, test.fullTitle(), test.duration); 50 | }); 51 | 52 | runner.on('fail', function(test, err){ 53 | cursor.CR(); 54 | console.log(color('fail', ' %d) %s'), ++n, test.fullTitle()); 55 | }); 56 | 57 | runner.on('end', self.epilogue.bind(self)); 58 | } 59 | 60 | /** 61 | * Inherit from `Base.prototype`. 62 | */ 63 | 64 | List.prototype.__proto__ = Base.prototype; 65 | -------------------------------------------------------------------------------- /lib/reporters/markdown.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies. 3 | */ 4 | 5 | var Base = require('./base') 6 | , utils = require('../utils'); 7 | 8 | /** 9 | * Expose `Markdown`. 10 | */ 11 | 12 | exports = module.exports = Markdown; 13 | 14 | /** 15 | * Initialize a new `Markdown` reporter. 16 | * 17 | * @param {Runner} runner 18 | * @api public 19 | */ 20 | 21 | function Markdown(runner) { 22 | Base.call(this, runner); 23 | 24 | var self = this 25 | , stats = this.stats 26 | , level = 0 27 | , buf = ''; 28 | 29 | function title(str) { 30 | return Array(level).join('#') + ' ' + str; 31 | } 32 | 33 | function indent() { 34 | return Array(level).join(' '); 35 | } 36 | 37 | function mapTOC(suite, obj) { 38 | var ret = obj; 39 | obj = obj[suite.title] = obj[suite.title] || { suite: suite }; 40 | suite.suites.forEach(function(suite){ 41 | mapTOC(suite, obj); 42 | }); 43 | return ret; 44 | } 45 | 46 | function stringifyTOC(obj, level) { 47 | ++level; 48 | var buf = ''; 49 | var link; 50 | for (var key in obj) { 51 | if ('suite' == key) continue; 52 | if (key) link = ' - [' + key + '](#' + utils.slug(obj[key].suite.fullTitle()) + ')\n'; 53 | if (key) buf += Array(level).join(' ') + link; 54 | buf += stringifyTOC(obj[key], level); 55 | } 56 | --level; 57 | return buf; 58 | } 59 | 60 | function generateTOC(suite) { 61 | var obj = mapTOC(suite, {}); 62 | return stringifyTOC(obj, 0); 63 | } 64 | 65 | generateTOC(runner.suite); 66 | 67 | runner.on('suite', function(suite){ 68 | ++level; 69 | var slug = utils.slug(suite.fullTitle()); 70 | buf += '' + '\n'; 71 | buf += title(suite.title) + '\n'; 72 | }); 73 | 74 | runner.on('suite end', function(suite){ 75 | --level; 76 | }); 77 | 78 | runner.on('pass', function(test){ 79 | var code = utils.clean(test.fn.toString()); 80 | buf += test.title + '.\n'; 81 | buf += '\n```js\n'; 82 | buf += code + '\n'; 83 | buf += '```\n\n'; 84 | }); 85 | 86 | runner.on('end', function(){ 87 | process.stdout.write('# TOC\n'); 88 | process.stdout.write(generateTOC(runner.suite)); 89 | process.stdout.write(buf); 90 | }); 91 | } -------------------------------------------------------------------------------- /lib/reporters/min.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Module dependencies. 4 | */ 5 | 6 | var Base = require('./base'); 7 | 8 | /** 9 | * Expose `Min`. 10 | */ 11 | 12 | exports = module.exports = Min; 13 | 14 | /** 15 | * Initialize a new `Min` minimal test reporter (best used with --watch). 16 | * 17 | * @param {Runner} runner 18 | * @api public 19 | */ 20 | 21 | function Min(runner) { 22 | Base.call(this, runner); 23 | 24 | runner.on('start', function(){ 25 | // clear screen 26 | process.stdout.write('\u001b[2J'); 27 | // set cursor position 28 | process.stdout.write('\u001b[1;3H'); 29 | }); 30 | 31 | runner.on('end', this.epilogue.bind(this)); 32 | } 33 | 34 | /** 35 | * Inherit from `Base.prototype`. 36 | */ 37 | 38 | Min.prototype.__proto__ = Base.prototype; 39 | -------------------------------------------------------------------------------- /lib/reporters/nyan.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies. 3 | */ 4 | 5 | var Base = require('./base') 6 | , color = Base.color; 7 | 8 | /** 9 | * Expose `Dot`. 10 | */ 11 | 12 | exports = module.exports = NyanCat; 13 | 14 | /** 15 | * Initialize a new `Dot` matrix test reporter. 16 | * 17 | * @param {Runner} runner 18 | * @api public 19 | */ 20 | 21 | function NyanCat(runner) { 22 | Base.call(this, runner); 23 | var self = this 24 | , stats = this.stats 25 | , width = Base.window.width * .75 | 0 26 | , rainbowColors = this.rainbowColors = self.generateColors() 27 | , colorIndex = this.colorIndex = 0 28 | , numerOfLines = this.numberOfLines = 4 29 | , trajectories = this.trajectories = [[], [], [], []] 30 | , nyanCatWidth = this.nyanCatWidth = 11 31 | , trajectoryWidthMax = this.trajectoryWidthMax = (width - nyanCatWidth) 32 | , scoreboardWidth = this.scoreboardWidth = 5 33 | , tick = this.tick = 0 34 | , n = 0; 35 | 36 | runner.on('start', function(){ 37 | Base.cursor.hide(); 38 | self.draw(); 39 | }); 40 | 41 | runner.on('pending', function(test){ 42 | self.draw(); 43 | }); 44 | 45 | runner.on('pass', function(test){ 46 | self.draw(); 47 | }); 48 | 49 | runner.on('fail', function(test, err){ 50 | self.draw(); 51 | }); 52 | 53 | runner.on('end', function(){ 54 | Base.cursor.show(); 55 | for (var i = 0; i < self.numberOfLines; i++) write('\n'); 56 | self.epilogue(); 57 | }); 58 | } 59 | 60 | /** 61 | * Draw the nyan cat 62 | * 63 | * @api private 64 | */ 65 | 66 | NyanCat.prototype.draw = function(){ 67 | this.appendRainbow(); 68 | this.drawScoreboard(); 69 | this.drawRainbow(); 70 | this.drawNyanCat(); 71 | this.tick = !this.tick; 72 | }; 73 | 74 | /** 75 | * Draw the "scoreboard" showing the number 76 | * of passes, failures and pending tests. 77 | * 78 | * @api private 79 | */ 80 | 81 | NyanCat.prototype.drawScoreboard = function(){ 82 | var stats = this.stats; 83 | var colors = Base.colors; 84 | 85 | function draw(color, n) { 86 | write(' '); 87 | write('\u001b[' + color + 'm' + n + '\u001b[0m'); 88 | write('\n'); 89 | } 90 | 91 | draw(colors.green, stats.passes); 92 | draw(colors.fail, stats.failures); 93 | draw(colors.pending, stats.pending); 94 | write('\n'); 95 | 96 | this.cursorUp(this.numberOfLines); 97 | }; 98 | 99 | /** 100 | * Append the rainbow. 101 | * 102 | * @api private 103 | */ 104 | 105 | NyanCat.prototype.appendRainbow = function(){ 106 | var segment = this.tick ? '_' : '-'; 107 | var rainbowified = this.rainbowify(segment); 108 | 109 | for (var index = 0; index < this.numberOfLines; index++) { 110 | var trajectory = this.trajectories[index]; 111 | if (trajectory.length >= this.trajectoryWidthMax) trajectory.shift(); 112 | trajectory.push(rainbowified); 113 | } 114 | }; 115 | 116 | /** 117 | * Draw the rainbow. 118 | * 119 | * @api private 120 | */ 121 | 122 | NyanCat.prototype.drawRainbow = function(){ 123 | var self = this; 124 | 125 | this.trajectories.forEach(function(line, index) { 126 | write('\u001b[' + self.scoreboardWidth + 'C'); 127 | write(line.join('')); 128 | write('\n'); 129 | }); 130 | 131 | this.cursorUp(this.numberOfLines); 132 | }; 133 | 134 | /** 135 | * Draw the nyan cat 136 | * 137 | * @api private 138 | */ 139 | 140 | NyanCat.prototype.drawNyanCat = function() { 141 | var self = this; 142 | var startWidth = this.scoreboardWidth + this.trajectories[0].length; 143 | var color = '\u001b[' + startWidth + 'C'; 144 | var padding = ''; 145 | 146 | write(color); 147 | write('_,------,'); 148 | write('\n'); 149 | 150 | write(color); 151 | padding = self.tick ? ' ' : ' '; 152 | write('_|' + padding + '/\\_/\\ '); 153 | write('\n'); 154 | 155 | write(color); 156 | padding = self.tick ? '_' : '__'; 157 | var tail = self.tick ? '~' : '^'; 158 | var face; 159 | write(tail + '|' + padding + this.face() + ' '); 160 | write('\n'); 161 | 162 | write(color); 163 | padding = self.tick ? ' ' : ' '; 164 | write(padding + '"" "" '); 165 | write('\n'); 166 | 167 | this.cursorUp(this.numberOfLines); 168 | }; 169 | 170 | /** 171 | * Draw nyan cat face. 172 | * 173 | * @return {String} 174 | * @api private 175 | */ 176 | 177 | NyanCat.prototype.face = function() { 178 | var stats = this.stats; 179 | if (stats.failures) { 180 | return '( x .x)'; 181 | } else if (stats.pending) { 182 | return '( o .o)'; 183 | } else if(stats.passes) { 184 | return '( ^ .^)'; 185 | } else { 186 | return '( - .-)'; 187 | } 188 | } 189 | 190 | /** 191 | * Move cursor up `n`. 192 | * 193 | * @param {Number} n 194 | * @api private 195 | */ 196 | 197 | NyanCat.prototype.cursorUp = function(n) { 198 | write('\u001b[' + n + 'A'); 199 | }; 200 | 201 | /** 202 | * Move cursor down `n`. 203 | * 204 | * @param {Number} n 205 | * @api private 206 | */ 207 | 208 | NyanCat.prototype.cursorDown = function(n) { 209 | write('\u001b[' + n + 'B'); 210 | }; 211 | 212 | /** 213 | * Generate rainbow colors. 214 | * 215 | * @return {Array} 216 | * @api private 217 | */ 218 | 219 | NyanCat.prototype.generateColors = function(){ 220 | var colors = []; 221 | 222 | for (var i = 0; i < (6 * 7); i++) { 223 | var pi3 = Math.floor(Math.PI / 3); 224 | var n = (i * (1.0 / 6)); 225 | var r = Math.floor(3 * Math.sin(n) + 3); 226 | var g = Math.floor(3 * Math.sin(n + 2 * pi3) + 3); 227 | var b = Math.floor(3 * Math.sin(n + 4 * pi3) + 3); 228 | colors.push(36 * r + 6 * g + b + 16); 229 | } 230 | 231 | return colors; 232 | }; 233 | 234 | /** 235 | * Apply rainbow to the given `str`. 236 | * 237 | * @param {String} str 238 | * @return {String} 239 | * @api private 240 | */ 241 | 242 | NyanCat.prototype.rainbowify = function(str){ 243 | var color = this.rainbowColors[this.colorIndex % this.rainbowColors.length]; 244 | this.colorIndex += 1; 245 | return '\u001b[38;5;' + color + 'm' + str + '\u001b[0m'; 246 | }; 247 | 248 | /** 249 | * Stdout helper. 250 | */ 251 | 252 | function write(string) { 253 | process.stdout.write(string); 254 | } 255 | 256 | /** 257 | * Inherit from `Base.prototype`. 258 | */ 259 | 260 | NyanCat.prototype.__proto__ = Base.prototype; 261 | -------------------------------------------------------------------------------- /lib/reporters/progress.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Module dependencies. 4 | */ 5 | 6 | var Base = require('./base') 7 | , cursor = Base.cursor 8 | , color = Base.color; 9 | 10 | /** 11 | * Expose `Progress`. 12 | */ 13 | 14 | exports = module.exports = Progress; 15 | 16 | /** 17 | * General progress bar color. 18 | */ 19 | 20 | Base.colors.progress = 90; 21 | 22 | /** 23 | * Initialize a new `Progress` bar test reporter. 24 | * 25 | * @param {Runner} runner 26 | * @param {Object} options 27 | * @api public 28 | */ 29 | 30 | function Progress(runner, options) { 31 | Base.call(this, runner); 32 | 33 | var self = this 34 | , options = options || {} 35 | , stats = this.stats 36 | , width = Base.window.width * .50 | 0 37 | , total = runner.total 38 | , complete = 0 39 | , max = Math.max; 40 | 41 | // default chars 42 | options.open = options.open || '['; 43 | options.complete = options.complete || '▬'; 44 | options.incomplete = options.incomplete || Base.symbols.dot; 45 | options.close = options.close || ']'; 46 | options.verbose = false; 47 | 48 | // tests started 49 | runner.on('start', function(){ 50 | console.log(); 51 | cursor.hide(); 52 | }); 53 | 54 | // tests complete 55 | runner.on('test end', function(){ 56 | complete++; 57 | var incomplete = total - complete 58 | , percent = complete / total 59 | , n = width * percent | 0 60 | , i = width - n; 61 | 62 | cursor.CR(); 63 | process.stdout.write('\u001b[J'); 64 | process.stdout.write(color('progress', ' ' + options.open)); 65 | process.stdout.write(Array(n).join(options.complete)); 66 | process.stdout.write(Array(i).join(options.incomplete)); 67 | process.stdout.write(color('progress', options.close)); 68 | if (options.verbose) { 69 | process.stdout.write(color('progress', ' ' + complete + ' of ' + total)); 70 | } 71 | }); 72 | 73 | // tests are complete, output some stats 74 | // and the failures if any 75 | runner.on('end', function(){ 76 | cursor.show(); 77 | console.log(); 78 | self.epilogue(); 79 | }); 80 | } 81 | 82 | /** 83 | * Inherit from `Base.prototype`. 84 | */ 85 | 86 | Progress.prototype.__proto__ = Base.prototype; 87 | -------------------------------------------------------------------------------- /lib/reporters/spec.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Module dependencies. 4 | */ 5 | 6 | var Base = require('./base') 7 | , cursor = Base.cursor 8 | , color = Base.color; 9 | 10 | /** 11 | * Expose `Spec`. 12 | */ 13 | 14 | exports = module.exports = Spec; 15 | 16 | /** 17 | * Initialize a new `Spec` test reporter. 18 | * 19 | * @param {Runner} runner 20 | * @api public 21 | */ 22 | 23 | function Spec(runner) { 24 | Base.call(this, runner); 25 | 26 | var self = this 27 | , stats = this.stats 28 | , indents = 0 29 | , n = 0; 30 | 31 | function indent() { 32 | return Array(indents).join(' ') 33 | } 34 | 35 | runner.on('start', function(){ 36 | console.log(); 37 | }); 38 | 39 | runner.on('suite', function(suite){ 40 | ++indents; 41 | console.log(color('suite', '%s%s'), indent(), suite.title); 42 | }); 43 | 44 | runner.on('suite end', function(suite){ 45 | --indents; 46 | if (1 == indents) console.log(); 47 | }); 48 | 49 | runner.on('pending', function(test){ 50 | var fmt = indent() + color('pending', ' - %s'); 51 | console.log(fmt, test.title); 52 | }); 53 | 54 | runner.on('pass', function(test){ 55 | if ('fast' == test.speed) { 56 | var fmt = indent() 57 | + color('checkmark', ' ' + Base.symbols.ok) 58 | + color('pass', ' %s '); 59 | cursor.CR(); 60 | console.log(fmt, test.title); 61 | } else { 62 | var fmt = indent() 63 | + color('checkmark', ' ' + Base.symbols.ok) 64 | + color('pass', ' %s ') 65 | + color(test.speed, '(%dms)'); 66 | cursor.CR(); 67 | console.log(fmt, test.title, test.duration); 68 | } 69 | }); 70 | 71 | runner.on('fail', function(test, err){ 72 | cursor.CR(); 73 | console.log(indent() + color('fail', ' %d) %s'), ++n, test.title); 74 | }); 75 | 76 | runner.on('end', self.epilogue.bind(self)); 77 | } 78 | 79 | /** 80 | * Inherit from `Base.prototype`. 81 | */ 82 | 83 | Spec.prototype.__proto__ = Base.prototype; 84 | -------------------------------------------------------------------------------- /lib/reporters/tap.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Module dependencies. 4 | */ 5 | 6 | var Base = require('./base') 7 | , cursor = Base.cursor 8 | , color = Base.color; 9 | 10 | /** 11 | * Expose `TAP`. 12 | */ 13 | 14 | exports = module.exports = TAP; 15 | 16 | /** 17 | * Initialize a new `TAP` reporter. 18 | * 19 | * @param {Runner} runner 20 | * @api public 21 | */ 22 | 23 | function TAP(runner) { 24 | Base.call(this, runner); 25 | 26 | var self = this 27 | , stats = this.stats 28 | , n = 1 29 | , passes = 0 30 | , failures = 0; 31 | 32 | runner.on('start', function(){ 33 | var total = runner.grepTotal(runner.suite); 34 | console.log('%d..%d', 1, total); 35 | }); 36 | 37 | runner.on('test end', function(){ 38 | ++n; 39 | }); 40 | 41 | runner.on('pending', function(test){ 42 | console.log('ok %d %s # SKIP -', n, title(test)); 43 | }); 44 | 45 | runner.on('pass', function(test){ 46 | passes++; 47 | console.log('ok %d %s', n, title(test)); 48 | }); 49 | 50 | runner.on('fail', function(test, err){ 51 | failures++; 52 | console.log('not ok %d %s', n, title(test)); 53 | if (err.stack) console.log(err.stack.replace(/^/gm, ' ')); 54 | }); 55 | 56 | runner.on('end', function(){ 57 | console.log('# tests ' + (passes + failures)); 58 | console.log('# pass ' + passes); 59 | console.log('# fail ' + failures); 60 | }); 61 | } 62 | 63 | /** 64 | * Return a TAP-safe title of `test` 65 | * 66 | * @param {Object} test 67 | * @return {String} 68 | * @api private 69 | */ 70 | 71 | function title(test) { 72 | return test.fullTitle().replace(/#/g, ''); 73 | } 74 | -------------------------------------------------------------------------------- /lib/reporters/templates/coverage.jade: -------------------------------------------------------------------------------- 1 | !!! 5 2 | html 3 | head 4 | title Coverage 5 | include script.html 6 | include style.html 7 | body 8 | #coverage 9 | h1#overview Coverage 10 | include menu 11 | 12 | #stats(class=coverageClass(cov.coverage)) 13 | .percentage #{cov.coverage | 0}% 14 | .sloc= cov.sloc 15 | .hits= cov.hits 16 | .misses= cov.misses 17 | 18 | #files 19 | for file in cov.files 20 | .file 21 | h2(id=file.filename)= file.filename 22 | #stats(class=coverageClass(file.coverage)) 23 | .percentage #{file.coverage | 0}% 24 | .sloc= file.sloc 25 | .hits= file.hits 26 | .misses= file.misses 27 | 28 | table#source 29 | thead 30 | tr 31 | th Line 32 | th Hits 33 | th Source 34 | tbody 35 | for line, number in file.source 36 | if line.coverage > 0 37 | tr.hit 38 | td.line= number 39 | td.hits= line.coverage 40 | td.source= line.source 41 | else if 0 === line.coverage 42 | tr.miss 43 | td.line= number 44 | td.hits 0 45 | td.source= line.source 46 | else 47 | tr 48 | td.line= number 49 | td.hits 50 | td.source= line.source || ' ' 51 | -------------------------------------------------------------------------------- /lib/reporters/templates/menu.jade: -------------------------------------------------------------------------------- 1 | #menu 2 | li 3 | a(href='#overview') overview 4 | for file in cov.files 5 | li 6 | span.cov(class=coverageClass(file.coverage)) #{file.coverage | 0} 7 | a(href='##{file.filename}') 8 | segments = file.filename.split('/') 9 | basename = segments.pop() 10 | if segments.length 11 | span.dirname= segments.join('/') + '/' 12 | span.basename= basename 13 | a#logo(href='http://visionmedia.github.io/mocha/') m 14 | -------------------------------------------------------------------------------- /lib/reporters/templates/script.html: -------------------------------------------------------------------------------- 1 | 35 | -------------------------------------------------------------------------------- /lib/reporters/xunit.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Module dependencies. 4 | */ 5 | 6 | var Base = require('./base') 7 | , utils = require('../utils') 8 | , escape = utils.escape; 9 | 10 | /** 11 | * Save timer references to avoid Sinon interfering (see GH-237). 12 | */ 13 | 14 | var Date = global.Date 15 | , setTimeout = global.setTimeout 16 | , setInterval = global.setInterval 17 | , clearTimeout = global.clearTimeout 18 | , clearInterval = global.clearInterval; 19 | 20 | /** 21 | * Expose `XUnit`. 22 | */ 23 | 24 | exports = module.exports = XUnit; 25 | 26 | /** 27 | * Initialize a new `XUnit` reporter. 28 | * 29 | * @param {Runner} runner 30 | * @api public 31 | */ 32 | 33 | function XUnit(runner) { 34 | Base.call(this, runner); 35 | var stats = this.stats 36 | , tests = [] 37 | , self = this; 38 | 39 | runner.on('pending', function(test){ 40 | tests.push(test); 41 | }); 42 | 43 | runner.on('pass', function(test){ 44 | tests.push(test); 45 | }); 46 | 47 | runner.on('fail', function(test){ 48 | tests.push(test); 49 | }); 50 | 51 | runner.on('end', function(){ 52 | console.log(tag('testsuite', { 53 | name: 'Mocha Tests' 54 | , tests: stats.tests 55 | , failures: stats.failures 56 | , errors: stats.failures 57 | , skipped: stats.tests - stats.failures - stats.passes 58 | , timestamp: (new Date).toUTCString() 59 | , time: (stats.duration / 1000) || 0 60 | }, false)); 61 | 62 | tests.forEach(test); 63 | console.log(''); 64 | }); 65 | } 66 | 67 | /** 68 | * Inherit from `Base.prototype`. 69 | */ 70 | 71 | XUnit.prototype.__proto__ = Base.prototype; 72 | 73 | /** 74 | * Output tag for the given `test.` 75 | */ 76 | 77 | function test(test) { 78 | var attrs = { 79 | classname: test.parent.fullTitle() 80 | , name: test.title 81 | , time: (test.duration / 1000) || 0 82 | }; 83 | 84 | if ('failed' == test.state) { 85 | var err = test.err; 86 | attrs.message = escape(err.message); 87 | console.log(tag('testcase', attrs, false, tag('failure', attrs, false, cdata(err.stack)))); 88 | } else if (test.pending) { 89 | console.log(tag('testcase', attrs, false, tag('skipped', {}, true))); 90 | } else { 91 | console.log(tag('testcase', attrs, true) ); 92 | } 93 | } 94 | 95 | /** 96 | * HTML tag helper. 97 | */ 98 | 99 | function tag(name, attrs, close, content) { 100 | var end = close ? '/>' : '>' 101 | , pairs = [] 102 | , tag; 103 | 104 | for (var key in attrs) { 105 | pairs.push(key + '="' + escape(attrs[key]) + '"'); 106 | } 107 | 108 | tag = '<' + name + (pairs.length ? ' ' + pairs.join(' ') : '') + end; 109 | if (content) tag += content + ''; 119 | } 120 | -------------------------------------------------------------------------------- /lib/runnable.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies. 3 | */ 4 | 5 | var EventEmitter = require('events').EventEmitter 6 | , debug = require('debug')('mocha:runnable') 7 | , milliseconds = require('./ms') 8 | , co = require('co'); 9 | 10 | /** 11 | * Save timer references to avoid Sinon interfering (see GH-237). 12 | */ 13 | 14 | var Date = global.Date 15 | , setTimeout = global.setTimeout 16 | , setInterval = global.setInterval 17 | , clearTimeout = global.clearTimeout 18 | , clearInterval = global.clearInterval; 19 | 20 | /** 21 | * Object#toString(). 22 | */ 23 | 24 | var toString = Object.prototype.toString; 25 | 26 | /** 27 | * Expose `Runnable`. 28 | */ 29 | 30 | module.exports = Runnable; 31 | 32 | /** 33 | * Initialize a new `Runnable` with the given `title` and callback `fn`. 34 | * 35 | * @param {String} title 36 | * @param {Function} fn 37 | * @api private 38 | */ 39 | 40 | function Runnable(title, fn) { 41 | this.title = title; 42 | this.fn = fn; 43 | this.async = fn && fn.length; 44 | this.sync = ! this.async; 45 | this._timeout = 2000; 46 | this._slow = 75; 47 | this.timedOut = false; 48 | } 49 | 50 | /** 51 | * Inherit from `EventEmitter.prototype`. 52 | */ 53 | 54 | Runnable.prototype.__proto__ = EventEmitter.prototype; 55 | 56 | /** 57 | * Set & get timeout `ms`. 58 | * 59 | * @param {Number|String} ms 60 | * @return {Runnable|Number} ms or self 61 | * @api private 62 | */ 63 | 64 | Runnable.prototype.timeout = function(ms){ 65 | if (0 == arguments.length) return this._timeout; 66 | if ('string' == typeof ms) ms = milliseconds(ms); 67 | debug('timeout %d', ms); 68 | this._timeout = ms; 69 | if (this.timer) this.resetTimeout(); 70 | return this; 71 | }; 72 | 73 | /** 74 | * Set & get slow `ms`. 75 | * 76 | * @param {Number|String} ms 77 | * @return {Runnable|Number} ms or self 78 | * @api private 79 | */ 80 | 81 | Runnable.prototype.slow = function(ms){ 82 | if (0 === arguments.length) return this._slow; 83 | if ('string' == typeof ms) ms = milliseconds(ms); 84 | debug('timeout %d', ms); 85 | this._slow = ms; 86 | return this; 87 | }; 88 | 89 | /** 90 | * Return the full title generated by recursively 91 | * concatenating the parent's full title. 92 | * 93 | * @return {String} 94 | * @api public 95 | */ 96 | 97 | Runnable.prototype.fullTitle = function(){ 98 | return this.parent.fullTitle() + ' ' + this.title; 99 | }; 100 | 101 | /** 102 | * Clear the timeout. 103 | * 104 | * @api private 105 | */ 106 | 107 | Runnable.prototype.clearTimeout = function(){ 108 | clearTimeout(this.timer); 109 | }; 110 | 111 | /** 112 | * Inspect the runnable void of private properties. 113 | * 114 | * @return {String} 115 | * @api private 116 | */ 117 | 118 | Runnable.prototype.inspect = function(){ 119 | return JSON.stringify(this, function(key, val){ 120 | if ('_' == key[0]) return; 121 | if ('parent' == key) return '#'; 122 | if ('ctx' == key) return '#'; 123 | return val; 124 | }, 2); 125 | }; 126 | 127 | /** 128 | * Reset the timeout. 129 | * 130 | * @api private 131 | */ 132 | 133 | Runnable.prototype.resetTimeout = function(){ 134 | var self = this; 135 | var ms = this.timeout() || 1e9; 136 | 137 | this.clearTimeout(); 138 | this.timer = setTimeout(function(){ 139 | self.callback(new Error('timeout of ' + ms + 'ms exceeded')); 140 | self.timedOut = true; 141 | }, ms); 142 | }; 143 | 144 | /** 145 | * Whitelist these globals for this test run 146 | * 147 | * @api private 148 | */ 149 | Runnable.prototype.globals = function(arr){ 150 | var self = this; 151 | this._allowedGlobals = arr; 152 | }; 153 | 154 | /** 155 | * Run the test and invoke `fn(err)`. 156 | * 157 | * @param {Function} fn 158 | * @api private 159 | */ 160 | 161 | Runnable.prototype.run = function(fn){ 162 | var self = this 163 | , ms = this.timeout() 164 | , start = new Date 165 | , ctx = this.ctx 166 | , finished 167 | , emitted; 168 | 169 | if (ctx) ctx.runnable(this); 170 | 171 | // timeout 172 | if (this.async) { 173 | if (ms) { 174 | this.timer = setTimeout(function(){ 175 | done(new Error('timeout of ' + ms + 'ms exceeded')); 176 | self.timedOut = true; 177 | }, ms); 178 | } 179 | } 180 | 181 | // called multiple times 182 | function multiple(err) { 183 | if (emitted) return; 184 | emitted = true; 185 | self.emit('error', err || new Error('done() called multiple times')); 186 | } 187 | 188 | // finished 189 | function done(err) { 190 | if (self.timedOut) return; 191 | if (finished) return multiple(err); 192 | self.clearTimeout(); 193 | self.duration = new Date - start; 194 | finished = true; 195 | fn(err); 196 | } 197 | 198 | // for .resetTimeout() 199 | this.callback = done; 200 | 201 | // async 202 | if (this.async) { 203 | try { 204 | this.fn.call(ctx, function(err){ 205 | if (err instanceof Error || toString.call(err) === "[object Error]") return done(err); 206 | if (null != err) return done(new Error('done() invoked with non-Error: ' + err)); 207 | done(); 208 | }); 209 | } catch (err) { 210 | done(err); 211 | } 212 | return; 213 | } 214 | 215 | if (this.asyncOnly) { 216 | return done(new Error('--async-only option in use without declaring `done()`')); 217 | } 218 | 219 | var result; 220 | 221 | try { 222 | if (!this.pending) { 223 | result = this.fn.call(ctx); 224 | } 225 | 226 | // This is where we determine if the result is a generator 227 | if (result && typeof(result.next) == 'function' && typeof(result.throw) == 'function') { 228 | // Mocha timeout for async function 229 | if (ms) { 230 | this.timer = setTimeout(function(){ 231 | done(new Error('timeout of ' + ms + 'ms exceeded')); 232 | self.timedOut = true; 233 | }, ms); 234 | } 235 | // Use co to run generator to completion 236 | co(result)(function(err) { 237 | this.duration = new Date - start; 238 | done(err); 239 | }); 240 | } else { 241 | // Default Mocha handling of sync function 242 | this.duration = new Date - start; 243 | fn(); 244 | } 245 | } catch (err) { 246 | fn(err); 247 | } 248 | }; 249 | -------------------------------------------------------------------------------- /lib/suite.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Module dependencies. 4 | */ 5 | 6 | var EventEmitter = require('events').EventEmitter 7 | , debug = require('debug')('mocha:suite') 8 | , milliseconds = require('./ms') 9 | , utils = require('./utils') 10 | , Hook = require('./hook'); 11 | 12 | /** 13 | * Expose `Suite`. 14 | */ 15 | 16 | exports = module.exports = Suite; 17 | 18 | /** 19 | * Create a new `Suite` with the given `title` 20 | * and parent `Suite`. When a suite with the 21 | * same title is already present, that suite 22 | * is returned to provide nicer reporter 23 | * and more flexible meta-testing. 24 | * 25 | * @param {Suite} parent 26 | * @param {String} title 27 | * @return {Suite} 28 | * @api public 29 | */ 30 | 31 | exports.create = function(parent, title){ 32 | var suite = new Suite(title, parent.ctx); 33 | suite.parent = parent; 34 | if (parent.pending) suite.pending = true; 35 | title = suite.fullTitle(); 36 | parent.addSuite(suite); 37 | return suite; 38 | }; 39 | 40 | /** 41 | * Initialize a new `Suite` with the given 42 | * `title` and `ctx`. 43 | * 44 | * @param {String} title 45 | * @param {Context} ctx 46 | * @api private 47 | */ 48 | 49 | function Suite(title, ctx) { 50 | this.title = title; 51 | this.ctx = ctx; 52 | this.suites = []; 53 | this.tests = []; 54 | this.pending = false; 55 | this._beforeEach = []; 56 | this._beforeAll = []; 57 | this._afterEach = []; 58 | this._afterAll = []; 59 | this.root = !title; 60 | this._timeout = 2000; 61 | this._slow = 75; 62 | this._bail = false; 63 | } 64 | 65 | /** 66 | * Inherit from `EventEmitter.prototype`. 67 | */ 68 | 69 | Suite.prototype.__proto__ = EventEmitter.prototype; 70 | 71 | /** 72 | * Return a clone of this `Suite`. 73 | * 74 | * @return {Suite} 75 | * @api private 76 | */ 77 | 78 | Suite.prototype.clone = function(){ 79 | var suite = new Suite(this.title); 80 | debug('clone'); 81 | suite.ctx = this.ctx; 82 | suite.timeout(this.timeout()); 83 | suite.slow(this.slow()); 84 | suite.bail(this.bail()); 85 | return suite; 86 | }; 87 | 88 | /** 89 | * Set timeout `ms` or short-hand such as "2s". 90 | * 91 | * @param {Number|String} ms 92 | * @return {Suite|Number} for chaining 93 | * @api private 94 | */ 95 | 96 | Suite.prototype.timeout = function(ms){ 97 | if (0 == arguments.length) return this._timeout; 98 | if ('string' == typeof ms) ms = milliseconds(ms); 99 | debug('timeout %d', ms); 100 | this._timeout = parseInt(ms, 10); 101 | return this; 102 | }; 103 | 104 | /** 105 | * Set slow `ms` or short-hand such as "2s". 106 | * 107 | * @param {Number|String} ms 108 | * @return {Suite|Number} for chaining 109 | * @api private 110 | */ 111 | 112 | Suite.prototype.slow = function(ms){ 113 | if (0 === arguments.length) return this._slow; 114 | if ('string' == typeof ms) ms = milliseconds(ms); 115 | debug('slow %d', ms); 116 | this._slow = ms; 117 | return this; 118 | }; 119 | 120 | /** 121 | * Sets whether to bail after first error. 122 | * 123 | * @parma {Boolean} bail 124 | * @return {Suite|Number} for chaining 125 | * @api private 126 | */ 127 | 128 | Suite.prototype.bail = function(bail){ 129 | if (0 == arguments.length) return this._bail; 130 | debug('bail %s', bail); 131 | this._bail = bail; 132 | return this; 133 | }; 134 | 135 | /** 136 | * Run `fn(test[, done])` before running tests. 137 | * 138 | * @param {Function} fn 139 | * @return {Suite} for chaining 140 | * @api private 141 | */ 142 | 143 | Suite.prototype.beforeAll = function(title, fn){ 144 | if (this.pending) return this; 145 | if ('function' === typeof title) { 146 | fn = title; 147 | title = fn.name; 148 | } 149 | title = '"before all" hook' + (title ? ': ' + title : ''); 150 | 151 | var hook = new Hook(title, fn); 152 | hook.parent = this; 153 | hook.timeout(this.timeout()); 154 | hook.slow(this.slow()); 155 | hook.ctx = this.ctx; 156 | this._beforeAll.push(hook); 157 | this.emit('beforeAll', hook); 158 | return this; 159 | }; 160 | 161 | /** 162 | * Run `fn(test[, done])` after running tests. 163 | * 164 | * @param {Function} fn 165 | * @return {Suite} for chaining 166 | * @api private 167 | */ 168 | 169 | Suite.prototype.afterAll = function(title, fn){ 170 | if (this.pending) return this; 171 | if ('function' === typeof title) { 172 | fn = title; 173 | title = fn.name; 174 | } 175 | title = '"after all" hook' + (title ? ': ' + title : ''); 176 | 177 | var hook = new Hook(title, fn); 178 | hook.parent = this; 179 | hook.timeout(this.timeout()); 180 | hook.slow(this.slow()); 181 | hook.ctx = this.ctx; 182 | this._afterAll.push(hook); 183 | this.emit('afterAll', hook); 184 | return this; 185 | }; 186 | 187 | /** 188 | * Run `fn(test[, done])` before each test case. 189 | * 190 | * @param {Function} fn 191 | * @return {Suite} for chaining 192 | * @api private 193 | */ 194 | 195 | Suite.prototype.beforeEach = function(title, fn){ 196 | if (this.pending) return this; 197 | if ('function' === typeof title) { 198 | fn = title; 199 | title = fn.name; 200 | } 201 | title = '"before each" hook' + (title ? ': ' + title : ''); 202 | 203 | var hook = new Hook(title, fn); 204 | hook.parent = this; 205 | hook.timeout(this.timeout()); 206 | hook.slow(this.slow()); 207 | hook.ctx = this.ctx; 208 | this._beforeEach.push(hook); 209 | this.emit('beforeEach', hook); 210 | return this; 211 | }; 212 | 213 | /** 214 | * Run `fn(test[, done])` after each test case. 215 | * 216 | * @param {Function} fn 217 | * @return {Suite} for chaining 218 | * @api private 219 | */ 220 | 221 | Suite.prototype.afterEach = function(title, fn){ 222 | if (this.pending) return this; 223 | if ('function' === typeof title) { 224 | fn = title; 225 | title = fn.name; 226 | } 227 | title = '"after each" hook' + (title ? ': ' + title : ''); 228 | 229 | var hook = new Hook(title, fn); 230 | hook.parent = this; 231 | hook.timeout(this.timeout()); 232 | hook.slow(this.slow()); 233 | hook.ctx = this.ctx; 234 | this._afterEach.push(hook); 235 | this.emit('afterEach', hook); 236 | return this; 237 | }; 238 | 239 | /** 240 | * Add a test `suite`. 241 | * 242 | * @param {Suite} suite 243 | * @return {Suite} for chaining 244 | * @api private 245 | */ 246 | 247 | Suite.prototype.addSuite = function(suite){ 248 | suite.parent = this; 249 | suite.timeout(this.timeout()); 250 | suite.slow(this.slow()); 251 | suite.bail(this.bail()); 252 | this.suites.push(suite); 253 | this.emit('suite', suite); 254 | return this; 255 | }; 256 | 257 | /** 258 | * Add a `test` to this suite. 259 | * 260 | * @param {Test} test 261 | * @return {Suite} for chaining 262 | * @api private 263 | */ 264 | 265 | Suite.prototype.addTest = function(test){ 266 | test.parent = this; 267 | test.timeout(this.timeout()); 268 | test.slow(this.slow()); 269 | test.ctx = this.ctx; 270 | this.tests.push(test); 271 | this.emit('test', test); 272 | return this; 273 | }; 274 | 275 | /** 276 | * Return the full title generated by recursively 277 | * concatenating the parent's full title. 278 | * 279 | * @return {String} 280 | * @api public 281 | */ 282 | 283 | Suite.prototype.fullTitle = function(){ 284 | if (this.parent) { 285 | var full = this.parent.fullTitle(); 286 | if (full) return full + ' ' + this.title; 287 | } 288 | return this.title; 289 | }; 290 | 291 | /** 292 | * Return the total number of tests. 293 | * 294 | * @return {Number} 295 | * @api public 296 | */ 297 | 298 | Suite.prototype.total = function(){ 299 | return utils.reduce(this.suites, function(sum, suite){ 300 | return sum + suite.total(); 301 | }, 0) + this.tests.length; 302 | }; 303 | 304 | /** 305 | * Iterates through each suite recursively to find 306 | * all tests. Applies a function in the format 307 | * `fn(test)`. 308 | * 309 | * @param {Function} fn 310 | * @return {Suite} 311 | * @api private 312 | */ 313 | 314 | Suite.prototype.eachTest = function(fn){ 315 | utils.forEach(this.tests, fn); 316 | utils.forEach(this.suites, function(suite){ 317 | suite.eachTest(fn); 318 | }); 319 | return this; 320 | }; 321 | -------------------------------------------------------------------------------- /lib/template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Mocha 5 | 6 | 7 | 8 | 9 | 10 |
    11 | 12 | 13 | 14 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /lib/test.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Module dependencies. 4 | */ 5 | 6 | var Runnable = require('./runnable'); 7 | 8 | /** 9 | * Expose `Test`. 10 | */ 11 | 12 | module.exports = Test; 13 | 14 | /** 15 | * Initialize a new `Test` with the given `title` and callback `fn`. 16 | * 17 | * @param {String} title 18 | * @param {Function} fn 19 | * @api private 20 | */ 21 | 22 | function Test(title, fn) { 23 | Runnable.call(this, title, fn); 24 | this.pending = !fn; 25 | this.type = 'test'; 26 | } 27 | 28 | /** 29 | * Inherit from `Runnable.prototype`. 30 | */ 31 | 32 | Test.prototype.__proto__ = Runnable.prototype; 33 | -------------------------------------------------------------------------------- /lib/utils.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies. 3 | */ 4 | 5 | var fs = require('fs') 6 | , path = require('path') 7 | , join = path.join 8 | , debug = require('debug')('mocha:watch'); 9 | 10 | /** 11 | * Ignored directories. 12 | */ 13 | 14 | var ignore = ['node_modules', '.git']; 15 | 16 | /** 17 | * Escape special characters in the given string of html. 18 | * 19 | * @param {String} html 20 | * @return {String} 21 | * @api private 22 | */ 23 | 24 | exports.escape = function(html){ 25 | return String(html) 26 | .replace(/&/g, '&') 27 | .replace(/"/g, '"') 28 | .replace(//g, '>'); 30 | }; 31 | 32 | /** 33 | * Array#forEach (<=IE8) 34 | * 35 | * @param {Array} array 36 | * @param {Function} fn 37 | * @param {Object} scope 38 | * @api private 39 | */ 40 | 41 | exports.forEach = function(arr, fn, scope){ 42 | for (var i = 0, l = arr.length; i < l; i++) 43 | fn.call(scope, arr[i], i); 44 | }; 45 | 46 | /** 47 | * Array#map (<=IE8) 48 | * 49 | * @param {Array} array 50 | * @param {Function} fn 51 | * @param {Object} scope 52 | * @api private 53 | */ 54 | 55 | exports.map = function(arr, fn, scope){ 56 | var result = []; 57 | for (var i = 0, l = arr.length; i < l; i++) 58 | result.push(fn.call(scope, arr[i], i)); 59 | return result; 60 | }; 61 | 62 | /** 63 | * Array#indexOf (<=IE8) 64 | * 65 | * @parma {Array} arr 66 | * @param {Object} obj to find index of 67 | * @param {Number} start 68 | * @api private 69 | */ 70 | 71 | exports.indexOf = function(arr, obj, start){ 72 | for (var i = start || 0, l = arr.length; i < l; i++) { 73 | if (arr[i] === obj) 74 | return i; 75 | } 76 | return -1; 77 | }; 78 | 79 | /** 80 | * Array#reduce (<=IE8) 81 | * 82 | * @param {Array} array 83 | * @param {Function} fn 84 | * @param {Object} initial value 85 | * @api private 86 | */ 87 | 88 | exports.reduce = function(arr, fn, val){ 89 | var rval = val; 90 | 91 | for (var i = 0, l = arr.length; i < l; i++) { 92 | rval = fn(rval, arr[i], i, arr); 93 | } 94 | 95 | return rval; 96 | }; 97 | 98 | /** 99 | * Array#filter (<=IE8) 100 | * 101 | * @param {Array} array 102 | * @param {Function} fn 103 | * @api private 104 | */ 105 | 106 | exports.filter = function(arr, fn){ 107 | var ret = []; 108 | 109 | for (var i = 0, l = arr.length; i < l; i++) { 110 | var val = arr[i]; 111 | if (fn(val, i, arr)) ret.push(val); 112 | } 113 | 114 | return ret; 115 | }; 116 | 117 | /** 118 | * Object.keys (<=IE8) 119 | * 120 | * @param {Object} obj 121 | * @return {Array} keys 122 | * @api private 123 | */ 124 | 125 | exports.keys = Object.keys || function(obj) { 126 | var keys = [] 127 | , has = Object.prototype.hasOwnProperty // for `window` on <=IE8 128 | 129 | for (var key in obj) { 130 | if (has.call(obj, key)) { 131 | keys.push(key); 132 | } 133 | } 134 | 135 | return keys; 136 | }; 137 | 138 | /** 139 | * Watch the given `files` for changes 140 | * and invoke `fn(file)` on modification. 141 | * 142 | * @param {Array} files 143 | * @param {Function} fn 144 | * @api private 145 | */ 146 | 147 | exports.watch = function(files, fn){ 148 | var options = { interval: 100 }; 149 | files.forEach(function(file){ 150 | debug('file %s', file); 151 | fs.watchFile(file, options, function(curr, prev){ 152 | if (prev.mtime < curr.mtime) fn(file); 153 | }); 154 | }); 155 | }; 156 | 157 | /** 158 | * Ignored files. 159 | */ 160 | 161 | function ignored(path){ 162 | return !~ignore.indexOf(path); 163 | } 164 | 165 | /** 166 | * Lookup files in the given `dir`. 167 | * 168 | * @return {Array} 169 | * @api private 170 | */ 171 | 172 | exports.files = function(dir, ret){ 173 | ret = ret || []; 174 | 175 | fs.readdirSync(dir) 176 | .filter(ignored) 177 | .forEach(function(path){ 178 | path = join(dir, path); 179 | if (fs.statSync(path).isDirectory()) { 180 | exports.files(path, ret); 181 | } else if (path.match(/\.(js|coffee|litcoffee|coffee.md)$/)) { 182 | ret.push(path); 183 | } 184 | }); 185 | 186 | return ret; 187 | }; 188 | 189 | /** 190 | * Compute a slug from the given `str`. 191 | * 192 | * @param {String} str 193 | * @return {String} 194 | * @api private 195 | */ 196 | 197 | exports.slug = function(str){ 198 | return str 199 | .toLowerCase() 200 | .replace(/ +/g, '-') 201 | .replace(/[^-\w]/g, ''); 202 | }; 203 | 204 | /** 205 | * Strip the function definition from `str`, 206 | * and re-indent for pre whitespace. 207 | */ 208 | 209 | exports.clean = function(str) { 210 | str = str 211 | .replace(/\r\n?|[\n\u2028\u2029]/g, "\n").replace(/^\uFEFF/, '') 212 | .replace(/^function *\(.*\) *{/, '') 213 | .replace(/\s+\}$/, ''); 214 | 215 | var spaces = str.match(/^\n?( *)/)[1].length 216 | , tabs = str.match(/^\n?(\t*)/)[1].length 217 | , re = new RegExp('^\n?' + (tabs ? '\t' : ' ') + '{' + (tabs ? tabs : spaces) + '}', 'gm'); 218 | 219 | str = str.replace(re, ''); 220 | 221 | return exports.trim(str); 222 | }; 223 | 224 | /** 225 | * Escape regular expression characters in `str`. 226 | * 227 | * @param {String} str 228 | * @return {String} 229 | * @api private 230 | */ 231 | 232 | exports.escapeRegexp = function(str){ 233 | return str.replace(/[-\\^$*+?.()|[\]{}]/g, "\\$&"); 234 | }; 235 | 236 | /** 237 | * Trim the given `str`. 238 | * 239 | * @param {String} str 240 | * @return {String} 241 | * @api private 242 | */ 243 | 244 | exports.trim = function(str){ 245 | return str.replace(/^\s+|\s+$/g, ''); 246 | }; 247 | 248 | /** 249 | * Parse the given `qs`. 250 | * 251 | * @param {String} qs 252 | * @return {Object} 253 | * @api private 254 | */ 255 | 256 | exports.parseQuery = function(qs){ 257 | return exports.reduce(qs.replace('?', '').split('&'), function(obj, pair){ 258 | var i = pair.indexOf('=') 259 | , key = pair.slice(0, i) 260 | , val = pair.slice(++i); 261 | 262 | obj[key] = decodeURIComponent(val); 263 | return obj; 264 | }, {}); 265 | }; 266 | 267 | /** 268 | * Highlight the given string of `js`. 269 | * 270 | * @param {String} js 271 | * @return {String} 272 | * @api private 273 | */ 274 | 275 | function highlight(js) { 276 | return js 277 | .replace(//g, '>') 279 | .replace(/\/\/(.*)/gm, '//$1') 280 | .replace(/('.*?')/gm, '$1') 281 | .replace(/(\d+\.\d+)/gm, '$1') 282 | .replace(/(\d+)/gm, '$1') 283 | .replace(/\bnew *(\w+)/gm, 'new $1') 284 | .replace(/\b(function|new|throw|return|var|if|else)\b/gm, '$1') 285 | } 286 | 287 | /** 288 | * Highlight the contents of tag `name`. 289 | * 290 | * @param {String} name 291 | * @api private 292 | */ 293 | 294 | exports.highlightTags = function(name) { 295 | var code = document.getElementsByTagName(name); 296 | for (var i = 0, len = code.length; i < len; ++i) { 297 | code[i].innerHTML = highlight(code[i].innerHTML); 298 | } 299 | }; 300 | -------------------------------------------------------------------------------- /media/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | mocha 8 | 9 | -------------------------------------------------------------------------------- /mocha.css: -------------------------------------------------------------------------------- 1 | @charset "utf-8"; 2 | 3 | body { 4 | margin:0; 5 | } 6 | 7 | #mocha { 8 | font: 20px/1.5 "Helvetica Neue", Helvetica, Arial, sans-serif; 9 | margin: 60px 50px; 10 | } 11 | 12 | #mocha ul, 13 | #mocha li { 14 | margin: 0; 15 | padding: 0; 16 | } 17 | 18 | #mocha ul { 19 | list-style: none; 20 | } 21 | 22 | #mocha h1, 23 | #mocha h2 { 24 | margin: 0; 25 | } 26 | 27 | #mocha h1 { 28 | margin-top: 15px; 29 | font-size: 1em; 30 | font-weight: 200; 31 | } 32 | 33 | #mocha h1 a { 34 | text-decoration: none; 35 | color: inherit; 36 | } 37 | 38 | #mocha h1 a:hover { 39 | text-decoration: underline; 40 | } 41 | 42 | #mocha .suite .suite h1 { 43 | margin-top: 0; 44 | font-size: .8em; 45 | } 46 | 47 | #mocha .hidden { 48 | display: none; 49 | } 50 | 51 | #mocha h2 { 52 | font-size: 12px; 53 | font-weight: normal; 54 | cursor: pointer; 55 | } 56 | 57 | #mocha .suite { 58 | margin-left: 15px; 59 | } 60 | 61 | #mocha .test { 62 | margin-left: 15px; 63 | overflow: hidden; 64 | } 65 | 66 | #mocha .test.pending:hover h2::after { 67 | content: '(pending)'; 68 | font-family: arial, sans-serif; 69 | } 70 | 71 | #mocha .test.pass.medium .duration { 72 | background: #c09853; 73 | } 74 | 75 | #mocha .test.pass.slow .duration { 76 | background: #b94a48; 77 | } 78 | 79 | #mocha .test.pass::before { 80 | content: '✓'; 81 | font-size: 12px; 82 | display: block; 83 | float: left; 84 | margin-right: 5px; 85 | color: #00d6b2; 86 | } 87 | 88 | #mocha .test.pass .duration { 89 | font-size: 9px; 90 | margin-left: 5px; 91 | padding: 2px 5px; 92 | color: #fff; 93 | -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.2); 94 | -moz-box-shadow: inset 0 1px 1px rgba(0,0,0,.2); 95 | box-shadow: inset 0 1px 1px rgba(0,0,0,.2); 96 | -webkit-border-radius: 5px; 97 | -moz-border-radius: 5px; 98 | -ms-border-radius: 5px; 99 | -o-border-radius: 5px; 100 | border-radius: 5px; 101 | } 102 | 103 | #mocha .test.pass.fast .duration { 104 | display: none; 105 | } 106 | 107 | #mocha .test.pending { 108 | color: #0b97c4; 109 | } 110 | 111 | #mocha .test.pending::before { 112 | content: '◦'; 113 | color: #0b97c4; 114 | } 115 | 116 | #mocha .test.fail { 117 | color: #c00; 118 | } 119 | 120 | #mocha .test.fail pre { 121 | color: black; 122 | } 123 | 124 | #mocha .test.fail::before { 125 | content: '✖'; 126 | font-size: 12px; 127 | display: block; 128 | float: left; 129 | margin-right: 5px; 130 | color: #c00; 131 | } 132 | 133 | #mocha .test pre.error { 134 | color: #c00; 135 | max-height: 300px; 136 | overflow: auto; 137 | } 138 | 139 | /** 140 | * (1): approximate for browsers not supporting calc 141 | * (2): 42 = 2*15 + 2*10 + 2*1 (padding + margin + border) 142 | * ^^ seriously 143 | */ 144 | #mocha .test pre { 145 | display: block; 146 | float: left; 147 | clear: left; 148 | font: 12px/1.5 monaco, monospace; 149 | margin: 5px; 150 | padding: 15px; 151 | border: 1px solid #eee; 152 | max-width: 85%; /*(1)*/ 153 | max-width: calc(100% - 42px); /*(2)*/ 154 | word-wrap: break-word; 155 | border-bottom-color: #ddd; 156 | -webkit-border-radius: 3px; 157 | -webkit-box-shadow: 0 1px 3px #eee; 158 | -moz-border-radius: 3px; 159 | -moz-box-shadow: 0 1px 3px #eee; 160 | border-radius: 3px; 161 | } 162 | 163 | #mocha .test h2 { 164 | position: relative; 165 | } 166 | 167 | #mocha .test a.replay { 168 | position: absolute; 169 | top: 3px; 170 | right: 0; 171 | text-decoration: none; 172 | vertical-align: middle; 173 | display: block; 174 | width: 15px; 175 | height: 15px; 176 | line-height: 15px; 177 | text-align: center; 178 | background: #eee; 179 | font-size: 15px; 180 | -moz-border-radius: 15px; 181 | border-radius: 15px; 182 | -webkit-transition: opacity 200ms; 183 | -moz-transition: opacity 200ms; 184 | transition: opacity 200ms; 185 | opacity: 0.3; 186 | color: #888; 187 | } 188 | 189 | #mocha .test:hover a.replay { 190 | opacity: 1; 191 | } 192 | 193 | #mocha-report.pass .test.fail { 194 | display: none; 195 | } 196 | 197 | #mocha-report.fail .test.pass { 198 | display: none; 199 | } 200 | 201 | #mocha-report.pending .test.pass, 202 | #mocha-report.pending .test.fail { 203 | display: none; 204 | } 205 | #mocha-report.pending .test.pass.pending { 206 | display: block; 207 | } 208 | 209 | #mocha-error { 210 | color: #c00; 211 | font-size: 1.5em; 212 | font-weight: 100; 213 | letter-spacing: 1px; 214 | } 215 | 216 | #mocha-stats { 217 | position: fixed; 218 | top: 15px; 219 | right: 10px; 220 | font-size: 12px; 221 | margin: 0; 222 | color: #888; 223 | z-index: 1; 224 | } 225 | 226 | #mocha-stats .progress { 227 | float: right; 228 | padding-top: 0; 229 | } 230 | 231 | #mocha-stats em { 232 | color: black; 233 | } 234 | 235 | #mocha-stats a { 236 | text-decoration: none; 237 | color: inherit; 238 | } 239 | 240 | #mocha-stats a:hover { 241 | border-bottom: 1px solid #eee; 242 | } 243 | 244 | #mocha-stats li { 245 | display: inline-block; 246 | margin: 0 5px; 247 | list-style: none; 248 | padding-top: 11px; 249 | } 250 | 251 | #mocha-stats canvas { 252 | width: 40px; 253 | height: 40px; 254 | } 255 | 256 | #mocha code .comment { color: #ddd; } 257 | #mocha code .init { color: #2f6fad; } 258 | #mocha code .string { color: #5890ad; } 259 | #mocha code .keyword { color: #8a6343; } 260 | #mocha code .number { color: #2f6fad; } 261 | 262 | @media screen and (max-device-width: 480px) { 263 | #mocha { 264 | margin: 60px 0px; 265 | } 266 | 267 | #mocha #stats { 268 | position: absolute; 269 | } 270 | } 271 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mocha-co", 3 | "version": "1.17.2", 4 | "description": "simple, flexible, fun test framework", 5 | "keywords": [ 6 | "mocha", 7 | "test", 8 | "bdd", 9 | "tdd", 10 | "tap" 11 | ], 12 | "author": "Ilkka Oksanen ", 13 | "repository": { 14 | "type": "git", 15 | "url": "git://github.com/ilkkao/co-mocha.git" 16 | }, 17 | "main": "./index", 18 | "bin": { 19 | "mocha": "./bin/mocha", 20 | "_mocha": "./bin/_mocha" 21 | }, 22 | "engines": { 23 | "node": ">= 0.4.x" 24 | }, 25 | "scripts": { 26 | "test": "make test-all" 27 | }, 28 | "dependencies": { 29 | "commander": "2.0.0", 30 | "growl": "1.7.x", 31 | "jade": "0.26.3", 32 | "diff": "1.0.7", 33 | "debug": "*", 34 | "mkdirp": "0.3.5", 35 | "glob": "3.2.3", 36 | "co": "~3.0.2" 37 | }, 38 | "devDependencies": { 39 | "should": ">= 2.0.x", 40 | "coffee-script": "1.2" 41 | }, 42 | "files": [ 43 | "bin", 44 | "images", 45 | "lib", 46 | "index.js", 47 | "mocha.css", 48 | "mocha.js" 49 | ] 50 | } 51 | -------------------------------------------------------------------------------- /support/compile.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Module dependencies. 4 | */ 5 | 6 | var fs = require('fs'); 7 | 8 | /** 9 | * Arguments. 10 | */ 11 | 12 | var args = process.argv.slice(2) 13 | , pending = args.length 14 | , files = {}; 15 | 16 | console.log(''); 17 | 18 | // parse arguments 19 | 20 | args.forEach(function(file){ 21 | var mod = file.replace('lib/', ''); 22 | fs.readFile(file, 'utf8', function(err, js){ 23 | if (err) throw err; 24 | console.log(' \u001b[90mcompile : \u001b[0m\u001b[36m%s\u001b[0m', file); 25 | files[file] = ~js.indexOf('require: off') 26 | ? js 27 | : parse(js); 28 | --pending || compile(); 29 | }); 30 | }); 31 | 32 | /** 33 | * Parse the given `js`. 34 | */ 35 | 36 | function parse(js) { 37 | return parseRequires(parseInheritance(js)); 38 | } 39 | 40 | /** 41 | * Parse requires. 42 | */ 43 | 44 | function parseRequires(js) { 45 | return js 46 | .replace(/require\('events'\)/g, "require('browser/events')") 47 | .replace(/require\('debug'\)/g, "require('browser/debug')") 48 | .replace(/require\('path'\)/g, "require('browser/path')") 49 | .replace(/require\('diff'\)/g, "require('browser/diff')") 50 | .replace(/require\('tty'\)/g, "require('browser/tty')") 51 | .replace(/require\('fs'\)/g, "require('browser/fs')") 52 | } 53 | 54 | /** 55 | * Parse __proto__. 56 | */ 57 | 58 | function parseInheritance(js) { 59 | return js 60 | .replace(/^ *(\w+)\.prototype\.__proto__ * = *(\w+)\.prototype *;?/gm, function(_, child, parent){ 61 | return 'function F(){};\n' 62 | + 'F.prototype = ' + parent + '.prototype;\n' 63 | + child + '.prototype = new F;\n' 64 | + child + '.prototype.constructor = '+ child + ';\n'; 65 | }); 66 | } 67 | 68 | /** 69 | * Compile the files. 70 | */ 71 | 72 | function compile() { 73 | var buf = ''; 74 | buf += '\n// CommonJS require()\n\n'; 75 | buf += browser.require + '\n\n'; 76 | buf += 'require.modules = {};\n\n'; 77 | buf += 'require.resolve = ' + browser.resolve + ';\n\n'; 78 | buf += 'require.register = ' + browser.register + ';\n\n'; 79 | buf += 'require.relative = ' + browser.relative + ';\n\n'; 80 | args.forEach(function(file){ 81 | var js = files[file]; 82 | file = file.replace('lib/', ''); 83 | buf += '\nrequire.register("' + file + '", function(module, exports, require){\n'; 84 | buf += js; 85 | buf += '\n}); // module: ' + file + '\n'; 86 | }); 87 | fs.writeFile('_mocha.js', buf, function(err){ 88 | if (err) throw err; 89 | console.log(' \u001b[90m create : \u001b[0m\u001b[36m%s\u001b[0m', 'mocha.js'); 90 | console.log(); 91 | }); 92 | } 93 | 94 | // refactored version of weepy's 95 | // https://github.com/weepy/brequire/blob/master/browser/brequire.js 96 | 97 | var browser = { 98 | 99 | /** 100 | * Require a module. 101 | */ 102 | 103 | require: function require(p){ 104 | var path = require.resolve(p) 105 | , mod = require.modules[path]; 106 | if (!mod) throw new Error('failed to require "' + p + '"'); 107 | if (!mod.exports) { 108 | mod.exports = {}; 109 | mod.call(mod.exports, mod, mod.exports, require.relative(path)); 110 | } 111 | return mod.exports; 112 | }, 113 | 114 | /** 115 | * Resolve module path. 116 | */ 117 | 118 | resolve: function(path){ 119 | var orig = path 120 | , reg = path + '.js' 121 | , index = path + '/index.js'; 122 | return require.modules[reg] && reg 123 | || require.modules[index] && index 124 | || orig; 125 | }, 126 | 127 | /** 128 | * Return relative require(). 129 | */ 130 | 131 | relative: function(parent) { 132 | return function(p){ 133 | if ('.' != p.charAt(0)) return require(p); 134 | 135 | var path = parent.split('/') 136 | , segs = p.split('/'); 137 | path.pop(); 138 | 139 | for (var i = 0; i < segs.length; i++) { 140 | var seg = segs[i]; 141 | if ('..' == seg) path.pop(); 142 | else if ('.' != seg) path.push(seg); 143 | } 144 | 145 | return require(path.join('/')); 146 | }; 147 | }, 148 | 149 | /** 150 | * Register a module. 151 | */ 152 | 153 | register: function(path, fn){ 154 | require.modules[path] = fn; 155 | } 156 | }; 157 | -------------------------------------------------------------------------------- /support/foot.js: -------------------------------------------------------------------------------- 1 | })(); -------------------------------------------------------------------------------- /support/head.js: -------------------------------------------------------------------------------- 1 | ;(function(){ 2 | -------------------------------------------------------------------------------- /support/tail.js: -------------------------------------------------------------------------------- 1 | // The global object is "self" in Web Workers. 2 | global = (function() { return this; })(); 3 | 4 | /** 5 | * Save timer references to avoid Sinon interfering (see GH-237). 6 | */ 7 | 8 | var Date = global.Date; 9 | var setTimeout = global.setTimeout; 10 | var setInterval = global.setInterval; 11 | var clearTimeout = global.clearTimeout; 12 | var clearInterval = global.clearInterval; 13 | 14 | /** 15 | * Node shims. 16 | * 17 | * These are meant only to allow 18 | * mocha.js to run untouched, not 19 | * to allow running node code in 20 | * the browser. 21 | */ 22 | 23 | var process = {}; 24 | process.exit = function(status){}; 25 | process.stdout = {}; 26 | 27 | var uncaughtExceptionHandlers = []; 28 | 29 | /** 30 | * Remove uncaughtException listener. 31 | */ 32 | 33 | process.removeListener = function(e, fn){ 34 | if ('uncaughtException' == e) { 35 | global.onerror = function() {}; 36 | var i = Mocha.utils.indexOf(uncaughtExceptionHandlers, fn); 37 | if (i != -1) { uncaughtExceptionHandlers.splice(i, 1); } 38 | } 39 | }; 40 | 41 | /** 42 | * Implements uncaughtException listener. 43 | */ 44 | 45 | process.on = function(e, fn){ 46 | if ('uncaughtException' == e) { 47 | global.onerror = function(err, url, line){ 48 | fn(new Error(err + ' (' + url + ':' + line + ')')); 49 | return true; 50 | }; 51 | uncaughtExceptionHandlers.push(fn); 52 | } 53 | }; 54 | 55 | /** 56 | * Expose mocha. 57 | */ 58 | 59 | var Mocha = global.Mocha = require('mocha'), 60 | mocha = global.mocha = new Mocha({ reporter: 'html' }); 61 | 62 | // The BDD UI is registered by default, but no UI will be functional in the 63 | // browser without an explicit call to the overridden `mocha.ui` (see below). 64 | // Ensure that this default UI does not expose its methods to the global scope. 65 | mocha.suite.removeAllListeners('pre-require'); 66 | 67 | var immediateQueue = [] 68 | , immediateTimeout; 69 | 70 | function timeslice() { 71 | var immediateStart = new Date().getTime(); 72 | while (immediateQueue.length && (new Date().getTime() - immediateStart) < 100) { 73 | immediateQueue.shift()(); 74 | } 75 | if (immediateQueue.length) { 76 | immediateTimeout = setTimeout(timeslice, 0); 77 | } else { 78 | immediateTimeout = null; 79 | } 80 | } 81 | 82 | /** 83 | * High-performance override of Runner.immediately. 84 | */ 85 | 86 | Mocha.Runner.immediately = function(callback) { 87 | immediateQueue.push(callback); 88 | if (!immediateTimeout) { 89 | immediateTimeout = setTimeout(timeslice, 0); 90 | } 91 | }; 92 | 93 | /** 94 | * Function to allow assertion libraries to throw errors directly into mocha. 95 | * This is useful when running tests in a browser because window.onerror will 96 | * only receive the 'message' attribute of the Error. 97 | */ 98 | mocha.throwError = function(err) { 99 | Mocha.utils.forEach(uncaughtExceptionHandlers, function (fn) { 100 | fn(err); 101 | }); 102 | throw err; 103 | }; 104 | 105 | /** 106 | * Override ui to ensure that the ui functions are initialized. 107 | * Normally this would happen in Mocha.prototype.loadFiles. 108 | */ 109 | 110 | mocha.ui = function(ui){ 111 | Mocha.prototype.ui.call(this, ui); 112 | this.suite.emit('pre-require', global, null, this); 113 | return this; 114 | }; 115 | 116 | /** 117 | * Setup mocha with the given setting options. 118 | */ 119 | 120 | mocha.setup = function(opts){ 121 | if ('string' == typeof opts) opts = { ui: opts }; 122 | for (var opt in opts) this[opt](opts[opt]); 123 | return this; 124 | }; 125 | 126 | /** 127 | * Run mocha, returning the Runner. 128 | */ 129 | 130 | mocha.run = function(fn){ 131 | var options = mocha.options; 132 | mocha.globals('location'); 133 | 134 | var query = Mocha.utils.parseQuery(global.location.search || ''); 135 | if (query.grep) mocha.grep(query.grep); 136 | if (query.invert) mocha.invert(); 137 | 138 | return Mocha.prototype.run.call(mocha, function(){ 139 | // The DOM Document is not available in Web Workers. 140 | if (global.document) { 141 | Mocha.utils.highlightTags('code'); 142 | } 143 | if (fn) fn(); 144 | }); 145 | }; 146 | 147 | /** 148 | * Expose the process shim. 149 | */ 150 | 151 | Mocha.process = process; 152 | -------------------------------------------------------------------------------- /test/acceptance/context.js: -------------------------------------------------------------------------------- 1 | 2 | describe('Context', function(){ 3 | beforeEach(function(){ 4 | this.calls = ['before']; 5 | }) 6 | 7 | describe('nested', function(){ 8 | beforeEach(function(){ 9 | this.calls.push('before two'); 10 | }) 11 | 12 | it('should work', function(){ 13 | this.calls.should.eql(['before', 'before two']); 14 | this.calls.push('test'); 15 | }) 16 | 17 | after(function(){ 18 | this.calls.should.eql(['before', 'before two', 'test']); 19 | this.calls.push('after two'); 20 | }) 21 | }) 22 | 23 | after(function(){ 24 | this.calls.should.eql(['before', 'before two', 'test', 'after two']); 25 | }) 26 | }) -------------------------------------------------------------------------------- /test/acceptance/diffs.js: -------------------------------------------------------------------------------- 1 | 2 | var fs = require('fs') 3 | , cssin = fs.readFileSync('test/acceptance/fixtures/css.in', 'ascii') 4 | , cssout = fs.readFileSync('test/acceptance/fixtures/css.out', 'ascii'); 5 | 6 | describe('diffs', function(){ 7 | // uncomment the assertions, and run with different params to check the output 8 | // ex: --color, --no-color, --unified-diff 9 | 10 | it('should display a diff for small strings', function(){ 11 | var expected = 'foo bar baz' 12 | , actual = 'foo rar baz'; 13 | 14 | // expected.should.eql(actual); 15 | }); 16 | 17 | it('should display a diff of canonicalized objects', function(){ 18 | var actual = { name: 'travis j', age: 23 } 19 | , expected = { age: 23, name: 'travis' }; 20 | 21 | // actual.should.eql(expected); 22 | }); 23 | 24 | it('should display a diff for medium strings', function(){ 25 | var expected = 'foo bar baz\nfoo bar baz\nfoo bar baz' 26 | , actual = 'foo bar baz\nfoo rar baz\nfoo bar raz'; 27 | 28 | // expected.should.eql(actual); 29 | }); 30 | 31 | it('should display a diff for entire object dumps', function(){ 32 | var expected = { name: 'joe', age: 30, address: {city: 'new york', country: 'us' }} 33 | , actual = { name: 'joel', age: 30, address: {city: 'new york', country: 'usa' }}; 34 | 35 | // actual.should.eql(expected); 36 | }); 37 | 38 | it('should display a diff for multi-line strings', function(){ 39 | var expected = 'one two three\nfour five six\nseven eight nine'; 40 | var actual = 'one two three\nfour zzzz six\nseven eight nine'; 41 | 42 | // actual.should.eql(expected); 43 | }); 44 | 45 | it('should display a diff for entire object dumps', function(){ 46 | var expected = { name: 'joe', age: 30, address: {city: 'new york', country: 'us' }} 47 | var actual = { name: 'joel', age: 30, address: {city: 'new york', country: 'usa' }}; 48 | 49 | // actual.should.eql(expected); 50 | }); 51 | 52 | it('should display a full-comparison with escaped special characters', function(){ 53 | var expected = 'one\ttab\ntwo\t\ttabs'; 54 | var actual = 'one\ttab\ntwo\t\t\ttabs'; 55 | 56 | //actual.should.equal(expected); 57 | }); 58 | 59 | it('should display a word diff for large strings', function(){ 60 | // cssin.should.equal(cssout); 61 | }); 62 | 63 | it('should work with objects', function(){ 64 | var tobi = { 65 | name: 'tobi', 66 | species: 'ferret', 67 | color: 'white', 68 | age: 2 69 | }; 70 | 71 | var loki = { 72 | name: 'loki', 73 | species: 'ferret', 74 | color: 'brown', 75 | age: 2 76 | }; 77 | 78 | // tobi.should.eql(loki); 79 | }); 80 | }); 81 | -------------------------------------------------------------------------------- /test/acceptance/duration.js: -------------------------------------------------------------------------------- 1 | 2 | describe('durations', function(){ 3 | describe('when slow', function(){ 4 | it('should highlight in red', function(done){ 5 | setTimeout(function(){ 6 | done(); 7 | }, 100); 8 | }) 9 | }) 10 | 11 | describe('when reasonable', function(){ 12 | it('should highlight in yellow', function(done){ 13 | setTimeout(function(){ 14 | done(); 15 | }, 50); 16 | }) 17 | }) 18 | 19 | describe('when fast', function(){ 20 | it('should highlight in green', function(done){ 21 | setTimeout(function(){ 22 | done(); 23 | }, 10); 24 | }) 25 | }) 26 | }) 27 | -------------------------------------------------------------------------------- /test/acceptance/fixtures/css.in: -------------------------------------------------------------------------------- 1 | body { 2 | font: "Helvetica Neue", Helvetica, arial, sans-serif; 3 | background: black; 4 | color: white; 5 | } 6 | 7 | a { 8 | color: blue 9 | } -------------------------------------------------------------------------------- /test/acceptance/fixtures/css.out: -------------------------------------------------------------------------------- 1 | body { 2 | font: "Helvetica Neue", Helvetica, arial, sans-serif; 3 | background: black; 4 | color: #fff; 5 | } 6 | 7 | a { 8 | color: blue; 9 | } 10 | 11 | foo { 12 | bar: 'baz'; 13 | } -------------------------------------------------------------------------------- /test/acceptance/fs.js: -------------------------------------------------------------------------------- 1 | 2 | var fs = require('fs'); 3 | 4 | describe('fs.readFile()', function(){ 5 | describe('when the file exists', function(){ 6 | it('should succeed', function(done){ 7 | fs.writeFile('/tmp/mocha', 'wahoo', done) 8 | }) 9 | }) 10 | 11 | describe('when the file does not exist', function(){ 12 | it('should fail', function(done){ 13 | // uncomment 14 | // fs.readFile('/tmp/does-not-exist', done); 15 | done(); 16 | }) 17 | }) 18 | }) 19 | -------------------------------------------------------------------------------- /test/acceptance/glob/glob.js: -------------------------------------------------------------------------------- 1 | 2 | describe('globbing test', function(){ 3 | it('should find this test', function(){ 4 | // see glob.sh for details 5 | }) 6 | }); 7 | -------------------------------------------------------------------------------- /test/acceptance/glob/glob.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | REL_SCRIPT_DIR="`dirname \"$0\"`" 3 | SCRIPT_DIR="`( cd \"$REL_SCRIPT_DIR\" && pwd )`" 4 | 5 | cd $SCRIPT_DIR || { 6 | echo Could not cd to $SCRIPT_DIR from `pwd` 7 | exit 1 8 | } 9 | 10 | ../../../bin/mocha -R json-stream ./*.js > /tmp/mocha-glob.txt || { 11 | echo Globbing ./*.js in `pwd` failed. 12 | exit 1 13 | } 14 | 15 | cat /tmp/mocha-glob.txt | grep -q -F '["end",{"suites":1,"tests":1,"passes":1,"pending":0,"failures":0,' || { 16 | echo Globbing ./*.js in `pwd` should match glob.js with one test inside. 17 | exit 1 18 | } 19 | 20 | ../../../bin/mocha -R json-stream ./*-none.js 2> /tmp/mocha-glob.txt && { 21 | echo Globbing './*-none.js' in `pwd` failed. 22 | exit 1 23 | } 24 | 25 | cat /tmp/mocha-glob.txt | grep -q -F 'cannot resolve path' || { 26 | echo Globbing './*-none.js' in `pwd` should match no files and run no tests. 27 | exit 1 28 | } 29 | 30 | # Globbing in windows command-shell differs completely from unix-style globbing. 31 | # In bash, the shell expands globs and passes the result to executables. 32 | # In windows, the shell passes globs unexpanded, executables do expansion if they support it. 33 | # Adding single-quotes around the glob below makes bash pass glob unexpanded, 34 | # allowing us to test windows-style globbing in bash. 35 | ../../../bin/mocha -R json-stream './*.js' > /tmp/mocha-glob.txt || { 36 | echo Globbing './*.js' in `pwd` failed. 37 | exit 1 38 | } 39 | 40 | cat /tmp/mocha-glob.txt | grep -q -F '["end",{"suites":1,"tests":1,"passes":1,"pending":0,"failures":0,' || { 41 | echo Globbing './*.js' in `pwd` should match glob.js with one test inside. 42 | exit 1 43 | } 44 | 45 | ../../../bin/mocha -R json-stream './*-none.js' 2> /tmp/mocha-glob.txt && { 46 | echo Globbing './*-none.js' in `pwd` failed. 47 | exit 1 48 | } 49 | 50 | cat /tmp/mocha-glob.txt | grep -q -F 'cannot resolve path' || { 51 | echo Globbing './*-none.js' in `pwd` should match no files and run no tests. 52 | exit 1 53 | } 54 | 55 | echo Glob-test passed. 56 | -------------------------------------------------------------------------------- /test/acceptance/globals.js: -------------------------------------------------------------------------------- 1 | 2 | describe('global leaks', function(){ 3 | before(function(){ 4 | // uncomment to test 5 | // foo = 'hey'; 6 | // bar = 'hey'; 7 | }) 8 | 9 | beforeEach(function(){ 10 | // uncomment to test 11 | // foo = 'bar' 12 | }); 13 | 14 | it('should cause tests to fail', function(){ 15 | // uncomment to test 16 | // foo = 'bar'; 17 | // bar = 'baz'; 18 | // baz = 'raz'; 19 | }); 20 | 21 | it('should pass when accepted', function(){ 22 | global.okGlobalA = 1; 23 | global.okGlobalB = 1; 24 | global.okGlobalC = 1; 25 | }) 26 | 27 | it('should pass with wildcard', function(){ 28 | global.callback123 = 'foo'; 29 | global.callback345 = 'bar'; 30 | }); 31 | 32 | it('should pass when prefixed "mocha-"', function(){ 33 | // Opera and IE do this for HTML element IDs anyway 34 | // but to sure we can assert this in any browser, simulate it. 35 | global['mocha-example'] = { nodeType: 1 }; 36 | }); 37 | 38 | afterEach(function(){ 39 | // uncomment to test 40 | // foo = 'bar' 41 | }); 42 | }); 43 | -------------------------------------------------------------------------------- /test/acceptance/http.js: -------------------------------------------------------------------------------- 1 | 2 | var http = require('http'); 3 | 4 | var server = http.createServer(function(req, res){ 5 | res.end('Hello World\n'); 6 | }) 7 | 8 | server.listen(8888); 9 | 10 | describe('http', function(){ 11 | it('should provide an example', function(done){ 12 | http.get({ path: '/', port: 8888 }, function(res){ 13 | res.should.have.status(200); 14 | done(); 15 | }) 16 | }) 17 | }) -------------------------------------------------------------------------------- /test/acceptance/interfaces/bdd.js: -------------------------------------------------------------------------------- 1 | 2 | describe('Array', function(){ 3 | describe('#indexOf()', function(){ 4 | it('should return -1 when the value is not present', function(){ 5 | [1,2,3].indexOf(5).should.equal(-1); 6 | [1,2,3].indexOf(0).should.equal(-1); 7 | }) 8 | 9 | it('should return the correct index when the value is present', function(){ 10 | [1,2,3].indexOf(1).should.equal(0); 11 | [1,2,3].indexOf(2).should.equal(1); 12 | [1,2,3].indexOf(3).should.equal(2); 13 | }) 14 | }) 15 | }) 16 | 17 | describe('Array', function(){ 18 | describe('#pop()', function(){ 19 | it('should remove and return the last value', function(){ 20 | var arr = [1,2,3]; 21 | arr.pop().should.equal(3); 22 | arr.should.eql([1,2]); 23 | }) 24 | }) 25 | }) 26 | 27 | context('Array', function(){ 28 | beforeEach(function(){ 29 | this.arr = [1,2,3]; 30 | }) 31 | 32 | specify('has a length property', function(){ 33 | this.arr.length.should.equal(3); 34 | }) 35 | }) 36 | -------------------------------------------------------------------------------- /test/acceptance/interfaces/exports.js: -------------------------------------------------------------------------------- 1 | 2 | var calls = []; 3 | 4 | exports.Array = { 5 | before: function(){ 6 | calls.push('before'); 7 | }, 8 | 9 | after: function(){ 10 | calls.push('after'); 11 | calls.should.eql([ 12 | 'before' 13 | , 'before each' 14 | , 'one' 15 | , 'after each' 16 | , 'before each' 17 | , 'two' 18 | , 'after each' 19 | , 'after']); 20 | }, 21 | 22 | '#indexOf()': { 23 | beforeEach: function(){ 24 | calls.push('before each'); 25 | }, 26 | 27 | afterEach: function(){ 28 | calls.push('after each'); 29 | }, 30 | 31 | 'should return -1 when the value is not present': function(){ 32 | calls.push('one'); 33 | [1,2,3].indexOf(5).should.equal(-1); 34 | [1,2,3].indexOf(0).should.equal(-1); 35 | }, 36 | 37 | 'should return the correct index when the value is present': function(){ 38 | calls.push('two'); 39 | [1,2,3].indexOf(1).should.equal(0); 40 | [1,2,3].indexOf(2).should.equal(1); 41 | [1,2,3].indexOf(3).should.equal(2); 42 | } 43 | } 44 | }; 45 | -------------------------------------------------------------------------------- /test/acceptance/interfaces/qunit.js: -------------------------------------------------------------------------------- 1 | 2 | function ok(expr, msg) { 3 | if (!expr) throw new Error(msg); 4 | } 5 | 6 | suite('Array'); 7 | 8 | test('#length', function(){ 9 | var arr = [1,2,3]; 10 | ok(arr.length == 3); 11 | }); 12 | 13 | test('#indexOf()', function(){ 14 | var arr = [1,2,3]; 15 | ok(arr.indexOf(1) == 0); 16 | ok(arr.indexOf(2) == 1); 17 | ok(arr.indexOf(3) == 2); 18 | }); 19 | 20 | suite('String'); 21 | 22 | test('#length', function(){ 23 | ok('foo'.length == 3); 24 | }); -------------------------------------------------------------------------------- /test/acceptance/interfaces/tdd.js: -------------------------------------------------------------------------------- 1 | 2 | suite('Array', function(){ 3 | suite('#indexOf()', function(){ 4 | var initialValue = 32; 5 | 6 | suiteSetup(function(done){ 7 | initialValue.should.eql(32); 8 | initialValue = 42; 9 | done(); 10 | }); 11 | 12 | test('should return -1 when the value is not present', function(){ 13 | initialValue.should.eql(42); 14 | [1,2,3].indexOf(5).should.equal(-1); 15 | [1,2,3].indexOf(0).should.equal(-1); 16 | }); 17 | 18 | test('should return the correct index when the value is present', function(){ 19 | initialValue.should.eql(42); 20 | [1,2,3].indexOf(1).should.equal(0); 21 | [1,2,3].indexOf(2).should.equal(1); 22 | [1,2,3].indexOf(3).should.equal(2); 23 | }); 24 | 25 | test.skip('should skip this test', function(){ 26 | var zero = 0; 27 | zero.should.equal(1, 'this test should have been skipped'); 28 | }); 29 | 30 | suite.skip('should skip this suite', function(){ 31 | test('should skip this test', function(){ 32 | var zero = 0; 33 | zero.should.equal(1, 'this test should have been skipped'); 34 | }); 35 | }); 36 | 37 | suiteTeardown(function(done){ 38 | initialValue.should.eql(42); 39 | done(); 40 | }); 41 | }); 42 | }); 43 | -------------------------------------------------------------------------------- /test/acceptance/misc/asyncOnly.js: -------------------------------------------------------------------------------- 1 | 2 | describe('asyncOnly', function(){ 3 | it('should display an error', function(){ 4 | 5 | }) 6 | 7 | it('should pass', function(done){ 8 | done(); 9 | }) 10 | }) 11 | -------------------------------------------------------------------------------- /test/acceptance/misc/bail.js: -------------------------------------------------------------------------------- 1 | 2 | describe('bail', function(){ 3 | it('should only display this error', function(done){ 4 | throw new Error('this should be displayed'); 5 | }) 6 | 7 | it('should not display this error', function(done){ 8 | throw new Error('this should not be displayed'); 9 | }) 10 | }) 11 | 12 | describe('bail-2', function(){ 13 | 14 | before(function(done){ 15 | throw new Error('this hook should not be displayed'); 16 | }) 17 | 18 | it('should not display this error', function(done){ 19 | throw new Error('this should not be displayed'); 20 | }) 21 | }) 22 | -------------------------------------------------------------------------------- /test/acceptance/misc/cascade.js: -------------------------------------------------------------------------------- 1 | 2 | describe('one', function(){ 3 | before(function(){ 4 | console.log('before one'); 5 | }) 6 | 7 | after(function(){ 8 | console.log('after one'); 9 | }) 10 | 11 | beforeEach(function(){ 12 | console.log(' before each one'); 13 | }) 14 | 15 | afterEach(function(){ 16 | console.log(' after each one'); 17 | }) 18 | 19 | describe('two', function(){ 20 | before(function(){ 21 | console.log(' before two'); 22 | }) 23 | 24 | after(function(){ 25 | console.log(' after two'); 26 | }) 27 | 28 | beforeEach(function(){ 29 | console.log(' before each two'); 30 | }) 31 | 32 | afterEach(function(){ 33 | console.log(' after each two'); 34 | }) 35 | 36 | describe('three', function(){ 37 | before(function(){ 38 | console.log(' before three'); 39 | }) 40 | 41 | after(function(){ 42 | console.log(' after three'); 43 | }) 44 | 45 | beforeEach(function(){ 46 | console.log(' before each three'); 47 | }) 48 | 49 | afterEach(function(){ 50 | console.log(' after each three'); 51 | }) 52 | 53 | it('should three', function(){ 54 | console.log(' TEST three'); 55 | }) 56 | }) 57 | }) 58 | }) 59 | -------------------------------------------------------------------------------- /test/acceptance/misc/exit.js: -------------------------------------------------------------------------------- 1 | describe('exit', function(){ 2 | //note --bail works nicely in that it still allows an 'early exit' in an error scenario 3 | it('should not exit even in error scenario if called with --no-exit', function(done){ 4 | done(new Error('failure')); 5 | }) 6 | 7 | it('should take a long time to exit if called with --no-exit', function(done){ 8 | done(); 9 | setTimeout(function() { 10 | console.log('all done'); 11 | }, 2500) 12 | }) 13 | }) 14 | -------------------------------------------------------------------------------- /test/acceptance/misc/grep.js: -------------------------------------------------------------------------------- 1 | 2 | describe('grep', function(){ 3 | describe('fast', function(){ 4 | it('should run fast', function(){ 5 | 6 | }) 7 | 8 | it('should run fast again', function(){ 9 | 10 | }) 11 | }) 12 | 13 | describe('slow', function(){ 14 | it('should run slow', function(done){ 15 | setTimeout(done, 1000); 16 | }) 17 | 18 | it('should run slow again', function(done){ 19 | setTimeout(done, 1000); 20 | }) 21 | }) 22 | }) 23 | -------------------------------------------------------------------------------- /test/acceptance/misc/many.js: -------------------------------------------------------------------------------- 1 | // Useful for testing SIGINT handler 2 | // use env.big_number to tune iterations so that you have time to ctrl+c 3 | 4 | describe('a load of tests', function(){ 5 | it('should fail the first test', function(){ 6 | throw new Error('this should appear in the summary'); 7 | }) 8 | 9 | var iterations = (process.env.big_number || 1e7); 10 | function work() { 11 | var a = 0; 12 | for(var i=0; i 5 | it 'should work', -> 6 | obj.should.eql foo: 'bar' -------------------------------------------------------------------------------- /test/acceptance/test.foo: -------------------------------------------------------------------------------- 1 | 1 2 | -------------------------------------------------------------------------------- /test/acceptance/timeout.js: -------------------------------------------------------------------------------- 1 | 2 | describe('timeouts', function(){ 3 | beforeEach(function(done){ 4 | // uncomment 5 | // setTimeout(done, 3000); 6 | done(); 7 | }) 8 | 9 | it('should error on timeout', function(done){ 10 | // uncomment 11 | // setTimeout(done, 3000); 12 | done(); 13 | }) 14 | 15 | it('should allow overriding per-test', function(done){ 16 | this.timeout(1000); 17 | setTimeout(function(){ 18 | done(); 19 | }, 300); 20 | }) 21 | }) 22 | -------------------------------------------------------------------------------- /test/acceptance/uncaught.js: -------------------------------------------------------------------------------- 1 | 2 | describe('uncaught', function(){ 3 | beforeEach(function(done){ 4 | process.nextTick(function(){ 5 | // throw new Error('oh noes'); 6 | done(); 7 | }); 8 | }) 9 | 10 | it('should report properly', function(done){ 11 | process.nextTick(function(){ 12 | // if you uncomment this :) 13 | // throw new Error("I'm uncaught!"); 14 | done(); 15 | }) 16 | }) 17 | }) -------------------------------------------------------------------------------- /test/acceptance/utils.js: -------------------------------------------------------------------------------- 1 | var utils = require('../../lib/utils'); 2 | 3 | describe('lib/utils', function () { 4 | describe('clean', function () { 5 | it("should format a single line test function", function () { 6 | var fn = [ 7 | "function () {" 8 | , " var a = 1;" 9 | , "}" 10 | ].join("\n"); 11 | utils.clean(fn).should.equal("var a = 1;"); 12 | }); 13 | 14 | it("should format a multi line test indented with spaces", function () { 15 | // and no new lines after curly braces, shouldn't matter 16 | var fn = [ 17 | "function(){ var a = 1;" 18 | , " var b = 2;" // this one has more spaces 19 | , " var c = 3; }" 20 | ].join("\n"); 21 | utils.clean(fn).should.equal("var a = 1;\n var b = 2;\nvar c = 3;"); 22 | }); 23 | 24 | it("should format a multi line test indented with tabs", function () { 25 | var fn = [ 26 | "function (arg1, arg2) {" 27 | , "\tif (true) {" 28 | , "\t\tvar a = 1;" 29 | , "\t}" 30 | , "}" 31 | ].join("\n"); 32 | utils.clean(fn).should.equal("if (true) {\n\tvar a = 1;\n}"); 33 | }); 34 | 35 | it("should format functions saved in windows style - spaces", function () { 36 | var fn = [ 37 | "function (one) {" 38 | , " do {", 39 | , ' "nothing";', 40 | , " } while (false);" 41 | , ' }' 42 | ].join("\r\n"); 43 | utils.clean(fn).should.equal('do {\n "nothing";\n} while (false);'); 44 | }); 45 | 46 | it("should format functions saved in windows style - tabs", function () { 47 | var fn = [ 48 | "function ( ) {" 49 | , "\tif (false) {" 50 | , "\t\tvar json = {" 51 | , '\t\t\tone : 1' 52 | , '\t\t};' 53 | , "\t}" 54 | , "}" 55 | ].join("\r\n"); 56 | utils.clean(fn).should.equal("if (false) {\n\tvar json = {\n\t\tone : 1\n\t};\n}"); 57 | }); 58 | }); 59 | }); -------------------------------------------------------------------------------- /test/browser/array.js: -------------------------------------------------------------------------------- 1 | describe('Array', function(){ 2 | describe('#push()', function(){ 3 | it('should append a value', function(){ 4 | foo = 'asdf' 5 | var arr = []; 6 | arr.push('foo'); 7 | arr.push('bar'); 8 | arr.push('baz'); 9 | assert('foo' == arr[0]); // to test indentation 10 | assert('bar' == arr[1]); 11 | assert('baz' == arr[2]); 12 | }) 13 | 14 | it('should return the length', function(){ 15 | var arr = []; 16 | assert(1 == arr.push('foo')); 17 | assert(2 == arr.push('bar')); 18 | assert(3 == arr.push('baz')); 19 | }) 20 | }) 21 | }) 22 | 23 | describe('Array', function(){ 24 | describe('#pop()', function(){ 25 | it('should remove and return the last value', function(){ 26 | var arr = [1,2,3]; 27 | assert(arr.pop() == 3); 28 | assert(arr.pop() == 2); 29 | assert(arr.pop() == -1); 30 | }) 31 | 32 | it('should adjust .length', function(){ 33 | var arr = [1,2,3]; 34 | arr.pop(); 35 | assert(arr.length == 2); 36 | }) 37 | }) 38 | }) 39 | -------------------------------------------------------------------------------- /test/browser/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Mocha 4 | 5 | 6 | 7 | 8 | 9 | 14 | 15 | 16 | 17 | 28 | 29 | 30 |
    31 | 32 | 33 | -------------------------------------------------------------------------------- /test/browser/large.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Mocha 4 | 5 | 6 | 7 | 8 | 9 | 14 | 15 | 20 | 21 | 22 |
    23 | 24 | 25 | -------------------------------------------------------------------------------- /test/browser/large.js: -------------------------------------------------------------------------------- 1 | 2 | var n = 30; 3 | while (n--) { 4 | describe('Array ' + n, function(){ 5 | var arr; 6 | 7 | beforeEach(function(){ 8 | arr = [1,2,3]; 9 | }) 10 | 11 | describe('#indexOf()', function(){ 12 | it('should return -1 when the value is not present', function(){ 13 | assert(-1 == arr.indexOf(5)); 14 | }) 15 | 16 | it('should return the correct index when the value is present', function(done){ 17 | assert(0 == arr.indexOf(1)); 18 | assert(1 == arr.indexOf(2)); 19 | done(); 20 | }) 21 | }) 22 | }) 23 | } 24 | 25 | describe('something', function(){ 26 | it('should provide a useful error', function(done){ 27 | setTimeout(function(){ 28 | throw new Error('boom'); 29 | done(); 30 | }, 1); 31 | }) 32 | 33 | it('should provide an even better error on phantomjs', function(done){ 34 | setTimeout(function(){ 35 | var AssertionError = function(message, actual, expected) { 36 | this.message = message; 37 | this.actual = actual; 38 | this.expected = expected; 39 | this.showDiff = true; 40 | }; 41 | AssertionError.prototype = Object.create(Error.prototype); 42 | AssertionError.prototype.name = 'AssertionError'; 43 | AssertionError.prototype.constructor = AssertionError; 44 | 45 | mocha.throwError(new AssertionError('kabooom', 'text with a typo', 'text without a typo')); 46 | done(); 47 | }, 1); 48 | }) 49 | }) -------------------------------------------------------------------------------- /test/browser/opts.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Mocha 4 | 5 | 6 | 7 | 8 | 14 | 19 | 20 | 21 | 26 | 27 | 28 |
    29 | 30 | 31 | -------------------------------------------------------------------------------- /test/browser/opts.js: -------------------------------------------------------------------------------- 1 | describe('Options', function() { 2 | it('should set timeout value', function() { 3 | assert(this.test._timeout === 1500); 4 | }); 5 | }) 6 | -------------------------------------------------------------------------------- /test/compiler/foo.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | require.extensions['.foo'] = function(module, filename) { 3 | var content; 4 | content = fs.readFileSync(filename, 'utf8'); 5 | var test = 'describe("custom compiler",function(){ it("should work",function() { ' 6 | + content + '.should.eql(1); }); });'; 7 | return module._compile(test, filename); 8 | }; 9 | -------------------------------------------------------------------------------- /test/grep.js: -------------------------------------------------------------------------------- 1 | 2 | var Mocha = require('../'); 3 | 4 | describe('Mocha', function(){ 5 | describe('"grep" option', function(){ 6 | it('should add a RegExp to the mocha.options object', function(){ 7 | var mocha = new Mocha({ grep: /foo/ }); 8 | mocha.options.grep.toString().should.equal('/foo/'); 9 | }) 10 | 11 | it('should convert grep string to a RegExp', function(){ 12 | var mocha = new Mocha({ grep: 'foo' }); 13 | mocha.options.grep.toString().should.equal('/foo/'); 14 | }) 15 | }) 16 | 17 | describe('.grep()', function(){ 18 | it('should add a RegExp to the mocha.options object', function(){ 19 | var mocha = new Mocha; 20 | mocha.grep(/foo/); 21 | mocha.options.grep.toString().should.equal('/foo/'); 22 | }) 23 | 24 | it('should convert grep string to a RegExp', function(){ 25 | var mocha = new Mocha; 26 | mocha.grep('foo'); 27 | mocha.options.grep.toString().should.equal('/foo/'); 28 | }) 29 | 30 | it('should return it\'s parent Mocha object for chainability', function(){ 31 | var mocha = new Mocha; 32 | mocha.grep().should.equal(mocha); 33 | }) 34 | }) 35 | 36 | describe('"invert" option', function(){ 37 | it('should add a Boolean to the mocha.options object', function(){ 38 | var mocha = new Mocha({ invert: true }); 39 | mocha.options.invert.should.be.ok; 40 | }) 41 | }) 42 | }) 43 | -------------------------------------------------------------------------------- /test/hook.async.js: -------------------------------------------------------------------------------- 1 | 2 | describe('async', function(){ 3 | var calls = []; 4 | 5 | before(function(){ 6 | calls.push('root before all'); 7 | }) 8 | 9 | after(function(){ 10 | calls.push('root after all'); 11 | calls.should.eql([ 12 | 'root before all' 13 | , 'before all' 14 | , 'parent before' 15 | , 'before' 16 | , 'before test one' 17 | , 'one' 18 | , 'after' 19 | , 'after test one passed' 20 | , 'parent after' 21 | , 'parent before' 22 | , 'before' 23 | , 'before test two' 24 | , 'two' 25 | , 'after' 26 | , 'after test two passed' 27 | , 'parent after' 28 | , 'parent before' 29 | , 'before' 30 | , 'before test three' 31 | , 'three' 32 | , 'after' 33 | , 'after test three passed' 34 | , 'parent after' 35 | , 'after all' 36 | , 'root after all']); 37 | }) 38 | 39 | beforeEach(function(){ 40 | calls.push('parent before'); 41 | }) 42 | 43 | afterEach(function(){ 44 | calls.push('parent after' ); 45 | }) 46 | 47 | describe('hooks', function(){ 48 | before(function(){ 49 | calls.push('before all'); 50 | }); 51 | 52 | after(function(){ 53 | calls.push('after all'); 54 | }); 55 | 56 | beforeEach(function(done){ 57 | var ctx = this; 58 | process.nextTick(function(){ 59 | calls.push('before'); 60 | if (ctx.currentTest) { 61 | calls.push('before test ' + ctx.currentTest.title); 62 | } 63 | done(); 64 | }) 65 | }) 66 | 67 | it('one', function(done){ 68 | calls.should.eql([ 69 | 'root before all' 70 | , 'before all' 71 | , 'parent before' 72 | , 'before' 73 | , 'before test one']); 74 | calls.push('one'); 75 | process.nextTick(done); 76 | }) 77 | 78 | it('two', function(){ 79 | calls.should.eql([ 80 | 'root before all' 81 | , 'before all' 82 | , 'parent before' 83 | , 'before' 84 | , 'before test one' 85 | , 'one' 86 | , 'after' 87 | , 'after test one passed' 88 | , 'parent after' 89 | , 'parent before' 90 | , 'before' 91 | , 'before test two']); 92 | calls.push('two'); 93 | }) 94 | 95 | it('three', function(){ 96 | calls.should.eql([ 97 | 'root before all' 98 | , 'before all' 99 | , 'parent before' 100 | , 'before' 101 | , 'before test one' 102 | , 'one' 103 | , 'after' 104 | , 'after test one passed' 105 | , 'parent after' 106 | , 'parent before' 107 | , 'before' 108 | , 'before test two' 109 | , 'two' 110 | , 'after' 111 | , 'after test two passed' 112 | , 'parent after' 113 | , 'parent before' 114 | , 'before' 115 | , 'before test three']); 116 | calls.push('three'); 117 | }) 118 | 119 | afterEach(function(done){ 120 | var ctx = this; 121 | process.nextTick(function(){ 122 | calls.push('after'); 123 | if (ctx.currentTest) { 124 | calls.push('after test ' + ctx.currentTest.title + ' ' + ctx.currentTest.state); 125 | } 126 | done(); 127 | }) 128 | }) 129 | }) 130 | }) 131 | -------------------------------------------------------------------------------- /test/hook.err.js: -------------------------------------------------------------------------------- 1 | describe('hook error handling', function(){ 2 | // Lines in this test should be uncommented to see actual behavior 3 | // You will also see errors in hooks 4 | describe('before hook error', function() { 5 | var calls = []; 6 | describe('spec 1', function () { 7 | describe('spec 1 nested', function () { 8 | it('should not be called, because hook error was in a parent suite', function() { 9 | calls.push('test nested'); 10 | }) 11 | }) 12 | before(function(){ 13 | calls.push('before'); 14 | // throw new Error('before hook error'); 15 | }) 16 | after(function(){ 17 | calls.push('after'); 18 | }) 19 | it('should not be called because of error in before hook', function() { 20 | calls.push('test'); 21 | }) 22 | }) 23 | describe('spec 2', function () { 24 | before(function(){ 25 | calls.push('before 2'); 26 | }) 27 | after(function(){ 28 | calls.push('after 2'); 29 | }) 30 | it('should be called, because hook error was in a sibling suite', function() { 31 | calls.push('test 2'); 32 | }) 33 | }) 34 | after(function () { 35 | // calls.should.eql(['before', 'after', 'before 2', 'test 2', 'after 2']); 36 | }) 37 | }) 38 | 39 | describe('before each hook error', function() { 40 | var calls = []; 41 | describe('spec 1', function () { 42 | describe('spec 1 nested', function () { 43 | it('should not be called, because hook error was in a parent suite', function() { 44 | calls.push('test nested'); 45 | }) 46 | }) 47 | beforeEach(function(){ 48 | calls.push('before'); 49 | // throw new Error('before each hook error'); 50 | }) 51 | afterEach(function(){ 52 | calls.push('after'); 53 | }) 54 | it('should not be called because of error in before each hook', function() { 55 | calls.push('test'); 56 | }) 57 | }) 58 | describe('spec 2', function () { 59 | before(function(){ 60 | calls.push('before 2'); 61 | }) 62 | after(function(){ 63 | calls.push('after 2'); 64 | }) 65 | it('should be called, because hook error was in a sibling suite', function() { 66 | calls.push('test 2'); 67 | }) 68 | }) 69 | after(function () { 70 | // This should be called ! 71 | // calls.should.eql(['before', 'after', 'before 2', 'test 2', 'after 2']); 72 | }) 73 | }) 74 | 75 | describe('after hook error', function() { 76 | var calls = []; 77 | describe('spec 1', function () { 78 | describe('spec 1 nested', function () { 79 | it('should be called, because hook error will happen after parent suite', function() { 80 | calls.push('test nested'); 81 | }) 82 | }) 83 | before(function(){ 84 | calls.push('before'); 85 | }) 86 | after(function(){ 87 | calls.push('after'); 88 | // throw new Error('after hook error'); 89 | }) 90 | it('should be called because error is in after hook', function() { 91 | calls.push('test'); 92 | }) 93 | }) 94 | describe('spec 2', function () { 95 | before(function(){ 96 | calls.push('before 2'); 97 | }) 98 | after(function(){ 99 | calls.push('after 2'); 100 | }) 101 | it('should be called, because hook error was in a sibling suite', function() { 102 | calls.push('test 2'); 103 | }) 104 | }) 105 | after(function () { 106 | // Even this should be called ! 107 | // calls.should.eql(['before', 'test', 'test nested', 'after', 'before 2', 'test 2', 'after 2']); 108 | }) 109 | }) 110 | 111 | describe('after each hook error', function() { 112 | var calls = []; 113 | describe('spec 1', function () { 114 | describe('spec 1 nested', function () { 115 | it('should not be called, because hook error has already happened in parent suite', function() { 116 | calls.push('test nested'); 117 | }) 118 | }) 119 | beforeEach(function(){ 120 | calls.push('before'); 121 | }) 122 | afterEach(function(){ 123 | calls.push('after'); 124 | // throw new Error('after each hook error'); 125 | }) 126 | it('should be called because error is in after each hook, and this is the first test', function() { 127 | calls.push('test'); 128 | }) 129 | it('should not be called because error is in after each hook, and this is the second test', function() { 130 | calls.push('another test'); 131 | }) 132 | }) 133 | describe('spec 2', function () { 134 | before(function(){ 135 | calls.push('before 2'); 136 | }) 137 | after(function(){ 138 | calls.push('after 2'); 139 | }) 140 | it('should be called, because hook error was in a sibling suite', function() { 141 | calls.push('test 2'); 142 | }) 143 | }) 144 | after(function () { 145 | // This should be called ! 146 | // calls.should.eql(['before', 'test', 'after', 'before 2', 'test 2', 'after 2']); 147 | }) 148 | }) 149 | 150 | describe('multiple hook errors', function() { 151 | var calls = []; 152 | before(function(){ 153 | calls.push("root before"); 154 | }); 155 | beforeEach(function(){ 156 | calls.push("root before each"); 157 | }); 158 | describe('1', function(){ 159 | beforeEach(function() { 160 | calls.push('1 before each') 161 | }) 162 | 163 | describe('1.1', function(){ 164 | before(function() { 165 | calls.push('1.1 before'); 166 | }); 167 | beforeEach(function() { 168 | calls.push('1.1 before each') 169 | // throw new Error('1.1 before each hook failed') 170 | }); 171 | it('1.1 test 1', function () {calls.push('1.1 test 1')}); 172 | it('1.1 test 2', function () {calls.push('1.1 test 2')}); 173 | afterEach(function() { 174 | calls.push("1.1 after each"); 175 | }); 176 | after(function(){ 177 | calls.push("1.1 after"); 178 | // throw new Error('1.1 after hook failed') 179 | }); 180 | }); 181 | 182 | describe('1.2', function(){ 183 | before(function() { 184 | calls.push('1.2 before'); 185 | }); 186 | beforeEach(function() { 187 | calls.push('1.2 before each') 188 | }); 189 | it('1.2 test 1', function () {calls.push('1.2 test 1')}); 190 | it('1.2 test 2', function () {calls.push('1.2 test 2')}); 191 | afterEach(function() { 192 | calls.push("1.2 after each"); 193 | // throw new Error('1.2 after each hook failed') 194 | }); 195 | after(function(){ 196 | calls.push("1.2 after"); 197 | }); 198 | }); 199 | 200 | afterEach(function() { 201 | calls.push('1 after each') 202 | }) 203 | 204 | after(function(){ 205 | calls.push("1 after"); 206 | }); 207 | }) 208 | 209 | describe('2', function(){ 210 | beforeEach(function() { 211 | calls.push('2 before each') 212 | // throw new Error('2 before each hook failed') 213 | }) 214 | 215 | describe('2.1', function(){ 216 | before(function() { 217 | calls.push('2.1 before'); 218 | }); 219 | beforeEach(function() { 220 | calls.push('2.1 before each') 221 | }); 222 | it('2.1 test 1', function () {calls.push('2.1 test 1')}); 223 | it('2.1 test 2', function () {calls.push('2.1 test 2')}); 224 | afterEach(function() { 225 | calls.push("2.1 after each"); 226 | }); 227 | after(function(){ 228 | calls.push("2.1 after"); 229 | }); 230 | }); 231 | 232 | describe('2.2', function(){ 233 | before(function() { 234 | calls.push('2.2 before'); 235 | }); 236 | beforeEach(function() { 237 | calls.push('2.2 before each') 238 | }); 239 | it('2.2 test 1', function () {calls.push('2.2 test 1')}); 240 | it('2.2 test 2', function () {calls.push('2.2 test 2')}); 241 | afterEach(function() { 242 | calls.push("2.2 after each"); 243 | }); 244 | after(function(){ 245 | calls.push("2.2 after"); 246 | }); 247 | }); 248 | 249 | afterEach(function() { 250 | calls.push('2 after each') 251 | // throw new Error('2 after each hook failed') 252 | }) 253 | 254 | after(function(){ 255 | calls.push("2 after"); 256 | }); 257 | }) 258 | 259 | after(function(){ 260 | calls.push("root after"); 261 | /* calls.should.eql([ 262 | "root before", 263 | "1.1 before", 264 | "root before each", 265 | "1 before each", 266 | "1.1 before each", 267 | "1.1 after each", 268 | "1 after each", 269 | "root after each", 270 | "1.1 after", 271 | "1.2 before", 272 | "root before each", 273 | "1 before each", 274 | "1.2 before each", 275 | "1.2 test 1", 276 | "1.2 after each", 277 | "1 after each", 278 | "root after each", 279 | "1.2 after", 280 | "1 after", 281 | "2.1 before", 282 | "root before each", 283 | "2 before each", 284 | "2 after each", 285 | "root after each", 286 | "2.1 after", 287 | "2 after", 288 | "root after" 289 | ]); */ 290 | }); 291 | afterEach(function(){ 292 | calls.push("root after each"); 293 | }); 294 | }) 295 | 296 | }) -------------------------------------------------------------------------------- /test/hook.sync.js: -------------------------------------------------------------------------------- 1 | 2 | describe('serial', function(){ 3 | var calls = []; 4 | 5 | beforeEach(function(){ 6 | calls.push('parent before'); 7 | }) 8 | 9 | afterEach(function(){ 10 | calls.push('parent after'); 11 | }) 12 | 13 | describe('hooks', function(){ 14 | beforeEach(function(){ 15 | calls.push('before'); 16 | if (this.currentTest) { 17 | calls.push('before test ' + this.currentTest.title); 18 | } 19 | }) 20 | 21 | it('one', function(){ 22 | calls.should.eql([ 23 | 'parent before' 24 | , 'before' 25 | , 'before test one']); 26 | calls.push('one'); 27 | }) 28 | 29 | it('two', function(){ 30 | calls.should.eql([ 31 | 'parent before' 32 | , 'before' 33 | , 'before test one' 34 | , 'one' 35 | , 'after' 36 | , 'after test one passed' 37 | , 'parent after' 38 | , 'parent before' 39 | , 'before' 40 | , 'before test two']); 41 | calls.push('two'); 42 | }) 43 | 44 | it('three', function(){ 45 | calls.should.eql([ 46 | 'parent before' 47 | , 'before' 48 | , 'before test one' 49 | , 'one' 50 | , 'after' 51 | , 'after test one passed' 52 | , 'parent after' 53 | , 'parent before' 54 | , 'before' 55 | , 'before test two' 56 | , 'two' 57 | , 'after' 58 | , 'after test two passed' 59 | , 'parent after' 60 | , 'parent before' 61 | , 'before' 62 | , 'before test three']); 63 | calls.push('three'); 64 | }) 65 | 66 | afterEach(function(){ 67 | calls.push('after'); 68 | if (this.currentTest) { 69 | calls.push('after test ' + this.currentTest.title + ' ' + this.currentTest.state); 70 | } 71 | }) 72 | 73 | after(function(){ 74 | calls.should.eql([ 75 | 'parent before' 76 | , 'before' 77 | , 'before test one' 78 | , 'one' 79 | , 'after' 80 | , 'after test one passed' 81 | , 'parent after' 82 | , 'parent before' 83 | , 'before' 84 | , 'before test two' 85 | , 'two' 86 | , 'after' 87 | , 'after test two passed' 88 | , 'parent after' 89 | , 'parent before' 90 | , 'before' 91 | , 'before test three' 92 | , 'three' 93 | , 'after' 94 | , 'after test three passed' 95 | , 'parent after']); 96 | }) 97 | }) 98 | }) -------------------------------------------------------------------------------- /test/hook.sync.nested.js: -------------------------------------------------------------------------------- 1 | 2 | describe('serial', function(){ 3 | describe('nested', function(){ 4 | var calls = []; 5 | 6 | beforeEach(function(){ 7 | calls.push('parent before'); 8 | if (this.currentTest) { 9 | calls.push('parent before test ' + this.currentTest.title); 10 | } 11 | }) 12 | 13 | afterEach(function(){ 14 | calls.push('parent after'); 15 | if (this.currentTest) { 16 | calls.push('parent after test ' + this.currentTest.title + ' ' + this.currentTest.state); 17 | } 18 | }); 19 | 20 | it('foo', function(){ 21 | calls.should.eql([ 22 | 'parent before' 23 | , 'parent before test foo']); 24 | calls.push('foo'); 25 | }) 26 | 27 | it('bar', function(){ 28 | calls.should.eql([ 29 | 'parent before' 30 | , 'parent before test foo' 31 | , 'foo' 32 | , 'parent after' 33 | , 'parent after test foo passed' 34 | , 'parent before' 35 | , 'parent before test bar']); 36 | }) 37 | 38 | describe('hooks', function(){ 39 | beforeEach(function(){ 40 | calls.push('before'); 41 | if (this.currentTest) { 42 | calls.push('before test ' + this.currentTest.title); 43 | } 44 | }) 45 | 46 | it('one', function(){ 47 | calls.should.eql([ 48 | 'parent before' 49 | , 'parent before test foo' 50 | , 'foo' 51 | , 'parent after' 52 | , 'parent after test foo passed' 53 | , 'parent before' 54 | , 'parent before test bar' 55 | , 'parent after' 56 | , 'parent after test bar passed' 57 | , 'parent before' 58 | , 'parent before test one' 59 | , 'before' 60 | , 'before test one']); 61 | calls.push('one'); 62 | }) 63 | 64 | it('two', function(){ 65 | calls.should.eql([ 66 | 'parent before' 67 | , 'parent before test foo' 68 | , 'foo' 69 | , 'parent after' 70 | , 'parent after test foo passed' 71 | , 'parent before' 72 | , 'parent before test bar' 73 | , 'parent after' 74 | , 'parent after test bar passed' 75 | , 'parent before' 76 | , 'parent before test one' 77 | , 'before' 78 | , 'before test one' 79 | , 'one' 80 | , 'after' 81 | , 'after test one passed' 82 | , 'parent after' 83 | , 'parent after test one passed' 84 | , 'parent before' 85 | , 'parent before test two' 86 | , 'before' 87 | , 'before test two']); 88 | calls.push('two'); 89 | }); 90 | 91 | afterEach(function(){ 92 | calls.push('after'); 93 | if (this.currentTest) { 94 | calls.push('after test ' + this.currentTest.title + ' ' + this.currentTest.state); 95 | } 96 | }) 97 | }) 98 | }) 99 | }) 100 | -------------------------------------------------------------------------------- /test/hook.timeout.js: -------------------------------------------------------------------------------- 1 | 2 | before(function(done){ 3 | this.timeout(100); 4 | setTimeout(done, 50); 5 | }) 6 | 7 | it('should work', function(done) { 8 | done(); 9 | }); -------------------------------------------------------------------------------- /test/http.meta.2.js: -------------------------------------------------------------------------------- 1 | 2 | var http = require('http'); 3 | 4 | var server = http.createServer(function(req, res){ 5 | var accept = req.headers.accept || '' 6 | , json = ~accept.indexOf('json'); 7 | 8 | switch (req.url) { 9 | case '/': 10 | res.end('hello'); 11 | break; 12 | case '/users': 13 | if (json) { 14 | res.end('["tobi","loki","jane"]'); 15 | } else { 16 | res.end('tobi, loki, jane'); 17 | } 18 | break; 19 | } 20 | }) 21 | 22 | server.listen(8899); 23 | 24 | function get(url) { 25 | var fields 26 | , expected 27 | , header = {}; 28 | 29 | function request(done) { 30 | http.get({ path: url, port: 8899, headers: header }, function(res){ 31 | var buf = ''; 32 | res.should.have.status(200); 33 | res.setEncoding('utf8'); 34 | res.on('data', function(chunk){ buf += chunk }); 35 | res.on('end', function(){ 36 | buf.should.equal(expected); 37 | done(); 38 | }); 39 | }) 40 | } 41 | 42 | return { 43 | set: function(field, val){ 44 | header[field] = val; 45 | return this; 46 | }, 47 | 48 | should: { 49 | respond: function(body){ 50 | fields = Object.keys(header).map(function(field){ 51 | return field + ': ' + header[field]; 52 | }).join(', '); 53 | 54 | expected = body; 55 | describe('GET ' + url, function(){ 56 | if (fields) { 57 | describe('when given ' + fields, function(){ 58 | it('should respond with "' + body + '"', request); 59 | }); 60 | } else { 61 | it('should respond with "' + body + '"', request); 62 | } 63 | }); 64 | } 65 | } 66 | }; 67 | } 68 | 69 | describe('http server', function(){ 70 | get('/') 71 | .should 72 | .respond('hello') 73 | 74 | get('/users') 75 | .should 76 | .respond('tobi, loki, jane') 77 | 78 | get('/users') 79 | .set('Accept', 'application/json') 80 | .should 81 | .respond('["tobi","loki","jane"]') 82 | }) -------------------------------------------------------------------------------- /test/http.meta.js: -------------------------------------------------------------------------------- 1 | 2 | var http = require('http'); 3 | 4 | var server = http.createServer(function(req, res){ 5 | var accept = req.headers.accept || '' 6 | , json = ~accept.indexOf('json'); 7 | 8 | switch (req.url) { 9 | case '/': 10 | res.end('hello'); 11 | break; 12 | case '/users': 13 | if (json) { 14 | res.end('["tobi","loki","jane"]'); 15 | } else { 16 | res.end('tobi, loki, jane'); 17 | } 18 | break; 19 | } 20 | }) 21 | 22 | server.listen(8889); 23 | 24 | function get(url, body, header) { 25 | return function(done){ 26 | http.get({ path: url, port: 8889, headers: header }, function(res){ 27 | var buf = ''; 28 | res.should.have.status(200); 29 | res.setEncoding('utf8'); 30 | res.on('data', function(chunk){ buf += chunk }); 31 | res.on('end', function(){ 32 | buf.should.equal(body); 33 | done(); 34 | }); 35 | }) 36 | } 37 | } 38 | 39 | describe('http requests', function(){ 40 | describe('GET /', function(){ 41 | it('should respond with hello', 42 | get('/', 'hello')) 43 | }) 44 | 45 | describe('GET /users', function(){ 46 | it('should respond with users', 47 | get('/users', 'tobi, loki, jane')) 48 | 49 | it('should respond with users', 50 | get('/users', '["tobi","loki","jane"]', { Accept: 'application/json' })) 51 | }) 52 | }) -------------------------------------------------------------------------------- /test/jsapi/index.js: -------------------------------------------------------------------------------- 1 | 2 | var Mocha = require('../../') 3 | , path = require('path'); 4 | 5 | var mocha = new Mocha({ 6 | ui: 'bdd', 7 | globals: ['okGlobalA', 'okGlobalB', 'okGlobalC', 'callback*'], 8 | // ignoreLeaks: true, 9 | growl: true 10 | }); 11 | 12 | // mocha.reporter('spec'); 13 | require('should'); 14 | 15 | mocha.addFile('test/suite.js'); 16 | mocha.addFile('test/runner.js'); 17 | mocha.addFile('test/runnable.js'); 18 | mocha.addFile('test/hook.sync.js'); 19 | mocha.addFile('test/hook.sync.nested.js'); 20 | mocha.addFile('test/hook.async.js'); 21 | mocha.addFile('test/acceptance/duration.js'); 22 | mocha.addFile('test/acceptance/fs.js'); 23 | mocha.addFile('test/acceptance/globals.js'); 24 | mocha.addFile('test/acceptance/pending.js'); 25 | mocha.addFile('test/acceptance/timeout.js'); 26 | 27 | mocha.run(function(){ 28 | console.log('done'); 29 | }).on('pass', function(test){ 30 | // console.log('... %s', test.title); 31 | }); 32 | -------------------------------------------------------------------------------- /test/mocha.opts: -------------------------------------------------------------------------------- 1 | --require should 2 | --reporter dot 3 | --ui bdd 4 | --globals okGlobalA,okGlobalB 5 | --globals okGlobalC 6 | --globals callback* 7 | --timeout 200 8 | -------------------------------------------------------------------------------- /test/reporters/nyan.js: -------------------------------------------------------------------------------- 1 | var reporters = require('../../').reporters 2 | , NyanCat = reporters.Nyan; 3 | 4 | 5 | describe('nyan face', function () { 6 | it('nyan face:(x .x) when "failures" at least one', function () { 7 | var nyanCat = new NyanCat({on: function(){}}); 8 | nyanCat.stats = { passes: 2, pending: 1, failures: 1 }; 9 | nyanCat.face.call(nyanCat).should.equal('( x .x)'); 10 | }); 11 | 12 | it('expected nyan face:(x .x) when "peinding" at least one and no failing', function () { 13 | var nyanCat = new NyanCat({on: function(){}}); 14 | nyanCat.stats = { passes: 2, pending: 1, failures: 0 }; 15 | nyanCat.face.call(nyanCat).should.equal('( o .o)'); 16 | }); 17 | 18 | it('expected nyan face:(^ .^) when "passing" only', function () { 19 | var nyanCat = new NyanCat({on: function(){}}); 20 | nyanCat.stats = { passes: 1, pending: 0, failures: 0 }; 21 | nyanCat.face.call(nyanCat).should.equal('( ^ .^)'); 22 | }); 23 | 24 | it('nyan face:(- .-) when otherwise', function (done) { 25 | var nyanCat = new NyanCat({on: function(){}}); 26 | nyanCat.stats = { passes: 0, pending: 0, failures: 0 }; 27 | nyanCat.face.call(nyanCat).should.equal('( - .-)'); 28 | done(); 29 | }); 30 | }) 31 | -------------------------------------------------------------------------------- /test/runnable.js: -------------------------------------------------------------------------------- 1 | 2 | var mocha = require('../') 3 | , Runnable = mocha.Runnable 4 | , EventEmitter = require('events').EventEmitter; 5 | 6 | describe('Runnable(title, fn)', function(){ 7 | // For every test we poison the global time-related methods. 8 | // runnable.js etc. should keep its own local copy, in order to fix GH-237. 9 | // NB: we can't poison global.Date because the normal implementation of 10 | // global.setTimeout uses it [1] so if the runnable.js keeps a copy of 11 | // global.setTimeout (like it's supposed to), that will blow up. 12 | // [1]: https://github.com/joyent/node/blob/7fc835afe362ebd30a0dbec81d3360bd24525222/lib/timers.js#L74 13 | var setTimeout = global.setTimeout 14 | , setInterval = global.setInterval 15 | , clearTimeout = global.clearTimeout 16 | , clearInterval = global.clearInterval; 17 | 18 | function poisonPill() { 19 | throw new Error("Don't use global time-related stuff."); 20 | } 21 | 22 | beforeEach(function(){ 23 | global.setTimeout = 24 | global.setInterval = 25 | global.clearTimeout = 26 | global.clearInterval = poisonPill; 27 | }) 28 | 29 | afterEach(function(){ 30 | global.setTimeout = setTimeout; 31 | global.setInterval = setInterval; 32 | global.clearTimeout = clearTimeout; 33 | global.clearInterval = clearInterval; 34 | }) 35 | 36 | describe('#timeout(ms)', function(){ 37 | it('should set the timeout', function(){ 38 | var run = new Runnable; 39 | run.timeout(1000) 40 | run.timeout().should.equal(1000); 41 | }) 42 | }) 43 | 44 | describe('#slow(ms)', function(){ 45 | it('should set the slow threshold', function(){ 46 | var run = new Runnable; 47 | run.slow(100) 48 | run.slow().should.equal(100); 49 | }) 50 | }) 51 | 52 | describe('.title', function(){ 53 | it('should be present', function(){ 54 | new Runnable('foo').title.should.equal('foo'); 55 | }) 56 | }) 57 | 58 | describe('when arity >= 1', function(){ 59 | it('should be .async', function(){ 60 | var run = new Runnable('foo', function(done){}); 61 | run.async.should.equal(1); 62 | run.sync.should.be.false; 63 | }) 64 | }) 65 | 66 | describe('when arity == 0', function(){ 67 | it('should be .sync', function(){ 68 | var run = new Runnable('foo', function(){}); 69 | run.async.should.be.equal(0); 70 | run.sync.should.be.true; 71 | }) 72 | }) 73 | 74 | describe('#globals', function(){ 75 | it('should allow for whitelisting globals', function(done){ 76 | var test = new Runnable('foo', function(){}); 77 | test.async.should.be.equal(0); 78 | test.sync.should.be.true; 79 | test.globals(['foobar']); 80 | test.run(done); 81 | }) 82 | }) 83 | 84 | describe('.run(fn)', function(){ 85 | describe('when .pending', function(){ 86 | it('should not invoke the callback', function(done){ 87 | var test = new Runnable('foo', function(){ 88 | throw new Error('should not be called'); 89 | }); 90 | 91 | test.pending = true; 92 | test.run(done); 93 | }) 94 | }) 95 | 96 | describe('when sync', function(){ 97 | describe('without error', function(){ 98 | it('should invoke the callback', function(done){ 99 | var calls = 0; 100 | var test = new Runnable('foo', function(){ 101 | ++calls; 102 | }); 103 | 104 | test.run(function(err){ 105 | calls.should.equal(1); 106 | test.duration.should.be.type('number'); 107 | done(err); 108 | }) 109 | }) 110 | }) 111 | 112 | describe('when an exception is thrown', function(){ 113 | it('should invoke the callback', function(done){ 114 | var calls = 0; 115 | var test = new Runnable('foo', function(){ 116 | ++calls; 117 | throw new Error('fail'); 118 | }); 119 | 120 | test.run(function(err){ 121 | calls.should.equal(1); 122 | err.message.should.equal('fail'); 123 | done(); 124 | }) 125 | }) 126 | }) 127 | }) 128 | 129 | describe('when async', function(){ 130 | describe('without error', function(){ 131 | it('should invoke the callback', function(done){ 132 | var calls = 0; 133 | var test = new Runnable('foo', function(done){ 134 | process.nextTick(done); 135 | }); 136 | 137 | test.run(done); 138 | }) 139 | }) 140 | 141 | describe('when the callback is invoked several times', function(){ 142 | describe('without an error', function(){ 143 | it('should emit a single "error" event', function(done){ 144 | var calls = 0; 145 | var errCalls = 0; 146 | 147 | var test = new Runnable('foo', function(done){ 148 | process.nextTick(done); 149 | process.nextTick(done); 150 | process.nextTick(done); 151 | process.nextTick(done); 152 | }); 153 | 154 | test.on('error', function(err){ 155 | ++errCalls; 156 | err.message.should.equal('done() called multiple times'); 157 | calls.should.equal(1); 158 | errCalls.should.equal(1); 159 | done(); 160 | }); 161 | 162 | test.run(function(){ 163 | ++calls; 164 | }); 165 | }) 166 | }) 167 | 168 | describe('with an error', function(){ 169 | it('should emit a single "error" event', function(done){ 170 | var calls = 0; 171 | var errCalls = 0; 172 | 173 | var test = new Runnable('foo', function(done){ 174 | done(new Error('fail')); 175 | process.nextTick(done); 176 | done(new Error('fail')); 177 | process.nextTick(done); 178 | process.nextTick(done); 179 | }); 180 | 181 | test.on('error', function(err){ 182 | ++errCalls; 183 | err.message.should.equal('fail'); 184 | calls.should.equal(1); 185 | errCalls.should.equal(1); 186 | done(); 187 | }); 188 | 189 | test.run(function(){ 190 | ++calls; 191 | }); 192 | }) 193 | }) 194 | }) 195 | 196 | describe('when an exception is thrown', function(){ 197 | it('should invoke the callback', function(done){ 198 | var calls = 0; 199 | var test = new Runnable('foo', function(done){ 200 | throw new Error('fail'); 201 | process.nextTick(done); 202 | }); 203 | 204 | test.run(function(err){ 205 | err.message.should.equal('fail'); 206 | done(); 207 | }); 208 | }) 209 | }) 210 | 211 | describe('when an error is passed', function(){ 212 | it('should invoke the callback', function(done){ 213 | var calls = 0; 214 | var test = new Runnable('foo', function(done){ 215 | done(new Error('fail')); 216 | }); 217 | 218 | test.run(function(err){ 219 | err.message.should.equal('fail'); 220 | done(); 221 | }); 222 | }) 223 | }) 224 | 225 | it('should allow updating the timeout', function(done){ 226 | var callCount = 0; 227 | var increment = function() { 228 | callCount++; 229 | }; 230 | var test = new Runnable('foo', function(done){ 231 | setTimeout(increment, 1); 232 | setTimeout(increment, 100); 233 | }); 234 | test.timeout(10); 235 | test.run(function(err){ 236 | err.should.be.ok; 237 | callCount.should.equal(1); 238 | done(); 239 | }); 240 | }) 241 | 242 | it('should allow a timeout of 0') 243 | }) 244 | 245 | }) 246 | }) 247 | -------------------------------------------------------------------------------- /test/runner.js: -------------------------------------------------------------------------------- 1 | 2 | var mocha = require('../') 3 | , Suite = mocha.Suite 4 | , Runner = mocha.Runner 5 | , Test = mocha.Test; 6 | 7 | describe('Runner', function(){ 8 | var suite, runner; 9 | 10 | beforeEach(function(){ 11 | suite = new Suite(null, 'root'); 12 | runner = new Runner(suite); 13 | }) 14 | 15 | describe('.grep()', function(){ 16 | it('should update the runner.total with number of matched tests', function(){ 17 | suite.addTest(new Test('im a test about lions')); 18 | suite.addTest(new Test('im another test about lions')); 19 | suite.addTest(new Test('im a test about bears')); 20 | var newRunner = new Runner(suite); 21 | newRunner.grep(/lions/); 22 | newRunner.total.should.equal(2); 23 | }) 24 | 25 | it('should update the runner.total with number of matched tests when inverted', function(){ 26 | suite.addTest(new Test('im a test about lions')); 27 | suite.addTest(new Test('im another test about lions')); 28 | suite.addTest(new Test('im a test about bears')); 29 | var newRunner = new Runner(suite); 30 | newRunner.grep(/lions/, true); 31 | newRunner.total.should.equal(1); 32 | }) 33 | }) 34 | 35 | describe('.grepTotal()', function(){ 36 | it('should return the total number of matched tests', function(){ 37 | suite.addTest(new Test('im a test about lions')); 38 | suite.addTest(new Test('im another test about lions')); 39 | suite.addTest(new Test('im a test about bears')); 40 | runner.grep(/lions/); 41 | runner.grepTotal(suite).should.equal(2); 42 | }) 43 | 44 | it('should return the total number of matched tests when inverted', function(){ 45 | suite.addTest(new Test('im a test about lions')); 46 | suite.addTest(new Test('im another test about lions')); 47 | suite.addTest(new Test('im a test about bears')); 48 | runner.grep(/lions/, true); 49 | runner.grepTotal(suite).should.equal(1); 50 | }) 51 | }) 52 | 53 | describe('.globalProps()', function(){ 54 | it('should include common non enumerable globals', function() { 55 | var props = runner.globalProps(); 56 | props.should.include('setTimeout'); 57 | props.should.include('clearTimeout'); 58 | props.should.include('setInterval'); 59 | props.should.include('clearInterval'); 60 | props.should.include('Date'); 61 | props.should.include('XMLHttpRequest'); 62 | }); 63 | }); 64 | 65 | describe('.globals()', function(){ 66 | it('should default to the known globals', function(){ 67 | runner.globals().length.should.be.above(16); 68 | }) 69 | 70 | it('should white-list globals', function(){ 71 | runner.globals(['foo', 'bar']); 72 | runner.globals().should.include('foo'); 73 | runner.globals().should.include('bar'); 74 | }) 75 | }) 76 | 77 | describe('.checkGlobals(test)', function(){ 78 | it('should allow variables that match a wildcard', function(done) { 79 | runner.globals(['foo*', 'giz*']); 80 | global.foo = 'baz'; 81 | global.gizmo = 'quux'; 82 | runner.checkGlobals(); 83 | delete global.foo; 84 | delete global.gizmo; 85 | done() 86 | }) 87 | 88 | it('should emit "fail" when a new global is introduced', function(done){ 89 | runner.checkGlobals(); 90 | global.foo = 'bar'; 91 | runner.on('fail', function(test, err){ 92 | test.should.equal('im a test'); 93 | err.message.should.equal('global leak detected: foo'); 94 | delete global.foo; 95 | done(); 96 | }); 97 | runner.checkGlobals('im a test'); 98 | }) 99 | 100 | it('should emit "fail" when a single new disallowed global is introduced after a single extra global is allowed', function(done) { 101 | var doneCalled = false; 102 | runner.globals('good'); 103 | global.bad = 1; 104 | runner.on('fail', function(test, err) { 105 | delete global.bad; 106 | done(); 107 | doneCalled = true; 108 | }); 109 | runner.checkGlobals('test'); 110 | if (!doneCalled) { 111 | done(Error("Expected test failure did not occur.")); 112 | } 113 | }); 114 | 115 | it ('should not fail when a new common global is introduced', function(){ 116 | // verify that the prop isn't enumerable 117 | delete global.XMLHttpRequest; 118 | global.propertyIsEnumerable('XMLHttpRequest').should.not.be.ok; 119 | 120 | // create a new runner and keep a reference to the test. 121 | var test = new Test('im a test about bears'); 122 | suite.addTest(test); 123 | var newRunner = new Runner(suite); 124 | 125 | // make the prop enumerable again. 126 | global.XMLHttpRequest = function() {}; 127 | global.propertyIsEnumerable('XMLHttpRequest').should.be.ok; 128 | 129 | // verify the test hasn't failed. 130 | newRunner.checkGlobals(test); 131 | test.should.not.have.key('state'); 132 | 133 | // clean up our global space. 134 | delete global.XMLHttpRequest; 135 | }); 136 | 137 | it('should pluralize the error message when several are introduced', function(done){ 138 | runner.checkGlobals(); 139 | global.foo = 'bar'; 140 | global.bar = 'baz'; 141 | runner.on('fail', function(test, err){ 142 | test.should.equal('im a test'); 143 | err.message.should.equal('global leaks detected: foo, bar'); 144 | delete global.foo; 145 | delete global.bar; 146 | done(); 147 | }); 148 | runner.checkGlobals('im a test'); 149 | }) 150 | 151 | it('should respect per test whitelisted globals', function() { 152 | var test = new Test('im a test about lions'); 153 | test.globals(['foo']); 154 | 155 | suite.addTest(test); 156 | var runner = new Runner(suite); 157 | 158 | global.foo = 'bar'; 159 | 160 | // verify the test hasn't failed. 161 | runner.checkGlobals(test); 162 | test.should.not.have.key('state'); 163 | 164 | delete global.foo; 165 | }) 166 | 167 | it('should respect per test whitelisted globals but still detect other leaks', function(done) { 168 | var test = new Test('im a test about lions'); 169 | test.globals(['foo']); 170 | 171 | suite.addTest(test); 172 | 173 | global.foo = 'bar'; 174 | global.bar = 'baz'; 175 | runner.on('fail', function(test, err){ 176 | test.title.should.equal('im a test about lions'); 177 | err.message.should.equal('global leak detected: bar'); 178 | delete global.foo; 179 | done(); 180 | }); 181 | runner.checkGlobals(test); 182 | }) 183 | }) 184 | 185 | describe('.fail(test, err)', function(){ 186 | it('should increment .failures', function(){ 187 | runner.failures.should.equal(0); 188 | runner.fail({}, {}); 189 | runner.failures.should.equal(1); 190 | runner.fail({}, {}); 191 | runner.failures.should.equal(2); 192 | }) 193 | 194 | it('should set test.state to "failed"', function(){ 195 | var test = {}; 196 | runner.fail(test, 'some error'); 197 | test.state.should.equal('failed'); 198 | }) 199 | 200 | it('should emit "fail"', function(done){ 201 | var test = {}, err = {}; 202 | runner.on('fail', function(test, err){ 203 | test.should.equal(test); 204 | err.should.equal(err); 205 | done(); 206 | }); 207 | runner.fail(test, err); 208 | }) 209 | }) 210 | 211 | describe('.failHook(hook, err)', function(){ 212 | it('should increment .failures', function(){ 213 | runner.failures.should.equal(0); 214 | runner.failHook({}, {}); 215 | runner.failures.should.equal(1); 216 | runner.failHook({}, {}); 217 | runner.failures.should.equal(2); 218 | }) 219 | 220 | it('should emit "fail"', function(done){ 221 | var hook = {}, err = {}; 222 | runner.on('fail', function(hook, err){ 223 | hook.should.equal(hook); 224 | err.should.equal(err); 225 | done(); 226 | }); 227 | runner.failHook(hook, err); 228 | }) 229 | 230 | it('should emit "end" if suite bail is true', function(done){ 231 | var hook = {}, err = {}; 232 | suite.bail(true); 233 | runner.on('end', done); 234 | runner.failHook(hook, err); 235 | }) 236 | 237 | it('should not emit "end" if suite bail is not true', function(done){ 238 | var hook = {}, err = {}; 239 | suite.bail(false); 240 | runner.on('end', function() { throw new Error('"end" was emit, but the bail is false'); }); 241 | runner.failHook(hook, err); 242 | done(); 243 | }) 244 | }) 245 | }) 246 | -------------------------------------------------------------------------------- /test/utils.js: -------------------------------------------------------------------------------- 1 | 2 | var mocha = require('..'); 3 | var utils = mocha.utils; 4 | var clean = utils.clean; 5 | 6 | describe('utils', function(){ 7 | describe('.clean()', function(){ 8 | it('should remove the wrapping function declaration', function(){ 9 | clean('function (one, two, three) {\n//code\n}').should.equal('//code'); 10 | }) 11 | 12 | it('should remove space character indentation from the function body', function(){ 13 | clean(' //line1\n //line2').should.equal('//line1\n //line2'); 14 | }) 15 | 16 | it('should remove tab character indentation from the function body', function(){ 17 | clean('\t//line1\n\t\t//line2').should.equal('//line1\n\t//line2'); 18 | }) 19 | }) 20 | }) 21 | --------------------------------------------------------------------------------