拆分编辑器素材库模型
抽出素材库分组和本地状态变更规则 补充素材库模型单测 更新 TRACKING.md 记录第三十七阶段验证
This commit is contained in:
187
src/components/image-editor/ImageCanvasAssetLibraryModel.test.ts
Normal file
187
src/components/image-editor/ImageCanvasAssetLibraryModel.test.ts
Normal file
@@ -0,0 +1,187 @@
|
||||
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' });
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user