@molecule/api-ai-tools 1.10.2 → 1.10.3
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 +68 -2
- package/dist/no-test-run.d.ts +3 -1
- package/dist/no-test-run.d.ts.map +1 -1
- package/dist/no-test-run.js +114 -3
- package/dist/trace-artifacts.d.ts +55 -0
- package/dist/trace-artifacts.d.ts.map +1 -1
- package/dist/trace-artifacts.js +236 -8
- 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-19T03:15:38.119Z
|
|
7
7
|
-->
|
|
8
8
|
|
|
9
9
|
# @molecule/api-ai-tools
|
|
@@ -626,6 +626,43 @@ interface TraceReadContradiction {
|
|
|
626
626
|
}
|
|
627
627
|
```
|
|
628
628
|
|
|
629
|
+
#### `TraceHiddenBox`
|
|
630
|
+
|
|
631
|
+
A visibility failure whose locator RESOLVED: Playwright calls an element with a zero-size box
|
|
632
|
+
"hidden" even when it is painted, so the useful fact is why the box is empty. The trace's own
|
|
633
|
+
failure snapshot carries the element's children — read here, never guessed.
|
|
634
|
+
|
|
635
|
+
```typescript
|
|
636
|
+
interface TraceHiddenBox {
|
|
637
|
+
/** The locator the assertion printed (`locator('a.navbar-brand')`), or the element it names. */
|
|
638
|
+
locator: string
|
|
639
|
+
/** The element's tag, lowercased (`a`). */
|
|
640
|
+
tag: string
|
|
641
|
+
/** The element's own inline style, when the snapshot carries one. */
|
|
642
|
+
style?: string
|
|
643
|
+
/**
|
|
644
|
+
* The `<img>` the element wraps when that image is its only element child and the element has
|
|
645
|
+
* no text of its own: the size the box would collapse to.
|
|
646
|
+
*/
|
|
647
|
+
image?: {
|
|
648
|
+
/** The image's `src`, bounded. */
|
|
649
|
+
src?: string
|
|
650
|
+
/** Its `width` attribute. */
|
|
651
|
+
width?: string
|
|
652
|
+
/** Its `height` attribute. */
|
|
653
|
+
height?: string
|
|
654
|
+
/** Its inline `style`. */
|
|
655
|
+
style?: string
|
|
656
|
+
/** Its `class`. */
|
|
657
|
+
className?: string
|
|
658
|
+
/** Its `alt` text. */
|
|
659
|
+
alt?: string
|
|
660
|
+
}
|
|
661
|
+
/** True when the element has no text and no element children at all. */
|
|
662
|
+
empty?: true
|
|
663
|
+
}
|
|
664
|
+
```
|
|
665
|
+
|
|
629
666
|
#### `TraceArtifact`
|
|
630
667
|
|
|
631
668
|
What one trace says: the test it belongs to, the requests it made, and its console lines.
|
|
@@ -715,6 +752,13 @@ interface TraceArtifact {
|
|
|
715
752
|
* and on traces read before this field existed.
|
|
716
753
|
*/
|
|
717
754
|
readContradiction?: TraceReadContradiction
|
|
755
|
+
/**
|
|
756
|
+
* A visibility failure whose locator RESOLVED into an empty box — Playwright's "hidden" — with
|
|
757
|
+
* the element's children read from the failure snapshot ({@link TraceHiddenBox}). The note uses
|
|
758
|
+
* it to name why the box is empty (an `<img>` with no CSS size, a textless element) instead of
|
|
759
|
+
* leaving the run to probe computed styles (xr-molecule-flash-m197).
|
|
760
|
+
*/
|
|
761
|
+
hiddenBox?: TraceHiddenBox
|
|
718
762
|
}
|
|
719
763
|
```
|
|
720
764
|
|
|
@@ -1818,7 +1862,9 @@ the runner invocation each caller read as a run is one.
|
|
|
1818
1862
|
|
|
1819
1863
|
A command that also runs tests is false however it is spelled: `npx playwright install &&
|
|
1820
1864
|
npx playwright test` installs and runs, so a caller that sees `playwright test` in it is
|
|
1821
|
-
reading a real run.
|
|
1865
|
+
reading a real run. So is a command carrying a segment this reader cannot place (a script it
|
|
1866
|
+
does not know, which may start a suite): only the runners and the {@link NO_TEST_PROGRAMS}
|
|
1867
|
+
table can be accounted for, and "only prints" is a claim about the WHOLE command.
|
|
1822
1868
|
|
|
1823
1869
|
```typescript
|
|
1824
1870
|
function commandRunsNoTests(command: string): boolean
|
|
@@ -2409,6 +2455,26 @@ function isCredentialExchangePath(path: string, authEndpoints?: readonly string[
|
|
|
2409
2455
|
|
|
2410
2456
|
**Returns:** True when the path names a credential exchange.
|
|
2411
2457
|
|
|
2458
|
+
#### `hiddenBoxNote(items)`
|
|
2459
|
+
|
|
2460
|
+
The sentence a "hidden" visibility failure earns when the trace proved WHY the box is empty.
|
|
2461
|
+
|
|
2462
|
+
Playwright calls an element with a zero-size box hidden even when it is painted, so the answer
|
|
2463
|
+
is in the element's children. `a.navbar-brand` on xr-molecule-flash-m197 was 0×4 px because its
|
|
2464
|
+
only child was `<img src="/conduit-logo.svg" height="26">`: an SVG with a `viewBox` and no
|
|
2465
|
+
width/height has no intrinsic size, and the `height` attribute loses to Tailwind's preflight
|
|
2466
|
+
(`img { max-width: 100%; height: auto }`), which the scaffold's own `index.css` imports. The
|
|
2467
|
+
note said only "the cause is in the page", and the run spent over four minutes rasterising an
|
|
2468
|
+
SVG to PNG. Only what the snapshot shows is claimed ({@link TraceHiddenBox}).
|
|
2469
|
+
|
|
2470
|
+
```typescript
|
|
2471
|
+
function hiddenBoxNote(items: readonly TraceArtifact[]): string | null
|
|
2472
|
+
```
|
|
2473
|
+
|
|
2474
|
+
- `items` — The traces {@link parseTraceArtifacts} read.
|
|
2475
|
+
|
|
2476
|
+
**Returns:** The sentence, or null when no failure carries a hidden-box reading.
|
|
2477
|
+
|
|
2412
2478
|
#### `failureTraceNote(items, target, earlierFailures, apiPrefixes, report)`
|
|
2413
2479
|
|
|
2414
2480
|
The note a failed suite run's result carries: what each failing test's page actually sent, and
|
package/dist/no-test-run.d.ts
CHANGED
|
@@ -49,7 +49,9 @@
|
|
|
49
49
|
*
|
|
50
50
|
* A command that also runs tests is false however it is spelled: `npx playwright install &&
|
|
51
51
|
* npx playwright test` installs and runs, so a caller that sees `playwright test` in it is
|
|
52
|
-
* reading a real run.
|
|
52
|
+
* reading a real run. So is a command carrying a segment this reader cannot place (a script it
|
|
53
|
+
* does not know, which may start a suite): only the runners and the {@link NO_TEST_PROGRAMS}
|
|
54
|
+
* table can be accounted for, and "only prints" is a claim about the WHOLE command.
|
|
53
55
|
*
|
|
54
56
|
* @param command - The shell command as the executor sent it.
|
|
55
57
|
* @returns True when the command invokes a runner only to print.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"no-test-run.d.ts","sourceRoot":"","sources":["../src/no-test-run.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;
|
|
1
|
+
{"version":3,"file":"no-test-run.d.ts","sourceRoot":"","sources":["../src/no-test-run.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAiLH;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAiB3D"}
|
package/dist/no-test-run.js
CHANGED
|
@@ -75,6 +75,108 @@ const NO_RUN_FLAGS = {
|
|
|
75
75
|
],
|
|
76
76
|
hurl: [/(?:^|\s)--help(?=\s|$)/, /(?:^|\s)-h(?=\s|$)/, /(?:^|\s)--version(?=\s|$)/],
|
|
77
77
|
};
|
|
78
|
+
/**
|
|
79
|
+
* The programs that start no test — the shell's own bookkeeping (`cd`, `export`, `echo`), and the
|
|
80
|
+
* readers, printers and file commands an executor uses to look at what is already there (`cat`,
|
|
81
|
+
* `ls`, `cp`, `tail`, …).
|
|
82
|
+
*
|
|
83
|
+
* A segment whose program is neither a runner nor one of these is a program this reader cannot
|
|
84
|
+
* account for: the suite's own script (`./run-api-tests-hurl.sh`), a wrapper the executor wrote,
|
|
85
|
+
* anything that may itself start a suite. {@link commandRunsNoTests} answers false for a command
|
|
86
|
+
* carrying one — "every invocation only prints" is not a claim an unknown step can support.
|
|
87
|
+
* X1 xr-molecule-flash-m197 (+05:18) sent
|
|
88
|
+
* `hurl --version | head -2 && HOST=http://localhost:4000 ./run-api-tests-hurl.sh 2>&1 | tail -60`,
|
|
89
|
+
* the suite passed 13/13, and the supplied script was the one segment nobody could place: the
|
|
90
|
+
* whole command read as a version probe, so every "has the suite run?" reader said no for the
|
|
91
|
+
* rest of the run and the early deploy was held to the end of the list.
|
|
92
|
+
*/
|
|
93
|
+
const NO_TEST_PROGRAMS = new Set([
|
|
94
|
+
'[',
|
|
95
|
+
'[[',
|
|
96
|
+
':',
|
|
97
|
+
'awk',
|
|
98
|
+
'base64',
|
|
99
|
+
'basename',
|
|
100
|
+
'case',
|
|
101
|
+
'cat',
|
|
102
|
+
'cd',
|
|
103
|
+
'chmod',
|
|
104
|
+
'chown',
|
|
105
|
+
'column',
|
|
106
|
+
'comm',
|
|
107
|
+
'cp',
|
|
108
|
+
'cut',
|
|
109
|
+
'date',
|
|
110
|
+
'df',
|
|
111
|
+
'diff',
|
|
112
|
+
'dirname',
|
|
113
|
+
'do',
|
|
114
|
+
'done',
|
|
115
|
+
'du',
|
|
116
|
+
'echo',
|
|
117
|
+
'egrep',
|
|
118
|
+
'else',
|
|
119
|
+
'env',
|
|
120
|
+
'esac',
|
|
121
|
+
'export',
|
|
122
|
+
'expr',
|
|
123
|
+
'false',
|
|
124
|
+
'fgrep',
|
|
125
|
+
'fi',
|
|
126
|
+
'file',
|
|
127
|
+
'fold',
|
|
128
|
+
'for',
|
|
129
|
+
'grep',
|
|
130
|
+
'head',
|
|
131
|
+
'hostname',
|
|
132
|
+
'id',
|
|
133
|
+
'if',
|
|
134
|
+
'jq',
|
|
135
|
+
'kill',
|
|
136
|
+
'less',
|
|
137
|
+
'ln',
|
|
138
|
+
'ls',
|
|
139
|
+
'md5sum',
|
|
140
|
+
'mkdir',
|
|
141
|
+
'mktemp',
|
|
142
|
+
'more',
|
|
143
|
+
'mv',
|
|
144
|
+
'nl',
|
|
145
|
+
'od',
|
|
146
|
+
'paste',
|
|
147
|
+
'printf',
|
|
148
|
+
'pwd',
|
|
149
|
+
'readlink',
|
|
150
|
+
'realpath',
|
|
151
|
+
'rev',
|
|
152
|
+
'rm',
|
|
153
|
+
'rmdir',
|
|
154
|
+
'seq',
|
|
155
|
+
'set',
|
|
156
|
+
'sha256sum',
|
|
157
|
+
'sleep',
|
|
158
|
+
'sort',
|
|
159
|
+
'stat',
|
|
160
|
+
'tac',
|
|
161
|
+
'tail',
|
|
162
|
+
'tee',
|
|
163
|
+
'test',
|
|
164
|
+
'then',
|
|
165
|
+
'touch',
|
|
166
|
+
'tr',
|
|
167
|
+
'true',
|
|
168
|
+
'type',
|
|
169
|
+
'uname',
|
|
170
|
+
'uniq',
|
|
171
|
+
'unset',
|
|
172
|
+
'until',
|
|
173
|
+
'wait',
|
|
174
|
+
'wc',
|
|
175
|
+
'which',
|
|
176
|
+
'while',
|
|
177
|
+
'whoami',
|
|
178
|
+
'xxd',
|
|
179
|
+
]);
|
|
78
180
|
/**
|
|
79
181
|
* A heredoc body is data a command writes, not what it runs: a `playwright test --list` inside
|
|
80
182
|
* the config a command writes is not a run of this command either way. The pattern consumes the
|
|
@@ -113,7 +215,9 @@ function informational(runner, after) {
|
|
|
113
215
|
*
|
|
114
216
|
* A command that also runs tests is false however it is spelled: `npx playwright install &&
|
|
115
217
|
* npx playwright test` installs and runs, so a caller that sees `playwright test` in it is
|
|
116
|
-
* reading a real run.
|
|
218
|
+
* reading a real run. So is a command carrying a segment this reader cannot place (a script it
|
|
219
|
+
* does not know, which may start a suite): only the runners and the {@link NO_TEST_PROGRAMS}
|
|
220
|
+
* table can be accounted for, and "only prints" is a claim about the WHOLE command.
|
|
117
221
|
*
|
|
118
222
|
* @param command - The shell command as the executor sent it.
|
|
119
223
|
* @returns True when the command invokes a runner only to print.
|
|
@@ -123,9 +227,16 @@ export function commandRunsNoTests(command) {
|
|
|
123
227
|
return false;
|
|
124
228
|
let invocations = 0;
|
|
125
229
|
for (const segment of commandSegments(command.replace(HEREDOC, ' '))) {
|
|
126
|
-
|
|
127
|
-
|
|
230
|
+
// An own key only: `constructor` is a program name (`constructor --version`) and a
|
|
231
|
+
// prototype key is not a runner.
|
|
232
|
+
const runner = Object.hasOwn(RUNNERS, segment.program) ? RUNNERS[segment.program] : undefined;
|
|
233
|
+
if (runner === undefined) {
|
|
234
|
+
// A program this reader cannot place may start a suite of its own; only a known
|
|
235
|
+
// non-test program lets the command keep reading as "prints only".
|
|
236
|
+
if (!NO_TEST_PROGRAMS.has(segment.program))
|
|
237
|
+
return false;
|
|
128
238
|
continue;
|
|
239
|
+
}
|
|
129
240
|
invocations += 1;
|
|
130
241
|
if (!informational(runner, segment.args.join(' ')))
|
|
131
242
|
return false;
|
|
@@ -223,6 +223,39 @@ export interface TraceReadContradiction {
|
|
|
223
223
|
/** The later GET of the same resource (`GET /api/profiles/x`). */
|
|
224
224
|
read: TraceRequest;
|
|
225
225
|
}
|
|
226
|
+
/**
|
|
227
|
+
* A visibility failure whose locator RESOLVED: Playwright calls an element with a zero-size box
|
|
228
|
+
* "hidden" even when it is painted, so the useful fact is why the box is empty. The trace's own
|
|
229
|
+
* failure snapshot carries the element's children — read here, never guessed.
|
|
230
|
+
*/
|
|
231
|
+
export interface TraceHiddenBox {
|
|
232
|
+
/** The locator the assertion printed (`locator('a.navbar-brand')`), or the element it names. */
|
|
233
|
+
locator: string;
|
|
234
|
+
/** The element's tag, lowercased (`a`). */
|
|
235
|
+
tag: string;
|
|
236
|
+
/** The element's own inline style, when the snapshot carries one. */
|
|
237
|
+
style?: string;
|
|
238
|
+
/**
|
|
239
|
+
* The `<img>` the element wraps when that image is its only element child and the element has
|
|
240
|
+
* no text of its own: the size the box would collapse to.
|
|
241
|
+
*/
|
|
242
|
+
image?: {
|
|
243
|
+
/** The image's `src`, bounded. */
|
|
244
|
+
src?: string;
|
|
245
|
+
/** Its `width` attribute. */
|
|
246
|
+
width?: string;
|
|
247
|
+
/** Its `height` attribute. */
|
|
248
|
+
height?: string;
|
|
249
|
+
/** Its inline `style`. */
|
|
250
|
+
style?: string;
|
|
251
|
+
/** Its `class`. */
|
|
252
|
+
className?: string;
|
|
253
|
+
/** Its `alt` text. */
|
|
254
|
+
alt?: string;
|
|
255
|
+
};
|
|
256
|
+
/** True when the element has no text and no element children at all. */
|
|
257
|
+
empty?: true;
|
|
258
|
+
}
|
|
226
259
|
/** What one trace says: the test it belongs to, the requests it made, and its console lines. */
|
|
227
260
|
export interface TraceArtifact {
|
|
228
261
|
/** The failure's spec file, relative to the run's directory, when the trace names one. */
|
|
@@ -308,6 +341,13 @@ export interface TraceArtifact {
|
|
|
308
341
|
* and on traces read before this field existed.
|
|
309
342
|
*/
|
|
310
343
|
readContradiction?: TraceReadContradiction;
|
|
344
|
+
/**
|
|
345
|
+
* A visibility failure whose locator RESOLVED into an empty box — Playwright's "hidden" — with
|
|
346
|
+
* the element's children read from the failure snapshot ({@link TraceHiddenBox}). The note uses
|
|
347
|
+
* it to name why the box is empty (an `<img>` with no CSS size, a textless element) instead of
|
|
348
|
+
* leaving the run to probe computed styles (xr-molecule-flash-m197).
|
|
349
|
+
*/
|
|
350
|
+
hiddenBox?: TraceHiddenBox;
|
|
311
351
|
}
|
|
312
352
|
/** How many traces one read reports, newest first. */
|
|
313
353
|
export declare const MAX_TRACE_ARTIFACTS = 12;
|
|
@@ -486,6 +526,21 @@ export interface TraceRunReport {
|
|
|
486
526
|
* @returns True when the path names a credential exchange.
|
|
487
527
|
*/
|
|
488
528
|
export declare function isCredentialExchangePath(path: string, authEndpoints?: readonly string[]): boolean;
|
|
529
|
+
/**
|
|
530
|
+
* The sentence a "hidden" visibility failure earns when the trace proved WHY the box is empty.
|
|
531
|
+
*
|
|
532
|
+
* Playwright calls an element with a zero-size box hidden even when it is painted, so the answer
|
|
533
|
+
* is in the element's children. `a.navbar-brand` on xr-molecule-flash-m197 was 0×4 px because its
|
|
534
|
+
* only child was `<img src="/conduit-logo.svg" height="26">`: an SVG with a `viewBox` and no
|
|
535
|
+
* width/height has no intrinsic size, and the `height` attribute loses to Tailwind's preflight
|
|
536
|
+
* (`img { max-width: 100%; height: auto }`), which the scaffold's own `index.css` imports. The
|
|
537
|
+
* note said only "the cause is in the page", and the run spent over four minutes rasterising an
|
|
538
|
+
* SVG to PNG. Only what the snapshot shows is claimed ({@link TraceHiddenBox}).
|
|
539
|
+
*
|
|
540
|
+
* @param items - The traces {@link parseTraceArtifacts} read.
|
|
541
|
+
* @returns The sentence, or null when no failure carries a hidden-box reading.
|
|
542
|
+
*/
|
|
543
|
+
export declare function hiddenBoxNote(items: readonly TraceArtifact[]): string | null;
|
|
489
544
|
/**
|
|
490
545
|
* The note a failed suite run's result carries: what each failing test's page actually sent, and
|
|
491
546
|
* what came back.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"trace-artifacts.d.ts","sourceRoot":"","sources":["../src/trace-artifacts.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"trace-artifacts.d.ts","sourceRoot":"","sources":["../src/trace-artifacts.ts"],"names":[],"mappings":"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"}
|
package/dist/trace-artifacts.js
CHANGED
|
@@ -236,6 +236,109 @@ function waitedTextOf(message) {
|
|
|
236
236
|
return quoted(/Expected(?: string)?:\\s*(["'])((?:(?!\\1).)*)\\1/)
|
|
237
237
|
}
|
|
238
238
|
|
|
239
|
+
// The attributes of one serialized element's opening tag (\`class="a b" id="c"\`). The trace's
|
|
240
|
+
// snapshots carry them as an object already; the ASSERTION's error prints the tag as text.
|
|
241
|
+
function attrsOf(source) {
|
|
242
|
+
const out = {}
|
|
243
|
+
const pattern = /([A-Za-z_:][-A-Za-z0-9_:.]*)\\s*=\\s*("([^"]*)"|'([^']*)')/g
|
|
244
|
+
let match
|
|
245
|
+
while ((match = pattern.exec(String(source || ''))) !== null)
|
|
246
|
+
out[match[1].toLowerCase()] = match[3] !== undefined ? match[3] : match[4]
|
|
247
|
+
return out
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Whether a serialized subtree carries text of its own. A text node is a string; an element is
|
|
251
|
+
// \`[tag, attrs, …children]\`. A reference to an earlier snapshot is not followed here: the caller
|
|
252
|
+
// only makes a claim when every child is readable in place.
|
|
253
|
+
function subtreeText(node) {
|
|
254
|
+
if (typeof node === 'string') return node.trim() !== ''
|
|
255
|
+
if (!Array.isArray(node) || Array.isArray(node[0]) || typeof node[0] !== 'string') return false
|
|
256
|
+
for (let i = 2; i < node.length; i++) if (subtreeText(node[i])) return true
|
|
257
|
+
return false
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// The serialized element children of a node, in document order (references skipped).
|
|
261
|
+
function elementChildren(node) {
|
|
262
|
+
const out = []
|
|
263
|
+
if (!Array.isArray(node)) return out
|
|
264
|
+
for (let i = 2; i < node.length; i++) {
|
|
265
|
+
const child = node[i]
|
|
266
|
+
if (typeof child === 'string' || !Array.isArray(child)) continue
|
|
267
|
+
if (Array.isArray(child[0]) || typeof child[0] !== 'string') continue
|
|
268
|
+
out.push(child)
|
|
269
|
+
}
|
|
270
|
+
return out
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// The first element in a serialized DOM tree with this tag and (when given) id or first class.
|
|
274
|
+
// Playwright's snapshot serializes elements as \`[tag, attrs, …children]\`.
|
|
275
|
+
function findElement(node, tag, id, cls) {
|
|
276
|
+
if (typeof node === 'string' || !Array.isArray(node)) return null
|
|
277
|
+
if (Array.isArray(node[0]) || typeof node[0] !== 'string') return null
|
|
278
|
+
if (node[0].toLowerCase() === tag) {
|
|
279
|
+
const attrs = node[1] && typeof node[1] === 'object' ? node[1] : {}
|
|
280
|
+
const idMatch = id === '' || String(attrs.id || '') === id
|
|
281
|
+
const classes = String(attrs.class || attrs.className || '').split(/\\s+/)
|
|
282
|
+
const classMatch = id !== '' || cls === '' || classes.indexOf(cls) !== -1
|
|
283
|
+
if (idMatch && classMatch) return node
|
|
284
|
+
}
|
|
285
|
+
for (let i = 2; i < node.length; i++) {
|
|
286
|
+
const deeper = findElement(node[i], tag, id, cls)
|
|
287
|
+
if (deeper !== null) return deeper
|
|
288
|
+
}
|
|
289
|
+
return null
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// A locator that RESOLVED whose box Playwright calls hidden. Playwright reports "hidden" for an
|
|
293
|
+
// element with a zero-size box even when it is painted, so the interesting question is why the
|
|
294
|
+
// box is empty — and the trace's own frame snapshot carries the element's children, the only
|
|
295
|
+
// evidence the run would otherwise have to probe for (xr-molecule-flash-m197: \`a.navbar-brand\`
|
|
296
|
+
// was 0x4 px because its only child was an <img> with no CSS size, and the note said only that
|
|
297
|
+
// "the cause is in the page"; the run spent 4.5 minutes rasterising an SVG to PNG).
|
|
298
|
+
// Only what the snapshot proves is reported: an inline display:none/visibility:hidden, a lone
|
|
299
|
+
// <img> child, or a textless element with no children. Anything else says nothing.
|
|
300
|
+
function hiddenBoxOf(message, frames) {
|
|
301
|
+
if (!message) return null
|
|
302
|
+
const text = String(message).replace(/\\u001b\\[[0-9;]*m/g, '')
|
|
303
|
+
if (text.indexOf('strict mode violation') !== -1) return null
|
|
304
|
+
if (!/Received:\\s*hidden/.test(text) && text.indexOf('element is not visible') === -1) return null
|
|
305
|
+
const resolved = /locator resolved to <([A-Za-z][\\w:-]*)((?:[^>"']|"[^"]*"|'[^']*')*)>/.exec(text)
|
|
306
|
+
if (resolved === null) return null
|
|
307
|
+
const tag = resolved[1].toLowerCase()
|
|
308
|
+
const attrs = attrsOf(resolved[2])
|
|
309
|
+
const id = attrs.id || ''
|
|
310
|
+
const cls = String(attrs.class || attrs.className || '').split(/\\s+/).filter(Boolean)[0] || ''
|
|
311
|
+
const named = /Locator:\\s*([^\\n]+)/.exec(text)
|
|
312
|
+
const reading = {
|
|
313
|
+
locator: named !== null ? named[1].trim() : cls ? tag + '.' + cls : tag,
|
|
314
|
+
tag: tag,
|
|
315
|
+
}
|
|
316
|
+
const snapshot = frames.length > 0 ? frames[frames.length - 1].snapshot : null
|
|
317
|
+
const node = snapshot === null || snapshot === undefined ? null : findElement(snapshot, tag, id, cls)
|
|
318
|
+
if (node === null) return null
|
|
319
|
+
const own = node[1] && typeof node[1] === 'object' ? node[1] : {}
|
|
320
|
+
if (typeof own.style === 'string' && own.style) reading.style = own.style.slice(0, 160)
|
|
321
|
+
if (!subtreeText(node)) {
|
|
322
|
+
const children = elementChildren(node)
|
|
323
|
+
if (children.length === 1 && children[0][0].toLowerCase() === 'img') {
|
|
324
|
+
const image = children[0][1] && typeof children[0][1] === 'object' ? children[0][1] : {}
|
|
325
|
+
reading.image = {}
|
|
326
|
+
for (const key of ['src', 'width', 'height', 'style', 'alt']) {
|
|
327
|
+
if (typeof image[key] === 'string' && image[key])
|
|
328
|
+
reading.image[key] = image[key].slice(0, 120)
|
|
329
|
+
}
|
|
330
|
+
if (typeof image.class === 'string' && image.class) reading.image.className = image.class.slice(0, 120)
|
|
331
|
+
} else if (children.length === 0) {
|
|
332
|
+
reading.empty = true
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
const hiddenStyle = /(?:^|;)\\s*display\\s*:\\s*none|(?:^|;)\\s*visibility\\s*:\\s*hidden/.test(
|
|
336
|
+
typeof own.style === 'string' ? own.style : '',
|
|
337
|
+
)
|
|
338
|
+
if (!reading.image && !reading.empty && !hiddenStyle) return null
|
|
339
|
+
return reading
|
|
340
|
+
}
|
|
341
|
+
|
|
239
342
|
// A request/response body exactly as Playwright stored it: inline on the value, or in a resource
|
|
240
343
|
// entry the value names. Bounded, and binary is dropped — a JSON body is what this is for, and a
|
|
241
344
|
// multipart upload would print bytes.
|
|
@@ -892,6 +995,10 @@ for (const one of found.slice(0, MAX_TRACES)) {
|
|
|
892
995
|
}
|
|
893
996
|
}
|
|
894
997
|
}
|
|
998
|
+
// A visibility failure whose element RESOLVED but has an empty box: the snapshot's own DOM says
|
|
999
|
+
// what the element is made of, so the note names the cause instead of sending the run at the
|
|
1000
|
+
// selector (m197 — an <img> with no CSS size collapsed inside its link).
|
|
1001
|
+
const hiddenBox = hiddenBoxOf(failedError, frames)
|
|
895
1002
|
function wire(r) {
|
|
896
1003
|
return {
|
|
897
1004
|
method: r.method,
|
|
@@ -1112,7 +1219,13 @@ for (const one of found.slice(0, MAX_TRACES)) {
|
|
|
1112
1219
|
const specific = consoleErrors.filter(function (text) { return !/^Failed to load resource/.test(text) })
|
|
1113
1220
|
const keptConsole = specific.concat(consoleSignals, consoleWarnings)
|
|
1114
1221
|
if (specific.length === 0) keptConsole.push.apply(keptConsole, consoleErrors)
|
|
1115
|
-
if (
|
|
1222
|
+
if (
|
|
1223
|
+
chosen.length === 0 &&
|
|
1224
|
+
keptConsole.length === 0 &&
|
|
1225
|
+
actions.length === 0 &&
|
|
1226
|
+
hiddenBox === null
|
|
1227
|
+
)
|
|
1228
|
+
continue
|
|
1116
1229
|
process.stdout.write('TRACE ' + JSON.stringify({
|
|
1117
1230
|
dir: path.relative(ROOT, path.dirname(one.file)),
|
|
1118
1231
|
spec: spec,
|
|
@@ -1125,6 +1238,7 @@ for (const one of found.slice(0, MAX_TRACES)) {
|
|
|
1125
1238
|
requests: chosen.slice(0, MAX_REQUESTS),
|
|
1126
1239
|
...(missingText === null ? {} : { missingText: missingText }),
|
|
1127
1240
|
...(readContradiction === null ? {} : { readContradiction: readContradiction }),
|
|
1241
|
+
...(hiddenBox === null ? {} : { hiddenBox: hiddenBox }),
|
|
1128
1242
|
console: keptConsole.slice(0, MAX_CONSOLE).map(function (text) { return text.slice(0, 300) }),
|
|
1129
1243
|
actions: actions.slice(-MAX_ACTIONS),
|
|
1130
1244
|
...(unattributed ? { unattributed: true } : {}),
|
|
@@ -1207,6 +1321,45 @@ function parseReadContradiction(value) {
|
|
|
1207
1321
|
return null;
|
|
1208
1322
|
return { field: raw.field, writeValue: raw.writeValue, readValue: raw.readValue, write, read };
|
|
1209
1323
|
}
|
|
1324
|
+
/**
|
|
1325
|
+
* One `hiddenBox` out of a `TRACE` line's json, or null when the value is not one.
|
|
1326
|
+
*
|
|
1327
|
+
* The locator and tag are required; the image/empty/style detail is optional evidence, kept only
|
|
1328
|
+
* in the shape the script wrote it.
|
|
1329
|
+
*
|
|
1330
|
+
* @param value - The line's `hiddenBox`.
|
|
1331
|
+
* @returns The element the failed locator resolved to, or null.
|
|
1332
|
+
*/
|
|
1333
|
+
function parseHiddenBox(value) {
|
|
1334
|
+
if (typeof value !== 'object' || value === null)
|
|
1335
|
+
return null;
|
|
1336
|
+
const raw = value;
|
|
1337
|
+
if (typeof raw.locator !== 'string' || raw.locator === '')
|
|
1338
|
+
return null;
|
|
1339
|
+
if (typeof raw.tag !== 'string' || raw.tag === '')
|
|
1340
|
+
return null;
|
|
1341
|
+
// An `image` that parses to no attributes is still the fact that the element wraps an <img>:
|
|
1342
|
+
// its presence is the evidence, so an empty object stays.
|
|
1343
|
+
const image = typeof raw.image === 'object' && raw.image !== null
|
|
1344
|
+
? (() => {
|
|
1345
|
+
const at = raw.image;
|
|
1346
|
+
const out = {};
|
|
1347
|
+
for (const key of ['src', 'width', 'height', 'style', 'className', 'alt']) {
|
|
1348
|
+
const field = at[key];
|
|
1349
|
+
if (typeof field === 'string' && field !== '')
|
|
1350
|
+
out[key] = field;
|
|
1351
|
+
}
|
|
1352
|
+
return out;
|
|
1353
|
+
})()
|
|
1354
|
+
: null;
|
|
1355
|
+
return {
|
|
1356
|
+
locator: raw.locator,
|
|
1357
|
+
tag: raw.tag,
|
|
1358
|
+
...(typeof raw.style === 'string' && raw.style ? { style: raw.style } : {}),
|
|
1359
|
+
...(image === null ? {} : { image }),
|
|
1360
|
+
...(raw.empty === true ? { empty: true } : {}),
|
|
1361
|
+
};
|
|
1362
|
+
}
|
|
1210
1363
|
/**
|
|
1211
1364
|
* One `lastAction` out of a `TRACE` line's json, or null when the value is not one.
|
|
1212
1365
|
*
|
|
@@ -1254,6 +1407,7 @@ export function parseTraceArtifacts(stdout) {
|
|
|
1254
1407
|
const lastAction = parseLastAction(raw.lastAction);
|
|
1255
1408
|
const missingText = parseMissingText(raw.missingText);
|
|
1256
1409
|
const readContradiction = parseReadContradiction(raw.readContradiction);
|
|
1410
|
+
const hiddenBox = parseHiddenBox(raw.hiddenBox);
|
|
1257
1411
|
const otherContexts = Array.isArray(raw.otherContexts)
|
|
1258
1412
|
? raw.otherContexts
|
|
1259
1413
|
.filter((c) => typeof c === 'object' && c !== null)
|
|
@@ -1291,6 +1445,7 @@ export function parseTraceArtifacts(stdout) {
|
|
|
1291
1445
|
...(raw.unattributed === true ? { unattributed: true } : {}),
|
|
1292
1446
|
...(missingText === null ? {} : { missingText }),
|
|
1293
1447
|
...(readContradiction === null ? {} : { readContradiction }),
|
|
1448
|
+
...(hiddenBox === null ? {} : { hiddenBox }),
|
|
1294
1449
|
});
|
|
1295
1450
|
}
|
|
1296
1451
|
catch (_error) {
|
|
@@ -2452,6 +2607,71 @@ function consoleLine(text) {
|
|
|
2452
2607
|
return (`${url} answered the app's index.html, not a font — the file is missing from ` +
|
|
2453
2608
|
`\`app/public/\` (or the asset import); copy/import it`);
|
|
2454
2609
|
}
|
|
2610
|
+
/** An `<img>` as the failure snapshot recorded it, for the note's sentence. */
|
|
2611
|
+
function imageMarkup(image) {
|
|
2612
|
+
const attrs = [
|
|
2613
|
+
image.src === undefined ? '' : `src="${image.src}"`,
|
|
2614
|
+
image.alt === undefined ? '' : `alt="${image.alt}"`,
|
|
2615
|
+
image.width === undefined ? '' : `width="${image.width}"`,
|
|
2616
|
+
image.height === undefined ? '' : `height="${image.height}"`,
|
|
2617
|
+
image.className === undefined ? '' : `class="${image.className}"`,
|
|
2618
|
+
image.style === undefined ? '' : `style="${image.style}"`,
|
|
2619
|
+
].filter(Boolean);
|
|
2620
|
+
return `<img${attrs.length > 0 ? ` ${attrs.join(' ')}` : ''}>`;
|
|
2621
|
+
}
|
|
2622
|
+
/** Whether an image's CSS (its class or inline style) declares a width or height. */
|
|
2623
|
+
const IMAGE_SIZE_RULE = /(?:^|;)\s*(?:width|height|max-width|max-height)\s*:/;
|
|
2624
|
+
const HIDING_RULE = /(?:^|;)\s*(?:display\s*:\s*none|visibility\s*:\s*hidden)/;
|
|
2625
|
+
/**
|
|
2626
|
+
* The sentence a "hidden" visibility failure earns when the trace proved WHY the box is empty.
|
|
2627
|
+
*
|
|
2628
|
+
* Playwright calls an element with a zero-size box hidden even when it is painted, so the answer
|
|
2629
|
+
* is in the element's children. `a.navbar-brand` on xr-molecule-flash-m197 was 0×4 px because its
|
|
2630
|
+
* only child was `<img src="/conduit-logo.svg" height="26">`: an SVG with a `viewBox` and no
|
|
2631
|
+
* width/height has no intrinsic size, and the `height` attribute loses to Tailwind's preflight
|
|
2632
|
+
* (`img { max-width: 100%; height: auto }`), which the scaffold's own `index.css` imports. The
|
|
2633
|
+
* note said only "the cause is in the page", and the run spent over four minutes rasterising an
|
|
2634
|
+
* SVG to PNG. Only what the snapshot shows is claimed ({@link TraceHiddenBox}).
|
|
2635
|
+
*
|
|
2636
|
+
* @param items - The traces {@link parseTraceArtifacts} read.
|
|
2637
|
+
* @returns The sentence, or null when no failure carries a hidden-box reading.
|
|
2638
|
+
*/
|
|
2639
|
+
export function hiddenBoxNote(items) {
|
|
2640
|
+
const box = items.find((item) => item.hiddenBox !== undefined)?.hiddenBox;
|
|
2641
|
+
if (box === undefined)
|
|
2642
|
+
return null;
|
|
2643
|
+
const element = `\`${box.locator}\``;
|
|
2644
|
+
if (box.image !== undefined) {
|
|
2645
|
+
const image = box.image;
|
|
2646
|
+
const shown = imageMarkup(image);
|
|
2647
|
+
const sized = image.className !== undefined ||
|
|
2648
|
+
(image.style !== undefined && IMAGE_SIZE_RULE.test(image.style));
|
|
2649
|
+
if (sized) {
|
|
2650
|
+
const rule = image.className === undefined ? 'its inline size' : `\`${image.className}\``;
|
|
2651
|
+
return (`${element} resolved but Playwright calls it hidden: its box has no size, and its only ` +
|
|
2652
|
+
`content is ${shown}. That image declares a size (${rule}), so the rule is not applying ` +
|
|
2653
|
+
`on this page — check that the stylesheet defining it is actually loaded (a supplied ` +
|
|
2654
|
+
`theme has to be linked in index.html, not only imported) and that the class name is ` +
|
|
2655
|
+
`spelled the way the theme defines it.`);
|
|
2656
|
+
}
|
|
2657
|
+
return (`${element} resolved but Playwright calls it hidden: the element's box is empty, and its ` +
|
|
2658
|
+
`only content is ${shown} — an image with no CSS size. An <img> takes its size from CSS or ` +
|
|
2659
|
+
`from the file; an SVG that declares only a viewBox has no intrinsic size, and a ` +
|
|
2660
|
+
`width/height ATTRIBUTE loses to any stylesheet rule (Tailwind's preflight, which the ` +
|
|
2661
|
+
`scaffold's index.css imports, sets img { max-width: 100%; height: auto }). Give the image a ` +
|
|
2662
|
+
`CSS width/height — a class the supplied theme already defines for it, or ` +
|
|
2663
|
+
`style={{ height: … }} — rather than changing the SVG or rasterising it to PNG.`);
|
|
2664
|
+
}
|
|
2665
|
+
if (box.style !== undefined && HIDING_RULE.test(box.style)) {
|
|
2666
|
+
return (`${element} resolved but Playwright calls it hidden: its own inline style sets ` +
|
|
2667
|
+
`\`${box.style}\`. Remove that declaration (or the rule that sets it) so the element has a box.`);
|
|
2668
|
+
}
|
|
2669
|
+
if (box.empty === true) {
|
|
2670
|
+
return (`${element} resolved but Playwright calls it hidden: the element has no text and no ` +
|
|
2671
|
+
`children, so its box has zero size. Give it content or an explicit width/height.`);
|
|
2672
|
+
}
|
|
2673
|
+
return null;
|
|
2674
|
+
}
|
|
2455
2675
|
/**
|
|
2456
2676
|
* The note a failed suite run's result carries: what each failing test's page actually sent, and
|
|
2457
2677
|
* what came back.
|
|
@@ -2590,6 +2810,10 @@ export function failureTraceNote(items, target, earlierFailures = [], apiPrefixe
|
|
|
2590
2810
|
const wrapped = traced.some((item) => item.requests.some((request) => request.sent !== undefined && wrappedInData(request.sent)));
|
|
2591
2811
|
// The "one of these requests failing" answer is only honest when a listed request really failed.
|
|
2592
2812
|
const anyFailed = traced.some((item) => item.requests.some(failedRequest));
|
|
2813
|
+
// A visibility failure the trace's own snapshot explains: said instead of the generic
|
|
2814
|
+
// "the cause is in the page" sentence, which m197 read as "go look at the page" and then spent
|
|
2815
|
+
// four minutes rasterising an SVG ({@link hiddenBoxNote}).
|
|
2816
|
+
const hiddenBox = hiddenBoxNote(traced);
|
|
2593
2817
|
// m145's shape: the app's HTTP client lost its base URL, so every API call reached the page
|
|
2594
2818
|
// origin without the prefix the app mounts its routes under. The prefix is READ — the caller's
|
|
2595
2819
|
// client config plus what the traces show — never assumed to be `/api`.
|
|
@@ -2954,13 +3178,17 @@ export function failureTraceNote(items, target, earlierFailures = [], apiPrefixe
|
|
|
2954
3178
|
'action sent no request at all, so there is no response to read. The page changed ' +
|
|
2955
3179
|
'route and fetched nothing, which is why it still shows the previous route’s data: ' +
|
|
2956
3180
|
'fix how the page reads the route, not the selector.'
|
|
2957
|
-
:
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
'
|
|
2962
|
-
|
|
2963
|
-
|
|
3181
|
+
: hiddenBox !== null
|
|
3182
|
+
? // The trace's own snapshot explains why the element Playwright resolved has no box:
|
|
3183
|
+
// a fact about the element, so it replaces the "look at the page" advice (m197).
|
|
3184
|
+
hiddenBox
|
|
3185
|
+
: 'No listed request failed — none got a 4xx/5xx or a network error — so the cause is in the ' +
|
|
3186
|
+
"page, not the API. Start with what the test's last action sent (the first line of each " +
|
|
3187
|
+
'row) and what the page did next: a request that answered (2xx included) while the page ' +
|
|
3188
|
+
'still did not show the state the assertion waited on is the fact to read — its status ' +
|
|
3189
|
+
'and response body, not the selector. If the request is not the one the test meant — an ' +
|
|
3190
|
+
'unfollow sent where the test clicked follow — the state the action read was stale ' +
|
|
3191
|
+
'before it.') +
|
|
2964
3192
|
prefixAdvice +
|
|
2965
3193
|
(wrapped
|
|
2966
3194
|
? ' The API received a body wrapped in `data`: app-http’s `post(url, data, config)` / ' +
|