Files
Genarrative/apps/ai-game-creator-shell/tests/resourceCanvasCodeOnlyProject.test.tsx
T
suzmii 2f8753f597 AGC 资源画布切换为 6 类资产分类加项目版本栏目(阶段二:投影轴与栏目表落地)
- 契约层把 ProjectResourceCanvasPosition.section 加宽为 PersistedProjectResourceCanvasSection 并删除旧 ProjectResourceCanvasSection 联合
- 七个分区类型消费点改用 ProjectResourceCanvasCategory:投影、布局模型、布局 Hook、分区高度、依赖图层与画布历史
- RESOURCE_CANVAS_SECTION_ORDER 改为 PROJECT_RESOURCE_CANVAS_SECTIONS,删除 RESOURCE_CANVAS_VISIBLE_SECTION_ORDER
- reconcileResourceCanvasLayout 接入读时归一化:旧栏目值按资源当前分区改写 section,坐标与手动标记原样保留
- 归并后的布局与原布局逐项不同,由既有协调路径判定 changed 并写回一次新分区,不引入迁移脚本
- 投影层五个资源入表点统一走 projectResourceCanvasCategory:项目版本独立成栏、Agent 回执归文档、其余按资产分类
- 新增 projectResourceDisplayKind 收敛 manifest 资产 subtype 即 kind 的显示类型口径,projectResourceTypeLabel 不再依赖旧栏目
- 卡片预览、编辑分流、媒体工具条、预览文案与画布历史快照改用显示类型与新分区,历史快照入栈时收窄到现行分区
- index.tsx 删除可见栏目导入与 visibleCategoryOrder,栏目标签由资源筛选唯一中文口径派生并补齐 7 栏
- index.tsx 补齐 7 栏默认视口与 7 个栏目图标(新增 LayoutGrid、Users、PackageOpen,移除已无用的 Code2)
- index.tsx 删除 canvasResources 的 code 过滤:只登记游戏代码的项目现在落在待归类栏目并正常显示卡片
- 新增只登记游戏代码项目的 UI 回归用例,覆盖栏目大纲、默认落地栏目与代码卡片渲染
- 测试夹具按新分区轴迁移,新增旧栏目 sidecar 读时归并用例与栏目顺序用例
- 共享测试 harness 的资源卡查询正则允许栏目标签自带空格(UI 交互)
- 同步 PRD 与共享记忆决策记录的栏目口径描述
2026-09-10 20:41:19 +08:00

126 lines
3.6 KiB
TypeScript

// @vitest-environment jsdom
import { afterEach, describe, expect, test, vi } from 'vitest';
import type { GameCreationAppManifest } from '../../../packages/shared/src/contracts/gameCreationApp';
import {
cleanup,
createGameCreationAppManifest,
createGameCreationAppSeedTasks,
ProjectDevelopmentView,
React,
render,
screen,
within,
} from './appSurface/harness';
const PROJECT_PATH = '/tmp/workbench-code-only-project';
function installResourceCardIntersectionObserver() {
class ResourceCardIntersectionObserver {
readonly root = null;
readonly rootMargin = '160px';
readonly thresholds = [0];
readonly observed = new Set<Element>();
constructor(readonly callback: IntersectionObserverCallback) {}
observe(element: Element) {
this.observed.add(element);
}
unobserve(element: Element) {
this.observed.delete(element);
}
disconnect() {
this.observed.clear();
}
takeRecords() {
return [];
}
}
Object.defineProperty(window, 'IntersectionObserver', {
configurable: true,
value: ResourceCardIntersectionObserver,
});
}
/**
* 只登记游戏代码的项目:分区轴上没有代码栏目,代码资产(manifest 资产与已完成任务登记
* 的产物)按资产功能分类落入「待归类」。旧四栏目口径会把它们整体排除在可见栏目之外,
* 于是资源签名非空、画布已进入分页态,却四栏全空、一张卡都不显示。
*/
function createCodeOnlyManifest(): GameCreationAppManifest {
const manifest = createGameCreationAppManifest(
'workbench-code-only',
'只登记游戏代码的项目',
);
manifest.assets = [
{
id: 'asset-game-entry',
kind: 'code',
mediaType: 'text/html',
localPath: 'game/index.html',
source: { kind: 'generated' },
},
];
manifest.tasks = createGameCreationAppSeedTasks().map((task) =>
task.id === 'code-prototype'
? { ...task, status: 'completed' as const, artifacts: ['game/main.js'] }
: task,
);
return manifest;
}
function renderWorkbench(manifest: GameCreationAppManifest) {
return render(
React.createElement(ProjectDevelopmentView, {
projectName: manifest.name,
projectPath: PROJECT_PATH,
manifest,
attachments: [],
recentRunStatus: null,
recentRunStopReason: null,
supervisor: React.createElement('div', null, '项目总控'),
onHomeOpen: vi.fn(),
onProjectsOpen: vi.fn(),
onPlay: vi.fn(),
}),
);
}
describe('只登记游戏代码的项目分区', () => {
afterEach(() => {
cleanup();
});
test('代码资产落在待归类栏目并渲染出卡片,而不是四栏全空', async () => {
installResourceCardIntersectionObserver();
renderWorkbench(createCodeOnlyManifest());
const outline = await screen.findByLabelText('资源栏目大纲');
expect(
within(outline).getByRole('button', { name: /待归类/ }),
).not.toBeNull();
// 项目版本仍然是末尾独立栏目,代码栏目不再存在。
expect(
within(outline).getByRole('button', { name: /项目版本/ }),
).not.toBeNull();
// 默认停留在顺序里第一个非空栏目,也就是代码资产所在的「待归类」。
expect(
await screen.findByRole('region', { name: '待归类' }),
).not.toBeNull();
// manifest 资产与已完成任务的代码产物都必须真的渲染成卡片。
await screen.findByRole('button', {
name: /选中资源:待归类 index\.html/,
});
await screen.findByRole('button', {
name: /选中资源:待归类 main\.js/,
});
});
});