test: wait for web probe submit readiness

This commit is contained in:
Codex Agent
2026-06-05 23:35:19 +08:00
parent 0b6aa9f628
commit 0f406063f0
+101 -2
View File
@@ -103,7 +103,7 @@ export async function runProbe(args) {
await page.locator("#workspace").waitFor({ state: "visible", timeout: args.timeoutMs });
await page.locator("#command-input").waitFor({ state: "visible", timeout: args.timeoutMs });
if (args.conversationId) await selectConversationInBrowserSession(page, args, actions);
if (args.freshSession) await maybeClick(page, "#session-create", actions, "fresh-session");
if (args.freshSession) await createFreshSession(page, args, actions);
if (args.message) await submitPrompt(page, args, actions);
await waitForMessageCards(page, args, actions);
await page.screenshot({ path: screenshotPath, fullPage: true });
@@ -273,14 +273,113 @@ async function maybeClick(page, selector, actions, action) {
return false;
}
async function createFreshSession(page, args, actions) {
const selector = "#session-create";
const locator = page.locator(selector).first();
if (!(await locator.isVisible({ timeout: 3000 }).catch(() => false))) {
actions.push({ action: "fresh-session", selector, skipped: "not-visible" });
return false;
}
const shellReady = await waitForSessionShellReady(page, args, actions);
const before = await collectSessionState(page);
await locator.click();
const timeoutMs = Math.min(args.timeoutMs, 30000);
const settled = await page.waitForFunction((initial) => {
const statusText = document.querySelector("#session-status")?.textContent?.trim() ?? null;
const changed = Boolean(statusText && statusText !== initial.statusText && !/加载中|等待 workspace/u.test(statusText));
const messageCount = document.querySelectorAll(".message-card").length;
const emptyVisible = Boolean(document.querySelector("[data-conversation-empty]"));
const input = document.querySelector("#command-input");
const send = document.querySelector("#command-send");
return changed && messageCount === 0 && emptyVisible && input && send && !input.disabled && !send.disabled;
}, before, { timeout: timeoutMs }).catch(() => null);
const after = await collectSessionState(page);
actions.push({ action: "fresh-session", selector, settled: Boolean(settled), timeoutMs, shellReady, before, after });
if (!settled) throw new Error(`fresh session did not settle: ${JSON.stringify({ before, after, timeoutMs })}`);
return true;
}
async function waitForSessionShellReady(page, args, actions) {
const timeoutMs = Math.min(args.timeoutMs, 30000);
const before = await collectSessionState(page);
const ready = await page.waitForFunction(() => {
const statusText = document.querySelector("#session-status")?.textContent?.trim() ?? "";
const workspace = document.querySelector("#workspace");
const create = document.querySelector("#session-create");
return Boolean(workspace && create && !create.disabled && statusText && !/加载中/u.test(statusText));
}, null, { timeout: timeoutMs }).catch(() => null);
const after = await collectSessionState(page);
const result = { action: "session-shell-ready", ready: Boolean(ready), timeoutMs, before, after };
actions.push(result);
if (!ready) throw new Error(`session shell did not finish initial loading: ${JSON.stringify(result)}`);
return after;
}
async function collectSessionState(page) {
return page.evaluate(() => {
const activeTab = document.querySelector(".session-tab[aria-selected='true']");
return {
statusText: document.querySelector("#session-status")?.textContent?.trim() ?? null,
activeTabTitle: activeTab?.getAttribute("title") ?? null,
tabCount: document.querySelectorAll(".session-tab").length,
messageCount: document.querySelectorAll(".message-card").length,
emptyVisible: Boolean(document.querySelector("[data-conversation-empty]")),
command: {
inputDisabled: Boolean(document.querySelector("#command-input")?.disabled),
sendDisabled: Boolean(document.querySelector("#command-send")?.disabled),
sendText: document.querySelector("#command-send")?.textContent?.trim() ?? null,
formTitle: document.querySelector("#command-form")?.getAttribute("title") ?? null
}
};
});
}
async function submitPrompt(page, args, actions) {
const readyBefore = await waitForCommandReady(page, args, actions);
await page.locator("#command-input").fill(args.message);
await page.waitForFunction((expected) => document.querySelector("#command-input")?.value === expected, args.message, { timeout: Math.min(args.timeoutMs, 5000) });
const afterFill = await collectCommandState(page);
await page.locator("#command-send").click();
actions.push({ action: "submit-prompt", chars: args.message.length });
const afterClick = await collectCommandState(page);
actions.push({ action: "submit-prompt", chars: args.message.length, readyBefore, afterFill, afterClick });
await page.waitForTimeout(args.waitAfterSubmitMs);
if (args.cancelRunning) await maybeClick(page, ".message-action-cancel", actions, "cancel-running-message");
}
async function waitForCommandReady(page, args, actions) {
const timeoutMs = Math.min(args.timeoutMs, 30000);
const before = await collectCommandState(page);
const ready = await page.waitForFunction(() => {
const input = document.querySelector("#command-input");
const send = document.querySelector("#command-send");
return Boolean(input && send && !input.disabled && !send.disabled);
}, null, { timeout: timeoutMs }).catch(() => null);
const after = await collectCommandState(page);
const result = { action: "command-ready", ready: Boolean(ready), timeoutMs, before, after };
actions.push(result);
if (!ready) throw new Error(`command bar is not ready for submit: ${JSON.stringify(result)}`);
return after;
}
async function collectCommandState(page) {
return page.evaluate(() => {
const input = document.querySelector("#command-input");
const send = document.querySelector("#command-send");
const form = document.querySelector("#command-form");
return {
inputVisible: Boolean(input),
inputDisabled: input ? Boolean(input.disabled) : null,
inputLength: input && typeof input.value === "string" ? input.value.length : null,
sendVisible: Boolean(send),
sendDisabled: send ? Boolean(send.disabled) : null,
sendText: send?.textContent?.trim() ?? null,
formTitle: form?.getAttribute("title") ?? null,
messageCount: document.querySelectorAll(".message-card").length,
authState: document.body.getAttribute("data-auth-state")
};
});
}
async function selectConversationInBrowserSession(page, args, actions) {
const result = await page.evaluate(async ({ projectId, conversationId }) => {
const summarizeAuthSession = (body) => ({