chai-as-promised 5.0.0 → 6.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE.txt CHANGED
@@ -1,4 +1,4 @@
1
- Copyright © 2012–2015 Domenic Denicola <d@domenic.me>
1
+ Copyright © 2012–2016 Domenic Denicola <d@domenic.me>
2
2
 
3
3
  This work is free. You can redistribute it and/or modify it under the
4
4
  terms of the Do What The Fuck You Want To Public License, Version 2,
package/README.md CHANGED
@@ -27,19 +27,19 @@ you can write code that expresses what you really mean:
27
27
  return doSomethingAsync().should.eventually.equal("foo");
28
28
  ```
29
29
 
30
- or if you have a testing framework that doesn't allow returning promises to signal asynchronous test completion, then
31
- you can use the following workaround:
30
+ or if you have a case where `return` is not preferable (e.g. style considerations) or not possible (e.g. the testing framework doesn't allow returning promises to signal asynchronous test completion), then you can use the following workaround (where `done()` is supplied by the test framework):
32
31
 
33
32
  ```javascript
34
33
  doSomethingAsync().should.eventually.equal("foo").notify(done);
35
34
  ```
36
35
 
36
+ *Notice*: either `return` or `notify(done)` _must_ be used with promise assertions. This can be a slight departure from the existing format of assertions being used on a project or by a team. Those other assertions are likely synchronous and thus do not require special handling.
37
+
37
38
  ## How to Use
38
39
 
39
40
  ### `should`/`expect` Interface
40
41
 
41
- The most powerful extension provided by Chai as Promised is the `eventually` property. With it, you can transform any
42
- existing Chai assertion into one that acts on a promise:
42
+ The most powerful extension provided by Chai as Promised is the `eventually` property. With it, you can transform any existing Chai assertion into one that acts on a promise:
43
43
 
44
44
  ```javascript
45
45
  (2 + 2).should.equal(4);
@@ -66,8 +66,7 @@ return promise.should.be.rejectedWith(Error); // other variants of Chai's `throw
66
66
 
67
67
  ### `assert` Interface
68
68
 
69
- As with the `should`/`expect` interface, Chai as Promised provides an `eventually` extender to `chai.assert`, allowing
70
- any existing Chai assertion to be used on a promise:
69
+ As with the `should`/`expect` interface, Chai as Promised provides an `eventually` extender to `chai.assert`, allowing any existing Chai assertion to be used on a promise:
71
70
 
72
71
  ```javascript
73
72
  assert.equal(2 + 2, 4, "This had better be true");
@@ -91,9 +90,7 @@ return assert.isRejected(promise, /error message matcher/, "optional message");
91
90
 
92
91
  ### Progress Callbacks
93
92
 
94
- Chai as Promised does not have any intrinsic support for testing promise progress callbacks. The properties you would
95
- want to test are probably much better suited to a library like [Sinon.JS][sinon], perhaps in conjunction with
96
- [Sinon–Chai][sinon-chai]:
93
+ Chai as Promised does not have any intrinsic support for testing promise progress callbacks. The properties you would want to test are probably much better suited to a library like [Sinon.JS][sinon], perhaps in conjunction with [Sinon–Chai][sinon-chai]:
97
94
 
98
95
  ```javascript
99
96
  var progressSpy = sinon.spy();
@@ -107,10 +104,7 @@ return promise.then(null, null, progressSpy).then(function () {
107
104
 
108
105
  ### Customizing Output Promises
109
106
 
110
- By default, the promises returned by Chai as Promised's assertions are regular Chai assertion objects, extended with
111
- a single `then` method derived from the input promise. To change this behavior, for instance to output a promise with
112
- more useful sugar methods such as are found in most promise libraries, you can override
113
- `chaiAsPromised.transferPromiseness`. Here's an example that transfer's Q's `finally` and `done` methods:
107
+ By default, the promises returned by Chai as Promised's assertions are regular Chai assertion objects, extended with a single `then` method derived from the input promise. To change this behavior, for instance to output a promise with more useful sugar methods such as are found in most promise libraries, you can override `chaiAsPromised.transferPromiseness`. Here's an example that transfer's Q's `finally` and `done` methods:
114
108
 
115
109
  ```js
116
110
  chaiAsPromised.transferPromiseness = function (assertion, promise) {
@@ -122,8 +116,7 @@ chaiAsPromised.transferPromiseness = function (assertion, promise) {
122
116
 
123
117
  ### Transforming Arguments to the Asserters
124
118
 
125
- Another advanced customization hook Chai as Promised allows is if you want to transform the arguments to the
126
- asserters, possibly asynchronously. Here is a toy example:
119
+ Another advanced customization hook Chai as Promised allows is if you want to transform the arguments to the asserters, possibly asynchronously. Here is a toy example:
127
120
 
128
121
  ```js
129
122
  chaiAsPromised.transformAsserterArgs = function (args) {
@@ -131,12 +124,10 @@ chaiAsPromised.transformAsserterArgs = function (args) {
131
124
  }
132
125
 
133
126
  Promise.resolve(2).should.eventually.equal(2); // will now fail!
134
- Promise.resolve(2).should.eventually.equal(3); // will now pass!
127
+ Promise.resolve(3).should.eventually.equal(2); // will now pass!
135
128
  ```
136
129
 
137
- The transform can even be asynchronous, returning a promise for an array instead of an array directly. An example
138
- of that might be using `Promise.all` so that an array of promises becomes a promise for an array. If you do that,
139
- then you can compare promises against other promises using the asserters:
130
+ The transform can even be asynchronous, returning a promise for an array instead of an array directly. An example of that might be using `Promise.all` so that an array of promises becomes a promise for an array. If you do that, then you can compare promises against other promises using the asserters:
140
131
 
141
132
  ```js
142
133
  // This will normally fail, since within() only works on numbers.
@@ -153,17 +144,15 @@ Promise.resolve(2).should.eventually.be.within(Promise.resolve(1), Promise.resol
153
144
 
154
145
  ### Compatibility
155
146
 
156
- Chai as Promised is compatible with all promises following the [Promises/A+ specification][spec]. Notably, jQuery's
157
- so-called “promises” are not up to spec, and Chai as Promised will not work with them. In particular, Chai as Promised
158
- makes extensive use of the standard [transformation behavior][] of `then`, which jQuery does not support.
147
+ Chai as Promised is compatible with all promises following the [Promises/A+ specification][spec].
148
+
149
+ Notably, jQuery's promises were not up to spec before jQuery 3.0, and Chai as Promised will not work with them. In particular, Chai as Promised makes extensive use of the standard [transformation behavior][] of `then`, which jQuery<3.0 does not support.
150
+
151
+ Angular promises have a special digest cycle for their processing, and [need extra setup code to work with Chai as Promised](http://stackoverflow.com/a/37374041/3191).
159
152
 
160
153
  ### Working with Non-Promise–Friendly Test Runners
161
154
 
162
- Some test runners (e.g. Jasmine, QUnit, or tap/tape) do not have the ability to use the returned promise to signal
163
- asynchronous test completion. If possible, I'd recommend switching to ones that do, such as [Mocha][mocha-promises],
164
- [Buster][buster-promises], or [blue-tape][]. But if that's not an option, Chai as Promised still has you covered. As
165
- long as your test framework takes a callback indicating when the asynchronous test run is over, Chai as Promised can
166
- adapt to that situation with its `notify` method, like so:
155
+ Some test runners (e.g. Jasmine, QUnit, or tap/tape) do not have the ability to use the returned promise to signal asynchronous test completion. If possible, I'd recommend switching to ones that do, such as [Mocha][mocha-promises], [Buster][buster-promises], or [blue-tape][]. But if that's not an option, Chai as Promised still has you covered. As long as your test framework takes a callback indicating when the asynchronous test run is over, Chai as Promised can adapt to that situation with its `notify` method, like so:
167
156
 
168
157
  ```javascript
169
158
  it("should be fulfilled", function (done) {
@@ -175,12 +164,9 @@ it("should be rejected", function (done) {
175
164
  });
176
165
  ```
177
166
 
178
- In these examples, if the conditions are not met, the test runner will receive an error of the form `"expected promise
179
- to be fulfilled but it was rejected with [Error: error message]"`, or `"expected promise to be rejected but it was
180
- fulfilled."`
167
+ In these examples, if the conditions are not met, the test runner will receive an error of the form `"expected promise to be fulfilled but it was rejected with [Error: error message]"`, or `"expected promise to be rejected but it was fulfilled."`
181
168
 
182
- There's another form of `notify` which is useful in certain situations, like doing assertions after a promise is
183
- complete. For example:
169
+ There's another form of `notify` which is useful in certain situations, like doing assertions after a promise is complete. For example:
184
170
 
185
171
  ```javascript
186
172
  it("should change the state", function (done) {
@@ -191,26 +177,23 @@ it("should change the state", function (done) {
191
177
  });
192
178
  ```
193
179
 
194
- Notice how `.notify(done)` is hanging directly off of `.should`, instead of appearing after a promise assertion. This
195
- indicates to Chai as Promised that it should pass fulfillment or rejection directly through to the testing framework.
196
- Thus, the above code will fail with a Chai as Promised error (`"expected promise to be fulfilled…"`) if `promise` is
197
- rejected, but will fail with a simple Chai error (`expected "before" to equal "after"`) if `otherState` does not change.
180
+ Notice how `.notify(done)` is hanging directly off of `.should`, instead of appearing after a promise assertion. This indicates to Chai as Promised that it should pass fulfillment or rejection directly through to the testing framework. Thus, the above code will fail with a Chai as Promised error (`"expected promise to be fulfilled…"`) if `promise` is rejected, but will fail with a simple Chai error (`expected "before" to equal "after"`) if `otherState` does not change.
198
181
 
199
- Another example of where this can be useful is when performing assertions on multiple promises:
182
+ ### Multiple Promise Assertions
183
+
184
+ To perform assertions on multiple promises, use `Promise.all` to combine multiple Chai as Promised assertions:
200
185
 
201
186
  ```javascript
202
- it("should all be well", function (done) {
203
- Q.all([
187
+ it("should all be well", function () {
188
+ return Promise.all([
204
189
  promiseA.should.become("happy"),
205
190
  promiseB.should.eventually.have.property("fun times"),
206
191
  promiseC.should.be.rejectedWith(TypeError, "only joyful types are allowed")
207
- ]).should.notify(done);
192
+ ]);
208
193
  });
209
194
  ```
210
195
 
211
- This will pass any failures of the individual promise assertions up to the test framework, instead of wrapping them in
212
- an `"expected promise to be fulfilled…"` message as would happen if you did
213
- `Q.all([…]).should.be.fulfilled.and.notify(done)`.
196
+ This will pass any failures of the individual promise assertions up to the test framework, instead of wrapping them in an `"expected promise to be fulfilled…"` message as would happen if you did `return Promise.all([…]).should.be.fulfilled`. If you can't use `return`, then use `.should.notify(done)`, similar to the previous examples.
214
197
 
215
198
  ## Installation and Setup
216
199
 
@@ -223,35 +206,21 @@ var chai = require("chai");
223
206
  var chaiAsPromised = require("chai-as-promised");
224
207
 
225
208
  chai.use(chaiAsPromised);
226
- ```
227
-
228
- You can of course put this code in a common test fixture file; for an example using [Mocha][], see
229
- [the Chai as Promised tests themselves][fixturedemo].
230
209
 
231
- ### AMD
232
-
233
- Chai as Promised supports being used as an [AMD][amd] module, registering itself anonymously (just like Chai). So,
234
- assuming you have configured your loader to map the Chai and Chai as Promised files to the respective module IDs
235
- `"chai"` and `"chai-as-promised"`, you can use them as follows:
236
-
237
- ```javascript
238
- define(function (require, exports, module) {
239
- var chai = require("chai");
240
- var chaiAsPromised = require("chai-as-promised");
241
-
242
- chai.use(chaiAsPromised);
243
- });
210
+ // Then either:
211
+ var expect = chai.expect;
212
+ // or:
213
+ var assert = chai.assert;
214
+ // or:
215
+ chai.should();
216
+ // according to your preference of assertion style
244
217
  ```
245
218
 
246
- ### `<script>` tag
219
+ You can of course put this code in a common test fixture file; for an example using [Mocha][], see [the Chai as Promised tests themselves][fixturedemo].
247
220
 
248
- If you include Chai as Promised directly with a `<script>` tag, after the one for Chai itself, then it will
249
- automatically plug in to Chai and be ready for use:
221
+ ### In the Browser
250
222
 
251
- ```html
252
- <script src="chai.js"></script>
253
- <script src="chai-as-promised.js"></script>
254
- ```
223
+ To use Chai as Promised in environments that don't support Node.js-like CommonJS modules, you'll need to use a bundling tool like [browserify][].
255
224
 
256
225
  ### Karma
257
226
 
@@ -275,3 +244,4 @@ Chai as Promised is only compatible with modern browsers (IE ≥9, Safari ≥6,
275
244
  [sinon-chai]: https://github.com/domenic/sinon-chai
276
245
  [Karma]: https://karma-runner.github.io/
277
246
  [karma-chai-as-promised]: https://github.com/vlkosinov/karma-chai-as-promised
247
+ [browserify]: http://browserify.org/
@@ -1,371 +1,374 @@
1
- (function () {
2
- "use strict";
3
-
4
- // Module systems magic dance.
5
-
6
- /* istanbul ignore else */
7
- if (typeof require === "function" && typeof exports === "object" && typeof module === "object") {
8
- // NodeJS
9
- module.exports = chaiAsPromised;
10
- } else if (typeof define === "function" && define.amd) {
11
- // AMD
12
- define(function () {
13
- return chaiAsPromised;
14
- });
15
- } else {
16
- /*global self: false */
1
+ "use strict";
2
+ var checkError = require("check-error");
3
+
4
+ module.exports = function (chai, utils) {
5
+ var Assertion = chai.Assertion;
6
+ var assert = chai.assert;
7
+
8
+ // If we are using a version of Chai that has checkError on it,
9
+ // we want to use that version to be consistent. Otherwise, we use
10
+ // what was passed to the factory.
11
+ if (utils.checkError) {
12
+ checkError = utils.checkError;
13
+ }
17
14
 
18
- // Other environment (usually <script> tag): plug in to global chai instance directly.
19
- chai.use(chaiAsPromised);
15
+ function isLegacyJQueryPromise(thenable) {
16
+ // jQuery promises are Promises/A+-compatible since 3.0.0. jQuery 3.0.0 is also the first version
17
+ // to define the catch method.
18
+ return typeof thenable.catch !== "function" &&
19
+ typeof thenable.always === "function" &&
20
+ typeof thenable.done === "function" &&
21
+ typeof thenable.fail === "function" &&
22
+ typeof thenable.pipe === "function" &&
23
+ typeof thenable.progress === "function" &&
24
+ typeof thenable.state === "function";
25
+ }
20
26
 
21
- // Expose as a property of the global object so that consumers can configure the `transferPromiseness` property.
22
- self.chaiAsPromised = chaiAsPromised;
27
+ function assertIsAboutPromise(assertion) {
28
+ if (typeof assertion._obj.then !== "function") {
29
+ throw new TypeError(utils.inspect(assertion._obj) + " is not a thenable.");
30
+ }
31
+ if (isLegacyJQueryPromise(assertion._obj)) {
32
+ throw new TypeError("Chai as Promised is incompatible with thenables of jQuery<3.0.0, sorry! Please " +
33
+ "upgrade jQuery or use another Promises/A+ compatible library (see " +
34
+ "http://promisesaplus.com/).");
35
+ }
23
36
  }
24
37
 
25
- chaiAsPromised.transferPromiseness = function (assertion, promise) {
26
- assertion.then = promise.then.bind(promise);
27
- };
38
+ function method(name, asserter) {
39
+ utils.addMethod(Assertion.prototype, name, function () {
40
+ assertIsAboutPromise(this);
41
+ return asserter.apply(this, arguments);
42
+ });
43
+ }
28
44
 
29
- chaiAsPromised.transformAsserterArgs = function (values) {
30
- return values;
31
- };
45
+ function property(name, asserter) {
46
+ utils.addProperty(Assertion.prototype, name, function () {
47
+ assertIsAboutPromise(this);
48
+ return asserter.apply(this, arguments);
49
+ });
50
+ }
32
51
 
33
- function chaiAsPromised(chai, utils) {
34
- var Assertion = chai.Assertion;
35
- var assert = chai.assert;
36
-
37
- function isJQueryPromise(thenable) {
38
- return typeof thenable.always === "function" &&
39
- typeof thenable.done === "function" &&
40
- typeof thenable.fail === "function" &&
41
- typeof thenable.pipe === "function" &&
42
- typeof thenable.progress === "function" &&
43
- typeof thenable.state === "function";
44
- }
52
+ function doNotify(promise, done) {
53
+ promise.then(function () { done(); }, done);
54
+ }
45
55
 
46
- function assertIsAboutPromise(assertion) {
47
- if (typeof assertion._obj.then !== "function") {
48
- throw new TypeError(utils.inspect(assertion._obj) + " is not a thenable.");
49
- }
50
- if (isJQueryPromise(assertion._obj)) {
51
- throw new TypeError("Chai as Promised is incompatible with jQuery's thenables, sorry! Please use a " +
52
- "Promises/A+ compatible library (see http://promisesaplus.com/).");
53
- }
54
- }
56
+ // These are for clarity and to bypass Chai refusing to allow `undefined` as actual when used with `assert`.
57
+ function assertIfNegated(assertion, message, extra) {
58
+ assertion.assert(true, null, message, extra.expected, extra.actual);
59
+ }
55
60
 
56
- function method(name, asserter) {
57
- utils.addMethod(Assertion.prototype, name, function () {
58
- assertIsAboutPromise(this);
59
- return asserter.apply(this, arguments);
60
- });
61
- }
61
+ function assertIfNotNegated(assertion, message, extra) {
62
+ assertion.assert(false, message, null, extra.expected, extra.actual);
63
+ }
62
64
 
63
- function property(name, asserter) {
64
- utils.addProperty(Assertion.prototype, name, function () {
65
- assertIsAboutPromise(this);
66
- return asserter.apply(this, arguments);
67
- });
68
- }
65
+ function getBasePromise(assertion) {
66
+ // We need to chain subsequent asserters on top of ones in the chain already (consider
67
+ // `eventually.have.property("foo").that.equals("bar")`), only running them after the existing ones pass.
68
+ // So the first base-promise is `assertion._obj`, but after that we use the assertions themselves, i.e.
69
+ // previously derived promises, to chain off of.
70
+ return typeof assertion.then === "function" ? assertion : assertion._obj;
71
+ }
69
72
 
70
- function doNotify(promise, done) {
71
- promise.then(function () { done(); }, done);
72
- }
73
+ function getReasonName(reason) {
74
+ return (reason instanceof Error) ? reason.toString() : checkError.getConstructorName(reason);
75
+ }
73
76
 
74
- // These are for clarity and to bypass Chai refusing to allow `undefined` as actual when used with `assert`.
75
- function assertIfNegated(assertion, message, extra) {
76
- assertion.assert(true, null, message, extra.expected, extra.actual);
77
+ // Grab these first, before we modify `Assertion.prototype`.
78
+
79
+ var propertyNames = Object.getOwnPropertyNames(Assertion.prototype);
80
+
81
+ var propertyDescs = {};
82
+ propertyNames.forEach(function (name) {
83
+ propertyDescs[name] = Object.getOwnPropertyDescriptor(Assertion.prototype, name);
84
+ });
85
+
86
+ property("fulfilled", function () {
87
+ var that = this;
88
+ var derivedPromise = getBasePromise(that).then(
89
+ function (value) {
90
+ assertIfNegated(that,
91
+ "expected promise not to be fulfilled but it was fulfilled with #{act}",
92
+ { actual: value });
93
+ return value;
94
+ },
95
+ function (reason) {
96
+ assertIfNotNegated(that,
97
+ "expected promise to be fulfilled but it was rejected with #{act}",
98
+ { actual: getReasonName(reason) });
99
+ return reason;
100
+ }
101
+ );
102
+
103
+ module.exports.transferPromiseness(that, derivedPromise);
104
+ });
105
+
106
+ property("rejected", function () {
107
+ var that = this;
108
+ var derivedPromise = getBasePromise(that).then(
109
+ function (value) {
110
+ assertIfNotNegated(that,
111
+ "expected promise to be rejected but it was fulfilled with #{act}",
112
+ { actual: value });
113
+ return value;
114
+ },
115
+ function (reason) {
116
+ assertIfNegated(that,
117
+ "expected promise not to be rejected but it was rejected with #{act}",
118
+ { actual: getReasonName(reason) });
119
+
120
+ // Return the reason, transforming this into a fulfillment, to allow further assertions, e.g.
121
+ // `promise.should.be.rejected.and.eventually.equal("reason")`.
122
+ return reason;
123
+ }
124
+ );
125
+
126
+ module.exports.transferPromiseness(that, derivedPromise);
127
+ });
128
+
129
+ method("rejectedWith", function (errorLike, errMsgMatcher, message) {
130
+ var errorLikeName = null;
131
+ var negate = utils.flag(this, "negate") || false;
132
+
133
+ // rejectedWith with that is called without arguments is
134
+ // the same as a plain ".rejected" use.
135
+ if (errorLike === undefined && errMsgMatcher === undefined &&
136
+ message === undefined) {
137
+ /* jshint expr: true */
138
+ this.rejected;
139
+ return;
77
140
  }
78
141
 
79
- function assertIfNotNegated(assertion, message, extra) {
80
- assertion.assert(false, message, null, extra.expected, extra.actual);
142
+ if (message !== undefined) {
143
+ utils.flag(this, "message", message);
81
144
  }
82
145
 
83
- function getBasePromise(assertion) {
84
- // We need to chain subsequent asserters on top of ones in the chain already (consider
85
- // `eventually.have.property("foo").that.equals("bar")`), only running them after the existing ones pass.
86
- // So the first base-promise is `assertion._obj`, but after that we use the assertions themselves, i.e.
87
- // previously derived promises, to chain off of.
88
- return typeof assertion.then === "function" ? assertion : assertion._obj;
146
+ if (errorLike instanceof RegExp || typeof errorLike === "string") {
147
+ errMsgMatcher = errorLike;
148
+ errorLike = null;
149
+ } else if (errorLike && errorLike instanceof Error) {
150
+ errorLikeName = errorLike.toString();
151
+ } else if (typeof errorLike === "function") {
152
+ errorLikeName = checkError.getConstructorName(errorLike);
153
+ } else {
154
+ errorLike = null;
89
155
  }
156
+ var everyArgIsDefined = Boolean(errorLike && errMsgMatcher);
90
157
 
91
- // Grab these first, before we modify `Assertion.prototype`.
92
-
93
- var propertyNames = Object.getOwnPropertyNames(Assertion.prototype);
94
-
95
- var propertyDescs = {};
96
- propertyNames.forEach(function (name) {
97
- propertyDescs[name] = Object.getOwnPropertyDescriptor(Assertion.prototype, name);
98
- });
158
+ var matcherRelation = "including";
159
+ if (errMsgMatcher instanceof RegExp) {
160
+ matcherRelation = "matching";
161
+ }
99
162
 
100
- property("fulfilled", function () {
101
- var that = this;
102
- var derivedPromise = getBasePromise(that).then(
103
- function (value) {
104
- that._obj = value;
105
- assertIfNegated(that,
106
- "expected promise not to be fulfilled but it was fulfilled with #{act}",
107
- { actual: value });
108
- return value;
109
- },
110
- function (reason) {
111
- assertIfNotNegated(that,
112
- "expected promise to be fulfilled but it was rejected with #{act}",
113
- { actual: reason });
163
+ var that = this;
164
+ var derivedPromise = getBasePromise(that).then(
165
+ function (value) {
166
+ var assertionMessage = null;
167
+ var expected = null;
168
+
169
+ if (errorLike) {
170
+ assertionMessage = "expected promise to be rejected with #{exp} but it was fulfilled with " +
171
+ "#{act}";
172
+ expected = errorLikeName;
173
+ } else if (errMsgMatcher) {
174
+ assertionMessage = "expected promise to be rejected with an error " + matcherRelation +
175
+ " #{exp} but it was fulfilled with #{act}";
176
+ expected = errMsgMatcher;
114
177
  }
115
- );
116
-
117
- chaiAsPromised.transferPromiseness(that, derivedPromise);
118
- });
119
178
 
120
- property("rejected", function () {
121
- var that = this;
122
- var derivedPromise = getBasePromise(that).then(
123
- function (value) {
124
- that._obj = value;
125
- assertIfNotNegated(that,
126
- "expected promise to be rejected but it was fulfilled with #{act}",
127
- { actual: value });
128
- return value;
129
- },
130
- function (reason) {
131
- assertIfNegated(that,
132
- "expected promise not to be rejected but it was rejected with #{act}",
133
- { actual: reason });
134
-
135
- // Return the reason, transforming this into a fulfillment, to allow further assertions, e.g.
136
- // `promise.should.be.rejected.and.eventually.equal("reason")`.
137
- return reason;
138
- }
139
- );
140
-
141
- chaiAsPromised.transferPromiseness(that, derivedPromise);
142
- });
143
-
144
- method("rejectedWith", function (Constructor, message) {
145
- var desiredReason = null;
146
- var constructorName = null;
147
-
148
- if (Constructor instanceof RegExp || typeof Constructor === "string") {
149
- message = Constructor;
150
- Constructor = null;
151
- } else if (Constructor && Constructor instanceof Error) {
152
- desiredReason = Constructor;
153
- Constructor = null;
154
- message = null;
155
- } else if (typeof Constructor === "function") {
156
- constructorName = (new Constructor()).name;
157
- } else {
158
- Constructor = null;
159
- }
160
-
161
- var that = this;
162
- var derivedPromise = getBasePromise(that).then(
163
- function (value) {
164
- var assertionMessage = null;
165
- var expected = null;
166
-
167
- if (Constructor) {
168
- assertionMessage = "expected promise to be rejected with #{exp} but it was fulfilled with " +
169
- "#{act}";
170
- expected = constructorName;
171
- } else if (message) {
172
- var verb = message instanceof RegExp ? "matching" : "including";
173
- assertionMessage = "expected promise to be rejected with an error " + verb + " #{exp} but it " +
174
- "was fulfilled with #{act}";
175
- expected = message;
176
- } else if (desiredReason) {
177
- assertionMessage = "expected promise to be rejected with #{exp} but it was fulfilled with " +
178
- "#{act}";
179
- expected = desiredReason;
179
+ assertIfNotNegated(that, assertionMessage, { expected: expected, actual: value });
180
+ return value;
181
+ },
182
+ function (reason) {
183
+ var errorLikeCompatible = errorLike && (errorLike instanceof Error ?
184
+ checkError.compatibleInstance(reason, errorLike) :
185
+ checkError.compatibleConstructor(reason, errorLike));
186
+
187
+ var errMsgMatcherCompatible = errMsgMatcher && checkError.compatibleMessage(reason, errMsgMatcher);
188
+
189
+ var reasonName = getReasonName(reason);
190
+
191
+ if (negate && everyArgIsDefined) {
192
+ if (errorLikeCompatible && errMsgMatcherCompatible) {
193
+ that.assert(true,
194
+ null,
195
+ "expected promise not to be rejected with #{exp} but it was rejected " +
196
+ "with #{act}",
197
+ errorLikeName,
198
+ reasonName);
180
199
  }
181
-
182
- that._obj = value;
183
-
184
- assertIfNotNegated(that, assertionMessage, { expected: expected, actual: value });
185
- },
186
- function (reason) {
187
- if (Constructor) {
188
- that.assert(reason instanceof Constructor,
200
+ }
201
+ else {
202
+ if (errorLike) {
203
+ that.assert(errorLikeCompatible,
189
204
  "expected promise to be rejected with #{exp} but it was rejected with #{act}",
190
- "expected promise not to be rejected with #{exp} but it was rejected with #{act}",
191
- constructorName,
192
- reason);
205
+ "expected promise not to be rejected with #{exp} but it was rejected " +
206
+ "with #{act}",
207
+ errorLikeName,
208
+ reasonName);
193
209
  }
194
210
 
195
- var reasonMessage = utils.type(reason) === "object" && "message" in reason ?
196
- reason.message :
197
- "" + reason;
198
- if (message && reasonMessage !== null && reasonMessage !== undefined) {
199
- if (message instanceof RegExp) {
200
- that.assert(message.test(reasonMessage),
201
- "expected promise to be rejected with an error matching #{exp} but got #{act}",
202
- "expected promise not to be rejected with an error matching #{exp}",
203
- message,
204
- reasonMessage);
205
- }
206
- if (typeof message === "string") {
207
- that.assert(reasonMessage.indexOf(message) !== -1,
208
- "expected promise to be rejected with an error including #{exp} but got #{act}",
209
- "expected promise not to be rejected with an error including #{exp}",
210
- message,
211
- reasonMessage);
212
- }
213
- }
214
-
215
- if (desiredReason) {
216
- that.assert(reason === desiredReason,
217
- "expected promise to be rejected with #{exp} but it was rejected with #{act}",
218
- "expected promise not to be rejected with #{exp}",
219
- desiredReason,
220
- reason);
211
+ if (errMsgMatcher) {
212
+ that.assert(errMsgMatcherCompatible,
213
+ "expected promise to be rejected with an error " + matcherRelation +
214
+ " #{exp} but got #{act}",
215
+ "expected promise not to be rejected with an error " + matcherRelation +
216
+ " #{exp}",
217
+ errMsgMatcher,
218
+ checkError.getMessage(reason));
221
219
  }
222
220
  }
223
- );
224
221
 
225
- chaiAsPromised.transferPromiseness(that, derivedPromise);
226
- });
222
+ return reason;
223
+ }
224
+ );
227
225
 
228
- property("eventually", function () {
229
- utils.flag(this, "eventually", true);
230
- });
226
+ module.exports.transferPromiseness(that, derivedPromise);
227
+ });
231
228
 
232
- method("notify", function (done) {
233
- doNotify(getBasePromise(this), done);
234
- });
229
+ property("eventually", function () {
230
+ utils.flag(this, "eventually", true);
231
+ });
235
232
 
236
- method("become", function (value) {
237
- return this.eventually.deep.equal(value);
238
- });
233
+ method("notify", function (done) {
234
+ doNotify(getBasePromise(this), done);
235
+ });
239
236
 
240
- ////////
241
- // `eventually`
237
+ method("become", function (value, message) {
238
+ return this.eventually.deep.equal(value, message);
239
+ });
242
240
 
243
- // We need to be careful not to trigger any getters, thus `Object.getOwnPropertyDescriptor` usage.
244
- var methodNames = propertyNames.filter(function (name) {
245
- return name !== "assert" && typeof propertyDescs[name].value === "function";
246
- });
241
+ ////////
242
+ // `eventually`
247
243
 
248
- methodNames.forEach(function (methodName) {
249
- Assertion.overwriteMethod(methodName, function (originalMethod) {
250
- return function () {
251
- doAsserterAsyncAndAddThen(originalMethod, this, arguments);
252
- };
253
- });
254
- });
244
+ // We need to be careful not to trigger any getters, thus `Object.getOwnPropertyDescriptor` usage.
245
+ var methodNames = propertyNames.filter(function (name) {
246
+ return name !== "assert" && typeof propertyDescs[name].value === "function";
247
+ });
255
248
 
256
- var getterNames = propertyNames.filter(function (name) {
257
- return name !== "_obj" && typeof propertyDescs[name].get === "function";
249
+ methodNames.forEach(function (methodName) {
250
+ Assertion.overwriteMethod(methodName, function (originalMethod) {
251
+ return function () {
252
+ doAsserterAsyncAndAddThen(originalMethod, this, arguments);
253
+ };
258
254
  });
259
-
260
- getterNames.forEach(function (getterName) {
261
- // Chainable methods are things like `an`, which can work both for `.should.be.an.instanceOf` and as
262
- // `should.be.an("object")`. We need to handle those specially.
263
- var isChainableMethod = Assertion.prototype.__methods.hasOwnProperty(getterName);
264
-
265
- if (isChainableMethod) {
266
- Assertion.overwriteChainableMethod(
267
- getterName,
268
- function (originalMethod) {
269
- return function() {
270
- doAsserterAsyncAndAddThen(originalMethod, this, arguments);
271
- };
272
- },
273
- function (originalGetter) {
274
- return function() {
275
- doAsserterAsyncAndAddThen(originalGetter, this);
276
- };
277
- }
278
- );
279
- } else {
280
- Assertion.overwriteProperty(getterName, function (originalGetter) {
281
- return function () {
255
+ });
256
+
257
+ var getterNames = propertyNames.filter(function (name) {
258
+ return name !== "_obj" && typeof propertyDescs[name].get === "function";
259
+ });
260
+
261
+ getterNames.forEach(function (getterName) {
262
+ // Chainable methods are things like `an`, which can work both for `.should.be.an.instanceOf` and as
263
+ // `should.be.an("object")`. We need to handle those specially.
264
+ var isChainableMethod = Assertion.prototype.__methods.hasOwnProperty(getterName);
265
+
266
+ if (isChainableMethod) {
267
+ Assertion.overwriteChainableMethod(
268
+ getterName,
269
+ function (originalMethod) {
270
+ return function() {
271
+ doAsserterAsyncAndAddThen(originalMethod, this, arguments);
272
+ };
273
+ },
274
+ function (originalGetter) {
275
+ return function() {
282
276
  doAsserterAsyncAndAddThen(originalGetter, this);
283
277
  };
284
- });
285
- }
286
- });
287
-
288
- function doAsserterAsyncAndAddThen(asserter, assertion, args) {
289
- // Since we're intercepting all methods/properties, we need to just pass through if they don't want
290
- // `eventually`, or if we've already fulfilled the promise (see below).
291
- if (!utils.flag(assertion, "eventually")) {
292
- return asserter.apply(assertion, args);
293
- }
294
-
295
- var derivedPromise = getBasePromise(assertion).then(function (value) {
296
- // Set up the environment for the asserter to actually run: `_obj` should be the fulfillment value, and
297
- // now that we have the value, we're no longer in "eventually" mode, so we won't run any of this code,
298
- // just the base Chai code that we get to via the short-circuit above.
299
- assertion._obj = value;
300
- utils.flag(assertion, "eventually", false);
301
-
302
- return args ? chaiAsPromised.transformAsserterArgs(args) : args;
303
- }).then(function (args) {
304
- asserter.apply(assertion, args);
305
-
306
- // Because asserters, for example `property`, can change the value of `_obj` (i.e. change the "object"
307
- // flag), we need to communicate this value change to subsequent chained asserters. Since we build a
308
- // promise chain paralleling the asserter chain, we can use it to communicate such changes.
309
- return assertion._obj;
278
+ }
279
+ );
280
+ } else {
281
+ Assertion.overwriteProperty(getterName, function (originalGetter) {
282
+ return function () {
283
+ doAsserterAsyncAndAddThen(originalGetter, this);
284
+ };
310
285
  });
286
+ }
287
+ });
311
288
 
312
- chaiAsPromised.transferPromiseness(assertion, derivedPromise);
289
+ function doAsserterAsyncAndAddThen(asserter, assertion, args) {
290
+ // Since we're intercepting all methods/properties, we need to just pass through if they don't want
291
+ // `eventually`, or if we've already fulfilled the promise (see below).
292
+ if (!utils.flag(assertion, "eventually")) {
293
+ return asserter.apply(assertion, args);
313
294
  }
314
295
 
315
- ///////
316
- // Now use the `Assertion` framework to build an `assert` interface.
317
- var originalAssertMethods = Object.getOwnPropertyNames(assert).filter(function (propName) {
318
- return typeof assert[propName] === "function";
296
+ var derivedPromise = getBasePromise(assertion).then(function (value) {
297
+ // Set up the environment for the asserter to actually run: `_obj` should be the fulfillment value, and
298
+ // now that we have the value, we're no longer in "eventually" mode, so we won't run any of this code,
299
+ // just the base Chai code that we get to via the short-circuit above.
300
+ assertion._obj = value;
301
+ utils.flag(assertion, "eventually", false);
302
+
303
+ return args ? module.exports.transformAsserterArgs(args) : args;
304
+ }).then(function (args) {
305
+ asserter.apply(assertion, args);
306
+
307
+ // Because asserters, for example `property`, can change the value of `_obj` (i.e. change the "object"
308
+ // flag), we need to communicate this value change to subsequent chained asserters. Since we build a
309
+ // promise chain paralleling the asserter chain, we can use it to communicate such changes.
310
+ return assertion._obj;
319
311
  });
320
312
 
321
- assert.isFulfilled = function (promise, message) {
322
- return (new Assertion(promise, message)).to.be.fulfilled;
323
- };
324
-
325
- assert.isRejected = function (promise, toTestAgainst, message) {
326
- if (typeof toTestAgainst === "string") {
327
- message = toTestAgainst;
328
- toTestAgainst = undefined;
329
- }
313
+ module.exports.transferPromiseness(assertion, derivedPromise);
314
+ }
330
315
 
331
- var assertion = (new Assertion(promise, message));
332
- return toTestAgainst !== undefined ? assertion.to.be.rejectedWith(toTestAgainst) : assertion.to.be.rejected;
333
- };
316
+ ///////
317
+ // Now use the `Assertion` framework to build an `assert` interface.
318
+ var originalAssertMethods = Object.getOwnPropertyNames(assert).filter(function (propName) {
319
+ return typeof assert[propName] === "function";
320
+ });
334
321
 
335
- assert.becomes = function (promise, value, message) {
336
- return assert.eventually.deepEqual(promise, value, message);
337
- };
322
+ assert.isFulfilled = function (promise, message) {
323
+ return (new Assertion(promise, message)).to.be.fulfilled;
324
+ };
338
325
 
339
- assert.doesNotBecome = function (promise, value, message) {
340
- return assert.eventually.notDeepEqual(promise, value, message);
341
- };
326
+ assert.isRejected = function (promise, errorLike, errMsgMatcher, message) {
327
+ var assertion = (new Assertion(promise, message));
328
+ return assertion.to.be.rejectedWith(errorLike, errMsgMatcher, message);
329
+ };
342
330
 
343
- assert.eventually = {};
344
- originalAssertMethods.forEach(function (assertMethodName) {
345
- assert.eventually[assertMethodName] = function (promise) {
346
- var otherArgs = Array.prototype.slice.call(arguments, 1);
331
+ assert.becomes = function (promise, value, message) {
332
+ return assert.eventually.deepEqual(promise, value, message);
333
+ };
347
334
 
348
- var customRejectionHandler;
349
- var message = arguments[assert[assertMethodName].length - 1];
350
- if (typeof message === "string") {
351
- customRejectionHandler = function (reason) {
352
- throw new chai.AssertionError(message + "\n\nOriginal reason: " + utils.inspect(reason));
353
- };
354
- }
335
+ assert.doesNotBecome = function (promise, value, message) {
336
+ return assert.eventually.notDeepEqual(promise, value, message);
337
+ };
355
338
 
356
- var returnedPromise = promise.then(
357
- function (fulfillmentValue) {
358
- return assert[assertMethodName].apply(assert, [fulfillmentValue].concat(otherArgs));
359
- },
360
- customRejectionHandler
361
- );
339
+ assert.eventually = {};
340
+ originalAssertMethods.forEach(function (assertMethodName) {
341
+ assert.eventually[assertMethodName] = function (promise) {
342
+ var otherArgs = Array.prototype.slice.call(arguments, 1);
362
343
 
363
- returnedPromise.notify = function (done) {
364
- doNotify(returnedPromise, done);
344
+ var customRejectionHandler;
345
+ var message = arguments[assert[assertMethodName].length - 1];
346
+ if (typeof message === "string") {
347
+ customRejectionHandler = function (reason) {
348
+ throw new chai.AssertionError(message + "\n\nOriginal reason: " + utils.inspect(reason));
365
349
  };
350
+ }
351
+
352
+ var returnedPromise = promise.then(
353
+ function (fulfillmentValue) {
354
+ return assert[assertMethodName].apply(assert, [fulfillmentValue].concat(otherArgs));
355
+ },
356
+ customRejectionHandler
357
+ );
366
358
 
367
- return returnedPromise;
359
+ returnedPromise.notify = function (done) {
360
+ doNotify(returnedPromise, done);
368
361
  };
369
- });
370
- }
371
- }());
362
+
363
+ return returnedPromise;
364
+ };
365
+ });
366
+ };
367
+
368
+ module.exports.transferPromiseness = function (assertion, promise) {
369
+ assertion.then = promise.then.bind(promise);
370
+ };
371
+
372
+ module.exports.transformAsserterArgs = function (values) {
373
+ return values;
374
+ };
package/package.json CHANGED
@@ -1,43 +1,47 @@
1
1
  {
2
- "name": "chai-as-promised",
3
- "description": "Extends Chai with assertions about promises.",
4
- "keywords": [
5
- "chai",
6
- "testing",
7
- "assertions",
8
- "promises",
9
- "promises-aplus"
10
- ],
11
- "version": "5.0.0",
12
- "author": "Domenic Denicola <d@domenic.me> (https://domenic.me)",
13
- "license": "WTFPL",
14
- "repository": "domenic/chai-as-promised",
15
- "main": "./lib/chai-as-promised.js",
16
- "files": [
17
- "lib"
18
- ],
19
- "scripts": {
20
- "test": "npm run test-plugin && npm run test-intercompatibility",
21
- "test-plugin": "mocha",
22
- "test-intercompatibility": "mocha test-intercompatibility --opts test-intercompatibility/mocha.opts",
23
- "test-browser-q": "coffee ./test/browser/runner.coffee q",
24
- "test-browser-when": "coffee ./test/browser/runner.coffee when",
25
- "lint": "jshint ./lib",
26
- "cover": "istanbul cover node_modules/mocha/bin/_mocha && opener ./coverage/lcov-report/lib/chai-as-promised.js.html"
27
- },
28
- "peerDependencies": {
29
- "chai": ">= 2.1.2 < 3"
30
- },
31
- "devDependencies": {
32
- "chai": "^2.1.2",
33
- "coffee-script": "1.9.0",
34
- "istanbul": "0.3.5",
35
- "ecstatic": "0.5.8",
36
- "glob": "^4.3.5",
37
- "jshint": "^2.6.0",
38
- "mocha": "^1.21.5",
39
- "opener": "^1.4.0",
40
- "q": "^1.1.2",
41
- "underscore": "1.7.0"
42
- }
2
+ "name": "chai-as-promised",
3
+ "description": "Extends Chai with assertions about promises.",
4
+ "keywords": [
5
+ "chai",
6
+ "chai-plugin",
7
+ "browser",
8
+ "async",
9
+ "testing",
10
+ "assertions",
11
+ "promises",
12
+ "promises-aplus"
13
+ ],
14
+ "version": "6.0.0",
15
+ "author": "Domenic Denicola <d@domenic.me> (https://domenic.me)",
16
+ "license": "WTFPL",
17
+ "repository": "domenic/chai-as-promised",
18
+ "main": "./lib/chai-as-promised.js",
19
+ "files": [
20
+ "lib"
21
+ ],
22
+ "scripts": {
23
+ "test": "npm run test-plugin && npm run test-intercompatibility",
24
+ "test-plugin": "mocha",
25
+ "test-intercompatibility": "mocha test-intercompatibility --opts test-intercompatibility/mocha.opts",
26
+ "lint": "jshint ./lib",
27
+ "cover": "istanbul cover node_modules/mocha/bin/_mocha && opener ./coverage/lcov-report/lib/chai-as-promised.js.html"
28
+ },
29
+ "dependencies": {
30
+ "check-error": "^1.0.2"
31
+ },
32
+ "peerDependencies": {
33
+ "chai": ">= 2.1.2 < 4"
34
+ },
35
+ "devDependencies": {
36
+ "chai": "^3.0.0",
37
+ "coffee-script": "1.10.0",
38
+ "ecstatic": "^1.3.1",
39
+ "glob": "^6.0.1",
40
+ "istanbul": "0.4.1",
41
+ "jshint": "^2.8.0",
42
+ "mocha": "^2.3.4",
43
+ "opener": "^1.4.1",
44
+ "q": "^1.4.1",
45
+ "underscore": "1.8.3"
46
+ }
43
47
  }