重写 UI 设计文档桥接单测到当前入口
- 退役 ensureUiDesignResourceForPrototype / findLinkedUiDesignResource 的 6 条旧用例已随实现删除而无对应导出,测试文件整体失败 - 改为覆盖 createUiDesignDocFromImages:空图片列表拒绝且不调用命令,正常调用透传项目身份与设计图引用
This commit is contained in:
@@ -1,201 +1,43 @@
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import type {
|
||||
GameCreationAppAssetManifestEntry,
|
||||
GameCreationAppManifest,
|
||||
} from '../../../packages/shared/src/contracts/gameCreationApp';
|
||||
import {
|
||||
GAME_CREATION_APP_UI_DESIGN_DOC_ASSET_KIND,
|
||||
GAME_CREATION_APP_UI_DESIGN_DOC_MEDIA_TYPE,
|
||||
} from '../../../packages/shared/src/contracts/gameCreationApp';
|
||||
import {
|
||||
ensureUiDesignResourceForPrototype,
|
||||
findLinkedUiDesignResource,
|
||||
} from '../src/features/ui-editor/uiDesignResourceBridge';
|
||||
|
||||
function manifestWithPrototype(): GameCreationAppManifest {
|
||||
const prototype: GameCreationAppAssetManifestEntry = {
|
||||
id: 'prototype-asset',
|
||||
kind: 'ui-design',
|
||||
mediaType: 'image/png',
|
||||
localPath: 'assets/ui-prototype.png',
|
||||
source: { kind: 'canvas', referenceResourceIds: [] },
|
||||
imageSequenceFrames: null,
|
||||
imageSequenceDurationMs: null,
|
||||
};
|
||||
return {
|
||||
schemaVersion: 'game-creator-app.v1',
|
||||
projectId: 'project-1',
|
||||
projectName: 'UI bridge',
|
||||
tasks: [],
|
||||
assets: [prototype],
|
||||
versions: [],
|
||||
godotProjectRoot: null,
|
||||
preview: null,
|
||||
resourceCanvasLayouts: [],
|
||||
} as unknown as GameCreationAppManifest;
|
||||
}
|
||||
import { createUiDesignDocFromImages } from '../src/features/ui-editor/uiDesignResourceBridge';
|
||||
|
||||
describe('ui design resource bridge', () => {
|
||||
it('finds only a JSON UI resource linked to the exact prototype asset', () => {
|
||||
const manifest = manifestWithPrototype();
|
||||
manifest.assets.push({
|
||||
id: 'unrelated-ui',
|
||||
kind: GAME_CREATION_APP_UI_DESIGN_DOC_ASSET_KIND,
|
||||
mediaType: GAME_CREATION_APP_UI_DESIGN_DOC_MEDIA_TYPE,
|
||||
localPath: 'ui/unrelated.json',
|
||||
source: { kind: 'generated', referenceResourceIds: ['other'] },
|
||||
imageSequenceFrames: null,
|
||||
imageSequenceDurationMs: null,
|
||||
});
|
||||
expect(findLinkedUiDesignResource(manifest, 'prototype-asset')).toBeNull();
|
||||
manifest.assets.push({
|
||||
id: 'linked-ui',
|
||||
kind: GAME_CREATION_APP_UI_DESIGN_DOC_ASSET_KIND,
|
||||
mediaType: GAME_CREATION_APP_UI_DESIGN_DOC_MEDIA_TYPE,
|
||||
localPath: 'ui/linked.json',
|
||||
source: { kind: 'generated', referenceResourceIds: ['prototype-asset'] },
|
||||
imageSequenceFrames: null,
|
||||
imageSequenceDurationMs: null,
|
||||
});
|
||||
expect(findLinkedUiDesignResource(manifest, 'prototype-asset')?.id).toBe(
|
||||
'linked-ui',
|
||||
);
|
||||
});
|
||||
|
||||
it('reuses workflow resources linked by the prototype canonical resource id', () => {
|
||||
const manifest = manifestWithPrototype();
|
||||
manifest.assets[0]!.source.resourceId = 'canvas-resource-1';
|
||||
manifest.assets.push({
|
||||
id: 'workflow-ui',
|
||||
kind: GAME_CREATION_APP_UI_DESIGN_DOC_ASSET_KIND,
|
||||
mediaType: GAME_CREATION_APP_UI_DESIGN_DOC_MEDIA_TYPE,
|
||||
localPath: 'ui/workflow.json',
|
||||
source: {
|
||||
kind: 'generated',
|
||||
resourceId: 'ui-workflow-page-1',
|
||||
referenceResourceIds: ['canvas-resource-1', 'design-resource-1'],
|
||||
},
|
||||
imageSequenceFrames: null,
|
||||
imageSequenceDurationMs: null,
|
||||
});
|
||||
expect(findLinkedUiDesignResource(manifest, 'prototype-asset')?.id).toBe(
|
||||
'workflow-ui',
|
||||
);
|
||||
});
|
||||
|
||||
it('prefers a completed workflow resource when an older bridge resource also matches', () => {
|
||||
const manifest = manifestWithPrototype();
|
||||
manifest.assets.push(
|
||||
{
|
||||
id: 'bridge-ui',
|
||||
kind: GAME_CREATION_APP_UI_DESIGN_DOC_ASSET_KIND,
|
||||
mediaType: GAME_CREATION_APP_UI_DESIGN_DOC_MEDIA_TYPE,
|
||||
localPath: 'ui/UI 设计 1.json',
|
||||
source: {
|
||||
kind: 'generated',
|
||||
referenceResourceIds: ['prototype-asset'],
|
||||
},
|
||||
imageSequenceFrames: null,
|
||||
imageSequenceDurationMs: null,
|
||||
},
|
||||
{
|
||||
id: 'completed-ui',
|
||||
kind: GAME_CREATION_APP_UI_DESIGN_DOC_ASSET_KIND,
|
||||
mediaType: GAME_CREATION_APP_UI_DESIGN_DOC_MEDIA_TYPE,
|
||||
localPath: 'ui/ui-workflow-page.json',
|
||||
source: {
|
||||
kind: 'generated',
|
||||
generationKind: 'ui-workflow.completed',
|
||||
referenceResourceIds: ['prototype-asset'],
|
||||
},
|
||||
imageSequenceFrames: null,
|
||||
imageSequenceDurationMs: null,
|
||||
},
|
||||
);
|
||||
expect(findLinkedUiDesignResource(manifest, 'prototype-asset')?.id).toBe(
|
||||
'completed-ui',
|
||||
);
|
||||
});
|
||||
|
||||
it('reuses an existing link without invoking a mutating command', async () => {
|
||||
const manifest = manifestWithPrototype();
|
||||
const linked = {
|
||||
id: 'linked-ui',
|
||||
kind: GAME_CREATION_APP_UI_DESIGN_DOC_ASSET_KIND,
|
||||
mediaType: GAME_CREATION_APP_UI_DESIGN_DOC_MEDIA_TYPE,
|
||||
localPath: 'ui/linked.json',
|
||||
source: {
|
||||
kind: 'generated' as const,
|
||||
referenceResourceIds: ['prototype-asset'],
|
||||
},
|
||||
imageSequenceFrames: null,
|
||||
imageSequenceDurationMs: null,
|
||||
};
|
||||
manifest.assets.push(linked);
|
||||
it('rejects an empty image list without invoking the command', async () => {
|
||||
const invoke = vi.fn();
|
||||
const result = await ensureUiDesignResourceForPrototype({
|
||||
await expect(
|
||||
createUiDesignDocFromImages({
|
||||
projectPath: '/project',
|
||||
expectedProjectId: 'project-1',
|
||||
images: [],
|
||||
invoke,
|
||||
}),
|
||||
).rejects.toThrow('请至少选择一张界面图');
|
||||
expect(invoke).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('creates a document through the command with the project identity injected', async () => {
|
||||
const created = {
|
||||
asset: { id: 'ui-1' },
|
||||
manifest: { projectId: 'project-1' },
|
||||
relativePath: 'ui/UI 设计 1.json',
|
||||
imageIds: ['image-a'],
|
||||
committedProjectRevision: 7,
|
||||
};
|
||||
const invoke = vi.fn().mockResolvedValue(created);
|
||||
const result = await createUiDesignDocFromImages({
|
||||
projectPath: '/project',
|
||||
manifest,
|
||||
prototypeAssetId: 'prototype-asset',
|
||||
expectedProjectId: 'project-1',
|
||||
images: [{ assetId: 'image-a', path: null }],
|
||||
invoke,
|
||||
});
|
||||
expect(result.asset).toEqual(linked);
|
||||
expect(result.created).toBe(false);
|
||||
expect(invoke).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('invokes the atomic Tauri bridge for a missing link', async () => {
|
||||
const manifest = manifestWithPrototype();
|
||||
const result = {
|
||||
asset: {
|
||||
id: 'new-ui',
|
||||
kind: GAME_CREATION_APP_UI_DESIGN_DOC_ASSET_KIND,
|
||||
mediaType: GAME_CREATION_APP_UI_DESIGN_DOC_MEDIA_TYPE,
|
||||
localPath: 'ui/UI 设计 1.json',
|
||||
source: {
|
||||
kind: 'generated' as const,
|
||||
referenceResourceIds: ['prototype-asset'],
|
||||
},
|
||||
imageSequenceFrames: null,
|
||||
imageSequenceDurationMs: null,
|
||||
},
|
||||
manifest: { ...manifest, assets: [...manifest.assets] },
|
||||
committedProjectRevision: 7,
|
||||
created: true,
|
||||
};
|
||||
const invoke = vi.fn().mockResolvedValue(result);
|
||||
await expect(
|
||||
ensureUiDesignResourceForPrototype({
|
||||
expect(invoke).toHaveBeenCalledWith('create_ui_design_doc_from_images', {
|
||||
input: {
|
||||
projectPath: '/project',
|
||||
manifest,
|
||||
prototypeAssetId: ' prototype-asset ',
|
||||
invoke,
|
||||
}),
|
||||
).resolves.toEqual(result);
|
||||
expect(invoke).toHaveBeenCalledWith(
|
||||
'ensure_ui_design_resource_for_prototype',
|
||||
{
|
||||
input: {
|
||||
projectPath: '/project',
|
||||
expectedProjectId: 'project-1',
|
||||
prototypeAssetId: 'prototype-asset',
|
||||
},
|
||||
expectedProjectId: 'project-1',
|
||||
images: [{ assetId: 'image-a', path: null }],
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it('rejects non-prototype assets before invoking Tauri', async () => {
|
||||
const manifest = manifestWithPrototype();
|
||||
const invoke = vi.fn();
|
||||
await expect(
|
||||
ensureUiDesignResourceForPrototype({
|
||||
projectPath: '/project',
|
||||
manifest,
|
||||
prototypeAssetId: 'missing',
|
||||
invoke,
|
||||
}),
|
||||
).rejects.toThrow('目标资源不是 UI 原型图片');
|
||||
expect(invoke).not.toHaveBeenCalled();
|
||||
});
|
||||
expect(result).toBe(created);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user