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 断言改为新契约(原地址逐字保留、参数逐次递增、节点每次都是新的)
135 lines
4.8 KiB
TypeScript
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();
|
|
});
|
|
});
|