feat(tests): add frontend automated testing setup with Vitest and jsdom

This commit is contained in:
Will Miao
2025-09-23 23:05:55 +08:00
parent dde7cf71c6
commit ae145de2f2
7 changed files with 2697 additions and 0 deletions

2
.gitignore vendored
View File

@@ -6,3 +6,5 @@ py/run_test.py
.vscode/
cache/
civitai/
node_modules/
coverage/

View File

@@ -0,0 +1,23 @@
# Frontend Automation Testing Roadmap
This roadmap tracks the planned rollout of automated testing for the ComfyUI LoRA Manager frontend. Each phase builds on the infrastructure introduced in this change set and records progress so future contributors can quickly identify the next tasks.
## Phase Overview
| Phase | Goal | Primary Focus | Status | Notes |
| --- | --- | --- | --- | --- |
| 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` | 🟡 In Progress | Prioritize deterministic scenarios for `storageHelpers`, `AppState` derivatives, and regression cases for migrations |
| Phase 2 | Test AppCore orchestration | Simulate page bootstrapping, infinite scroll hooks, and manager registration using JSDOM DOM fixtures | ⚪ Not Started | Requires DOM fixtures from `templates/*.html` and stubbing of manager modules |
| 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 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 |
## Next Steps Checklist
- [ ] Expand unit tests for `storageHelpers` covering migrations and namespace behavior.
- [ ] Document DOM fixture strategy for reproducing template structures in tests.
- [ ] Prototype AppCore initialization test that verifies manager bootstrapping with stubbed dependencies.
- [ ] Evaluate integrating coverage reporting once test surface grows (> 20 specs).
Maintaining this roadmap alongside code changes will make it easier to append new automated test tasks and update their progress.

2572
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

14
package.json Normal file
View File

@@ -0,0 +1,14 @@
{
"name": "comfyui-lora-manager-frontend",
"version": "0.1.0",
"private": true,
"type": "module",
"scripts": {
"test": "vitest run",
"test:watch": "vitest"
},
"devDependencies": {
"jsdom": "^24.0.0",
"vitest": "^1.6.0"
}
}

View File

@@ -0,0 +1,53 @@
import { describe, it, expect, beforeEach } from 'vitest';
import { createDefaultSettings, getCurrentPageState, initPageState, setCurrentPageType, state } from './index.js';
import { MODEL_TYPES } from '../api/apiConfig.js';
import { DEFAULT_PATH_TEMPLATES } from '../utils/constants.js';
describe('state module', () => {
beforeEach(() => {
// Reset to default page before each assertion
state.currentPageType = MODEL_TYPES.LORA;
});
it('creates default settings with immutable template copies', () => {
const defaultSettings = createDefaultSettings();
expect(defaultSettings).toMatchObject({
civitai_api_key: '',
language: 'en',
blur_mature_content: true
});
expect(defaultSettings.download_path_templates).toEqual(DEFAULT_PATH_TEMPLATES);
// ensure nested objects are new references so tests can safely mutate
expect(defaultSettings.download_path_templates).not.toBe(DEFAULT_PATH_TEMPLATES);
expect(defaultSettings.base_model_path_mappings).toEqual({});
expect(Object.isFrozen(defaultSettings)).toBe(false);
});
it('switches current page type when valid', () => {
const didSwitch = setCurrentPageType(MODEL_TYPES.CHECKPOINT);
expect(didSwitch).toBe(true);
expect(state.currentPageType).toBe(MODEL_TYPES.CHECKPOINT);
expect(getCurrentPageState()).toBe(state.pages[MODEL_TYPES.CHECKPOINT]);
});
it('rejects switching to an unknown page type', () => {
state.currentPageType = MODEL_TYPES.LORA;
const didSwitch = setCurrentPageType('invalid-page');
expect(didSwitch).toBe(false);
expect(state.currentPageType).toBe(MODEL_TYPES.LORA);
});
it('initializes and returns state for a known page', () => {
const pageState = initPageState(MODEL_TYPES.EMBEDDING);
expect(pageState).toBeDefined();
expect(pageState).toBe(state.pages[MODEL_TYPES.EMBEDDING]);
expect(state.currentPageType).toBe(MODEL_TYPES.EMBEDDING);
});
});

16
tests/frontend/setup.js Normal file
View File

@@ -0,0 +1,16 @@
import { afterEach, beforeEach } from 'vitest';
beforeEach(() => {
// Ensure storage is clean before each test to avoid cross-test pollution
localStorage.clear();
sessionStorage.clear();
// Reset DOM state for modules that rely on body attributes
document.body.innerHTML = '';
document.body.dataset.page = '';
});
afterEach(() => {
// Clean any dynamically attached globals by tests
delete document.body.dataset.page;
});

17
vitest.config.js Normal file
View File

@@ -0,0 +1,17 @@
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
environment: 'jsdom',
globals: true,
setupFiles: ['tests/frontend/setup.js'],
include: [
'static/js/**/*.test.js',
'tests/frontend/**/*.test.js'
],
coverage: {
enabled: false,
reportsDirectory: 'coverage/frontend'
}
}
});