/** * @vitest-environment jsdom */ import { cleanup, render, screen, waitFor } from "@testing-library/react"; import { getAdminInviteList } from "@workspace/ui/services/admin/invite"; import type { AxiosResponse } from "axios"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import InviteManagement from "."; vi.mock("react-i18next", () => ({ useTranslation: () => ({ t: ( _key: string, fallback: string, options?: { count?: number } ): string => { if (typeof options?.count === "number") { return `${options.count} days`; } return fallback; }, }), })); vi.mock("@/stores/global", () => ({ useGlobalStore: () => ({ common: { currency: { currency_symbol: "$", }, }, }), })); vi.mock("@/utils/common", () => ({ formatDate: (timestamp: number) => `date-${timestamp}`, })); vi.mock("@workspace/ui/services/admin/invite", () => ({ getAdminInviteList: vi.fn(), })); const mockedGetAdminInviteList = vi.mocked(getAdminInviteList); function createInviteListResponse( data: API.GetAdminInviteListResponse ): AxiosResponse { return { data: { code: 200, data, }, status: 200, statusText: "OK", headers: {}, config: { headers: {} as AxiosResponse["config"]["headers"], }, }; } afterEach(() => { cleanup(); vi.clearAllMocks(); }); beforeEach(() => { localStorage.setItem("timezone", "UTC"); }); describe("InviteManagement", () => { it("renders invite rows with status, purchase state, commission, and gift days", async () => { mockedGetAdminInviteList.mockResolvedValue( createInviteListResponse({ total: 1, list: [ { inviter_id: 10, inviter_identifier: "agent@example.com", invitee_id: 123, invitee_identifier: "user@example.com", invitee_avatar: "", invitee_enable: true, invited_at: 1_716_800_000, order_count: 3, has_purchased: true, inviter_commission: 1500, inviter_gift_days: 30, invitee_gift_days: 7, }, ], }) ); render(); await waitFor(() => expect(mockedGetAdminInviteList).toHaveBeenCalledWith({ page: 1, size: 200, search: undefined, inviter_id: undefined, invitee_id: undefined, }) ); expect(await screen.findByText("agent@example.com")).not.toBeNull(); expect(screen.getByText("user@example.com")).not.toBeNull(); expect(screen.getByText("Enabled")).not.toBeNull(); expect(screen.getByText("Yes")).not.toBeNull(); expect(screen.getByText("$15.00")).not.toBeNull(); expect(screen.getByText("30 days")).not.toBeNull(); expect(screen.getByText("7 days")).not.toBeNull(); expect(screen.getByText("date-1716800000")).not.toBeNull(); }); it("shows the empty state when the API returns no rows", async () => { mockedGetAdminInviteList.mockResolvedValue( createInviteListResponse({ total: 0, list: [], }) ); render(); expect(await screen.findByText("No invite records")).not.toBeNull(); }); it("shows a loading state while invites are being fetched", () => { let resolveRequest: | (( value: AxiosResponse< API.Response & { data?: API.GetAdminInviteListResponse } > ) => void) | undefined; mockedGetAdminInviteList.mockReturnValue( new Promise((resolve) => { resolveRequest = resolve; }) ); render(); expect(screen.getByRole("status", { name: "Loading data" })).not.toBeNull(); expect(resolveRequest).toBeTypeOf("function"); }); it("shows an error state when the request fails", async () => { mockedGetAdminInviteList.mockRejectedValue(new Error("network failed")); render(); await waitFor(() => expect(mockedGetAdminInviteList).toHaveBeenCalled()); expect(await screen.findByText("Failed to load invites")).not.toBeNull(); expect(screen.queryByText("No invite records")).toBeNull(); }); });