Files
timeTableFix/test/ical-io.test.js
2026-07-13 18:30:52 +08:00

109 lines
3.6 KiB
JavaScript

import { describe, it, expect } from 'vitest'
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')
describe('parseICS', () => {
const { meta, events } = parseICS(GOLDEN)
it('parses calendar meta', () => {
expect(meta.prodid).toBe('-//Allocate//iCal4j 1.0//EN')
expect(meta.version).toBe('2.0')
expect(meta.calscale).toBe('GREGORIAN')
expect(meta.vtimezones.length).toBe(1)
})
it('parses all 61 events', () => {
expect(events).toHaveLength(61)
})
it('parses first event fields correctly', () => {
const e = events[0]
expect(e.summary).toBe('ACC INFO SYS, Tutorial')
expect(e.location).toBe('CA_B_B471')
expect(e.dtstartDate).toBe('2026-07-27')
expect(e.dtstartTime).toBe('18:00')
expect(e.dtendTime).toBe('20:00')
expect(e.tzid).toBe('Australia/Melbourne')
expect(e.rrule).toBeNull()
expect(e.exdates).toEqual([])
})
it('preserves description with escaped commas', () => {
const e = events[0]
expect(e.description).toContain('ACF2400_CA_S2_ON-CAMPUS')
expect(e.description).toContain('ACC INFO SYS')
})
it('captures dtstamp in _raw', () => {
expect(events[0]._raw.dtstamp).toBe('20260711T085008Z')
})
it('events have UIDs', () => {
expect(events[0].uid).toBe('uid0')
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')
})
})