/* @vitest-environment jsdom */ import { fireEvent, render, screen, within } from '@testing-library/react'; import { useState } from 'react'; import { beforeEach, describe, expect, it, vi } from 'vitest'; import { PlatformEntryFlowShellImpl } from './PlatformEntryActiveFlowShell'; import type { SelectionStage } from './platformEntryActiveTypes'; const authUiMock = vi.hoisted(() => ({ value: { user: null, canAccessProtectedData: false, openLoginModal: vi.fn(), openAccountModal: vi.fn(), openSettingsModal: vi.fn(), }, })); const responsiveMock = vi.hoisted(() => ({ isDesktopLayout: true, })); vi.mock('../auth/AuthUiContext', () => ({ useAuthUi: () => authUiMock.value, })); vi.mock('./platformEntryResponsive', () => ({ usePlatformDesktopLayout: () => responsiveMock.isDesktopLayout, })); vi.mock('../creation-home/CreationLandingView', () => ({ CreationLandingView: ({ onOpenProject, onOpenProjects, searchKeyword, }: { onOpenProject: ( projectId: string, options?: { guide?: boolean; tool?: string }, ) => void; onOpenProjects: () => void; searchKeyword?: string; }) => (
创作主页
), })); vi.mock('../project/ProjectGalleryView', () => ({ ProjectGalleryView: ({ onOpenProject, searchKeyword, }: { onOpenProject: (projectId: string, options?: { guide?: boolean }) => void; searchKeyword?: string; }) => (
项目
), })); vi.mock('../image-editor/ImageCanvasEditorView', () => ({ ImageCanvasEditorView: () =>
, })); vi.mock('../../services/platform-entry/platformProfileClient', () => ({ getPlatformProfileDashboard: vi.fn(), })); vi.mock('./usePlatformProfileCenterController', () => ({ usePlatformProfileCenterController: () => ({ buyRechargeProduct: vi.fn(), closeNativeWechatPayment: vi.fn(), confirmNativeWechatPayment: vi.fn(), isLoadingRechargeCenter: false, isLoadingWalletLedger: false, isRechargeOpen: false, isWalletLedgerOpen: false, loadRechargeCenter: vi.fn(), nativeWechatPayment: null, openWalletLedgerPanel: vi.fn(), rechargeCenter: null, rechargeError: null, rechargePaymentResult: null, setIsRechargeOpen: vi.fn(), setIsWalletLedgerOpen: vi.fn(), setRechargePaymentResult: vi.fn(), submittingRechargeProductId: null, walletLedger: null, walletLedgerError: null, wechatRechargeOrderConfirmationState: null, }), })); function StatefulPlatformEntryFlowShell({ initialStage, }: { initialStage: SelectionStage; }) { const [selectionStage, setSelectionStage] = useState(initialStage); return ( setSelectionStage(nextStage)} /> ); } describe('PlatformEntryActiveFlowShell', () => { beforeEach(() => { window.history.replaceState(null, '', '/creation'); authUiMock.value.openLoginModal.mockReset(); responsiveMock.isDesktopLayout = true; }); it('keeps the active desktop rail and the shared account capsule', async () => { const setSelectionStage = vi.fn(); const { container, rerender } = render( , ); expect( await screen.findByRole('main', { name: '陶泥儿创作主页' }), ).toBeTruthy(); const navigation = screen.getByRole('navigation', { name: '平台导航' }); expect( within(navigation) .getAllByRole('button') .map((button) => button.getAttribute('aria-label')), ).toEqual(['创作', '项目', '我的']); expect( within(navigation) .getByRole('button', { name: '创作' }) .getAttribute('aria-current'), ).toBe('page'); const topbar = container.querySelector('.platform-desktop-topbar'); expect(topbar).toBeTruthy(); expect( topbar?.querySelectorAll('button.platform-desktop-search'), ).toHaveLength(1); expect( within(topbar as HTMLElement).queryByRole('button', { name: '设置' }), ).toBeNull(); fireEvent.change( screen.getByRole('searchbox', { name: '搜索项目和素材' }), { target: { value: '角色' } }, ); fireEvent.click(screen.getByRole('button', { name: '搜索' })); expect( screen .getByRole('main', { name: '陶泥儿创作主页' }) .getAttribute('data-search'), ).toBe('角色'); fireEvent.click(within(navigation).getByRole('button', { name: '我的' })); expect(window.location.pathname).toBe('/profile'); expect(setSelectionStage).toHaveBeenCalledWith('profile', { path: '/profile', }); rerender( , ); expect(await screen.findByRole('main', { name: '我的' })).toBeTruthy(); expect( within(navigation) .getByRole('button', { name: '我的' }) .getAttribute('aria-current'), ).toBe('page'); }); it('keeps only profile reachable from the mobile dock', async () => { responsiveMock.isDesktopLayout = false; const setSelectionStage = vi.fn(); render( , ); const navigation = await screen.findByRole('navigation', { name: '移动平台导航', }); expect( within(navigation) .getAllByRole('button') .map((button) => button.getAttribute('aria-label')), ).toEqual(['我的']); fireEvent.click(within(navigation).getByRole('button', { name: '我的' })); expect(window.location.pathname).toBe('/profile'); expect(setSelectionStage).toHaveBeenCalledWith('profile', { path: '/profile', }); }); it.each(['creation-home', 'project', 'image-editor'] as const)( 'shows the desktop guide instead of mounting the %s page on mobile', async (selectionStage) => { responsiveMock.isDesktopLayout = false; render( , ); 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(); }, ); it('shows the desktop guide instead of the editor after opening a canvas tool from the mobile root', async () => { responsiveMock.isDesktopLayout = false; render(); fireEvent.click( await screen.findByRole('button', { name: '打开音乐工具' }), ); expect( await screen.findByRole('main', { name: '桌面端创作提示' }), ).toBeTruthy(); expect( screen.queryByRole('main', { name: '图片画布编辑器' }), ).toBeNull(); }); it('switches between creation and projects and passes search to the active page', async () => { const setSelectionStage = vi.fn(); const { rerender } = render( , ); const navigation = await screen.findByRole('navigation', { name: '平台导航', }); fireEvent.click(within(navigation).getByRole('button', { name: '项目' })); expect(window.location.pathname).toBe('/project'); expect(setSelectionStage).toHaveBeenCalledWith('project', { path: '/project', }); rerender( , ); const projectPage = await screen.findByRole('main', { name: '项目' }); expect( within(navigation) .getByRole('button', { name: '项目' }) .getAttribute('aria-current'), ).toBe('page'); expect( within(navigation) .getByRole('button', { name: '创作' }) .getAttribute('aria-current'), ).toBeNull(); fireEvent.change( screen.getByRole('searchbox', { name: '搜索项目和素材' }), { target: { value: '场景' } }, ); fireEvent.click(screen.getByRole('button', { name: '搜索' })); expect(projectPage.getAttribute('data-search')).toBe('场景'); fireEvent.click(within(navigation).getByRole('button', { name: '创作' })); expect(window.location.pathname).toBe('/creation'); expect(setSelectionStage).toHaveBeenCalledWith('creation-home', { path: '/creation', }); }); it('keeps guide and tool intent in editor navigation URLs', async () => { const setSelectionStage = vi.fn(); const { rerender } = render( , ); fireEvent.click( await screen.findByRole('button', { name: '打开引导项目' }), ); expect(`${window.location.pathname}${window.location.search}`).toBe( '/editor/canvas?projectid=guide-project&guide=toolbar', ); expect(setSelectionStage).toHaveBeenCalledWith('image-editor', { path: '/editor/canvas?projectid=guide-project&guide=toolbar', }); window.history.replaceState(null, '', '/creation'); setSelectionStage.mockClear(); rerender( , ); fireEvent.click( await screen.findByRole('button', { name: '打开音乐工具' }), ); expect(`${window.location.pathname}${window.location.search}`).toBe( '/editor/canvas?projectid=tool-project&tool=background-music', ); expect(setSelectionStage).toHaveBeenCalledWith('image-editor', { path: '/editor/canvas?projectid=tool-project&tool=background-music', }); }); });