Files
Genarrative/apps/ai-game-creator-shell/tests/runPreviewRefresh.test.tsx
T
suzmii 14283db943
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust crates (pull_request) Has been cancelled
Project CI / Backend tests (pull_request) Has been cancelled
Project CI / Native shell tests (pull_request) Has been cancelled
Project CI / Frontend tests (pull_request) Has been cancelled
Project CI / Repository checks (pull_request) Has been cancelled
Project CI / AI game creator shell web tests (pull_request) Has been cancelled
修复运行页「刷新运行画面」不是真正重载
- 刷新时给 iframe 地址加一枚随 reloadNonce 递增的 __agc_reload 参数:同一地址的导航可能命中缓存,只换元素身份不足以保证重新请求
- 重新载入时丢掉上一版量到的内容尺寸,新页面先在容器尺寸上适配,不再被上一版的适配结果渲染
- tests/localGamePreviewFrame.test.ts 补刷新回归:换新节点 + 地址带一次性参数 + 丢弃旧测量
- tests/runPreviewRefresh.test.tsx 断言改为新契约(原地址逐字保留、参数逐次递增、节点每次都是新的)
2026-10-05 17:08:21 +08:00

135 lines
4.8 KiB
TypeScript

/** @vitest-environment jsdom */
import { cleanup, fireEvent, render, screen } from '@testing-library/react';
import { afterEach, describe, expect, it, vi } from 'vitest';
import {
createGameCreationAppManifest,
type GameCreationAppPreviewState,
} from '../../../packages/shared/src/contracts/gameCreationApp';
import ProjectDevelopmentView from '../src/view/project-development';
import { installTauriRuntime } from './tauriRuntimeFake';
const PREVIEW_URL = 'http://127.0.0.1:4173/';
function installInvoke() {
installTauriRuntime({
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}`);
}),
},
});
}
function renderRunView(
preview: GameCreationAppPreviewState = {
status: 'running',
url: PREVIEW_URL,
port: 4173,
},
) {
installInvoke();
const onPlay = vi.fn();
const manifest = createGameCreationAppManifest(
'run-preview-refresh',
'运行页刷新画面',
);
manifest.tasks = manifest.tasks.map((task) =>
task.id === 'code-prototype' ? { ...task, status: 'completed' } : task,
);
render(
<ProjectDevelopmentView
projectName={manifest.name}
projectPath="/tmp/run-preview-refresh"
manifest={manifest}
attachments={[]}
recentRunStatus="completed"
recentRunStopReason={null}
preview={preview}
chat={null}
onHomeOpen={vi.fn()}
onProjectsOpen={vi.fn()}
onNotice={vi.fn()}
onPlay={onPlay}
/>,
);
return { onPlay };
}
afterEach(() => {
cleanup();
vi.clearAllMocks();
vi.restoreAllMocks();
});
describe('运行页「刷新运行画面」', () => {
it('点一下就是一次真正的重新载入:换新节点 + 地址带上一次性刷新参数,且顺带把预览确认成活体', async () => {
const { onPlay } = renderRunView();
await screen.findByTitle('运行页刷新画面 游戏运行画面');
const before = document.querySelector('iframe');
expect(before).not.toBeNull();
// 首次加载不动地址:原样的 loopback 地址,不带任何非业务参数。
expect(before?.getAttribute('src')).toBe(PREVIEW_URL);
fireEvent.click(screen.getByRole('button', { name: '刷新运行画面' }));
const after = document.querySelector('iframe');
// 换元素身份=浏览器重新载入这一页:游戏预览不是 vite dev 的实时刷新,只有这一条路。
expect(after).not.toBe(before);
// 地址只多一枚随刷新递增的参数:原地址(含既有 query)逐字保留,导航因此一定是一次新请求。
const afterUrl = new URL(String(after?.getAttribute('src')));
expect(`${afterUrl.origin}${afterUrl.pathname}`).toBe(PREVIEW_URL);
expect(afterUrl.searchParams.get('__agc_reload')).toBe('1');
expect(onPlay).toHaveBeenCalledTimes(1);
});
it('连点两次各自都是一次新的载入:参数逐次递增,不是同一个地址原地打转', async () => {
renderRunView();
await screen.findByTitle('运行页刷新画面 游戏运行画面');
fireEvent.click(screen.getByRole('button', { name: '刷新运行画面' }));
const first = document.querySelector('iframe');
fireEvent.click(screen.getByRole('button', { name: '刷新运行画面' }));
const second = document.querySelector('iframe');
expect(second).not.toBe(first);
expect(new URL(String(first?.getAttribute('src'))).searchParams.get('__agc_reload')).toBe('1');
expect(new URL(String(second?.getAttribute('src'))).searchParams.get('__agc_reload')).toBe('2');
expect(new URL(String(second?.getAttribute('src'))).origin).toBe('http://127.0.0.1:4173');
});
it('没有活预览时不渲染刷新,而不是留一个点了没反应的入口', async () => {
// 非 loopback 地址会被 `resolveEmbeddedPreviewUrl` 拒掉。此时切到运行页只有空态,
// 该走的是顶栏「运行」页签——刷新入口不该在没画面可刷的时候出现。
renderRunView({
status: 'running',
url: 'https://example.com/game',
port: 443,
});
fireEvent.click(screen.getByRole('tab', { name: '运行' }));
await screen.findByText('客户端运行画面尚未载入');
expect(screen.queryByRole('button', { name: '刷新运行画面' })).toBeNull();
});
});