chai-as-promised 4.0.0 → 4.3.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,7 +1,13 @@
1
- DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
1
+ Copyright © 2012–2015 Domenic Denicola <d@domenic.me>
2
+
3
+ This work is free. You can redistribute it and/or modify it under the
4
+ terms of the Do What The Fuck You Want To Public License, Version 2,
5
+ as published by Sam Hocevar. See below for more details.
6
+
7
+ DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
2
8
  Version 2, December 2004
3
9
 
4
- Copyright © 2012–2013 Domenic Denicola <domenic@domenicdenicola.com>
10
+ Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
5
11
 
6
12
  Everyone is permitted to copy and distribute verbatim or modified
7
13
  copies of this license document, and changing it is allowed as long
@@ -11,4 +17,3 @@
11
17
  TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
12
18
 
13
19
  0. You just DO WHAT THE FUCK YOU WANT TO.
14
-
package/README.md CHANGED
@@ -1,5 +1,5 @@
1
- <a href="http://promises-aplus.github.com/promises-spec">
2
- <img src="http://promises-aplus.github.com/promises-spec/assets/logo-small.png"
1
+ <a href="http://promisesaplus.com/">
2
+ <img src="https://promises-aplus.github.io/promises-spec/assets/logo-small.png"
3
3
  align="right" valign="top" alt="Promises/A+ logo" />
4
4
  </a>
5
5
 
@@ -24,14 +24,14 @@ doSomethingAsync().then(
24
24
  you can write code that expresses what you really mean:
25
25
 
26
26
  ```javascript
27
- doSomethingAsync().should.eventually.equal("foo").notify(done);
27
+ return doSomethingAsync().should.eventually.equal("foo");
28
28
  ```
29
29
 
30
- or if you have a testing framework that follows the [UncommonJS specification][uncommonjs] for handling promises,
31
- simply
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:
32
32
 
33
33
  ```javascript
34
- return doSomethingAsync().should.eventually.equal("foo");
34
+ doSomethingAsync().should.eventually.equal("foo").notify(done);
35
35
  ```
36
36
 
37
37
  ## How to Use
@@ -45,13 +45,13 @@ existing Chai assertion into one that acts on a promise:
45
45
  (2 + 2).should.equal(4);
46
46
 
47
47
  // becomes
48
- return promiseFor(2 + 2).should.eventually.equal(4);
48
+ return Promise.resolve(2 + 2).should.eventually.equal(4);
49
49
 
50
50
 
51
51
  expect({ foo: "bar" }).to.have.property("foo");
52
52
 
53
53
  // becomes
54
- return expect(promiseFor({ foo: "bar" })).to.eventually.have.property("foo");
54
+ return expect(Promise.resolve({ foo: "bar" })).to.eventually.have.property("foo");
55
55
  ```
56
56
 
57
57
  There are also a few promise-specific extensions (with the usual `expect` equivalents also available):
@@ -73,7 +73,7 @@ any existing Chai assertion to be used on a promise:
73
73
  assert.equal(2 + 2, 4, "This had better be true");
74
74
 
75
75
  // becomes
76
- return assert.eventually.equal(promiseFor(2 + 2), 4, "This had better be true, eventually");
76
+ return assert.eventually.equal(Promise.resolve(2 + 2), 4, "This had better be true, eventually");
77
77
  ```
78
78
 
79
79
  And there are, of course, promise-specific extensions:
@@ -105,11 +105,65 @@ return promise.then(null, null, progressSpy).then(function () {
105
105
  });
106
106
  ```
107
107
 
108
+ ### Customizing Output Promises
109
+
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:
114
+
115
+ ```js
116
+ chaiAsPromised.transferPromiseness = function (assertion, promise) {
117
+ assertion.then = promise.then.bind(promise); // this is all you get by default
118
+ assertion.finally = promise.finally.bind(promise);
119
+ assertion.done = promise.done.bind(promise);
120
+ };
121
+ ```
122
+
123
+ ### Transforming Arguments to the Asserters
124
+
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:
127
+
128
+ ```js
129
+ chaiAsPromised.transformAsserterArgs = function (args) {
130
+ return args.map(function (x) { return x + 1; });
131
+ }
132
+
133
+ Promise.resolve(2).should.eventually.equal(2); // will now fail!
134
+ Promise.resolve(2).should.eventually.equal(3); // will now pass!
135
+ ```
136
+
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:
140
+
141
+ ```js
142
+ // This will normally fail, since within() only works on numbers.
143
+ Promise.resolve(2).should.eventually.be.within(Promise.resolve(1), Promise.resolve(6));
144
+
145
+ chaiAsPromised.transformAsserterArgs = function (args) {
146
+ return Promise.all(args);
147
+ };
148
+
149
+ // But now it will pass, since we transformed the array of promises for numbers into
150
+ // (a promise for) an array of numbers
151
+ Promise.resolve(2).should.eventually.be.within(Promise.resolve(1), Promise.resolve(6));
152
+ ```
153
+
154
+ ### Compatibility
155
+
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.
159
+
108
160
  ### Working with Non-Promise–Friendly Test Runners
109
161
 
110
- As mentioned, many test runners (\*cough\* [Mocha][mocha-makes-me-sad] \*cough\* but see [Mocha as Promised][]!)
111
- don't support the nice `return` style shown above. Instead, they take a callback indicating when the asynchronous test
112
- run is over. Chai as Promised adapts to this situation with the `notify` method, like so:
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:
113
167
 
114
168
  ```javascript
115
169
  it("should be fulfilled", function (done) {
@@ -158,12 +212,6 @@ This will pass any failures of the individual promise assertions up to the test
158
212
  an `"expected promise to be fulfilled…"` message as would happen if you did
159
213
  `Q.all([…]).should.be.fulfilled.and.notify(done)`.
160
214
 
161
- ### Compatibility
162
-
163
- Chai as Promised is compatible with all promises following the [Promises/A+ specification][spec]. Notably, jQuery's
164
- so-called “promises” are not up to spec, and Chai as Promised will not work with them. In particular, Chai as Promised
165
- makes extensive use of the standard [transformation behavior][] of `then`, which jQuery does not support.
166
-
167
215
  ## Installation and Setup
168
216
 
169
217
  ### Node
@@ -177,7 +225,7 @@ var chaiAsPromised = require("chai-as-promised");
177
225
  chai.use(chaiAsPromised);
178
226
  ```
179
227
 
180
- You can of course put this code in a common test fixture file; for an example using [Mocha][mocha], see
228
+ You can of course put this code in a common test fixture file; for an example using [Mocha][], see
181
229
  [the Chai as Promised tests themselves][fixturedemo].
182
230
 
183
231
  ### AMD
@@ -205,15 +253,18 @@ automatically plug in to Chai and be ready for use:
205
253
  <script src="chai-as-promised.js"></script>
206
254
  ```
207
255
 
256
+ ### Browser Compatibility
257
+
258
+ Chai as Promised is only compatible with modern browsers (IE ≥9, Safari ≥6, no PhantomJS).
208
259
 
209
260
  [presentation]: http://www.slideshare.net/domenicdenicola/callbacks-promises-and-coroutines-oh-my-the-evolution-of-asynchronicity-in-javascript
210
261
  [chai]: http://chaijs.com/
211
- [mocha]: http://visionmedia.github.com/mocha/
212
- [mocha-makes-me-sad]: https://github.com/visionmedia/mocha/pull/329
213
- [Mocha as Promised]: https://github.com/domenic/mocha-as-promised
214
- [uncommonjs]: http://kriskowal.github.com/uncommonjs/tests/specification
215
- [spec]: http://promises-aplus.github.com/promises-spec/
216
- [transformation behavior]: https://gist.github.com/3889970#that-second-paragraph
262
+ [Mocha-promises]: http://visionmedia.github.io/mocha/#asynchronous-code
263
+ [Buster-promises]: http://docs.busterjs.org/en/latest/modules/buster-test/spec/#returning-a-promise
264
+ [blue-tape]: https://github.com/spion/blue-tape
265
+ [spec]: http://promisesaplus.com/
266
+ [transformation behavior]: http://domenic.me/2012/10/14/youre-missing-the-point-of-promises/#toc_2
267
+ [Mocha]: http://visionmedia.github.com/mocha/
217
268
  [fixturedemo]: https://github.com/domenic/chai-as-promised/tree/master/test/
218
269
  [amd]: https://github.com/amdjs/amdjs-api/wiki/AMD
219
270
  [sinon]: http://sinonjs.org/
@@ -1,8 +1,9 @@
1
- (function (chaiAsPromised) {
1
+ (function () {
2
2
  "use strict";
3
3
 
4
4
  // Module systems magic dance.
5
5
 
6
+ /* istanbul ignore else */
6
7
  if (typeof require === "function" && typeof exports === "object" && typeof module === "object") {
7
8
  // NodeJS
8
9
  module.exports = chaiAsPromised;
@@ -12,346 +13,365 @@
12
13
  return chaiAsPromised;
13
14
  });
14
15
  } else {
16
+ /*global self: false */
17
+
15
18
  // Other environment (usually <script> tag): plug in to global chai instance directly.
16
19
  chai.use(chaiAsPromised);
20
+
21
+ // Expose as a property of the global object so that consumers can configure the `transferPromiseness` property.
22
+ self.chaiAsPromised = chaiAsPromised;
17
23
  }
18
- }(function chaiAsPromised(chai, utils) {
19
- "use strict";
20
24
 
21
- var Assertion = chai.Assertion;
22
- var assert = chai.assert;
25
+ chaiAsPromised.transferPromiseness = function (assertion, promise) {
26
+ assertion.then = promise.then.bind(promise);
27
+ };
23
28
 
24
- function isJQueryPromise(thenable) {
25
- return typeof thenable.always === "function" &&
26
- typeof thenable.done === "function" &&
27
- typeof thenable.fail === "function" &&
28
- typeof thenable.pipe === "function" &&
29
- typeof thenable.progress === "function" &&
30
- typeof thenable.state === "function";
31
- }
29
+ chaiAsPromised.transformAsserterArgs = function (values) {
30
+ return values;
31
+ };
32
32
 
33
- function assertIsAboutPromise(assertion) {
34
- if (typeof assertion._obj.then !== "function") {
35
- throw new TypeError(utils.inspect(assertion._obj) + " is not a thenable.");
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";
36
44
  }
37
- if (isJQueryPromise(assertion._obj)) {
38
- throw new TypeError("Chai as Promised is incompatible with jQuery's thenables, sorry! Please use a " +
39
- "Promises/A+ compatible library (see http://promisesaplus.com/).");
45
+
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
+ }
40
54
  }
41
- }
42
55
 
43
- function method(name, asserter) {
44
- utils.addMethod(Assertion.prototype, name, function () {
45
- assertIsAboutPromise(this);
46
- return asserter.apply(this, arguments);
47
- });
48
- }
56
+ function method(name, asserter) {
57
+ utils.addMethod(Assertion.prototype, name, function () {
58
+ assertIsAboutPromise(this);
59
+ return asserter.apply(this, arguments);
60
+ });
61
+ }
49
62
 
50
- function property(name, asserter) {
51
- utils.addProperty(Assertion.prototype, name, function () {
52
- assertIsAboutPromise(this);
53
- return asserter.apply(this, arguments);
54
- });
55
- }
63
+ function property(name, asserter) {
64
+ utils.addProperty(Assertion.prototype, name, function () {
65
+ assertIsAboutPromise(this);
66
+ return asserter.apply(this, arguments);
67
+ });
68
+ }
56
69
 
57
- function doNotify(promise, done) {
58
- promise.then(function () { done(); }, done);
59
- }
70
+ function doNotify(promise, done) {
71
+ promise.then(function () { done(); }, done);
72
+ }
60
73
 
61
- // These are for clarity and to bypass Chai refusing to allow `undefined` as actual when used with `assert`.
62
- function assertIfNegated(assertion, message, extra) {
63
- assertion.assert(true, null, message, extra.expected, extra.actual);
64
- }
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
+ }
65
78
 
66
- function assertIfNotNegated(assertion, message, extra) {
67
- assertion.assert(false, message, null, extra.expected, extra.actual);
68
- }
79
+ function assertIfNotNegated(assertion, message, extra) {
80
+ assertion.assert(false, message, null, extra.expected, extra.actual);
81
+ }
69
82
 
70
- function getBasePromise(assertion) {
71
- // We need to chain subsequent asserters on top of ones in the chain already (consider
72
- // `eventually.have.property("foo").that.equals("bar")`), only running them after the existing ones pass.
73
- // So the first base-promise is `assertion._obj`, but after that we use the assertions themselves, i.e.
74
- // previously derived promises, to chain off of.
75
- return typeof assertion.then === "function" ? assertion : assertion._obj;
76
- }
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;
89
+ }
77
90
 
78
- // Grab these first, before we modify `Assertion.prototype`.
79
-
80
- var propertyNames = Object.getOwnPropertyNames(Assertion.prototype);
81
-
82
- var propertyDescs = {};
83
- propertyNames.forEach(function (name) {
84
- propertyDescs[name] = Object.getOwnPropertyDescriptor(Assertion.prototype, name);
85
- });
86
-
87
- property("fulfilled", function () {
88
- var that = this;
89
- var derivedPromise = getBasePromise(that).then(
90
- function (value) {
91
- that._obj = value;
92
- assertIfNegated(that,
93
- "expected promise not to be fulfilled but it was fulfilled with #{act}",
94
- { actual: value });
95
- return value;
96
- },
97
- function (reason) {
98
- assertIfNotNegated(that,
99
- "expected promise to be fulfilled but it was rejected with #{act}",
100
- { actual: reason });
101
- });
91
+ // Grab these first, before we modify `Assertion.prototype`.
102
92
 
103
- that.then = derivedPromise.then.bind(derivedPromise);
104
- });
105
-
106
- property("rejected", function () {
107
- var that = this;
108
- var derivedPromise = getBasePromise(that).then(
109
- function (value) {
110
- that._obj = value;
111
- assertIfNotNegated(that,
112
- "expected promise to be rejected but it was fulfilled with #{act}",
113
- { actual: value });
114
- return value;
115
- },
116
- function (reason) {
117
- assertIfNegated(that,
118
- "expected promise not to be rejected but it was rejected with #{act}",
119
- { actual: reason });
120
-
121
- // Return the reason, transforming this into a fulfillment, to allow further assertions, e.g.
122
- // `promise.should.be.rejected.and.eventually.equal("reason")`.
123
- return reason;
124
- }
125
- );
126
-
127
- that.then = derivedPromise.then.bind(derivedPromise);
128
- });
129
-
130
- method("rejectedWith", function (Constructor, message) {
131
- var desiredReason = null;
132
- var constructorName = null;
133
-
134
- if (Constructor instanceof RegExp || typeof Constructor === "string") {
135
- message = Constructor;
136
- Constructor = null;
137
- } else if (Constructor && Constructor instanceof Error) {
138
- desiredReason = Constructor;
139
- Constructor = null;
140
- message = null;
141
- } else if (typeof Constructor === "function") {
142
- constructorName = (new Constructor()).name;
143
- } else {
144
- Constructor = null;
145
- }
93
+ var propertyNames = Object.getOwnPropertyNames(Assertion.prototype);
146
94
 
147
- var that = this;
148
- var derivedPromise = getBasePromise(that).then(
149
- function (value) {
150
- var assertionMessage = null;
151
- var expected = null;
152
-
153
- if (Constructor) {
154
- assertionMessage = "expected promise to be rejected with #{exp} but it was fulfilled with #{act}";
155
- expected = constructorName;
156
- } else if (message) {
157
- var verb = message instanceof RegExp ? "matching" : "including";
158
- assertionMessage = "expected promise to be rejected with an error " + verb + " #{exp} but it was " +
159
- "fulfilled with #{act}";
160
- expected = message;
161
- } else if (desiredReason) {
162
- assertionMessage = "expected promise to be rejected with #{exp} but it was fulfilled with #{act}";
163
- expected = desiredReason;
164
- }
95
+ var propertyDescs = {};
96
+ propertyNames.forEach(function (name) {
97
+ propertyDescs[name] = Object.getOwnPropertyDescriptor(Assertion.prototype, name);
98
+ });
165
99
 
166
- that._obj = value;
167
-
168
- assertIfNotNegated(that, assertionMessage, { expected: expected, actual: value });
169
- },
170
- function (reason) {
171
- if (Constructor) {
172
- that.assert(reason instanceof Constructor,
173
- "expected promise to be rejected with #{exp} but it was rejected with #{act}",
174
- "expected promise not to be rejected with #{exp} but it was rejected with #{act}",
175
- constructorName,
176
- reason);
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 });
177
114
  }
115
+ );
178
116
 
179
- var reasonMessage = utils.type(reason) === "object" && "message" in reason ?
180
- reason.message :
181
- "" + reason;
182
- if (message && reasonMessage !== null && reasonMessage !== undefined) {
183
- if (message instanceof RegExp) {
184
- that.assert(message.test(reasonMessage),
185
- "expected promise to be rejected with an error matching #{exp} but got #{act}",
186
- "expected promise not to be rejected with an error matching #{exp}",
187
- message,
188
- reasonMessage);
189
- }
190
- if (typeof message === "string") {
191
- that.assert(reasonMessage.indexOf(message) !== -1,
192
- "expected promise to be rejected with an error including #{exp} but got #{act}",
193
- "expected promise not to be rejected with an error including #{exp}",
194
- message,
195
- reasonMessage);
196
- }
197
- }
117
+ chaiAsPromised.transferPromiseness(that, derivedPromise);
118
+ });
198
119
 
199
- if (desiredReason) {
200
- that.assert(reason === desiredReason,
201
- "expected promise to be rejected with #{exp} but it was rejected with #{act}",
202
- "expected promise not to be rejected with #{exp}",
203
- desiredReason,
204
- reason);
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;
205
138
  }
206
- }
207
- );
139
+ );
208
140
 
209
- that.then = derivedPromise.then.bind(derivedPromise);
210
- });
141
+ chaiAsPromised.transferPromiseness(that, derivedPromise);
142
+ });
211
143
 
212
- property("eventually", function () {
213
- utils.flag(this, "eventually", true);
214
- });
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
+ }
215
160
 
216
- method("notify", function (done) {
217
- doNotify(getBasePromise(this), done);
218
- });
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;
180
+ }
219
181
 
220
- method("become", function (value) {
221
- return this.eventually.deep.equal(value);
222
- });
182
+ that._obj = value;
223
183
 
224
- ////////
225
- // `eventually`
184
+ assertIfNotNegated(that, assertionMessage, { expected: expected, actual: value });
185
+ },
186
+ function (reason) {
187
+ if (Constructor) {
188
+ that.assert(reason instanceof Constructor,
189
+ "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);
193
+ }
226
194
 
227
- // We need to be careful not to trigger any getters, thus `Object.getOwnPropertyDescriptor` usage.
228
- var methodNames = propertyNames.filter(function (name) {
229
- return name !== "assert" && typeof propertyDescs[name].value === "function";
230
- });
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
+ }
231
214
 
232
- methodNames.forEach(function (methodName) {
233
- Assertion.overwriteMethod(methodName, function (originalMethod) {
234
- return function () {
235
- doAsserterAsyncAndAddThen(originalMethod, this, arguments);
236
- };
237
- });
238
- });
239
-
240
- var getterNames = propertyNames.filter(function (name) {
241
- return name !== "_obj" && typeof propertyDescs[name].get === "function";
242
- });
243
-
244
- getterNames.forEach(function (getterName) {
245
- var propertyDesc = propertyDescs[getterName];
246
-
247
- // Chainable methods are things like `an`, which can work both for `.should.be.an.instanceOf` and as
248
- // `should.be.an("object")`. We need to handle those specially.
249
- var isChainableMethod = false;
250
- try {
251
- isChainableMethod = typeof propertyDesc.get.call({}) === "function";
252
- } catch (e) { }
253
-
254
- if (isChainableMethod) {
255
- Assertion.addChainableMethod(
256
- getterName,
257
- function () {
258
- var assertion = this;
259
- function originalMethod() {
260
- return propertyDesc.get.call(assertion).apply(assertion, arguments);
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);
261
221
  }
262
- doAsserterAsyncAndAddThen(originalMethod, this, arguments);
263
- },
264
- function () {
265
- var originalGetter = propertyDesc.get;
266
- doAsserterAsyncAndAddThen(originalGetter, this);
267
222
  }
268
223
  );
269
- } else {
270
- Assertion.overwriteProperty(getterName, function (originalGetter) {
224
+
225
+ chaiAsPromised.transferPromiseness(that, derivedPromise);
226
+ });
227
+
228
+ property("eventually", function () {
229
+ utils.flag(this, "eventually", true);
230
+ });
231
+
232
+ method("notify", function (done) {
233
+ doNotify(getBasePromise(this), done);
234
+ });
235
+
236
+ method("become", function (value) {
237
+ return this.eventually.deep.equal(value);
238
+ });
239
+
240
+ ////////
241
+ // `eventually`
242
+
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
+ });
247
+
248
+ methodNames.forEach(function (methodName) {
249
+ Assertion.overwriteMethod(methodName, function (originalMethod) {
271
250
  return function () {
272
- doAsserterAsyncAndAddThen(originalGetter, this);
251
+ doAsserterAsyncAndAddThen(originalMethod, this, arguments);
273
252
  };
274
253
  });
275
- }
276
- });
277
-
278
- function doAsserterAsyncAndAddThen(asserter, assertion, args) {
279
- // Since we're intercepting all methods/properties, we need to just pass through if they don't want
280
- // `eventually`, or if we've already fulfilled the promise (see below).
281
- if (!utils.flag(assertion, "eventually")) {
282
- return asserter.apply(assertion, args);
283
- }
254
+ });
284
255
 
285
- var derivedPromise = getBasePromise(assertion).then(function (value) {
286
- // Set up the environment for the asserter to actually run: `_obj` should be the fulfillment value, and
287
- // now that we have the value, we're no longer in "eventually" mode, so we won't run any of this code, just
288
- // the base Chai code that we get to via the short-circuit above.
289
- assertion._obj = value;
290
- utils.flag(assertion, "eventually", false);
291
- asserter.apply(assertion, args);
292
-
293
- // Because asserters, for example `property`, can change the value of `_obj` (i.e. change the "object"
294
- // flag), we need to communicate this value change to subsequent chained asserters. Since we build a
295
- // promise chain paralleling the asserter chain, we can use it to communicate such changes.
296
- return assertion._obj;
256
+ var getterNames = propertyNames.filter(function (name) {
257
+ return name !== "_obj" && typeof propertyDescs[name].get === "function";
297
258
  });
298
259
 
299
- assertion.then = derivedPromise.then.bind(derivedPromise);
300
- }
260
+ getterNames.forEach(function (getterName) {
261
+ var propertyDesc = propertyDescs[getterName];
262
+
263
+ // Chainable methods are things like `an`, which can work both for `.should.be.an.instanceOf` and as
264
+ // `should.be.an("object")`. We need to handle those specially.
265
+ var isChainableMethod = false;
266
+ try {
267
+ isChainableMethod = typeof propertyDesc.get.call({}) === "function";
268
+ } catch (e) { }
269
+
270
+ if (isChainableMethod) {
271
+ Assertion.addChainableMethod(
272
+ getterName,
273
+ function () {
274
+ var assertion = this;
275
+ function originalMethod() {
276
+ return propertyDesc.get.call(assertion).apply(assertion, arguments);
277
+ }
278
+ doAsserterAsyncAndAddThen(originalMethod, this, arguments);
279
+ },
280
+ function () {
281
+ var originalGetter = propertyDesc.get;
282
+ doAsserterAsyncAndAddThen(originalGetter, this);
283
+ }
284
+ );
285
+ } else {
286
+ Assertion.overwriteProperty(getterName, function (originalGetter) {
287
+ return function () {
288
+ doAsserterAsyncAndAddThen(originalGetter, this);
289
+ };
290
+ });
291
+ }
292
+ });
301
293
 
302
- ///////
303
- // Now use the `Assertion` framework to build an `assert` interface.
304
- var originalAssertMethods = Object.getOwnPropertyNames(assert).filter(function (propName) {
305
- return typeof assert[propName] === "function";
306
- });
294
+ function doAsserterAsyncAndAddThen(asserter, assertion, args) {
295
+ // Since we're intercepting all methods/properties, we need to just pass through if they don't want
296
+ // `eventually`, or if we've already fulfilled the promise (see below).
297
+ if (!utils.flag(assertion, "eventually")) {
298
+ return asserter.apply(assertion, args);
299
+ }
307
300
 
308
- assert.isFulfilled = function (promise, message) {
309
- return (new Assertion(promise, message)).to.be.fulfilled;
310
- };
301
+ var derivedPromise = getBasePromise(assertion).then(function (value) {
302
+ // Set up the environment for the asserter to actually run: `_obj` should be the fulfillment value, and
303
+ // now that we have the value, we're no longer in "eventually" mode, so we won't run any of this code,
304
+ // just the base Chai code that we get to via the short-circuit above.
305
+ assertion._obj = value;
306
+ utils.flag(assertion, "eventually", false);
307
+
308
+ return args ? chaiAsPromised.transformAsserterArgs(args) : args;
309
+ }).then(function (args) {
310
+ asserter.apply(assertion, args);
311
+
312
+ // Because asserters, for example `property`, can change the value of `_obj` (i.e. change the "object"
313
+ // flag), we need to communicate this value change to subsequent chained asserters. Since we build a
314
+ // promise chain paralleling the asserter chain, we can use it to communicate such changes.
315
+ return assertion._obj;
316
+ });
311
317
 
312
- assert.isRejected = function (promise, toTestAgainst, message) {
313
- if (typeof toTestAgainst === "string") {
314
- message = toTestAgainst;
315
- toTestAgainst = undefined;
318
+ chaiAsPromised.transferPromiseness(assertion, derivedPromise);
316
319
  }
317
320
 
318
- var assertion = (new Assertion(promise, message));
319
- return toTestAgainst !== undefined ? assertion.to.be.rejectedWith(toTestAgainst) : assertion.to.be.rejected;
320
- };
321
-
322
- assert.becomes = function (promise, value) {
323
- return assert.eventually.deepEqual(promise, value);
324
- };
325
-
326
- assert.doesNotBecome = function (promise, value) {
327
- return assert.eventually.notDeepEqual(promise, value);
328
- };
321
+ ///////
322
+ // Now use the `Assertion` framework to build an `assert` interface.
323
+ var originalAssertMethods = Object.getOwnPropertyNames(assert).filter(function (propName) {
324
+ return typeof assert[propName] === "function";
325
+ });
329
326
 
330
- assert.eventually = {};
331
- originalAssertMethods.forEach(function (assertMethodName) {
332
- assert.eventually[assertMethodName] = function (promise) {
333
- var otherArgs = Array.prototype.slice.call(arguments, 1);
327
+ assert.isFulfilled = function (promise, message) {
328
+ return (new Assertion(promise, message)).to.be.fulfilled;
329
+ };
334
330
 
335
- var customRejectionHandler;
336
- var message = arguments[assert[assertMethodName].length - 1];
337
- if (typeof message === "string") {
338
- customRejectionHandler = function (reason) {
339
- throw new chai.AssertionError(message + "\n\nOriginal reason: " + utils.inspect(reason));
340
- };
331
+ assert.isRejected = function (promise, toTestAgainst, message) {
332
+ if (typeof toTestAgainst === "string") {
333
+ message = toTestAgainst;
334
+ toTestAgainst = undefined;
341
335
  }
342
336
 
343
- var returnedPromise = promise.then(
344
- function (fulfillmentValue) {
345
- return assert[assertMethodName].apply(assert, [fulfillmentValue].concat(otherArgs));
346
- },
347
- customRejectionHandler
348
- );
337
+ var assertion = (new Assertion(promise, message));
338
+ return toTestAgainst !== undefined ? assertion.to.be.rejectedWith(toTestAgainst) : assertion.to.be.rejected;
339
+ };
349
340
 
350
- returnedPromise.notify = function (done) {
351
- doNotify(returnedPromise, done);
352
- };
341
+ assert.becomes = function (promise, value, message) {
342
+ return assert.eventually.deepEqual(promise, value, message);
343
+ };
353
344
 
354
- return returnedPromise;
345
+ assert.doesNotBecome = function (promise, value, message) {
346
+ return assert.eventually.notDeepEqual(promise, value, message);
355
347
  };
356
- });
357
- }));
348
+
349
+ assert.eventually = {};
350
+ originalAssertMethods.forEach(function (assertMethodName) {
351
+ assert.eventually[assertMethodName] = function (promise) {
352
+ var otherArgs = Array.prototype.slice.call(arguments, 1);
353
+
354
+ var customRejectionHandler;
355
+ var message = arguments[assert[assertMethodName].length - 1];
356
+ if (typeof message === "string") {
357
+ customRejectionHandler = function (reason) {
358
+ throw new chai.AssertionError(message + "\n\nOriginal reason: " + utils.inspect(reason));
359
+ };
360
+ }
361
+
362
+ var returnedPromise = promise.then(
363
+ function (fulfillmentValue) {
364
+ return assert[assertMethodName].apply(assert, [fulfillmentValue].concat(otherArgs));
365
+ },
366
+ customRejectionHandler
367
+ );
368
+
369
+ returnedPromise.notify = function (done) {
370
+ doNotify(returnedPromise, done);
371
+ };
372
+
373
+ return returnedPromise;
374
+ };
375
+ });
376
+ }
377
+ }());
package/package.json CHANGED
@@ -8,35 +8,34 @@
8
8
  "promises",
9
9
  "promises-aplus"
10
10
  ],
11
- "version": "4.0.0",
12
- "author": "Domenic Denicola <domenic@domenicdenicola.com> (http://domenicdenicola.com)",
11
+ "version": "4.3.0",
12
+ "author": "Domenic Denicola <d@domenic.me> (https://domenic.me)",
13
13
  "license": "WTFPL",
14
- "repository": {
15
- "type": "git",
16
- "url": "git://github.com/domenic/chai-as-promised.git"
17
- },
18
- "bugs": "https://github.com/domenic/chai-as-promised/issues",
14
+ "repository": "domenic/chai-as-promised",
19
15
  "main": "./lib/chai-as-promised.js",
16
+ "files": [
17
+ "lib"
18
+ ],
20
19
  "scripts": {
21
20
  "test": "mocha",
22
21
  "test-browser-q": "coffee ./test/browser/runner.coffee q",
23
22
  "test-browser-when": "coffee ./test/browser/runner.coffee when",
24
23
  "lint": "jshint ./lib",
25
- "cover": "cover run node_modules/mocha/bin/_mocha && cover report html && opener ./cover_html/index.html"
24
+ "cover": "istanbul cover node_modules/mocha/bin/_mocha && opener ./coverage/lcov-report/lib/chai-as-promised.js.html"
26
25
  },
27
26
  "peerDependencies": {
28
- "chai": ">= 1.7.0 < 2"
27
+ "chai": ">= 1.7.0 < 3"
29
28
  },
30
29
  "devDependencies": {
31
- "chai": "~1.8.0",
32
- "coffee-script": "~1.6.3",
33
- "cover": "~0.2.8",
34
- "ecstatic": "~0.4.9",
35
- "glob": "~3.2.6",
36
- "jshint": "~2.1.11",
37
- "mocha": "~1.13.0",
38
- "opener": "~1.3",
39
- "q": "~0.9.7",
40
- "underscore": "~1.5.2"
30
+ "chai": "^2.0.0",
31
+ "coffee-script": "1.9.0",
32
+ "istanbul": "0.3.5",
33
+ "ecstatic": "0.5.8",
34
+ "glob": "^4.3.5",
35
+ "jshint": "^2.6.0",
36
+ "mocha": "^1.21.5",
37
+ "opener": "^1.4.0",
38
+ "q": "^1.1.2",
39
+ "underscore": "1.7.0"
41
40
  }
42
41
  }