e2e/find-work.spec.ts
Linked repository file from this build.
import { fixtureReviewKey } from "../src/programs/snap-negative/fixture-key";
import { resetDemoFixture } from "./demo-fixture";
import { expect, test, type APIRequestContext } from "@playwright/test";
import type { ReviewCommand } from "../src/features/reviews/commands";
async function change(request: APIRequestContext, command: ReviewCommand) {
const headers = { "x-qc-test-staff": command.type === "identity" ? "101" : "201" };
const stored = await (
await request.get("/api/reviews/aa1635c0-c99d-5f12-8f26-54d3f23bf13d", { headers })
).json();
const response = await request.post("/api/reviews/aa1635c0-c99d-5f12-8f26-54d3f23bf13d", {
headers,
data: {
version: stored.version,
requestId: crypto.randomUUID(),
commands: [
{
id: crypto.randomUUID(),
command,
},
],
},
});
expect(response.ok(), await response.text()).toBe(true);
}
test.beforeEach(async () => {
await resetDemoFixture();
});
test("stacked search combines independent suffix, surname and month filters", async ({
page,
request,
}) => {
await change(request, {
type: "identity",
value: { clientName: "Ada Zane", sampleMonth: "2026-04" },
});
const filters = [
{ field: "reviewNumber", mode: "ends", term: "01" },
{ field: "clientName", mode: "starts", term: "Z" },
{ field: "sampleMonth", from: "2026-04", through: "2026-07" },
];
const params = new URLSearchParams({ filters: JSON.stringify(filters), limit: "100" });
const response = await request.get(`/api/search?${params}`);
expect(response.ok()).toBe(true);
const result = await response.json();
expect(result.reviews.some((row: { reviewNumber: string }) => row.reviewNumber === "60001")).toBe(
true,
);
expect(
result.reviews.every(
(row: { reviewNumber: string; clientName: string; sampleMonth: string }) =>
row.reviewNumber.endsWith("01") &&
row.clientName.split(/\s+/).at(-1)?.startsWith("Z") &&
row.sampleMonth >= "2026-04" &&
row.sampleMonth <= "2026-07",
),
).toBe(true);
await page.goto(`/search?${params}`);
await expect(page.getByLabel("Client name term", { exact: true })).toHaveValue("Z");
await expect(
page
.locator('a[href="/reviews/aa1635c0-c99d-5f12-8f26-54d3f23bf13d"]')
.filter({ hasText: /^60001$/ }),
).toBeVisible();
await page.getByLabel("Client name term", { exact: true }).fill("NoSuchSurname");
await page.getByRole("main").getByRole("button", { name: "Search reviews", exact: true }).click();
await expect(
page
.locator('a[href="/reviews/aa1635c0-c99d-5f12-8f26-54d3f23bf13d"]')
.filter({ hasText: /^60001$/ }),
).toHaveCount(0);
await page.goto("/dashboard");
await page.getByRole("textbox", { name: "Search reviews", exact: true }).fill("60001");
await page.getByRole("link", { name: /^See all \d+ results?$/ }).click();
await expect(page).toHaveURL(/\/search\?/);
await expect(
page
.locator('a[href="/reviews/aa1635c0-c99d-5f12-8f26-54d3f23bf13d"]')
.filter({ hasText: /^60001$/ }),
).toBeVisible();
});
test("catalog flags are personal and external changes become unread", async ({ request }) => {
const staff = (id: string) => ({ "x-qc-test-staff": id });
const params = "examiner=101&month=2026-09&pageSize=100";
const row = async (id: string) => {
const response = await request.get(`/api/catalog?${params}`, { headers: staff(id) });
expect(response.ok()).toBe(true);
return (await response.json()).reviews.find(
(r: { reviewNumber: string }) => r.reviewNumber === "60001",
);
};
await request.get("/api/reviews/aa1635c0-c99d-5f12-8f26-54d3f23bf13d", { headers: staff("101") });
expect((await row("101")).unread).toBe(false);
expect(
(
await request.patch("/api/catalog/flag", {
headers: staff("101"),
data: { reviewKey: "aa1635c0-c99d-5f12-8f26-54d3f23bf13d", flagged: true },
})
).ok(),
).toBe(true);
expect((await row("101")).flagged).toBe(true);
expect((await row("201")).flagged).toBe(false);
await change(request, { type: "appointment", value: "2026-09-20T11:00" });
expect((await row("101")).unread).toBe(true);
await request.get("/api/reviews/aa1635c0-c99d-5f12-8f26-54d3f23bf13d", { headers: staff("101") });
expect((await row("101")).unread).toBe(false);
expect((await request.get("/api/catalog?queue=true", { headers: staff("101") })).status()).toBe(
403,
);
expect(
(
await request.patch("/api/catalog/flag", {
headers: staff("108"),
data: { reviewKey: "aa1635c0-c99d-5f12-8f26-54d3f23bf13d", flagged: true },
})
).status(),
).toBe(403);
await request.patch("/api/catalog/flag", {
headers: staff("101"),
data: { reviewKey: "aa1635c0-c99d-5f12-8f26-54d3f23bf13d", flagged: false },
});
});
test("review folders retain selection and senior queue scope", async ({ page }) => {
await page.setViewportSize({ width: 1440, height: 900 });
await page.goto("/reviews");
await expect(page.getByLabel("Reviews per page")).toHaveValue("10");
await page.getByRole("button", { name: /^FFY 2026/ }).click();
await page.getByRole("button", { name: /^September 2026/ }).click();
await expect(page.getByRole("link", { name: /60001 · FFY 2026 · Case/ })).toBeVisible();
await page.getByLabel("Reviews per page").selectOption("5");
await expect(page.getByRole("button", { name: /^September 2026/ })).toHaveAttribute(
"aria-current",
"true",
);
await page
.context()
.addCookies([{ name: "qc-test-staff", value: "201", url: "http://localhost:3001" }]);
await page.goto("/approval-queue");
await expect(page.getByRole("heading", { name: "Approval Queue", exact: true })).toBeVisible();
await expect(page.getByLabel("Reviews per page")).toHaveValue("10");
await page.getByRole("button", { name: /^Alex Examiner/ }).click();
await expect(page.getByRole("button", { name: /^FFY 2026/ })).toBeVisible();
});
test("header opens selected quick matches and Enter searches the latest query", async ({
page,
}) => {
await page.goto("/dashboard");
const search = page.getByRole("textbox", { name: "Search reviews", exact: true });
const suggestions = page.getByRole("list", { name: "Suggested reviews" });
await search.fill("6");
await expect(suggestions.getByRole("link")).toHaveCount(5);
await expect(page.getByRole("link", { name: /^See all \d+ results$/ })).toContainText(
/\+\d+ more/,
);
await search.fill("60001");
await expect(suggestions.getByRole("link").first()).toContainText("60001");
expect(await suggestions.getByRole("link").count()).toBeLessThanOrEqual(5);
await suggestions.locator('a[href="/reviews/aa1635c0-c99d-5f12-8f26-54d3f23bf13d"]').click();
await expect(page).toHaveURL(/\/reviews\/aa1635c0-c99d-5f12-8f26-54d3f23bf13d$/);
await expect(page.getByRole("heading", { name: "60001", exact: true })).toBeVisible();
// Enter during a pending lookahead must not open the previous query's top match.
await search.click();
await search.fill("60001");
await expect(suggestions.getByRole("link").first()).toContainText("60001");
const lookup = await page.request.get("/api/search?q=6&quick=true&limit=5");
const match = (await lookup.json()).reviews.find(
(row: { reviewNumber: string }) => row.reviewNumber !== "60001",
);
const target = match.reviewNumber;
let release: () => void = () => {};
const gate = new Promise<void>((resolve) => {
release = resolve;
});
let waiting = false;
let finishRoute: () => void = () => {};
const routeFinished = new Promise<void>((resolve) => {
finishRoute = resolve;
});
await page.route("**/api/search?**", async (route) => {
const params = new URL(route.request().url()).searchParams;
if (
params.get("q") === target &&
params.get("quick") === "true" &&
params.get("limit") === "5"
) {
waiting = true;
try {
await gate;
await route.continue();
} finally {
finishRoute();
}
return;
}
await route.continue();
});
try {
await search.fill(target);
await expect.poll(() => waiting).toBe(true);
await search.press("Enter");
await expect(page).toHaveURL(new RegExp(`/search\\?q=${target}$`));
release();
} finally {
release();
if (waiting) await routeFinished;
await page.unrouteAll({ behavior: "wait" });
}
await page.goto("/dashboard");
await search.fill("60001");
await expect(suggestions.getByRole("link").first()).toContainText("60001");
await page.getByRole("link", { name: /^See all \d+ results?$/ }).click();
await expect(page).toHaveURL(/\/search\?/);
expect(new URL(page.url()).searchParams.get("q")).toBe("60001");
await expect(page.getByRole("textbox", { name: "Search reviews", exact: true })).toHaveCount(1);
await expect(
page
.locator('a[href="/reviews/aa1635c0-c99d-5f12-8f26-54d3f23bf13d"]')
.filter({ hasText: /^60001$/ }),
).toBeVisible();
await search.fill("no-such-review-zzzz");
await search.press("Enter");
await expect(page.getByText("No reviews match these filters.")).toBeVisible();
await expect(suggestions).toHaveCount(0);
});
test("quick search separates the same review number across fiscal years", async ({ page }) => {
const duplicateKeys: string[] = [];
page.on("console", (message) => {
if (message.text().includes("same key")) duplicateKeys.push(message.text());
});
await page.route("**/api/search?**", (route) =>
route.fulfill({
json: {
total: 2,
reviews: [2025, 2026].map((year) => ({
reviewKey: fixtureReviewKey("60001", `${year}-09`),
fiscalYear: year,
reviewNumber: "60001",
sampleMonth: `${year}-09`,
clientName: `Case for ${year}`,
caseNumber: "100000001",
examiner: "101",
state: "new_active",
appointment: null,
})),
},
}),
);
await page.goto("/dashboard");
await page.getByRole("textbox", { name: "Search reviews", exact: true }).fill("60001");
const suggestions = page.getByRole("list", { name: "Suggested reviews" });
await expect(suggestions.getByRole("link")).toHaveCount(2);
for (const year of [2025, 2026]) {
const link = suggestions.getByRole("link", { name: new RegExp(`Case for ${year}`) });
await expect(link).toHaveAttribute(
"href",
`/reviews/${fixtureReviewKey("60001", `${year}-09`)}`,
);
await expect(link).toContainText(`FFY ${year}`);
}
await suggestions.getByRole("link", { name: /Case for 2026/ }).click();
await expect(page).toHaveURL(/\/reviews\/aa1635c0-c99d-5f12-8f26-54d3f23bf13d$/);
await expect(page.getByRole("heading", { name: "60001", exact: true })).toBeVisible();
expect(duplicateKeys).toEqual([]);
});