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"),
});
});
});