mirror of
https://github.com/AmruthPillai/Reactive-Resume.git
synced 2026-10-03 10:13:47 +10:00
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:
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
});
|
||||
@@ -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" });
|
||||
});
|
||||
@@ -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");
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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" });
|
||||
});
|
||||
@@ -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}`);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user