konacha-chai-matchers 0.1.5 → 0.1.6

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.
@@ -1,4 +1,4 @@
1
- (function (chaiAsPromised) {
1
+ (function () {
2
2
  "use strict";
3
3
 
4
4
  // Module systems magic dance.
@@ -12,122 +12,133 @@
12
12
  return chaiAsPromised;
13
13
  });
14
14
  } else {
15
+ /*global self: false */
16
+
15
17
  // Other environment (usually <script> tag): plug in to global chai instance directly.
16
18
  chai.use(chaiAsPromised);
19
+
20
+ // Expose as a property of the global object so that consumers can configure the `transferPromiseness` property.
21
+ self.chaiAsPromised = chaiAsPromised;
17
22
  }
18
- }(function chaiAsPromised(chai, utils) {
19
- "use strict";
20
23
 
21
- var Assertion = chai.Assertion;
22
- var assert = chai.assert;
24
+ chaiAsPromised.transferPromiseness = function (assertion, promise) {
25
+ assertion.then = promise.then.bind(promise);
26
+ };
23
27
 
24
- function assertIsAboutPromise(assertion) {
25
- if (typeof assertion._obj.then !== "function") {
26
- throw new TypeError(utils.inspect(assertion._obj) + " is not a promise!");
28
+ function chaiAsPromised(chai, utils) {
29
+ var Assertion = chai.Assertion;
30
+ var assert = chai.assert;
31
+
32
+ function isJQueryPromise(thenable) {
33
+ return typeof thenable.always === "function" &&
34
+ typeof thenable.done === "function" &&
35
+ typeof thenable.fail === "function" &&
36
+ typeof thenable.pipe === "function" &&
37
+ typeof thenable.progress === "function" &&
38
+ typeof thenable.state === "function";
27
39
  }
28
- if (typeof assertion._obj.pipe === "function") {
29
- throw new TypeError("Chai as Promised is incompatible with jQuery's so-called “promises.” Sorry!");
40
+
41
+ function assertIsAboutPromise(assertion) {
42
+ if (typeof assertion._obj.then !== "function") {
43
+ throw new TypeError(utils.inspect(assertion._obj) + " is not a thenable.");
44
+ }
45
+ if (isJQueryPromise(assertion._obj)) {
46
+ throw new TypeError("Chai as Promised is incompatible with jQuery's thenables, sorry! Please use a " +
47
+ "Promises/A+ compatible library (see http://promisesaplus.com/).");
48
+ }
30
49
  }
31
- }
32
50
 
33
- function property(name, asserter) {
34
- utils.addProperty(Assertion.prototype, name, function () {
35
- assertIsAboutPromise(this);
36
- return asserter.apply(this, arguments);
37
- });
38
- }
51
+ function method(name, asserter) {
52
+ utils.addMethod(Assertion.prototype, name, function () {
53
+ assertIsAboutPromise(this);
54
+ return asserter.apply(this, arguments);
55
+ });
56
+ }
39
57
 
40
- function method(name, asserter) {
41
- utils.addMethod(Assertion.prototype, name, function () {
42
- assertIsAboutPromise(this);
43
- return asserter.apply(this, arguments);
44
- });
45
- }
58
+ function property(name, asserter) {
59
+ utils.addProperty(Assertion.prototype, name, function () {
60
+ assertIsAboutPromise(this);
61
+ return asserter.apply(this, arguments);
62
+ });
63
+ }
46
64
 
47
- function notify(promise, callback) {
48
- return promise.then(function () { callback(); }, callback);
49
- }
65
+ function doNotify(promise, done) {
66
+ promise.then(function () { done(); }, done);
67
+ }
50
68
 
51
- function addNotifyMethod(extensiblePromise) {
52
- extensiblePromise.notify = function (callback) {
53
- return notify(extensiblePromise, callback);
54
- };
55
- }
69
+ // These are for clarity and to bypass Chai refusing to allow `undefined` as actual when used with `assert`.
70
+ function assertIfNegated(assertion, message, extra) {
71
+ assertion.assert(true, null, message, extra.expected, extra.actual);
72
+ }
56
73
 
57
- function fulfilledAsserter() {
58
- /*jshint validthis:true */
59
- var assertion = this;
60
-
61
- var transformedPromise = assertion._obj.then(
62
- function (value) {
63
- if (utils.flag(assertion, "negate")) {
64
- // If we're negated, `this.assert`'s behavior is actually flipped, so `this.assert(true, ...)` will
65
- // throw an error, as desired.
66
- assertion.assert(true, null, "expected promise to be rejected but it was fulfilled with " +
67
- utils.inspect(value));
68
- }
74
+ function assertIfNotNegated(assertion, message, extra) {
75
+ assertion.assert(false, message, null, extra.expected, extra.actual);
76
+ }
69
77
 
70
- return value;
71
- },
72
- function (reason) {
73
- // If we're in a negated state (i.e. `.not.fulfilled`) then this assertion will get flipped and thus
74
- // pass, as desired.
75
- assertion.assert(false, "expected promise to be fulfilled but it was rejected with " +
76
- utils.inspect(reason));
77
- }
78
- );
78
+ function getBasePromise(assertion) {
79
+ // We need to chain subsequent asserters on top of ones in the chain already (consider
80
+ // `eventually.have.property("foo").that.equals("bar")`), only running them after the existing ones pass.
81
+ // So the first base-promise is `assertion._obj`, but after that we use the assertions themselves, i.e.
82
+ // previously derived promises, to chain off of.
83
+ return typeof assertion.then === "function" ? assertion : assertion._obj;
84
+ }
79
85
 
80
- return makeAssertionPromise(transformedPromise, assertion);
81
- }
86
+ // Grab these first, before we modify `Assertion.prototype`.
82
87
 
83
- function rejectedAsserter() {
84
- // THIS SHIT IS COMPLICATED. Best illustrated by exhaustive example.
85
- ////////////////////////////////////////////////////////////////////
86
- // `fulfilledPromise.should.be.rejected`:
87
- // `onOriginalFulfilled` → `this.assert(false, …)` throws → rejects
88
- // `fulfilledPromise.should.not.be.rejected`:
89
- // `onOriginalFulfilled` → `this.assert(false, …)` does nothing → fulfills
90
- // `rejectedPromise.should.be.rejected`:
91
- // `onOriginalRejected` does nothing relevant → fulfills
92
- // `rejectedPromise.should.not.be.rejected`:
93
- // `onOriginalRejected` → `this.assert(true, …)` throws → rejects
94
- // `rejectedPromise.should.be.rejected.with(xxx)`:
95
- // `onOriginalRejected` saves `rejectionReason` → fulfills →
96
- // `with(xxx)` called → `onTransformedFulfilled` → assert about xxx → fulfills/rejects based on asserts
97
- // `rejectedPromise.should.not.be.rejected.with(xxx)`:
98
- // `onOriginalRejected` saves `rejectionReason`, `this.assert(true, …)` throws → rejects →
99
- // `with(xxx)` called → `onTransformedRejected` → assert about xxx → fulfills/rejects based on asserts
100
- // `fulfilledPromise.should.be.rejected.with(xxx)`:
101
- // `onOriginalFulfilled` → `this.assert(false, …)` throws → rejects →
102
- // `with(xxx)` called → `onTransformedRejected` → `this.assert(false, …)` throws → rejected
103
- // `fulfilledPromise.should.not.be.rejected.with(xxx)`:
104
- // `onOriginalFulfilled` → `this.assert(false, …)` does nothing → fulfills →
105
- // `with(xxx)` called → `onTransformedFulfilled` → fulfills
106
-
107
- /*jshint validthis:true */
108
- var assertion = this;
109
- var rejectionReason = null;
110
-
111
- function onOriginalFulfilled(value) {
112
- assertion.assert(false, "expected promise to be rejected but it was fulfilled with " + utils.inspect(value));
113
- }
88
+ var propertyNames = Object.getOwnPropertyNames(Assertion.prototype);
114
89
 
115
- function onOriginalRejected(reason) {
116
- // Store the reason so that `with` can look at it later. Be sure to do this before asserting, since
117
- // throwing an error from the assert would cancel the process.
118
- rejectionReason = reason;
90
+ var propertyDescs = {};
91
+ propertyNames.forEach(function (name) {
92
+ propertyDescs[name] = Object.getOwnPropertyDescriptor(Assertion.prototype, name);
93
+ });
119
94
 
120
- if (utils.flag(assertion, "negate")) {
121
- assertion.assert(true, null, "expected promise to be fulfilled but it was rejected with " +
122
- utils.inspect(reason));
123
- }
95
+ property("fulfilled", function () {
96
+ var that = this;
97
+ var derivedPromise = getBasePromise(that).then(
98
+ function (value) {
99
+ that._obj = value;
100
+ assertIfNegated(that,
101
+ "expected promise not to be fulfilled but it was fulfilled with #{act}",
102
+ { actual: value });
103
+ return value;
104
+ },
105
+ function (reason) {
106
+ assertIfNotNegated(that,
107
+ "expected promise to be fulfilled but it was rejected with #{act}",
108
+ { actual: reason });
109
+ }
110
+ );
124
111
 
125
- // If we didn't throw from the assert, transform rejections into fulfillments, by not re-throwing the
126
- // reason.
127
- }
112
+ chaiAsPromised.transferPromiseness(that, derivedPromise);
113
+ });
114
+
115
+ property("rejected", function () {
116
+ var that = this;
117
+ var derivedPromise = getBasePromise(that).then(
118
+ function (value) {
119
+ that._obj = value;
120
+ assertIfNotNegated(that,
121
+ "expected promise to be rejected but it was fulfilled with #{act}",
122
+ { actual: value });
123
+ return value;
124
+ },
125
+ function (reason) {
126
+ assertIfNegated(that,
127
+ "expected promise not to be rejected but it was rejected with #{act}",
128
+ { actual: reason });
129
+
130
+ // Return the reason, transforming this into a fulfillment, to allow further assertions, e.g.
131
+ // `promise.should.be.rejected.and.eventually.equal("reason")`.
132
+ return reason;
133
+ }
134
+ );
128
135
 
129
- function withMethod(Constructor, message) {
136
+ chaiAsPromised.transferPromiseness(that, derivedPromise);
137
+ });
138
+
139
+ method("rejectedWith", function (Constructor, message) {
130
140
  var desiredReason = null;
141
+ var constructorName = null;
131
142
 
132
143
  if (Constructor instanceof RegExp || typeof Constructor === "string") {
133
144
  message = Constructor;
@@ -136,272 +147,223 @@
136
147
  desiredReason = Constructor;
137
148
  Constructor = null;
138
149
  message = null;
139
- }
140
-
141
- var messageVerb = null;
142
- var messageIsGood = null;
143
-
144
- if (message instanceof RegExp) {
145
- messageVerb = "matching";
146
- messageIsGood = function () {
147
- return message.test(rejectionReason.message);
148
- };
150
+ } else if (typeof Constructor === "function") {
151
+ constructorName = (new Constructor()).name;
149
152
  } else {
150
- messageVerb = "including";
151
- messageIsGood = function () {
152
- return rejectionReason && rejectionReason.message.indexOf(message) !== -1;
153
- };
154
- }
155
-
156
- function constructorIsGood() {
157
- return rejectionReason instanceof Constructor;
158
- }
159
-
160
- function matchesDesiredReason() {
161
- return rejectionReason === desiredReason;
153
+ Constructor = null;
162
154
  }
163
155
 
164
- function onTransformedFulfilled() {
165
- if (!utils.flag(assertion, "negate")) {
166
- if (desiredReason) {
167
- assertion.assert(matchesDesiredReason(),
168
- null,
169
- "expected promise to be rejected with " + utils.inspect(desiredReason) + " but " +
170
- "it was rejected with " + utils.inspect(rejectionReason));
171
- }
156
+ var that = this;
157
+ var derivedPromise = getBasePromise(that).then(
158
+ function (value) {
159
+ var assertionMessage = null;
160
+ var expected = null;
172
161
 
173
162
  if (Constructor) {
174
- assertion.assert(constructorIsGood(),
175
- "expected promise to be rejected with " + Constructor.prototype.name + " but it " +
176
- "was rejected with " + utils.inspect(rejectionReason));
163
+ assertionMessage = "expected promise to be rejected with #{exp} but it was fulfilled with " +
164
+ "#{act}";
165
+ expected = constructorName;
166
+ } else if (message) {
167
+ var verb = message instanceof RegExp ? "matching" : "including";
168
+ assertionMessage = "expected promise to be rejected with an error " + verb + " #{exp} but it " +
169
+ "was fulfilled with #{act}";
170
+ expected = message;
171
+ } else if (desiredReason) {
172
+ assertionMessage = "expected promise to be rejected with #{exp} but it was fulfilled with " +
173
+ "#{act}";
174
+ expected = desiredReason;
177
175
  }
178
176
 
179
- if (message) {
180
- assertion.assert(messageIsGood(),
181
- "expected promise to be rejected with an error " + messageVerb + " " + message +
182
- " but got " + utils.inspect(rejectionReason && rejectionReason.message));
183
- }
184
- }
185
- }
186
-
187
- function onTransformedRejected() {
188
- if (utils.flag(assertion, "negate")) {
189
- if (desiredReason) {
190
- assertion.assert(matchesDesiredReason(),
191
- null,
192
- "expected promise to not be rejected with " + utils.inspect(desiredReason));
193
- }
177
+ that._obj = value;
194
178
 
179
+ assertIfNotNegated(that, assertionMessage, { expected: expected, actual: value });
180
+ },
181
+ function (reason) {
195
182
  if (Constructor) {
196
- assertion.assert(constructorIsGood(),
197
- null,
198
- "expected promise to not be rejected with " + Constructor.prototype.name);
199
- }
200
-
201
- if (message) {
202
- assertion.assert(messageIsGood(),
203
- null,
204
- "expected promise to be not be rejected with an error " + messageVerb + " " +
205
- message);
206
- }
207
- } else {
208
- if (desiredReason) {
209
- assertion.assert(false,
210
- "expected promise to be rejected with " + utils.inspect(desiredReason) +
211
- " but it was fulfilled");
183
+ that.assert(reason instanceof Constructor,
184
+ "expected promise to be rejected with #{exp} but it was rejected with #{act}",
185
+ "expected promise not to be rejected with #{exp} but it was rejected with #{act}",
186
+ constructorName,
187
+ reason);
212
188
  }
213
189
 
214
- if (Constructor) {
215
- assertion.assert(false, "expected promise to be rejected with " + Constructor.prototype.name +
216
- " but it was fulfilled");
190
+ var reasonMessage = utils.type(reason) === "object" && "message" in reason ?
191
+ reason.message :
192
+ "" + reason;
193
+ if (message && reasonMessage !== null && reasonMessage !== undefined) {
194
+ if (message instanceof RegExp) {
195
+ that.assert(message.test(reasonMessage),
196
+ "expected promise to be rejected with an error matching #{exp} but got #{act}",
197
+ "expected promise not to be rejected with an error matching #{exp}",
198
+ message,
199
+ reasonMessage);
200
+ }
201
+ if (typeof message === "string") {
202
+ that.assert(reasonMessage.indexOf(message) !== -1,
203
+ "expected promise to be rejected with an error including #{exp} but got #{act}",
204
+ "expected promise not to be rejected with an error including #{exp}",
205
+ message,
206
+ reasonMessage);
207
+ }
217
208
  }
218
209
 
219
- if (message) {
220
- assertion.assert(false, "expected promise to be rejected with an error " + messageVerb + " " +
221
- message + " but it was fulfilled");
210
+ if (desiredReason) {
211
+ that.assert(reason === desiredReason,
212
+ "expected promise to be rejected with #{exp} but it was rejected with #{act}",
213
+ "expected promise not to be rejected with #{exp}",
214
+ desiredReason,
215
+ reason);
222
216
  }
223
217
  }
224
- }
225
-
226
- return makeAssertionPromise(
227
- transformedPromise.then(onTransformedFulfilled, onTransformedRejected),
228
- assertion
229
218
  );
230
- }
231
-
232
- var derivedPromise = assertion._obj.then(onOriginalFulfilled, onOriginalRejected);
233
- var transformedPromise = makeAssertionPromise(derivedPromise, assertion);
234
- Object.defineProperty(transformedPromise, "with", { enumerable: true, configurable: true, value: withMethod });
235
-
236
- return transformedPromise;
237
- }
238
-
239
- function isChaiAsPromisedAsserter(asserterName) {
240
- return ["fulfilled", "rejected", "broken", "eventually", "become"].indexOf(asserterName) !== -1;
241
- }
242
219
 
243
- function makeAssertionPromiseToDoAsserter(currentAssertionPromise, previousAssertionPromise, doAsserter) {
244
- var promiseToDoAsserter = currentAssertionPromise.then(function (fulfillmentValue) {
245
- // The previous assertion promise might have picked up some flags while waiting for fulfillment.
246
- utils.transferFlags(previousAssertionPromise, currentAssertionPromise);
247
-
248
- // Replace the object flag with the fulfillment value, so that doAsserter can operate properly.
249
- utils.flag(currentAssertionPromise, "object", fulfillmentValue);
220
+ chaiAsPromised.transferPromiseness(that, derivedPromise);
221
+ });
250
222
 
251
- // Perform the actual asserter action and return the result of it.
252
- return doAsserter();
223
+ property("eventually", function () {
224
+ utils.flag(this, "eventually", true);
253
225
  });
254
- return makeAssertionPromise(promiseToDoAsserter, currentAssertionPromise);
255
- }
256
226
 
257
- function makeAssertionPromise(promise, baseAssertion) {
258
- // An assertion-promise is an (extensible!) promise with the following additions:
259
- var assertionPromise = Object.create(promise);
227
+ method("notify", function (done) {
228
+ doNotify(getBasePromise(this), done);
229
+ });
260
230
 
261
- // 1. A `notify` method.
262
- addNotifyMethod(assertionPromise);
231
+ method("become", function (value) {
232
+ return this.eventually.deep.equal(value);
233
+ });
263
234
 
264
- // 2. An `assert` method that acts exactly as it would on an assertion. This is called by promisified
265
- // asserters after the promise fulfills.
266
- assertionPromise.assert = function () {
267
- return Assertion.prototype.assert.apply(assertionPromise, arguments);
268
- };
235
+ ////////
236
+ // `eventually`
269
237
 
270
- Object.defineProperty(assertionPromise, "_obj", Object.getOwnPropertyDescriptor(Assertion.prototype, "_obj"));
238
+ // We need to be careful not to trigger any getters, thus `Object.getOwnPropertyDescriptor` usage.
239
+ var methodNames = propertyNames.filter(function (name) {
240
+ return name !== "assert" && typeof propertyDescs[name].value === "function";
241
+ });
271
242
 
272
- // 3. Chai asserters, which act upon the promise's fulfillment value.
273
- var asserterNames = Object.getOwnPropertyNames(Assertion.prototype);
274
- asserterNames.forEach(function (asserterName) {
275
- // We already added `notify` and `assert`; don't mess with those.
276
- if (asserterName === "notify" || asserterName === "assert" || asserterName === "_obj") {
277
- return;
278
- }
243
+ methodNames.forEach(function (methodName) {
244
+ Assertion.overwriteMethod(methodName, function (originalMethod) {
245
+ return function () {
246
+ doAsserterAsyncAndAddThen(originalMethod, this, arguments);
247
+ };
248
+ });
249
+ });
279
250
 
280
- // Only add asserters for other libraries; poison-pill Chai as Promised ones.
281
- if (isChaiAsPromisedAsserter(asserterName)) {
282
- utils.addProperty(assertionPromise, asserterName, function () {
283
- throw new Error("Cannot use Chai as Promised asserters more than once in an assertion.");
284
- });
285
- return;
286
- }
251
+ var getterNames = propertyNames.filter(function (name) {
252
+ return name !== "_obj" && typeof propertyDescs[name].get === "function";
253
+ });
287
254
 
288
- // The asserter will need to be added differently depending on its type. In all cases we use
289
- // `makeAssertionPromiseToDoAsserter`, which, given this current `assertionPromise` we are going to
290
- // return, plus the `baseAssertion` we are basing it off of, will return a new assertion-promise that
291
- // builds off of `assertionPromise` and `baseAssertion` to perform the actual asserter action upon
292
- // fulfillment.
293
- var propertyDescriptor = Object.getOwnPropertyDescriptor(Assertion.prototype, asserterName);
294
-
295
- if (typeof propertyDescriptor.value === "function") {
296
- // Case 1: simple method asserters
297
- utils.addMethod(assertionPromise, asserterName, function () {
298
- var args = arguments;
299
-
300
- return makeAssertionPromiseToDoAsserter(assertionPromise, baseAssertion, function () {
301
- return propertyDescriptor.value.apply(assertionPromise, args);
302
- });
303
- });
304
- } else if (typeof propertyDescriptor.get === "function") {
305
- // Case 2: property asserters. These break down into two subcases: chainable methods, and pure
306
- // properties. An example of the former is `a`/`an`: `.should.be.an.instanceOf` vs.
307
- // `should.be.an("object")`.
308
- var isChainableMethod = false;
309
- try {
310
- isChainableMethod = typeof propertyDescriptor.get.call({}) === "function";
311
- } catch (e) { }
312
-
313
- if (isChainableMethod) {
314
- // Case 2A: chainable methods. Recreate the chainable method, but operating on the augmented
315
- // promise. We need to copy both the assertion behavior and the chaining behavior, since the
316
- // chaining behavior might for example set flags on the object.
317
- utils.addChainableMethod(
318
- assertionPromise,
319
- asserterName,
320
- function () {
321
- var args = arguments;
322
-
323
- return makeAssertionPromiseToDoAsserter(assertionPromise, baseAssertion, function () {
324
- // Due to https://github.com/chaijs/chai/commit/514dd6ce466d7b4110b38345e4073d586c017f3f
325
- // we can't use `propertyDescriptor.get().apply`.
326
- return Function.prototype.apply.call(propertyDescriptor.get(), assertionPromise, args);
327
- });
328
- },
329
- function () {
330
- // As above.
331
- return Function.prototype.call.call(propertyDescriptor.get, assertionPromise);
255
+ getterNames.forEach(function (getterName) {
256
+ var propertyDesc = propertyDescs[getterName];
257
+
258
+ // Chainable methods are things like `an`, which can work both for `.should.be.an.instanceOf` and as
259
+ // `should.be.an("object")`. We need to handle those specially.
260
+ var isChainableMethod = false;
261
+ try {
262
+ isChainableMethod = typeof propertyDesc.get.call({}) === "function";
263
+ } catch (e) { }
264
+
265
+ if (isChainableMethod) {
266
+ Assertion.addChainableMethod(
267
+ getterName,
268
+ function () {
269
+ var assertion = this;
270
+ function originalMethod() {
271
+ return propertyDesc.get.call(assertion).apply(assertion, arguments);
332
272
  }
333
- );
334
- } else {
335
- // Case 2B: pure property case
336
- utils.addProperty(assertionPromise, asserterName, function () {
337
- return makeAssertionPromiseToDoAsserter(assertionPromise, baseAssertion, function () {
338
- return propertyDescriptor.get.call(assertionPromise);
339
- });
340
- });
341
- }
273
+ doAsserterAsyncAndAddThen(originalMethod, this, arguments);
274
+ },
275
+ function () {
276
+ var originalGetter = propertyDesc.get;
277
+ doAsserterAsyncAndAddThen(originalGetter, this);
278
+ }
279
+ );
280
+ } else {
281
+ Assertion.overwriteProperty(getterName, function (originalGetter) {
282
+ return function () {
283
+ doAsserterAsyncAndAddThen(originalGetter, this);
284
+ };
285
+ });
342
286
  }
343
287
  });
344
288
 
345
- return assertionPromise;
346
- }
347
-
348
- property("fulfilled", fulfilledAsserter);
349
- property("rejected", rejectedAsserter);
350
- property("broken", rejectedAsserter);
351
-
352
- property("eventually", function () {
353
- return makeAssertionPromise(this._obj, this);
354
- });
289
+ function doAsserterAsyncAndAddThen(asserter, assertion, args) {
290
+ // Since we're intercepting all methods/properties, we need to just pass through if they don't want
291
+ // `eventually`, or if we've already fulfilled the promise (see below).
292
+ if (!utils.flag(assertion, "eventually")) {
293
+ return asserter.apply(assertion, args);
294
+ }
355
295
 
356
- method("become", function (value) {
357
- return this.eventually.eql(value);
358
- });
296
+ var derivedPromise = getBasePromise(assertion).then(function (value) {
297
+ // Set up the environment for the asserter to actually run: `_obj` should be the fulfillment value, and
298
+ // now that we have the value, we're no longer in "eventually" mode, so we won't run any of this code,
299
+ // just the base Chai code that we get to via the short-circuit above.
300
+ assertion._obj = value;
301
+ utils.flag(assertion, "eventually", false);
302
+ asserter.apply(assertion, args);
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
+ });
359
309
 
360
- method("notify", function (callback) {
361
- return notify(this._obj, callback);
362
- });
310
+ chaiAsPromised.transferPromiseness(assertion, derivedPromise);
311
+ }
363
312
 
364
- // Now use the Assertion framework to build an `assert` interface.
365
- var originalAssertMethods = Object.getOwnPropertyNames(assert).filter(function (propName) {
366
- return typeof assert[propName] === "function";
367
- });
313
+ ///////
314
+ // Now use the `Assertion` framework to build an `assert` interface.
315
+ var originalAssertMethods = Object.getOwnPropertyNames(assert).filter(function (propName) {
316
+ return typeof assert[propName] === "function";
317
+ });
368
318
 
369
- assert.isFulfilled = function (promise, message) {
370
- return (new Assertion(promise, message)).to.be.fulfilled;
371
- };
319
+ assert.isFulfilled = function (promise, message) {
320
+ return (new Assertion(promise, message)).to.be.fulfilled;
321
+ };
372
322
 
373
- assert.isRejected = assert.isBroken = function (promise, toTestAgainst, message) {
374
- if (typeof toTestAgainst === "string") {
375
- message = toTestAgainst;
376
- toTestAgainst = null;
377
- }
323
+ assert.isRejected = function (promise, toTestAgainst, message) {
324
+ if (typeof toTestAgainst === "string") {
325
+ message = toTestAgainst;
326
+ toTestAgainst = undefined;
327
+ }
378
328
 
379
- var shouldBeRejectedPromise = (new Assertion(promise, message)).to.be.rejected;
329
+ var assertion = (new Assertion(promise, message));
330
+ return toTestAgainst !== undefined ? assertion.to.be.rejectedWith(toTestAgainst) : assertion.to.be.rejected;
331
+ };
380
332
 
381
- // Use `['with']` to handle crappy non-ES5 environments like PhantomJS.
382
- return toTestAgainst ? shouldBeRejectedPromise['with'](toTestAgainst) : shouldBeRejectedPromise;
383
- };
333
+ assert.becomes = function (promise, value) {
334
+ return assert.eventually.deepEqual(promise, value);
335
+ };
384
336
 
385
- assert.eventually = {};
386
- originalAssertMethods.forEach(function (assertMethodName) {
387
- assert.eventually[assertMethodName] = function (promise) {
388
- var otherArgs = Array.prototype.slice.call(arguments, 1);
337
+ assert.doesNotBecome = function (promise, value) {
338
+ return assert.eventually.notDeepEqual(promise, value);
339
+ };
389
340
 
390
- var promiseToAssert = promise.then(function (fulfillmentValue) {
391
- return assert[assertMethodName].apply(assert, [fulfillmentValue].concat(otherArgs));
392
- });
341
+ assert.eventually = {};
342
+ originalAssertMethods.forEach(function (assertMethodName) {
343
+ assert.eventually[assertMethodName] = function (promise) {
344
+ var otherArgs = Array.prototype.slice.call(arguments, 1);
345
+
346
+ var customRejectionHandler;
347
+ var message = arguments[assert[assertMethodName].length - 1];
348
+ if (typeof message === "string") {
349
+ customRejectionHandler = function (reason) {
350
+ throw new chai.AssertionError(message + "\n\nOriginal reason: " + utils.inspect(reason));
351
+ };
352
+ }
393
353
 
394
- var augmentedPromiseToAssert = Object.create(promiseToAssert);
395
- addNotifyMethod(augmentedPromiseToAssert);
396
- return augmentedPromiseToAssert;
397
- };
398
- });
354
+ var returnedPromise = promise.then(
355
+ function (fulfillmentValue) {
356
+ return assert[assertMethodName].apply(assert, [fulfillmentValue].concat(otherArgs));
357
+ },
358
+ customRejectionHandler
359
+ );
399
360
 
400
- assert.becomes = function (promise, value) {
401
- return assert.eventually.deepEqual(promise, value);
402
- };
361
+ returnedPromise.notify = function (done) {
362
+ doNotify(returnedPromise, done);
363
+ };
403
364
 
404
- assert.doesNotBecome = function (promise, value) {
405
- return assert.eventually.notDeepEqual(promise, value);
406
- };
407
- }));
365
+ return returnedPromise;
366
+ };
367
+ });
368
+ }
369
+ }());