diff --git a/lib/chai/core/assertions.js b/lib/chai/core/assertions.js index 3145e1ba8..622f76b5b 100644 --- a/lib/chai/core/assertions.js +++ b/lib/chai/core/assertions.js @@ -555,7 +555,7 @@ module.exports = function (chai, _) { try { propAssertion.property(prop, val[prop]); } catch (err) { - if (!_.checkError.compatibleConstructor(err, AssertionError)) { + if (!(err instanceof AssertionError)) { throw err; } if (firstErr === null) firstErr = err; @@ -2394,26 +2394,26 @@ module.exports = function (chai, _) { Assertion.addMethod('key', assertKeys); /** - * ### .throw([errorLike], [errMsgMatcher], [msg]) + * ### .throw([errLike[, errMsgMatcher[, msg]]]) * * When no arguments are provided, `.throw` invokes the target function and - * asserts that an error is thrown. + * asserts that it throws. * * var badFn = function () { throw new TypeError('Illegal salmon!'); }; * * expect(badFn).to.throw(); * - * When one argument is provided, and it's an error constructor, `.throw` - * invokes the target function and asserts that an error is thrown that's an - * instance of that error constructor. + * When one argument is provided, and it's an `Error` constructor, `.throw` + * invokes the target function and asserts that an `Error` is thrown that's an + * instance of that `Error` constructor. * * var badFn = function () { throw new TypeError('Illegal salmon!'); }; * * expect(badFn).to.throw(TypeError); * - * When one argument is provided, and it's an error instance, `.throw` invokes - * the target function and asserts that an error is thrown that's strictly - * (`===`) equal to that error instance. + * When one argument is provided, and it's an `Error` instance, `.throw` + * invokes the target function and asserts that an `Error` is thrown that's + * strictly (`===`) equal to that `Error` instance. * * var err = new TypeError('Illegal salmon!'); * var badFn = function () { throw err; }; @@ -2421,24 +2421,24 @@ module.exports = function (chai, _) { * expect(badFn).to.throw(err); * * When one argument is provided, and it's a string, `.throw` invokes the - * target function and asserts that an error is thrown with a message that - * contains that string. + * target function and asserts that an `Error` instance is thrown with a + * message that contains that string. * * var badFn = function () { throw new TypeError('Illegal salmon!'); }; * * expect(badFn).to.throw('salmon'); * * When one argument is provided, and it's a regular expression, `.throw` - * invokes the target function and asserts that an error is thrown with a - * message that matches that regular expression. + * invokes the target function and asserts that an `Error` instance is thrown + * with a message that matches that regular expression. * * var badFn = function () { throw new TypeError('Illegal salmon!'); }; * * expect(badFn).to.throw(/salmon/); * - * When two arguments are provided, and the first is an error instance or - * constructor, and the second is a string or regular expression, `.throw` - * invokes the function and asserts that an error is thrown that fulfills both + * When two arguments are provided, and the first is an `Error` constructor, + * and the second is a string or regular expression, `.throw` invokes the + * function and asserts that an `Error` instance is thrown that fulfills both * conditions as described above. * * var err = new TypeError('Illegal salmon!'); @@ -2446,8 +2446,16 @@ module.exports = function (chai, _) { * * expect(badFn).to.throw(TypeError, 'salmon'); * expect(badFn).to.throw(TypeError, /salmon/); - * expect(badFn).to.throw(err, 'salmon'); - * expect(badFn).to.throw(err, /salmon/); + * + * `.throw` changes the target of any assertions that follow in the chain to + * be the object that's thrown. This allows you to perform assertions on the + * thrown object. + * + * var err = new TypeError('Illegal salmon!'); + * err.code = 42; + * var badFn = function () { throw err; }; + * + * expect(badFn).to.throw(TypeError).with.property('code', 42); * * Add `.not` earlier in the chain to negate `.throw`. * @@ -2457,39 +2465,31 @@ module.exports = function (chai, _) { * * However, it's dangerous to negate `.throw` when providing any arguments. * The problem is that it creates uncertain expectations by asserting that the - * target either doesn't throw an error, or that it throws an error but of a - * different type than the given type, or that it throws an error of the given - * type but with a message that doesn't include the given string. It's often - * best to identify the exact output that's expected, and then write an - * assertion that only accepts that exact output. + * target either doesn't throw, or that it throws but it doesn't throw an + * instance of the given `Error` constructor, or that it throws an instance of + * the given `Error` constructor but with a message that doesn't include the + * given string. It's often best to identify the exact output that's expected, + * and then write an assertion that only accepts that exact output. * - * When the target isn't expected to throw an error, it's often best to assert - * exactly that. + * When the target isn't expected to throw, it's often best to assert exactly + * that. * * var goodFn = function () {}; * * expect(goodFn).to.not.throw(); // Recommended * expect(goodFn).to.not.throw(ReferenceError, 'x'); // Not recommended * - * When the target is expected to throw an error, it's often best to assert - * that the error is of its expected type, and has a message that includes an - * expected string, rather than asserting that it doesn't have one of many - * unexpected types, and doesn't have a message that includes some string. + * When the target is expected to throw, it's often best to assert that it + * throws an instance of a certain `Error` constructor, and has a message that + * includes a certain string, rather than asserting that it isn't an instance + * of one of countless `Error` constructors, and doesn't have a message that + * includes one of countless strings. * * var badFn = function () { throw new TypeError('Illegal salmon!'); }; * * expect(badFn).to.throw(TypeError, 'salmon'); // Recommended * expect(badFn).to.not.throw(ReferenceError, 'x'); // Not recommended * - * `.throw` changes the target of any assertions that follow in the chain to - * be the error object that's thrown. - * - * var err = new TypeError('Illegal salmon!'); - * err.code = 42; - * var badFn = function () { throw err; }; - * - * expect(badFn).to.throw(TypeError).with.property('code', 42); - * * `.throw` accepts an optional `msg` argument which is a custom error message * to show when the assertion fails. The message can also be given as the * second argument to `expect`. When not providing two arguments, always use @@ -2500,20 +2500,20 @@ module.exports = function (chai, _) { * expect(goodFn).to.throw(TypeError, 'x', 'nooo why fail??'); * expect(goodFn, 'nooo why fail??').to.throw(); * - * Due to limitations in ES5, `.throw` may not always work as expected when - * using a transpiler such as Babel or TypeScript. In particular, it may - * produce unexpected results when subclassing the built-in `Error` object and - * then passing the subclassed constructor to `.throw`. See your transpiler's - * docs for details: + * Due to limitations in ES5, using `.throw` with arguments may not always + * work as expected when using a transpiler such as Babel or TypeScript. In + * particular, it may produce unexpected results when subclassing the built-in + * `Error` constructor and then passing the subclassed constructor to + * `.throw`. See your transpiler's docs for details: * * - ([Babel](https://babeljs.io/docs/usage/caveats/#classes)) * - ([TypeScript](https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#extending-built-ins-like-error-array-and-map-may-no-longer-work)) * * Beware of some common mistakes when using the `throw` assertion. One common * mistake is to accidentally invoke the function yourself instead of letting - * the `throw` assertion invoke the function for you. For example, when - * testing if a function named `fn` throws, provide `fn` instead of `fn()` as - * the target for the assertion. + * `.throw` invoke the function for you. For example, when testing if a + * function named `fn` throws, provide `fn` instead of `fn()` as the target + * for the assertion. * * expect(fn).to.throw(); // Good! Tests `fn` as desired * expect(fn()).to.throw(); // Bad! Tests result of `fn()`, not `fn` @@ -2537,11 +2537,22 @@ module.exports = function (chai, _) { * expect(cat.meow.bind(cat)).to.throw(); // Bind * * Finally, it's worth mentioning that it's a best practice in JavaScript to - * only throw `Error` and derivatives of `Error` such as `ReferenceError`, - * `TypeError`, and user-defined objects that extend `Error`. No other type of - * value will generate a stack trace when initialized. With that said, the - * `throw` assertion does technically support any type of value being thrown, - * not just `Error` and its derivatives. + * only throw `Error` instances. This includes `Error` instances created from + * subclassed `Error` constructors such as `ReferenceError`, `TypeError`, and + * user-defined constructors that extend `Error`. No other type of value will + * generate a stack trace when initialized. With that said, it's still + * possible to assert on non-`Error` objects by using `.throw` with no + * arguments and then performing additional assertions later in the chain. + * + * function NonErrorConstructor(message) { + * this.message = message; + * } + * var nonErrorObject = new NonErrorConstructor('Illegal salmon!'); + * var badFn = function () { throw nonErrorObject; }; + * + * expect(badFn).to.throw() + * .an.instanceof(NonErrorConstructor) + * .with.property('message', 'Illegal salmon!'); * * The aliases `.throws` and `.Throw` can be used interchangeably with * `.throw`. @@ -2549,7 +2560,7 @@ module.exports = function (chai, _) { * @name throw * @alias throws * @alias Throw - * @param {Error|ErrorConstructor} errorLike + * @param {Error|ErrorConstructor} errLike * @param {String|RegExp} errMsgMatcher error message * @param {String} msg _optional_ * @see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error#Error_types @@ -2558,128 +2569,66 @@ module.exports = function (chai, _) { * @api public */ - function assertThrows (errorLike, errMsgMatcher, msg) { + function assertThrows (errLike, errMsgMatcher, msg) { if (msg) flag(this, 'message', msg); - var obj = flag(this, 'object') - , ssfi = flag(this, 'ssfi') - , flagMsg = flag(this, 'message') - , negate = flag(this, 'negate') || false; - new Assertion(obj, flagMsg, ssfi, true).is.a('function'); + _.expectTypes(this, ['function']); - if (errorLike instanceof RegExp || typeof errorLike === 'string') { - errMsgMatcher = errorLike; - errorLike = null; - } + var obj = flag(this, 'object'); + var criteria = _.checkError.createCriteria(errLike, errMsgMatcher); + // Note that any type of value can be thrown, even `undefined`, so it's + // unreliable to use the value of `caughtErr` to determine if an error was + // thrown. Instead, track if an error was thrown using a separate boolean. + var wasErrThrown = false; var caughtErr; try { obj(); } catch (err) { + wasErrThrown = true; caughtErr = err; } - // If we have the negate flag enabled and at least one valid argument it means we do expect an error - // but we want it to match a given set of criteria - var everyArgIsUndefined = errorLike === undefined && errMsgMatcher === undefined; - - // If we've got the negate flag enabled and both args, we should only fail if both aren't compatible - // See Issue #551 and PR #683@GitHub - var everyArgIsDefined = Boolean(errorLike && errMsgMatcher); - var errorLikeFail = false; - var errMsgMatcherFail = false; - - // Checking if error was thrown - if (everyArgIsUndefined || !everyArgIsUndefined && !negate) { - // We need this to display results correctly according to their types - var errorLikeString = 'an error'; - if (errorLike instanceof Error) { - errorLikeString = '#{exp}'; - } else if (errorLike) { - errorLikeString = _.checkError.getConstructorName(errorLike); - } + var failMsg = 'expected #{this} to throw'; + var negatedFailMsg = 'expected #{this} to not throw'; + // Short-circuit if we're only asserting whether or not a function throws + if ((errLike === undefined || errLike === null) && + (errMsgMatcher === undefined || errMsgMatcher === null)) { this.assert( - caughtErr - , 'expected #{this} to throw ' + errorLikeString - , 'expected #{this} to not throw an error but #{act} was thrown' - , errorLike && errorLike.toString() - , (caughtErr instanceof Error ? - caughtErr.toString() : (typeof caughtErr === 'string' ? caughtErr : caughtErr && - _.checkError.getConstructorName(caughtErr))) + wasErrThrown, + failMsg, + negatedFailMsg + ' but #{act} was thrown', + errLike || errMsgMatcher, + caughtErr ); - } - - if (errorLike && caughtErr) { - // We should compare instances only if `errorLike` is an instance of `Error` - if (errorLike instanceof Error) { - var isCompatibleInstance = _.checkError.compatibleInstance(caughtErr, errorLike); - if (isCompatibleInstance === negate) { - // These checks were created to ensure we won't fail too soon when we've got both args and a negate - // See Issue #551 and PR #683@GitHub - if (everyArgIsDefined && negate) { - errorLikeFail = true; - } else { - this.assert( - negate - , 'expected #{this} to throw #{exp} but #{act} was thrown' - , 'expected #{this} to not throw #{exp}' + (caughtErr && !negate ? ' but #{act} was thrown' : '') - , errorLike.toString() - , caughtErr.toString() - ); - } - } - } - - var isCompatibleConstructor = _.checkError.compatibleConstructor(caughtErr, errorLike); - if (isCompatibleConstructor === negate) { - if (everyArgIsDefined && negate) { - errorLikeFail = true; - } else { - this.assert( - negate - , 'expected #{this} to throw #{exp} but #{act} was thrown' - , 'expected #{this} to not throw #{exp}' + (caughtErr ? ' but #{act} was thrown' : '') - , (errorLike instanceof Error ? errorLike.toString() : errorLike && _.checkError.getConstructorName(errorLike)) - , (caughtErr instanceof Error ? caughtErr.toString() : caughtErr && _.checkError.getConstructorName(caughtErr)) - ); - } - } + flag(this, 'object', caughtErr); + return; } - if (caughtErr && errMsgMatcher !== undefined && errMsgMatcher !== null) { - // Here we check compatible messages - var placeholder = 'including'; - if (errMsgMatcher instanceof RegExp) { - placeholder = 'matching' - } - - var isCompatibleMessage = _.checkError.compatibleMessage(caughtErr, errMsgMatcher); - if (isCompatibleMessage === negate) { - if (everyArgIsDefined && negate) { - errMsgMatcherFail = true; - } else { - this.assert( - negate - , 'expected #{this} to throw error ' + placeholder + ' #{exp} but got #{act}' - , 'expected #{this} to throw error not ' + placeholder + ' #{exp}' - , errMsgMatcher - , _.checkError.getMessage(caughtErr) - ); - } - } + var expectedDesc = _.checkError.describeExpectedError(criteria); + // We append the expected value instead of using #{exp} because #{exp} adds + // single quotes around the expected value, even when it doesn't make sense + // (e.g., "expected [Function] to throw 'a TypeError'"). + failMsg += ' ' + expectedDesc; + negatedFailMsg += ' ' + expectedDesc; + + if (wasErrThrown) { + failMsg += ' but #{act} was thrown'; + if (caughtErr !== errLike) { + negatedFailMsg += ' but #{act} was thrown'; + } // Else, it's implicit and doesn't need to be added. + } else { + failMsg += ' but no error was thrown'; } - // If both assertions failed and both should've matched we throw an error - if (errorLikeFail && errMsgMatcherFail) { - this.assert( - negate - , 'expected #{this} to throw #{exp} but #{act} was thrown' - , 'expected #{this} to not throw #{exp}' + (caughtErr ? ' but #{act} was thrown' : '') - , (errorLike instanceof Error ? errorLike.toString() : errorLike && _.checkError.getConstructorName(errorLike)) - , (caughtErr instanceof Error ? caughtErr.toString() : caughtErr && _.checkError.getConstructorName(caughtErr)) - ); - } + this.assert( + wasErrThrown && _.checkError.checkError(caughtErr, criteria), + failMsg, + negatedFailMsg, + errLike || errMsgMatcher, + caughtErr + ); flag(this, 'object', caughtErr); }; diff --git a/lib/chai/interface/assert.js b/lib/chai/interface/assert.js index 59e5f8b66..1488c99b2 100644 --- a/lib/chai/interface/assert.js +++ b/lib/chai/interface/assert.js @@ -1962,11 +1962,6 @@ module.exports = function (chai, util) { */ assert.throws = function (fn, errorLike, errMsgMatcher, msg) { - if ('string' === typeof errorLike || errorLike instanceof RegExp) { - errMsgMatcher = errorLike; - errorLike = null; - } - var assertErr = new Assertion(fn, msg, assert.throws, true) .to.throw(errorLike, errMsgMatcher); return flag(assertErr, 'object'); @@ -2002,11 +1997,6 @@ module.exports = function (chai, util) { */ assert.doesNotThrow = function (fn, errorLike, errMsgMatcher, msg) { - if ('string' === typeof errorLike || errorLike instanceof RegExp) { - errMsgMatcher = errorLike; - errorLike = null; - } - new Assertion(fn, msg, assert.doesNotThrow, true) .to.not.throw(errorLike, errMsgMatcher); }; diff --git a/package-lock.json b/package-lock.json index 49f3d3bd2..b7f956527 100644 --- a/package-lock.json +++ b/package-lock.json @@ -731,9 +731,18 @@ } }, "check-error": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", - "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=" + "version": "git+https://github.com/meeber/check-error.git#bba0681407449fc3175a967307e153c4a6508683", + "requires": { + "get-func-name": "1.0.0", + "type-detect": "4.0.3" + }, + "dependencies": { + "get-func-name": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-1.0.0.tgz", + "integrity": "sha1-1k442o5FrLdGcmBJ82vviev6kcI=" + } + } }, "chokidar": { "version": "1.7.0", diff --git a/package.json b/package.json index 40acbcc4f..7e769cd43 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,7 @@ }, "dependencies": { "assertion-error": "^1.0.1", - "check-error": "^1.0.1", + "check-error": "git+https://github.com/meeber/check-error.git#add-check-error-function", "deep-eql": "^3.0.1", "get-func-name": "^2.0.0", "pathval": "^1.0.0", diff --git a/test/assert.js b/test/assert.js index 5197f2ef4..6a4bc6788 100644 --- a/test/assert.js +++ b/test/assert.js @@ -1588,41 +1588,60 @@ describe('assert', function () { assert(thrownErr instanceof Error, 'assert.' + throws + ' returns error'); assert(thrownErr.message === 'foo', 'assert.' + throws + ' returns error message'); + var notErrFn = function () { throw { message: 'testing' }; } + assert[throws](notErrFn); + err(function () { assert[throws](function() { throw new Error('foo') }, TypeError); - }, "expected [Function] to throw 'TypeError' but 'Error: foo' was thrown") + }, "expected [Function] to throw a TypeError but [Error: foo] was thrown") err(function () { assert[throws](function() { throw new Error('foo') }, 'bar'); - }, "expected [Function] to throw error including 'bar' but got 'foo'") + }, "expected [Function] to throw an Error including 'bar' but [Error: foo] was thrown") err(function () { assert[throws](function() { throw new Error('foo') }, Error, 'bar', 'blah'); - }, "blah: expected [Function] to throw error including 'bar' but got 'foo'") + }, "blah: expected [Function] to throw an Error including 'bar' but [Error: foo] was thrown") err(function () { assert[throws](function() { throw new Error('foo') }, TypeError, 'bar', 'blah'); - }, "blah: expected [Function] to throw 'TypeError' but 'Error: foo' was thrown") + }, "blah: expected [Function] to throw a TypeError including 'bar' but [Error: foo] was thrown") err(function () { assert[throws](function() {}); - }, "expected [Function] to throw an error"); + }, "expected [Function] to throw"); err(function () { assert[throws](function() { throw new Error('') }, 'bar'); - }, "expected [Function] to throw error including 'bar' but got ''"); + }, "expected [Function] to throw an Error including 'bar' but [Error] was thrown"); err(function () { assert[throws](function() { throw new Error('') }, /bar/); - }, "expected [Function] to throw error matching /bar/ but got ''"); + }, "expected [Function] to throw an Error matching /bar/ but [Error] was thrown"); err(function () { assert[throws]({}); - }, "expected {} to be a function"); + }, "object tested must be a function, but object given"); err(function () { assert[throws]({}, Error, 'testing', 'blah'); - }, "blah: expected {} to be a function"); + }, "blah: object tested must be a function, but object given"); + + err(function () { + assert[throws](function() {}, 'testing', 'testing'); + }, "errMsgMatcher must be null or undefined when errLike is a string or regular expression", true); + + err(function () { + assert[throws](function() {}, {}); + }, "errLike must be an Error constructor or instance", true); + + err(function () { + assert[throws](function() {}, Error, {}, 'blah'); + }, "errMsgMatcher must be a string or regular expression", true); + + err(function () { + assert[throws](function() {}, new Error(), 'testing', 'blah'); + }, "errMsgMatcher must be null or undefined when errLike is an Error instance", true); }); }); @@ -1665,53 +1684,73 @@ describe('assert', function () { throw new Error('This is a message'); }, TypeError, /Another message/); + var notErrFn = function () { throw { message: 'testing' }; } + assert.doesNotThrow(notErrFn, Error); + assert.doesNotThrow(notErrFn, 'testing'); + err(function () { assert.doesNotThrow(function() { throw new Error('foo'); }); - }, "expected [Function] to not throw an error but 'Error: foo' was thrown"); + }, "expected [Function] to not throw but [Error: foo] was thrown"); err(function () { assert.doesNotThrow(function() { throw new CustomError('foo'); }); - }, "expected [Function] to not throw an error but 'CustomError: foo' was thrown"); + }, /expected \[Function\] to not throw but {.*name.*message.*} was thrown/); err(function () { assert.doesNotThrow(function() { throw new Error('foo'); }, Error); - }, "expected [Function] to not throw 'Error' but 'Error: foo' was thrown"); + }, "expected [Function] to not throw an Error but [Error: foo] was thrown"); err(function () { assert.doesNotThrow(function() { throw new CustomError('foo'); }, CustomError); - }, "expected [Function] to not throw 'CustomError' but 'CustomError: foo' was thrown"); + }, /expected \[Function\] to not throw a CustomError but {.*name.*message.*} was thrown/); err(function () { assert.doesNotThrow(function() { throw new Error('foo'); }, 'foo'); - }, "expected [Function] to throw error not including 'foo'"); + }, "expected [Function] to not throw an Error including 'foo' but [Error: foo] was thrown"); err(function () { assert.doesNotThrow(function() { throw new Error('foo'); }, /foo/); - }, "expected [Function] to throw error not matching /foo/"); + }, "expected [Function] to not throw an Error matching /foo/ but [Error: foo] was thrown"); err(function () { assert.doesNotThrow(function() { throw new Error('foo'); }, Error, 'foo', 'blah'); - }, "blah: expected [Function] to not throw 'Error' but 'Error: foo' was thrown"); + }, "blah: expected [Function] to not throw an Error including 'foo' but [Error: foo] was thrown"); err(function () { assert.doesNotThrow(function() { throw new CustomError('foo'); }, CustomError, 'foo', 'blah'); - }, "blah: expected [Function] to not throw 'CustomError' but 'CustomError: foo' was thrown"); + }, /blah: expected \[Function\] to not throw a CustomError including 'foo' but {.*name.*message.*} was thrown/); err(function () { assert.doesNotThrow(function() { throw new Error(''); }, ''); - }, "expected [Function] to throw error not including ''"); + }, "expected [Function] to not throw an Error including '' but [Error] was thrown"); err(function () { assert.doesNotThrow(function() { throw new Error(''); }, Error, ''); - }, "expected [Function] to not throw 'Error' but 'Error' was thrown"); + }, "expected [Function] to not throw an Error including '' but [Error] was thrown"); err(function () { assert.doesNotThrow({}); - }, "expected {} to be a function"); + }, "object tested must be a function, but object given"); err(function () { assert.doesNotThrow({}, Error, 'testing', 'blah'); - }, "blah: expected {} to be a function"); + }, "blah: object tested must be a function, but object given"); + + err(function () { + assert.doesNotThrow(function() {}, 'testing', 'testing'); + }, "errMsgMatcher must be null or undefined when errLike is a string or regular expression", true); + + err(function () { + assert.doesNotThrow(function() {}, {}); + }, "errLike must be an Error constructor or instance", true); + + err(function () { + assert.doesNotThrow(function() {}, Error, {}); + }, "errMsgMatcher must be a string or regular expression", true); + + err(function () { + assert.doesNotThrow(function() {}, new Error(), 'testing'); + }, "errMsgMatcher must be null or undefined when errLike is an Error instance", true); }); it('ifError', function() { diff --git a/test/expect.js b/test/expect.js index bb4fcbfca..c5cff90be 100644 --- a/test/expect.js +++ b/test/expect.js @@ -2722,9 +2722,9 @@ describe('expect', function () { , ickyErrFn = function () { throw new PoorlyConstructedError(); } , specificErrFn = function () { throw specificError; } , customErrFn = function() { throw new CustomError('foo'); } + , notErrFn = function () { throw { message: 'testing' }; } , emptyErrFn = function () { throw new Error(); } , emptyStringErrFn = function () { throw new Error(''); }; - expect(goodFn).to.not.throw(); expect(goodFn).to.not.throw(Error); expect(goodFn).to.not.throw(specificError); @@ -2760,113 +2760,133 @@ describe('expect', function () { expect(badFn).to.not.throw(Error, 'I am the wrong error message'); expect(badFn).to.not.throw(TypeError, 'testing'); + expect(notErrFn).to.throw(); + expect(notErrFn).to.not.throw(Error); + expect(notErrFn).to.not.throw('testing'); + err(function(){ expect(goodFn, 'blah').to.throw(); - }, /^blah: expected \[Function(: goodFn)*\] to throw an error$/); + }, /^blah: expected \[Function(: goodFn)*\] to throw$/); err(function(){ expect(goodFn, 'blah').to.throw(ReferenceError); - }, /^blah: expected \[Function(: goodFn)*\] to throw ReferenceError$/); + }, /^blah: expected \[Function(: goodFn)*\] to throw a ReferenceError but no error was thrown$/); err(function(){ expect(goodFn, 'blah').to.throw(specificError); - }, /^blah: expected \[Function(: goodFn)*\] to throw 'RangeError: boo'$/); + }, /^blah: expected \[Function(: goodFn)*\] to throw \[RangeError: boo\] but no error was thrown$/); err(function(){ expect(badFn, 'blah').to.not.throw(); - }, /^blah: expected \[Function(: badFn)*\] to not throw an error but 'Error: testing' was thrown$/); + }, /^blah: expected \[Function(: badFn)*\] to not throw but \[Error: testing\] was thrown$/); err(function(){ expect(badFn, 'blah').to.throw(ReferenceError); - }, /^blah: expected \[Function(: badFn)*\] to throw 'ReferenceError' but 'Error: testing' was thrown$/); + }, /^blah: expected \[Function(: badFn)*\] to throw a ReferenceError but \[Error: testing\] was thrown$/); err(function(){ expect(badFn, 'blah').to.throw(specificError); - }, /^blah: expected \[Function(: badFn)*\] to throw 'RangeError: boo' but 'Error: testing' was thrown$/); + }, /^blah: expected \[Function(: badFn)*\] to throw \[RangeError: boo\] but \[Error: testing\] was thrown$/); err(function(){ expect(badFn, 'blah').to.not.throw(Error); - }, /^blah: expected \[Function(: badFn)*\] to not throw 'Error' but 'Error: testing' was thrown$/); + }, /^blah: expected \[Function(: badFn)*\] to not throw an Error but \[Error: testing\] was thrown$/); err(function(){ expect(refErrFn, 'blah').to.not.throw(ReferenceError); - }, /^blah: expected \[Function(: refErrFn)*\] to not throw 'ReferenceError' but 'ReferenceError: hello' was thrown$/); + }, /^blah: expected \[Function(: refErrFn)*\] to not throw a ReferenceError but \[ReferenceError: hello\] was thrown$/); err(function(){ expect(badFn, 'blah').to.throw(PoorlyConstructedError); - }, /^blah: expected \[Function(: badFn)*\] to throw 'PoorlyConstructedError' but 'Error: testing' was thrown$/); + }, /^blah: expected \[Function(: badFn)*\] to throw a PoorlyConstructedError but \[Error: testing\] was thrown$/); err(function(){ expect(ickyErrFn, 'blah').to.not.throw(PoorlyConstructedError); - }, /^blah: (expected \[Function(: ickyErrFn)*\] to not throw 'PoorlyConstructedError' but)(.*)(PoorlyConstructedError|\{ Object \()(.*)(was thrown)$/); + }, /^blah: expected \[Function(: ickyErrFn)*\] to not throw a PoorlyConstructedError but {.*name.*} was thrown$/); err(function(){ expect(ickyErrFn, 'blah').to.throw(ReferenceError); - }, /^blah: (expected \[Function(: ickyErrFn)*\] to throw 'ReferenceError' but)(.*)(PoorlyConstructedError|\{ Object \()(.*)(was thrown)$/); + }, /^blah: expected \[Function(: ickyErrFn)*\] to throw a ReferenceError but {.*name.*} was thrown$/); err(function(){ expect(specificErrFn, 'blah').to.throw(new ReferenceError('eek')); - }, /^blah: expected \[Function(: specificErrFn)*\] to throw 'ReferenceError: eek' but 'RangeError: boo' was thrown$/); + }, /^blah: expected \[Function(: specificErrFn)*\] to throw \[ReferenceError: eek\] but \[RangeError: boo\] was thrown$/); err(function(){ expect(specificErrFn, 'blah').to.not.throw(specificError); - }, /^blah: expected \[Function(: specificErrFn)*\] to not throw 'RangeError: boo'$/); + }, /^blah: expected \[Function(: specificErrFn)*\] to not throw \[RangeError: boo\]$/); err(function (){ expect(badFn, 'blah').to.not.throw(/testing/); - }, /^blah: expected \[Function(: badFn)*\] to throw error not matching \/testing\/$/); + }, /^blah: expected \[Function(: badFn)*\] to not throw an Error matching \/testing\/ but \[Error: testing\] was thrown$/); err(function () { expect(badFn, 'blah').to.throw(/hello/); - }, /^blah: expected \[Function(: badFn)*\] to throw error matching \/hello\/ but got 'testing'$/); + }, /^blah: expected \[Function(: badFn)*\] to throw an Error matching \/hello\/ but \[Error: testing\] was thrown$/); err(function () { expect(badFn).to.throw(Error, /hello/, 'blah'); - }, /^blah: expected \[Function(: badFn)*\] to throw error matching \/hello\/ but got 'testing'$/); + }, /^blah: expected \[Function(: badFn)*\] to throw an Error matching \/hello\/ but \[Error: testing\] was thrown$/); err(function () { expect(badFn, 'blah').to.throw(Error, /hello/); - }, /^blah: expected \[Function(: badFn)*\] to throw error matching \/hello\/ but got 'testing'$/); + }, /^blah: expected \[Function(: badFn)*\] to throw an Error matching \/hello\/ but \[Error: testing\] was thrown$/); err(function () { expect(badFn).to.throw(Error, 'hello', 'blah'); - }, /^blah: expected \[Function(: badFn)*\] to throw error including 'hello' but got 'testing'$/); + }, /^blah: expected \[Function(: badFn)*\] to throw an Error including 'hello' but \[Error: testing\] was thrown$/); err(function () { expect(badFn, 'blah').to.throw(Error, 'hello'); - }, /^blah: expected \[Function(: badFn)*\] to throw error including 'hello' but got 'testing'$/); + }, /^blah: expected \[Function(: badFn)*\] to throw an Error including 'hello' but \[Error: testing\] was thrown$/); err(function () { expect(customErrFn, 'blah').to.not.throw(); - }, /^blah: expected \[Function(: customErrFn)*\] to not throw an error but 'CustomError: foo' was thrown$/); + }, /^blah: expected \[Function(: customErrFn)*\] to not throw but {.*name.*message.*} was thrown$/); err(function(){ expect(badFn).to.not.throw(Error, 'testing', 'blah'); - }, /^blah: expected \[Function(: badFn)*\] to not throw 'Error' but 'Error: testing' was thrown$/); + }, /^blah: expected \[Function(: badFn)*\] to not throw an Error including 'testing' but \[Error: testing\] was thrown$/); err(function(){ expect(badFn, 'blah').to.not.throw(Error, 'testing'); - }, /^blah: expected \[Function(: badFn)*\] to not throw 'Error' but 'Error: testing' was thrown$/); + }, /^blah: expected \[Function(: badFn)*\] to not throw an Error including 'testing' but \[Error: testing\] was thrown$/); err(function(){ expect(emptyStringErrFn).to.not.throw(Error, '', 'blah'); - }, /^blah: expected \[Function(: emptyStringErrFn)*\] to not throw 'Error' but 'Error' was thrown$/); + }, /^blah: expected \[Function(: emptyStringErrFn)*\] to not throw an Error including '' but \[Error\] was thrown$/); err(function(){ expect(emptyStringErrFn, 'blah').to.not.throw(Error, ''); - }, /^blah: expected \[Function(: emptyStringErrFn)*\] to not throw 'Error' but 'Error' was thrown$/); + }, /^blah: expected \[Function(: emptyStringErrFn)*\] to not throw an Error including '' but \[Error\] was thrown$/); err(function(){ expect(emptyStringErrFn, 'blah').to.not.throw(''); - }, /^blah: expected \[Function(: emptyStringErrFn)*\] to throw error not including ''$/); + }, /^blah: expected \[Function(: emptyStringErrFn)*\] to not throw an Error including '' but \[Error\] was thrown$/); err(function () { expect({}, 'blah').to.throw(); - }, "blah: expected {} to be a function"); + }, "blah: object tested must be a function, but object given"); err(function () { expect({}).to.throw(Error, 'testing', 'blah'); - }, "blah: expected {} to be a function"); + }, "blah: object tested must be a function, but object given"); + + err(function () { + expect(goodFn).to.throw('testing', 'testing'); + }, "errMsgMatcher must be null or undefined when errLike is a string or regular expression", true); + + err(function () { + expect(goodFn).to.throw({}); + }, "errLike must be an Error constructor or instance", true); + + err(function () { + expect(goodFn).to.throw(Error, {}); + }, "errMsgMatcher must be a string or regular expression", true); + + err(function () { + expect(goodFn).to.throw(specificError, 'testing'); + }, "errMsgMatcher must be null or undefined when errLike is an Error instance", true); }); it('respondTo', function(){ diff --git a/test/should.js b/test/should.js index 2b62af318..652c3b7db 100644 --- a/test/should.js +++ b/test/should.js @@ -279,11 +279,11 @@ describe('should', function() { err(function () { should.Throw(function () { throw new Error('error!') }, Error, 'needed user!', 'blah'); - }, "blah: expected [Function] to throw error including 'needed user!' but got 'error!'"); + }, "blah: expected [Function] to throw an Error including 'needed user!' but [Error: error!] was thrown"); err(function () { should.not.Throw(function () { throw new Error('error!') }, Error, 'error!', 'blah'); - }, "blah: expected [Function] to not throw 'Error' but 'Error: error!' was thrown"); + }, "blah: expected [Function] to not throw an Error including 'error!' but [Error: error!] was thrown"); }); it('true', function(){ @@ -2296,6 +2296,7 @@ describe('should', function() { , ickyErrFn = function () { throw new PoorlyConstructedError(); } , specificErrFn = function () { throw specificError; } , customErrFn = function () { throw new CustomError('foo'); } + , notErrFn = function () { throw { message: 'testing' }; } , emptyErrFn = function () { throw new Error(); } , emptyStringErrFn = function () { throw new Error(''); }; @@ -2334,11 +2335,6 @@ describe('should', function() { (badFn).should.throw(Error, 'testing'); (emptyErrFn).should.not.throw(Error, 'testing'); - (stringErrFn).should.throw(/testing/); - (stringErrFn).should.throw('testing'); - (stringErrFn).should.not.throw(/hello/); - (stringErrFn).should.not.throw('hello'); - should.throw(badFn); should.throw(refErrFn, ReferenceError); should.throw(refErrFn, Error); @@ -2354,113 +2350,129 @@ describe('should', function() { (badFn).should.not.throw(Error, 'I am the wrong error message'); (badFn).should.not.throw(TypeError, 'testing'); + notErrFn.should.throw(); + notErrFn.should.not.throw(Error); + notErrFn.should.not.throw('testing'); + err(function(){ (goodFn).should.throw(); - }, /^expected \[Function(: goodFn)*\] to throw an error$/); + }, /^expected \[Function(: goodFn)*\] to throw$/); err(function(){ (goodFn).should.throw(ReferenceError); - }, /^expected \[Function(: goodFn)*\] to throw ReferenceError$/); + }, /^expected \[Function(: goodFn)*\] to throw a ReferenceError but no error was thrown$/); err(function(){ (goodFn).should.throw(specificError); - }, /^expected \[Function(: goodFn)*\] to throw 'RangeError: boo'$/); + }, /^expected \[Function(: goodFn)*\] to throw \[RangeError: boo\] but no error was thrown$/); err(function(){ (badFn).should.not.throw(); - }, /^expected \[Function(: badFn)*\] to not throw an error but 'Error: testing' was thrown$/); + }, /^expected \[Function(: badFn)*\] to not throw but \[Error: testing\] was thrown$/); err(function(){ (badFn).should.throw(ReferenceError); - }, /^expected \[Function(: badFn)*\] to throw 'ReferenceError' but 'Error: testing' was thrown$/); + }, /^expected \[Function(: badFn)*\] to throw a ReferenceError but \[Error: testing\] was thrown$/); err(function(){ (badFn).should.throw(specificError); - }, /^expected \[Function(: badFn)*\] to throw 'RangeError: boo' but 'Error: testing' was thrown$/); + }, /^expected \[Function(: badFn)*\] to throw \[RangeError: boo\] but \[Error: testing\] was thrown$/); err(function(){ (badFn).should.not.throw(Error); - }, /^expected \[Function(: badFn)*\] to not throw 'Error' but 'Error: testing' was thrown$/); + }, /^expected \[Function(: badFn)*\] to not throw an Error but \[Error: testing\] was thrown$/); err(function(){ (stringErrFn).should.not.throw(); - }, /^expected \[Function(: stringErrFn)*\] to not throw an error but 'testing' was thrown$/); + }, /^expected \[Function(: stringErrFn)*\] to not throw but 'testing' was thrown$/); err(function(){ (stringErrFn).should.throw(ReferenceError); - }, /^expected \[Function(: stringErrFn)*\] to throw 'ReferenceError' but 'testing' was thrown$/); + }, /^expected \[Function(: stringErrFn)*\] to throw a ReferenceError but 'testing' was thrown$/); err(function(){ (stringErrFn).should.throw(specificError); - }, /^expected \[Function(: stringErrFn)*\] to throw 'RangeError: boo' but 'testing' was thrown$/); - - err(function(){ - (stringErrFn).should.not.throw('testing'); - }, /^expected \[Function(: stringErrFn)*\] to throw error not including 'testing'$/); + }, /^expected \[Function(: stringErrFn)*\] to throw \[RangeError: boo\] but 'testing' was thrown$/); err(function(){ (refErrFn).should.not.throw(ReferenceError); - }, /^expected \[Function(: refErrFn)*\] to not throw 'ReferenceError' but 'ReferenceError: hello' was thrown$/); + }, /^expected \[Function(: refErrFn)*\] to not throw a ReferenceError but \[ReferenceError: hello\] was thrown$/); err(function(){ (badFn).should.throw(PoorlyConstructedError); - }, /^expected \[Function(: badFn)*\] to throw 'PoorlyConstructedError' but 'Error: testing' was thrown$/); + }, /^expected \[Function(: badFn)*\] to throw a PoorlyConstructedError but \[Error: testing\] was thrown$/); err(function(){ (ickyErrFn).should.not.throw(PoorlyConstructedError); - }, /^(expected \[Function(: ickyErrFn)*\] to not throw 'PoorlyConstructedError' but)(.*)(PoorlyConstructedError|\{ Object \()(.*)(was thrown)$/); + }, /^expected \[Function(: ickyErrFn)*\] to not throw a PoorlyConstructedError but {.*name.*} was thrown$/); err(function(){ (ickyErrFn).should.throw(ReferenceError); - }, /^(expected \[Function(: ickyErrFn)*\] to throw 'ReferenceError' but)(.*)(PoorlyConstructedError|\{ Object \()(.*)(was thrown)$/); + }, /^expected \[Function(: ickyErrFn)*\] to throw a ReferenceError but {.*name.*} was thrown$/); err(function(){ (specificErrFn).should.throw(new ReferenceError('eek')); - }, /^expected \[Function(: specificErrFn)*\] to throw 'ReferenceError: eek' but 'RangeError: boo' was thrown$/); + }, /^expected \[Function(: specificErrFn)*\] to throw \[ReferenceError: eek\] but \[RangeError: boo\] was thrown$/); err(function(){ (specificErrFn).should.not.throw(specificError); - }, /^expected \[Function(: specificErrFn)*\] to not throw 'RangeError: boo'$/); + }, /^expected \[Function(: specificErrFn)*\] to not throw \[RangeError: boo\]$/); err(function (){ (badFn).should.not.throw(/testing/); - }, /^expected \[Function(: badFn)*\] to throw error not matching \/testing\/$/); + }, /^expected \[Function(: badFn)*\] to not throw an Error matching \/testing\/ but \[Error: testing\] was thrown$/); err(function () { (badFn).should.throw(/hello/); - }, /^expected \[Function(: badFn)*\] to throw error matching \/hello\/ but got \'testing\'$/); + }, /^expected \[Function(: badFn)*\] to throw an Error matching \/hello\/ but \[Error: testing\] was thrown$/); err(function () { (badFn).should.throw(Error, /hello/, 'blah'); - }, /^blah: expected \[Function(: badFn)*\] to throw error matching \/hello\/ but got 'testing'$/); + }, /^blah: expected \[Function(: badFn)*\] to throw an Error matching \/hello\/ but \[Error: testing\] was thrown$/); err(function () { (badFn).should.throw(Error, 'hello', 'blah'); - }, /^blah: expected \[Function(: badFn)*\] to throw error including 'hello' but got 'testing'$/); + }, /^blah: expected \[Function(: badFn)*\] to throw an Error including 'hello' but \[Error: testing\] was thrown$/); err(function () { (customErrFn).should.not.throw(); - }, /^expected \[Function(: customErrFn)*\] to not throw an error but 'CustomError: foo' was thrown$/); + }, /^expected \[Function(: customErrFn)*\] to not throw but {.*name.*message.*} was thrown$/); err(function(){ (badFn).should.not.throw(Error, 'testing'); - }, /^expected \[Function(: badFn)*\] to not throw 'Error' but 'Error: testing' was thrown$/); + }, /^expected \[Function(: badFn)*\] to not throw an Error including 'testing' but \[Error: testing\] was thrown$/); err(function(){ (emptyStringErrFn).should.not.throw(Error, ''); - }, /^expected \[Function(: emptyStringErrFn)*\] to not throw 'Error' but 'Error' was thrown$/); + }, /^expected \[Function(: emptyStringErrFn)*\] to not throw an Error including '' but \[Error\] was thrown$/); err(function(){ (emptyStringErrFn).should.not.throw(''); - }, /^expected \[Function(: emptyStringErrFn)*\] to throw error not including ''$/); + }, /^expected \[Function(: emptyStringErrFn)*\] to not throw an Error including '' but \[Error\] was thrown$/); err(function () { ({}).should.throw(); - }, "expected {} to be a function"); + }, "object tested must be a function, but object given"); err(function () { ({}).should.throw(Error, 'testing', 'blah'); - }, "blah: expected {} to be a function"); + }, "blah: object tested must be a function, but object given"); + + err(function () { + goodFn.should.throw('testing', 'testing'); + }, "errMsgMatcher must be null or undefined when errLike is a string or regular expression", true); + + err(function () { + goodFn.should.throw({}); + }, "errLike must be an Error constructor or instance", true); + + err(function () { + goodFn.should.throw(Error, {}); + }, "errMsgMatcher must be a string or regular expression", true); + + err(function () { + goodFn.should.throw(specificError, 'testing'); + }, "errMsgMatcher must be null or undefined when errLike is an Error instance", true); }); it('respondTo', function(){