@molecule/api-ai-tools 1.9.16 → 1.9.18
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 +270 -15
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/output-filter.d.ts +5 -1
- package/dist/output-filter.d.ts.map +1 -1
- package/dist/output-filter.js +12 -3
- package/dist/server-command.d.ts +73 -0
- package/dist/server-command.d.ts.map +1 -0
- package/dist/server-command.js +180 -0
- package/dist/suite-overlap.d.ts +20 -1
- package/dist/suite-overlap.d.ts.map +1 -1
- package/dist/suite-overlap.js +48 -3
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +88 -7
- package/dist/trace-artifacts.d.ts +57 -5
- package/dist/trace-artifacts.d.ts.map +1 -1
- package/dist/trace-artifacts.js +336 -26
- package/dist/types.d.ts +39 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utilities.d.ts +52 -1
- package/dist/utilities.d.ts.map +1 -1
- package/dist/utilities.js +143 -23
- package/package.json +1 -1
package/dist/trace-artifacts.js
CHANGED
|
@@ -27,7 +27,6 @@
|
|
|
27
27
|
* @module
|
|
28
28
|
*/
|
|
29
29
|
import { locatedCommandSegments, statementArgumentEnd } from './command-position.js';
|
|
30
|
-
import { isDiagnosisRun } from './suite-overlap.js';
|
|
31
30
|
import { isPlaywrightRunCommand, redactSecrets } from './utilities.js';
|
|
32
31
|
/** How many traces one read reports, newest first. */
|
|
33
32
|
export const MAX_TRACE_ARTIFACTS = 12;
|
|
@@ -198,6 +197,27 @@ function messageOf(value) {
|
|
|
198
197
|
return null
|
|
199
198
|
}
|
|
200
199
|
|
|
200
|
+
// The text a failing assertion waited for, named by its own error: a \`has-text(…)\` CSS
|
|
201
|
+
// pseudo-class, a \`getByText(…)\` locator, or a \`toContainText(…)\` expectation. A strict-mode
|
|
202
|
+
// violation is not this shape — two elements DID match the text — and neither is a wait on a
|
|
203
|
+
// selector with no text argument. The ANSI colouring Playwright leaves in the message is dropped
|
|
204
|
+
// before the read, or the quotes it wraps around a locator would never parse.
|
|
205
|
+
function waitedTextOf(message) {
|
|
206
|
+
if (!message) return null
|
|
207
|
+
const text = String(message).replace(/\\u001b\\[[0-9;]*m/g, '')
|
|
208
|
+
if (text.indexOf('strict mode violation') !== -1) return null
|
|
209
|
+
const quoted = function (pattern) {
|
|
210
|
+
const m = pattern.exec(text)
|
|
211
|
+
return m === null ? null : m[2]
|
|
212
|
+
}
|
|
213
|
+
const hasText = quoted(/has-text\\(\\s*(['"])((?:(?!\\1).)*)\\1/)
|
|
214
|
+
if (hasText !== null) return hasText
|
|
215
|
+
const byText = quoted(/getByText\\(\\s*(['"])((?:(?!\\1).)*)\\1/)
|
|
216
|
+
if (byText !== null) return byText
|
|
217
|
+
if (text.indexOf('toContainText') === -1) return null
|
|
218
|
+
return quoted(/Expected(?: string)?:\\s*(["'])((?:(?!\\1).)*)\\1/)
|
|
219
|
+
}
|
|
220
|
+
|
|
201
221
|
// A request/response body exactly as Playwright stored it: inline on the value, or in a resource
|
|
202
222
|
// entry the value names. Bounded, and binary is dropped — a JSON body is what this is for, and a
|
|
203
223
|
// multipart upload would print bytes.
|
|
@@ -459,6 +479,7 @@ for (const one of found.slice(0, MAX_TRACES)) {
|
|
|
459
479
|
let failedStepId = null
|
|
460
480
|
let failedStep = null
|
|
461
481
|
let failedEnd = null
|
|
482
|
+
let failedError = ''
|
|
462
483
|
let failureSpec = null
|
|
463
484
|
let failureLine = 0
|
|
464
485
|
let lastSpec = null
|
|
@@ -468,6 +489,7 @@ for (const one of found.slice(0, MAX_TRACES)) {
|
|
|
468
489
|
if (entry.type === 'after' && entry.error && typeof entry.callId === 'string' && failedStepId === null) {
|
|
469
490
|
failedStepId = entry.callId
|
|
470
491
|
failedEnd = typeof entry.endTime === 'number' ? entry.endTime : null
|
|
492
|
+
failedError = messageOf(entry.error) || ''
|
|
471
493
|
}
|
|
472
494
|
}
|
|
473
495
|
// The fallback location — the last step that names a spec — is read from every member, runner
|
|
@@ -710,6 +732,26 @@ for (const one of found.slice(0, MAX_TRACES)) {
|
|
|
710
732
|
if (sent) r.sent = sent
|
|
711
733
|
if (got) r.got = got
|
|
712
734
|
}
|
|
735
|
+
// The text the failing assertion waited for, and a response the SAME page received that carried
|
|
736
|
+
// it (m169). The assertion's own error names the text; the network log says whether it ever
|
|
737
|
+
// reached the browser. Only an answered fetch/xhr can say the API returned it — a 4xx body is an
|
|
738
|
+
// error message, not data. The most focused response wins (smallest body): an articles list also
|
|
739
|
+
// carries tag names in its \`tagList\`, while the tags response is the collection whose membership
|
|
740
|
+
// the assertion tests.
|
|
741
|
+
const waitedText = waitedTextOf(failedError)
|
|
742
|
+
let missingText = null
|
|
743
|
+
if (waitedText !== null && !unattributed) {
|
|
744
|
+
let best = null
|
|
745
|
+
for (const r of requests) {
|
|
746
|
+
if (r.type !== 'fetch' && r.type !== 'xhr') continue
|
|
747
|
+
if (r.status < 200 || r.status >= 400) continue
|
|
748
|
+
const got = gotText(buf, entries, r)
|
|
749
|
+
if (!got || got.indexOf(waitedText) === -1) continue
|
|
750
|
+
if (best === null || got.length < best.got.length) best = { request: r, got: got }
|
|
751
|
+
}
|
|
752
|
+
if (best !== null)
|
|
753
|
+
missingText = { text: waitedText, method: best.request.method, path: best.request.path }
|
|
754
|
+
}
|
|
713
755
|
function wire(r) {
|
|
714
756
|
return {
|
|
715
757
|
method: r.method,
|
|
@@ -881,6 +923,7 @@ for (const one of found.slice(0, MAX_TRACES)) {
|
|
|
881
923
|
// tail here dropped every failed request as soon as a page made more than MAX_REQUESTS of them
|
|
882
924
|
// plus the answered ones (xr-molecule-flash-m112's note listed only -1s and 200s).
|
|
883
925
|
requests: chosen.slice(0, MAX_REQUESTS),
|
|
926
|
+
...(missingText === null ? {} : { missingText: missingText }),
|
|
884
927
|
console: keptConsole.slice(0, MAX_CONSOLE).map(function (text) { return text.slice(0, 300) }),
|
|
885
928
|
actions: actions.slice(-MAX_ACTIONS),
|
|
886
929
|
...(unattributed ? { unattributed: true } : {}),
|
|
@@ -917,6 +960,24 @@ function parseTraceRequest(value) {
|
|
|
917
960
|
...(typeof r.pageId === 'string' && r.pageId ? { pageId: r.pageId } : {}),
|
|
918
961
|
};
|
|
919
962
|
}
|
|
963
|
+
/**
|
|
964
|
+
* One `missingText` out of a `TRACE` line's json, or null when the value is not one.
|
|
965
|
+
*
|
|
966
|
+
* @param value - The line's `missingText`.
|
|
967
|
+
* @returns The awaited text and the response that carried it, or null.
|
|
968
|
+
*/
|
|
969
|
+
function parseMissingText(value) {
|
|
970
|
+
if (typeof value !== 'object' || value === null)
|
|
971
|
+
return null;
|
|
972
|
+
const raw = value;
|
|
973
|
+
if (typeof raw.text !== 'string' || raw.text === '')
|
|
974
|
+
return null;
|
|
975
|
+
if (typeof raw.method !== 'string' || raw.method === '')
|
|
976
|
+
return null;
|
|
977
|
+
if (typeof raw.path !== 'string' || raw.path === '')
|
|
978
|
+
return null;
|
|
979
|
+
return { text: raw.text, method: raw.method, path: raw.path };
|
|
980
|
+
}
|
|
920
981
|
/**
|
|
921
982
|
* One `lastAction` out of a `TRACE` line's json, or null when the value is not one.
|
|
922
983
|
*
|
|
@@ -962,6 +1023,7 @@ export function parseTraceArtifacts(stdout) {
|
|
|
962
1023
|
const actions = Array.isArray(raw.actions) ? raw.actions : [];
|
|
963
1024
|
const lead = parseTraceRequest(raw.lead);
|
|
964
1025
|
const lastAction = parseLastAction(raw.lastAction);
|
|
1026
|
+
const missingText = parseMissingText(raw.missingText);
|
|
965
1027
|
const otherContexts = Array.isArray(raw.otherContexts)
|
|
966
1028
|
? raw.otherContexts
|
|
967
1029
|
.filter((c) => typeof c === 'object' && c !== null)
|
|
@@ -995,6 +1057,7 @@ export function parseTraceArtifacts(stdout) {
|
|
|
995
1057
|
})),
|
|
996
1058
|
...(otherContexts.length === 0 ? {} : { otherContexts }),
|
|
997
1059
|
...(raw.unattributed === true ? { unattributed: true } : {}),
|
|
1060
|
+
...(missingText === null ? {} : { missingText }),
|
|
998
1061
|
});
|
|
999
1062
|
}
|
|
1000
1063
|
catch (_error) {
|
|
@@ -1500,6 +1563,68 @@ function sameOriginPath(request) {
|
|
|
1500
1563
|
return null;
|
|
1501
1564
|
return raw.replace(/\/+$/, '');
|
|
1502
1565
|
}
|
|
1566
|
+
/**
|
|
1567
|
+
* The path segments a request ASKS FOR a credential through — a login, a registration, a token
|
|
1568
|
+
* endpoint — as opposed to a protected resource that presents one.
|
|
1569
|
+
*/
|
|
1570
|
+
const CREDENTIAL_EXCHANGE_PATH = /(?:^|\/)(?:login|log-in|signin|sign-in|register|signup|sign-up|token)(?:\/|$)/i;
|
|
1571
|
+
/** The field a credential payload carries: a password, a grant, or a refresh token. */
|
|
1572
|
+
const CREDENTIAL_FIELD = /"(?:password|credentials|grant_type|refresh_token)"\s*:/i;
|
|
1573
|
+
/** The field that pairs with a password in a login/registration body. */
|
|
1574
|
+
const CREDENTIAL_IDENTITY = /"(?:email|username)"\s*:/i;
|
|
1575
|
+
/**
|
|
1576
|
+
* Whether a request's recorded body is a credential payload — the shape a login, a registration
|
|
1577
|
+
* or a token exchange sends, never a protected write (an article, a comment, a profile).
|
|
1578
|
+
*
|
|
1579
|
+
* @param sent - What the request sent, as the trace recorded it (bounded).
|
|
1580
|
+
* @returns True for a body carrying credentials.
|
|
1581
|
+
*/
|
|
1582
|
+
function credentialExchangeBody(sent) {
|
|
1583
|
+
if (!sent)
|
|
1584
|
+
return false;
|
|
1585
|
+
const text = sent.slice(0, MAX_TRACED_BODY_CHARS);
|
|
1586
|
+
// A password alone is a profile write as often as a login; the exchange is a password beside
|
|
1587
|
+
// an identity, or a token grant.
|
|
1588
|
+
if (!CREDENTIAL_FIELD.test(text))
|
|
1589
|
+
return false;
|
|
1590
|
+
return (CREDENTIAL_IDENTITY.test(text) || /"(?:credentials|grant_type|refresh_token)"\s*:/i.test(text));
|
|
1591
|
+
}
|
|
1592
|
+
/**
|
|
1593
|
+
* Whether a refused request is a CREDENTIAL EXCHANGE: the app was asked to mint a token (a login,
|
|
1594
|
+
* a registration, a token endpoint), so it never needed one attached.
|
|
1595
|
+
*
|
|
1596
|
+
* Three readings, weakest last: the app's own auth config names the endpoint (a project may mount
|
|
1597
|
+
* `/session` or `/auth/signin`), the path's own last segments name it (`/users/login`), or the
|
|
1598
|
+
* body is a credential payload (`{"user":{"email":…,"password":…}}` sends a registration).
|
|
1599
|
+
*
|
|
1600
|
+
* xr-molecule-flash-m165 (+10:22): `POST /api/users/login → 401
|
|
1601
|
+
* {"errors":{"credentials":["invalid"]}}` was answered with the missing-token sentence — "the
|
|
1602
|
+
* client did not attach the token … The API handler is not the problem" — for a request whose
|
|
1603
|
+
* whole purpose is to obtain a token. The model read helpers and configs for 40 minutes instead of
|
|
1604
|
+
* the real cause: the suite's setup had seeded `https://api.realworld.show`, not this app.
|
|
1605
|
+
*
|
|
1606
|
+
* @param request - The refused request (401/403).
|
|
1607
|
+
* @param authEndpoints - The endpoints the app's auth config names (`/users/login`).
|
|
1608
|
+
* @returns True when the request is a credential exchange.
|
|
1609
|
+
*/
|
|
1610
|
+
function isCredentialExchange(request, authEndpoints) {
|
|
1611
|
+
if (request.method !== 'POST')
|
|
1612
|
+
return false;
|
|
1613
|
+
const path = sameOriginPath(request);
|
|
1614
|
+
if (path === null)
|
|
1615
|
+
return false;
|
|
1616
|
+
// The app's own config names it: matched as a path suffix, because an auth config writes the
|
|
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))
|
|
1625
|
+
return true;
|
|
1626
|
+
return credentialExchangeBody(request.sent);
|
|
1627
|
+
}
|
|
1503
1628
|
/**
|
|
1504
1629
|
* The sentence a 401/403 on the app's own API gets: which half of the wire refused the request.
|
|
1505
1630
|
*
|
|
@@ -1521,6 +1646,15 @@ function sameOriginPath(request) {
|
|
|
1521
1646
|
* sent through. When the trace carries that line, the no-header sentence names the ordering fix
|
|
1522
1647
|
* instead of leaving the run to re-derive it.
|
|
1523
1648
|
*
|
|
1649
|
+
* xr-molecule-flash-m165 (+10:22): a `POST /api/users/login → 401
|
|
1650
|
+
* {"errors":{"credentials":["invalid"]}}` is a CREDENTIAL EXCHANGE — the request is how a token is
|
|
1651
|
+
* obtained, so an absent `Authorization` header is the request being what it is, not a client bug.
|
|
1652
|
+
* The note said "the client did not attach the token … The API handler is not the problem" and the
|
|
1653
|
+
* run went looking through the app instead of at the credentials or the setup's API origin. The
|
|
1654
|
+
* exchange branch names the credentials, the account's origin, and the response body; the
|
|
1655
|
+
* categorical handler sentence is gone from the no-header branch too, because a route that
|
|
1656
|
+
* requires a token where the app allows anonymous access is the handler's own mistake.
|
|
1657
|
+
*
|
|
1524
1658
|
* The trace cannot name the file that made the call: a real HAR entry carries the request, response,
|
|
1525
1659
|
* `_resourceType`, `_monotonicTime` and a frame ref, but no initiator stack. So the sentence names
|
|
1526
1660
|
* the shared client and the auth bond instead of guessing a file.
|
|
@@ -1528,14 +1662,30 @@ function sameOriginPath(request) {
|
|
|
1528
1662
|
* @param request - The refused request, a 401 or 403 on the app's own origin.
|
|
1529
1663
|
* @param unbondedClient - Whether the traced page logged `no client is bonded`, which explains a
|
|
1530
1664
|
* request that carried no `Authorization` header.
|
|
1665
|
+
* @param authEndpoints - The endpoints the app's auth config names, so a credential exchange is
|
|
1666
|
+
* recognised even under a path the segment list does not.
|
|
1531
1667
|
* @returns The sentence the note appends.
|
|
1532
1668
|
*/
|
|
1533
|
-
function authRefusalSentence(request, unbondedClient = false) {
|
|
1669
|
+
function authRefusalSentence(request, unbondedClient = false, authEndpoints = []) {
|
|
1534
1670
|
const label = requestLabel(request);
|
|
1535
1671
|
// The header's scheme, when it parsed; presence alone is still the server's half of the wire.
|
|
1536
1672
|
const carried = request.authorization !== undefined
|
|
1537
1673
|
? `with \`Authorization: ${request.authorization} …\``
|
|
1538
1674
|
: 'with an `Authorization` header';
|
|
1675
|
+
// A credential exchange asks the app to MINT a token: no token is expected to be attached, so a
|
|
1676
|
+
// refusal here is about the credentials themselves — never the client's omission (m165).
|
|
1677
|
+
if (isCredentialExchange(request, authEndpoints)) {
|
|
1678
|
+
const got = request.got
|
|
1679
|
+
? ` got ${bodyLine(redactSecrets(request.got), TRACE_NOTE_GOT_CHARS)}`
|
|
1680
|
+
: '';
|
|
1681
|
+
return (`The browser sent \`${label}\`${got} and the API answered ${request.status} — this is a ` +
|
|
1682
|
+
'credential exchange (a login/registration/token endpoint), so the client is not expected ' +
|
|
1683
|
+
'to attach a token: the credentials were rejected. Check that the account the test logs in ' +
|
|
1684
|
+
'with was created on THIS app — the same database and origin this run drives; a user seeded ' +
|
|
1685
|
+
'somewhere else is unknown here — and read the response body’s `errors` (a login answers 401 ' +
|
|
1686
|
+
'`{"errors":{"credentials":["invalid"]}}` for a wrong email/password or a user that does not ' +
|
|
1687
|
+
'exist here).');
|
|
1688
|
+
}
|
|
1539
1689
|
// The console line names the mechanism the no-header branch otherwise has to guess at:
|
|
1540
1690
|
// `getClient()` ran before `setupHttpDefault()` and the token was mirrored into the
|
|
1541
1691
|
// client that later setup replaced (xr-molecule-flash-m156).
|
|
@@ -1573,21 +1723,23 @@ function authRefusalSentence(request, unbondedClient = false) {
|
|
|
1573
1723
|
? 'The request did carry a `Cookie` header, so if this app signs in with a session cookie ' +
|
|
1574
1724
|
'that is the credential the server did not accept. '
|
|
1575
1725
|
: '') +
|
|
1576
|
-
'The API handler is not the problem.' +
|
|
1577
1726
|
unbonded);
|
|
1578
1727
|
}
|
|
1579
1728
|
/**
|
|
1580
1729
|
* The note's credential-refusal advice, one sentence per distinct shape — at most two.
|
|
1581
1730
|
*
|
|
1582
|
-
* The shapes are `(status, Authorization scheme, cookie present)`: m148's ten
|
|
1583
|
-
* shape (`401`, no header) on every failing test, and repeating its sentence per
|
|
1584
|
-
* the fact rather than state it.
|
|
1731
|
+
* The shapes are `(status, Authorization scheme, cookie present, credential exchange)`: m148's ten
|
|
1732
|
+
* walks repeated one shape (`401`, no header) on every failing test, and repeating its sentence per
|
|
1733
|
+
* row would bury the fact rather than state it. A login and a protected request that both carry no
|
|
1734
|
+
* header are DIFFERENT shapes — one is the credentials being checked, the other is a token that was
|
|
1735
|
+
* never attached (m165).
|
|
1585
1736
|
*
|
|
1586
1737
|
* @param items - The traces the note is about.
|
|
1738
|
+
* @param authEndpoints - The endpoints the app's auth config names.
|
|
1587
1739
|
* @returns The sentences to append (each led by a space), or '' when no same-origin 401/403 was
|
|
1588
1740
|
* traced.
|
|
1589
1741
|
*/
|
|
1590
|
-
function authRefusalAdvice(items) {
|
|
1742
|
+
function authRefusalAdvice(items, authEndpoints = []) {
|
|
1591
1743
|
const sentences = [];
|
|
1592
1744
|
const seen = new Set();
|
|
1593
1745
|
// A page that logged `no client is bonded` explains every request it sent without a
|
|
@@ -1604,11 +1756,12 @@ function authRefusalAdvice(items) {
|
|
|
1604
1756
|
// ignores it and clears the session, and the page owes the navigation (m156).
|
|
1605
1757
|
if (isLogoutRequest(request))
|
|
1606
1758
|
continue;
|
|
1607
|
-
const
|
|
1759
|
+
const exchange = isCredentialExchange(request, authEndpoints);
|
|
1760
|
+
const key = `${request.status} ${exchange ? 'exchange' : request.sentAuthorization ? (request.authorization ?? 'header') : 'none'} ${request.sentCookie ? 'cookie' : ''}`;
|
|
1608
1761
|
if (seen.has(key))
|
|
1609
1762
|
continue;
|
|
1610
1763
|
seen.add(key);
|
|
1611
|
-
sentences.push(authRefusalSentence(request, unbondedClient));
|
|
1764
|
+
sentences.push(authRefusalSentence(request, unbondedClient, authEndpoints));
|
|
1612
1765
|
if (sentences.length === 2)
|
|
1613
1766
|
return ` ${sentences.join(' ')}`;
|
|
1614
1767
|
}
|
|
@@ -1674,6 +1827,117 @@ function tracedApiPrefixes(items) {
|
|
|
1674
1827
|
}
|
|
1675
1828
|
return [...prefixes].sort();
|
|
1676
1829
|
}
|
|
1830
|
+
/**
|
|
1831
|
+
* Every API path prefix the traces support: the one the app's client config names and the ones the
|
|
1832
|
+
* traces' own same-origin requests show ({@link tracedApiPrefixes}).
|
|
1833
|
+
*
|
|
1834
|
+
* @param items - The traces the note is about.
|
|
1835
|
+
* @param apiPrefixes - Prefixes the app's client config names (`['/api']`), never a literal.
|
|
1836
|
+
* @returns The one-segment prefixes, without duplicates.
|
|
1837
|
+
*/
|
|
1838
|
+
function knownApiPrefixes(items, apiPrefixes) {
|
|
1839
|
+
const prefixes = new Set([...apiPrefixes, ...tracedApiPrefixes(items)]
|
|
1840
|
+
.map((prefix) => `/${String(prefix).replace(/^\/+|\/+$/g, '')}`)
|
|
1841
|
+
.filter((prefix) => prefix !== '/'));
|
|
1842
|
+
return [...prefixes];
|
|
1843
|
+
}
|
|
1844
|
+
/** A URL written in full (`https://api.realworld.show/api/articles`). */
|
|
1845
|
+
const ABSOLUTE_URL = /^[a-z][a-z0-9+.-]*:\/\//i;
|
|
1846
|
+
/**
|
|
1847
|
+
* The API requests a trace made to an origin other than the page's own — set-up traffic that ran
|
|
1848
|
+
* against someone else's server.
|
|
1849
|
+
*
|
|
1850
|
+
* The trace reader relativises every request whose origin is the page's (`pathOf`), so a request
|
|
1851
|
+
* recorded with an absolute URL went to ANOTHER origin by construction. It counts as set-up/API
|
|
1852
|
+
* traffic when its path carries a prefix the app's own API is known to use ({@link
|
|
1853
|
+
* knownApiPrefixes}) — a CDN font or a third-party script has no `/api` under it, and with no
|
|
1854
|
+
* known prefix nothing is claimed.
|
|
1855
|
+
*
|
|
1856
|
+
* xr-molecule-flash-m165 (+10:22): the suite's helper defaulted `API_BASE` to
|
|
1857
|
+
* `https://api.realworld.show/api` and its set-up seeded rows there, while the browser drove this
|
|
1858
|
+
* app — whose own `/api` answers a login for none of those users. The trace's absolute-URL rows
|
|
1859
|
+
* were the evidence, and the note said nothing about them.
|
|
1860
|
+
*
|
|
1861
|
+
* @param items - The traces the note is about.
|
|
1862
|
+
* @param apiPrefixes - Prefixes the app's client config names (`['/api']`).
|
|
1863
|
+
* @returns One entry per distinct (origin, prefix), in trace order.
|
|
1864
|
+
*/
|
|
1865
|
+
function foreignOriginApiRequests(items, apiPrefixes) {
|
|
1866
|
+
const prefixes = knownApiPrefixes(items, apiPrefixes);
|
|
1867
|
+
if (prefixes.length === 0)
|
|
1868
|
+
return [];
|
|
1869
|
+
const out = [];
|
|
1870
|
+
const seen = new Set();
|
|
1871
|
+
for (const item of items) {
|
|
1872
|
+
for (const request of item.requests) {
|
|
1873
|
+
const raw = String(request.path ?? '');
|
|
1874
|
+
if (!ABSOLUTE_URL.test(raw))
|
|
1875
|
+
continue;
|
|
1876
|
+
let parsed;
|
|
1877
|
+
try {
|
|
1878
|
+
parsed = new URL(raw);
|
|
1879
|
+
}
|
|
1880
|
+
catch (_error) {
|
|
1881
|
+
// Not a URL this can parse: it is not evidence of another origin.
|
|
1882
|
+
continue;
|
|
1883
|
+
}
|
|
1884
|
+
const prefix = prefixes.find((one) => parsed.pathname === one || parsed.pathname.startsWith(`${one}/`));
|
|
1885
|
+
if (prefix === undefined)
|
|
1886
|
+
continue;
|
|
1887
|
+
const key = `${parsed.origin}${prefix}`;
|
|
1888
|
+
if (seen.has(key))
|
|
1889
|
+
continue;
|
|
1890
|
+
seen.add(key);
|
|
1891
|
+
out.push({ origin: parsed.origin, prefix, request });
|
|
1892
|
+
}
|
|
1893
|
+
}
|
|
1894
|
+
return out;
|
|
1895
|
+
}
|
|
1896
|
+
/**
|
|
1897
|
+
* The origin a run's target names, when it names one: a URL (`http://localhost:4173`), or the
|
|
1898
|
+
* deployed host the note prints (`loopcheck-….apps.mlcl.dev`). Null for the note's descriptive
|
|
1899
|
+
* targets ("the deployed app", "the local app").
|
|
1900
|
+
*
|
|
1901
|
+
* @param target - Where the run went, as {@link failureTraceNote} names it.
|
|
1902
|
+
* @returns The origin, or null.
|
|
1903
|
+
*/
|
|
1904
|
+
function appOriginOfTarget(target) {
|
|
1905
|
+
const text = target.trim();
|
|
1906
|
+
try {
|
|
1907
|
+
if (ABSOLUTE_URL.test(text))
|
|
1908
|
+
return new URL(text).origin;
|
|
1909
|
+
if (/^[a-z0-9.-]+(?::\d+)?$/i.test(text))
|
|
1910
|
+
return new URL(`https://${text}`).origin;
|
|
1911
|
+
}
|
|
1912
|
+
catch (_error) {
|
|
1913
|
+
// A target that is not a URL names no origin.
|
|
1914
|
+
}
|
|
1915
|
+
return null;
|
|
1916
|
+
}
|
|
1917
|
+
/**
|
|
1918
|
+
* The sentence naming the foreign origin a run's set-up traffic went to, and the setting that
|
|
1919
|
+
* points it at this app instead.
|
|
1920
|
+
*
|
|
1921
|
+
* It leads the note because it is the cheapest possible wrong result: every row the suite creates
|
|
1922
|
+
* lands in a database this app never reads, so the failure says nothing about the app's code.
|
|
1923
|
+
* xr-molecule-flash-m165 spent the walk after its failed login reading helpers and configs for a
|
|
1924
|
+
* missing token, when the command's `BASE_URL`/`API_BASE` disagreement was the cause.
|
|
1925
|
+
*
|
|
1926
|
+
* @param found - The foreign-origin request the trace holds.
|
|
1927
|
+
* @param target - Where the run went, for the app's own origin when the note knows it.
|
|
1928
|
+
* @returns The sentence, led by nothing and closed by a blank line.
|
|
1929
|
+
*/
|
|
1930
|
+
function foreignOriginSentence(found, target) {
|
|
1931
|
+
const appOrigin = appOriginOfTarget(target);
|
|
1932
|
+
const where = appOrigin ?? 'this app’s origin';
|
|
1933
|
+
const apiBase = `${appOrigin ?? '<this app’s origin>'}${found.prefix}`;
|
|
1934
|
+
return (`The suite’s setup ran against ${found.origin}, not this app ` +
|
|
1935
|
+
`(\`${requestLabel(found.request)}\`) — its set-up/API traffic went to another origin while ` +
|
|
1936
|
+
`this app serves its own API at \`${found.prefix}\` on ${where}. Set ` +
|
|
1937
|
+
`\`API_BASE=${apiBase}\` on the run — the supplied suite reads it (verify the variable name ` +
|
|
1938
|
+
`in the suite’s own helpers before writing it) — so the rows it creates are in the database ` +
|
|
1939
|
+
`this app reads.\n\n`);
|
|
1940
|
+
}
|
|
1677
1941
|
/**
|
|
1678
1942
|
* The failed 404s whose path carries none of the known API prefixes.
|
|
1679
1943
|
*
|
|
@@ -1687,12 +1951,9 @@ function tracedApiPrefixes(items) {
|
|
|
1687
1951
|
* @returns One entry per distinct failed 404, in trace order.
|
|
1688
1952
|
*/
|
|
1689
1953
|
function missingApiPrefixRequests(items, apiPrefixes) {
|
|
1690
|
-
const
|
|
1691
|
-
|
|
1692
|
-
.filter((prefix) => prefix !== '/'));
|
|
1693
|
-
if (prefixes.size === 0)
|
|
1954
|
+
const known = knownApiPrefixes(items, apiPrefixes);
|
|
1955
|
+
if (known.length === 0)
|
|
1694
1956
|
return [];
|
|
1695
|
-
const known = [...prefixes];
|
|
1696
1957
|
const out = [];
|
|
1697
1958
|
const seen = new Set();
|
|
1698
1959
|
for (const item of items) {
|
|
@@ -1726,6 +1987,22 @@ function missingApiPrefixSentence(request) {
|
|
|
1726
1987
|
`without the \`${request.prefix}\` prefix — the HTTP client's base URL is not set: is ` +
|
|
1727
1988
|
'`setupProviders()` (which runs the HTTP bond setup) called in `main.tsx`?');
|
|
1728
1989
|
}
|
|
1990
|
+
/**
|
|
1991
|
+
* The sentence for a failing assertion whose awaited text a same-page API response carried.
|
|
1992
|
+
*
|
|
1993
|
+
* It names the response and the fact the trace proves — the value reached the browser and the page
|
|
1994
|
+
* did not render it — and stops there. Which cut drops it (a slice, a filter, a limit) is in the
|
|
1995
|
+
* component that maps the response, and the note cannot see that component; naming the mapping
|
|
1996
|
+
* step is the whole instruction (xr-molecule-flash-m169).
|
|
1997
|
+
*
|
|
1998
|
+
* @param missing - The awaited text and the response that carried it.
|
|
1999
|
+
* @returns The sentence the note leads with.
|
|
2000
|
+
*/
|
|
2001
|
+
function missingTextSentence(missing) {
|
|
2002
|
+
return (`The API returned "${missing.text}" (in ${missing.method} ${missing.path}), but the page did ` +
|
|
2003
|
+
`not render it — the page drops it before rendering (a slice/filter/limit in the component ` +
|
|
2004
|
+
`that renders that list); find where that response is mapped.`);
|
|
2005
|
+
}
|
|
1729
2006
|
/**
|
|
1730
2007
|
* The note a failed suite run's result carries: what each failing test's page actually sent, and
|
|
1731
2008
|
* what came back.
|
|
@@ -1803,6 +2080,13 @@ function missingApiPrefixSentence(request) {
|
|
|
1803
2080
|
* xr-molecule-flash-m148 (+29:18) hit this exactly: six local walks read
|
|
1804
2081
|
* `POST …/favorite → 401 {"errors":{"token":["is missing"]}}` as a handler bug.
|
|
1805
2082
|
*
|
|
2083
|
+
* A credential EXCHANGE (a login/registration/token endpoint, read from the app's auth config, the
|
|
2084
|
+
* path or the body) is never that shape: the request's purpose is to obtain a token, so an absent
|
|
2085
|
+
* header is not a client omission ({@link isCredentialExchange}). xr-molecule-flash-m165's login
|
|
2086
|
+
* 401 was answered as one for 40 minutes; the trace also showed the suite's SET-UP traffic going
|
|
2087
|
+
* to `https://api.realworld.show/api` instead of this app, which is what the note leads with
|
|
2088
|
+
* ({@link foreignOriginApiRequests}).
|
|
2089
|
+
*
|
|
1806
2090
|
* @param items - The traces {@link parseTraceArtifacts} read.
|
|
1807
2091
|
* @param target - Where the run went — the deployed host, as the note names it.
|
|
1808
2092
|
* @param earlierFailures - The failing `file:line`s earlier walks of this suite left, so a repeat
|
|
@@ -1870,7 +2154,12 @@ export function failureTraceNote(items, target, earlierFailures = [], apiPrefixe
|
|
|
1870
2154
|
// Every recorded failure is the logout endpoint answering 4xx: the row's own sentence explains
|
|
1871
2155
|
// it, and neither the handler lead nor the credential reading applies (m156 +24:02).
|
|
1872
2156
|
const onlyLogoutFailures = failedRequests.length > 0 && failedRequests.every((request) => isLogoutRequest(request));
|
|
1873
|
-
const authAdvice = authRefusalAdvice(traced);
|
|
2157
|
+
const authAdvice = authRefusalAdvice(traced, report.authEndpoints ?? []);
|
|
2158
|
+
// Set-up traffic the trace shows going to an origin other than the app's (m165): the rows it
|
|
2159
|
+
// creates are not in this app's database, so the note says it before any app-side reading.
|
|
2160
|
+
const foreignOrigins = foreignOriginApiRequests(traced, apiPrefixes);
|
|
2161
|
+
const [firstForeign] = foreignOrigins;
|
|
2162
|
+
const setupOriginLead = firstForeign === undefined ? '' : foreignOriginSentence(firstForeign, target);
|
|
1874
2163
|
// The stalled-navigation reading, per traced test: non-null only when the RUNNER says this
|
|
1875
2164
|
// test's failing operation is a navigation and a document subresource still pending at the end
|
|
1876
2165
|
// started inside that wait ({@link stalledNavigation}).
|
|
@@ -2098,7 +2387,24 @@ export function failureTraceNote(items, target, earlierFailures = [], apiPrefixe
|
|
|
2098
2387
|
`does not define is always undefined, and a missing effect dependency on the route means ` +
|
|
2099
2388
|
`the fetch never re-runs — before touching the markup or the CSS.\n\n`
|
|
2100
2389
|
: '';
|
|
2101
|
-
|
|
2390
|
+
// An assertion whose awaited text a same-page API response carried: the fact the run needs
|
|
2391
|
+
// first, before any row, is that the value arrived and the page dropped it (m169's missing
|
|
2392
|
+
// "trending" came back from GET /api/tags while the sidebar rendered a cut list). Each distinct
|
|
2393
|
+
// response is named once; the note makes no claim about which cut did it.
|
|
2394
|
+
const missingTexts = [
|
|
2395
|
+
...new Map(traced
|
|
2396
|
+
.filter((item) => item.missingText !== undefined)
|
|
2397
|
+
.map((item) => {
|
|
2398
|
+
const missing = item.missingText;
|
|
2399
|
+
return [`${missing.method} ${missing.path}\u0000${missing.text}`, missing];
|
|
2400
|
+
})).values(),
|
|
2401
|
+
];
|
|
2402
|
+
const missingTextLead = missingTexts.length === 0
|
|
2403
|
+
? ''
|
|
2404
|
+
: `${missingTexts.slice(0, 2).map(missingTextSentence).join(' ')}\n\n`;
|
|
2405
|
+
return (missingTextLead +
|
|
2406
|
+
transportLead +
|
|
2407
|
+
setupOriginLead +
|
|
2102
2408
|
stalledLead +
|
|
2103
2409
|
staleRouteLead +
|
|
2104
2410
|
`The platform read the failing tests' own Playwright traces from the run against ${target} ` +
|
|
@@ -2289,24 +2595,27 @@ async function readRunConfig(command, cwd, readFile) {
|
|
|
2289
2595
|
return '';
|
|
2290
2596
|
}
|
|
2291
2597
|
/**
|
|
2292
|
-
* The command a
|
|
2598
|
+
* The command a Playwright run needs to leave a readable trace behind, when the suite's config
|
|
2293
2599
|
* would record none.
|
|
2294
2600
|
*
|
|
2295
2601
|
* The failing tests' requests are only readable if the run wrote them down (see the module doc),
|
|
2296
2602
|
* and the config is where the decision about that lives — the platform does not edit the suite's
|
|
2297
|
-
* config, so it passes the flag instead,
|
|
2298
|
-
*
|
|
2299
|
-
*
|
|
2300
|
-
*
|
|
2603
|
+
* config, so it passes the flag instead, on every Playwright run that starts tests and does not
|
|
2604
|
+
* already decide its own `--trace`. That includes the whole-suite proof run, which an earlier
|
|
2605
|
+
* version of this function skipped on the reasoning that its flags are graded: the flag changes
|
|
2606
|
+
* what is RETAINED, not what passes. `retain-on-failure` keeps the trace only of a test that
|
|
2607
|
+
* fails — Playwright records it and deletes it when the test passes (playwright's
|
|
2608
|
+
* `_shouldAbandonTrace` unlinks the temporary trace files of a passing test) — so a green proof
|
|
2609
|
+
* run keeps nothing extra. A failing one keeps the network log its failures need.
|
|
2610
|
+
* xr-molecule-flash-m163 (+24:58) is why the proof run is no longer skipped: its one deployed
|
|
2611
|
+
* failure left only an `error-context.md`, the executor spent the next calls hunting a `trace.zip`
|
|
2612
|
+
* that could not exist, and then shipped a guessed fix.
|
|
2301
2613
|
*
|
|
2302
2614
|
* @param args - The command, the directory it starts in, and the backend's file read.
|
|
2303
2615
|
* @returns The command to run and the sentence to report, or the command unchanged.
|
|
2304
2616
|
*/
|
|
2305
2617
|
export async function diagnosisTraceCommand(args) {
|
|
2306
2618
|
const unchanged = { command: args.command, change: null };
|
|
2307
|
-
// The proof run is the one run this must never touch: its flags are graded.
|
|
2308
|
-
if (!isDiagnosisRun(args.command))
|
|
2309
|
-
return unchanged;
|
|
2310
2619
|
if (namesTraceMode(args.command))
|
|
2311
2620
|
return unchanged;
|
|
2312
2621
|
// Only a Playwright run leaves a trace to read.
|
|
@@ -2330,7 +2639,8 @@ export async function diagnosisTraceCommand(args) {
|
|
|
2330
2639
|
return {
|
|
2331
2640
|
command: `${args.command.slice(0, at)} ${DIAGNOSIS_TRACE_FLAG}${args.command.slice(at)}`,
|
|
2332
2641
|
change: `Added \`${DIAGNOSIS_TRACE_FLAG}\`: the suite's config records no trace for a failure` +
|
|
2333
|
-
`${/trace:/.test(config) ? '' : ' (it sets none)'}, and
|
|
2334
|
-
'what the platform reads the
|
|
2642
|
+
`${/trace:/.test(config) ? '' : ' (it sets none)'}, and a failing test's own network log is ` +
|
|
2643
|
+
'what the platform reads the cause from. It keeps a trace only for a test that fails, so a ' +
|
|
2644
|
+
'green run retains nothing.',
|
|
2335
2645
|
};
|
|
2336
2646
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -49,6 +49,29 @@ export interface ExecutionBackend {
|
|
|
49
49
|
exitCode: number;
|
|
50
50
|
}>;
|
|
51
51
|
}
|
|
52
|
+
/**
|
|
53
|
+
* The supplied suite's own settings, put back on a run whose runner config changes them.
|
|
54
|
+
*
|
|
55
|
+
* A graded suite is walked by the grader under its own per-test budget, worker count and
|
|
56
|
+
* timeouts. An executor's run that reads a config setting different numbers measures a run the
|
|
57
|
+
* grader never sees: a shorter budget fails tests the graded walk passes, a longer one passes
|
|
58
|
+
* tests the graded walk times out on. The platform that knows the suite supplies these values
|
|
59
|
+
* before the run ({@link ToolBuildConfig.suiteBudget}); the shared tool puts the flags on the
|
|
60
|
+
* runner's own command line, where Playwright lets them win over every config file.
|
|
61
|
+
*/
|
|
62
|
+
export interface SuiteBudgetPin {
|
|
63
|
+
/**
|
|
64
|
+
* The Playwright flags that carry the suite's own settings onto the command
|
|
65
|
+
* (`--timeout=15000`, `--workers=1`), as the consumer's own scan reads them. Empty when the
|
|
66
|
+
* difference is in settings no flag carries — the pin is then the sentence alone.
|
|
67
|
+
*/
|
|
68
|
+
flags: string[];
|
|
69
|
+
/**
|
|
70
|
+
* One sentence for the run's result: this run uses the suite's own budget, and which settings
|
|
71
|
+
* a config keeps because Playwright has no flag for them.
|
|
72
|
+
*/
|
|
73
|
+
note: string;
|
|
74
|
+
}
|
|
52
75
|
/**
|
|
53
76
|
* Configuration for building the tool set.
|
|
54
77
|
* Allows consumers to customize security, callbacks, and tool selection.
|
|
@@ -141,6 +164,22 @@ export interface ToolBuildConfig {
|
|
|
141
164
|
* @returns The quoted titles the request carves out.
|
|
142
165
|
*/
|
|
143
166
|
excludedTestTitles?: () => readonly string[];
|
|
167
|
+
/**
|
|
168
|
+
* The supplied suite's own budget, when the Playwright config a command would load sets
|
|
169
|
+
* different settings, asked BEFORE the run executes. The consumer knows where the suite and its
|
|
170
|
+
* base config live; this shared package does not, so the reading is the consumer's and the
|
|
171
|
+
* mechanics — putting the flags on the runner's own arguments, before the pipeline and outside
|
|
172
|
+
* any redirect or group closer — are this tool's.
|
|
173
|
+
*
|
|
174
|
+
* Called for a Playwright suite command only, and only the LAST suite step is touched. A run
|
|
175
|
+
* that already keeps the suite's settings, or a sandbox with no supplied suite, answers `null`.
|
|
176
|
+
* Unset (or a hook that throws) changes nothing: the command runs as the model wrote it.
|
|
177
|
+
*
|
|
178
|
+
* @param command - The shell command, as the model sent it.
|
|
179
|
+
* @param cwd - The resolved working directory it will run in.
|
|
180
|
+
* @returns The flags and the sentence, or null when nothing is put back.
|
|
181
|
+
*/
|
|
182
|
+
suiteBudget?: (command: string, cwd: string) => Promise<SuiteBudgetPin | null | undefined>;
|
|
144
183
|
/** Post-write hook (e.g. auto-format via Prettier/ESLint). Called after every write_file/edit_file. */
|
|
145
184
|
onAfterWrite?: (path: string) => Promise<void>;
|
|
146
185
|
/** Diff tracking callback. Called before writes with old/new content. */
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|