strong-mock 8.0.0-beta.1 → 8.0.0-beta.2

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/dist/index.js CHANGED
@@ -168,151 +168,71 @@ ${snippet}`);
168
168
 
169
169
  }
170
170
 
171
- /**
172
- * Controls what happens when a property is accessed, or a call is made,
173
- * and there are no expectations set for it.
174
- */
175
- exports.Strictness = void 0;
171
+ exports.UnexpectedProperty = void 0;
176
172
 
177
- (function (Strictness) {
173
+ (function (UnexpectedProperty) {
178
174
  /**
179
- * Any property that's accessed, or any call that's made, without a matching
180
- * expectation, will throw immediately.
175
+ * Throw an error immediately.
181
176
  *
182
177
  * @example
183
- * type Service = { foo: (x: number) => number };
184
- * const service = mock<Service>();
185
- *
186
- * // This will throw.
178
+ * // Will throw "Didn't expect foo to be accessed".
187
179
  * const { foo } = service;
188
180
  *
189
181
  * // Will throw "Didn't expect foo to be accessed",
190
182
  * // without printing the arguments.
191
183
  * foo(42);
192
184
  */
193
- Strictness[Strictness["SUPER_STRICT"] = 0] = "SUPER_STRICT";
185
+ UnexpectedProperty[UnexpectedProperty["THROW"] = 0] = "THROW";
194
186
  /**
195
- * Properties with unmatched expectations will return functions that will
196
- * throw if called. This can be useful if your code destructures a function
197
- * but never calls it.
187
+ * Return a function that will throw if called. This can be useful if your
188
+ * code destructures a function but never calls it.
198
189
  *
199
190
  * It will also improve error messages for unexpected calls because arguments
200
191
  * will be captured instead of throwing immediately on the property access.
201
192
  *
202
- * @example
203
- * type Service = { foo: (x: number) => number };
204
- * const service = mock<Service>();
193
+ * The function will be returned even if the property is not supposed to be a
194
+ * function. This could cause weird behavior at runtime, when your code expects
195
+ * e.g. a number and gets a function instead.
205
196
  *
206
- * // This will not throw.
197
+ * @example
198
+ * // This will NOT throw.
207
199
  * const { foo } = service;
208
200
  *
201
+ * // This will NOT throw, and might produce unexpected results.
202
+ * foo > 0
203
+ *
209
204
  * // Will throw "Didn't expect foo(42) to be called".
210
205
  * foo(42);
211
206
  */
212
207
 
213
- Strictness[Strictness["STRICT"] = 1] = "STRICT";
214
- })(exports.Strictness || (exports.Strictness = {}));
215
-
216
- const createProxy = traps => // eslint-disable-next-line no-empty-function
217
- new Proxy(
218
- /* istanbul ignore next */
219
- () => {}, {
220
- get: (target, prop) => {
221
- if (prop === 'bind') {
222
- return (thisArg, ...args) => (...moreArgs) => traps.apply([...args, ...moreArgs]);
223
- }
208
+ UnexpectedProperty[UnexpectedProperty["CALL_THROW"] = 1] = "CALL_THROW";
209
+ })(exports.UnexpectedProperty || (exports.UnexpectedProperty = {}));
224
210
 
225
- if (prop === 'apply') {
226
- return (thisArg, args) => traps.apply(args || []);
227
- }
228
-
229
- if (prop === 'call') {
230
- return (thisArg, ...args) => traps.apply(args);
231
- }
232
-
233
- return traps.property(prop);
234
- },
235
- apply: (target, thisArg, args) => traps.apply(args),
236
- ownKeys: () => traps.ownKeys(),
237
-
238
- getOwnPropertyDescriptor(target, prop) {
239
- const keys = traps.ownKeys();
240
-
241
- if (keys.includes(prop)) {
242
- return {
243
- configurable: true,
244
- enumerable: true
245
- };
246
- }
247
-
248
- return undefined;
249
- }
250
-
251
- });
252
-
253
- /**
254
- * Since `when` doesn't receive the mock subject (because we can't make it
255
- * consistently return it from `mock()`, `mock.foo` and `mock.bar()`) we need
256
- * to store a global state for the currently active mock.
257
- *
258
- * We also want to throw in the following case:
259
- *
260
- * ```
261
- * when(() => mock()) // forgot returns here
262
- * when(() => mock()) // should throw
263
- * ```
264
- *
265
- * For that reason we can't just store the currently active mock, but also
266
- * whether we finished the expectation or not.
267
- */
268
-
269
- let activeMock;
270
- const setActiveMock = mock => {
271
- activeMock = mock;
272
- };
273
- const getActiveMock = () => activeMock;
274
211
  /**
275
- * Store a global map of all mocks created and their state.
276
- *
277
- * This is needed because we can't reliably pass the state between `when`
278
- * and `thenReturn`.
279
- */
280
-
281
- const mockMap = new Map();
282
- const getMockState = mock => {
283
- if (mockMap.has(mock)) {
284
- return mockMap.get(mock);
285
- }
286
-
287
- throw new NotAMock();
288
- };
289
- const setMockState = (mock, state) => {
290
- mockMap.set(mock, state);
291
- };
292
- const getAllMocks = () => Array.from(mockMap.entries());
293
-
294
- /**
295
- * Return the expectation's return value.
212
+ * Unbox the expectation's return value.
296
213
  *
297
214
  * If the value is an error then throw it.
298
215
  *
299
216
  * If the value is a promise then resolve/reject it.
300
217
  */
301
-
302
- const returnOrThrow = ({
218
+ const unboxReturnValue = ({
303
219
  isError,
304
220
  isPromise,
305
221
  value
306
222
  }) => {
307
223
  if (isError) {
308
- if (isPromise) {
309
- return Promise.reject(value);
310
- }
311
-
312
224
  if (value instanceof Error) {
225
+ if (isPromise) {
226
+ return Promise.reject(value);
227
+ }
228
+
313
229
  throw value;
314
230
  }
315
231
 
232
+ if (isPromise) {
233
+ return Promise.reject(new Error(value));
234
+ }
235
+
316
236
  throw new Error(value);
317
237
  }
318
238
 
@@ -322,67 +242,20 @@ const returnOrThrow = ({
322
242
 
323
243
  return value;
324
244
  };
325
- const createStub = (repo, pendingExpectation, getCurrentMode, concreteMatcher) => {
326
- const stub = createProxy({
327
- property: property => {
328
- if (getCurrentMode() === Mode.CALL) {
329
- return returnOrThrow(repo.get(property));
330
- }
331
-
332
- setActiveMock(stub);
333
- pendingExpectation.start(repo, concreteMatcher); // eslint-disable-next-line no-param-reassign
334
-
335
- pendingExpectation.property = property;
336
- return createProxy({
337
- property: childProp => {
338
- pendingExpectation.clear();
339
- throw new NestedWhen(property, childProp);
340
- },
341
- apply: args => {
342
- // eslint-disable-next-line no-param-reassign
343
- pendingExpectation.args = args;
344
- },
345
- ownKeys: () => {
346
- throw new Error('Spreading during an expectation is not supported.');
347
- }
348
- });
349
- },
350
- apply: args => {
351
- if (getCurrentMode() === Mode.CALL) {
352
- return repo.apply(args);
353
- }
354
-
355
- setActiveMock(stub);
356
- pendingExpectation.start(repo, concreteMatcher); // eslint-disable-next-line no-param-reassign
357
-
358
- pendingExpectation.property = ApplyProp; // eslint-disable-next-line no-param-reassign
359
-
360
- pendingExpectation.args = args;
361
- return undefined;
362
- },
363
- ownKeys: () => {
364
- if (getCurrentMode() === Mode.CALL) {
365
- return repo.getAllProperties();
366
- }
367
-
368
- throw new Error('Spreading during an expectation is not supported.');
369
- }
370
- });
371
- return stub;
372
- };
373
245
 
374
246
  /**
375
- * An expectation repository for configurable levels of strictness.
247
+ * An expectation repository with a configurable behavior for
248
+ * unexpected property access.
376
249
  */
377
250
 
378
251
  class FlexibleRepository {
379
- constructor(strictness = exports.Strictness.SUPER_STRICT) {
380
- this.strictness = void 0;
252
+ constructor(unexpectedProperty = exports.UnexpectedProperty.THROW) {
253
+ this.unexpectedProperty = void 0;
381
254
  this.expectations = new Map();
382
255
  this.expectedCallStats = new Map();
383
256
  this.unexpectedCallStats = new Map();
384
257
 
385
- this.apply = args => this.get(ApplyProp).value(...args);
258
+ this.apply = args => this.get(ApplyProp)(...args);
386
259
 
387
260
  this.handlePropertyWithMatchingExpectations = (property, expectations) => {
388
261
  // Avoid recording call stats for function calls, since the property is an
@@ -397,64 +270,51 @@ class FlexibleRepository {
397
270
 
398
271
  if (propertyExpectation) {
399
272
  this.countAndConsume(propertyExpectation);
400
- return propertyExpectation.expectation.returnValue;
273
+ return unboxReturnValue(propertyExpectation.expectation.returnValue);
401
274
  }
402
275
 
403
- return {
404
- value: (...args) => {
405
- const callExpectation = expectations.find(e => e.expectation.matches(args));
406
-
407
- if (callExpectation) {
408
- this.recordExpected(property, args);
409
- this.countAndConsume(callExpectation); // TODO: this is duplicated in stub
276
+ return (...args) => {
277
+ const callExpectation = expectations.find(e => e.expectation.matches(args));
410
278
 
411
- return returnOrThrow(callExpectation.expectation.returnValue);
412
- }
413
-
414
- return this.getValueForUnexpectedCall(property, args);
279
+ if (callExpectation) {
280
+ this.recordExpected(property, args);
281
+ this.countAndConsume(callExpectation);
282
+ return unboxReturnValue(callExpectation.expectation.returnValue);
415
283
  }
284
+
285
+ return this.getValueForUnexpectedCall(property, args);
416
286
  };
417
287
  };
418
288
 
419
289
  this.handlePropertyWithNoExpectations = property => {
420
290
  switch (property) {
421
291
  case 'toString':
422
- return {
423
- value: () => 'mock'
424
- };
292
+ return () => 'mock';
425
293
 
426
294
  case '@@toStringTag':
427
295
  case Symbol.toStringTag:
428
296
  case 'name':
429
- return {
430
- value: 'mock'
431
- };
297
+ return 'mock';
432
298
  // pretty-format
433
299
 
434
300
  case '$$typeof':
435
301
  case 'constructor':
436
302
  case '@@__IMMUTABLE_ITERABLE__@@':
437
303
  case '@@__IMMUTABLE_RECORD__@@':
438
- return {
439
- value: null
440
- };
304
+ return null;
441
305
 
442
306
  case MATCHER_SYMBOL:
443
- return {
444
- value: false
445
- };
307
+ return false;
446
308
 
447
309
  case ApplyProp:
448
- return {
449
- value: (...args) => this.getValueForUnexpectedCall(property, args)
450
- };
310
+ return (...args) => this.getValueForUnexpectedCall(property, args);
451
311
 
452
312
  default:
453
313
  return this.getValueForUnexpectedAccess(property);
454
314
  }
455
315
  };
456
316
 
457
- this.strictness = strictness;
317
+ this.unexpectedProperty = unexpectedProperty;
458
318
  }
459
319
 
460
320
  add(expectation) {
@@ -474,6 +334,8 @@ class FlexibleRepository {
474
334
  this.unexpectedCallStats.clear();
475
335
  }
476
336
 
337
+ // TODO: this returns any, but the interface returns unknown
338
+ // unknown causes errors in apply tests, and any causes bugs in bootstrapped SM
477
339
  get(property) {
478
340
  const expectations = this.expectations.get(property);
479
341
 
@@ -537,16 +399,14 @@ class FlexibleRepository {
537
399
  }
538
400
 
539
401
  getValueForUnexpectedAccess(property) {
540
- if (this.strictness === exports.Strictness.SUPER_STRICT) {
402
+ if (this.unexpectedProperty === exports.UnexpectedProperty.THROW) {
541
403
  this.recordUnexpected(property, undefined);
542
404
  throw new UnexpectedAccess(property, this.getUnmet());
543
405
  }
544
406
 
545
- return {
546
- value: (...args) => {
547
- this.recordUnexpected(property, args);
548
- throw new UnexpectedCall(property, args, this.getUnmet());
549
- }
407
+ return (...args) => {
408
+ this.recordUnexpected(property, args);
409
+ throw new UnexpectedCall(property, args, this.getUnmet());
550
410
  };
551
411
  }
552
412
 
@@ -565,16 +425,18 @@ class FlexibleRepository {
565
425
  */
566
426
 
567
427
  class StrongExpectation {
568
- constructor(property, args, returnValue) {
428
+ constructor(property, args, returnValue, exactParams = false) {
569
429
  this.property = void 0;
570
430
  this.args = void 0;
571
431
  this.returnValue = void 0;
432
+ this.exactParams = void 0;
572
433
  this.matched = 0;
573
434
  this.min = 1;
574
435
  this.max = 1;
575
436
  this.property = property;
576
437
  this.args = args;
577
438
  this.returnValue = returnValue;
439
+ this.exactParams = exactParams;
578
440
  }
579
441
 
580
442
  setInvocationCount(min, max = 1) {
@@ -604,6 +466,12 @@ class StrongExpectation {
604
466
  return false;
605
467
  }
606
468
 
469
+ if (this.exactParams) {
470
+ if (this.args.length !== received.length) {
471
+ return false;
472
+ }
473
+ }
474
+
607
475
  return this.args.every((arg, i) => arg.matches(received[i]));
608
476
  }
609
477
 
@@ -613,55 +481,43 @@ class StrongExpectation {
613
481
 
614
482
  }
615
483
 
616
- class RepoSideEffectPendingExpectation {
617
- constructor(createExpectation) {
484
+ class PendingExpectationWithFactory {
485
+ constructor(createExpectation, concreteMatcher, exactParams) {
618
486
  this.createExpectation = void 0;
619
- this._repo = void 0;
620
- this._concreteMatcher = void 0;
621
- this._args = void 0;
622
- this._property = '';
487
+ this.concreteMatcher = void 0;
488
+ this.exactParams = void 0;
489
+ this.args = void 0;
490
+ this.property = void 0;
623
491
  this.createExpectation = createExpectation;
492
+ this.concreteMatcher = concreteMatcher;
493
+ this.exactParams = exactParams;
624
494
  }
625
495
 
626
- start(repo, concreteMatcher) {
627
- if (this._repo) {
496
+ setProperty(value) {
497
+ if (this.property) {
628
498
  throw new UnfinishedExpectation(this);
629
499
  }
630
500
 
631
- this.clear();
632
- this._repo = repo;
633
- this._concreteMatcher = concreteMatcher;
501
+ this.property = value;
634
502
  }
635
503
 
636
- set property(value) {
637
- this._property = value;
638
- }
639
-
640
- set args(value) {
641
- this._args = value;
504
+ setArgs(value) {
505
+ this.args = value;
642
506
  }
643
507
 
644
508
  finish(returnValue) {
645
- if (!this._repo || !this._concreteMatcher) {
509
+ if (!this.property) {
646
510
  throw new MissingWhen();
647
511
  }
648
512
 
649
- const expectation = this.createExpectation(this._property, this._args, returnValue, this._concreteMatcher);
650
-
651
- this._repo.add(expectation);
652
-
653
- this.clear();
513
+ const expectation = this.createExpectation(this.property, this.args, returnValue, this.concreteMatcher, this.exactParams);
514
+ this.property = undefined;
515
+ this.args = undefined;
654
516
  return expectation;
655
517
  }
656
518
 
657
- clear() {
658
- this._repo = undefined;
659
- this._args = undefined;
660
- this._property = '';
661
- }
662
-
663
519
  toJSON() {
664
- return printWhen(this._property, this._args);
520
+ return printWhen(this.property, this.args);
665
521
  }
666
522
 
667
523
  }
@@ -952,7 +808,8 @@ const It = {
952
808
 
953
809
  const defaults = {
954
810
  concreteMatcher: It.deepEquals,
955
- strictness: exports.Strictness.STRICT
811
+ unexpectedProperty: exports.UnexpectedProperty.CALL_THROW,
812
+ exactParams: false
956
813
  };
957
814
  let currentDefaults = defaults;
958
815
  /**
@@ -967,8 +824,128 @@ const setDefaults = newDefaults => {
967
824
  currentDefaults = _extends({}, defaults, newDefaults);
968
825
  };
969
826
 
970
- const strongExpectationFactory = (property, args, returnValue, concreteMatcher) => new StrongExpectation(property, // Wrap every non-matcher in the default matcher.
971
- args == null ? void 0 : args.map(arg => isMatcher(arg) ? arg : concreteMatcher(arg)), returnValue);
827
+ /**
828
+ * Since `when` doesn't receive the mock subject (because we can't make it
829
+ * consistently return it from `mock()`, `mock.foo` and `mock.bar()`) we need
830
+ * to store a global state for the currently active mock.
831
+ *
832
+ * We also want to throw in the following case:
833
+ *
834
+ * ```
835
+ * when(() => mock()) // forgot returns here
836
+ * when(() => mock()) // should throw
837
+ * ```
838
+ *
839
+ * For that reason we can't just store the currently active mock, but also
840
+ * whether we finished the expectation or not.
841
+ */
842
+
843
+ let activeMock;
844
+ const setActiveMock = mock => {
845
+ activeMock = mock;
846
+ };
847
+ const getActiveMock = () => activeMock;
848
+ /**
849
+ * Store a global map of all mocks created and their state.
850
+ *
851
+ * This is needed because we can't reliably pass the state between `when`
852
+ * and `thenReturn`.
853
+ */
854
+
855
+ const mockMap = new Map();
856
+ const getMockState = mock => {
857
+ if (mockMap.has(mock)) {
858
+ return mockMap.get(mock);
859
+ }
860
+
861
+ throw new NotAMock();
862
+ };
863
+ const setMockState = (mock, state) => {
864
+ mockMap.set(mock, state);
865
+ };
866
+ const getAllMocks = () => Array.from(mockMap.entries());
867
+
868
+ const createProxy = traps => // eslint-disable-next-line no-empty-function
869
+ new Proxy(
870
+ /* istanbul ignore next */
871
+ () => {}, {
872
+ get: (target, prop) => {
873
+ if (prop === 'bind') {
874
+ return (thisArg, ...args) => (...moreArgs) => traps.apply([...args, ...moreArgs]);
875
+ }
876
+
877
+ if (prop === 'apply') {
878
+ return (thisArg, args) => traps.apply(args || []);
879
+ }
880
+
881
+ if (prop === 'call') {
882
+ return (thisArg, ...args) => traps.apply(args);
883
+ }
884
+
885
+ return traps.property(prop);
886
+ },
887
+ apply: (target, thisArg, args) => traps.apply(args),
888
+ ownKeys: () => traps.ownKeys(),
889
+
890
+ getOwnPropertyDescriptor(target, prop) {
891
+ const keys = traps.ownKeys();
892
+
893
+ if (keys.includes(prop)) {
894
+ return {
895
+ configurable: true,
896
+ enumerable: true
897
+ };
898
+ }
899
+
900
+ return undefined;
901
+ }
902
+
903
+ });
904
+
905
+ const createStub = (repo, pendingExpectation, getCurrentMode) => {
906
+ const stub = createProxy({
907
+ property: property => {
908
+ if (getCurrentMode() === Mode.CALL) {
909
+ return repo.get(property);
910
+ }
911
+
912
+ setActiveMock(stub);
913
+ pendingExpectation.setProperty(property);
914
+ return createProxy({
915
+ property: childProp => {
916
+ throw new NestedWhen(property, childProp);
917
+ },
918
+ apply: args => {
919
+ pendingExpectation.setArgs(args);
920
+ },
921
+ ownKeys: () => {
922
+ throw new Error('Spreading during an expectation is not supported.');
923
+ }
924
+ });
925
+ },
926
+ apply: args => {
927
+ if (getCurrentMode() === Mode.CALL) {
928
+ return repo.apply(args);
929
+ }
930
+
931
+ setActiveMock(stub);
932
+ pendingExpectation.setProperty(ApplyProp);
933
+ pendingExpectation.setArgs(args);
934
+ return undefined;
935
+ },
936
+ ownKeys: () => {
937
+ if (getCurrentMode() === Mode.CALL) {
938
+ return repo.getAllProperties();
939
+ }
940
+
941
+ throw new Error('Spreading during an expectation is not supported.');
942
+ }
943
+ });
944
+ return stub;
945
+ };
946
+
947
+ const strongExpectationFactory = (property, args, returnValue, concreteMatcher, exactParams) => new StrongExpectation(property, // Wrap every non-matcher in the default matcher.
948
+ args == null ? void 0 : args.map(arg => isMatcher(arg) ? arg : concreteMatcher(arg)), returnValue, exactParams);
972
949
 
973
950
  var Mode;
974
951
 
@@ -981,6 +958,8 @@ let currentMode = Mode.CALL;
981
958
  const setMode = mode => {
982
959
  currentMode = mode;
983
960
  };
961
+
962
+ const getMode = () => currentMode;
984
963
  /**
985
964
  * Create a type safe mock.
986
965
  *
@@ -988,9 +967,12 @@ const setMode = mode => {
988
967
  *
989
968
  * @param options Configure the options for this specific mock, overriding any
990
969
  * defaults that were set with {@link setDefaults}.
991
- * @param options.strictness Controls what happens when a property is accessed,
992
- * or a call is made, and there are no expectations set for it.
993
- * @param options.concreteMatcher The matcher that will be used when one isn't specified explicitly.
970
+ * @param options.unexpectedProperty Controls what happens when an unexpected
971
+ * property is accessed.
972
+ * @param options.concreteMatcher The matcher that will be used when one isn't
973
+ * specified explicitly.
974
+ * @param options.exactParams Controls whether the number of received arguments
975
+ * has to match the expectation.
994
976
  *
995
977
  * @example
996
978
  * const fn = mock<() => number>();
@@ -1000,17 +982,20 @@ const setMode = mode => {
1000
982
  * fn() === 23;
1001
983
  */
1002
984
 
985
+
1003
986
  const mock = ({
1004
- strictness,
1005
- concreteMatcher
987
+ unexpectedProperty,
988
+ concreteMatcher,
989
+ exactParams
1006
990
  } = {}) => {
1007
- const pendingExpectation = new RepoSideEffectPendingExpectation(strongExpectationFactory);
1008
991
  const options = {
1009
- strictness: strictness != null ? strictness : currentDefaults.strictness,
1010
- concreteMatcher: concreteMatcher != null ? concreteMatcher : currentDefaults.concreteMatcher
992
+ unexpectedProperty: unexpectedProperty != null ? unexpectedProperty : currentDefaults.unexpectedProperty,
993
+ concreteMatcher: concreteMatcher != null ? concreteMatcher : currentDefaults.concreteMatcher,
994
+ exactParams: exactParams != null ? exactParams : currentDefaults.exactParams
1011
995
  };
1012
- const repository = new FlexibleRepository(options.strictness);
1013
- const stub = createStub(repository, pendingExpectation, () => currentMode, options.concreteMatcher);
996
+ const repository = new FlexibleRepository(options.unexpectedProperty);
997
+ const pendingExpectation = new PendingExpectationWithFactory(strongExpectationFactory, options.concreteMatcher, options.exactParams);
998
+ const stub = createStub(repository, pendingExpectation, getMode);
1014
999
  setMockState(stub, {
1015
1000
  repository,
1016
1001
  pendingExpectation,
@@ -1058,13 +1043,9 @@ const createInvocationCount = expectation => ({
1058
1043
 
1059
1044
  });
1060
1045
 
1061
- /**
1062
- * Set a return value for the currently pending expectation.
1063
- */
1064
-
1065
- const finishPendingExpectation = (returnValue, pendingExpectation) => {
1046
+ const finishPendingExpectation = (returnValue, pendingExpectation, repo) => {
1066
1047
  const finishedExpectation = pendingExpectation.finish(returnValue);
1067
- pendingExpectation.clear();
1048
+ repo.add(finishedExpectation);
1068
1049
  return createInvocationCount(finishedExpectation);
1069
1050
  };
1070
1051
 
@@ -1080,45 +1061,29 @@ const getError = errorOrMessage => {
1080
1061
  return new Error();
1081
1062
  };
1082
1063
 
1083
- const createReturns = pendingExpectation => {
1084
- const nonPromiseStub = {
1085
- // TODO: merge this with the promise version
1086
- thenReturn:
1087
- /* istanbul ignore next: because this is overwritten by the promise version */
1088
- returnValue => finishPendingExpectation({
1089
- value: returnValue,
1090
- isError: false,
1091
- isPromise: false
1092
- }, pendingExpectation),
1093
- thenThrow: errorOrMessage => finishPendingExpectation({
1094
- value: getError(errorOrMessage),
1095
- isError: true,
1096
- isPromise: false
1097
- }, pendingExpectation)
1098
- };
1099
- const promiseStub = {
1100
- thenReturn: promise => finishPendingExpectation({
1101
- value: promise,
1102
- isError: false,
1103
- // We're setting this to false because we can't distinguish between a
1104
- // promise thenReturn and a normal thenReturn.
1105
- isPromise: false
1106
- }, pendingExpectation),
1107
- thenResolve: promiseValue => finishPendingExpectation({
1108
- value: promiseValue,
1109
- isError: false,
1110
- isPromise: true
1111
- }, pendingExpectation),
1112
- thenReject: errorOrMessage => finishPendingExpectation({
1113
- value: getError(errorOrMessage),
1114
- isError: true,
1115
- isPromise: true
1116
- }, pendingExpectation)
1117
- }; // @ts-expect-error because the return type is a conditional, and we're merging
1118
- // both branches here
1119
-
1120
- return _extends({}, nonPromiseStub, promiseStub);
1121
- };
1064
+ const createReturns = (pendingExpectation, repository) => ({
1065
+ thenReturn: returnValue => finishPendingExpectation( // This will handle both thenReturn(23) and thenReturn(Promise.resolve(3)).
1066
+ {
1067
+ value: returnValue,
1068
+ isError: false,
1069
+ isPromise: false
1070
+ }, pendingExpectation, repository),
1071
+ thenThrow: errorOrMessage => finishPendingExpectation({
1072
+ value: getError(errorOrMessage),
1073
+ isError: true,
1074
+ isPromise: false
1075
+ }, pendingExpectation, repository),
1076
+ thenResolve: promiseValue => finishPendingExpectation({
1077
+ value: promiseValue,
1078
+ isError: false,
1079
+ isPromise: true
1080
+ }, pendingExpectation, repository),
1081
+ thenReject: errorOrMessage => finishPendingExpectation({
1082
+ value: getError(errorOrMessage),
1083
+ isError: true,
1084
+ isPromise: true
1085
+ }, pendingExpectation, repository)
1086
+ });
1122
1087
 
1123
1088
  /**
1124
1089
  * Set an expectation on a mock.
@@ -1145,16 +1110,16 @@ const createReturns = pendingExpectation => {
1145
1110
  * const fn = mock<(x: number) => Promise<number>();
1146
1111
  * when(() => fn(23)).thenResolve(42);
1147
1112
  */
1148
- // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-unused-vars
1149
1113
 
1150
1114
  const when = expectation => {
1151
1115
  setMode(Mode.EXPECT);
1152
1116
  expectation();
1153
1117
  setMode(Mode.CALL);
1154
1118
  const {
1155
- pendingExpectation
1119
+ pendingExpectation,
1120
+ repository
1156
1121
  } = getMockState(getActiveMock());
1157
- return createReturns(pendingExpectation);
1122
+ return createReturns(pendingExpectation, repository);
1158
1123
  };
1159
1124
 
1160
1125
  /**