From 8dc45ee7e769cf661a597d3afd361d82505b9c08 Mon Sep 17 00:00:00 2001 From: Amruth Pillai Date: Mon, 28 Sep 2026 22:14:21 +0200 Subject: [PATCH] test(e2e): drive the rebuilt applications page --- tests/e2e/specs/applications-export.spec.ts | 15 +-- tests/e2e/specs/applications-tracker.spec.ts | 100 +++++++++++-------- 2 files changed, 65 insertions(+), 50 deletions(-) diff --git a/tests/e2e/specs/applications-export.spec.ts b/tests/e2e/specs/applications-export.spec.ts index 329a7f14a..c89038d54 100644 --- a/tests/e2e/specs/applications-export.spec.ts +++ b/tests/e2e/specs/applications-export.spec.ts @@ -43,8 +43,9 @@ test("exports filtered or all owned applications with an inclusive date range", ); } await page.goto("/dashboard/applications"); - await page.getByPlaceholder("Search applications…").fill("Alpha"); - await page.getByRole("button", { name: "Export CSV", exact: true }).click(); + await page.getByPlaceholder("Search role, company or contact").fill("Alpha"); + await page.getByRole("button", { name: "Import or export CSV" }).click(); + await page.getByRole("menuitem", { name: "Export to CSV…" }).click(); const sheet = page.getByRole("dialog", { name: "Export applications" }); await sheet.getByLabel("Application date from").fill("2026-08-03"); await sheet.getByLabel("Application date to").fill("2026-08-03"); @@ -60,9 +61,10 @@ test("exports filtered or all owned applications with an inclusive date range", expect(csv).toContain('"Applied (2026-08-03)"'); expect(csv).toContain('"Quoted ""note""\nSecond line"'); - await page.getByRole("button", { name: "Export CSV", exact: true }).click(); + await page.getByRole("button", { name: "Import or export CSV" }).click(); + await page.getByRole("menuitem", { name: "Export to CSV…" }).click(); await sheet.getByLabel("Applications to export").click(); - await page.getByRole("option", { name: "All applications (including archived)" }).click(); + await page.getByRole("option", { name: "All applications (including closed)" }).click(); await expect(sheet.getByText("2 applications to export")).toBeVisible(); await sheet.getByLabel("Application date from").fill("2026-08-04"); await expect(sheet.getByRole("button", { name: "Download CSV" })).toBeDisabled(); @@ -80,7 +82,7 @@ test("exports filtered or all owned applications with an inclusive date range", await page.reload(); await expect(page.getByRole("heading", { name: "Applications", exact: true })).toBeVisible(); const title = await page.getByRole("heading", { name: "Applications", exact: true }).boundingBox(); - const exportButton = await page.getByRole("button", { name: "Export CSV", exact: true }).boundingBox(); + const exportButton = await page.getByRole("button", { name: "Import or export CSV" }).boundingBox(); if (!title || !exportButton) throw new Error("Application header is not visible"); const overlaps = title.x < exportButton.x + exportButton.width && @@ -89,7 +91,8 @@ test("exports filtered or all owned applications with an inclusive date range", title.y + title.height > exportButton.y; expect(overlaps).toBe(false); await page.screenshot({ path: testInfo.outputPath("mobile-header.png"), animations: "disabled" }); - await page.getByRole("button", { name: "Export CSV", exact: true }).click(); + await page.getByRole("button", { name: "Import or export CSV" }).click(); + await page.getByRole("menuitem", { name: "Export to CSV…" }).click(); await expect(sheet.getByRole("button", { name: "Download CSV" })).toBeVisible(); await page.screenshot({ path: testInfo.outputPath("mobile-export.png"), animations: "disabled" }); } finally { diff --git a/tests/e2e/specs/applications-tracker.spec.ts b/tests/e2e/specs/applications-tracker.spec.ts index cf994e53c..82c64796b 100644 --- a/tests/e2e/specs/applications-tracker.spec.ts +++ b/tests/e2e/specs/applications-tracker.spec.ts @@ -1,80 +1,92 @@ -import type { Locator, Page } from "@playwright/test"; +import type { Page } from "@playwright/test"; import { expect, test } from "../fixtures/test"; -const field = (sheet: Locator, label: string) => - sheet - .locator("label") - .filter({ hasText: label }) - .locator("xpath=following-sibling::input | following-sibling::textarea") - .first(); - async function openApplications(page: Page) { await page.goto("/dashboard/applications"); - await expect(page.getByRole("heading", { name: "Applications" })).toBeVisible(); + await expect(page.getByRole("heading", { name: "Applications", exact: true })).toBeVisible(); } -test("adds an application and logs stage changes and notes", async ({ authPage: page }) => { +test("adds an application from a posting, moves it, notes it and closes it with a reason", async ({ + authPage: page, +}) => { const company = `E2E Company ${Date.now().toString(36)}`; const role = "Frontend Platform Engineer"; const note = "Follow up with the hiring manager after the screen."; await openApplications(page); - await page.getByRole("button", { name: "Add application" }).click(); + await page.getByRole("button", { name: "Add application" }).first().click(); - const sheet = page.getByRole("dialog", { name: "Add application" }); - await field(sheet, "Company").fill(company); - await field(sheet, "Role / title").fill(role); - await field(sheet, "Location").fill("Remote"); - await field(sheet, "Salary range").fill("$180k"); - await field(sheet, "Source").fill("Referral"); - await field(sheet, "Notes").fill("Submitted through a referral."); - await sheet.getByRole("button", { name: "Add to pipeline" }).click(); + const add = page.getByRole("dialog", { name: "Add an application" }); + await add + .getByLabel("Job link or posting text") + .fill("We're hiring a Frontend Platform Engineer to own our design system."); + await add.getByLabel("Role").fill(role); + await add.getByLabel("Company").fill(company); + await add.getByRole("button", { name: "Add", exact: true }).click(); - const card = page.getByRole("button", { name: new RegExp(`${role}.*${company}`) }).first(); - await expect(card).toBeVisible(); - await expect(card).toContainText("Referral"); - - await card.click(); + // The new application opens; its stage is Applied, and the stepper moves it on. const detail = page.getByRole("dialog", { name: role }); await expect(detail.getByText(company)).toBeVisible(); - await detail.getByRole("button", { name: "Move to Applied" }).click(); - await expect(detail.getByRole("button", { name: "Move to Screening" })).toBeVisible(); - await expect(detail.locator('[data-timeline-entry="stage"][data-stage="applied"]')).toBeVisible(); + await detail.getByRole("button", { name: "Move to Screening" }).click(); + // The move is logged in the activity. + await expect(detail.getByRole("listitem").filter({ hasText: "Moved to Screening" })).toBeVisible(); + await expect(detail.getByRole("button", { name: "Move to Interview" })).toBeVisible(); - await detail.locator("[data-timeline-note-input]").fill(note); + await detail.getByRole("textbox", { name: "Add a note" }).fill(note); await detail.getByRole("button", { name: "Add", exact: true }).click(); await expect(detail.getByText(note)).toBeVisible(); + + await detail.getByRole("button", { name: "Close application…" }).click(); + const closing = page.getByRole("dialog", { name: "Close this application" }); + await closing.getByRole("radio", { name: "I withdrew" }).click(); + await closing.getByRole("button", { name: "Close application" }).click(); + await expect(detail.getByText("I withdrew").first()).toBeVisible(); + await expect(detail.getByRole("button", { name: "Reopen" })).toBeVisible(); + + // Closed applications stay out of the list until asked for. + await page.keyboard.press("Escape"); + const row = page.getByRole("button", { name: new RegExp(role) }).filter({ hasText: company }); + await expect(row).toHaveCount(0); + await page.getByRole("button", { name: "Show closed" }).click(); + await expect(row).toBeVisible(); }); -test("imports applications from CSV and archives them from the table view", async ({ authPage: page }) => { +test("imports from CSV with a confirmed column match, then closes them in bulk", async ({ authPage: page }) => { const company = `E2E Import ${Date.now().toString(36)}`; const role = "Imported Product Engineer"; const csv = [ - "Company,Role,Stage,Location,Salary,Source,Tags", - `${company},${role},applied,Remote,$190k,LinkedIn,remote;imported`, + "Employer,Job Title,Status,Location", + `${company},${role},applied,Remote`, + ",No company,saved,Remote", ].join("\n"); await openApplications(page); - await page.getByRole("button", { name: /Import (CSV|from CSV)/ }).click(); + await page.getByRole("button", { name: "Import or export CSV" }).click(); + await page.getByRole("menuitem", { name: "Import from CSV…" }).click(); const sheet = page.getByRole("dialog", { name: "Import from CSV" }); - await sheet.locator("textarea").fill(csv); - await expect(sheet.getByText("1 ready to import")).toBeVisible(); - await sheet.getByRole("button", { name: "Import 1" }).click(); + await sheet.getByLabel("CSV data").fill(csv); + // Headers are matched automatically, and the match can be changed before importing. + await expect(sheet.getByRole("combobox", { name: "Column Employer goes to" })).toHaveValue("company"); + await expect(sheet.getByRole("combobox", { name: "Column Job Title goes to" })).toHaveValue("role"); + await expect(sheet.getByText("1 application ready to import")).toBeVisible(); + await expect(sheet.getByText("1 row has no company or role and will be skipped.")).toBeVisible(); + await sheet.getByRole("button", { name: "Import 1 application" }).click(); - await expect(page.getByRole("button", { name: new RegExp(`${role}.*${company}`) }).first()).toBeVisible(); + const row = page.getByRole("button", { name: new RegExp(role) }).filter({ hasText: company }); + await expect(row).toBeVisible(); await page.getByRole("tab", { name: "Insights" }).click(); - await expect(page.getByText("Pipeline health across all applications")).toBeVisible(); + await expect(page.getByText("How far applications get")).toBeVisible(); await expect(page.getByText("Where your applications went")).toBeVisible(); - await page.getByRole("tab", { name: "Table" }).click(); - await expect(page.getByRole("button", { name: new RegExp(`${role}.*${company}`) }).first()).toBeVisible(); - await page.getByRole("checkbox", { name: `Select ${company}` }).check(); + await page.getByRole("tab", { name: "List" }).click(); + await page.getByRole("checkbox", { name: `Select ${role} at ${company}` }).check(); await expect(page.getByText("1 selected")).toBeVisible(); - await page.getByRole("button", { name: "Archive" }).click(); + await page.getByRole("button", { name: "Close…" }).click(); + await page.getByRole("menuitem", { name: "Not selected" }).click(); - await expect(page.getByText("No applications match your filters.")).toBeVisible(); - await page.getByRole("button", { name: "Archived (1)" }).click(); - await expect(page.getByRole("button", { name: new RegExp(`${role}.*${company}`) }).first()).toBeVisible(); + await expect(row).toHaveCount(0); + await page.getByRole("button", { name: "Show closed" }).click(); + await expect(row).toBeVisible(); });