@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/dist/trace-artifacts.js
CHANGED
|
@@ -197,6 +197,11 @@ function messageOf(value) {
|
|
|
197
197
|
return null
|
|
198
198
|
}
|
|
199
199
|
|
|
200
|
+
// A fill/type/selectOption records what the test typed; the note compares it with the body the
|
|
201
|
+
// page submitted (m173: the Settings form sent bio:"" although the test had just typed a value).
|
|
202
|
+
// Only those actions keep it, so a click's selector does not ride every row.
|
|
203
|
+
const FILLING_ACTION = /\\.(?:fill|type|pressSequentially|selectOption)$/
|
|
204
|
+
|
|
200
205
|
// The text a failing assertion waited for, named by its own error: a \`has-text(…)\` CSS
|
|
201
206
|
// pseudo-class, a \`getByText(…)\` locator, or a \`toContainText(…)\` expectation. A strict-mode
|
|
202
207
|
// violation is not this shape — two elements DID match the text — and neither is a wait on a
|
|
@@ -446,14 +451,19 @@ for (const one of found.slice(0, MAX_TRACES)) {
|
|
|
446
451
|
if (!entry || entry.type !== 'before') continue
|
|
447
452
|
if (typeof entry.class !== 'string' || typeof entry.method !== 'string') continue
|
|
448
453
|
if (entry.class === 'Test' || typeof entry.startTime !== 'number') continue
|
|
454
|
+
const api = entry.class + '.' + entry.method
|
|
455
|
+
const params = entry.params && typeof entry.params === 'object' ? entry.params : {}
|
|
456
|
+
const filling = FILLING_ACTION.test(api)
|
|
449
457
|
const action = {
|
|
450
|
-
api:
|
|
458
|
+
api: api,
|
|
451
459
|
start: entry.startTime,
|
|
452
460
|
end: typeof ended[entry.callId] === 'number' ? ended[entry.callId] : null,
|
|
453
461
|
context: ctx.id,
|
|
454
462
|
pageId: typeof entry.callId === 'string' ? pageByCall[entry.callId] : undefined,
|
|
455
463
|
frameId: undefined,
|
|
456
464
|
stepId: typeof entry.stepId === 'string' ? entry.stepId : undefined,
|
|
465
|
+
...(filling && typeof params.selector === 'string' ? { selector: params.selector } : {}),
|
|
466
|
+
...(filling && typeof params.value === 'string' ? { value: params.value } : {}),
|
|
457
467
|
}
|
|
458
468
|
if (action.pageId !== undefined) {
|
|
459
469
|
for (const frame of ctx.frames) {
|
|
@@ -1054,6 +1064,8 @@ export function parseTraceArtifacts(stdout) {
|
|
|
1054
1064
|
...(typeof a.context === 'string' ? { context: a.context } : {}),
|
|
1055
1065
|
...(typeof a.pageId === 'string' ? { pageId: a.pageId } : {}),
|
|
1056
1066
|
...(typeof a.frameId === 'string' ? { frameId: a.frameId } : {}),
|
|
1067
|
+
...(typeof a.selector === 'string' && a.selector ? { selector: a.selector } : {}),
|
|
1068
|
+
...(typeof a.value === 'string' ? { value: a.value } : {}),
|
|
1057
1069
|
})),
|
|
1058
1070
|
...(otherContexts.length === 0 ? {} : { otherContexts }),
|
|
1059
1071
|
...(raw.unattributed === true ? { unattributed: true } : {}),
|
|
@@ -1589,6 +1601,33 @@ function credentialExchangeBody(sent) {
|
|
|
1589
1601
|
return false;
|
|
1590
1602
|
return (CREDENTIAL_IDENTITY.test(text) || /"(?:credentials|grant_type|refresh_token)"\s*:/i.test(text));
|
|
1591
1603
|
}
|
|
1604
|
+
/**
|
|
1605
|
+
* Whether a request PATH asks the app to mint a credential — a login, a registration, a token
|
|
1606
|
+
* endpoint — as opposed to a protected resource that presents one.
|
|
1607
|
+
*
|
|
1608
|
+
* This is the path half of {@link isCredentialExchange}, exported so a caller holding only a
|
|
1609
|
+
* `METHOD path` reads the same classification: a Hurl failing entry says
|
|
1610
|
+
* `POST /api/users/login` and nothing about the body, and the whole-suite note must not explain
|
|
1611
|
+
* a wrong-password login that answered 200 as a cookie-jar effect (xr-molecule-flash-m176).
|
|
1612
|
+
*
|
|
1613
|
+
* @param path - The request path (`/api/users/login`); a query or fragment is ignored.
|
|
1614
|
+
* @param authEndpoints - The endpoints the app's auth config names (`/users/login`).
|
|
1615
|
+
* @returns True when the path names a credential exchange.
|
|
1616
|
+
*/
|
|
1617
|
+
export function isCredentialExchangePath(path, authEndpoints = []) {
|
|
1618
|
+
const clean = (path.split(/[?#]/)[0] ?? '').replace(/\/+$/, '');
|
|
1619
|
+
if (clean === '')
|
|
1620
|
+
return false;
|
|
1621
|
+
// The app's own config names it: matched as a path suffix, because an auth config writes the
|
|
1622
|
+
// endpoint relative to its API base (`/users/login`) while the trace records the mounted path
|
|
1623
|
+
// (`/api/users/login`).
|
|
1624
|
+
if (authEndpoints.some((endpoint) => {
|
|
1625
|
+
const named = `/${String(endpoint).replace(/^\/+|\/+$/g, '')}`;
|
|
1626
|
+
return named.length > 1 && (clean === named || clean.endsWith(named));
|
|
1627
|
+
}))
|
|
1628
|
+
return true;
|
|
1629
|
+
return CREDENTIAL_EXCHANGE_PATH.test(clean);
|
|
1630
|
+
}
|
|
1592
1631
|
/**
|
|
1593
1632
|
* Whether a refused request is a CREDENTIAL EXCHANGE: the app was asked to mint a token (a login,
|
|
1594
1633
|
* a registration, a token endpoint), so it never needed one attached.
|
|
@@ -1613,15 +1652,7 @@ function isCredentialExchange(request, authEndpoints) {
|
|
|
1613
1652
|
const path = sameOriginPath(request);
|
|
1614
1653
|
if (path === null)
|
|
1615
1654
|
return false;
|
|
1616
|
-
|
|
1617
|
-
// endpoint relative to its API base (`/users/login`) while the trace records the mounted path
|
|
1618
|
-
// (`/api/users/login`).
|
|
1619
|
-
if (authEndpoints.some((endpoint) => {
|
|
1620
|
-
const named = `/${String(endpoint).replace(/^\/+|\/+$/g, '')}`;
|
|
1621
|
-
return named.length > 1 && (path === named || path.endsWith(named));
|
|
1622
|
-
}))
|
|
1623
|
-
return true;
|
|
1624
|
-
if (CREDENTIAL_EXCHANGE_PATH.test(path))
|
|
1655
|
+
if (isCredentialExchangePath(path, authEndpoints))
|
|
1625
1656
|
return true;
|
|
1626
1657
|
return credentialExchangeBody(request.sent);
|
|
1627
1658
|
}
|
|
@@ -2003,6 +2034,131 @@ function missingTextSentence(missing) {
|
|
|
2003
2034
|
`not render it — the page drops it before rendering (a slice/filter/limit in the component ` +
|
|
2004
2035
|
`that renders that list); find where that response is mapped.`);
|
|
2005
2036
|
}
|
|
2037
|
+
/**
|
|
2038
|
+
* The `name` attribute a locator's selector reads (`textarea[name="bio"]` → `bio`), or null when
|
|
2039
|
+
* the selector names no field.
|
|
2040
|
+
*
|
|
2041
|
+
* Matching the form field to the request body's key is the whole point: a body that carries
|
|
2042
|
+
* `"bio": ""` says nothing unless the trace also says the test typed a bio.
|
|
2043
|
+
*
|
|
2044
|
+
* @param selector - The selector a `fill`/`type`/`selectOption` action carried.
|
|
2045
|
+
* @returns The field name, or null.
|
|
2046
|
+
*/
|
|
2047
|
+
function fieldNameOf(selector) {
|
|
2048
|
+
const quoted = /\[\s*name\s*=\s*(["'])((?:(?!\1).)*)\1\s*\]/.exec(selector);
|
|
2049
|
+
if (quoted !== null)
|
|
2050
|
+
return quoted[2] === '' ? null : quoted[2];
|
|
2051
|
+
const bare = /\[\s*name\s*=\s*([^\s\]]+)\s*\]/.exec(selector);
|
|
2052
|
+
return bare === null ? null : (bare[1] ?? null);
|
|
2053
|
+
}
|
|
2054
|
+
/**
|
|
2055
|
+
* Whether a JSON request body carries `key` with an EMPTY value (`""` or null), looking inside the
|
|
2056
|
+
* wrapper object an API nests its entity under (`user`, `data`, `props`).
|
|
2057
|
+
*
|
|
2058
|
+
* A body that does not parse, or carries the key with a real value, is not evidence: the note only
|
|
2059
|
+
* claims the page sent a field empty when it did.
|
|
2060
|
+
*
|
|
2061
|
+
* @param sent - The body the page sent, as the trace recorded it.
|
|
2062
|
+
* @param key - The field name from the filled input's `name` attribute.
|
|
2063
|
+
* @returns True when the body carries that key empty.
|
|
2064
|
+
*/
|
|
2065
|
+
function bodyCarriesEmptyField(sent, key) {
|
|
2066
|
+
let parsed;
|
|
2067
|
+
try {
|
|
2068
|
+
parsed = JSON.parse(sent);
|
|
2069
|
+
}
|
|
2070
|
+
catch (_error) {
|
|
2071
|
+
// A truncated body (the trace read bounds what it keeps) names no field confidently.
|
|
2072
|
+
return false;
|
|
2073
|
+
}
|
|
2074
|
+
const read = (value, level) => {
|
|
2075
|
+
if (level > 4 || value === null || typeof value !== 'object')
|
|
2076
|
+
return null;
|
|
2077
|
+
if (Array.isArray(value)) {
|
|
2078
|
+
for (const item of value) {
|
|
2079
|
+
const found = read(item, level + 1);
|
|
2080
|
+
if (found !== null)
|
|
2081
|
+
return found;
|
|
2082
|
+
}
|
|
2083
|
+
return null;
|
|
2084
|
+
}
|
|
2085
|
+
const record = value;
|
|
2086
|
+
if (Object.prototype.hasOwnProperty.call(record, key)) {
|
|
2087
|
+
return record[key] === '' || record[key] === null;
|
|
2088
|
+
}
|
|
2089
|
+
for (const nested of Object.values(record)) {
|
|
2090
|
+
const found = read(nested, level + 1);
|
|
2091
|
+
if (found !== null)
|
|
2092
|
+
return found;
|
|
2093
|
+
}
|
|
2094
|
+
return null;
|
|
2095
|
+
};
|
|
2096
|
+
return read(parsed, 0) === true;
|
|
2097
|
+
}
|
|
2098
|
+
/**
|
|
2099
|
+
* The field a test typed into that the same page submitted empty, when the trace recorded both.
|
|
2100
|
+
*
|
|
2101
|
+
* This is the Settings race of xr-molecule-flash-m173: `page.fill('textarea[name="bio"]', newBio)`
|
|
2102
|
+
* then `button[type=submit]`, and the `PUT /api/user` carried `"bio": ""`. The form's seeding
|
|
2103
|
+
* effect re-ran between the fill and the submit — on the deployed URL the background session
|
|
2104
|
+
* verification lands later than it does locally, and it handed the form a new user object. The
|
|
2105
|
+
* reading only pairs facts the trace holds: the filled value (its `selector`/`value`), the field
|
|
2106
|
+
* name the selector names, and the next call to the app's API on that page whose body carries
|
|
2107
|
+
* that key empty.
|
|
2108
|
+
*
|
|
2109
|
+
* @param item - One traced failure.
|
|
2110
|
+
* @returns The evidence, or null when no filled field was submitted empty.
|
|
2111
|
+
*/
|
|
2112
|
+
function formResetOf(item) {
|
|
2113
|
+
const requests = [...(item.lastAction?.requests ?? []), ...item.requests];
|
|
2114
|
+
// A trace read before `actions` existed (or a hand-built one) carries none: no claim.
|
|
2115
|
+
for (const action of item.actions ?? []) {
|
|
2116
|
+
if (action.selector === undefined || action.value === undefined || action.value === '')
|
|
2117
|
+
continue;
|
|
2118
|
+
const field = fieldNameOf(action.selector);
|
|
2119
|
+
if (field === null)
|
|
2120
|
+
continue;
|
|
2121
|
+
let next = null;
|
|
2122
|
+
for (const request of requests) {
|
|
2123
|
+
if (!/^(?:POST|PUT|PATCH)$/.test(request.method) || request.sent === undefined)
|
|
2124
|
+
continue;
|
|
2125
|
+
// The submit follows the typing; a request that started before it cannot have sent it.
|
|
2126
|
+
if (typeof request.startedAt === 'number' && request.startedAt < action.start)
|
|
2127
|
+
continue;
|
|
2128
|
+
// Same page only: another context's set-up call is not this form's submit.
|
|
2129
|
+
if (action.pageId !== undefined &&
|
|
2130
|
+
request.pageId !== undefined &&
|
|
2131
|
+
action.pageId !== request.pageId)
|
|
2132
|
+
continue;
|
|
2133
|
+
if (!bodyCarriesEmptyField(request.sent, field))
|
|
2134
|
+
continue;
|
|
2135
|
+
const earlier = next === null ||
|
|
2136
|
+
(typeof request.startedAt === 'number' &&
|
|
2137
|
+
(typeof next.startedAt !== 'number' || request.startedAt < next.startedAt));
|
|
2138
|
+
if (earlier)
|
|
2139
|
+
next = request;
|
|
2140
|
+
}
|
|
2141
|
+
if (next !== null)
|
|
2142
|
+
return { value: action.value, selector: action.selector, field, request: next };
|
|
2143
|
+
}
|
|
2144
|
+
return null;
|
|
2145
|
+
}
|
|
2146
|
+
/**
|
|
2147
|
+
* The sentence for a submitted form field the test had just typed into, sent empty.
|
|
2148
|
+
*
|
|
2149
|
+
* It states the two recorded facts and the one inference they support — the form re-seeded after
|
|
2150
|
+
* the typing — then the fix at the source: copy the loaded data into the form once, on its first
|
|
2151
|
+
* load, instead of on every change of the data it was seeded from.
|
|
2152
|
+
*
|
|
2153
|
+
* @param reset - The filled field and the submit that carried it empty.
|
|
2154
|
+
* @returns The sentence the note leads with.
|
|
2155
|
+
*/
|
|
2156
|
+
function formResetSentence(reset) {
|
|
2157
|
+
return (`The test typed "${reset.value}" into ${reset.selector}, but ` +
|
|
2158
|
+
`${requestLabel(reset.request)} sent "${reset.field}": "" — the form's state was reset after ` +
|
|
2159
|
+
`typing (an effect re-seeding the form from data that changed identity ran again after the ` +
|
|
2160
|
+
`test typed). Seed the form once, not every time that data changes.`);
|
|
2161
|
+
}
|
|
2006
2162
|
/**
|
|
2007
2163
|
* The note a failed suite run's result carries: what each failing test's page actually sent, and
|
|
2008
2164
|
* what came back.
|
|
@@ -2042,6 +2198,14 @@ function missingTextSentence(missing) {
|
|
|
2042
2198
|
* context and the assertion's page cannot be identified, the note lists the requests and makes NO
|
|
2043
2199
|
* causal claim — no "the test's last action", no "the page stayed at …" — instead of merging them.
|
|
2044
2200
|
*
|
|
2201
|
+
* A field the test filled and the page submitted EMPTY ({@link formResetOf}) leads the note: the
|
|
2202
|
+
* trace holds both facts — the value typed into `textarea[name="bio"]` and the `PUT /api/user`
|
|
2203
|
+
* whose body carried `"bio": ""` — so the form's state was reset between the typing and the
|
|
2204
|
+
* submit. The note names the effect that re-seeds a form from data whose identity changed and the
|
|
2205
|
+
* fix at the source (seed the form once). xr-molecule-flash-m173 lost a deploy-and-walk cycle to
|
|
2206
|
+
* this on the deployed URL only, where the background session verification lands later than it
|
|
2207
|
+
* does locally.
|
|
2208
|
+
*
|
|
2045
2209
|
* WHICH rows it spends is evidence, not artifact order (xr-molecule-flash-m112 +53:46): a row
|
|
2046
2210
|
* needs a recorded response, a real failure or a console line ({@link traceRowEvidence}), rows are
|
|
2047
2211
|
* deduplicated by the test's location, and they are ordered real failure → repeated from an
|
|
@@ -2402,7 +2566,19 @@ export function failureTraceNote(items, target, earlierFailures = [], apiPrefixe
|
|
|
2402
2566
|
const missingTextLead = missingTexts.length === 0
|
|
2403
2567
|
? ''
|
|
2404
2568
|
: `${missingTexts.slice(0, 2).map(missingTextSentence).join(' ')}\n\n`;
|
|
2405
|
-
|
|
2569
|
+
// A field the test typed and the page submitted empty: the form re-seeded itself after the
|
|
2570
|
+
// typing (m173's Settings race — the deployed URL lands the background session verification
|
|
2571
|
+
// later than a local run, and its new user object re-ran the form's seeding effect between the
|
|
2572
|
+
// fill and the submit). It is a direct cause of the failing request, so it leads the note.
|
|
2573
|
+
const formResets = [
|
|
2574
|
+
...new Map(traced
|
|
2575
|
+
.map(formResetOf)
|
|
2576
|
+
.filter((reset) => reset !== null)
|
|
2577
|
+
.map((reset) => [`${reset.request.method} ${reset.request.path}\u0000${reset.field}`, reset])).values(),
|
|
2578
|
+
];
|
|
2579
|
+
const formResetLead = formResets.length === 0 ? '' : `${formResets.slice(0, 2).map(formResetSentence).join(' ')}\n\n`;
|
|
2580
|
+
return (formResetLead +
|
|
2581
|
+
missingTextLead +
|
|
2406
2582
|
transportLead +
|
|
2407
2583
|
setupOriginLead +
|
|
2408
2584
|
stalledLead +
|
package/dist/utilities.d.ts
CHANGED
|
@@ -171,15 +171,19 @@ export declare const PLAYWRIGHT_FAILURES_INLINE_ENV = "PLAYWRIGHT_LIST_PRINT_FAI
|
|
|
171
171
|
* is exactly when the executor reads the log, and without it a running log carries `✘` rows and
|
|
172
172
|
* nothing else (xr-molecule-flash-m99 spent ~90 s on greps of one, then fixed what it guessed).
|
|
173
173
|
*
|
|
174
|
-
* Only the
|
|
175
|
-
* something: `npx playwright test --list` takes two seconds and writes nothing per test, so
|
|
176
|
-
* neither reporter nor `--retries=0` is added to it ({@link isPlaywrightRunCommand}).
|
|
174
|
+
* Only the step that runs the suite is touched, and only its reporter, and only when that step
|
|
175
|
+
* runs something: `npx playwright test --list` takes two seconds and writes nothing per test, so
|
|
176
|
+
* neither reporter nor `--retries=0` is added to it ({@link isPlaywrightRunCommand}). A trailing
|
|
177
|
+
* status step (`; echo EXIT $?`, `&& true`) is not the run and is skipped when the run step is
|
|
178
|
+
* found ({@link isStatusOnlyStep}); a run whose output is redirected to a file is still readable
|
|
179
|
+
* through that file, which the launch records as the task's own output.
|
|
177
180
|
*
|
|
178
181
|
* A command that starts the suite MORE THAN ONCE is not launched at all: every run it carries is
|
|
179
182
|
* a whole walk of the same suite against the same app, and only the last step is rewritten, so the
|
|
180
183
|
* earlier walks run unmeasured for nothing. X1 xr-molecule-flash-m157 (+14:02) chained two whole
|
|
181
184
|
* walks of one 138-test suite with `;` — 4.5 minutes each — and the second measured nothing the
|
|
182
|
-
* first had not. The platform
|
|
185
|
+
* first had not. The platform refuses a second CONCURRENT run against one app and database by the
|
|
186
|
+
* ONE rule ({@link suiteRunInTheWay}) — for every pair, whatever uid a run mints — and this is the
|
|
183
187
|
* same hazard inside one command ({@link suiteRunStatements}). `error` then carries the refusal,
|
|
184
188
|
* naming each suite step and the ONE command to send instead (the last suite step, rewritten as
|
|
185
189
|
* this function rewrites a run), and nothing is launched.
|
|
@@ -944,10 +948,21 @@ export interface UniversalCause {
|
|
|
944
948
|
}
|
|
945
949
|
/**
|
|
946
950
|
* How many tests a run must have walked — with nothing passing since its first failure — before
|
|
947
|
-
* "the page renders nothing" can be its one cause.
|
|
948
|
-
*
|
|
949
|
-
|
|
950
|
-
|
|
951
|
+
* "the page renders nothing" can be its one cause.
|
|
952
|
+
*
|
|
953
|
+
* THREE, and the number is the cost of the alternative. The old floor of 15 was chosen when the
|
|
954
|
+
* reading was only offered at a completed run; once it STOPS the walk, every test waited for is
|
|
955
|
+
* a test whose result is thrown away, and a blank page costs its per-test budget each time
|
|
956
|
+
* (m177: 18 tests, ~4.2 minutes, on a page whose thrown error was visible in the first
|
|
957
|
+
* `pageerror`). Three is the smallest count that is not one page's own bug: the cause additionally
|
|
958
|
+
* needs three DISTINCT spec files (`UNIVERSAL_CAUSE_FILES`, or the files a narrowed run selected),
|
|
959
|
+
* each spec waiting for its OWN first element, and no test passing after the first failure. A page
|
|
960
|
+
* that renders misses elements, but three unrelated specs in three files failing at three
|
|
961
|
+
* different first locators with nothing passing in between is the page, not three bugs. At the
|
|
962
|
+
* suite's own 5 s action timeout that verdict lands ~20 s into the walk, so the early stop costs
|
|
963
|
+
* about one test's worth of time instead of fifteen.
|
|
964
|
+
*/
|
|
965
|
+
export declare const EMPTY_PAGE_CAUSE_TESTS = 3;
|
|
951
966
|
/**
|
|
952
967
|
* How many tests a connection-class cause must cover before the run is stopped even when they
|
|
953
968
|
* are all in one file — see {@link universalCause}.
|
package/dist/utilities.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utilities.d.ts","sourceRoot":"","sources":["../src/utilities.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAWH,OAAO,EAAsB,KAAK,cAAc,EAAE,MAAM,mBAAmB,CAAA;AAE3E,OAAO,EACL,KAAK,mBAAmB,EAOzB,MAAM,oBAAoB,CAAA;AAE3B;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAS5C;AAED;;;;;;GAMG;AACH,eAAO,MAAM,iBAAiB,GAAI,GAAG,MAAM,KAAG,MAEE,CAAA;AAuGhD;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAK/C;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAErD;AAKD;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAsCzD;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAGnD;AAED;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,OAAO,GAAG,MAAM,CAUpF;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAElE;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAQnE;AAuDD;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,8BAA8B,0CAA0C,CAAA;
|
|
1
|
+
{"version":3,"file":"utilities.d.ts","sourceRoot":"","sources":["../src/utilities.ts"],"names":[],"mappings":"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"}
|
package/dist/utilities.js
CHANGED
|
@@ -350,6 +350,25 @@ const PRINTING_REPORTERS = new Set(['list', 'line', 'dot', 'basic', 'spec']);
|
|
|
350
350
|
* edited `app/src` while the run was still walking — which voids the run.
|
|
351
351
|
*/
|
|
352
352
|
export const PLAYWRIGHT_FAILURES_INLINE_ENV = 'PLAYWRIGHT_LIST_PRINT_FAILURES_INLINE';
|
|
353
|
+
/**
|
|
354
|
+
* Whether a command step only reports or discards the status of the steps before it: `echo EXIT
|
|
355
|
+
* $?`, `echo $?`, `printf …`, `true`, `exit 0`.
|
|
356
|
+
*
|
|
357
|
+
* The shape models and the platform's own re-run notes send routinely ends on one of these —
|
|
358
|
+
* `npx playwright test … > /tmp/pw.log 2>&1; echo EXIT $?` — and reading the split's last part as
|
|
359
|
+
* the run withheld the reporter, the inline-failure switch and `--retries=0` from every one of
|
|
360
|
+
* them: the log then carried `✘` rows and nothing else while the wait note promised each failure's
|
|
361
|
+
* error under them (xr-molecule-flash-m173 +17:07, +39:14). Whitespace and a group's closing
|
|
362
|
+
* punctuation are not part of the step; {@link streamingBackgroundCommand} strips those before
|
|
363
|
+
* asking, and strips them here too for the split it is given.
|
|
364
|
+
*
|
|
365
|
+
* @param step - One statement of the command, as the `&&`/`||`/`;` split left it.
|
|
366
|
+
* @returns True when the step runs nothing but a status report.
|
|
367
|
+
*/
|
|
368
|
+
function isStatusOnlyStep(step) {
|
|
369
|
+
const text = step.replace(/[\s)}]+$/, '').trim();
|
|
370
|
+
return /^(?:echo|printf)\b/.test(text) || /^true$/.test(text) || /^exit(?:\s+\S+)?$/.test(text);
|
|
371
|
+
}
|
|
353
372
|
/**
|
|
354
373
|
* Rewrite a command about to run in the background so its log grows while it
|
|
355
374
|
* runs. Two shapes left the log empty until exit, and an empty log is what
|
|
@@ -384,15 +403,19 @@ export const PLAYWRIGHT_FAILURES_INLINE_ENV = 'PLAYWRIGHT_LIST_PRINT_FAILURES_IN
|
|
|
384
403
|
* is exactly when the executor reads the log, and without it a running log carries `✘` rows and
|
|
385
404
|
* nothing else (xr-molecule-flash-m99 spent ~90 s on greps of one, then fixed what it guessed).
|
|
386
405
|
*
|
|
387
|
-
* Only the
|
|
388
|
-
* something: `npx playwright test --list` takes two seconds and writes nothing per test, so
|
|
389
|
-
* neither reporter nor `--retries=0` is added to it ({@link isPlaywrightRunCommand}).
|
|
406
|
+
* Only the step that runs the suite is touched, and only its reporter, and only when that step
|
|
407
|
+
* runs something: `npx playwright test --list` takes two seconds and writes nothing per test, so
|
|
408
|
+
* neither reporter nor `--retries=0` is added to it ({@link isPlaywrightRunCommand}). A trailing
|
|
409
|
+
* status step (`; echo EXIT $?`, `&& true`) is not the run and is skipped when the run step is
|
|
410
|
+
* found ({@link isStatusOnlyStep}); a run whose output is redirected to a file is still readable
|
|
411
|
+
* through that file, which the launch records as the task's own output.
|
|
390
412
|
*
|
|
391
413
|
* A command that starts the suite MORE THAN ONCE is not launched at all: every run it carries is
|
|
392
414
|
* a whole walk of the same suite against the same app, and only the last step is rewritten, so the
|
|
393
415
|
* earlier walks run unmeasured for nothing. X1 xr-molecule-flash-m157 (+14:02) chained two whole
|
|
394
416
|
* walks of one 138-test suite with `;` — 4.5 minutes each — and the second measured nothing the
|
|
395
|
-
* first had not. The platform
|
|
417
|
+
* first had not. The platform refuses a second CONCURRENT run against one app and database by the
|
|
418
|
+
* ONE rule ({@link suiteRunInTheWay}) — for every pair, whatever uid a run mints — and this is the
|
|
396
419
|
* same hazard inside one command ({@link suiteRunStatements}). `error` then carries the refusal,
|
|
397
420
|
* naming each suite step and the ONE command to send instead (the last suite step, rewritten as
|
|
398
421
|
* this function rewrites a run), and nothing is launched.
|
|
@@ -446,10 +469,15 @@ export function streamingBackgroundCommand(command, context) {
|
|
|
446
469
|
}
|
|
447
470
|
// The last step that RUNS something. A trailing group closer is punctuation, not a step: a
|
|
448
471
|
// brace group writes it after a `;` (`{ …; }`), so reading the split's last part literally
|
|
449
|
-
// made `}` the step and withheld every rewrite from a run inside a group.
|
|
472
|
+
// made `}` the step and withheld every rewrite from a run inside a group. A trailing status
|
|
473
|
+
// step is not the run either: `… > /tmp/pw.log 2>&1; echo EXIT $?` ends on the echo, and
|
|
474
|
+
// treating THAT as the run withheld the reporter, the inline-failure switch and `--retries=0`
|
|
475
|
+
// from the exact shape models and the platform's re-run notes send (xr-molecule-flash-m173
|
|
476
|
+
// +17:07, +39:14).
|
|
450
477
|
const parts = out.split(/&&|\|\||;/);
|
|
451
478
|
let stepIndex = parts.length - 1;
|
|
452
|
-
while (stepIndex > 0 &&
|
|
479
|
+
while (stepIndex > 0 &&
|
|
480
|
+
(/^[\s)}]*$/.test(parts[stepIndex] ?? '') || isStatusOnlyStep(parts[stepIndex] ?? '')))
|
|
453
481
|
stepIndex -= 1;
|
|
454
482
|
const rawLastStep = parts[stepIndex] ?? '';
|
|
455
483
|
// The step's own text, with the punctuation that closes the group it sits in taken off — the
|
|
@@ -459,8 +487,9 @@ export function streamingBackgroundCommand(command, context) {
|
|
|
459
487
|
// A command that starts the suite more than once is not launched: each statement is a whole walk
|
|
460
488
|
// of the same suite against the same app, only the last of them is rewritten, and the earlier
|
|
461
489
|
// ones measure nothing the first did. X1 xr-molecule-flash-m157 (+14:02) chained two whole walks
|
|
462
|
-
// of one 138-test suite with `;` (4.5 minutes each); the platform
|
|
463
|
-
//
|
|
490
|
+
// of one 138-test suite with `;` (4.5 minutes each); the platform refuses a second CONCURRENT
|
|
491
|
+
// run against one app and database by the ONE rule ({@link suiteRunInTheWay}) — for every pair,
|
|
492
|
+
// whatever uid a run mints — and this is the same hazard inside one command. The reading is
|
|
464
493
|
// {@link repeatedSuiteRunError}'s, so a foreground `exec_command` refuses the same commands.
|
|
465
494
|
const repeated = repeatedSuiteRunError(out);
|
|
466
495
|
if (repeated)
|
|
@@ -469,9 +498,17 @@ export function streamingBackgroundCommand(command, context) {
|
|
|
469
498
|
// "nothing is piped out of the last step" withheld the reporter, the inline-failure switch and
|
|
470
499
|
// `--retries=0` from exactly the runs whose output was filtered (xr-molecule-flash-m101
|
|
471
500
|
// +25:54); what the runner writes into its log does not depend on what reads the pipe. A
|
|
472
|
-
// REDIRECT
|
|
473
|
-
//
|
|
474
|
-
|
|
501
|
+
// REDIRECT used to withhold them too (`> out.txt`), but a redirect to a FILE is readable: the
|
|
502
|
+
// launch records that file as the task's own output (`outputFileByTask`, resolved from this
|
|
503
|
+
// same reader) and every wait reads it back. `> /tmp/pw-full.log 2>&1; echo EXIT $?` is the
|
|
504
|
+
// shape models and the platform's re-run notes send, so a run whose own redirect target is the
|
|
505
|
+
// one the launch will read is a readable run (xr-molecule-flash-m173 +17:07, +39:14). A target
|
|
506
|
+
// that is not a file (`/dev/null`, `2>&1`) still leaves the output unreadable.
|
|
507
|
+
const runRedirect = outputRedirectTarget(lastStep);
|
|
508
|
+
const finalPlaywrightRun = isPlaywrightRunCommand(lastStep) &&
|
|
509
|
+
(runRedirect === null
|
|
510
|
+
? !/>/.test(lastStep.replace(/\d?>&\d/g, ''))
|
|
511
|
+
: runRedirect === outputRedirectTarget(out));
|
|
475
512
|
/**
|
|
476
513
|
* Add a word to the suite runner's own arguments; false when the runner could not be placed.
|
|
477
514
|
*
|
|
@@ -483,14 +520,17 @@ export function streamingBackgroundCommand(command, context) {
|
|
|
483
520
|
* platform's runner shape (xr-molecule-flash-m129 +34:29); appending after a pipe hands the
|
|
484
521
|
* word to the filter (m102's dangling `--trace` flag, in the same shape).
|
|
485
522
|
*
|
|
486
|
-
*
|
|
487
|
-
*
|
|
488
|
-
*
|
|
489
|
-
*
|
|
523
|
+
* The suite's own segment is found wherever it sits ({@link lastSuiteRun}), because a trailing
|
|
524
|
+
* status step is not another command's output: in `… > /tmp/pw.log 2>&1; echo EXIT $?` the run
|
|
525
|
+
* is what the launcher measures, and the echo only repeats its status
|
|
526
|
+
* ({@link isStatusOnlyStep}). A suite the parse could not place — one inside a nested
|
|
527
|
+
* `sh -c '<command>'` string, whose offsets are positions in THAT string — is left exactly as
|
|
528
|
+
* written and the caller reports no rewrite. There is nowhere in this command the word
|
|
529
|
+
* belongs, and guessing at the text is what produced both failures above.
|
|
490
530
|
*/
|
|
491
531
|
const addToRun = (flag) => {
|
|
492
|
-
const
|
|
493
|
-
const runner =
|
|
532
|
+
const run = lastSuiteRun(out);
|
|
533
|
+
const runner = run ? run.located[run.at] : undefined;
|
|
494
534
|
if (!runner)
|
|
495
535
|
return false;
|
|
496
536
|
const at = statementArgumentEnd(out, runner.start, runner.end);
|
|
@@ -2344,10 +2384,21 @@ function failureBlockTests(text) {
|
|
|
2344
2384
|
export const UNIVERSAL_CAUSE_FILES = 3;
|
|
2345
2385
|
/**
|
|
2346
2386
|
* How many tests a run must have walked — with nothing passing since its first failure — before
|
|
2347
|
-
* "the page renders nothing" can be its one cause.
|
|
2348
|
-
*
|
|
2349
|
-
|
|
2350
|
-
|
|
2387
|
+
* "the page renders nothing" can be its one cause.
|
|
2388
|
+
*
|
|
2389
|
+
* THREE, and the number is the cost of the alternative. The old floor of 15 was chosen when the
|
|
2390
|
+
* reading was only offered at a completed run; once it STOPS the walk, every test waited for is
|
|
2391
|
+
* a test whose result is thrown away, and a blank page costs its per-test budget each time
|
|
2392
|
+
* (m177: 18 tests, ~4.2 minutes, on a page whose thrown error was visible in the first
|
|
2393
|
+
* `pageerror`). Three is the smallest count that is not one page's own bug: the cause additionally
|
|
2394
|
+
* needs three DISTINCT spec files (`UNIVERSAL_CAUSE_FILES`, or the files a narrowed run selected),
|
|
2395
|
+
* each spec waiting for its OWN first element, and no test passing after the first failure. A page
|
|
2396
|
+
* that renders misses elements, but three unrelated specs in three files failing at three
|
|
2397
|
+
* different first locators with nothing passing in between is the page, not three bugs. At the
|
|
2398
|
+
* suite's own 5 s action timeout that verdict lands ~20 s into the walk, so the early stop costs
|
|
2399
|
+
* about one test's worth of time instead of fifteen.
|
|
2400
|
+
*/
|
|
2401
|
+
export const EMPTY_PAGE_CAUSE_TESTS = 3;
|
|
2351
2402
|
/**
|
|
2352
2403
|
* A failure body whose whole complaint is a wait for an element the page never rendered:
|
|
2353
2404
|
* Playwright's `waiting for locator(…)` call log, its `element(s) not found` line, or the
|
|
@@ -2371,22 +2422,32 @@ function pageErrorInLog(text) {
|
|
|
2371
2422
|
return m?.[1]?.trim() || null;
|
|
2372
2423
|
}
|
|
2373
2424
|
/**
|
|
2374
|
-
* The "nothing has passed and nothing renders" cause:
|
|
2375
|
-
*
|
|
2376
|
-
*
|
|
2425
|
+
* The "nothing has passed and nothing renders" cause: the run's FIRST consecutive failures — from
|
|
2426
|
+
* its first failed test, with no pass since — are all locator waits, at least
|
|
2427
|
+
* {@link EMPTY_PAGE_CAUSE_TESTS} of them across {@link UNIVERSAL_CAUSE_FILES} different spec
|
|
2428
|
+
* files, and each waits for its own spec's first element.
|
|
2429
|
+
*
|
|
2430
|
+
* The file span stays at {@link UNIVERSAL_CAUSE_FILES} whatever the run selected: "the page
|
|
2431
|
+
* renders nothing" is a claim about the APP, and one file's failures can be that file's own setup
|
|
2432
|
+
* (a broken login helper fails every test of one spec at its first locator). A run narrowed to one
|
|
2433
|
+
* or two files still has the signature and connection causes for a whole-run stop.
|
|
2434
|
+
*
|
|
2435
|
+
* Stops the walk as soon as that verdict is readable, not after a fixed floor of failures have
|
|
2436
|
+
* been burned: the page error arrives with the first test, so every further test only repeats it.
|
|
2377
2437
|
*
|
|
2378
2438
|
* This is ONE cause even though the signatures differ — each spec asks for its own first element
|
|
2379
2439
|
* (`input[name="username"]`, `a.navbar-brand`, `h1`), so {@link repeatingFailure} cannot group
|
|
2380
2440
|
* them and {@link universalCause}'s signature test cannot see the run is over. xr-molecule-flash-
|
|
2381
2441
|
* m141 (+20:30) walked a built app that rendered nothing: 36 tests had failed, every one at its
|
|
2382
|
-
* first locator, and the note told the run to keep walking for another ~9 minutes.
|
|
2442
|
+
* first locator, and the note told the run to keep walking for another ~9 minutes. xr-molecule-
|
|
2443
|
+
* flash-m177 (+08:38 → +12:52) repeated it: 18 tests, one whole walk, on a page whose
|
|
2444
|
+
* `PAGEERROR No 'auth-client' provider bonded` was in the log from the first one.
|
|
2383
2445
|
*
|
|
2384
2446
|
* @param text - The log (ANSI already stripped).
|
|
2385
|
-
* @param threshold - The spec-file threshold this run is judged against ({@link universalCause}).
|
|
2386
2447
|
* @param found - The repeating cause {@link repeatingFailure} found, when it found one.
|
|
2387
2448
|
* @returns The cause, or null when the log is not this shape.
|
|
2388
2449
|
*/
|
|
2389
|
-
function emptyPageCause(text,
|
|
2450
|
+
function emptyPageCause(text, found) {
|
|
2390
2451
|
const marks = resultMarks(text);
|
|
2391
2452
|
const firstFailure = marks.indexOf('✘');
|
|
2392
2453
|
if (firstFailure === -1 || marks.slice(firstFailure).includes('✓'))
|
|
@@ -2404,10 +2465,15 @@ function emptyPageCause(text, threshold, found) {
|
|
|
2404
2465
|
// (which names the repeated error) says more than "the page renders nothing" would.
|
|
2405
2466
|
if (found !== null && found.message !== null && found.count >= tests.length)
|
|
2406
2467
|
return null;
|
|
2468
|
+
// The cause is the FIRST consecutive failures: `tests` starts at the first ✘ and no ✓ follows
|
|
2469
|
+
// it, so a page that has failed three tests in a row across three files has already said
|
|
2470
|
+
// everything the rest of the walk can say (m177 paid 4.2 minutes learning that).
|
|
2407
2471
|
if (tests.length < EMPTY_PAGE_CAUSE_TESTS)
|
|
2408
2472
|
return null;
|
|
2409
2473
|
const files = [...new Set(tests.map((t) => specFile(t.where)).filter(Boolean))];
|
|
2410
|
-
|
|
2474
|
+
// The blank-page claim is cross-file by construction (see the doc): a selected-file count
|
|
2475
|
+
// below the span does not shrink it, it removes the claim.
|
|
2476
|
+
if (files.length < UNIVERSAL_CAUSE_FILES)
|
|
2411
2477
|
return null;
|
|
2412
2478
|
const pageError = pageErrorInLog(text);
|
|
2413
2479
|
return {
|
|
@@ -2544,7 +2610,7 @@ export function universalCause(log, found, command) {
|
|
|
2544
2610
|
const threshold = selected === null ? UNIVERSAL_CAUSE_FILES : Math.min(UNIVERSAL_CAUSE_FILES, selected);
|
|
2545
2611
|
// Nothing has passed and nothing renders: EVERY spec so far is waiting for its own first
|
|
2546
2612
|
// element, so the signatures differ while the cause does not (xr-molecule-flash-m141).
|
|
2547
|
-
const emptyPage = emptyPageCause(text,
|
|
2613
|
+
const emptyPage = emptyPageCause(text, found);
|
|
2548
2614
|
if (emptyPage)
|
|
2549
2615
|
return emptyPage;
|
|
2550
2616
|
if (!found)
|