diff --git a/tests/runner-e2e/playwright-support.config.ts b/tests/runner-e2e/playwright-support.config.ts
index 72cc2bfac65..0960467d8ce 100644
--- a/tests/runner-e2e/playwright-support.config.ts
+++ b/tests/runner-e2e/playwright-support.config.ts
@@ -3,7 +3,7 @@ import { defineConfig } from "@playwright/test";
/** Browser-only harness regressions: no Paperclip instance or provider credentials. */
export default defineConfig({
testDir: ".",
- testMatch: ["question-resume-ui.spec.ts", "clarification-readback.spec.ts", "project-context-readback.spec.ts", "skill-studio-flow.spec.ts", "native-task-reply.spec.ts", "screenshot-readiness.spec.ts", "service-worker-reload.spec.ts", "lost-send.spec.ts", "chat-restart.spec.ts", "settings-toggle.spec.ts", "browser-bootstrap-diagnostics.spec.ts", "service-worker-module-loading.spec.ts", "browser-bootstrap-recovery.spec.ts", "blocker-input.spec.ts", "native-blocker-visible.spec.ts", "copilot-permission-card.spec.ts", "kimi-permission-card.spec.ts", "github-review-check.spec.ts", "antigravity-journey-browser.spec.ts", "task-search-filter.spec.ts"],
+ testMatch: ["question-resume-ui.spec.ts", "clarification-readback.spec.ts", "project-context-readback.spec.ts", "skill-studio-flow.spec.ts", "native-task-reply.spec.ts", "screenshot-readiness.spec.ts", "service-worker-reload.spec.ts", "lost-send.spec.ts", "chat-restart.spec.ts", "settings-toggle.spec.ts", "browser-bootstrap-diagnostics.spec.ts", "service-worker-module-loading.spec.ts", "browser-bootstrap-recovery.spec.ts", "blocker-input.spec.ts", "native-blocker-visible.spec.ts", "copilot-permission-card.spec.ts", "kimi-permission-card.spec.ts", "github-review-check.spec.ts", "antigravity-journey-browser.spec.ts", "task-search-filter.spec.ts", "task-document-navigation.spec.ts"],
workers: 1,
retries: 0,
timeout: 10_000,
diff --git a/tests/runner-e2e/product-journeys/task-document-flow.test.ts b/tests/runner-e2e/product-journeys/task-document-flow.test.ts
index 0d28da0be36..21b546778ee 100644
--- a/tests/runner-e2e/product-journeys/task-document-flow.test.ts
+++ b/tests/runner-e2e/product-journeys/task-document-flow.test.ts
@@ -25,7 +25,7 @@ describe("task document reopen navigation", () => {
expect(hasText).toBe(issue.title);
return { first: () => ({ click }) };
} }
- : { isVisible: async () => true },
+ : { isVisible: async () => true, first: () => ({ isVisible: async () => true }) },
},
} as unknown as JourneyContext;
await reopenTaskDocument(context, issue);
diff --git a/tests/runner-e2e/product-journeys/task-document-flow.ts b/tests/runner-e2e/product-journeys/task-document-flow.ts
index 29ea5491a7d..8e6327f68c2 100644
--- a/tests/runner-e2e/product-journeys/task-document-flow.ts
+++ b/tests/runner-e2e/product-journeys/task-document-flow.ts
@@ -4,13 +4,20 @@ import { JourneyError, type JourneyContext, type JourneyEvidence, type TargetSes
async function openDocumentAuthoring({ page }: JourneyContext) {
const create = page.getByRole("button", { name: "New document", exact: true });
- if (await create.isVisible()) return;
const properties = page.getByRole("button", { name: /^(Show properties|Properties)$/ }).first();
- if (await properties.isVisible()) await properties.click();
const artifacts = page.getByRole("tab", { name: /^Artifacts/ }).first();
+ const addTab = page.getByRole("button", { name: "Open a new tab", exact: true });
+ const panelReady = async () => await create.isVisible() || await artifacts.isVisible() || await addTab.isVisible();
+ await expect.poll(async () => await properties.isVisible() || await panelReady()).toBe(true);
+ if (await create.isVisible()) return;
+ if (await properties.isVisible()) {
+ await properties.click();
+ await expect.poll(panelReady).toBe(true);
+ if (await create.isVisible()) return;
+ }
if (await artifacts.isVisible()) await artifacts.click();
else {
- await page.getByRole("button", { name: "Open a new tab", exact: true }).click();
+ await addTab.click();
await page.getByRole("option", { name: /^Artifacts/ }).click();
}
await expect(create).toBeVisible();
diff --git a/tests/runner-e2e/task-document-navigation.spec.ts b/tests/runner-e2e/task-document-navigation.spec.ts
new file mode 100644
index 00000000000..8571290d779
--- /dev/null
+++ b/tests/runner-e2e/task-document-navigation.spec.ts
@@ -0,0 +1,42 @@
+import { expect, test } from "@playwright/test";
+import { reopenTaskDocument } from "./product-journeys/task-document-flow.js";
+import type { JourneyContext } from "./product-journeys/types.js";
+
+for (const layout of ["collapsed", "artifacts", "launcher", "classic"] as const) {
+ test(`document reopen waits for the ${layout} task controls after reload`, async ({ page }) => {
+ const issue = { title: "Journey QA document navigation" };
+ let taskLoads = 0;
+ await page.route("https://document-navigation.test/**", async route => {
+ if (new URL(route.request().url()).pathname.endsWith("/issues")) {
+ await route.fulfill({ contentType: "text/html", body: `${issue.title}` });
+ return;
+ }
+ taskLoads++;
+ await route.fulfill({ contentType: "text/html", body: `` });
+ });
+ const context = { page, api: { origin: "https://document-navigation.test" }, company: { issuePrefix: "QA" } } as JourneyContext;
+ await reopenTaskDocument(context, issue);
+ await expect(page.locator("#document-qa-note")).toHaveText("Saved revision two");
+ expect(taskLoads).toBe(2);
+ });
+}
+
+test("document reopen fails when the task has no authoring controls", async ({ page }) => {
+ await page.route("https://document-navigation.test/**", async route => {
+ const list = new URL(route.request().url()).pathname.endsWith("/issues");
+ await route.fulfill({ contentType: "text/html", body: list ? 'Journey QA document navigation' : 'Task without document permissions' });
+ });
+ const context = { page, api: { origin: "https://document-navigation.test" }, company: { issuePrefix: "QA" } } as JourneyContext;
+ await expect(reopenTaskDocument(context, { title: "Journey QA document navigation" })).rejects.toThrow();
+ await expect(page.getByRole("button", { name: "New document", exact: true })).toHaveCount(0);
+});