@overkill-dev/doubles 0.0.1 → 0.0.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/LICENSE +21 -0
- package/assert/assertion-extension.d.ts +65 -0
- package/assert/assertion-extension.d.ts.map +1 -0
- package/assert/assertion-extension.js +250 -0
- package/assert/assertion-extension.js.map +1 -0
- package/assertion-protocol/assertion-evaluation.d.ts +13 -0
- package/assertion-protocol/assertion-evaluation.d.ts.map +1 -0
- package/assertion-protocol/assertion-node-shape.d.ts +71 -0
- package/assertion-protocol/assertion-node-shape.d.ts.map +1 -0
- package/assertion-protocol/assertion-node.d.ts +97 -0
- package/assertion-protocol/assertion-node.d.ts.map +1 -0
- package/assertion-protocol/assertion-reference.d.ts +73 -0
- package/assertion-protocol/assertion-reference.d.ts.map +1 -0
- package/assertion-protocol/assertion-reference.js +49 -0
- package/assertion-protocol/assertion-reference.js.map +1 -0
- package/assertion-protocol/assertions/boolean.d.ts +11 -0
- package/assertion-protocol/assertions/boolean.d.ts.map +1 -0
- package/assertion-protocol/assertions/collection.d.ts +22 -0
- package/assertion-protocol/assertions/collection.d.ts.map +1 -0
- package/assertion-protocol/assertions/equality.d.ts +17 -0
- package/assertion-protocol/assertions/equality.d.ts.map +1 -0
- package/assertion-protocol/assertions/fail.d.ts +13 -0
- package/assertion-protocol/assertions/fail.d.ts.map +1 -0
- package/assertion-protocol/assertions/numeric.d.ts +29 -0
- package/assertion-protocol/assertions/numeric.d.ts.map +1 -0
- package/assertion-protocol/assertions/partial.d.ts +14 -0
- package/assertion-protocol/assertions/partial.d.ts.map +1 -0
- package/assertion-protocol/assertions/presence.d.ts +17 -0
- package/assertion-protocol/assertions/presence.d.ts.map +1 -0
- package/assertion-protocol/assertions/string.d.ts +28 -0
- package/assertion-protocol/assertions/string.d.ts.map +1 -0
- package/assertion-protocol/assertions/type-shape.d.ts +42 -0
- package/assertion-protocol/assertions/type-shape.d.ts.map +1 -0
- package/assertion-protocol/thrown-error-record.d.ts +8 -0
- package/assertion-protocol/thrown-error-record.d.ts.map +1 -0
- package/assertion-protocol/thrown-error-record.js +17 -0
- package/assertion-protocol/thrown-error-record.js.map +1 -0
- package/assertion-protocol/thrown-matcher.d.ts +51 -0
- package/assertion-protocol/thrown-matcher.d.ts.map +1 -0
- package/assertion-protocol/thrown-matcher.js +186 -0
- package/assertion-protocol/thrown-matcher.js.map +1 -0
- package/compare/comparison-result.d.ts +25 -0
- package/compare/comparison-result.d.ts.map +1 -0
- package/compare/comparison-state.d.ts +7 -0
- package/compare/comparison-state.d.ts.map +1 -0
- package/compare/comparison-state.js +20 -0
- package/compare/comparison-state.js.map +1 -0
- package/compare/comparison.d.ts +12 -0
- package/compare/comparison.d.ts.map +1 -0
- package/compare/raw-comparison.d.ts +24 -0
- package/compare/raw-comparison.d.ts.map +1 -0
- package/compare/raw-comparison.js +285 -0
- package/compare/raw-comparison.js.map +1 -0
- package/compare/serialized-value-shape.d.ts +143 -0
- package/compare/serialized-value-shape.d.ts.map +1 -0
- package/compare/serialized-value.d.ts +11 -0
- package/compare/serialized-value.d.ts.map +1 -0
- package/compare/value-classification.d.ts +26 -0
- package/compare/value-classification.d.ts.map +1 -0
- package/compare/value-classification.js +110 -0
- package/compare/value-classification.js.map +1 -0
- package/diff/diff-shape.d.ts +117 -0
- package/diff/diff-shape.d.ts.map +1 -0
- package/doubles/disposal-symbol.d.ts +3 -0
- package/doubles/disposal-symbol.d.ts.map +1 -0
- package/doubles/disposal-symbol.js +10 -0
- package/doubles/disposal-symbol.js.map +1 -0
- package/doubles/double-behavior.d.ts +106 -0
- package/doubles/double-behavior.d.ts.map +1 -0
- package/doubles/double-behavior.js +257 -0
- package/doubles/double-behavior.js.map +1 -0
- package/doubles/double-chronology.d.ts +21 -0
- package/doubles/double-chronology.d.ts.map +1 -0
- package/doubles/double-chronology.js +78 -0
- package/doubles/double-chronology.js.map +1 -0
- package/doubles/double-history-api.d.ts +33 -0
- package/doubles/double-history-api.d.ts.map +1 -0
- package/doubles/double-history-api.js +125 -0
- package/doubles/double-history-api.js.map +1 -0
- package/doubles/double-history-record.d.ts +82 -0
- package/doubles/double-history-record.d.ts.map +1 -0
- package/doubles/double-history-record.js +65 -0
- package/doubles/double-history-record.js.map +1 -0
- package/doubles/double-history-store.d.ts +21 -0
- package/doubles/double-history-store.d.ts.map +1 -0
- package/doubles/double-history-store.js +118 -0
- package/doubles/double-history-store.js.map +1 -0
- package/doubles/double-history.d.ts +141 -0
- package/doubles/double-history.d.ts.map +1 -0
- package/doubles/double-history.js +32 -0
- package/doubles/double-history.js.map +1 -0
- package/doubles/double-iterator-event-types.d.ts +25 -0
- package/doubles/double-iterator-event-types.d.ts.map +1 -0
- package/doubles/double-iterator-tracking.d.ts +12 -0
- package/doubles/double-iterator-tracking.d.ts.map +1 -0
- package/doubles/double-iterator-tracking.js +161 -0
- package/doubles/double-iterator-tracking.js.map +1 -0
- package/doubles/double-rule-arguments.d.ts +18 -0
- package/doubles/double-rule-arguments.d.ts.map +1 -0
- package/doubles/double-rule-callback.d.ts +40 -0
- package/doubles/double-rule-callback.d.ts.map +1 -0
- package/doubles/double-rule-callback.js +84 -0
- package/doubles/double-rule-callback.js.map +1 -0
- package/doubles/double-rule-generator.d.ts +108 -0
- package/doubles/double-rule-generator.d.ts.map +1 -0
- package/doubles/double-rule-generator.js +37 -0
- package/doubles/double-rule-generator.js.map +1 -0
- package/doubles/double-rule-types.d.ts +162 -0
- package/doubles/double-rule-types.d.ts.map +1 -0
- package/doubles/double-rule.d.ts +3 -0
- package/doubles/double-rule.d.ts.map +1 -0
- package/doubles/double-rule.js +113 -0
- package/doubles/double-rule.js.map +1 -0
- package/doubles/double-runtime.d.ts +9 -0
- package/doubles/double-runtime.d.ts.map +1 -0
- package/doubles/double-runtime.js +173 -0
- package/doubles/double-runtime.js.map +1 -0
- package/doubles/double-signature.d.ts +5 -0
- package/doubles/double-signature.d.ts.map +1 -0
- package/doubles/double-usage-argument-assertion.d.ts +27 -0
- package/doubles/double-usage-argument-assertion.d.ts.map +1 -0
- package/doubles/double-usage-argument-assertion.js +202 -0
- package/doubles/double-usage-argument-assertion.js.map +1 -0
- package/doubles/double-usage-contract.d.ts +29 -0
- package/doubles/double-usage-contract.d.ts.map +1 -0
- package/doubles/double-usage-contract.js +11 -0
- package/doubles/double-usage-contract.js.map +1 -0
- package/doubles/double-usage-count-assertion.d.ts +6 -0
- package/doubles/double-usage-count-assertion.d.ts.map +1 -0
- package/doubles/double-usage-count-assertion.js +73 -0
- package/doubles/double-usage-count-assertion.js.map +1 -0
- package/doubles/double-usage-disposal-assertion.d.ts +7 -0
- package/doubles/double-usage-disposal-assertion.d.ts.map +1 -0
- package/doubles/double-usage-disposal-assertion.js +175 -0
- package/doubles/double-usage-disposal-assertion.js.map +1 -0
- package/doubles/double-usage-inspection.d.ts +22 -0
- package/doubles/double-usage-inspection.d.ts.map +1 -0
- package/doubles/double-usage-inspection.js +59 -0
- package/doubles/double-usage-inspection.js.map +1 -0
- package/doubles/double-usage-iterator-assertion.d.ts +7 -0
- package/doubles/double-usage-iterator-assertion.d.ts.map +1 -0
- package/doubles/double-usage-iterator-assertion.js +124 -0
- package/doubles/double-usage-iterator-assertion.js.map +1 -0
- package/doubles/double-usage-order-assertion.d.ts +3 -0
- package/doubles/double-usage-order-assertion.d.ts.map +1 -0
- package/doubles/double-usage-order-assertion.js +91 -0
- package/doubles/double-usage-order-assertion.js.map +1 -0
- package/doubles/double-usage.d.ts +81 -0
- package/doubles/double-usage.d.ts.map +1 -0
- package/doubles/double-usage.js +228 -0
- package/doubles/double-usage.js.map +1 -0
- package/doubles/protocol-disposable-double.d.ts +16 -0
- package/doubles/protocol-disposable-double.d.ts.map +1 -0
- package/doubles/protocol-disposable-double.js +37 -0
- package/doubles/protocol-disposable-double.js.map +1 -0
- package/doubles/protocol-double-metadata.d.ts +15 -0
- package/doubles/protocol-double-metadata.d.ts.map +1 -0
- package/doubles/protocol-double-metadata.js +30 -0
- package/doubles/protocol-double-metadata.js.map +1 -0
- package/doubles/protocol-double.d.ts +50 -0
- package/doubles/protocol-double.d.ts.map +1 -0
- package/doubles/protocol-double.js +76 -0
- package/doubles/protocol-double.js.map +1 -0
- package/doubles/protocol-iterator-double.d.ts +93 -0
- package/doubles/protocol-iterator-double.d.ts.map +1 -0
- package/doubles/protocol-iterator-double.js +512 -0
- package/doubles/protocol-iterator-double.js.map +1 -0
- package/doubles/test-double.d.ts +62 -0
- package/doubles/test-double.d.ts.map +1 -0
- package/doubles/test-double.js +192 -0
- package/doubles/test-double.js.map +1 -0
- package/package.json +36 -6
- package/packages/doubles/doubles.entry-point.d.ts +13 -0
- package/packages/doubles/doubles.entry-point.d.ts.map +1 -0
- package/packages/doubles/doubles.entry-point.js +5 -0
- package/packages/doubles/doubles.entry-point.js.map +1 -0
- package/readme.md +423 -5
- package/sbom.cdx.json +30 -0
package/readme.md
CHANGED
|
@@ -1,7 +1,425 @@
|
|
|
1
|
-
#
|
|
1
|
+
# `@overkill-dev/doubles`
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
can subsequently be configured for it. It contains no real package content and is published already
|
|
5
|
-
deprecated.
|
|
3
|
+
Explicit function-first test doubles for Overkill.
|
|
6
4
|
|
|
7
|
-
|
|
5
|
+
Use this package when a test can inject a function or constructor directly.
|
|
6
|
+
It does not patch modules, replace object methods, manage sandboxes, or keep a
|
|
7
|
+
restore registry.
|
|
8
|
+
|
|
9
|
+
## Import
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
import {
|
|
13
|
+
doubleUsage,
|
|
14
|
+
rule,
|
|
15
|
+
testAsyncDisposable,
|
|
16
|
+
testAsyncIterable,
|
|
17
|
+
testAsyncIterator,
|
|
18
|
+
testDisposable,
|
|
19
|
+
testDouble,
|
|
20
|
+
testIterable,
|
|
21
|
+
testIterator
|
|
22
|
+
} from '@overkill-dev/doubles';
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Public runtime values:
|
|
26
|
+
|
|
27
|
+
- `testDouble`: creates callable or constructable test doubles.
|
|
28
|
+
- `testIterator`: creates one consumable sync iterator double.
|
|
29
|
+
- `testIterable`: creates a reusable sync iterable double.
|
|
30
|
+
- `testAsyncIterator`: creates one consumable async iterator double.
|
|
31
|
+
- `testAsyncIterable`: creates a reusable async iterable double.
|
|
32
|
+
- `testDisposable`: creates a sync disposable double for `using`.
|
|
33
|
+
- `testAsyncDisposable`: creates an async disposable double for `await using`.
|
|
34
|
+
- `rule`: builds reusable behavior rules.
|
|
35
|
+
- `doubleUsage`: assertion references for `scope.assert(...)`.
|
|
36
|
+
|
|
37
|
+
## Create A Double
|
|
38
|
+
|
|
39
|
+
Untyped doubles are callable and return `undefined`:
|
|
40
|
+
|
|
41
|
+
```ts
|
|
42
|
+
const log = testDouble();
|
|
43
|
+
|
|
44
|
+
log('saved');
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Typed doubles keep their function signature:
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
type SaveUser = (user: { readonly id: string; readonly name: string; }) => boolean;
|
|
51
|
+
|
|
52
|
+
const saveUser = testDouble.returns<SaveUser>(true);
|
|
53
|
+
|
|
54
|
+
saveUser({ id: '42', name: 'Ada' });
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Constructor doubles keep their constructor signature:
|
|
58
|
+
|
|
59
|
+
```ts
|
|
60
|
+
type ClientConstructor = new (baseUrl: string) => Client;
|
|
61
|
+
|
|
62
|
+
const client = createClient();
|
|
63
|
+
const Client = testDouble.constructs<ClientConstructor>(client);
|
|
64
|
+
|
|
65
|
+
new Client('https://api.example.test');
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Fixed Behavior
|
|
69
|
+
|
|
70
|
+
- `testDouble()`: creates an untyped function double that returns `undefined`.
|
|
71
|
+
- `testDouble.returns(value)`: returns `value` from every call.
|
|
72
|
+
- `testDouble.returns<Fn>(value)`: returns `value` and keeps `Fn`.
|
|
73
|
+
- `testDouble.returns<Fn>()`: valid for `void` functions.
|
|
74
|
+
- `testDouble.resolves(value)`: returns a promise resolved with `value`.
|
|
75
|
+
- `testDouble.rejects(reason)`: returns a rejected promise.
|
|
76
|
+
- `testDouble.throws(thrown)`: throws `thrown` from every call.
|
|
77
|
+
- `testDouble.constructs(instance)`: returns `instance` from `new Double(...)`.
|
|
78
|
+
- `testDouble.yields(values, returnValue?)`: returns a fresh tracked sync
|
|
79
|
+
iterator from a finite value array.
|
|
80
|
+
- `testDouble.yieldsFrom(factory)`: returns a fresh tracked sync iterator that
|
|
81
|
+
delegates with `yield*`. The factory receives the double call arguments and
|
|
82
|
+
runs lazily on first iterator consumption.
|
|
83
|
+
- `testDouble.yieldsAsync(values, returnValue?)`: returns a fresh tracked async
|
|
84
|
+
iterator from a finite value array.
|
|
85
|
+
- `testDouble.yieldsAsyncFrom(factory)`: returns a fresh tracked async iterator
|
|
86
|
+
that delegates with async `yield*`. The factory receives the double call
|
|
87
|
+
arguments and may return a sync or async iterable.
|
|
88
|
+
|
|
89
|
+
## Rules
|
|
90
|
+
|
|
91
|
+
Rules choose behavior for specific calls or constructions:
|
|
92
|
+
|
|
93
|
+
```ts
|
|
94
|
+
type LoadUser = (id: string) => Promise<User>;
|
|
95
|
+
|
|
96
|
+
const loadUser = testDouble<LoadUser>({
|
|
97
|
+
rules: [
|
|
98
|
+
rule.when('admin').resolves(adminUser),
|
|
99
|
+
rule.when('guest').resolves(guestUser),
|
|
100
|
+
rule.onCall(2).rejects(new Error('unexpected retry'))
|
|
101
|
+
],
|
|
102
|
+
fallback: rule.rejects(new Error('unknown user id'))
|
|
103
|
+
});
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Rule starters:
|
|
107
|
+
|
|
108
|
+
- `rule.when(...args)`: matches a call by a partial-deep argument prefix.
|
|
109
|
+
- `rule.whenConstructedWith(...args)`: matches a construction by a partial-deep argument prefix.
|
|
110
|
+
- `rule.onCall(index)`: matches the call at zero-based `index`.
|
|
111
|
+
- `rule.onConstruction(index)`: matches the construction at zero-based `index`.
|
|
112
|
+
|
|
113
|
+
Rule terminators:
|
|
114
|
+
|
|
115
|
+
- `.returns(value)`: returns a value.
|
|
116
|
+
- `.resolves(value)`: returns a resolved promise.
|
|
117
|
+
- `.rejects(reason)`: returns a rejected promise.
|
|
118
|
+
- `.throws(thrown)`: throws a value.
|
|
119
|
+
- `.constructs(instance)`: returns an instance from construction rules.
|
|
120
|
+
- `.calls(fn)`: calls `fn` for custom behavior.
|
|
121
|
+
- `.callsCallback(index, arguments, returnValue, receiver?)`: calls a callback
|
|
122
|
+
argument synchronously, then returns `returnValue`.
|
|
123
|
+
- `.callsCallbackAsync(index, arguments, returnValue, receiver?)`: returns
|
|
124
|
+
`returnValue`, then calls a callback argument in a microtask.
|
|
125
|
+
- `.sequence(entries)`: uses entries in order for repeated matches.
|
|
126
|
+
- `.yields(values, returnValue?)`: returns a fresh tracked sync iterator.
|
|
127
|
+
- `.yieldsFrom(factory)`: returns a fresh tracked sync iterator delegated from
|
|
128
|
+
the call arguments.
|
|
129
|
+
- `.yieldsAsync(values, returnValue?)`: returns a fresh tracked async iterator.
|
|
130
|
+
- `.yieldsAsyncFrom(factory)`: returns a fresh tracked async iterator delegated
|
|
131
|
+
from the call arguments.
|
|
132
|
+
|
|
133
|
+
Behavior factories can also be used directly in `fallback`:
|
|
134
|
+
|
|
135
|
+
- `rule.returns(value)`
|
|
136
|
+
- `rule.resolves(value)`
|
|
137
|
+
- `rule.rejects(reason)`
|
|
138
|
+
- `rule.throws(thrown)`
|
|
139
|
+
- `rule.constructs(instance)`
|
|
140
|
+
- `rule.calls(fn)`
|
|
141
|
+
- `rule.callsCallback(index, arguments, returnValue, receiver?)`
|
|
142
|
+
- `rule.callsCallbackAsync(index, arguments, returnValue, receiver?)`
|
|
143
|
+
- `rule.sequence(entries)`
|
|
144
|
+
- `rule.yields(values, returnValue?)`
|
|
145
|
+
- `rule.yieldsFrom(factory)`
|
|
146
|
+
- `rule.yieldsAsync(values, returnValue?)`
|
|
147
|
+
- `rule.yieldsAsyncFrom(factory)`
|
|
148
|
+
|
|
149
|
+
A fallback can separate call and construction behavior for a value that is both
|
|
150
|
+
callable and constructable:
|
|
151
|
+
|
|
152
|
+
```ts
|
|
153
|
+
const Client = testDouble<ClientFactory>({
|
|
154
|
+
fallback: {
|
|
155
|
+
call: rule.returns(clientFromCall),
|
|
156
|
+
construction: rule.constructs(clientFromNew)
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Callback-style dependencies can invoke a callback argument without dropping to
|
|
162
|
+
custom behavior:
|
|
163
|
+
|
|
164
|
+
```ts
|
|
165
|
+
type ReadUser = (id: string, callback: (error: Error | null, user: User) => void) => undefined;
|
|
166
|
+
|
|
167
|
+
const readUser = testDouble<ReadUser>({
|
|
168
|
+
fallback: rule.callsCallback(1, [ null, user ], undefined)
|
|
169
|
+
});
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
## History
|
|
173
|
+
|
|
174
|
+
Every double records usage. History properties are non-enumerable.
|
|
175
|
+
|
|
176
|
+
- `interactionCount`: calls plus constructions.
|
|
177
|
+
- `interactions`: all calls and constructions in order for this double.
|
|
178
|
+
- `firstInteraction`, `lastInteraction`, `nthInteraction(index)`: selected interaction records.
|
|
179
|
+
- `callCount`: normal function calls.
|
|
180
|
+
- `calls`: call records in order.
|
|
181
|
+
- `firstCall`, `lastCall`, `nthCall(index)`: selected call records.
|
|
182
|
+
- `constructionCount`: `new Double(...)` usages.
|
|
183
|
+
- `constructions`: construction records in order.
|
|
184
|
+
- `firstConstruction`, `lastConstruction`, `nthConstruction(index)`: selected construction records.
|
|
185
|
+
- `results`: returned and thrown results in order.
|
|
186
|
+
- `firstResult`, `lastResult`: selected result records.
|
|
187
|
+
- `iteratorEventCount`: tracked iterator protocol events.
|
|
188
|
+
- `iteratorEvents`: tracked `next`, `return`, and `throw` outcomes in order.
|
|
189
|
+
- `firstIteratorEvent`, `lastIteratorEvent`, `nthIteratorEvent(index)`:
|
|
190
|
+
selected iterator event records.
|
|
191
|
+
- `reset()`: clears this double's public history and rewinds double-owned ordered behavior.
|
|
192
|
+
|
|
193
|
+
Records include:
|
|
194
|
+
|
|
195
|
+
- `arguments`: the argument tuple.
|
|
196
|
+
- `index`: the zero-based call or construction index for that mode.
|
|
197
|
+
- `kind`: `call` or `construction`.
|
|
198
|
+
- `order`: the per-double interaction order.
|
|
199
|
+
- `result`: returned or thrown result data.
|
|
200
|
+
- `thisValue`: only on call records.
|
|
201
|
+
- `instance`: only on construction records.
|
|
202
|
+
|
|
203
|
+
Iterator records are created only for iterators produced by `yields`,
|
|
204
|
+
`yieldsFrom`, `yieldsAsync`, and `yieldsAsyncFrom`. They include:
|
|
205
|
+
|
|
206
|
+
- `kind`: `yield`, `return`, or `throw`.
|
|
207
|
+
- `protocol`: `sync` or `async`.
|
|
208
|
+
- `method`: `next`, `return`, or `throw`.
|
|
209
|
+
- `arguments`: the iterator method arguments.
|
|
210
|
+
- `value`: yielded or returned values.
|
|
211
|
+
- `thrown`: thrown values.
|
|
212
|
+
- `callIndex`, `iteratorIndex`, and `index`: related call and iterator event
|
|
213
|
+
positions.
|
|
214
|
+
|
|
215
|
+
## Protocol Doubles
|
|
216
|
+
|
|
217
|
+
Use protocol doubles when the dependency itself is an iterator, iterable, or
|
|
218
|
+
disposable object rather than a function that returns one.
|
|
219
|
+
|
|
220
|
+
```ts
|
|
221
|
+
const values = testIterator.yields([ 'created', 'updated' ]);
|
|
222
|
+
|
|
223
|
+
values.next();
|
|
224
|
+
|
|
225
|
+
scope.assert(doubleUsage.iterated, values);
|
|
226
|
+
scope.assert(doubleUsage.yieldedExactly, values, [ 'created' ]);
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
`testIterator` creates one consumable well-formed iterator. It works with
|
|
230
|
+
`for...of`, exposes platform iterator helpers when the runtime provides them,
|
|
231
|
+
and records `next`, `return`, and `throw` through method doubles.
|
|
232
|
+
|
|
233
|
+
```ts
|
|
234
|
+
const source = testIterable.yields([ 'created', 'updated' ]);
|
|
235
|
+
|
|
236
|
+
[ ...source ];
|
|
237
|
+
[ ...source ];
|
|
238
|
+
|
|
239
|
+
scope.assert(doubleUsage.yieldedExactly, source, [
|
|
240
|
+
'created',
|
|
241
|
+
'updated',
|
|
242
|
+
'created',
|
|
243
|
+
'updated'
|
|
244
|
+
]);
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
`testIterable` creates a reusable iterable. Each `[Symbol.iterator]()` call
|
|
248
|
+
returns a fresh well-formed iterator. Async variants mirror these shapes with
|
|
249
|
+
`for await...of`.
|
|
250
|
+
|
|
251
|
+
Disposable protocol methods are inspectable doubles too:
|
|
252
|
+
|
|
253
|
+
```ts
|
|
254
|
+
const resource = testDisposable({
|
|
255
|
+
dispose: { fallback: rule.throws(new Error('expected')) }
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
using value = resource;
|
|
259
|
+
|
|
260
|
+
scope.assert(doubleUsage.disposedOnce, resource);
|
|
261
|
+
scope.assert(doubleUsage.calledOnce, resource.dispose);
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
`testAsyncDisposable` installs `[Symbol.asyncDispose]()` for `await using`,
|
|
265
|
+
exposes `.asyncDispose` for inspection, and defaults to resolving `undefined`.
|
|
266
|
+
|
|
267
|
+
## Assertions
|
|
268
|
+
|
|
269
|
+
`doubleUsage` contains assertion references for Overkill's engine-owned
|
|
270
|
+
assertion context:
|
|
271
|
+
|
|
272
|
+
```ts
|
|
273
|
+
test('saves a user', (scope) => {
|
|
274
|
+
const saveUser = testDouble.returns<SaveUser>(true);
|
|
275
|
+
|
|
276
|
+
saveUser({ id: '42', name: 'Ada' });
|
|
277
|
+
|
|
278
|
+
scope.assert(doubleUsage.calledOnceWith, saveUser, [ { id: '42' } ]);
|
|
279
|
+
|
|
280
|
+
return scope.assert.collect();
|
|
281
|
+
});
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
Argument assertions always receive one `args` tuple array.
|
|
285
|
+
|
|
286
|
+
- `*With`: partial-deep matching with exact arity.
|
|
287
|
+
- `*WithPrefix`: partial-deep matching on the argument prefix. The tuple must not be empty.
|
|
288
|
+
- `*WithExactly`: exact-deep matching with exact arity. These assertions are positive only.
|
|
289
|
+
|
|
290
|
+
Examples:
|
|
291
|
+
|
|
292
|
+
```ts
|
|
293
|
+
scope.assert(doubleUsage.calledWith, saveUser, [ { id: '42' } ]);
|
|
294
|
+
scope.assert(doubleUsage.calledWithPrefix, writeMetric, [ 'signup' ]);
|
|
295
|
+
scope.assert(doubleUsage.calledWithExactly, saveUser, [ { id: '42', name: 'Ada' } ]);
|
|
296
|
+
scope.assert(doubleUsage.calledWith, ping, []);
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
### Counts
|
|
300
|
+
|
|
301
|
+
- `doubleUsage.interacted(double)`: at least one call or construction.
|
|
302
|
+
- `doubleUsage.notInteracted(double)`: no calls or constructions.
|
|
303
|
+
- `doubleUsage.interactionCount(double, count)`: exact call plus construction count.
|
|
304
|
+
- `doubleUsage.interactedOnce(double)`: exactly one call or construction.
|
|
305
|
+
- `doubleUsage.called(double)`: at least one call.
|
|
306
|
+
- `doubleUsage.notCalled(double)`: no calls.
|
|
307
|
+
- `doubleUsage.callCount(double, count)`: exact call count.
|
|
308
|
+
- `doubleUsage.calledOnce(double)`: exactly one call.
|
|
309
|
+
- `doubleUsage.constructed(double)`: at least one construction.
|
|
310
|
+
- `doubleUsage.notConstructed(double)`: no constructions.
|
|
311
|
+
- `doubleUsage.constructionCount(double, count)`: exact construction count.
|
|
312
|
+
- `doubleUsage.constructedOnce(double)`: exactly one construction.
|
|
313
|
+
- `doubleUsage.disposed(disposable)`: at least one disposal.
|
|
314
|
+
- `doubleUsage.notDisposed(disposable)`: no disposal.
|
|
315
|
+
- `doubleUsage.disposeCount(disposable, count)`: exact disposal count.
|
|
316
|
+
- `doubleUsage.disposedOnce(disposable)`: exactly one disposal.
|
|
317
|
+
- `doubleUsage.disposeOrder([a, b, ...])`: disposal order.
|
|
318
|
+
- `doubleUsage.iterated(double)`: at least one tracked iterator event.
|
|
319
|
+
- `doubleUsage.notIterated(double)`: no tracked iterator events.
|
|
320
|
+
- `doubleUsage.iteratorEventCount(double, count)`: exact tracked iterator event
|
|
321
|
+
count.
|
|
322
|
+
- `doubleUsage.yieldCount(double, count)`: exact yielded value count.
|
|
323
|
+
- `doubleUsage.yieldedExactly(double, values)`: exact yielded values across all
|
|
324
|
+
tracked iterators in event order.
|
|
325
|
+
|
|
326
|
+
### Any Matching Usage
|
|
327
|
+
|
|
328
|
+
- `doubleUsage.interactedWith(double, args)`
|
|
329
|
+
- `doubleUsage.interactedWithPrefix(double, args)`
|
|
330
|
+
- `doubleUsage.interactedWithExactly(double, args)`
|
|
331
|
+
- `doubleUsage.calledWith(double, args)`
|
|
332
|
+
- `doubleUsage.calledWithPrefix(double, args)`
|
|
333
|
+
- `doubleUsage.calledWithExactly(double, args)`
|
|
334
|
+
- `doubleUsage.constructedWith(double, args)`
|
|
335
|
+
- `doubleUsage.constructedWithPrefix(double, args)`
|
|
336
|
+
- `doubleUsage.constructedWithExactly(double, args)`
|
|
337
|
+
|
|
338
|
+
### Negative Matching Usage
|
|
339
|
+
|
|
340
|
+
- `doubleUsage.notInteractedWith(double, args)`
|
|
341
|
+
- `doubleUsage.notInteractedWithPrefix(double, args)`
|
|
342
|
+
- `doubleUsage.notCalledWith(double, args)`
|
|
343
|
+
- `doubleUsage.notCalledWithPrefix(double, args)`
|
|
344
|
+
- `doubleUsage.notConstructedWith(double, args)`
|
|
345
|
+
- `doubleUsage.notConstructedWithPrefix(double, args)`
|
|
346
|
+
|
|
347
|
+
There are no negative `*WithExactly` assertions. Use `not*With` or
|
|
348
|
+
`not*WithPrefix` for negative argument checks.
|
|
349
|
+
|
|
350
|
+
### Once Matching Usage
|
|
351
|
+
|
|
352
|
+
These assertions require exactly one relevant event, then check its arguments:
|
|
353
|
+
|
|
354
|
+
- `doubleUsage.interactedOnceWith(double, args)`
|
|
355
|
+
- `doubleUsage.interactedOnceWithPrefix(double, args)`
|
|
356
|
+
- `doubleUsage.interactedOnceWithExactly(double, args)`
|
|
357
|
+
- `doubleUsage.calledOnceWith(double, args)`
|
|
358
|
+
- `doubleUsage.calledOnceWithPrefix(double, args)`
|
|
359
|
+
- `doubleUsage.calledOnceWithExactly(double, args)`
|
|
360
|
+
- `doubleUsage.constructedOnceWith(double, args)`
|
|
361
|
+
- `doubleUsage.constructedOnceWithPrefix(double, args)`
|
|
362
|
+
- `doubleUsage.constructedOnceWithExactly(double, args)`
|
|
363
|
+
|
|
364
|
+
### Last Matching Usage
|
|
365
|
+
|
|
366
|
+
- `doubleUsage.lastInteractedWith(double, args)`
|
|
367
|
+
- `doubleUsage.lastInteractedWithPrefix(double, args)`
|
|
368
|
+
- `doubleUsage.lastInteractedWithExactly(double, args)`
|
|
369
|
+
- `doubleUsage.lastCalledWith(double, args)`
|
|
370
|
+
- `doubleUsage.lastCalledWithPrefix(double, args)`
|
|
371
|
+
- `doubleUsage.lastCalledWithExactly(double, args)`
|
|
372
|
+
- `doubleUsage.lastConstructedWith(double, args)`
|
|
373
|
+
- `doubleUsage.lastConstructedWithPrefix(double, args)`
|
|
374
|
+
- `doubleUsage.lastConstructedWithExactly(double, args)`
|
|
375
|
+
|
|
376
|
+
### Indexed Matching Usage
|
|
377
|
+
|
|
378
|
+
Indexes are zero-based:
|
|
379
|
+
|
|
380
|
+
- `doubleUsage.nthInteractionWith(double, index, args)`
|
|
381
|
+
- `doubleUsage.nthInteractionWithPrefix(double, index, args)`
|
|
382
|
+
- `doubleUsage.nthInteractionWithExactly(double, index, args)`
|
|
383
|
+
- `doubleUsage.nthCallWith(double, index, args)`
|
|
384
|
+
- `doubleUsage.nthCallWithPrefix(double, index, args)`
|
|
385
|
+
- `doubleUsage.nthCallWithExactly(double, index, args)`
|
|
386
|
+
- `doubleUsage.nthConstructionWith(double, index, args)`
|
|
387
|
+
- `doubleUsage.nthConstructionWithPrefix(double, index, args)`
|
|
388
|
+
- `doubleUsage.nthConstructionWithExactly(double, index, args)`
|
|
389
|
+
|
|
390
|
+
### Order
|
|
391
|
+
|
|
392
|
+
Order assertions compare usage across doubles:
|
|
393
|
+
|
|
394
|
+
```ts
|
|
395
|
+
scope.assert(doubleUsage.callOrder, [ loadUser, saveUser, publishEvent ]);
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
- `doubleUsage.interactionOrder([first, second, ...])`
|
|
399
|
+
- `doubleUsage.callOrder([first, second, ...])`
|
|
400
|
+
- `doubleUsage.constructionOrder([first, second, ...])`
|
|
401
|
+
|
|
402
|
+
The list must contain at least two doubles. Each double must have at least one
|
|
403
|
+
relevant event. For each adjacent pair, all relevant events on the previous
|
|
404
|
+
double must happen before any relevant event on the next double.
|
|
405
|
+
|
|
406
|
+
Order is tracked inside the doubles package. Resetting a double clears its
|
|
407
|
+
public history, but it does not reset the hidden chronology used for
|
|
408
|
+
cross-double order assertions.
|
|
409
|
+
|
|
410
|
+
## Types
|
|
411
|
+
|
|
412
|
+
Exported types:
|
|
413
|
+
|
|
414
|
+
- `TestDouble<Signature>`: a function or constructor with history attached.
|
|
415
|
+
- `TestDoubleFactory`: the `testDouble` factory shape.
|
|
416
|
+
- `DoubleHistory<Signature>`: the introspection API attached to each double.
|
|
417
|
+
- `DoubleInvocation<Arguments>`: invocation data passed to answer callbacks.
|
|
418
|
+
- `DoubleCall<Arguments, ReturnValue, ThisValue>`: a recorded function call.
|
|
419
|
+
- `DoubleConstruction<Arguments, Instance>`: a recorded construction.
|
|
420
|
+
- `DoubleInteraction`: a call or construction record.
|
|
421
|
+
- `DoubleResult<Value>`: a returned or thrown result.
|
|
422
|
+
- `DoubleReturnedResult<Value>`: a returned result.
|
|
423
|
+
- `DoubleThrownResult`: a thrown result.
|
|
424
|
+
- `RuleFactory`: the `rule` namespace shape.
|
|
425
|
+
- `DoubleUsageAssertions`: the `doubleUsage` namespace shape.
|
package/sbom.cdx.json
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://cyclonedx.org/schema/bom-1.6.schema.json",
|
|
3
|
+
"bomFormat": "CycloneDX",
|
|
4
|
+
"specVersion": "1.6",
|
|
5
|
+
"version": 1,
|
|
6
|
+
"metadata": {
|
|
7
|
+
"tools": {
|
|
8
|
+
"components": [
|
|
9
|
+
{
|
|
10
|
+
"type": "application",
|
|
11
|
+
"name": "packtory",
|
|
12
|
+
"version": "0.0.78"
|
|
13
|
+
}
|
|
14
|
+
]
|
|
15
|
+
},
|
|
16
|
+
"component": {
|
|
17
|
+
"type": "library",
|
|
18
|
+
"name": "@overkill-dev/doubles",
|
|
19
|
+
"version": "0.0.2",
|
|
20
|
+
"bom-ref": "pkg:npm/@overkill-dev/doubles@0.0.2",
|
|
21
|
+
"purl": "pkg:npm/@overkill-dev/doubles@0.0.2"
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"components": [],
|
|
25
|
+
"dependencies": [
|
|
26
|
+
{
|
|
27
|
+
"ref": "pkg:npm/@overkill-dev/doubles@0.0.2"
|
|
28
|
+
}
|
|
29
|
+
]
|
|
30
|
+
}
|