feat: implement serializeICS with round-trip support

This commit is contained in:
timeTable2 dev
2026-07-13 18:30:52 +08:00
parent 138b89e3bf
commit 7e708932b1
2 changed files with 183 additions and 0 deletions

View File

@@ -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')
})
})