import { afterEach, expect, test, vi } from 'vitest'; import { dots } from '../src/dots.ts'; function fakeCarousel() { let cb: (i: number) => void = () => {}; return { index: 0, count: 3, next: vi.fn(), prev: vi.fn(), scrollToIndex: vi.fn(), on: (_e: string, fn: (i: number) => void) => { cb = fn; return () => {}; }, destroy: vi.fn(), fire: (i: number) => cb(i), }; } function dotContainer() { const el = document.createElement('div'); el.innerHTML = ''; return el; } afterEach(() => { vi.restoreAllMocks(); vi.unstubAllGlobals(); }); test('clicking a dot scrolls to its index', () => { const c = fakeCarousel(); const container = dotContainer(); dots(c as never, container); (container.children[2] as HTMLButtonElement).click(); expect(c.scrollToIndex).toHaveBeenCalledWith(2); }); test('change marks the active dot with aria-current', () => { const c = fakeCarousel(); const container = dotContainer(); dots(c as never, container); c.fire(1); expect(container.children[1].getAttribute('aria-current')).toBe('true'); expect(container.children[0].hasAttribute('aria-current')).toBe(false); }); test('no-ops (no listeners wired) when native ::scroll-marker is supported', () => { vi.stubGlobal('CSS', { supports: () => true }); // jsdom has no CSS global const c = fakeCarousel(); const container = dotContainer(); const cleanup = dots(c as never, container); (container.children[0] as HTMLButtonElement).click(); expect(c.scrollToIndex).not.toHaveBeenCalled(); expect(typeof cleanup).toBe('function'); // noop cleanup, safe to call cleanup(); });