132 lines
5.0 KiB
TypeScript
132 lines
5.0 KiB
TypeScript
import {
|
|
_buildEventBody,
|
|
_addMinutesIso,
|
|
_addDaysDate,
|
|
} from '../src/features/calendar/calendarApi';
|
|
import { _parseCalEventAttrs } from '../src/agent';
|
|
|
|
describe('_addMinutesIso', () => {
|
|
test('adds minutes to local ISO without timezone, preserves no-tz format', () => {
|
|
const out = _addMinutesIso('2026-05-21T14:00', 60);
|
|
expect(out).toBe('2026-05-21T15:00:00');
|
|
});
|
|
|
|
test('handles ISO with seconds', () => {
|
|
const out = _addMinutesIso('2026-05-21T14:30:00', 30);
|
|
expect(out).toBe('2026-05-21T15:00:00');
|
|
});
|
|
|
|
test('handles UTC marker Z', () => {
|
|
const out = _addMinutesIso('2026-05-21T14:00:00Z', 60);
|
|
expect(out).toBe('2026-05-21T15:00:00.000Z');
|
|
});
|
|
|
|
test('returns null on malformed input', () => {
|
|
expect(_addMinutesIso('not-a-date', 30)).toBeNull();
|
|
expect(_addMinutesIso('', 30)).toBeNull();
|
|
});
|
|
|
|
test('handles day-rollover correctly', () => {
|
|
const out = _addMinutesIso('2026-05-21T23:30', 60);
|
|
expect(out).toBe('2026-05-22T00:30:00');
|
|
});
|
|
});
|
|
|
|
describe('_addDaysDate', () => {
|
|
test('adds days to YYYY-MM-DD', () => {
|
|
expect(_addDaysDate('2026-05-21', 1)).toBe('2026-05-22');
|
|
expect(_addDaysDate('2026-12-31', 1)).toBe('2027-01-01');
|
|
});
|
|
});
|
|
|
|
describe('_buildEventBody', () => {
|
|
test('rejects empty title or start', () => {
|
|
const r1 = _buildEventBody({ title: '', start: '2026-05-21T14:00' }, 60);
|
|
expect(r1.ok).toBe(false);
|
|
const r2 = _buildEventBody({ title: 'x', start: '' }, 60);
|
|
expect(r2.ok).toBe(false);
|
|
});
|
|
|
|
test('builds basic timed event with default duration', () => {
|
|
const r = _buildEventBody({ title: '회의', start: '2026-05-21T14:00' }, 60);
|
|
if (!r.ok) throw new Error('expected ok');
|
|
expect(r.event.summary).toBe('회의');
|
|
expect(r.event.start.dateTime).toBe('2026-05-21T14:00');
|
|
expect(r.event.end.dateTime).toBe('2026-05-21T15:00:00');
|
|
// 로컬 timezone 자동 포함 — Intl 결과 (값은 시스템 의존이라 존재만 확인)
|
|
expect(r.event.start.timeZone).toBeTruthy();
|
|
expect(r.event.reminders.overrides).toHaveLength(2);
|
|
});
|
|
|
|
test('respects explicit duration', () => {
|
|
const r = _buildEventBody({
|
|
title: '미팅', start: '2026-05-21T14:00', durationMinutes: 90,
|
|
}, 60);
|
|
if (!r.ok) throw new Error('expected ok');
|
|
expect(r.event.end.dateTime).toBe('2026-05-21T15:30:00');
|
|
});
|
|
|
|
test('respects explicit end over duration', () => {
|
|
const r = _buildEventBody({
|
|
title: '미팅', start: '2026-05-21T14:00', end: '2026-05-21T16:00', durationMinutes: 30,
|
|
}, 60);
|
|
if (!r.ok) throw new Error('expected ok');
|
|
expect(r.event.end.dateTime).toBe('2026-05-21T16:00');
|
|
});
|
|
|
|
test('builds all-day event with exclusive end (+1 day)', () => {
|
|
const r = _buildEventBody({
|
|
title: '생일', start: '2026-06-15', allDay: true,
|
|
}, 60);
|
|
if (!r.ok) throw new Error('expected ok');
|
|
expect(r.event.start.date).toBe('2026-06-15');
|
|
expect(r.event.end.date).toBe('2026-06-16');
|
|
expect(r.event.start.dateTime).toBeUndefined();
|
|
});
|
|
|
|
test('omits timeZone when input has explicit offset', () => {
|
|
const r = _buildEventBody({
|
|
title: '회의', start: '2026-05-21T14:00:00+09:00',
|
|
}, 60);
|
|
if (!r.ok) throw new Error('expected ok');
|
|
expect(r.event.start.timeZone).toBeUndefined();
|
|
});
|
|
});
|
|
|
|
describe('_parseCalEventAttrs', () => {
|
|
test('parses double-quoted attrs', () => {
|
|
const a = _parseCalEventAttrs(' title="팀 미팅" start="2026-05-21T14:00" duration="60" ');
|
|
expect(a.title).toBe('팀 미팅');
|
|
expect(a.start).toBe('2026-05-21T14:00');
|
|
expect(a.duration).toBe(60);
|
|
});
|
|
|
|
test('parses single-quoted + bare attrs', () => {
|
|
const a = _parseCalEventAttrs(`title='간단' start=2026-05-21T14:00 duration=30`);
|
|
expect(a.title).toBe('간단');
|
|
expect(a.start).toBe('2026-05-21T14:00');
|
|
expect(a.duration).toBe(30);
|
|
});
|
|
|
|
test('parses all_day variants', () => {
|
|
const a1 = _parseCalEventAttrs('title="x" start="2026-05-21" all_day="true"');
|
|
expect(a1.allDay).toBe(true);
|
|
const a2 = _parseCalEventAttrs('title="x" start="2026-05-21" allday="1"');
|
|
expect(a2.allDay).toBe(true);
|
|
const a3 = _parseCalEventAttrs('title="x" start="2026-05-21" all-day="yes"');
|
|
expect(a3.allDay).toBe(true);
|
|
const a4 = _parseCalEventAttrs('title="x" start="2026-05-21" all_day="false"');
|
|
expect(a4.allDay).toBe(false);
|
|
});
|
|
|
|
test('ignores invalid duration value', () => {
|
|
const a = _parseCalEventAttrs('title="x" start="t" duration="abc"');
|
|
expect(a.duration).toBeUndefined();
|
|
});
|
|
|
|
test('returns empty when attrs are missing', () => {
|
|
expect(_parseCalEventAttrs('')).toEqual({});
|
|
expect(_parseCalEventAttrs(' ')).toEqual({});
|
|
});
|
|
});
|