| 1 | import { Children, isValidElement, type ReactElement } from "react"; |
| 2 | import { describe, expect, it, vi } from "vite-plus/test"; |
| 3 | |
| 4 | import { WizardSteps } from "../ui/wizard"; |
| 5 | import { ADD_PROVIDER_WIZARD_STEPS } from "./AddProviderInstanceDialog.logic"; |
| 6 | import { AddProviderInstanceWizardSteps } from "./AddProviderInstanceWizardSteps"; |
| 7 | |
| 8 | interface StepButtonProps { |
| 9 | readonly "aria-current"?: string; |
| 10 | readonly onClick: () => void; |
| 11 | } |
| 12 | |
| 13 | interface StepListItemProps { |
| 14 | readonly children: ReactElement<StepButtonProps>; |
| 15 | } |
| 16 | |
| 17 | function renderStepButtons( |
| 18 | currentStep: number, |
| 19 | instanceIdError: string | null, |
| 20 | onNavigation: Parameters<typeof AddProviderInstanceWizardSteps>[0]["onNavigation"], |
| 21 | ): ReactElement<StepButtonProps>[] { |
| 22 | const header = AddProviderInstanceWizardSteps({ |
| 23 | currentStep, |
| 24 | summaries: ["Codex", "Codex Workspace", null], |
| 25 | instanceIdError, |
| 26 | onNavigation, |
| 27 | }); |
| 28 | |
| 29 | return Children.toArray(WizardSteps(header.props).props.children) |
| 30 | .filter((child): child is ReactElement<StepListItemProps> => isValidElement(child)) |
| 31 | .map((item) => item.props.children); |
| 32 | } |
| 33 | |
| 34 | describe("AddProviderInstanceWizardSteps", () => { |
| 35 | it("gates the actual Config header click through Identity validation", () => { |
| 36 | const onNavigation = vi.fn(); |
| 37 | const buttons = renderStepButtons(0, "Instance ID is required.", onNavigation); |
| 38 | |
| 39 | expect(buttons).toHaveLength(ADD_PROVIDER_WIZARD_STEPS.length); |
| 40 | buttons[2]!.props.onClick(); |
| 41 | |
| 42 | expect(onNavigation).toHaveBeenCalledOnce(); |
| 43 | expect(onNavigation).toHaveBeenCalledWith({ |
| 44 | kind: "blocked", |
| 45 | step: 1, |
| 46 | error: "Instance ID is required.", |
| 47 | }); |
| 48 | }); |
| 49 | |
| 50 | it("marks the wizard step separately from the clicked button focus", () => { |
| 51 | const buttons = renderStepButtons(1, "Instance ID is required.", vi.fn()); |
| 52 | |
| 53 | expect(buttons[0]!.props["aria-current"]).toBeUndefined(); |
| 54 | expect(buttons[1]!.props["aria-current"]).toBe("step"); |
| 55 | expect(buttons[2]!.props["aria-current"]).toBeUndefined(); |
| 56 | }); |
| 57 | |
| 58 | it("preserves the actual backward header click", () => { |
| 59 | const onNavigation = vi.fn(); |
| 60 | const buttons = renderStepButtons(2, "Instance ID is required.", onNavigation); |
| 61 | |
| 62 | buttons[0]!.props.onClick(); |
| 63 | |
| 64 | expect(onNavigation).toHaveBeenCalledWith({ kind: "navigate", step: 0 }); |
| 65 | }); |
| 66 | }); |