拆分图片画布编辑器前端模型
抽出编辑器共享类型、画布模型、生成模型和导出模型 补充模型层单测覆盖素材、吸附、生成快照和导出规则 新增前端拆分计划并更新 TRACKING 浏览器回归记录
This commit is contained in:
118
src/components/image-editor/ImageCanvasExportModel.test.ts
Normal file
118
src/components/image-editor/ImageCanvasExportModel.test.ts
Normal file
@@ -0,0 +1,118 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import {
|
||||
buildLayerExportMetadata,
|
||||
dataUrlToBlob,
|
||||
formatExportDate,
|
||||
getImageExtensionFromTypeOrSrc,
|
||||
getLayerExportKey,
|
||||
sanitizeExportFilePart,
|
||||
} from './ImageCanvasExportModel';
|
||||
import type { CanvasLayer } from './ImageCanvasEditorTypes';
|
||||
|
||||
describe('ImageCanvasExportModel', () => {
|
||||
it('normalizes export file names and dates', () => {
|
||||
expect(sanitizeExportFilePart(' 角色/草图:*? ', 'fallback')).toBe(
|
||||
'角色 草图',
|
||||
);
|
||||
expect(sanitizeExportFilePart(' ', 'fallback')).toBe('fallback');
|
||||
expect(formatExportDate(new Date('2026-06-17T01:02:03.000Z'))).toBe(
|
||||
'20260617',
|
||||
);
|
||||
});
|
||||
|
||||
it('chooses stable image export keys by persistence identity', () => {
|
||||
expect(
|
||||
getLayerExportKey({
|
||||
...buildLayer(),
|
||||
assetObjectId: 'object-1',
|
||||
objectKey: 'object-key',
|
||||
sourceAssetId: 'asset-1',
|
||||
src: 'data:image/png;base64,one',
|
||||
}),
|
||||
).toBe('object-1');
|
||||
expect(
|
||||
getLayerExportKey({
|
||||
...buildLayer(),
|
||||
objectKey: 'object-key',
|
||||
sourceAssetId: 'asset-1',
|
||||
src: 'data:image/png;base64,one',
|
||||
}),
|
||||
).toBe('object-key');
|
||||
});
|
||||
|
||||
it('detects image extensions from content type before falling back to src', () => {
|
||||
expect(getImageExtensionFromTypeOrSrc('image/jpeg', '/image.png')).toBe(
|
||||
'jpg',
|
||||
);
|
||||
expect(getImageExtensionFromTypeOrSrc('', '/image.webp?x=1')).toBe('webp');
|
||||
expect(getImageExtensionFromTypeOrSrc('', '/image.unknown')).toBe('png');
|
||||
});
|
||||
|
||||
it('converts data URLs and builds layer metadata for manifest files', async () => {
|
||||
const blob = dataUrlToBlob('data:text/plain;base64,SGVsbG8=');
|
||||
expect(blob.type).toBe('text/plain');
|
||||
expect(await blob.text()).toBe('Hello');
|
||||
|
||||
expect(buildLayerExportMetadata(buildLayer(), 'images/001-layer.png')).toEqual({
|
||||
layerId: 'layer-1',
|
||||
title: '导出图层',
|
||||
file: 'images/001-layer.png',
|
||||
sourceType: 'generated',
|
||||
prompt: '生成提示',
|
||||
actualPrompt: '实际提示',
|
||||
model: 'gpt-image-2',
|
||||
provider: 'VectorEngine',
|
||||
taskId: 'task-1',
|
||||
objectKey: 'generated/layer.png',
|
||||
assetObjectId: undefined,
|
||||
sourceResourceId: 'source-resource',
|
||||
sourceAssetId: 'asset-1',
|
||||
exportError: undefined,
|
||||
canvas: {
|
||||
x: 10,
|
||||
y: 20,
|
||||
width: 512,
|
||||
height: 512,
|
||||
originalWidth: 1024,
|
||||
originalHeight: 1024,
|
||||
zIndex: 3,
|
||||
groupId: 'group-1',
|
||||
hidden: true,
|
||||
locked: false,
|
||||
flipX: true,
|
||||
flipY: false,
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function buildLayer(): CanvasLayer {
|
||||
return {
|
||||
id: 'layer-1',
|
||||
resourceId: 'resource-1',
|
||||
title: '导出图层',
|
||||
src: 'data:image/png;base64,one',
|
||||
x: 10,
|
||||
y: 20,
|
||||
width: 512,
|
||||
height: 512,
|
||||
originalWidth: 1024,
|
||||
originalHeight: 1024,
|
||||
zIndex: 3,
|
||||
sourceType: 'generated',
|
||||
prompt: '生成提示',
|
||||
actualPrompt: '实际提示',
|
||||
model: 'gpt-image-2',
|
||||
provider: 'VectorEngine',
|
||||
taskId: 'task-1',
|
||||
objectKey: 'generated/layer.png',
|
||||
sourceResourceId: 'source-resource',
|
||||
sourceAssetId: 'asset-1',
|
||||
groupId: 'group-1',
|
||||
hidden: true,
|
||||
locked: false,
|
||||
flipX: true,
|
||||
flipY: false,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user