feat: implement serializeICS with round-trip support
This commit is contained in:
@@ -3,6 +3,7 @@ import { readFileSync } from 'node:fs'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { dirname, join } from 'node:path'
|
||||
import { parseICS } from '../src/lib/ical-io.js'
|
||||
import { serializeICS } from '../src/lib/ical-io.js'
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url))
|
||||
const GOLDEN = readFileSync(join(__dirname, 'golden-org.ics'), 'utf-8')
|
||||
@@ -48,3 +49,60 @@ describe('parseICS', () => {
|
||||
expect(events[60].uid).toBe('uid60')
|
||||
})
|
||||
})
|
||||
|
||||
describe('serializeICS round-trip', () => {
|
||||
it('round-trips: parse(serialize(parse(x))) equals parse(x)', () => {
|
||||
const original = parseICS(GOLDEN)
|
||||
const text = serializeICS(original)
|
||||
const reparsed = parseICS(text)
|
||||
|
||||
expect(reparsed.events).toHaveLength(original.events.length)
|
||||
// Spot-check first event
|
||||
const e0 = reparsed.events[0]
|
||||
expect(e0.summary).toBe(original.events[0].summary)
|
||||
expect(e0.dtstartDate).toBe(original.events[0].dtstartDate)
|
||||
expect(e0.dtstartTime).toBe(original.events[0].dtstartTime)
|
||||
expect(e0.tzid).toBe(original.events[0].tzid)
|
||||
expect(e0._raw.dtstamp).toBe(original.events[0]._raw.dtstamp)
|
||||
})
|
||||
|
||||
it('preserves VTIMEZONE in output', () => {
|
||||
const { meta } = parseICS(serializeICS(parseICS(GOLDEN)))
|
||||
expect(meta.vtimezones).toHaveLength(1)
|
||||
})
|
||||
|
||||
it('serializes a manually-built recurring event', () => {
|
||||
const ev = {
|
||||
uid: 'test-1',
|
||||
summary: 'Test Event',
|
||||
location: 'Room A',
|
||||
description: 'Desc',
|
||||
dtstartDate: '2026-07-27',
|
||||
dtstartTime: '09:00',
|
||||
dtendTime: '10:30',
|
||||
tzid: 'UTC',
|
||||
rrule: { freq: 'WEEKLY', interval: 1, byday: 'MO', untilDate: '2026-10-19' },
|
||||
exdates: ['2026-09-21'],
|
||||
_raw: { dtstamp: '20260711T085008Z' },
|
||||
}
|
||||
const text = serializeICS({
|
||||
meta: {
|
||||
prodid: '-//Test//EN', version: '2.0', calscale: 'GREGORIAN',
|
||||
method: null, xWrCalname: null, xWrTimezone: null, vtimezones: [],
|
||||
},
|
||||
events: [ev],
|
||||
})
|
||||
expect(text).toContain('BEGIN:VEVENT')
|
||||
expect(text).toContain('SUMMARY:Test Event')
|
||||
expect(text).toContain('RRULE:FREQ=WEEKLY')
|
||||
expect(text).toContain('BYDAY=MO')
|
||||
expect(text).toContain('EXDATE')
|
||||
expect(text).toContain('DTSTART:20260727T090000Z')
|
||||
|
||||
// Re-parse and verify
|
||||
const reparsed = parseICS(text)
|
||||
expect(reparsed.events[0].rrule.freq).toBe('WEEKLY')
|
||||
expect(reparsed.events[0].rrule.byday).toBe('MO')
|
||||
expect(reparsed.events[0].exdates).toContain('2026-09-21')
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user