import { act } from "react"; import { createRoot, type Root } from "react-dom/client"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import type { PayChannel } from "@/data/schemas/payment"; import type { PaymentMethodConfig } from "@/lib/payment/payment_method"; import { usePaymentMethodSelection } from "../use-payment-method-selection"; describe("usePaymentMethodSelection", () => { let container: HTMLDivElement; let root: Root; beforeEach(() => { (globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }) .IS_REACT_ACT_ENVIRONMENT = true; container = document.createElement("div"); document.body.appendChild(container); root = createRoot(container); }); afterEach(() => { act(() => root.unmount()); container.remove(); }); it("forces Stripe when payment method selection is unavailable", () => { const onChange = vi.fn(); renderHarness(root, { config: { canChoosePaymentMethod: false, initialPayChannel: "stripe", showPaymentMethodSelector: false, }, currentPayChannel: "ezpay", onChange, }); expect(onChange).toHaveBeenCalledWith("stripe"); }); it("applies the default channel once when no return channel was requested", () => { const onChange = vi.fn(); const config = { canChoosePaymentMethod: true, initialPayChannel: "ezpay", showPaymentMethodSelector: true, } as const; renderHarness(root, { config, currentPayChannel: "stripe", onChange, }); renderHarness(root, { config, currentPayChannel: "ezpay", onChange, }); expect(onChange).toHaveBeenCalledTimes(1); expect(onChange).toHaveBeenCalledWith("ezpay"); }); it("preserves an explicit return channel and pauses while payment is busy", () => { const onChange = vi.fn(); renderHarness(root, { config: { canChoosePaymentMethod: true, initialPayChannel: "ezpay", showPaymentMethodSelector: true, }, currentPayChannel: "stripe", requestedPayChannel: "stripe", onChange, }); renderHarness(root, { config: { canChoosePaymentMethod: false, initialPayChannel: "stripe", showPaymentMethodSelector: false, }, currentPayChannel: "ezpay", isPaymentBusy: true, onChange, }); expect(onChange).not.toHaveBeenCalled(); }); }); interface HarnessProps { config: PaymentMethodConfig; currentPayChannel: PayChannel; requestedPayChannel?: PayChannel | null; isPaymentBusy?: boolean; onChange: (payChannel: PayChannel) => void; } function renderHarness(root: Root, props: HarnessProps): void { act(() => { root.render(); }); } function Harness({ config, currentPayChannel, requestedPayChannel = null, isPaymentBusy = false, onChange, }: HarnessProps) { usePaymentMethodSelection({ config, currentPayChannel, requestedPayChannel, isPaymentBusy, onChange, }); return null; }