fix(probe): wait for login form values

This commit is contained in:
lyon
2026-06-15 20:14:41 +08:00
parent 7715c506d5
commit 27398ad55e
+26 -2
View File
@@ -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")) {