Files
calendar/apps/web/components/plain/__tests__/PlainContactForm.test.tsx
T
devin-ai-integration[bot]GitHubpeer@cal.com <peer@cal.com>Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>Amit SharmaPeer Richelsen
b9c49b4567 feat: Add contact form for free users in Plain support widget (#22311)
Co-authored-by: peer@cal.com <peer@cal.com>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Amit Sharma <74371312+Amit91848@users.noreply.github.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
2025-07-24 18:14:38 +00:00

194 lines
5.5 KiB
TypeScript

import { render, screen, fireEvent, waitFor } from "@testing-library/react";
import { useSession } from "next-auth/react";
import { describe, it, expect, vi, beforeEach } from "vitest";
import { showToast } from "@calcom/ui/components/toast";
import PlainContactForm from "../../../lib/plain/PlainContactForm";
vi.mock("next-auth/react", () => ({
useSession: vi.fn(),
}));
vi.mock("@calcom/ui/components/toast", () => ({
showToast: vi.fn(),
}));
const mockUseSession = vi.mocked(useSession);
const mockShowToast = vi.mocked(showToast);
const mockFetch = vi.fn();
global.fetch = mockFetch;
describe("PlainContactForm", () => {
beforeEach(() => {
vi.clearAllMocks();
mockUseSession.mockReturnValue({
data: {
hasValidLicense: true,
upId: "test-up-id",
expires: "2025-12-31T23:59:59.999Z",
user: {
id: 123,
name: "Test User",
email: "test@example.com",
},
},
status: "authenticated",
update: vi.fn(),
});
});
it("should render contact button when closed", () => {
render(<PlainContactForm />);
const button = screen.getByRole("button");
expect(button).toBeInTheDocument();
});
it("should open contact form when button is clicked", () => {
render(<PlainContactForm />);
const button = screen.getByRole("button");
fireEvent.click(button);
expect(screen.getByText("Contact support")).toBeInTheDocument();
expect(screen.getByLabelText("Describe the issue")).toBeInTheDocument();
expect(screen.getByText("Attachments (optional)")).toBeInTheDocument();
});
it("should show empty form initially", () => {
render(<PlainContactForm />);
const button = screen.getByRole("button");
fireEvent.click(button);
const messageInput = screen.getByLabelText("Describe the issue") as HTMLTextAreaElement;
expect(messageInput.value).toBe("");
});
it("should close form when X button is clicked", () => {
render(<PlainContactForm />);
const openButton = screen.getByRole("button");
fireEvent.click(openButton);
const buttons = screen.getAllByRole("button");
const closeButton = buttons.find((button) => button.querySelector('svg use[href="#x"]'));
expect(closeButton).toBeDefined();
fireEvent.click(closeButton!);
expect(screen.queryByText("Contact support")).not.toBeInTheDocument();
});
it("should handle form submission successfully", async () => {
mockFetch.mockResolvedValueOnce({
ok: true,
json: () => Promise.resolve({ success: true }),
});
render(<PlainContactForm />);
const openButton = screen.getByRole("button");
fireEvent.click(openButton);
fireEvent.change(screen.getByLabelText("Describe the issue"), {
target: { value: "Test message" },
});
const submitButton = screen.getByRole("button", { name: /send message/i });
fireEvent.click(submitButton);
await waitFor(() => {
expect(screen.getByText("Message Sent")).toBeInTheDocument();
});
expect(mockFetch).toHaveBeenCalledWith("/api/support", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
message: "Test message",
attachmentIds: [],
}),
});
});
it("should show loading state during submission", async () => {
// eslint-disable-next-line @typescript-eslint/no-empty-function
mockFetch.mockImplementation(() => new Promise((_resolve) => {}));
render(<PlainContactForm />);
const openButton = screen.getByRole("button");
fireEvent.click(openButton);
fireEvent.change(screen.getByLabelText("Describe the issue"), {
target: { value: "Test message" },
});
const submitButton = screen.getByRole("button", { name: /send message/i });
fireEvent.click(submitButton);
expect(screen.getByText("Sending")).toBeInTheDocument();
await expect(submitButton).toBeDisabled();
});
it("should reset form after successful submission", async () => {
mockFetch.mockResolvedValueOnce({
ok: true,
json: () => Promise.resolve({ success: true }),
});
render(<PlainContactForm />);
const openButton = screen.getByRole("button");
fireEvent.click(openButton);
fireEvent.change(screen.getByLabelText("Describe the issue"), {
target: { value: "Test message" },
});
const submitButton = screen.getByRole("button", { name: /send message/i });
fireEvent.click(submitButton);
await waitFor(() => {
expect(screen.getByText("Message Sent")).toBeInTheDocument();
});
const sendAnotherButton = screen.getByRole("button", { name: /send another message/i });
fireEvent.click(sendAnotherButton);
await waitFor(() => {
const messageInput = screen.getByLabelText("Describe the issue") as HTMLTextAreaElement;
expect(messageInput.value).toBe("");
});
});
it("should handle missing user session", () => {
mockUseSession.mockReturnValue({
data: null,
status: "unauthenticated",
update: vi.fn(),
});
render(<PlainContactForm />);
const openButton = screen.getByRole("button");
fireEvent.click(openButton);
const messageInput = screen.getByLabelText("Describe the issue") as HTMLTextAreaElement;
expect(messageInput.value).toBe("");
});
it("should require message field", async () => {
render(<PlainContactForm />);
const openButton = screen.getByRole("button");
fireEvent.click(openButton);
await expect(screen.getByLabelText("Describe the issue")).toHaveAttribute("required");
});
});