strong-mock 8.0.1 → 9.0.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +52 -30
- package/dist/errors/api.d.ts +13 -0
- package/dist/errors/unexpected-access.d.ts +5 -0
- package/dist/errors/unexpected-call.d.ts +17 -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 +810 -512
- 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-object.d.ts +27 -0
- package/dist/matchers/is-string.d.ts +15 -0
- package/dist/matchers/is.d.ts +9 -0
- package/dist/matchers/it.d.ts +9 -0
- package/dist/matchers/matcher.d.ts +92 -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 +20 -16
- 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,8 @@ 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
34
|
- [Installation](#installation)
|
|
35
35
|
- [Requirements](#requirements)
|
|
36
36
|
- [API](#api)
|
|
@@ -43,18 +43,19 @@ console.log(foo.bar(23)); // 'I am strong!'
|
|
|
43
43
|
- [Throwing errors](#throwing-errors)
|
|
44
44
|
- [Verifying expectations](#verifying-expectations)
|
|
45
45
|
- [Resetting expectations](#resetting-expectations)
|
|
46
|
-
- [
|
|
46
|
+
- [Matchers](#matchers-1)
|
|
47
47
|
- [Mock options](#mock-options)
|
|
48
48
|
- [Unexpected property return value](#unexpected-property-return-value)
|
|
49
49
|
- [Exact params](#exact-params)
|
|
50
50
|
- [Concrete matcher](#concrete-matcher)
|
|
51
51
|
- [FAQ](#faq)
|
|
52
52
|
- [Why do I have to set all expectations first?](#why-do-i-have-to-set-all-expectations-first)
|
|
53
|
-
- [
|
|
54
|
-
- [How do I set expectations on setters?](#how-do-i-set-expectations-on-setters)
|
|
53
|
+
- [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)
|
|
55
54
|
- [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)
|
|
55
|
+
- [Can I partially mock a concrete implementation?](#can-i-partially-mock-a-concrete-implementation)
|
|
56
|
+
- [How do I set expectations on setters?](#how-do-i-set-expectations-on-setters)
|
|
56
57
|
- [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
|
|
58
|
+
- [Can I spread or enumerate a mock?](#can-i-spread-or-enumerate-a-mock)
|
|
58
59
|
- [How can I ignore `undefined` keys when setting expectations on objects?](#how-can-i-ignore-undefined-keys-when-setting-expectations-on-objects)
|
|
59
60
|
|
|
60
61
|
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
|
|
@@ -67,27 +68,34 @@ The mock expectations will share the same type guarantees as your production cod
|
|
|
67
68
|
|
|
68
69
|

|
|
69
70
|
|
|
70
|
-
###
|
|
71
|
+
### Matchers
|
|
71
72
|
|
|
72
|
-
|
|
73
|
+
You can use matchers to partially match values, or create complex expectations, while still maintaining type safety.
|
|
73
74
|
|
|
74
|
-
|
|
75
|
-
import { mock, when } from 'strong-mock';
|
|
75
|
+

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

|
|
91
99
|
|
|
92
100
|
## Installation
|
|
93
101
|
|
|
@@ -99,6 +107,10 @@ npm i -D strong-mock
|
|
|
99
107
|
yarn add -D strong-mock
|
|
100
108
|
```
|
|
101
109
|
|
|
110
|
+
```shell
|
|
111
|
+
pnpm add -D strong-mock
|
|
112
|
+
```
|
|
113
|
+
|
|
102
114
|
## Requirements
|
|
103
115
|
|
|
104
116
|
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 +119,7 @@ strong-mock requires an environment that supports the [ES6 Proxy object](https:/
|
|
|
107
119
|
|
|
108
120
|
### Setting expectations
|
|
109
121
|
|
|
110
|
-
Expectations are set by calling the mock inside a `when` callback and
|
|
122
|
+
Expectations are set by calling the mock inside a `when` callback and setting a return value.
|
|
111
123
|
|
|
112
124
|
```typescript
|
|
113
125
|
when(() => foo.bar(23)).thenReturn('awesome');
|
|
@@ -186,7 +198,7 @@ const fn = mock<Fn>();
|
|
|
186
198
|
|
|
187
199
|
when(() => fn(1)).thenResolve(2);
|
|
188
200
|
|
|
189
|
-
console.log(await fn()); // 2
|
|
201
|
+
console.log(await fn(1)); // 2
|
|
190
202
|
```
|
|
191
203
|
|
|
192
204
|
### Throwing errors
|
|
@@ -266,7 +278,7 @@ beforeEach(() => {
|
|
|
266
278
|
})
|
|
267
279
|
```
|
|
268
280
|
|
|
269
|
-
###
|
|
281
|
+
### Matchers
|
|
270
282
|
|
|
271
283
|
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
284
|
|
|
@@ -286,7 +298,7 @@ console.log(fn(
|
|
|
286
298
|
); // 'matched!'
|
|
287
299
|
```
|
|
288
300
|
|
|
289
|
-
You can mix
|
|
301
|
+
You can mix matchers with concrete arguments:
|
|
290
302
|
|
|
291
303
|
```typescript
|
|
292
304
|
when(() => fn(42, It.isObject())).thenReturn('matched');
|
|
@@ -397,7 +409,7 @@ propertiesThrow.bar(42);
|
|
|
397
409
|
|
|
398
410
|
### Exact params
|
|
399
411
|
|
|
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 [
|
|
412
|
+
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
413
|
|
|
402
414
|
```typescript
|
|
403
415
|
import { mock } from 'strong-mock';
|
|
@@ -429,7 +441,7 @@ console.log(fn(1)); // throws
|
|
|
429
441
|
|
|
430
442
|
### Concrete matcher
|
|
431
443
|
|
|
432
|
-
You can configure the [matcher](#
|
|
444
|
+
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
445
|
|
|
434
446
|
```typescript
|
|
435
447
|
import { mock, when, It } from 'strong-mock';
|
|
@@ -458,18 +470,28 @@ This design decision has a few reasons behind it. First, it forces you to be awa
|
|
|
458
470
|
|
|
459
471
|
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
472
|
|
|
461
|
-
###
|
|
473
|
+
### Why do I get a `Didn't expect mock to be called` error?
|
|
462
474
|
|
|
463
|
-
|
|
475
|
+
This error happens when your code under test calls a method from the mock, or the mock itself if it's a function, 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)).
|
|
464
476
|
|
|
465
|
-
|
|
477
|
+
In rare cases, the code under test may try to inspect the mock by accessing special properties on it. For instance, React's `setState(state)` accepts 2 types of values: functions and everything else. To differentiate between the 2 types, React will internally do a `typeof` check. All mocks created by strong-mock return `'function'` for this check, so React will try to call them in case you pass them directly to `setState`. This might lead to the `Didn't expect mock(...) to be called` error, as the mock receives the previous state and doesn't find an expectation for it.
|
|
466
478
|
|
|
467
|
-
|
|
479
|
+
If you run into any of these cases, feel free to [open an issue](https://github.com/NiGhTTraX/strong-mock/issues) with a minimal reproduction. Most of the time the issue can be fixed by returning stubbed values for these special properties.
|
|
480
|
+
|
|
481
|
+
Unfortunately, this is not always possible, such as with the React example above. You might have to adjust your code slightly to work around the checks your code, or some library, is doing. With React, simply putting the mock inside an object e.g. `setState({ foo: theMock })` will avoid the `typeof` check and work as expected.
|
|
468
482
|
|
|
469
483
|
### Why do I have to set a return value even if it's `undefined`?
|
|
470
484
|
|
|
471
485
|
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
486
|
|
|
487
|
+
### Can I partially mock a concrete implementation?
|
|
488
|
+
|
|
489
|
+
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.
|
|
490
|
+
|
|
491
|
+
### How do I set expectations on setters?
|
|
492
|
+
|
|
493
|
+
You currently can't do that. Please use a normal method instead e.g. `setFoo()` vs `set foo()`.
|
|
494
|
+
|
|
473
495
|
### How do I provide a function for the mock to call?
|
|
474
496
|
|
|
475
497
|
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 +510,7 @@ console.log(foo.bar(23)); // 'called 23'
|
|
|
488
510
|
|
|
489
511
|
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
512
|
|
|
491
|
-
### Can I spread
|
|
513
|
+
### Can I spread or enumerate a mock?
|
|
492
514
|
|
|
493
515
|
Yes, and you will only get the properties that have expectations on them.
|
|
494
516
|
|
|
@@ -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,17 @@
|
|
|
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 const printArgsDiff: (expected: unknown[], actual: unknown[]) => string;
|
|
11
|
+
export declare const printExpectationDiff: (e: Expectation, args: unknown[]) => string;
|
|
12
|
+
export declare const printDiffForAllExpectations: (expectations: Expectation[], actual: unknown[]) => string;
|
|
13
|
+
export declare class UnexpectedCall extends Error implements MatcherError {
|
|
14
|
+
matcherResult?: MatcherResult;
|
|
15
|
+
constructor(property: Property, args: unknown[], expectations: Expectation[]);
|
|
16
|
+
}
|
|
17
|
+
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
|
+
}
|
|
@@ -1,38 +1,38 @@
|
|
|
1
|
-
import { UnexpectedProperty } from '../../mock/options';
|
|
2
|
-
import type { Property } from '../../proxy';
|
|
3
|
-
import type { Expectation } from '../expectation';
|
|
4
|
-
import type { CallMap, ExpectationRepository } from './expectation-repository';
|
|
5
|
-
type CountableExpectation = {
|
|
6
|
-
expectation: Expectation;
|
|
7
|
-
matchCount: number;
|
|
8
|
-
};
|
|
9
|
-
/**
|
|
10
|
-
* An expectation repository with a configurable behavior for
|
|
11
|
-
* unexpected property access.
|
|
12
|
-
*/
|
|
13
|
-
export declare class FlexibleRepository implements ExpectationRepository {
|
|
14
|
-
private unexpectedProperty;
|
|
15
|
-
constructor(unexpectedProperty?: UnexpectedProperty);
|
|
16
|
-
protected readonly expectations: Map<Property, CountableExpectation[]>;
|
|
17
|
-
private readonly expectedCallStats;
|
|
18
|
-
private readonly unexpectedCallStats;
|
|
19
|
-
add(expectation: Expectation): void;
|
|
20
|
-
clear(): void;
|
|
21
|
-
apply: (args: unknown[]) => unknown;
|
|
22
|
-
get(property: Property): any;
|
|
23
|
-
private handlePropertyWithMatchingExpectations;
|
|
24
|
-
private handlePropertyWithNoExpectations;
|
|
25
|
-
getAllProperties(): Property[];
|
|
26
|
-
getCallStats(): {
|
|
27
|
-
expected: CallMap;
|
|
28
|
-
unexpected: CallMap;
|
|
29
|
-
};
|
|
30
|
-
getUnmet(): Expectation[];
|
|
31
|
-
private recordExpected;
|
|
32
|
-
private recordUnexpected;
|
|
33
|
-
private countAndConsume;
|
|
34
|
-
private consumeExpectation;
|
|
35
|
-
private getValueForUnexpectedCall;
|
|
36
|
-
private getValueForUnexpectedAccess;
|
|
37
|
-
}
|
|
38
|
-
export {};
|
|
1
|
+
import { UnexpectedProperty } from '../../mock/options';
|
|
2
|
+
import type { Property } from '../../proxy';
|
|
3
|
+
import type { Expectation } from '../expectation';
|
|
4
|
+
import type { CallMap, ExpectationRepository } from './expectation-repository';
|
|
5
|
+
type CountableExpectation = {
|
|
6
|
+
expectation: Expectation;
|
|
7
|
+
matchCount: number;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* An expectation repository with a configurable behavior for
|
|
11
|
+
* unexpected property access.
|
|
12
|
+
*/
|
|
13
|
+
export declare class FlexibleRepository implements ExpectationRepository {
|
|
14
|
+
private unexpectedProperty;
|
|
15
|
+
constructor(unexpectedProperty?: UnexpectedProperty);
|
|
16
|
+
protected readonly expectations: Map<Property, CountableExpectation[]>;
|
|
17
|
+
private readonly expectedCallStats;
|
|
18
|
+
private readonly unexpectedCallStats;
|
|
19
|
+
add(expectation: Expectation): void;
|
|
20
|
+
clear(): void;
|
|
21
|
+
apply: (args: unknown[]) => unknown;
|
|
22
|
+
get(property: Property): any;
|
|
23
|
+
private handlePropertyWithMatchingExpectations;
|
|
24
|
+
private handlePropertyWithNoExpectations;
|
|
25
|
+
getAllProperties(): Property[];
|
|
26
|
+
getCallStats(): {
|
|
27
|
+
expected: CallMap;
|
|
28
|
+
unexpected: CallMap;
|
|
29
|
+
};
|
|
30
|
+
getUnmet(): Expectation[];
|
|
31
|
+
private recordExpected;
|
|
32
|
+
private recordUnexpected;
|
|
33
|
+
private countAndConsume;
|
|
34
|
+
private consumeExpectation;
|
|
35
|
+
private getValueForUnexpectedCall;
|
|
36
|
+
private getValueForUnexpectedAccess;
|
|
37
|
+
}
|
|
38
|
+
export {};
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
export type ReturnValue = {
|
|
2
|
-
value: any;
|
|
3
|
-
isPromise?: boolean;
|
|
4
|
-
isError?: boolean;
|
|
5
|
-
};
|
|
6
|
-
/**
|
|
7
|
-
* Unbox the expectation's return value.
|
|
8
|
-
*
|
|
9
|
-
* If the value is an error then throw it.
|
|
10
|
-
*
|
|
11
|
-
* If the value is a promise then resolve/reject it.
|
|
12
|
-
*/
|
|
13
|
-
export declare const unboxReturnValue: ({ isError, isPromise, value, }: ReturnValue) => any;
|
|
1
|
+
export type ReturnValue = {
|
|
2
|
+
value: any;
|
|
3
|
+
isPromise?: boolean;
|
|
4
|
+
isError?: boolean;
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* Unbox the expectation's return value.
|
|
8
|
+
*
|
|
9
|
+
* If the value is an error then throw it.
|
|
10
|
+
*
|
|
11
|
+
* If the value is a promise then resolve/reject it.
|
|
12
|
+
*/
|
|
13
|
+
export declare const unboxReturnValue: ({ isError, isPromise, value, }: ReturnValue) => any;
|
|
@@ -1,30 +1,30 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type {
|
|
3
|
-
import type {
|
|
4
|
-
import type { ReturnValue } from './repository/return-value';
|
|
5
|
-
/**
|
|
6
|
-
* Matches a call with more parameters than expected because it is assumed the
|
|
7
|
-
* compiler will check that those parameters are optional.
|
|
8
|
-
*
|
|
9
|
-
* @example
|
|
10
|
-
* new StrongExpectation(
|
|
11
|
-
* 'bar',
|
|
12
|
-
* deepEquals([1, 2, 3]),
|
|
13
|
-
* 23
|
|
14
|
-
* ).matches('bar', [1, 2, 3]) === true;
|
|
15
|
-
*/
|
|
16
|
-
export declare class StrongExpectation implements Expectation {
|
|
17
|
-
property: Property;
|
|
18
|
-
args: Matcher[] | undefined;
|
|
19
|
-
returnValue: ReturnValue;
|
|
20
|
-
private exactParams;
|
|
21
|
-
private matched;
|
|
22
|
-
min: number;
|
|
23
|
-
max: number;
|
|
24
|
-
constructor(property: Property, args: Matcher[] | undefined, returnValue: ReturnValue, exactParams?: boolean);
|
|
25
|
-
setInvocationCount(min: number, max?: number): void;
|
|
26
|
-
matches(args: any[] | undefined): boolean;
|
|
27
|
-
isUnmet(): boolean;
|
|
28
|
-
private matchesArgs;
|
|
29
|
-
|
|
30
|
-
}
|
|
1
|
+
import type { Matcher } from '../matchers/matcher';
|
|
2
|
+
import type { Property } from '../proxy';
|
|
3
|
+
import type { Expectation } from './expectation';
|
|
4
|
+
import type { ReturnValue } from './repository/return-value';
|
|
5
|
+
/**
|
|
6
|
+
* Matches a call with more parameters than expected because it is assumed the
|
|
7
|
+
* compiler will check that those parameters are optional.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* new StrongExpectation(
|
|
11
|
+
* 'bar',
|
|
12
|
+
* deepEquals([1, 2, 3]),
|
|
13
|
+
* 23
|
|
14
|
+
* ).matches('bar', [1, 2, 3]) === true;
|
|
15
|
+
*/
|
|
16
|
+
export declare class StrongExpectation implements Expectation {
|
|
17
|
+
property: Property;
|
|
18
|
+
args: Matcher[] | undefined;
|
|
19
|
+
returnValue: ReturnValue;
|
|
20
|
+
private exactParams;
|
|
21
|
+
private matched;
|
|
22
|
+
min: number;
|
|
23
|
+
max: number;
|
|
24
|
+
constructor(property: Property, args: Matcher[] | undefined, returnValue: ReturnValue, exactParams?: boolean);
|
|
25
|
+
setInvocationCount(min: number, max?: number): void;
|
|
26
|
+
matches(args: any[] | undefined): boolean;
|
|
27
|
+
isUnmet(): boolean;
|
|
28
|
+
private matchesArgs;
|
|
29
|
+
toString(): string;
|
|
30
|
+
}
|