import { beforeEach, describe, expect, it, vi } from 'vitest';
import { Stepper } from './stepper';
import { STEPPER_CHANGE_EVENT, type StepperChangeEvent } from './stepper.types';
const MARKUP = `
`;
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);
});
});