Merge pull request #471 from willmiao/codex/organize-frontend-tests-into-new-directories

test(frontend): document dom fixture workflow
This commit is contained in:
pixelpaws
2025-09-24 15:44:23 +08:00
committed by GitHub
7 changed files with 129 additions and 10 deletions

View File

@@ -0,0 +1,51 @@
# Frontend DOM Fixture Strategy
This guide outlines how to reproduce the markup emitted by the Django templates while running Vitest in jsdom. The aim is to make it straightforward to write integration-style unit tests for managers and UI helpers without having to duplicate template fragments inline.
## Loading Template Markup
Vitest executes inside Node, so we can read the same HTML templates that ship with the extension:
1. Use the helper utilities from `tests/frontend/utils/domFixtures.js` to read files under the `templates/` directory.
2. Mount the returned markup into `document.body` (or any custom container) before importing the module under test so its query selectors resolve correctly.
```js
import { renderTemplate } from '../utils/domFixtures.js'; // adjust the relative path to your spec
beforeEach(() => {
renderTemplate('loras.html', {
dataset: { page: 'loras' }
});
});
```
The helper ensures the dataset is applied to the container, which mirrors how Django sets `data-page` in production.
## Working with Partial Components
Many features are implemented as template partials located under `templates/components/`. When a test only needs a fragment (for example, the progress panel or context menu markup), load the component file directly:
```js
const container = renderTemplate('components/progress_panel.html');
const progressPanel = container.querySelector('#progress-panel');
```
This pattern avoids hand-written fixture strings and keeps the tests aligned with the actual markup.
## Resetting Between Tests
The shared Vitest setup clears `document.body` and storage APIs before each test. If a suite adds additional DOM nodes outside of the body or needs to reset custom attributes mid-test, use `resetDom()` exported from `domFixtures.js`.
```js
import { resetDom } from '../utils/domFixtures.js';
afterEach(() => {
resetDom();
});
```
## Future Enhancements
- Provide typed helpers for injecting mock script tags (e.g., replicating ComfyUI globals).
- Compose higher-level fixtures that mimic specific pages (loras, checkpoints, recipes) once those managers receive dedicated suites.

View File

@@ -8,7 +8,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 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 | 🟡 In Progress | AppCore initialization specs landed; expand to additional page wiring and scroll hooks |
| Phase 2 | Test AppCore orchestration | Simulate page bootstrapping, infinite scroll hooks, and manager registration using JSDOM DOM fixtures | 🟡 In Progress | AppCore initialization specs landed; documented DOM fixture workflow and plan to expand to additional page wiring and scroll hooks |
| 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 |
@@ -16,8 +16,9 @@ This roadmap tracks the planned rollout of automated testing for the ComfyUI LoR
## Next Steps Checklist
- [x] Expand unit tests for `storageHelpers` covering migrations and namespace behavior.
- [ ] Document DOM fixture strategy for reproducing template structures in tests.
- [x] Document DOM fixture strategy for reproducing template structures in tests.
- [x] Prototype AppCore initialization test that verifies manager bootstrapping with stubbed dependencies.
- [ ] 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.
Maintaining this roadmap alongside code changes will make it easier to append new automated test tasks and update their progress.

View File

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

View File

@@ -1,7 +1,7 @@
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';
import { createDefaultSettings, getCurrentPageState, initPageState, setCurrentPageType, state } from '../../../static/js/state/index.js';
import { MODEL_TYPES } from '../../../static/js/api/apiConfig.js';
import { DEFAULT_PATH_TEMPLATES } from '../../../static/js/utils/constants.js';
describe('state module', () => {
beforeEach(() => {

View File

@@ -0,0 +1,68 @@
import fs from 'node:fs';
import path from 'node:path';
const TEMPLATE_ROOT = path.resolve(process.cwd(), 'templates');
/**
* Reads an HTML template from the templates directory and returns its markup.
* @param {string} relativePath - Path relative to the templates directory.
* @returns {string}
*/
export function readTemplate(relativePath) {
const filePath = path.join(TEMPLATE_ROOT, relativePath);
return fs.readFileSync(filePath, 'utf-8');
}
/**
* Injects the provided HTML markup into the supplied container (defaults to document.body).
* @param {string} html
* @param {Element} [container=document.body]
* @returns {Element}
*/
export function mountMarkup(html, container = document.body) {
container.innerHTML = html;
return container;
}
/**
* Loads a template file and mounts it into the DOM, returning the container used.
* @param {string} relativePath - Template path relative to templates directory.
* @param {{
* container?: Element,
* dataset?: Record<string, string>,
* beforeMount?: (options: { container: Element }) => void,
* afterMount?: (options: { container: Element }) => void
* }} [options]
* @returns {Element}
*/
export function renderTemplate(relativePath, options = {}) {
const { container = document.body, dataset = {}, beforeMount, afterMount } = options;
if (beforeMount) {
beforeMount({ container });
}
const html = readTemplate(relativePath);
const target = mountMarkup(html, container);
Object.entries(dataset).forEach(([key, value]) => {
target.dataset[key] = value;
});
if (afterMount) {
afterMount({ container: target });
}
return target;
}
/**
* Utility to reset the DOM to a clean state. Useful when tests modify the structure
* beyond what the shared Vitest setup clears.
* @param {Element} [container=document.body]
*/
export function resetDom(container = document.body) {
container.innerHTML = '';
if (container === document.body) {
document.body.removeAttribute('data-page');
}
}

View File

@@ -1,5 +1,5 @@
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
import * as storageHelpers from './storageHelpers.js';
import * as storageHelpers from '../../../static/js/utils/storageHelpers.js';
const {
getStorageItem,

View File

@@ -6,7 +6,6 @@ export default defineConfig({
globals: true,
setupFiles: ['tests/frontend/setup.js'],
include: [
'static/js/**/*.test.js',
'tests/frontend/**/*.test.js'
],
coverage: {