mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-08-06 22:10:14 -03:00
fix(settings): suppress error toasts when optional model roots are empty
This commit is contained in:
@@ -1517,11 +1517,20 @@ export class SettingsManager {
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
async loadLoraRoots() {
|
showNoRootsPlaceholder(select) {
|
||||||
try {
|
select.innerHTML = '';
|
||||||
const defaultLoraRootSelect = document.getElementById('defaultLoraRoot');
|
const option = document.createElement('option');
|
||||||
if (!defaultLoraRootSelect) return;
|
option.value = '';
|
||||||
|
option.textContent = translate('settings.folderSettings.noDefault', {}, 'No Default');
|
||||||
|
select.appendChild(option);
|
||||||
|
select.disabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
async loadLoraRoots() {
|
||||||
|
const defaultLoraRootSelect = document.getElementById('defaultLoraRoot');
|
||||||
|
if (!defaultLoraRootSelect) return;
|
||||||
|
|
||||||
|
try {
|
||||||
// Fetch lora roots
|
// Fetch lora roots
|
||||||
const response = await fetch('/api/lm/loras/roots');
|
const response = await fetch('/api/lm/loras/roots');
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
@@ -1530,10 +1539,12 @@ export class SettingsManager {
|
|||||||
|
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
if (!data.roots || data.roots.length === 0) {
|
if (!data.roots || data.roots.length === 0) {
|
||||||
throw new Error('No LoRA roots found');
|
this.showNoRootsPlaceholder(defaultLoraRootSelect);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultLoraRootSelect.innerHTML = '';
|
defaultLoraRootSelect.innerHTML = '';
|
||||||
|
defaultLoraRootSelect.disabled = false;
|
||||||
|
|
||||||
// Add options for each root
|
// Add options for each root
|
||||||
data.roots.forEach(root => {
|
data.roots.forEach(root => {
|
||||||
@@ -1548,15 +1559,16 @@ export class SettingsManager {
|
|||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error loading LoRA roots:', error);
|
console.error('Error loading LoRA roots:', error);
|
||||||
|
this.showNoRootsPlaceholder(defaultLoraRootSelect);
|
||||||
showToast('toast.settings.loraRootsFailed', { message: error.message }, 'error');
|
showToast('toast.settings.loraRootsFailed', { message: error.message }, 'error');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async loadCheckpointRoots() {
|
async loadCheckpointRoots() {
|
||||||
try {
|
const defaultCheckpointRootSelect = document.getElementById('defaultCheckpointRoot');
|
||||||
const defaultCheckpointRootSelect = document.getElementById('defaultCheckpointRoot');
|
if (!defaultCheckpointRootSelect) return;
|
||||||
if (!defaultCheckpointRootSelect) return;
|
|
||||||
|
|
||||||
|
try {
|
||||||
// Fetch checkpoint roots (checkpoint paths only, not unet)
|
// Fetch checkpoint roots (checkpoint paths only, not unet)
|
||||||
const response = await fetch('/api/lm/checkpoints/checkpoints_roots');
|
const response = await fetch('/api/lm/checkpoints/checkpoints_roots');
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
@@ -1565,10 +1577,12 @@ export class SettingsManager {
|
|||||||
|
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
if (!data.roots || data.roots.length === 0) {
|
if (!data.roots || data.roots.length === 0) {
|
||||||
throw new Error('No checkpoint roots found');
|
this.showNoRootsPlaceholder(defaultCheckpointRootSelect);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultCheckpointRootSelect.innerHTML = '';
|
defaultCheckpointRootSelect.innerHTML = '';
|
||||||
|
defaultCheckpointRootSelect.disabled = false;
|
||||||
|
|
||||||
// Add options for each root
|
// Add options for each root
|
||||||
data.roots.forEach(root => {
|
data.roots.forEach(root => {
|
||||||
@@ -1583,15 +1597,16 @@ export class SettingsManager {
|
|||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error loading checkpoint roots:', error);
|
console.error('Error loading checkpoint roots:', error);
|
||||||
|
this.showNoRootsPlaceholder(defaultCheckpointRootSelect);
|
||||||
showToast('toast.settings.checkpointRootsFailed', { message: error.message }, 'error');
|
showToast('toast.settings.checkpointRootsFailed', { message: error.message }, 'error');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async loadUnetRoots() {
|
async loadUnetRoots() {
|
||||||
try {
|
const defaultUnetRootSelect = document.getElementById('defaultUnetRoot');
|
||||||
const defaultUnetRootSelect = document.getElementById('defaultUnetRoot');
|
if (!defaultUnetRootSelect) return;
|
||||||
if (!defaultUnetRootSelect) return;
|
|
||||||
|
|
||||||
|
try {
|
||||||
// Fetch unet roots (diffusion model paths only)
|
// Fetch unet roots (diffusion model paths only)
|
||||||
const response = await fetch('/api/lm/checkpoints/unet_roots');
|
const response = await fetch('/api/lm/checkpoints/unet_roots');
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
@@ -1600,10 +1615,12 @@ export class SettingsManager {
|
|||||||
|
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
if (!data.roots || data.roots.length === 0) {
|
if (!data.roots || data.roots.length === 0) {
|
||||||
throw new Error('No diffusion model roots found');
|
this.showNoRootsPlaceholder(defaultUnetRootSelect);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultUnetRootSelect.innerHTML = '';
|
defaultUnetRootSelect.innerHTML = '';
|
||||||
|
defaultUnetRootSelect.disabled = false;
|
||||||
|
|
||||||
// Add options for each root
|
// Add options for each root
|
||||||
data.roots.forEach(root => {
|
data.roots.forEach(root => {
|
||||||
@@ -1618,15 +1635,16 @@ export class SettingsManager {
|
|||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error loading diffusion model roots:', error);
|
console.error('Error loading diffusion model roots:', error);
|
||||||
|
this.showNoRootsPlaceholder(defaultUnetRootSelect);
|
||||||
showToast('toast.settings.unetRootsFailed', { message: error.message }, 'error');
|
showToast('toast.settings.unetRootsFailed', { message: error.message }, 'error');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async loadEmbeddingRoots() {
|
async loadEmbeddingRoots() {
|
||||||
try {
|
const defaultEmbeddingRootSelect = document.getElementById('defaultEmbeddingRoot');
|
||||||
const defaultEmbeddingRootSelect = document.getElementById('defaultEmbeddingRoot');
|
if (!defaultEmbeddingRootSelect) return;
|
||||||
if (!defaultEmbeddingRootSelect) return;
|
|
||||||
|
|
||||||
|
try {
|
||||||
// Fetch embedding roots
|
// Fetch embedding roots
|
||||||
const response = await fetch('/api/lm/embeddings/roots');
|
const response = await fetch('/api/lm/embeddings/roots');
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
@@ -1635,10 +1653,12 @@ export class SettingsManager {
|
|||||||
|
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
if (!data.roots || data.roots.length === 0) {
|
if (!data.roots || data.roots.length === 0) {
|
||||||
throw new Error('No embedding roots found');
|
this.showNoRootsPlaceholder(defaultEmbeddingRootSelect);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultEmbeddingRootSelect.innerHTML = '';
|
defaultEmbeddingRootSelect.innerHTML = '';
|
||||||
|
defaultEmbeddingRootSelect.disabled = false;
|
||||||
|
|
||||||
// Add options for each root
|
// Add options for each root
|
||||||
data.roots.forEach(root => {
|
data.roots.forEach(root => {
|
||||||
@@ -1653,6 +1673,7 @@ export class SettingsManager {
|
|||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error loading embedding roots:', error);
|
console.error('Error loading embedding roots:', error);
|
||||||
|
this.showNoRootsPlaceholder(defaultEmbeddingRootSelect);
|
||||||
showToast('toast.settings.embeddingRootsFailed', { message: error.message }, 'error');
|
showToast('toast.settings.embeddingRootsFailed', { message: error.message }, 'error');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -106,6 +106,118 @@ afterEach(() => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('SettingsManager root selects', () => {
|
||||||
|
const rootCases = [
|
||||||
|
{
|
||||||
|
method: 'loadLoraRoots',
|
||||||
|
selectId: 'defaultLoraRoot',
|
||||||
|
endpoint: '/api/lm/loras/roots',
|
||||||
|
errorKey: 'toast.settings.loraRootsFailed',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
method: 'loadCheckpointRoots',
|
||||||
|
selectId: 'defaultCheckpointRoot',
|
||||||
|
endpoint: '/api/lm/checkpoints/checkpoints_roots',
|
||||||
|
errorKey: 'toast.settings.checkpointRootsFailed',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
method: 'loadUnetRoots',
|
||||||
|
selectId: 'defaultUnetRoot',
|
||||||
|
endpoint: '/api/lm/checkpoints/unet_roots',
|
||||||
|
errorKey: 'toast.settings.unetRootsFailed',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
method: 'loadEmbeddingRoots',
|
||||||
|
selectId: 'defaultEmbeddingRoot',
|
||||||
|
endpoint: '/api/lm/embeddings/roots',
|
||||||
|
errorKey: 'toast.settings.embeddingRootsFailed',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const appendRootSelect = (id) => {
|
||||||
|
const select = document.createElement('select');
|
||||||
|
select.id = id;
|
||||||
|
document.body.appendChild(select);
|
||||||
|
return select;
|
||||||
|
};
|
||||||
|
|
||||||
|
it.each(rootCases)(
|
||||||
|
'populates the $method select with roots and keeps it enabled',
|
||||||
|
async ({ method, selectId, endpoint }) => {
|
||||||
|
const manager = createManager();
|
||||||
|
const select = appendRootSelect(selectId);
|
||||||
|
select.disabled = true;
|
||||||
|
|
||||||
|
global.fetch = vi.fn().mockResolvedValue({
|
||||||
|
ok: true,
|
||||||
|
json: async () => ({
|
||||||
|
success: true,
|
||||||
|
roots: ['/models/root-a', '/models/root-b'],
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
await manager[method]();
|
||||||
|
|
||||||
|
expect(global.fetch).toHaveBeenCalledWith(endpoint);
|
||||||
|
expect(Array.from(select.options).map(option => option.value)).toEqual([
|
||||||
|
'/models/root-a',
|
||||||
|
'/models/root-b',
|
||||||
|
]);
|
||||||
|
expect(select.disabled).toBe(false);
|
||||||
|
expect(showToast).not.toHaveBeenCalled();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
it.each(rootCases)(
|
||||||
|
'shows a placeholder and no error toast when $method has empty roots',
|
||||||
|
async ({ method, selectId, endpoint }) => {
|
||||||
|
const manager = createManager();
|
||||||
|
const select = appendRootSelect(selectId);
|
||||||
|
|
||||||
|
global.fetch = vi.fn().mockResolvedValue({
|
||||||
|
ok: true,
|
||||||
|
json: async () => ({
|
||||||
|
success: true,
|
||||||
|
roots: [],
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
await manager[method]();
|
||||||
|
|
||||||
|
expect(global.fetch).toHaveBeenCalledWith(endpoint);
|
||||||
|
expect(select.options).toHaveLength(1);
|
||||||
|
expect(select.options[0].value).toBe('');
|
||||||
|
expect(select.options[0].textContent).toBe('No Default');
|
||||||
|
expect(select.disabled).toBe(true);
|
||||||
|
expect(showToast).not.toHaveBeenCalled();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
it.each(rootCases)(
|
||||||
|
'shows an error toast when the $method roots request fails',
|
||||||
|
async ({ method, selectId, errorKey }) => {
|
||||||
|
const manager = createManager();
|
||||||
|
const select = appendRootSelect(selectId);
|
||||||
|
|
||||||
|
global.fetch = vi.fn().mockResolvedValue({
|
||||||
|
ok: false,
|
||||||
|
status: 500,
|
||||||
|
});
|
||||||
|
|
||||||
|
await manager[method]();
|
||||||
|
|
||||||
|
expect(select.options).toHaveLength(1);
|
||||||
|
expect(select.options[0].value).toBe('');
|
||||||
|
expect(select.disabled).toBe(true);
|
||||||
|
expect(showToast).toHaveBeenCalledWith(
|
||||||
|
errorKey,
|
||||||
|
expect.objectContaining({ message: expect.any(String) }),
|
||||||
|
'error',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
describe('SettingsManager library controls', () => {
|
describe('SettingsManager library controls', () => {
|
||||||
it('loads libraries and populates the select', async () => {
|
it('loads libraries and populates the select', async () => {
|
||||||
const manager = createManager();
|
const manager = createManager();
|
||||||
|
|||||||
Reference in New Issue
Block a user