test: wait for web probe submit readiness
This commit is contained in:
@@ -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) => ({
|
||||
|
||||
Reference in New Issue
Block a user