Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: rewrite .throw jsdoc #866

Merged
merged 1 commit into from Nov 15, 2016
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
132 changes: 107 additions & 25 deletions lib/chai/core/assertions.js
Expand Up @@ -1501,31 +1501,113 @@ module.exports = function (chai, _) {
Assertion.addMethod('key', assertKeys);

/**
* ### .throw(constructor)
*
* 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.
*
* 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);
*
* 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.
*
* expect(fn).to.throw(ReferenceError)
* .and.have.property('message').equal('This is a bad function.');
* ### .throw([errorLike], [errMsgMatcher])
*
* 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.
*
* If no arguments are provided, `throw` invokes the function and asserts that
* an error is thrown.
*
* var badFn = function () { throw new TypeError("Illegal salmon!"); };
* expect(badFn).to.throw();
* var goodFn = function () {};
* expect(goodFn).to.not.throw();
*
* 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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is all great. Very clear. Kudos.

*
* 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
Expand Down