fix(private-room): preserve payment return route

This commit is contained in:
2026-07-16 18:06:15 +08:00
parent b64a90f0e3
commit 4981de9b18
17 changed files with 280 additions and 21 deletions
@@ -0,0 +1,112 @@
import { act, type Dispatch } from "react";
import { createRoot, type Root } from "react-dom/client";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import type { LoginStatus } from "@/data/dto/auth";
import { ROUTES } from "@/router/routes";
import type { PrivateRoomEvent } from "@/stores/private-room";
import type { PrivateRoomUnlockPaywallRequest } from "@/stores/private-room/private-room-state";
import { usePrivateRoomUnlockPaywallNavigation } from "../use-private-room-flow";
const mocks = vi.hoisted(() => ({
openAuth: vi.fn(),
openSubscription: vi.fn(),
paywallShown: vi.fn(),
}));
vi.mock("@/router/use-app-navigator", () => ({
useAppNavigator: () => ({
openAuth: mocks.openAuth,
openSubscription: mocks.openSubscription,
}),
}));
vi.mock("@/lib/analytics", () => ({
behaviorAnalytics: {
paywallShown: mocks.paywallShown,
},
}));
const unlockPaywallRequest: PrivateRoomUnlockPaywallRequest = {
albumId: "album-1",
reason: "insufficient_credits",
requiredCredits: 10,
currentCredits: 3,
shortfallCredits: 7,
};
describe("Private Room paywall navigation", () => {
let container: HTMLDivElement;
let root: Root;
beforeEach(() => {
(globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean })
.IS_REACT_ACT_ENVIRONMENT = true;
mocks.openAuth.mockReset();
mocks.openSubscription.mockReset();
mocks.paywallShown.mockReset();
container = document.createElement("div");
document.body.appendChild(container);
root = createRoot(container);
});
afterEach(() => {
act(() => root.unmount());
container.remove();
});
it("opens top up with a private room return target", () => {
const roomDispatch = vi.fn<Dispatch<PrivateRoomEvent>>();
renderHarness(root, "email", roomDispatch);
expect(mocks.openSubscription).toHaveBeenCalledWith({
type: "topup",
returnTo: "private-room",
analytics: {
entryPoint: "private_album_unlock",
triggerReason: "insufficient_credits",
},
});
expect(roomDispatch).toHaveBeenCalledWith({
type: "PrivateRoomUnlockPaywallConsumed",
});
});
it("keeps guest users on the private room auth return path", () => {
const roomDispatch = vi.fn<Dispatch<PrivateRoomEvent>>();
renderHarness(root, "guest", roomDispatch);
expect(mocks.openAuth).toHaveBeenCalledWith(ROUTES.privateRoom);
expect(mocks.openSubscription).not.toHaveBeenCalled();
});
});
function renderHarness(
root: Root,
loginStatus: LoginStatus,
roomDispatch: Dispatch<PrivateRoomEvent>,
): void {
act(() => {
root.render(
<Harness loginStatus={loginStatus} roomDispatch={roomDispatch} />,
);
});
}
function Harness({
loginStatus,
roomDispatch,
}: {
loginStatus: LoginStatus;
roomDispatch: Dispatch<PrivateRoomEvent>;
}) {
usePrivateRoomUnlockPaywallNavigation({
loginStatus,
roomDispatch,
unlockPaywallRequest,
});
return null;
}
@@ -111,6 +111,7 @@ export function PrivateRoomScreen() {
}
navigator.openSubscription({
type: "topup",
returnTo: "private-room",
analytics: {
entryPoint: "private_room",
triggerReason: "vip_cta",
@@ -91,6 +91,7 @@ export function usePrivateRoomUnlockPaywallNavigation({
});
navigator.openSubscription({
type: "topup",
returnTo: "private-room",
analytics: {
entryPoint: "private_album_unlock",
triggerReason: "insufficient_credits",