@uniflowed/test 0.0.0-alpha.6 → 0.0.0-alpha.7
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/frames.js +44 -1
- package/internal/run.js +21 -6
- package/package.json +2 -2
package/internal/frames.js
CHANGED
|
@@ -16,11 +16,24 @@
|
|
|
16
16
|
/** A position in a source file, one-based line and column. */
|
|
17
17
|
export type Site = {| readonly line: number, readonly column: number |};
|
|
18
18
|
|
|
19
|
-
/**
|
|
19
|
+
/**
|
|
20
|
+
* Frames belonging to the testing libraries, which no test author wrote.
|
|
21
|
+
*
|
|
22
|
+
* `@uniflowed/react-testing` is here for exactly the reason `@uniflowed/test`
|
|
23
|
+
* is, and was missing because it is a different package. Every `getBy…`
|
|
24
|
+
* failure is *constructed* inside it, so the first surviving frame of the
|
|
25
|
+
* commonest failure a component test can produce was `internal/queries.js` —
|
|
26
|
+
* and since a site is reported as a line of the file being run, the reader was
|
|
27
|
+
* sent to a line their own file does not have. That is ubugeeei-prod/uf#319.
|
|
28
|
+
* A library that raises on the caller's behalf is the runner as far as the
|
|
29
|
+
* report is concerned.
|
|
30
|
+
*/
|
|
20
31
|
const INTERNAL_MARKERS = [
|
|
21
32
|
"/packages/test/internal/",
|
|
22
33
|
"/packages/test/worker.js",
|
|
34
|
+
"/packages/react-testing/",
|
|
23
35
|
"/@uniflowed/test/",
|
|
36
|
+
"/@uniflowed/react-testing/",
|
|
24
37
|
"node:internal/",
|
|
25
38
|
];
|
|
26
39
|
|
|
@@ -93,6 +106,36 @@ export function firstUserSite(
|
|
|
93
106
|
return null;
|
|
94
107
|
}
|
|
95
108
|
|
|
109
|
+
/**
|
|
110
|
+
* The first position in `stack` that is in `file`, or `null`.
|
|
111
|
+
*
|
|
112
|
+
* The reporter draws a failure's position as `path:line:column`, and it takes
|
|
113
|
+
* the path from the file it asked the worker to run rather than from the
|
|
114
|
+
* frame. So a number lifted from any other file is not a vaguer answer than
|
|
115
|
+
* none — it is a wrong one, naming a line the reader can open and that has
|
|
116
|
+
* nothing to do with what failed. Restricting the search to the file that will
|
|
117
|
+
* be named makes the two halves of that position come from the same place.
|
|
118
|
+
*
|
|
119
|
+
* `null` when the file is on no frame, which is a failure raised from work the
|
|
120
|
+
* case left behind: no line of it is the one that failed, and printing none is
|
|
121
|
+
* the honest answer.
|
|
122
|
+
*/
|
|
123
|
+
export function siteInFile(stack: string | null | void, file: string): Site | null {
|
|
124
|
+
if (stack == null) {
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
for (const frame of stack.split("\n").slice(1)) {
|
|
128
|
+
if (!frame.includes(file)) {
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
const site = frameSite(frame);
|
|
132
|
+
if (site != null) {
|
|
133
|
+
return site;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
|
|
96
139
|
/**
|
|
97
140
|
* `stack` with the runner's own frames removed.
|
|
98
141
|
*
|
package/internal/run.js
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
import * as output from "./output.js";
|
|
13
13
|
import * as snapshot from "./snapshot.js";
|
|
14
14
|
import { AssertionError } from "./expect.js";
|
|
15
|
-
import { firstUserSite, userFrames } from "./frames.js";
|
|
15
|
+
import { type Site, firstUserSite, siteInFile, userFrames } from "./frames.js";
|
|
16
16
|
import { type Body, type Case, type Suite, collected } from "./registry.js";
|
|
17
17
|
|
|
18
18
|
/** How one case ended. */
|
|
@@ -102,7 +102,22 @@ async function withTimeout(body: Body, timeoutMs: number): Promise<void> {
|
|
|
102
102
|
}
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
-
|
|
105
|
+
/**
|
|
106
|
+
* Where to say a failure happened.
|
|
107
|
+
*
|
|
108
|
+
* The reported position is printed under the path of the file being run, so
|
|
109
|
+
* when that file is known the line has to come from it: `firstUserSite` will
|
|
110
|
+
* hand back the first frame of whatever library raised, and a library's line
|
|
111
|
+
* number wearing the test file's path sends the reader to the wrong place
|
|
112
|
+
* (ubugeeei-prod/uf#319). The fallback is for a caller that did not say which
|
|
113
|
+
* file it is running — `run` is driven directly by this repository's own
|
|
114
|
+
* tests as well as by the worker — and is what every failure used before.
|
|
115
|
+
*/
|
|
116
|
+
function siteOf(stack: string | null, file: string | null): Site | null {
|
|
117
|
+
return file == null || file === "" ? firstUserSite(stack, false) : siteInFile(stack, file);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function failure(thrown: mixed, file: string | null): Outcome {
|
|
106
121
|
if (thrown instanceof AssertionError) {
|
|
107
122
|
const stack = userFrames(thrown.stack);
|
|
108
123
|
return {
|
|
@@ -111,7 +126,7 @@ function failure(thrown: mixed): Outcome {
|
|
|
111
126
|
stack,
|
|
112
127
|
expected: thrown.expected,
|
|
113
128
|
received: thrown.received,
|
|
114
|
-
site:
|
|
129
|
+
site: siteOf(stack, file),
|
|
115
130
|
};
|
|
116
131
|
}
|
|
117
132
|
if (thrown instanceof Error) {
|
|
@@ -122,7 +137,7 @@ function failure(thrown: mixed): Outcome {
|
|
|
122
137
|
stack,
|
|
123
138
|
expected: null,
|
|
124
139
|
received: null,
|
|
125
|
-
site:
|
|
140
|
+
site: siteOf(stack, file),
|
|
126
141
|
};
|
|
127
142
|
}
|
|
128
143
|
return {
|
|
@@ -209,7 +224,7 @@ async function runCase(
|
|
|
209
224
|
}
|
|
210
225
|
await withTimeout(test.body, timeoutMs);
|
|
211
226
|
} catch (thrown) {
|
|
212
|
-
outcome = failure(thrown);
|
|
227
|
+
outcome = failure(thrown, options.file ?? null);
|
|
213
228
|
}
|
|
214
229
|
// Teardown runs whatever happened above, and only reports its own failure
|
|
215
230
|
// when the body had not already failed.
|
|
@@ -218,7 +233,7 @@ async function runCase(
|
|
|
218
233
|
await withTimeout(hook, timeoutMs);
|
|
219
234
|
} catch (thrown) {
|
|
220
235
|
if (outcome.status === "passed") {
|
|
221
|
-
outcome = failure(thrown);
|
|
236
|
+
outcome = failure(thrown, options.file ?? null);
|
|
222
237
|
}
|
|
223
238
|
}
|
|
224
239
|
}
|
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.7",
|
|
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.7"
|
|
25
25
|
}
|
|
26
26
|
}
|