import { fireEvent, render, screen, waitFor } from "@testing-library/react"; import { describe, expect, it, vi } from "vitest"; import LeadForm from "./LeadForm"; import { scenarios } from "./demo/scenarios"; function fillRequiredFields() { fireEvent.change(screen.getByRole("textbox", { name: "Họ và tên" }), { target: { value: "Nguyễn Minh Anh" }, }); fireEvent.change(screen.getByRole("textbox", { name: "Vai trò" }), { target: { value: "Giám đốc phòng khám" }, }); fireEvent.change(screen.getByRole("textbox", { name: "Email hoặc Zalo" }), { target: { value: "minhanh@example.com" }, }); } describe("LeadForm", () => { it("posts the configured payload to the lead endpoint", async () => { const fetcher = vi.fn(async () => new Response(null, { status: 204 })) as unknown as typeof fetch; render( , ); fillRequiredFields(); fireEvent.click( screen.getByRole("button", { name: "Gửi yêu cầu thí điểm" }), ); await waitFor(() => expect(screen.getByText("Đã gửi yêu cầu thí điểm.")).toBeInTheDocument(), ); expect(fetcher).toHaveBeenCalledOnce(); const [, request] = (fetcher as ReturnType).mock.calls[0]; const payload = JSON.parse(String(request.body)); expect(payload.interest).toBe("both"); expect(payload.scenarioId).toBe(""); expect(payload.transcript).toBe(""); }); it("opens a prefilled mail draft when no endpoint is configured", async () => { const onMailto = vi.fn(); render( , ); fillRequiredFields(); fireEvent.click( screen.getByRole("button", { name: "Mở email đã điền sẵn" }), ); await waitFor(() => expect(onMailto).toHaveBeenCalledOnce()); const mailto = decodeURIComponent(onMailto.mock.calls[0][0]); expect(mailto).toContain("mailto:pilot@example.com"); expect(mailto).not.toContain("Kiểm tra dị ứng — xác nhận phủ định"); expect(mailto).not.toContain("Not allergic"); expect( screen.getByText( "Đã mở bản nháp email. Hãy kiểm tra và gửi thư để hoàn tất.", ), ).toBeInTheDocument(); }); it("reports an error instead of a dead mail draft when no channel is configured", async () => { const onMailto = vi.fn(); render( , ); fillRequiredFields(); fireEvent.click( screen.getByRole("button", { name: "Mở email đã điền sẵn" }), ); await waitFor(() => expect( screen.getByText( "Không thể gửi yêu cầu. Vui lòng kiểm tra kết nối và thử lại.", ), ).toBeInTheDocument(), ); expect(onMailto).not.toHaveBeenCalled(); }); it("shows inline errors without attempting submission", () => { const fetcher = vi.fn(); render( , ); fireEvent.click( screen.getByRole("button", { name: "Gửi yêu cầu thí điểm" }), ); expect(screen.getAllByText("Vui lòng điền trường này.")).toHaveLength(3); expect(fetcher).not.toHaveBeenCalled(); }); it("submits a selectable Scribe interest without interpreter context", async () => { const fetcher = vi.fn(async () => new Response(null, { status: 204 })) as unknown as typeof fetch; render( , ); fireEvent.change(screen.getByRole("combobox", { name: "Chức năng quan tâm" }), { target: { value: "scribe" }, }); fillRequiredFields(); fireEvent.click( screen.getByRole("button", { name: "Gửi yêu cầu thí điểm" }), ); await waitFor(() => expect(fetcher).toHaveBeenCalledOnce()); const [, request] = (fetcher as ReturnType).mock.calls[0]; expect(JSON.parse(String(request.body))).toMatchObject({ interest: "scribe", scenarioId: "", scenarioTitle: "", transcript: "", }); }); it("reports controlled product-interest changes", () => { const onInterestChange = vi.fn(); render( , ); const selector = screen.getByRole("combobox", { name: "Chức năng quan tâm" }); expect(selector).toHaveValue("interpreter"); fireEvent.change(selector, { target: { value: "both" } }); expect(onInterestChange).toHaveBeenCalledWith("both"); }); it("submits a fixed Scribe interest when the landing selector is hidden", async () => { const fetcher = vi.fn(async () => new Response(null, { status: 204 })) as unknown as typeof fetch; render( , ); expect(screen.queryByRole("combobox", { name: "Chức năng quan tâm" })).not.toBeInTheDocument(); fillRequiredFields(); fireEvent.click(screen.getByRole("button", { name: "Gửi yêu cầu thí điểm" })); await waitFor(() => expect(fetcher).toHaveBeenCalledOnce()); const [, request] = (fetcher as ReturnType).mock.calls[0]; expect(JSON.parse(String(request.body)).interest).toBe("scribe"); }); it("includes the simulation context only after explicit opt-in", async () => { const fetcher = vi.fn(async () => new Response(null, { status: 204 })) as unknown as typeof fetch; render( , ); fireEvent.click(screen.getByLabelText("Gửi kèm kịch bản mô phỏng và bản chép song ngữ")); fillRequiredFields(); fireEvent.click(screen.getByRole("button", { name: "Gửi yêu cầu thí điểm" })); await waitFor(() => expect(fetcher).toHaveBeenCalledOnce()); const [, request] = (fetcher as ReturnType).mock.calls[0]; expect(JSON.parse(String(request.body))).toMatchObject({ scenarioId: "prescription", transcript: expect.stringContaining("Take half a tablet"), }); }); });