合并主分支

解决合并冲突
This commit is contained in:
2026-08-08 10:47:31 +08:00
140 changed files with 11920 additions and 2046 deletions
@@ -12,6 +12,7 @@ import userEvent from '@testing-library/user-event';
import JSZip from 'jszip';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { usePlatformWalletStore } from '../../stores/usePlatformWalletStore';
import type { EditorAgentConversationClient } from './EditorAgentConversation/useEditorAgentConversation';
import {
ApiClientError,
@@ -294,6 +295,7 @@ describe('ImageCanvasEditorView', () => {
});
beforeEach(() => {
usePlatformWalletStore.getState().resetWalletBalance();
loadFrontendRuntimeConfigMock.mockImplementation(() =>
immediateAsync({
imageEditorAgentSidebarEnabled: false,
@@ -532,6 +534,37 @@ describe('ImageCanvasEditorView', () => {
expect(window.location.search).toBe('?projectid=editor-project-music');
});
it('opens the scene startup tool with scene defaults', async () => {
loadEditorProjectMock.mockResolvedValueOnce(
withEditorProjectCanvasRevision({
projectId: 'editor-project-scene',
title: '场景项目',
viewport: { x: 0, y: 0, scale: 1 },
layers: [],
resources: [],
updatedAt: '2026-08-04T00:00:00.000Z',
}),
);
window.history.replaceState(
null,
'',
'/editor/canvas?projectid=editor-project-scene&tool=scene',
);
render(<ImageCanvasEditorView />);
const dialog = await screen.findByRole('dialog', {
name: '游戏场景生成器',
});
expect(
within(dialog).getByRole('button', { name: '游戏场景尺寸 16:9·1K' }),
).toBeTruthy();
expect(
within(dialog).getByRole('button', { name: '画风预设 日系动画' }),
).toBeTruthy();
expect(window.location.search).toBe('?projectid=editor-project-scene');
});
it('flushes project persistence before returning from the topbar', async () => {
const onPopState = vi.fn();
window.addEventListener('popstate', onPopState);
@@ -593,6 +626,21 @@ describe('ImageCanvasEditorView', () => {
});
it('shows the live mud point balance in the canvas topbar when logged in', async () => {
usePlatformWalletStore.getState().setWalletOwner('user-1');
const walletSnapshot = usePlatformWalletStore
.getState()
.captureWalletBalanceSnapshot('user-1');
usePlatformWalletStore
.getState()
.applyWalletBalanceSnapshot(walletSnapshot!, {
totalPoints: 1234,
permanentPoints: 1000,
limitedPoints: 214,
limitedExpiresAt: '2026-07-31T16:00:00Z',
dailyFreePoints: 20,
dailyFreeResetPoints: 20,
dailyFreeResetsAt: '2026-07-12T16:00:00Z',
});
render(
<AuthUiContext.Provider
value={createAuthValue({
@@ -614,12 +662,7 @@ describe('ImageCanvasEditorView', () => {
);
expect(await screen.findByLabelText('泥点 1,234')).toBeTruthy();
expect(getPlatformProfileDashboardMock).toHaveBeenCalledWith({
authImpact: 'local',
skipRefresh: true,
notifyAuthStateChange: false,
clearAuthOnUnauthorized: false,
});
expect(getPlatformProfileDashboardMock).not.toHaveBeenCalled();
});
it('opens the account modal from the canvas topbar avatar entry', async () => {
@@ -658,8 +701,97 @@ describe('ImageCanvasEditorView', () => {
expect(openAccountModal).toHaveBeenCalledTimes(1);
});
it('shows the owner-matched legacy wallet total without inventing breakdown rows', async () => {
const user = userEvent.setup();
usePlatformWalletStore.getState().setWalletOwner('user-1');
const walletSnapshot = usePlatformWalletStore
.getState()
.captureWalletBalanceSnapshot('user-1');
usePlatformWalletStore
.getState()
.applyLegacyWalletBalanceSnapshot(walletSnapshot!, 37);
getPlatformProfileRechargeCenterMock.mockResolvedValue({
walletBalance: 37,
});
render(
<AuthUiContext.Provider
value={createAuthValue({
user: {
id: 'user-1',
publicUserCode: 'U001',
displayName: '测试用户',
avatarUrl: null,
phoneNumberMasked: '138****0000',
loginMethod: 'password',
bindingStatus: 'active',
wechatBound: false,
},
canAccessProtectedData: true,
})}
>
<ImageCanvasEditorView />
</AuthUiContext.Provider>,
);
const walletButton = await screen.findByRole('button', {
name: '泥点 37',
});
await user.hover(walletButton);
const details = await screen.findByRole('dialog', {
name: '泥点账户详情',
});
expect(within(details).getByText('泥点明细读取失败')).toBeTruthy();
expect(within(details).queryByText('充值中心响应缺少泥点余额')).toBeNull();
expect(within(details).queryByText('不限时泥点')).toBeNull();
expect(within(details).queryByText('每日免费泥点')).toBeNull();
});
it('keeps the wallet entry loading while the authenticated owner is not bound yet', async () => {
render(
<AuthUiContext.Provider
value={createAuthValue({
user: {
id: 'user-1',
publicUserCode: 'U001',
displayName: '测试用户',
avatarUrl: null,
phoneNumberMasked: '138****0000',
loginMethod: 'password',
bindingStatus: 'active',
wechatBound: false,
},
canAccessProtectedData: true,
})}
>
<ImageCanvasEditorView />
</AuthUiContext.Provider>,
);
expect(
(await screen.findByRole('button', { name: '泥点 --' })).getAttribute(
'aria-busy',
),
).toBe('true');
});
it('opens the shared wallet breakdown and ledger from the canvas topbar', async () => {
const user = userEvent.setup();
usePlatformWalletStore.getState().setWalletOwner('user-1');
const walletSnapshot = usePlatformWalletStore
.getState()
.captureWalletBalanceSnapshot('user-1');
usePlatformWalletStore
.getState()
.applyWalletBalanceSnapshot(walletSnapshot!, {
totalPoints: 1234,
permanentPoints: 1000,
limitedPoints: 0,
limitedExpiresAt: null,
dailyFreePoints: 234,
dailyFreeResetPoints: 20,
dailyFreeResetsAt: '2026-07-12T16:00:00Z',
});
render(
<AuthUiContext.Provider
value={createAuthValue({