chai-as-promised 4.0.0 → 4.3.0

Sign up to get free protection for your applications and to get access to all the features.
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
  }