Files
Genarrative/src/components/image-editor/ImageCanvasAssetLibraryModel.test.ts
kdletters 6e8089c297 拆分编辑器素材库模型
抽出素材库分组和本地状态变更规则

补充素材库模型单测

更新 TRACKING.md 记录第三十七阶段验证
2026-06-17 18:15:34 +08:00

188 lines
5.3 KiB
TypeScript

import { describe, expect, it } from 'vitest';
import type {
EditorAsset,
EditorAssetFolder,
} from './ImageCanvasEditorTypes';
import {
areAllSelectableAssetsSelected,
createLocalAssetFolder,
deleteAssetFolderLocally,
getSelectableAssets,
groupAssetsByFolder,
moveAssetToFolderLocally,
removeAssetById,
removeSelectedAssets,
renameAssetById,
renameAssetFolderById,
replaceLocalAssetFolder,
resolveAllAssetSelection,
resolveDefaultAssetFolder,
toggleAssetFolderCollapsed,
toggleAssetSelection,
} from './ImageCanvasAssetLibraryModel';
function createFolder(
overrides: Partial<EditorAssetFolder> = {},
): EditorAssetFolder {
return {
id: 'project',
label: '项目素材',
collapsed: false,
systemDefault: true,
persisted: true,
...overrides,
};
}
function createAsset(overrides: Partial<EditorAsset> = {}): EditorAsset {
return {
id: 'asset-a',
label: '素材A',
src: 'data:image/png;base64,YQ==',
width: 320,
height: 240,
folderId: 'project',
sourceKind: 'uploaded',
sourceType: 'uploaded',
persisted: true,
...overrides,
};
}
describe('ImageCanvasAssetLibraryModel', () => {
it('groups assets by folder and resolves selectable uploaded assets', () => {
const folders = [
createFolder(),
createFolder({ id: 'folder-role', label: '角色素材' }),
];
const assets = [
createAsset({ id: 'asset-a', folderId: 'project' }),
createAsset({
id: 'asset-b',
label: '素材B',
folderId: 'folder-role',
}),
createAsset({
id: 'built-in',
label: '内置素材',
sourceKind: 'built-in',
}),
];
expect(groupAssetsByFolder(folders, assets)).toMatchObject([
{ id: 'project', assets: [{ id: 'asset-a' }, { id: 'built-in' }] },
{ id: 'folder-role', assets: [{ id: 'asset-b' }] },
]);
expect(getSelectableAssets(assets).map((asset) => asset.id)).toEqual([
'asset-a',
'asset-b',
]);
});
it('renames assets, toggles folders and creates local folders', () => {
expect(renameAssetById([createAsset()], 'asset-a', '新名字')[0]).toMatchObject(
{ label: '新名字' },
);
expect(
renameAssetFolderById([createFolder()], 'project', '默认素材')[0],
).toMatchObject({ label: '默认素材' });
expect(toggleAssetFolderCollapsed([createFolder()], 'project')[0]).toMatchObject(
{ collapsed: true },
);
expect(
createLocalAssetFolder({
folderId: 'folder-local',
label: '角色素材',
}),
).toEqual({
id: 'folder-local',
label: '角色素材',
collapsed: false,
systemDefault: false,
persisted: false,
});
});
it('replaces local folder ids in folders and contained assets', () => {
const result = replaceLocalAssetFolder({
folders: [createFolder(), createFolder({ id: 'folder-local' })],
assets: [createAsset({ folderId: 'folder-local' })],
localFolderId: 'folder-local',
persistedFolder: {
folderId: 'folder-role',
label: '角色素材',
collapsed: false,
systemDefault: false,
},
});
expect(result.folders[1]).toEqual({
id: 'folder-role',
label: '角色素材',
collapsed: false,
systemDefault: false,
persisted: true,
});
expect(result.assets[0]).toMatchObject({ folderId: 'folder-role' });
});
it('deletes assets and moves deleted folder contents to the default folder', () => {
const folders = [
createFolder(),
createFolder({ id: 'folder-role', systemDefault: false }),
];
const assets = [
createAsset({ id: 'asset-a', folderId: 'folder-role' }),
createAsset({ id: 'asset-b', folderId: 'project' }),
];
expect(removeAssetById(assets, 'asset-a').map((asset) => asset.id)).toEqual([
'asset-b',
]);
expect(resolveDefaultAssetFolder(folders)?.id).toBe('project');
expect(
deleteAssetFolderLocally({
folders,
assets,
folderId: 'folder-role',
defaultFolderId: 'project',
}),
).toMatchObject({
folders: [{ id: 'project' }],
assets: [
{ id: 'asset-a', folderId: 'project' },
{ id: 'asset-b', folderId: 'project' },
],
});
});
it('toggles selection, selects all uploaded assets and removes selected assets', () => {
const assets = [
createAsset({ id: 'asset-a' }),
createAsset({ id: 'asset-b' }),
];
const selected = toggleAssetSelection(new Set<string>(), 'asset-a');
expect([...selected]).toEqual(['asset-a']);
expect([...toggleAssetSelection(selected, 'asset-a')]).toEqual([]);
expect(areAllSelectableAssetsSelected(assets, new Set(['asset-a']))).toBe(
false,
);
const allSelected = resolveAllAssetSelection({
allSelectableAssetsSelected: false,
selectableAssets: assets,
});
expect([...allSelected]).toEqual(['asset-a', 'asset-b']);
const removal = removeSelectedAssets(assets, new Set(['asset-b']));
expect(removal.assets.map((asset) => asset.id)).toEqual(['asset-a']);
expect(removal.deletedAssets.map((asset) => asset.id)).toEqual(['asset-b']);
});
it('moves assets between folders locally', () => {
expect(
moveAssetToFolderLocally([createAsset()], 'asset-a', 'folder-role')[0],
).toMatchObject({ folderId: 'folder-role' });
});
});