修复泥点消耗刷新不及时 (#138)
refactor: - 主站复用game agent的zustand store fix: - 修复旧请求覆盖问题 - 修复账号切换问题 - 主站原本已经每 4 秒轮询 external generation 任务状态, 在这里补充触发余额刷新的时机 --------- Co-authored-by: 段舒康 <kdletters@qq.com> Reviewed-on: http://192.168.35.82/git/GenarrativeAI/Genarrative/pulls/138 Co-authored-by: 王德宇 <kvtodev@outlook.com> Co-committed-by: 王德宇 <kvtodev@outlook.com>
This commit was merged in pull request #138.
This commit is contained in:
@@ -1,15 +1,27 @@
|
||||
/* @vitest-environment jsdom */
|
||||
|
||||
import { fireEvent, render, screen, within } from '@testing-library/react';
|
||||
import { useState } from 'react';
|
||||
import {
|
||||
act,
|
||||
fireEvent,
|
||||
render as testingLibraryRender,
|
||||
screen,
|
||||
waitFor,
|
||||
within,
|
||||
} from '@testing-library/react';
|
||||
import { type ReactElement, type ReactNode, useState } from 'react';
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import type { AuthUser } from '../../../packages/shared/src/contracts/auth';
|
||||
import {
|
||||
usePlatformWalletLifecycle,
|
||||
usePlatformWalletStore,
|
||||
} from '../../stores/usePlatformWalletStore';
|
||||
import { PlatformEntryFlowShellImpl } from './PlatformEntryActiveFlowShell';
|
||||
import type { SelectionStage } from './platformEntryActiveTypes';
|
||||
|
||||
const authUiMock = vi.hoisted(() => ({
|
||||
value: {
|
||||
user: null,
|
||||
user: null as AuthUser | null,
|
||||
canAccessProtectedData: false,
|
||||
openLoginModal: vi.fn(),
|
||||
openAccountModal: vi.fn(),
|
||||
@@ -21,6 +33,16 @@ const responsiveMock = vi.hoisted(() => ({
|
||||
isDesktopLayout: true,
|
||||
}));
|
||||
|
||||
const profileClientMock = vi.hoisted(() => ({
|
||||
getPlatformProfileDashboard: vi.fn(),
|
||||
getPlatformProfileRechargeCenter: vi.fn(),
|
||||
}));
|
||||
|
||||
const profileCenterMock = vi.hoisted(() => ({
|
||||
isWalletLedgerOpen: false,
|
||||
rechargeCenter: null as { walletBalance: number } | null,
|
||||
}));
|
||||
|
||||
vi.mock('../auth/AuthUiContext', () => ({
|
||||
useAuthUi: () => authUiMock.value,
|
||||
}));
|
||||
@@ -80,12 +102,22 @@ vi.mock('../project/ProjectGalleryView', () => ({
|
||||
}));
|
||||
|
||||
vi.mock('../image-editor/ImageCanvasEditorView', () => ({
|
||||
ImageCanvasEditorView: () => <main aria-label="图片画布编辑器" />,
|
||||
ImageCanvasEditorView: ({
|
||||
legacyWalletBalance,
|
||||
}: {
|
||||
legacyWalletBalance?: number | null;
|
||||
}) => (
|
||||
<main
|
||||
aria-label="图片画布编辑器"
|
||||
data-legacy-wallet-balance={legacyWalletBalance ?? ''}
|
||||
/>
|
||||
),
|
||||
}));
|
||||
|
||||
vi.mock('../../services/platform-entry/platformProfileClient', () => ({
|
||||
getPlatformProfileDashboard: vi.fn(),
|
||||
}));
|
||||
vi.mock(
|
||||
'../../services/platform-entry/platformProfileClient',
|
||||
() => profileClientMock,
|
||||
);
|
||||
|
||||
vi.mock('./usePlatformProfileCenterController', () => ({
|
||||
usePlatformProfileCenterController: () => ({
|
||||
@@ -95,11 +127,11 @@ vi.mock('./usePlatformProfileCenterController', () => ({
|
||||
isLoadingRechargeCenter: false,
|
||||
isLoadingWalletLedger: false,
|
||||
isRechargeOpen: false,
|
||||
isWalletLedgerOpen: false,
|
||||
isWalletLedgerOpen: profileCenterMock.isWalletLedgerOpen,
|
||||
loadRechargeCenter: vi.fn(),
|
||||
nativeWechatPayment: null,
|
||||
openWalletLedgerPanel: vi.fn(),
|
||||
rechargeCenter: null,
|
||||
rechargeCenter: profileCenterMock.rechargeCenter,
|
||||
rechargeError: null,
|
||||
rechargePaymentResult: null,
|
||||
setIsRechargeOpen: vi.fn(),
|
||||
@@ -112,6 +144,24 @@ vi.mock('./usePlatformProfileCenterController', () => ({
|
||||
}),
|
||||
}));
|
||||
|
||||
function AuthWalletLifecycleTestBoundary({
|
||||
children,
|
||||
}: {
|
||||
children: ReactNode;
|
||||
}) {
|
||||
usePlatformWalletLifecycle(
|
||||
authUiMock.value.user?.id ?? null,
|
||||
authUiMock.value.canAccessProtectedData,
|
||||
);
|
||||
return children;
|
||||
}
|
||||
|
||||
function render(ui: ReactElement) {
|
||||
return testingLibraryRender(ui, {
|
||||
wrapper: AuthWalletLifecycleTestBoundary,
|
||||
});
|
||||
}
|
||||
|
||||
function StatefulPlatformEntryFlowShell({
|
||||
initialStage,
|
||||
}: {
|
||||
@@ -130,10 +180,225 @@ function StatefulPlatformEntryFlowShell({
|
||||
describe('PlatformEntryActiveFlowShell', () => {
|
||||
beforeEach(() => {
|
||||
window.history.replaceState(null, '', '/creation');
|
||||
usePlatformWalletStore.getState().resetWalletBalance();
|
||||
profileClientMock.getPlatformProfileDashboard.mockReset();
|
||||
profileClientMock.getPlatformProfileRechargeCenter.mockReset();
|
||||
profileCenterMock.isWalletLedgerOpen = false;
|
||||
profileCenterMock.rechargeCenter = null;
|
||||
authUiMock.value.user = null;
|
||||
authUiMock.value.canAccessProtectedData = false;
|
||||
authUiMock.value.openLoginModal.mockReset();
|
||||
responsiveMock.isDesktopLayout = true;
|
||||
});
|
||||
|
||||
it('uses one recharge-center snapshot for the topbar and profile wallet when dashboard differs', async () => {
|
||||
authUiMock.value.user = {
|
||||
id: 'user-1',
|
||||
publicUserCode: '100001',
|
||||
displayName: '测试用户',
|
||||
avatarUrl: null,
|
||||
phoneNumberMasked: null,
|
||||
loginMethod: 'password',
|
||||
bindingStatus: 'active',
|
||||
wechatBound: false,
|
||||
};
|
||||
authUiMock.value.canAccessProtectedData = true;
|
||||
profileClientMock.getPlatformProfileDashboard.mockResolvedValue({
|
||||
walletBalance: 999,
|
||||
totalPlayTimeMs: 0,
|
||||
playedWorldCount: 0,
|
||||
updatedAt: null,
|
||||
});
|
||||
profileClientMock.getPlatformProfileRechargeCenter.mockResolvedValue({
|
||||
walletBalance: 888,
|
||||
mudPointBalance: {
|
||||
totalPoints: 207,
|
||||
permanentPoints: 180,
|
||||
limitedPoints: 7,
|
||||
limitedExpiresAt: '2026-08-31T16:00:00Z',
|
||||
dailyFreePoints: 20,
|
||||
dailyFreeResetPoints: 20,
|
||||
dailyFreeResetsAt: '2026-08-04T00:00:00+08:00',
|
||||
},
|
||||
});
|
||||
|
||||
const { rerender } = render(
|
||||
<PlatformEntryFlowShellImpl
|
||||
selectionStage="creation-home"
|
||||
setSelectionStage={vi.fn()}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(await screen.findByLabelText('泥点 207')).toBeTruthy();
|
||||
|
||||
rerender(
|
||||
<PlatformEntryFlowShellImpl
|
||||
selectionStage="profile"
|
||||
setSelectionStage={vi.fn()}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(
|
||||
await screen.findByRole('button', { name: '泥点余额 207' }),
|
||||
).toBeTruthy();
|
||||
expect(screen.queryByText('999')).toBeNull();
|
||||
});
|
||||
|
||||
it('passes the lifecycle legacy total to profile and editor without opening recharge', async () => {
|
||||
authUiMock.value.user = {
|
||||
id: 'user-1',
|
||||
publicUserCode: '100001',
|
||||
displayName: '测试用户',
|
||||
avatarUrl: null,
|
||||
phoneNumberMasked: null,
|
||||
loginMethod: 'password',
|
||||
bindingStatus: 'active',
|
||||
wechatBound: false,
|
||||
};
|
||||
authUiMock.value.canAccessProtectedData = true;
|
||||
profileClientMock.getPlatformProfileDashboard.mockResolvedValue(null);
|
||||
profileClientMock.getPlatformProfileRechargeCenter.mockResolvedValue({
|
||||
walletBalance: 37,
|
||||
});
|
||||
profileCenterMock.isWalletLedgerOpen = true;
|
||||
|
||||
const { rerender } = render(
|
||||
<PlatformEntryFlowShellImpl
|
||||
selectionStage="profile"
|
||||
setSelectionStage={vi.fn()}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(
|
||||
await screen.findByRole('button', { name: '泥点余额 37' }),
|
||||
).toBeTruthy();
|
||||
expect(await screen.findByText('37泥点')).toBeTruthy();
|
||||
expect(screen.getByText('暂无账单记录')).toBeTruthy();
|
||||
|
||||
rerender(
|
||||
<PlatformEntryFlowShellImpl
|
||||
selectionStage="image-editor"
|
||||
setSelectionStage={vi.fn()}
|
||||
/>,
|
||||
);
|
||||
const imageEditor = await screen.findByRole('main', {
|
||||
name: '图片画布编辑器',
|
||||
});
|
||||
expect(imageEditor.getAttribute('data-legacy-wallet-balance')).toBe('37');
|
||||
});
|
||||
|
||||
it('clears the previous wallet synchronously when the authenticated account changes', async () => {
|
||||
authUiMock.value.user = {
|
||||
id: 'user-1',
|
||||
publicUserCode: '100001',
|
||||
displayName: '用户一',
|
||||
avatarUrl: null,
|
||||
phoneNumberMasked: null,
|
||||
loginMethod: 'password',
|
||||
bindingStatus: 'active',
|
||||
wechatBound: false,
|
||||
};
|
||||
authUiMock.value.canAccessProtectedData = true;
|
||||
profileClientMock.getPlatformProfileDashboard.mockResolvedValue(null);
|
||||
profileClientMock.getPlatformProfileRechargeCenter.mockResolvedValue({
|
||||
mudPointBalance: {
|
||||
totalPoints: 66,
|
||||
permanentPoints: 66,
|
||||
limitedPoints: 0,
|
||||
limitedExpiresAt: null,
|
||||
dailyFreePoints: 0,
|
||||
dailyFreeResetPoints: 20,
|
||||
dailyFreeResetsAt: '2026-08-04T00:00:00+08:00',
|
||||
},
|
||||
});
|
||||
const { rerender } = render(
|
||||
<PlatformEntryFlowShellImpl
|
||||
selectionStage="creation-home"
|
||||
setSelectionStage={vi.fn()}
|
||||
/>,
|
||||
);
|
||||
await screen.findByLabelText('泥点 66');
|
||||
|
||||
let resolveNextOwner!: (value: unknown) => void;
|
||||
profileClientMock.getPlatformProfileRechargeCenter.mockImplementation(
|
||||
() =>
|
||||
new Promise((resolve) => {
|
||||
resolveNextOwner = resolve;
|
||||
}),
|
||||
);
|
||||
authUiMock.value.user = { ...authUiMock.value.user, id: 'user-2' };
|
||||
rerender(
|
||||
<PlatformEntryFlowShellImpl
|
||||
selectionStage="creation-home"
|
||||
setSelectionStage={vi.fn()}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(screen.queryByLabelText('泥点 66')).toBeNull();
|
||||
await waitFor(() => {
|
||||
expect(usePlatformWalletStore.getState()).toMatchObject({
|
||||
ownerUserId: 'user-2',
|
||||
mudPointBalance: null,
|
||||
});
|
||||
});
|
||||
resolveNextOwner({
|
||||
mudPointBalance: {
|
||||
totalPoints: 67,
|
||||
permanentPoints: 67,
|
||||
limitedPoints: 0,
|
||||
limitedExpiresAt: null,
|
||||
dailyFreePoints: 0,
|
||||
dailyFreeResetPoints: 20,
|
||||
dailyFreeResetsAt: '2026-08-04T00:00:00+08:00',
|
||||
},
|
||||
});
|
||||
await screen.findByLabelText('泥点 67');
|
||||
});
|
||||
|
||||
it('coalesces visibility and focus refreshes when the page returns to the foreground', async () => {
|
||||
authUiMock.value.user = {
|
||||
id: 'user-1',
|
||||
publicUserCode: '100001',
|
||||
displayName: '测试用户',
|
||||
avatarUrl: null,
|
||||
phoneNumberMasked: null,
|
||||
loginMethod: 'password',
|
||||
bindingStatus: 'active',
|
||||
wechatBound: false,
|
||||
};
|
||||
authUiMock.value.canAccessProtectedData = true;
|
||||
profileClientMock.getPlatformProfileDashboard.mockResolvedValue(null);
|
||||
profileClientMock.getPlatformProfileRechargeCenter.mockResolvedValue({
|
||||
mudPointBalance: {
|
||||
totalPoints: 10,
|
||||
permanentPoints: 10,
|
||||
limitedPoints: 0,
|
||||
limitedExpiresAt: null,
|
||||
dailyFreePoints: 0,
|
||||
dailyFreeResetPoints: 20,
|
||||
dailyFreeResetsAt: '2026-08-04T00:00:00+08:00',
|
||||
},
|
||||
});
|
||||
render(
|
||||
<PlatformEntryFlowShellImpl
|
||||
selectionStage="creation-home"
|
||||
setSelectionStage={vi.fn()}
|
||||
/>,
|
||||
);
|
||||
await screen.findByLabelText('泥点 10');
|
||||
|
||||
await act(async () => {
|
||||
document.dispatchEvent(new Event('visibilitychange'));
|
||||
window.dispatchEvent(new Event('focus'));
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(
|
||||
profileClientMock.getPlatformProfileRechargeCenter,
|
||||
).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
});
|
||||
|
||||
it('keeps the active desktop rail and the shared account capsule', async () => {
|
||||
const setSelectionStage = vi.fn();
|
||||
const { container, rerender } = render(
|
||||
@@ -241,13 +506,9 @@ describe('PlatformEntryActiveFlowShell', () => {
|
||||
expect(
|
||||
await screen.findByRole('main', { name: '桌面端创作提示' }),
|
||||
).toBeTruthy();
|
||||
expect(
|
||||
screen.queryByRole('main', { name: '陶泥儿创作主页' }),
|
||||
).toBeNull();
|
||||
expect(screen.queryByRole('main', { name: '陶泥儿创作主页' })).toBeNull();
|
||||
expect(screen.queryByRole('main', { name: '项目' })).toBeNull();
|
||||
expect(
|
||||
screen.queryByRole('main', { name: '图片画布编辑器' }),
|
||||
).toBeNull();
|
||||
expect(screen.queryByRole('main', { name: '图片画布编辑器' })).toBeNull();
|
||||
},
|
||||
);
|
||||
|
||||
@@ -263,9 +524,7 @@ describe('PlatformEntryActiveFlowShell', () => {
|
||||
expect(
|
||||
await screen.findByRole('main', { name: '桌面端创作提示' }),
|
||||
).toBeTruthy();
|
||||
expect(
|
||||
screen.queryByRole('main', { name: '图片画布编辑器' }),
|
||||
).toBeNull();
|
||||
expect(screen.queryByRole('main', { name: '图片画布编辑器' })).toBeNull();
|
||||
});
|
||||
|
||||
it('switches between creation and projects and passes search to the active page', async () => {
|
||||
|
||||
Reference in New Issue
Block a user