chai-as-promised 4.3.0 → 5.3.0

Sign up to get free protection for your applications and to get access to all the features.
package/README.md CHANGED
@@ -27,19 +27,19 @@ you can write code that expresses what you really mean:
27
27
  return doSomethingAsync().should.eventually.equal("foo");
28
28
  ```
29
29
 
30
- or if you have a testing framework that doesn't allow returning promises to signal asynchronous test completion, then
31
- you can use the following workaround:
30
+ or if you have a case where `return` is not preferable (e.g. style considerations) or not possible (e.g. the testing framework doesn't allow returning promises to signal asynchronous test completion), then you can use the following workaround (where `done()` is supplied by the test framework):
32
31
 
33
32
  ```javascript
34
33
  doSomethingAsync().should.eventually.equal("foo").notify(done);
35
34
  ```
36
35
 
36
+ *Notice*: either `return` or `notify(done)` _must_ be used with promise assertions. This can be a slight departure from the existing format of assertions being used on a project or by a team. Those other assertions are likely synchronous and thus do not require special handling.
37
+
37
38
  ## How to Use
38
39
 
39
40
  ### `should`/`expect` Interface
40
41
 
41
- The most powerful extension provided by Chai as Promised is the `eventually` property. With it, you can transform any
42
- existing Chai assertion into one that acts on a promise:
42
+ The most powerful extension provided by Chai as Promised is the `eventually` property. With it, you can transform any existing Chai assertion into one that acts on a promise:
43
43
 
44
44
  ```javascript
45
45
  (2 + 2).should.equal(4);
@@ -66,8 +66,7 @@ return promise.should.be.rejectedWith(Error); // other variants of Chai's `throw
66
66
 
67
67
  ### `assert` Interface
68
68
 
69
- As with the `should`/`expect` interface, Chai as Promised provides an `eventually` extender to `chai.assert`, allowing
70
- any existing Chai assertion to be used on a promise:
69
+ As with the `should`/`expect` interface, Chai as Promised provides an `eventually` extender to `chai.assert`, allowing any existing Chai assertion to be used on a promise:
71
70
 
72
71
  ```javascript
73
72
  assert.equal(2 + 2, 4, "This had better be true");
@@ -91,9 +90,7 @@ return assert.isRejected(promise, /error message matcher/, "optional message");
91
90
 
92
91
  ### Progress Callbacks
93
92
 
94
- Chai as Promised does not have any intrinsic support for testing promise progress callbacks. The properties you would
95
- want to test are probably much better suited to a library like [Sinon.JS][sinon], perhaps in conjunction with
96
- [Sinon–Chai][sinon-chai]:
93
+ Chai as Promised does not have any intrinsic support for testing promise progress callbacks. The properties you would want to test are probably much better suited to a library like [Sinon.JS][sinon], perhaps in conjunction with [Sinon–Chai][sinon-chai]:
97
94
 
98
95
  ```javascript
99
96
  var progressSpy = sinon.spy();
@@ -107,10 +104,7 @@ return promise.then(null, null, progressSpy).then(function () {
107
104
 
108
105
  ### Customizing Output Promises
109
106
 
110
- By default, the promises returned by Chai as Promised's assertions are regular Chai assertion objects, extended with
111
- a single `then` method derived from the input promise. To change this behavior, for instance to output a promise with
112
- more useful sugar methods such as are found in most promise libraries, you can override
113
- `chaiAsPromised.transferPromiseness`. Here's an example that transfer's Q's `finally` and `done` methods:
107
+ By default, the promises returned by Chai as Promised's assertions are regular Chai assertion objects, extended with a single `then` method derived from the input promise. To change this behavior, for instance to output a promise with more useful sugar methods such as are found in most promise libraries, you can override `chaiAsPromised.transferPromiseness`. Here's an example that transfer's Q's `finally` and `done` methods:
114
108
 
115
109
  ```js
116
110
  chaiAsPromised.transferPromiseness = function (assertion, promise) {
@@ -122,8 +116,7 @@ chaiAsPromised.transferPromiseness = function (assertion, promise) {
122
116
 
123
117
  ### Transforming Arguments to the Asserters
124
118
 
125
- Another advanced customization hook Chai as Promised allows is if you want to transform the arguments to the
126
- asserters, possibly asynchronously. Here is a toy example:
119
+ Another advanced customization hook Chai as Promised allows is if you want to transform the arguments to the asserters, possibly asynchronously. Here is a toy example:
127
120
 
128
121
  ```js
129
122
  chaiAsPromised.transformAsserterArgs = function (args) {
@@ -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
- indicates to Chai as Promised that it should pass fulfillment or rejection directly through to the testing framework.
196
- Thus, the above code will fail with a Chai as Promised error (`"expected promise to be fulfilled…"`) if `promise` is
197
- rejected, but will fail with a simple Chai error (`expected "before" to equal "after"`) if `otherState` does not change.
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
- Another example of where this can be useful is when performing assertions on multiple promises:
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 (done) {
203
- Q.all([
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
- ]).should.notify(done);
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://visionmedia.github.io/mocha/#asynchronous-code
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://visionmedia.github.com/mocha/
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
@@ -34,8 +34,11 @@
34
34
  var Assertion = chai.Assertion;
35
35
  var assert = chai.assert;
36
36
 
37
- function isJQueryPromise(thenable) {
38
- return typeof thenable.always === "function" &&
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 (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
+ 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 = false;
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.addChainableMethod(
270
+ Assertion.overwriteChainableMethod(
272
271
  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);
272
+ function (originalMethod) {
273
+ return function() {
274
+ doAsserterAsyncAndAddThen(originalMethod, this, arguments);
275
+ };
279
276
  },
280
- function () {
281
- var originalGetter = propertyDesc.get;
282
- doAsserterAsyncAndAddThen(originalGetter, this);
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
- "name": "chai-as-promised",
3
- "description": "Extends Chai with assertions about promises.",
4
- "keywords": [
5
- "chai",
6
- "testing",
7
- "assertions",
8
- "promises",
9
- "promises-aplus"
10
- ],
11
- "version": "4.3.0",
12
- "author": "Domenic Denicola <d@domenic.me> (https://domenic.me)",
13
- "license": "WTFPL",
14
- "repository": "domenic/chai-as-promised",
15
- "main": "./lib/chai-as-promised.js",
16
- "files": [
17
- "lib"
18
- ],
19
- "scripts": {
20
- "test": "mocha",
21
- "test-browser-q": "coffee ./test/browser/runner.coffee q",
22
- "test-browser-when": "coffee ./test/browser/runner.coffee when",
23
- "lint": "jshint ./lib",
24
- "cover": "istanbul cover node_modules/mocha/bin/_mocha && opener ./coverage/lcov-report/lib/chai-as-promised.js.html"
25
- },
26
- "peerDependencies": {
27
- "chai": ">= 1.7.0 < 3"
28
- },
29
- "devDependencies": {
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"
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
  }