@molecule/api-ai-tools 1.10.1 → 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 +229 -15
- 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/tools.d.ts.map +1 -1
- package/dist/tools.js +64 -30
- package/dist/trace-artifacts.d.ts +65 -0
- package/dist/trace-artifacts.d.ts.map +1 -1
- package/dist/trace-artifacts.js +370 -29
- package/dist/types.d.ts +27 -6
- package/dist/types.d.ts.map +1 -1
- package/dist/utilities.d.ts +114 -12
- package/dist/utilities.d.ts.map +1 -1
- package/dist/utilities.js +424 -33
- package/package.json +1 -1
package/dist/trace-artifacts.js
CHANGED
|
@@ -203,26 +203,142 @@ function messageOf(value) {
|
|
|
203
203
|
const FILLING_ACTION = /\\.(?:fill|type|pressSequentially|selectOption)$/
|
|
204
204
|
|
|
205
205
|
// The text a failing assertion waited for, named by its own error: a \`has-text(…)\` CSS
|
|
206
|
-
// pseudo-class, a \`getByText(…)\` locator, or a \`toContainText(…)\` expectation
|
|
207
|
-
//
|
|
208
|
-
//
|
|
209
|
-
//
|
|
206
|
+
// pseudo-class, a \`getByText(…)\` locator, or a \`toContainText(…)\` expectation — and only when
|
|
207
|
+
// the failure IS an absence of that text. A strict-mode violation is not this shape (two elements
|
|
208
|
+
// DID match), and neither is a wait on a selector with no text argument.
|
|
209
|
+
//
|
|
210
|
+
// A locator that RESOLVED is not an absent element either: Playwright names the element it matched
|
|
211
|
+
// and then says why the action could not run (m194's disabled button carried the very title the
|
|
212
|
+
// note called missing). A disabled, invisible, unstable, detached or intercepted control is a
|
|
213
|
+
// control problem, not a rendering one, so none of those shapes may produce a missing-text reading.
|
|
214
|
+
// The ANSI colouring Playwright leaves in the message is dropped before the read, or the quotes it
|
|
215
|
+
// wraps around a locator would never parse.
|
|
210
216
|
function waitedTextOf(message) {
|
|
211
217
|
if (!message) return null
|
|
212
218
|
const text = String(message).replace(/\\u001b\\[[0-9;]*m/g, '')
|
|
213
219
|
if (text.indexOf('strict mode violation') !== -1) return null
|
|
220
|
+
if (text.indexOf('locator resolved to') !== -1) return null
|
|
221
|
+
if (/element is not (?:enabled|visible|stable|attached)/.test(text)) return null
|
|
222
|
+
if (/intercepts? pointer events/.test(text)) return null
|
|
223
|
+
// Playwright's own not-found marker is what an absence assertion leaves behind; a text
|
|
224
|
+
// expectation that failed on an element it DID find is the other absence shape (its received
|
|
225
|
+
// text lacks the expected one).
|
|
226
|
+
const notFound = /element\\(s\\) not found|element is not found/.test(text)
|
|
214
227
|
const quoted = function (pattern) {
|
|
215
228
|
const m = pattern.exec(text)
|
|
216
229
|
return m === null ? null : m[2]
|
|
217
230
|
}
|
|
218
231
|
const hasText = quoted(/has-text\\(\\s*(['"])((?:(?!\\1).)*)\\1/)
|
|
219
|
-
if (hasText !== null) return hasText
|
|
232
|
+
if (hasText !== null) return notFound ? hasText : null
|
|
220
233
|
const byText = quoted(/getByText\\(\\s*(['"])((?:(?!\\1).)*)\\1/)
|
|
221
|
-
if (byText !== null) return byText
|
|
234
|
+
if (byText !== null) return notFound ? byText : null
|
|
222
235
|
if (text.indexOf('toContainText') === -1) return null
|
|
223
236
|
return quoted(/Expected(?: string)?:\\s*(["'])((?:(?!\\1).)*)\\1/)
|
|
224
237
|
}
|
|
225
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
|
+
|
|
226
342
|
// A request/response body exactly as Playwright stored it: inline on the value, or in a resource
|
|
227
343
|
// entry the value names. Bounded, and binary is dropped — a JSON body is what this is for, and a
|
|
228
344
|
// multipart upload would print bytes.
|
|
@@ -479,6 +595,11 @@ for (const one of found.slice(0, MAX_TRACES)) {
|
|
|
479
595
|
context: ctx.id,
|
|
480
596
|
pageId: typeof snap.pageId === 'string' ? snap.pageId : undefined,
|
|
481
597
|
frameId: typeof snap.frameId === 'string' ? snap.frameId : undefined,
|
|
598
|
+
// The snapshot's own DOM tree, kept by reference (the trace line already holds it). The
|
|
599
|
+
// note reads its text only when it has to prove an awaited value was absent from the page
|
|
600
|
+
// (m194's false "dropped before rendering" lead). Playwright serializes text nodes as
|
|
601
|
+
// strings and elements as [tag, attrs, ...children].
|
|
602
|
+
snapshot: snap.html,
|
|
482
603
|
})
|
|
483
604
|
}
|
|
484
605
|
for (const entry of ctx.trace) {
|
|
@@ -776,26 +897,108 @@ for (const one of found.slice(0, MAX_TRACES)) {
|
|
|
776
897
|
if (sent) r.sent = sent
|
|
777
898
|
if (got) r.got = got
|
|
778
899
|
}
|
|
900
|
+
// The text a frame snapshot SHOWED, so a page that still carried the awaited value is never
|
|
901
|
+
// described as having dropped it. Playwright serializes the DOM as strings (text nodes) and
|
|
902
|
+
// [tag, attrs, ...children] elements, with a subtree reference ([[snapshotIndex, nodeIndex]]) to
|
|
903
|
+
// a node an earlier snapshot of the same frame carried. \`script\`/\`style\` bodies are skipped,
|
|
904
|
+
// as the trace viewer skips them: code is not what the page showed. Null when the snapshot is
|
|
905
|
+
// absent or a reference cannot be resolved — an unreadable snapshot proves nothing.
|
|
906
|
+
function snapshotShowed(frame) {
|
|
907
|
+
const tree = frame.snapshot
|
|
908
|
+
if (tree === undefined || tree === null) return null
|
|
909
|
+
const shown = framesAll.filter(function (f) {
|
|
910
|
+
return f.context === frame.context && f.pageId === frame.pageId
|
|
911
|
+
})
|
|
912
|
+
const nodesOf = function (value) {
|
|
913
|
+
const nodes = []
|
|
914
|
+
const collect = function (n) {
|
|
915
|
+
if (typeof n === 'string') { nodes.push(n); return }
|
|
916
|
+
if (!Array.isArray(n) || typeof n[0] !== 'string') return
|
|
917
|
+
for (let i = 2; i < n.length; i++) collect(n[i])
|
|
918
|
+
nodes.push(n)
|
|
919
|
+
}
|
|
920
|
+
collect(value)
|
|
921
|
+
return nodes
|
|
922
|
+
}
|
|
923
|
+
const out = []
|
|
924
|
+
let visited = 0
|
|
925
|
+
let readable = true
|
|
926
|
+
const visit = function (n) {
|
|
927
|
+
if (++visited > 400000) { readable = false; return }
|
|
928
|
+
if (typeof n === 'string') { out.push(n); return }
|
|
929
|
+
if (!Array.isArray(n)) return
|
|
930
|
+
if (Array.isArray(n[0])) {
|
|
931
|
+
const ref = n[0]
|
|
932
|
+
const at = Array.isArray(ref) && typeof ref[0] === 'number' ? ref[0] : -1
|
|
933
|
+
const node = Array.isArray(ref) && typeof ref[1] === 'number' ? ref[1] : -1
|
|
934
|
+
const earlier = at >= 0 && at < shown.length && shown[at].snapshot != null ? shown[at].snapshot : null
|
|
935
|
+
const nodes = earlier === null ? [] : nodesOf(earlier)
|
|
936
|
+
if (node < 0 || node >= nodes.length) { readable = false; return }
|
|
937
|
+
visit(nodes[node])
|
|
938
|
+
return
|
|
939
|
+
}
|
|
940
|
+
if (typeof n[0] !== 'string') return
|
|
941
|
+
const name = n[0].toUpperCase()
|
|
942
|
+
if (name === 'SCRIPT' || name === 'STYLE') return
|
|
943
|
+
for (let i = 2; i < n.length; i++) visit(n[i])
|
|
944
|
+
}
|
|
945
|
+
visit(tree)
|
|
946
|
+
return readable ? out.join('') : null
|
|
947
|
+
}
|
|
779
948
|
// The text the failing assertion waited for, and a response the SAME page received that carried
|
|
780
949
|
// it (m169). The assertion's own error names the text; the network log says whether it ever
|
|
781
950
|
// reached the browser. Only an answered fetch/xhr can say the API returned it — a 4xx body is an
|
|
782
951
|
// error message, not data. The most focused response wins (smallest body): an articles list also
|
|
783
952
|
// carries tag names in its \`tagList\`, while the tags response is the collection whose membership
|
|
784
953
|
// the assertion tests.
|
|
954
|
+
//
|
|
955
|
+
// The "page drops it before rendering" claim is made only when the trace PROVES it: a successful
|
|
956
|
+
// GET of the collection that STARTED AFTER the last navigation, and the awaited text absent from
|
|
957
|
+
// the page's own failure snapshot. A creation POST says nothing about what a collection render
|
|
958
|
+
// drops — m194's button failure was labelled missing while its title was on the page and the
|
|
959
|
+
// only carrier was \`POST /api/articles\` — and a snapshot that still shows the text says the
|
|
960
|
+
// opposite. Without that proof the note only says a response contained the text.
|
|
785
961
|
const waitedText = waitedTextOf(failedError)
|
|
786
962
|
let missingText = null
|
|
787
963
|
if (waitedText !== null && !unattributed) {
|
|
964
|
+
let lastNavigation = null
|
|
965
|
+
for (const a of actions) {
|
|
966
|
+
if (!/\\.(?:goto|reload|goBack|goForward)$/.test(a.api)) continue
|
|
967
|
+
if (lastNavigation === null || a.start > lastNavigation) lastNavigation = a.start
|
|
968
|
+
}
|
|
788
969
|
let best = null
|
|
970
|
+
let bestGet = null
|
|
789
971
|
for (const r of requests) {
|
|
790
972
|
if (r.type !== 'fetch' && r.type !== 'xhr') continue
|
|
791
973
|
if (r.status < 200 || r.status >= 400) continue
|
|
792
974
|
const got = gotText(buf, entries, r)
|
|
793
975
|
if (!got || got.indexOf(waitedText) === -1) continue
|
|
794
976
|
if (best === null || got.length < best.got.length) best = { request: r, got: got }
|
|
977
|
+
// The collection read the claim may rest on: a GET that started after the last navigation
|
|
978
|
+
// (a request that started before it belongs to the page load, not to the failure).
|
|
979
|
+
if (r.method !== 'GET') continue
|
|
980
|
+
if (lastNavigation === null || typeof r.startedAt !== 'number' || r.startedAt <= lastNavigation) continue
|
|
981
|
+
if (bestGet === null || got.length < bestGet.got.length) bestGet = { request: r, got: got }
|
|
982
|
+
}
|
|
983
|
+
if (best !== null) {
|
|
984
|
+
const named = bestGet === null ? best : bestGet
|
|
985
|
+
let dropped = false
|
|
986
|
+
if (bestGet !== null && frames.length > 0) {
|
|
987
|
+
const shown = snapshotShowed(frames[frames.length - 1])
|
|
988
|
+
dropped = shown !== null && shown.indexOf(waitedText) === -1
|
|
989
|
+
}
|
|
990
|
+
missingText = {
|
|
991
|
+
text: waitedText,
|
|
992
|
+
method: named.request.method,
|
|
993
|
+
path: named.request.path,
|
|
994
|
+
...(dropped ? { dropped: true } : {}),
|
|
995
|
+
}
|
|
795
996
|
}
|
|
796
|
-
if (best !== null)
|
|
797
|
-
missingText = { text: waitedText, method: best.request.method, path: best.request.path }
|
|
798
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)
|
|
799
1002
|
function wire(r) {
|
|
800
1003
|
return {
|
|
801
1004
|
method: r.method,
|
|
@@ -1016,7 +1219,13 @@ for (const one of found.slice(0, MAX_TRACES)) {
|
|
|
1016
1219
|
const specific = consoleErrors.filter(function (text) { return !/^Failed to load resource/.test(text) })
|
|
1017
1220
|
const keptConsole = specific.concat(consoleSignals, consoleWarnings)
|
|
1018
1221
|
if (specific.length === 0) keptConsole.push.apply(keptConsole, consoleErrors)
|
|
1019
|
-
if (
|
|
1222
|
+
if (
|
|
1223
|
+
chosen.length === 0 &&
|
|
1224
|
+
keptConsole.length === 0 &&
|
|
1225
|
+
actions.length === 0 &&
|
|
1226
|
+
hiddenBox === null
|
|
1227
|
+
)
|
|
1228
|
+
continue
|
|
1020
1229
|
process.stdout.write('TRACE ' + JSON.stringify({
|
|
1021
1230
|
dir: path.relative(ROOT, path.dirname(one.file)),
|
|
1022
1231
|
spec: spec,
|
|
@@ -1029,6 +1238,7 @@ for (const one of found.slice(0, MAX_TRACES)) {
|
|
|
1029
1238
|
requests: chosen.slice(0, MAX_REQUESTS),
|
|
1030
1239
|
...(missingText === null ? {} : { missingText: missingText }),
|
|
1031
1240
|
...(readContradiction === null ? {} : { readContradiction: readContradiction }),
|
|
1241
|
+
...(hiddenBox === null ? {} : { hiddenBox: hiddenBox }),
|
|
1032
1242
|
console: keptConsole.slice(0, MAX_CONSOLE).map(function (text) { return text.slice(0, 300) }),
|
|
1033
1243
|
actions: actions.slice(-MAX_ACTIONS),
|
|
1034
1244
|
...(unattributed ? { unattributed: true } : {}),
|
|
@@ -1081,7 +1291,12 @@ function parseMissingText(value) {
|
|
|
1081
1291
|
return null;
|
|
1082
1292
|
if (typeof raw.path !== 'string' || raw.path === '')
|
|
1083
1293
|
return null;
|
|
1084
|
-
return {
|
|
1294
|
+
return {
|
|
1295
|
+
text: raw.text,
|
|
1296
|
+
method: raw.method,
|
|
1297
|
+
path: raw.path,
|
|
1298
|
+
...(raw.dropped === true ? { dropped: true } : {}),
|
|
1299
|
+
};
|
|
1085
1300
|
}
|
|
1086
1301
|
/**
|
|
1087
1302
|
* One `readContradiction` out of a `TRACE` line's json, or null when the value is not one.
|
|
@@ -1106,6 +1321,45 @@ function parseReadContradiction(value) {
|
|
|
1106
1321
|
return null;
|
|
1107
1322
|
return { field: raw.field, writeValue: raw.writeValue, readValue: raw.readValue, write, read };
|
|
1108
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
|
+
}
|
|
1109
1363
|
/**
|
|
1110
1364
|
* One `lastAction` out of a `TRACE` line's json, or null when the value is not one.
|
|
1111
1365
|
*
|
|
@@ -1153,6 +1407,7 @@ export function parseTraceArtifacts(stdout) {
|
|
|
1153
1407
|
const lastAction = parseLastAction(raw.lastAction);
|
|
1154
1408
|
const missingText = parseMissingText(raw.missingText);
|
|
1155
1409
|
const readContradiction = parseReadContradiction(raw.readContradiction);
|
|
1410
|
+
const hiddenBox = parseHiddenBox(raw.hiddenBox);
|
|
1156
1411
|
const otherContexts = Array.isArray(raw.otherContexts)
|
|
1157
1412
|
? raw.otherContexts
|
|
1158
1413
|
.filter((c) => typeof c === 'object' && c !== null)
|
|
@@ -1190,6 +1445,7 @@ export function parseTraceArtifacts(stdout) {
|
|
|
1190
1445
|
...(raw.unattributed === true ? { unattributed: true } : {}),
|
|
1191
1446
|
...(missingText === null ? {} : { missingText }),
|
|
1192
1447
|
...(readContradiction === null ? {} : { readContradiction }),
|
|
1448
|
+
...(hiddenBox === null ? {} : { hiddenBox }),
|
|
1193
1449
|
});
|
|
1194
1450
|
}
|
|
1195
1451
|
catch (_error) {
|
|
@@ -2141,15 +2397,22 @@ function missingApiPrefixSentence(request) {
|
|
|
2141
2397
|
/**
|
|
2142
2398
|
* The sentence for a failing assertion whose awaited text a same-page API response carried.
|
|
2143
2399
|
*
|
|
2144
|
-
*
|
|
2145
|
-
* did not render it — and stops there. Which
|
|
2146
|
-
*
|
|
2147
|
-
* step is the whole instruction
|
|
2400
|
+
* With proof ({@link TraceMissingText.dropped}) it names the response and the fact the trace
|
|
2401
|
+
* proves — the value reached the browser and the page did not render it — and stops there. Which
|
|
2402
|
+
* cut drops it (a slice, a filter, a limit) is in the component that maps the response, and the
|
|
2403
|
+
* note cannot see that component; naming the mapping step is the whole instruction
|
|
2404
|
+
* (xr-molecule-flash-m169).
|
|
2405
|
+
*
|
|
2406
|
+
* Without that proof the sentence says only what the trace shows — a response contained the text —
|
|
2407
|
+
* and makes no rendering claim: m194's awaited title was on the page, in a disabled button, while
|
|
2408
|
+
* the only response carrying it was the creation POST (a button failure is not a missing article).
|
|
2148
2409
|
*
|
|
2149
2410
|
* @param missing - The awaited text and the response that carried it.
|
|
2150
2411
|
* @returns The sentence the note leads with.
|
|
2151
2412
|
*/
|
|
2152
2413
|
function missingTextSentence(missing) {
|
|
2414
|
+
if (missing.dropped !== true)
|
|
2415
|
+
return `The response ${missing.method} ${missing.path} contained "${missing.text}".`;
|
|
2153
2416
|
return (`The API returned "${missing.text}" (in ${missing.method} ${missing.path}), but the page did ` +
|
|
2154
2417
|
`not render it — the page drops it before rendering (a slice/filter/limit in the component ` +
|
|
2155
2418
|
`that renders that list); find where that response is mapped.`);
|
|
@@ -2344,6 +2607,71 @@ function consoleLine(text) {
|
|
|
2344
2607
|
return (`${url} answered the app's index.html, not a font — the file is missing from ` +
|
|
2345
2608
|
`\`app/public/\` (or the asset import); copy/import it`);
|
|
2346
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
|
+
}
|
|
2347
2675
|
/**
|
|
2348
2676
|
* The note a failed suite run's result carries: what each failing test's page actually sent, and
|
|
2349
2677
|
* what came back.
|
|
@@ -2482,6 +2810,10 @@ export function failureTraceNote(items, target, earlierFailures = [], apiPrefixe
|
|
|
2482
2810
|
const wrapped = traced.some((item) => item.requests.some((request) => request.sent !== undefined && wrappedInData(request.sent)));
|
|
2483
2811
|
// The "one of these requests failing" answer is only honest when a listed request really failed.
|
|
2484
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);
|
|
2485
2817
|
// m145's shape: the app's HTTP client lost its base URL, so every API call reached the page
|
|
2486
2818
|
// origin without the prefix the app mounts its routes under. The prefix is READ — the caller's
|
|
2487
2819
|
// client config plus what the traces show — never assumed to be `/api`.
|
|
@@ -2746,14 +3078,19 @@ export function failureTraceNote(items, target, earlierFailures = [], apiPrefixe
|
|
|
2746
3078
|
// first, before any row, is that the value arrived and the page dropped it (m169's missing
|
|
2747
3079
|
// "trending" came back from GET /api/tags while the sidebar rendered a cut list). Each distinct
|
|
2748
3080
|
// response is named once; the note makes no claim about which cut did it.
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
3081
|
+
// One sentence per response; a trace that PROVES the drop upgrades one that only saw the same
|
|
3082
|
+
// response carry the text.
|
|
3083
|
+
const missingByResponse = new Map();
|
|
3084
|
+
for (const item of traced) {
|
|
3085
|
+
if (item.missingText === undefined)
|
|
3086
|
+
continue;
|
|
3087
|
+
const missing = item.missingText;
|
|
3088
|
+
const key = `${missing.method} ${missing.path}\u0000${missing.text}`;
|
|
3089
|
+
const seen = missingByResponse.get(key);
|
|
3090
|
+
if (seen === undefined || (seen.dropped !== true && missing.dropped === true))
|
|
3091
|
+
missingByResponse.set(key, missing);
|
|
3092
|
+
}
|
|
3093
|
+
const missingTexts = [...missingByResponse.values()];
|
|
2757
3094
|
const missingTextLead = missingTexts.length === 0
|
|
2758
3095
|
? ''
|
|
2759
3096
|
: `${missingTexts.slice(0, 2).map(missingTextSentence).join(' ')}\n\n`;
|
|
@@ -2841,13 +3178,17 @@ export function failureTraceNote(items, target, earlierFailures = [], apiPrefixe
|
|
|
2841
3178
|
'action sent no request at all, so there is no response to read. The page changed ' +
|
|
2842
3179
|
'route and fetched nothing, which is why it still shows the previous route’s data: ' +
|
|
2843
3180
|
'fix how the page reads the route, not the selector.'
|
|
2844
|
-
:
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
'
|
|
2849
|
-
|
|
2850
|
-
|
|
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.') +
|
|
2851
3192
|
prefixAdvice +
|
|
2852
3193
|
(wrapped
|
|
2853
3194
|
? ' The API received a body wrapped in `data`: app-http’s `post(url, data, config)` / ' +
|
package/dist/types.d.ts
CHANGED
|
@@ -63,14 +63,34 @@ export interface SuiteBudgetPin {
|
|
|
63
63
|
/**
|
|
64
64
|
* The Playwright flags that carry the suite's own settings onto the command
|
|
65
65
|
* (`--timeout=15000`, `--workers=1`), as the consumer's own scan reads them. Empty when the
|
|
66
|
-
* difference is in settings no flag carries
|
|
66
|
+
* difference is in settings no flag carries.
|
|
67
67
|
*/
|
|
68
68
|
flags: string[];
|
|
69
|
+
/** One sentence for the run's result: which of the suite's own settings this run uses. */
|
|
70
|
+
note: string;
|
|
69
71
|
/**
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
+
* A config file the consumer wrote OUTSIDE the supplied suite, which re-imports the config the
|
|
73
|
+
* run selected and pins the settings no Playwright flag can carry (`expect.timeout`,
|
|
74
|
+
* `use.actionTimeout`, `use.navigationTimeout`). When present, the run is launched with
|
|
75
|
+
* `--config <this path>` in place of the config it named, so those settings are the suite's own
|
|
76
|
+
* rather than the executor's. Absent when the consumer could not write one.
|
|
72
77
|
*/
|
|
73
|
-
|
|
78
|
+
config?: string;
|
|
79
|
+
/**
|
|
80
|
+
* Every setting the derived config ({@link config}) pins at the suite's own value, by the scan's
|
|
81
|
+
* key (`timeout`, `expect.timeout`, `actionTimeout`, `navigationTimeout`), in milliseconds as
|
|
82
|
+
* digits. Empty when no derived config was placed.
|
|
83
|
+
*/
|
|
84
|
+
values?: Record<string, string>;
|
|
85
|
+
/**
|
|
86
|
+
* Every setting the run's own config makes different from the suite's, as the consumer's scan
|
|
87
|
+
* read them: the value this run's config writes and the suite's own. The tool reports the ones
|
|
88
|
+
* its placement did NOT put back, so a result never claims a drift it carried.
|
|
89
|
+
*/
|
|
90
|
+
drift?: Record<string, {
|
|
91
|
+
used: string;
|
|
92
|
+
suite: string;
|
|
93
|
+
}>;
|
|
74
94
|
}
|
|
75
95
|
/**
|
|
76
96
|
* Configuration for building the tool set.
|
|
@@ -188,7 +208,8 @@ export interface ToolBuildConfig {
|
|
|
188
208
|
* different settings, asked BEFORE the run executes. The consumer knows where the suite and its
|
|
189
209
|
* base config live; this shared package does not, so the reading is the consumer's and the
|
|
190
210
|
* mechanics — putting the flags on the runner's own arguments, before the pipeline and outside
|
|
191
|
-
* any redirect or group closer
|
|
211
|
+
* any redirect or group closer, and pointing `--config` at the derived config that carries the
|
|
212
|
+
* settings no flag can — are this tool's.
|
|
192
213
|
*
|
|
193
214
|
* Called for a Playwright suite command only, and only the LAST suite step is touched. A run
|
|
194
215
|
* that already keeps the suite's settings, or a sandbox with no supplied suite, answers `null`.
|
|
@@ -196,7 +217,7 @@ export interface ToolBuildConfig {
|
|
|
196
217
|
*
|
|
197
218
|
* @param command - The shell command, as the model sent it.
|
|
198
219
|
* @param cwd - The resolved working directory it will run in.
|
|
199
|
-
* @returns The flags and the sentence, or null when nothing is put back.
|
|
220
|
+
* @returns The flags, the derived config and the sentence, or null when nothing is put back.
|
|
200
221
|
*/
|
|
201
222
|
suiteBudget?: (command: string, cwd: string) => Promise<SuiteBudgetPin | null | undefined>;
|
|
202
223
|
/** Post-write hook (e.g. auto-format via Prettier/ESLint). Called after every write_file/edit_file. */
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAC/B,yFAAyF;IACzF,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAE5B;;;;;;OAMG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAA;IAEzB,6CAA6C;IAC7C,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;IAEvC,qEAAqE;IACrE,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAEvD,qBAAqB;IACrB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAEvC,mCAAmC;IACnC,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAAA;KAAE,CAAC,CAAC,CAAA;IAEnF;;;OAGG;IACH,GAAG,CACD,OAAO,EAAE,MAAM,EACf,IAAI,CAAC,EAAE;QACL,GAAG,CAAC,EAAE,MAAM,CAAA;QACZ,OAAO,CAAC,EAAE,MAAM,CAAA;QAChB;;;;;WAKG;QACH,QAAQ,CAAC,EAAE,MAAM,CAAA;KAClB,GACA,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CACjE;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,cAAc;IAC7B;;;;OAIG;IACH,KAAK,EAAE,MAAM,EAAE,CAAA;IACf
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|