Line | Hits | Source |
---|---|---|
1 | ||
2 | /*! | |
3 | * mocha | |
4 | * Copyright(c) 2011 TJ Holowaychuk <tj@vision-media.ca> | |
5 | * MIT Licensed | |
6 | */ | |
7 | ||
8 | /** | |
9 | * Library version. | |
10 | */ | |
11 | ||
12 | 1 | exports.version = '0.12.1'; |
13 | ||
14 | 1 | exports.utils = require('./utils'); |
15 | 1 | exports.interfaces = require('./interfaces'); |
16 | 1 | exports.reporters = require('./reporters'); |
17 | 1 | exports.Runnable = require('./runnable'); |
18 | 1 | exports.Context = require('./context'); |
19 | 1 | exports.Runner = require('./runner'); |
20 | 1 | exports.Suite = require('./suite'); |
21 | 1 | exports.Hook = require('./hook'); |
22 | 1 | exports.Test = require('./test'); |
Line | Hits | Source |
---|---|---|
1 | ||
2 | /** | |
3 | * Module dependencies. | |
4 | */ | |
5 | ||
6 | 1 | var fs = require('fs') |
7 | , path = require('path') | |
8 | , join = path.join | |
9 | , debug = require('debug')('watch'); | |
10 | ||
11 | /** | |
12 | * Ignored directories. | |
13 | */ | |
14 | ||
15 | 1 | var ignore = ['node_modules', '.git']; |
16 | ||
17 | /** | |
18 | * Escape special characters in the given string of html. | |
19 | * | |
20 | * @param {String} html | |
21 | * @return {String} | |
22 | * @api private | |
23 | */ | |
24 | ||
25 | 1 | exports.escape = function(html) { |
26 | 0 | return String(html) |
27 | .replace(/&/g, '&') | |
28 | .replace(/"/g, '"') | |
29 | .replace(/</g, '<') | |
30 | .replace(/>/g, '>'); | |
31 | }; | |
32 | ||
33 | /** | |
34 | * Array#forEach (<=IE8) | |
35 | * | |
36 | * @param {Array} array | |
37 | * @param {Function} fn | |
38 | * @param {Object} scope | |
39 | * @api private | |
40 | */ | |
41 | ||
42 | 1 | exports.forEach = function(arr, fn, scope) { |
43 | 13 | for (var i = 0, l = arr.length; i < l; i++) |
44 | 364 | fn.call(scope, arr[i], i); |
45 | }; | |
46 | ||
47 | /** | |
48 | * Array#indexOf (<=IE8) | |
49 | * | |
50 | * @parma {Array} arr | |
51 | * @param {Object} obj to find index of | |
52 | * @param {Number} start | |
53 | * @api private | |
54 | */ | |
55 | ||
56 | 1 | exports.indexOf = function (arr, obj, start) { |
57 | 5769 | for (var i = start || 0, l = arr.length; i < l; i++) { |
58 | 93936 | if (arr[i] === obj) |
59 | 5766 | return i; |
60 | } | |
61 | 3 | return -1; |
62 | }; | |
63 | ||
64 | /** | |
65 | * Array#reduce (<=IE8) | |
66 | * | |
67 | * @param {Array} array | |
68 | * @param {Function} fn | |
69 | * @param {Object} initial value | |
70 | * @param {Object} scope | |
71 | * @api private | |
72 | */ | |
73 | ||
74 | 1 | exports.reduce = function(arr, fn, val, scope) { |
75 | 89 | var rval = val; |
76 | ||
77 | 89 | for (var i = 0, l = arr.length; i < l; i++) { |
78 | 76 | rval = fn.call(scope, rval, arr[i], i, arr); |
79 | } | |
80 | ||
81 | 89 | return rval; |
82 | }; | |
83 | ||
84 | /** | |
85 | * Array#filter (<=IE8) | |
86 | * | |
87 | * @param {Array} array | |
88 | * @param {Function} fn | |
89 | * @param {Object} scope | |
90 | * @api private | |
91 | */ | |
92 | ||
93 | 1 | exports.filter = function(arr, fn, scope) { |
94 | 183 | var ret = []; |
95 | ||
96 | 183 | for (var i = 0, l = arr.length; i < l; i++) { |
97 | 5769 | var val = arr[i]; |
98 | 5769 | if (fn.call(scope, val, i, arr)) |
99 | 3 | ret.push(val); |
100 | } | |
101 | ||
102 | 183 | return ret; |
103 | }; | |
104 | ||
105 | /** | |
106 | * Object.keys (<=IE8) | |
107 | * | |
108 | * @param {Object} obj | |
109 | * @return {Array} keys | |
110 | * @api private | |
111 | */ | |
112 | ||
113 | 1 | exports.keys = Object.keys || function(obj) { |
114 | 0 | var keys = [] |
115 | , has = Object.prototype.hasOwnProperty // for `window` on <=IE8 | |
116 | ||
117 | 0 | for (var key in obj) { |
118 | 0 | if (has.call(obj, key)) { |
119 | 0 | keys.push(key); |
120 | } | |
121 | } | |
122 | ||
123 | 0 | return keys; |
124 | }; | |
125 | ||
126 | /** | |
127 | * Watch the given `files` for changes | |
128 | * and invoke `fn(file)` on modification. | |
129 | * | |
130 | * @param {Array} files | |
131 | * @param {Function} fn | |
132 | * @api private | |
133 | */ | |
134 | ||
135 | 1 | exports.watch = function(files, fn){ |
136 | 0 | var options = { interval: 100 }; |
137 | 0 | files.forEach(function(file){ |
138 | 0 | debug('file %s', file); |
139 | 0 | fs.watchFile(file, options, function(curr, prev){ |
140 | 0 | if (prev.mtime < curr.mtime) fn(file); |
141 | }); | |
142 | }); | |
143 | }; | |
144 | ||
145 | /** | |
146 | * Ignored files. | |
147 | */ | |
148 | ||
149 | 1 | function ignored(path){ |
150 | 0 | return !~ignore.indexOf(path); |
151 | } | |
152 | ||
153 | /** | |
154 | * Lookup files in the given `dir`. | |
155 | * | |
156 | * @return {Array} | |
157 | * @api private | |
158 | */ | |
159 | ||
160 | 1 | exports.files = function(dir, ret){ |
161 | 0 | ret = ret || []; |
162 | ||
163 | 0 | fs.readdirSync(dir) |
164 | .filter(ignored) | |
165 | .forEach(function(path){ | |
166 | 0 | path = join(dir, path); |
167 | 0 | if (fs.statSync(path).isDirectory()) { |
168 | 0 | exports.files(path, ret); |
169 | 0 | } else if (path.match(/\.(js|coffee)$/)) { |
170 | 0 | ret.push(path); |
171 | } | |
172 | }); | |
173 | ||
174 | 0 | return ret; |
175 | }; |
Line | Hits | Source |
---|---|---|
1 | ||
2 | 1 | exports.bdd = require('./bdd'); |
3 | 1 | exports.tdd = require('./tdd'); |
4 | 1 | exports.qunit = require('./qunit'); |
5 | 1 | exports.exports = require('./exports'); |
Line | Hits | Source |
---|---|---|
1 | ||
2 | /** | |
3 | * Module dependencies. | |
4 | */ | |
5 | ||
6 | 1 | var Suite = require('../suite') |
7 | , Test = require('../test'); | |
8 | ||
9 | /** | |
10 | * BDD-style interface: | |
11 | * | |
12 | * describe('Array', function(){ | |
13 | * describe('#indexOf()', function(){ | |
14 | * it('should return -1 when not present', function(){ | |
15 | * | |
16 | * }); | |
17 | * | |
18 | * it('should return the index when present', function(){ | |
19 | * | |
20 | * }); | |
21 | * }); | |
22 | * }); | |
23 | * | |
24 | */ | |
25 | ||
26 | 1 | module.exports = function(suite){ |
27 | 1 | var suites = [suite]; |
28 | ||
29 | 1 | suite.on('pre-require', function(context){ |
30 | ||
31 | // noop variants | |
32 | ||
33 | 18 | context.xdescribe = function(){}; |
34 | 18 | context.xit = function(){}; |
35 | ||
36 | /** | |
37 | * Execute before running tests. | |
38 | */ | |
39 | ||
40 | 18 | context.before = function(fn){ |
41 | 5 | suites[0].beforeAll(fn); |
42 | }; | |
43 | ||
44 | /** | |
45 | * Execute after running tests. | |
46 | */ | |
47 | ||
48 | 18 | context.after = function(fn){ |
49 | 4 | suites[0].afterAll(fn); |
50 | }; | |
51 | ||
52 | /** | |
53 | * Execute before each test case. | |
54 | */ | |
55 | ||
56 | 18 | context.beforeEach = function(fn){ |
57 | 23 | suites[0].beforeEach(fn); |
58 | }; | |
59 | ||
60 | /** | |
61 | * Execute after each test case. | |
62 | */ | |
63 | ||
64 | 18 | context.afterEach = function(fn){ |
65 | 9 | suites[0].afterEach(fn); |
66 | }; | |
67 | ||
68 | /** | |
69 | * Describe a "suite" with the given `title` | |
70 | * and callback `fn` containing nested suites | |
71 | * and/or tests. | |
72 | */ | |
73 | ||
74 | 18 | context.describe = function(title, fn){ |
75 | 76 | var suite = Suite.create(suites[0], title); |
76 | 76 | suites.unshift(suite); |
77 | 76 | fn(); |
78 | 76 | suites.shift(); |
79 | }; | |
80 | ||
81 | /** | |
82 | * Describe a specification or test-case | |
83 | * with the given `title` and callback `fn` | |
84 | * acting as a thunk. | |
85 | */ | |
86 | ||
87 | 18 | context.it = function(title, fn){ |
88 | 80 | suites[0].addTest(new Test(title, fn)); |
89 | }; | |
90 | }); | |
91 | }; |
Line | Hits | Source |
---|---|---|
1 | ||
2 | /** | |
3 | * Module dependencies. | |
4 | */ | |
5 | ||
6 | 1 | var EventEmitter = require('events').EventEmitter |
7 | , debug = require('debug')('suite') | |
8 | , utils = require('./utils') | |
9 | , Hook = require('./hook'); | |
10 | ||
11 | /** | |
12 | * Expose `Suite`. | |
13 | */ | |
14 | ||
15 | 1 | exports = module.exports = Suite; |
16 | ||
17 | /** | |
18 | * Create a new `Suite` with the given `title` | |
19 | * and parent `Suite`. When a suite with the | |
20 | * same title is already present, that suite | |
21 | * is returned to provide nicer reporter | |
22 | * and more flexible meta-testing. | |
23 | * | |
24 | * @param {Suite} parent | |
25 | * @param {String} title | |
26 | * @return {Suite} | |
27 | * @api public | |
28 | */ | |
29 | ||
30 | 1 | exports.create = function(parent, title){ |
31 | 76 | var suite = new Suite(title, parent.ctx); |
32 | 76 | suite.parent = parent; |
33 | 76 | title = suite.fullTitle(); |
34 | 76 | parent.addSuite(suite); |
35 | 76 | return suite; |
36 | }; | |
37 | ||
38 | /** | |
39 | * Initialize a new `Suite` with the given | |
40 | * `title` and `ctx`. | |
41 | * | |
42 | * @param {String} title | |
43 | * @param {Context} ctx | |
44 | * @api private | |
45 | */ | |
46 | ||
47 | 1 | function Suite(title, ctx) { |
48 | 124 | this.title = title; |
49 | 124 | this.ctx = ctx; |
50 | 124 | this.suites = []; |
51 | 124 | this.tests = []; |
52 | 124 | this._beforeEach = []; |
53 | 124 | this._beforeAll = []; |
54 | 124 | this._afterEach = []; |
55 | 124 | this._afterAll = []; |
56 | 124 | this.root = !title; |
57 | 124 | this._timeout = 2000; |
58 | 124 | this._bail = false; |
59 | } | |
60 | ||
61 | /** | |
62 | * Inherit from `EventEmitter.prototype`. | |
63 | */ | |
64 | ||
65 | 1 | Suite.prototype.__proto__ = EventEmitter.prototype; |
66 | ||
67 | /** | |
68 | * Return a clone of this `Suite`. | |
69 | * | |
70 | * @return {Suite} | |
71 | * @api private | |
72 | */ | |
73 | ||
74 | 1 | Suite.prototype.clone = function(){ |
75 | 9 | var suite = new Suite(this.title); |
76 | 9 | debug('clone'); |
77 | 9 | suite.ctx = this.ctx; |
78 | 9 | suite.timeout(this.timeout()); |
79 | 9 | suite.bail(this.bail()); |
80 | 9 | return suite; |
81 | }; | |
82 | ||
83 | /** | |
84 | * Set timeout `ms` or short-hand such as "2s". | |
85 | * | |
86 | * @param {Number|String} ms | |
87 | * @return {Suite|Number} for chaining | |
88 | * @api private | |
89 | */ | |
90 | ||
91 | 1 | Suite.prototype.timeout = function(ms){ |
92 | 534 | if (0 == arguments.length) return this._timeout; |
93 | 94 | if (String(ms).match(/s$/)) ms = parseFloat(ms) * 1000; |
94 | 94 | debug('timeout %d', ms); |
95 | 94 | this._timeout = parseInt(ms, 10); |
96 | 94 | return this; |
97 | }; | |
98 | ||
99 | /** | |
100 | * Sets whether to bail after first error. | |
101 | * | |
102 | * @parma {Boolean} bail | |
103 | * @return {Suite|Number} for chaining | |
104 | * @api private | |
105 | */ | |
106 | ||
107 | 1 | Suite.prototype.bail = function(bail){ |
108 | 275 | if (0 == arguments.length) return this._bail; |
109 | 91 | debug('bail %s', bail); |
110 | 91 | this._bail = bail; |
111 | 91 | return this; |
112 | }; | |
113 | ||
114 | /** | |
115 | * Run `fn(test[, done])` before running tests. | |
116 | * | |
117 | * @param {Function} fn | |
118 | * @return {Suite} for chaining | |
119 | * @api private | |
120 | */ | |
121 | ||
122 | 1 | Suite.prototype.beforeAll = function(fn){ |
123 | 6 | var hook = new Hook('"before all" hook', fn); |
124 | 6 | hook.parent = this; |
125 | 6 | hook.timeout(this.timeout()); |
126 | 6 | hook.ctx = this.ctx; |
127 | 6 | this._beforeAll.push(hook); |
128 | 6 | this.emit('beforeAll', hook); |
129 | 6 | return this; |
130 | }; | |
131 | ||
132 | /** | |
133 | * Run `fn(test[, done])` after running tests. | |
134 | * | |
135 | * @param {Function} fn | |
136 | * @return {Suite} for chaining | |
137 | * @api private | |
138 | */ | |
139 | ||
140 | 1 | Suite.prototype.afterAll = function(fn){ |
141 | 5 | var hook = new Hook('"after all" hook', fn); |
142 | 5 | hook.parent = this; |
143 | 5 | hook.timeout(this.timeout()); |
144 | 5 | hook.ctx = this.ctx; |
145 | 5 | this._afterAll.push(hook); |
146 | 5 | this.emit('afterAll', hook); |
147 | 5 | return this; |
148 | }; | |
149 | ||
150 | /** | |
151 | * Run `fn(test[, done])` before each test case. | |
152 | * | |
153 | * @param {Function} fn | |
154 | * @return {Suite} for chaining | |
155 | * @api private | |
156 | */ | |
157 | ||
158 | 1 | Suite.prototype.beforeEach = function(fn){ |
159 | 24 | var hook = new Hook('"before each" hook', fn); |
160 | 24 | hook.parent = this; |
161 | 24 | hook.timeout(this.timeout()); |
162 | 24 | hook.ctx = this.ctx; |
163 | 24 | this._beforeEach.push(hook); |
164 | 24 | this.emit('beforeEach', hook); |
165 | 24 | return this; |
166 | }; | |
167 | ||
168 | /** | |
169 | * Run `fn(test[, done])` after each test case. | |
170 | * | |
171 | * @param {Function} fn | |
172 | * @return {Suite} for chaining | |
173 | * @api private | |
174 | */ | |
175 | ||
176 | 1 | Suite.prototype.afterEach = function(fn){ |
177 | 10 | var hook = new Hook('"after each" hook', fn); |
178 | 10 | hook.parent = this; |
179 | 10 | hook.timeout(this.timeout()); |
180 | 10 | hook.ctx = this.ctx; |
181 | 10 | this._afterEach.push(hook); |
182 | 10 | this.emit('afterEach', hook); |
183 | 10 | return this; |
184 | }; | |
185 | ||
186 | /** | |
187 | * Add a test `suite`. | |
188 | * | |
189 | * @param {Suite} suite | |
190 | * @return {Suite} for chaining | |
191 | * @api private | |
192 | */ | |
193 | ||
194 | 1 | Suite.prototype.addSuite = function(suite){ |
195 | 80 | suite.parent = this; |
196 | 80 | suite.timeout(this.timeout()); |
197 | 80 | suite.bail(this.bail()); |
198 | 80 | this.suites.push(suite); |
199 | 80 | this.emit('suite', suite); |
200 | 80 | return this; |
201 | }; | |
202 | ||
203 | /** | |
204 | * Add a `test` to this suite. | |
205 | * | |
206 | * @param {Test} test | |
207 | * @return {Suite} for chaining | |
208 | * @api private | |
209 | */ | |
210 | ||
211 | 1 | Suite.prototype.addTest = function(test){ |
212 | 82 | test.parent = this; |
213 | 82 | test.timeout(this.timeout()); |
214 | 82 | test.ctx = this.ctx; |
215 | 82 | this.tests.push(test); |
216 | 82 | this.emit('test', test); |
217 | 82 | return this; |
218 | }; | |
219 | ||
220 | /** | |
221 | * Return the full title generated by recursively | |
222 | * concatenating the parent's full title. | |
223 | * | |
224 | * @return {String} | |
225 | * @api public | |
226 | */ | |
227 | ||
228 | 1 | Suite.prototype.fullTitle = function(){ |
229 | 755 | if (this.parent) { |
230 | 520 | var full = this.parent.fullTitle(); |
231 | 808 | if (full) return full + ' ' + this.title; |
232 | } | |
233 | 467 | return this.title; |
234 | }; | |
235 | ||
236 | /** | |
237 | * Return the total number of tests. | |
238 | * | |
239 | * @return {Number} | |
240 | * @api public | |
241 | */ | |
242 | ||
243 | 1 | Suite.prototype.total = function(){ |
244 | 89 | return utils.reduce(this.suites, function(sum, suite){ |
245 | 76 | return sum + suite.total(); |
246 | }, 0) + this.tests.length; | |
247 | }; |
Line | Hits | Source |
---|---|---|
1 | ||
2 | /** | |
3 | * Module dependencies. | |
4 | */ | |
5 | ||
6 | 1 | var Runnable = require('./runnable'); |
7 | ||
8 | /** | |
9 | * Expose `Hook`. | |
10 | */ | |
11 | ||
12 | 1 | 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 | 1 | function Hook(title, fn) { |
23 | 45 | Runnable.call(this, title, fn); |
24 | 45 | this.type = 'hook'; |
25 | } | |
26 | ||
27 | /** | |
28 | * Inherit from `Runnable.prototype`. | |
29 | */ | |
30 | ||
31 | 1 | Hook.prototype.__proto__ = Runnable.prototype; |
Line | Hits | Source |
---|---|---|
1 | ||
2 | /** | |
3 | * Module dependencies. | |
4 | */ | |
5 | ||
6 | 1 | var EventEmitter = require('events').EventEmitter |
7 | , debug = require('debug')('runnable'); | |
8 | ||
9 | /** | |
10 | * Expose `Runnable`. | |
11 | */ | |
12 | ||
13 | 1 | module.exports = Runnable; |
14 | ||
15 | /** | |
16 | * Initialize a new `Runnable` with the given `title` and callback `fn`. | |
17 | * | |
18 | * @param {String} title | |
19 | * @param {Function} fn | |
20 | * @api private | |
21 | */ | |
22 | ||
23 | 1 | function Runnable(title, fn) { |
24 | 139 | this.title = title; |
25 | 139 | this.fn = fn; |
26 | 139 | this.async = fn && fn.length; |
27 | 139 | this.sync = ! this.async; |
28 | 139 | this._timeout = 2000; |
29 | 139 | this.timedOut = false; |
30 | } | |
31 | ||
32 | /** | |
33 | * Inherit from `EventEmitter.prototype`. | |
34 | */ | |
35 | ||
36 | 1 | Runnable.prototype.__proto__ = EventEmitter.prototype; |
37 | ||
38 | /** | |
39 | * Set & get timeout `ms`. | |
40 | * | |
41 | * @param {Number} ms | |
42 | * @return {Runnable|Number} ms or self | |
43 | * @api private | |
44 | */ | |
45 | ||
46 | 1 | Runnable.prototype.timeout = function(ms){ |
47 | 505 | if (0 == arguments.length) return this._timeout; |
48 | 129 | debug('timeout %d', ms); |
49 | 129 | this._timeout = ms; |
50 | 130 | if (this.timer) this.resetTimeout(); |
51 | 129 | return this; |
52 | }; | |
53 | ||
54 | /** | |
55 | * Return the full title generated by recursively | |
56 | * concatenating the parent's full title. | |
57 | * | |
58 | * @return {String} | |
59 | * @api public | |
60 | */ | |
61 | ||
62 | 1 | Runnable.prototype.fullTitle = function(){ |
63 | 80 | return this.parent.fullTitle() + ' ' + this.title; |
64 | }; | |
65 | ||
66 | /** | |
67 | * Clear the timeout. | |
68 | * | |
69 | * @api private | |
70 | */ | |
71 | ||
72 | 1 | Runnable.prototype.clearTimeout = function(){ |
73 | 52 | clearTimeout(this.timer); |
74 | }; | |
75 | ||
76 | /** | |
77 | * Reset the timeout. | |
78 | * | |
79 | * @api private | |
80 | */ | |
81 | ||
82 | 1 | Runnable.prototype.resetTimeout = function(){ |
83 | 1 | var self = this |
84 | , ms = this.timeout(); | |
85 | ||
86 | 1 | this.clearTimeout(); |
87 | 1 | if (ms) { |
88 | 1 | this.timer = setTimeout(function(){ |
89 | 0 | self.callback(new Error('timeout of ' + ms + 'ms exceeded')); |
90 | 0 | self.timedOut = true; |
91 | }, ms); | |
92 | } | |
93 | }; | |
94 | ||
95 | /** | |
96 | * Run the test and invoke `fn(err)`. | |
97 | * | |
98 | * @param {Function} fn | |
99 | * @api private | |
100 | */ | |
101 | ||
102 | 1 | Runnable.prototype.run = function(fn){ |
103 | 186 | var self = this |
104 | , ms = this.timeout() | |
105 | , start = new Date | |
106 | , ctx = this.ctx | |
107 | , finished | |
108 | , emitted; | |
109 | ||
110 | // timeout | |
111 | 186 | if (this.async) { |
112 | 51 | if (ms) { |
113 | 51 | this.timer = setTimeout(function(){ |
114 | 0 | done(new Error('timeout of ' + ms + 'ms exceeded')); |
115 | 0 | self.timedOut = true; |
116 | }, ms); | |
117 | } | |
118 | } | |
119 | ||
120 | // called multiple times | |
121 | 186 | function multiple() { |
122 | 12 | if (emitted) return; |
123 | 2 | emitted = true; |
124 | 2 | self.emit('error', new Error('done() called multiple times')); |
125 | } | |
126 | ||
127 | // finished | |
128 | 186 | function done(err) { |
129 | 58 | if (self.timedOut) return; |
130 | 65 | if (finished) return multiple(); |
131 | 51 | self.clearTimeout(); |
132 | 51 | self.duration = new Date - start; |
133 | 51 | finished = true; |
134 | 51 | fn(err); |
135 | } | |
136 | ||
137 | // for .resetTimeout() | |
138 | 186 | this.callback = done; |
139 | ||
140 | // async | |
141 | 186 | if (this.async) { |
142 | 51 | try { |
143 | 51 | this.fn.call(ctx, function(err){ |
144 | 60 | if (err instanceof Error) return done(err); |
145 | 54 | if (null != err) return done(new Error('done() invoked with non-Error: ' + err)); |
146 | 54 | done(); |
147 | }); | |
148 | } catch (err) { | |
149 | 1 | done(err); |
150 | } | |
151 | 51 | return; |
152 | } | |
153 | ||
154 | // sync | |
155 | 135 | try { |
156 | 269 | if (!this.pending) this.fn.call(ctx); |
157 | 134 | this.duration = new Date - start; |
158 | 134 | fn(); |
159 | } catch (err) { | |
160 | 1 | fn(err); |
161 | } | |
162 | }; |
Line | Hits | Source |
---|---|---|
1 | ||
2 | /** | |
3 | * Module dependencies. | |
4 | */ | |
5 | ||
6 | 1 | var Runnable = require('./runnable'); |
7 | ||
8 | /** | |
9 | * Expose `Test`. | |
10 | */ | |
11 | ||
12 | 1 | 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 | 1 | function Test(title, fn) { |
23 | 82 | Runnable.call(this, title, fn); |
24 | 82 | this.pending = !fn; |
25 | 82 | this.type = 'test'; |
26 | } | |
27 | ||
28 | /** | |
29 | * Inherit from `Runnable.prototype`. | |
30 | */ | |
31 | ||
32 | 1 | Test.prototype.__proto__ = Runnable.prototype; |
33 | ||
34 | /** | |
35 | * Inspect the context void of private properties. | |
36 | * | |
37 | * @return {String} | |
38 | * @api private | |
39 | */ | |
40 | ||
41 | 1 | Test.prototype.inspect = function(){ |
42 | 0 | return JSON.stringify(this, function(key, val){ |
43 | 0 | return '_' == key[0] |
44 | ? undefined | |
45 | : 'parent' == key | |
46 | ? '#<Suite>' | |
47 | : val; | |
48 | }, 2); | |
49 | }; |
Line | Hits | Source |
---|---|---|
1 | ||
2 | /** | |
3 | * Module dependencies. | |
4 | */ | |
5 | ||
6 | 1 | var Suite = require('../suite') |
7 | , Test = require('../test'); | |
8 | ||
9 | /** | |
10 | * TDD-style interface: | |
11 | * | |
12 | * suite('Array', function(){ | |
13 | * suite('#indexOf()', function(){ | |
14 | * suiteSetup(function(){ | |
15 | * | |
16 | * }); | |
17 | * | |
18 | * test('should return -1 when not present', function(){ | |
19 | * | |
20 | * }); | |
21 | * | |
22 | * test('should return the index when present', function(){ | |
23 | * | |
24 | * }); | |
25 | * | |
26 | * suiteTeardown(function(){ | |
27 | * | |
28 | * }); | |
29 | * }); | |
30 | * }); | |
31 | * | |
32 | */ | |
33 | ||
34 | 1 | module.exports = function(suite){ |
35 | 0 | var suites = [suite]; |
36 | ||
37 | 0 | suite.on('pre-require', function(context){ |
38 | ||
39 | /** | |
40 | * Execute before each test case. | |
41 | */ | |
42 | ||
43 | 0 | context.setup = function(fn){ |
44 | 0 | suites[0].beforeEach(fn); |
45 | }; | |
46 | ||
47 | /** | |
48 | * Execute after each test case. | |
49 | */ | |
50 | ||
51 | 0 | context.teardown = function(fn){ |
52 | 0 | suites[0].afterEach(fn); |
53 | }; | |
54 | ||
55 | /** | |
56 | * Execute before the suite. | |
57 | */ | |
58 | ||
59 | 0 | context.suiteSetup = function(fn){ |
60 | 0 | suites[0].beforeAll(fn); |
61 | }; | |
62 | ||
63 | /** | |
64 | * Execute after the suite. | |
65 | */ | |
66 | ||
67 | 0 | context.suiteTeardown = function(fn){ |
68 | 0 | suites[0].afterAll(fn); |
69 | }; | |
70 | ||
71 | /** | |
72 | * Describe a "suite" with the given `title` | |
73 | * and callback `fn` containing nested suites | |
74 | * and/or tests. | |
75 | */ | |
76 | ||
77 | 0 | context.suite = function(title, fn){ |
78 | 0 | var suite = Suite.create(suites[0], title); |
79 | 0 | suites.unshift(suite); |
80 | 0 | fn(); |
81 | 0 | suites.shift(); |
82 | }; | |
83 | ||
84 | /** | |
85 | * Describe a specification or test-case | |
86 | * with the given `title` and callback `fn` | |
87 | * acting as a thunk. | |
88 | */ | |
89 | ||
90 | 0 | context.test = function(title, fn){ |
91 | 0 | suites[0].addTest(new Test(title, fn)); |
92 | }; | |
93 | }); | |
94 | }; |
Line | Hits | Source |
---|---|---|
1 | ||
2 | /** | |
3 | * Module dependencies. | |
4 | */ | |
5 | ||
6 | 1 | var Suite = require('../suite') |
7 | , Test = require('../test'); | |
8 | ||
9 | /** | |
10 | * QUnit-style interface: | |
11 | * | |
12 | * suite('Array'); | |
13 | * | |
14 | * test('#length', function(){ | |
15 | * var arr = [1,2,3]; | |
16 | * ok(arr.length == 3); | |
17 | * }); | |
18 | * | |
19 | * test('#indexOf()', function(){ | |
20 | * var arr = [1,2,3]; | |
21 | * ok(arr.indexOf(1) == 0); | |
22 | * ok(arr.indexOf(2) == 1); | |
23 | * ok(arr.indexOf(3) == 2); | |
24 | * }); | |
25 | * | |
26 | * suite('String'); | |
27 | * | |
28 | * test('#length', function(){ | |
29 | * ok('foo'.length == 3); | |
30 | * }); | |
31 | * | |
32 | */ | |
33 | ||
34 | 1 | module.exports = function(suite){ |
35 | 0 | var suites = [suite]; |
36 | ||
37 | 0 | suite.on('pre-require', function(context){ |
38 | ||
39 | /** | |
40 | * Execute before running tests. | |
41 | */ | |
42 | ||
43 | 0 | context.before = function(fn){ |
44 | 0 | suites[0].beforeAll(fn); |
45 | }; | |
46 | ||
47 | /** | |
48 | * Execute after running tests. | |
49 | */ | |
50 | ||
51 | 0 | context.after = function(fn){ |
52 | 0 | suites[0].afterAll(fn); |
53 | }; | |
54 | ||
55 | /** | |
56 | * Execute before each test case. | |
57 | */ | |
58 | ||
59 | 0 | context.beforeEach = function(fn){ |
60 | 0 | suites[0].beforeEach(fn); |
61 | }; | |
62 | ||
63 | /** | |
64 | * Execute after each test case. | |
65 | */ | |
66 | ||
67 | 0 | context.afterEach = function(fn){ |
68 | 0 | suites[0].afterEach(fn); |
69 | }; | |
70 | ||
71 | /** | |
72 | * Describe a "suite" with the given `title`. | |
73 | */ | |
74 | ||
75 | 0 | context.suite = function(title){ |
76 | 0 | if (suites.length > 1) suites.shift(); |
77 | 0 | var suite = Suite.create(suites[0], title); |
78 | 0 | suites.unshift(suite); |
79 | }; | |
80 | ||
81 | /** | |
82 | * Describe a specification or test-case | |
83 | * with the given `title` and callback `fn` | |
84 | * acting as a thunk. | |
85 | */ | |
86 | ||
87 | 0 | context.test = function(title, fn){ |
88 | 0 | suites[0].addTest(new Test(title, fn)); |
89 | }; | |
90 | }); | |
91 | }; |
Line | Hits | Source |
---|---|---|
1 | ||
2 | /** | |
3 | * Module dependencies. | |
4 | */ | |
5 | ||
6 | 1 | 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 | 1 | module.exports = function(suite){ |
27 | 0 | var suites = [suite]; |
28 | ||
29 | 0 | suite.on('require', visit); |
30 | ||
31 | 0 | function visit(obj) { |
32 | 0 | var suite; |
33 | 0 | for (var key in obj) { |
34 | 0 | if ('function' == typeof obj[key]) { |
35 | 0 | var fn = obj[key]; |
36 | 0 | switch (key) { |
37 | case 'before': | |
38 | 0 | suites[0].beforeAll(fn); |
39 | 0 | break; |
40 | case 'after': | |
41 | 0 | suites[0].afterAll(fn); |
42 | 0 | break; |
43 | case 'beforeEach': | |
44 | 0 | suites[0].beforeEach(fn); |
45 | 0 | break; |
46 | case 'afterEach': | |
47 | 0 | suites[0].afterEach(fn); |
48 | 0 | break; |
49 | default: | |
50 | 0 | suites[0].addTest(new Test(key, fn)); |
51 | } | |
52 | } else { | |
53 | 0 | var suite = Suite.create(suites[0], key); |
54 | 0 | suites.unshift(suite); |
55 | 0 | visit(obj[key]); |
56 | 0 | suites.shift(); |
57 | } | |
58 | } | |
59 | } | |
60 | }; |
Line | Hits | Source |
---|---|---|
1 | ||
2 | 1 | exports.Base = require('./base'); |
3 | 1 | exports.Dot = require('./dot'); |
4 | 1 | exports.Doc = require('./doc'); |
5 | 1 | exports.TAP = require('./tap'); |
6 | 1 | exports.JSON = require('./json'); |
7 | 1 | exports.HTML = require('./html'); |
8 | 1 | exports.List = require('./list'); |
9 | 1 | exports.Spec = require('./spec'); |
10 | 1 | exports.Progress = require('./progress'); |
11 | 1 | exports.Landing = require('./landing'); |
12 | 1 | exports.JSONCov = require('./json-cov'); |
13 | 1 | exports.HTMLCov = require('./html-cov'); |
14 | 1 | exports.JSONStream = require('./json-stream'); |
15 | 1 | exports.XUnit = require('./xunit') |
Line | Hits | Source |
---|---|---|
1 | ||
2 | /** | |
3 | * Module dependencies. | |
4 | */ | |
5 | ||
6 | 1 | var tty = require('tty'); |
7 | ||
8 | /** | |
9 | * Check if both stdio streams are associated with a tty. | |
10 | */ | |
11 | ||
12 | 1 | var isatty = tty.isatty(1) && tty.isatty(2); |
13 | ||
14 | /** | |
15 | * Expose `Base`. | |
16 | */ | |
17 | ||
18 | 1 | exports = module.exports = Base; |
19 | ||
20 | /** | |
21 | * Enable coloring by default. | |
22 | */ | |
23 | ||
24 | 1 | exports.useColors = isatty; |
25 | ||
26 | /** | |
27 | * Default color map. | |
28 | */ | |
29 | ||
30 | 1 | exports.colors = { |
31 | 'pass': 90 | |
32 | , 'fail': 31 | |
33 | , 'bright pass': 92 | |
34 | , 'bright fail': 91 | |
35 | , 'bright yellow': 93 | |
36 | , 'pending': 36 | |
37 | , 'suite': 0 | |
38 | , 'error title': 0 | |
39 | , 'error message': 31 | |
40 | , 'error stack': 90 | |
41 | , 'checkmark': 32 | |
42 | , 'fast': 90 | |
43 | , 'medium': 33 | |
44 | , 'slow': 31 | |
45 | , 'green': 32 | |
46 | , 'light': 90 | |
47 | }; | |
48 | ||
49 | /** | |
50 | * Color `str` with the given `type`, | |
51 | * allowing colors to be disabled, | |
52 | * as well as user-defined color | |
53 | * schemes. | |
54 | * | |
55 | * @param {String} type | |
56 | * @param {String} str | |
57 | * @return {String} | |
58 | * @api private | |
59 | */ | |
60 | ||
61 | 1 | var color = exports.color = function(type, str) { |
62 | 0 | if (!exports.useColors) return str; |
63 | 0 | return '\033[' + exports.colors[type] + 'm' + str + '\033[0m'; |
64 | }; | |
65 | ||
66 | /** | |
67 | * Expose term window size, with some | |
68 | * defaults for when stderr is not a tty. | |
69 | */ | |
70 | ||
71 | 1 | exports.window = { |
72 | width: isatty | |
73 | ? process.stdout.getWindowSize | |
74 | ? process.stdout.getWindowSize(1)[0] | |
75 | : tty.getWindowSize()[1] | |
76 | : 75 | |
77 | }; | |
78 | ||
79 | /** | |
80 | * Expose some basic cursor interactions | |
81 | * that are common among reporters. | |
82 | */ | |
83 | ||
84 | 1 | exports.cursor = { |
85 | hide: function(){ | |
86 | 0 | process.stdout.write('\033[?25l'); |
87 | }, | |
88 | ||
89 | show: function(){ | |
90 | 0 | process.stdout.write('\033[?25h'); |
91 | }, | |
92 | ||
93 | deleteLine: function(){ | |
94 | 0 | process.stdout.write('\033[2K'); |
95 | }, | |
96 | ||
97 | beginningOfLine: function(){ | |
98 | 0 | process.stdout.write('\033[0G'); |
99 | }, | |
100 | ||
101 | CR: function(){ | |
102 | 0 | exports.cursor.deleteLine(); |
103 | 0 | exports.cursor.beginningOfLine(); |
104 | } | |
105 | }; | |
106 | ||
107 | /** | |
108 | * A test is considered slow if it | |
109 | * exceeds the following value in milliseconds. | |
110 | */ | |
111 | ||
112 | 1 | exports.slow = 75; |
113 | ||
114 | /** | |
115 | * Outut the given `failures` as a list. | |
116 | * | |
117 | * @param {Array} failures | |
118 | * @api public | |
119 | */ | |
120 | ||
121 | 1 | exports.list = function(failures){ |
122 | 0 | console.error(); |
123 | 0 | failures.forEach(function(test, i){ |
124 | // format | |
125 | 0 | var fmt = color('error title', ' %s) %s:\n') |
126 | + color('error message', ' %s') | |
127 | + color('error stack', '\n%s\n'); | |
128 | ||
129 | // msg | |
130 | 0 | var err = test.err |
131 | , message = err.message || '' | |
132 | , stack = err.stack || message | |
133 | , index = stack.indexOf(message) + message.length | |
134 | , msg = stack.slice(0, index); | |
135 | ||
136 | // indent stack trace without msg | |
137 | 0 | stack = stack.slice(index + 1) |
138 | .replace(/^/gm, ' '); | |
139 | ||
140 | 0 | console.error(fmt, (i + 1), test.fullTitle(), msg, stack); |
141 | }); | |
142 | }; | |
143 | ||
144 | /** | |
145 | * Initialize a new `Base` reporter. | |
146 | * | |
147 | * All other reporters generally | |
148 | * inherit from this reporter, providing | |
149 | * stats such as test duration, number | |
150 | * of tests passed / failed etc. | |
151 | * | |
152 | * @param {Runner} runner | |
153 | * @api public | |
154 | */ | |
155 | ||
156 | 1 | function Base(runner) { |
157 | 0 | var self = this |
158 | , stats = this.stats = { suites: 0, tests: 0, passes: 0, failures: 0 } | |
159 | , failures = this.failures = []; | |
160 | ||
161 | 0 | if (!runner) return; |
162 | 0 | this.runner = runner; |
163 | ||
164 | 0 | runner.on('start', function(){ |
165 | 0 | stats.start = new Date; |
166 | }); | |
167 | ||
168 | 0 | runner.on('suite', function(suite){ |
169 | 0 | stats.suites = stats.suites || 0; |
170 | 0 | suite.root || stats.suites++; |
171 | }); | |
172 | ||
173 | 0 | runner.on('test end', function(test){ |
174 | 0 | stats.tests = stats.tests || 0; |
175 | 0 | stats.tests++; |
176 | }); | |
177 | ||
178 | 0 | runner.on('pass', function(test){ |
179 | 0 | stats.passes = stats.passes || 0; |
180 | ||
181 | 0 | var medium = exports.slow / 2; |
182 | 0 | test.speed = test.duration > exports.slow |
183 | ? 'slow' | |
184 | : test.duration > medium | |
185 | ? 'medium' | |
186 | : 'fast'; | |
187 | ||
188 | 0 | stats.passes++; |
189 | }); | |
190 | ||
191 | 0 | runner.on('fail', function(test, err){ |
192 | 0 | stats.failures = stats.failures || 0; |
193 | 0 | stats.failures++; |
194 | 0 | test.err = err; |
195 | 0 | failures.push(test); |
196 | }); | |
197 | ||
198 | 0 | runner.on('end', function(){ |
199 | 0 | stats.end = new Date; |
200 | 0 | stats.duration = new Date - stats.start; |
201 | }); | |
202 | } | |
203 | ||
204 | /** | |
205 | * Output common epilogue used by many of | |
206 | * the bundled reporters. | |
207 | * | |
208 | * @api public | |
209 | */ | |
210 | ||
211 | 1 | Base.prototype.epilogue = function(){ |
212 | 0 | var stats = this.stats |
213 | , fmt; | |
214 | ||
215 | 0 | console.log(); |
216 | ||
217 | // failure | |
218 | 0 | if (stats.failures) { |
219 | 0 | fmt = color('bright fail', ' ✖') |
220 | + color('fail', ' %d of %d tests failed') | |
221 | + color('light', ':') | |
222 | ||
223 | 0 | console.error(fmt, stats.failures, this.runner.total); |
224 | 0 | Base.list(this.failures); |
225 | 0 | console.error(); |
226 | 0 | return; |
227 | } | |
228 | ||
229 | // pass | |
230 | 0 | fmt = color('bright pass', ' ✔') |
231 | + color('green', ' %d tests complete') | |
232 | + color('light', ' (%dms)'); | |
233 | ||
234 | 0 | console.log(fmt, stats.tests || 0, stats.duration); |
235 | 0 | console.log(); |
236 | }; |
Line | Hits | Source |
---|---|---|
1 | ||
2 | /** | |
3 | * Module dependencies. | |
4 | */ | |
5 | ||
6 | 1 | var Base = require('./base') |
7 | , color = Base.color; | |
8 | ||
9 | /** | |
10 | * Expose `Dot`. | |
11 | */ | |
12 | ||
13 | 1 | 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 | 1 | function Dot(runner) { |
23 | 0 | Base.call(this, runner); |
24 | ||
25 | 0 | var self = this |
26 | , stats = this.stats | |
27 | , width = Base.window.width * .75 | 0 | |
28 | , n = 0; | |
29 | ||
30 | 0 | runner.on('start', function(){ |
31 | 0 | process.stdout.write('\n '); |
32 | }); | |
33 | ||
34 | 0 | runner.on('pending', function(test){ |
35 | 0 | process.stdout.write(color('pending', '.')); |
36 | }); | |
37 | ||
38 | 0 | runner.on('pass', function(test){ |
39 | 0 | if (++n % width == 0) process.stdout.write('\n '); |
40 | 0 | if ('slow' == test.speed) { |
41 | 0 | process.stdout.write(color('bright yellow', '.')); |
42 | } else { | |
43 | 0 | process.stdout.write(color(test.speed, '.')); |
44 | } | |
45 | }); | |
46 | ||
47 | 0 | runner.on('fail', function(test, err){ |
48 | 0 | if (++n % width == 0) process.stdout.write('\n '); |
49 | 0 | process.stdout.write(color('fail', '.')); |
50 | }); | |
51 | ||
52 | 0 | runner.on('end', function(){ |
53 | 0 | console.log(); |
54 | 0 | self.epilogue(); |
55 | }); | |
56 | } | |
57 | ||
58 | /** | |
59 | * Inherit from `Base.prototype`. | |
60 | */ | |
61 | ||
62 | 1 | Dot.prototype.__proto__ = Base.prototype; |
Line | Hits | Source |
---|---|---|
1 | ||
2 | /** | |
3 | * Module dependencies. | |
4 | */ | |
5 | ||
6 | 1 | var Base = require('./base') |
7 | , utils = require('../utils'); | |
8 | ||
9 | /** | |
10 | * Expose `Doc`. | |
11 | */ | |
12 | ||
13 | 1 | exports = module.exports = Doc; |
14 | ||
15 | /** | |
16 | * Initialize a new `Doc` reporter. | |
17 | * | |
18 | * @param {Runner} runner | |
19 | * @api public | |
20 | */ | |
21 | ||
22 | 1 | function Doc(runner) { |
23 | 0 | Base.call(this, runner); |
24 | ||
25 | 0 | var self = this |
26 | , stats = this.stats | |
27 | , total = runner.total | |
28 | , indents = 2; | |
29 | ||
30 | 0 | function indent() { |
31 | 0 | return Array(indents).join(' '); |
32 | } | |
33 | ||
34 | 0 | runner.on('suite', function(suite){ |
35 | 0 | if (suite.root) return; |
36 | 0 | ++indents; |
37 | 0 | console.log('%s<section class="suite">', indent()); |
38 | 0 | ++indents; |
39 | 0 | console.log('%s<h1>%s</h1>', indent(), suite.title); |
40 | 0 | console.log('%s<dl>', indent()); |
41 | }); | |
42 | ||
43 | 0 | runner.on('suite end', function(suite){ |
44 | 0 | if (suite.root) return; |
45 | 0 | console.log('%s</dl>', indent()); |
46 | 0 | --indents; |
47 | 0 | console.log('%s</section>', indent()); |
48 | 0 | --indents; |
49 | }); | |
50 | ||
51 | 0 | runner.on('pass', function(test){ |
52 | 0 | console.log('%s <dt>%s</dt>', indent(), test.title); |
53 | 0 | var code = utils.escape(clean(test.fn.toString())); |
54 | 0 | console.log('%s <dd><pre><code>%s</code></pre></dd>', indent(), code); |
55 | }); | |
56 | } | |
57 | ||
58 | /** | |
59 | * Strip the function definition from `str`, | |
60 | * and re-indent for pre whitespace. | |
61 | */ | |
62 | ||
63 | 1 | function clean(str) { |
64 | 0 | str = str |
65 | .replace(/^function *\(.*\) *{/, '') | |
66 | .replace(/\s+\}$/, ''); | |
67 | ||
68 | 0 | var spaces = str.match(/^\n?( *)/)[1].length |
69 | , re = new RegExp('^ {' + spaces + '}', 'gm'); | |
70 | ||
71 | 0 | str = str.replace(re, ''); |
72 | ||
73 | 0 | return str; |
74 | } |
Line | Hits | Source |
---|---|---|
1 | ||
2 | /** | |
3 | * Module dependencies. | |
4 | */ | |
5 | ||
6 | 1 | var Base = require('./base') |
7 | , cursor = Base.cursor | |
8 | , color = Base.color; | |
9 | ||
10 | /** | |
11 | * Expose `TAP`. | |
12 | */ | |
13 | ||
14 | 1 | exports = module.exports = TAP; |
15 | ||
16 | /** | |
17 | * Initialize a new `TAP` reporter. | |
18 | * | |
19 | * @param {Runner} runner | |
20 | * @api public | |
21 | */ | |
22 | ||
23 | 1 | function TAP(runner) { |
24 | 0 | Base.call(this, runner); |
25 | ||
26 | 0 | var self = this |
27 | , stats = this.stats | |
28 | , total = runner.total | |
29 | , n = 1; | |
30 | ||
31 | 0 | runner.on('start', function(){ |
32 | 0 | console.log('%d..%d', 1, total); |
33 | }); | |
34 | ||
35 | 0 | runner.on('test end', function(){ |
36 | 0 | ++n; |
37 | }); | |
38 | ||
39 | 0 | runner.on('pending', function(test){ |
40 | 0 | console.log('ok %d %s # SKIP -', n, title(test)); |
41 | }); | |
42 | ||
43 | 0 | runner.on('pass', function(test){ |
44 | 0 | console.log('ok %d %s', n, title(test)); |
45 | }); | |
46 | ||
47 | 0 | runner.on('fail', function(test, err){ |
48 | 0 | console.log('not ok %d %s', n, title(test)); |
49 | 0 | console.log(err.stack.replace(/^/gm, ' ')); |
50 | }); | |
51 | } | |
52 | ||
53 | /** | |
54 | * Return a TAP-safe title of `test` | |
55 | * | |
56 | * @param {Object} test | |
57 | * @return {String} | |
58 | * @api private | |
59 | */ | |
60 | ||
61 | 1 | function title(test) { |
62 | 0 | return test.fullTitle().replace(/#/g, ''); |
63 | } |
Line | Hits | Source |
---|---|---|
1 | ||
2 | /** | |
3 | * Module dependencies. | |
4 | */ | |
5 | ||
6 | 1 | var Base = require('./base') |
7 | , cursor = Base.cursor | |
8 | , color = Base.color; | |
9 | ||
10 | /** | |
11 | * Expose `JSON`. | |
12 | */ | |
13 | ||
14 | 1 | exports = module.exports = JSONReporter; |
15 | ||
16 | /** | |
17 | * Initialize a new `JSON` reporter. | |
18 | * | |
19 | * @param {Runner} runner | |
20 | * @api public | |
21 | */ | |
22 | ||
23 | 1 | function JSONReporter(runner) { |
24 | 0 | var self = this; |
25 | 0 | Base.call(this, runner); |
26 | ||
27 | 0 | var tests = [] |
28 | , failures = [] | |
29 | , passes = []; | |
30 | ||
31 | 0 | runner.on('test end', function(test){ |
32 | 0 | tests.push(test); |
33 | }); | |
34 | ||
35 | 0 | runner.on('pass', function(test){ |
36 | 0 | passes.push(test); |
37 | }); | |
38 | ||
39 | 0 | runner.on('fail', function(test){ |
40 | 0 | failures.push(test); |
41 | }); | |
42 | ||
43 | 0 | runner.on('end', function(){ |
44 | 0 | var obj = { |
45 | stats: self.stats | |
46 | , tests: tests.map(clean) | |
47 | , failures: failures.map(clean) | |
48 | , passes: passes.map(clean) | |
49 | }; | |
50 | ||
51 | 0 | 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 | 1 | function clean(test) { |
65 | 0 | return { |
66 | title: test.title | |
67 | , fullTitle: test.fullTitle() | |
68 | , duration: test.duration | |
69 | } | |
70 | } |
Line | Hits | Source |
---|---|---|
1 | ||
2 | /** | |
3 | * Module dependencies. | |
4 | */ | |
5 | ||
6 | 1 | var Base = require('./base') |
7 | , utils = require('../utils') | |
8 | , Progress = require('../browser/progress') | |
9 | , escape = utils.escape; | |
10 | ||
11 | /** | |
12 | * Expose `Doc`. | |
13 | */ | |
14 | ||
15 | 1 | exports = module.exports = HTML; |
16 | ||
17 | /** | |
18 | * Stats template. | |
19 | */ | |
20 | ||
21 | 1 | var statsTemplate = '<ul id="stats">' |
22 | + '<li class="progress"><canvas width="40" height="40"></canvas></li>' | |
23 | + '<li class="passes">passes: <em>0</em></li>' | |
24 | + '<li class="failures">failures: <em>0</em></li>' | |
25 | + '<li class="duration">duration: <em>0</em>s</li>' | |
26 | + '</ul>'; | |
27 | ||
28 | /** | |
29 | * Initialize a new `Doc` reporter. | |
30 | * | |
31 | * @param {Runner} runner | |
32 | * @api public | |
33 | */ | |
34 | ||
35 | 1 | function HTML(runner) { |
36 | 0 | Base.call(this, runner); |
37 | ||
38 | // TODO: clean up | |
39 | ||
40 | 0 | var self = this |
41 | , stats = this.stats | |
42 | , total = runner.total | |
43 | , root = $('#mocha') | |
44 | , stack = [root] | |
45 | , stat = $(statsTemplate).appendTo(root) | |
46 | , canvas = stat.find('canvas').get(0) | |
47 | , progress | |
48 | , ctx | |
49 | ||
50 | 0 | if (canvas.getContext) { |
51 | 0 | ctx = canvas.getContext('2d'); |
52 | 0 | progress = new Progress; |
53 | } | |
54 | ||
55 | 0 | if (!root.length) return error('#mocha div missing, add it to your document'); |
56 | ||
57 | 0 | if (progress) progress.size(40); |
58 | ||
59 | 0 | runner.on('suite', function(suite){ |
60 | 0 | if (suite.root) return; |
61 | ||
62 | // suite | |
63 | 0 | var el = $('<div class="suite"><h1>' + suite.title + '</h1></div>'); |
64 | ||
65 | // container | |
66 | 0 | stack[0].append(el); |
67 | 0 | stack.unshift($('<div>')); |
68 | 0 | el.append(stack[0]); |
69 | }); | |
70 | ||
71 | 0 | runner.on('suite end', function(suite){ |
72 | 0 | if (suite.root) return; |
73 | 0 | stack.shift(); |
74 | }); | |
75 | ||
76 | 0 | runner.on('fail', function(test, err){ |
77 | 0 | if (err.uncaught) runner.emit('test end', test); |
78 | }); | |
79 | ||
80 | 0 | runner.on('test end', function(test){ |
81 | // TODO: add to stats | |
82 | 0 | var percent = stats.tests / total * 100 | 0; |
83 | ||
84 | 0 | if (progress) { |
85 | 0 | progress.update(percent).draw(ctx); |
86 | } | |
87 | ||
88 | // update stats | |
89 | 0 | var ms = new Date - stats.start; |
90 | 0 | stat.find('.passes em').text(stats.passes); |
91 | 0 | stat.find('.failures em').text(stats.failures); |
92 | 0 | stat.find('.duration em').text((ms / 1000).toFixed(2)); |
93 | ||
94 | // test | |
95 | 0 | if ('passed' == test.state) { |
96 | 0 | var el = $('<div class="test pass"><h2>' + escape(test.title) + '</h2></div>') |
97 | 0 | } else if (test.pending) { |
98 | 0 | var el = $('<div class="test pass pending"><h2>' + escape(test.title) + '</h2></div>') |
99 | } else { | |
100 | 0 | var el = $('<div class="test fail"><h2>' + escape(test.title) + '</h2></div>'); |
101 | 0 | var str = test.err.stack || test.err; |
102 | ||
103 | // <=IE7 stringifies to [Object Error]. Since it can be overloaded, we | |
104 | // check for the result of the stringifying. | |
105 | 0 | if ('[object Error]' == str) str = test.err.message; |
106 | ||
107 | 0 | $('<pre class="error">' + escape(str) + '</pre>').appendTo(el); |
108 | } | |
109 | ||
110 | // toggle code | |
111 | 0 | el.find('h2').toggle(function(){ |
112 | 0 | pre && pre.slideDown('fast'); |
113 | }, function(){ | |
114 | 0 | pre && pre.slideUp('fast'); |
115 | }); | |
116 | ||
117 | // code | |
118 | // TODO: defer | |
119 | 0 | if (!test.pending) { |
120 | 0 | var code = escape(clean(test.fn.toString())); |
121 | 0 | var pre = $('<pre><code>' + code + '</code></pre>'); |
122 | 0 | pre.appendTo(el).hide(); |
123 | } | |
124 | 0 | stack[0].append(el); |
125 | }); | |
126 | } | |
127 | ||
128 | /** | |
129 | * Display error `msg`. | |
130 | */ | |
131 | ||
132 | 1 | function error(msg) { |
133 | 0 | $('<div id="error">' + msg + '</div>').appendTo('body'); |
134 | } | |
135 | ||
136 | /** | |
137 | * Strip the function definition from `str`, | |
138 | * and re-indent for pre whitespace. | |
139 | */ | |
140 | ||
141 | 1 | function clean(str) { |
142 | 0 | str = str |
143 | .replace(/^function *\(.*\) *{/, '') | |
144 | .replace(/\s+\}$/, ''); | |
145 | ||
146 | 0 | var spaces = str.match(/^\n?( *)/)[1].length |
147 | , re = new RegExp('^ {' + spaces + '}', 'gm'); | |
148 | ||
149 | 0 | str = str |
150 | .replace(re, '') | |
151 | .replace(/^\s+/, ''); | |
152 | ||
153 | 0 | return str; |
154 | } |
Line | Hits | Source |
---|---|---|
1 | ||
2 | /** | |
3 | * Expose `Progress`. | |
4 | */ | |
5 | ||
6 | 1 | module.exports = Progress; |
7 | ||
8 | /** | |
9 | * Initialize a new `Progress` indicator. | |
10 | */ | |
11 | ||
12 | 1 | function Progress() { |
13 | 0 | this.percent = 0; |
14 | 0 | this.size(0); |
15 | 0 | this.fontSize(11); |
16 | 0 | this.font('helvetica, arial, sans-serif'); |
17 | } | |
18 | ||
19 | /** | |
20 | * Set progress size to `n`. | |
21 | * | |
22 | * @param {Number} n | |
23 | * @return {Progress} for chaining | |
24 | * @api public | |
25 | */ | |
26 | ||
27 | 1 | Progress.prototype.size = function(n){ |
28 | 0 | this._size = n; |
29 | 0 | return this; |
30 | }; | |
31 | ||
32 | /** | |
33 | * Set text to `str`. | |
34 | * | |
35 | * @param {String} str | |
36 | * @return {Progress} for chaining | |
37 | * @api public | |
38 | */ | |
39 | ||
40 | 1 | Progress.prototype.text = function(str){ |
41 | 0 | this._text = str; |
42 | 0 | return this; |
43 | }; | |
44 | ||
45 | /** | |
46 | * Set font size to `n`. | |
47 | * | |
48 | * @param {Number} n | |
49 | * @return {Progress} for chaining | |
50 | * @api public | |
51 | */ | |
52 | ||
53 | 1 | Progress.prototype.fontSize = function(n){ |
54 | 0 | this._fontSize = n; |
55 | 0 | return this; |
56 | }; | |
57 | ||
58 | /** | |
59 | * Set font `family`. | |
60 | * | |
61 | * @param {String} family | |
62 | * @return {Progress} for chaining | |
63 | */ | |
64 | ||
65 | 1 | Progress.prototype.font = function(family){ |
66 | 0 | this._font = family; |
67 | 0 | return this; |
68 | }; | |
69 | ||
70 | /** | |
71 | * Update percentage to `n`. | |
72 | * | |
73 | * @param {Number} n | |
74 | * @return {Progress} for chaining | |
75 | */ | |
76 | ||
77 | 1 | Progress.prototype.update = function(n){ |
78 | 0 | this.percent = n; |
79 | 0 | return this; |
80 | }; | |
81 | ||
82 | /** | |
83 | * Draw on `ctx`. | |
84 | * | |
85 | * @param {CanvasRenderingContext2d} ctx | |
86 | * @return {Progress} for chaining | |
87 | */ | |
88 | ||
89 | 1 | Progress.prototype.draw = function(ctx){ |
90 | 0 | 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 | 0 | ctx.font = fontSize + 'px ' + this._font; |
99 | ||
100 | 0 | var angle = Math.PI * 2 * (percent / 100); |
101 | 0 | ctx.clearRect(0, 0, size, size); |
102 | ||
103 | // outer circle | |
104 | 0 | ctx.strokeStyle = '#9f9f9f'; |
105 | 0 | ctx.beginPath(); |
106 | 0 | ctx.arc(x, y, rad, 0, angle, false); |
107 | 0 | ctx.stroke(); |
108 | ||
109 | // inner circle | |
110 | 0 | ctx.strokeStyle = '#eee'; |
111 | 0 | ctx.beginPath(); |
112 | 0 | ctx.arc(x, y, rad - 1, 0, angle, true); |
113 | 0 | ctx.stroke(); |
114 | ||
115 | // text | |
116 | 0 | var text = this._text || (percent | 0) + '%' |
117 | , w = ctx.measureText(text).width; | |
118 | ||
119 | 0 | ctx.fillText( |
120 | text | |
121 | , x - w / 2 + 1 | |
122 | , y + fontSize / 2 - 1); | |
123 | ||
124 | 0 | return this; |
125 | }; |
Line | Hits | Source |
---|---|---|
1 | ||
2 | /** | |
3 | * Module dependencies. | |
4 | */ | |
5 | ||
6 | 1 | var Base = require('./base') |
7 | , cursor = Base.cursor | |
8 | , color = Base.color; | |
9 | ||
10 | /** | |
11 | * Expose `List`. | |
12 | */ | |
13 | ||
14 | 1 | exports = module.exports = List; |
15 | ||
16 | /** | |
17 | * Initialize a new `List` test reporter. | |
18 | * | |
19 | * @param {Runner} runner | |
20 | * @api public | |
21 | */ | |
22 | ||
23 | 1 | function List(runner) { |
24 | 0 | Base.call(this, runner); |
25 | ||
26 | 0 | var self = this |
27 | , stats = this.stats | |
28 | , n = 0; | |
29 | ||
30 | 0 | runner.on('start', function(){ |
31 | 0 | console.log(); |
32 | }); | |
33 | ||
34 | 0 | runner.on('test', function(test){ |
35 | 0 | process.stdout.write(color('pass', ' ' + test.fullTitle() + ': ')); |
36 | }); | |
37 | ||
38 | 0 | runner.on('pending', function(test){ |
39 | 0 | var fmt = color('checkmark', ' -') |
40 | + color('pending', ' %s'); | |
41 | 0 | console.log(fmt, test.fullTitle()); |
42 | }); | |
43 | ||
44 | 0 | runner.on('pass', function(test){ |
45 | 0 | var fmt = color('checkmark', ' ✓') |
46 | + color('pass', ' %s: ') | |
47 | + color(test.speed, '%dms'); | |
48 | 0 | cursor.CR(); |
49 | 0 | console.log(fmt, test.fullTitle(), test.duration); |
50 | }); | |
51 | ||
52 | 0 | runner.on('fail', function(test, err){ |
53 | 0 | cursor.CR(); |
54 | 0 | console.log(color('fail', ' %d) %s'), ++n, test.fullTitle()); |
55 | }); | |
56 | ||
57 | 0 | runner.on('end', self.epilogue.bind(self)); |
58 | } | |
59 | ||
60 | /** | |
61 | * Inherit from `Base.prototype`. | |
62 | */ | |
63 | ||
64 | 1 | List.prototype.__proto__ = Base.prototype; |
Line | Hits | Source |
---|---|---|
1 | ||
2 | /** | |
3 | * Module dependencies. | |
4 | */ | |
5 | ||
6 | 1 | var Base = require('./base') |
7 | , cursor = Base.cursor | |
8 | , color = Base.color; | |
9 | ||
10 | /** | |
11 | * Expose `Spec`. | |
12 | */ | |
13 | ||
14 | 1 | exports = module.exports = Spec; |
15 | ||
16 | /** | |
17 | * Initialize a new `Spec` test reporter. | |
18 | * | |
19 | * @param {Runner} runner | |
20 | * @api public | |
21 | */ | |
22 | ||
23 | 1 | function Spec(runner) { |
24 | 0 | Base.call(this, runner); |
25 | ||
26 | 0 | var self = this |
27 | , stats = this.stats | |
28 | , indents = 0 | |
29 | , n = 0; | |
30 | ||
31 | 0 | function indent() { |
32 | 0 | return Array(indents).join(' ') |
33 | } | |
34 | ||
35 | 0 | runner.on('start', function(){ |
36 | 0 | console.log(); |
37 | }); | |
38 | ||
39 | 0 | runner.on('suite', function(suite){ |
40 | 0 | ++indents; |
41 | 0 | console.log(color('suite', '%s%s'), indent(), suite.title); |
42 | }); | |
43 | ||
44 | 0 | runner.on('suite end', function(suite){ |
45 | 0 | --indents; |
46 | 0 | if (1 == indents) console.log(); |
47 | }); | |
48 | ||
49 | 0 | runner.on('test', function(test){ |
50 | 0 | process.stdout.write(indent() + color('pass', ' â—¦ ' + test.title + ': ')); |
51 | }); | |
52 | ||
53 | 0 | runner.on('pending', function(test){ |
54 | 0 | var fmt = indent() + color('pending', ' - %s'); |
55 | 0 | console.log(fmt, test.title); |
56 | }); | |
57 | ||
58 | 0 | runner.on('pass', function(test){ |
59 | 0 | if ('fast' == test.speed) { |
60 | 0 | var fmt = indent() |
61 | + color('checkmark', ' ✓') | |
62 | + color('pass', ' %s '); | |
63 | 0 | cursor.CR(); |
64 | 0 | console.log(fmt, test.title); |
65 | } else { | |
66 | 0 | var fmt = indent() |
67 | + color('checkmark', ' ✓') | |
68 | + color('pass', ' %s ') | |
69 | + color(test.speed, '(%dms)'); | |
70 | 0 | cursor.CR(); |
71 | 0 | console.log(fmt, test.title, test.duration); |
72 | } | |
73 | }); | |
74 | ||
75 | 0 | runner.on('fail', function(test, err){ |
76 | 0 | cursor.CR(); |
77 | 0 | console.log(indent() + color('fail', ' %d) %s'), ++n, test.title); |
78 | }); | |
79 | ||
80 | 0 | runner.on('end', self.epilogue.bind(self)); |
81 | } | |
82 | ||
83 | /** | |
84 | * Inherit from `Base.prototype`. | |
85 | */ | |
86 | ||
87 | 1 | Spec.prototype.__proto__ = Base.prototype; |
Line | Hits | Source |
---|---|---|
1 | ||
2 | /** | |
3 | * Module dependencies. | |
4 | */ | |
5 | ||
6 | 1 | var Base = require('./base') |
7 | , cursor = Base.cursor | |
8 | , color = Base.color; | |
9 | ||
10 | /** | |
11 | * Expose `Progress`. | |
12 | */ | |
13 | ||
14 | 1 | exports = module.exports = Progress; |
15 | ||
16 | /** | |
17 | * General progress bar color. | |
18 | */ | |
19 | ||
20 | 1 | 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 | 1 | function Progress(runner, options) { |
31 | 0 | Base.call(this, runner); |
32 | ||
33 | 0 | 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 | 0 | options.open = options.open || '['; |
43 | 0 | options.complete = options.complete || 'â–¬'; |
44 | 0 | options.incomplete = options.incomplete || 'â‹…'; |
45 | 0 | options.close = options.close || ']'; |
46 | 0 | options.verbose = false; |
47 | ||
48 | // tests started | |
49 | 0 | runner.on('start', function(){ |
50 | 0 | console.log(); |
51 | 0 | cursor.hide(); |
52 | }); | |
53 | ||
54 | // tests complete | |
55 | 0 | runner.on('test end', function(){ |
56 | 0 | var incomplete = total - complete |
57 | , percent = complete++ / total | |
58 | , n = width * percent | 0 | |
59 | , i = width - n; | |
60 | ||
61 | 0 | cursor.CR(); |
62 | 0 | process.stdout.write('\033[J'); |
63 | 0 | process.stdout.write(color('progress', ' ' + options.open)); |
64 | 0 | process.stdout.write(Array(n).join(options.complete)); |
65 | 0 | process.stdout.write(Array(i).join(options.incomplete)); |
66 | 0 | process.stdout.write(color('progress', options.close)); |
67 | 0 | if (options.verbose) { |
68 | 0 | process.stdout.write(color('progress', ' ' + complete + ' of ' + total)); |
69 | } | |
70 | }); | |
71 | ||
72 | // tests are complete, output some stats | |
73 | // and the failures if any | |
74 | 0 | runner.on('end', function(){ |
75 | 0 | cursor.show(); |
76 | 0 | console.log(); |
77 | 0 | self.epilogue(); |
78 | }); | |
79 | } | |
80 | ||
81 | /** | |
82 | * Inherit from `Base.prototype`. | |
83 | */ | |
84 | ||
85 | 1 | Progress.prototype.__proto__ = Base.prototype; |
Line | Hits | Source |
---|---|---|
1 | ||
2 | /** | |
3 | * Module dependencies. | |
4 | */ | |
5 | ||
6 | 1 | var Base = require('./base') |
7 | , cursor = Base.cursor | |
8 | , color = Base.color; | |
9 | ||
10 | /** | |
11 | * Expose `Landing`. | |
12 | */ | |
13 | ||
14 | 1 | exports = module.exports = Landing; |
15 | ||
16 | /** | |
17 | * Airplane color. | |
18 | */ | |
19 | ||
20 | 1 | Base.colors.plane = 0; |
21 | ||
22 | /** | |
23 | * Airplane crash color. | |
24 | */ | |
25 | ||
26 | 1 | Base.colors['plane crash'] = 31; |
27 | ||
28 | /** | |
29 | * Runway color. | |
30 | */ | |
31 | ||
32 | 1 | Base.colors.runway = 90; |
33 | ||
34 | /** | |
35 | * Initialize a new `Landing` reporter. | |
36 | * | |
37 | * @param {Runner} runner | |
38 | * @api public | |
39 | */ | |
40 | ||
41 | 1 | function Landing(runner) { |
42 | 0 | Base.call(this, runner); |
43 | ||
44 | 0 | 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 | 0 | function runway() { |
54 | 0 | var buf = Array(width).join('-'); |
55 | 0 | return ' ' + color('runway', buf); |
56 | } | |
57 | ||
58 | 0 | runner.on('start', function(){ |
59 | 0 | stream.write('\n '); |
60 | 0 | cursor.hide(); |
61 | }); | |
62 | ||
63 | 0 | runner.on('test end', function(test){ |
64 | // check if the plane crashed | |
65 | 0 | var col = -1 == crashed |
66 | ? width * ++n / total | 0 | |
67 | : crashed; | |
68 | ||
69 | // show the crash | |
70 | 0 | if ('failed' == test.state) { |
71 | 0 | plane = color('plane crash', '✈'); |
72 | 0 | crashed = col; |
73 | } | |
74 | ||
75 | // render landing strip | |
76 | 0 | stream.write('\033[4F\n\n'); |
77 | 0 | stream.write(runway()); |
78 | 0 | stream.write('\n '); |
79 | 0 | stream.write(color('runway', Array(col).join('â‹…'))); |
80 | 0 | stream.write(plane) |
81 | 0 | stream.write(color('runway', Array(width - col).join('â‹…') + '\n')); |
82 | 0 | stream.write(runway()); |
83 | 0 | stream.write('\033[0m'); |
84 | }); | |
85 | ||
86 | 0 | runner.on('end', function(){ |
87 | 0 | cursor.show(); |
88 | 0 | console.log(); |
89 | 0 | self.epilogue(); |
90 | }); | |
91 | } | |
92 | ||
93 | /** | |
94 | * Inherit from `Base.prototype`. | |
95 | */ | |
96 | ||
97 | 1 | Landing.prototype.__proto__ = Base.prototype; |
Line | Hits | Source |
---|---|---|
1 | ||
2 | /** | |
3 | * Module dependencies. | |
4 | */ | |
5 | ||
6 | 1 | var Base = require('./base'); |
7 | ||
8 | /** | |
9 | * Expose `JSONCov`. | |
10 | */ | |
11 | ||
12 | 1 | 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 | 1 | function JSONCov(runner, output) { |
23 | 0 | var self = this |
24 | , output = 1 == arguments.length ? true : output; | |
25 | ||
26 | 0 | Base.call(this, runner); |
27 | ||
28 | 0 | var tests = [] |
29 | , failures = [] | |
30 | , passes = []; | |
31 | ||
32 | 0 | runner.on('test end', function(test){ |
33 | 0 | tests.push(test); |
34 | }); | |
35 | ||
36 | 0 | runner.on('pass', function(test){ |
37 | 0 | passes.push(test); |
38 | }); | |
39 | ||
40 | 0 | runner.on('fail', function(test){ |
41 | 0 | failures.push(test); |
42 | }); | |
43 | ||
44 | 0 | runner.on('end', function(){ |
45 | 0 | var cov = global._$jscoverage || {}; |
46 | 0 | var result = self.cov = map(cov); |
47 | 0 | result.stats = self.stats; |
48 | 0 | result.tests = tests.map(clean); |
49 | 0 | result.failures = failures.map(clean); |
50 | 0 | result.passes = passes.map(clean); |
51 | 0 | if (!output) return; |
52 | 0 | 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 | 1 | function map(cov) { |
66 | 0 | var ret = { |
67 | instrumentation: 'node-jscoverage' | |
68 | , sloc: 0 | |
69 | , hits: 0 | |
70 | , misses: 0 | |
71 | , coverage: 0 | |
72 | , files: [] | |
73 | }; | |
74 | ||
75 | 0 | for (var filename in cov) { |
76 | 0 | var data = coverage(filename, cov[filename]); |
77 | 0 | ret.files.push(data); |
78 | 0 | ret.hits += data.hits; |
79 | 0 | ret.misses += data.misses; |
80 | 0 | ret.sloc += data.sloc; |
81 | } | |
82 | ||
83 | 0 | if (ret.sloc > 0) { |
84 | 0 | ret.coverage = (ret.hits / ret.sloc) * 100; |
85 | } | |
86 | ||
87 | 0 | return ret; |
88 | 1 | }; |
89 | ||
90 | /** | |
91 | * Map jscoverage data for a single source file | |
92 | * to a JSON structure suitable for reporting. | |
93 | * | |
94 | * @param {String} filename name of the source file | |
95 | * @param {Object} data jscoverage coverage data | |
96 | * @return {Object} | |
97 | * @api private | |
98 | */ | |
99 | ||
100 | 1 | function coverage(filename, data) { |
101 | 0 | var ret = { |
102 | filename: filename, | |
103 | coverage: 0, | |
104 | hits: 0, | |
105 | misses: 0, | |
106 | sloc: 0, | |
107 | source: {} | |
108 | }; | |
109 | ||
110 | 0 | data.source.forEach(function(line, num){ |
111 | 0 | num++; |
112 | ||
113 | 0 | if (data[num] === 0) { |
114 | 0 | ret.misses++; |
115 | 0 | ret.sloc++; |
116 | 0 | } else if (data[num] !== undefined) { |
117 | 0 | ret.hits++; |
118 | 0 | ret.sloc++; |
119 | } | |
120 | ||
121 | 0 | ret.source[num] = { |
122 | source: line | |
123 | , coverage: data[num] === undefined | |
124 | ? '' | |
125 | : data[num] | |
126 | }; | |
127 | }); | |
128 | ||
129 | 0 | ret.coverage = ret.hits / ret.sloc * 100; |
130 | ||
131 | 0 | return ret; |
132 | } | |
133 | ||
134 | /** | |
135 | * Return a plain-object representation of `test` | |
136 | * free of cyclic properties etc. | |
137 | * | |
138 | * @param {Object} test | |
139 | * @return {Object} | |
140 | * @api private | |
141 | */ | |
142 | ||
143 | 1 | function clean(test) { |
144 | 0 | return { |
145 | title: test.title | |
146 | , fullTitle: test.fullTitle() | |
147 | , duration: test.duration | |
148 | } | |
149 | } |
Line | Hits | Source |
---|---|---|
1 | ||
2 | /** | |
3 | * Module dependencies. | |
4 | */ | |
5 | ||
6 | 1 | var JSONCov = require('./json-cov') |
7 | , fs = require('fs'); | |
8 | ||
9 | /** | |
10 | * Expose `HTMLCov`. | |
11 | */ | |
12 | ||
13 | 1 | exports = module.exports = HTMLCov; |
14 | ||
15 | /** | |
16 | * Initialize a new `JsCoverage` reporter. | |
17 | * | |
18 | * @param {Runner} runner | |
19 | * @api public | |
20 | */ | |
21 | ||
22 | 1 | function HTMLCov(runner) { |
23 | 0 | 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 | 0 | JSONCov.call(this, runner, false); |
30 | ||
31 | 0 | runner.on('end', function(){ |
32 | 0 | process.stdout.write(fn({ |
33 | cov: self.cov | |
34 | , coverageClass: coverageClass | |
35 | })); | |
36 | }); | |
37 | } | |
38 | ||
39 | 1 | function coverageClass(n) { |
40 | 0 | if (n >= 75) return 'high'; |
41 | 0 | if (n >= 50) return 'medium'; |
42 | 0 | if (n >= 25) return 'low'; |
43 | 0 | return 'terrible'; |
44 | } |
Line | Hits | Source |
---|---|---|
1 | ||
2 | /** | |
3 | * Module dependencies. | |
4 | */ | |
5 | ||
6 | 1 | var Base = require('./base') |
7 | , color = Base.color; | |
8 | ||
9 | /** | |
10 | * Expose `List`. | |
11 | */ | |
12 | ||
13 | 1 | exports = module.exports = List; |
14 | ||
15 | /** | |
16 | * Initialize a new `List` test reporter. | |
17 | * | |
18 | * @param {Runner} runner | |
19 | * @api public | |
20 | */ | |
21 | ||
22 | 1 | function List(runner) { |
23 | 0 | Base.call(this, runner); |
24 | ||
25 | 0 | var self = this |
26 | , stats = this.stats | |
27 | , total = runner.total; | |
28 | ||
29 | 0 | runner.on('start', function(){ |
30 | 0 | console.log(JSON.stringify(['start', { total: total }])); |
31 | }); | |
32 | ||
33 | 0 | runner.on('pass', function(test){ |
34 | 0 | console.log(JSON.stringify(['pass', clean(test)])); |
35 | }); | |
36 | ||
37 | 0 | runner.on('fail', function(test, err){ |
38 | 0 | console.log(JSON.stringify(['fail', clean(test)])); |
39 | }); | |
40 | ||
41 | 0 | runner.on('end', function(){ |
42 | 0 | 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 | 1 | function clean(test) { |
56 | 0 | return { |
57 | title: test.title | |
58 | , fullTitle: test.fullTitle() | |
59 | , duration: test.duration | |
60 | } | |
61 | } |
Line | Hits | Source |
---|---|---|
1 | ||
2 | /** | |
3 | * Module dependencies. | |
4 | */ | |
5 | ||
6 | 1 | var Base = require('./base') |
7 | , utils = require('../utils') | |
8 | , escape = utils.escape; | |
9 | ||
10 | /** | |
11 | * Expose `XUnit`. | |
12 | */ | |
13 | ||
14 | 1 | exports = module.exports = XUnit; |
15 | ||
16 | /** | |
17 | * Initialize a new `XUnit` reporter. | |
18 | * | |
19 | * @param {Runner} runner | |
20 | * @api public | |
21 | */ | |
22 | ||
23 | 1 | function XUnit(runner) { |
24 | 0 | Base.call(this, runner); |
25 | 0 | var stats = this.stats |
26 | , tests = [] | |
27 | , self = this; | |
28 | ||
29 | 0 | runner.on('test end', function(test){ |
30 | 0 | tests.push(test); |
31 | }); | |
32 | ||
33 | 0 | runner.on('end', function(){ |
34 | 0 | console.log(tag('testsuite', { |
35 | name: 'Mocha Tests' | |
36 | , tests: stats.tests | |
37 | , failures: stats.failures | |
38 | , errors: stats.failures | |
39 | , skip: stats.tests - stats.failures - stats.passes | |
40 | , timestamp: (new Date).toUTCString() | |
41 | , time: stats.duration / 1000 | |
42 | }, false)); | |
43 | ||
44 | 0 | tests.forEach(test); |
45 | 0 | console.log('</testsuite>'); |
46 | }); | |
47 | } | |
48 | ||
49 | /** | |
50 | * Inherit from `Base.prototype`. | |
51 | */ | |
52 | ||
53 | 1 | XUnit.prototype.__proto__ = Base.prototype; |
54 | ||
55 | /** | |
56 | * Output tag for the given `test.` | |
57 | */ | |
58 | ||
59 | 1 | function test(test) { |
60 | 0 | var attrs = { |
61 | classname: test.parent.fullTitle() | |
62 | , name: test.title | |
63 | , time: test.duration / 1000 | |
64 | }; | |
65 | ||
66 | 0 | if ('failed' == test.state) { |
67 | 0 | var err = test.err; |
68 | 0 | attrs.message = escape(err.message); |
69 | 0 | console.log(tag('testcase', attrs, false, tag('failure', attrs, false, cdata(err.stack)))); |
70 | 0 | } else if (test.pending) { |
71 | 0 | console.log(tag('testcase', attrs, false, tag('skipped', {}, true))); |
72 | } else { | |
73 | 0 | console.log(tag('testcase', attrs, true) ); |
74 | } | |
75 | } | |
76 | ||
77 | /** | |
78 | * HTML tag helper. | |
79 | */ | |
80 | ||
81 | 1 | function tag(name, attrs, close, content) { |
82 | 0 | var end = close ? '/>' : '>' |
83 | , pairs = [] | |
84 | , tag; | |
85 | ||
86 | 0 | for (var key in attrs) { |
87 | 0 | pairs.push(key + '="' + escape(attrs[key]) + '"'); |
88 | } | |
89 | ||
90 | 0 | tag = '<' + name + (pairs.length ? ' ' + pairs.join(' ') : '') + end; |
91 | 0 | if (content) tag += content + '</' + name + end; |
92 | 0 | return tag; |
93 | } | |
94 | ||
95 | /** | |
96 | * Return cdata escaped CDATA `str`. | |
97 | */ | |
98 | ||
99 | 1 | function cdata(str) { |
100 | 0 | return '<![CDATA[' + escape(str) + ']]>'; |
101 | } |
Line | Hits | Source |
---|---|---|
1 | ||
2 | /** | |
3 | * Expose `Context`. | |
4 | */ | |
5 | ||
6 | 1 | module.exports = Context; |
7 | ||
8 | /** | |
9 | * Initialize a new `Context`. | |
10 | * | |
11 | * @api private | |
12 | */ | |
13 | ||
14 | 1 | function Context(){} |
15 | ||
16 | /** | |
17 | * Set the context `Test` to `test`. | |
18 | * | |
19 | * @param {Test} test | |
20 | * @return {Context} | |
21 | * @api private | |
22 | */ | |
23 | ||
24 | 1 | Context.prototype.test = function(test){ |
25 | 178 | this._test = test; |
26 | 178 | return this; |
27 | }; | |
28 | ||
29 | /** | |
30 | * Set test timeout `ms`. | |
31 | * | |
32 | * @param {Number} ms | |
33 | * @return {Context} self | |
34 | * @api private | |
35 | */ | |
36 | ||
37 | 1 | Context.prototype.timeout = function(ms){ |
38 | 1 | this._test.timeout(ms); |
39 | 1 | return this; |
40 | }; | |
41 | ||
42 | /** | |
43 | * Inspect the context void of `._test`. | |
44 | * | |
45 | * @return {String} | |
46 | * @api private | |
47 | */ | |
48 | ||
49 | 1 | Context.prototype.inspect = function(){ |
50 | 9 | return JSON.stringify(this, function(key, val){ |
51 | 36 | return '_test' == key |
52 | ? undefined | |
53 | : val; | |
54 | }, 2); | |
55 | }; |
Line | Hits | Source |
---|---|---|
1 | ||
2 | /** | |
3 | * Module dependencies. | |
4 | */ | |
5 | ||
6 | 1 | var EventEmitter = require('events').EventEmitter |
7 | , debug = require('debug')('runner') | |
8 | , Test = require('./test') | |
9 | , utils = require('./utils') | |
10 | , noop = function(){}; | |
11 | ||
12 | /** | |
13 | * Expose `Runner`. | |
14 | */ | |
15 | ||
16 | 1 | module.exports = Runner; |
17 | ||
18 | /** | |
19 | * Initialize a `Runner` for the given `suite`. | |
20 | * | |
21 | * Events: | |
22 | * | |
23 | * - `start` execution started | |
24 | * - `end` execution complete | |
25 | * - `suite` (suite) test suite execution started | |
26 | * - `suite end` (suite) all tests (and sub-suites) have finished | |
27 | * - `test` (test) test execution started | |
28 | * - `test end` (test) test completed | |
29 | * - `hook` (hook) hook execution started | |
30 | * - `hook end` (hook) hook complete | |
31 | * - `pass` (test) test passed | |
32 | * - `fail` (test, err) test failed | |
33 | * | |
34 | * @api public | |
35 | */ | |
36 | ||
37 | 1 | function Runner(suite) { |
38 | 11 | var self = this; |
39 | 11 | this._globals = []; |
40 | 11 | this.suite = suite; |
41 | 11 | this.total = suite.total(); |
42 | 11 | this.failures = 0; |
43 | 91 | this.on('test end', function(test){ self.checkGlobals(test); }); |
44 | 110 | this.on('hook end', function(hook){ self.checkGlobals(hook); }); |
45 | 11 | this.grep(/.*/); |
46 | 11 | this.globals(utils.keys(global).concat(['errno'])); |
47 | } | |
48 | ||
49 | /** | |
50 | * Inherit from `EventEmitter.prototype`. | |
51 | */ | |
52 | ||
53 | 1 | Runner.prototype.__proto__ = EventEmitter.prototype; |
54 | ||
55 | /** | |
56 | * Run tests with full titles matching `re`. | |
57 | * | |
58 | * @param {RegExp} re | |
59 | * @return {Runner} for chaining | |
60 | * @api public | |
61 | */ | |
62 | ||
63 | 1 | Runner.prototype.grep = function(re){ |
64 | 11 | debug('grep %s', re); |
65 | 11 | this._grep = re; |
66 | 11 | return this; |
67 | }; | |
68 | ||
69 | /** | |
70 | * Allow the given `arr` of globals. | |
71 | * | |
72 | * @param {Array} arr | |
73 | * @return {Runner} for chaining | |
74 | * @api public | |
75 | */ | |
76 | ||
77 | 1 | Runner.prototype.globals = function(arr){ |
78 | 19 | if (0 == arguments.length) return this._globals; |
79 | 13 | debug('globals %j', arr); |
80 | 13 | utils.forEach(arr, function(arr){ |
81 | 364 | this._globals.push(arr); |
82 | }, this); | |
83 | 13 | return this; |
84 | }; | |
85 | ||
86 | /** | |
87 | * Check for global variable leaks. | |
88 | * | |
89 | * @api private | |
90 | */ | |
91 | ||
92 | 1 | Runner.prototype.checkGlobals = function(test){ |
93 | 183 | if (this.ignoreLeaks) return; |
94 | ||
95 | 183 | var leaks = utils.filter(utils.keys(global), function(key){ |
96 | 5769 | return !~utils.indexOf(this._globals, key) && (!global.navigator || 'onerror' !== key); |
97 | }, this); | |
98 | ||
99 | 183 | this._globals = this._globals.concat(leaks); |
100 | ||
101 | 183 | if (leaks.length > 1) { |
102 | 1 | this.fail(test, new Error('global leaks detected: ' + leaks.join(', ') + '')); |
103 | 182 | } else if (leaks.length) { |
104 | 1 | this.fail(test, new Error('global leak detected: ' + leaks[0])); |
105 | } | |
106 | }; | |
107 | ||
108 | /** | |
109 | * Fail the given `test`. | |
110 | * | |
111 | * @param {Test} test | |
112 | * @param {Error} err | |
113 | * @api private | |
114 | */ | |
115 | ||
116 | 1 | Runner.prototype.fail = function(test, err){ |
117 | 10 | ++this.failures; |
118 | 10 | test.state = 'failed'; |
119 | 10 | this.emit('fail', test, err); |
120 | }; | |
121 | ||
122 | /** | |
123 | * Fail the given `hook` with `err`. | |
124 | * | |
125 | * Hook failures (currently) hard-end due | |
126 | * to that fact that a failing hook will | |
127 | * surely cause subsequent tests to fail, | |
128 | * causing jumbled reporting. | |
129 | * | |
130 | * @param {Hook} hook | |
131 | * @param {Error} err | |
132 | * @api private | |
133 | */ | |
134 | ||
135 | 1 | Runner.prototype.failHook = function(hook, err){ |
136 | 4 | this.fail(hook, err); |
137 | 4 | this.emit('end'); |
138 | }; | |
139 | ||
140 | /** | |
141 | * Run hook `name` callbacks and then invoke `fn()`. | |
142 | * | |
143 | * @param {String} name | |
144 | * @param {Function} function | |
145 | * @api private | |
146 | */ | |
147 | ||
148 | 1 | Runner.prototype.hook = function(name, fn){ |
149 | 672 | var suite = this.suite |
150 | , hooks = suite['_' + name] | |
151 | , ms = suite._timeout | |
152 | , self = this | |
153 | , timer; | |
154 | ||
155 | 672 | function next(i) { |
156 | 771 | var hook = hooks[i]; |
157 | 1443 | if (!hook) return fn(); |
158 | 99 | self.currentRunnable = hook; |
159 | 99 | hook.ctx.test(self.test); |
160 | ||
161 | 99 | self.emit('hook', hook); |
162 | ||
163 | 99 | hook.on('error', function(err){ |
164 | 0 | self.failHook(hook, err); |
165 | }); | |
166 | ||
167 | 99 | hook.run(function(err){ |
168 | 99 | hook.removeAllListeners('error'); |
169 | 99 | if (err) return self.failHook(hook, err); |
170 | 99 | self.emit('hook end', hook); |
171 | 99 | next(++i); |
172 | }); | |
173 | } | |
174 | ||
175 | 672 | process.nextTick(function(){ |
176 | 672 | next(0); |
177 | }); | |
178 | }; | |
179 | ||
180 | /** | |
181 | * Run hook `name` for the given array of `suites` | |
182 | * in order, and callback `fn(err)`. | |
183 | * | |
184 | * @param {String} name | |
185 | * @param {Array} suites | |
186 | * @param {Function} fn | |
187 | * @api private | |
188 | */ | |
189 | ||
190 | 1 | Runner.prototype.hooks = function(name, suites, fn){ |
191 | 158 | var self = this |
192 | , orig = this.suite; | |
193 | ||
194 | 158 | function next(suite) { |
195 | 676 | self.suite = suite; |
196 | ||
197 | 676 | if (!suite) { |
198 | 158 | self.suite = orig; |
199 | 158 | return fn(); |
200 | } | |
201 | ||
202 | 518 | self.hook(name, function(err){ |
203 | 518 | if (err) { |
204 | 0 | self.suite = orig; |
205 | 0 | return fn(err); |
206 | } | |
207 | ||
208 | 518 | next(suites.pop()); |
209 | }); | |
210 | } | |
211 | ||
212 | 158 | next(suites.pop()); |
213 | }; | |
214 | ||
215 | /** | |
216 | * Run hooks from the top level down. | |
217 | * | |
218 | * @param {String} name | |
219 | * @param {Function} fn | |
220 | * @api private | |
221 | */ | |
222 | ||
223 | 1 | Runner.prototype.hookUp = function(name, fn){ |
224 | 79 | var suites = [this.suite].concat(this.parents()).reverse(); |
225 | 79 | this.hooks(name, suites, fn); |
226 | }; | |
227 | ||
228 | /** | |
229 | * Run hooks from the bottom up. | |
230 | * | |
231 | * @param {String} name | |
232 | * @param {Function} fn | |
233 | * @api private | |
234 | */ | |
235 | ||
236 | 1 | Runner.prototype.hookDown = function(name, fn){ |
237 | 79 | var suites = [this.suite].concat(this.parents()); |
238 | 79 | this.hooks(name, suites, fn); |
239 | }; | |
240 | ||
241 | /** | |
242 | * Return an array of parent Suites from | |
243 | * closest to furthest. | |
244 | * | |
245 | * @return {Array} | |
246 | * @api private | |
247 | */ | |
248 | ||
249 | 1 | Runner.prototype.parents = function(){ |
250 | 158 | var suite = this.suite |
251 | , suites = []; | |
252 | 518 | while (suite = suite.parent) suites.push(suite); |
253 | 158 | return suites; |
254 | }; | |
255 | ||
256 | /** | |
257 | * Run the current test and callback `fn(err)`. | |
258 | * | |
259 | * @param {Function} fn | |
260 | * @api private | |
261 | */ | |
262 | ||
263 | 1 | Runner.prototype.runTest = function(fn){ |
264 | 79 | var test = this.test |
265 | , self = this; | |
266 | ||
267 | 79 | try { |
268 | 79 | test.ctx.test(test); |
269 | 79 | test.on('error', function(err){ |
270 | 0 | self.fail(test, err); |
271 | }); | |
272 | 79 | test.run(fn); |
273 | } catch (err) { | |
274 | 0 | fn(err); |
275 | } | |
276 | }; | |
277 | ||
278 | /** | |
279 | * Run tests in the given `suite` and invoke | |
280 | * the callback `fn()` when complete. | |
281 | * | |
282 | * @param {Suite} suite | |
283 | * @param {Function} fn | |
284 | * @api private | |
285 | */ | |
286 | ||
287 | 1 | Runner.prototype.runTests = function(suite, fn){ |
288 | 77 | var self = this |
289 | , tests = suite.tests | |
290 | , test; | |
291 | ||
292 | 77 | function next(err) { |
293 | // if we bail after first err | |
294 | 157 | if (self.failures && suite._bail) return fn(); |
295 | ||
296 | // next test | |
297 | 157 | test = tests.shift(); |
298 | ||
299 | // all done | |
300 | 234 | if (!test) return fn(); |
301 | ||
302 | // grep | |
303 | 80 | if (!self._grep.test(test.fullTitle())) return next(); |
304 | ||
305 | // pending | |
306 | 80 | if (test.pending) { |
307 | 1 | self.emit('pending', test); |
308 | 1 | self.emit('test end', test); |
309 | 1 | return next(); |
310 | } | |
311 | ||
312 | // execute test and hook(s) | |
313 | 79 | self.emit('test', self.test = test); |
314 | 79 | self.hookDown('beforeEach', function(){ |
315 | 79 | self.currentRunnable = self.test; |
316 | 79 | self.runTest(function(err){ |
317 | 79 | test = self.test; |
318 | ||
319 | 79 | if (err) { |
320 | 0 | self.fail(test, err); |
321 | 0 | self.emit('test end', test); |
322 | 0 | return self.hookUp('afterEach', next); |
323 | } | |
324 | ||
325 | 79 | test.state = 'passed'; |
326 | 79 | self.emit('pass', test); |
327 | 79 | self.emit('test end', test); |
328 | 79 | self.hookUp('afterEach', next); |
329 | }); | |
330 | }); | |
331 | } | |
332 | ||
333 | 77 | this.next = next; |
334 | 77 | next(); |
335 | }; | |
336 | ||
337 | /** | |
338 | * Run the given `suite` and invoke the | |
339 | * callback `fn()` when complete. | |
340 | * | |
341 | * @param {Suite} suite | |
342 | * @param {Function} fn | |
343 | * @api private | |
344 | */ | |
345 | ||
346 | 1 | Runner.prototype.runSuite = function(suite, fn){ |
347 | 77 | var self = this |
348 | , i = 0; | |
349 | ||
350 | 77 | debug('run suite %s', suite.fullTitle()); |
351 | 77 | this.emit('suite', this.suite = suite); |
352 | ||
353 | 77 | function next() { |
354 | 153 | var curr = suite.suites[i++]; |
355 | 230 | if (!curr) return done(); |
356 | 76 | self.runSuite(curr, next); |
357 | } | |
358 | ||
359 | 77 | function done() { |
360 | 77 | self.suite = suite; |
361 | 77 | self.hook('afterAll', function(){ |
362 | 77 | self.emit('suite end', suite); |
363 | 77 | fn(); |
364 | }); | |
365 | } | |
366 | ||
367 | 77 | this.hook('beforeAll', function(){ |
368 | 77 | self.runTests(suite, next); |
369 | }); | |
370 | }; | |
371 | ||
372 | /** | |
373 | * Handle uncaught exceptions. | |
374 | * | |
375 | * @param {Error} err | |
376 | * @api private | |
377 | */ | |
378 | ||
379 | 1 | Runner.prototype.uncaught = function(err){ |
380 | 0 | debug('uncaught exception'); |
381 | 0 | var runnable = this.currentRunnable; |
382 | 0 | if ('failed' == runnable.state) return; |
383 | 0 | runnable.clearTimeout(); |
384 | 0 | err.uncaught = true; |
385 | 0 | this.fail(runnable, err); |
386 | ||
387 | // recover from test | |
388 | 0 | if ('test' == runnable.type) { |
389 | 0 | this.emit('test end', runnable); |
390 | 0 | this.hookUp('afterEach', this.next); |
391 | 0 | return; |
392 | } | |
393 | ||
394 | // bail on hooks | |
395 | 0 | this.emit('end'); |
396 | }; | |
397 | ||
398 | /** | |
399 | * Run the root suite and invoke `fn(failures)` | |
400 | * on completion. | |
401 | * | |
402 | * @param {Function} fn | |
403 | * @return {Runner} for chaining | |
404 | * @api public | |
405 | */ | |
406 | ||
407 | 1 | Runner.prototype.run = function(fn){ |
408 | 1 | var self = this |
409 | , fn = fn || function(){}; | |
410 | ||
411 | 1 | debug('start'); |
412 | ||
413 | // callback | |
414 | 1 | this.on('end', function(){ |
415 | 0 | debug('end'); |
416 | 0 | process.removeListener('uncaughtException', this.uncaught); |
417 | 0 | fn(self.failures); |
418 | }); | |
419 | ||
420 | // run suites | |
421 | 1 | this.emit('start'); |
422 | 1 | this.runSuite(this.suite, function(){ |
423 | 1 | debug('finished running'); |
424 | 1 | self.emit('end'); |
425 | }); | |
426 | ||
427 | // uncaught exception | |
428 | 1 | process.on('uncaughtException', function(err){ |
429 | 0 | self.uncaught(err); |
430 | }); | |
431 | ||
432 | 1 | return this; |
433 | }; |