import { expect, test, type Page, type Route } from "@playwright/test"; function deferred() { let resolve!: () => void; const promise = new Promise((done) => { resolve = done; }); return { promise, resolve }; } function response(campaignId = "campaign-a", revision = 1, versionId = `${campaignId}-version`, hasMore = false) { const version = { id: versionId, campaign_id: campaignId, version_number: 1, edit_revision: revision, raw_json: {} }; return { campaign: { id: campaignId, current_version_id: versionId }, versions: [version], current_version: version, summary: null, deleted: [], watermark: `watermark-${campaignId}-${revision}`, has_more: hasMore, full: true }; } async function fixture(page: Page) { const requests: URL[] = []; const state = { handler: async (route: Route) => { const url = new URL(route.request().url()); const campaignId = url.pathname.split("/")[4]; await route.fulfill({ json: response(campaignId, 1, url.searchParams.get("version_id") ?? undefined) }); } }; await page.route((url) => url.pathname.startsWith("/api/"), async (route) => { const url = new URL(route.request().url()); if (url.pathname.endsWith("/workspace/delta")) { requests.push(url); await state.handler(route); } else await route.fulfill({ json: {} }); }); await page.goto("/?campaign-workspace"); await expect(page.getByTestId("workspace-version")).toHaveText("campaign-a-version"); await expect(page.getByTestId("workspace-loading")).toHaveText("false"); requests.length = 0; return { state, requests }; } test("failed refresh retains saved workspace and retries without a stale watermark", async ({ page }) => { const { state, requests } = await fixture(page); state.handler = async (route) => { await route.fulfill({ status: 503, json: { detail: "Refresh unavailable" } }); }; await page.getByRole("button", { name: "Force reload workspace", exact: true }).click(); await expect(page.getByTestId("workspace-error")).toContainText("Refresh unavailable"); await expect(page.getByTestId("workspace-loading")).toHaveText("false"); await expect(page.getByTestId("workspace-version")).toHaveText("campaign-a-version"); state.handler = async (route) => { await route.fulfill({ json: response("campaign-a", 2) }); }; await page.getByRole("button", { name: "Reload workspace", exact: true }).click(); await expect(page.getByTestId("workspace-revision")).toHaveText("2"); expect(requests[requests.length - 1].searchParams.has("since")).toBe(false); await expect(page.getByTestId("workspace-error")).toBeEmpty(); }); for (const staleFails of [false, true]) { test(`newer reload wins over an older ${staleFails ? "failed" : "successful"} response`, async ({ page }) => { const { state } = await fixture(page); const pending = deferred(); const finished = deferred(); let requestCount = 0; state.handler = async (route) => { requestCount += 1; if (requestCount === 1) { await pending.promise; await route.fulfill(staleFails ? { status: 503, json: { detail: "Obsolete error" } } : { json: response("campaign-a", 2) }); finished.resolve(); } else await route.fulfill({ json: response("campaign-a", 3) }); }; await page.getByRole("button", { name: "Reload workspace", exact: true }).click(); await expect.poll(() => requestCount).toBe(1); await page.getByRole("button", { name: "Reload workspace", exact: true }).click(); await expect(page.getByTestId("workspace-revision")).toHaveText("3"); pending.resolve(); await finished.promise; await page.evaluate(() => new Promise((done) => requestAnimationFrame(() => requestAnimationFrame(done)))); await expect(page.getByTestId("workspace-revision")).toHaveText("3"); await expect(page.getByTestId("workspace-loading")).toHaveText("false"); await expect(page.getByTestId("workspace-error")).toBeEmpty(); }); } for (const change of ["campaign", "identity", "version"] as const) { test(`changing ${change} discards old responses and cancels obsolete pagination`, async ({ page }) => { const { state, requests } = await fixture(page); const pending = deferred(); const finished = deferred(); let requestCount = 0; state.handler = async (route) => { requestCount += 1; if (requestCount === 1) { await pending.promise; await route.fulfill({ json: response("campaign-a", 99, "obsolete-version", true) }); finished.resolve(); } else await route.fulfill({ json: response(change === "campaign" ? "campaign-b" : "campaign-a", 2, "fresh-version") }); }; await page.getByRole("button", { name: "Reload workspace", exact: true }).click(); await expect.poll(() => requestCount).toBe(1); await page.getByRole("button", { name: { campaign: "Switch campaign", identity: "Switch identity", version: "Select version" }[change] }).click(); await expect(page.getByTestId("workspace-version")).toHaveText("fresh-version"); pending.resolve(); await finished.promise; await page.evaluate(() => new Promise((done) => requestAnimationFrame(() => requestAnimationFrame(done)))); await expect(page.getByTestId("workspace-version")).toHaveText("fresh-version"); expect(requests).toHaveLength(2); expect(requests[1].searchParams.has("since")).toBe(false); }); } test("failed initial load for a different campaign never reveals the old campaign", async ({ page }) => { const { state } = await fixture(page); state.handler = async (route) => { await route.fulfill({ status: 403, json: { detail: "Campaign unavailable" } }); }; await page.getByRole("button", { name: "Switch campaign" }).click(); await expect(page.getByTestId("workspace-error")).toContainText("Campaign unavailable"); await expect(page.getByTestId("workspace-campaign")).toHaveText("none"); await expect(page.getByTestId("workspace-version")).toHaveText("none"); }); test("a failed later page never publishes a partially refreshed workspace", async ({ page }) => { const { state } = await fixture(page); let requestCount = 0; state.handler = async (route) => { requestCount += 1; await route.fulfill(requestCount === 1 ? { json: response("campaign-a", 4, "partial-version", true) } : { status: 503, json: { detail: "Second page unavailable" } }); }; await page.getByRole("button", { name: "Reload workspace", exact: true }).click(); await expect(page.getByTestId("workspace-error")).toContainText("Second page unavailable"); await expect(page.getByTestId("workspace-version")).toHaveText("campaign-a-version"); await expect(page.getByTestId("workspace-revision")).toHaveText("1"); });