import { beforeEach, describe, expect, it, vi } from 'vitest'; import { Stepper } from './stepper'; import { STEPPER_CHANGE_EVENT, type StepperChangeEvent } from './stepper.types'; const MARKUP = `
Account panel
Profile panel
Review panel
`; function setup(): HTMLElement { document.body.innerHTML = MARKUP; return document.body.firstElementChild as HTMLElement; } function steps(root: HTMLElement): HTMLElement[] { return Array.from(root.querySelectorAll('[data-c42-stepper-step]')); } function triggers(root: HTMLElement): HTMLElement[] { return Array.from(root.querySelectorAll('[data-c42-stepper-trigger]')); } describe('Stepper', () => { beforeEach(() => { document.body.innerHTML = ''; }); it('marks the first step current and others upcoming', () => { const root = setup(); new Stepper(root); const [a, b] = steps(root); expect(a.dataset.state).toBe('current'); expect(b.dataset.state).toBe('upcoming'); expect(triggers(root)[0].getAttribute('aria-current')).toBe('step'); }); it('shows only the current panel', () => { const root = setup(); new Stepper(root); const panels = root.querySelectorAll('[data-c42-stepper-panel]'); expect(panels[0].hasAttribute('hidden')).toBe(false); expect(panels[1].hasAttribute('hidden')).toBe(true); }); it('advances with next() and marks previous completed', () => { const root = setup(); const stepper = new Stepper(root); stepper.next(); const [a, b] = steps(root); expect(a.dataset.state).toBe('completed'); expect(b.dataset.state).toBe('current'); expect(stepper.value).toBe('profile'); }); it('goes back with prev()', () => { const root = setup(); const stepper = new Stepper(root); stepper.next(); stepper.prev(); expect(stepper.index).toBe(0); }); it('blocks jumping ahead in linear mode', () => { const root = setup(); const stepper = new Stepper(root); triggers(root)[2].click(); expect(stepper.index).toBe(0); expect(triggers(root)[2].getAttribute('aria-disabled')).toBe('true'); }); it('allows revisiting completed steps in linear mode', () => { const root = setup(); const stepper = new Stepper(root); stepper.next(); triggers(root)[0].click(); expect(stepper.index).toBe(0); }); it('allows free navigation when linear=false', () => { const root = setup(); const stepper = new Stepper(root, { linear: false }); triggers(root)[2].click(); expect(stepper.index).toBe(2); }); it('honors defaultValue and marks earlier steps completed', () => { const root = setup(); const stepper = new Stepper(root, { defaultValue: 'profile' }); expect(stepper.index).toBe(1); expect(steps(root)[0].dataset.state).toBe('completed'); }); it('emits change events', () => { const root = setup(); const stepper = new Stepper(root); const spy = vi.fn(); stepper.on(STEPPER_CHANGE_EVENT, spy); stepper.next(); expect(spy).toHaveBeenCalledOnce(); expect(spy.mock.calls[0][0].detail).toEqual({ value: 'profile', index: 1 }); }); it('stops reacting after destroy', () => { const root = setup(); const stepper = new Stepper(root, { linear: false }); stepper.destroy(); triggers(root)[2].click(); expect(stepper.index).toBe(0); }); });