From 2e5dc57e9df9ddfe0ac703a4d26fdb7471727b3a Mon Sep 17 00:00:00 2001 From: Suzumiya Date: Mon, 7 Sep 2026 17:28:16 +0800 Subject: [PATCH] =?UTF-8?q?=E6=A8=A1=E5=9E=8B=E9=80=89=E6=8B=A9=E4=BF=9D?= =?UTF-8?q?=E5=AD=98=E6=9C=9F=E9=97=B4=E7=A6=81=E7=94=A8=E9=80=89=E9=A1=B9?= =?UTF-8?q?=EF=BC=8C=E9=81=BF=E5=85=8D=E5=B9=B6=E5=8F=91=E9=80=89=E6=8B=A9?= =?UTF-8?q?=E7=AB=9E=E6=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 针对 review 提出的 P2:保存 select_game_creator_model 期间重新打开菜单再选一次会挂起两条 invoke,完成第一条即解锁发送导致显示/配置错乱 保存期间禁用下拉选项(可查看菜单,不可再选),保存完成后恢复;补充并发回归测试 --- .../ConversationModelSelect.tsx | 1 + .../tests/conversationModelSelect.test.tsx | 39 +++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/apps/ai-game-creator-shell/src/features/project-workspace/ConversationModelSelect.tsx b/apps/ai-game-creator-shell/src/features/project-workspace/ConversationModelSelect.tsx index eef2a3150..d6e81735a 100644 --- a/apps/ai-game-creator-shell/src/features/project-workspace/ConversationModelSelect.tsx +++ b/apps/ai-game-creator-shell/src/features/project-workspace/ConversationModelSelect.tsx @@ -160,6 +160,7 @@ export function ConversationModelSelect({ type="button" role="option" aria-selected={model.id === selected} + disabled={disabled || busy} onClick={() => { setOpen(false); void select(model.id); diff --git a/apps/ai-game-creator-shell/tests/conversationModelSelect.test.tsx b/apps/ai-game-creator-shell/tests/conversationModelSelect.test.tsx index 883d17b06..1f160b63b 100644 --- a/apps/ai-game-creator-shell/tests/conversationModelSelect.test.tsx +++ b/apps/ai-game-creator-shell/tests/conversationModelSelect.test.tsx @@ -137,3 +137,42 @@ test('marks the default model in the menu', async () => { const fastOption = screen.getByRole('option', { name: '快速' }); expect(within(fastOption).queryByText('默认')).toBeNull(); }); + +test('keeps model options disabled while a selection save is in flight', async () => { + let resolveSave: ((value: unknown) => void) | undefined; + invoke.mockImplementation(async (command, input) => { + if (command === 'select_game_creator_model') { + return new Promise((resolve) => { + resolveSave = resolve; + }); + } + return { config: { selectedModelId: 'quality' } }; + }); + const onReady = vi.fn(); + render(); + await screen.findByRole('button', { name: '对话模型' }); + await waitFor(() => expect(onReady).toHaveBeenLastCalledWith(true)); + + fireEvent.click(screen.getByRole('button', { name: '对话模型' })); + await screen.findByRole('option', { name: '快速' }); + fireEvent.click(screen.getByRole('option', { name: '快速' })); + + // 保存期间重新打开菜单:可以查看,但选项应禁用,避免并发选择。 + fireEvent.click(screen.getByRole('button', { name: '对话模型' })); + expect(screen.getByRole('option', { name: '快速' })).toHaveProperty( + 'disabled', + true, + ); + expect(screen.getByRole('option', { name: /高质量/ })).toHaveProperty( + 'disabled', + true, + ); + expect(onReady).toHaveBeenLastCalledWith(false); + + resolveSave?.({ config: { selectedModelId: 'fast' } }); + await waitFor(() => expect(onReady).toHaveBeenLastCalledWith(true)); + expect(screen.getByRole('option', { name: '快速' })).toHaveProperty( + 'disabled', + false, + ); +});