test(e2e): drive documents, trash and the new-document dialog

The shared fixture creates its sample resume through the API and opens
it; documents-new covers Start blank (named after the headline) and Copy
for a job (linked and searchable by the application). dashboard-lifecycle
renames inline, duplicates, trashes with undo, restores and deletes now;
the letter, lock, view, import, auth and direction specs follow the new
library and menus.
This commit is contained in:
Amruth Pillai
2026-09-28 20:39:04 +02:00
parent daa7d331f3
commit 443897dcb0
12 changed files with 209 additions and 124 deletions
+5 -1
View File
@@ -27,7 +27,11 @@ export async function loginViaUi(page: Page, account: E2EAccount) {
}
export async function logoutViaUi(page: Page, account: E2EAccount) {
await page.getByText(account.email).click();
// The avatar row at the foot of the sidebar opens the account menu.
await page
.getByRole("complementary")
.getByRole("button", { name: new RegExp(account.name) })
.click();
await page.getByRole("menuitem", { name: "Sign out" }).click();
await page.goto("/auth/login");
}
+15 -14
View File
@@ -2,20 +2,20 @@ import type { Page, TestInfo } from "@playwright/test";
import { expect } from "@playwright/test";
import { createResumeName } from "./data";
/**
* Creates a sample resume named after the test and opens it in the editor. It goes through the API: the New
* dialog has its own spec (documents), and every other spec just needs a resume.
*/
export async function createSampleResumeFromDashboard(page: Page, testInfo: TestInfo) {
const resumeName = createResumeName(testInfo);
await page.goto("/dashboard/resumes");
await page.getByText("Create a new resume").click();
const response = await page.request.post("/api/openapi/resumes", {
data: { name: resumeName, tags: [], withSampleData: true },
});
expect(response.ok()).toBe(true);
const resumeId = (await response.json()) as string;
const dialog = page.getByRole("dialog", { name: "Create a new resume" });
await dialog.getByLabel("Name").fill(resumeName);
const createGroup = dialog.getByRole("group", { name: "Create resume with options" });
await createGroup.getByRole("button").last().click();
await page.getByRole("menuitem", { name: "Create a Sample Resume" }).click();
// Creating a resume now navigates straight into the builder.
await page.goto(`/builder/${resumeId}`);
await page.waitForURL(/\/builder\/.+/);
return resumeName;
@@ -87,10 +87,11 @@ export async function getPublicUrl(page: Page) {
return (await link.getAttribute("href")) as string;
}
/** Opens a document's card menu in Documents (right-click, the same menu as ⋯). */
export async function openResumeCardMenu(page: Page, resumeName: string, { reload = true } = {}) {
if (reload) await page.goto("/dashboard/resumes");
const resumeLink = page.getByRole("link", { name: new RegExp(resumeName) });
await expect(resumeLink).toBeVisible();
await resumeLink.click({ button: "right" });
if (reload) await page.goto("/dashboard");
const card = page.getByRole("link", { name: resumeName, exact: true });
await expect(card).toBeVisible();
await card.click({ button: "right" });
await expect(page.getByRole("menuitem", { name: "Open" })).toBeVisible();
}
+2 -2
View File
@@ -3,11 +3,11 @@ import { expect, test } from "../fixtures/test";
test("registers and logs in with email credentials", async ({ page, account }) => {
await registerViaUi(page, account);
await expect(page.getByRole("heading", { name: "Resumes" })).toBeVisible();
await expect(page.getByRole("heading", { name: "Documents", level: 1 })).toBeVisible();
await logoutViaUi(page, account);
await expect(page.getByRole("heading", { name: "Sign in to your account" })).toBeVisible();
await loginViaUi(page, account);
await expect(page.getByRole("heading", { name: "Resumes" })).toBeVisible();
await expect(page.getByRole("heading", { name: "Documents", level: 1 })).toBeVisible();
});
+33 -19
View File
@@ -1,18 +1,26 @@
import type { Page } from "@playwright/test";
import { randomUUID } from "node:crypto";
import { readFile } from "node:fs/promises";
import { Pool } from "pg";
import { createSampleResumeFromDashboard, openSidebarSection } from "../fixtures/resume";
import { expect, test } from "../fixtures/test";
/** New → "New cover letter instead" opens the letter editor on an untitled letter; this names it. */
async function createLetterFromDocuments(page: Page, name: string) {
await page.goto("/dashboard");
await page.getByRole("button", { name: "New", exact: true }).click();
await page.getByRole("button", { name: "New cover letter instead" }).click();
const editor = page.getByRole("dialog", { name: "Edit cover letter", exact: true });
await expect(editor).toBeVisible();
await editor.getByLabel("Name", { exact: true }).fill(name);
return editor;
}
test("imports a library letter into the builder as an independent copy", async ({ authPage: page }, testInfo) => {
test.setTimeout(90_000);
await createSampleResumeFromDashboard(page, testInfo);
const builderUrl = page.url();
await page.goto("/dashboard/cover-letters");
await page.getByRole("button", { name: "Create", exact: true }).click();
await page.getByLabel("Name", { exact: true }).fill("Platform engineer letter");
await page.getByRole("button", { name: "Create cover letter", exact: true }).click();
const editor = page.getByRole("dialog", { name: "Edit cover letter", exact: true });
const editor = await createLetterFromDocuments(page, "Platform engineer letter");
await editor.getByLabel("Recipient", { exact: true }).fill("Dear hiring team,");
await editor.getByLabel("Content", { exact: true }).fill("I build reliable platforms for growing teams.");
await editor.getByRole("button", { name: "Save Changes", exact: true }).click();
@@ -59,20 +67,25 @@ test("imports a library letter into the builder as an independent copy", async (
await resumeSaved;
await expect(page.getByRole("button", { name: /^Updated independent resume copy\./ })).toBeVisible();
await page.goto("/dashboard/cover-letters");
await page.getByRole("button", { name: "Edit Platform engineer letter", exact: true }).click();
await page.goto("/dashboard?type=letter");
await page.getByRole("button", { name: "Platform engineer letter", exact: true }).click();
await expect(editor.getByLabel("Content", { exact: true })).toContainText("reliable platforms");
await expect(editor.getByLabel("Content", { exact: true })).not.toContainText("Updated independent resume copy");
await editor.getByRole("button", { name: "Close", exact: true }).click();
await page.getByLabel("Import cover letter JSON", { exact: true }).setInputFiles(jsonPath);
// New → Import reads a saved letter's JSON too, and opens the copy.
await page.getByRole("button", { name: "New", exact: true }).click();
await page
.getByRole("dialog", { name: "New document" })
.getByLabel("Choose a file to import")
.setInputFiles(jsonPath);
await expect(editor.getByLabel("Content", { exact: true })).toContainText("reliable platforms");
await editor.getByLabel("Name", { exact: true }).fill("Imported independent copy");
await editor.getByRole("button", { name: "Save Changes", exact: true }).click();
await expect(editor.getByRole("button", { name: "Save Changes", exact: true })).toBeDisabled();
await editor.getByRole("button", { name: "Close", exact: true }).click();
// The renamed copy shows once the list has refetched; until then both rows carry the original name.
await expect(page.getByRole("button", { name: "Edit Imported independent copy", exact: true })).toBeVisible();
await expect(page.getByRole("button", { name: "Edit Platform engineer letter", exact: true })).toBeVisible();
// The renamed copy shows once the list has refetched; until then both cards carry the original name.
await expect(page.getByRole("button", { name: "Imported independent copy", exact: true })).toBeVisible();
await expect(page.getByRole("button", { name: "Platform engineer letter", exact: true })).toBeVisible();
});
test("keeps the application PDF snapshot after the library letter is deleted", async ({ authPage: page, account }) => {
@@ -84,11 +97,7 @@ test("keeps the application PDF snapshot after the library letter is deleted", a
'insert into application (id, user_id, company, role) select $1, id, $2, $3 from "user" where email = $4',
[applicationId, "Snapshot Company", "Platform Engineer", account.email],
);
await page.goto("/dashboard/cover-letters");
await page.getByRole("button", { name: "Create", exact: true }).click();
await page.getByLabel("Name", { exact: true }).fill("Snapshot letter");
await page.getByRole("button", { name: "Create cover letter", exact: true }).click();
const editor = page.getByRole("dialog", { name: "Edit cover letter", exact: true });
const editor = await createLetterFromDocuments(page, "Snapshot letter");
await editor.getByLabel("Content", { exact: true }).fill("My application snapshot remains available.");
await editor.getByRole("button", { name: "Save Changes", exact: true }).click();
await expect(editor.getByRole("button", { name: "Save Changes", exact: true })).toBeDisabled();
@@ -106,10 +115,15 @@ test("keeps the application PDF snapshot after the library letter is deleted", a
expect(before.ok()).toBe(true);
const bytes = await before.body();
expect(bytes.subarray(0, 5).toString()).toBe("%PDF-");
await editor.getByRole("button", { name: "Delete", exact: true }).click();
await page.getByRole("alertdialog").getByRole("button", { name: "Delete", exact: true }).click();
await editor.getByRole("button", { name: "Move to Trash", exact: true }).click();
await expect(editor).not.toBeVisible();
await expect(page.getByRole("button", { name: "Edit Snapshot letter", exact: true })).not.toBeVisible();
await expect(page.getByRole("button", { name: "Snapshot letter", exact: true })).not.toBeVisible();
// Deleting it for good from Trash leaves the application's snapshot alone too.
await page.goto("/dashboard/trash");
await page.getByRole("button", { name: "Options for Snapshot letter" }).click();
await page.getByRole("menuitem", { name: "Delete now…" }).click();
await page.getByRole("alertdialog").getByRole("button", { name: "Delete now" }).click();
await expect(page.getByText("Trash is empty")).toBeVisible();
const after = await page.request.get(url);
expect(after.ok()).toBe(true);
expect(await after.body()).toEqual(bytes);
+39 -18
View File
@@ -1,30 +1,51 @@
import { createSampleResumeFromDashboard, openResumeCardMenu } from "../fixtures/resume";
import { expect, test } from "../fixtures/test";
test("renames, duplicates and deletes a resume from the dashboard", async ({ authPage: page }, testInfo) => {
test("renames, duplicates, moves to Trash, restores and deletes a resume", async ({ authPage: page }, testInfo) => {
const resumeName = await createSampleResumeFromDashboard(page, testInfo);
// Rename via the card context menu (resume names are capped at 64 chars, keep it short)
// Rename inline from the card menu: Enter commits.
const renamedTo = `E2E Renamed ${Date.now().toString(36)}`;
await openResumeCardMenu(page, resumeName);
await page.getByRole("menuitem", { name: "Edit details" }).click();
const updateDialog = page.getByRole("dialog", { name: "Update Resume" });
await updateDialog.getByLabel("Name").fill(renamedTo);
await updateDialog.getByRole("button", { name: "Save Changes" }).click();
await expect(page.getByRole("link", { name: new RegExp(renamedTo) })).toBeVisible();
await page.getByRole("menuitem", { name: "Rename" }).click();
const nameField = page.getByRole("textbox", { name: "Name", exact: true });
await nameField.fill(renamedTo);
await nameField.press("Enter");
await expect(page.getByRole("link", { name: renamedTo, exact: true })).toBeVisible();
// Duplicate — the copy defaults to "<name> (Copy)"
// Duplicate at once: "<name> (copy)".
await openResumeCardMenu(page, renamedTo, { reload: false });
await page.getByRole("menuitem", { name: "Duplicate" }).click();
const duplicateDialog = page.getByRole("dialog", { name: "Duplicate Resume" });
await duplicateDialog.getByRole("button", { name: "Duplicate" }).click();
const copyLink = page.getByRole("link", { name: new RegExp(`${renamedTo} \\(Copy\\)`) });
await expect(copyLink).toBeVisible();
const copyName = `${renamedTo} (copy)`;
await expect(page.getByRole("link", { name: copyName, exact: true })).toBeVisible();
// Delete the copy and verify it disappears while the original stays
await openResumeCardMenu(page, `${renamedTo} \\(Copy\\)`, { reload: false });
await page.getByRole("menuitem", { name: "Delete" }).click();
await page.getByRole("alertdialog").getByRole("button", { name: "Confirm" }).click();
await expect(copyLink).toBeHidden();
await expect(page.getByRole("link", { name: new RegExp(renamedTo) })).toBeVisible();
// Move the copy to Trash, undo, then move it again.
await openResumeCardMenu(page, copyName, { reload: false });
await page.getByRole("menuitem", { name: "Move to Trash" }).click();
await expect(page.getByRole("link", { name: copyName, exact: true })).toBeHidden();
await page.getByRole("button", { name: "Undo", exact: true }).click();
await expect(page.getByRole("link", { name: copyName, exact: true })).toBeVisible();
await openResumeCardMenu(page, copyName, { reload: false });
await page.getByRole("menuitem", { name: "Move to Trash" }).click();
await expect(page.getByRole("link", { name: copyName, exact: true })).toBeHidden();
// Trash: restore it, trash it again, then delete it now.
await page.getByRole("link", { name: /^Trash/ }).click();
await expect(page.getByRole("heading", { name: "Trash", level: 1 })).toBeVisible();
await expect(page.getByText("30 days left")).toBeVisible();
await page.getByRole("button", { name: `Options for ${copyName}` }).click();
await page.getByRole("menuitem", { name: "Restore" }).click();
await expect(page.getByText("Trash is empty")).toBeVisible();
await openResumeCardMenu(page, copyName);
await page.getByRole("menuitem", { name: "Move to Trash" }).click();
await page.goto("/dashboard/trash");
await page.getByRole("button", { name: `Options for ${copyName}` }).click();
await page.getByRole("menuitem", { name: "Delete now…" }).click();
await page.getByRole("alertdialog").getByRole("button", { name: "Delete now" }).click();
await expect(page.getByText("Trash is empty")).toBeVisible();
await page.goto("/dashboard");
await expect(page.getByRole("link", { name: renamedTo, exact: true })).toBeVisible();
await expect(page.getByRole("link", { name: copyName, exact: true })).toBeHidden();
});
+55
View File
@@ -0,0 +1,55 @@
import { randomUUID } from "node:crypto";
import { Pool } from "pg";
import { createSampleResumeFromDashboard } from "../fixtures/resume";
import { expect, test } from "../fixtures/test";
test("starts a blank resume on the name field and names it after the headline", async ({ authPage: page }) => {
await page.goto("/dashboard");
await page.getByRole("button", { name: "New", exact: true }).click();
await page.getByRole("button", { name: /^Start blank/ }).click();
await page.waitForURL(/\/builder\/.+/);
const fullName = page.getByRole("textbox", { name: "Full name", exact: true });
await expect(fullName).toBeFocused();
await fullName.fill("Jordan Reyes");
await page.getByRole("textbox", { name: "Headline", exact: true }).fill("Product Designer");
// Until someone renames it, the document is called by its headline.
await expect(page.getByRole("banner").getByText("Product Designer", { exact: true })).toBeVisible();
await page.goto("/dashboard");
await expect(page.getByRole("link", { name: "Product Designer", exact: true })).toBeVisible();
});
test("copies a resume for a job and links the copy to the application", async ({
authPage: page,
account,
}, testInfo) => {
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
try {
const resumeName = await createSampleResumeFromDashboard(page, testInfo);
await pool.query(
'insert into application (id, user_id, company, role) select $1, id, $2, $3 from "user" where email = $4',
[randomUUID(), "Orbital", "Product Designer", account.email],
);
await page.goto("/dashboard");
await page.getByRole("button", { name: "New", exact: true }).click();
await page.getByRole("button", { name: /^Copy a resume for a job/ }).click();
const dialog = page.getByRole("dialog", { name: "Copy a resume for a job" });
await dialog.getByText(resumeName, { exact: true }).click();
await dialog.getByRole("button", { name: "Orbital", exact: true }).click();
await expect(dialog.getByRole("textbox", { name: "Name", exact: true })).toHaveValue(`${resumeName} — Orbital`);
await dialog.getByRole("button", { name: "Create and open" }).click();
await page.waitForURL(/\/builder\/.+/);
await page.goto("/dashboard");
const copy = page.getByRole("article").filter({ has: page.getByRole("link", { name: `${resumeName} — Orbital` }) });
await expect(copy.getByText("Orbital", { exact: true })).toBeVisible();
// Search covers linked applications.
await page.getByRole("searchbox", { name: "Search documents" }).fill("orbital");
await expect(page.getByRole("link", { name: resumeName, exact: true })).toBeHidden();
await expect(page.getByRole("link", { name: `${resumeName} — Orbital`, exact: true })).toBeVisible();
} finally {
await pool.end();
}
});
+39
View File
@@ -0,0 +1,39 @@
import { createSampleResumeFromDashboard } from "../fixtures/resume";
import { expect, test } from "../fixtures/test";
test("switches between grid and list, remembers the choice, and honours the URL", async ({
authPage: page,
}, testInfo) => {
test.setTimeout(60_000);
await page.setViewportSize({ width: 1440, height: 1000 });
const name = await createSampleResumeFromDashboard(page, testInfo);
await page.goto("/dashboard");
const card = page.getByRole("link", { name, exact: true });
await expect(card).toBeVisible();
// The card shows the resume's real first page once it renders.
await expect(card.locator('[style*="background-image: url("]')).toBeVisible({ timeout: 30_000 });
const list = page.getByRole("radio", { name: "List" });
const grid = page.getByRole("radio", { name: "Grid" });
await list.click();
await expect(list).toHaveAttribute("aria-checked", "true");
await expect(page.getByRole("columnheader", { name: "Application" })).toBeVisible();
// The last view picked is remembered on this device.
await card.click();
await page.waitForURL(/\/builder\/.+/);
await page.goto("/dashboard");
await expect(list).toHaveAttribute("aria-checked", "true");
// The URL wins, and an unknown view falls back.
await page.goto("/dashboard?view=grid");
await expect(grid).toHaveAttribute("aria-checked", "true");
await page.goto("/dashboard?view=invalid");
await expect(list).toHaveAttribute("aria-checked", "true");
await page.setViewportSize({ width: 390, height: 844 });
await grid.click();
await expect(card).toBeVisible();
await expect.poll(() => page.evaluate(() => document.documentElement.scrollWidth)).toBeLessThanOrEqual(390);
await page.screenshot({ path: testInfo.outputPath("documents-mobile.png"), animations: "disabled" });
});
+6 -6
View File
@@ -16,12 +16,12 @@ test("exports and imports a resume JSON backup", async ({ authPage: page }, test
await download.saveAs(downloadPath);
const exportedData = JSON.parse(await readFile(downloadPath, "utf-8")) as { basics: { name: string } };
await page.goto("/dashboard/resumes");
await page.getByRole("button", { name: "Import", exact: true }).click();
const dialog = page.getByRole("dialog", { name: "Import an existing resume" });
// Import is now file-first: selecting the file auto-detects the Reactive Resume JSON format.
await dialog.locator('input[type="file"]').setInputFiles(downloadPath);
await dialog.getByRole("button", { name: "Import", exact: true }).click();
// New → Import a resume: picking the file detects the format and imports it in three steps.
await page.goto("/dashboard");
await page.getByRole("button", { name: "New", exact: true }).click();
const dialog = page.getByRole("dialog", { name: "New document" });
await dialog.getByLabel("Choose a file to import").setInputFiles(downloadPath);
await page.getByRole("button", { name: "Open in editor" }).click();
await page.waitForURL(/\/builder\/.+/);
await openSidebarSection(page, "Basics");
+7 -7
View File
@@ -1,7 +1,7 @@
import { createSampleResumeFromDashboard, openResumeCardMenu } from "../fixtures/resume";
import { expect, test } from "../fixtures/test";
test("locks a resume, blocking updates and deletion until unlocked", async ({ authPage: page }, testInfo) => {
test("locks a resume, blocking renames and Trash until unlocked", async ({ authPage: page }, testInfo) => {
const resumeName = await createSampleResumeFromDashboard(page, testInfo);
await openResumeCardMenu(page, resumeName);
@@ -10,15 +10,15 @@ test("locks a resume, blocking updates and deletion until unlocked", async ({ au
if (!response.ok()) return false;
return (response.request().postData() ?? "").includes('"isLocked":true');
});
await page.getByRole("menuitem", { name: "Lock" }).click();
await page.getByRole("alertdialog").getByRole("button", { name: "Confirm" }).click();
// Locking is reversible, so it doesn't ask first.
await page.getByRole("menuitem", { name: "Lock editing" }).click();
await lockPromise;
// Locked: the menu now offers Unlock, and destructive/edit actions are disabled
await openResumeCardMenu(page, resumeName);
await expect(page.getByRole("menuitem", { name: "Unlock" })).toBeVisible();
await expect(page.getByRole("menuitem", { name: "Edit details" })).toBeDisabled();
await expect(page.getByRole("menuitem", { name: "Delete" })).toBeDisabled();
await expect(page.getByRole("menuitem", { name: "Rename" })).toBeDisabled();
await expect(page.getByRole("menuitem", { name: "Move to Trash" })).toBeDisabled();
// Unlock restores the actions — wait for the mutation to land before re-reading the menu
const unlockPromise = page.waitForResponse((response) => {
@@ -29,6 +29,6 @@ test("locks a resume, blocking updates and deletion until unlocked", async ({ au
await page.getByRole("menuitem", { name: "Unlock" }).click();
await unlockPromise;
await openResumeCardMenu(page, resumeName);
await expect(page.getByRole("menuitem", { name: "Lock" })).toBeVisible();
await expect(page.getByRole("menuitem", { name: "Delete" })).toBeEnabled();
await expect(page.getByRole("menuitem", { name: "Lock editing" })).toBeVisible();
await expect(page.getByRole("menuitem", { name: "Move to Trash" })).toBeEnabled();
});
+7 -9
View File
@@ -26,15 +26,13 @@ for (const uiLanguage of ["English", "Arabic"]) {
test(`centers preview with ${uiLanguage} UI and ${resumeLocale} resume`, async ({ authPage: page }, info) => {
test.setTimeout(60_000);
await page.setViewportSize({ width: 1920, height: 950 });
await page.goto("/dashboard/resumes");
await page.getByText("Create a new resume", { exact: true }).click();
const dialog = page.getByRole("dialog", { name: "Create a new resume" });
await dialog.getByLabel("Name", { exact: true }).fill("Preview direction fixture");
await dialog.getByRole("button", { name: "Create", exact: true }).click();
await page.waitForURL(/\/builder\/.+/);
const builderUrl = page.url();
const resumeId = builderUrl.split("/").at(-1);
await page.goto("/dashboard/resumes");
const created = await page.request.post("/api/openapi/resumes", {
data: { name: "Preview direction fixture", tags: [] },
});
expect(created.ok()).toBe(true);
const resumeId = (await created.json()) as string;
await page.goto("/dashboard");
const builderUrl = new URL(`/builder/${resumeId}`, page.url()).toString();
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
try {
await pool.query(
-46
View File
@@ -1,46 +0,0 @@
import { createSampleResumeFromDashboard } from "../fixtures/resume";
import { expect, test } from "../fixtures/test";
test("keeps compact and list preferences through navigation and reload, with explicit URL overrides", async ({
authPage: page,
}, testInfo) => {
test.setTimeout(60_000);
await page.setViewportSize({ width: 1440, height: 1000 });
const name = await createSampleResumeFromDashboard(page, testInfo);
await page.goto("/dashboard/resumes");
const card = page.getByRole("link").filter({ hasText: name });
await expect(card).toBeVisible();
const gridWidth = (await card.boundingBox())?.width;
if (!gridWidth) throw new Error("Resume card has no width.");
await page.getByRole("tab", { name: "Compact", exact: true }).click();
await expect(page.getByRole("tab", { name: "Compact", exact: true })).toHaveAttribute("aria-selected", "true");
await expect.poll(async () => (await card.boundingBox())?.width ?? gridWidth).toBeLessThan(gridWidth);
await card.click();
await page.waitForURL(/\/builder\/.+/);
await page.goto("/dashboard/resumes");
await expect(page.getByRole("tab", { name: "Compact", exact: true })).toHaveAttribute("aria-selected", "true");
await page.reload();
await expect(page.getByRole("tab", { name: "Compact", exact: true })).toHaveAttribute("aria-selected", "true");
await expect(card.locator('[style*="background-image: url("]')).toBeVisible({ timeout: 30_000 });
await page.screenshot({ path: testInfo.outputPath("compact-resumes.png"), animations: "disabled" });
await page.getByRole("tab", { name: "Grid", exact: true }).click();
await expect(page).toHaveURL(/view=grid/);
await expect(page.getByRole("tab", { name: "Grid", exact: true })).toHaveAttribute("aria-selected", "true");
await expect.poll(async () => (await card.boundingBox())?.width).toBe(gridWidth);
await page.getByRole("tab", { name: "List", exact: true }).click();
await expect(page.getByRole("tab", { name: "List", exact: true })).toHaveAttribute("aria-selected", "true");
await page.goto("/dashboard/resumes");
await expect(page.getByRole("tab", { name: "List", exact: true })).toHaveAttribute("aria-selected", "true");
await page.goto("/dashboard/resumes?view=grid");
await expect(page.getByRole("tab", { name: "Grid", exact: true })).toHaveAttribute("aria-selected", "true");
await page.goto("/dashboard/resumes?view=invalid");
await expect(page.getByRole("tab", { name: "Grid", exact: true })).toHaveAttribute("aria-selected", "true");
await page.setViewportSize({ width: 390, height: 844 });
await page.getByRole("tab", { name: "Compact", exact: true }).click();
await expect(page.getByRole("tab", { name: "Compact", exact: true })).toHaveAttribute("aria-selected", "true");
await expect(card).toBeVisible();
await expect.poll(() => page.evaluate(() => document.documentElement.scrollWidth)).toBeLessThanOrEqual(390);
await page.screenshot({ path: testInfo.outputPath("compact-resumes-mobile.png"), animations: "disabled" });
});
+1 -2
View File
@@ -178,8 +178,7 @@ test("recovers hidden printable sections without changing authored placement", a
.toBe(true);
await openResumeCardMenu(page, resumeName);
const locked = page.waitForResponse((response) => (response.request().postData() ?? "").includes('"isLocked":true'));
await page.getByRole("menuitem", { name: "Lock" }).click();
await page.getByRole("alertdialog").getByRole("button", { name: "Confirm" }).click();
await page.getByRole("menuitem", { name: "Lock editing" }).click();
await locked;
await page.goto(`/builder/${resumeId}`);