Files
Genarrative/apps/ai-game-creator-shell/tests/runPreviewFullscreen.test.tsx
T
k88936 25c5ecdf6e
Project CI / AI game creator shell Rust crates (pull_request) Successful in 2m58s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 6m34s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Successful in 5m29s
Project CI / Backend tests (pull_request) Failing after 4m38s
Project CI / Frontend tests (pull_request) Successful in 3m33s
Project CI / Repository checks (pull_request) Failing after 4m4s
Project CI / AI game creator shell web tests (pull_request) Successful in 3m13s
Project CI / Native shell tests (pull_request) Successful in 6m35s
测试:全仓测试统一 Tauri 运行时替身安装点
- 78 处内联 window.__TAURI__ 赋值改为 installTauriRuntime,覆盖 appSurface 之外的 37 个测试文件
- 31 处 delete window.__TAURI__ / window.__TAURI__ = undefined 复位改为 resetTauriRuntime
- 删除 recentProjectsHook、projectCreationDirectory、projectResourceLiveIntegration、useProjectResourceCardPreviews、resourceVersionReplacementModel、unityProjectOpen 里的本地 asTauriInvoke / asTauriListen 适配器与相关类型别名
- designProjectRestore 的 setup 参数由 ReturnType<typeof vi.fn> 收窄为具体 invoke 签名
- 迁移后仅 tests/tauriRuntimeFake.ts 直接读写 window.__TAURI__
2026-10-03 20:19:45 +08:00

145 lines
5.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/** @vitest-environment jsdom */
import { fireEvent, render, screen } 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';
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() {
installInvoke();
const manifest = createGameCreationAppManifest(
'run-preview-fullscreen',
'运行页全屏预览',
);
render(
<ProjectDevelopmentView
projectName={manifest.name}
projectPath="/tmp/run-preview-fullscreen"
manifest={manifest}
attachments={[]}
recentRunStatus={null}
recentRunStopReason={null}
preview={{ status: 'running', url: PREVIEW_URL, port: 4173 }}
// ProjectDevelopmentViewProps 现在必填 chat;旧的 supervisor 入参已不存在。
chat={null}
onHomeOpen={vi.fn()}
onProjectsOpen={vi.fn()}
onNotice={vi.fn()}
/>,
);
}
/**
* jsdom 完全没有 Fullscreen API(`document.fullscreenEnabled` / `requestFullscreen` /
* `fullscreenElement` 都是 undefined),所以这一组用例必须自己把宿主那一份补出来:
* 全屏状态、元素身份与 `fullscreenchange` 都按规范最小实现,只用于验按钮的真实行为。
*/
function installFullscreenHost() {
let fullscreenElement: Element | null = null;
const requestFullscreen = vi.fn(function (this: Element) {
// 这枚桩就是要记录调用方传进来的 `this`:全屏元素身份正是本用例的断言对象(画面那一格)。
// eslint-disable-next-line @typescript-eslint/no-this-alias
fullscreenElement = this;
document.dispatchEvent(new Event('fullscreenchange'));
return Promise.resolve();
});
const exitFullscreen = vi.fn(() => {
fullscreenElement = null;
document.dispatchEvent(new Event('fullscreenchange'));
return Promise.resolve();
});
Object.defineProperty(document, 'fullscreenElement', {
configurable: true,
get: () => fullscreenElement,
});
Object.defineProperty(document, 'exitFullscreen', {
configurable: true,
value: exitFullscreen,
});
Object.defineProperty(Element.prototype, 'requestFullscreen', {
configurable: true,
writable: true,
value: requestFullscreen,
});
return { requestFullscreen, exitFullscreen };
}
afterEach(() => {
document.body.innerHTML = '';
Reflect.deleteProperty(document, 'fullscreenElement');
Reflect.deleteProperty(document, 'exitFullscreen');
Reflect.deleteProperty(Element.prototype, 'requestFullscreen');
vi.clearAllMocks();
vi.restoreAllMocks();
});
describe('运行页「全屏预览」', () => {
it('点画面右下角那枚按钮就把画面那一格送进全屏,再点退出', async () => {
const { requestFullscreen, exitFullscreen } = installFullscreenHost();
renderRunView();
const button = await screen.findByRole('button', { name: '全屏预览' });
// 按钮住在画面那一格里(右下角由样式给),全屏的也是那一格——不是整块工作台。
const stage = document.querySelector('.game-run-preview');
expect(stage).not.toBeNull();
expect(button.closest('.game-run-preview')).toBe(stage);
expect(button.getAttribute('aria-pressed')).toBe('false');
fireEvent.click(button);
expect(requestFullscreen).toHaveBeenCalledTimes(1);
// 全屏元素就是画面那一格:按钮自己也算得出来(状态来自 fullscreenchange,不是乐观值)。
expect(document.fullscreenElement).toBe(stage);
const exitButton = await screen.findByRole('button', {
name: '退出全屏预览',
});
expect(exitButton.getAttribute('aria-pressed')).toBe('true');
fireEvent.click(exitButton);
expect(exitFullscreen).toHaveBeenCalledTimes(1);
expect(
(await screen.findByRole('button', { name: '全屏预览' })).getAttribute(
'aria-pressed',
),
).toBe('false');
});
it('宿主没有 Fullscreen API 时不渲染这枚按钮,而不是留一个点了没反应的入口', async () => {
renderRunView();
await screen.findByTitle('运行页全屏预览 游戏运行画面');
expect(screen.queryByRole('button', { name: '全屏预览' })).toBeNull();
});
});