test(e2e): drive the rebuilt applications page

This commit is contained in:
Amruth Pillai
2026-09-28 22:14:21 +02:00
parent 462db6011a
commit 8dc45ee7e7
2 changed files with 65 additions and 50 deletions
+9 -6
View File
@@ -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 {
+56 -44
View File
@@ -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();
});