@molecule/api-ai-tools 1.9.18 → 1.9.19
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 +67 -13
- package/dist/command-position.d.ts +3 -1
- package/dist/command-position.d.ts.map +1 -1
- package/dist/command-position.js +95 -6
- package/dist/output-filter.d.ts +13 -6
- package/dist/output-filter.d.ts.map +1 -1
- package/dist/output-filter.js +35 -16
- package/dist/suite-overlap.d.ts +4 -0
- package/dist/suite-overlap.d.ts.map +1 -1
- package/dist/suite-overlap.js +106 -33
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +49 -11
- package/dist/trace-artifacts.d.ts +33 -0
- package/dist/trace-artifacts.d.ts.map +1 -1
- package/dist/trace-artifacts.js +187 -11
- package/dist/utilities.d.ts +23 -8
- package/dist/utilities.d.ts.map +1 -1
- package/dist/utilities.js +95 -29
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@ AUTO-GENERATED — DO NOT EDIT THIS FILE.
|
|
|
3
3
|
Generated by `mlcl sync-docs` from the package's src/index.ts JSDoc + mlcl/registry.json.
|
|
4
4
|
Edits here are overwritten on the next commit (molecule's pre-commit hook regenerates).
|
|
5
5
|
To change this document, edit the module-level JSDoc in src/index.ts.
|
|
6
|
-
Generated: 2026-10-
|
|
6
|
+
Generated: 2026-10-18T20:46:28.562Z
|
|
7
7
|
-->
|
|
8
8
|
|
|
9
9
|
# @molecule/api-ai-tools
|
|
@@ -66,7 +66,9 @@ One statement or pipeline stage of a shell command: what it runs, and with what.
|
|
|
66
66
|
interface CommandSegment {
|
|
67
67
|
/**
|
|
68
68
|
* The word in command position, its directory removed (`/usr/bin/hurl` → `hurl`): the program
|
|
69
|
-
* the shell would run once whatever wrappers stood in front of it are taken away.
|
|
69
|
+
* the shell would run once whatever wrappers stood in front of it are taken away. When a
|
|
70
|
+
* wrapper runs a tool's own CLI by file, this is the tool's name — `node
|
|
71
|
+
* <path>/node_modules/.bin/playwright test` is a `playwright` invocation, not a `node` one.
|
|
70
72
|
*/
|
|
71
73
|
program: string
|
|
72
74
|
/** The words after it, as written — quotes kept, so a quoted `"a|b"` is one word. */
|
|
@@ -284,9 +286,13 @@ A cutting stage that follows the runner, and the command with the whole pipe rem
|
|
|
284
286
|
|
|
285
287
|
```typescript
|
|
286
288
|
interface RunnerOutputFilter {
|
|
287
|
-
/**
|
|
289
|
+
/**
|
|
290
|
+
* The stage exactly as the runner's command wrote it — `tail -6`. When several cutting filters
|
|
291
|
+
* follow the runner, this is the whole trailing chain as written, pipes included:
|
|
292
|
+
* `grep -E "Failure|actual" | head -40`.
|
|
293
|
+
*/
|
|
288
294
|
stage: string
|
|
289
|
-
/** The pipe as a sentence names it — `| tail -6`. */
|
|
295
|
+
/** The pipe as a sentence names it — `| tail -6`, or the whole chain `| grep … | head -40`. */
|
|
290
296
|
pipe: string
|
|
291
297
|
/**
|
|
292
298
|
* The command with the pipe and the stage removed — `hurl --variable uid=… --test /tmp/t.hurl
|
|
@@ -511,6 +517,17 @@ interface TraceAction {
|
|
|
511
517
|
pageId?: string
|
|
512
518
|
/** The frame the action ran in, from the same snapshot. */
|
|
513
519
|
frameId?: string
|
|
520
|
+
/**
|
|
521
|
+
* The selector a `fill`/`type`/`selectOption` acted on (`textarea[name="bio"]`), from the
|
|
522
|
+
* trace's own params. Kept so the note can compare what the test typed with what the page
|
|
523
|
+
* submitted (m173: the field it typed into was sent empty).
|
|
524
|
+
*/
|
|
525
|
+
selector?: string
|
|
526
|
+
/**
|
|
527
|
+
* The value a `fill`/`type`/`selectOption` typed or chose, from the trace's own params —
|
|
528
|
+
* absent when the action carried none (a `selectOption` by label only).
|
|
529
|
+
*/
|
|
530
|
+
value?: string
|
|
514
531
|
}
|
|
515
532
|
```
|
|
516
533
|
|
|
@@ -1699,10 +1716,13 @@ function commandRunsNoTests(command: string): boolean
|
|
|
1699
1716
|
|
|
1700
1717
|
The output filter that follows the runner in `command`, or null when there is none.
|
|
1701
1718
|
|
|
1702
|
-
Only
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1719
|
+
Only stages of the runner's OWN pipeline count (their separator is `|`), and they are read as
|
|
1720
|
+
one trailing CHAIN: `| grep … | head -40` is two cuts, and removing only the first would leave
|
|
1721
|
+
the run piped into the second. Every stage after the runner must be a cutting filter and the
|
|
1722
|
+
chain must run to the pipeline's end — a pipeline that continues into `wc -l` is not answered
|
|
1723
|
+
here, because removing the cutting stages alone would still leave the failure hidden. A filter
|
|
1724
|
+
before the runner (`cat run.log | tail -20 && hurl …`) reads something else's output and is not
|
|
1725
|
+
this.
|
|
1706
1726
|
|
|
1707
1727
|
```typescript
|
|
1708
1728
|
function outputFilterAfterRunner(
|
|
@@ -1888,6 +1908,10 @@ invocation — same directory, same settings — with the named `file:line`s in
|
|
|
1888
1908
|
suite, `--last-failed` dropped (it re-runs the tests that never ran too, X1 flash-m60) and
|
|
1889
1909
|
`--retries=0` set (a re-run of a known failure does not need to pay for retries).
|
|
1890
1910
|
|
|
1911
|
+
The re-run keeps the run's `> <file>` redirect only in the sense of keeping its OUTPUT there:
|
|
1912
|
+
the target is rewritten to a fresh sibling ({@link redirectToFreshFile}), so a re-run never
|
|
1913
|
+
overwrites the log the run itself is the evidence for (xr-molecule-flash-m173).
|
|
1914
|
+
|
|
1891
1915
|
`config` is the config the run was measured under ({@link withSuiteConfig}): the narrowed re-run
|
|
1892
1916
|
carries it whenever the command itself does not, so a run that dropped `--config` cannot be
|
|
1893
1917
|
handed back a command with the same defect (xr-molecule-flash-m113: every failing test was
|
|
@@ -2226,6 +2250,25 @@ function stalledDocumentSubresource(request: TraceRequest): boolean
|
|
|
2226
2250
|
|
|
2227
2251
|
**Returns:** True when the request is a document subresource that never finished.
|
|
2228
2252
|
|
|
2253
|
+
#### `isCredentialExchangePath(path, authEndpoints)`
|
|
2254
|
+
|
|
2255
|
+
Whether a request PATH asks the app to mint a credential — a login, a registration, a token
|
|
2256
|
+
endpoint — as opposed to a protected resource that presents one.
|
|
2257
|
+
|
|
2258
|
+
This is the path half of {@link isCredentialExchange}, exported so a caller holding only a
|
|
2259
|
+
`METHOD path` reads the same classification: a Hurl failing entry says
|
|
2260
|
+
`POST /api/users/login` and nothing about the body, and the whole-suite note must not explain
|
|
2261
|
+
a wrong-password login that answered 200 as a cookie-jar effect (xr-molecule-flash-m176).
|
|
2262
|
+
|
|
2263
|
+
```typescript
|
|
2264
|
+
function isCredentialExchangePath(path: string, authEndpoints?: readonly string[]): boolean
|
|
2265
|
+
```
|
|
2266
|
+
|
|
2267
|
+
- `path` — The request path (`/api/users/login`); a query or fragment is ignored.
|
|
2268
|
+
- `authEndpoints` — The endpoints the app's auth config names (`/users/login`).
|
|
2269
|
+
|
|
2270
|
+
**Returns:** True when the path names a credential exchange.
|
|
2271
|
+
|
|
2229
2272
|
#### `failureTraceNote(items, target, earlierFailures, apiPrefixes, report)`
|
|
2230
2273
|
|
|
2231
2274
|
The note a failed suite run's result carries: what each failing test's page actually sent, and
|
|
@@ -4026,11 +4069,22 @@ const UNIVERSAL_CAUSE_FILES: 3
|
|
|
4026
4069
|
#### `EMPTY_PAGE_CAUSE_TESTS`
|
|
4027
4070
|
|
|
4028
4071
|
How many tests a run must have walked — with nothing passing since its first failure — before
|
|
4029
|
-
"the page renders nothing" can be its one cause.
|
|
4030
|
-
|
|
4031
|
-
|
|
4032
|
-
|
|
4033
|
-
|
|
4072
|
+
"the page renders nothing" can be its one cause.
|
|
4073
|
+
|
|
4074
|
+
THREE, and the number is the cost of the alternative. The old floor of 15 was chosen when the
|
|
4075
|
+
reading was only offered at a completed run; once it STOPS the walk, every test waited for is
|
|
4076
|
+
a test whose result is thrown away, and a blank page costs its per-test budget each time
|
|
4077
|
+
(m177: 18 tests, ~4.2 minutes, on a page whose thrown error was visible in the first
|
|
4078
|
+
`pageerror`). Three is the smallest count that is not one page's own bug: the cause additionally
|
|
4079
|
+
needs three DISTINCT spec files (`UNIVERSAL_CAUSE_FILES`, or the files a narrowed run selected),
|
|
4080
|
+
each spec waiting for its OWN first element, and no test passing after the first failure. A page
|
|
4081
|
+
that renders misses elements, but three unrelated specs in three files failing at three
|
|
4082
|
+
different first locators with nothing passing in between is the page, not three bugs. At the
|
|
4083
|
+
suite's own 5 s action timeout that verdict lands ~20 s into the walk, so the early stop costs
|
|
4084
|
+
about one test's worth of time instead of fifteen.
|
|
4085
|
+
|
|
4086
|
+
```typescript
|
|
4087
|
+
const EMPTY_PAGE_CAUSE_TESTS: 3
|
|
4034
4088
|
```
|
|
4035
4089
|
|
|
4036
4090
|
#### `CONNECTION_CAUSE_TESTS`
|
|
@@ -36,7 +36,9 @@
|
|
|
36
36
|
export interface CommandSegment {
|
|
37
37
|
/**
|
|
38
38
|
* The word in command position, its directory removed (`/usr/bin/hurl` → `hurl`): the program
|
|
39
|
-
* the shell would run once whatever wrappers stood in front of it are taken away.
|
|
39
|
+
* the shell would run once whatever wrappers stood in front of it are taken away. When a
|
|
40
|
+
* wrapper runs a tool's own CLI by file, this is the tool's name — `node
|
|
41
|
+
* <path>/node_modules/.bin/playwright test` is a `playwright` invocation, not a `node` one.
|
|
40
42
|
*/
|
|
41
43
|
program: string;
|
|
42
44
|
/** The words after it, as written — quotes kept, so a quoted `"a|b"` is one word. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"command-position.d.ts","sourceRoot":"","sources":["../src/command-position.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,uFAAuF;AACvF,MAAM,WAAW,cAAc;IAC7B
|
|
1
|
+
{"version":3,"file":"command-position.d.ts","sourceRoot":"","sources":["../src/command-position.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,uFAAuF;AACvF,MAAM,WAAW,cAAc;IAC7B;;;;;OAKG;IACH,OAAO,EAAE,MAAM,CAAA;IACf,qFAAqF;IACrF,IAAI,EAAE,SAAS,MAAM,EAAE,CAAA;IACvB,4FAA4F;IAC5F,IAAI,EAAE,MAAM,CAAA;IACZ;;;OAGG;IACH,cAAc,EAAE,MAAM,GAAG,IAAI,CAAA;CAC9B;AA8RD;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAM3D;AAwkBD;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,cAAc,EAAE,CAIjE;AAmCD;;;;;;;;;GASG;AACH,MAAM,WAAW,qBAAqB;IACpC;;;;OAIG;IACH,OAAO,EAAE,cAAc,GAAG,IAAI,CAAA;IAC9B,0FAA0F;IAC1F,WAAW,EAAE,SAAS,MAAM,EAAE,CAAA;IAC9B;;;;OAIG;IACH,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB;;;;;OAKG;IACH,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAA;IAC/B;;;OAGG;IACH,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;IAClB;;;;OAIG;IACH,aAAa,EAAE,MAAM,GAAG,IAAI,CAAA;IAC5B;;;;;OAKG;IACH,SAAS,EAAE,MAAM,CAAA;IACjB;;;OAGG;IACH,UAAU,EAAE,MAAM,CAAA;IAClB;;;;;OAKG;IACH,cAAc,EAAE,MAAM,GAAG,IAAI,CAAA;CAC9B;AAED;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,GAAG,qBAAqB,EAAE,CAE/E;AAmDD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,GAAG,IAAI,EACpB,GAAG,EAAE,MAAM,GAAG,IAAI,GACjB,MAAM,GAAG,IAAI,CAyDf;AA2FD;;;;;;;;;;;;GAYG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAOpE;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,OAAO,GAC1C;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAoD7C;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAK/E"}
|
package/dist/command-position.js
CHANGED
|
@@ -68,6 +68,75 @@ const LAUNCHERS = new Set([
|
|
|
68
68
|
]);
|
|
69
69
|
/** The shells whose `-c '<command>'` argument is a command of its own. */
|
|
70
70
|
const SHELLS = new Set(['ash', 'bash', 'dash', 'ksh', 'sh', 'zsh']);
|
|
71
|
+
/**
|
|
72
|
+
* Node, which is a wrapper only when the script it runs IS a tool's own CLI — never in general:
|
|
73
|
+
* `node script.mjs` RUNS that script, so the parser's answer for it stays `node`.
|
|
74
|
+
*
|
|
75
|
+
* The Playwright runner is spelled this way whenever a project reaches it by file instead of by
|
|
76
|
+
* the `.bin` shim: xr-molecule-flash-m179 (+17:51) ran
|
|
77
|
+
* `node /workspace/node_modules/.bin/playwright test` after `npx playwright test` in every
|
|
78
|
+
* earlier call, and no reader recognised it — the run got no `--trace`, no suite-budget pin and
|
|
79
|
+
* no proof credit, and the platform spent the rest of the turn re-walking a suite it had already
|
|
80
|
+
* walked green.
|
|
81
|
+
*/
|
|
82
|
+
const NODE = new Set(['node']);
|
|
83
|
+
/**
|
|
84
|
+
* The Playwright CLI, as the path operand of a `node` invocation: the package's `.bin` shim, or
|
|
85
|
+
* the `cli.js` a package installs (`playwright/cli.js`, `@playwright/test/cli.js`).
|
|
86
|
+
*/
|
|
87
|
+
const PLAYWRIGHT_CLI = /(?:^|\/)node_modules\/(?:\.bin\/playwright|(?:playwright|@playwright\/test)\/cli\.(?:js|mjs|cjs))$/;
|
|
88
|
+
/**
|
|
89
|
+
* The runner a `node` script operand IS, or null when the operand is any other script.
|
|
90
|
+
*
|
|
91
|
+
* @param word - The script operand, as written.
|
|
92
|
+
* @returns The runner's name (`playwright`), or null.
|
|
93
|
+
*/
|
|
94
|
+
function nodeScriptRunner(word) {
|
|
95
|
+
return PLAYWRIGHT_CLI.test(unquoted(word)) ? 'playwright' : null;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Node's own value-taking options: the ones that consume the word after them, so the script
|
|
99
|
+
* operand is not mistaken for a value (`node -r tsx/cli.js …`). Node also accepts `--flag=value`.
|
|
100
|
+
*/
|
|
101
|
+
const NODE_VALUE_FLAGS = new Set([
|
|
102
|
+
'-e',
|
|
103
|
+
'--eval',
|
|
104
|
+
'-p',
|
|
105
|
+
'--print',
|
|
106
|
+
'-r',
|
|
107
|
+
'--require',
|
|
108
|
+
'--import',
|
|
109
|
+
'--loader',
|
|
110
|
+
'--experimental-loader',
|
|
111
|
+
'--input-type',
|
|
112
|
+
'--conditions',
|
|
113
|
+
'--title',
|
|
114
|
+
]);
|
|
115
|
+
/**
|
|
116
|
+
* The word a `node` invocation runs as its script: the first operand after node's own options,
|
|
117
|
+
* with `--` ending the options. Null when node runs no file (`node --test`, `node -e '<code>'`).
|
|
118
|
+
*
|
|
119
|
+
* A wrong guess here is harmless: the operand is then checked against {@link PLAYWRIGHT_CLI}, so
|
|
120
|
+
* an ordinary script (`node probe.mjs`) is never read as a runner.
|
|
121
|
+
*
|
|
122
|
+
* @param words - The segment's words.
|
|
123
|
+
* @param from - The index just after `node`.
|
|
124
|
+
* @returns The script operand's index, or null.
|
|
125
|
+
*/
|
|
126
|
+
function nodeScriptIndex(words, from) {
|
|
127
|
+
let at = from;
|
|
128
|
+
for (let guard = 0; guard < MAX_WRAPPERS && at < words.length; guard++) {
|
|
129
|
+
const word = words[at] ?? '';
|
|
130
|
+
if (!word.startsWith('-'))
|
|
131
|
+
return at;
|
|
132
|
+
const flag = unquoted(word);
|
|
133
|
+
// `--` ends node's options; the script is the next word.
|
|
134
|
+
if (flag === '--')
|
|
135
|
+
return at + 1 < words.length ? at + 1 : null;
|
|
136
|
+
at += flag.includes('=') || !NODE_VALUE_FLAGS.has(flag) ? 1 : 2;
|
|
137
|
+
}
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
71
140
|
/** Package runners that run the program named after them (`npx playwright test`). */
|
|
72
141
|
const PACKAGE_RUNNERS = new Set(['bunx', 'npx', 'pnpx']);
|
|
73
142
|
/** Package managers whose sub-command runs a package's binary (`bun x`, `pnpm dlx`, `npm exec`). */
|
|
@@ -345,6 +414,8 @@ function nestedCommand(words, from) {
|
|
|
345
414
|
* @returns The index of the word in command position (one past the end when the segment runs
|
|
346
415
|
* nothing), the command string of a `sh -c '<command>'` this segment turns out to be, and the
|
|
347
416
|
* seconds its own `timeout` wrapper allows that command (`timeout 280 hurl …` → 280) or null.
|
|
417
|
+
* `program` names what runs when that is not the word itself's name — `node <path>/playwright`
|
|
418
|
+
* is a `playwright` invocation, not a `node` one ({@link nodeScriptRunner}).
|
|
348
419
|
*/
|
|
349
420
|
function commandStart(words) {
|
|
350
421
|
let at = 0;
|
|
@@ -364,14 +435,14 @@ function commandStart(words) {
|
|
|
364
435
|
if (SHELLS.has(name)) {
|
|
365
436
|
const nested = nestedCommand(words, at + 1);
|
|
366
437
|
if (nested !== null)
|
|
367
|
-
return { at, nested, timeoutSeconds };
|
|
438
|
+
return { at, nested, timeoutSeconds, program: null };
|
|
368
439
|
at += 1 + flagWords(words, at + 1, name);
|
|
369
440
|
continue;
|
|
370
441
|
}
|
|
371
442
|
if (PACKAGE_RUNNERS.has(name)) {
|
|
372
443
|
const runner = packageRunnerOptions(words, at + 1, name);
|
|
373
444
|
if (runner.nested !== null)
|
|
374
|
-
return { at, nested: runner.nested, timeoutSeconds };
|
|
445
|
+
return { at, nested: runner.nested, timeoutSeconds, program: null };
|
|
375
446
|
at += 1 + runner.count;
|
|
376
447
|
continue;
|
|
377
448
|
}
|
|
@@ -380,19 +451,35 @@ function commandStart(words) {
|
|
|
380
451
|
const subcommand = unquoted(words[at + 1] ?? '');
|
|
381
452
|
const runner = packageRunnerOptions(words, at + 2, `${name} ${subcommand}`);
|
|
382
453
|
if (runner.nested !== null)
|
|
383
|
-
return { at, nested: runner.nested, timeoutSeconds };
|
|
454
|
+
return { at, nested: runner.nested, timeoutSeconds, program: null };
|
|
384
455
|
at += 2 + runner.count;
|
|
385
456
|
continue;
|
|
386
457
|
}
|
|
458
|
+
if (name === 'yarn' && unquoted(words[at + 1] ?? '') === 'playwright') {
|
|
459
|
+
// Yarn classic runs the binary named after it (`yarn playwright test`); Yarn 2+ spells the
|
|
460
|
+
// same thing `yarn exec`/`yarn dlx` ({@link PACKAGE_RUNNER_SUBCOMMANDS}). Only the runner's
|
|
461
|
+
// own name is read through it — `yarn test` is the project's own script, not a binary.
|
|
462
|
+
at += 1;
|
|
463
|
+
continue;
|
|
464
|
+
}
|
|
465
|
+
if (NODE.has(name)) {
|
|
466
|
+
// `node <path>/playwright test` is a Playwright invocation; `node probe.mjs` runs the
|
|
467
|
+
// project's own script and stays a `node` statement (`program: null`).
|
|
468
|
+
const script = nodeScriptIndex(words, at + 1);
|
|
469
|
+
const runner = script === null ? null : nodeScriptRunner(words[script] ?? '');
|
|
470
|
+
return runner === null
|
|
471
|
+
? { at, nested: null, timeoutSeconds, program: null }
|
|
472
|
+
: { at: script ?? at, nested: null, timeoutSeconds, program: runner };
|
|
473
|
+
}
|
|
387
474
|
if (!LAUNCHERS.has(name))
|
|
388
|
-
return { at, nested: null, timeoutSeconds };
|
|
475
|
+
return { at, nested: null, timeoutSeconds, program: null };
|
|
389
476
|
at += 1 + flagWords(words, at + 1, name);
|
|
390
477
|
if (name === 'timeout' && DURATION.test(words[at] ?? '')) {
|
|
391
478
|
timeoutSeconds = durationSeconds(words[at] ?? '');
|
|
392
479
|
at += 1;
|
|
393
480
|
}
|
|
394
481
|
}
|
|
395
|
-
return { at, nested: null, timeoutSeconds };
|
|
482
|
+
return { at, nested: null, timeoutSeconds, program: null };
|
|
396
483
|
}
|
|
397
484
|
/**
|
|
398
485
|
* A command with every heredoc BODY blanked to spaces, so its length and every offset in it are
|
|
@@ -993,7 +1080,9 @@ function locatedSegmentsOf(command, depth) {
|
|
|
993
1080
|
assignmentOffset: part.start + leading.end,
|
|
994
1081
|
programOffset: words[start.at] === undefined ? null : part.start + (wordOffset(part.text, start.at) ?? 0),
|
|
995
1082
|
};
|
|
996
|
-
|
|
1083
|
+
// The runner the word RUNS names, when that is not the word's own name (`node <path>/playwright`
|
|
1084
|
+
// is a `playwright` invocation, xr-molecule-flash-m179).
|
|
1085
|
+
const program = words[start.at] === undefined ? '' : (start.program ?? programName(words[start.at] ?? ''));
|
|
997
1086
|
const args = words.slice(start.at + 1);
|
|
998
1087
|
const redirectTarget = redirectTargetOf(part.text, part.depths, program, args);
|
|
999
1088
|
if (start.nested !== null) {
|
package/dist/output-filter.d.ts
CHANGED
|
@@ -19,9 +19,13 @@
|
|
|
19
19
|
import { type CommandSegment } from './command-position.js';
|
|
20
20
|
/** A cutting stage that follows the runner, and the command with the whole pipe removed. */
|
|
21
21
|
export interface RunnerOutputFilter {
|
|
22
|
-
/**
|
|
22
|
+
/**
|
|
23
|
+
* The stage exactly as the runner's command wrote it — `tail -6`. When several cutting filters
|
|
24
|
+
* follow the runner, this is the whole trailing chain as written, pipes included:
|
|
25
|
+
* `grep -E "Failure|actual" | head -40`.
|
|
26
|
+
*/
|
|
23
27
|
stage: string;
|
|
24
|
-
/** The pipe as a sentence names it — `| tail -6`. */
|
|
28
|
+
/** The pipe as a sentence names it — `| tail -6`, or the whole chain `| grep … | head -40`. */
|
|
25
29
|
pipe: string;
|
|
26
30
|
/**
|
|
27
31
|
* The command with the pipe and the stage removed — `hurl --variable uid=… --test /tmp/t.hurl
|
|
@@ -33,10 +37,13 @@ export interface RunnerOutputFilter {
|
|
|
33
37
|
/**
|
|
34
38
|
* The output filter that follows the runner in `command`, or null when there is none.
|
|
35
39
|
*
|
|
36
|
-
* Only
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
+
* Only stages of the runner's OWN pipeline count (their separator is `|`), and they are read as
|
|
41
|
+
* one trailing CHAIN: `| grep … | head -40` is two cuts, and removing only the first would leave
|
|
42
|
+
* the run piped into the second. Every stage after the runner must be a cutting filter and the
|
|
43
|
+
* chain must run to the pipeline's end — a pipeline that continues into `wc -l` is not answered
|
|
44
|
+
* here, because removing the cutting stages alone would still leave the failure hidden. A filter
|
|
45
|
+
* before the runner (`cat run.log | tail -20 && hurl …`) reads something else's output and is not
|
|
46
|
+
* this.
|
|
40
47
|
*
|
|
41
48
|
* @param command - The command as the executor sent it.
|
|
42
49
|
* @param runs - Whether a segment is the runner this run is about (the caller knows its own kind:
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"output-filter.d.ts","sourceRoot":"","sources":["../src/output-filter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,
|
|
1
|
+
{"version":3,"file":"output-filter.d.ts","sourceRoot":"","sources":["../src/output-filter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EACL,KAAK,cAAc,EAGpB,MAAM,uBAAuB,CAAA;AAS9B,4FAA4F;AAC5F,MAAM,WAAW,kBAAkB;IACjC;;;;OAIG;IACH,KAAK,EAAE,MAAM,CAAA;IACb,+FAA+F;IAC/F,IAAI,EAAE,MAAM,CAAA;IACZ;;;;OAIG;IACH,OAAO,EAAE,MAAM,CAAA;CAChB;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,OAAO,EAC1C,UAAU,SAAI,GACb,kBAAkB,GAAG,IAAI,CAmC3B"}
|
package/dist/output-filter.js
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
*
|
|
17
17
|
* @module
|
|
18
18
|
*/
|
|
19
|
-
import { locatedCommandSegments } from './command-position.js';
|
|
19
|
+
import { locatedCommandSegments, } from './command-position.js';
|
|
20
20
|
/**
|
|
21
21
|
* The programs whose job is to re-shape what an earlier stage printed and that can DROP lines —
|
|
22
22
|
* the reader's own cut, never part of the run. `tee`, `sort` and `wc` are output plumbing too, but
|
|
@@ -26,10 +26,13 @@ const CUTTING_FILTERS = new Set(['awk', 'cut', 'grep', 'head', 'sed', 'tail']);
|
|
|
26
26
|
/**
|
|
27
27
|
* The output filter that follows the runner in `command`, or null when there is none.
|
|
28
28
|
*
|
|
29
|
-
* Only
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
29
|
+
* Only stages of the runner's OWN pipeline count (their separator is `|`), and they are read as
|
|
30
|
+
* one trailing CHAIN: `| grep … | head -40` is two cuts, and removing only the first would leave
|
|
31
|
+
* the run piped into the second. Every stage after the runner must be a cutting filter and the
|
|
32
|
+
* chain must run to the pipeline's end — a pipeline that continues into `wc -l` is not answered
|
|
33
|
+
* here, because removing the cutting stages alone would still leave the failure hidden. A filter
|
|
34
|
+
* before the runner (`cat run.log | tail -20 && hurl …`) reads something else's output and is not
|
|
35
|
+
* this.
|
|
33
36
|
*
|
|
34
37
|
* @param command - The command as the executor sent it.
|
|
35
38
|
* @param runs - Whether a segment is the runner this run is about (the caller knows its own kind:
|
|
@@ -51,23 +54,39 @@ export function outputFilterAfterRunner(command, runs, occurrence = 1) {
|
|
|
51
54
|
runners.push(index);
|
|
52
55
|
});
|
|
53
56
|
const runnerAt = runners[occurrence - 1];
|
|
54
|
-
if (runnerAt === undefined
|
|
57
|
+
if (runnerAt === undefined)
|
|
55
58
|
return null;
|
|
56
59
|
const runner = entries[runnerAt];
|
|
57
|
-
|
|
58
|
-
// The stage must be fed by the runner's own pipe, and nothing may be piped out of the filter:
|
|
59
|
-
// those are the two positions where cutting it out leaves a command that still runs the run.
|
|
60
|
-
if (!runner || !filter || filter.separator.trim() !== '|')
|
|
60
|
+
if (!runner || runner.end === null)
|
|
61
61
|
return null;
|
|
62
|
-
|
|
62
|
+
// The chain is every stage fed by the runner's own pipe, until the pipeline ends. Each one has
|
|
63
|
+
// to be a cutting filter: a stage that does not drop lines ends the chain, and the chain then
|
|
64
|
+
// stops before the pipeline's end — which is not answerable (`| grep … | wc -l` would still
|
|
65
|
+
// hide the assertion in a count).
|
|
66
|
+
const chain = [];
|
|
67
|
+
for (let index = runnerAt + 1; index < entries.length; index++) {
|
|
68
|
+
const entry = entries[index];
|
|
69
|
+
if (!entry || entry.separator.trim() !== '|')
|
|
70
|
+
break;
|
|
71
|
+
if (!entry.segment || !CUTTING_FILTERS.has(entry.segment.program))
|
|
72
|
+
break;
|
|
73
|
+
chain.push(entry);
|
|
74
|
+
if (entry.terminator.trim() !== '|')
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
const first = chain[0];
|
|
78
|
+
const last = chain[chain.length - 1];
|
|
79
|
+
if (!first || !last)
|
|
63
80
|
return null;
|
|
64
|
-
|
|
81
|
+
// A pipeline that continues past the chain means a non-cutting stage sits after it.
|
|
82
|
+
if (last.terminator.trim() === '|')
|
|
65
83
|
return null;
|
|
66
|
-
if (
|
|
84
|
+
if (first.start === null || last.end === null)
|
|
67
85
|
return null;
|
|
86
|
+
const stage = command.slice(first.start, last.end);
|
|
68
87
|
return {
|
|
69
|
-
stage
|
|
70
|
-
pipe: `| ${
|
|
71
|
-
command: `${command.slice(0, runner.end)}${command.slice(
|
|
88
|
+
stage,
|
|
89
|
+
pipe: `| ${stage}`,
|
|
90
|
+
command: `${command.slice(0, runner.end)}${command.slice(last.end)}`,
|
|
72
91
|
};
|
|
73
92
|
}
|
package/dist/suite-overlap.d.ts
CHANGED
|
@@ -83,6 +83,10 @@ export declare function withSuiteConfig(command: string, config: string | null |
|
|
|
83
83
|
* suite, `--last-failed` dropped (it re-runs the tests that never ran too, X1 flash-m60) and
|
|
84
84
|
* `--retries=0` set (a re-run of a known failure does not need to pay for retries).
|
|
85
85
|
*
|
|
86
|
+
* The re-run keeps the run's `> <file>` redirect only in the sense of keeping its OUTPUT there:
|
|
87
|
+
* the target is rewritten to a fresh sibling ({@link redirectToFreshFile}), so a re-run never
|
|
88
|
+
* overwrites the log the run itself is the evidence for (xr-molecule-flash-m173).
|
|
89
|
+
*
|
|
86
90
|
* `config` is the config the run was measured under ({@link withSuiteConfig}): the narrowed re-run
|
|
87
91
|
* carries it whenever the command itself does not, so a run that dropped `--config` cannot be
|
|
88
92
|
* handed back a command with the same defect (xr-molecule-flash-m113: every failing test was
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"suite-overlap.d.ts","sourceRoot":"","sources":["../src/suite-overlap.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAQH;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAoB1D;AAKD;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAEzD;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"suite-overlap.d.ts","sourceRoot":"","sources":["../src/suite-overlap.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAQH;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAoB1D;AAKD;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAEzD;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAS1F;AAqCD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,SAAS,MAAM,EAAE,EAC5B,QAAQ,GAAE,SAAS,MAAM,EAAO,EAChC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,EACtB,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,GAClB,MAAM,CA8CR;AAkHD;;;;;;GAMG;AACH,MAAM,WAAW,gBAAgB;IAC/B,+FAA+F;IAC/F,SAAS,EAAE,OAAO,CAAA;IAClB,oFAAoF;IACpF,MAAM,EAAE,MAAM,CAAA;IACd;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAA;CAC3B;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,SAAS,gBAAgB,EACzD,SAAS,EAAE;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,EAC7B,QAAQ,EAAE,SAAS,CAAC,EAAE,GACrB,CAAC,GAAG,IAAI,CAMV;AA2FD;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC;;;OAGG;IACH,kBAAkB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;CACvC;AAmCD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAYtF"}
|