Set up unit testing infrastructure
Extract slot generation from book.ts into pure utility for unit testing. Add 8 API unit tests and 4 web component tests with coverage thresholds. Closes #39 Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit was merged in pull request #42.
This commit is contained in:
committed by
GitHub
parent
d718821515
commit
cba502e35f
@@ -0,0 +1,116 @@
|
||||
import { describe, it, expect } from "vitest";
|
||||
import {
|
||||
generateAvailableSlots,
|
||||
BUSINESS_START_HOUR,
|
||||
BUSINESS_END_HOUR,
|
||||
} from "../lib/slots.js";
|
||||
|
||||
const DATE = "2026-03-18";
|
||||
const G1 = "groomer-1";
|
||||
const G2 = "groomer-2";
|
||||
|
||||
function utc(h: number, m = 0): Date {
|
||||
const d = new Date(`${DATE}T00:00:00Z`);
|
||||
d.setUTCHours(h, m, 0, 0);
|
||||
return d;
|
||||
}
|
||||
|
||||
describe("generateAvailableSlots", () => {
|
||||
it("returns slots within business hours", () => {
|
||||
const slots = generateAvailableSlots({
|
||||
dateStr: DATE,
|
||||
durationMinutes: 60,
|
||||
groomerIds: [G1],
|
||||
booked: [],
|
||||
});
|
||||
expect(slots.length).toBeGreaterThan(0);
|
||||
slots.forEach((s) => {
|
||||
const h = new Date(s).getUTCHours();
|
||||
expect(h).toBeGreaterThanOrEqual(BUSINESS_START_HOUR);
|
||||
expect(h).toBeLessThan(BUSINESS_END_HOUR);
|
||||
});
|
||||
});
|
||||
|
||||
it("returns correct count of 60-min slots across 8-hour window", () => {
|
||||
// 09:00–17:00 = 8 hours → 8 one-hour slots
|
||||
const slots = generateAvailableSlots({
|
||||
dateStr: DATE,
|
||||
durationMinutes: 60,
|
||||
groomerIds: [G1],
|
||||
booked: [],
|
||||
});
|
||||
expect(slots).toHaveLength(8);
|
||||
});
|
||||
|
||||
it("returns empty array when no groomers", () => {
|
||||
const slots = generateAvailableSlots({
|
||||
dateStr: DATE,
|
||||
durationMinutes: 60,
|
||||
groomerIds: [],
|
||||
booked: [],
|
||||
});
|
||||
expect(slots).toHaveLength(0);
|
||||
});
|
||||
|
||||
it("excludes slots blocked by a booking", () => {
|
||||
const slots = generateAvailableSlots({
|
||||
dateStr: DATE,
|
||||
durationMinutes: 60,
|
||||
groomerIds: [G1],
|
||||
booked: [{ staffId: G1, startTime: utc(9), endTime: utc(10) }],
|
||||
});
|
||||
expect(slots).not.toContain(new Date(`${DATE}T09:00:00.000Z`).toISOString());
|
||||
expect(slots).toContain(new Date(`${DATE}T10:00:00.000Z`).toISOString());
|
||||
});
|
||||
|
||||
it("keeps slot available when only the other groomer is booked", () => {
|
||||
const slots = generateAvailableSlots({
|
||||
dateStr: DATE,
|
||||
durationMinutes: 60,
|
||||
groomerIds: [G1, G2],
|
||||
booked: [{ staffId: G1, startTime: utc(9), endTime: utc(10) }],
|
||||
});
|
||||
// G2 is free at 09:00 so slot should still appear
|
||||
expect(slots).toContain(new Date(`${DATE}T09:00:00.000Z`).toISOString());
|
||||
});
|
||||
|
||||
it("excludes a slot only when ALL groomers are booked", () => {
|
||||
const slots = generateAvailableSlots({
|
||||
dateStr: DATE,
|
||||
durationMinutes: 60,
|
||||
groomerIds: [G1, G2],
|
||||
booked: [
|
||||
{ staffId: G1, startTime: utc(9), endTime: utc(10) },
|
||||
{ staffId: G2, startTime: utc(9), endTime: utc(10) },
|
||||
],
|
||||
});
|
||||
expect(slots).not.toContain(new Date(`${DATE}T09:00:00.000Z`).toISOString());
|
||||
});
|
||||
|
||||
it("correctly handles a booking that partially overlaps a slot", () => {
|
||||
// Booking 09:30–10:30 should block the 09:00 and 10:00 slots for G1
|
||||
const slots = generateAvailableSlots({
|
||||
dateStr: DATE,
|
||||
durationMinutes: 60,
|
||||
groomerIds: [G1],
|
||||
booked: [{ staffId: G1, startTime: utc(9, 30), endTime: utc(10, 30) }],
|
||||
});
|
||||
expect(slots).not.toContain(new Date(`${DATE}T09:00:00.000Z`).toISOString());
|
||||
expect(slots).not.toContain(new Date(`${DATE}T10:00:00.000Z`).toISOString());
|
||||
expect(slots).toContain(new Date(`${DATE}T11:00:00.000Z`).toISOString());
|
||||
});
|
||||
|
||||
it("does not generate a slot that would exceed business hours end", () => {
|
||||
// 30-min slots: last valid start is 16:30 (ends at 17:00)
|
||||
const slots = generateAvailableSlots({
|
||||
dateStr: DATE,
|
||||
durationMinutes: 30,
|
||||
groomerIds: [G1],
|
||||
booked: [],
|
||||
});
|
||||
const last = slots[slots.length - 1];
|
||||
expect(last).toBeDefined();
|
||||
expect(new Date(last!).getUTCHours()).toBe(16);
|
||||
expect(new Date(last!).getUTCMinutes()).toBe(30);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user