e2e/expanded-search.spec.ts
Linked repository file from this build.
import { resetDemoFixture } from "./demo-fixture";
import { expect, test, type APIRequestContext, type Locator } from "@playwright/test";
import { normalizeReviewAnswers } from "../src/programs/snap-negative/field-catalog";
import type { ReviewCommand } from "../src/features/reviews/commands";
import { demoScenarioTemplate } from "../src/programs/snap-negative/seed";
async function change(request: APIRequestContext, command: ReviewCommand) {
const stored = await (
await request.get("/api/reviews/aa1635c0-c99d-5f12-8f26-54d3f23bf13d")
).json();
const response = await request.post("/api/reviews/aa1635c0-c99d-5f12-8f26-54d3f23bf13d", {
data: {
version: stored.version,
requestId: crypto.randomUUID(),
commands: [
{
id: crypto.randomUUID(),
command,
},
],
},
});
expect(response.ok(), await response.text()).toBe(true);
}
async function selectExtraField(region: Locator, key: string) {
await region.getByRole("button", { name: "Add filter", exact: true }).click();
const row = region.locator("[data-search-filter-row]").last();
await row.getByRole("button", { name: /^Filter \d+ field$/ }).click();
const chooser = region.getByRole("dialog", { name: "Choose search field" });
await chooser.getByRole("textbox", { name: "Find a field" }).fill(key);
await chooser.getByText(key, { exact: true }).click();
return row;
}
test("expanded search retains common fields and supports exclusion, ranges, people, nested data and sorting", async ({
page,
request,
}) => {
await resetDemoFixture();
await change(request, { type: "reset_demo" });
await change(request, {
type: "identity",
value: { clientName: "Ada Zane", sampleMonth: "2026-04" },
});
await change(request, {
type: "scenario",
scenario: "eligibility",
value: { ...demoScenarioTemplate(1800), householdSize: 3 },
});
await change(request, {
type: "answers",
value: {
...normalizeReviewAnswers({}),
narrative: "Expanded search collateral verification marker.",
},
});
const fieldsResponse = await request.get("/api/search/fields");
expect(fieldsResponse.ok()).toBe(true);
const { fields } = (await fieldsResponse.json()) as { fields: { key: string; label: string }[] };
const label = (key: string) => fields.find((field) => field.key === key)!.label;
await page.goto("/search");
const advanced = page.getByRole("region", { name: "Advanced search", exact: true });
const apply = page.getByRole("main").getByRole("button", { name: "Search reviews", exact: true });
await expect(advanced).toBeVisible();
await advanced.getByLabel("Review number term", { exact: true }).fill("60001");
await advanced.getByLabel("Client name term", { exact: true }).fill("Zane");
await advanced.getByLabel("Sample month from", { exact: true }).fill("2026-04");
await advanced.getByLabel("Sample month through", { exact: true }).fill("2026-04");
await apply.click();
await expect(page).toHaveURL(/filters=/);
const results = page.getByRole("table", { name: "Search results" });
await expect(
results
.locator('a[href="/reviews/aa1635c0-c99d-5f12-8f26-54d3f23bf13d"]')
.filter({ hasText: /^60001$/ }),
).toBeVisible();
await page.reload();
await expect(advanced.getByLabel("Review number term", { exact: true })).toHaveValue("60001");
await expect(advanced.getByLabel("Client name term", { exact: true })).toHaveValue("Zane");
await expect(advanced.getByLabel("Sample month from", { exact: true })).toHaveValue("2026-04");
const month = await selectExtraField(advanced, "sampleMonth");
await month.getByLabel("Sample month match mode").selectOption("exact");
await month.getByLabel("Sample month term", { exact: true }).fill("2026-04");
await month.getByLabel("Sample month match mode").selectOption("not:exact");
await apply.click();
await expect(page.getByText("No reviews match these filters.")).toBeVisible();
await month.getByRole("button", { name: "Remove Sample month filter", exact: true }).click();
const householdKey = "facts.scenarios.eligibility.householdSize";
const household = await selectExtraField(advanced, householdKey);
await household.getByLabel(`${label(householdKey)} match mode`).selectOption("between");
await household.getByLabel(`${label(householdKey)} from`, { exact: true }).fill("3");
await household.getByLabel(`${label(householdKey)} through`, { exact: true }).fill("3");
const examiner = await selectExtraField(advanced, "examiner");
await examiner
.getByLabel("Examiner value", { exact: true })
.selectOption({ label: "Alex Examiner" });
const narrativeKey = "facts.answers.narrative";
const narrative = await selectExtraField(advanced, narrativeKey);
await narrative
.getByLabel(`${label(narrativeKey)} term`, { exact: true })
.fill("collateral verification marker");
await apply.click();
await expect(
results
.locator('a[href="/reviews/aa1635c0-c99d-5f12-8f26-54d3f23bf13d"]')
.filter({ hasText: /^60001$/ }),
).toBeVisible();
await expect(
results.getByRole("columnheader", { name: label(householdKey), exact: true }),
).toBeVisible();
await expect(
results.getByRole("columnheader", { name: label(narrativeKey), exact: true }),
).toBeVisible();
await expect(results).toContainText("Expanded search collateral verification marker.");
const narrowRow = advanced.locator("[data-search-filter-row]").last();
expect((await narrowRow.boundingBox())!.height).toBeLessThanOrEqual(40);
await expect(advanced.getByLabel("Sample month from", { exact: true })).toHaveAttribute(
"type",
"month",
);
await advanced.getByRole("button", { name: "Add sort", exact: true }).click();
await advanced.getByRole("button", { name: "Sort 1 field", exact: true }).click();
const sortChooser = advanced.getByRole("dialog", { name: "Choose search field" });
await sortChooser.getByRole("textbox", { name: "Find a field" }).fill("reviewNumber");
await sortChooser.getByRole("button").filter({ hasText: "reviewNumber" }).click();
await apply.click();
await expect
.poll(() => JSON.parse(new URL(page.url()).searchParams.get("sorts") || "[]"))
.toEqual([{ field: "reviewNumber", direction: "asc" }]);
await results
.getByRole("columnheader", { name: "Review", exact: true })
.getByRole("button")
.click();
await expect(results.getByRole("columnheader", { name: "Review", exact: true })).toHaveAttribute(
"aria-sort",
"descending",
);
await advanced.getByRole("button", { name: "Add sort", exact: true }).click();
await apply.click();
await expect
.poll(() => JSON.parse(new URL(page.url()).searchParams.get("sorts") || "[]").length)
.toBe(2);
await advanced.getByRole("button", { name: "Clear sort", exact: true }).click();
await expect.poll(() => new URL(page.url()).searchParams.get("sorts")).toBeNull();
await expect(advanced.getByText("Sort: Relevance", { exact: true })).toBeVisible();
await expect(
results
.locator('a[href="/reviews/aa1635c0-c99d-5f12-8f26-54d3f23bf13d"]')
.filter({ hasText: /^60001$/ }),
).toBeVisible();
await expect(page.getByRole("status").filter({ hasText: "matching review" })).toBeVisible();
});