import { describe, expect, test } from 'vitest'; import { resolveResourceInfoFieldRows, resolveResourceInfoPanelStyle, } from '../src/features/resource-canvas/resourceCanvasInfoModel'; import type { ProjectResource } from '../src/view/project-development/resourceProjectionModel'; function resourceFixture(overrides: Partial): ProjectResource { return { id: 'asset:hero', category: 'character', subtype: 'character', label: 'hero.png', path: 'assets/hero.png', mediaType: 'image/png', sourceLabel: 'Agent 生成', taskTitle: null, manifestAssetId: 'hero', producerTaskId: null, externalResourceId: null, referenceResourceIds: [], dependencies: [], dependencyDepth: 0, ...overrides, }; } const SOURCE_LAYER = { id: 'asset:hero', resourceId: 'asset:hero', title: 'hero.png', src: 'blob:hero', x: 100, y: 40, width: 200, height: 120, originalWidth: 200, originalHeight: 120, zIndex: 1, sourceType: 'generated' as const, }; describe('resourceCanvasInfoModel', () => { test('字段固定为名称 / 路径 / 类型 + 只读分类与标签', () => { expect(resolveResourceInfoFieldRows(resourceFixture({}))).toEqual([ { label: '名称', value: 'hero.png' }, { label: '路径', value: 'assets/hero.png' }, { label: '类型', value: 'image/png' }, { label: '分类', value: '角色与对象' }, { label: '标签', value: '暂无标签' }, ]); // 面板是给用户看的事实卡:manifest 内部标识(来源任务、资产 ID、producer)不上桌。 const labels = resolveResourceInfoFieldRows(resourceFixture({})).map( (row) => row.label, ); for (const forbidden of ['来源任务', '资产 ID', 'ID', '来源']) { expect(labels).not.toContain(forbidden); } }); test('分类跟随画布栏目口径,标签只读拼接', () => { expect( resolveResourceInfoFieldRows( resourceFixture({ category: 'version', assetCategory: undefined }), ).find((row) => row.label === '分类')?.value, ).toBe('项目版本'); expect( resolveResourceInfoFieldRows( resourceFixture({ category: 'unclassified', assetCategory: undefined }), ).find((row) => row.label === '分类')?.value, ).toBe('待归类'); expect( resolveResourceInfoFieldRows( resourceFixture({ assetTags: ['主角', '待定稿'] }), ).find((row) => row.label === '标签')?.value, ).toBe('主角、待定稿'); }); test('浮层锚点与快速编辑浮层同一条几何:没有选中图层就没有面板', () => { const anchor = { sourceLayer: SOURCE_LAYER, viewport: { scale: 1, x: 0, y: 0 }, canvasSize: { width: 900, height: 600 }, }; // 锚在图层下沿居中 + 12px 间距(与 `resolveQuickEditPanelStyle` 同口径)。 expect(resolveResourceInfoPanelStyle(anchor)).toEqual({ left: 200, top: 172, }); expect( resolveResourceInfoPanelStyle({ ...anchor, sourceLayer: null }), ).toBeNull(); }); });