import { describe, it, expect, afterEach, vi } from 'vitest' import { formatDateForDisplay, formatDateForInput, formatDateLocalISO, parseInputDate, isValidDate, getDayName, getMonthName, getISOWeekNumber, formatHolidayName, datesInRange, } from '@/utils/dates' describe('dates.ts utilities', () => { describe('formatDateForDisplay', () => { it('formats ISO string to Swedish display format', () => { const result = formatDateForDisplay('2026-06-15') expect(result).toBe('15 juni 2026') }) it('formats Date object to Swedish display format', () => { const date = new Date('2026-06-15T00:00:00Z') const result = formatDateForDisplay(date) expect(result).toBe('15 juni 2026') }) it('handles January correctly', () => { const result = formatDateForDisplay('2026-01-01') expect(result).toBe('1 januari 2026') }) it('handles December correctly', () => { const result = formatDateForDisplay('2026-12-25') expect(result).toBe('25 december 2026') }) it('returns empty string for invalid date', () => { const result = formatDateForDisplay('invalid') expect(result).toBe('') }) it('defaults to Swedish when no locale is passed', () => { expect(formatDateForDisplay('2026-06-15')).toBe(formatDateForDisplay('2026-06-15', 'sv')) }) it('formats in English when the "en" locale is passed', () => { const result = formatDateForDisplay('2026-06-15', 'en') expect(result).toBe('June 15, 2026') }) it('formats in Swedish when the "sv" locale is explicitly passed', () => { const result = formatDateForDisplay('2026-06-15', 'sv') expect(result).toBe('15 juni 2026') }) it('returns empty string for invalid date in the "en" locale too', () => { expect(formatDateForDisplay('invalid', 'en')).toBe('') }) }) describe('formatDateForInput', () => { it('formats ISO string to input format', () => { const result = formatDateForInput('2026-06-15') expect(result).toBe('2026-06-15') }) it('formats Date object to input format', () => { const date = new Date('2026-06-15T00:00:00Z') const result = formatDateForInput(date) expect(result).toBe('2026-06-15') }) it('pads month and day with zeros', () => { const result = formatDateForInput('2026-01-05') expect(result).toBe('2026-01-05') }) it('returns empty string for invalid date', () => { const result = formatDateForInput('invalid') expect(result).toBe('') }) }) describe('formatDateLocalISO', () => { afterEach(() => { vi.unstubAllEnvs() }) it('formats a Date built from local year/month/day components', () => { const date = new Date(2026, 5, 15) // local June 15, 2026 expect(formatDateLocalISO(date)).toBe('2026-06-15') }) it('pads single-digit month and day', () => { const date = new Date(2026, 0, 5) // local January 5, 2026 expect(formatDateLocalISO(date)).toBe('2026-01-05') }) it('reads the local calendar date, not the UTC one — this is the whole point of the function', () => { // Pacific/Kiritimati is UTC+14. A moment at 23:30 UTC on Jan 1 is // already local Jan 2 there. `formatDateForInput` (UTC getters) must // report Jan 1; `formatDateLocalISO` (local getters) must report // Jan 2. If this ever collapses to the same value, someone has // swapped in the UTC variant and reintroduced the off-by-one-day bug // this function exists to avoid. vi.stubEnv('TZ', 'Pacific/Kiritimati') const date = new Date(Date.UTC(2026, 0, 1, 23, 30)) expect(formatDateForInput(date)).toBe('2026-01-01') expect(formatDateLocalISO(date)).toBe('2026-01-02') }) }) describe('parseInputDate', () => { it('parses ISO date string to Date object', () => { const result = parseInputDate('2026-06-15') expect(result).toBeInstanceOf(Date) expect(result.toISOString()).toContain('2026-06-15') }) it('creates valid Date for valid input', () => { const result = parseInputDate('2026-06-15') expect(isValidDate(result)).toBe(true) }) }) describe('isValidDate', () => { it('returns true for valid Date object', () => { const date = new Date('2026-06-15') expect(isValidDate(date)).toBe(true) }) it('returns false for invalid Date', () => { const date = new Date('invalid') expect(isValidDate(date)).toBe(false) }) it('returns false for non-Date values', () => { expect(isValidDate('2026-06-15')).toBe(false) expect(isValidDate(20260615)).toBe(false) expect(isValidDate(null)).toBe(false) expect(isValidDate(undefined)).toBe(false) }) }) describe('getDayName', () => { it('returns Swedish day name', () => { const date = new Date('2026-06-15T00:00:00Z') // Monday const result = getDayName(date) expect(result.toLowerCase()).toBe('måndag') }) it('returns empty string for invalid date', () => { const result = getDayName(new Date('invalid')) expect(result).toBe('') }) }) describe('getMonthName', () => { it('returns Swedish month name', () => { const date = new Date('2026-06-15T00:00:00Z') const result = getMonthName(date) expect(result).toBe('juni') }) it('returns correct month names for all months', () => { const monthNames = [ 'januari', 'februari', 'mars', 'april', 'maj', 'juni', 'juli', 'augusti', 'september', 'oktober', 'november', 'december', ] monthNames.forEach((monthName, index) => { const date = new Date(`2026-${String(index + 1).padStart(2, '0')}-15T00:00:00Z`) const result = getMonthName(date) expect(result).toBe(monthName) }) }) it('returns empty string for invalid date', () => { const result = getMonthName(new Date('invalid')) expect(result).toBe('') }) }) describe('getISOWeekNumber', () => { it('returns 0 for an invalid date', () => { expect(getISOWeekNumber(new Date('invalid'))).toBe(0) }) it('computes the ISO week number for a mid-week date', () => { // 2026-06-15 is a Monday. expect(getISOWeekNumber(new Date('2026-06-15T00:00:00Z'))).toBe(25) }) it('handles a Sunday correctly (getUTCDay() === 0 falls back to ISO day 7)', () => { // 2026-01-11 is a Sunday, closing out ISO week 2 of 2026. expect(getISOWeekNumber(new Date('2026-01-11T00:00:00Z'))).toBe(2) }) }) describe('formatHolidayName', () => { it('returns the name unchanged when it has no ";" segments', () => { expect(formatHolidayName('Midsommardagen')).toBe('Midsommardagen') }) it('strips a trailing weekday segment', () => { expect(formatHolidayName('Påskdagen; Söndag')).toBe('Påskdagen') }) it('falls back to the first segment when every segment is a weekday name', () => { // Contrived input where nothing survives the weekday filter — the // function must not return an empty string in that case. expect(formatHolidayName('Söndag; Måndag')).toBe('Söndag') }) }) describe('datesInRange', () => { it('returns an empty array when the start date is invalid', () => { expect(datesInRange('invalid', '2026-06-15')).toEqual([]) }) it('returns an empty array when the end date is invalid', () => { expect(datesInRange('2026-06-15', 'invalid')).toEqual([]) }) it('returns the inclusive range in ascending order for start before end', () => { expect(datesInRange('2026-06-01', '2026-06-03')).toEqual([ '2026-06-01', '2026-06-02', '2026-06-03', ]) }) it('returns the same range regardless of argument order (reversed input)', () => { expect(datesInRange('2026-06-03', '2026-06-01')).toEqual([ '2026-06-01', '2026-06-02', '2026-06-03', ]) }) it('returns a single date when start equals end', () => { expect(datesInRange('2026-06-01', '2026-06-01')).toEqual(['2026-06-01']) }) }) })