import { describe, it, expect, vi, beforeEach } from "vitest"; import { orderNotificationService } from "@/lib/services/OrderNotificationService"; import { sendEmail } from "@/lib/resend"; import { mockOrderConverted } from "../../fixtures/orders"; // Mock the resend module vi.mock("@/lib/resend", () => ({ sendEmail: vi.fn().mockResolvedValue({ id: "test-email-id" }), ADMIN_EMAILS: ["me@hytham.me", "tamara@hytham.me"], })); describe("OrderNotificationService", () => { beforeEach(() => { vi.clearAllMocks(); }); describe("sendOrderConfirmation", () => { it("should send customer order confirmation in correct language (EN)", async () => { const order = { ...mockOrderConverted, languageCode: "EN" }; await orderNotificationService.sendOrderConfirmation(order); expect(sendEmail).toHaveBeenCalledWith( expect.objectContaining({ to: "test@hytham.me", subject: "Order Confirmation #1524", }) ); }); it("should send customer order confirmation in Serbian (SR)", async () => { const order = { ...mockOrderConverted, languageCode: "SR" }; await orderNotificationService.sendOrderConfirmation(order); expect(sendEmail).toHaveBeenCalledWith( expect.objectContaining({ to: "test@hytham.me", subject: "Potvrda narudžbine #1524", }) ); }); it("should send customer order confirmation in German (DE)", async () => { const order = { ...mockOrderConverted, languageCode: "DE" }; await orderNotificationService.sendOrderConfirmation(order); expect(sendEmail).toHaveBeenCalledWith( expect.objectContaining({ to: "test@hytham.me", subject: "Bestellbestätigung #1524", }) ); }); it("should send customer order confirmation in French (FR)", async () => { const order = { ...mockOrderConverted, languageCode: "FR" }; await orderNotificationService.sendOrderConfirmation(order); expect(sendEmail).toHaveBeenCalledWith( expect.objectContaining({ to: "test@hytham.me", subject: "Confirmation de commande #1524", }) ); }); it("should format price correctly", async () => { const order = { ...mockOrderConverted, total: { gross: { amount: 5479, currency: "RSD" } }, }; await orderNotificationService.sendOrderConfirmation(order); const callArgs = vi.mocked(sendEmail).mock.calls[0][0]; expect(callArgs.react.props.total).toBe("5.479,00 RSD"); }); it("should handle missing variant name gracefully", async () => { const order = { ...mockOrderConverted, lines: [ { ...mockOrderConverted.lines[0], variantName: undefined, }, ], }; await orderNotificationService.sendOrderConfirmation(order); expect(sendEmail).toHaveBeenCalled(); const callArgs = vi.mocked(sendEmail).mock.calls[0][0]; expect(callArgs.react.props.items[0].name).toBe("Manoon Anti-age Serum"); }); it("should include variant name when present", async () => { await orderNotificationService.sendOrderConfirmation(mockOrderConverted); const callArgs = vi.mocked(sendEmail).mock.calls[0][0]; expect(callArgs.react.props.items[0].name).toBe("Manoon Anti-age Serum (50ml)"); }); }); describe("sendOrderConfirmationToAdmin", () => { it("should send admin notification with order details", async () => { await orderNotificationService.sendOrderConfirmationToAdmin(mockOrderConverted); expect(sendEmail).toHaveBeenCalledWith( expect.objectContaining({ to: ["me@hytham.me", "tamara@hytham.me"], subject: "[Admin] 🎉 New Order #1524 - 10.000,00 RSD", }) ); }); it("should always use English for admin emails", async () => { const order = { ...mockOrderConverted, languageCode: "SR" }; await orderNotificationService.sendOrderConfirmationToAdmin(order); const callArgs = vi.mocked(sendEmail).mock.calls[0][0]; expect(callArgs.react.props.language).toBe("en"); }); it("should include all order details in admin email", async () => { await orderNotificationService.sendOrderConfirmationToAdmin(mockOrderConverted); const callArgs = vi.mocked(sendEmail).mock.calls[0][0]; expect(callArgs.react.props.isAdmin).toBe(true); expect(callArgs.react.props.customerEmail).toBe("test@hytham.me"); expect(callArgs.react.props.phone).toBe("+38160123456"); }); }); describe("sendOrderShipped", () => { it("should send shipping confirmation with tracking", async () => { await orderNotificationService.sendOrderShipped( mockOrderConverted, "TRK123", "https://track.com/TRK123" ); expect(sendEmail).toHaveBeenCalledWith( expect.objectContaining({ to: "test@hytham.me", subject: "Your Order #1524 Has Shipped!", }) ); const callArgs = vi.mocked(sendEmail).mock.calls[0][0]; expect(callArgs.react.props.trackingNumber).toBe("TRK123"); expect(callArgs.react.props.trackingUrl).toBe("https://track.com/TRK123"); }); it("should handle missing tracking info", async () => { await orderNotificationService.sendOrderShipped(mockOrderConverted); const callArgs = vi.mocked(sendEmail).mock.calls[0][0]; expect(callArgs.react.props.trackingNumber).toBeUndefined(); }); }); describe("sendOrderCancelled", () => { it("should send cancellation email with reason", async () => { await orderNotificationService.sendOrderCancelled( mockOrderConverted, "Out of stock" ); const callArgs = vi.mocked(sendEmail).mock.calls[0][0]; expect(callArgs.react.props.reason).toBe("Out of stock"); }); }); describe("sendOrderPaid", () => { it("should send payment confirmation", async () => { await orderNotificationService.sendOrderPaid(mockOrderConverted); expect(sendEmail).toHaveBeenCalledWith( expect.objectContaining({ to: "test@hytham.me", subject: "Payment Received for Order #1524!", }) ); }); }); describe("formatPrice", () => { it("should format prices correctly for RSD", () => { // This is tested indirectly through the email calls above // The formatPrice function is in utils.ts }); }); describe("getCustomerName", () => { it("should extract name from user object", async () => { const order = { ...mockOrderConverted, user: { firstName: "John", lastName: "Doe" }, billingAddress: { firstName: "Jane", lastName: "Smith" }, }; await orderNotificationService.sendOrderConfirmation(order); const callArgs = vi.mocked(sendEmail).mock.calls[0][0]; expect(callArgs.react.props.customerName).toBe("John Doe"); }); it("should fallback to billing address if user not available", async () => { const order = { ...mockOrderConverted, user: undefined, billingAddress: { firstName: "Jane", lastName: "Smith" }, }; await orderNotificationService.sendOrderConfirmation(order); const callArgs = vi.mocked(sendEmail).mock.calls[0][0]; expect(callArgs.react.props.customerName).toBe("Jane Smith"); }); }); describe("formatAddress", () => { it("should format address with all fields", async () => { await orderNotificationService.sendOrderConfirmation(mockOrderConverted); const callArgs = vi.mocked(sendEmail).mock.calls[0][0]; expect(callArgs.react.props.shippingAddress).toContain("123 Test Street"); expect(callArgs.react.props.shippingAddress).toContain("Belgrade"); expect(callArgs.react.props.shippingAddress).toContain("11000"); }); it("should handle missing address fields", async () => { const order = { ...mockOrderConverted, shippingAddress: { firstName: "Test", lastName: "Customer", city: "Belgrade", }, }; await orderNotificationService.sendOrderConfirmation(order); expect(sendEmail).toHaveBeenCalled(); }); }); });