import { test, expect } from 'vitest'; import { buildIcs, fold, icsText, sessionDates } from './icsFile.js'; const NOW = new Date(Date.UTC(2026, 9, 6, 12, 0, 0)); test('sessions fall on the start date and then every N days', () => { const d = sessionDates(new Date(2026, 9, 11), 3, 7); expect(d.map((x) => `${x.getMonth() + 1}/${x.getDate()}`)).toEqual(['10/11', '10/18', '10/25']); expect(sessionDates(new Date(2026, 11, 28), 2, 7).map((x) => `${x.getFullYear()}-${x.getMonth() + 1}-${x.getDate()}`)).toEqual(['2026-12-28', '2027-1-4']); // across the new year }); test('all-day events end the next day', () => { const ics = buildIcs([{ date: new Date(2026, 9, 11), title: 'Romans — Ep 1: Grace' }], { name: 'Romans', now: NOW }); expect(ics.startsWith('BEGIN:VCALENDAR\r\n')).toBe(true); expect(ics).toContain('DTSTART;VALUE=DATE:20261011'); expect(ics).toContain('DTEND;VALUE=DATE:20261012'); expect(ics).toContain('SUMMARY:Romans — Ep 1: Grace'); expect(ics).toContain('X-WR-CALNAME:Romans'); expect(ics.endsWith('END:VCALENDAR\r\n')).toBe(true); }); test('a time makes a timed event with a length', () => { const ics = buildIcs([{ date: new Date(2026, 9, 11), title: 'Lesson', time: '09:30', minutes: 45 }], { now: NOW }); expect(ics).toContain('DTSTART:20261011T093000'); expect(ics).toContain('DTEND:20261011T101500'); }); test('special characters are escaped and long lines are folded', () => { expect(icsText('A, B; C\nD\\E')).toBe('A\\, B\\; C\\nD\\\\E'); const folded = fold(`DESCRIPTION:${'x'.repeat(200)}`); expect(folded.split('\r\n').every((l) => l.length <= 76)).toBe(true); expect(folded.replace(/\r\n /g, '')).toBe(`DESCRIPTION:${'x'.repeat(200)}`); });