/** @vitest-environment jsdom */ import type { ReactNode } from 'react'; import { useCallback } from 'react'; import { vi } from 'vitest'; import { WindowChrome } from '../src/components/WindowChrome'; import { useWindowChrome, type WindowChromeActiveProjectRuns, WindowChromeContext, } from '../src/components/windowChromeContext'; import { WorkspaceLauncherShell } from '../src/features/app-shell/WorkspaceLauncher'; import { act, expect, it, React, render, testAuthUser, } from './appSurface/harness'; const homeProjectOverride = vi.hoisted(() => ({ openProject: null as | null | ((path: string, mode: 'open' | 'create') => Promise), })); vi.mock('../src/features/app-shell/useHomeProjectCreation', async () => { const actual = await vi.importActual< typeof import('../src/features/app-shell/useHomeProjectCreation') >('../src/features/app-shell/useHomeProjectCreation'); return { ...actual, useHomeProjectCreation( ...args: Parameters ) { const result = actual.useHomeProjectCreation(...args); return homeProjectOverride.openProject ? { ...result, openProject: homeProjectOverride.openProject } : result; }, }; }); it('真实窗口与工作台状态同步收敛,回调读取最新处理器且卸载才清理', async () => { const publications: WindowChromeActiveProjectRuns[] = []; let cleanups = 0; // 仍经过真实 WindowChrome 的 setState/Context;上限只防止回归时测试无限循环。 function BoundedWindowBridge({ children }: { children: ReactNode }) { const chrome = useWindowChrome(); const { setActiveProjectRuns } = chrome; const publish = useCallback( (next: WindowChromeActiveProjectRuns | null) => { if (next) publications.push(next); else cleanups += 1; if (publications.length < 12) setActiveProjectRuns(next); }, [setActiveProjectRuns], ); return ( {children} ); } const chat = () => null; const view = (displayName = '测试用户') => ( undefined} initialView="projects" ProjectChat={chat} /> ); delete window.__TAURI__; const rendered = render(view()); try { await act(async () => { await Promise.resolve(); }); // 无原生 invoke 时空快照引用不变,只发布一次。 expect(publications).toHaveLength(1); expect(cleanups).toBe(0); expect(new Set(publications.map((item) => item.onOpenProject)).size).toBe( 1, ); const openProject = publications[0]!.onOpenProject!; const latestOpen = vi.fn(async () => undefined); homeProjectOverride.openProject = latestOpen; rendered.rerender(view('更改显示名')); // 非依赖变化的重渲染不能让窗口面板重新发布。 expect(publications).toHaveLength(1); act(() => openProject('/tmp/window-latest-project')); expect(latestOpen).toHaveBeenCalledWith( '/tmp/window-latest-project', 'open', ); // 回调执行只换 ref 里的实现,发布次数与清理次数都不能变。 expect(publications).toHaveLength(1); expect(cleanups).toBe(0); rendered.unmount(); expect(cleanups).toBe(1); } finally { homeProjectOverride.openProject = null; rendered.unmount(); } });