chai-as-promised 7.1.2 → 8.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +23 -0
- package/README.md +17 -15
- package/lib/chai-as-promised.js +428 -336
- package/package.json +17 -10
- package/LICENSE.txt +0 -19
package/LICENSE
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2012-2016 Domenic Denicola
|
4
|
+
Copyright (c) 2004 Sam Hocevar
|
5
|
+
Copyright (c) 2024 Chai Maintainers
|
6
|
+
|
7
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
8
|
+
of this software and associated documentation files (the "Software"), to deal
|
9
|
+
in the Software without restriction, including without limitation the rights
|
10
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
11
|
+
copies of the Software, and to permit persons to whom the Software is
|
12
|
+
furnished to do so, subject to the following conditions:
|
13
|
+
|
14
|
+
The above copyright notice and this permission notice shall be included in all
|
15
|
+
copies or substantial portions of the Software.
|
16
|
+
|
17
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
18
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
19
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
20
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
21
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
22
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
23
|
+
SOFTWARE.
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
217
|
-
|
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
|
-
|
226
|
+
const expect = chai.expect;
|
223
227
|
// or:
|
224
|
-
|
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
|
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/).
|
package/lib/chai-as-promised.js
CHANGED
@@ -1,361 +1,453 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
let checkError =
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
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
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
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
|
-
|
41
|
-
|
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
|
-
|
59
|
-
|
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
|
-
|
63
|
-
|
64
|
-
|
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
|
-
|
68
|
-
|
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
|
-
|
72
|
-
|
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
|
-
|
80
|
-
|
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
|
-
|
84
|
-
|
85
|
-
|
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
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
}
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
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
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
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
|
-
|
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
|
-
|
165
|
-
|
166
|
-
|
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
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
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
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
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
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
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
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
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
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
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": "
|
15
|
+
"version": "8.0.0",
|
15
16
|
"author": "Domenic Denicola <d@domenic.me> (https://domenic.me)",
|
16
17
|
"license": "WTFPL",
|
17
|
-
"repository":
|
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
|
-
"
|
32
|
+
"format": "prettier --write lib test"
|
27
33
|
},
|
28
34
|
"dependencies": {
|
29
|
-
"check-error": "^
|
35
|
+
"check-error": "^2.0.0"
|
30
36
|
},
|
31
37
|
"peerDependencies": {
|
32
38
|
"chai": ">= 2.1.2 < 6"
|
33
39
|
},
|
34
40
|
"devDependencies": {
|
35
|
-
"
|
36
|
-
"
|
37
|
-
"
|
38
|
-
"mocha": "^
|
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
|
}
|
package/LICENSE.txt
DELETED
@@ -1,19 +0,0 @@
|
|
1
|
-
Copyright © 2012–2016 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
|
8
|
-
Version 2, December 2004
|
9
|
-
|
10
|
-
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
|
11
|
-
|
12
|
-
Everyone is permitted to copy and distribute verbatim or modified
|
13
|
-
copies of this license document, and changing it is allowed as long
|
14
|
-
as the name is changed.
|
15
|
-
|
16
|
-
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
17
|
-
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
18
|
-
|
19
|
-
0. You just DO WHAT THE FUCK YOU WANT TO.
|