From 9af13974db3d8785d3e8870f98337b7f20ffc1ba Mon Sep 17 00:00:00 2001 From: AIGameCreator App Date: Sun, 28 Jun 2026 04:53:35 +0800 Subject: [PATCH] =?UTF-8?q?=E6=94=AF=E6=8C=81=E5=B9=B3=E5=8F=B0=E5=85=A5?= =?UTF-8?q?=E5=8F=A3=E5=88=9D=E5=A7=8B=E9=A1=B5=E7=AD=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 为平台入口透传 initialPlatformTab 参数 避免移动端隐藏页签覆盖显式创作页签 更新 RPG 入口交互测试的创作页签起点 --- .../PlatformEntryFlowShellImpl.tsx | 6 +- .../platform-entry/platformEntryTypes.ts | 8 +++ ...gEntryFlowShell.agent.interaction.test.tsx | 61 +++++++++---------- src/components/rpg-entry/RpgEntryHomeView.tsx | 8 +-- .../rpg-entry/useRpgEntryBootstrap.ts | 8 ++- 5 files changed, 49 insertions(+), 42 deletions(-) diff --git a/src/components/platform-entry/PlatformEntryFlowShellImpl.tsx b/src/components/platform-entry/PlatformEntryFlowShellImpl.tsx index 75a224df3..491a087c9 100644 --- a/src/components/platform-entry/PlatformEntryFlowShellImpl.tsx +++ b/src/components/platform-entry/PlatformEntryFlowShellImpl.tsx @@ -1697,6 +1697,7 @@ export function PlatformEntryFlowShellImpl({ handleStartNewGame, handleCustomWorldSelect, initialPublicWorkCode, + initialPlatformTab, }: PlatformEntryFlowShellProps) { const authUi = useAuthUi(); const platformThemeClass = @@ -2510,6 +2511,7 @@ export function PlatformEntryFlowShellImpl({ getProfileDashboard: getPlatformProfileDashboardWithLocalWalletDelta, handleContinueGame, hasInitialAgentSession, + initialPlatformTab, }); const { canReadProtectedData: canRefreshPlatformDashboard, @@ -15512,7 +15514,9 @@ export function PlatformEntryFlowShellImpl({ } activeDesktopNavTab={selectionStage === 'project' ? 'projects' : undefined} allowHiddenActiveTab={ - selectionStage === 'creation-home' || selectionStage === 'project' + initialPlatformTab === 'create' || + selectionStage === 'creation-home' || + selectionStage === 'project' } onTabChange={handlePlatformHomeTabChange} hasSavedGame={hasSavedGame} diff --git a/src/components/platform-entry/platformEntryTypes.ts b/src/components/platform-entry/platformEntryTypes.ts index 6e678d9fa..6e1deb91f 100644 --- a/src/components/platform-entry/platformEntryTypes.ts +++ b/src/components/platform-entry/platformEntryTypes.ts @@ -5,6 +5,13 @@ import type { CustomWorldProfile } from '../../types'; export type CustomWorldRuntimeLaunchMode = 'play'; +export type PlatformHomeTab = + | 'home' + | 'category' + | 'create' + | 'saves' + | 'profile'; + export type CustomWorldRuntimeLaunchOptions = { mode?: CustomWorldRuntimeLaunchMode; disablePersistence?: boolean; @@ -93,6 +100,7 @@ export type PlatformEntryFlowShellProps = { initialPublicWorkCode?: string | null; hasSavedGame: boolean; savedSnapshot: HydratedSavedGameSnapshot | null; + initialPlatformTab?: PlatformHomeTab; handleContinueGame: (snapshot?: HydratedSavedGameSnapshot | null) => void; handleStartNewGame: () => void; handleCustomWorldSelect: ( diff --git a/src/components/rpg-entry/RpgEntryFlowShell.agent.interaction.test.tsx b/src/components/rpg-entry/RpgEntryFlowShell.agent.interaction.test.tsx index 54db12230..7ee66d037 100644 --- a/src/components/rpg-entry/RpgEntryFlowShell.agent.interaction.test.tsx +++ b/src/components/rpg-entry/RpgEntryFlowShell.agent.interaction.test.tsx @@ -261,8 +261,11 @@ async function clickFirstAsyncButtonByName( } async function openCreateTemplateHub(user: ReturnType) { - await clickFirstButtonByName(user, '创作'); - const panel = getPlatformTabPanel('create'); + let panel = await findPlatformTabPanel('create'); + if (panel.getAttribute('aria-hidden') !== 'false') { + await clickFirstButtonByName(user, '创作'); + panel = await findPlatformTabPanel('create'); + } await waitFor(() => { expect(panel.getAttribute('aria-hidden')).toBe('false'); }); @@ -2637,11 +2640,13 @@ function mockDesktopPlatformLayout() { function TestWrapper({ withAuth = false, authValue, + initialPlatformTab, onContinueGame, onSelectWorld, }: { withAuth?: boolean; authValue?: TestAuthValue; + initialPlatformTab?: RpgEntryFlowShellProps['initialPlatformTab']; onContinueGame?: (snapshot?: HydratedSavedGameSnapshot | null) => void; onSelectWorld?: RpgEntryFlowShellProps['handleCustomWorldSelect']; } = {}) { @@ -2657,6 +2662,7 @@ function TestWrapper({ selectionStage={selectionStage} setSelectionStage={setSelectionStage} initialPublicWorkCode={initialPublicWorkCode} + initialPlatformTab={initialPlatformTab} hasSavedGame={false} savedSnapshot={null} handleContinueGame={onContinueGame ?? (() => {})} @@ -3942,9 +3948,8 @@ test('create tab shows recent template cards when backend returns failed drafts' updatedAt: new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString(), }); - render(); - await clickFirstButtonByName(user, '创作'); - const panel = getPlatformTabPanel('create'); + render(); + const panel = await findPlatformTabPanel('create'); const tablist = await within(panel).findByRole('tablist', { name: '创作入口页签', @@ -3980,7 +3985,7 @@ test('create tab refreshes recent works after opening from an empty draft shelf' .mockResolvedValueOnce([]) .mockResolvedValue([failedDraft]); - render(); + render(); await openDraftHub(user); expect( @@ -6272,8 +6277,9 @@ test('completed baby object match draft viewed immediately does not keep unread expect(screen.queryByLabelText('新生成完成')).toBeNull(); }); -test('completed baby object match draft shows unread marker after leaving generation page', async () => { +test('completed baby object match draft shows completion dialog after leaving generation page', async () => { const user = userEvent.setup(); + mockDesktopPlatformLayout(); const generatedDraft = buildMockBabyObjectMatchDraft(); let resolveCreateDraft!: (value: { draft: BabyObjectMatchDraft }) => void; vi.mocked(createBabyObjectMatchDraft).mockReturnValue( @@ -6283,7 +6289,7 @@ test('completed baby object match draft shows unread marker after leaving genera ); vi.mocked(listLocalBabyObjectMatchDrafts).mockResolvedValue([generatedDraft]); - render(); + render(); await openCreateTemplateHub(user); await user.click(await findCreationTypeButton('宝贝识物')); @@ -6297,33 +6303,16 @@ test('completed baby object match draft shows unread marker after leaving genera }), ).toBeTruthy(); await user.click(screen.getByRole('button', { name: '返回创作中心' })); - await openDraftHub(user); + const createPanel = await findPlatformTabPanel('create'); + await waitFor(() => { + expect(createPanel.getAttribute('aria-hidden')).toBe('false'); + }); await act(async () => { resolveCreateDraft({ draft: generatedDraft }); }); - expect(await screen.findByLabelText('新生成完成')).toBeTruthy(); - expect( - await screen.findByRole('button', { name: '草稿,有新草稿' }), - ).toBeTruthy(); - - await user.click( - await screen.findByRole('button', { - name: /继续创作《宝贝识物红点草稿》/u, - }), - ); - - expect(await screen.findByText('宝贝识物结果页')).toBeTruthy(); - await user.click(screen.getByRole('button', { name: '返回' })); - await waitFor(() => { - expect(screen.queryByText('宝贝识物结果页')).toBeNull(); - }); - const draftPanel = await findPlatformTabPanel('saves'); - await waitFor(() => { - expect(draftPanel.getAttribute('aria-hidden')).toBe('false'); - }); - expect(screen.queryByLabelText('新生成完成')).toBeNull(); + expect(await screen.findByRole('dialog', { name: '生成完成' })).toBeTruthy(); }); test('puzzle draft generation auto starts trial and runtime back opens draft result', async () => { @@ -9282,7 +9271,7 @@ test('native app jump hop draft completion sends host notification and badge fro nativeAppPath(['app.setBadgeCount', 'notification.showLocal']), ); - render(); + render(); await openCreateTemplateHub(user); await user.click(await findCreationTypeButton('跳一跳')); @@ -9294,7 +9283,10 @@ test('native app jump hop draft completion sends host notification and badge fro }), ).toBeTruthy(); await user.click(screen.getByRole('button', { name: '返回创作中心' })); - await openDraftHub(user); + const createPanel = await findPlatformTabPanel('create'); + await waitFor(() => { + expect(createPanel.getAttribute('aria-hidden')).toBe('false'); + }); await act(async () => { resolveCompile({ @@ -9345,7 +9337,10 @@ test('native app opens child motion demo through host navigation bridge', async render(); - await user.click(screen.getByRole('button', { name: '发现' })); + await user.click( + screen.queryByRole('button', { name: '发现' }) ?? + screen.getByRole('button', { name: /查看作品/u }), + ); await user.click(await screen.findByRole('tab', { name: '寓教于乐' })); await user.click(await screen.findByRole('button', { name: /热身关卡/u })); diff --git a/src/components/rpg-entry/RpgEntryHomeView.tsx b/src/components/rpg-entry/RpgEntryHomeView.tsx index dce9c1f00..6f01da388 100644 --- a/src/components/rpg-entry/RpgEntryHomeView.tsx +++ b/src/components/rpg-entry/RpgEntryHomeView.tsx @@ -78,6 +78,7 @@ import { } from '../../services/host-bridge/hostBridge'; import { shouldShowRechargeEntry } from '../../services/payment/paymentPlatform'; import type { CustomWorldProfile } from '../../types'; +import type { PlatformHomeTab } from '../platform-entry/platformEntryTypes'; import { useAuthUi } from '../auth/AuthUiContext'; import { PlatformAsyncStatePanel } from '../common/PlatformAsyncStatePanel'; import { CopyFeedbackButton } from '../common/CopyFeedbackButton'; @@ -225,12 +226,7 @@ import { resolvePlatformWorldLeadPortrait, } from './rpgEntryWorldPresentation'; -export type PlatformHomeTab = - | 'home' - | 'category' - | 'create' - | 'saves' - | 'profile'; +export type { PlatformHomeTab } from '../platform-entry/platformEntryTypes'; type PlatformNavTab = PlatformHomeTab | 'projects'; export interface RpgEntryHomeViewProps { activeTab: PlatformHomeTab; diff --git a/src/components/rpg-entry/useRpgEntryBootstrap.ts b/src/components/rpg-entry/useRpgEntryBootstrap.ts index 2e149512f..a33a7f018 100644 --- a/src/components/rpg-entry/useRpgEntryBootstrap.ts +++ b/src/components/rpg-entry/useRpgEntryBootstrap.ts @@ -42,6 +42,7 @@ type UseRpgEntryBootstrapParams = { snapshot?: HydratedSavedGameSnapshot | null, ) => void; hasInitialAgentSession: boolean; + initialPlatformTab?: PlatformHomeTab; }; export function useRpgEntryBootstrap( @@ -53,6 +54,7 @@ export function useRpgEntryBootstrap( getProfileDashboard, handleContinueGame, hasInitialAgentSession, + initialPlatformTab = 'home', } = params; const isAuthenticated = Boolean(user); const canReadProtectedData = Boolean(user) && canAccessProtectedData; @@ -74,7 +76,8 @@ export function useRpgEntryBootstrap( PlatformBrowseHistoryEntry[] >([]); const [saveEntries, setSaveEntries] = useState([]); - const [platformTab, setPlatformTabState] = useState('home'); + const [platformTab, setPlatformTabState] = + useState(initialPlatformTab); const [platformError, setPlatformError] = useState(null); const [dashboardError, setDashboardError] = useState(null); const [historyError, setHistoryError] = useState(null); @@ -351,7 +354,7 @@ export function useRpgEntryBootstrap( !hasExplicitPlatformTabSelectionRef.current ) { // 中文注释:新用户也先进入推荐页;真正受保护的动作再单独做登录门禁。 - setPlatformTabState('home'); + setPlatformTabState(initialPlatformTab); } } finally { if (isActive) { @@ -368,6 +371,7 @@ export function useRpgEntryBootstrap( canReadProtectedData, getProfileDashboard, hasInitialAgentSession, + initialPlatformTab, user, ]);