import { fireEvent, render, screen } from "@testing-library/react"; import { afterEach, expect, test, vi } from "vitest"; import { HostWithdrawalsPage } from "./HostWithdrawalsPage.jsx"; import { useHostWithdrawalsPage } from "@/features/host-org/hooks/useHostWithdrawalsPage.js"; const mocks = vi.hoisted(() => ({ changeIdentity: vi.fn(), changeKeyword: vi.fn(), changeRecordType: vi.fn(), changeStatus: vi.fn(), changeTimeRange: vi.fn(), dataTable: vi.fn(), resetFilters: vi.fn(), })); vi.mock("@/features/host-org/hooks/useHostWithdrawalsPage.js", () => ({ useHostWithdrawalsPage: vi.fn(), })); vi.mock("@/shared/ui/DataTable.jsx", () => ({ DataTable: (props) => { mocks.dataTable(props); return (
{props.columns.map((column) => (
{column.label}
))} {props.items.map((item) => (
{props.columns.map((column) => (
{column.render ? column.render(item) : item[column.key]}
))}
))}
); }, })); vi.mock("@/shared/ui/TimeRangeFilter.jsx", () => ({ TimeRangeFilter: ({ onChange }) => ( ), })); afterEach(() => { vi.clearAllMocks(); }); test("host withdrawals page renders salary transfer and withdrawal records", () => { vi.mocked(useHostWithdrawalsPage).mockReturnValue(pageFixture()); render(); expect(screen.getByText("工资转币商")).toBeInTheDocument(); expect(screen.getByText("提现申请")).toBeInTheDocument(); expect(screen.getByText("Host")).toBeInTheDocument(); expect(screen.getByText("BD Leader/Admin")).toBeInTheDocument(); expect(screen.getByText("$123.45")).toBeInTheDocument(); expect(screen.getByText("1,112")).toBeInTheDocument(); expect(screen.getByText("已完成")).toBeInTheDocument(); expect(screen.getByText("待审核")).toBeInTheDocument(); expect(screen.getByText("USDT-TRC20")).toBeInTheDocument(); expect(screen.getByText("TRON9ADDRESS")).toBeInTheDocument(); expect(screen.getByText("tx-salary-1")).toBeInTheDocument(); expect(screen.getByText("9")).toBeInTheDocument(); }); test("host withdrawals filters are wired to the page hook", () => { vi.mocked(useHostWithdrawalsPage).mockReturnValue(pageFixture({ hasActiveFilters: true })); render(); const props = mocks.dataTable.mock.calls.at(-1)?.[0]; props.columns.find((column) => column.key === "recordType").filter.onChange("withdrawal"); props.columns.find((column) => column.key === "identity").filter.onChange("bd_leader_admin"); props.columns.find((column) => column.key === "sourceUser").filter.onChange("168557"); props.columns.find((column) => column.key === "status").filter.onChange("approved"); fireEvent.click(screen.getByRole("button", { name: "时间" })); fireEvent.click(screen.getByRole("button", { name: "重置" })); expect(mocks.changeRecordType).toHaveBeenCalledWith("withdrawal"); expect(mocks.changeIdentity).toHaveBeenCalledWith("bd_leader_admin"); expect(mocks.changeKeyword).toHaveBeenCalledWith("168557"); expect(mocks.changeStatus).toHaveBeenCalledWith("approved"); expect(mocks.changeTimeRange).toHaveBeenCalledWith({ endMs: "2000", startMs: "1000" }); expect(mocks.resetFilters).toHaveBeenCalled(); }); function pageFixture(patch = {}) { return { abilities: { canView: true }, changeIdentity: mocks.changeIdentity, changeKeyword: mocks.changeKeyword, changeRecordType: mocks.changeRecordType, changeStatus: mocks.changeStatus, changeTimeRange: mocks.changeTimeRange, data: { items: [salaryTransferFixture(), withdrawalFixture()], page: 1, pageSize: 50, total: 2, }, error: null, hasActiveFilters: false, identity: "", keyword: "", loading: false, page: 1, recordType: "", reload: vi.fn(), resetFilters: mocks.resetFilters, setPage: vi.fn(), status: "", timeRange: { endMs: "", startMs: "" }, ...patch, }; } function salaryTransferFixture() { return { coinAmount: 1112, commandId: "cmd-salary-1", createdAtMs: 1780000000000, id: "salary_transfer:101", identity: "host", recordType: "salary_transfer", salaryAssetType: "HOST_SALARY_USD", sellerUser: { displayUserId: "168556", userId: "3001", username: "Coin Seller", }, sellerUserId: "3001", sourceUser: { displayUserId: "168577", userId: "1001", username: "Raj", }, sourceUserId: "1001", status: "completed", transactionId: "tx-salary-1", updatedAtMs: 1780000000000, usdMinorAmount: 12345, }; } function withdrawalFixture() { return { applicationId: "9", coinAmount: 0, commandId: "freeze-cmd-9", createdAtMs: 1780000001000, freezeTransactionId: "freeze-tx-9", id: "withdrawal:9", identity: "bd_leader_admin", recordType: "withdrawal", salaryAssetType: "ADMIN_SALARY_USD", sourceUser: { displayUserId: "481479", userId: "2002", username: "Mao", }, sourceUserId: "2002", status: "pending", updatedAtMs: 1780000001000, usdMinorAmount: 5000, withdrawAddress: "TRON9ADDRESS", withdrawMethod: "USDT-TRC20", }; }