Files
Genarrative/apps/ai-game-creator-shell/tests/runPreviewFullscreen.test.tsx
suzmii 09fb8bc076
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 1m52s
Project CI / Backend tests (pull_request) Failing after 13s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 1m5s
Project CI / AI game creator shell Rust lane 2/2 (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
Project CI / Frontend tests (pull_request) Has been cancelled
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Has been cancelled
Project CI / Native shell tests (pull_request) Has been cancelled
运行页签补齐全屏预览与可收起的信息栏
- 运行画面右下角新增「全屏预览」:新增 `useElementFullscreen`,只把画面那一格(`.game-run-preview`)送进全屏,按钮态只认 `fullscreenchange`,宿主没有 Fullscreen API 时整枚入口不渲染。
- 底部信息栏改为「有内容才存在」:运行页选中资源时自动展开,可手动收起到只剩一行开合按钮,清空选中后整栏不渲染;只有一栏内容时卡片铺满整行。
- 「数值微调」在没有登记表前不渲染区域标题与卡片,随这一栏删除其 label / input 样式声明。
- 新增 `tests/runPreviewFullscreen.test.tsx`;AGC 子集补信息栏开合用例,并把「没有内容仍渲染两张空卡片」的旧断言改成整栏不渲染。
- 同步 PRD §3.4、技术方案运行表现层条目与 project-memory 决策记录。
2026-09-23 17:26:53 +08:00

143 lines
5.0 KiB
TypeScript
Raw Permalink 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';
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() {
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 }}
supervisor={<div>项目总控</div>}
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();
});
});