mirror of
https://github.com/AmruthPillai/Reactive-Resume.git
synced 2026-07-12 22:14:54 +10:00
test: add e2e specs for dashboard, sections, templates, sharing and settings workflows
- dashboard-lifecycle: rename, duplicate, delete via card context menu - section-editing: add experience item, verify persistence across reloads - template-switch: switch template in gallery, verify persisted selection - sharing-password: password-protect public link, unlock as anonymous visitor - lock-resume: lock blocks update/delete, unlock restores them - settings-profile: profile name change persists
This commit is contained in:
@@ -27,3 +27,11 @@ export async function openSidebarSection(page: Page, title: string) {
|
||||
await page.getByTitle(title, { exact: true }).click();
|
||||
await expect(page.getByRole("heading", { name: title, exact: true })).toBeVisible();
|
||||
}
|
||||
|
||||
export async function openResumeCardMenu(page: Page, resumeName: string) {
|
||||
await page.goto("/dashboard/resumes");
|
||||
const resumeLink = page.getByRole("link", { name: new RegExp(resumeName) });
|
||||
await expect(resumeLink).toBeVisible();
|
||||
await resumeLink.click({ button: "right" });
|
||||
await expect(page.getByRole("menuitem", { name: "Open" })).toBeVisible();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
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) => {
|
||||
const resumeName = await createSampleResumeFromDashboard(page, testInfo);
|
||||
|
||||
// Rename via the card context menu (resume names are capped at 64 chars, keep it short)
|
||||
const renamedTo = `E2E Renamed ${Date.now().toString(36)}`;
|
||||
await openResumeCardMenu(page, resumeName);
|
||||
await page.getByRole("menuitem", { name: "Update" }).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();
|
||||
|
||||
// Duplicate — the copy defaults to "<name> (Copy)"
|
||||
await openResumeCardMenu(page, renamedTo);
|
||||
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();
|
||||
|
||||
// Delete the copy and verify it disappears while the original stays
|
||||
await openResumeCardMenu(page, `${renamedTo} \\(Copy\\)`);
|
||||
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();
|
||||
});
|
||||
@@ -0,0 +1,34 @@
|
||||
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) => {
|
||||
const resumeName = await createSampleResumeFromDashboard(page, testInfo);
|
||||
|
||||
await openResumeCardMenu(page, resumeName);
|
||||
const lockPromise = page.waitForResponse((response) => {
|
||||
if (!response.url().includes("/api/rpc")) return false;
|
||||
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();
|
||||
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: "Update" })).toBeDisabled();
|
||||
await expect(page.getByRole("menuitem", { name: "Delete" })).toBeDisabled();
|
||||
|
||||
// Unlock restores the actions — wait for the mutation to land before re-reading the menu
|
||||
const unlockPromise = page.waitForResponse((response) => {
|
||||
if (!response.url().includes("/api/rpc")) return false;
|
||||
if (!response.ok()) return false;
|
||||
return (response.request().postData() ?? "").includes('"isLocked":false');
|
||||
});
|
||||
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();
|
||||
});
|
||||
@@ -0,0 +1,34 @@
|
||||
import { createSampleResumeFromDashboard, openSidebarSection } from "../fixtures/resume";
|
||||
import { expect, test } from "../fixtures/test";
|
||||
|
||||
test("adds an experience item and persists it across reloads", async ({ authPage: page }, testInfo) => {
|
||||
await createSampleResumeFromDashboard(page, testInfo);
|
||||
|
||||
const company = `E2E Corp ${Date.now()}`;
|
||||
const position = "Principal Tester";
|
||||
|
||||
await openSidebarSection(page, "Experience");
|
||||
await page.getByRole("button", { name: "Add a new experience" }).click();
|
||||
|
||||
const dialog = page.getByRole("dialog", { name: "Create a new experience" });
|
||||
await dialog.getByLabel("Company").fill(company);
|
||||
await dialog.getByLabel("Position").fill(position);
|
||||
|
||||
const savePromise = page.waitForResponse((response) => {
|
||||
if (!response.url().includes("/api/rpc")) return false;
|
||||
if (response.request().method() !== "POST") return false;
|
||||
if (!response.ok()) return false;
|
||||
return (response.request().postData() ?? "").includes(company);
|
||||
});
|
||||
await dialog.getByRole("button", { name: "Create" }).click();
|
||||
await savePromise;
|
||||
|
||||
// The new item shows up in the section list with the company as its title
|
||||
await expect(page.getByText(company)).toBeVisible();
|
||||
|
||||
// And it survives a full reload
|
||||
await page.reload();
|
||||
await openSidebarSection(page, "Experience");
|
||||
await expect(page.getByText(company)).toBeVisible();
|
||||
await expect(page.getByText(position)).toBeVisible();
|
||||
});
|
||||
@@ -0,0 +1,15 @@
|
||||
import { expect, test } from "../fixtures/test";
|
||||
|
||||
test("updates the profile name from settings and persists it", async ({ authPage: page }) => {
|
||||
await page.goto("/dashboard/settings/profile");
|
||||
|
||||
const updatedName = `E2E Renamed ${Date.now()}`;
|
||||
const nameField = page.getByLabel("Name", { exact: true });
|
||||
await expect(nameField).toBeVisible();
|
||||
await nameField.fill(updatedName);
|
||||
await page.getByRole("button", { name: "Save Changes" }).click();
|
||||
|
||||
// The save round-trips through the API; a reload must show the new value
|
||||
await page.reload();
|
||||
await expect(page.getByLabel("Name", { exact: true })).toHaveValue(updatedName);
|
||||
});
|
||||
@@ -0,0 +1,34 @@
|
||||
import { createSampleResumeFromDashboard, openSidebarSection } from "../fixtures/resume";
|
||||
import { expect, test } from "../fixtures/test";
|
||||
|
||||
test("password-protects a public resume and unlocks it as a visitor", async ({ browser, authPage: page }, testInfo) => {
|
||||
await createSampleResumeFromDashboard(page, testInfo);
|
||||
await openSidebarSection(page, "Sharing");
|
||||
|
||||
await page.getByRole("switch", { name: /Allow Public Access/ }).click();
|
||||
const sharingUrl = page.locator("#sharing-url");
|
||||
await expect(sharingUrl).toHaveValue(/\/e2e_/);
|
||||
const publicUrl = await sharingUrl.inputValue();
|
||||
|
||||
const password = "e2e-secret-42";
|
||||
await page.getByRole("button", { name: "Set Password" }).click();
|
||||
const prompt = page.getByRole("alertdialog");
|
||||
await prompt.locator('input[type="password"]').fill(password);
|
||||
await prompt.getByRole("button", { name: "Set Password" }).click();
|
||||
await expect(page.getByRole("button", { name: "Remove Password" })).toBeVisible();
|
||||
|
||||
const anonymous = await browser.newPage();
|
||||
try {
|
||||
// Anonymous visitors hit the password gate first
|
||||
await anonymous.goto(publicUrl);
|
||||
await anonymous.waitForURL(/\/auth\/resume-password/);
|
||||
|
||||
await anonymous.getByLabel("Password", { exact: true }).fill(password);
|
||||
await anonymous.getByRole("button", { name: "Unlock" }).click();
|
||||
|
||||
// The correct password reveals the actual resume
|
||||
await expect(anonymous.getByRole("button", { name: "Download PDF" })).toBeVisible();
|
||||
} finally {
|
||||
await anonymous.close();
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,27 @@
|
||||
import { createSampleResumeFromDashboard, openSidebarSection } from "../fixtures/resume";
|
||||
import { expect, test } from "../fixtures/test";
|
||||
|
||||
test("switches the resume template and persists the choice", async ({ authPage: page }, testInfo) => {
|
||||
await createSampleResumeFromDashboard(page, testInfo);
|
||||
|
||||
await openSidebarSection(page, "Template");
|
||||
// Sample resumes start on Azurill; its preview button opens the gallery
|
||||
await page.getByRole("button", { name: "Azurill", exact: true }).click();
|
||||
const gallery = page.getByRole("dialog", { name: "Template Gallery" });
|
||||
await expect(gallery).toBeVisible();
|
||||
|
||||
const savePromise = page.waitForResponse((response) => {
|
||||
if (!response.url().includes("/api/rpc")) return false;
|
||||
if (response.request().method() !== "POST") return false;
|
||||
if (!response.ok()) return false;
|
||||
return (response.request().postData() ?? "").includes("bronzor");
|
||||
});
|
||||
await gallery.getByRole("img", { name: "Bronzor", exact: true }).click();
|
||||
await savePromise;
|
||||
await page.keyboard.press("Escape");
|
||||
|
||||
// After a reload the Template section previews the newly selected template
|
||||
await page.reload();
|
||||
await openSidebarSection(page, "Template");
|
||||
await expect(page.getByRole("img", { name: "Bronzor", exact: true })).toBeVisible();
|
||||
});
|
||||
Reference in New Issue
Block a user