import { expect, test } from "@playwright/test"; import { installMockApi } from "./mockApi"; test.beforeEach(async ({ page }) => { await installMockApi(page); }); test("shell renders navigation, brand and connection status", async ({ page }) => { await page.goto("/"); await expect(page.getByText("RPKI Explorer")).toBeVisible(); const nav = page.getByRole("navigation", { name: "Primary" }); for (const label of [ "Overview", "Repositories", "Publication Points", "Objects", "Validation", "Exports", "Runs", "API", ]) { await expect(nav.getByRole("link", { name: label })).toBeVisible(); } await expect(page.getByText("connected")).toBeVisible(); }); test("every nav entry navigates to a real page (no dead controls)", async ({ page }) => { await page.goto("/"); const nav = page.getByRole("navigation", { name: "Primary" }); const cases: { label: string; path: RegExp; heading: string | RegExp }[] = [ { label: "Repositories", path: /\/repositories$/, heading: "Repositories" }, { label: "Publication Points", path: /\/publication-points$/, heading: "Publication Points" }, { label: "Objects", path: /\/objects$/, heading: "Objects" }, { label: "Validation", path: /\/validation$/, heading: "Validation" }, { label: "Exports", path: /\/exports$/, heading: "Exports" }, { label: "Runs", path: /\/runs$/, heading: "Runs" }, { label: "API", path: /\/api$/, heading: "API" }, ]; for (const { label, path, heading } of cases) { await nav.getByRole("link", { name: label, exact: true }).click(); await expect(page).toHaveURL(path); await expect(page.getByRole("heading", { name: heading }).first()).toBeVisible(); } }); test("run selector pins the run via ?run= and pages use it", async ({ page }) => { const requests: string[] = []; page.on("request", (req) => { if (req.url().includes("/api/v1/runs/")) requests.push(req.url()); }); await page.goto("/"); const selector = page.getByLabel("Run"); await expect(selector).toBeVisible(); await selector.selectOption("run_0001"); await expect(page).toHaveURL(/run=run_0001/); await expect.poll(() => requests.some((url) => url.includes("/runs/run_0001/"))).toBe(true); }); test("run selector polls for new runs without a page refresh", async ({ page }) => { test.setTimeout(60_000); let runsRequests = 0; page.on("request", (req) => { const url = new URL(req.url()); if (/\/api\/v1\/runs$/.test(url.pathname)) runsRequests += 1; }); await page.goto("/"); await expect(page.getByLabel("Run")).toBeVisible(); // Initial fetch happens immediately; the 30s refetchInterval issues another. await expect .poll(() => runsRequests, { timeout: 45_000, intervals: [1_000, 2_000, 5_000] }) .toBeGreaterThan(1); }); test("topbar search routes to the search page with the query", async ({ page }) => { await page.goto("/"); const input = page.getByRole("searchbox", { name: "Global search" }); await input.fill("repo-a.example"); await input.press("Enter"); await expect(page).toHaveURL(/\/search\?q=repo-a/); await expect(page.getByRole("heading", { name: "Search", exact: true })).toBeVisible(); }); test("unknown routes render the 404 page", async ({ page }) => { await page.goto("/definitely-not-a-page"); await expect(page.getByText("404")).toBeVisible(); await page.getByRole("link", { name: "Back to Overview" }).click(); await expect(page).toHaveURL(/\/$/); }); test("deep links restore page state after reload", async ({ page }) => { await page.goto("/objects?type=manifest"); await expect(page).toHaveURL(/type=manifest/); await expect(page.getByLabel("Type")).toHaveValue("manifest"); await page.reload(); await expect(page.getByLabel("Type")).toHaveValue("manifest"); }); test("runs page lists runs, pins a run and expands artifacts", async ({ page }) => { await page.goto("/runs"); // Scope to the row: the actions cell also contains the run id in its // accessible name ("Use run run_0002 …"), so a bare cell lookup is ambiguous. await expect( page.getByRole("row", { name: /run_0002/ }).getByRole("cell").first(), ).toContainText("run_0002"); const row = page.getByRole("row", { name: /run_0001/ }); await expect(row.getByRole("cell").first()).toContainText("run_0001"); // Expand artifacts for run_0001 await row.getByRole("button", { name: "Artifacts" }).click(); await expect(page.getByText("/data/runs/run_0001/report.json")).toBeVisible(); // Pin run_0001 — navigates to overview with ?run=run_0001 await row.getByRole("button", { name: "Use run" }).click(); await expect(page).toHaveURL(/run=run_0001/); });