@ia-qa/self-healing 1.24.0 → 1.26.0

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/dist/cli/login.js CHANGED
@@ -38,40 +38,17 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
39
  exports.runLogin = runLogin;
40
40
  exports.replaySession = replaySession;
41
+ exports.loginProven = loginProven;
42
+ exports.watchForLogin = watchForLogin;
41
43
  exports.sessionDecision = sessionDecision;
42
44
  const fs = __importStar(require("fs"));
43
45
  const path = __importStar(require("path"));
44
46
  const prompts_1 = __importDefault(require("prompts"));
45
47
  const launcher_1 = require("../launcher");
46
48
  const map_1 = require("./map");
49
+ const sessionCheck_1 = require("../sessionCheck");
47
50
  const config_1 = require("../config");
48
- /**
49
- * `ia-qa-heal login` — open a browser, let a human log in, keep the session.
50
- *
51
- * `config.auth` fills a form: a username field, a password field, a submit button. That
52
- * describes *one shape of login*, and every app whose login is not that shape is out of
53
- * reach — federated SSO, MFA, a consent screen, a magic link, a device check, a captcha.
54
- * No selector list gets through any of them, so "add an auth block" is the wrong answer
55
- * to most walls, and `map` was left mapping a login form under the name `dashboard`.
56
- *
57
- * This verb models nothing at all, which is why it is the general one: the human performs
58
- * whatever the app asks, and the resulting session is saved in Playwright's `storageState`
59
- * shape and reused by `map`. It cannot know *how* your app authenticates, and never needs to.
60
- *
61
- * Deliberately not automatic, and never part of a gate:
62
- * - It is the only caller that opens a **visible** browser (`headed`) — everything that
63
- * produces a contract stays headless, because a window in CI is a hung build.
64
- * - It refuses without a TTY and under CI: it waits for a person twice over (at the
65
- * browser, then at the keyboard), and a pipeline that runs it just hangs.
66
- * - What it writes is a **secret** — live cookies, i.e. whoever holds the file is logged
67
- * in as that user. So it announces the path before opening anything, and writes
68
- * `.ia-qa/.gitignore` so a commit cannot carry it. The file never leaves the machine:
69
- * nothing in this package uploads it.
70
- *
71
- * For CI, `config.auth` (reproducible, no human) and capture-during-run (`IAQA_CAPTURE=1`
72
- * with your own suite's login) remain the two paths. A saved session is a local artefact
73
- * with a shelf life — when it expires, `map` hits the wall again and says to re-run this.
74
- */
51
+ const sessionCheck_2 = require("../sessionCheck");
75
52
  async function runLogin(args) {
76
53
  if (process.env.CI) {
77
54
  console.error(`❌ Refusing to open a login browser under CI.\n\n` +
@@ -80,14 +57,14 @@ async function runLogin(args) {
80
57
  ` "auth" in .ia-qa/config.json fills a login form from env/SSM secrets\n` +
81
58
  ` IAQA_CAPTURE=1 + testCommand your own suite logs in; map rides along`);
82
59
  process.exitCode = 2;
83
- return;
60
+ return { ok: false };
84
61
  }
85
62
  if (!process.stdin.isTTY) {
86
63
  console.error(`❌ \`ia-qa-heal login\` needs a terminal: it opens a browser, then waits for you to say\n` +
87
64
  ` you are logged in. With stdin closed it would wait forever.\n` +
88
65
  ` Run it in your own shell — an agent cannot do this step for you.`);
89
66
  process.exitCode = 2;
90
- return;
67
+ return { ok: false };
91
68
  }
92
69
  const config = (0, config_1.loadConfig)();
93
70
  const urlIdx = args.indexOf('--url');
@@ -95,7 +72,7 @@ async function runLogin(args) {
95
72
  if (urlIdx !== -1 && (!wanted || wanted.startsWith('-'))) {
96
73
  console.error('❌ --url needs a URL or a path (e.g. --url /dashboard).');
97
74
  process.exitCode = 2;
98
- return;
75
+ return { ok: false };
99
76
  }
100
77
  const target = new URL(wanted || config.pages[0]?.url || '/', config.baseUrl).toString();
101
78
  // Written where this project keeps its session, not always the default: someone who
@@ -106,9 +83,19 @@ async function runLogin(args) {
106
83
  if (sessionIdx !== -1 && (!explicit || explicit.startsWith('-'))) {
107
84
  console.error('❌ --session needs a file path.');
108
85
  process.exitCode = 2;
109
- return;
86
+ return { ok: false };
110
87
  }
111
88
  const file = (0, config_1.sessionTarget)(config, explicit).file;
89
+ // How long to watch the window before falling back to "tell me what is in there".
90
+ // `--wait 0` turns the watch off and restores the plain press-Enter flow, which is the
91
+ // escape hatch for an app whose login cannot be observed from the outside at all.
92
+ const waitIdx = args.indexOf('--wait');
93
+ const waitSeconds = waitIdx !== -1 ? Number(args[waitIdx + 1]) : 600;
94
+ if (waitIdx !== -1 && (!Number.isFinite(waitSeconds) || waitSeconds < 0)) {
95
+ console.error('❌ --wait needs a number of seconds (0 turns the watch off).');
96
+ process.exitCode = 2;
97
+ return { ok: false };
98
+ }
112
99
  console.log(`\n🔑 ia-qa-heal login`);
113
100
  console.log(` Opens a visible browser at ${target}.`);
114
101
  console.log(` Log in there however your app asks — SSO, MFA, a consent screen, all of it.`);
@@ -122,7 +109,7 @@ async function runLogin(args) {
122
109
  if (!go.ok) {
123
110
  console.log('Nothing opened, nothing written.');
124
111
  process.exitCode = 1;
125
- return;
112
+ return { ok: false };
126
113
  }
127
114
  const { browser, using } = await (0, launcher_1.launchBrowser)({ headed: true });
128
115
  console.log(`🌐 Using ${using} (visible window)`);
@@ -131,26 +118,75 @@ async function runLogin(args) {
131
118
  const context = await browser.newContext(contextOptions);
132
119
  const page = await context.newPage();
133
120
  await page.goto(target, { waitUntil: 'domcontentloaded', timeout: 60000 });
134
- await (0, prompts_1.default)({
135
- type: 'confirm',
136
- name: 'done',
137
- message: 'Logged in? (leave the browser open — press Enter here when the app is showing)',
138
- initial: true,
139
- }, { onCancel: () => ({ done: true }) });
121
+ // Where an UNauthenticated browser lands is the thing a real login has to beat, so it
122
+ // is read before anything else happens.
123
+ const startedAt = page.url();
124
+ console.log(waitSeconds > 0
125
+ ? `\n👀 Log in in the browser window. I am watching it — when the session actually opens\n` +
126
+ ` the app, I save it and carry on by myself. Nothing to come back and confirm here.\n` +
127
+ ` (Press Enter if you would rather stop the wait now.)`
128
+ : `\n👀 Log in in the browser window, then press Enter here.`);
129
+ const enter = waitForEnter();
130
+ let watch;
131
+ try {
132
+ watch =
133
+ waitSeconds > 0
134
+ ? await watchForLogin({
135
+ browser,
136
+ context,
137
+ page,
138
+ target,
139
+ startedAt,
140
+ contextOptions,
141
+ confirmed: enter.promise,
142
+ timeoutMs: waitSeconds * 1000,
143
+ })
144
+ : (await enter.promise, { end: 'confirmed' });
145
+ }
146
+ finally {
147
+ enter.cancel();
148
+ }
149
+ // A timeout is **not** a refusal. It only means nobody told us the login finished, so
150
+ // the question becomes the one this verb always answers: what does the browser actually
151
+ // hold right now? `sessionDecision` says it honestly — an empty state writes nothing and
152
+ // keeps the file that worked, a wall goes beside it, neither is ever a ✅. Returning
153
+ // early instead would throw away a session somebody really did obtain, just because the
154
+ // watch could not see the app from the outside.
155
+ if (watch.end === 'timeout') {
156
+ console.log(`\n⏳ Nothing that looks like a login happened in ${(0, sessionCheck_2.describeAge)(waitSeconds)}. Checking what the\n` +
157
+ ` browser holds anyway — whatever it is, it is reported rather than assumed.`);
158
+ }
140
159
  // Taken in memory, written only once it has been replayed. `storageState({ path })`
141
160
  // wrote first and asked nothing, so an empty state — an app keeping its token in
142
161
  // IndexedDB, or a login that never completed — printed a green "Session saved" *and*
143
162
  // overwrote a session that worked. Two failures in one line: a false green, and the
144
163
  // destruction of the good file it replaced.
145
- const takenOn = page.url();
146
- const state = await context.storageState();
147
- console.log(`\n🔁 Checking the session actually holds reopening ${target} with it, in a clean browser.`);
148
- const replay = await replaySession(browser, state, target, contextOptions);
164
+ //
165
+ // A closed window is not a special case: whatever the context held when it went is what
166
+ // gets judged, and `sessionDecision` says the honest thing about it.
167
+ const takenOn = page.isClosed() ? startedAt : page.url();
168
+ // The state and the proof about it travel together — writing a state taken *after* the
169
+ // verification would report a proof about a file that was never the file.
170
+ //
171
+ // Which pair, though, depends on how the watch ended. `verified` is the watch's own, by
172
+ // definition. A **closed** window leaves no way to take another, so the last attempt is
173
+ // all there is. But `confirmed` must be re-checked: pressing Enter means *look now*, and
174
+ // the person very often finishes the login and only then reaches for the keyboard — the
175
+ // last failed attempt would be a verdict about the moment before they succeeded.
176
+ const reusable = watch.end === 'verified' || (watch.end === 'closed' && watch.state);
177
+ let state = reusable ? watch.state : undefined;
178
+ let replay = reusable ? watch.replay : undefined;
179
+ if (!state || !replay) {
180
+ state = await context.storageState().catch(() => ({ cookies: [], origins: [] }));
181
+ console.log(`\n🔁 Checking the session actually holds — reopening ${target} with it, in a clean browser.`);
182
+ replay = await replaySession(browser, state, target, contextOptions);
183
+ }
149
184
  const decision = sessionDecision({
150
185
  cookies: state.cookies.length,
151
186
  origins: state.origins?.length || 0,
152
187
  landed: replay.landed,
153
188
  wall: replay.wall,
189
+ offSite: replay.offSite,
154
190
  error: replay.error,
155
191
  file,
156
192
  existed: fs.existsSync(file),
@@ -165,11 +201,37 @@ async function runLogin(args) {
165
201
  (decision.ok ? console.log : console.error)(line);
166
202
  if (!decision.ok)
167
203
  process.exitCode = 1;
204
+ return { ok: decision.ok, ...(decision.action !== 'none' ? { file: decision.target } : {}) };
168
205
  }
169
206
  finally {
170
207
  await browser.close();
171
208
  }
172
209
  }
210
+ /**
211
+ * "Press Enter" as a *cancellable* promise, which `prompts` cannot be.
212
+ *
213
+ * The watch and the keyboard race each other, so the loser has to be taken back: a `prompts`
214
+ * question left open keeps stdin resumed and the process alive long after the browser has
215
+ * closed and the session has been written. Raw stdin gives back a `cancel` that removes the
216
+ * listener and pauses the stream, which is the whole reason for not reusing `prompts` here.
217
+ */
218
+ function waitForEnter() {
219
+ let done = () => { };
220
+ const promise = new Promise((resolve) => {
221
+ done = resolve;
222
+ });
223
+ const onData = () => {
224
+ cancel();
225
+ done();
226
+ };
227
+ const cancel = () => {
228
+ process.stdin.off('data', onData);
229
+ process.stdin.pause();
230
+ };
231
+ process.stdin.resume();
232
+ process.stdin.on('data', onData);
233
+ return { promise, cancel };
234
+ }
173
235
  /**
174
236
  * `.ia-qa/.gitignore`, scoped to the one file in there that is a secret.
175
237
  *
@@ -203,8 +265,12 @@ function writeSessionGitignore() {
203
265
  * on screen is logged in. That gap is what produced "✅ Session saved — 0 cookies".
204
266
  *
205
267
  * A wall is read the way `assertLanded` reads it: sent somewhere else *and* shown a
206
- * password field. A plain redirect (`/` `/home`) is not a wall. A navigation that
207
- * throws is not a verdict either it is reported as unverified, never as a pass.
268
+ * password field asked of every frame, and given a moment to render one. A plain
269
+ * redirect (`/` → `/home`) is not a wall. A navigation that throws is not a verdict
270
+ * either — it is reported as unverified, never as a pass.
271
+ *
272
+ * `offSite` is the third fact, and it is the one that was missing: "no password field"
273
+ * says nothing when the browser is not even on the app any more.
208
274
  */
209
275
  async function replaySession(browser, state, target, contextOptions = {}) {
210
276
  const context = await browser.newContext({ ...contextOptions, storageState: state });
@@ -212,8 +278,10 @@ async function replaySession(browser, state, target, contextOptions = {}) {
212
278
  const page = await context.newPage();
213
279
  await page.goto(target, { waitUntil: 'domcontentloaded', timeout: 60000 });
214
280
  const landed = page.url();
215
- const passwords = await page.locator('input[type="password"]').count();
216
- return { landed, wall: !(0, map_1.samePath)(landed, target) && passwords > 0 };
281
+ const elsewhere = !(0, map_1.samePath)(landed, target);
282
+ const wall = elsewhere && (await (0, map_1.hasPasswordField)(page, 3000));
283
+ const offSite = !(0, sessionCheck_1.sameSite)(landed, target);
284
+ return { landed, wall, ...(offSite ? { offSite: true } : {}) };
217
285
  }
218
286
  catch (err) {
219
287
  return { landed: '', wall: false, error: err instanceof Error ? err.message : String(err) };
@@ -222,6 +290,117 @@ async function replaySession(browser, state, target, contextOptions = {}) {
222
290
  await context.close();
223
291
  }
224
292
  }
293
+ /**
294
+ * Is this state, replayed in a clean context, evidence that somebody logged in?
295
+ *
296
+ * One rule, and it is the definition rather than a heuristic: **a login worked when a clean
297
+ * context reaches somewhere the unauthenticated browser could not.** `startedAt` is where
298
+ * the very first navigation landed — the wall — so a replay that lands back there gained
299
+ * nothing, whatever it shows.
300
+ *
301
+ * Reading "no password field" as success instead is the trap this avoids, and it is not
302
+ * theoretical: a magic-link or passkey login page has no password field at all, so the
303
+ * cheap signal says *logged in* from the first second, and the watch would save a state
304
+ * that opens nothing and call it ✅ — the exact false green this verb was rewritten to stop.
305
+ */
306
+ function loginProven(replay, startedAt) {
307
+ if (replay.error || replay.wall || replay.offSite)
308
+ return false;
309
+ if (!replay.landed)
310
+ return false;
311
+ return !(0, map_1.samePath)(replay.landed, startedAt);
312
+ }
313
+ /**
314
+ * Watch a visible browser until the person is logged in — instead of asking them to say so.
315
+ *
316
+ * The prompt this replaces was the last place in the verb that trusted a human's word, and
317
+ * the verb's whole history is about not doing that: `storageState({ path })` wrote on that
318
+ * word and printed `✅ Session saved — 0 cookies` over a file that logged nobody in. Asking
319
+ * "are you done?" has the same shape — it is a question about the world answered by
320
+ * someone who is looking at a browser, not at the cookie jar. And it fails in the most
321
+ * ordinary way there is: the person logs in, goes back to what they were doing, and the
322
+ * terminal sits on an unanswered prompt. It was reported by the person who wrote it.
323
+ *
324
+ * So the watch asks the browser, on a loop, and the evidence it accepts is the one the rest
325
+ * of this file already accepts: a **clean context** carrying only the captured state,
326
+ * reaching somewhere the unauthenticated browser could not (`loginProven`).
327
+ *
328
+ * Three restraints:
329
+ *
330
+ * - **It never gives up early.** A verification that fails means *not yet* — a multi-step
331
+ * login, a consent screen still to come — never *this will not work*. After a few failed
332
+ * attempts it explains the one case that genuinely cannot succeed (a token in IndexedDB
333
+ * leaves a session file empty) and keeps watching, because the alternative is telling
334
+ * somebody their login failed while they are still typing.
335
+ * - **The keyboard still works.** `confirmed` is the escape hatch for every app whose
336
+ * login this cannot observe, and whichever finishes first wins. Nothing that worked
337
+ * before stops working.
338
+ * - **Replaying is not free**, so the cheap gate runs first: on the app's own site, no
339
+ * password field in any frame, and something in the cookie jar. A clean context is
340
+ * opened only once that holds, and then only when the live page has moved again or
341
+ * enough time has passed.
342
+ */
343
+ async function watchForLogin(opts) {
344
+ const note = opts.note ?? ((line) => console.log(line));
345
+ const now = opts.now ?? Date.now;
346
+ const deadline = now() + (opts.timeoutMs ?? 10 * 60 * 1000);
347
+ let confirmed = false;
348
+ void opts.confirmed.then(() => {
349
+ confirmed = true;
350
+ });
351
+ let lastVerifiedUrl = '';
352
+ let lastVerifiedAt = 0;
353
+ let attempts = 0;
354
+ // The last attempt, kept as a pair. It is offered on a `confirmed`/`closed` ending only
355
+ // because it is a true statement about a state we still hold; it is never a success.
356
+ let last;
357
+ for (;;) {
358
+ if (confirmed)
359
+ return { end: 'confirmed', ...last };
360
+ if (opts.page.isClosed())
361
+ return { end: 'closed', ...last };
362
+ if (now() >= deadline)
363
+ return { end: 'timeout', ...last };
364
+ let url = '';
365
+ let cookies = 0;
366
+ try {
367
+ url = opts.page.url();
368
+ cookies = (await opts.context.cookies()).length;
369
+ }
370
+ catch {
371
+ // The context went away under us — the person closed the window.
372
+ return { end: 'closed', ...last };
373
+ }
374
+ const worthVerifying = cookies > 0 &&
375
+ (0, sessionCheck_1.sameSite)(url, opts.target) &&
376
+ !(0, map_1.samePath)(url, opts.startedAt) &&
377
+ !(await (0, map_1.hasPasswordField)(opts.page, 0)) &&
378
+ (url !== lastVerifiedUrl || now() - lastVerifiedAt > 8000);
379
+ if (worthVerifying) {
380
+ lastVerifiedUrl = url;
381
+ lastVerifiedAt = now();
382
+ attempts++;
383
+ let state;
384
+ try {
385
+ state = await opts.context.storageState();
386
+ }
387
+ catch {
388
+ return { end: 'closed', ...last };
389
+ }
390
+ const replay = await replaySession(opts.browser, state, opts.target, opts.contextOptions ?? {});
391
+ last = { replay, state };
392
+ if (loginProven(replay, opts.startedAt))
393
+ return { end: 'verified', replay, state };
394
+ if (attempts === 3) {
395
+ note(`\n ⏳ You look logged in, but the session I can carry does not open the app on its own yet.\n` +
396
+ ` Still watching — a multi-step login is normal here. If your app keeps its token in\n` +
397
+ ` IndexedDB, in memory or in a service worker, a session file can never hold it:\n` +
398
+ ` press Enter and I will tell you exactly that instead of waiting.`);
399
+ }
400
+ }
401
+ await new Promise((r) => setTimeout(r, 1200));
402
+ }
403
+ }
225
404
  /**
226
405
  * What to do with a session that has just been replayed — and above all, what never to do.
227
406
  *
@@ -272,6 +451,28 @@ function sessionDecision(v) {
272
451
  ].filter(Boolean),
273
452
  };
274
453
  }
454
+ // A check that landed somewhere else is a check that did not run, and the doctrine for
455
+ // those is already written above: quarantine, never over the existing file. It reaches
456
+ // here rather than into the branch above because nothing about it says "wall" — the
457
+ // honest sentence is "the app was not reached", and it must not be printed as a pass.
458
+ if (v.offSite) {
459
+ const quarantine = `${v.file}.unverified`;
460
+ return {
461
+ ok: false,
462
+ action: v.existed ? 'quarantine' : 'write',
463
+ target: v.existed ? quarantine : v.file,
464
+ lines: [
465
+ `\n⚠ Saved ${held}, but they could not be checked — reopening the app with them landed on`,
466
+ ` ${v.landed}, which is another site. No login form was found there, and that proves`,
467
+ ` nothing: an identity provider asking for an email first looks exactly like this.`,
468
+ v.existed
469
+ ? ` ${v.file} was left untouched. The new state is beside it:\n ${quarantine}\n` +
470
+ ` Open that URL yourself: if it is your app, rename the file over the other one.`
471
+ : ` Written anyway, so your login is not lost:\n ${v.file}\n` +
472
+ ` Open that URL yourself to see whether it is your app or its login provider.`,
473
+ ],
474
+ };
475
+ }
275
476
  return {
276
477
  ok: true,
277
478
  action: 'write',
@@ -1 +1 @@
1
- {"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/cli/login.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/cli/login.ts"],"names":[],"mappings":"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"}
package/dist/cli/map.d.ts CHANGED
@@ -112,6 +112,27 @@ export declare function runSteps(page: Page, target: PageTarget): Promise<void>;
112
112
  * only the app can mint, and is therefore not a URL anyone can navigate to twice.
113
113
  */
114
114
  export declare function suggestAuthBlock(page: Page, loginUrl?: string): Promise<string | null>;
115
+ /**
116
+ * Is a password field on screen — in any frame, and after the page has had a moment to draw one?
117
+ *
118
+ * Both halves were false negatives in the field, and a false negative here is the worst
119
+ * answer this package gives: it is what lets a contract named `dashboard` describe a login
120
+ * form, and what let `session --check` print `✅ This session opens the app` beside an
121
+ * identity provider's own URL.
122
+ *
123
+ * - **The wait.** `domcontentloaded` then an instant `count()` asks an SPA for a form it
124
+ * has not rendered yet. Nothing about the answer was wrong except when it was asked.
125
+ * - **The frames.** `page.locator` searches the main frame only, and a federated login is
126
+ * very often in an iframe. Asking the main frame alone is asking the wrong document.
127
+ *
128
+ * Both callers reach this only after a redirect — a page that landed where it was asked
129
+ * never gets here — so a healthy run pays nothing, and only an already abnormal page pays.
130
+ * The wait is short by default for the case that *is* common on a healthy app (a declared
131
+ * page that moved, with no login anywhere): one redirected page must not cost three
132
+ * seconds twenty times over. `session --check` raises it, because there the whole verb is
133
+ * this one question and there is exactly one page to ask it of.
134
+ */
135
+ export declare function hasPasswordField(page: Page, timeoutMs?: number): Promise<boolean>;
115
136
  /** Compare by pathname alone: a query string or a trailing slash is not another page. */
116
137
  export declare function samePath(a: string, b: string): boolean;
117
138
  /**
package/dist/cli/map.js CHANGED
@@ -37,6 +37,7 @@ exports.LoginWallError = void 0;
37
37
  exports.runMap = runMap;
38
38
  exports.runSteps = runSteps;
39
39
  exports.suggestAuthBlock = suggestAuthBlock;
40
+ exports.hasPasswordField = hasPasswordField;
40
41
  exports.samePath = samePath;
41
42
  exports.assertLanded = assertLanded;
42
43
  exports.login = login;
@@ -1186,6 +1187,50 @@ async function suggestAuthBlock(page, loginUrl) {
1186
1187
  };
1187
1188
  return JSON.stringify(block, null, 2);
1188
1189
  }
1190
+ /**
1191
+ * Is a password field on screen — in any frame, and after the page has had a moment to draw one?
1192
+ *
1193
+ * Both halves were false negatives in the field, and a false negative here is the worst
1194
+ * answer this package gives: it is what lets a contract named `dashboard` describe a login
1195
+ * form, and what let `session --check` print `✅ This session opens the app` beside an
1196
+ * identity provider's own URL.
1197
+ *
1198
+ * - **The wait.** `domcontentloaded` then an instant `count()` asks an SPA for a form it
1199
+ * has not rendered yet. Nothing about the answer was wrong except when it was asked.
1200
+ * - **The frames.** `page.locator` searches the main frame only, and a federated login is
1201
+ * very often in an iframe. Asking the main frame alone is asking the wrong document.
1202
+ *
1203
+ * Both callers reach this only after a redirect — a page that landed where it was asked
1204
+ * never gets here — so a healthy run pays nothing, and only an already abnormal page pays.
1205
+ * The wait is short by default for the case that *is* common on a healthy app (a declared
1206
+ * page that moved, with no login anywhere): one redirected page must not cost three
1207
+ * seconds twenty times over. `session --check` raises it, because there the whole verb is
1208
+ * this one question and there is exactly one page to ask it of.
1209
+ */
1210
+ async function hasPasswordField(page, timeoutMs = 1500) {
1211
+ const deadline = Date.now() + timeoutMs;
1212
+ for (;;) {
1213
+ if (page.isClosed())
1214
+ return false;
1215
+ for (const frame of page.frames()) {
1216
+ try {
1217
+ if ((await frame.locator('input[type="password"]').count()) > 0)
1218
+ return true;
1219
+ }
1220
+ catch {
1221
+ /* a frame that navigated mid-check answers nothing; the next pass asks again */
1222
+ }
1223
+ }
1224
+ if (Date.now() >= deadline)
1225
+ return false;
1226
+ try {
1227
+ await page.waitForTimeout(200);
1228
+ }
1229
+ catch {
1230
+ return false;
1231
+ }
1232
+ }
1233
+ }
1189
1234
  /** Compare by pathname alone: a query string or a trailing slash is not another page. */
1190
1235
  function samePath(a, b) {
1191
1236
  const pathnameOf = (u) => {
@@ -1221,7 +1266,7 @@ async function assertLanded(page, target, wanted, config, session) {
1221
1266
  const landed = page.url();
1222
1267
  if (samePath(landed, wanted))
1223
1268
  return {};
1224
- if ((await page.locator('input[type="password"]').count()) === 0) {
1269
+ if (!(await hasPasswordField(page))) {
1225
1270
  console.warn(` ⚠ Redirected to ${landed} — this contract describes that page, not ${wanted}.`);
1226
1271
  // Returned as well as printed. Field report: this line appeared once, at line 37 of a
1227
1272
  // 172-line log, in the middle of the mapping stream — and it was the only fact that