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