mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-03-21 21:22:11 -03:00
refactor: remove storage migration logic and associated tests
This commit is contained in:
@@ -12,7 +12,6 @@ import { helpManager } from './managers/HelpManager.js';
|
|||||||
import { bannerService } from './managers/BannerService.js';
|
import { bannerService } from './managers/BannerService.js';
|
||||||
import { initTheme, initBackToTop } from './utils/uiHelpers.js';
|
import { initTheme, initBackToTop } from './utils/uiHelpers.js';
|
||||||
import { initializeInfiniteScroll } from './utils/infiniteScroll.js';
|
import { initializeInfiniteScroll } from './utils/infiniteScroll.js';
|
||||||
import { migrateStorageItems } from './utils/storageHelpers.js';
|
|
||||||
import { i18n } from './i18n/index.js';
|
import { i18n } from './i18n/index.js';
|
||||||
import { onboardingManager } from './managers/OnboardingManager.js';
|
import { onboardingManager } from './managers/OnboardingManager.js';
|
||||||
import { BulkContextMenu } from './components/ContextMenu/BulkContextMenu.js';
|
import { BulkContextMenu } from './components/ContextMenu/BulkContextMenu.js';
|
||||||
@@ -123,10 +122,5 @@ export class AppCore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
|
||||||
// Migrate localStorage items to use the namespace prefix
|
|
||||||
migrateStorageItems();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Create and export a singleton instance
|
// Create and export a singleton instance
|
||||||
export const appCore = new AppCore();
|
export const appCore = new AppCore();
|
||||||
@@ -1,277 +0,0 @@
|
|||||||
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
||||||
|
|
||||||
const migrateStorageItemsMock = vi.fn();
|
|
||||||
const initializeInfiniteScrollMock = vi.fn();
|
|
||||||
const initThemeMock = vi.fn();
|
|
||||||
const initBackToTopMock = vi.fn();
|
|
||||||
const initializeEventManagementMock = vi.fn();
|
|
||||||
const createPageContextMenuMock = vi.fn().mockReturnValue('page-menu');
|
|
||||||
const createGlobalContextMenuMock = vi.fn().mockReturnValue('global-menu');
|
|
||||||
const bulkManagerInitializeMock = vi.fn();
|
|
||||||
const setBulkContextMenuMock = vi.fn();
|
|
||||||
const helpManagerInitializeMock = vi.fn();
|
|
||||||
const updateServiceInitializeMock = vi.fn();
|
|
||||||
const bannerServiceInitializeMock = vi.fn();
|
|
||||||
const isBannerVisibleMock = vi.fn().mockReturnValue(false);
|
|
||||||
const onboardingStartMock = vi.fn();
|
|
||||||
const settingsWaitMock = vi.fn().mockResolvedValue();
|
|
||||||
const i18nWaitMock = vi.fn().mockResolvedValue();
|
|
||||||
const i18nLocaleMock = vi.fn().mockReturnValue('en');
|
|
||||||
|
|
||||||
const mockState = {
|
|
||||||
currentPageType: 'loras',
|
|
||||||
global: {
|
|
||||||
settings: {
|
|
||||||
card_info_display: 'hover'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const mockModalManager = { initialize: vi.fn() };
|
|
||||||
const mockBulkManager = {
|
|
||||||
initialize: bulkManagerInitializeMock,
|
|
||||||
setBulkContextMenu: setBulkContextMenuMock
|
|
||||||
};
|
|
||||||
const mockHelpManager = { initialize: helpManagerInitializeMock };
|
|
||||||
const mockUpdateService = { initialize: updateServiceInitializeMock };
|
|
||||||
const mockBannerService = {
|
|
||||||
initialize: bannerServiceInitializeMock,
|
|
||||||
isBannerVisible: isBannerVisibleMock
|
|
||||||
};
|
|
||||||
const mockOnboardingManager = { start: onboardingStartMock };
|
|
||||||
const mockSettingsManager = { waitForInitialization: settingsWaitMock };
|
|
||||||
const mockMoveManager = {};
|
|
||||||
const mockI18n = {
|
|
||||||
waitForReady: i18nWaitMock,
|
|
||||||
getCurrentLocale: i18nLocaleMock
|
|
||||||
};
|
|
||||||
|
|
||||||
const loadingManagerInstances = [];
|
|
||||||
const HeaderManagerInstances = [];
|
|
||||||
const bulkContextMenuInstances = [];
|
|
||||||
const exampleImagesManagerInitializeMock = vi.fn();
|
|
||||||
|
|
||||||
const LoadingManagerMock = vi.fn(() => {
|
|
||||||
const instance = { id: Symbol('LoadingManager') };
|
|
||||||
loadingManagerInstances.push(instance);
|
|
||||||
return instance;
|
|
||||||
});
|
|
||||||
|
|
||||||
const HeaderManagerMock = vi.fn(() => {
|
|
||||||
const instance = { id: Symbol('HeaderManager') };
|
|
||||||
HeaderManagerInstances.push(instance);
|
|
||||||
return instance;
|
|
||||||
});
|
|
||||||
|
|
||||||
const BulkContextMenuMock = vi.fn(() => {
|
|
||||||
const instance = { id: Symbol('BulkContextMenu') };
|
|
||||||
bulkContextMenuInstances.push(instance);
|
|
||||||
return instance;
|
|
||||||
});
|
|
||||||
|
|
||||||
const ExampleImagesManagerMock = vi.fn(() => {
|
|
||||||
const instance = { initialize: exampleImagesManagerInitializeMock };
|
|
||||||
globalThis.exampleImagesManager = instance;
|
|
||||||
return instance;
|
|
||||||
});
|
|
||||||
|
|
||||||
vi.stubGlobal('exampleImagesManager', null);
|
|
||||||
|
|
||||||
vi.mock('./utils/storageHelpers.js', () => ({
|
|
||||||
migrateStorageItems: migrateStorageItemsMock
|
|
||||||
}));
|
|
||||||
|
|
||||||
vi.mock('./state/index.js', () => ({
|
|
||||||
state: mockState
|
|
||||||
}));
|
|
||||||
|
|
||||||
vi.mock('./managers/LoadingManager.js', () => ({
|
|
||||||
LoadingManager: LoadingManagerMock
|
|
||||||
}));
|
|
||||||
|
|
||||||
vi.mock('./managers/ModalManager.js', () => ({
|
|
||||||
ModalManager: vi.fn(),
|
|
||||||
modalManager: mockModalManager
|
|
||||||
}));
|
|
||||||
|
|
||||||
vi.mock('./managers/UpdateService.js', () => ({
|
|
||||||
updateService: mockUpdateService
|
|
||||||
}));
|
|
||||||
|
|
||||||
vi.mock('./components/Header.js', () => ({
|
|
||||||
HeaderManager: HeaderManagerMock
|
|
||||||
}));
|
|
||||||
|
|
||||||
vi.mock('./managers/SettingsManager.js', () => ({
|
|
||||||
settingsManager: mockSettingsManager
|
|
||||||
}));
|
|
||||||
|
|
||||||
vi.mock('./managers/MoveManager.js', () => ({
|
|
||||||
moveManager: mockMoveManager
|
|
||||||
}));
|
|
||||||
|
|
||||||
vi.mock('./managers/BulkManager.js', () => ({
|
|
||||||
bulkManager: mockBulkManager
|
|
||||||
}));
|
|
||||||
|
|
||||||
vi.mock('./managers/ExampleImagesManager.js', () => ({
|
|
||||||
ExampleImagesManager: ExampleImagesManagerMock
|
|
||||||
}));
|
|
||||||
|
|
||||||
vi.mock('./managers/HelpManager.js', () => ({
|
|
||||||
helpManager: mockHelpManager
|
|
||||||
}));
|
|
||||||
|
|
||||||
vi.mock('./managers/BannerService.js', () => ({
|
|
||||||
bannerService: mockBannerService
|
|
||||||
}));
|
|
||||||
|
|
||||||
vi.mock('./utils/uiHelpers.js', () => ({
|
|
||||||
initTheme: initThemeMock,
|
|
||||||
initBackToTop: initBackToTopMock
|
|
||||||
}));
|
|
||||||
|
|
||||||
vi.mock('./utils/infiniteScroll.js', () => ({
|
|
||||||
initializeInfiniteScroll: initializeInfiniteScrollMock
|
|
||||||
}));
|
|
||||||
|
|
||||||
vi.mock('./i18n/index.js', () => ({
|
|
||||||
i18n: mockI18n
|
|
||||||
}));
|
|
||||||
|
|
||||||
vi.mock('./managers/OnboardingManager.js', () => ({
|
|
||||||
onboardingManager: mockOnboardingManager
|
|
||||||
}));
|
|
||||||
|
|
||||||
vi.mock('./components/ContextMenu/BulkContextMenu.js', () => ({
|
|
||||||
BulkContextMenu: BulkContextMenuMock
|
|
||||||
}));
|
|
||||||
|
|
||||||
vi.mock('./components/ContextMenu/index.js', () => ({
|
|
||||||
createPageContextMenu: createPageContextMenuMock,
|
|
||||||
createGlobalContextMenu: createGlobalContextMenuMock
|
|
||||||
}));
|
|
||||||
|
|
||||||
vi.mock('./utils/eventManagementInit.js', () => ({
|
|
||||||
initializeEventManagement: initializeEventManagementMock
|
|
||||||
}));
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
vi.clearAllMocks();
|
|
||||||
document.body.innerHTML = '';
|
|
||||||
document.body.removeAttribute('data-page');
|
|
||||||
mockState.currentPageType = 'loras';
|
|
||||||
mockState.global.settings.card_info_display = 'hover';
|
|
||||||
isBannerVisibleMock.mockReturnValue(false);
|
|
||||||
loadingManagerInstances.length = 0;
|
|
||||||
HeaderManagerInstances.length = 0;
|
|
||||||
bulkContextMenuInstances.length = 0;
|
|
||||||
delete window.pageContextMenu;
|
|
||||||
delete window.globalContextMenuInstance;
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(() => {
|
|
||||||
vi.useRealTimers();
|
|
||||||
});
|
|
||||||
|
|
||||||
const loadCoreModule = async () => {
|
|
||||||
vi.resetModules();
|
|
||||||
return import('./core.js');
|
|
||||||
};
|
|
||||||
|
|
||||||
describe('AppCore module bootstrapping', () => {
|
|
||||||
it('registers storage migration on DOMContentLoaded', async () => {
|
|
||||||
const addEventListenerSpy = vi.spyOn(document, 'addEventListener');
|
|
||||||
await loadCoreModule();
|
|
||||||
|
|
||||||
expect(addEventListenerSpy).toHaveBeenCalledWith('DOMContentLoaded', expect.any(Function));
|
|
||||||
const listener = addEventListenerSpy.mock.calls.find(([event]) => event === 'DOMContentLoaded')[1];
|
|
||||||
listener();
|
|
||||||
expect(migrateStorageItemsMock).toHaveBeenCalledTimes(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('initializes managers, UI helpers, and onboarding sequence', async () => {
|
|
||||||
vi.useFakeTimers();
|
|
||||||
const { AppCore } = await loadCoreModule();
|
|
||||||
const appCore = new AppCore();
|
|
||||||
|
|
||||||
document.body.dataset.page = 'loras';
|
|
||||||
|
|
||||||
const result = await appCore.initialize();
|
|
||||||
|
|
||||||
expect(result).toBe(appCore);
|
|
||||||
expect(i18nWaitMock).toHaveBeenCalled();
|
|
||||||
expect(i18nLocaleMock).toHaveBeenCalled();
|
|
||||||
expect(settingsWaitMock).toHaveBeenCalled();
|
|
||||||
|
|
||||||
expect(LoadingManagerMock).toHaveBeenCalledTimes(1);
|
|
||||||
const loadingInstance = LoadingManagerMock.mock.results[0].value;
|
|
||||||
expect(mockState.loadingManager).toBe(loadingInstance);
|
|
||||||
|
|
||||||
expect(mockModalManager.initialize).toHaveBeenCalled();
|
|
||||||
expect(updateServiceInitializeMock).toHaveBeenCalled();
|
|
||||||
expect(bannerServiceInitializeMock).toHaveBeenCalled();
|
|
||||||
expect(window.modalManager).toBe(mockModalManager);
|
|
||||||
expect(window.settingsManager).toBe(mockSettingsManager);
|
|
||||||
expect(window.bulkManager).toBe(mockBulkManager);
|
|
||||||
expect(window.helpManager).toBe(mockHelpManager);
|
|
||||||
|
|
||||||
expect(HeaderManagerMock).toHaveBeenCalledTimes(1);
|
|
||||||
expect(initThemeMock).toHaveBeenCalled();
|
|
||||||
expect(initBackToTopMock).toHaveBeenCalled();
|
|
||||||
|
|
||||||
expect(bulkManagerInitializeMock).toHaveBeenCalled();
|
|
||||||
expect(BulkContextMenuMock).toHaveBeenCalledTimes(1);
|
|
||||||
expect(setBulkContextMenuMock).toHaveBeenCalledWith(bulkContextMenuInstances[0]);
|
|
||||||
|
|
||||||
expect(ExampleImagesManagerMock).toHaveBeenCalledTimes(1);
|
|
||||||
expect(exampleImagesManagerInitializeMock).toHaveBeenCalled();
|
|
||||||
expect(helpManagerInitializeMock).toHaveBeenCalled();
|
|
||||||
|
|
||||||
expect(document.body.classList.contains('hover-reveal')).toBe(true);
|
|
||||||
expect(initializeEventManagementMock).toHaveBeenCalled();
|
|
||||||
|
|
||||||
vi.runAllTimers();
|
|
||||||
expect(onboardingStartMock).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('skips bulk manager when recipes page is active', async () => {
|
|
||||||
const { AppCore } = await loadCoreModule();
|
|
||||||
const appCore = new AppCore();
|
|
||||||
mockState.currentPageType = 'recipes';
|
|
||||||
|
|
||||||
await appCore.initialize();
|
|
||||||
|
|
||||||
expect(bulkManagerInitializeMock).not.toHaveBeenCalled();
|
|
||||||
expect(BulkContextMenuMock).not.toHaveBeenCalled();
|
|
||||||
expect(setBulkContextMenuMock).not.toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('initializes page features with context menus and infinite scroll', async () => {
|
|
||||||
const { AppCore } = await loadCoreModule();
|
|
||||||
const appCore = new AppCore();
|
|
||||||
document.body.dataset.page = 'loras';
|
|
||||||
|
|
||||||
appCore.initializeContextMenus = vi.fn(appCore.initializeContextMenus.bind(appCore));
|
|
||||||
|
|
||||||
appCore.initializePageFeatures();
|
|
||||||
|
|
||||||
expect(appCore.initializeContextMenus).toHaveBeenCalledWith('loras');
|
|
||||||
expect(initializeInfiniteScrollMock).toHaveBeenCalledWith('loras');
|
|
||||||
expect(createPageContextMenuMock).toHaveBeenCalledWith('loras');
|
|
||||||
expect(window.pageContextMenu).toBe('page-menu');
|
|
||||||
expect(createGlobalContextMenuMock).toHaveBeenCalled();
|
|
||||||
expect(window.globalContextMenuInstance).toBe('global-menu');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('does not reinitialize once initialized', async () => {
|
|
||||||
const { AppCore } = await loadCoreModule();
|
|
||||||
const appCore = new AppCore();
|
|
||||||
|
|
||||||
await appCore.initialize();
|
|
||||||
await appCore.initialize();
|
|
||||||
|
|
||||||
expect(i18nWaitMock).toHaveBeenCalledTimes(1);
|
|
||||||
expect(settingsWaitMock).toHaveBeenCalledTimes(1);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -116,64 +116,6 @@ export function removeSessionItem(key) {
|
|||||||
sessionStorage.removeItem(STORAGE_PREFIX + key);
|
sessionStorage.removeItem(STORAGE_PREFIX + key);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Migrate all existing localStorage items to use the prefix
|
|
||||||
* This should be called once during application initialization
|
|
||||||
*/
|
|
||||||
export function migrateStorageItems() {
|
|
||||||
// Check if migration has already been performed
|
|
||||||
if (localStorage.getItem(STORAGE_PREFIX + 'migration_completed')) {
|
|
||||||
console.log('Lora Manager: Storage migration already completed');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// List of known keys used in the application
|
|
||||||
const knownKeys = [
|
|
||||||
'nsfwBlurLevel',
|
|
||||||
'theme',
|
|
||||||
'activeFolder',
|
|
||||||
'folderTagsCollapsed',
|
|
||||||
'settings',
|
|
||||||
'loras_filters',
|
|
||||||
'recipes_filters',
|
|
||||||
'checkpoints_filters',
|
|
||||||
'loras_search_prefs',
|
|
||||||
'recipes_search_prefs',
|
|
||||||
'checkpoints_search_prefs',
|
|
||||||
'show_update_notifications',
|
|
||||||
'last_update_check',
|
|
||||||
'dismissed_banners'
|
|
||||||
];
|
|
||||||
|
|
||||||
// Migrate each known key
|
|
||||||
knownKeys.forEach(key => {
|
|
||||||
const prefixedKey = STORAGE_PREFIX + key;
|
|
||||||
|
|
||||||
// Only migrate if the prefixed key doesn't already exist
|
|
||||||
if (localStorage.getItem(prefixedKey) === null) {
|
|
||||||
const value = localStorage.getItem(key);
|
|
||||||
if (value !== null) {
|
|
||||||
try {
|
|
||||||
// Try to parse as JSON first
|
|
||||||
const parsedValue = JSON.parse(value);
|
|
||||||
setStorageItem(key, parsedValue);
|
|
||||||
} catch (e) {
|
|
||||||
// If not JSON, store as is
|
|
||||||
setStorageItem(key, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
// We can optionally remove the old key after migration
|
|
||||||
localStorage.removeItem(key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Mark migration as completed
|
|
||||||
localStorage.setItem(STORAGE_PREFIX + 'migration_completed', 'true');
|
|
||||||
|
|
||||||
console.log('Lora Manager: Storage migration completed');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Save a Map to localStorage
|
* Save a Map to localStorage
|
||||||
* @param {string} key - The localStorage key
|
* @param {string} key - The localStorage key
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ const {
|
|||||||
getSessionItem,
|
getSessionItem,
|
||||||
setSessionItem,
|
setSessionItem,
|
||||||
removeSessionItem,
|
removeSessionItem,
|
||||||
migrateStorageItems
|
|
||||||
} = storageHelpers;
|
} = storageHelpers;
|
||||||
|
|
||||||
const createFakeStorage = () => {
|
const createFakeStorage = () => {
|
||||||
@@ -110,33 +109,3 @@ describe('storageHelpers namespace utilities', () => {
|
|||||||
expect(sessionStorage.getItem('lora_manager_flag')).toBeNull();
|
expect(sessionStorage.getItem('lora_manager_flag')).toBeNull();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('migrateStorageItems', () => {
|
|
||||||
it('migrates known keys and logs completion', () => {
|
|
||||||
const setStorageSpy = vi.spyOn(storageHelpers, 'setStorageItem');
|
|
||||||
localStorage.setItem('theme', '"light"');
|
|
||||||
localStorage.setItem('loras_filters', JSON.stringify({ sort: 'asc' }));
|
|
||||||
localStorage.setItem('nsfwBlurLevel', '3');
|
|
||||||
|
|
||||||
migrateStorageItems();
|
|
||||||
|
|
||||||
expect(setStorageSpy).toHaveBeenCalledTimes(3);
|
|
||||||
expect(localStorage.getItem('lora_manager_theme')).toBe('light');
|
|
||||||
expect(localStorage.getItem('lora_manager_loras_filters')).toBe(JSON.stringify({ sort: 'asc' }));
|
|
||||||
expect(localStorage.getItem('loras_filters')).toBeNull();
|
|
||||||
expect(localStorage.getItem('lora_manager_nsfwBlurLevel')).toBe('3');
|
|
||||||
expect(localStorage.getItem('nsfwBlurLevel')).toBeNull();
|
|
||||||
expect(localStorage.getItem('lora_manager_migration_completed')).toBe('true');
|
|
||||||
expect(consoleLogMock).toHaveBeenCalledWith('Lora Manager: Storage migration completed');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('skips migration when already completed and logs notice', () => {
|
|
||||||
const setStorageSpy = vi.spyOn(storageHelpers, 'setStorageItem');
|
|
||||||
localStorage.setItem('lora_manager_migration_completed', 'true');
|
|
||||||
|
|
||||||
migrateStorageItems();
|
|
||||||
|
|
||||||
expect(setStorageSpy).not.toHaveBeenCalled();
|
|
||||||
expect(consoleLogMock).toHaveBeenCalledWith('Lora Manager: Storage migration already completed');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|||||||
Reference in New Issue
Block a user