import { describe, it, expect, vi, beforeEach } from "vitest"; // Mock Payload CMS const { mockCreate, mockSendEmail } = vi.hoisted(() => ({ mockCreate: vi.fn(), mockSendEmail: vi.fn(), })); vi.mock("payload", () => ({ getPayload: vi.fn().mockResolvedValue({ create: mockCreate, sendEmail: mockSendEmail, }), })); // Mock Email Template renders vi.mock("@mintel/mail", () => ({ render: vi.fn().mockResolvedValue("Mocked Email HTML"), ContactFormNotification: () => "ContactFormNotification", ConfirmationMessage: () => "ConfirmationMessage", })); // Mock Notifications and Analytics const { mockNotify, mockTrack, mockCaptureException } = vi.hoisted(() => ({ mockNotify: vi.fn(), mockTrack: vi.fn(), mockCaptureException: vi.fn(), })); vi.mock("@/lib/services/create-services.server", () => ({ getServerAppServices: () => ({ logger: { child: () => ({ info: vi.fn(), warn: vi.fn(), error: vi.fn(), }), }, analytics: { setServerContext: vi.fn(), track: mockTrack, }, notifications: { notify: mockNotify, }, errors: { captureException: mockCaptureException, }, }), })); // Import the route handler we want to test import { POST } from "../app/api/contact/route"; import { NextResponse } from "next/server"; import type { Mock } from "vitest"; describe("Contact API Integration", () => { beforeEach(() => { vi.clearAllMocks(); (NextResponse.json as Mock).mockImplementation((body: any, init?: any) => ({ status: init?.status || 200, json: async () => body, })); }); it("should validate and decline empty or short messages", async () => { const req = new Request("http://localhost/api/contact", { method: "POST", body: JSON.stringify({ name: "Test User", email: "test@example.com", message: "too short", }), }); const response = await POST(req); expect(response.status).toBe(400); const data = await response.json(); expect(data.error).toBe("message_too_short"); // Ensure payload and email were NOT called expect(mockCreate).not.toHaveBeenCalled(); expect(mockSendEmail).not.toHaveBeenCalled(); }); it("should catch honeypot submissions", async () => { const req = new Request("http://localhost/api/contact", { method: "POST", body: JSON.stringify({ name: "Spam Bot", email: "spam@example.com", message: "This is a very long spam message that passes length checks.", website: "http://spam.com", // Honeypot filled }), }); const response = await POST(req); // Honeypot returns 200 OK so the bot thinks it succeeded expect(response.status).toBe(200); // But it actually does NOTHING internally expect(mockCreate).not.toHaveBeenCalled(); expect(mockSendEmail).not.toHaveBeenCalled(); }); it("should successfully save to Payload and send emails", async () => { const req = new Request("http://localhost/api/contact", { method: "POST", headers: { "user-agent": "vitest", "x-forwarded-for": "127.0.0.1", }, body: JSON.stringify({ name: "Jane Doe", email: "jane@example.com", company: "Jane Tech", message: "Hello, I am interested in exploring your high-voltage grid solutions.", }), }); const response = await POST(req); expect(response.status).toBe(200); const data = await response.json(); expect(data.message).toBe("Ok"); // 1. Verify Payload creation expect(mockCreate).toHaveBeenCalledTimes(1); expect(mockCreate).toHaveBeenCalledWith({ collection: "form-submissions", data: { name: "Jane Doe", email: "jane@example.com", company: "Jane Tech", message: "Hello, I am interested in exploring your high-voltage grid solutions.", }, }); // 2. Verify Email Sending // Note: sendEmail is called twice (Notification + User Confirmation) expect(mockSendEmail).toHaveBeenCalledTimes(2); expect(mockSendEmail).toHaveBeenNthCalledWith( 1, expect.objectContaining({ subject: "Kontaktanfrage von Jane Doe", replyTo: "jane@example.com", }), ); expect(mockSendEmail).toHaveBeenNthCalledWith( 2, expect.objectContaining({ to: "jane@example.com", subject: "Ihre Kontaktanfrage bei MB Grid Solutions", }), ); // 3. Verify notification and analytics expect(mockNotify).toHaveBeenCalledTimes(1); expect(mockTrack).toHaveBeenCalledWith("contact-form-success", { has_company: true, }); }); });