import { fireEvent, render, screen } from "@solidjs/testing-library"; import type { JSX } from "solid-js"; import { describe, expect, it, vi } from "vitest"; import { Wizard, WizardMeta, WizardRootPropsSchema } from "../../../src/components/wizard/index"; import type { WizardRootProps } from "../../../src/components/wizard/index"; interface ThreeStepWizardProps extends Omit {} function ThreeStepWizard(props: ThreeStepWizardProps): JSX.Element { return ( Step 1 Content 0 Step 2 Content 1 Step 3 Content 2 Prev Next ); } function renderWizard(props: ThreeStepWizardProps = {}) { return render(() => ); } describe("Wizard — rendering", () => { it("renders first step content by default", () => { renderWizard(); expect(screen.getByTestId("content-0")).toBeTruthy(); expect(screen.queryByTestId("content-1")).toBeNull(); expect(screen.queryByTestId("content-2")).toBeNull(); }); it("first trigger has aria-selected=true", () => { renderWizard(); expect(screen.getByTestId("trigger-0").getAttribute("aria-selected")).toBe("true"); expect(screen.getByTestId("trigger-1").getAttribute("aria-selected")).toBe("false"); }); it("step list has role=tablist", () => { renderWizard(); expect(screen.getByRole("tablist")).toBeTruthy(); }); it("active content has role=tabpanel", () => { renderWizard(); expect(screen.getByRole("tabpanel")).toBeTruthy(); }); }); describe("Wizard — navigation", () => { it("next button advances to step 1", () => { renderWizard(); fireEvent.click(screen.getByTestId("next")); expect(screen.getByTestId("content-1")).toBeTruthy(); expect(screen.queryByTestId("content-0")).toBeNull(); }); it("prev button goes back", () => { renderWizard(); fireEvent.click(screen.getByTestId("next")); fireEvent.click(screen.getByTestId("prev")); expect(screen.getByTestId("content-0")).toBeTruthy(); }); it("prev button not disabled after advancing", () => { renderWizard(); fireEvent.click(screen.getByTestId("next")); expect(screen.getByTestId("prev")).not.toBeDisabled(); }); }); describe("Wizard — linear mode", () => { it("unreached step triggers are disabled", () => { renderWizard(); expect(screen.getByTestId("trigger-1")).toBeDisabled(); expect(screen.getByTestId("trigger-2")).toBeDisabled(); }); it("clicking disabled trigger does not navigate", () => { renderWizard(); fireEvent.click(screen.getByTestId("trigger-2")); expect(screen.getByTestId("content-0")).toBeTruthy(); }); it("trigger becomes enabled after completing prior step", () => { renderWizard(); fireEvent.click(screen.getByTestId("next")); expect(screen.getByTestId("trigger-1")).not.toBeDisabled(); }); it("non-linear: all triggers enabled from start", () => { renderWizard({ linear: false }); expect(screen.getByTestId("trigger-2")).not.toBeDisabled(); }); }); describe("Wizard — callbacks", () => { it("onStepChange called on advance", () => { const onStepChange = vi.fn(); renderWizard({ onStepChange }); fireEvent.click(screen.getByTestId("next")); expect(onStepChange).toHaveBeenCalledWith(1); }); it("onStepChange called on prev", () => { const onStepChange = vi.fn(); renderWizard({ onStepChange }); fireEvent.click(screen.getByTestId("next")); fireEvent.click(screen.getByTestId("prev")); expect(onStepChange).toHaveBeenCalledWith(0); }); it("onComplete called after last step", () => { const onComplete = vi.fn(); renderWizard({ onComplete }); fireEvent.click(screen.getByTestId("next")); fireEvent.click(screen.getByTestId("next")); fireEvent.click(screen.getByTestId("next")); expect(onComplete).toHaveBeenCalledTimes(1); }); }); describe("Wizard — schema and meta", () => { it("schema validates correct input", () => { const result = WizardRootPropsSchema.safeParse({ step: 1, linear: true, orientation: "horizontal" }); expect(result.success).toBe(true); }); it("schema rejects invalid orientation", () => { const result = WizardRootPropsSchema.safeParse({ orientation: "diagonal" }); expect(result.success).toBe(false); }); it("meta has name and required parts", () => { expect(WizardMeta.name).toBe("Wizard"); expect(WizardMeta.parts).toContain("Root"); expect(WizardMeta.parts).toContain("StepContent"); }); });