/**
* Component tests for SvDateRangeInput: the field renders the formatted range,
* the two-month range calendar portals to
, picking a range emits
* [start, end], and the shared editor contract (label / error) is wired.
*/
import { describe, expect, it, afterEach } from 'vitest'
import { mount, unmount, flushSync } from 'svelte'
import SvDateRangeInput from './SvDateRangeInput.svelte'
function mountDri(props: Record) {
const target = document.createElement('div')
document.body.appendChild(target)
const app = mount(SvDateRangeInput, { target, props: props as any })
flushSync()
return { target, destroy: () => { unmount(app); target.remove() } }
}
// The calendar panel portals to , so query the document.
const panel = () => document.querySelector('.sv-dri__panel')
afterEach(() => {
document.querySelectorAll('.sv-dri__panel').forEach((n) => n.remove())
})
describe('SvDateRangeInput', () => {
it('formats the initial [start, end] range with the "to" separator', () => {
const { target, destroy } = mountDri({
value: [new Date(2026, 5, 1), new Date(2026, 5, 7)],
formatString: 'yyyy-MM-dd',
})
try {
const input = target.querySelector('.sv-dri__input')!
expect(input.value).toBe('2026-06-01 to 2026-06-07')
} finally { destroy() }
})
it('shows the placeholder when empty and opens the calendar on click', () => {
const { target, destroy } = mountDri({ value: null, placeholder: 'Pick a range' })
try {
const input = target.querySelector('.sv-dri__input')!
expect(input.value).toBe('')
expect(input.placeholder).toBe('Pick a range')
expect(panel()).toBeNull()
input.click()
flushSync()
expect(panel()).not.toBeNull()
// The portalled panel embeds a range-mode SvCalendar.
expect(panel()!.querySelector('.sv-cal')).not.toBeNull()
} finally { destroy() }
})
it('wires the shared editor contract (label + aria-invalid + error text)', () => {
const { target, destroy } = mountDri({
value: null, label: 'Period', required: true, invalid: true, error: 'Required',
})
try {
const input = target.querySelector('.sv-dri__input')!
const labelEl = target.querySelector('.sv-field__label')!
expect(labelEl.textContent).toContain('Period')
expect(labelEl.getAttribute('for')).toBe(input.id)
expect(input.getAttribute('aria-invalid')).toBe('true')
expect(input.getAttribute('aria-required')).toBe('true')
const err = target.querySelector('.sv-field__error')!
expect(err.textContent).toBe('Required')
expect(err.id).toBe(input.getAttribute('aria-describedby'))
} finally { destroy() }
})
it('localizes the "to" separator via messages', () => {
const { target, destroy } = mountDri({
value: [new Date(2026, 5, 1), new Date(2026, 5, 7)],
formatString: 'dd/MM/yyyy',
messages: { to: 'au' },
})
try {
const input = target.querySelector('.sv-dri__input')!
expect(input.value).toBe('01/06/2026 au 07/06/2026')
} finally { destroy() }
})
})