import { render, screen, waitFor } from "@testing-library/react"; import { afterEach, beforeEach, expect, test, vi } from "vitest"; import { ToastProvider } from "@/shared/ui/ToastProvider.jsx"; import { FinanceApp } from "./FinanceApp.jsx"; import { fetchFinanceOptions, fetchFinanceRechargeApps, fetchFinanceRechargeOverview, fetchFinanceRechargeSummaries, fetchFinanceSession, listFinanceCoinSellerRechargeOrders, listFinanceRechargeDetails, listFinanceRechargeRegions, listFinanceWithdrawalApplications, } from "./api.js"; import { EMPTY_RECHARGE_OVERVIEW, EMPTY_RECHARGE_SUMMARY } from "./format.js"; vi.mock("./api.js", () => ({ approveFinanceWithdrawalApplication: vi.fn(), createFinanceCoinSellerRechargeOrder: vi.fn(), exportFinanceRechargeBills: vi.fn(), fetchFinanceOptions: vi.fn(), fetchFinanceRechargeApps: vi.fn(), fetchFinanceRechargeOverview: vi.fn(), fetchFinanceRechargeSummaries: vi.fn(), fetchFinanceSession: vi.fn(), grantFinanceCoinSellerRechargeOrder: vi.fn(), getFinanceCoinSellerRechargeExchangeRate: vi.fn(), getFinanceUSDTAddresses: vi.fn(), listFinanceCoinSellerRechargeOrders: vi.fn(), listFinanceRechargeDetails: vi.fn(), listFinanceRechargeRegions: vi.fn(), listFinanceWithdrawalApplications: vi.fn(), rejectFinanceWithdrawalApplication: vi.fn(), refreshFinanceGoogleRechargePaid: vi.fn(), replaceFinanceCoinSellerRechargeExchangeRate: vi.fn(), upsertFinanceUSDTAddress: vi.fn(), quoteFinanceCoinSellerRechargeOrder: vi.fn(), verifyFinanceCoinSellerRechargeOrder: vi.fn(), verifyFinanceCoinSellerRechargeReceipt: vi.fn() })); const defaultSession = { canAccessFinance: true, canAuditWithdrawalApplications: false, canConfigureCoinSellerRechargeExchangeRate: false, canCreateCoinSellerRechargeOrder: false, canGrantCoinSellerRechargeOrder: false, canUpdateUSDTAddress: false, canVerifyCoinSellerRechargeOrder: false, canViewAppRechargeDetails: false, canViewCoinSellerRechargeOrders: false, canViewWithdrawalApplications: true, permissions: ["finance-withdrawal:view"], user: { account: "admin", name: "admin" } }; beforeEach(() => { vi.mocked(fetchFinanceOptions).mockResolvedValue({ apps: [{ appCode: "lalu", appName: "lalu" }] }); vi.mocked(fetchFinanceRechargeApps).mockResolvedValue([{ appCode: "lalu", appName: "Lalu" }]); vi.mocked(fetchFinanceRechargeOverview).mockResolvedValue(EMPTY_RECHARGE_OVERVIEW); vi.mocked(fetchFinanceRechargeSummaries).mockResolvedValue({ merged: EMPTY_RECHARGE_SUMMARY, perApp: [] }); vi.mocked(fetchFinanceSession).mockResolvedValue(defaultSession); vi.mocked(listFinanceCoinSellerRechargeOrders).mockResolvedValue({ items: [], page: 1, pageSize: 50, total: 0 }); vi.mocked(listFinanceRechargeDetails).mockResolvedValue({ items: [], page: 1, pageSize: 50, total: 0 }); vi.mocked(listFinanceRechargeRegions).mockResolvedValue([]); vi.mocked(listFinanceWithdrawalApplications).mockResolvedValue({ items: [], page: 1, pageSize: 50, total: 0 }); }); afterEach(() => { vi.restoreAllMocks(); vi.clearAllMocks(); window.history.pushState(null, "", "/"); }); test("preserves the finance entry when the expired session asks the user to log in again", async () => { vi.mocked(fetchFinanceSession).mockRejectedValueOnce(new Error("登录已失效")); window.history.pushState(null, "", "/finance/?view=rechargeDetails"); render( ); expect(await screen.findByRole("link", { name: "重新登录" })).toHaveAttribute( "href", "/login?redirect=%2Ffinance%2F%3Fview%3DrechargeDetails" ); }); test("defaults recharge finance views to yesterday in China timezone", async () => { vi.spyOn(Date, "now").mockReturnValue(Date.UTC(2026, 6, 8, 4)); vi.mocked(fetchFinanceSession).mockResolvedValue({ ...defaultSession, canViewAppRechargeDetails: true, permissions: ["finance-recharge-detail:view"] }); render( ); const yesterdayButton = await screen.findByRole("button", { name: "昨天" }); expect(yesterdayButton).toHaveClass("is-on"); expect(screen.getByRole("button", { name: "今天" })).toBeInTheDocument(); await waitFor(() => expect(listFinanceRechargeDetails).toHaveBeenCalled()); const [query] = vi.mocked(listFinanceRechargeDetails).mock.calls.at(-1); expect(query.start_at_ms).toBe(Date.UTC(2026, 6, 6, 16)); expect(query.end_at_ms).toBe(Date.UTC(2026, 6, 7, 16)); });