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 { return { id: 'project', label: '项目素材', collapsed: false, systemDefault: true, persisted: true, ...overrides, }; } function createAsset(overrides: Partial = {}): 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(), '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' }); }); });