chai-as-promised 7.1.2 → 8.0.0-beta.1

Sign up to get free protection for your applications and to get access to all the features.
package/README.md CHANGED
@@ -107,11 +107,13 @@ return promise.then(null, null, progressSpy).then(function () {
107
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:
108
108
 
109
109
  ```js
110
- chaiAsPromised.transferPromiseness = function (assertion, promise) {
110
+ import {setTransferPromiseness} from 'chai-as-promised';
111
+
112
+ setTransferPromiseness(function (assertion, promise) {
111
113
  assertion.then = promise.then.bind(promise); // this is all you get by default
112
114
  assertion.finally = promise.finally.bind(promise);
113
115
  assertion.done = promise.done.bind(promise);
114
- };
116
+ });
115
117
  ```
116
118
 
117
119
  ### Transforming Arguments to the Asserters
@@ -119,9 +121,11 @@ chaiAsPromised.transferPromiseness = function (assertion, promise) {
119
121
  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:
120
122
 
121
123
  ```js
122
- chaiAsPromised.transformAsserterArgs = function (args) {
124
+ import {transformAsserterArgs} from 'chai-as-promised';
125
+
126
+ setTransformAsserterArgs(function (args) {
123
127
  return args.map(function (x) { return x + 1; });
124
- }
128
+ });
125
129
 
126
130
  Promise.resolve(2).should.eventually.equal(2); // will now fail!
127
131
  Promise.resolve(3).should.eventually.equal(2); // will now pass!
@@ -133,9 +137,9 @@ The transform can even be asynchronous, returning a promise for an array instead
133
137
  // This will normally fail, since within() only works on numbers.
134
138
  Promise.resolve(2).should.eventually.be.within(Promise.resolve(1), Promise.resolve(6));
135
139
 
136
- chaiAsPromised.transformAsserterArgs = function (args) {
140
+ setTransformAsserterArgs(function (args) {
137
141
  return Promise.all(args);
138
- };
142
+ });
139
143
 
140
144
  // But now it will pass, since we transformed the array of promises for numbers into
141
145
  // (a promise for) an array of numbers
@@ -213,15 +217,15 @@ This will pass any failures of the individual promise assertions up to the test
213
217
  Do an `npm install chai-as-promised` to get up and running. Then:
214
218
 
215
219
  ```javascript
216
- var chai = require("chai");
217
- var chaiAsPromised = require("chai-as-promised");
220
+ import * as chai from 'chai';
221
+ import chaiAsPromised from 'chai-as-promised';
218
222
 
219
223
  chai.use(chaiAsPromised);
220
224
 
221
225
  // Then either:
222
- var expect = chai.expect;
226
+ const expect = chai.expect;
223
227
  // or:
224
- var assert = chai.assert;
228
+ const assert = chai.assert;
225
229
  // or:
226
230
  chai.should();
227
231
  // according to your preference of assertion style
@@ -231,14 +235,12 @@ You can of course put this code in a common test fixture file; for an example us
231
235
 
232
236
  **Note when using other Chai plugins:** Chai as Promised finds all currently-registered asserters and promisifies them, at the time it is installed. Thus, you should install Chai as Promised _last_, after any other Chai plugins, if you expect their asserters to be promisified.
233
237
 
234
- ### In the Browser
235
-
236
- To use Chai as Promised in environments that don't support Node.js-like CommonJS modules, you'll need to use a bundling tool like [browserify](http://browserify.org/). See also the note below about browser compatibility.
237
-
238
238
  ### Karma
239
239
 
240
240
  If you're using [Karma](https://karma-runner.github.io/), check out the accompanying [karma-chai-as-promised](https://github.com/vlkosinov/karma-chai-as-promised) plugin.
241
241
 
242
242
  ### Browser/Node Compatibility
243
243
 
244
- Chai as Promised requires Node v4+ or a browser with equivalent support for modern JavaScript syntax. If your browser doesn't support modern JavaScript syntax, you'll need to transpile it down using a tool like [Babel](http://babeljs.io/).
244
+ Chai as Promised requires support for ES modules and modern JavaScript syntax.
245
+ If your browser doesn't support this, you will need to transpile it down using
246
+ a tool like [Babel](https://babeljs.io/).
@@ -1,361 +1,453 @@
1
- "use strict";
2
- /* eslint-disable no-invalid-this */
3
- let checkError = require("check-error");
4
-
5
- module.exports = (chai, utils) => {
6
- const Assertion = chai.Assertion;
7
- const assert = chai.assert;
8
- const proxify = utils.proxify;
9
-
10
- // If we are using a version of Chai that has checkError on it,
11
- // we want to use that version to be consistent. Otherwise, we use
12
- // what was passed to the factory.
13
- if (utils.checkError) {
14
- checkError = utils.checkError;
1
+ import * as checkErrorDefault from 'check-error';
2
+
3
+ let checkError = checkErrorDefault;
4
+
5
+ export default function (chai, utils) {
6
+ const Assertion = chai.Assertion;
7
+ const assert = chai.assert;
8
+ const proxify = utils.proxify;
9
+
10
+ // If we are using a version of Chai that has checkError on it,
11
+ // we want to use that version to be consistent. Otherwise, we use
12
+ // what was passed to the factory.
13
+ if (utils.checkError) {
14
+ checkError = utils.checkError;
15
+ }
16
+
17
+ function isLegacyJQueryPromise(thenable) {
18
+ // jQuery promises are Promises/A+-compatible since 3.0.0. jQuery 3.0.0 is also the first version
19
+ // to define the catch method.
20
+ return (
21
+ typeof thenable.catch !== 'function' &&
22
+ typeof thenable.always === 'function' &&
23
+ typeof thenable.done === 'function' &&
24
+ typeof thenable.fail === 'function' &&
25
+ typeof thenable.pipe === 'function' &&
26
+ typeof thenable.progress === 'function' &&
27
+ typeof thenable.state === 'function'
28
+ );
29
+ }
30
+
31
+ function assertIsAboutPromise(assertion) {
32
+ if (typeof assertion._obj.then !== 'function') {
33
+ throw new TypeError(
34
+ utils.inspect(assertion._obj) + ' is not a thenable.'
35
+ );
15
36
  }
16
-
17
- function isLegacyJQueryPromise(thenable) {
18
- // jQuery promises are Promises/A+-compatible since 3.0.0. jQuery 3.0.0 is also the first version
19
- // to define the catch method.
20
- return typeof thenable.catch !== "function" &&
21
- typeof thenable.always === "function" &&
22
- typeof thenable.done === "function" &&
23
- typeof thenable.fail === "function" &&
24
- typeof thenable.pipe === "function" &&
25
- typeof thenable.progress === "function" &&
26
- typeof thenable.state === "function";
27
- }
28
-
29
- function assertIsAboutPromise(assertion) {
30
- if (typeof assertion._obj.then !== "function") {
31
- throw new TypeError(utils.inspect(assertion._obj) + " is not a thenable.");
32
- }
33
- if (isLegacyJQueryPromise(assertion._obj)) {
34
- throw new TypeError("Chai as Promised is incompatible with thenables of jQuery<3.0.0, sorry! Please " +
35
- "upgrade jQuery or use another Promises/A+ compatible library (see " +
36
- "http://promisesaplus.com/).");
37
- }
37
+ if (isLegacyJQueryPromise(assertion._obj)) {
38
+ throw new TypeError(
39
+ 'Chai as Promised is incompatible with thenables of jQuery<3.0.0, sorry! Please ' +
40
+ 'upgrade jQuery or use another Promises/A+ compatible library (see ' +
41
+ 'http://promisesaplus.com/).'
42
+ );
38
43
  }
44
+ }
39
45
 
40
- function proxifyIfSupported(assertion) {
41
- return proxify === undefined ? assertion : proxify(assertion);
42
- }
43
-
44
- function method(name, asserter) {
45
- utils.addMethod(Assertion.prototype, name, function () {
46
- assertIsAboutPromise(this);
47
- return asserter.apply(this, arguments);
48
- });
49
- }
50
-
51
- function property(name, asserter) {
52
- utils.addProperty(Assertion.prototype, name, function () {
53
- assertIsAboutPromise(this);
54
- return proxifyIfSupported(asserter.apply(this, arguments));
55
- });
56
- }
46
+ function proxifyIfSupported(assertion) {
47
+ return proxify === undefined ? assertion : proxify(assertion);
48
+ }
57
49
 
58
- function doNotify(promise, done) {
59
- promise.then(() => done(), done);
60
- }
50
+ function method(name, asserter) {
51
+ utils.addMethod(Assertion.prototype, name, function () {
52
+ assertIsAboutPromise(this);
53
+ return asserter.apply(this, arguments);
54
+ });
55
+ }
61
56
 
62
- // These are for clarity and to bypass Chai refusing to allow `undefined` as actual when used with `assert`.
63
- function assertIfNegated(assertion, message, extra) {
64
- assertion.assert(true, null, message, extra.expected, extra.actual);
65
- }
57
+ function property(name, asserter) {
58
+ utils.addProperty(Assertion.prototype, name, function () {
59
+ assertIsAboutPromise(this);
60
+ return proxifyIfSupported(asserter.apply(this, arguments));
61
+ });
62
+ }
63
+
64
+ function doNotify(promise, done) {
65
+ promise.then(() => done(), done);
66
+ }
67
+
68
+ // These are for clarity and to bypass Chai refusing to allow `undefined` as actual when used with `assert`.
69
+ function assertIfNegated(assertion, message, extra) {
70
+ assertion.assert(true, null, message, extra.expected, extra.actual);
71
+ }
72
+
73
+ function assertIfNotNegated(assertion, message, extra) {
74
+ assertion.assert(false, message, null, extra.expected, extra.actual);
75
+ }
76
+
77
+ function getBasePromise(assertion) {
78
+ // We need to chain subsequent asserters on top of ones in the chain already (consider
79
+ // `eventually.have.property("foo").that.equals("bar")`), only running them after the existing ones pass.
80
+ // So the first base-promise is `assertion._obj`, but after that we use the assertions themselves, i.e.
81
+ // previously derived promises, to chain off of.
82
+ return typeof assertion.then === 'function' ? assertion : assertion._obj;
83
+ }
84
+
85
+ function getReasonName(reason) {
86
+ return reason instanceof Error
87
+ ? reason.toString()
88
+ : checkError.getConstructorName(reason);
89
+ }
90
+
91
+ // Grab these first, before we modify `Assertion.prototype`.
92
+
93
+ const propertyNames = Object.getOwnPropertyNames(Assertion.prototype);
94
+
95
+ const propertyDescs = {};
96
+ for (const name of propertyNames) {
97
+ propertyDescs[name] = Object.getOwnPropertyDescriptor(
98
+ Assertion.prototype,
99
+ name
100
+ );
101
+ }
102
+
103
+ property('fulfilled', function () {
104
+ const derivedPromise = getBasePromise(this).then(
105
+ (value) => {
106
+ assertIfNegated(
107
+ this,
108
+ 'expected promise not to be fulfilled but it was fulfilled with #{act}',
109
+ {actual: value}
110
+ );
111
+ return value;
112
+ },
113
+ (reason) => {
114
+ assertIfNotNegated(
115
+ this,
116
+ 'expected promise to be fulfilled but it was rejected with #{act}',
117
+ {actual: getReasonName(reason)}
118
+ );
119
+ return reason;
120
+ }
121
+ );
122
+
123
+ transferPromiseness(this, derivedPromise);
124
+ return this;
125
+ });
126
+
127
+ property('rejected', function () {
128
+ const derivedPromise = getBasePromise(this).then(
129
+ (value) => {
130
+ assertIfNotNegated(
131
+ this,
132
+ 'expected promise to be rejected but it was fulfilled with #{act}',
133
+ {actual: value}
134
+ );
135
+ return value;
136
+ },
137
+ (reason) => {
138
+ assertIfNegated(
139
+ this,
140
+ 'expected promise not to be rejected but it was rejected with #{act}',
141
+ {actual: getReasonName(reason)}
142
+ );
66
143
 
67
- function assertIfNotNegated(assertion, message, extra) {
68
- assertion.assert(false, message, null, extra.expected, extra.actual);
144
+ // Return the reason, transforming this into a fulfillment, to allow further assertions, e.g.
145
+ // `promise.should.be.rejected.and.eventually.equal("reason")`.
146
+ return reason;
147
+ }
148
+ );
149
+
150
+ transferPromiseness(this, derivedPromise);
151
+ return this;
152
+ });
153
+
154
+ method('rejectedWith', function (errorLike, errMsgMatcher, message) {
155
+ let errorLikeName = null;
156
+ const negate = utils.flag(this, 'negate') || false;
157
+
158
+ // rejectedWith with that is called without arguments is
159
+ // the same as a plain ".rejected" use.
160
+ if (
161
+ errorLike === undefined &&
162
+ errMsgMatcher === undefined &&
163
+ message === undefined
164
+ ) {
165
+ /* eslint-disable no-unused-expressions */
166
+ return this.rejected;
167
+ /* eslint-enable no-unused-expressions */
69
168
  }
70
169
 
71
- function getBasePromise(assertion) {
72
- // We need to chain subsequent asserters on top of ones in the chain already (consider
73
- // `eventually.have.property("foo").that.equals("bar")`), only running them after the existing ones pass.
74
- // So the first base-promise is `assertion._obj`, but after that we use the assertions themselves, i.e.
75
- // previously derived promises, to chain off of.
76
- return typeof assertion.then === "function" ? assertion : assertion._obj;
170
+ if (message !== undefined) {
171
+ utils.flag(this, 'message', message);
77
172
  }
78
173
 
79
- function getReasonName(reason) {
80
- return reason instanceof Error ? reason.toString() : checkError.getConstructorName(reason);
174
+ if (errorLike instanceof RegExp || typeof errorLike === 'string') {
175
+ errMsgMatcher = errorLike;
176
+ errorLike = null;
177
+ } else if (errorLike && errorLike instanceof Error) {
178
+ errorLikeName = errorLike.toString();
179
+ } else if (typeof errorLike === 'function') {
180
+ errorLikeName = checkError.getConstructorName(errorLike);
181
+ } else {
182
+ errorLike = null;
81
183
  }
184
+ const everyArgIsDefined = Boolean(errorLike && errMsgMatcher);
82
185
 
83
- // Grab these first, before we modify `Assertion.prototype`.
84
-
85
- const propertyNames = Object.getOwnPropertyNames(Assertion.prototype);
86
-
87
- const propertyDescs = {};
88
- for (const name of propertyNames) {
89
- propertyDescs[name] = Object.getOwnPropertyDescriptor(Assertion.prototype, name);
186
+ let matcherRelation = 'including';
187
+ if (errMsgMatcher instanceof RegExp) {
188
+ matcherRelation = 'matching';
90
189
  }
91
190
 
92
- property("fulfilled", function () {
93
- const derivedPromise = getBasePromise(this).then(
94
- value => {
95
- assertIfNegated(this,
96
- "expected promise not to be fulfilled but it was fulfilled with #{act}",
97
- { actual: value });
98
- return value;
99
- },
100
- reason => {
101
- assertIfNotNegated(this,
102
- "expected promise to be fulfilled but it was rejected with #{act}",
103
- { actual: getReasonName(reason) });
104
- return reason;
105
- }
106
- );
107
-
108
- module.exports.transferPromiseness(this, derivedPromise);
109
- return this;
110
- });
111
-
112
- property("rejected", function () {
113
- const derivedPromise = getBasePromise(this).then(
114
- value => {
115
- assertIfNotNegated(this,
116
- "expected promise to be rejected but it was fulfilled with #{act}",
117
- { actual: value });
118
- return value;
119
- },
120
- reason => {
121
- assertIfNegated(this,
122
- "expected promise not to be rejected but it was rejected with #{act}",
123
- { actual: getReasonName(reason) });
124
-
125
- // Return the reason, transforming this into a fulfillment, to allow further assertions, e.g.
126
- // `promise.should.be.rejected.and.eventually.equal("reason")`.
127
- return reason;
128
- }
129
- );
130
-
131
- module.exports.transferPromiseness(this, derivedPromise);
132
- return this;
133
- });
134
-
135
- method("rejectedWith", function (errorLike, errMsgMatcher, message) {
136
- let errorLikeName = null;
137
- const negate = utils.flag(this, "negate") || false;
138
-
139
- // rejectedWith with that is called without arguments is
140
- // the same as a plain ".rejected" use.
141
- if (errorLike === undefined && errMsgMatcher === undefined &&
142
- message === undefined) {
143
- /* eslint-disable no-unused-expressions */
144
- return this.rejected;
145
- /* eslint-enable no-unused-expressions */
191
+ const derivedPromise = getBasePromise(this).then(
192
+ (value) => {
193
+ let assertionMessage = null;
194
+ let expected = null;
195
+
196
+ if (errorLike) {
197
+ assertionMessage =
198
+ 'expected promise to be rejected with #{exp} but it was fulfilled with #{act}';
199
+ expected = errorLikeName;
200
+ } else if (errMsgMatcher) {
201
+ assertionMessage =
202
+ `expected promise to be rejected with an error ${matcherRelation} #{exp} but ` +
203
+ `it was fulfilled with #{act}`;
204
+ expected = errMsgMatcher;
146
205
  }
147
206
 
148
- if (message !== undefined) {
149
- utils.flag(this, "message", message);
150
- }
151
-
152
- if (errorLike instanceof RegExp || typeof errorLike === "string") {
153
- errMsgMatcher = errorLike;
154
- errorLike = null;
155
- } else if (errorLike && errorLike instanceof Error) {
156
- errorLikeName = errorLike.toString();
157
- } else if (typeof errorLike === "function") {
158
- errorLikeName = checkError.getConstructorName(errorLike);
207
+ assertIfNotNegated(this, assertionMessage, {expected, actual: value});
208
+ return value;
209
+ },
210
+ (reason) => {
211
+ const errorLikeCompatible =
212
+ errorLike &&
213
+ (errorLike instanceof Error
214
+ ? checkError.compatibleInstance(reason, errorLike)
215
+ : checkError.compatibleConstructor(reason, errorLike));
216
+
217
+ const errMsgMatcherCompatible =
218
+ errMsgMatcher === reason ||
219
+ (errMsgMatcher &&
220
+ reason &&
221
+ checkError.compatibleMessage(reason, errMsgMatcher));
222
+
223
+ const reasonName = getReasonName(reason);
224
+
225
+ if (negate && everyArgIsDefined) {
226
+ if (errorLikeCompatible && errMsgMatcherCompatible) {
227
+ this.assert(
228
+ true,
229
+ null,
230
+ 'expected promise not to be rejected with #{exp} but it was rejected ' +
231
+ 'with #{act}',
232
+ errorLikeName,
233
+ reasonName
234
+ );
235
+ }
159
236
  } else {
160
- errorLike = null;
237
+ if (errorLike) {
238
+ this.assert(
239
+ errorLikeCompatible,
240
+ 'expected promise to be rejected with #{exp} but it was rejected with #{act}',
241
+ 'expected promise not to be rejected with #{exp} but it was rejected ' +
242
+ 'with #{act}',
243
+ errorLikeName,
244
+ reasonName
245
+ );
246
+ }
247
+
248
+ if (errMsgMatcher) {
249
+ this.assert(
250
+ errMsgMatcherCompatible,
251
+ `expected promise to be rejected with an error ${matcherRelation} #{exp} but got ` +
252
+ `#{act}`,
253
+ `expected promise not to be rejected with an error ${matcherRelation} #{exp}`,
254
+ errMsgMatcher,
255
+ checkError.getMessage(reason)
256
+ );
257
+ }
161
258
  }
162
- const everyArgIsDefined = Boolean(errorLike && errMsgMatcher);
163
259
 
164
- let matcherRelation = "including";
165
- if (errMsgMatcher instanceof RegExp) {
166
- matcherRelation = "matching";
260
+ return reason;
261
+ }
262
+ );
263
+
264
+ transferPromiseness(this, derivedPromise);
265
+ return this;
266
+ });
267
+
268
+ property('eventually', function () {
269
+ utils.flag(this, 'eventually', true);
270
+ return this;
271
+ });
272
+
273
+ method('notify', function (done) {
274
+ doNotify(getBasePromise(this), done);
275
+ return this;
276
+ });
277
+
278
+ method('become', function (value, message) {
279
+ return this.eventually.deep.equal(value, message);
280
+ });
281
+
282
+ // ### `eventually`
283
+
284
+ // We need to be careful not to trigger any getters, thus `Object.getOwnPropertyDescriptor` usage.
285
+ const methodNames = propertyNames.filter((name) => {
286
+ return name !== 'assert' && typeof propertyDescs[name].value === 'function';
287
+ });
288
+
289
+ methodNames.forEach((methodName) => {
290
+ Assertion.overwriteMethod(
291
+ methodName,
292
+ (originalMethod) =>
293
+ function () {
294
+ return doAsserterAsyncAndAddThen(originalMethod, this, arguments);
167
295
  }
168
-
169
- const derivedPromise = getBasePromise(this).then(
170
- value => {
171
- let assertionMessage = null;
172
- let expected = null;
173
-
174
- if (errorLike) {
175
- assertionMessage = "expected promise to be rejected with #{exp} but it was fulfilled with #{act}";
176
- expected = errorLikeName;
177
- } else if (errMsgMatcher) {
178
- assertionMessage = `expected promise to be rejected with an error ${matcherRelation} #{exp} but ` +
179
- `it was fulfilled with #{act}`;
180
- expected = errMsgMatcher;
181
- }
182
-
183
- assertIfNotNegated(this, assertionMessage, { expected, actual: value });
184
- return value;
185
- },
186
- reason => {
187
- const errorLikeCompatible = errorLike && (errorLike instanceof Error ?
188
- checkError.compatibleInstance(reason, errorLike) :
189
- checkError.compatibleConstructor(reason, errorLike));
190
-
191
- const errMsgMatcherCompatible = errMsgMatcher && checkError.compatibleMessage(reason, errMsgMatcher);
192
-
193
- const reasonName = getReasonName(reason);
194
-
195
- if (negate && everyArgIsDefined) {
196
- if (errorLikeCompatible && errMsgMatcherCompatible) {
197
- this.assert(true,
198
- null,
199
- "expected promise not to be rejected with #{exp} but it was rejected " +
200
- "with #{act}",
201
- errorLikeName,
202
- reasonName);
203
- }
204
- } else {
205
- if (errorLike) {
206
- this.assert(errorLikeCompatible,
207
- "expected promise to be rejected with #{exp} but it was rejected with #{act}",
208
- "expected promise not to be rejected with #{exp} but it was rejected " +
209
- "with #{act}",
210
- errorLikeName,
211
- reasonName);
212
- }
213
-
214
- if (errMsgMatcher) {
215
- this.assert(errMsgMatcherCompatible,
216
- `expected promise to be rejected with an error ${matcherRelation} #{exp} but got ` +
217
- `#{act}`,
218
- `expected promise not to be rejected with an error ${matcherRelation} #{exp}`,
219
- errMsgMatcher,
220
- checkError.getMessage(reason));
221
- }
222
- }
223
-
224
- return reason;
225
- }
226
- );
227
-
228
- module.exports.transferPromiseness(this, derivedPromise);
229
- return this;
230
- });
231
-
232
- property("eventually", function () {
233
- utils.flag(this, "eventually", true);
234
- return this;
235
- });
236
-
237
- method("notify", function (done) {
238
- doNotify(getBasePromise(this), done);
239
- return this;
240
- });
241
-
242
- method("become", function (value, message) {
243
- return this.eventually.deep.equal(value, message);
244
- });
245
-
246
- // ### `eventually`
247
-
248
- // We need to be careful not to trigger any getters, thus `Object.getOwnPropertyDescriptor` usage.
249
- const methodNames = propertyNames.filter(name => {
250
- return name !== "assert" && typeof propertyDescs[name].value === "function";
251
- });
252
-
253
- methodNames.forEach(methodName => {
254
- Assertion.overwriteMethod(methodName, originalMethod => function () {
296
+ );
297
+ });
298
+
299
+ const getterNames = propertyNames.filter((name) => {
300
+ return name !== '_obj' && typeof propertyDescs[name].get === 'function';
301
+ });
302
+
303
+ getterNames.forEach((getterName) => {
304
+ // Chainable methods are things like `an`, which can work both for `.should.be.an.instanceOf` and as
305
+ // `should.be.an("object")`. We need to handle those specially.
306
+ const isChainableMethod = Object.prototype.hasOwnProperty.call(
307
+ Assertion.prototype.__methods,
308
+ getterName
309
+ );
310
+
311
+ if (isChainableMethod) {
312
+ Assertion.overwriteChainableMethod(
313
+ getterName,
314
+ (originalMethod) =>
315
+ function () {
255
316
  return doAsserterAsyncAndAddThen(originalMethod, this, arguments);
256
- });
257
- });
258
-
259
- const getterNames = propertyNames.filter(name => {
260
- return name !== "_obj" && typeof propertyDescs[name].get === "function";
261
- });
262
-
263
- getterNames.forEach(getterName => {
264
- // Chainable methods are things like `an`, which can work both for `.should.be.an.instanceOf` and as
265
- // `should.be.an("object")`. We need to handle those specially.
266
- const isChainableMethod = Assertion.prototype.__methods.hasOwnProperty(getterName);
267
-
268
- if (isChainableMethod) {
269
- Assertion.overwriteChainableMethod(
270
- getterName,
271
- originalMethod => function () {
272
- return doAsserterAsyncAndAddThen(originalMethod, this, arguments);
273
- },
274
- originalGetter => function () {
275
- return doAsserterAsyncAndAddThen(originalGetter, this);
276
- }
317
+ },
318
+ (originalGetter) =>
319
+ function () {
320
+ return doAsserterAsyncAndAddThen(originalGetter, this);
321
+ }
322
+ );
323
+ } else {
324
+ Assertion.overwriteProperty(
325
+ getterName,
326
+ (originalGetter) =>
327
+ function () {
328
+ return proxifyIfSupported(
329
+ doAsserterAsyncAndAddThen(originalGetter, this)
277
330
  );
278
- } else {
279
- Assertion.overwriteProperty(getterName, originalGetter => function () {
280
- return proxifyIfSupported(doAsserterAsyncAndAddThen(originalGetter, this));
281
- });
282
- }
283
- });
284
-
285
- function doAsserterAsyncAndAddThen(asserter, assertion, args) {
286
- // Since we're intercepting all methods/properties, we need to just pass through if they don't want
287
- // `eventually`, or if we've already fulfilled the promise (see below).
288
- if (!utils.flag(assertion, "eventually")) {
289
- asserter.apply(assertion, args);
290
- return assertion;
291
- }
292
-
293
- const derivedPromise = getBasePromise(assertion).then(value => {
294
- // Set up the environment for the asserter to actually run: `_obj` should be the fulfillment value, and
295
- // now that we have the value, we're no longer in "eventually" mode, so we won't run any of this code,
296
- // just the base Chai code that we get to via the short-circuit above.
297
- assertion._obj = value;
298
- utils.flag(assertion, "eventually", false);
299
-
300
- return args ? module.exports.transformAsserterArgs(args) : args;
301
- }).then(newArgs => {
302
- asserter.apply(assertion, newArgs);
303
-
304
- // Because asserters, for example `property`, can change the value of `_obj` (i.e. change the "object"
305
- // flag), we need to communicate this value change to subsequent chained asserters. Since we build a
306
- // promise chain paralleling the asserter chain, we can use it to communicate such changes.
307
- return assertion._obj;
308
- });
309
-
310
- module.exports.transferPromiseness(assertion, derivedPromise);
311
- return assertion;
331
+ }
332
+ );
333
+ }
334
+ });
335
+
336
+ function doAsserterAsyncAndAddThen(asserter, assertion, args) {
337
+ // Since we're intercepting all methods/properties, we need to just pass through if they don't want
338
+ // `eventually`, or if we've already fulfilled the promise (see below).
339
+ if (!utils.flag(assertion, 'eventually')) {
340
+ asserter.apply(assertion, args);
341
+ return assertion;
312
342
  }
313
343
 
314
- // ### Now use the `Assertion` framework to build an `assert` interface.
315
- const originalAssertMethods = Object.getOwnPropertyNames(assert).filter(propName => {
316
- return typeof assert[propName] === "function";
317
- });
318
-
319
- assert.isFulfilled = (promise, message) => (new Assertion(promise, message)).to.be.fulfilled;
320
-
321
- assert.isRejected = (promise, errorLike, errMsgMatcher, message) => {
322
- const assertion = new Assertion(promise, message);
323
- return assertion.to.be.rejectedWith(errorLike, errMsgMatcher, message);
324
- };
325
-
326
- assert.becomes = (promise, value, message) => assert.eventually.deepEqual(promise, value, message);
327
-
328
- assert.doesNotBecome = (promise, value, message) => assert.eventually.notDeepEqual(promise, value, message);
329
-
330
- assert.eventually = {};
331
- originalAssertMethods.forEach(assertMethodName => {
332
- assert.eventually[assertMethodName] = function (promise) {
333
- const otherArgs = Array.prototype.slice.call(arguments, 1);
334
-
335
- let customRejectionHandler;
336
- const message = arguments[assert[assertMethodName].length - 1];
337
- if (typeof message === "string") {
338
- customRejectionHandler = reason => {
339
- throw new chai.AssertionError(`${message}\n\nOriginal reason: ${utils.inspect(reason)}`);
340
- };
341
- }
342
-
343
- const returnedPromise = promise.then(
344
- fulfillmentValue => assert[assertMethodName].apply(assert, [fulfillmentValue].concat(otherArgs)),
345
- customRejectionHandler
346
- );
347
-
348
- returnedPromise.notify = done => {
349
- doNotify(returnedPromise, done);
350
- };
351
-
352
- return returnedPromise;
344
+ const derivedPromise = getBasePromise(assertion)
345
+ .then((value) => {
346
+ // Set up the environment for the asserter to actually run: `_obj` should be the fulfillment value, and
347
+ // now that we have the value, we're no longer in "eventually" mode, so we won't run any of this code,
348
+ // just the base Chai code that we get to via the short-circuit above.
349
+ assertion._obj = value;
350
+ utils.flag(assertion, 'eventually', false);
351
+
352
+ return args ? transformAsserterArgs(args) : args;
353
+ })
354
+ .then((newArgs) => {
355
+ asserter.apply(assertion, newArgs);
356
+
357
+ // Because asserters, for example `property`, can change the value of `_obj` (i.e. change the "object"
358
+ // flag), we need to communicate this value change to subsequent chained asserters. Since we build a
359
+ // promise chain paralleling the asserter chain, we can use it to communicate such changes.
360
+ return assertion._obj;
361
+ });
362
+
363
+ transferPromiseness(assertion, derivedPromise);
364
+ return assertion;
365
+ }
366
+
367
+ // ### Now use the `Assertion` framework to build an `assert` interface.
368
+ const originalAssertMethods = Object.getOwnPropertyNames(assert).filter(
369
+ (propName) => {
370
+ return typeof assert[propName] === 'function';
371
+ }
372
+ );
373
+
374
+ assert.isFulfilled = (promise, message) =>
375
+ new Assertion(promise, message).to.be.fulfilled;
376
+
377
+ assert.isRejected = (promise, errorLike, errMsgMatcher, message) => {
378
+ const assertion = new Assertion(promise, message);
379
+ return assertion.to.be.rejectedWith(errorLike, errMsgMatcher, message);
380
+ };
381
+
382
+ assert.becomes = (promise, value, message) =>
383
+ assert.eventually.deepEqual(promise, value, message);
384
+
385
+ assert.doesNotBecome = (promise, value, message) =>
386
+ assert.eventually.notDeepEqual(promise, value, message);
387
+
388
+ assert.eventually = {};
389
+ originalAssertMethods.forEach((assertMethodName) => {
390
+ assert.eventually[assertMethodName] = function (promise) {
391
+ const otherArgs = Array.prototype.slice.call(arguments, 1);
392
+
393
+ let customRejectionHandler;
394
+ const message = arguments[assert[assertMethodName].length - 1];
395
+ if (typeof message === 'string') {
396
+ customRejectionHandler = (reason) => {
397
+ throw new chai.AssertionError(
398
+ `${message}\n\nOriginal reason: ${utils.inspect(reason)}`
399
+ );
353
400
  };
354
- });
355
- };
356
-
357
- module.exports.transferPromiseness = (assertion, promise) => {
358
- assertion.then = promise.then.bind(promise);
359
- };
360
-
361
- module.exports.transformAsserterArgs = values => values;
401
+ }
402
+
403
+ const returnedPromise = promise.then(
404
+ (fulfillmentValue) =>
405
+ assert[assertMethodName].apply(
406
+ assert,
407
+ [fulfillmentValue].concat(otherArgs)
408
+ ),
409
+ customRejectionHandler
410
+ );
411
+
412
+ returnedPromise.notify = (done) => {
413
+ doNotify(returnedPromise, done);
414
+ };
415
+
416
+ return returnedPromise;
417
+ };
418
+ });
419
+ }
420
+
421
+ function defaultTransferPromiseness(assertion, promise) {
422
+ assertion.then = promise.then.bind(promise);
423
+ }
424
+
425
+ function defaultTransformAsserterArgs(values) {
426
+ return values;
427
+ }
428
+
429
+ let customTransferPromiseness;
430
+ let customTransformAsserterArgs;
431
+
432
+ export function setTransferPromiseness(fn) {
433
+ customTransferPromiseness = fn || defaultTransferPromiseness;
434
+ }
435
+
436
+ export function setTransformAsserterArgs(fn) {
437
+ customTransformAsserterArgs = fn || defaultTransformAsserterArgs;
438
+ }
439
+
440
+ export function transferPromiseness(assertion, promise) {
441
+ if (customTransferPromiseness) {
442
+ customTransferPromiseness(assertion, promise);
443
+ } else {
444
+ defaultTransferPromiseness(assertion, promise);
445
+ }
446
+ }
447
+
448
+ export function transformAsserterArgs(values) {
449
+ if (customTransformAsserterArgs) {
450
+ return customTransformAsserterArgs(values);
451
+ }
452
+ return defaultTransformAsserterArgs(values);
453
+ }
package/package.json CHANGED
@@ -1,5 +1,6 @@
1
1
  {
2
2
  "name": "chai-as-promised",
3
+ "type": "module",
3
4
  "description": "Extends Chai with assertions about promises.",
4
5
  "keywords": [
5
6
  "chai",
@@ -11,30 +12,36 @@
11
12
  "promises",
12
13
  "promises-aplus"
13
14
  ],
14
- "version": "7.1.2",
15
+ "version": "8.0.0-beta.1",
15
16
  "author": "Domenic Denicola <d@domenic.me> (https://domenic.me)",
16
17
  "license": "WTFPL",
17
- "repository": "domenic/chai-as-promised",
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "git+https://github.com/chaijs/chai-as-promised.git"
21
+ },
18
22
  "main": "./lib/chai-as-promised.js",
23
+ "exports": {
24
+ ".": "./lib/chai-as-promised.js"
25
+ },
19
26
  "files": [
20
27
  "lib"
21
28
  ],
22
29
  "scripts": {
23
- "test": "mocha",
24
- "test-travis": "npm install chai@$CHAI_VERSION && npm test",
30
+ "test": "c8 mocha",
25
31
  "lint": "eslint .",
26
- "cover": "istanbul cover node_modules/mocha/bin/_mocha && opener ./coverage/lcov-report/lib/chai-as-promised.js.html"
32
+ "format": "prettier --write lib test"
27
33
  },
28
34
  "dependencies": {
29
- "check-error": "^1.0.2"
35
+ "check-error": "^2.0.0"
30
36
  },
31
37
  "peerDependencies": {
32
38
  "chai": ">= 2.1.2 < 6"
33
39
  },
34
40
  "devDependencies": {
35
- "chai": "^4.0.2",
36
- "eslint": "^3.19.0",
37
- "istanbul": "0.4.5",
38
- "mocha": "^3.4.2"
41
+ "c8": "^9.1.0",
42
+ "chai": "^5.1.0",
43
+ "eslint": "^8.57.0",
44
+ "mocha": "^10.4.0",
45
+ "prettier": "^3.2.5"
39
46
  }
40
47
  }