From 19715612cbc131b0c5e2dff41d225eb6d638c1fb Mon Sep 17 00:00:00 2001 From: Grant Snodgrass Date: Mon, 14 Nov 2016 19:11:26 -0500 Subject: [PATCH] docs: rewrite `.throw` jsdoc --- lib/chai/core/assertions.js | 124 ++++++++++++++++++++++++++++++------ 1 file changed, 103 insertions(+), 21 deletions(-) diff --git a/lib/chai/core/assertions.js b/lib/chai/core/assertions.js index 10744ee..a2164af 100644 --- a/lib/chai/core/assertions.js +++ b/lib/chai/core/assertions.js @@ -1501,31 +1501,113 @@ module.exports = function (chai, _) { Assertion.addMethod('key', assertKeys); /** - * ### .throw(constructor) + * ### .throw([errorLike], [errMsgMatcher]) * - * Asserts that the target function will throw a specific error, or specific type of error - * (as determined using `instanceof`), optionally with a RegExp or String inclusion test - * for the error's message. - * If an Error instance is provided, it asserts that the error thrown and the instance - * provided are the same. - * If an Error constructor is provided, it asserts that the error thrown is an instance - * of the constructor provided. + * Invokes the target function and asserts that an error is thrown. The + * `throw` assertion accepts optional arguments that can be used to perform + * additional assertions on the thrown error, as explained below. * - * var err = new ReferenceError('This is a bad function.'); - * var fn = function () { throw err; } - * expect(fn).to.throw(); - * expect(fn).to.throw(ReferenceError); - * expect(fn).to.throw(Error); - * expect(fn).to.throw(/bad function/); - * expect(fn).to.not.throw('good function'); - * expect(fn).to.throw(ReferenceError, /bad function/); - * expect(fn).to.throw(err); + * If no arguments are provided, `throw` invokes the function and asserts that + * an error is thrown. * - * Furthermore, `throw` changes the context of the assertion (just like `property`) - * to the thrown error. This permits for further chainable assertions on the thrown error. + * var badFn = function () { throw new TypeError("Illegal salmon!"); }; + * expect(badFn).to.throw(); + * var goodFn = function () {}; + * expect(goodFn).to.not.throw(); * - * expect(fn).to.throw(ReferenceError) - * .and.have.property('message').equal('This is a bad function.'); + * If the first argument is an error constructor, `throw` invokes the function + * and asserts that an error is thrown that's an instance of that constructor. + * + * var badFn = function () { throw new TypeError("Illegal salmon!"); }; + * expect(badFn).to.throw(TypeError); + * expect(badFn).to.not.throw(ReferenceError); + * + * If the first argument is an error instance, `throw` invokes the function + * and asserts that an error is thrown that's referentially equal (===) to + * that instance. + * + * var err = new TypeError("Illegal salmon!"); + * var badFn = function () { throw err; }; + * expect(badFn).to.throw(err); + * expect(badFn).to.not.throw(new TypeError("Illegal salmon!")); + * + * If the first argument is a string, `throw` invokes the function and asserts + * that an error is thrown with a message that contains that string. + * + * var badFn = function () { throw new TypeError("Illegal salmon!"); }; + * expect(badFn).to.throw("salmon"); + * expect(badFn).to.not.throw("kangaroo"); + * + * If the first argument is a regular expression, `throw` invokes the function + * and asserts that an error is thrown with a message that matches that + * regular expression. + * + * var badFn = function () { throw new TypeError("Illegal salmon!"); }; + * expect(badFn).to.throw(/salmon/); + * expect(badFn).to.not.throw(/kangaroo/); + * + * It's also possible to combine two assertions into one by providing two + * arguments. If the first argument is an error instance or constructor, and + * the second argument is a string or regular expression, `throw` invokes the + * function and asserts that an error is thrown that passes both assertions as + * described above. + * + * var err = new TypeError("Illegal salmon!"); + * var badFn = function () { throw err; }; + * expect(badFn).to.throw(TypeError, "salmon"); + * expect(badFn).to.throw(TypeError, /salmon/); + * expect(badFn).to.throw(err, "salmon"); + * expect(badFn).to.throw(err, /salmon/); + * expect(badFn).to.not.throw(TypeError, "kangaroo"); + * expect(badFn).to.not.throw(TypeError, /kangaroo/); + * expect(badFn).to.not.throw(ReferenceError, "salmon"); + * expect(badFn).to.not.throw(ReferenceError, /salmon/); + * expect(badFn).to.not.throw(err, "kangaroo"); + * expect(badFn).to.not.throw(err, /kangaroo/); + * expect(badFn).to.not.throw(new TypeError("Illegal salmon!"), "salmon"); + * expect(badFn).to.not.throw(new TypeError("Illegal salmon!"), /salmon/); + * + * Note that `throw` changes the context of the assertion to the thrown error. + * This allows for further chainable assertions on the thrown error. + * + * var err = new TypeError("Illegal salmon!"); + * err.code = 42; + * var badFn = function () { throw err; }; + * expect(badFn).to.throw(TypeError).and.have.property('code', 42); + * + * 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: + * + * expect(fn).to.throw(); // Good! Tests `fn` as desired + * expect(fn()).to.throw(); // Bad! Tests result of `fn()`, not `fn` + * + * If you need to assert that your function `fn` throws when passed certain + * arguments, then wrap a call to `fn` inside of another function like so: + * + * expect(function () { fn(42); }).to.throw(); // Function expression + * expect(() => fn(42)).to.throw(); // ES6 arrow function + * + * Another common mistake is to provide an object method (or any stand-alone + * function that relies on `this`). Doing so is problematic because the `this` + * context will be lost when the function is invoked by the `throws` + * assertion; there's no way for it to know what `this` is supposed to be. + * There are two ways around this problem. One solution is to wrap the method + * or function call inside of another function. Another solution is to use + * `bind`. For example: + * + * expect(function () { cat.meow(); }).to.throw(); // Function expression + * expect(() => cat.meow()).to.throw(); // ES6 arrow function + * 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. * * @name throw * @alias throws