@rcompat/test 0.18.0 → 0.19.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 +120 -46
- package/lib/private/E.d.ts +1 -1
- package/lib/private/extend.d.ts +1 -1
- package/lib/private/index.d.ts +3 -1
- package/lib/private/index.js +4 -2
- package/lib/private/intercept.d.ts +1 -1
- package/lib/private/spy.d.ts +8 -0
- package/lib/private/spy.js +15 -0
- package/lib/private/to-object.d.ts +2 -2
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -278,6 +278,60 @@ test.case("static mock is loaded before the spec", assert => {
|
|
|
278
278
|
Static mocks are file-scoped when run through `proby`; they do not leak into
|
|
279
279
|
later spec files.
|
|
280
280
|
|
|
281
|
+
### Spying on functions
|
|
282
|
+
|
|
283
|
+
Use `spy` to wrap a function and record every call made to it. The wrapped
|
|
284
|
+
function behaves identically to the original but tracks its arguments.
|
|
285
|
+
|
|
286
|
+
```ts
|
|
287
|
+
import test from "@rcompat/test";
|
|
288
|
+
|
|
289
|
+
const add = (a: number, b: number) => a + b;
|
|
290
|
+
const tracked = test.spy(add);
|
|
291
|
+
|
|
292
|
+
tracked(1, 2);
|
|
293
|
+
tracked(3, 4);
|
|
294
|
+
|
|
295
|
+
tracked.called; // true
|
|
296
|
+
tracked.calls; // [[1, 2], [3, 4]]
|
|
297
|
+
tracked.calls[0]; // [1, 2]
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
Use `spy` inside a test case to make call-tracking assertions:
|
|
301
|
+
|
|
302
|
+
```ts
|
|
303
|
+
import test from "@rcompat/test";
|
|
304
|
+
|
|
305
|
+
test.case("tracks calls", assert => {
|
|
306
|
+
const tracked = test.spy((a: number, b: number) => a + b);
|
|
307
|
+
|
|
308
|
+
assert(tracked.called).false();
|
|
309
|
+
assert(tracked.calls).equals([]);
|
|
310
|
+
|
|
311
|
+
tracked(1, 2);
|
|
312
|
+
|
|
313
|
+
assert(tracked.called).true();
|
|
314
|
+
assert(tracked.calls).equals([[1, 2]]);
|
|
315
|
+
assert(tracked(3, 4)).equals(7);
|
|
316
|
+
});
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
Pass a second argument to replace the implementation while still tracking calls:
|
|
320
|
+
|
|
321
|
+
```ts
|
|
322
|
+
import test from "@rcompat/test";
|
|
323
|
+
|
|
324
|
+
test.case("mocks implementation", assert => {
|
|
325
|
+
const tracked = test.spy(
|
|
326
|
+
(a: number, b: number) => a + b,
|
|
327
|
+
(a: number, b: number) => a * b,
|
|
328
|
+
);
|
|
329
|
+
|
|
330
|
+
assert(tracked(2, 3)).equals(6); // mocker runs, not original
|
|
331
|
+
assert(tracked.calls).equals([[2, 3]]);
|
|
332
|
+
});
|
|
333
|
+
```
|
|
334
|
+
|
|
281
335
|
### Intercepting fetch
|
|
282
336
|
|
|
283
337
|
Use `test.intercept` to block outbound fetch calls to a specific origin and
|
|
@@ -392,9 +446,9 @@ test.group(name: string, fn: () => void): void;
|
|
|
392
446
|
Group test cases under a named scope. Groups can be targeted individually
|
|
393
447
|
when running proby.
|
|
394
448
|
|
|
395
|
-
| Parameter | Type | Description
|
|
396
|
-
| --------- | ---------- |
|
|
397
|
-
| `name` | `string` | Group name, used by proby to filter
|
|
449
|
+
| Parameter | Type | Description |
|
|
450
|
+
| --------- | ---------- | ------------------------------------- |
|
|
451
|
+
| `name` | `string` | Group name, used by proby to filter |
|
|
398
452
|
| `fn` | `function` | Function containing `test.case` calls |
|
|
399
453
|
|
|
400
454
|
### `test.mock`
|
|
@@ -409,10 +463,10 @@ test.mock<T extends object>(
|
|
|
409
463
|
Register a module mock and return a handle to the tracked mocked exports.
|
|
410
464
|
Function exports are wrapped so you can inspect `calls` and `called`.
|
|
411
465
|
|
|
412
|
-
| Parameter | Type | Description
|
|
413
|
-
| ----------- | ---------- |
|
|
414
|
-
| `specifier` | `string` | Module specifier to mock
|
|
415
|
-
| `factory` | `function` | Returns the mocked exports for that module
|
|
466
|
+
| Parameter | Type | Description |
|
|
467
|
+
| ----------- | ---------- | ------------------------------------------ |
|
|
468
|
+
| `specifier` | `string` | Module specifier to mock |
|
|
469
|
+
| `factory` | `function` | Returns the mocked exports for that module |
|
|
416
470
|
|
|
417
471
|
### `test.import`
|
|
418
472
|
|
|
@@ -434,10 +488,10 @@ test.intercept(
|
|
|
434
488
|
Intercept outbound fetch calls to `base_url`. Returns an `Intercept` object
|
|
435
489
|
for asserting on recorded requests.
|
|
436
490
|
|
|
437
|
-
| Parameter | Type | Description
|
|
438
|
-
| ---------- | ---------- |
|
|
439
|
-
| `base_url` | `string` | Origin to intercept, e.g. `"https://api.example.com"`
|
|
440
|
-
| `setup` | `function` | Register route handlers on the setup object
|
|
491
|
+
| Parameter | Type | Description |
|
|
492
|
+
| ---------- | ---------- | ------------------------------------------------------ |
|
|
493
|
+
| `base_url` | `string` | Origin to intercept, e.g. `"https://api.example.com"` |
|
|
494
|
+
| `setup` | `function` | Register route handlers on the setup object |
|
|
441
495
|
|
|
442
496
|
### `test.extend`
|
|
443
497
|
|
|
@@ -450,31 +504,52 @@ test.extend<Subject, Extensions>(
|
|
|
450
504
|
Create a new test object with custom assertion methods mixed into the
|
|
451
505
|
asserter.
|
|
452
506
|
|
|
453
|
-
| Parameter | Type | Description
|
|
454
|
-
| --------- | ---------- |
|
|
455
|
-
| `factory` | `function` | Returns extra methods to attach to each `Assert` instance
|
|
507
|
+
| Parameter | Type | Description |
|
|
508
|
+
| --------- | ---------- | --------------------------------------------------------- |
|
|
509
|
+
| `factory` | `function` | Returns extra methods to attach to each `Assert` instance |
|
|
510
|
+
|
|
511
|
+
### `test.spy`
|
|
512
|
+
|
|
513
|
+
```ts
|
|
514
|
+
test.spy<F extends (...args: any[]) => any>(fn: F, mocker?: F): Tracked<F>;
|
|
515
|
+
```
|
|
516
|
+
|
|
517
|
+
Wrap a function to track calls. Returns the wrapped function with two extra
|
|
518
|
+
properties.
|
|
519
|
+
|
|
520
|
+
| Parameter | Type | Description |
|
|
521
|
+
| --------- | ---- | ------------------------------------ |
|
|
522
|
+
| `fn` | `F` | The function to wrap |
|
|
523
|
+
| `mocker` | `F` | Optional replacement implementation |
|
|
524
|
+
|
|
525
|
+
#### `Tracked<F>`
|
|
526
|
+
|
|
527
|
+
| Property | Type | Description |
|
|
528
|
+
| -------- | ----------------- | -------------------------------------- |
|
|
529
|
+
| `calls` | `Parameters<F>[]` | Array of argument tuples, one per call |
|
|
530
|
+
| `called` | `boolean` | `true` if the function has been called |
|
|
456
531
|
|
|
457
532
|
#### `Setup`
|
|
458
533
|
|
|
459
|
-
| Method
|
|
460
|
-
|
|
|
461
|
-
| `get(path, handler)`
|
|
462
|
-
| `post(path, handler)`
|
|
463
|
-
| `put(path, handler)`
|
|
464
|
-
| `patch(path, handler)`
|
|
465
|
-
| `delete(path, handler)
|
|
534
|
+
| Method | Description |
|
|
535
|
+
| ----------------------- | ------------------------- |
|
|
536
|
+
| `get(path, handler)` | Register a GET handler |
|
|
537
|
+
| `post(path, handler)` | Register a POST handler |
|
|
538
|
+
| `put(path, handler)` | Register a PUT handler |
|
|
539
|
+
| `patch(path, handler)` | Register a PATCH handler |
|
|
540
|
+
| `delete(path, handler)` | Register a DELETE handler |
|
|
466
541
|
|
|
467
542
|
Each handler receives the incoming `Request` and returns a plain object,
|
|
468
543
|
which is serialized into a `Response` automatically.
|
|
469
544
|
|
|
470
545
|
#### `Intercept`
|
|
471
546
|
|
|
472
|
-
| Method
|
|
473
|
-
|
|
|
474
|
-
| `calls(path)`
|
|
475
|
-
| `requests(path)`
|
|
476
|
-
| `restore()`
|
|
477
|
-
| `[Symbol.dispose]`
|
|
547
|
+
| Method | Description |
|
|
548
|
+
| ------------------ | ---------------------------------------------- |
|
|
549
|
+
| `calls(path)` | Number of times `path` was hit |
|
|
550
|
+
| `requests(path)` | Array of `Request` objects recorded for `path` |
|
|
551
|
+
| `restore()` | Reinstate the original `globalThis.fetch` |
|
|
552
|
+
| `[Symbol.dispose]` | Called automatically by `using` |
|
|
478
553
|
|
|
479
554
|
### `Asserter`
|
|
480
555
|
|
|
@@ -486,24 +561,24 @@ The assert function passed to test cases.
|
|
|
486
561
|
|
|
487
562
|
### `Assert<T>`
|
|
488
563
|
|
|
489
|
-
| Method | Description
|
|
490
|
-
| ----------------------- |
|
|
491
|
-
| `equals(expected)` | Deep equality check
|
|
492
|
-
| `nequals(expected)` | Deep inequality check
|
|
493
|
-
| `includes(expected)` | Inclusion check (string, array, object)
|
|
494
|
-
| `true()` | Assert value is `true`
|
|
495
|
-
| `false()` | Assert value is `false`
|
|
496
|
-
| `null()` | Assert value is `null`
|
|
497
|
-
| `undefined()` | Assert value is `undefined`
|
|
498
|
-
| `defined()` | Assert value is not `undefined`
|
|
499
|
-
| `instance(constructor)` | Assert value is instance of class
|
|
500
|
-
| `throws(expected?)` | Assert function throws
|
|
501
|
-
| `tries()` | Assert function does not throw
|
|
502
|
-
| `not` | Negate the next assertion
|
|
503
|
-
| `type<T>()` | Compile-time type assertion
|
|
504
|
-
| `nottype<T>()` | Compile-time negative type assertion
|
|
505
|
-
| `pass()` | Manually pass the assertion
|
|
506
|
-
| `fail(reason?)` | Manually fail the assertion
|
|
564
|
+
| Method | Description |
|
|
565
|
+
| ----------------------- | --------------------------------------- |
|
|
566
|
+
| `equals(expected)` | Deep equality check |
|
|
567
|
+
| `nequals(expected)` | Deep inequality check |
|
|
568
|
+
| `includes(expected)` | Inclusion check (string, array, object) |
|
|
569
|
+
| `true()` | Assert value is `true` |
|
|
570
|
+
| `false()` | Assert value is `false` |
|
|
571
|
+
| `null()` | Assert value is `null` |
|
|
572
|
+
| `undefined()` | Assert value is `undefined` |
|
|
573
|
+
| `defined()` | Assert value is not `undefined` |
|
|
574
|
+
| `instance(constructor)` | Assert value is instance of class |
|
|
575
|
+
| `throws(expected?)` | Assert function throws |
|
|
576
|
+
| `tries()` | Assert function does not throw |
|
|
577
|
+
| `not` | Negate the next assertion |
|
|
578
|
+
| `type<T>()` | Compile-time type assertion |
|
|
579
|
+
| `nottype<T>()` | Compile-time negative type assertion |
|
|
580
|
+
| `pass()` | Manually pass the assertion |
|
|
581
|
+
| `fail(reason?)` | Manually fail the assertion |
|
|
507
582
|
|
|
508
583
|
### Utilities
|
|
509
584
|
|
|
@@ -644,4 +719,3 @@ MIT
|
|
|
644
719
|
## Contributing
|
|
645
720
|
|
|
646
721
|
See [CONTRIBUTING.md](../../CONTRIBUTING.md) in the repository root.
|
|
647
|
-
|
package/lib/private/E.d.ts
CHANGED
package/lib/private/extend.d.ts
CHANGED
|
@@ -14,6 +14,6 @@ type Base = {
|
|
|
14
14
|
case(name: string, body: Body): void;
|
|
15
15
|
ended(end: () => MaybePromise<void>): void;
|
|
16
16
|
};
|
|
17
|
-
declare const _default: <Subject, Extensions>(base: Base, factory: Factory<Subject, Extensions>) => ExtendedTest<Extensions>;
|
|
18
17
|
export default _default;
|
|
18
|
+
declare function _default<Subject, Extensions>(base: Base, factory: Factory<Subject, Extensions>): ExtendedTest<Extensions>;
|
|
19
19
|
//# sourceMappingURL=extend.d.ts.map
|
package/lib/private/index.d.ts
CHANGED
|
@@ -5,13 +5,15 @@ import type Env from "#Env";
|
|
|
5
5
|
import type Result from "#Result";
|
|
6
6
|
import type Test from "#Test";
|
|
7
7
|
import type { ExtendedTest, Factory } from "#extend";
|
|
8
|
-
import mock from "#mock";
|
|
9
8
|
import import_ from "#import";
|
|
9
|
+
import mock from "#mock";
|
|
10
|
+
import spy from "#spy";
|
|
10
11
|
declare const _default: {
|
|
11
12
|
case(name: string, body: Body): void;
|
|
12
13
|
ended(end: End): void;
|
|
13
14
|
group(name: string, fn: () => void): void;
|
|
14
15
|
mock: typeof mock;
|
|
16
|
+
spy: typeof spy;
|
|
15
17
|
import: typeof import_;
|
|
16
18
|
intercept: (base_url: string, setup: (setup: {
|
|
17
19
|
get(path: string, handler: (request: Request) => unknown): void;
|
package/lib/private/index.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import extend from "#extend";
|
|
2
|
-
import
|
|
2
|
+
import import_ from "#import";
|
|
3
3
|
import intercept from "#intercept";
|
|
4
4
|
import mock from "#mock";
|
|
5
|
-
import
|
|
5
|
+
import repository from "#repository";
|
|
6
|
+
import spy from "#spy";
|
|
6
7
|
const base = {
|
|
7
8
|
case(name, body) {
|
|
8
9
|
repository.put(name, body);
|
|
@@ -14,6 +15,7 @@ const base = {
|
|
|
14
15
|
repository.group(name, fn);
|
|
15
16
|
},
|
|
16
17
|
mock,
|
|
18
|
+
spy,
|
|
17
19
|
import: import_,
|
|
18
20
|
};
|
|
19
21
|
export default {
|
|
@@ -12,6 +12,6 @@ type Intercept = {
|
|
|
12
12
|
restore(): void;
|
|
13
13
|
[Symbol.dispose](): void;
|
|
14
14
|
};
|
|
15
|
-
declare const _default: (base_url: string, setup: (setup: Setup) => void) => Intercept;
|
|
16
15
|
export default _default;
|
|
16
|
+
declare function _default(base_url: string, setup: (setup: Setup) => void): Intercept;
|
|
17
17
|
//# sourceMappingURL=intercept.d.ts.map
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
type AnyFunction = (...args: any[]) => any;
|
|
2
|
+
type Tracked<F extends AnyFunction> = F & {
|
|
3
|
+
calls: Parameters<F>[];
|
|
4
|
+
called: boolean;
|
|
5
|
+
};
|
|
6
|
+
export default function spy<F extends AnyFunction>(fn: F, mocker?: F): Tracked<F>;
|
|
7
|
+
export {};
|
|
8
|
+
//# sourceMappingURL=spy.d.ts.map
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import is from "@rcompat/is";
|
|
2
|
+
export default function spy(fn, mocker) {
|
|
3
|
+
const calls = [];
|
|
4
|
+
const callee = is.defined(mocker) ? mocker : fn;
|
|
5
|
+
const tracked = ((...args) => {
|
|
6
|
+
calls.push(args);
|
|
7
|
+
return callee(...args);
|
|
8
|
+
});
|
|
9
|
+
tracked.calls = calls;
|
|
10
|
+
Object.defineProperty(tracked, "called", {
|
|
11
|
+
get: () => calls.length > 0,
|
|
12
|
+
});
|
|
13
|
+
return tracked;
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=spy.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { UnknownMap } from "@rcompat/type";
|
|
2
|
-
|
|
2
|
+
export default _default;
|
|
3
|
+
declare function _default(map: UnknownMap): {
|
|
3
4
|
[k: string]: unknown;
|
|
4
5
|
};
|
|
5
|
-
export default _default;
|
|
6
6
|
//# sourceMappingURL=to-object.d.ts.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rcompat/test",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.19.1",
|
|
4
4
|
"description": "Standard library testing",
|
|
5
5
|
"bugs": "https://github.com/rcompat/rcompat/issues",
|
|
6
6
|
"license": "MIT",
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"@rcompat/is": "^0.12.0"
|
|
19
19
|
},
|
|
20
20
|
"devDependencies": {
|
|
21
|
-
"@rcompat/fs": "^0.35.
|
|
21
|
+
"@rcompat/fs": "^0.35.1",
|
|
22
22
|
"@rcompat/type": "^0.18.0"
|
|
23
23
|
},
|
|
24
24
|
"type": "module",
|