mirror of
https://github.com/getpaseo/paseo.git
synced 2026-10-29 20:52:51 +00:00
Keep compact sidebars open while users inspect Search, Import session, Add project, or file sheets. Reveal the resolved destination when navigation commits, and place compact workspace sheets above both sidebars while preserving shared editing and cache state.
265 lines
10 KiB
TypeScript
265 lines
10 KiB
TypeScript
import { expect, test } from "../support/fixtures";
|
|
import type { Page } from "@playwright/test";
|
|
import { openAddProjectFlow, addProjectFlow } from "../support/helpers/add-project-flow";
|
|
import { installUsageReportsFixture } from "../support/helpers/usage-reports";
|
|
import {
|
|
claudeAndCodexReports,
|
|
expectPinnedUsage,
|
|
openCompactSidebar,
|
|
expectCompactSidebarOpen,
|
|
} from "../support/helpers/usage-sidebar-item";
|
|
import { gotoAppShell } from "../support/helpers/app";
|
|
import { SHOWCASE_PLUGIN_ID, installSidebarPlugins } from "../support/helpers/plugin-sidebar-items";
|
|
import {
|
|
expectFooterIconRow,
|
|
expectFooterSeparator,
|
|
hoverFooterAddProject,
|
|
footerScreenshot,
|
|
expectFooterItemHidden,
|
|
expectFooterOrder,
|
|
expectFooterSettingsKeys,
|
|
expectSidebarItemHidden,
|
|
expectSidebarNavSettingsOrder,
|
|
expectSidebarNavSettingsRow,
|
|
expectSidebarOrder,
|
|
expectStoredSidebarNav,
|
|
leaveSettings,
|
|
moveFooterItemUp,
|
|
moveSidebarNavItemUp,
|
|
openSidebarNavSettings,
|
|
seedSidebarFooterPreferences,
|
|
seedSidebarNavPreferences,
|
|
setFooterItemVisible,
|
|
setSidebarNavItemVisible,
|
|
} from "../support/helpers/sidebar-nav-settings";
|
|
|
|
async function showDeveloperToast(page: Page): Promise<void> {
|
|
await page.evaluate(() => {
|
|
document.getElementById("error-toast")?.remove();
|
|
const toast = document.createElement("div");
|
|
toast.id = "error-toast";
|
|
toast.style.cssText = "position:fixed;inset:0;z-index:2147483647;background:white";
|
|
// Expo renders its unnamed dismiss button in an open shadow root.
|
|
const button = document.createElement("button");
|
|
button.style.cssText = "width:24px;height:24px";
|
|
button.addEventListener("click", () => toast.remove());
|
|
toast.attachShadow({ mode: "open" }).appendChild(button);
|
|
document.body.appendChild(toast);
|
|
});
|
|
}
|
|
|
|
test("developer toasts leave concurrent sidebar layout checks uninterrupted", async ({ page }) => {
|
|
await gotoAppShell(page);
|
|
await showDeveloperToast(page);
|
|
await expectSidebarOrder(page, ["new-workspace", "history", "search", "schedules"]);
|
|
await expect(page.locator("#error-toast")).toBeHidden();
|
|
});
|
|
|
|
test("fixed footer line keeps its five icons, Help and Settings at the end", async ({ page }) => {
|
|
test.setTimeout(120_000);
|
|
await installUsageReportsFixture(page, { lists: [() => claudeAndCodexReports()] });
|
|
await seedSidebarFooterPreferences(page, [
|
|
{ key: "add-project", visible: false },
|
|
{ key: "usage", visible: true },
|
|
]);
|
|
await page.setViewportSize({ width: 1440, height: 900 });
|
|
await gotoAppShell(page);
|
|
await expectPinnedUsage(page, ["31% 5h", "54% wk", "7% 5h", "12% wk"]);
|
|
await expectFooterIconRow(page);
|
|
await expectFooterSeparator(page, true);
|
|
await footerScreenshot(page, "footer-desktop-with-rows");
|
|
await hoverFooterAddProject(page);
|
|
await footerScreenshot(page, "footer-desktop-add-project-tooltip");
|
|
await openAddProjectFlow(page);
|
|
await page.keyboard.press("Escape");
|
|
await expect(addProjectFlow(page)).toBeHidden();
|
|
await openSidebarNavSettings(page);
|
|
await expectFooterSettingsKeys(page, ["usage"]);
|
|
await setFooterItemVisible(page, "usage", false);
|
|
await leaveSettings(page);
|
|
await expectFooterSeparator(page, false);
|
|
await expectFooterIconRow(page);
|
|
await footerScreenshot(page, "footer-desktop-without-rows");
|
|
await page.setViewportSize({ width: 390, height: 844 });
|
|
await openCompactSidebar(page);
|
|
await expectFooterSeparator(page, false);
|
|
await expectFooterIconRow(page);
|
|
await footerScreenshot(page, "footer-compact-without-rows");
|
|
await page.locator('[data-testid="sidebar-add-project"]:visible').hover();
|
|
await expect(page.getByTestId("sidebar-add-project-tooltip")).toHaveCount(0);
|
|
await footerScreenshot(page, "footer-compact-add-project-hover");
|
|
await openAddProjectFlow(page);
|
|
await page.keyboard.press("Escape");
|
|
await expect(addProjectFlow(page)).toBeHidden();
|
|
await expectCompactSidebarOpen(page);
|
|
await page.setViewportSize({ width: 1440, height: 900 });
|
|
await openSidebarNavSettings(page);
|
|
await setFooterItemVisible(page, "usage", true);
|
|
await leaveSettings(page);
|
|
await page.setViewportSize({ width: 390, height: 844 });
|
|
await expectCompactSidebarOpen(page);
|
|
await expectFooterSeparator(page, true);
|
|
await expectFooterIconRow(page);
|
|
await footerScreenshot(page, "footer-compact-with-rows");
|
|
});
|
|
|
|
test.describe("Sidebar items in Appearance settings", () => {
|
|
test("owner reorders and hides top-level sidebar items", async ({ page }) => {
|
|
await gotoAppShell(page);
|
|
|
|
await test.step("the sidebar starts in the default order", async () => {
|
|
await expectSidebarOrder(page, ["new-workspace", "history", "search", "schedules"]);
|
|
});
|
|
|
|
await test.step("the Sidebar section lists every item in the same order", async () => {
|
|
await openSidebarNavSettings(page);
|
|
// The section explains itself through the header's info tooltip, not a paragraph.
|
|
await expect(page.getByTestId("sidebar-nav-section-header-info")).toHaveAccessibleName(
|
|
"About Header",
|
|
);
|
|
await expectSidebarNavSettingsOrder(page, [
|
|
"new-workspace",
|
|
"history",
|
|
"search",
|
|
"schedules",
|
|
]);
|
|
await expectSidebarNavSettingsRow(page, {
|
|
key: "history",
|
|
label: "History",
|
|
visible: true,
|
|
});
|
|
// Items with a keyboard shortcut badge it next to their name. Chords render
|
|
// with Ctrl off macOS, which is what the browser project runs on.
|
|
await expect(
|
|
page.getByTestId("sidebar-nav-item-new-workspace").getByText("Ctrl+N", { exact: true }),
|
|
).toBeVisible();
|
|
});
|
|
|
|
await test.step("moving Schedules up twice lifts it above History", async () => {
|
|
await moveSidebarNavItemUp(page, "schedules");
|
|
await expectSidebarNavSettingsOrder(page, [
|
|
"new-workspace",
|
|
"history",
|
|
"schedules",
|
|
"search",
|
|
]);
|
|
await moveSidebarNavItemUp(page, "schedules");
|
|
await expectSidebarNavSettingsOrder(page, [
|
|
"new-workspace",
|
|
"schedules",
|
|
"history",
|
|
"search",
|
|
]);
|
|
|
|
await leaveSettings(page);
|
|
await expectSidebarOrder(page, ["new-workspace", "schedules", "history", "search"]);
|
|
});
|
|
|
|
await test.step("turning History off removes it from the sidebar", async () => {
|
|
await openSidebarNavSettings(page);
|
|
await setSidebarNavItemVisible(page, "history", false);
|
|
await expectStoredSidebarNav(page, [
|
|
{ key: "new-workspace", visible: true },
|
|
{ key: "schedules", visible: true },
|
|
{ key: "history", visible: false },
|
|
{ key: "search", visible: true },
|
|
]);
|
|
|
|
await leaveSettings(page);
|
|
await expectSidebarItemHidden(page, "history");
|
|
await expectSidebarOrder(page, ["new-workspace", "schedules", "search"]);
|
|
});
|
|
|
|
await test.step("the sidebar keeps that shape across a reload", async () => {
|
|
await page.reload();
|
|
await expectSidebarItemHidden(page, "history");
|
|
await expectSidebarOrder(page, ["new-workspace", "schedules", "search"]);
|
|
});
|
|
});
|
|
|
|
test("renders no top-level items when every one is turned off", async ({ page }) => {
|
|
await seedSidebarNavPreferences(page, [
|
|
{ key: "new-workspace", visible: false },
|
|
{ key: "history", visible: false },
|
|
{ key: "search", visible: false },
|
|
{ key: "schedules", visible: false },
|
|
]);
|
|
await gotoAppShell(page);
|
|
|
|
// The sidebar itself still renders; only its top-level nav items are gone.
|
|
await expect(page.locator('[data-testid="sidebar-settings"]:visible')).toBeVisible({
|
|
timeout: 30_000,
|
|
});
|
|
await expectSidebarItemHidden(page, "new-workspace");
|
|
await expectSidebarItemHidden(page, "history");
|
|
await expectSidebarItemHidden(page, "search");
|
|
await expectSidebarItemHidden(page, "schedules");
|
|
});
|
|
});
|
|
|
|
test.describe("Sidebar footer rows in Appearance settings", () => {
|
|
const syncKey = `plugin:${SHOWCASE_PLUGIN_ID}:sync`;
|
|
const brokenKey = `plugin:${SHOWCASE_PLUGIN_ID}:broken`;
|
|
let cleanup: () => Promise<void>;
|
|
|
|
test.beforeEach(async () => {
|
|
({ cleanup } = await installSidebarPlugins());
|
|
});
|
|
|
|
test.afterEach(async () => {
|
|
await cleanup();
|
|
});
|
|
|
|
test("owner reorders and hides footer rows; the icon row stays fixed", async ({ page }) => {
|
|
test.setTimeout(120_000);
|
|
// The Usage item shows only with summary data.
|
|
await installUsageReportsFixture(page, { lists: [() => claudeAndCodexReports()] });
|
|
// Keys for the fixed footer icon buttons are ignored.
|
|
await seedSidebarFooterPreferences(page, [
|
|
{ key: "usage", visible: true },
|
|
{ key: "help", visible: false },
|
|
{ key: "hosts", visible: false },
|
|
{ key: "import", visible: false },
|
|
]);
|
|
await gotoAppShell(page);
|
|
|
|
await test.step("the Footer card lists only the footer rows", async () => {
|
|
await openSidebarNavSettings(page);
|
|
await expect(page.getByTestId("sidebar-nav-section-footer-info")).toHaveAccessibleName(
|
|
"About Footer",
|
|
);
|
|
await expectFooterSettingsKeys(page, ["usage", syncKey, brokenKey]);
|
|
await leaveSettings(page);
|
|
await expectFooterOrder(page, ["usage", syncKey]);
|
|
await expectFooterIconRow(page);
|
|
await expectFooterSeparator(page, true);
|
|
});
|
|
|
|
await test.step("moving Sync up and hiding the Usage item changes the rows", async () => {
|
|
await openSidebarNavSettings(page);
|
|
await moveFooterItemUp(page, syncKey);
|
|
await setFooterItemVisible(page, "usage", false);
|
|
await expectFooterSettingsKeys(page, [syncKey, "usage", brokenKey]);
|
|
await leaveSettings(page);
|
|
await expectFooterItemHidden(page, "usage");
|
|
await expect(
|
|
page.locator(`[data-testid="plugin-sidebar-footer-${SHOWCASE_PLUGIN_ID}-sync"]:visible`),
|
|
).toBeVisible();
|
|
await expectFooterIconRow(page);
|
|
await expectFooterSeparator(page, true);
|
|
});
|
|
|
|
await test.step("the rows keep that shape across a reload", async () => {
|
|
await page.reload();
|
|
await expect(
|
|
page.locator(`[data-testid="plugin-sidebar-footer-${SHOWCASE_PLUGIN_ID}-sync"]:visible`),
|
|
).toBeVisible({ timeout: 30_000 });
|
|
await expectFooterItemHidden(page, "usage");
|
|
await expectFooterIconRow(page);
|
|
await expectFooterSeparator(page, true);
|
|
await openSidebarNavSettings(page);
|
|
await expectFooterSettingsKeys(page, [syncKey, "usage", brokenKey]);
|
|
});
|
|
});
|
|
});
|