import { afterEach, describe, expect, it, vi } from 'vitest'; import { setAgcPluginProjectPath, startAvailableAgcEditorPlugins, } from '../src/services/pluginHost'; afterEach(() => vi.unstubAllGlobals()); const editorPlugins = ['agc-cocos-editor', 'agc-unity-editor'].map((id) => ({ id, name: id === 'agc-cocos-editor' ? 'Cocos Creator' : 'Unity', builtin: true, enabled: true, hasRuntime: true, status: 'stopped', })); describe('插件自动启动使用后端能力投影', () => { it.each( [ [], [ { ...editorPlugins[0], enabled: false, hasRuntime: true, status: 'stopped', }, ], [ { ...editorPlugins[0], enabled: true, hasRuntime: false, status: 'package', }, ], [ { ...editorPlugins[0], enabled: true, hasRuntime: true, status: 'invalid', }, ], [{ ...editorPlugins[0], status: 'running' }], [{ ...editorPlugins[0], status: 'failed' }], [{ ...editorPlugins[0], status: 'disabled' }], [{ ...editorPlugins[0], builtin: false }], [{ ...editorPlugins[0], id: 'other-plugin' }], ].map((plugins) => ({ plugins })), )('只启动宿主投影允许自动启动的内置编辑器插件(%j)', async ({ plugins }) => { const invoke = vi.fn(async () => plugins); vi.stubGlobal('window', { __TAURI__: { core: { invoke } } }); await startAvailableAgcEditorPlugins(); expect(invoke).toHaveBeenCalledTimes(1); expect(invoke).toHaveBeenCalledWith('list_agc_plugins'); }); it.each(['stopped', 'discovered'])( '从同一份宿主列表启动所有可用的编辑器插件:%s', async (status) => { const invoke = vi.fn(async (command: string) => command === 'list_agc_plugins' ? editorPlugins.map((plugin) => ({ ...plugin, status })) : {}, ); vi.stubGlobal('window', { __TAURI__: { core: { invoke } } }); await startAvailableAgcEditorPlugins(); expect(invoke.mock.calls).toEqual([ ['list_agc_plugins'], ['start_agc_plugin', { id: 'agc-cocos-editor' }], ['start_agc_plugin', { id: 'agc-unity-editor' }], ]); }, ); it('一个编辑器插件启动失败仍启动另一个,且不自动重试', async () => { const invoke = vi.fn(async (command: string, params?: { id: string }) => { if (command === 'list_agc_plugins') return editorPlugins; if (params?.id === 'agc-cocos-editor') throw new Error('进程已退出'); return {}; }); vi.stubGlobal('window', { __TAURI__: { core: { invoke } } }); await expect(startAvailableAgcEditorPlugins()).rejects.toThrow( 'Cocos Creator:进程已退出', ); expect(invoke.mock.calls).toEqual([ ['list_agc_plugins'], ['start_agc_plugin', { id: 'agc-cocos-editor' }], ['start_agc_plugin', { id: 'agc-unity-editor' }], ]); }); it('项目切换后迟到的插件列表不会启动旧项目的插件', async () => { let finishList!: (plugins: typeof editorPlugins) => void; const invoke = vi.fn( () => new Promise((resolve) => { finishList = resolve; }), ); vi.stubGlobal('window', { __TAURI__: { core: { invoke } } }); let active = true; const start = startAvailableAgcEditorPlugins(() => active); active = false; finishList(editorPlugins); await start; expect(invoke).toHaveBeenCalledTimes(1); }); it('快速切换项目时旧 cleanup 不能晚于新项目设置抵达宿主', async () => { const received: Array = []; let finishFirst: () => void = () => undefined; const invoke = vi.fn( async (_command: string, params: { projectPath: string | null }) => { if (params.projectPath === 'C:/A') await new Promise((resolve) => { finishFirst = resolve; }); received.push(params.projectPath); }, ); vi.stubGlobal('window', { __TAURI__: { core: { invoke } } }); const first = setAgcPluginProjectPath('C:/A'); const cleanup = setAgcPluginProjectPath(null); const second = setAgcPluginProjectPath('C:/B'); await vi.waitFor(() => expect(invoke).toHaveBeenCalledTimes(1)); finishFirst(); await Promise.all([first, cleanup, second]); expect(received).toEqual(['C:/A', null, 'C:/B']); }); });