Files
Genarrative/apps/ai-game-creator-shell/tests/runPreviewBrowserOpen.test.tsx
T
k88936 594063a1e0 测试:修正 AGC 测试文件的类型错误
- 按 vitest 0.34 的参数元组口径改写 mock 泛型
- 修正 noUncheckedIndexedAccess 下的下标访问
- 补齐与当前类型脱节的测试桩字段
- 按 scripts/*.mjs 的运行时契约补局部类型声明
2026-10-03 17:41:30 +08:00

120 lines
3.8 KiB
TypeScript

/** @vitest-environment jsdom */
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
import { afterEach, describe, expect, it, vi } from 'vitest';
import { createGameCreationAppManifest } from '../../../packages/shared/src/contracts/gameCreationApp';
import ProjectDevelopmentView from '../src/view/project-development';
const openUrl = vi.fn(async (_url: string) => undefined);
vi.mock('@tauri-apps/plugin-opener', () => ({
openUrl: (url: string) => openUrl(url),
}));
const PREVIEW_URL = 'http://127.0.0.1:4173/';
function installInvoke() {
window.__TAURI__ = {
core: {
invoke: vi.fn(async (command: string, args?: Record<string, unknown>) => {
if (command === 'read_local_project_resource_graph') {
return {
resourceIds: [],
referenceEdges: [],
taskFlows: [],
categories: [],
diagnostics: [],
};
}
if (command === 'read_local_project_resource_canvas_layout') {
return {
schemaVersion: 'game-creator-resource-layout.v1',
projectId: args?.expectedProjectId,
mode: args?.mode,
revision: 0,
positions: [],
updatedAt: 0,
};
}
throw new Error(`unexpected invoke ${command}`);
}),
},
} as unknown as typeof window.__TAURI__;
}
function renderRunView(options: { withPreview?: boolean } = {}) {
installInvoke();
const manifest = createGameCreationAppManifest(
'run-preview-browser-open',
'运行页浏览器入口',
);
const onNotice = vi.fn();
render(
<ProjectDevelopmentView
projectName={manifest.name}
projectPath="/tmp/run-preview-browser-open"
manifest={manifest}
attachments={[]}
recentRunStatus={null}
recentRunStopReason={null}
preview={
options.withPreview === false
? null
: { status: 'running', url: PREVIEW_URL, port: 4173 }
}
chat={<div>项目总控</div>}
onHomeOpen={vi.fn()}
onProjectsOpen={vi.fn()}
onNotice={onNotice}
/>,
);
return { onNotice };
}
afterEach(() => {
document.body.innerHTML = '';
vi.clearAllMocks();
vi.restoreAllMocks();
});
describe('运行页「在浏览器打开」', () => {
it('点顶栏那枚按钮就把当前预览地址交给系统浏览器', async () => {
renderRunView();
const entry = await screen.findByRole('button', {
name: '在浏览器打开',
});
// 入口住在工作台顶栏的动作区里,与版本入口同一排;预览地址本身不再以文字出现。
expect(entry.closest('.game-workbench-view-actions')).not.toBeNull();
expect(document.body.textContent ?? '').not.toContain(PREVIEW_URL);
expect(document.querySelector('.game-run-status-hint')).toBeNull();
fireEvent.click(entry);
await waitFor(() => expect(openUrl).toHaveBeenCalledWith(PREVIEW_URL));
// 成功不弹提示:浏览器真的起来了,用户自己看得见。
expect(openUrl).toHaveBeenCalledTimes(1);
});
it('没有活预览时不渲染这枚入口', async () => {
renderRunView({ withPreview: false });
await screen.findByRole('tab', { name: '运行' });
expect(screen.queryByRole('button', { name: '在浏览器打开' })).toBeNull();
});
it('打开失败时把原因交给统一提示通道,而不是静默吞掉', async () => {
openUrl.mockRejectedValueOnce(new Error('permission denied'));
const { onNotice } = renderRunView();
fireEvent.click(
await screen.findByRole('button', { name: '在浏览器打开' }),
);
await waitFor(() =>
expect(onNotice).toHaveBeenCalledWith({
tone: 'error',
message: '在浏览器打开失败:permission denied',
}),
);
});
});