mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-03-22 13:42:12 -03:00
Add new move_recipes_bulk endpoint to handle moving multiple recipes simultaneously. This improves efficiency when reorganizing recipe collections by allowing batch operations instead of individual moves. - Add move_recipes_bulk handler method with proper error handling - Register new POST /api/lm/recipes/move-bulk route - Implement bulk move logic in persistence service - Validate required parameters (recipe_ids and target_path) - Handle common error cases including validation, not found, and server errors
265 lines
9.5 KiB
JavaScript
265 lines
9.5 KiB
JavaScript
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
import { renderTemplate, resetDom } from '../utils/domFixtures.js';
|
|
|
|
const loadingManagerInstance = { showSimpleLoading: vi.fn(), hide: vi.fn() };
|
|
const exampleImagesManagerInitialize = vi.fn();
|
|
const exampleImagesManagerInstance = { initialize: exampleImagesManagerInitialize };
|
|
const bulkContextMenuInstance = { menu: 'bulk-context' };
|
|
const headerManagerInstance = { type: 'header-manager' };
|
|
|
|
vi.mock('../../../static/js/managers/LoadingManager.js', () => ({
|
|
LoadingManager: vi.fn(() => loadingManagerInstance),
|
|
}));
|
|
|
|
vi.mock('../../../static/js/managers/ModalManager.js', () => ({
|
|
modalManager: { initialize: vi.fn() },
|
|
}));
|
|
|
|
vi.mock('../../../static/js/managers/UpdateService.js', () => ({
|
|
updateService: { initialize: vi.fn() },
|
|
}));
|
|
|
|
vi.mock('../../../static/js/components/Header.js', () => ({
|
|
HeaderManager: vi.fn(() => headerManagerInstance),
|
|
}));
|
|
|
|
vi.mock('../../../static/js/managers/SettingsManager.js', () => ({
|
|
settingsManager: {
|
|
waitForInitialization: vi.fn().mockResolvedValue(undefined),
|
|
},
|
|
}));
|
|
|
|
vi.mock('../../../static/js/managers/MoveManager.js', () => ({
|
|
moveManager: { initialize: vi.fn() },
|
|
}));
|
|
|
|
vi.mock('../../../static/js/managers/BulkManager.js', () => ({
|
|
bulkManager: {
|
|
initialize: vi.fn(),
|
|
setBulkContextMenu: vi.fn(),
|
|
},
|
|
}));
|
|
|
|
vi.mock('../../../static/js/managers/ExampleImagesManager.js', () => ({
|
|
ExampleImagesManager: vi.fn(() => exampleImagesManagerInstance),
|
|
}));
|
|
|
|
vi.mock('../../../static/js/managers/HelpManager.js', () => ({
|
|
helpManager: {
|
|
initialize: vi.fn(),
|
|
},
|
|
}));
|
|
|
|
vi.mock('../../../static/js/managers/BannerService.js', () => ({
|
|
bannerService: {
|
|
initialize: vi.fn(),
|
|
isBannerVisible: vi.fn().mockReturnValue(false),
|
|
},
|
|
}));
|
|
|
|
vi.mock('../../../static/js/utils/uiHelpers.js', () => ({
|
|
initTheme: vi.fn(),
|
|
initBackToTop: vi.fn(),
|
|
showToast: vi.fn(),
|
|
}));
|
|
|
|
vi.mock('../../../static/js/i18n/index.js', () => ({
|
|
i18n: {
|
|
waitForReady: vi.fn().mockResolvedValue(undefined),
|
|
getCurrentLocale: vi.fn().mockReturnValue('en'),
|
|
},
|
|
}));
|
|
|
|
vi.mock('../../../static/js/managers/OnboardingManager.js', () => ({
|
|
onboardingManager: {
|
|
start: vi.fn(),
|
|
},
|
|
}));
|
|
|
|
vi.mock('../../../static/js/components/ContextMenu/BulkContextMenu.js', () => ({
|
|
BulkContextMenu: vi.fn(() => bulkContextMenuInstance),
|
|
}));
|
|
|
|
vi.mock('../../../static/js/utils/eventManagementInit.js', () => ({
|
|
initializeEventManagement: vi.fn(),
|
|
}));
|
|
|
|
vi.mock('../../../static/js/utils/infiniteScroll.js', () => ({
|
|
initializeInfiniteScroll: vi.fn(),
|
|
}));
|
|
|
|
vi.mock('../../../static/js/components/ContextMenu/index.js', () => ({
|
|
createPageContextMenu: vi.fn((pageType) => ({ pageType })),
|
|
createGlobalContextMenu: vi.fn(() => ({ type: 'global' })),
|
|
}));
|
|
|
|
import { appCore } from '../../../static/js/core.js';
|
|
import { state } from '../../../static/js/state/index.js';
|
|
import { LoadingManager } from '../../../static/js/managers/LoadingManager.js';
|
|
import { modalManager } from '../../../static/js/managers/ModalManager.js';
|
|
import { updateService } from '../../../static/js/managers/UpdateService.js';
|
|
import { settingsManager } from '../../../static/js/managers/SettingsManager.js';
|
|
import { moveManager } from '../../../static/js/managers/MoveManager.js';
|
|
import { bulkManager } from '../../../static/js/managers/BulkManager.js';
|
|
import { ExampleImagesManager } from '../../../static/js/managers/ExampleImagesManager.js';
|
|
import { helpManager } from '../../../static/js/managers/HelpManager.js';
|
|
import { bannerService } from '../../../static/js/managers/BannerService.js';
|
|
import { initTheme, initBackToTop } from '../../../static/js/utils/uiHelpers.js';
|
|
import { onboardingManager } from '../../../static/js/managers/OnboardingManager.js';
|
|
import { BulkContextMenu } from '../../../static/js/components/ContextMenu/BulkContextMenu.js';
|
|
import { HeaderManager } from '../../../static/js/components/Header.js';
|
|
import { initializeEventManagement } from '../../../static/js/utils/eventManagementInit.js';
|
|
import { initializeInfiniteScroll } from '../../../static/js/utils/infiniteScroll.js';
|
|
import { createPageContextMenu, createGlobalContextMenu } from '../../../static/js/components/ContextMenu/index.js';
|
|
|
|
const SUPPORTED_PAGES = ['loras', 'recipes', 'checkpoints', 'embeddings'];
|
|
|
|
describe('AppCore page orchestration', () => {
|
|
beforeEach(() => {
|
|
resetDom();
|
|
delete window.pageContextMenu;
|
|
delete window.globalContextMenuInstance;
|
|
vi.clearAllMocks();
|
|
});
|
|
|
|
afterEach(() => {
|
|
vi.restoreAllMocks();
|
|
});
|
|
|
|
it.each(SUPPORTED_PAGES)('initializes page features for %s pages', (pageType) => {
|
|
renderTemplate('loras.html', { dataset: { page: pageType } });
|
|
const contextSpy = vi.spyOn(appCore, 'initializeContextMenus');
|
|
|
|
appCore.initializePageFeatures();
|
|
|
|
expect(contextSpy).toHaveBeenCalledWith(pageType);
|
|
expect(initializeInfiniteScroll).toHaveBeenCalledWith(pageType);
|
|
});
|
|
|
|
it('skips initialization when page type is unsupported', () => {
|
|
renderTemplate('statistics.html', { dataset: { page: 'statistics' } });
|
|
const contextSpy = vi.spyOn(appCore, 'initializeContextMenus');
|
|
|
|
appCore.initializePageFeatures();
|
|
|
|
expect(contextSpy).not.toHaveBeenCalled();
|
|
expect(initializeInfiniteScroll).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('creates page and global context menus on first initialization', () => {
|
|
const pageMenu = { menu: 'page' };
|
|
const globalMenu = { menu: 'global' };
|
|
createPageContextMenu.mockReturnValueOnce(pageMenu);
|
|
createGlobalContextMenu.mockReturnValueOnce(globalMenu);
|
|
|
|
appCore.initializeContextMenus('loras');
|
|
|
|
expect(createPageContextMenu).toHaveBeenCalledWith('loras');
|
|
expect(window.pageContextMenu).toBe(pageMenu);
|
|
expect(createGlobalContextMenu).toHaveBeenCalledTimes(1);
|
|
expect(window.globalContextMenuInstance).toBe(globalMenu);
|
|
});
|
|
|
|
it('reuses the existing global context menu instance on subsequent calls', () => {
|
|
const existingGlobalMenu = { menu: 'existing' };
|
|
window.globalContextMenuInstance = existingGlobalMenu;
|
|
|
|
appCore.initializeContextMenus('loras');
|
|
|
|
expect(createGlobalContextMenu).not.toHaveBeenCalled();
|
|
expect(window.globalContextMenuInstance).toBe(existingGlobalMenu);
|
|
});
|
|
});
|
|
|
|
describe('AppCore initialization flow', () => {
|
|
beforeEach(() => {
|
|
vi.useFakeTimers();
|
|
vi.clearAllMocks();
|
|
resetDom();
|
|
document.body.className = '';
|
|
appCore.initialized = false;
|
|
state.loadingManager = undefined;
|
|
state.currentPageType = 'loras';
|
|
state.global.settings.card_info_display = 'always';
|
|
delete window.modalManager;
|
|
delete window.settingsManager;
|
|
delete window.exampleImagesManager;
|
|
delete window.helpManager;
|
|
delete window.moveManager;
|
|
delete window.bulkManager;
|
|
delete window.headerManager;
|
|
delete window.i18n;
|
|
delete window.pageContextMenu;
|
|
delete window.globalContextMenuInstance;
|
|
});
|
|
|
|
afterEach(async () => {
|
|
await vi.runAllTimersAsync();
|
|
vi.clearAllTimers();
|
|
vi.useRealTimers();
|
|
});
|
|
|
|
it('initializes core managers and global references', async () => {
|
|
state.global.settings.card_info_display = 'hover';
|
|
|
|
const result = await appCore.initialize();
|
|
|
|
expect(result).toBe(appCore);
|
|
expect(window.i18n).toBeDefined();
|
|
expect(settingsManager.waitForInitialization).toHaveBeenCalledTimes(1);
|
|
expect(LoadingManager).toHaveBeenCalledTimes(1);
|
|
expect(state.loadingManager).toBe(loadingManagerInstance);
|
|
expect(modalManager.initialize).toHaveBeenCalledTimes(1);
|
|
expect(updateService.initialize).toHaveBeenCalledTimes(1);
|
|
expect(bannerService.initialize).toHaveBeenCalledTimes(1);
|
|
expect(window.modalManager).toBe(modalManager);
|
|
expect(window.settingsManager).toBe(settingsManager);
|
|
expect(window.moveManager).toBe(moveManager);
|
|
expect(window.bulkManager).toBe(bulkManager);
|
|
expect(HeaderManager).toHaveBeenCalledTimes(1);
|
|
expect(window.headerManager).toBe(headerManagerInstance);
|
|
expect(initTheme).toHaveBeenCalledTimes(1);
|
|
expect(initBackToTop).toHaveBeenCalledTimes(1);
|
|
expect(bulkManager.initialize).toHaveBeenCalledTimes(1);
|
|
expect(BulkContextMenu).toHaveBeenCalledTimes(1);
|
|
expect(bulkManager.setBulkContextMenu).toHaveBeenCalledWith(bulkContextMenuInstance);
|
|
expect(ExampleImagesManager).toHaveBeenCalledTimes(1);
|
|
expect(window.exampleImagesManager).toBe(exampleImagesManagerInstance);
|
|
expect(exampleImagesManagerInitialize).toHaveBeenCalledTimes(1);
|
|
expect(helpManager.initialize).toHaveBeenCalledTimes(1);
|
|
expect(document.body.classList.contains('hover-reveal')).toBe(true);
|
|
expect(initializeEventManagement).toHaveBeenCalledTimes(1);
|
|
expect(onboardingManager.start).not.toHaveBeenCalled();
|
|
|
|
await vi.runAllTimersAsync();
|
|
|
|
expect(onboardingManager.start).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it('does not reinitialize once initialized', async () => {
|
|
await appCore.initialize();
|
|
await vi.runAllTimersAsync();
|
|
|
|
vi.clearAllMocks();
|
|
|
|
const result = await appCore.initialize();
|
|
|
|
expect(result).toBeUndefined();
|
|
expect(LoadingManager).not.toHaveBeenCalled();
|
|
expect(modalManager.initialize).not.toHaveBeenCalled();
|
|
expect(updateService.initialize).not.toHaveBeenCalled();
|
|
expect(ExampleImagesManager).not.toHaveBeenCalled();
|
|
expect(onboardingManager.start).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('initializes bulk setup when viewing recipes', async () => {
|
|
state.currentPageType = 'recipes';
|
|
|
|
await appCore.initialize();
|
|
|
|
expect(bulkManager.initialize).toHaveBeenCalledTimes(1);
|
|
expect(BulkContextMenu).toHaveBeenCalledTimes(1);
|
|
expect(bulkManager.setBulkContextMenu).toHaveBeenCalledTimes(1);
|
|
});
|
|
});
|