/**
* Component tests for SvCarousel, exercising the shared createCarousel core:
* slide/dot rendering, arrow navigation with looping, autoplay advance, and the
* persistent play/pause stop toggle.
*/
import { describe, expect, it, beforeEach, afterEach, vi } from 'vitest'
import { mount, unmount, flushSync, createRawSnippet } from 'svelte'
import SvCarousel from './SvCarousel.svelte'
const slide = createRawSnippet(() => ({ render: () => `
` }))
function mountCarousel(props: Record) {
const target = document.createElement('div')
document.body.appendChild(target)
const app = mount(SvCarousel, { target, props: { slide, ...props } as any })
flushSync()
return { target, destroy: () => { unmount(app); target.remove() } }
}
const activeDot = (target: HTMLElement) =>
[...target.querySelectorAll('.sv-carousel__dot')].findIndex((d) => d.classList.contains('is-active'))
const click = (el: Element | null | undefined) => { el?.dispatchEvent(new MouseEvent('click', { bubbles: true })); flushSync() }
describe('SvCarousel', () => {
beforeEach(() => vi.useFakeTimers())
afterEach(() => vi.useRealTimers())
it('renders one slide + dot per item, first active', () => {
const { target, destroy } = mountCarousel({ count: 3 })
try {
expect(target.querySelectorAll('.sv-carousel__slide').length).toBe(3)
expect(target.querySelectorAll('.sv-carousel__dot').length).toBe(3)
expect(activeDot(target)).toBe(0)
} finally { destroy() }
})
it('the next arrow advances and loops back to the first', () => {
const { target, destroy } = mountCarousel({ count: 2, loop: true })
try {
click(target.querySelector('.sv-carousel__arrow.is-next'))
expect(activeDot(target)).toBe(1)
click(target.querySelector('.sv-carousel__arrow.is-next'))
expect(activeDot(target)).toBe(0) // wrapped
} finally { destroy() }
})
it('a dot jumps directly to its slide', () => {
const { target, destroy } = mountCarousel({ count: 4 })
try {
click(target.querySelectorAll('.sv-carousel__dot')[2])
expect(activeDot(target)).toBe(2)
} finally { destroy() }
})
it('autoplay advances on the interval and the toggle stops it', () => {
const { target, destroy } = mountCarousel({ count: 3, autoplay: 1000 })
try {
expect(activeDot(target)).toBe(0)
vi.advanceTimersByTime(1000)
flushSync()
expect(activeDot(target)).toBe(1)
// Stop toggle: autoplay no longer advances.
click(target.querySelector('.sv-carousel__playpause'))
vi.advanceTimersByTime(3000)
flushSync()
expect(activeDot(target)).toBe(1)
} finally { destroy() }
})
})