Files
Genarrative/apps/ai-game-creator-shell/tests/bindingOverview.test.ts
T
k88936 6ac2931bb2 显式化 UI 编辑器字体来源
新增 SystemFont 与 Bound 字体来源领域枚举并生成 TypeScript 契约

将系统字体统计为已绑定并保留字体资源校验

更新文本编辑器、预览与绑定概览回归测试
2026-08-18 21:10:27 +08:00

132 lines
3.4 KiB
TypeScript

import { describe, expect, it } from 'vitest';
import {
getBindingOverview,
nodeHasBlockedComponents,
nodeHasPendingBinding,
nodeNeedsComponentReview,
} from '../src/features/ui-editor/bindingOverview';
import { getNextMatchingUiTreeNodeTarget } from '../src/features/ui-editor/stageStatusOverview';
import type { Component } from '../src/features/ui-editor/types/Component';
import type { Node } from '../src/features/ui-editor/types/Node';
import type { SpriteAsset } from '../src/features/ui-editor/types/SpriteAsset';
import type { UITree } from '../src/features/ui-editor/types/UITree';
function image(targetGraphic: string | null): Component {
return {
Image: {
target_graphic: targetGraphic,
image_type: { Simple: { preserve_aspect: false } },
},
};
}
function text(font: 'SystemFont' | { Bound: string }): Component {
return {
Text: {
content: '文本',
font,
font_style: 'Normal',
font_sizing: { Fixed: 14 },
color: [255, 255, 255, 255],
alignment: 'UpperLeft',
horizontal_overflow: 'Wrap',
vertical_overflow: 'Truncate',
line_spacing: 1,
},
};
}
function node(
id: string,
components: Component[],
componentsStatus: Node['metadata']['components_status'] = 'Passed',
children: Node[] = [],
): Node {
return {
id,
transform: {
anchor_min: [0, 0],
anchor_max: [1, 1],
offset_min: [0, 0],
offset_max: [0, 0],
},
metadata: {
name: id,
description: '',
layout_status: 'Passed',
components_status: componentsStatus,
allow_llm_edit_layout: true,
allow_llm_edit_component: true,
source: 'System',
},
components,
children,
};
}
function sprite(id: string): SpriteAsset {
return {
asset_id: id,
metadata: { name: id, asset_type: 'Normal' },
path: `${id}.png`,
pixel_size: [16, 16],
pixels_per_unit: 1,
border: { left: 0, right: 0, top: 0, bottom: 0 },
};
}
const sprites = {
validSprite: sprite('validSprite'),
unused: sprite('unused'),
};
const trees: UITree[] = [
{
src_ui_design: 'page-a',
root: node(
'root',
[image('validSprite'), text({ Bound: 'font-id' })],
'Passed',
[
node('review', [image(null)], { NeedReview: '确认素材' }),
node('blocked', [text('SystemFont')], 'Blocked'),
],
),
},
];
describe('getBindingOverview', () => {
it('uses per-component helpers to include both image and text slots', () => {
expect(getBindingOverview(trees, sprites)).toEqual({
componentsNeedingAssets: 4,
assetSlots: 4,
boundSlots: 3,
pendingSlots: 1,
needsAttention: 2,
blocked: 1,
independentAssets: 2,
});
});
it('reuses the common preorder next-target search for every binding queue', () => {
expect(
getNextMatchingUiTreeNodeTarget(trees, null, (target) =>
nodeHasPendingBinding(target),
)?.node.id,
).toBe('review');
expect(
getNextMatchingUiTreeNodeTarget(trees, 'review', (target) =>
nodeHasPendingBinding(target),
)?.node.id,
).toBe('review');
expect(
getNextMatchingUiTreeNodeTarget(trees, null, nodeNeedsComponentReview)
?.node.id,
).toBe('review');
expect(
getNextMatchingUiTreeNodeTarget(trees, null, nodeHasBlockedComponents)
?.node.id,
).toBe('blocked');
});
});