Files
kandev/apps/web/e2e/tests/layout/toggle-sidebar-shortcut.spec.ts
T
José Almeida e626d33a7c fix(web): make toggle-sidebar shortcut work on every route (#1283)
* fix(web): make toggle-sidebar shortcut work on every route

TOGGLE_SIDEBAR (Cmd/Ctrl+B) was handled only inside useEditorKeybinds,
mounted just by the dockview desktop layout, so it was dead on Kanban,
the task list, Office, Settings, etc. Lift it to a global app-root
listener (useAppShortcuts, mounted in GlobalCommands) that honors user
overrides via getShortcut and keeps the isEditableTarget guard, and drop
the now-duplicate branch from useEditorKeybinds. Adds a hook unit test
and a Playwright e2e covering Kanban, Settings, and the dockview session.

* fix(web): hydrate userSettings into the root store on settings routes

Each StateProvider creates its own store; the settings/general page mounts
a nested one that the root-mounted GlobalCommands/AppSidebar can't see. The
settings layout hydrated the root store but omitted userSettings, so
override-driven global shortcuts (TOGGLE_SIDEBAR, Quick Chat) were dead on
every /settings route. Hydrate userSettings there too, matching the
kanban/tasks/office/github routes.

* test(web): cover the macOS Cmd path; note the settings-layout catch trade-off

Address review feedback: add a metaKey (Cmd) variant to the useAppShortcuts
test so both ctrlOrCmd branches are exercised, and document that the settings
layout's outer catch intentionally drops userSettings overrides in the
already-degraded error path.
2026-06-06 23:09:12 +01:00

84 lines
3.4 KiB
TypeScript

import { test, expect } from "../../fixtures/test-base";
import type { Page, Locator } from "@playwright/test";
import type { SeedData } from "../../fixtures/test-base";
import type { ApiClient } from "../../helpers/api-client";
import { SessionPage } from "../../pages/session-page";
// The TOGGLE_SIDEBAR shortcut lives on a global app-root listener (useAppShortcuts),
// so Cmd/Ctrl+B must collapse/expand the unified AppSidebar on every route — not
// just inside the dockview session editor. It is UNBOUND by default on this
// branch, so each test binds it to Cmd/Ctrl+B first (mirroring how a user would
// set it in Settings).
const MODIFIER = process.platform === "darwin" ? "Meta" : "Control";
/** Bind TOGGLE_SIDEBAR to Cmd/Ctrl+B (unbound by default) for the active user. */
async function bindToggleSidebar(apiClient: ApiClient, seedData: SeedData): Promise<void> {
await apiClient.saveUserSettings({
workspace_id: seedData.workspaceId,
keyboard_shortcuts: {
TOGGLE_SIDEBAR: { key: "b", modifiers: { ctrlOrCmd: true } },
},
});
}
/** Press Cmd/Ctrl+B and assert the AppSidebar's collapsed state flips both ways. */
async function expectShortcutTogglesSidebar(page: Page, sidebar: Locator): Promise<void> {
await expect(sidebar).toBeVisible();
// The shortcut is intentionally ignored while typing — clear focus first so a
// page-autofocused input can't suppress it.
await page.evaluate(() => (document.activeElement as HTMLElement | null)?.blur());
const initial = (await sidebar.getAttribute("data-collapsed")) ?? "false";
const flipped = initial === "true" ? "false" : "true";
await page.keyboard.press(`${MODIFIER}+b`);
await expect(sidebar).toHaveAttribute("data-collapsed", flipped);
await page.keyboard.press(`${MODIFIER}+b`);
await expect(sidebar).toHaveAttribute("data-collapsed", initial);
}
test.describe("Toggle sidebar shortcut (global)", () => {
test("toggles the AppSidebar on the Kanban board", async ({ testPage, apiClient, seedData }) => {
await bindToggleSidebar(apiClient, seedData);
await testPage.goto("/");
await expectShortcutTogglesSidebar(testPage, testPage.getByTestId("app-sidebar"));
});
test("toggles the AppSidebar on a Settings page", async ({ testPage, apiClient, seedData }) => {
await bindToggleSidebar(apiClient, seedData);
await testPage.goto("/settings/general");
await expectShortcutTogglesSidebar(testPage, testPage.getByTestId("app-sidebar"));
});
test("still toggles the AppSidebar on the dockview session page", async ({
testPage,
apiClient,
seedData,
}) => {
await bindToggleSidebar(apiClient, seedData);
const task = await apiClient.createTaskWithAgent(
seedData.workspaceId,
"Toggle sidebar shortcut session",
seedData.agentProfileId,
{
description: "/e2e:simple-message",
workflow_id: seedData.workflowId,
workflow_step_id: seedData.startStepId,
repository_ids: [seedData.repositoryId],
},
);
if (!task.session_id) throw new Error("createTaskWithAgent did not return a session_id");
await testPage.goto(`/t/${task.id}`);
const session = new SessionPage(testPage);
await session.waitForLoad();
// Target the global rail (app-sidebar), not the dockview's per-session
// sidebar (task-sidebar).
await expectShortcutTogglesSidebar(testPage, testPage.getByTestId("app-sidebar"));
});
});