@rcompat/test 0.17.0 → 0.19.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 +124 -46
- package/lib/private/Assert.d.ts +1 -1
- package/lib/private/index.d.ts +8 -6
- package/lib/private/index.js +4 -2
- package/lib/private/spy.d.ts +8 -0
- package/lib/private/spy.js +15 -0
- package/package.json +7 -6
package/README.md
CHANGED
|
@@ -278,6 +278,62 @@ 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 spy from "@rcompat/spy";
|
|
288
|
+
|
|
289
|
+
const add = (a: number, b: number) => a + b;
|
|
290
|
+
const tracked = 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
|
+
import spy from "@rcompat/spy";
|
|
305
|
+
|
|
306
|
+
test.case("tracks calls", assert => {
|
|
307
|
+
const tracked = spy((a: number, b: number) => a + b);
|
|
308
|
+
|
|
309
|
+
assert(tracked.called).false();
|
|
310
|
+
assert(tracked.calls).equals([]);
|
|
311
|
+
|
|
312
|
+
tracked(1, 2);
|
|
313
|
+
|
|
314
|
+
assert(tracked.called).true();
|
|
315
|
+
assert(tracked.calls).equals([[1, 2]]);
|
|
316
|
+
assert(tracked(3, 4)).equals(7);
|
|
317
|
+
});
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
Pass a second argument to replace the implementation while still tracking calls:
|
|
321
|
+
|
|
322
|
+
```ts
|
|
323
|
+
import test from "@rcompat/test";
|
|
324
|
+
import spy from "@rcompat/spy";
|
|
325
|
+
|
|
326
|
+
test.case("mocks implementation", assert => {
|
|
327
|
+
const tracked = spy(
|
|
328
|
+
(a: number, b: number) => a + b,
|
|
329
|
+
(a: number, b: number) => a * b,
|
|
330
|
+
);
|
|
331
|
+
|
|
332
|
+
assert(tracked(2, 3)).equals(6); // mocker runs, not original
|
|
333
|
+
assert(tracked.calls).equals([[2, 3]]);
|
|
334
|
+
});
|
|
335
|
+
```
|
|
336
|
+
|
|
281
337
|
### Intercepting fetch
|
|
282
338
|
|
|
283
339
|
Use `test.intercept` to block outbound fetch calls to a specific origin and
|
|
@@ -392,9 +448,9 @@ test.group(name: string, fn: () => void): void;
|
|
|
392
448
|
Group test cases under a named scope. Groups can be targeted individually
|
|
393
449
|
when running proby.
|
|
394
450
|
|
|
395
|
-
| Parameter | Type | Description
|
|
396
|
-
| --------- | ---------- |
|
|
397
|
-
| `name` | `string` | Group name, used by proby to filter
|
|
451
|
+
| Parameter | Type | Description |
|
|
452
|
+
| --------- | ---------- | ------------------------------------- |
|
|
453
|
+
| `name` | `string` | Group name, used by proby to filter |
|
|
398
454
|
| `fn` | `function` | Function containing `test.case` calls |
|
|
399
455
|
|
|
400
456
|
### `test.mock`
|
|
@@ -409,10 +465,10 @@ test.mock<T extends object>(
|
|
|
409
465
|
Register a module mock and return a handle to the tracked mocked exports.
|
|
410
466
|
Function exports are wrapped so you can inspect `calls` and `called`.
|
|
411
467
|
|
|
412
|
-
| Parameter | Type | Description
|
|
413
|
-
| ----------- | ---------- |
|
|
414
|
-
| `specifier` | `string` | Module specifier to mock
|
|
415
|
-
| `factory` | `function` | Returns the mocked exports for that module
|
|
468
|
+
| Parameter | Type | Description |
|
|
469
|
+
| ----------- | ---------- | ------------------------------------------ |
|
|
470
|
+
| `specifier` | `string` | Module specifier to mock |
|
|
471
|
+
| `factory` | `function` | Returns the mocked exports for that module |
|
|
416
472
|
|
|
417
473
|
### `test.import`
|
|
418
474
|
|
|
@@ -434,10 +490,10 @@ test.intercept(
|
|
|
434
490
|
Intercept outbound fetch calls to `base_url`. Returns an `Intercept` object
|
|
435
491
|
for asserting on recorded requests.
|
|
436
492
|
|
|
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
|
|
493
|
+
| Parameter | Type | Description |
|
|
494
|
+
| ---------- | ---------- | ------------------------------------------------------ |
|
|
495
|
+
| `base_url` | `string` | Origin to intercept, e.g. `"https://api.example.com"` |
|
|
496
|
+
| `setup` | `function` | Register route handlers on the setup object |
|
|
441
497
|
|
|
442
498
|
### `test.extend`
|
|
443
499
|
|
|
@@ -450,31 +506,54 @@ test.extend<Subject, Extensions>(
|
|
|
450
506
|
Create a new test object with custom assertion methods mixed into the
|
|
451
507
|
asserter.
|
|
452
508
|
|
|
453
|
-
| Parameter | Type | Description
|
|
454
|
-
| --------- | ---------- |
|
|
455
|
-
| `factory` | `function` | Returns extra methods to attach to each `Assert` instance
|
|
509
|
+
| Parameter | Type | Description |
|
|
510
|
+
| --------- | ---------- | --------------------------------------------------------- |
|
|
511
|
+
| `factory` | `function` | Returns extra methods to attach to each `Assert` instance |
|
|
512
|
+
|
|
513
|
+
### `spy`
|
|
514
|
+
|
|
515
|
+
```ts
|
|
516
|
+
import spy from "@rcompat/spy";
|
|
517
|
+
|
|
518
|
+
spy<F extends (...args: any[]) => any>(fn: F, mocker?: F): Tracked<F>;
|
|
519
|
+
```
|
|
520
|
+
|
|
521
|
+
Wrap a function to track calls. Returns the wrapped function with two extra
|
|
522
|
+
properties.
|
|
523
|
+
|
|
524
|
+
| Parameter | Type | Description |
|
|
525
|
+
| --------- | ---- | ------------------------------------ |
|
|
526
|
+
| `fn` | `F` | The function to wrap |
|
|
527
|
+
| `mocker` | `F` | Optional replacement implementation |
|
|
528
|
+
|
|
529
|
+
#### `Tracked<F>`
|
|
530
|
+
|
|
531
|
+
| Property | Type | Description |
|
|
532
|
+
| -------- | ----------------- | -------------------------------------- |
|
|
533
|
+
| `calls` | `Parameters<F>[]` | Array of argument tuples, one per call |
|
|
534
|
+
| `called` | `boolean` | `true` if the function has been called |
|
|
456
535
|
|
|
457
536
|
#### `Setup`
|
|
458
537
|
|
|
459
|
-
| Method
|
|
460
|
-
|
|
|
461
|
-
| `get(path, handler)`
|
|
462
|
-
| `post(path, handler)`
|
|
463
|
-
| `put(path, handler)`
|
|
464
|
-
| `patch(path, handler)`
|
|
465
|
-
| `delete(path, handler)
|
|
538
|
+
| Method | Description |
|
|
539
|
+
| ----------------------- | ------------------------- |
|
|
540
|
+
| `get(path, handler)` | Register a GET handler |
|
|
541
|
+
| `post(path, handler)` | Register a POST handler |
|
|
542
|
+
| `put(path, handler)` | Register a PUT handler |
|
|
543
|
+
| `patch(path, handler)` | Register a PATCH handler |
|
|
544
|
+
| `delete(path, handler)` | Register a DELETE handler |
|
|
466
545
|
|
|
467
546
|
Each handler receives the incoming `Request` and returns a plain object,
|
|
468
547
|
which is serialized into a `Response` automatically.
|
|
469
548
|
|
|
470
549
|
#### `Intercept`
|
|
471
550
|
|
|
472
|
-
| Method
|
|
473
|
-
|
|
|
474
|
-
| `calls(path)`
|
|
475
|
-
| `requests(path)`
|
|
476
|
-
| `restore()`
|
|
477
|
-
| `[Symbol.dispose]`
|
|
551
|
+
| Method | Description |
|
|
552
|
+
| ------------------ | ---------------------------------------------- |
|
|
553
|
+
| `calls(path)` | Number of times `path` was hit |
|
|
554
|
+
| `requests(path)` | Array of `Request` objects recorded for `path` |
|
|
555
|
+
| `restore()` | Reinstate the original `globalThis.fetch` |
|
|
556
|
+
| `[Symbol.dispose]` | Called automatically by `using` |
|
|
478
557
|
|
|
479
558
|
### `Asserter`
|
|
480
559
|
|
|
@@ -486,24 +565,24 @@ The assert function passed to test cases.
|
|
|
486
565
|
|
|
487
566
|
### `Assert<T>`
|
|
488
567
|
|
|
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
|
|
568
|
+
| Method | Description |
|
|
569
|
+
| ----------------------- | --------------------------------------- |
|
|
570
|
+
| `equals(expected)` | Deep equality check |
|
|
571
|
+
| `nequals(expected)` | Deep inequality check |
|
|
572
|
+
| `includes(expected)` | Inclusion check (string, array, object) |
|
|
573
|
+
| `true()` | Assert value is `true` |
|
|
574
|
+
| `false()` | Assert value is `false` |
|
|
575
|
+
| `null()` | Assert value is `null` |
|
|
576
|
+
| `undefined()` | Assert value is `undefined` |
|
|
577
|
+
| `defined()` | Assert value is not `undefined` |
|
|
578
|
+
| `instance(constructor)` | Assert value is instance of class |
|
|
579
|
+
| `throws(expected?)` | Assert function throws |
|
|
580
|
+
| `tries()` | Assert function does not throw |
|
|
581
|
+
| `not` | Negate the next assertion |
|
|
582
|
+
| `type<T>()` | Compile-time type assertion |
|
|
583
|
+
| `nottype<T>()` | Compile-time negative type assertion |
|
|
584
|
+
| `pass()` | Manually pass the assertion |
|
|
585
|
+
| `fail(reason?)` | Manually fail the assertion |
|
|
507
586
|
|
|
508
587
|
### Utilities
|
|
509
588
|
|
|
@@ -644,4 +723,3 @@ MIT
|
|
|
644
723
|
## Contributing
|
|
645
724
|
|
|
646
725
|
See [CONTRIBUTING.md](../../CONTRIBUTING.md) in the repository root.
|
|
647
|
-
|
package/lib/private/Assert.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type Test from "#Test";
|
|
2
2
|
import type { Not, Print, UnknownFunction } from "@rcompat/type";
|
|
3
|
-
type Equals<X, Y> = (<T>() => T extends X ? true : false) extends <T>() => T extends Y ? true : false ? true : false;
|
|
3
|
+
type Equals<X, Y> = (<T>() => T extends X ? true : false) extends <T>() => (T extends Y ? true : false) ? true : false;
|
|
4
4
|
export default class Assert<const Actual> {
|
|
5
5
|
#private;
|
|
6
6
|
constructor(test: Test, actual?: Actual);
|
package/lib/private/index.d.ts
CHANGED
|
@@ -5,9 +5,16 @@ 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: {
|
|
12
|
+
case(name: string, body: Body): void;
|
|
13
|
+
ended(end: End): void;
|
|
14
|
+
group(name: string, fn: () => void): void;
|
|
15
|
+
mock: typeof mock;
|
|
16
|
+
spy: typeof spy;
|
|
17
|
+
import: typeof import_;
|
|
11
18
|
intercept: (base_url: string, setup: (setup: {
|
|
12
19
|
get(path: string, handler: (request: Request) => unknown): void;
|
|
13
20
|
post(path: string, handler: (request: Request) => unknown): void;
|
|
@@ -21,11 +28,6 @@ declare const _default: {
|
|
|
21
28
|
[Symbol.dispose](): void;
|
|
22
29
|
};
|
|
23
30
|
extend<Subject, Extensions>(factory: Factory<Subject, Extensions>): ExtendedTest<Extensions>;
|
|
24
|
-
case(name: string, body: Body): void;
|
|
25
|
-
ended(end: End): void;
|
|
26
|
-
group(name: string, fn: () => void): void;
|
|
27
|
-
mock: typeof mock;
|
|
28
|
-
import: typeof import_;
|
|
29
31
|
};
|
|
30
32
|
export default _default;
|
|
31
33
|
export type { Asserter, Env, ExtendedTest, Result, Test };
|
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 {
|
|
@@ -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
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rcompat/test",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.19.0",
|
|
4
4
|
"description": "Standard library testing",
|
|
5
5
|
"bugs": "https://github.com/rcompat/rcompat/issues",
|
|
6
6
|
"license": "MIT",
|
|
@@ -15,11 +15,11 @@
|
|
|
15
15
|
"directory": "packages/test"
|
|
16
16
|
},
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"@rcompat/is": "^0.
|
|
18
|
+
"@rcompat/is": "^0.12.0"
|
|
19
19
|
},
|
|
20
20
|
"devDependencies": {
|
|
21
|
-
"@rcompat/fs": "^0.
|
|
22
|
-
"@rcompat/type": "^0.
|
|
21
|
+
"@rcompat/fs": "^0.35.1",
|
|
22
|
+
"@rcompat/type": "^0.18.0"
|
|
23
23
|
},
|
|
24
24
|
"type": "module",
|
|
25
25
|
"imports": {
|
|
@@ -51,8 +51,9 @@
|
|
|
51
51
|
}
|
|
52
52
|
},
|
|
53
53
|
"scripts": {
|
|
54
|
-
"build": "npm run clean &&
|
|
54
|
+
"build": "npm run clean && tsgo",
|
|
55
55
|
"test": "npx proby",
|
|
56
|
-
"clean": "rm -rf ./lib"
|
|
56
|
+
"clean": "rm -rf ./lib",
|
|
57
|
+
"lint": "eslint ."
|
|
57
58
|
}
|
|
58
59
|
}
|