strong-mock 7.2.1 → 8.0.0-beta.1

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/README.md CHANGED
@@ -6,7 +6,7 @@
6
6
  </div>
7
7
 
8
8
  ```typescript
9
- import { mock, when, instance } from 'strong-mock';
9
+ import { mock, when } from 'strong-mock';
10
10
 
11
11
  interface Foo {
12
12
  bar: (x: number) => string;
@@ -14,9 +14,9 @@ interface Foo {
14
14
 
15
15
  const foo = mock<Foo>();
16
16
 
17
- when(foo.bar(23)).thenReturn('I am strong!');
17
+ when(() => foo.bar(23)).thenReturn('I am strong!');
18
18
 
19
- console.log(instance(foo).bar(23)); // 'I am strong!'
19
+ console.log(foo.bar(23)); // 'I am strong!'
20
20
  ```
21
21
 
22
22
  ----
@@ -27,12 +27,12 @@ console.log(instance(foo).bar(23)); // 'I am strong!'
27
27
  <!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
28
28
  **Table of Contents**
29
29
 
30
- - [Installation](#installation)
31
- - [Requirements](#requirements)
32
30
  - [Features](#features)
33
31
  - [Type safety](#type-safety)
34
32
  - [Useful error messages](#useful-error-messages)
35
33
  - [Type safe argument matchers](#type-safe-argument-matchers)
34
+ - [Installation](#installation)
35
+ - [Requirements](#requirements)
36
36
  - [API](#api)
37
37
  - [Setting expectations](#setting-expectations)
38
38
  - [Setting multiple expectations](#setting-multiple-expectations)
@@ -44,75 +44,85 @@ console.log(instance(foo).bar(23)); // 'I am strong!'
44
44
  - [Verifying expectations](#verifying-expectations)
45
45
  - [Resetting expectations](#resetting-expectations)
46
46
  - [Argument matchers](#argument-matchers)
47
+ - [Mock options](#mock-options)
48
+ - [Strictness](#strictness)
49
+ - [Concrete matcher](#concrete-matcher)
50
+ - [Defaults](#defaults)
47
51
  - [FAQ](#faq)
48
52
  - [Why do I have to set all expectations first?](#why-do-i-have-to-set-all-expectations-first)
49
- - [Can I mock an existing object/function?](#can-i-mock-an-existing-objectfunction)
53
+ - [Can I partially mock an existing object/function?](#can-i-partially-mock-an-existing-objectfunction)
50
54
  - [How do I set expectations on setters?](#how-do-i-set-expectations-on-setters)
51
55
  - [Why do I have to set a return value even if it's `undefined`?](#why-do-i-have-to-set-a-return-value-even-if-its-undefined)
52
56
  - [How do I provide a function for the mock to call?](#how-do-i-provide-a-function-for-the-mock-to-call)
53
- - [Why does accessing an unused method throw?](#why-does-accessing-an-unused-method-throw)
54
- - [Can I spread/enumerate a mock instance?](#can-i-spreadenumerate-a-mock-instance)
57
+ - [Can I spread/enumerate a mock?](#can-i-spreadenumerate-a-mock)
58
+ - [How can I ignore `undefined` keys when setting expectations on objects?](#how-can-i-ignore-undefined-keys-when-setting-expectations-on-objects)
55
59
 
56
60
  <!-- END doctoc generated TOC please keep comment here to allow auto update -->
57
61
 
58
- ## Installation
59
-
60
- ```
61
- npm i -D strong-mock
62
- ```
63
-
64
- ```
65
- yarn add -D strong-mock
66
- ```
67
-
68
- ## Requirements
69
-
70
- strong-mock requires an environment that supports the [ES6 Proxy object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy). This is necessary to create dynamic mocks from types because TypeScript does not support reflection i.e. exposing the type info at runtime.
71
-
72
62
  ## Features
73
63
 
74
64
  ### Type safety
75
65
 
76
66
  The created mock matches the mocked type so all expectations are type safe. Moreover, refactorings in an IDE will also cover your expectations.
77
67
 
78
- ![rename-interface](media/rename-interface.gif)
68
+ ![Renaming production code and test code](media/rename-refactor.gif)
79
69
 
80
70
  ### Useful error messages
81
71
 
82
72
  Error messages include the property that has been accessed, any arguments passed to it and any remaining unmet expectations.
83
73
 
84
- ![error messages](media/error-messages.png)
74
+ ```typescript
75
+ import { mock, when } from 'strong-mock';
76
+
77
+ const fn = mock<(a: number, b: number, c: number) => number>();
78
+
79
+ when(() => fn(1, 2, 3)).thenReturn(42);
80
+
81
+ fn(4, 5, 6);
82
+ ```
83
+
84
+ ![Test output showing details about mock expectations](media/error-messages.png)
85
85
 
86
86
  ### Type safe argument matchers
87
87
 
88
- ![type safe matchers](./media/type-safe-matchers.png)
88
+ Optional argument matchers allow you to create complex expectations, while still maintaining type safety.
89
89
 
90
- ## API
90
+ ![Type safe matcher showing a type error](media/type-safe-matchers.png)
91
91
 
92
- ### Setting expectations
92
+ ## Installation
93
93
 
94
- Expectations are set by calling the mock inside a `when()` call and finishing it by setting a return value.
94
+ ```shell
95
+ npm i -D strong-mock
96
+ ```
95
97
 
96
- ```typescript
97
- when(foo.bar(23)).thenReturn('awesome');
98
+ ```shell
99
+ yarn add -D strong-mock
98
100
  ```
99
101
 
100
- After expectations have been set you need to get an instance of the mock by calling `instance()`.
102
+ ## Requirements
103
+
104
+ strong-mock requires an environment that supports the [ES6 Proxy object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy). This is necessary to create dynamic mocks from types because TypeScript does not support reflection i.e. exposing the type info at runtime.
105
+
106
+ ## API
107
+
108
+ ### Setting expectations
109
+
110
+ Expectations are set by calling the mock inside a `when` callback and finishing it by setting a return value.
101
111
 
102
112
  ```typescript
103
- instance(foo)
113
+ when(() => foo.bar(23)).thenReturn('awesome');
104
114
  ```
105
115
 
106
116
  ### Setting multiple expectations
107
117
 
108
- You can set as many expectations as you want by calling `when()` multiple times. If you have multiple expectations with the same arguments they will be consumed in the order they were created.
118
+ You can set as many expectations as you want by calling `when` multiple times. If you have multiple expectations with the same arguments they will be consumed in the order they were created.
109
119
 
110
120
  ```typescript
111
- when(foo.bar(23)).thenReturn('awesome');
112
- when(foo.bar(23)).thenReturn('even more awesome');
121
+ when(() => foo.bar(23)).thenReturn('awesome');
122
+ when(() => foo.bar(23)).thenReturn('even more awesome');
113
123
 
114
- console.log(instance(foo).bar(23)); // awesome
115
- console.log(instance(foo).bar(23)); // even more awesome
124
+ console.log(foo.bar(23)); // awesome
125
+ console.log(foo.bar(23)); // even more awesome
116
126
  ```
117
127
 
118
128
  By default, each call is expected to be called only once. You can expect a call to be made multiple times using the [invocation count](#setting-invocation-count-expectations) helpers.
@@ -124,12 +134,12 @@ You can expect a call to be made multiple times by using the invocation count he
124
134
  ```typescript
125
135
  const fn = mock<(x: number) => number>();
126
136
 
127
- when(fn(1)).thenReturn(1).between(2, 3);
137
+ when(() => fn(1)).thenReturn(1).between(2, 3);
128
138
 
129
- console.log(instance(fn)(1)); // 1
130
- console.log(instance(fn)(1)); // 1
131
- console.log(instance(fn)(1)); // 1
132
- console.log(instance(fn)(1)); // throws because the expectation is finished
139
+ console.log(fn(1)); // 1
140
+ console.log(fn(1)); // 1
141
+ console.log(fn(1)); // 1
142
+ console.log(fn(1)); // throws because the expectation is finished
133
143
  ```
134
144
 
135
145
  ### Mocking interfaces
@@ -144,11 +154,11 @@ interface Foo {
144
154
 
145
155
  const foo = mock<Foo>();
146
156
 
147
- when(foo.bar(23)).thenReturn('awesome');
148
- when(foo.baz).thenReturn(100);
157
+ when(() => foo.bar(23)).thenReturn('awesome');
158
+ when(() => foo.baz).thenReturn(100);
149
159
 
150
- console.log(instance(foo).bar(23)); // 'awesome'
151
- console.log(instance(foo).baz); // 100
160
+ console.log(foo.bar(23)); // 'awesome'
161
+ console.log(foo.baz); // 100
152
162
  ```
153
163
 
154
164
  Since the mock is type safe the compiler will guarantee that you're only mocking things that actually exist on the interface.
@@ -162,9 +172,9 @@ type Fn = (x: number) => number;
162
172
 
163
173
  const fn = mock<Fn>();
164
174
 
165
- when(fn(1)).thenReturn(2);
175
+ when(() => fn(1)).thenReturn(2);
166
176
 
167
- console.log(instance(fn)(1)); // 2
177
+ console.log(fn(1)); // 2
168
178
  ```
169
179
 
170
180
  ### Mocking promises
@@ -176,9 +186,9 @@ type Fn = (x: number) => Promise<number>;
176
186
 
177
187
  const fn = mock<Fn>();
178
188
 
179
- when(fn(1)).thenResolve(2);
189
+ when(() => fn(1)).thenResolve(2);
180
190
 
181
- console.log(await instance(fn)()); // 2
191
+ console.log(await fn()); // 2
182
192
  ```
183
193
 
184
194
  ### Throwing errors
@@ -190,8 +200,8 @@ type FnWithPromise = (x: number) => Promise<void>;
190
200
  const fn = mock<Fn>();
191
201
  const fnWithPromise = mock<FnWithPromise>();
192
202
 
193
- when(fn(1)).thenThrow();
194
- when(fnWithPromise(1)).thenReject();
203
+ when(() => fn(1)).thenThrow();
204
+ when(() => fnWithPromise(1)).thenReject();
195
205
  ```
196
206
 
197
207
  You'll notice there is no `never()` helper - if you expect a call to not be made simply don't set an expectation on it and the mock will throw if the call happens.
@@ -203,7 +213,7 @@ Calling `verify(mock)` will make sure that all expectations set on `mock` have b
203
213
  ```typescript
204
214
  const fn = mock<(x: number) => number>();
205
215
 
206
- when(fn(1)).thenReturn(1).between(2, 10);
216
+ when(() => fn(1)).thenReturn(1).between(2, 10);
207
217
 
208
218
  verify(fn); // throws
209
219
  ```
@@ -214,7 +224,7 @@ It will also throw if any unexpected calls happened that were maybe caught in th
214
224
  const fn = mock<() => void>();
215
225
 
216
226
  try {
217
- instance(fn)(); // throws because the call is unexpected
227
+ fn(); // throws because the call is unexpected
218
228
  } catch(e) {
219
229
  // your code might transition to an error state here
220
230
  }
@@ -222,7 +232,7 @@ try {
222
232
  verify(fn); // throws
223
233
  ```
224
234
 
225
- It is recommended that that you call `verify()` on your mocks at the end of every test. This will make sure you don't have any unused expectations in your tests and that your code hasn't silently caught any of the errors that are thrown when an unexpected call happens. You can use `verifyAll()` to check all existing mocks e.g. in an `afterEach` hook.
235
+ It is recommended that you call `verify()` on your mocks at the end of every test. This will make sure you don't have any unused expectations in your tests and that your code did not silently catch any of the errors that are thrown when an unexpected call happens. You can use `verifyAll()` to check all existing mocks e.g. in an `afterEach` hook.
226
236
 
227
237
  ![verify error](./media/verify.png)
228
238
 
@@ -233,11 +243,11 @@ You can remove all expectations from a mock by using the `reset()` method:
233
243
  ```typescript
234
244
  const fn = mock<(x: number) => number>();
235
245
 
236
- when(fn(1)).thenReturn(1);
246
+ when(() => fn(1)).thenReturn(1);
237
247
 
238
248
  reset(fn);
239
249
 
240
- instance(fn)(1); // throws
250
+ fn(1); // throws
241
251
  ```
242
252
 
243
253
  If you create common mocks that are shared by multiple tests you should reset them before using them e.g. in a `beforeEach` hook. You can use `resetAll()` to reset all existing mocks.
@@ -251,18 +261,26 @@ const fn = mock<
251
261
  (x: number, data: { values: number[]; labels: string[] }) => string
252
262
  >();
253
263
 
254
- when(fn(
264
+ when(() => fn(
255
265
  It.isAny(),
256
266
  It.isObject({ values: [1, 2, 3] })
257
267
  )).thenReturn('matched!');
258
268
 
259
- console.log(instance(fn)(
269
+ console.log(fn(
260
270
  123,
261
271
  { values: [1, 2, 3], labels: ['a', 'b', 'c'] })
262
272
  ); // 'matched!'
263
273
  ```
264
274
 
275
+ You can mix argument matchers with concrete arguments:
276
+
277
+ ```typescript
278
+ when(() => fn(42, It.isObject())).thenReturn('matched');
279
+ ```
280
+
265
281
  Available matchers:
282
+ - `deepEquals` - the default, uses deep equality,
283
+ - `is` - uses `Object.is` for comparison,
266
284
  - `isAny` - matches anything,
267
285
  - `isNumber` - matches any number,
268
286
  - `isString` - matches any string, can search for substrings and patterns,
@@ -271,6 +289,15 @@ Available matchers:
271
289
  - `matches` - build your own matcher,
272
290
  - `willCapture` - matches anything and stores the received value.
273
291
 
292
+ The following table illustrates the differences between the equality matchers:
293
+
294
+ | expected | actual | `It.is` | `It.deepEquals` | `It.deepEquals({ strict: false })` |
295
+ |--------------------|----------------------|-----------|-----------------|------------------------------------|
296
+ | `"foo"` | `"bar"` | equal | equal | equal |
297
+ | `{ foo: "bar" }` | `{ foo: "bar" }` | not equal | equal | equal |
298
+ | `{ }` | `{ foo: undefined }` | not equal | not equal | equal |
299
+ | `new (class {})()` | `new (class {})()` | not equal | not equal | equal |
300
+
274
301
  Some matchers, like `isObject` and `isArray` support nesting matchers:
275
302
 
276
303
  ```typescript
@@ -286,9 +313,9 @@ You can create arbitrarily complex and type safe matchers with `It.matches(cb)`:
286
313
  ```typescript
287
314
  const fn = mock<(x: number, y: number[]) => string>();
288
315
 
289
- when(fn(
316
+ when(() => fn(
290
317
  It.matches(x => x > 0),
291
- It.matches(y => y.values.includes(42))
318
+ It.matches(y => y.includes(42))
292
319
  )).thenReturn('matched');
293
320
  ```
294
321
 
@@ -300,25 +327,86 @@ type Cb = (value: number) => number;
300
327
  const fn = mock<(cb: Cb) => number>();
301
328
 
302
329
  const matcher = It.willCapture<Cb>();
303
- when(fn(matcher)).thenReturn(42);
330
+ when(() => fn(matcher)).thenReturn(42);
304
331
 
305
- console.log(instance(fn)(23, (x) => x + 1)); // 42
332
+ console.log(fn(23, (x) => x + 1)); // 42
306
333
  console.log(matcher.value?.(3)); // 4
307
334
  ```
308
335
 
336
+ ### Mock options
337
+
338
+ #### Strictness
339
+
340
+ strong-mock has a few levels of "strictness" that control what values are returned when an unexpected property is accessed or an unexpected call is made. The strictness can be configured for each mock, or for all mocks with `setDefaults`.
341
+
342
+ ```typescript
343
+ import { mock, when } from 'strong-mock';
344
+ import { Strictness } from './options';
345
+
346
+ type Foo = {
347
+ bar: (value: number) => number;
348
+ }
349
+
350
+ // This is the default.
351
+ const strictFoo = mock<Foo>({ strictness: Strictness.STRICT });
352
+
353
+ // Accessing properties with no expectations is fine.
354
+ strictFoo.bar;
355
+ // Throws "Didn't expect bar(42) to be called".
356
+ strictFoo.bar(42);
357
+
358
+ const superStrictFoo = mock<Foo>({ strictness: Strictness.SUPER_STRICT });
359
+
360
+ // Throws "Didn't expect property bar to be accessed".
361
+ superStrictFoo.bar;
362
+ // Throws "Didn't expect property bar to be accessed".
363
+ superStrictFoo.bar(42);
364
+ ```
365
+
366
+ #### Concrete matcher
367
+
368
+ You can set the matcher that will be used in expectations with concrete values e.g. `42` or `{ foo: "bar" }`. Passing in a [matcher argument](#argument-matchers) will always take priority.
369
+
370
+ ```typescript
371
+ import { mock, when, It } from 'strong-mock';
372
+
373
+ // Use strict equality instead of deep equality.
374
+ const fn = mock<(x: number[]) => boolean>({ concreteMatcher: It.is });
375
+ when(() => fn([1, 2, 3])).thenReturn(true);
376
+
377
+ fn([1, 2, 3]); // throws because different arrays
378
+ ```
379
+
380
+ #### Defaults
381
+
382
+ Mock options can be set for all mocks with `setDefaults`.
383
+
384
+ ```typescript
385
+ import { mock, when, setDefaults, Strictness } from 'strong-mock';
386
+
387
+ setDefaults({
388
+ strictness: Strictness.SUPER_STRICT
389
+ });
390
+
391
+ // Uses the new default.
392
+ const superStrictMock = mock<() => void>();
393
+ // Overrides the default.
394
+ const strictMock = mock<() => void>({ strictness: Strictness.STRICT });
395
+ ```
396
+
309
397
  ## FAQ
310
398
 
311
399
  ### Why do I have to set all expectations first?
312
400
 
313
401
  This library is different from other mocking/spying libraries you might have used before such as [sinon](https://sinonjs.org) or [jest](https://jestjs.io/docs/en/mock-functions). Whereas those libraries are focused on recording calls to the mocks and always returning something, strong-mock requires you to set your expectations upfront. If a call happens that is not expected the mock will throw an error.
314
402
 
315
- This design decision has a few reasons behind it. First of all, it forces you to be aware of what your code needs from its dependencies. Spying libraries encourage checking those needs at the end of the test after the code has already called the mocks. This can lead to tests missing dependency calls that just happen to not throw any error at runtime with the dummy values that the spies return.
403
+ This design decision has a few reasons behind it. First, it forces you to be aware of what your code needs from its dependencies. Spying libraries encourage checking those needs at the end of the test after the code has already called the mocks. This can lead to tests missing dependency calls that just happen to not throw any error at runtime with the dummy values that the spies return.
316
404
 
317
405
  Secondly, it will highlight potential design problems such as violations of the SOLID principles. If you find yourself duplicating expectations between tests and passing dummy values to them because your test is not concerned with them then you might want to look into splitting the code to only depend on things it really needs.
318
406
 
319
- ### Can I mock an existing object/function?
407
+ ### Can I partially mock an existing object/function?
320
408
 
321
- No, although you can pass its type to `mock()` and set expectations on it as you would with a type.
409
+ No, passing a concrete implementation to `mock()` will be the same as passing a type: all properties will be mocked, and you have to set expectations on the ones that will be accessed.
322
410
 
323
411
  ### How do I set expectations on setters?
324
412
 
@@ -326,11 +414,11 @@ You currently can't do that. Please use a normal method instead e.g. `setFoo()`
326
414
 
327
415
  ### Why do I have to set a return value even if it's `undefined`?
328
416
 
329
- To make side effects explicit and to prevent future refactoring headaches. If you would have just `when(fn())` and you later changed `fn()` to return a `number` then your expectation would become incorrect and the compiler couldn't check that for you.
417
+ To make side effects explicit and to prevent future refactoring headaches. If you had just `when(() => fn())` and you later changed `fn()` to return a `number` then your expectation would become incorrect and the compiler couldn't check that for you.
330
418
 
331
419
  ### How do I provide a function for the mock to call?
332
420
 
333
- There is no `thenCall()` method because it can't be safely typed - the type for `thenReturn()` is inferred from the return type in `when()`, meaning that the required type would be the return value for the function, not the function itself. However, we can leverage this by setting an expectation on the function property instead:
421
+ There is no `thenCall()` method because it can't be safely typed - the type for `thenReturn()` is inferred from the return type in `when`, meaning that the required type would be the return value for the function, not the function itself. However, we can leverage this by setting an expectation on the function property instead:
334
422
 
335
423
  ```typescript
336
424
  interface Foo {
@@ -339,73 +427,47 @@ interface Foo {
339
427
 
340
428
  const foo = mock<Foo>();
341
429
 
342
- when(foo.bar).thenReturn(x => `called ${x}`);
430
+ when(() => foo.bar).thenReturn(x => `called ${x}`);
343
431
 
344
- console.log(instance(foo).bar(23)); // 'called 23'
432
+ console.log(foo.bar(23)); // 'called 23'
345
433
  ```
346
434
 
347
- The function in `thenReturn()` will be type checked against the actual interface so you can make sure you're passing in an implementation that makes sense. Moreover, refactoring the interface will also refactor the expectation (in a capable IDE).
348
-
349
- ![call-rename](media/rename-args.gif)
435
+ The function in `thenReturn()` will be type checked against the actual interface, so you can make sure you're passing in an implementation that makes sense. Moreover, refactoring the interface will also refactor the expectation (in a capable IDE).
350
436
 
351
- ### Why does accessing an unused method throw?
437
+ ### Can I spread/enumerate a mock?
352
438
 
353
- Any unexpected property access will throw an error, even if the property is a method, and you never call it. This can sometimes be inconvenient if your code e.g. destructures your mock and only calls parts of it inside your test.
439
+ Yes, and you will only get the properties that have expectations on them.
354
440
 
355
441
  ```typescript
356
- interface Foo {
357
- bar: () => number;
358
- baz: () => number;
359
- }
360
-
361
- function doFoo(foo: Foo, { callBaz }: { callBaz: boolean }) {
362
- // Will throw here with unexpected access on `baz`.
363
- const { bar, baz } = foo;
364
-
365
- bar();
442
+ const foo = mock<{ bar: number; baz: number }>();
443
+ when(() => foo.bar).thenReturn(42);
366
444
 
367
- if (callBaz) {
368
- baz();
369
- }
370
- }
445
+ console.log(Object.keys(foo)); // ['bar']
371
446
 
372
- const foo = mock<Foo>();
373
- when(foo.bar()).thenReturn(42);
447
+ const foo2 = { ...foo };
374
448
 
375
- // Throws with unexpected access on `baz`.
376
- doFoo(instance(foo), { callBaz: false });
449
+ console.log(foo2.bar); // 42
450
+ console.log(foo2.baz); // undefined
377
451
  ```
378
452
 
379
- To work around this, either change your code to avoid destructuring
453
+ ### How can I ignore `undefined` keys when setting expectations on objects?
380
454
 
381
- ```typescript
382
- function doFoo(foo: Foo, callBaz: boolean) {
383
- foo.bar();
455
+ Use the `It.deepEquals` matcher explicitly inside `when` and pass `{ strict: false }`:
384
456
 
385
- if (callBaz) {
386
- foo.baz();
387
- }
388
- }
389
- ```
457
+ ```ts
458
+ const fn = mock<(x: { foo: string }) => boolean>();
390
459
 
391
- or set a dummy expectation on the methods you're not interested in during the test.
460
+ when(() => fn(
461
+ It.deepEquals({ foo: "bar" }, { strict: false }))
462
+ ).thenReturn(true);
392
463
 
393
- ```typescript
394
- when(foo.baz()).thenThrow('should not be called').anyTimes();
464
+ fn({ foo: "bar", baz: undefined }) === true
395
465
  ```
396
466
 
397
- ### Can I spread/enumerate a mock instance?
398
-
399
- Yes, and you will only get the properties that have expectations on them.
400
-
401
- ```typescript
402
- const foo = mock<{ bar: number; baz: number }>();
403
- when(foo.bar).thenReturn(42);
404
-
405
- console.log(Object.keys(instance(foo))); // ['bar']
406
-
407
- const foo2 = { ...instance(foo) };
467
+ You can set this behavior to be the default by configuring the [concrete matcher](#concrete-matcher), and set it on all mocks using [setDefaults](#defaults):
408
468
 
409
- console.log(foo2.bar); // 42
410
- console.log(foo2.baz); // undefined
469
+ ```ts
470
+ setDefaults({
471
+ concreteMatcher: (expected) => It.deepEquals(expected, { strict: false })
472
+ });
411
473
  ```
package/dist/errors.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { Expectation } from './expectation/expectation';
2
2
  import { CallMap } from './expectation/repository/expectation-repository';
3
- import { PendingExpectation } from './when/pending-expectation';
4
3
  import { Property } from './proxy';
4
+ import { PendingExpectation } from './when/pending-expectation';
5
5
  export declare class UnfinishedExpectation extends Error {
6
6
  constructor(pendingExpectation: PendingExpectation);
7
7
  }
@@ -1,16 +1,20 @@
1
1
  import { Property } from '../proxy';
2
+ import { Matcher } from './matcher';
2
3
  export declare type ReturnValue = {
3
4
  value: any;
4
5
  isPromise?: boolean;
5
6
  isError?: boolean;
6
7
  };
8
+ /**
9
+ * Compare received arguments against matchers.
10
+ */
7
11
  export interface Expectation {
8
12
  property: Property;
9
13
  /**
10
14
  * `undefined` means this is a property expectation.
11
15
  * `[]` means this is a function call with no arguments.
12
16
  */
13
- args: any[] | undefined;
17
+ args: Matcher[] | undefined;
14
18
  returnValue: ReturnValue;
15
19
  min: number;
16
20
  max: number;
@@ -0,0 +1,29 @@
1
+ import { Matcher, TypeMatcher } from './matcher';
2
+ declare type DeepPartial<T> = T extends object ? {
3
+ [K in keyof T]?: DeepPartial<T[K]>;
4
+ } : T;
5
+ /**
6
+ * Contains argument matchers that can be used to ignore arguments in an
7
+ * expectation or to match complex arguments.
8
+ */
9
+ export declare const It: {
10
+ matches: <T>(cb: (actual: T) => boolean, { toJSON }?: {
11
+ toJSON?: (() => string) | undefined;
12
+ }) => TypeMatcher<T>;
13
+ deepEquals: <T_1>(expected: T_1, { strict }?: {
14
+ strict?: boolean | undefined;
15
+ }) => TypeMatcher<T_1>;
16
+ is: <T_2 = unknown>(expected: T_2) => TypeMatcher<T_2>;
17
+ isAny: () => TypeMatcher<any>;
18
+ isObject: <T_3 extends object, K extends DeepPartial<T_3>>(partial?: K | undefined) => TypeMatcher<T_3>;
19
+ isNumber: () => TypeMatcher<number>;
20
+ isString: ({ matching, containing, }?: {
21
+ matching?: RegExp | undefined;
22
+ containing?: string | undefined;
23
+ }) => TypeMatcher<string>;
24
+ isArray: <T_4 extends any[]>(containing?: T_4 | undefined) => TypeMatcher<T_4>;
25
+ willCapture: <T_5 = unknown>(name?: string) => T_5 & Matcher & {
26
+ value: T_5 | undefined;
27
+ };
28
+ };
29
+ export {};
@@ -1,57 +1,21 @@
1
- export declare type Matcher<T> = T & {
1
+ export declare const MATCHER_SYMBOL: unique symbol;
2
+ export declare type Matcher = {
2
3
  /**
3
4
  * Will be called with a value to match against.
4
5
  */
5
- matches: (arg: unknown) => boolean;
6
- /**
7
- * TODO: turn into a symbol
8
- */
9
- __isMatcher: boolean;
6
+ matches: (arg: any) => boolean;
7
+ [MATCHER_SYMBOL]: boolean;
10
8
  /**
11
9
  * Used by `pretty-format`.
12
10
  */
13
11
  toJSON(): string;
14
12
  };
15
13
  /**
16
- * Use to test if an expectation on an argument is a custom matcher.
14
+ * This takes the shape of T to satisfy call sites, but strong-mock will only
15
+ * care about the matcher type.
17
16
  */
18
- export declare function isMatcher(f: unknown): f is Matcher<unknown>;
17
+ export declare type TypeMatcher<T> = T & Matcher;
19
18
  /**
20
- * The default matcher that checks for deep equality.
19
+ * Used to test if an expectation on an argument is a custom matcher.
21
20
  */
22
- export declare const deepEquals: <T>(expected: T) => Matcher<T>;
23
- declare type DeepPartial<T> = T extends object ? {
24
- [K in keyof T]?: DeepPartial<T[K]>;
25
- } : T;
26
- /**
27
- * Contains argument matchers that can be used to ignore arguments in an
28
- * expectation or to match complex arguments.
29
- */
30
- export declare const It: {
31
- isAny: () => Matcher<any>;
32
- matches: <T>(cb: (arg: T) => boolean) => Matcher<T>;
33
- isObject: <T_1 extends object, K extends DeepPartial<T_1>>(partial?: K | undefined) => Matcher<T_1>;
34
- isNumber: () => Matcher<number>;
35
- isString: ({ matching, containing, }?: {
36
- matching?: RegExp | undefined;
37
- containing?: string | undefined;
38
- }) => Matcher<string>;
39
- isArray: <T_2 extends any[]>(containing?: T_2 | undefined) => Matcher<T_2>;
40
- willCapture: <T_3 = unknown>(name?: string | undefined) => T_3 & {
41
- /**
42
- * Will be called with a value to match against.
43
- */
44
- matches: (arg: unknown) => boolean;
45
- /**
46
- * TODO: turn into a symbol
47
- */
48
- __isMatcher: boolean;
49
- /**
50
- * Used by `pretty-format`.
51
- */
52
- toJSON(): string;
53
- } & {
54
- value: T_3 | undefined;
55
- };
56
- };
57
- export {};
21
+ export declare function isMatcher(f: unknown): f is Matcher;