mirror of
https://github.com/penpot/penpot.git
synced 2026-10-29 14:00:37 +00:00
* ♻️ Use DS buttons and icons in the export assets modal The export modal close button becomes a ghost DS icon button and the progress toast close button an action one, which fits its 24px grid column; both are labelled "Close". Cancel and Export become DS buttons. The checkbox ticks and the toast icons use DS icons. The selection rows stay raw buttons, since they are list rows, and so does the link-styled retry/cancel button of the toast. The export frames e2e spec now finds the Export button by role, and a visual test covers the modal with all, some and no rows selected. AI-assisted-by: claude-opus-5-5 * ♻️ Make the export progress ring a button The export progress ring in the header opens the export details, but it was a div with a click handler. It is now a button named after the export status, with aria-expanded, and keeps its look. AI-assisted-by: claude-opus-5-5 * ♻️ Move export assets modal styles off the deprecated styles Spacing, sizes, borders, typographies and color aliases move to DS tokens and logical properties, and the button placeholders go away. The classes left without use are removed: the old modal buttons, the old close icon, and the file export entries, which live in files.scss. The refactor/ import stays only for the shared modal and checkbox placeholders, which move to the DS with the modals and the checkboxes. The checkbox placeholder shrinks a ticked icon to 12px, which pushed the DS tick off center: the tick keeps its 16px box now, and does not shrink inside the 14px flex box either. AI-assisted-by: claude-opus-5-5
200 lines
6.7 KiB
JavaScript
200 lines
6.7 KiB
JavaScript
import { test, expect } from "@playwright/test";
|
|
import { WasmWorkspacePage } from "../pages/WasmWorkspacePage";
|
|
|
|
test.beforeEach(async ({ page }) => {
|
|
await WasmWorkspacePage.init(page);
|
|
});
|
|
|
|
/**
|
|
* Helper function to setup workspace with frames for export testing
|
|
*/
|
|
async function setupWorkspaceWithFrames(workspacePage) {
|
|
await workspacePage.setupEmptyFile();
|
|
await workspacePage.mockRPC(
|
|
/get\-file\?/,
|
|
"workspace/get-file-export-frames.json",
|
|
);
|
|
await workspacePage.mockRPC(
|
|
"get-file-object-thumbnails?file-id=*",
|
|
"workspace/get-file-object-thumbnails-blank.json",
|
|
);
|
|
await workspacePage.mockRPC(
|
|
"get-file-fragment?file-id=*",
|
|
"workspace/get-file-fragment-blank.json",
|
|
);
|
|
await workspacePage.goToWorkspace({
|
|
fileId: "8d102c33-b98f-81f6-8006-fdd4ea9780d7",
|
|
pageId: "8d102c33-b98f-81f6-8006-fdd4ea9780d8",
|
|
});
|
|
}
|
|
|
|
test.describe("Export frames to PDF", () => {
|
|
test("Export frames menu option is NOT visible when page has no frames", async ({
|
|
page,
|
|
}) => {
|
|
const workspacePage = new WasmWorkspacePage(page);
|
|
await workspacePage.setupEmptyFile();
|
|
|
|
await workspacePage.goToWorkspace();
|
|
|
|
// Open main menu
|
|
await page.getByRole("button", { name: "Main menu" }).click();
|
|
await page.getByText("file").last().click();
|
|
|
|
// The "Export frames to PDF" option should NOT be visible when there are no frames
|
|
await expect(page.locator("#file-menu-export-frames")).not.toBeVisible();
|
|
});
|
|
|
|
test("Export frames menu option is visible when there are frames (even if not selected)", async ({
|
|
page,
|
|
}) => {
|
|
const workspacePage = new WasmWorkspacePage(page);
|
|
await setupWorkspaceWithFrames(workspacePage);
|
|
|
|
// Open main menu
|
|
await page.getByRole("button", { name: "Main menu" }).click();
|
|
await page.getByText("file").last().click();
|
|
|
|
// The "Export frames to PDF" option should be visible when there are frames on the page
|
|
await expect(page.locator("#file-menu-export-frames")).toBeVisible();
|
|
});
|
|
|
|
test("Export frames modal shows all frames when none are selected", async ({
|
|
page,
|
|
}) => {
|
|
const workspacePage = new WasmWorkspacePage(page);
|
|
await setupWorkspaceWithFrames(workspacePage);
|
|
|
|
// Don't select any frame
|
|
|
|
// Open main menu
|
|
await page.getByRole("button", { name: "Main menu" }).click();
|
|
await page.getByText("file").last().click();
|
|
|
|
// Click on "Export frames to PDF"
|
|
await page.locator("#file-menu-export-frames").click();
|
|
|
|
// The modal title should be correct
|
|
await expect(page.getByText("Export as PDF")).toBeVisible();
|
|
|
|
// Both frames should appear in the list
|
|
await expect(page.getByRole("button", { name: "Board 1" })).toBeVisible();
|
|
await expect(page.getByRole("button", { name: "Board 2" })).toBeVisible();
|
|
|
|
// The selection counter should show "2 of 2"
|
|
await expect(page.getByText("2 of 2 elements selected")).toBeVisible();
|
|
});
|
|
|
|
test("Export frames modal shows only the selected frames", async ({
|
|
page,
|
|
}) => {
|
|
const workspacePage = new WasmWorkspacePage(page);
|
|
await setupWorkspaceWithFrames(workspacePage);
|
|
|
|
// Select Frame 1
|
|
await workspacePage.clickLeafLayer("Board 1");
|
|
|
|
// Open main menu
|
|
await page.getByRole("button", { name: "Main menu" }).click();
|
|
await page.getByText("file").last().click();
|
|
|
|
// Click on "Export frames to PDF"
|
|
await page.locator("#file-menu-export-frames").click();
|
|
|
|
// The modal title should be correct
|
|
await expect(page.getByText("Export as PDF")).toBeVisible();
|
|
|
|
// Only Frame 1 should appear in the list
|
|
// await page.getByRole("button", { name: "Board 1" }),
|
|
await expect(page.getByRole("button", { name: "Board 1" })).toBeVisible();
|
|
await expect(
|
|
page.getByRole("button", { name: "Board 2" }),
|
|
).not.toBeVisible();
|
|
|
|
// The selection counter should show "1 of 1"
|
|
await expect(page.getByText("1 of 1 elements selected")).toBeVisible();
|
|
});
|
|
|
|
test("User can deselect frames in the export modal", async ({ page }) => {
|
|
const workspacePage = new WasmWorkspacePage(page);
|
|
await setupWorkspaceWithFrames(workspacePage);
|
|
|
|
// Select Frame 1
|
|
await workspacePage.clickLeafLayer("Board 1");
|
|
|
|
// Add Frame 2 to selection
|
|
await page.keyboard.down("Shift");
|
|
await workspacePage.clickLeafLayer("Board 2");
|
|
await page.keyboard.up("Shift");
|
|
|
|
// Open main menu and click export
|
|
await page.getByRole("button", { name: "Main menu" }).click();
|
|
await page.getByText("file").last().click();
|
|
await page.locator("#file-menu-export-frames").click();
|
|
|
|
// The modal should appear with 2 frames
|
|
await expect(page.getByText("2 of 2 elements selected")).toBeVisible();
|
|
|
|
// Click on the checkbox next to Frame 1 to deselect it
|
|
await page.getByRole("button", { name: "Board 1" }).click();
|
|
|
|
// Now only 1 frame should be selected
|
|
await expect(page.getByText("1 of 2 elements selected")).toBeVisible();
|
|
|
|
// The export button should still be enabled
|
|
const exportButton = page.getByRole("button", {
|
|
name: "Export",
|
|
exact: true,
|
|
});
|
|
await expect(exportButton).toBeEnabled();
|
|
});
|
|
|
|
test("Export button is disabled when all frames are deselected", async ({
|
|
page,
|
|
}) => {
|
|
const workspacePage = new WasmWorkspacePage(page);
|
|
await setupWorkspaceWithFrames(workspacePage);
|
|
|
|
// Select Frame 1
|
|
await workspacePage.clickLeafLayer("Board 1");
|
|
|
|
// Open main menu and click export
|
|
await page.getByRole("button", { name: "Main menu" }).click();
|
|
await page.getByText("file").last().click();
|
|
await page.locator("#file-menu-export-frames").click();
|
|
|
|
// Deselect the frame
|
|
await page.getByRole("button", { name: "Board 1" }).click();
|
|
|
|
// Now 0 frames are selected
|
|
await expect(page.getByText("0 of 1 elements selected")).toBeVisible();
|
|
|
|
// // The export button should be disabled
|
|
await expect(
|
|
page.getByRole("button", { name: "Export", exact: true }),
|
|
).toBeDisabled();
|
|
});
|
|
|
|
test("User can cancel the export modal", async ({ page }) => {
|
|
const workspacePage = new WasmWorkspacePage(page);
|
|
await setupWorkspaceWithFrames(workspacePage);
|
|
|
|
// Select Frame 1
|
|
await workspacePage.clickLeafLayer("Board 1");
|
|
|
|
// Open main menu and click export
|
|
await page.getByRole("button", { name: "Main menu" }).click();
|
|
await page.getByText("file").last().click();
|
|
await page.locator("#file-menu-export-frames").click();
|
|
|
|
// Now only 1 frame should be selected
|
|
await expect(page.getByText("1 of 1 elements selected")).toBeVisible();
|
|
|
|
// Click cancel
|
|
await page.getByRole("button", { name: "Cancel" }).click();
|
|
|
|
// The modal should be hidden
|
|
await expect(page.getByText("0 of 1 elements selected")).not.toBeVisible();
|
|
});
|
|
});
|