diff --git a/mocha.js b/mocha.js index 2fdbf16db8..84c384bd83 100644 --- a/mocha.js +++ b/mocha.js @@ -1029,7 +1029,7 @@ function Mocha(options) { this.ui(options.ui); this.bail(options.bail); this.reporter(options.reporter, options.reporterOptions); - if (options.timeout != null) { + if (typeof options.timeout !== 'undefined' && options.timeout !== null) { this.timeout(options.timeout); } this.useColors(options.useColors); @@ -1751,7 +1751,14 @@ exports.list = function(failures) { // msg var msg; var err = test.err; - var message = err.message || ''; + var message; + if (err.message) { + message = err.message; + } else if (typeof err.inspect === 'function') { + message = err.inspect() + ''; + } else { + message = ''; + } var stack = err.stack || message; var index = stack.indexOf(message); var actual = err.actual; @@ -1983,7 +1990,7 @@ function unifiedDiff(err, escape) { return indent + line; } function notBlank(line) { - return line != null; + return typeof line !== 'undefined' && line !== null; } var msg = diff.createPatch('string', err.actual, err.expected); var lines = msg.split('\n').splice(4); @@ -3120,7 +3127,7 @@ function Markdown(runner) { var key = SUITE_PREFIX + suite.title; obj = obj[key] = obj[key] || { suite: suite }; - suite.suites.forEach(function() { + suite.suites.forEach(function(suite) { mapTOC(suite, obj); }); @@ -3813,6 +3820,11 @@ function XUnit(runner, options) { }); } +/** + * Inherit from `Base.prototype`. + */ +inherits(XUnit, Base); + /** * Override done to close the stream (if it's a file). * @@ -3829,11 +3841,6 @@ XUnit.prototype.done = function(failures, fn) { } }; -/** - * Inherit from `Base.prototype`. - */ -inherits(XUnit, Base); - /** * Write out the given line. * @@ -4240,6 +4247,7 @@ var Pending = require('./pending'); var utils = require('./utils'); var inherits = utils.inherits; var debug = require('debug')('mocha:runner'); +var Runnable = require('./runnable'); var filter = utils.filter; var indexOf = utils.indexOf; var keys = utils.keys; @@ -4297,6 +4305,7 @@ function Runner(suite, delay) { this._abort = false; this._delay = delay; this.suite = suite; + this.started = false; this.total = suite.total(); this.failures = 0; this.on('test end', function(test) { @@ -4519,12 +4528,13 @@ Runner.prototype.hook = function(name, fn) { self.emit('hook', hook); - hook.on('error', function(err) { - self.failHook(hook, err); - }); + if (!hook.listeners('error').length) { + hook.on('error', function(err) { + self.failHook(hook, err); + }); + } hook.run(function(err) { - hook.removeAllListeners('error'); var testError = hook.error(); if (testError) { self.fail(self.test, testError); @@ -4619,7 +4629,8 @@ Runner.prototype.hookDown = function(name, fn) { Runner.prototype.parents = function() { var suite = this.suite; var suites = []; - while (suite = suite.parent) { + while (suite.parent) { + suite = suite.parent; suites.push(suite); } return suites; @@ -4885,7 +4896,20 @@ Runner.prototype.uncaught = function(err) { err.uncaught = true; var runnable = this.currentRunnable; + if (!runnable) { + runnable = new Runnable('Uncaught error outside test suite'); + runnable.parent = this.suite; + + if (this.started) { + this.fail(runnable, err); + } else { + // Can't recover from this failure + this.emit('start'); + this.fail(runnable, err); + this.emit('end'); + } + return; } @@ -4944,6 +4968,7 @@ Runner.prototype.run = function(fn) { } function start() { + self.started = true; self.emit('start'); self.runSuite(rootSuite, function() { debug('finished running'); @@ -5039,7 +5064,8 @@ function filterLeaks(ok, globals) { */ function extraGlobals() { if (typeof process === 'object' && typeof process.version === 'string') { - var nodeVersion = process.version.split('.').reduce(function(a, v) { + var parts = process.version.split('.'); + var nodeVersion = utils.reduce(parts, function(a, v) { return a << 8 | v; }); @@ -5054,7 +5080,7 @@ function extraGlobals() { } }).call(this,require('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) -},{"./pending":16,"./utils":39,"_process":51,"debug":2,"events":3}],37:[function(require,module,exports){ +},{"./pending":16,"./runnable":35,"./utils":39,"_process":51,"debug":2,"events":3}],37:[function(require,module,exports){ /** * Module dependencies. */ @@ -6146,7 +6172,7 @@ exports.getError = function(err) { * @description * When invoking this function you get a filter function that get the Error.stack as an input, * and return a prettify output. - * (i.e: strip Mocha, node_modules, bower and componentJS from stack trace). + * (i.e: strip Mocha and internal node functions from stack trace). * @returns {Function} */ exports.stackTraceFilter = function() { @@ -6158,14 +6184,10 @@ exports.stackTraceFilter = function() { : (typeof location === 'undefined' ? window.location : location).href.replace(/\/[^\/]*$/, '/'); function isMochaInternal(line) { - return (~line.indexOf('node_modules' + slash + 'mocha')) - || (~line.indexOf('components' + slash + 'mochajs')) - || (~line.indexOf('components' + slash + 'mocha')); - } - - // node_modules, bower, componentJS - function isBrowserModule(line) { - return (~line.indexOf('node_modules')) || (~line.indexOf('components')); + return (~line.indexOf('node_modules' + slash + 'mocha' + slash)) + || (~line.indexOf('components' + slash + 'mochajs' + slash)) + || (~line.indexOf('components' + slash + 'mocha' + slash)) + || (~line.indexOf(slash + 'mocha.js')); } function isNodeInternal(line) { @@ -6181,11 +6203,11 @@ exports.stackTraceFilter = function() { stack = stack.split('\n'); stack = exports.reduce(stack, function(list, line) { - if (is.node && (isMochaInternal(line) || isNodeInternal(line))) { + if (isMochaInternal(line)) { return list; } - if (is.browser && (isBrowserModule(line))) { + if (is.node && isNodeInternal(line)) { return list; } @@ -6259,28 +6281,35 @@ var rootParent = {} * Browsers that support typed arrays are IE 10+, Firefox 4+, Chrome 7+, Safari 5.1+, * Opera 11.6+, iOS 4.2+. * + * Due to various browser bugs, sometimes the Object implementation will be used even + * when the browser supports typed arrays. + * * Note: * - * - Implementation must support adding new properties to `Uint8Array` instances. - * Firefox 4-29 lacked support, fixed in Firefox 30+. - * See: https://bugzilla.mozilla.org/show_bug.cgi?id=695438. + * - Firefox 4-29 lacks support for adding new properties to `Uint8Array` instances, + * See: https://bugzilla.mozilla.org/show_bug.cgi?id=695438. * - * - Chrome 9-10 is missing the `TypedArray.prototype.subarray` function. + * - Safari 5-7 lacks support for changing the `Object.prototype.constructor` property + * on objects. * - * - IE10 has a broken `TypedArray.prototype.subarray` function which returns arrays of - * incorrect length in some situations. + * - Chrome 9-10 is missing the `TypedArray.prototype.subarray` function. * - * We detect these buggy browsers and set `Buffer.TYPED_ARRAY_SUPPORT` to `false` so they will - * get the Object implementation, which is slower but will work correctly. + * - IE10 has a broken `TypedArray.prototype.subarray` function which returns arrays of + * incorrect length in some situations. + + * We detect these buggy browsers and set `Buffer.TYPED_ARRAY_SUPPORT` to `false` so they + * get the Object implementation, which is slower but behaves correctly. */ Buffer.TYPED_ARRAY_SUPPORT = (function () { + function Bar () {} try { - var buf = new ArrayBuffer(0) - var arr = new Uint8Array(buf) + var arr = new Uint8Array(1) arr.foo = function () { return 42 } + arr.constructor = Bar return arr.foo() === 42 && // typed array instances can be augmented + arr.constructor === Bar && // constructor can be set typeof arr.subarray === 'function' && // chrome 9-10 lack `subarray` - new Uint8Array(1).subarray(1, 1).byteLength === 0 // ie10 has broken `subarray` + arr.subarray(1, 1).byteLength === 0 // ie10 has broken `subarray` } catch (e) { return false } @@ -6358,8 +6387,13 @@ function fromObject (that, object) { throw new TypeError('must start with number, buffer, array or string') } - if (typeof ArrayBuffer !== 'undefined' && object.buffer instanceof ArrayBuffer) { - return fromTypedArray(that, object) + if (typeof ArrayBuffer !== 'undefined') { + if (object.buffer instanceof ArrayBuffer) { + return fromTypedArray(that, object) + } + if (object instanceof ArrayBuffer) { + return fromArrayBuffer(that, object) + } } if (object.length) return fromArrayLike(that, object) @@ -6396,6 +6430,18 @@ function fromTypedArray (that, array) { return that } +function fromArrayBuffer (that, array) { + if (Buffer.TYPED_ARRAY_SUPPORT) { + // Return an augmented `Uint8Array` instance, for best performance + array.byteLength + that = Buffer._augment(new Uint8Array(array)) + } else { + // Fallback: Return an object instance of the Buffer class + that = fromTypedArray(that, new Uint8Array(array)) + } + return that +} + function fromArrayLike (that, array) { var length = checked(array.length) | 0 that = allocate(that, length) @@ -6513,8 +6559,6 @@ Buffer.concat = function concat (list, length) { if (list.length === 0) { return new Buffer(0) - } else if (list.length === 1) { - return list[0] } var i @@ -6689,13 +6733,13 @@ Buffer.prototype.indexOf = function indexOf (val, byteOffset) { throw new TypeError('val must be string, number or Buffer') } -// `get` will be removed in Node 0.13+ +// `get` is deprecated Buffer.prototype.get = function get (offset) { console.log('.get() is deprecated. Access using array indexes instead.') return this.readUInt8(offset) } -// `set` will be removed in Node 0.13+ +// `set` is deprecated Buffer.prototype.set = function set (v, offset) { console.log('.set() is deprecated. Access using array indexes instead.') return this.writeUInt8(v, offset) @@ -6836,20 +6880,99 @@ function base64Slice (buf, start, end) { } function utf8Slice (buf, start, end) { - var res = '' - var tmp = '' end = Math.min(buf.length, end) + var res = [] + + var i = start + while (i < end) { + var firstByte = buf[i] + var codePoint = null + var bytesPerSequence = (firstByte > 0xEF) ? 4 + : (firstByte > 0xDF) ? 3 + : (firstByte > 0xBF) ? 2 + : 1 + + if (i + bytesPerSequence <= end) { + var secondByte, thirdByte, fourthByte, tempCodePoint + + switch (bytesPerSequence) { + case 1: + if (firstByte < 0x80) { + codePoint = firstByte + } + break + case 2: + secondByte = buf[i + 1] + if ((secondByte & 0xC0) === 0x80) { + tempCodePoint = (firstByte & 0x1F) << 0x6 | (secondByte & 0x3F) + if (tempCodePoint > 0x7F) { + codePoint = tempCodePoint + } + } + break + case 3: + secondByte = buf[i + 1] + thirdByte = buf[i + 2] + if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80) { + tempCodePoint = (firstByte & 0xF) << 0xC | (secondByte & 0x3F) << 0x6 | (thirdByte & 0x3F) + if (tempCodePoint > 0x7FF && (tempCodePoint < 0xD800 || tempCodePoint > 0xDFFF)) { + codePoint = tempCodePoint + } + } + break + case 4: + secondByte = buf[i + 1] + thirdByte = buf[i + 2] + fourthByte = buf[i + 3] + if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80 && (fourthByte & 0xC0) === 0x80) { + tempCodePoint = (firstByte & 0xF) << 0x12 | (secondByte & 0x3F) << 0xC | (thirdByte & 0x3F) << 0x6 | (fourthByte & 0x3F) + if (tempCodePoint > 0xFFFF && tempCodePoint < 0x110000) { + codePoint = tempCodePoint + } + } + } + } - for (var i = start; i < end; i++) { - if (buf[i] <= 0x7F) { - res += decodeUtf8Char(tmp) + String.fromCharCode(buf[i]) - tmp = '' - } else { - tmp += '%' + buf[i].toString(16) + if (codePoint === null) { + // we did not generate a valid codePoint so insert a + // replacement char (U+FFFD) and advance only 1 byte + codePoint = 0xFFFD + bytesPerSequence = 1 + } else if (codePoint > 0xFFFF) { + // encode to utf16 (surrogate pair dance) + codePoint -= 0x10000 + res.push(codePoint >>> 10 & 0x3FF | 0xD800) + codePoint = 0xDC00 | codePoint & 0x3FF } + + res.push(codePoint) + i += bytesPerSequence } - return res + decodeUtf8Char(tmp) + return decodeCodePointsArray(res) +} + +// Based on http://stackoverflow.com/a/22747272/680742, the browser with +// the lowest limit is Chrome, with 0x10000 args. +// We go 1 magnitude less, for safety +var MAX_ARGUMENTS_LENGTH = 0x1000 + +function decodeCodePointsArray (codePoints) { + var len = codePoints.length + if (len <= MAX_ARGUMENTS_LENGTH) { + return String.fromCharCode.apply(String, codePoints) // avoid extra slice() + } + + // Decode in chunks to avoid "call stack size exceeded". + var res = '' + var i = 0 + while (i < len) { + res += String.fromCharCode.apply( + String, + codePoints.slice(i, i += MAX_ARGUMENTS_LENGTH) + ) + } + return res } function asciiSlice (buf, start, end) { @@ -7384,9 +7507,16 @@ Buffer.prototype.copy = function copy (target, targetStart, start, end) { } var len = end - start + var i - if (len < 1000 || !Buffer.TYPED_ARRAY_SUPPORT) { - for (var i = 0; i < len; i++) { + if (this === target && start < targetStart && targetStart < end) { + // descending copy from end + for (i = len - 1; i >= 0; i--) { + target[i + targetStart] = this[i + start] + } + } else if (len < 1000 || !Buffer.TYPED_ARRAY_SUPPORT) { + // ascending copy from start + for (i = 0; i < len; i++) { target[i + targetStart] = this[i + start] } } else { @@ -7462,7 +7592,7 @@ Buffer._augment = function _augment (arr) { // save reference to original Uint8Array set method before overwriting arr._set = arr.set - // deprecated, will be removed in node 0.13+ + // deprecated arr.get = BP.get arr.set = BP.set @@ -7518,7 +7648,7 @@ Buffer._augment = function _augment (arr) { return arr } -var INVALID_BASE64_RE = /[^+\/0-9A-z\-]/g +var INVALID_BASE64_RE = /[^+\/0-9A-Za-z-_]/g function base64clean (str) { // Node strips out invalid characters like \n and \t from the string, base64-js does not @@ -7548,28 +7678,15 @@ function utf8ToBytes (string, units) { var length = string.length var leadSurrogate = null var bytes = [] - var i = 0 - for (; i < length; i++) { + for (var i = 0; i < length; i++) { codePoint = string.charCodeAt(i) // is surrogate component if (codePoint > 0xD7FF && codePoint < 0xE000) { // last char was a lead - if (leadSurrogate) { - // 2 leads in a row - if (codePoint < 0xDC00) { - if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) - leadSurrogate = codePoint - continue - } else { - // valid surrogate pair - codePoint = leadSurrogate - 0xD800 << 10 | codePoint - 0xDC00 | 0x10000 - leadSurrogate = null - } - } else { + if (!leadSurrogate) { // no lead yet - if (codePoint > 0xDBFF) { // unexpected trail if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) @@ -7578,18 +7695,30 @@ function utf8ToBytes (string, units) { // unpaired lead if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) continue - } else { - // valid lead - leadSurrogate = codePoint - continue } + + // valid lead + leadSurrogate = codePoint + + continue } + + // 2 leads in a row + if (codePoint < 0xDC00) { + if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) + leadSurrogate = codePoint + continue + } + + // valid surrogate pair + codePoint = leadSurrogate - 0xD800 << 10 | codePoint - 0xDC00 | 0x10000 } else if (leadSurrogate) { // valid bmp char, but last char was a lead if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) - leadSurrogate = null } + leadSurrogate = null + // encode utf8 if (codePoint < 0x80) { if ((units -= 1) < 0) break @@ -7607,7 +7736,7 @@ function utf8ToBytes (string, units) { codePoint >> 0x6 & 0x3F | 0x80, codePoint & 0x3F | 0x80 ) - } else if (codePoint < 0x200000) { + } else if (codePoint < 0x110000) { if ((units -= 4) < 0) break bytes.push( codePoint >> 0x12 | 0xF0, @@ -7660,14 +7789,6 @@ function blitBuffer (src, dst, offset, length) { return i } -function decodeUtf8Char (str) { - try { - return decodeURIComponent(str) - } catch (err) { - return String.fromCharCode(0xFFFD) // UTF 8 invalid char - } -} - },{"base64-js":44,"ieee754":45,"is-array":46}],44:[function(require,module,exports){ var lookup = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; @@ -8328,7 +8449,9 @@ function drainQueue() { currentQueue = queue; queue = []; while (++queueIndex < len) { - currentQueue[queueIndex].run(); + if (currentQueue) { + currentQueue[queueIndex].run(); + } } queueIndex = -1; len = queue.length; @@ -8380,7 +8503,6 @@ process.binding = function (name) { throw new Error('process.binding is not supported'); }; -// TODO(shtylman) process.cwd = function () { return '/' }; process.chdir = function (dir) { throw new Error('process.chdir is not supported');