69 lines
2.8 KiB
TypeScript
69 lines
2.8 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
|
import { dayBoundsUtc, generateSlots, overlaps, parseDate, zonedTimeToUtc } from '@/lib/slots';
|
|
|
|
const MONDAY = '2025-01-06';
|
|
const HOURS = [{ weekday: 1, startMin: 9 * 60, endMin: 12 * 60 }];
|
|
|
|
describe('overlaps', () => {
|
|
it('detects intersecting half-open intervals', () => {
|
|
const a = { start: new Date('2025-01-06T09:00:00Z'), end: new Date('2025-01-06T10:00:00Z') };
|
|
const b = { start: new Date('2025-01-06T09:30:00Z'), end: new Date('2025-01-06T10:30:00Z') };
|
|
const c = { start: new Date('2025-01-06T10:00:00Z'), end: new Date('2025-01-06T11:00:00Z') };
|
|
expect(overlaps(a, b)).toBe(true);
|
|
// Touching intervals do not overlap: [9,10) and [10,11).
|
|
expect(overlaps(a, c)).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('generateSlots', () => {
|
|
const base = { date: MONDAY, durationMin: 60, stepMin: 60, hours: HOURS, timeZone: 'UTC', now: new Date('2025-01-06T00:00:00Z') };
|
|
|
|
it('creates slots across the working window', () => {
|
|
const slots = generateSlots(base);
|
|
expect(slots.map((s) => s.start)).toEqual([
|
|
'2025-01-06T09:00:00.000Z',
|
|
'2025-01-06T10:00:00.000Z',
|
|
'2025-01-06T11:00:00.000Z',
|
|
]);
|
|
});
|
|
|
|
it('excludes slots that would double-book an existing booking', () => {
|
|
const busy = [{ start: new Date('2025-01-06T10:00:00Z'), end: new Date('2025-01-06T11:00:00Z') }];
|
|
const slots = generateSlots({ ...base, busy });
|
|
expect(slots.map((s) => s.start)).toEqual(['2025-01-06T09:00:00.000Z', '2025-01-06T11:00:00.000Z']);
|
|
});
|
|
|
|
it('excludes past slots relative to now', () => {
|
|
const slots = generateSlots({ ...base, now: new Date('2025-01-06T09:30:00Z') });
|
|
expect(slots.map((s) => s.start)).toEqual(['2025-01-06T10:00:00.000Z', '2025-01-06T11:00:00.000Z']);
|
|
});
|
|
|
|
it('honours the site timezone', () => {
|
|
const slots = generateSlots({ ...base, timeZone: 'America/New_York' });
|
|
// 09:00 EST (UTC-5) is 14:00 UTC.
|
|
expect(slots[0].start).toBe('2025-01-06T14:00:00.000Z');
|
|
});
|
|
|
|
it('ignores working hours for other weekdays', () => {
|
|
expect(generateSlots({ ...base, date: '2025-01-07' })).toEqual([]);
|
|
});
|
|
});
|
|
|
|
describe('timezone helpers', () => {
|
|
it('round-trips a wall-clock time to UTC', () => {
|
|
expect(zonedTimeToUtc({ year: 2025, month: 1, day: 6 }, 9 * 60, 'America/New_York').toISOString()).toBe('2025-01-06T14:00:00.000Z');
|
|
expect(zonedTimeToUtc({ year: 2025, month: 1, day: 6 }, 9 * 60, 'UTC').toISOString()).toBe('2025-01-06T09:00:00.000Z');
|
|
});
|
|
|
|
it('bounds a calendar day in UTC', () => {
|
|
const bounds = dayBoundsUtc(MONDAY, 'America/New_York')!;
|
|
expect(bounds.start.toISOString()).toBe('2025-01-06T05:00:00.000Z');
|
|
expect(bounds.end.toISOString()).toBe('2025-01-07T05:00:00.000Z');
|
|
});
|
|
|
|
it('rejects malformed dates', () => {
|
|
expect(parseDate('2025-13-01')).toBeNull();
|
|
expect(parseDate('nope')).toBeNull();
|
|
});
|
|
});
|