test(e2e): drive the share and export sheet

The sharing specs use Share → Link (public switch, visitor downloads, the
password switch and the public address from Open public page), and the
export specs pick a format in the Download tab. share-history covers
renaming the address with the old one redirecting, and naming, previewing
and restoring versions.
This commit is contained in:
Amruth Pillai
2026-09-28 19:48:15 +02:00
parent c8df749258
commit 6540c2aee9
10 changed files with 96 additions and 56 deletions
+19 -2
View File
@@ -64,10 +64,27 @@ export async function openSidebarSection(page: Page, title: string) {
await expect(row).toHaveAttribute("aria-expanded", "true");
}
/** Opens the Download dialog with every format from the ▾ next to Download PDF. */
/** Opens Share & export on its Download tab, from the ▾ next to Download PDF. */
export async function openDownloadDialog(page: Page) {
await page.getByRole("button", { name: "More download formats", exact: true }).click();
await expect(page.getByRole("dialog", { name: "Download" })).toBeVisible();
const sheet = page.getByRole("dialog", { name: "Share & export" });
await expect(sheet.getByRole("tab", { name: "Download", exact: true })).toHaveAttribute("aria-selected", "true");
return sheet;
}
/** Turns the public link on in Share → Link and returns the public address. */
export async function makeResumePublic(page: Page) {
await openSidebarSection(page, "Sharing");
const sheet = page.getByRole("dialog", { name: "Share & export" });
await sheet.getByRole("switch", { name: "Public link" }).click();
return getPublicUrl(page);
}
/** The public address, from Share → Link's "Open public page" (it fills in once the session has loaded). */
export async function getPublicUrl(page: Page) {
const link = page.getByRole("dialog", { name: "Share & export" }).getByRole("link", { name: "Open public page" });
await expect(link).toHaveAttribute("href", /\/e2e_/);
return (await link.getAttribute("href")) as string;
}
export async function openResumeCardMenu(page: Page, resumeName: string, { reload = true } = {}) {
+2 -5
View File
@@ -61,12 +61,9 @@ async function seedHyphenationResume(page: Page) {
}
async function downloadPdfText(page: Page, testInfo: TestInfo, name: string) {
await openDownloadDialog(page);
const sheet = await openDownloadDialog(page);
const pending = page.waitForEvent("download");
await page
.getByRole("dialog", { name: "Download" })
.getByRole("button", { name: "Download PDF", exact: true })
.click();
await sheet.getByRole("button", { name: "Download PDF", exact: true }).click();
const download = await pending;
const path = testInfo.outputPath(`${name}.pdf`);
await download.saveAs(path);
+3 -2
View File
@@ -5,9 +5,10 @@ import { expect, test } from "../fixtures/test";
test("exports and imports a resume JSON backup", async ({ authPage: page }, testInfo) => {
await createSampleResumeFromDashboard(page, testInfo);
await openDownloadDialog(page);
const sheet = await openDownloadDialog(page);
await sheet.getByRole("radio", { name: /^JSON/ }).click();
const downloadPromise = page.waitForEvent("download");
await page.getByRole("button", { name: "Download JSON" }).click();
await sheet.getByRole("button", { name: "Download JSON" }).click();
const download = await downloadPromise;
expect(download.suggestedFilename()).toMatch(/\.json$/);
+2 -5
View File
@@ -360,12 +360,9 @@ test.describe("offline font diagnostic", () => {
let rasterEvidenceStatus = "not-attempted";
try {
await page.goto(fixture.builderURL);
await openDownloadDialog(page);
const sheet = await openDownloadDialog(page);
const downloadPromise = page.waitForEvent("download", { timeout: 20_000 });
await page
.getByRole("dialog", { name: "Download" })
.getByRole("button", { name: "Download PDF", exact: true })
.click();
await sheet.getByRole("button", { name: "Download PDF", exact: true }).click();
const download = await downloadPromise;
downloadStatus = "received";
const path = testInfo.outputPath("offline-font-browser-download.pdf");
@@ -4,7 +4,6 @@ import { createRequire } from "node:module";
import { getDocument } from "pdfjs-dist/legacy/build/pdf.mjs";
import { Pool } from "pg";
import { defaultResumeData } from "@reactive-resume/schema/resume/default";
import { generateFilename } from "@reactive-resume/utils/file";
import { createSampleResumeFromDashboard, openDownloadDialog } from "../fixtures/resume";
import { expect, test } from "../fixtures/test";
@@ -544,8 +543,7 @@ async function runGeometryMatrix(page: Page, testInfo: TestInfo, scenarios: Geom
dedicatedDatabaseUrl();
await page.setViewportSize({ width: 1920, height: 1000 });
await installPreviewCapture(page);
const resumeName = await createSampleResumeFromDashboard(page, testInfo);
const expectedExportFilename = generateFilename(resumeName, "pdf");
await createSampleResumeFromDashboard(page, testInfo);
const resumeId = new URL(page.url()).pathname.match(/^\/builder\/([^/]+)/)?.[1];
if (!resumeId) throw new Error("Missing synthetic geometry resume id.");
@@ -582,17 +580,11 @@ async function runGeometryMatrix(page: Page, testInfo: TestInfo, scenarios: Geom
persistedFixture.revision,
);
const pending = page.waitForEvent("download");
await openDownloadDialog(page);
const downloadDialog = page.getByRole("dialog", { name: "Download" });
await expect(downloadDialog.getByRole("tab", { name: "Resume", exact: true })).toHaveAttribute(
"aria-selected",
"true",
);
await downloadDialog.getByRole("button", { name: "Download PDF", exact: true }).click();
const downloadSheet = await openDownloadDialog(page);
await downloadSheet.getByRole("button", { name: "Download PDF", exact: true }).click();
const download = await pending;
expect(download.suggestedFilename(), `${scenarioName(scenario)} zoom ${zoom} export target`).toBe(
expectedExportFilename,
);
// The default file name is "First-Last-Resume"; this test only checks it's the PDF.
expect(download.suggestedFilename(), `${scenarioName(scenario)} zoom ${zoom} export target`).toMatch(/\.pdf$/);
const downloadPath = testInfo.outputPath(`${scenarioName(scenario)}.zoom-${zoom}.pdf`);
await download.saveAs(downloadPath);
const downloadBytes = new Uint8Array(await readFile(downloadPath));
@@ -1,20 +1,22 @@
import { createSampleResumeFromDashboard, openSidebarSection } from "../fixtures/resume";
import {
createSampleResumeFromDashboard,
getPublicUrl,
makeResumePublic,
openSidebarSection,
} from "../fixtures/resume";
import { expect, test } from "../fixtures/test";
test("persists public download-button visibility", async ({ browser, authPage: page }, testInfo) => {
await createSampleResumeFromDashboard(page, testInfo);
await openSidebarSection(page, "Sharing");
await page.getByRole("switch", { name: /Allow Public Access/ }).click();
const downloadPreference = page.getByRole("switch", { name: "Show Download Buttons" });
await makeResumePublic(page);
const downloadPreference = page.getByRole("switch", { name: "Visitors can download the PDF" });
await expect(downloadPreference).toBeChecked();
await downloadPreference.click();
await expect(downloadPreference).not.toBeChecked();
await page.reload();
await openSidebarSection(page, "Sharing");
await expect(downloadPreference).not.toBeChecked();
// The address fills in once the session has loaded after the reload.
await expect(page.locator("#sharing-url")).toHaveValue(/\/e2e_/);
const publicUrl = await page.locator("#sharing-url").inputValue();
const publicUrl = await getPublicUrl(page);
const anonymous = await browser.newPage();
try {
+2 -8
View File
@@ -1,4 +1,4 @@
import { createSampleResumeFromDashboard, openSidebarSection } from "../fixtures/resume";
import { createSampleResumeFromDashboard, makeResumePublic } from "../fixtures/resume";
import { expect, test } from "../fixtures/test";
test("counts a visitor's PDF download without counting the preview", async ({ browser, authPage: page }, testInfo) => {
@@ -11,13 +11,7 @@ test("counts a visitor's PDF download without counting the preview", async ({ br
expect(response.ok()).toBe(true);
return response.json();
};
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();
expect(publicUrl).toMatch(/\/e2e_/);
const publicUrl = await makeResumePublic(page);
const anonymous = await browser.newPage();
try {
+2 -5
View File
@@ -100,12 +100,9 @@ async function showSection(page: Page, title: string) {
}
async function downloadPdfText(page: Page, testInfo: TestInfo, name: string) {
await openDownloadDialog(page);
const sheet = await openDownloadDialog(page);
const pending = page.waitForEvent("download");
await page
.getByRole("dialog", { name: "Download" })
.getByRole("button", { name: "Download PDF", exact: true })
.click();
await sheet.getByRole("button", { name: "Download PDF", exact: true }).click();
const download = await pending;
const path = testInfo.outputPath(`${name}.pdf`);
await download.saveAs(path);
+47
View File
@@ -0,0 +1,47 @@
import { createSampleResumeFromDashboard, getPublicUrl, makeResumePublic } from "../fixtures/resume";
import { expect, test } from "../fixtures/test";
test("renames the public address and keeps the old one redirecting", async ({ browser, authPage: page }, testInfo) => {
await createSampleResumeFromDashboard(page, testInfo);
const oldUrl = await makeResumePublic(page);
const sheet = page.getByRole("dialog", { name: "Share & export" });
const address = sheet.getByRole("textbox", { name: "Address" });
await address.fill("not_valid!");
await expect(sheet.getByText("Use lowercase letters, numbers and single dashes.")).toBeVisible();
await address.fill("renamed-address");
await expect(sheet.getByText(/^Live at .*\/renamed-address$/)).toBeVisible();
expect(await getPublicUrl(page)).toMatch(/\/renamed-address$/);
const visitor = await browser.newPage();
try {
await visitor.goto(oldUrl);
await visitor.waitForURL(/\/renamed-address$/);
await expect(visitor.getByRole("heading", { level: 1 })).toBeVisible();
} finally {
await visitor.close();
}
});
test("names a version, previews an older one read-only and restores it", async ({ authPage: page }, testInfo) => {
await createSampleResumeFromDashboard(page, testInfo);
await page.getByRole("button", { name: "History", exact: true }).click();
const sheet = page.getByRole("dialog", { name: "Share & export" });
const versions = sheet.getByRole("list", { name: "Versions" });
// A new document's history starts with where it came from.
await expect(versions.getByRole("button", { name: /^Created/ })).toBeVisible();
await sheet.getByRole("textbox", { name: "Name this version" }).fill("Sent to Lumen");
await sheet.getByRole("button", { name: "Save", exact: true }).click();
await expect(versions.getByRole("button", { name: /^Sent to Lumen/ })).toBeVisible();
await versions.getByRole("button", { name: /^Created/ }).click();
// The sheet announces it; the page (outside the modal sheet) shows the same banner above the version.
await expect(sheet.getByRole("status").filter({ hasText: /Viewing .* · Created · read-only/ })).toBeVisible();
await expect(page.getByText(/^Viewing .* · Created · read-only$/).first()).toBeVisible();
await sheet.getByRole("button", { name: "Restore this version" }).click();
await expect(versions.getByRole("button", { name: /^Before restore/ })).toBeVisible();
await expect(versions.getByRole("button", { name: /^Now/ })).toHaveAttribute("aria-pressed", "true");
});
+5 -9
View File
@@ -1,17 +1,13 @@
import { createSampleResumeFromDashboard, openSidebarSection } from "../fixtures/resume";
import { createSampleResumeFromDashboard, makeResumePublic } 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 publicUrl = await makeResumePublic(page);
const password = "e2e-secret-42";
await page.getByRole("button", { name: "Set Password" }).click();
const requirePassword = page.getByRole("switch", { name: "Require a password" });
await requirePassword.click();
const dialog = page.getByRole("dialog", { name: "Protect your resume with a password" });
await dialog.getByLabel("Password", { exact: true }).fill(password);
await dialog.getByLabel("Confirm Password", { exact: true }).fill("different-password");
@@ -19,7 +15,7 @@ test("password-protects a public resume and unlocks it as a visitor", async ({ b
await expect(dialog.getByRole("alert")).toHaveText("Passwords do not match.");
await dialog.getByLabel("Confirm Password", { exact: true }).fill(password);
await dialog.getByRole("button", { name: "Set Password" }).click();
await expect(page.getByRole("button", { name: "Remove Password" })).toBeVisible();
await expect(requirePassword).toBeChecked();
const anonymous = await browser.newPage();
try {