mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-05-06 08:26:45 -03:00
281 lines
8.3 KiB
JavaScript
281 lines
8.3 KiB
JavaScript
import { describe, it, beforeEach, afterEach, expect, vi } from 'vitest';
|
|
|
|
const {
|
|
I18N_MODULE,
|
|
STATE_MODULE,
|
|
STORAGE_MODULE,
|
|
CONSTANTS_MODULE,
|
|
EVENT_MANAGER_MODULE,
|
|
BANNER_SERVICE_MODULE,
|
|
MODAL_MANAGER_MODULE,
|
|
UI_HELPERS_MODULE,
|
|
} = vi.hoisted(() => ({
|
|
I18N_MODULE: new URL('../../../static/js/utils/i18nHelpers.js', import.meta.url).pathname,
|
|
STATE_MODULE: new URL('../../../static/js/state/index.js', import.meta.url).pathname,
|
|
STORAGE_MODULE: new URL('../../../static/js/utils/storageHelpers.js', import.meta.url).pathname,
|
|
CONSTANTS_MODULE: new URL('../../../static/js/utils/constants.js', import.meta.url).pathname,
|
|
EVENT_MANAGER_MODULE: new URL('../../../static/js/utils/EventManager.js', import.meta.url).pathname,
|
|
BANNER_SERVICE_MODULE: new URL('../../../static/js/managers/BannerService.js', import.meta.url).pathname,
|
|
MODAL_MANAGER_MODULE: new URL('../../../static/js/managers/ModalManager.js', import.meta.url).pathname,
|
|
UI_HELPERS_MODULE: new URL('../../../static/js/utils/uiHelpers.js', import.meta.url).pathname,
|
|
}));
|
|
|
|
const translateMock = vi.fn((key, _params, fallback) => fallback || key);
|
|
const getStorageItemMock = vi.fn();
|
|
const setStorageItemMock = vi.fn();
|
|
const registerBannerMock = vi.fn();
|
|
const showModalMock = vi.fn();
|
|
|
|
vi.mock(I18N_MODULE, () => ({
|
|
translate: translateMock,
|
|
}));
|
|
|
|
vi.mock(STATE_MODULE, () => ({
|
|
state: {},
|
|
getCurrentPageState: vi.fn(),
|
|
}));
|
|
|
|
vi.mock(STORAGE_MODULE, () => ({
|
|
getStorageItem: getStorageItemMock,
|
|
setStorageItem: setStorageItemMock,
|
|
}));
|
|
|
|
vi.mock(CONSTANTS_MODULE, () => ({
|
|
NODE_TYPE_ICONS: {},
|
|
DEFAULT_NODE_COLOR: '#ffffff',
|
|
}));
|
|
|
|
vi.mock(EVENT_MANAGER_MODULE, () => ({
|
|
eventManager: {
|
|
emit: vi.fn(),
|
|
on: vi.fn(),
|
|
off: vi.fn(),
|
|
addHandler: vi.fn(),
|
|
removeHandler: vi.fn(),
|
|
setState: vi.fn(),
|
|
},
|
|
}));
|
|
|
|
vi.mock(BANNER_SERVICE_MODULE, () => ({
|
|
bannerService: {
|
|
registerBanner: registerBannerMock,
|
|
},
|
|
}));
|
|
|
|
vi.mock(MODAL_MANAGER_MODULE, () => ({
|
|
modalManager: {
|
|
showModal: showModalMock,
|
|
},
|
|
}));
|
|
|
|
describe('UI helper DOM utilities', () => {
|
|
beforeEach(() => {
|
|
document.body.innerHTML = '';
|
|
document.body.removeAttribute('data-theme');
|
|
document.documentElement.removeAttribute('data-theme');
|
|
getStorageItemMock.mockReset();
|
|
setStorageItemMock.mockReset();
|
|
registerBannerMock.mockReset();
|
|
showModalMock.mockReset();
|
|
translateMock.mockReset();
|
|
globalThis.requestAnimationFrame = (cb) => cb();
|
|
});
|
|
|
|
afterEach(() => {
|
|
vi.useRealTimers();
|
|
delete global.fetch;
|
|
delete navigator.clipboard;
|
|
delete window.open;
|
|
});
|
|
|
|
it('creates toast elements and cleans them up after timeout', async () => {
|
|
vi.useFakeTimers();
|
|
translateMock.mockReturnValue('Toast message');
|
|
|
|
const { showToast } = await import(UI_HELPERS_MODULE);
|
|
|
|
showToast('uiHelpers.clipboard.copied', {}, 'success');
|
|
|
|
const container = document.querySelector('.toast-container');
|
|
expect(container).not.toBeNull();
|
|
expect(container.querySelectorAll('.toast')).toHaveLength(1);
|
|
|
|
await Promise.resolve();
|
|
vi.advanceTimersByTime(2000);
|
|
|
|
const toast = container.querySelector('.toast');
|
|
toast.dispatchEvent(new Event('transitionend', { bubbles: true }));
|
|
await Promise.resolve();
|
|
|
|
expect(toast.classList.contains('show')).toBe(false);
|
|
});
|
|
|
|
it('toggles the persisted theme and updates DOM attributes', async () => {
|
|
getStorageItemMock.mockReturnValue('light');
|
|
document.body.innerHTML = '<button class="theme-toggle"></button>';
|
|
globalThis.matchMedia = vi.fn(() => ({
|
|
matches: false,
|
|
addEventListener: vi.fn(),
|
|
removeEventListener: vi.fn(),
|
|
}));
|
|
|
|
const { toggleTheme } = await import(UI_HELPERS_MODULE);
|
|
|
|
const nextTheme = toggleTheme();
|
|
|
|
expect(nextTheme).toBe('dark');
|
|
expect(setStorageItemMock).toHaveBeenCalledWith('theme', 'dark');
|
|
expect(document.documentElement.getAttribute('data-theme')).toBe('dark');
|
|
expect(document.body.dataset.theme).toBe('dark');
|
|
expect(document.querySelector('.theme-toggle').classList.contains('theme-dark')).toBe(true);
|
|
});
|
|
|
|
it('renders subgraph names in the node selector list', async () => {
|
|
const registryResponse = {
|
|
success: true,
|
|
data: {
|
|
node_count: 2,
|
|
nodes: {
|
|
'root:1': {
|
|
id: 1,
|
|
graph_id: 'root',
|
|
graph_name: null,
|
|
title: 'Root Loader',
|
|
type: 1,
|
|
bgcolor: '#123456',
|
|
},
|
|
'subgraph-uuid:2': {
|
|
id: 2,
|
|
graph_id: 'subgraph-uuid',
|
|
graph_name: 'Character Subgraph',
|
|
title: 'Nested Loader',
|
|
type: 1,
|
|
bgcolor: '#654321',
|
|
},
|
|
},
|
|
},
|
|
};
|
|
|
|
global.fetch = vi.fn().mockResolvedValue({
|
|
json: async () => registryResponse,
|
|
});
|
|
|
|
document.body.innerHTML = '<div id="nodeSelector"></div>';
|
|
|
|
const { sendLoraToWorkflow } = await import(UI_HELPERS_MODULE);
|
|
|
|
const result = await sendLoraToWorkflow('<lora:test:1>');
|
|
|
|
expect(result).toBe(true);
|
|
expect(global.fetch).toHaveBeenCalledWith('/api/lm/get-registry');
|
|
|
|
const nodeLabels = Array.from(
|
|
document.querySelectorAll('#nodeSelector .node-item[data-node-id] span')
|
|
).map((span) => span.textContent.trim());
|
|
|
|
expect(nodeLabels).toEqual([
|
|
'#1 Root Loader',
|
|
'#2 (Character Subgraph) Nested Loader',
|
|
]);
|
|
});
|
|
|
|
it('opens Civitai links using the preferred host and registers the first-use banner once', async () => {
|
|
const openSpy = vi.fn();
|
|
globalThis.window.open = openSpy;
|
|
|
|
getStorageItemMock.mockImplementation((key, defaultValue) => {
|
|
if (key === 'civitai_host_info_banner_seen') {
|
|
return false;
|
|
}
|
|
return defaultValue;
|
|
});
|
|
|
|
const { openCivitaiByMetadata } = await import(UI_HELPERS_MODULE);
|
|
|
|
openCivitaiByMetadata(123, 456, 'Demo Model');
|
|
|
|
expect(setStorageItemMock).toHaveBeenCalledWith('civitai_host_info_banner_seen', true);
|
|
expect(registerBannerMock).toHaveBeenCalledTimes(1);
|
|
expect(openSpy).toHaveBeenCalledWith(
|
|
'https://civitai.com/models/123?modelVersionId=456',
|
|
'_blank',
|
|
'noopener,noreferrer'
|
|
);
|
|
});
|
|
|
|
it('uses the configured red host for fallback searches', async () => {
|
|
const openSpy = vi.fn();
|
|
globalThis.window.open = openSpy;
|
|
|
|
getStorageItemMock.mockImplementation((key, defaultValue) => {
|
|
if (key === 'civitai_host_info_banner_seen') {
|
|
return true;
|
|
}
|
|
return defaultValue;
|
|
});
|
|
|
|
const stateModule = await import(STATE_MODULE);
|
|
stateModule.state.global = {
|
|
settings: {
|
|
civitai_host: 'civitai.red',
|
|
},
|
|
};
|
|
|
|
const { openCivitaiByMetadata } = await import(UI_HELPERS_MODULE);
|
|
|
|
openCivitaiByMetadata(null, null, 'Demo Model');
|
|
|
|
expect(registerBannerMock).not.toHaveBeenCalled();
|
|
expect(openSpy).toHaveBeenCalledWith(
|
|
'https://civitai.red/models?query=Demo%20Model',
|
|
'_blank',
|
|
'noopener,noreferrer'
|
|
);
|
|
});
|
|
|
|
it('copies mapped local example-image paths when the backend requests clipboard mode', async () => {
|
|
global.fetch = vi.fn().mockResolvedValue({
|
|
json: async () => ({
|
|
success: true,
|
|
mode: 'clipboard',
|
|
path: '/Volumes/ComfyUI/examples/demo',
|
|
}),
|
|
});
|
|
navigator.clipboard = {
|
|
writeText: vi.fn().mockResolvedValue(),
|
|
};
|
|
|
|
const { openExampleImagesFolder } = await import(UI_HELPERS_MODULE);
|
|
|
|
const result = await openExampleImagesFolder('abc123');
|
|
|
|
expect(result).toBe(true);
|
|
expect(navigator.clipboard.writeText).toHaveBeenCalledWith('/Volumes/ComfyUI/examples/demo');
|
|
expect(global.fetch).toHaveBeenCalledWith('/api/lm/open-example-images-folder', expect.objectContaining({
|
|
method: 'POST',
|
|
}));
|
|
});
|
|
|
|
it('opens custom URIs for example-image folders when requested by the backend', async () => {
|
|
global.fetch = vi.fn().mockResolvedValue({
|
|
json: async () => ({
|
|
success: true,
|
|
mode: 'uri',
|
|
uri: 'shortcuts://run-shortcut?name=OpenFinder',
|
|
}),
|
|
});
|
|
window.open = vi.fn(() => ({}));
|
|
|
|
const { openExampleImagesFolder } = await import(UI_HELPERS_MODULE);
|
|
|
|
const result = await openExampleImagesFolder('abc123');
|
|
|
|
expect(result).toBe(true);
|
|
expect(window.open).toHaveBeenCalledWith(
|
|
'shortcuts://run-shortcut?name=OpenFinder',
|
|
'_blank',
|
|
'noopener,noreferrer'
|
|
);
|
|
});
|
|
});
|