templates2.Booking.Next/tests/unit/slots.test.ts
w4c-templates 6a0c2e88d2
Some checks are pending
CI / quality (push) Waiting to run
chore(template): publish Booking.Next
2026-10-11 23:27:02 +03:00

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();
});
});