补资源画布工具条动作模型定向测试,把放行判据钉在测试里

- 新增 tests/resourceCanvasToolbarModel.test.ts:直接覆盖 resolveResourceCanvasToolbarActions,此前该模型零直接覆盖

- 逐条钉住当前真实接通的动作集合:栅格图片与角色资源只放行 quick-edit,音频/视频不再放行 redraw,角色资源不再放行 character-animation

- 覆盖派生门禁:未登记 manifest 资产且无已完成任务产物背书不放行、只认已完成任务的栅格产物、非栅格图片不放行

- 判据出处为 docs/project-memory/shared-memory/pitfalls.md 的「渲染出来的动作必须真的能跑通」
This commit is contained in:
2026-09-10 17:53:25 +08:00
parent ecca7050b5
commit c9413fae0c
@@ -0,0 +1,163 @@
import { describe, expect, it } from 'vitest';
import { createGameCreationAppManifest } from '../../../packages/shared/src/contracts/gameCreationApp';
import { resolveResourceCanvasToolbarActions } from '../src/features/resource-canvas/resourceCanvasToolbarModel';
import type { ProjectResource } from '../src/view/project-development/resourceProjectionModel';
function createResource(
overrides: Partial<ProjectResource> = {},
): ProjectResource {
return {
id: 'asset:hero',
category: 'art',
subtype: 'icon',
label: '主角立绘',
path: 'assets/hero.png',
mediaType: 'image/png',
sourceLabel: '生成',
taskTitle: null,
manifestAssetId: 'hero',
producerTaskId: null,
externalResourceId: null,
referenceResourceIds: [],
dependencies: [],
dependencyDepth: 0,
...overrides,
};
}
function createManifest() {
return createGameCreationAppManifest(
'project-toolbar-actions',
'工具条动作测试',
);
}
/** 把种子任务的第一个任务改成已完成并登记指定产物,用于覆盖任务产物正规化门禁。 */
function createManifestWithProducingTask(
status: 'completed' | 'running',
artifactPath: string,
) {
const manifest = createManifest();
manifest.tasks = manifest.tasks.map((task, index) =>
index === 0 ? { ...task, status, artifacts: [artifactPath] } : task,
);
return manifest;
}
function toolbarActions(
manifest: ReturnType<typeof createManifest>,
resource: ProjectResource,
) {
return [...resolveResourceCanvasToolbarActions(manifest, resource)];
}
/**
* AGC 资源画布工具条的放行判据只有一条:**进集合的动作必须真能跑通**。
*
* `ImageCanvasSelectedLayerToolbarView` 收到 `supportedActions` 后完全以集合为准,
* 宿主编排层里还是空回调的动作一旦进集合就会渲染成"点了没反应"的按钮,因此这里
* 逐条钉住当前真正接通的动作集合(见
* `docs/project-memory/shared-memory/pitfalls.md` 的「资源画布工具条的『改造』
* 『角色动画』按钮点了没反应」)。
*/
describe('resource canvas toolbar actions', () => {
it('已登记 manifest 资产的栅格图片只放行真实接通的快速编辑', () => {
expect(toolbarActions(createManifest(), createResource())).toEqual([
'quick-edit',
]);
});
it('音频与视频资源不再放行宿主编排层空回调的改造动作', () => {
const manifest = createManifest();
expect(
toolbarActions(
manifest,
createResource({
id: 'asset:bgm',
category: 'audio',
subtype: 'background-music',
label: '背景音乐',
path: 'assets/bgm.mp3',
mediaType: 'audio/mpeg',
manifestAssetId: 'bgm',
}),
),
).toEqual([]);
expect(
toolbarActions(
manifest,
createResource({
id: 'asset:intro',
subtype: 'video',
label: '开场动画',
path: 'assets/intro.mp4',
mediaType: 'video/mp4',
manifestAssetId: 'intro',
}),
),
).toEqual([]);
});
it('角色资源只放行快速编辑,不再放行空回调的角色动画动作', () => {
expect(
toolbarActions(
createManifest(),
createResource({ id: 'asset:hero-action', subtype: 'character' }),
),
).toEqual(['quick-edit']);
});
it('未登记 manifest 资产且没有已完成任务产物背书的资源不放行任何动作', () => {
expect(
toolbarActions(
createManifest(),
createResource({ manifestAssetId: null, producerTaskId: 'task-art' }),
),
).toEqual([]);
});
it('只认已完成任务的栅格产物,未完成任务的产物不放行快速编辑', () => {
const artifactPath = 'assets/hero.png';
const completedManifest = createManifestWithProducingTask(
'completed',
artifactPath,
);
expect(
toolbarActions(
completedManifest,
createResource({
manifestAssetId: null,
producerTaskId: completedManifest.tasks[0].id,
}),
),
).toEqual(['quick-edit']);
const runningManifest = createManifestWithProducingTask(
'running',
artifactPath,
);
expect(
toolbarActions(
runningManifest,
createResource({
manifestAssetId: null,
producerTaskId: runningManifest.tasks[0].id,
}),
),
).toEqual([]);
});
it('非栅格图片不进快速编辑的派生门禁', () => {
expect(
toolbarActions(
createManifest(),
createResource({
id: 'asset:icon',
path: 'assets/icon.svg',
mediaType: 'image/svg+xml',
}),
),
).toEqual([]);
});
});