@uniflowed/test 0.0.0-alpha.13 → 0.0.0-alpha.15
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/internal/isolation.js +200 -0
- package/package.json +2 -2
- package/worker.js +16 -19
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");
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
// @flow
|
|
2
|
+
//
|
|
3
|
+
// Internal to `@uniflowed/test`: what a file is allowed to leave behind.
|
|
4
|
+
//
|
|
5
|
+
// A worker serves many files out of one process, one at a time (`../worker.js`
|
|
6
|
+
// says why). Everything a file registers lives in this package and is cleared
|
|
7
|
+
// with it; everything a file *reaches around* the package to change belongs to
|
|
8
|
+
// the process, outlives the file, and is handed to whichever file the schedule
|
|
9
|
+
// puts next in that worker.
|
|
10
|
+
//
|
|
11
|
+
// That second list is what this module is. It has been discovered three times,
|
|
12
|
+
// once per entry, and each time the same way — a suite that passed alone and
|
|
13
|
+
// failed beside another, naming the file that read the value rather than the
|
|
14
|
+
// file that wrote it:
|
|
15
|
+
//
|
|
16
|
+
// * ubugeeei-prod/uf#417, `uft.stubEnv("NODE_ENV", …)` still set for the
|
|
17
|
+
// next file;
|
|
18
|
+
// * ubugeeei-prod/uf#581, a fake clock still installed, so the next file's
|
|
19
|
+
// `setTimeout` — including the one each case is raced against — never
|
|
20
|
+
// fired and the file hung with nothing on screen;
|
|
21
|
+
// * ubugeeei-prod/uf#607, `document.body` still holding the markup a
|
|
22
|
+
// hydration test wrote into it, so the next file's "there is one image on
|
|
23
|
+
// the page" found six.
|
|
24
|
+
//
|
|
25
|
+
// Which files share a worker is decided by `.uf/test-timings.json`, so a leak
|
|
26
|
+
// makes the *result* of a suite depend on how the machine was loaded the last
|
|
27
|
+
// time it ran. That is the failure this module exists to end, and the reason
|
|
28
|
+
// it is a list in one place rather than three calls in `../worker.js`: the
|
|
29
|
+
// question "what else does a file share with the next one" now has somewhere
|
|
30
|
+
// to be answered, and a fourth answer is one entry rather than one more thing
|
|
31
|
+
// to remember.
|
|
32
|
+
//
|
|
33
|
+
// # What belongs here
|
|
34
|
+
//
|
|
35
|
+
// State that (a) the process shares, (b) a test can change from inside a file,
|
|
36
|
+
// and (c) nothing else puts back. Anything a file merely *reads* does not
|
|
37
|
+
// belong here, and neither does anything the registry already clears — a spy
|
|
38
|
+
// is not on this list because `reset()` is what a spy lives in.
|
|
39
|
+
//
|
|
40
|
+
// # When it runs
|
|
41
|
+
//
|
|
42
|
+
// Before the next file is imported, not after the previous one is run. A file
|
|
43
|
+
// that throws while loading is still a file that has run code, and it still
|
|
44
|
+
// hands the next one whatever that code changed; putting things back on the
|
|
45
|
+
// way *in* covers the load failure and the crash as well as the ordinary end.
|
|
46
|
+
// It also means the first file in a worker starts from the same state as the
|
|
47
|
+
// tenth.
|
|
48
|
+
|
|
49
|
+
import { reset } from "./registry.js";
|
|
50
|
+
import { resetModuleState } from "./modules.js";
|
|
51
|
+
import { unstubAllEnvs, unstubAllGlobals } from "./namespace.js";
|
|
52
|
+
// Renamed at the door, for two reasons that agree. It reads as the resets
|
|
53
|
+
// beside it do — `reset`, `unstubAllEnvs`, `resetModuleState` are all
|
|
54
|
+
// verb-first, and so is what this does to the clock. And `useRealTimers` is
|
|
55
|
+
// not a React hook: it is uf's own timer control, which happens to be named
|
|
56
|
+
// the way every runner names it, and calling it bare in a plain function is a
|
|
57
|
+
// `react/hooks-rules` error on the name alone. A suppression would assert
|
|
58
|
+
// something about this call; the name is simply accurate.
|
|
59
|
+
import { useRealTimers as restoreRealClock } from "./timers.js";
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* One piece of process-wide state a file can change, and how to put it back.
|
|
63
|
+
*
|
|
64
|
+
* `what` is written for a person reading this list rather than for any code:
|
|
65
|
+
* nothing branches on it, and it is here because a list of five bare function
|
|
66
|
+
* references is a list nobody can check against the paragraph above it.
|
|
67
|
+
*/
|
|
68
|
+
type Shared = {|
|
|
69
|
+
readonly what: string,
|
|
70
|
+
readonly restore: () => void,
|
|
71
|
+
|};
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Everything a file shares with the file after it, in the order it goes back.
|
|
75
|
+
*
|
|
76
|
+
* The order matters in one place and is harmless everywhere else: the clock
|
|
77
|
+
* goes back before the module stand-ins do, because a stand-in's factory runs
|
|
78
|
+
* on the next import and a factory that schedules anything under a leaked fake
|
|
79
|
+
* clock would schedule it into a clock nobody is going to advance.
|
|
80
|
+
*/
|
|
81
|
+
const SHARED: $ReadOnlyArray<Shared> = [
|
|
82
|
+
{
|
|
83
|
+
what: "the tests, hooks and spies this package registered",
|
|
84
|
+
restore: reset,
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
// `process.env` belongs to the process. `uft.stubEnv("NODE_ENV",
|
|
88
|
+
// "production")` in one file is still set when the next one imports, and
|
|
89
|
+
// the file that fails is the one that read it.
|
|
90
|
+
what: "environment variables `uft.stubEnv` replaced",
|
|
91
|
+
restore: unstubAllEnvs,
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
// `globalThis` likewise, and worse: a stubbed `fetch` makes the next file
|
|
95
|
+
// talk to a stand-in that does not know about it.
|
|
96
|
+
what: "globals `uft.stubGlobal` replaced",
|
|
97
|
+
restore: unstubAllGlobals,
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
// Worse than a leaked value, and worse in a way that hides it. A leaked
|
|
101
|
+
// stub makes the next file read something wrong, which arrives as an
|
|
102
|
+
// assertion naming the value. A leaked clock makes the next file's
|
|
103
|
+
// `setTimeout` never fire — including the one `withTimeout` races each
|
|
104
|
+
// case against — so the file hangs with nothing on screen until `uf`'s own
|
|
105
|
+
// deadline kills the worker, and the report names the file that waited
|
|
106
|
+
// rather than the file that stopped time.
|
|
107
|
+
what: "the clock, whatever `uft.useFakeTimers` did to it",
|
|
108
|
+
restore: restoreRealClock,
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
// A worker serves many files out of one module registry, so this is the
|
|
112
|
+
// difference between "one file at a time" and "one file's mocks at a
|
|
113
|
+
// time".
|
|
114
|
+
what: "modules `uft.mock` stood in for",
|
|
115
|
+
restore: resetModuleState,
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
what: "the document, if this process has one",
|
|
119
|
+
restore: restoreDocument,
|
|
120
|
+
},
|
|
121
|
+
];
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Hand the next file a document nobody else has written to.
|
|
125
|
+
*
|
|
126
|
+
* The document is process-wide in a way that is easy to miss, because nothing
|
|
127
|
+
* in this package installs it: `@uniflowed/react-testing` puts one on the
|
|
128
|
+
* global object the first time a test renders and deliberately keeps it for
|
|
129
|
+
* the life of the process — replacing it would strand every React root already
|
|
130
|
+
* mounted in the old one. So one document serves every file a worker runs, and
|
|
131
|
+
* what a file leaves in it is what the next file queries.
|
|
132
|
+
*
|
|
133
|
+
* Its *contents* are put back rather than the document itself, and "back"
|
|
134
|
+
* means empty: a file is handed the body it would have had if it had installed
|
|
135
|
+
* the document itself. `cleanup()` already unmounts what `render` mounted, and
|
|
136
|
+
* that is not the leak — the leak is markup a test wrote into the body by
|
|
137
|
+
* hand, which a hydration test must do because hydration is React attaching to
|
|
138
|
+
* markup that is already there. `rsc-split.test.js` and `streaming.test.js`
|
|
139
|
+
* both `replaceChildren` into the body, and the file after them in that worker
|
|
140
|
+
* started with somebody else's page.
|
|
141
|
+
*
|
|
142
|
+
* Read through `globalThis` and guarded by `typeof`, because a worker running
|
|
143
|
+
* a suite that never renders has no `document` at all and must not pay for
|
|
144
|
+
* one — and because on a host that *is* a browser this is the page, whose body
|
|
145
|
+
* a run of `uf test` has no business emptying. It only ever clears a body that
|
|
146
|
+
* a test process is using as scratch space, which is every case this can
|
|
147
|
+
* reach: the shim's document, or a page the project chose to run its own tests
|
|
148
|
+
* in.
|
|
149
|
+
*/
|
|
150
|
+
function restoreDocument(): void {
|
|
151
|
+
if (typeof globalThis.document === "undefined") {
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
const body = globalThis.document.body;
|
|
155
|
+
if (body == null) {
|
|
156
|
+
// A document a parser produced need not have one, and a document with no
|
|
157
|
+
// body is a document with nothing to put back.
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
body.replaceChildren();
|
|
161
|
+
// The attributes too, and not for tidiness: `<body class="dark">` is how a
|
|
162
|
+
// theme test says what it is testing, and a file that leaves one behind
|
|
163
|
+
// makes the next file's "the page is in light mode" false for a reason it
|
|
164
|
+
// cannot see. Read into an array first — removing an attribute while
|
|
165
|
+
// iterating a live list is the loop that skips every other entry.
|
|
166
|
+
for (const name of [...body.getAttributeNames()]) {
|
|
167
|
+
body.removeAttribute(name);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Put back everything the file that just ran may have changed.
|
|
173
|
+
*
|
|
174
|
+
* Called by `../worker.js` before it imports the next file. Nothing here
|
|
175
|
+
* reports a file for having changed any of this: a file is *allowed* to — that
|
|
176
|
+
* is what the `uft` namespace is for — and the contract is that the change
|
|
177
|
+
* does not outlive the file, not that it never happened.
|
|
178
|
+
*
|
|
179
|
+
* Every entry is attempted even after one has thrown, and the first failure is
|
|
180
|
+
* raised afterwards under the name of what it could not put back. Stopping at
|
|
181
|
+
* the first would leave the four entries behind it un-restored, which is the
|
|
182
|
+
* defect this module exists to prevent arriving by a new route — and a bare
|
|
183
|
+
* throw from one of these used to say only that something in the runner failed
|
|
184
|
+
* between two files.
|
|
185
|
+
*/
|
|
186
|
+
export function restoreSharedState(): void {
|
|
187
|
+
let failure: { readonly what: string, readonly thrown: mixed } | null = null;
|
|
188
|
+
for (const shared of SHARED) {
|
|
189
|
+
try {
|
|
190
|
+
shared.restore();
|
|
191
|
+
} catch (thrown) {
|
|
192
|
+
failure ??= { what: shared.what, thrown };
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
if (failure != null) {
|
|
196
|
+
const cause = failure.thrown;
|
|
197
|
+
const message = cause instanceof Error ? cause.message : String(cause);
|
|
198
|
+
throw new Error(`@uniflowed/test could not put back ${failure.what}: ${message}`);
|
|
199
|
+
}
|
|
200
|
+
}
|
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.15",
|
|
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.15"
|
|
25
25
|
}
|
|
26
26
|
}
|
package/worker.js
CHANGED
|
@@ -53,10 +53,8 @@ import { writeChangedSnapshots } from "./internal/snapshot.js";
|
|
|
53
53
|
import { createInterface } from "node:readline";
|
|
54
54
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
55
55
|
|
|
56
|
-
import {
|
|
57
|
-
import { resetModuleState } from "./internal/modules.js";
|
|
56
|
+
import { restoreSharedState } from "./internal/isolation.js";
|
|
58
57
|
import { run } from "./internal/run.js";
|
|
59
|
-
import { unstubAllEnvs, unstubAllGlobals } from "./internal/namespace.js";
|
|
60
58
|
|
|
61
59
|
/** What `uf` sends for one file. */
|
|
62
60
|
type Request = {|
|
|
@@ -140,22 +138,21 @@ function write(event: { readonly [string]: mixed }): void {
|
|
|
140
138
|
*/
|
|
141
139
|
async function runFile(request: Request, generation: number): Promise<void> {
|
|
142
140
|
const started = performance.now();
|
|
143
|
-
|
|
144
|
-
//
|
|
145
|
-
//
|
|
146
|
-
//
|
|
147
|
-
//
|
|
148
|
-
// across workers by size,
|
|
149
|
-
//
|
|
150
|
-
//
|
|
151
|
-
//
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
//
|
|
155
|
-
|
|
156
|
-
//
|
|
157
|
-
//
|
|
158
|
-
resetModuleState();
|
|
141
|
+
// Everything the previous file changed and this package shares with it goes
|
|
142
|
+
// back: the registry, the stubbed environment and globals, the clock, the
|
|
143
|
+
// module stand-ins, and the document. What each of those is and why it is on
|
|
144
|
+
// the list is `internal/isolation.js`, which is one place rather than five
|
|
145
|
+
// calls here — a worker serves many files out of one process, `uf test` fans
|
|
146
|
+
// files across workers by size, and a leak therefore makes the *result* of a
|
|
147
|
+
// suite a function of the timings file rather than of the code under test.
|
|
148
|
+
// See ubugeeei-prod/uf#417, #581 and #607, which are that sentence three
|
|
149
|
+
// times over.
|
|
150
|
+
//
|
|
151
|
+
// Before the import rather than after the run, so a file that throws while
|
|
152
|
+
// loading still hands the next one a clean process.
|
|
153
|
+
restoreSharedState();
|
|
154
|
+
// Not a restore, and so not on that list: this is the output budget for the
|
|
155
|
+
// file about to run, rather than something the previous file left behind.
|
|
159
156
|
output.startFile();
|
|
160
157
|
|
|
161
158
|
try {
|