mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-03-25 15:15:44 -03:00
Merge pull request #474 from willmiao/codex/plan-next-steps-for-roadmap-tasks
test(frontend): add loras page manager suite
This commit is contained in:
@@ -9,7 +9,7 @@ This roadmap tracks the planned rollout of automated testing for the ComfyUI LoR
|
|||||||
| Phase 0 | Establish baseline tooling | Add Node test runner, jsdom environment, and seed smoke tests | ✅ Complete | Vitest + jsdom configured, example state tests committed |
|
| Phase 0 | Establish baseline tooling | Add Node test runner, jsdom environment, and seed smoke tests | ✅ Complete | Vitest + jsdom configured, example state tests committed |
|
||||||
| Phase 1 | Cover state management logic | Unit test selectors, derived data helpers, and storage utilities under `static/js/state` and `static/js/utils` | ✅ Complete | Storage helpers and state selectors now exercised via deterministic suites |
|
| Phase 1 | Cover state management logic | Unit test selectors, derived data helpers, and storage utilities under `static/js/state` and `static/js/utils` | ✅ Complete | Storage helpers and state selectors now exercised via deterministic suites |
|
||||||
| Phase 2 | Test AppCore orchestration | Simulate page bootstrapping, infinite scroll hooks, and manager registration using JSDOM DOM fixtures | ✅ Complete | AppCore initialization + page feature suites now validate manager wiring, infinite scroll hooks, and onboarding gating |
|
| Phase 2 | Test AppCore orchestration | Simulate page bootstrapping, infinite scroll hooks, and manager registration using JSDOM DOM fixtures | ✅ Complete | AppCore initialization + page feature suites now validate manager wiring, infinite scroll hooks, and onboarding gating |
|
||||||
| Phase 3 | Validate page-specific managers | Add focused suites for `loras`, `checkpoints`, `embeddings`, and `recipes` managers covering filtering, sorting, and bulk actions | ⚪ Not Started | Consider shared helpers for mocking API modules and storage |
|
| Phase 3 | Validate page-specific managers | Add focused suites for `loras`, `checkpoints`, `embeddings`, and `recipes` managers covering filtering, sorting, and bulk actions | 🚧 In Progress | LoRA manager initialization suite landed; shared page fixtures ready for checkpoints |
|
||||||
| Phase 4 | Interaction-level regression tests | Exercise template fragments, modals, and menus to ensure UI wiring remains intact | ⚪ Not Started | Evaluate Playwright component testing or happy-path DOM snapshots |
|
| Phase 4 | Interaction-level regression tests | Exercise template fragments, modals, and menus to ensure UI wiring remains intact | ⚪ Not Started | Evaluate Playwright component testing or happy-path DOM snapshots |
|
||||||
| Phase 5 | Continuous integration & coverage | Integrate frontend tests into CI workflow and track coverage metrics | ⚪ Not Started | Align reporting directories with backend coverage for unified reporting |
|
| Phase 5 | Continuous integration & coverage | Integrate frontend tests into CI workflow and track coverage metrics | ⚪ Not Started | Align reporting directories with backend coverage for unified reporting |
|
||||||
|
|
||||||
@@ -21,7 +21,8 @@ This roadmap tracks the planned rollout of automated testing for the ComfyUI LoR
|
|||||||
- [x] Add AppCore page feature suite exercising context menu creation and infinite scroll registration via DOM fixtures.
|
- [x] Add AppCore page feature suite exercising context menu creation and infinite scroll registration via DOM fixtures.
|
||||||
- [x] Extend AppCore orchestration tests to cover manager wiring, bulk menu setup, and onboarding gating scenarios.
|
- [x] Extend AppCore orchestration tests to cover manager wiring, bulk menu setup, and onboarding gating scenarios.
|
||||||
- [ ] Evaluate integrating coverage reporting once test surface grows (> 20 specs).
|
- [ ] Evaluate integrating coverage reporting once test surface grows (> 20 specs).
|
||||||
- [ ] Create shared fixtures for the loras and checkpoints pages once dedicated manager suites are added.
|
- [x] Create shared fixtures for the loras and checkpoints pages once dedicated manager suites are added.
|
||||||
- [ ] Draft focused test matrix for loras/checkpoints manager filtering and sorting paths ahead of Phase 3.
|
- [ ] Draft focused test matrix for loras/checkpoints manager filtering and sorting paths ahead of Phase 3.
|
||||||
|
- [ ] Implement checkpoints page manager smoke tests covering initialization and duplicate badge wiring.
|
||||||
|
|
||||||
Maintaining this roadmap alongside code changes will make it easier to append new automated test tasks and update their progress.
|
Maintaining this roadmap alongside code changes will make it easier to append new automated test tasks and update their progress.
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { ModelDuplicatesManager } from './components/ModelDuplicatesManager.js';
|
|||||||
import { MODEL_TYPES } from './api/apiConfig.js';
|
import { MODEL_TYPES } from './api/apiConfig.js';
|
||||||
|
|
||||||
// Initialize the Checkpoints page
|
// Initialize the Checkpoints page
|
||||||
class CheckpointsPageManager {
|
export class CheckpointsPageManager {
|
||||||
constructor() {
|
constructor() {
|
||||||
// Initialize page controls
|
// Initialize page controls
|
||||||
this.pageControls = createPageControls(MODEL_TYPES.CHECKPOINT);
|
this.pageControls = createPageControls(MODEL_TYPES.CHECKPOINT);
|
||||||
@@ -31,17 +31,21 @@ class CheckpointsPageManager {
|
|||||||
async initialize() {
|
async initialize() {
|
||||||
// Initialize common page features (including context menus)
|
// Initialize common page features (including context menus)
|
||||||
appCore.initializePageFeatures();
|
appCore.initializePageFeatures();
|
||||||
|
|
||||||
console.log('Checkpoints Manager initialized');
|
console.log('Checkpoints Manager initialized');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize everything when DOM is ready
|
export async function initializeCheckpointsPage() {
|
||||||
document.addEventListener('DOMContentLoaded', async () => {
|
|
||||||
// Initialize core application
|
// Initialize core application
|
||||||
await appCore.initialize();
|
await appCore.initialize();
|
||||||
|
|
||||||
// Initialize checkpoints page
|
// Initialize checkpoints page
|
||||||
const checkpointsPage = new CheckpointsPageManager();
|
const checkpointsPage = new CheckpointsPageManager();
|
||||||
await checkpointsPage.initialize();
|
await checkpointsPage.initialize();
|
||||||
});
|
|
||||||
|
return checkpointsPage;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize everything when DOM is ready
|
||||||
|
document.addEventListener('DOMContentLoaded', initializeCheckpointsPage);
|
||||||
@@ -6,7 +6,7 @@ import { confirmDelete, closeDeleteModal, confirmExclude, closeExcludeModal } fr
|
|||||||
import { ModelDuplicatesManager } from './components/ModelDuplicatesManager.js';
|
import { ModelDuplicatesManager } from './components/ModelDuplicatesManager.js';
|
||||||
|
|
||||||
// Initialize the LoRA page
|
// Initialize the LoRA page
|
||||||
class LoraPageManager {
|
export class LoraPageManager {
|
||||||
constructor() {
|
constructor() {
|
||||||
// Add bulk mode to state
|
// Add bulk mode to state
|
||||||
state.bulkMode = false;
|
state.bulkMode = false;
|
||||||
@@ -38,18 +38,22 @@ class LoraPageManager {
|
|||||||
async initialize() {
|
async initialize() {
|
||||||
// Initialize cards for current bulk mode state (should be false initially)
|
// Initialize cards for current bulk mode state (should be false initially)
|
||||||
updateCardsForBulkMode(state.bulkMode);
|
updateCardsForBulkMode(state.bulkMode);
|
||||||
|
|
||||||
// Initialize common page features (including context menus and virtual scroll)
|
// Initialize common page features (including context menus and virtual scroll)
|
||||||
appCore.initializePageFeatures();
|
appCore.initializePageFeatures();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize everything when DOM is ready
|
export async function initializeLoraPage() {
|
||||||
document.addEventListener('DOMContentLoaded', async () => {
|
|
||||||
// Initialize core application
|
// Initialize core application
|
||||||
await appCore.initialize();
|
await appCore.initialize();
|
||||||
|
|
||||||
// Initialize page-specific functionality
|
// Initialize page-specific functionality
|
||||||
const loraPage = new LoraPageManager();
|
const loraPage = new LoraPageManager();
|
||||||
await loraPage.initialize();
|
await loraPage.initialize();
|
||||||
});
|
|
||||||
|
return loraPage;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize everything when DOM is ready
|
||||||
|
document.addEventListener('DOMContentLoaded', initializeLoraPage);
|
||||||
109
tests/frontend/pages/lorasPage.test.js
Normal file
109
tests/frontend/pages/lorasPage.test.js
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
||||||
|
import { renderLorasPage } from '../utils/pageFixtures.js';
|
||||||
|
|
||||||
|
const initializeAppMock = vi.fn();
|
||||||
|
const initializePageFeaturesMock = vi.fn();
|
||||||
|
const updateCardsForBulkModeMock = vi.fn();
|
||||||
|
const createPageControlsMock = vi.fn();
|
||||||
|
const confirmDeleteMock = vi.fn();
|
||||||
|
const closeDeleteModalMock = vi.fn();
|
||||||
|
const confirmExcludeMock = vi.fn();
|
||||||
|
const closeExcludeModalMock = vi.fn();
|
||||||
|
const state = {};
|
||||||
|
const duplicatesManagerMock = vi.fn();
|
||||||
|
|
||||||
|
vi.mock('../../../static/js/core.js', () => ({
|
||||||
|
appCore: {
|
||||||
|
initialize: initializeAppMock,
|
||||||
|
initializePageFeatures: initializePageFeaturesMock,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.mock('../../../static/js/state/index.js', () => ({
|
||||||
|
state,
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.mock('../../../static/js/components/shared/ModelCard.js', () => ({
|
||||||
|
updateCardsForBulkMode: updateCardsForBulkModeMock,
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.mock('../../../static/js/components/controls/index.js', () => ({
|
||||||
|
createPageControls: createPageControlsMock,
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.mock('../../../static/js/utils/modalUtils.js', () => ({
|
||||||
|
confirmDelete: confirmDeleteMock,
|
||||||
|
closeDeleteModal: closeDeleteModalMock,
|
||||||
|
confirmExclude: confirmExcludeMock,
|
||||||
|
closeExcludeModal: closeExcludeModalMock,
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.mock('../../../static/js/components/ModelDuplicatesManager.js', () => ({
|
||||||
|
ModelDuplicatesManager: duplicatesManagerMock,
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('LoraPageManager', () => {
|
||||||
|
let LoraPageManager;
|
||||||
|
let initializeLoraPage;
|
||||||
|
let duplicatesManagerInstance;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
vi.clearAllMocks();
|
||||||
|
|
||||||
|
state.bulkMode = undefined;
|
||||||
|
state.selectedLoras = undefined;
|
||||||
|
|
||||||
|
duplicatesManagerInstance = {
|
||||||
|
checkDuplicatesCount: vi.fn(),
|
||||||
|
};
|
||||||
|
|
||||||
|
duplicatesManagerMock.mockReturnValue(duplicatesManagerInstance);
|
||||||
|
createPageControlsMock.mockReturnValue({ destroy: vi.fn() });
|
||||||
|
initializeAppMock.mockResolvedValue(undefined);
|
||||||
|
|
||||||
|
renderLorasPage();
|
||||||
|
|
||||||
|
({ LoraPageManager, initializeLoraPage } = await import('../../../static/js/loras.js'));
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
delete window.confirmDelete;
|
||||||
|
delete window.closeDeleteModal;
|
||||||
|
delete window.confirmExclude;
|
||||||
|
delete window.closeExcludeModal;
|
||||||
|
delete window.modelDuplicatesManager;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('configures state and exposes globals during construction', () => {
|
||||||
|
const manager = new LoraPageManager();
|
||||||
|
|
||||||
|
expect(state.bulkMode).toBe(false);
|
||||||
|
expect(state.selectedLoras).toBeInstanceOf(Set);
|
||||||
|
expect(createPageControlsMock).toHaveBeenCalledWith('loras');
|
||||||
|
expect(duplicatesManagerMock).toHaveBeenCalledWith(manager);
|
||||||
|
|
||||||
|
expect(window.confirmDelete).toBe(confirmDeleteMock);
|
||||||
|
expect(window.closeDeleteModal).toBe(closeDeleteModalMock);
|
||||||
|
expect(window.confirmExclude).toBe(confirmExcludeMock);
|
||||||
|
expect(window.closeExcludeModal).toBe(closeExcludeModalMock);
|
||||||
|
expect(window.modelDuplicatesManager).toBe(duplicatesManagerInstance);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('initializes cards and page features', async () => {
|
||||||
|
const manager = new LoraPageManager();
|
||||||
|
|
||||||
|
await manager.initialize();
|
||||||
|
|
||||||
|
expect(updateCardsForBulkModeMock).toHaveBeenCalledWith(false);
|
||||||
|
expect(initializePageFeaturesMock).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('boots the page when DOMContentLoaded handler runs', async () => {
|
||||||
|
const manager = await initializeLoraPage();
|
||||||
|
|
||||||
|
expect(initializeAppMock).toHaveBeenCalledTimes(1);
|
||||||
|
expect(manager).toBeInstanceOf(LoraPageManager);
|
||||||
|
expect(updateCardsForBulkModeMock).toHaveBeenCalledWith(false);
|
||||||
|
expect(window.modelDuplicatesManager).toBe(duplicatesManagerInstance);
|
||||||
|
});
|
||||||
|
});
|
||||||
25
tests/frontend/utils/pageFixtures.js
Normal file
25
tests/frontend/utils/pageFixtures.js
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { renderTemplate } from './domFixtures.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders the LoRAs page template with expected dataset attributes.
|
||||||
|
* @returns {Element}
|
||||||
|
*/
|
||||||
|
export function renderLorasPage() {
|
||||||
|
return renderTemplate('loras.html', {
|
||||||
|
dataset: {
|
||||||
|
page: 'loras',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders the Checkpoints page template with expected dataset attributes.
|
||||||
|
* @returns {Element}
|
||||||
|
*/
|
||||||
|
export function renderCheckpointsPage() {
|
||||||
|
return renderTemplate('checkpoints.html', {
|
||||||
|
dataset: {
|
||||||
|
page: 'checkpoints',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user