@uniflowed/test 0.0.0-alpha.13 → 0.0.0-alpha.14
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/internal/expect.js +66 -42
- package/package.json +2 -2
- package/worker.js +24 -0
package/internal/expect.js
CHANGED
|
@@ -289,29 +289,36 @@ function matchesThrown(thrown: mixed, expected: mixed): boolean {
|
|
|
289
289
|
* Every entry returns a [`Verdict`] rather than throwing, which is what lets
|
|
290
290
|
* `.not` reuse all of them.
|
|
291
291
|
*
|
|
292
|
-
* #
|
|
292
|
+
* # Every entry takes `mixed`, and that is what makes the indexer sayable
|
|
293
293
|
*
|
|
294
|
-
*
|
|
295
|
-
*
|
|
296
|
-
*
|
|
297
|
-
*
|
|
298
|
-
*
|
|
299
|
-
*
|
|
300
|
-
*
|
|
294
|
+
* [`bind`] reaches an entry by a computed key and applies it to the
|
|
295
|
+
* `$ReadOnlyArray<mixed>` it collected from the caller, so the indexer has to
|
|
296
|
+
* describe a function that will accept those arguments. Parameters are
|
|
297
|
+
* contravariant, so an entry that demanded a `number` could not be described
|
|
298
|
+
* by one — `(...args: $ReadOnlyArray<mixed>)` rejects it, and
|
|
299
|
+
* `(...args: $ReadOnlyArray<empty>)` accepts it and rejects the call. That
|
|
300
|
+
* disagreement is why this indexer used to be written `$ReadOnlyArray<any>`,
|
|
301
|
+
* with a `flow/unclear-type` suppression on it.
|
|
301
302
|
*
|
|
302
|
-
*
|
|
303
|
-
*
|
|
304
|
-
*
|
|
305
|
-
*
|
|
306
|
-
*
|
|
307
|
-
*
|
|
308
|
-
*
|
|
309
|
-
*
|
|
310
|
-
*
|
|
303
|
+
* So the disagreement is gone instead of papered over: every entry here takes
|
|
304
|
+
* `mixed` and coerces what it needs, the way most of them — `toBe`,
|
|
305
|
+
* `toBeGreaterThan`, `toHaveAttribute` — already did. Nothing a caller can see
|
|
306
|
+
* got wider: `toHaveLength` still refuses a string and `toBeTypeOf` still
|
|
307
|
+
* refuses a word `typeof` never says, because those are [`Matchers`]'s
|
|
308
|
+
* signatures and [`Matchers`] is the published type. What changed is that the
|
|
309
|
+
* table behind them stopped claiming a narrower argument than the one `bind`
|
|
310
|
+
* can hand it, which is a claim that was never true. The `String(…)` and
|
|
311
|
+
* `Number(…)` calls that appeared with it are the coercion the runtime was
|
|
312
|
+
* already doing, said out loud, and each produces the same message the implicit
|
|
313
|
+
* one did for the same input.
|
|
314
|
+
*
|
|
315
|
+
* The alternative — narrowing the indexer by writing `bind`'s forty-one
|
|
316
|
+
* wrappers out to avoid the computed lookup — is a second copy of the listing
|
|
317
|
+
* to keep in step with [`Matchers`], and is a worse trade than either. See
|
|
318
|
+
* ubugeeei-prod/uf#402.
|
|
311
319
|
*/
|
|
312
320
|
function verdicts(received: mixed): {
|
|
313
|
-
|
|
314
|
-
readonly [string]: (...args: $ReadOnlyArray<any>) => Verdict,
|
|
321
|
+
readonly [string]: (...args: $ReadOnlyArray<mixed>) => Verdict,
|
|
315
322
|
} {
|
|
316
323
|
const shown = () => render(received);
|
|
317
324
|
const simple = (pass: boolean, what: string, expected?: mixed): Verdict => ({
|
|
@@ -386,14 +393,15 @@ function verdicts(received: mixed): {
|
|
|
386
393
|
`to be at most ${render(expected)}`,
|
|
387
394
|
expected,
|
|
388
395
|
),
|
|
389
|
-
toBeCloseTo: (expected:
|
|
390
|
-
const
|
|
396
|
+
toBeCloseTo: (expected: mixed, digits?: mixed) => {
|
|
397
|
+
const target = Number(expected);
|
|
398
|
+
const places = digits === undefined ? 2 : Number(digits);
|
|
391
399
|
const tolerance = 10 ** -places / 2;
|
|
392
|
-
const difference = Math.abs((received as $FlowFixMe) -
|
|
400
|
+
const difference = Math.abs((received as $FlowFixMe) - target);
|
|
393
401
|
return simple(
|
|
394
402
|
difference < tolerance,
|
|
395
|
-
`to be within ${tolerance} of ${
|
|
396
|
-
|
|
403
|
+
`to be within ${tolerance} of ${target}, but it is off by ${difference}`,
|
|
404
|
+
target,
|
|
397
405
|
);
|
|
398
406
|
},
|
|
399
407
|
toContain: (expected: mixed) => {
|
|
@@ -419,22 +427,23 @@ function verdicts(received: mixed): {
|
|
|
419
427
|
expected,
|
|
420
428
|
);
|
|
421
429
|
},
|
|
422
|
-
toHaveLength: (expected:
|
|
430
|
+
toHaveLength: (expected: mixed) => {
|
|
423
431
|
const length = received == null ? undefined : (received as $FlowFixMe).length;
|
|
424
432
|
return simple(
|
|
425
433
|
length === expected,
|
|
426
|
-
`to have length ${expected}, not ${render(length)}`,
|
|
434
|
+
`to have length ${String(expected)}, not ${render(length)}`,
|
|
427
435
|
expected,
|
|
428
436
|
);
|
|
429
437
|
},
|
|
430
|
-
toHaveProperty: (path:
|
|
431
|
-
const
|
|
438
|
+
toHaveProperty: (path: mixed, ...rest: $ReadOnlyArray<mixed>) => {
|
|
439
|
+
const at = String(path);
|
|
440
|
+
const found = propertyAt(received, at);
|
|
432
441
|
if (rest.length === 0) {
|
|
433
|
-
return simple(found.found, `to have a property at \`${
|
|
442
|
+
return simple(found.found, `to have a property at \`${at}\``);
|
|
434
443
|
}
|
|
435
444
|
return simple(
|
|
436
445
|
found.found && equals(found.value, rest[0]),
|
|
437
|
-
`to have \`${
|
|
446
|
+
`to have \`${at}\` equal to ${render(rest[0])}, not ${render(found.value)}`,
|
|
438
447
|
rest[0],
|
|
439
448
|
);
|
|
440
449
|
},
|
|
@@ -454,16 +463,24 @@ function verdicts(received: mixed): {
|
|
|
454
463
|
`to be an instance of ${render(expected)}`,
|
|
455
464
|
expected,
|
|
456
465
|
),
|
|
457
|
-
toBeTypeOf: (expected:
|
|
466
|
+
toBeTypeOf: (expected: mixed) => {
|
|
467
|
+
const name = String(expected);
|
|
468
|
+
return simple(
|
|
469
|
+
typeof received === name,
|
|
470
|
+
`to be of type ${name}, not ${typeof received}`,
|
|
471
|
+
name,
|
|
472
|
+
);
|
|
473
|
+
},
|
|
474
|
+
toSatisfy: (predicate: mixed) =>
|
|
458
475
|
simple(
|
|
459
|
-
typeof received ===
|
|
460
|
-
|
|
461
|
-
expected,
|
|
476
|
+
typeof predicate === "function" && predicate(received) === true,
|
|
477
|
+
"to satisfy the predicate",
|
|
462
478
|
),
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
479
|
+
toMatchSnapshot: (hint?: mixed): Verdict => {
|
|
480
|
+
const verdict = snapshot.matchSnapshot(
|
|
481
|
+
received,
|
|
482
|
+
hint === undefined ? undefined : String(hint),
|
|
483
|
+
);
|
|
467
484
|
return {
|
|
468
485
|
pass: verdict.pass,
|
|
469
486
|
expected: verdict.expected ?? "(no snapshot yet)",
|
|
@@ -477,8 +494,11 @@ function verdicts(received: mixed): {
|
|
|
477
494
|
negatedFailure: () => "expected the value not to match its snapshot",
|
|
478
495
|
};
|
|
479
496
|
},
|
|
480
|
-
toMatchInlineSnapshot: (expected?:
|
|
481
|
-
const verdict = snapshot.matchInlineSnapshot(
|
|
497
|
+
toMatchInlineSnapshot: (expected?: mixed): Verdict => {
|
|
498
|
+
const verdict = snapshot.matchInlineSnapshot(
|
|
499
|
+
received,
|
|
500
|
+
expected === undefined ? undefined : String(expected),
|
|
501
|
+
);
|
|
482
502
|
return {
|
|
483
503
|
pass: verdict.pass,
|
|
484
504
|
expected: verdict.expected ?? "(no inline snapshot yet)",
|
|
@@ -529,10 +549,14 @@ function verdicts(received: mixed): {
|
|
|
529
549
|
requireSpy("toHaveBeenCalled");
|
|
530
550
|
return simple(spyCalls().length > 0, "to have been called");
|
|
531
551
|
},
|
|
532
|
-
toHaveBeenCalledTimes: (count:
|
|
552
|
+
toHaveBeenCalledTimes: (count: mixed) => {
|
|
533
553
|
requireSpy("toHaveBeenCalledTimes");
|
|
534
554
|
const actual = spyCalls().length;
|
|
535
|
-
return simple(
|
|
555
|
+
return simple(
|
|
556
|
+
actual === count,
|
|
557
|
+
`to have been called ${String(count)} times, not ${actual}`,
|
|
558
|
+
count,
|
|
559
|
+
);
|
|
536
560
|
},
|
|
537
561
|
toHaveBeenCalledWith: (...args: $ReadOnlyArray<mixed>) => {
|
|
538
562
|
requireSpy("toHaveBeenCalledWith");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uniflowed/test",
|
|
3
|
-
"version": "0.0.0-alpha.
|
|
3
|
+
"version": "0.0.0-alpha.14",
|
|
4
4
|
"description": "The test API and worker for `uf test`: describe/it, a full matcher set, and the process uf fans test files out to.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -21,6 +21,6 @@
|
|
|
21
21
|
"internal"
|
|
22
22
|
],
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@uniflowed/host": "0.0.0-alpha.
|
|
24
|
+
"@uniflowed/host": "0.0.0-alpha.14"
|
|
25
25
|
}
|
|
26
26
|
}
|
package/worker.js
CHANGED
|
@@ -57,6 +57,14 @@ import { reset } from "./internal/registry.js";
|
|
|
57
57
|
import { resetModuleState } from "./internal/modules.js";
|
|
58
58
|
import { run } from "./internal/run.js";
|
|
59
59
|
import { unstubAllEnvs, unstubAllGlobals } from "./internal/namespace.js";
|
|
60
|
+
// Renamed at the door, for two reasons that agree. It reads as the resets
|
|
61
|
+
// beside it do — `reset`, `unstubAllEnvs`, `resetModuleState` are all
|
|
62
|
+
// verb-first, and so is what this does to the clock. And `useRealTimers` is
|
|
63
|
+
// not a React hook: it is uf's own timer control, which happens to be named
|
|
64
|
+
// the way every runner names it, and calling it bare in a plain function is
|
|
65
|
+
// a `react/hooks-rules` error on the name alone. A suppression would assert
|
|
66
|
+
// something about this call; the name is simply accurate.
|
|
67
|
+
import { useRealTimers as restoreRealClock } from "./internal/timers.js";
|
|
60
68
|
|
|
61
69
|
/** What `uf` sends for one file. */
|
|
62
70
|
type Request = {|
|
|
@@ -151,6 +159,22 @@ async function runFile(request: Request, generation: number): Promise<void> {
|
|
|
151
159
|
// wrote it. See ubugeeei-prod/uf#417.
|
|
152
160
|
unstubAllEnvs();
|
|
153
161
|
unstubAllGlobals();
|
|
162
|
+
// And the clock goes back, whatever the previous file did with it. A spy
|
|
163
|
+
// lives in the registry `reset` clears; a fake clock is a write to the
|
|
164
|
+
// scheduling globals the whole process shares, so `uft.useFakeTimers()` in
|
|
165
|
+
// one file is still installed when the next one imports.
|
|
166
|
+
//
|
|
167
|
+
// Worse than a leaked value, and worse in a way that hides it. A leaked stub
|
|
168
|
+
// makes the next file read something wrong, which arrives as an assertion
|
|
169
|
+
// naming the value. A leaked clock makes the next file's `setTimeout` never
|
|
170
|
+
// fire — including the one `withTimeout` races each case against — so the
|
|
171
|
+
// file hangs with nothing on screen until `uf`'s own deadline kills the
|
|
172
|
+
// worker, and the report names the file that waited rather than the file
|
|
173
|
+
// that stopped time. See ubugeeei-prod/uf#581.
|
|
174
|
+
//
|
|
175
|
+
// Before the import rather than after the run, so a file that throws while
|
|
176
|
+
// loading still hands the next one a real clock.
|
|
177
|
+
restoreRealClock();
|
|
154
178
|
// Every module this file stood in for goes back, before the next file can
|
|
155
179
|
// import one of them and be handed the previous file's stand-in. A worker
|
|
156
180
|
// serves many files out of one module registry, so this is the difference
|