Files
Genarrative/apps/ai-game-creator-shell/tests/designProjectRestore.test.tsx
T
kdletters 1d6cbd4b67
Project CI / AI game creator shell Rust crates (push) Successful in 1m29s
Project CI / AI game creator shell Rust smoke (push) Successful in 1m57s
Project CI / Backend tests (push) Successful in 3m57s
Project CI / Frontend tests (push) Successful in 2m0s
Project CI / Native shell tests (push) Successful in 6m5s
Project CI / Repository checks (push) Failing after 42s
Project CI / AI game creator shell Rust lane 2/2 (push) Successful in 8m49s
Project CI / AI game creator shell Rust lane 1/2 (push) Successful in 8m54s
Project CI / AI game creator shell web tests (push) Successful in 1m40s
外壳状态栏退役,状态改由浮层提示承载
- 首页输入框下方与项目组页面头部那行状态栏退役:status 不再由页面渲染,统一由外壳浮层承载(WorkspaceLauncher 合成粘性状态与一次性提示,渲染 notice ?? statusNotice)
- 进行中进度与失败结论走粘性浮层(sticky: true,不自动收起,切页回来仍可见);成功与取消回显走一次性浮层(成功/取消 2.6 秒、失败 6 秒收起),一次性提示优先显示,收起后粘性状态继续显示
- 粘性状态只挂首页与项目组两个页面,进项目工作台、模板库、帮助页不再飘在界面上;运行/预览提示与取消回显不受该限制
- RunNoticeToast/RunNotice 更名为 LauncherNoticeToast/LauncherNotice,新增 sticky 与 neutral 色调,挂点改为 data-launcher-notice-toast,长文案容器限宽 min(680px, 100vw - 2rem) 并换行
- 收起回调只从 ref 读,不再写进 effect 依赖:此前外壳每次重渲染都会换一个内联箭头函数,把收起计时重新起算,长驻页面上的提示可能永远收不回去
- useHomeProjectCreation 三处取消(非空文件夹确认弹窗、打开项目选目录、新建项目选目录)改为清空状态并弹一次性浮层回显「已取消」,不再往页面上那行写文字
- 删除 HomeView 的 status prop 与状态行节点、ProjectsPage 的 status prop 与 .launcher-project-page-status 节点及对应 CSS 规则
- launcherNoticeToast 用例补粘性不收起、父组件重渲染不重新计时、取消回显短时收起;launcherNoticeShellWiring 用例补进行中状态与取消回显都只走浮层、首页不留那一行、粘性状态不进项目工作台
- appSurface/home.suite 三处取消断言改断浮层挂点;homeWebPreflight 与 designProjectRestore 的 hook 夹具补 showTransientNotice
- 技术方案与 decision-log 记录该口径与边界:首页带按钮的「已创建的工作区」提示仍留在页面上
2026-09-29 19:35:49 +08:00

84 lines
3.0 KiB
TypeScript

// @vitest-environment jsdom
import { act, cleanup, renderHook } from '@testing-library/react';
import { afterEach, expect, it, vi } from 'vitest';
import { createGameCreationAppManifest } from '../../../packages/shared/src/contracts/gameCreationApp';
import { useHomeProjectCreation } from '../src/features/app-shell/useHomeProjectCreation';
afterEach(() => {
cleanup();
delete window.__TAURI__;
});
const projectPath = 'C:/test/design-project';
const manifest = createGameCreationAppManifest('test-project', '策划项目');
function setup(invoke: ReturnType<typeof vi.fn>) {
window.__TAURI__ = { core: { invoke } } as unknown as typeof window.__TAURI__;
return renderHook(() =>
useHomeProjectCreation({
setStatus: vi.fn(),
showTransientNotice: vi.fn(),
setLauncherView: vi.fn(),
setAgentChatProjectPath: vi.fn(),
rememberRecentWorkspace: vi.fn(),
}),
);
}
it.each(['design', 'game', null] as const)(
'restores %s before publishing the project context',
async (mode) => {
let resolveMode!: (value: { activeRuntime: string } | null) => void;
const modePromise = new Promise<{ activeRuntime: string } | null>(
(resolve) => {
resolveMode = resolve;
},
);
const invoke = vi.fn(async (command: string) => {
if (command === 'inspect_local_project_directory')
return { exists: true, isDirectory: true, isGameCreatorProject: true };
if (command === 'get_local_game_manifest') return manifest;
if (command === 'get_local_game_project_revision') return { revision: 1 };
if (command === 'get_design_agent_runtime_mode') return modePromise;
throw new Error(command);
});
const { result } = setup(invoke);
let opening!: Promise<void>;
await act(async () => {
opening = result.current.openProject(projectPath, 'open');
});
expect(result.current.currentProjectContext).toBeNull();
await act(async () => {
resolveMode(mode ? { activeRuntime: mode } : null);
await opening;
});
expect(result.current.currentProjectContext?.startMode).toBe(
mode === 'design' ? 'planning' : null,
);
},
);
it('persists design mode before entering a newly created planning project', async () => {
const invoke = vi.fn(async (command: string) => {
if (command === 'create_automatic_local_game_project')
return { projectPath, manifest };
if (command === 'set_design_agent_runtime_mode')
return { activeRuntime: 'design' };
if (command === 'get_local_game_project_revision') return { revision: 1 };
throw new Error(command);
});
const { result } = setup(invoke);
await act(async () => {
await result.current.createHomeDraftAutomatically(
{ creationType: 'game', prompt: '测试', attachments: [] },
'planning',
);
});
expect(invoke).toHaveBeenCalledWith('set_design_agent_runtime_mode', {
projectPath,
activeRuntime: 'design',
});
expect(result.current.currentProjectContext?.startMode).toBe('planning');
});