import { describe, it, expect, vi, beforeEach } from "vitest"; import { render, screen, fireEvent } from "@testing-library/react"; import { PetForm } from "../portal/sections/PetForm.js"; import type { Pet } from "@groombook/types"; const BASE_PET: Pet = { id: "pet-1", clientId: "client-1", name: "Buddy", species: "dog", breed: "Labrador", weightKg: 25, dateOfBirth: "2020-03-15T00:00:00.000Z", healthAlerts: null, groomingNotes: null, cutStyle: null, shampooPreference: null, specialCareNotes: null, customFields: {}, coatType: null, preferredCuts: [], medicalAlerts: [], createdAt: "2024-01-01T00:00:00.000Z", updatedAt: "2024-01-01T00:00:00.000Z", }; describe("PetForm", () => { const onSave = vi.fn(); const onCancel = vi.fn(); beforeEach(() => { onSave.mockClear(); onCancel.mockClear(); }); // ── Coat type ─────────────────────────────────────────────────────────────── it("allows coat type selection from dropdown", () => { render(); const select = screen.getByRole("combobox", { name: /coat type/i }); fireEvent.change(select, { target: { value: "curly" } }); expect((select as HTMLSelectElement).value).toBe("curly"); }); it("persists coat type on save", () => { render(); fireEvent.change(screen.getByRole("combobox", { name: /coat type/i }), { target: { value: "double" } }); fireEvent.click(screen.getByRole("button", { name: /save/i })); expect(onSave).toHaveBeenCalledWith( expect.objectContaining({ coatType: "double" }) ); }); // ── Preferred cuts tag input ──────────────────────────────────────────────── it("adds a cut when Enter is pressed", () => { render(); const input = screen.getByPlaceholderText(/type a cut name/i); fireEvent.change(input, { target: { value: "Puppy Cut" } }); fireEvent.keyDown(input, { key: "Enter" }); expect(screen.getByText("Puppy Cut")).toBeTruthy(); }); it("adds a cut when the + button is clicked", () => { render(); const input = screen.getByPlaceholderText(/type a cut name/i); fireEvent.change(input, { target: { value: "Teddy Bear" } }); fireEvent.click(screen.getByRole("button", { name: "Add" })); expect(screen.getByText("Teddy Bear")).toBeTruthy(); }); it("removes a cut when X is clicked", () => { const petWithCuts: Pet = { ...BASE_PET, preferredCuts: ["Puppy Cut", "Teddy Bear"], }; render(); const puppyCutSpans = screen.getAllByText("Puppy Cut"); const puppyCutTag = puppyCutSpans[0]?.closest("span"); if (!puppyCutTag) return; const removeBtn = puppyCutTag.querySelector("button"); if (!removeBtn) return; fireEvent.click(removeBtn); expect(screen.queryByText("Puppy Cut")).toBeNull(); expect(screen.getByText("Teddy Bear")).toBeTruthy(); }); it("includes preferred cuts in save payload", () => { render(); fireEvent.change(screen.getByPlaceholderText(/type a cut name/i), { target: { value: "Puppy Cut" } }); fireEvent.keyDown(screen.getByPlaceholderText(/type a cut name/i), { key: "Enter" }); fireEvent.click(screen.getByRole("button", { name: /save/i })); expect(onSave).toHaveBeenCalledWith( expect.objectContaining({ preferredCuts: ["Puppy Cut"] }) ); }); // ── Medical alerts ─────────────────────────────────────────────────────────── it("adds a medical alert", () => { render(); fireEvent.click(screen.getByRole("button", { name: /add alert/i })); expect(screen.getByPlaceholderText(/alert type/i)).toBeTruthy(); }); it("removes a medical alert", () => { const petWithAlert: Pet = { ...BASE_PET, medicalAlerts: [{ id: "alert-1", type: "Allergic to chicken", description: "Causes hives", severity: "high" }], }; render(); const removeButtons = screen.getAllByRole("button", { name: "" }); if (removeButtons.length === 0) return; const removeButton = removeButtons[0]!; if (!removeButton) return; fireEvent.click(removeButton); expect(screen.queryByText("Allergic to chicken")).toBeNull(); }); it("validates alert type is non-empty", () => { render(); fireEvent.click(screen.getByRole("button", { name: /add alert/i })); fireEvent.click(screen.getByRole("button", { name: /save/i })); expect(screen.getByText(/type is required/i)).toBeTruthy(); expect(onSave).not.toHaveBeenCalled(); }); it("shows medical alerts in save payload", () => { const petWithAlert: Pet = { ...BASE_PET, medicalAlerts: [{ id: "alert-1", type: "Sensitive skin", description: "Use hypoallergenic shampoo only", severity: "medium" }], }; render(); fireEvent.click(screen.getByRole("button", { name: /save/i })); expect(onSave).toHaveBeenCalledWith( expect.objectContaining({ medicalAlerts: expect.arrayContaining([ expect.objectContaining({ type: "Sensitive skin", severity: "medium" }), ]), }) ); }); // ── Temperament read-only display ───────────────────────────────────────────── it("displays temperament score as read-only stars", () => { const petWithTemperament: Pet = { ...BASE_PET, temperamentScore: 4, temperamentFlags: ["Anxious", "Good with kids"], }; render(); expect(screen.getByText("(4/5)")).toBeTruthy(); expect(screen.getByText("Anxious")).toBeTruthy(); expect(screen.getByText("Good with kids")).toBeTruthy(); }); // ── Weight pre-fill from portal `weight` key (GRO-2207) ─────────────────────── it("pre-fills weight from the portal `weight` key when weightKg is absent", () => { const portalPet: Pet = { ...BASE_PET, weightKg: null, weight: "12.50" }; render(); expect(screen.getByDisplayValue(12.5)).toBeTruthy(); }); });