chai-as-promised 4.3.0 → 5.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/README.md +32 -51
- package/lib/chai-as-promised.js +21 -23
- package/package.json +45 -39
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
|
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) {
|
@@ -134,9 +127,7 @@ Promise.resolve(2).should.eventually.equal(2); // will now fail!
|
|
134
127
|
Promise.resolve(2).should.eventually.equal(3); // 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,11 @@ 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]. 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.
|
159
148
|
|
160
149
|
### Working with Non-Promise–Friendly Test Runners
|
161
150
|
|
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:
|
151
|
+
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
152
|
|
168
153
|
```javascript
|
169
154
|
it("should be fulfilled", function (done) {
|
@@ -175,12 +160,9 @@ it("should be rejected", function (done) {
|
|
175
160
|
});
|
176
161
|
```
|
177
162
|
|
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."`
|
163
|
+
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
164
|
|
182
|
-
There's another form of `notify` which is useful in certain situations, like doing assertions after a promise is
|
183
|
-
complete. For example:
|
165
|
+
There's another form of `notify` which is useful in certain situations, like doing assertions after a promise is complete. For example:
|
184
166
|
|
185
167
|
```javascript
|
186
168
|
it("should change the state", function (done) {
|
@@ -191,26 +173,23 @@ it("should change the state", function (done) {
|
|
191
173
|
});
|
192
174
|
```
|
193
175
|
|
194
|
-
Notice how `.notify(done)` is hanging directly off of `.should`, instead of appearing after a promise assertion. This
|
195
|
-
|
196
|
-
|
197
|
-
rejected, but will fail with a simple Chai error (`expected "before" to equal "after"`) if `otherState` does not change.
|
176
|
+
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.
|
177
|
+
|
178
|
+
### Multiple Promise Assertions
|
198
179
|
|
199
|
-
|
180
|
+
To perform assertions on multiple promises, use `Promise.all` to combine multiple Chai as Promised assertions:
|
200
181
|
|
201
182
|
```javascript
|
202
|
-
it("should all be well", function (
|
203
|
-
|
183
|
+
it("should all be well", function () {
|
184
|
+
return Promise.all([
|
204
185
|
promiseA.should.become("happy"),
|
205
186
|
promiseB.should.eventually.have.property("fun times"),
|
206
187
|
promiseC.should.be.rejectedWith(TypeError, "only joyful types are allowed")
|
207
|
-
])
|
188
|
+
]);
|
208
189
|
});
|
209
190
|
```
|
210
191
|
|
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)`.
|
192
|
+
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
193
|
|
215
194
|
## Installation and Setup
|
216
195
|
|
@@ -225,14 +204,11 @@ var chaiAsPromised = require("chai-as-promised");
|
|
225
204
|
chai.use(chaiAsPromised);
|
226
205
|
```
|
227
206
|
|
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].
|
207
|
+
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].
|
230
208
|
|
231
209
|
### AMD
|
232
210
|
|
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:
|
211
|
+
Chai as Promised supports being used as an [AMD][amd] module, registering itself anonymously (just like Chai). So, assuming you have configured your loader to map the Chai and Chai as Promised files to the respective module IDs `"chai"` and `"chai-as-promised"`, you can use them as follows:
|
236
212
|
|
237
213
|
```javascript
|
238
214
|
define(function (require, exports, module) {
|
@@ -245,27 +221,32 @@ define(function (require, exports, module) {
|
|
245
221
|
|
246
222
|
### `<script>` tag
|
247
223
|
|
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:
|
224
|
+
If you include Chai as Promised directly with a `<script>` tag, after the one for Chai itself, then it will automatically plug in to Chai and be ready for use:
|
250
225
|
|
251
226
|
```html
|
252
227
|
<script src="chai.js"></script>
|
253
228
|
<script src="chai-as-promised.js"></script>
|
254
229
|
```
|
255
230
|
|
231
|
+
### Karma
|
232
|
+
|
233
|
+
If you're using [Karma][], check out the accompanying [karma-chai-as-promised][] plugin.
|
234
|
+
|
256
235
|
### Browser Compatibility
|
257
236
|
|
258
237
|
Chai as Promised is only compatible with modern browsers (IE ≥9, Safari ≥6, no PhantomJS).
|
259
238
|
|
260
239
|
[presentation]: http://www.slideshare.net/domenicdenicola/callbacks-promises-and-coroutines-oh-my-the-evolution-of-asynchronicity-in-javascript
|
261
240
|
[chai]: http://chaijs.com/
|
262
|
-
[Mocha-promises]: http://
|
241
|
+
[Mocha-promises]: http://mochajs.org/#asynchronous-code
|
263
242
|
[Buster-promises]: http://docs.busterjs.org/en/latest/modules/buster-test/spec/#returning-a-promise
|
264
243
|
[blue-tape]: https://github.com/spion/blue-tape
|
265
244
|
[spec]: http://promisesaplus.com/
|
266
245
|
[transformation behavior]: http://domenic.me/2012/10/14/youre-missing-the-point-of-promises/#toc_2
|
267
|
-
[Mocha]: http://
|
246
|
+
[Mocha]: http://mochajs.org
|
268
247
|
[fixturedemo]: https://github.com/domenic/chai-as-promised/tree/master/test/
|
269
248
|
[amd]: https://github.com/amdjs/amdjs-api/wiki/AMD
|
270
249
|
[sinon]: http://sinonjs.org/
|
271
250
|
[sinon-chai]: https://github.com/domenic/sinon-chai
|
251
|
+
[Karma]: https://karma-runner.github.io/
|
252
|
+
[karma-chai-as-promised]: https://github.com/vlkosinov/karma-chai-as-promised
|
package/lib/chai-as-promised.js
CHANGED
@@ -34,8 +34,11 @@
|
|
34
34
|
var Assertion = chai.Assertion;
|
35
35
|
var assert = chai.assert;
|
36
36
|
|
37
|
-
function
|
38
|
-
|
37
|
+
function isLegacyJQueryPromise(thenable) {
|
38
|
+
// jQuery promises are Promises/A+-compatible since 3.0.0. jQuery 3.0.0 is also the first version
|
39
|
+
// to define the catch method.
|
40
|
+
return typeof thenable.catch !== "function" &&
|
41
|
+
typeof thenable.always === "function" &&
|
39
42
|
typeof thenable.done === "function" &&
|
40
43
|
typeof thenable.fail === "function" &&
|
41
44
|
typeof thenable.pipe === "function" &&
|
@@ -47,9 +50,10 @@
|
|
47
50
|
if (typeof assertion._obj.then !== "function") {
|
48
51
|
throw new TypeError(utils.inspect(assertion._obj) + " is not a thenable.");
|
49
52
|
}
|
50
|
-
if (
|
51
|
-
throw new TypeError("Chai as Promised is incompatible with jQuery
|
52
|
-
"Promises/A+ compatible library (see
|
53
|
+
if (isLegacyJQueryPromise(assertion._obj)) {
|
54
|
+
throw new TypeError("Chai as Promised is incompatible with thenables of jQuery<3.0.0, sorry! Please " +
|
55
|
+
"upgrade jQuery or use another Promises/A+ compatible library (see " +
|
56
|
+
"http://promisesaplus.com/).");
|
53
57
|
}
|
54
58
|
}
|
55
59
|
|
@@ -233,8 +237,8 @@
|
|
233
237
|
doNotify(getBasePromise(this), done);
|
234
238
|
});
|
235
239
|
|
236
|
-
method("become", function (value) {
|
237
|
-
return this.eventually.deep.equal(value);
|
240
|
+
method("become", function (value, message) {
|
241
|
+
return this.eventually.deep.equal(value, message);
|
238
242
|
});
|
239
243
|
|
240
244
|
////////
|
@@ -258,28 +262,22 @@
|
|
258
262
|
});
|
259
263
|
|
260
264
|
getterNames.forEach(function (getterName) {
|
261
|
-
var propertyDesc = propertyDescs[getterName];
|
262
|
-
|
263
265
|
// Chainable methods are things like `an`, which can work both for `.should.be.an.instanceOf` and as
|
264
266
|
// `should.be.an("object")`. We need to handle those specially.
|
265
|
-
var isChainableMethod =
|
266
|
-
try {
|
267
|
-
isChainableMethod = typeof propertyDesc.get.call({}) === "function";
|
268
|
-
} catch (e) { }
|
267
|
+
var isChainableMethod = Assertion.prototype.__methods.hasOwnProperty(getterName);
|
269
268
|
|
270
269
|
if (isChainableMethod) {
|
271
|
-
Assertion.
|
270
|
+
Assertion.overwriteChainableMethod(
|
272
271
|
getterName,
|
273
|
-
function () {
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
}
|
278
|
-
doAsserterAsyncAndAddThen(originalMethod, this, arguments);
|
272
|
+
function (originalMethod) {
|
273
|
+
return function() {
|
274
|
+
doAsserterAsyncAndAddThen(originalMethod, this, arguments);
|
275
|
+
};
|
279
276
|
},
|
280
|
-
function () {
|
281
|
-
|
282
|
-
|
277
|
+
function (originalGetter) {
|
278
|
+
return function() {
|
279
|
+
doAsserterAsyncAndAddThen(originalGetter, this);
|
280
|
+
};
|
283
281
|
}
|
284
282
|
);
|
285
283
|
} else {
|
package/package.json
CHANGED
@@ -1,41 +1,47 @@
|
|
1
1
|
{
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
"
|
12
|
-
"
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
"
|
27
|
-
|
28
|
-
|
29
|
-
"
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
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": "5.3.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
|
+
"test-browser-jquery": "coffee ./test/browser/runner.coffee jquery",
|
27
|
+
"test-browser-q": "coffee ./test/browser/runner.coffee q",
|
28
|
+
"test-browser-when": "coffee ./test/browser/runner.coffee when",
|
29
|
+
"lint": "jshint ./lib",
|
30
|
+
"cover": "istanbul cover node_modules/mocha/bin/_mocha && opener ./coverage/lcov-report/lib/chai-as-promised.js.html"
|
31
|
+
},
|
32
|
+
"peerDependencies": {
|
33
|
+
"chai": ">= 2.1.2 < 4"
|
34
|
+
},
|
35
|
+
"devDependencies": {
|
36
|
+
"chai": "^3.0.0",
|
37
|
+
"coffee-script": "1.10.0",
|
38
|
+
"istanbul": "0.4.1",
|
39
|
+
"ecstatic": "^1.3.1",
|
40
|
+
"glob": "^6.0.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
|
+
}
|
41
47
|
}
|