feat(GRO-2319): dev→uat — live StatusBadge palette (web) (#70)
CI / Test (push) Successful in 21s
CI / Lint & Typecheck (push) Successful in 27s
CI / Build & Push Docker Image (push) Successful in 14s

This commit was merged in pull request #70.
This commit is contained in:
2026-06-09 11:04:15 +00:00
parent f58a0e569b
commit b52b8e10ad
3 changed files with 118 additions and 9 deletions
+47 -1
View File
@@ -1,6 +1,6 @@
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
import { render, screen, fireEvent, waitFor } from "@testing-library/react";
import { parseTimeTo24Hour, isUpcoming, normalizeAppointment, normalizeService, formatServicePrice, CustomerNotesSection, ConfirmationSection, StatusBadge, formatSlotLabel, slotToTime, BookingFlow } from "../portal/sections/Appointments.tsx";
import { parseTimeTo24Hour, isUpcoming, normalizeAppointment, normalizeService, formatServicePrice, CustomerNotesSection, ConfirmationSection, StatusBadge, normalizeStatusKey, deriveDisplayStatus, formatSlotLabel, slotToTime, BookingFlow } from "../portal/sections/Appointments.tsx";
const UPCOMING_APPT = {
id: "appt-1",
@@ -517,6 +517,52 @@ describe("StatusBadge", () => {
expect(badge?.className).toContain("bg-stone-100");
expect(badge?.className).toContain("text-stone-600");
});
// GRO-2319 item 1: DB stores `no_show` (underscore) but the palette key is
// `no-show` (hyphen) — without normalization it rendered raw gray text.
it("renders the styled No-show badge for DB `no_show` status", () => {
render(<StatusBadge status="no_show" />);
const badge = screen.getByText("No-show").closest('span');
expect(badge?.className).toContain("bg-yellow-100");
expect(badge?.className).toContain("text-yellow-700");
});
});
describe("normalizeStatusKey (GRO-2319 item 1)", () => {
it("maps underscore status keys to the hyphen palette key", () => {
expect(normalizeStatusKey("no_show")).toBe("no-show");
});
it("leaves already-hyphenated / single-word keys unchanged", () => {
expect(normalizeStatusKey("no-show")).toBe("no-show");
expect(normalizeStatusKey("confirmed")).toBe("confirmed");
});
});
describe("deriveDisplayStatus (GRO-2319 item 2)", () => {
it("derives Pending for an upcoming, unconfirmed appointment", () => {
expect(
deriveDisplayStatus({ ...UPCOMING_APPT, status: "scheduled", confirmationStatus: "pending" }),
).toBe("pending");
});
it("keeps the raw status when the appointment is confirmed", () => {
expect(
deriveDisplayStatus({ ...UPCOMING_APPT, status: "confirmed", confirmationStatus: "confirmed" }),
).toBe("confirmed");
});
it("does not derive Pending for a past appointment", () => {
expect(
deriveDisplayStatus({ ...PAST_APPT, status: "completed", confirmationStatus: "pending" }),
).toBe("completed");
});
it("always shows Waitlisted for a waitlist-backed entry", () => {
expect(
deriveDisplayStatus({ ...UPCOMING_APPT, status: "waitlisted", confirmationStatus: undefined }),
).toBe("waitlisted");
});
});
describe("RescheduleFlow dynamic time slots", () => {