[0]);
+ },
+ [dispatch]
+ );
+
+ return (
+
+ {state.currentStep}
+
+
+ );
+ }
+
+ // We'll test dispatch behavior by checking actual state changes instead
+ return { dispatched, ...render() };
+}
+
+// A version of StepIndicator that accepts an optional dispatch override for testing
+// Actually we'll just test via rendered output and state changes
describe('StepIndicator', () => {
describe('WIZD-03: back navigation preserves state', () => {
- it('clicking a completed step dispatches SET_STEP', () => {
- expect.fail('not yet implemented');
+ it('step 0 shows as active when currentStep is 0', async () => {
+ render(
+
+
+
+ );
+ // Step 0 (Backend) should be the active step — rendered as bold/active span
+ const backendLabel = screen.getByText(/Backend/);
+ expect(backendLabel).toBeDefined();
});
- it('clicking back to step 0 dispatches SET_REMOTE_PARAMS({}) to clear params', () => {
- expect.fail('not yet implemented');
+ it('completed steps are clickable', async () => {
+ // Render with currentStep=2 so steps 0 and 1 are completed
+ function Wrapper() {
+ const { dispatch } = useWizard();
+ React.useEffect(() => {
+ dispatch({ type: 'SET_STEP', payload: 2 });
+ }, [dispatch]);
+ return ;
+ }
+
+ render(
+
+
+
+ );
+
+ // Wait for useEffect to set step to 2
+ await screen.findByText(/Deployment/);
+
+ // Steps 0 and 1 should be clickable buttons
+ const buttons = screen.getAllByRole('button');
+ // Should have at least 2 buttons for step 0 and step 1
+ expect(buttons.length).toBeGreaterThanOrEqual(2);
});
- it('clicking back to step 0 does NOT dispatch RESET (deployment preserved)', () => {
- expect.fail('not yet implemented');
+ it('clicking a completed step dispatches SET_STEP', async () => {
+ const user = userEvent.setup();
+
+ function Wrapper() {
+ const { dispatch, state } = useWizard();
+ React.useEffect(() => {
+ dispatch({ type: 'SET_STEP', payload: 2 });
+ }, [dispatch]);
+ return (
+ <>
+ {state.currentStep}
+
+ >
+ );
+ }
+
+ render(
+
+
+
+ );
+
+ // Wait until currentStep is 2
+ await screen.findByText('2', { selector: '[data-testid="step"]' });
+
+ // Click the "Backend" step (step 0 — completed)
+ const buttons = screen.getAllByRole('button');
+ const backendButton = buttons.find((b) => b.textContent?.includes('Backend'));
+ expect(backendButton).toBeDefined();
+ await user.click(backendButton!);
+
+ // After clicking, currentStep should be 0
+ const stepEl = screen.getByTestId('step');
+ expect(stepEl.textContent).toBe('0');
});
- it('step 0 shows as active when currentStep is 0', () => {
- expect.fail('not yet implemented');
+ it('clicking back to step 0 dispatches SET_REMOTE_PARAMS({}) to clear params', async () => {
+ const user = userEvent.setup();
+
+ function Wrapper() {
+ const { dispatch, state } = useWizard();
+ React.useEffect(() => {
+ dispatch({ type: 'SET_BACKEND_TYPE', payload: 'azureblob' });
+ dispatch({ type: 'SET_REMOTE_PARAMS', payload: { account: 'myaccount' } });
+ dispatch({ type: 'SET_STEP', payload: 2 });
+ }, [dispatch]);
+ return (
+ <>
+ {JSON.stringify(state.remote.params)}
+
+ >
+ );
+ }
+
+ render(
+
+
+
+ );
+
+ // Wait until step is 2 and params are set
+ await screen.findByText((text) => text.includes('myaccount'), { selector: '[data-testid="params"]' });
+
+ // Click the "Backend" step (step 0)
+ const buttons = screen.getAllByRole('button');
+ const backendButton = buttons.find((b) => b.textContent?.includes('Backend'));
+ expect(backendButton).toBeDefined();
+ await user.click(backendButton!);
+
+ // params should be cleared
+ const paramsEl = screen.getByTestId('params');
+ expect(paramsEl.textContent).toBe('{}');
});
- it('completed steps are clickable', () => {
- expect.fail('not yet implemented');
+ it('clicking back to step 0 does NOT dispatch RESET (deployment preserved)', async () => {
+ const user = userEvent.setup();
+
+ function Wrapper() {
+ const { dispatch, state } = useWizard();
+ React.useEffect(() => {
+ // Set a non-default deployment state
+ dispatch({ type: 'SET_DEPLOYMENT', payload: { includeInstall: true } });
+ dispatch({ type: 'SET_DEPLOYMENT', payload: { scriptTargets: ['intune'] } });
+ dispatch({ type: 'SET_STEP', payload: 2 });
+ }, [dispatch]);
+ return (
+ <>
+ {String(state.deployment.includeInstall)}
+ {JSON.stringify(state.deployment.scriptTargets)}
+
+ >
+ );
+ }
+
+ render(
+
+
+
+ );
+
+ // Wait until deployment state is customized and step is 2
+ await screen.findByText('true', { selector: '[data-testid="include-install"]' });
+
+ // Click the "Backend" step
+ const buttons = screen.getAllByRole('button');
+ const backendButton = buttons.find((b) => b.textContent?.includes('Backend'));
+ expect(backendButton).toBeDefined();
+ await user.click(backendButton!);
+
+ // Deployment options should be preserved (not reset)
+ const includeInstallEl = screen.getByTestId('include-install');
+ expect(includeInstallEl.textContent).toBe('true');
+ const targetsEl = screen.getByTestId('targets');
+ expect(targetsEl.textContent).toBe('["intune"]');
});
});
});
+
+// Placeholder type for the spy component — not actually used in tests
+function StepIndicatorWithDispatch(_props: { dispatch: (action: { type: string; payload?: unknown }) => void }) {
+ return null;
+}