From 27398ad55edf472c7ee5c98def61445392a179b7 Mon Sep 17 00:00:00 2001 From: lyon Date: Mon, 15 Jun 2026 20:14:41 +0800 Subject: [PATCH] fix(probe): wait for login form values --- scripts/web-live-dom-probe.mjs | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/scripts/web-live-dom-probe.mjs b/scripts/web-live-dom-probe.mjs index 63280523..40a039d6 100644 --- a/scripts/web-live-dom-probe.mjs +++ b/scripts/web-live-dom-probe.mjs @@ -332,7 +332,7 @@ async function collectFailureDomEvidence(page) { cardVisible: Boolean(document.querySelector(".login-card")), usernameVisible: Boolean(document.querySelector('#login-username, input[autocomplete="username"]')), passwordVisible: Boolean(document.querySelector('#login-password, input[autocomplete="current-password"], input[type="password"]')), - errorText: text(".login-error, [role='alert'], .error, .text-red-500, .text-destructive") + errorText: text(".form-error, .login-error, [role='alert'], .error, .text-red-500, .text-destructive") }, requiredSelectors: { workspace: Boolean(document.querySelector("#workspace")), @@ -520,15 +520,39 @@ async function submitCurrentLoginForm(page, args, actions, selectorMode) { } await username.fill(args.user); await password.fill(args.pass); + const valuesReady = await waitForCurrentLoginValues(page, args); await Promise.allSettled([ page.waitForURL(/\/workbench/u, { timeout: Math.min(args.timeoutMs, 15000) }), submit.click() ]); - actions.push({ action: "login", user: args.user, selectorMode }); + actions.push({ action: "login", user: args.user, selectorMode, valuesReady }); + const outcome = await waitForLoginOutcome(page, args); + if (outcome?.kind === "error") throw new Error(`login failed: ${outcome.errorText || "unknown login error"}`); await page.locator("#command-input").waitFor({ state: "visible", timeout: args.timeoutMs }); assertNoCredentialLeak(page.url()); } +async function waitForCurrentLoginValues(page, args) { + const ready = await page.waitForFunction((expected) => { + const username = document.querySelector('input[autocomplete="username"], .login-card input'); + const password = document.querySelector('input[autocomplete="current-password"], .login-card input[type="password"], input[type="password"]'); + const submit = document.querySelector('.login-card button[type="submit"], button[type="submit"]'); + return Boolean(username && password && username.value === expected.user && password.value.length === expected.passwordLength && !submit?.disabled); + }, { user: args.user, passwordLength: args.pass.length }, { timeout: Math.min(args.timeoutMs, 5000) }).catch(() => null); + return Boolean(ready); +} + +async function waitForLoginOutcome(page, args) { + const handle = await page.waitForFunction(() => { + if (document.querySelector("#command-input")) return { kind: "workspace" }; + const errorText = document.querySelector(".form-error, .login-error, [role='alert'], .error, .text-red-500, .text-destructive")?.textContent?.trim(); + if (errorText) return { kind: "error", errorText }; + return null; + }, null, { timeout: Math.min(args.timeoutMs, 15000) }).catch(() => null); + if (!handle) return null; + return handle.jsonValue().catch(() => null); +} + function assertNoCredentialLeak(value) { const finalUrl = new URL(value); if (finalUrl.searchParams.has("username") || finalUrl.searchParams.has("password")) {