fix(jobs): align table and board query contracts
This commit is contained in:
@@ -24,7 +24,7 @@ async function scan(page: Page, info: TestInfo, name: string) {
|
||||
expect(result.violations).toEqual([]);
|
||||
}
|
||||
|
||||
test("[PRES-J07 J05 W01 W02 E07 E08 E09] full Board keeps its own population, moves, focus and route", async ({ page, playwright, context }, info) => {
|
||||
test("[PRES-J07 J05 W01 W02 E07 E08 E09] Board query membership matches Table and preserves moves, focus and route", async ({ page, playwright, context }, info) => {
|
||||
test.setTimeout(240_000);
|
||||
const admin = await playwright.request.newContext();
|
||||
const ids: number[] = [];
|
||||
@@ -55,10 +55,52 @@ test("[PRES-J07 J05 W01 W02 E07 E08 E09] full Board keeps its own population, mo
|
||||
}
|
||||
|
||||
await page.setViewportSize({ width: 1440, height: 900 });
|
||||
await page.goto("/kanban?q=table-only");
|
||||
// Native API membership, not just visible counts. Board uses one complete read; only this
|
||||
// regression test enumerates Table pages to compare the full logical population.
|
||||
for (const query of ['', 'q=Board', 'status=Saved', 'status=Take-home%20assignment', 'q=Board&status=Saved', 'q=absent', `companyId=${companyId}&location=Troms%C3%B8&sortBy=jobTitle&sortDir=asc`]) {
|
||||
const boardResponse = await page.request.get(`${apiUrl}/jobapplications/board?${query}`);
|
||||
expect(boardResponse.status()).toBe(200);
|
||||
const board = await boardResponse.json();
|
||||
const tableIds: number[] = [];
|
||||
let total = 0;
|
||||
for (let number = 1; ; number++) {
|
||||
const response = await page.request.get(`${apiUrl}/jobapplications?${query}&page=${number}&pageSize=15`);
|
||||
expect(response.status()).toBe(200);
|
||||
const data = await response.json();
|
||||
total = data.total;
|
||||
tableIds.push(...data.items.map((item: { id: number }) => item.id));
|
||||
if (tableIds.length >= total) break;
|
||||
}
|
||||
expect(board.map((item: { id: number }) => item.id).sort((a: number, b: number) => a - b)).toEqual([...tableIds].sort((a, b) => a - b));
|
||||
expect(board.length).toBe(total);
|
||||
expect(tableIds.every(id => ids.includes(id))).toBe(true);
|
||||
}
|
||||
await page.goto("/kanban?q=Board&status=Saved");
|
||||
await expect(page.locator('[data-kanban-card-id]')).toHaveCount(24);
|
||||
await capture(page, info, "board-filtered-query-light-en");
|
||||
await page.getByRole('link', { name: 'Job Applications', exact: true }).click();
|
||||
await expect(page).toHaveURL(url => url.pathname === '/jobs' && url.search === '?q=Board&status=Saved');
|
||||
await expect(page.locator('[data-job-row-id]')).toHaveCount(15);
|
||||
const visibleIds = await page.locator('[data-job-row-id]').evaluateAll(rows => rows.map(row => Number(row.getAttribute('data-job-row-id'))));
|
||||
expect(visibleIds.every(id => ids.slice(3).includes(id))).toBe(true);
|
||||
await page.goBack();
|
||||
await expect(page.locator('[data-kanban-card-id]')).toHaveCount(24);
|
||||
const filteredControl = page.locator(`[data-kanban-card-id="${ids[4]}"] [role="button"]`);
|
||||
await filteredControl.focus(); await page.keyboard.press('Space');
|
||||
await page.getByRole('group', { name: 'Active column' }).focus(); await page.keyboard.press('Enter');
|
||||
await expect(page.locator('[data-kanban-card-id]')).toHaveCount(23);
|
||||
await expect(page.locator(`[data-kanban-card-id="${ids[4]}"]`)).toHaveCount(0);
|
||||
await expect(page.getByRole('group', { name: 'Not Applied column' })).toBeFocused();
|
||||
expect((await page.request.patch(`${apiUrl}/jobapplications/${ids[4]}/status`, { headers, data: { status: 'Saved' } })).status()).toBe(204);
|
||||
await page.goto('/kanban?q=absent');
|
||||
await expect(page.locator('[data-kanban-card-id]')).toHaveCount(0);
|
||||
await expect(page.getByText('No jobs in this group yet.')).toHaveCount(3);
|
||||
await capture(page, info, 'board-zero-query-light-en');
|
||||
|
||||
await page.goto("/kanban?source=board");
|
||||
const cards = page.locator("[data-kanban-card-id]");
|
||||
await expect(cards).toHaveCount(27);
|
||||
expect(new URL(page.url()).search).toBe("?q=table-only");
|
||||
expect(new URL(page.url()).search).toBe("?source=board");
|
||||
expect((await page.request.get(`${apiUrl}/jobapplications/board`)).status()).toBe(200);
|
||||
const tablePage = await (await page.request.get(`${apiUrl}/jobapplications?page=1&pageSize=15&q=Board`)).json();
|
||||
expect(tablePage.items.length).toBe(15);
|
||||
@@ -73,7 +115,7 @@ test("[PRES-J07 J05 W01 W02 E07 E08 E09] full Board keeps its own population, mo
|
||||
await expect(cards).toHaveCount(27);
|
||||
await expect(page.getByRole("group", { name: t.kanbanColumnLabel.replace("{label}", t.pipelineGroupNotApplied) })).toBeVisible();
|
||||
await expect(page.getByRole("group", { name: t.kanbanColumnNotDropTarget.replace("{label}", t.kanbanGroupOther) })).toBeVisible();
|
||||
await expect(page.getByRole("link", { name: t.jobApplications })).toHaveAttribute("href", "/jobs");
|
||||
await expect(page.getByRole("link", { name: t.jobApplications })).toHaveAttribute("href", "/jobs?source=board");
|
||||
await capture(page, info, `board-desktop-${mode}-${language}`);
|
||||
await page.setViewportSize({ width: 390, height: 844 });
|
||||
await expect(page.getByRole("group", { name: t.kanbanColumnLabel.replace("{label}", t.pipelineGroupNotApplied) })).toBeVisible();
|
||||
@@ -155,7 +197,7 @@ test("[PRES-J07 J05 W01 W02 E07 E08 E09] full Board keeps its own population, mo
|
||||
await expect(page).toHaveURL(new RegExp(`/jobs/${ids[0]}$`));
|
||||
await expect(page.getByRole("heading", { name: /Seniorrådgiver/ })).toBeVisible();
|
||||
await page.goBack();
|
||||
await expect(page).toHaveURL(url => url.pathname === "/kanban" && url.search === "?q=table-only");
|
||||
await expect(page).toHaveURL(url => url.pathname === "/kanban" && url.search === "?source=board");
|
||||
await expect(savedControl).toBeFocused();
|
||||
} finally {
|
||||
await admin.dispose();
|
||||
@@ -171,7 +213,7 @@ test("[PRES-J07 E08] Board loading, retryable error and fully empty columns", as
|
||||
await expect(page).toHaveURL(/\/dashboard$/);
|
||||
let release!: () => void;
|
||||
let state: "loading" | "error" | "empty" = "loading";
|
||||
await page.route("**/api/jobapplications/board", async route => {
|
||||
await page.route("**/api/jobapplications/board?**", async route => {
|
||||
if (state === "loading") await new Promise<void>(resolve => { release = resolve; });
|
||||
if (state === "error") await route.fulfill({ status: 503, json: { message: "Synthetic board outage" } });
|
||||
else await route.fulfill({ json: [] });
|
||||
|
||||
Reference in New Issue
Block a user