Files
kandev/apps/web/components/task/task-right-panels-toggle.test.tsx

151 lines
5.3 KiB
TypeScript

import { cleanup, fireEvent, render, screen } from "@testing-library/react";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
const mocks = vi.hoisted(() => ({
state: {
isSupported: true,
isReady: true,
isMaximized: false,
isAvailable: true,
rightPanelsVisible: true,
toggleRightPanels: vi.fn(),
},
}));
const HIDE_RIGHT_PANE = "Hide right pane";
const SHOW_RIGHT_PANE = "Show right pane";
const RIGHT_PANE_UNAVAILABLE = "No separate right pane to hide";
const RIGHT_PANE_UNAVAILABLE_WHILE_MAXIMIZED =
"Right pane is unavailable while a panel is maximized";
const TOGGLE_TEST_ID = "task-right-panels-toggle";
const ARIA_LABEL_ATTRIBUTE = "aria-label";
vi.mock("@/hooks/use-task-right-panels-toggle", () => ({
useTaskRightPanelsToggle: () => mocks.state,
}));
vi.mock("react-i18next", () => ({
useTranslation: () => ({
t: (key: string) => {
if (key === "task:hideRightPane") return HIDE_RIGHT_PANE;
if (key === "task:showRightPane") return SHOW_RIGHT_PANE;
if (key === "task:rightPaneUnavailable") return RIGHT_PANE_UNAVAILABLE;
if (key === "task:rightPaneUnavailableWhileMaximized") {
return RIGHT_PANE_UNAVAILABLE_WHILE_MAXIMIZED;
}
return key;
},
}),
}));
vi.mock("@kandev/ui/tooltip", () => ({
Tooltip: ({ children }: { children: React.ReactNode }) => <>{children}</>,
TooltipTrigger: ({ children }: { children: React.ReactNode }) => <>{children}</>,
TooltipContent: ({ children }: { children: React.ReactNode }) => <>{children}</>,
}));
import { TaskRightPanelsToggle } from "./task-right-panels-toggle";
afterEach(cleanup);
beforeEach(() => {
mocks.state = {
isSupported: true,
isReady: true,
isMaximized: false,
isAvailable: true,
rightPanelsVisible: true,
toggleRightPanels: vi.fn(),
};
});
describe("TaskRightPanelsToggle", () => {
it("keeps the action in one button position and exposes the next action", () => {
render(<TaskRightPanelsToggle sessionId="session-1" />);
const button = screen.getByTestId(TOGGLE_TEST_ID);
expect(button.getAttribute(ARIA_LABEL_ATTRIBUTE)).toBe(HIDE_RIGHT_PANE);
expect(button.getAttribute("aria-expanded")).toBe("true");
expect(button.getAttribute("title")).toBe(HIDE_RIGHT_PANE);
button.focus();
fireEvent.click(button);
expect(mocks.state.toggleRightPanels).toHaveBeenCalledTimes(1);
expect(document.activeElement).toBe(button);
});
it("switches to the show action when right panels are hidden", () => {
mocks.state.rightPanelsVisible = false;
render(<TaskRightPanelsToggle sessionId="session-1" />);
const button = screen.getByTestId(TOGGLE_TEST_ID);
expect(button.getAttribute(ARIA_LABEL_ATTRIBUTE)).toBe(SHOW_RIGHT_PANE);
expect(button.getAttribute("aria-expanded")).toBe("false");
});
it("uses the shared touch-sized icon button", () => {
render(<TaskRightPanelsToggle sessionId="session-1" />);
expect(screen.getByTestId(TOGGLE_TEST_ID).className).toContain(
"[@media(pointer:coarse)]:size-11",
);
});
it("disables the action while the selected layout is not ready", () => {
mocks.state.isReady = false;
render(<TaskRightPanelsToggle sessionId="session-1" />);
expect((screen.getByTestId(TOGGLE_TEST_ID) as HTMLButtonElement).disabled).toBe(true);
});
it("explains why the action is disabled while a panel is maximized", () => {
mocks.state.isMaximized = true;
mocks.state.isReady = false;
render(<TaskRightPanelsToggle sessionId="session-1" />);
const button = screen.getByTestId(TOGGLE_TEST_ID) as HTMLButtonElement;
expect(button.disabled).toBe(true);
expect(button.getAttribute(ARIA_LABEL_ATTRIBUTE)).toBeNull();
expect(button.getAttribute("title")).toBe(RIGHT_PANE_UNAVAILABLE_WHILE_MAXIMIZED);
expect(button.parentElement?.tagName).toBe("SPAN");
expect(button.parentElement?.getAttribute("tabindex")).toBe("0");
expect(button.parentElement?.getAttribute(ARIA_LABEL_ATTRIBUTE)).toBe(
RIGHT_PANE_UNAVAILABLE_WHILE_MAXIMIZED,
);
});
it("explains when the current layout has no separate right pane", () => {
mocks.state.isAvailable = false;
render(<TaskRightPanelsToggle sessionId="session-1" />);
const button = screen.getByTestId(TOGGLE_TEST_ID) as HTMLButtonElement;
expect(button.disabled).toBe(true);
expect(button.getAttribute("title")).toBe(RIGHT_PANE_UNAVAILABLE);
expect(button.parentElement?.getAttribute(ARIA_LABEL_ATTRIBUTE)).toBe(RIGHT_PANE_UNAVAILABLE);
});
it("restores focus after a layout transition disables the button", () => {
const toggleRightPanels = vi.fn(() => {
mocks.state.isReady = false;
});
mocks.state.toggleRightPanels = toggleRightPanels;
const { rerender } = render(<TaskRightPanelsToggle sessionId="session-1" />);
const button = screen.getByTestId(TOGGLE_TEST_ID);
button.focus();
fireEvent.click(button);
rerender(<TaskRightPanelsToggle sessionId="session-1" />);
expect((button as HTMLButtonElement).disabled).toBe(true);
mocks.state.isReady = true;
rerender(<TaskRightPanelsToggle sessionId="session-1" />);
expect(document.activeElement).toBe(button);
});
it("does not render on phones", () => {
mocks.state.isSupported = false;
render(<TaskRightPanelsToggle sessionId="session-1" />);
expect(screen.queryByTestId(TOGGLE_TEST_ID)).toBeNull();
});
});