strong-mock 8.0.1 → 9.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 +143 -51
- package/dist/errors/api.d.ts +13 -0
- package/dist/errors/diff.d.ts +4 -0
- package/dist/errors/unexpected-access.d.ts +5 -0
- package/dist/errors/unexpected-call.d.ts +14 -0
- package/dist/errors/verify.d.ts +8 -0
- package/dist/expectation/expectation.d.ts +27 -30
- package/dist/expectation/repository/expectation-repository.d.ts +90 -90
- package/dist/expectation/repository/flexible-repository.d.ts +38 -38
- package/dist/expectation/repository/return-value.d.ts +13 -13
- package/dist/expectation/strong-expectation.d.ts +30 -30
- package/dist/index.d.ts +14 -9
- package/dist/index.js +827 -511
- package/dist/index.js.map +1 -1
- package/dist/matchers/deep-equals.d.ts +16 -0
- package/dist/matchers/is-any.d.ts +11 -0
- package/dist/matchers/is-array.d.ts +22 -0
- package/dist/matchers/is-number.d.ts +12 -0
- package/dist/matchers/is-partial.d.ts +27 -0
- package/dist/matchers/is-plain-object.d.ts +17 -0
- package/dist/matchers/is-string.d.ts +15 -0
- package/dist/matchers/is.d.ts +9 -0
- package/dist/matchers/it.d.ts +10 -0
- package/dist/matchers/matcher.d.ts +93 -0
- package/dist/matchers/will-capture.d.ts +21 -0
- package/dist/mock/defaults.d.ts +11 -11
- package/dist/mock/map.d.ts +16 -16
- package/dist/mock/mock.d.ts +29 -29
- package/dist/mock/options.d.ts +99 -99
- package/dist/mock/stub.d.ts +5 -5
- package/dist/print.d.ts +10 -10
- package/dist/proxy.d.ts +48 -48
- package/dist/return/invocation-count.d.ts +44 -44
- package/dist/return/returns.d.ts +61 -87
- package/dist/verify/reset.d.ts +20 -20
- package/dist/verify/verify.d.ts +27 -27
- package/dist/when/{pending-expectation.d.ts → expectation-builder.d.ts} +26 -31
- package/dist/when/when.d.ts +32 -32
- package/package.json +21 -17
- package/dist/errors.d.ts +0 -28
- package/dist/expectation/it.d.ts +0 -29
- package/dist/expectation/matcher.d.ts +0 -21
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<div align="center">
|
|
3
3
|
<h1>💪 strong-mock</h1>
|
|
4
4
|
|
|
5
|
-
<p>
|
|
5
|
+
<p>Type safe mocking library for TypeScript</p>
|
|
6
6
|
</div>
|
|
7
7
|
|
|
8
8
|
```typescript
|
|
@@ -29,8 +29,9 @@ console.log(foo.bar(23)); // 'I am strong!'
|
|
|
29
29
|
|
|
30
30
|
- [Features](#features)
|
|
31
31
|
- [Type safety](#type-safety)
|
|
32
|
-
- [
|
|
33
|
-
- [
|
|
32
|
+
- [Matchers](#matchers)
|
|
33
|
+
- [Awesome error messages](#awesome-error-messages)
|
|
34
|
+
- [Works with Promises and Errors](#works-with-promises-and-errors)
|
|
34
35
|
- [Installation](#installation)
|
|
35
36
|
- [Requirements](#requirements)
|
|
36
37
|
- [API](#api)
|
|
@@ -43,19 +44,23 @@ console.log(foo.bar(23)); // 'I am strong!'
|
|
|
43
44
|
- [Throwing errors](#throwing-errors)
|
|
44
45
|
- [Verifying expectations](#verifying-expectations)
|
|
45
46
|
- [Resetting expectations](#resetting-expectations)
|
|
46
|
-
- [
|
|
47
|
+
- [Matchers](#matchers-1)
|
|
48
|
+
- [Creating your own matcher](#creating-your-own-matcher)
|
|
47
49
|
- [Mock options](#mock-options)
|
|
48
50
|
- [Unexpected property return value](#unexpected-property-return-value)
|
|
49
51
|
- [Exact params](#exact-params)
|
|
50
52
|
- [Concrete matcher](#concrete-matcher)
|
|
51
53
|
- [FAQ](#faq)
|
|
52
54
|
- [Why do I have to set all expectations first?](#why-do-i-have-to-set-all-expectations-first)
|
|
53
|
-
- [Can I partially mock an existing object/function?](#can-i-partially-mock-an-existing-objectfunction)
|
|
54
|
-
- [How do I set expectations on setters?](#how-do-i-set-expectations-on-setters)
|
|
55
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)
|
|
56
|
+
- [Why do I get a `Didn't expect mock to be called` error?](#why-do-i-get-a-didnt-expect-mock-to-be-called-error)
|
|
57
|
+
- [Can I partially mock a concrete implementation?](#can-i-partially-mock-a-concrete-implementation)
|
|
58
|
+
- [How do I set expectations on setters?](#how-do-i-set-expectations-on-setters)
|
|
56
59
|
- [How do I provide a function for the mock to call?](#how-do-i-provide-a-function-for-the-mock-to-call)
|
|
57
|
-
- [Can I spread
|
|
60
|
+
- [Can I spread or enumerate a mock?](#can-i-spread-or-enumerate-a-mock)
|
|
61
|
+
- [Why does `typeof mock()` return `function`?](#why-does-typeof-mock-return-function)
|
|
58
62
|
- [How can I ignore `undefined` keys when setting expectations on objects?](#how-can-i-ignore-undefined-keys-when-setting-expectations-on-objects)
|
|
63
|
+
- [How can I verify order of calls?](#how-can-i-verify-order-of-calls)
|
|
59
64
|
|
|
60
65
|
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
|
|
61
66
|
|
|
@@ -63,31 +68,57 @@ console.log(foo.bar(23)); // 'I am strong!'
|
|
|
63
68
|
|
|
64
69
|
### Type safety
|
|
65
70
|
|
|
66
|
-
The
|
|
71
|
+
The mocks will share the same types as your production code, and you can safely refactor in an IDE knowing that all usages will be updated.
|
|
67
72
|
|
|
68
73
|

|
|
69
74
|
|
|
70
|
-
###
|
|
75
|
+
### Matchers
|
|
76
|
+
|
|
77
|
+
You can use matchers to partially match values, or create complex expectations, while still maintaining type safety.
|
|
78
|
+
|
|
79
|
+

|
|
71
80
|
|
|
72
|
-
|
|
81
|
+
### Awesome error messages
|
|
82
|
+
|
|
83
|
+
Failed expectations will print a visual diff, and even integrate with the IDE.
|
|
73
84
|
|
|
74
85
|
```typescript
|
|
75
86
|
import { mock, when } from 'strong-mock';
|
|
76
87
|
|
|
77
|
-
const fn = mock<(
|
|
88
|
+
const fn = mock<(pos: { x: number; y: number }) => boolean>();
|
|
78
89
|
|
|
79
|
-
when(() =>
|
|
90
|
+
when(() =>
|
|
91
|
+
fn(
|
|
92
|
+
It.isPartial({
|
|
93
|
+
x: It.isNumber(),
|
|
94
|
+
y: It.matches<number>((y) => y > 0)
|
|
95
|
+
})
|
|
96
|
+
)
|
|
97
|
+
).thenReturn(true);
|
|
80
98
|
|
|
81
|
-
fn(
|
|
99
|
+
fn({ x: 1, y: -1 });
|
|
82
100
|
```
|
|
83
101
|
|
|
84
|
-

|
|
103
|
+
|
|
104
|
+
### Works with Promises and Errors
|
|
105
|
+
|
|
106
|
+
```typescript
|
|
107
|
+
import { mock, when } from 'strong-mock';
|
|
85
108
|
|
|
86
|
-
|
|
109
|
+
const fn = mock<(id: number) => Promise<string>>();
|
|
87
110
|
|
|
88
|
-
|
|
111
|
+
when(() => fn(42)).thenResolve('foo');
|
|
112
|
+
when(() => fn(-1)).thenReject('oops');
|
|
89
113
|
|
|
90
|
-
|
|
114
|
+
console.log(await fn(42)); // foo
|
|
115
|
+
|
|
116
|
+
try {
|
|
117
|
+
await fn(-1);
|
|
118
|
+
} catch (e) {
|
|
119
|
+
console.log(e.message); // oops
|
|
120
|
+
}
|
|
121
|
+
```
|
|
91
122
|
|
|
92
123
|
## Installation
|
|
93
124
|
|
|
@@ -99,6 +130,10 @@ npm i -D strong-mock
|
|
|
99
130
|
yarn add -D strong-mock
|
|
100
131
|
```
|
|
101
132
|
|
|
133
|
+
```shell
|
|
134
|
+
pnpm add -D strong-mock
|
|
135
|
+
```
|
|
136
|
+
|
|
102
137
|
## Requirements
|
|
103
138
|
|
|
104
139
|
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.
|
|
@@ -107,7 +142,7 @@ strong-mock requires an environment that supports the [ES6 Proxy object](https:/
|
|
|
107
142
|
|
|
108
143
|
### Setting expectations
|
|
109
144
|
|
|
110
|
-
Expectations are set by calling the mock inside a `when` callback and
|
|
145
|
+
Expectations are set by calling the mock inside a `when` callback and setting a return value.
|
|
111
146
|
|
|
112
147
|
```typescript
|
|
113
148
|
when(() => foo.bar(23)).thenReturn('awesome');
|
|
@@ -186,7 +221,7 @@ const fn = mock<Fn>();
|
|
|
186
221
|
|
|
187
222
|
when(() => fn(1)).thenResolve(2);
|
|
188
223
|
|
|
189
|
-
console.log(await fn()); // 2
|
|
224
|
+
console.log(await fn(1)); // 2
|
|
190
225
|
```
|
|
191
226
|
|
|
192
227
|
### Throwing errors
|
|
@@ -239,10 +274,10 @@ It is recommended that you call `verify()` on your mocks at the end of every tes
|
|
|
239
274
|
```typescript
|
|
240
275
|
afterEach(() => {
|
|
241
276
|
verifyAll();
|
|
242
|
-
})
|
|
277
|
+
});
|
|
243
278
|
```
|
|
244
279
|
|
|
245
|
-

|
|
246
281
|
|
|
247
282
|
### Resetting expectations
|
|
248
283
|
|
|
@@ -263,10 +298,10 @@ If you create common mocks that are shared by multiple tests you should reset th
|
|
|
263
298
|
```typescript
|
|
264
299
|
beforeEach(() => {
|
|
265
300
|
resetAll();
|
|
266
|
-
})
|
|
301
|
+
});
|
|
267
302
|
```
|
|
268
303
|
|
|
269
|
-
###
|
|
304
|
+
### Matchers
|
|
270
305
|
|
|
271
306
|
Sometimes you're not interested in specifying all the arguments in an expectation. Maybe they've been covered in another test, maybe they're hard to specify e.g. callbacks, or maybe you want to match just a property from an argument.
|
|
272
307
|
|
|
@@ -277,7 +312,7 @@ const fn = mock<
|
|
|
277
312
|
|
|
278
313
|
when(() => fn(
|
|
279
314
|
It.isAny(),
|
|
280
|
-
It.
|
|
315
|
+
It.isPartial({ values: [1, 2, 3] })
|
|
281
316
|
)).thenReturn('matched!');
|
|
282
317
|
|
|
283
318
|
console.log(fn(
|
|
@@ -286,10 +321,10 @@ console.log(fn(
|
|
|
286
321
|
); // 'matched!'
|
|
287
322
|
```
|
|
288
323
|
|
|
289
|
-
You can mix
|
|
324
|
+
You can mix matchers with concrete arguments:
|
|
290
325
|
|
|
291
326
|
```typescript
|
|
292
|
-
when(() => fn(42, It.
|
|
327
|
+
when(() => fn(42, It.isPlainObject())).thenReturn('matched');
|
|
293
328
|
```
|
|
294
329
|
|
|
295
330
|
Available matchers:
|
|
@@ -299,9 +334,10 @@ Available matchers:
|
|
|
299
334
|
- `isNumber` - matches any number,
|
|
300
335
|
- `isString` - matches any string, can search for substrings and patterns,
|
|
301
336
|
- `isArray` - matches any array, can search for subsets,
|
|
302
|
-
- `
|
|
303
|
-
- `
|
|
304
|
-
- `willCapture` - matches anything and stores the received value
|
|
337
|
+
- `isPlainObject` - matches any plain object,
|
|
338
|
+
- `isPartial` - recursively matches a subset of an object,
|
|
339
|
+
- `willCapture` - matches anything and stores the received value,
|
|
340
|
+
- `matches` - [build your own matcher](#creating-your-own-matcher).
|
|
305
341
|
|
|
306
342
|
The following table illustrates the differences between the equality matchers:
|
|
307
343
|
|
|
@@ -312,27 +348,16 @@ The following table illustrates the differences between the equality matchers:
|
|
|
312
348
|
| `{ }` | `{ foo: undefined }` | not equal | not equal | equal |
|
|
313
349
|
| `new (class {})()` | `new (class {})()` | not equal | not equal | equal |
|
|
314
350
|
|
|
315
|
-
Some matchers, like `
|
|
351
|
+
Some matchers, like `isPartial` and `isArray` support nesting matchers:
|
|
316
352
|
|
|
317
353
|
```typescript
|
|
318
|
-
It.
|
|
354
|
+
It.isPartial({ foo: It.isString() })
|
|
319
355
|
|
|
320
|
-
It.isArray([ It.
|
|
356
|
+
It.isArray([ It.isPartial({
|
|
321
357
|
foo: It.isString({ matching: /foo/ })
|
|
322
358
|
})])
|
|
323
359
|
```
|
|
324
360
|
|
|
325
|
-
You can create arbitrarily complex and type safe matchers with `It.matches(cb)`:
|
|
326
|
-
|
|
327
|
-
```typescript
|
|
328
|
-
const fn = mock<(x: number, y: number[]) => string>();
|
|
329
|
-
|
|
330
|
-
when(() => fn(
|
|
331
|
-
It.matches(x => x > 0),
|
|
332
|
-
It.matches(y => y.includes(42))
|
|
333
|
-
)).thenReturn('matched');
|
|
334
|
-
```
|
|
335
|
-
|
|
336
361
|
`It.willCapture` is a special matcher that will match any value and store it, so you can access it outside an expectation. This could be useful to capture a callback and then test it separately.
|
|
337
362
|
|
|
338
363
|
```ts
|
|
@@ -347,6 +372,57 @@ console.log(fn(23, (x) => x + 1)); // 42
|
|
|
347
372
|
console.log(matcher.value?.(3)); // 4
|
|
348
373
|
```
|
|
349
374
|
|
|
375
|
+
#### Creating your own matcher
|
|
376
|
+
|
|
377
|
+
You can create arbitrarily complex and type safe matchers with `It.matches()`:
|
|
378
|
+
|
|
379
|
+
```typescript
|
|
380
|
+
const fn = mock<(x: number, y: string) => string>();
|
|
381
|
+
|
|
382
|
+
when(() => fn(
|
|
383
|
+
It.matches(x => x > 0),
|
|
384
|
+
It.matches(y => y.startsWith('foo'))
|
|
385
|
+
)).thenReturn('matched');
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
The types are automatically inferred, but you can also specify them explicitly through the generic parameter, which is useful if you want to create reusable matchers:
|
|
389
|
+
|
|
390
|
+
```typescript
|
|
391
|
+
const startsWith = (expected: string) => It.matches<string>(
|
|
392
|
+
actual => actual.startsWith(expected)
|
|
393
|
+
);
|
|
394
|
+
|
|
395
|
+
when(() => fn(42, startsWith('foo'))).thenReturn('matched');
|
|
396
|
+
|
|
397
|
+
fn(42, 'foobar') // 'matched'
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
You can also customize how the matcher is printed in error messages, and how the diff is printed:
|
|
401
|
+
|
|
402
|
+
```typescript
|
|
403
|
+
const closeTo = (expected: number, precision = 0.01) => It.matches<number>(
|
|
404
|
+
actual => Math.abs(expected - actual) <= precision,
|
|
405
|
+
{
|
|
406
|
+
toString: () => `closeTo(${expected}, ${precision})`,
|
|
407
|
+
getDiff: (actual) => {
|
|
408
|
+
const diff = Math.abs(expected - actual);
|
|
409
|
+
const sign = diff < 0 ? '-' : '+';
|
|
410
|
+
|
|
411
|
+
return {
|
|
412
|
+
actual: `${actual} (${sign}${diff})`,
|
|
413
|
+
expected: `${expected} ±${precision}`,
|
|
414
|
+
};
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
);
|
|
418
|
+
|
|
419
|
+
when(() => fn(closeTo(1), 'foo')).thenReturn('matched');
|
|
420
|
+
|
|
421
|
+
fn(2, 'foo');
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+

|
|
425
|
+
|
|
350
426
|
## Mock options
|
|
351
427
|
|
|
352
428
|
The following options can be set per mock, or globally with `setDefaults`.
|
|
@@ -397,7 +473,7 @@ propertiesThrow.bar(42);
|
|
|
397
473
|
|
|
398
474
|
### Exact params
|
|
399
475
|
|
|
400
|
-
By default, function/method expectations will allow more arguments to be received than expected. Since the expectations are type safe, the TypeScript compiler will never allow expecting less arguments than required. Unspecified optional arguments will be considered ignored, as if they've been replaced with [
|
|
476
|
+
By default, function/method expectations will allow more arguments to be received than expected. Since the expectations are type safe, the TypeScript compiler will never allow expecting less arguments than required. Unspecified optional arguments will be considered ignored, as if they've been replaced with [matchers](#matchers-1).
|
|
401
477
|
|
|
402
478
|
```typescript
|
|
403
479
|
import { mock } from 'strong-mock';
|
|
@@ -429,7 +505,7 @@ console.log(fn(1)); // throws
|
|
|
429
505
|
|
|
430
506
|
### Concrete matcher
|
|
431
507
|
|
|
432
|
-
You can configure the [matcher](#
|
|
508
|
+
You can configure the [matcher](#matchers-1) that will be used in expectations with concrete values e.g. `42` or `{ foo: "bar" }`. This matcher can always be overwritten inside an expectation with another matcher.
|
|
433
509
|
|
|
434
510
|
```typescript
|
|
435
511
|
import { mock, when, It } from 'strong-mock';
|
|
@@ -458,7 +534,19 @@ This design decision has a few reasons behind it. First, it forces you to be awa
|
|
|
458
534
|
|
|
459
535
|
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.
|
|
460
536
|
|
|
461
|
-
###
|
|
537
|
+
### Why do I have to set a return value even if it's `undefined`?
|
|
538
|
+
|
|
539
|
+
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.
|
|
540
|
+
|
|
541
|
+
### Why do I get a `Didn't expect mock to be called` error?
|
|
542
|
+
|
|
543
|
+
This error happens when your code under test calls a mock that didn't have a matching expectation. It could be that the arguments received didn't match the ones set in the expectation (see [matchers](#matchers-1)), or the call was made more than the allowed number of times (see [invocation count expectations](#setting-invocation-count-expectations)).
|
|
544
|
+
|
|
545
|
+
In rare cases, the code under test may try to inspect the mock by accessing special properties on it. For instance, wrapping a mock in `Promise.resolve()` will try to access a `.then` property on it. strong-mock returns stub values for most of these, but if you find another one feel free to [open an issue](https://github.com/NiGhTTraX/strong-mock/issues) with a minimal reproduction.
|
|
546
|
+
|
|
547
|
+
Unfortunately, not all of these cases can be covered with stub values, and you may have to slightly adjust your code to work around this issue.
|
|
548
|
+
|
|
549
|
+
### Can I partially mock a concrete implementation?
|
|
462
550
|
|
|
463
551
|
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.
|
|
464
552
|
|
|
@@ -466,10 +554,6 @@ No, passing a concrete implementation to `mock()` will be the same as passing a
|
|
|
466
554
|
|
|
467
555
|
You currently can't do that. Please use a normal method instead e.g. `setFoo()` vs `set foo()`.
|
|
468
556
|
|
|
469
|
-
### Why do I have to set a return value even if it's `undefined`?
|
|
470
|
-
|
|
471
|
-
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.
|
|
472
|
-
|
|
473
557
|
### How do I provide a function for the mock to call?
|
|
474
558
|
|
|
475
559
|
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:
|
|
@@ -488,7 +572,7 @@ console.log(foo.bar(23)); // 'called 23'
|
|
|
488
572
|
|
|
489
573
|
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).
|
|
490
574
|
|
|
491
|
-
### Can I spread
|
|
575
|
+
### Can I spread or enumerate a mock?
|
|
492
576
|
|
|
493
577
|
Yes, and you will only get the properties that have expectations on them.
|
|
494
578
|
|
|
@@ -504,6 +588,10 @@ console.log(foo2.bar); // 42
|
|
|
504
588
|
console.log(foo2.baz); // undefined
|
|
505
589
|
```
|
|
506
590
|
|
|
591
|
+
### Why does `typeof mock()` return `function`?
|
|
592
|
+
|
|
593
|
+
All mocks and methods on them are functions in order to intercept function calls.
|
|
594
|
+
|
|
507
595
|
### How can I ignore `undefined` keys when setting expectations on objects?
|
|
508
596
|
|
|
509
597
|
Use the `It.deepEquals` matcher explicitly inside `when` and pass `{ strict: false }`:
|
|
@@ -525,3 +613,7 @@ setDefaults({
|
|
|
525
613
|
concreteMatcher: (expected) => It.deepEquals(expected, { strict: false })
|
|
526
614
|
});
|
|
527
615
|
```
|
|
616
|
+
|
|
617
|
+
### How can I verify order of calls?
|
|
618
|
+
|
|
619
|
+
`when()` expectations can be satisfied in any order. If your code under test depends on a specific order of execution, consider redesigning it to remove the coupling before the different calls.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Property } from '../proxy';
|
|
2
|
+
export declare class UnfinishedExpectation extends Error {
|
|
3
|
+
constructor(property: Property, args: any[] | undefined);
|
|
4
|
+
}
|
|
5
|
+
export declare class MissingWhen extends Error {
|
|
6
|
+
constructor();
|
|
7
|
+
}
|
|
8
|
+
export declare class NotAMock extends Error {
|
|
9
|
+
constructor();
|
|
10
|
+
}
|
|
11
|
+
export declare class NestedWhen extends Error {
|
|
12
|
+
constructor(parentProp: Property, childProp: Property);
|
|
13
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { Expectation } from '../expectation/expectation';
|
|
2
|
+
export declare const printArgsDiff: (expected: unknown[], actual: unknown[]) => string;
|
|
3
|
+
export declare const printExpectationDiff: (e: Expectation, args: unknown[]) => string;
|
|
4
|
+
export declare const printDiffForAllExpectations: (expectations: Expectation[], actual: unknown[]) => string;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Expectation } from '../expectation/expectation';
|
|
2
|
+
import type { Property } from '../proxy';
|
|
3
|
+
type MatcherResult = {
|
|
4
|
+
expected: unknown;
|
|
5
|
+
actual: unknown;
|
|
6
|
+
};
|
|
7
|
+
interface MatcherError {
|
|
8
|
+
matcherResult?: MatcherResult;
|
|
9
|
+
}
|
|
10
|
+
export declare class UnexpectedCall extends Error implements MatcherError {
|
|
11
|
+
matcherResult?: MatcherResult;
|
|
12
|
+
constructor(property: Property, args: unknown[], expectations: Expectation[]);
|
|
13
|
+
}
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Expectation } from '../expectation/expectation';
|
|
2
|
+
import type { CallMap } from '../expectation/repository/expectation-repository';
|
|
3
|
+
export declare class UnmetExpectations extends Error {
|
|
4
|
+
constructor(expectations: Expectation[]);
|
|
5
|
+
}
|
|
6
|
+
export declare class UnexpectedCalls extends Error {
|
|
7
|
+
constructor(unexpectedCalls: CallMap, expectations: Expectation[]);
|
|
8
|
+
}
|
|
@@ -1,30 +1,27 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type {
|
|
3
|
-
import type { ReturnValue } from './repository/return-value';
|
|
4
|
-
/**
|
|
5
|
-
* Compare received arguments against matchers.
|
|
6
|
-
*/
|
|
7
|
-
export interface Expectation {
|
|
8
|
-
property: Property;
|
|
9
|
-
/**
|
|
10
|
-
* `undefined` means this is a property expectation.
|
|
11
|
-
* `[]` means this is a function call with no arguments.
|
|
12
|
-
*/
|
|
13
|
-
args: Matcher[] | undefined;
|
|
14
|
-
returnValue: ReturnValue;
|
|
15
|
-
min: number;
|
|
16
|
-
max: number;
|
|
17
|
-
/**
|
|
18
|
-
* How many times should this expectation match?
|
|
19
|
-
*/
|
|
20
|
-
setInvocationCount(min: number, max: number)
|
|
21
|
-
matches(args:
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
* Special symbol denoting the call of a function.
|
|
29
|
-
*/
|
|
30
|
-
export declare const ApplyProp: unique symbol;
|
|
1
|
+
import type { Matcher } from '../matchers/matcher';
|
|
2
|
+
import type { Property } from '../proxy';
|
|
3
|
+
import type { ReturnValue } from './repository/return-value';
|
|
4
|
+
/**
|
|
5
|
+
* Compare received arguments against matchers.
|
|
6
|
+
*/
|
|
7
|
+
export interface Expectation {
|
|
8
|
+
property: Property;
|
|
9
|
+
/**
|
|
10
|
+
* `undefined` means this is a property expectation.
|
|
11
|
+
* `[]` means this is a function call with no arguments.
|
|
12
|
+
*/
|
|
13
|
+
args: Matcher[] | undefined;
|
|
14
|
+
returnValue: ReturnValue;
|
|
15
|
+
min: number;
|
|
16
|
+
max: number;
|
|
17
|
+
/**
|
|
18
|
+
* How many times should this expectation match?
|
|
19
|
+
*/
|
|
20
|
+
setInvocationCount: (min: number, max: number) => void;
|
|
21
|
+
matches: (args: unknown[] | undefined) => boolean;
|
|
22
|
+
toString: () => string;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Special symbol denoting the call of a function.
|
|
26
|
+
*/
|
|
27
|
+
export declare const ApplyProp: unique symbol;
|
|
@@ -1,90 +1,90 @@
|
|
|
1
|
-
import type { Property } from '../../proxy';
|
|
2
|
-
import type { Expectation } from '../expectation';
|
|
3
|
-
export type Call = {
|
|
4
|
-
arguments: any[] | undefined;
|
|
5
|
-
};
|
|
6
|
-
/**
|
|
7
|
-
* Method calls should be recorded both as a property access and a method call.
|
|
8
|
-
*
|
|
9
|
-
* @example
|
|
10
|
-
* // foo.bar(1, 2, 3) should generate
|
|
11
|
-
* {
|
|
12
|
-
* foo: [
|
|
13
|
-
* { arguments: undefined },
|
|
14
|
-
* { arguments: [1, 2, 3] }
|
|
15
|
-
* ]
|
|
16
|
-
* }
|
|
17
|
-
*/
|
|
18
|
-
export type CallMap = Map<Property, Call[]>;
|
|
19
|
-
export type CallStats = {
|
|
20
|
-
/**
|
|
21
|
-
* Calls that matched existing expectations.
|
|
22
|
-
*/
|
|
23
|
-
expected: CallMap;
|
|
24
|
-
/**
|
|
25
|
-
* Calls that didn't match any existing expectation.
|
|
26
|
-
*/
|
|
27
|
-
unexpected: CallMap;
|
|
28
|
-
};
|
|
29
|
-
export interface ExpectationRepository {
|
|
30
|
-
add(expectation: Expectation)
|
|
31
|
-
/**
|
|
32
|
-
* Get a return value for the given property.
|
|
33
|
-
*
|
|
34
|
-
* The value might be a non-callable e.g. a number or a string, or it might
|
|
35
|
-
* be a function that, upon receiving arguments, will start a new search and
|
|
36
|
-
* return a value again.
|
|
37
|
-
*
|
|
38
|
-
* The list of expectations should be consulted from first to last when
|
|
39
|
-
* getting a return value. If none of them match it is up to the
|
|
40
|
-
* implementation to decide what to do.
|
|
41
|
-
*
|
|
42
|
-
* @example
|
|
43
|
-
* add(new Expectation('getData', [1, 2], 23);
|
|
44
|
-
* get('getData')(1, 2) === 23
|
|
45
|
-
*
|
|
46
|
-
* @example
|
|
47
|
-
* add(new Expectation('hasData', undefined, true);
|
|
48
|
-
* get('hasData') === true
|
|
49
|
-
*
|
|
50
|
-
* @example
|
|
51
|
-
* add(new Expectation('getData', undefined, () => 42);
|
|
52
|
-
* get('getData')(1, 2, '3', false, NaN) === 42
|
|
53
|
-
*/
|
|
54
|
-
get(property: Property)
|
|
55
|
-
/**
|
|
56
|
-
* Get a return value for a function call.
|
|
57
|
-
*
|
|
58
|
-
* Note: this will only be invoked if the mocked type is a function. For
|
|
59
|
-
* method property calls {@link get} will be called instead.
|
|
60
|
-
*
|
|
61
|
-
* The list of expectations should be consulted from first to last when
|
|
62
|
-
* getting a return value. If none of them match it is up to the
|
|
63
|
-
* implementation to decide what to do.
|
|
64
|
-
*
|
|
65
|
-
* @example
|
|
66
|
-
* add(new Expectation(ApplyProp, [1, 2], 23);
|
|
67
|
-
* apply(1, 2) === 23
|
|
68
|
-
*/
|
|
69
|
-
apply(args: unknown[])
|
|
70
|
-
/**
|
|
71
|
-
* Get all the properties that have expectations.
|
|
72
|
-
*
|
|
73
|
-
* @example
|
|
74
|
-
* add(new Expectation('foo', undefined, 23));
|
|
75
|
-
* getAllProperties() === ['foo']
|
|
76
|
-
*/
|
|
77
|
-
getAllProperties()
|
|
78
|
-
/**
|
|
79
|
-
* Remove any expectations and clear the call stats.
|
|
80
|
-
*/
|
|
81
|
-
clear()
|
|
82
|
-
/**
|
|
83
|
-
* Return all unmet expectations.
|
|
84
|
-
*/
|
|
85
|
-
getUnmet()
|
|
86
|
-
/**
|
|
87
|
-
* Return all the calls that have been made so far.
|
|
88
|
-
*/
|
|
89
|
-
getCallStats()
|
|
90
|
-
}
|
|
1
|
+
import type { Property } from '../../proxy';
|
|
2
|
+
import type { Expectation } from '../expectation';
|
|
3
|
+
export type Call = {
|
|
4
|
+
arguments: any[] | undefined;
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* Method calls should be recorded both as a property access and a method call.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* // foo.bar(1, 2, 3) should generate
|
|
11
|
+
* {
|
|
12
|
+
* foo: [
|
|
13
|
+
* { arguments: undefined },
|
|
14
|
+
* { arguments: [1, 2, 3] }
|
|
15
|
+
* ]
|
|
16
|
+
* }
|
|
17
|
+
*/
|
|
18
|
+
export type CallMap = Map<Property, Call[]>;
|
|
19
|
+
export type CallStats = {
|
|
20
|
+
/**
|
|
21
|
+
* Calls that matched existing expectations.
|
|
22
|
+
*/
|
|
23
|
+
expected: CallMap;
|
|
24
|
+
/**
|
|
25
|
+
* Calls that didn't match any existing expectation.
|
|
26
|
+
*/
|
|
27
|
+
unexpected: CallMap;
|
|
28
|
+
};
|
|
29
|
+
export interface ExpectationRepository {
|
|
30
|
+
add: (expectation: Expectation) => void;
|
|
31
|
+
/**
|
|
32
|
+
* Get a return value for the given property.
|
|
33
|
+
*
|
|
34
|
+
* The value might be a non-callable e.g. a number or a string, or it might
|
|
35
|
+
* be a function that, upon receiving arguments, will start a new search and
|
|
36
|
+
* return a value again.
|
|
37
|
+
*
|
|
38
|
+
* The list of expectations should be consulted from first to last when
|
|
39
|
+
* getting a return value. If none of them match it is up to the
|
|
40
|
+
* implementation to decide what to do.
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* add(new Expectation('getData', [1, 2], 23);
|
|
44
|
+
* get('getData')(1, 2) === 23
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* add(new Expectation('hasData', undefined, true);
|
|
48
|
+
* get('hasData') === true
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* add(new Expectation('getData', undefined, () => 42);
|
|
52
|
+
* get('getData')(1, 2, '3', false, NaN) === 42
|
|
53
|
+
*/
|
|
54
|
+
get: (property: Property) => unknown;
|
|
55
|
+
/**
|
|
56
|
+
* Get a return value for a function call.
|
|
57
|
+
*
|
|
58
|
+
* Note: this will only be invoked if the mocked type is a function. For
|
|
59
|
+
* method property calls {@link get} will be called instead.
|
|
60
|
+
*
|
|
61
|
+
* The list of expectations should be consulted from first to last when
|
|
62
|
+
* getting a return value. If none of them match it is up to the
|
|
63
|
+
* implementation to decide what to do.
|
|
64
|
+
*
|
|
65
|
+
* @example
|
|
66
|
+
* add(new Expectation(ApplyProp, [1, 2], 23);
|
|
67
|
+
* apply(1, 2) === 23
|
|
68
|
+
*/
|
|
69
|
+
apply: (args: unknown[]) => unknown;
|
|
70
|
+
/**
|
|
71
|
+
* Get all the properties that have expectations.
|
|
72
|
+
*
|
|
73
|
+
* @example
|
|
74
|
+
* add(new Expectation('foo', undefined, 23));
|
|
75
|
+
* getAllProperties() === ['foo']
|
|
76
|
+
*/
|
|
77
|
+
getAllProperties: () => Property[];
|
|
78
|
+
/**
|
|
79
|
+
* Remove any expectations and clear the call stats.
|
|
80
|
+
*/
|
|
81
|
+
clear: () => void;
|
|
82
|
+
/**
|
|
83
|
+
* Return all unmet expectations.
|
|
84
|
+
*/
|
|
85
|
+
getUnmet: () => Expectation[];
|
|
86
|
+
/**
|
|
87
|
+
* Return all the calls that have been made so far.
|
|
88
|
+
*/
|
|
89
|
+
getCallStats: () => CallStats;
|
|
90
|
+
}
|