penpot/frontend/playwright/ui/specs/dashboard.spec.js
Eva Marco 29e79edc66
♻️ Use DS buttons and icons in the dashboard team pages (#12531)
* ♻️ Use DS buttons and icons in the dashboard team pages

The team pages (members, invitations, webhooks and settings) and their
modals move off the deprecated icons and raw buttons:

- The member and webhook row menus become ghost DS icon buttons named
  "Options", with aria-expanded.
- The member and invitation role selectors become buttons with
  aria-haspopup and aria-expanded, and use the DS arrow icon. They were
  divs; one carried a role and an id repeated on every row.
- The four modal close buttons become ghost DS icon buttons named
  "Close"; the class that places them goes on the tooltip wrapper.
- "Create webhook" becomes a DS primary button.
- The webhook status, team image and settings icons use the DS icons.

The invitation checkbox tick stays until the checkbox migration. A
visual test covers the role and row menus, the resend and webhook
modals and the team image overlay.

AI-assisted-by: claude-opus-5-5

* ♻️ Move dashboard team styles to logical properties and DS tokens

The remaining legacy color aliases resolve to their DS tokens, physical
offsets and margins become logical properties, and the DS typography
module is used without a namespace. Four selectors that team.cljs does
not use are commented out with a TODO.

refactor/ stays only for component-family placeholders: %input-checkbox
and %checkbox-icon (checkboxes) and %modal-* (modals). The shared
dashboard header styles (common-dashboard) are used by ten dashboard
modules and move in their own change. No visual change.

AI-assisted-by: claude-opus-5-5

* 🐛 Open the team row menus below their button

The member and webhook row menus were placed from the bottom of their
cell with a fixed offset, so their position depended on the menu
height and the button height. The two-item webhook menu covered its
button, the member menu covered the lower part of it, and the taller DS
icon button moved both 3px down. They now open 4px below the button.

AI-assisted-by: claude-opus-5-5

* 🔥 Remove unused styles from the dashboard team pages

The SCSS refactor left four rules commented out with a TODO because
team.cljs does not use them. CSS modules scope the classes to this
module and no other source uses them, so they go: .dashboard-top-cta,
.error-notification, .empty-invitations-buttons and
.invitations-dropdown. No visual change.

AI-assisted-by: claude-opus-5-5

* 🐛 Find the member role selector as a button in the dashboard test

The "Bug 9443" test looked for the member role selector as a combobox.
The selector is now a button with aria-haspopup, so the test failed in
CI. It now looks for a button named "Admin", and checks that the owner
has no such button.

AI-assisted-by: claude-opus-5-5
2026-10-27 12:19:47 +01:00

184 lines
5.8 KiB
JavaScript

import { test, expect } from "@playwright/test";
import DashboardPage from "../pages/DashboardPage";
test.beforeEach(async ({ page }) => {
await DashboardPage.init(page);
});
test("Dashboard page has title ", async ({ page }) => {
const dashboardPage = new DashboardPage(page);
await dashboardPage.goToDashboard();
await expect(dashboardPage.page).toHaveURL(/screen=dashboard/);
await expect(dashboardPage.mainHeading).toBeVisible();
});
test("User can create a new project", async ({ page }) => {
const dashboardPage = new DashboardPage(page);
await dashboardPage.setupNewProject();
await dashboardPage.goToDashboard();
await dashboardPage.addProjectButton.click();
await expect(dashboardPage.projectName).toBeVisible();
});
test("User goes to draft page", async ({ page }) => {
const dashboardPage = new DashboardPage(page);
await dashboardPage.setupDraftsEmpty();
await dashboardPage.goToDashboard();
await dashboardPage.draftsLink.click();
await expect(dashboardPage.mainHeading).toHaveText("Drafts");
});
test("Lists files in the drafts page", async ({ page }) => {
const dashboardPage = new DashboardPage(page);
await dashboardPage.setupDrafts();
await dashboardPage.goToDrafts();
await expect(
dashboardPage.page.getByRole("button", { name: /New File 1/ }),
).toBeVisible();
await expect(
dashboardPage.page.getByRole("button", { name: /New File 2/ }),
).toBeVisible();
});
test("User hasn't an empty placeholder", async ({ page }) => {
const dashboardPage = new DashboardPage(page);
await dashboardPage.goToDashboard();
await expect(
dashboardPage.page.getByTestId("empty-placeholder"),
).toBeHidden();
});
test("User has context menu options for edit file", async ({ page }) => {
await DashboardPage.mockRPC(
page,
"get-all-projects",
"dashboard/get-all-projects.json",
);
const dashboardPage = new DashboardPage(page);
await dashboardPage.setupDrafts();
await dashboardPage.goToDrafts();
const button = dashboardPage.page.getByRole("button", { name: /New File 2/ });
await button.click();
await button.click({ button: "right" });
await expect(dashboardPage.page.getByText("rename")).toBeVisible();
await expect(dashboardPage.page.getByText("duplicate")).toBeVisible();
await expect(
dashboardPage.page.getByText("add as shared library"),
).toBeVisible();
await expect(dashboardPage.page.getByText("delete")).toBeVisible();
});
test("Multiple elements in context", async ({ page }) => {
await DashboardPage.mockRPC(
page,
"get-all-projects",
"dashboard/get-all-projects.json",
);
const dashboardPage = new DashboardPage(page);
await dashboardPage.setupDrafts();
await dashboardPage.goToDrafts();
const button = dashboardPage.page.getByRole("button", { name: /New File 1/ });
await button.click();
const button2 = dashboardPage.page.getByRole("button", {
name: /New File 2/,
});
await button2.click({ modifiers: ["Shift"] });
await button.click({ button: "right" });
await expect(page.getByTestId("duplicate-multi")).toBeVisible();
await expect(page.getByTestId("file-move-multi")).toBeVisible();
await expect(page.getByTestId("file-binary-export-multi")).toBeVisible();
await expect(page.getByTestId("file-delete-multi")).toBeVisible();
});
test("User has create file button", async ({ page }) => {
const dashboardPage = new DashboardPage(page);
await dashboardPage.setupDrafts();
await dashboardPage.goToDrafts();
await expect(dashboardPage.page.getByText("+ New File")).toBeVisible();
});
test("User has add font button", async ({ page }) => {
const dashboardPage = new DashboardPage(page);
await dashboardPage.goToFonts();
await expect(dashboardPage.page.getByText("add custom font")).toBeVisible();
});
test("Bug 9443, Admin can not demote owner", async ({ page }) => {
const dashboardPage = new DashboardPage(page);
await dashboardPage.setupDashboardFull();
await DashboardPage.mockRPC(
page,
"get-team-members?team-id=*",
"dashboard/get-team-members-admin.json",
);
await dashboardPage.goToSecondTeamMembersSection();
await expect(page.getByRole("heading", { name: "Members" })).toBeVisible();
await expect(page.getByRole("button", { name: "Admin" })).toBeVisible();
await expect(page.getByText("Owner")).toBeVisible();
await expect(page.getByRole("button", { name: "Owner" })).toHaveCount(0);
});
test("Bug 9927, Don't show the banner to invite team members if the user has dismissed it", async ({
page,
}) => {
const dashboardPage = new DashboardPage(page);
await dashboardPage.setupDashboardFull();
await DashboardPage.mockRPC(
page,
"get-projects?team-id=*",
"dashboard/get-projects-second-team.json",
);
await dashboardPage.goToSecondTeamDashboard();
await expect(page.getByText("Team Up")).toBeVisible();
await page.getByRole("button", { name: "Close" }).click();
await page.reload();
await expect(page.getByText("Second team")).toBeVisible();
await expect(page.getByText("Team Up")).not.toBeVisible();
});
test("Bug 10141, The team does not disappear from the team list after deletion", async ({
page,
}) => {
const dashboardPage = new DashboardPage(page);
await dashboardPage.setupDashboardFull();
await DashboardPage.mockRPC(
page,
"get-teams",
"logged-in-user/get-teams-complete-owner.json",
);
await dashboardPage.goToDashboard();
await dashboardPage.teamDropdown.click();
await expect(page.getByText("Second Team")).toBeVisible();
await page.getByText("Second Team").click();
await page.getByTestId("team-options-button").click();
await page.getByTestId("delete-team").click();
await DashboardPage.mockRPC(
page,
"get-teams",
"logged-in-user/get-teams-default.json",
);
await page.getByRole("button", { name: "Delete team" }).click();
await dashboardPage.teamDropdown.click();
await expect(page.getByText("Second Team")).not.toBeVisible();
});