From 7af2e49579916fccf68b317fd67602ae7a388053 Mon Sep 17 00:00:00 2001 From: Lihua <1017343802@qq.com> Date: Thu, 1 Oct 2026 06:34:17 -0700 Subject: [PATCH 1/3] test(workspace): freeze elapsed clock across view switch Signed-off-by: Lihua <1017343802@qq.com> --- examples/personal-workspace-browser/conversation-activity.mjs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/personal-workspace-browser/conversation-activity.mjs b/examples/personal-workspace-browser/conversation-activity.mjs index e62428f9c1..e60a01b6c8 100644 --- a/examples/personal-workspace-browser/conversation-activity.mjs +++ b/examples/personal-workspace-browser/conversation-activity.mjs @@ -43,6 +43,8 @@ export const conversationActivityScenario = { const pending = page.locator(".personal-message").filter({ has: page.getByRole("button", { name: "中断本轮", exact: true }) }); await page.clock.install(); await page.clock.fastForward(25000); + // Keep elapsed time fixed while comparing the same request across views. + await page.clock.pauseAt(await page.evaluate(() => Date.now())); await pending.locator(".personal-message-quiet").waitFor(); const elapsedBefore = await pending.locator(".personal-message-elapsed").textContent(); await page.getByRole("navigation", { name: "管家视图" }).getByRole("button", { name: "总览", exact: true }).click(); From 0d12faded32b2f75cd59f86003dbed994805c11d Mon Sep 17 00:00:00 2001 From: Lihua <1017343802@qq.com> Date: Thu, 1 Oct 2026 07:02:02 -0700 Subject: [PATCH 2/3] test(chat): align isolated operation fixture stages Signed-off-by: Lihua <1017343802@qq.com> --- tests/test_chat_operation_actions.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/test_chat_operation_actions.py b/tests/test_chat_operation_actions.py index 21a980db9c..9e00f62a29 100644 --- a/tests/test_chat_operation_actions.py +++ b/tests/test_chat_operation_actions.py @@ -103,7 +103,9 @@ def test_browser_operation_fixture_needs_no_test_framework(tmp_path: Path) -> No check=True, ) fixtures = json.loads(result.stdout) - assert list(fixtures) == ["confirmed", "waiting", "unknown", "reconciled"] + assert list(fixtures) == [ + "prepared", "delivered", "confirmed", "waiting", "unknown", "reconciled" + ] for proposal in fixtures.values(): assert proposal["normalized_parameters"]["goal_id"] == "product-release" assert proposal["normalized_parameters"]["executor"]["kind"] == "managed_turn" From 9a016a9f78d477188897a653258806d96cd43bcc Mon Sep 17 00:00:00 2001 From: Lihua <1017343802@qq.com> Date: Thu, 1 Oct 2026 18:55:41 -0700 Subject: [PATCH 3/3] test(chat): exercise recovery preview retry before reload Signed-off-by: Lihua <1017343802@qq.com> --- examples/personal-workspace-browser/chat-todo-proposal.mjs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/examples/personal-workspace-browser/chat-todo-proposal.mjs b/examples/personal-workspace-browser/chat-todo-proposal.mjs index a3c3c4c580..23ec3605a6 100644 --- a/examples/personal-workspace-browser/chat-todo-proposal.mjs +++ b/examples/personal-workspace-browser/chat-todo-proposal.mjs @@ -124,13 +124,16 @@ export const chatTodoProposalScenario = { await composer.fill(RECOVERY_PROMPT); await page.getByRole("button", { name: "发送", exact: true }).click(); await waitFor(() => api.turnRequests.length > turnsBeforeRecovery, "The recovery prompt was not sent"); + // Arm the transient failure before reload: on a slow page, recovery can + // finish while the Goal Chat tab is reopening. + api.failNextActionPreview = true; await page.reload({ waitUntil: "domcontentloaded" }); await page.getByTestId("personal-goal-home").waitFor({ state: "visible" }); await openGoalChat(); - api.failNextActionPreview = true; await page.locator(".personal-channel-timeline").getByText("恢复后给出一个步骤。", { exact: true }).waitFor({ state: "visible", timeout: 15_000 }); const recoveredCard = page.locator(".personal-proposal-row", { hasText: RECOVERY_TEXT }); - await recoveredCard.waitFor({ state: "visible", timeout: 5_000 }); + await recoveredCard.waitFor({ state: "visible", timeout: 15_000 }); + if (api.failNextActionPreview) throw new Error("Recovery did not exercise the transient preview failure"); await waitFor(() => new Set(previewsWithText(RECOVERY_TEXT).map((item) => item.idempotency_key)).size === 1, "The recovered proposal did not map to exactly one Todo preview"); const [recoveredPreview] = previewsWithText(RECOVERY_TEXT);