5d6fe29293
* fix(web): collapse dockview workflow stepper to current step when cramped On narrow windows the full workflow step indicator outgrew the top-bar center track and rendered beneath the right-side action buttons. Switch the grid so the breadcrumb takes only its content width and the center track absorbs the real leftover space, then collapse the stepper to a minimal current-step view (via the shared useToolbarCollapsed hook) only when the full version genuinely cannot fit. * fix(web): stabilize stepper test id and drop margin skewing collapse Move data-testid="workflow-stepper" to the persistent outer container so the e2e locator resolves in both expanded and collapsed modes, and remove mx-2 from the w-full measuring container (it overflowed the wrapper and shifted the collapse threshold by ~8px); the grid gap-2 already spaces the columns. * refactor(web): trim verbose stepper collapse comment * fix(web): tag archived minimal badge with stepper test id Give the archived branch of MinimalWorkflowStepper the same workflow-stepper-minimal test id as the current-step branch so collapsed mode is detectable for archived tasks, and tighten two comments. * test(web): collapse multi-line comments in stepper test to single lines * fix(web): drop aria-current on minimal stepper no-current fallback When no step is current the minimal view falls back to the first step; it should not claim aria-current="step", matching the expanded path where no step is marked current in that case.
91 lines
3.6 KiB
TypeScript
91 lines
3.6 KiB
TypeScript
import { cleanup, render, screen } from "@testing-library/react";
|
|
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
import { WorkflowStepper, type WorkflowStepperStep } from "./workflow-stepper";
|
|
|
|
afterEach(() => {
|
|
cleanup();
|
|
vi.clearAllMocks();
|
|
});
|
|
|
|
// useToolbarCollapsed is mocked because the test DOM can't measure offsetWidth.
|
|
const collapsedMock = vi.fn(() => false);
|
|
vi.mock("@/hooks/use-toolbar-collapsed", () => ({
|
|
useToolbarCollapsed: () => collapsedMock(),
|
|
}));
|
|
|
|
vi.mock("@/components/state-provider", () => ({
|
|
useAppStore: () => undefined,
|
|
}));
|
|
vi.mock("@/lib/state/context-files-store", () => ({
|
|
useContextFilesStore: () => vi.fn(),
|
|
}));
|
|
vi.mock("@/lib/state/layout-store", () => ({
|
|
useLayoutStore: () => vi.fn(),
|
|
}));
|
|
vi.mock("@/lib/state/dockview-store", () => ({
|
|
useDockviewStore: () => vi.fn(),
|
|
}));
|
|
|
|
const STEPS: WorkflowStepperStep[] = [
|
|
{ id: "a", name: "Spec", color: "#111", position: 0 },
|
|
{ id: "b", name: "Work", color: "#222", position: 1 },
|
|
{ id: "c", name: "Review", color: "#333", position: 2 },
|
|
];
|
|
|
|
describe("WorkflowStepper", () => {
|
|
it("renders every step when there is room (not collapsed)", () => {
|
|
collapsedMock.mockReturnValue(false);
|
|
render(<WorkflowStepper steps={STEPS} currentStepId="b" />);
|
|
|
|
expect(screen.getByTestId("workflow-stepper")).toBeTruthy();
|
|
expect(screen.queryByTestId("workflow-stepper-minimal")).toBeNull();
|
|
// All steps render under the persistent outer container.
|
|
expect(screen.getByTestId("workflow-step-Spec")).toBeTruthy();
|
|
expect(screen.getByTestId("workflow-step-Work")).toBeTruthy();
|
|
expect(screen.getByTestId("workflow-step-Review")).toBeTruthy();
|
|
});
|
|
|
|
it("collapses to only the current step when space runs out", () => {
|
|
collapsedMock.mockReturnValue(true);
|
|
render(<WorkflowStepper steps={STEPS} currentStepId="b" />);
|
|
|
|
// Outer container persists across variants (stable e2e locator); minimal child marks collapsed state.
|
|
expect(screen.getByTestId("workflow-stepper")).toBeTruthy();
|
|
expect(screen.getByTestId("workflow-stepper-minimal")).toBeTruthy();
|
|
|
|
// Current step keeps its test id + aria-current in either variant.
|
|
const current = screen.getByTestId("workflow-step-Work");
|
|
expect(current.getAttribute("aria-current")).toBe("step");
|
|
expect(screen.queryByTestId("workflow-step-Spec")).toBeNull();
|
|
expect(screen.queryByTestId("workflow-step-Review")).toBeNull();
|
|
|
|
// Position indicator reflects the current step out of the total.
|
|
expect(screen.getByText("2/3")).toBeTruthy();
|
|
});
|
|
|
|
it("falls back to the first step when collapsed with no current step", () => {
|
|
collapsedMock.mockReturnValue(true);
|
|
render(<WorkflowStepper steps={STEPS} currentStepId={null} />);
|
|
|
|
// Fallback step isn't the real current step, so it must not claim aria-current.
|
|
expect(screen.getByTestId("workflow-step-Spec").getAttribute("aria-current")).toBeNull();
|
|
expect(screen.getByText("1/3")).toBeTruthy();
|
|
});
|
|
|
|
it("shows the archived badge instead of a step when collapsed and archived", () => {
|
|
collapsedMock.mockReturnValue(true);
|
|
render(<WorkflowStepper steps={STEPS} currentStepId="b" isArchived />);
|
|
|
|
expect(screen.getByText("Archived")).toBeTruthy();
|
|
// Archived badge carries the minimal test id for collapsed-mode detection.
|
|
expect(screen.getByTestId("workflow-stepper-minimal")).toBeTruthy();
|
|
expect(screen.queryByTestId("workflow-step-Work")).toBeNull();
|
|
});
|
|
|
|
it("renders nothing when there are no steps", () => {
|
|
collapsedMock.mockReturnValue(false);
|
|
const { container } = render(<WorkflowStepper steps={[]} currentStepId={null} />);
|
|
expect(container.innerHTML).toBe("");
|
|
});
|
|
});
|