Set up unit testing infrastructure

- Extract slot generation logic into apps/api/src/lib/slots.ts for testability
- Add 8 unit tests covering slot generation edge cases (overlap, multi-groomer, boundary)
- Add @testing-library/react + jsdom to apps/web; configure vitest with jsdom environment
- Add 4 component tests for App navigation rendering and active-link highlighting
- Remove passWithNoTests: true from both vitest configs; add coverage thresholds

Closes #39

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
Groom Book CTO
2026-03-18 01:52:36 +00:00
parent 817a76f8d5
commit 71ec6f1d70
10 changed files with 1046 additions and 26 deletions
+11 -21
View File
@@ -15,13 +15,14 @@ import {
clients,
pets,
} from "@groombook/db";
import {
generateAvailableSlots,
BUSINESS_START_HOUR,
BUSINESS_END_HOUR,
} from "../lib/slots.js";
export const bookRouter = new Hono();
// Business hours (UTC) — 09:0017:00
const BUSINESS_START_HOUR = 9;
const BUSINESS_END_HOUR = 17;
// ─── GET /api/book/services ─────────────────────────────────────────────────
// Public: list active services for the booking flow
@@ -86,23 +87,12 @@ bookRouter.get("/availability", async (c) => {
)
);
const durationMs = service.durationMinutes * 60_000;
const slots: string[] = [];
let slotStart = dayStart.getTime();
while (slotStart + durationMs <= dayEnd.getTime()) {
const slotEnd = slotStart + durationMs;
const hasGroomer = groomers.some(({ id: groomerId }) =>
!booked.some(
(a) =>
a.staffId === groomerId &&
a.startTime.getTime() < slotEnd &&
a.endTime.getTime() > slotStart
)
);
if (hasGroomer) slots.push(new Date(slotStart).toISOString());
slotStart += durationMs;
}
const slots = generateAvailableSlots({
dateStr,
durationMinutes: service.durationMinutes,
groomerIds: groomers.map((g) => g.id),
booked,
});
return c.json(slots);
});