mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-03-21 21:22:11 -03:00
test(settings): cover library switch workflow
This commit is contained in:
@@ -241,6 +241,10 @@
|
||||
}
|
||||
},
|
||||
"folderSettings": {
|
||||
"activeLibrary": "Active Library",
|
||||
"activeLibraryHelp": "Switch between configured libraries to update default folders. Changing the selection reloads the page.",
|
||||
"loadingLibraries": "Loading libraries...",
|
||||
"noLibraries": "No libraries configured",
|
||||
"defaultLoraRoot": "Standard-LoRA-Stammordner",
|
||||
"defaultLoraRootHelp": "Legen Sie den Standard-LoRA-Stammordner für Downloads, Importe und Verschiebungen fest",
|
||||
"defaultCheckpointRoot": "Standard-Checkpoint-Stammordner",
|
||||
@@ -1125,6 +1129,8 @@
|
||||
"compactModeToggled": "Kompakt-Modus {state}",
|
||||
"settingSaveFailed": "Fehler beim Speichern der Einstellung: {message}",
|
||||
"displayDensitySet": "Anzeige-Dichte auf {density} gesetzt",
|
||||
"libraryLoadFailed": "Failed to load libraries: {message}",
|
||||
"libraryActivateFailed": "Failed to activate library: {message}",
|
||||
"languageChangeFailed": "Fehler beim Ändern der Sprache: {message}",
|
||||
"cacheCleared": "Cache-Dateien wurden erfolgreich gelöscht. Cache wird bei der nächsten Aktion neu aufgebaut.",
|
||||
"cacheClearFailed": "Fehler beim Löschen des Caches: {error}",
|
||||
|
||||
@@ -241,6 +241,10 @@
|
||||
}
|
||||
},
|
||||
"folderSettings": {
|
||||
"activeLibrary": "Active Library",
|
||||
"activeLibraryHelp": "Switch between configured libraries to update default folders. Changing the selection reloads the page.",
|
||||
"loadingLibraries": "Loading libraries...",
|
||||
"noLibraries": "No libraries configured",
|
||||
"defaultLoraRoot": "Default LoRA Root",
|
||||
"defaultLoraRootHelp": "Set the default LoRA root directory for downloads, imports and moves",
|
||||
"defaultCheckpointRoot": "Default Checkpoint Root",
|
||||
@@ -1125,6 +1129,8 @@
|
||||
"compactModeToggled": "Compact Mode {state}",
|
||||
"settingSaveFailed": "Failed to save setting: {message}",
|
||||
"displayDensitySet": "Display Density set to {density}",
|
||||
"libraryLoadFailed": "Failed to load libraries: {message}",
|
||||
"libraryActivateFailed": "Failed to activate library: {message}",
|
||||
"languageChangeFailed": "Failed to change language: {message}",
|
||||
"cacheCleared": "Cache files have been cleared successfully. Cache will rebuild on next action.",
|
||||
"cacheClearFailed": "Failed to clear cache: {error}",
|
||||
|
||||
@@ -241,6 +241,10 @@
|
||||
}
|
||||
},
|
||||
"folderSettings": {
|
||||
"activeLibrary": "Active Library",
|
||||
"activeLibraryHelp": "Switch between configured libraries to update default folders. Changing the selection reloads the page.",
|
||||
"loadingLibraries": "Loading libraries...",
|
||||
"noLibraries": "No libraries configured",
|
||||
"defaultLoraRoot": "Raíz predeterminada de LoRA",
|
||||
"defaultLoraRootHelp": "Establecer el directorio raíz predeterminado de LoRA para descargas, importaciones y movimientos",
|
||||
"defaultCheckpointRoot": "Raíz predeterminada de checkpoint",
|
||||
@@ -1125,6 +1129,8 @@
|
||||
"compactModeToggled": "Modo compacto {state}",
|
||||
"settingSaveFailed": "Error al guardar configuración: {message}",
|
||||
"displayDensitySet": "Densidad de visualización establecida a {density}",
|
||||
"libraryLoadFailed": "Failed to load libraries: {message}",
|
||||
"libraryActivateFailed": "Failed to activate library: {message}",
|
||||
"languageChangeFailed": "Error al cambiar idioma: {message}",
|
||||
"cacheCleared": "Archivos de caché limpiados exitosamente. La caché se reconstruirá en la próxima acción.",
|
||||
"cacheClearFailed": "Error al limpiar caché: {error}",
|
||||
|
||||
@@ -241,6 +241,10 @@
|
||||
}
|
||||
},
|
||||
"folderSettings": {
|
||||
"activeLibrary": "Active Library",
|
||||
"activeLibraryHelp": "Switch between configured libraries to update default folders. Changing the selection reloads the page.",
|
||||
"loadingLibraries": "Loading libraries...",
|
||||
"noLibraries": "No libraries configured",
|
||||
"defaultLoraRoot": "Racine LoRA par défaut",
|
||||
"defaultLoraRootHelp": "Définir le répertoire racine LoRA par défaut pour les téléchargements, imports et déplacements",
|
||||
"defaultCheckpointRoot": "Racine Checkpoint par défaut",
|
||||
@@ -1125,6 +1129,8 @@
|
||||
"compactModeToggled": "Mode compact {state}",
|
||||
"settingSaveFailed": "Échec de la sauvegarde du paramètre : {message}",
|
||||
"displayDensitySet": "Densité d'affichage définie sur {density}",
|
||||
"libraryLoadFailed": "Failed to load libraries: {message}",
|
||||
"libraryActivateFailed": "Failed to activate library: {message}",
|
||||
"languageChangeFailed": "Échec du changement de langue : {message}",
|
||||
"cacheCleared": "Les fichiers de cache ont été vidés avec succès. Le cache sera reconstruit à la prochaine action.",
|
||||
"cacheClearFailed": "Échec du vidage du cache : {error}",
|
||||
|
||||
@@ -241,6 +241,10 @@
|
||||
}
|
||||
},
|
||||
"folderSettings": {
|
||||
"activeLibrary": "Active Library",
|
||||
"activeLibraryHelp": "Switch between configured libraries to update default folders. Changing the selection reloads the page.",
|
||||
"loadingLibraries": "Loading libraries...",
|
||||
"noLibraries": "No libraries configured",
|
||||
"defaultLoraRoot": "תיקיית שורש ברירת מחדל של LoRA",
|
||||
"defaultLoraRootHelp": "הגדר את ספריית השורש המוגדרת כברירת מחדל של LoRA להורדות, ייבוא והעברות",
|
||||
"defaultCheckpointRoot": "תיקיית שורש ברירת מחדל של Checkpoint",
|
||||
@@ -1125,6 +1129,8 @@
|
||||
"compactModeToggled": "מצב קומפקטי {state}",
|
||||
"settingSaveFailed": "שמירת ההגדרה נכשלה: {message}",
|
||||
"displayDensitySet": "צפיפות התצוגה הוגדרה ל-{density}",
|
||||
"libraryLoadFailed": "Failed to load libraries: {message}",
|
||||
"libraryActivateFailed": "Failed to activate library: {message}",
|
||||
"languageChangeFailed": "שינוי השפה נכשל: {message}",
|
||||
"cacheCleared": "קבצי המטמון נוקו בהצלחה. המטמון ייבנה מחדש בפעולה הבאה.",
|
||||
"cacheClearFailed": "ניקוי המטמון נכשל: {error}",
|
||||
|
||||
@@ -241,6 +241,10 @@
|
||||
}
|
||||
},
|
||||
"folderSettings": {
|
||||
"activeLibrary": "Active Library",
|
||||
"activeLibraryHelp": "Switch between configured libraries to update default folders. Changing the selection reloads the page.",
|
||||
"loadingLibraries": "Loading libraries...",
|
||||
"noLibraries": "No libraries configured",
|
||||
"defaultLoraRoot": "デフォルトLoRAルート",
|
||||
"defaultLoraRootHelp": "ダウンロード、インポート、移動用のデフォルトLoRAルートディレクトリを設定",
|
||||
"defaultCheckpointRoot": "デフォルトCheckpointルート",
|
||||
@@ -1125,6 +1129,8 @@
|
||||
"compactModeToggled": "コンパクトモード {state}",
|
||||
"settingSaveFailed": "設定の保存に失敗しました:{message}",
|
||||
"displayDensitySet": "表示密度が {density} に設定されました",
|
||||
"libraryLoadFailed": "Failed to load libraries: {message}",
|
||||
"libraryActivateFailed": "Failed to activate library: {message}",
|
||||
"languageChangeFailed": "言語の変更に失敗しました:{message}",
|
||||
"cacheCleared": "キャッシュファイルが正常にクリアされました。次回のアクションでキャッシュが再構築されます。",
|
||||
"cacheClearFailed": "キャッシュのクリアに失敗しました:{error}",
|
||||
|
||||
@@ -241,6 +241,10 @@
|
||||
}
|
||||
},
|
||||
"folderSettings": {
|
||||
"activeLibrary": "Active Library",
|
||||
"activeLibraryHelp": "Switch between configured libraries to update default folders. Changing the selection reloads the page.",
|
||||
"loadingLibraries": "Loading libraries...",
|
||||
"noLibraries": "No libraries configured",
|
||||
"defaultLoraRoot": "기본 LoRA 루트",
|
||||
"defaultLoraRootHelp": "다운로드, 가져오기 및 이동을 위한 기본 LoRA 루트 디렉토리를 설정합니다",
|
||||
"defaultCheckpointRoot": "기본 Checkpoint 루트",
|
||||
@@ -1125,6 +1129,8 @@
|
||||
"compactModeToggled": "컴팩트 모드 {state}",
|
||||
"settingSaveFailed": "설정 저장 실패: {message}",
|
||||
"displayDensitySet": "표시 밀도가 {density}로 설정되었습니다",
|
||||
"libraryLoadFailed": "Failed to load libraries: {message}",
|
||||
"libraryActivateFailed": "Failed to activate library: {message}",
|
||||
"languageChangeFailed": "언어 변경 실패: {message}",
|
||||
"cacheCleared": "캐시 파일이 성공적으로 지워졌습니다. 다음 작업 시 캐시가 재구축됩니다.",
|
||||
"cacheClearFailed": "캐시 지우기 실패: {error}",
|
||||
|
||||
@@ -241,6 +241,10 @@
|
||||
}
|
||||
},
|
||||
"folderSettings": {
|
||||
"activeLibrary": "Active Library",
|
||||
"activeLibraryHelp": "Switch between configured libraries to update default folders. Changing the selection reloads the page.",
|
||||
"loadingLibraries": "Loading libraries...",
|
||||
"noLibraries": "No libraries configured",
|
||||
"defaultLoraRoot": "Корневая папка LoRA по умолчанию",
|
||||
"defaultLoraRootHelp": "Установить корневую папку LoRA по умолчанию для загрузок, импорта и перемещений",
|
||||
"defaultCheckpointRoot": "Корневая папка Checkpoint по умолчанию",
|
||||
@@ -1125,6 +1129,8 @@
|
||||
"compactModeToggled": "Компактный режим {state}",
|
||||
"settingSaveFailed": "Не удалось сохранить настройку: {message}",
|
||||
"displayDensitySet": "Плотность отображения установлена на {density}",
|
||||
"libraryLoadFailed": "Failed to load libraries: {message}",
|
||||
"libraryActivateFailed": "Failed to activate library: {message}",
|
||||
"languageChangeFailed": "Не удалось изменить язык: {message}",
|
||||
"cacheCleared": "Файлы кэша успешно очищены. Кэш будет пересобран при следующем действии.",
|
||||
"cacheClearFailed": "Не удалось очистить кэш: {error}",
|
||||
|
||||
@@ -247,6 +247,10 @@
|
||||
}
|
||||
},
|
||||
"folderSettings": {
|
||||
"activeLibrary": "Active Library",
|
||||
"activeLibraryHelp": "Switch between configured libraries to update default folders. Changing the selection reloads the page.",
|
||||
"loadingLibraries": "Loading libraries...",
|
||||
"noLibraries": "No libraries configured",
|
||||
"defaultLoraRoot": "默认 LoRA 根目录",
|
||||
"defaultLoraRootHelp": "设置下载、导入和移动时的默认 LoRA 根目录",
|
||||
"defaultCheckpointRoot": "默认 Checkpoint 根目录",
|
||||
@@ -1131,6 +1135,8 @@
|
||||
"compactModeToggled": "紧凑模式 {state}",
|
||||
"settingSaveFailed": "保存设置失败:{message}",
|
||||
"displayDensitySet": "显示密度已设置为 {density}",
|
||||
"libraryLoadFailed": "Failed to load libraries: {message}",
|
||||
"libraryActivateFailed": "Failed to activate library: {message}",
|
||||
"languageChangeFailed": "切换语言失败:{message}",
|
||||
"cacheCleared": "缓存文件已成功清除。下次操作将重建缓存。",
|
||||
"cacheClearFailed": "清除缓存失败:{error}",
|
||||
|
||||
@@ -241,6 +241,10 @@
|
||||
}
|
||||
},
|
||||
"folderSettings": {
|
||||
"activeLibrary": "Active Library",
|
||||
"activeLibraryHelp": "Switch between configured libraries to update default folders. Changing the selection reloads the page.",
|
||||
"loadingLibraries": "Loading libraries...",
|
||||
"noLibraries": "No libraries configured",
|
||||
"defaultLoraRoot": "預設 LoRA 根目錄",
|
||||
"defaultLoraRootHelp": "設定下載、匯入和移動時的預設 LoRA 根目錄",
|
||||
"defaultCheckpointRoot": "預設 Checkpoint 根目錄",
|
||||
@@ -1125,6 +1129,8 @@
|
||||
"compactModeToggled": "緊湊模式已{state}",
|
||||
"settingSaveFailed": "儲存設定失敗:{message}",
|
||||
"displayDensitySet": "顯示密度已設為 {density}",
|
||||
"libraryLoadFailed": "Failed to load libraries: {message}",
|
||||
"libraryActivateFailed": "Failed to activate library: {message}",
|
||||
"languageChangeFailed": "切換語言失敗:{message}",
|
||||
"cacheCleared": "快取檔案已成功清除。快取將於下次操作時重建。",
|
||||
"cacheClearFailed": "清除快取失敗:{error}",
|
||||
|
||||
@@ -12,6 +12,8 @@ export class SettingsManager {
|
||||
this.initialized = false;
|
||||
this.isOpen = false;
|
||||
this.initializationPromise = null;
|
||||
this.availableLibraries = {};
|
||||
this.activeLibrary = '';
|
||||
|
||||
// Add initialization to sync with modal state
|
||||
this.currentPage = document.body.dataset.page || 'loras';
|
||||
@@ -301,6 +303,9 @@ export class SettingsManager {
|
||||
// Load base model path mappings
|
||||
this.loadBaseModelMappings();
|
||||
|
||||
// Load library options
|
||||
await this.loadLibraries();
|
||||
|
||||
// Load default lora root
|
||||
await this.loadLoraRoots();
|
||||
|
||||
@@ -372,6 +377,147 @@ export class SettingsManager {
|
||||
}
|
||||
}
|
||||
|
||||
async loadLibraries() {
|
||||
const librarySelect = document.getElementById('librarySelect');
|
||||
if (!librarySelect) {
|
||||
return;
|
||||
}
|
||||
|
||||
const setPlaceholderOption = (textKey, fallback) => {
|
||||
librarySelect.innerHTML = '';
|
||||
const option = document.createElement('option');
|
||||
option.value = '';
|
||||
option.textContent = translate(textKey, {}, fallback);
|
||||
librarySelect.appendChild(option);
|
||||
};
|
||||
|
||||
setPlaceholderOption('settings.folderSettings.loadingLibraries', 'Loading libraries...');
|
||||
librarySelect.disabled = true;
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/lm/settings/libraries');
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to fetch library registry');
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
if (data.success === false) {
|
||||
throw new Error(data.error || 'Failed to fetch library registry');
|
||||
}
|
||||
|
||||
const libraries = data.libraries && typeof data.libraries === 'object'
|
||||
? data.libraries
|
||||
: {};
|
||||
|
||||
this.availableLibraries = libraries;
|
||||
|
||||
const entries = Object.entries(libraries);
|
||||
if (entries.length === 0) {
|
||||
this.activeLibrary = '';
|
||||
setPlaceholderOption('settings.folderSettings.noLibraries', 'No libraries configured');
|
||||
return;
|
||||
}
|
||||
|
||||
const activeName = data.active_library && libraries[data.active_library]
|
||||
? data.active_library
|
||||
: entries[0][0];
|
||||
|
||||
this.activeLibrary = activeName;
|
||||
|
||||
librarySelect.innerHTML = '';
|
||||
const fragment = document.createDocumentFragment();
|
||||
entries
|
||||
.sort((a, b) => {
|
||||
const nameA = this.getLibraryDisplayName(a[0], a[1]).toLowerCase();
|
||||
const nameB = this.getLibraryDisplayName(b[0], b[1]).toLowerCase();
|
||||
return nameA.localeCompare(nameB);
|
||||
})
|
||||
.forEach(([name, info]) => {
|
||||
const option = document.createElement('option');
|
||||
option.value = name;
|
||||
option.textContent = this.getLibraryDisplayName(name, info);
|
||||
fragment.appendChild(option);
|
||||
});
|
||||
|
||||
librarySelect.appendChild(fragment);
|
||||
librarySelect.value = activeName;
|
||||
librarySelect.disabled = entries.length <= 1;
|
||||
} catch (error) {
|
||||
console.error('Error loading libraries:', error);
|
||||
setPlaceholderOption('settings.folderSettings.noLibraries', 'No libraries configured');
|
||||
this.availableLibraries = {};
|
||||
this.activeLibrary = '';
|
||||
librarySelect.disabled = true;
|
||||
showToast('toast.settings.libraryLoadFailed', { message: error.message }, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
getLibraryDisplayName(libraryName, libraryData = {}) {
|
||||
if (libraryData && typeof libraryData === 'object') {
|
||||
const metadata = libraryData.metadata;
|
||||
if (metadata && typeof metadata === 'object' && metadata.display_name) {
|
||||
return metadata.display_name;
|
||||
}
|
||||
|
||||
if (libraryData.display_name) {
|
||||
return libraryData.display_name;
|
||||
}
|
||||
}
|
||||
|
||||
return libraryName;
|
||||
}
|
||||
|
||||
async handleLibraryChange() {
|
||||
const librarySelect = document.getElementById('librarySelect');
|
||||
if (!librarySelect) {
|
||||
return;
|
||||
}
|
||||
|
||||
const selectedLibrary = librarySelect.value;
|
||||
if (!selectedLibrary || selectedLibrary === this.activeLibrary) {
|
||||
librarySelect.value = this.activeLibrary;
|
||||
return;
|
||||
}
|
||||
|
||||
librarySelect.disabled = true;
|
||||
|
||||
try {
|
||||
await this.activateLibrary(selectedLibrary);
|
||||
window.location.reload();
|
||||
} catch (error) {
|
||||
console.error('Failed to activate library:', error);
|
||||
showToast('toast.settings.libraryActivateFailed', { message: error.message }, 'error');
|
||||
await this.loadLibraries();
|
||||
} finally {
|
||||
if (!document.hidden) {
|
||||
librarySelect.disabled = librarySelect.options.length <= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async activateLibrary(libraryName) {
|
||||
const response = await fetch('/api/lm/settings/libraries/activate', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ library: libraryName }),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Request failed with status ${response.status}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
if (data.success === false) {
|
||||
throw new Error(data.error || 'Failed to activate library');
|
||||
}
|
||||
|
||||
const activeName = data.active_library || libraryName;
|
||||
this.activeLibrary = activeName;
|
||||
return data;
|
||||
}
|
||||
|
||||
async loadLoraRoots() {
|
||||
try {
|
||||
const defaultLoraRootSelect = document.getElementById('defaultLoraRoot');
|
||||
|
||||
@@ -181,7 +181,23 @@
|
||||
<!-- Add Folder Settings Section -->
|
||||
<div class="settings-section">
|
||||
<h3>{{ t('settings.sections.folderSettings') }}</h3>
|
||||
|
||||
|
||||
<div class="setting-item">
|
||||
<div class="setting-row">
|
||||
<div class="setting-info">
|
||||
<label for="librarySelect">{{ t('settings.folderSettings.activeLibrary') }}</label>
|
||||
</div>
|
||||
<div class="setting-control select-control">
|
||||
<select id="librarySelect" onchange="settingsManager.handleLibraryChange()">
|
||||
<option value="">{{ t('settings.folderSettings.loadingLibraries') }}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="input-help">
|
||||
{{ t('settings.folderSettings.activeLibraryHelp') }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="setting-item">
|
||||
<div class="setting-row">
|
||||
<div class="setting-info">
|
||||
|
||||
198
tests/frontend/managers/settingsManager.library.test.js
Normal file
198
tests/frontend/managers/settingsManager.library.test.js
Normal file
@@ -0,0 +1,198 @@
|
||||
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
||||
|
||||
vi.mock('../../../static/js/managers/ModalManager.js', () => ({
|
||||
modalManager: {
|
||||
closeModal: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock('../../../static/js/utils/uiHelpers.js', () => ({
|
||||
showToast: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock('../../../static/js/state/index.js', () => {
|
||||
const settings = {};
|
||||
return {
|
||||
state: {
|
||||
global: {
|
||||
settings,
|
||||
},
|
||||
},
|
||||
createDefaultSettings: () => ({
|
||||
language: 'en',
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock('../../../static/js/api/modelApiFactory.js', () => ({
|
||||
resetAndReload: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock('../../../static/js/utils/constants.js', () => ({
|
||||
DOWNLOAD_PATH_TEMPLATES: {},
|
||||
DEFAULT_PATH_TEMPLATES: {},
|
||||
MAPPABLE_BASE_MODELS: [],
|
||||
PATH_TEMPLATE_PLACEHOLDERS: {},
|
||||
}));
|
||||
|
||||
vi.mock('../../../static/js/utils/i18nHelpers.js', () => ({
|
||||
translate: (_key, _params, fallback) => fallback ?? '',
|
||||
}));
|
||||
|
||||
vi.mock('../../../static/js/i18n/index.js', () => ({
|
||||
i18n: {
|
||||
getCurrentLocale: () => 'en',
|
||||
setLanguage: vi.fn().mockResolvedValue(),
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock('../../../static/js/components/shared/ModelCard.js', () => ({
|
||||
configureModelCardVideo: vi.fn(),
|
||||
}));
|
||||
|
||||
import { SettingsManager } from '../../../static/js/managers/SettingsManager.js';
|
||||
import { showToast } from '../../../static/js/utils/uiHelpers.js';
|
||||
import { state } from '../../../static/js/state/index.js';
|
||||
|
||||
const originalLocation = window.location;
|
||||
|
||||
const createManager = () => {
|
||||
state.global.settings = {};
|
||||
const initSettingsSpy = vi
|
||||
.spyOn(SettingsManager.prototype, 'initializeSettings')
|
||||
.mockResolvedValue();
|
||||
const initializeSpy = vi
|
||||
.spyOn(SettingsManager.prototype, 'initialize')
|
||||
.mockImplementation(() => {});
|
||||
|
||||
const manager = new SettingsManager();
|
||||
|
||||
initSettingsSpy.mockRestore();
|
||||
initializeSpy.mockRestore();
|
||||
|
||||
return manager;
|
||||
};
|
||||
|
||||
const appendLibrarySelect = () => {
|
||||
const select = document.createElement('select');
|
||||
select.id = 'librarySelect';
|
||||
document.body.appendChild(select);
|
||||
return select;
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
document.body.innerHTML = '';
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
delete global.fetch;
|
||||
delete document.hidden;
|
||||
Object.defineProperty(window, 'location', {
|
||||
value: originalLocation,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
});
|
||||
});
|
||||
|
||||
describe('SettingsManager library controls', () => {
|
||||
it('loads libraries and populates the select', async () => {
|
||||
const manager = createManager();
|
||||
const select = appendLibrarySelect();
|
||||
|
||||
global.fetch = vi.fn().mockResolvedValue({
|
||||
ok: true,
|
||||
json: async () => ({
|
||||
success: true,
|
||||
libraries: {
|
||||
beta: { display_name: 'Beta' },
|
||||
alpha: { metadata: { display_name: 'Alpha' } },
|
||||
},
|
||||
active_library: 'beta',
|
||||
}),
|
||||
});
|
||||
|
||||
await manager.loadLibraries();
|
||||
|
||||
expect(manager.availableLibraries).toEqual({
|
||||
beta: { display_name: 'Beta' },
|
||||
alpha: { metadata: { display_name: 'Alpha' } },
|
||||
});
|
||||
expect(manager.activeLibrary).toBe('beta');
|
||||
expect(select.options).toHaveLength(2);
|
||||
expect(Array.from(select.options).map(option => option.value)).toEqual([
|
||||
'alpha',
|
||||
'beta',
|
||||
]);
|
||||
expect(select.value).toBe('beta');
|
||||
expect(select.disabled).toBe(false);
|
||||
});
|
||||
|
||||
it('handles load errors by disabling the select and showing a toast', async () => {
|
||||
const manager = createManager();
|
||||
const select = appendLibrarySelect();
|
||||
|
||||
global.fetch = vi.fn().mockResolvedValue({
|
||||
ok: false,
|
||||
status: 500,
|
||||
});
|
||||
|
||||
await manager.loadLibraries();
|
||||
|
||||
expect(select.options).toHaveLength(1);
|
||||
expect(select.options[0].value).toBe('');
|
||||
expect(select.disabled).toBe(true);
|
||||
expect(manager.availableLibraries).toEqual({});
|
||||
expect(manager.activeLibrary).toBe('');
|
||||
expect(showToast).toHaveBeenCalledWith(
|
||||
'toast.settings.libraryLoadFailed',
|
||||
expect.objectContaining({ message: 'Failed to fetch library registry' }),
|
||||
'error',
|
||||
);
|
||||
});
|
||||
|
||||
it('activates a newly selected library and reloads the page', async () => {
|
||||
const manager = createManager();
|
||||
const select = appendLibrarySelect();
|
||||
select.appendChild(new Option('Alpha', 'alpha'));
|
||||
select.appendChild(new Option('Beta', 'beta'));
|
||||
select.value = 'beta';
|
||||
manager.activeLibrary = 'alpha';
|
||||
|
||||
Object.defineProperty(document, 'hidden', {
|
||||
value: false,
|
||||
configurable: true,
|
||||
});
|
||||
|
||||
const reloadMock = vi.fn();
|
||||
Object.defineProperty(window, 'location', {
|
||||
value: { reload: reloadMock },
|
||||
configurable: true,
|
||||
});
|
||||
|
||||
const activateSpy = vi
|
||||
.spyOn(manager, 'activateLibrary')
|
||||
.mockResolvedValue({ success: true, active_library: 'beta' });
|
||||
|
||||
await manager.handleLibraryChange();
|
||||
|
||||
expect(activateSpy).toHaveBeenCalledWith('beta');
|
||||
expect(reloadMock).toHaveBeenCalledTimes(1);
|
||||
expect(select.disabled).toBe(false);
|
||||
});
|
||||
|
||||
it('ignores changes when selecting the active library', async () => {
|
||||
const manager = createManager();
|
||||
const select = appendLibrarySelect();
|
||||
select.appendChild(new Option('Alpha', 'alpha'));
|
||||
select.value = 'alpha';
|
||||
manager.activeLibrary = 'alpha';
|
||||
|
||||
const activateSpy = vi.spyOn(manager, 'activateLibrary');
|
||||
|
||||
await manager.handleLibraryChange();
|
||||
|
||||
expect(select.value).toBe('alpha');
|
||||
expect(activateSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user