e2e/reports-dashboard.spec.ts
Linked repository file from this build.
import { reportRowFromReview } from "../src/features/reports/report-input";
import { expect, test } from "@playwright/test";
import { readFile } from "node:fs/promises";
import { generateSeedBatch } from "../src/programs/snap-negative/seed-generator";
import {
aggregateReport as aggregateRows,
currentFfyFilters,
} from "../src/features/reports/report-data";
import type { ReportResponse } from "../src/features/reports/report-data";
test("report charts, filters and CSV runs share the same scoped totals", async ({
page,
request,
}) => {
await page
.context()
.addCookies([{ name: "qc-test-staff", value: "501", url: "http://localhost:3001" }]);
await page.goto("/reports");
await expect(page.getByRole("button", { name: "Run report", exact: true })).toBeEnabled();
const headers = { "x-qc-test-staff": "501" };
const bounds = currentFfyFilters();
await expect(page.getByLabel("From sample month", { exact: true })).toHaveValue(bounds.from);
await expect(page.getByLabel("Through sample month", { exact: true })).toHaveValue(
bounds.through,
);
const response = await request.get(`/api/reports?${new URLSearchParams(bounds)}`, { headers });
expect(response.ok()).toBe(true);
const report = (await response.json()) as ReportResponse;
const metric = page
.getByRole("heading", { name: "Reviews in scope", exact: true })
.locator("..")
.locator("p")
.first();
await expect(metric).toHaveText(report.summary.total.toLocaleString());
const codes = report.summary.byErrorCode;
await expect(
page.getByRole("heading", { name: "Errors by element and nature code", exact: true }),
).toBeVisible();
if (codes.length) {
const table = page.getByRole("table", {
name: "Error summary errors by element and nature code",
});
await expect(table.locator("tbody tr")).toHaveCount(codes.length);
await expect(table.locator("tbody tr").first()).toContainText(codes[0].elementLabel);
}
for (const group of ["Workflow state", "Sample month", "Examiner", "County"])
await expect(
page.getByRole("region", { name: `${group} progress`, exact: true }),
).toBeVisible();
await page.getByLabel("From sample month", { exact: true }).fill("2026-04");
await page.getByLabel("Through sample month", { exact: true }).fill("2026-07");
await page.getByRole("combobox", { name: "Examiner", exact: true }).selectOption("101");
const filteredResponse = await request.get(
"/api/reports?from=2026-04&through=2026-07&examiner=101",
{ headers },
);
expect(filteredResponse.ok()).toBe(true);
const filtered = (await filteredResponse.json()) as ReportResponse;
expect(filtered.summary.total).toBeGreaterThan(0);
await expect(metric).toHaveText(String(filtered.summary.total));
const countyGroup = filtered.summary.byCounty.find((group) => group.total);
expect(countyGroup).toBeDefined();
const county = countyGroup!.key;
await page.getByRole("combobox", { name: "County", exact: true }).selectOption(county);
const countyResponse = await request.get(
`/api/reports?from=2026-04&through=2026-07&examiner=101&county=${encodeURIComponent(county)}`,
{ headers },
);
expect(countyResponse.ok()).toBe(true);
const count = ((await countyResponse.json()) as ReportResponse).summary.total;
await expect(metric).toHaveText(String(count));
await page.getByRole("button", { name: "Refresh", exact: true }).click();
await expect(page.getByRole("button", { name: "Run report", exact: true })).toBeEnabled();
await expect(page.getByRole("combobox", { name: "County", exact: true })).toHaveValue(county);
await page.getByRole("button", { name: "Run report", exact: true }).click();
await expect(page.getByRole("table", { name: "Report runs" }).locator("tbody tr")).toHaveCount(1);
const download = page.waitForEvent("download");
await page.getByRole("button", { name: "CSV", exact: true }).click();
const file = await download;
expect(file.suggestedFilename()).toMatch(/^snap-negative-report-.*\.csv$/);
const csv = await readFile((await file.path())!, "utf8");
expect(csv).toContain(`Summary,all,All matching reviews,${count},`);
expect(csv).toContain("Sample month from,2026-04");
expect(csv).toContain("Sample month through,2026-07");
await page.getByRole("button", { name: "Clear filters", exact: true }).click();
const allYears = (await (
await request.get("/api/reports", { headers })
).json()) as ReportResponse;
await expect(metric).toHaveText(allYears.summary.total.toLocaleString());
await page.getByLabel("From sample month", { exact: true }).fill("2026-09");
await page.getByLabel("Through sample month", { exact: true }).fill("2026-04");
await expect(page.getByRole("button", { name: "Run report", exact: true })).toBeDisabled();
await expect(
page.getByRole("region", { name: "Report filters" }).getByRole("alert"),
).toContainText("From month");
});
test("error breakdown renders seeded template codes and exports the same pair counts", async ({
page,
}) => {
await page
.context()
.addCookies([{ name: "qc-test-staff", value: "501", url: "http://localhost:3001" }]);
const reviews = generateSeedBatch({
ffyFrom: 2026,
ffyTo: 2026,
perMonth: 5,
seed: 11,
asOf: new Date("2026-09-08T12:00:00Z"),
preserveReviewNumbers: [],
}).reviews;
const summary = aggregateReport(reviews);
expect(summary.byErrorCode.length).toBeGreaterThan(1);
await page.route("**/api/reports?*", (route) => {
const filters = Object.fromEntries(new URL(route.request().url()).searchParams);
return route.fulfill({
json: {
summary: aggregateReport(reviews, filters),
examiners: [...new Set(reviews.map((row) => row.examiner))],
counties: [...new Set(reviews.map((row) => row.county))],
},
});
});
await page.goto("/reports");
await page.getByLabel("From sample month", { exact: true }).fill("2025-10");
await page.getByLabel("Through sample month", { exact: true }).fill("2026-09");
await page.getByRole("button", { name: "Refresh", exact: true }).click();
await expect(page.getByRole("button", { name: "Run report", exact: true })).toBeEnabled();
const table = page.getByRole("table", {
name: "Error summary errors by element and nature code",
});
await expect(table.locator("tbody tr")).toHaveCount(summary.byErrorCode.length);
for (const [index, group] of summary.byErrorCode.entries()) {
const row = table.locator("tbody tr").nth(index);
await expect(row.locator("td").nth(0)).toContainText(group.element);
await expect(row.locator("td").nth(1)).toContainText(group.natureLabel);
await expect(row.locator("td").nth(2)).toHaveText(String(group.reviews));
}
await page.getByRole("button", { name: "Run report", exact: true }).click();
const pending = page.waitForEvent("download");
await page.getByRole("button", { name: "CSV", exact: true }).click();
const download = await pending;
const csv = await readFile((await download.path())!, "utf8");
for (const group of summary.byErrorCode)
expect(csv).toContain(`Error summary codes,${group.element}/${group.nature},`);
await page.getByLabel("Through sample month", { exact: true }).fill("2027-09");
await page.getByLabel("From sample month", { exact: true }).fill("2027-01");
await expect(
page.getByText("No released Invalid reviews in this scope.", { exact: true }),
).toBeVisible();
});
function aggregateReport(reviews: Parameters<typeof reportRowFromReview>[0][], filters = {}) {
return aggregateRows(reviews.map(reportRowFromReview), filters);
}
test("report API rejects unauthorized roles and malformed filters", async ({ request }) => {
expect(
(await request.get("/api/reports", { headers: { "x-qc-test-staff": "101" } })).status(),
).toBe(403);
for (const query of ["from=2026-13", "from=2026-09&through=2026-04", "from=2026"]) {
expect(
(
await request.get(`/api/reports?${query}`, { headers: { "x-qc-test-staff": "501" } })
).status(),
).toBe(400);
}
expect(
(
await request.get("/api/reports?from=2026-5&through=2026-09", {
headers: { "x-qc-test-staff": "501" },
})
).status(),
).toBe(200);
});
test("sample month filters sanitize typed values and expose a picker and sort", async ({
page,
}) => {
await page
.context()
.addCookies([{ name: "qc-test-staff", value: "501", url: "http://localhost:3001" }]);
await page.goto("/reports");
await expect(page.getByRole("button", { name: "Run report", exact: true })).toBeEnabled();
// The page opens on the current FFY, which has no seeded reviews early in the year.
await page.getByLabel("From sample month", { exact: true }).fill("2025-10");
await page.getByLabel("Through sample month", { exact: true }).fill("2026-09");
const monthChart = page.getByRole("region", { name: "Sample month progress", exact: true });
const sort = monthChart.getByRole("button", { name: /Sort sample month/ });
await expect(sort).toContainText("Newest first");
await sort.click();
await expect(sort).toContainText("Oldest first");
await expect(monthChart.getByRole("button", { name: /^Filter to/ }).first()).toContainText(
"October",
);
const countyChart = page.getByRole("region", { name: "County progress", exact: true });
const countySort = countyChart.getByRole("button", { name: /Sort county/ });
await expect(countySort).toContainText("A to Z");
await countySort.click();
await expect(countySort).toContainText("Z to A");
const from = page.getByLabel("From sample month", { exact: true });
await from.fill("2026-5");
await page.getByLabel("Through sample month", { exact: true }).click();
await expect(from).toHaveValue("2026-05");
await from.fill("5/2026");
await page.getByLabel("Through sample month", { exact: true }).click();
await expect(from).toHaveValue("2026-05");
await page.getByRole("button", { name: "Choose from sample month", exact: true }).click();
await page.getByRole("button", { name: "April 2026", exact: true }).click();
await expect(from).toHaveValue("2026-04");
});
test("failed report loads can be refreshed without enabling stale CSV runs", async ({ page }) => {
await page
.context()
.addCookies([{ name: "qc-test-staff", value: "501", url: "http://localhost:3001" }]);
let fail = true;
await page.route("**/api/reports?*", (route) =>
fail ? route.fulfill({ status: 503, json: { error: "Unavailable" } }) : route.continue(),
);
await page.goto("/reports");
await page.getByRole("button", { name: "Refresh", exact: true }).click();
await expect(page.locator("main").getByRole("alert")).toContainText("Unable to load reports");
await expect(page.getByRole("button", { name: "Run report", exact: true })).toBeDisabled();
fail = false;
await page.getByRole("button", { name: "Refresh", exact: true }).click();
await expect(page.getByRole("button", { name: "Run report", exact: true })).toBeEnabled();
});