Files
DemonGatanjieu-Anomalous_Mo…/tests/material_inspector_lazy.mjs
T
DemonGatanjieuandClaude Opus 5.5 7b09909e5b test: publish the plugin's tests, one command to run them
tests/ (Python backend tests, .mjs frontend rule tests) is no longer
ignored. tools/run_tests.mjs runs the structure check, every
tests/*.mjs and the Python tests with $PYTHON, the portable
python_embeded or python. docs/guides/testing.md says how to run and
write them; README links it. Developer notes no longer point at 18 test
files that were removed earlier (AGENTS.md table, backend, frontend and
update-guide notes).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 17:47:35 +08:00

62 lines
3.4 KiB
JavaScript

import assert from 'node:assert/strict';
import { renderDetailedNodeCards } from '../web/modules/material_inspector.js';
// Minimal DOM double: exercise allocation and toggle/copy handlers without a browser dependency.
class Element {
constructor(tag) {
this.tagName = tag;
this.children = [];
this.listeners = {};
this.dataset = {};
this.classList = { toggle() {} };
this.open = false;
}
appendChild(child) { this.children.push(child); return child; }
append(...children) { this.children.push(...children); }
prepend(...children) { this.children.unshift(...children); }
setAttribute() {}
querySelector(selector) { return this.children.find(child => child.className?.split(' ').includes(selector.slice(1))) || null; }
addEventListener(event, callback) { (this.listeners[event] ||= []).push(callback); }
toggle(open) { this.open = open; this.listeners.toggle?.forEach(callback => callback()); }
}
globalThis.document = { createElement: tag => new Element(tag) };
globalThis.window = { anomalous_browser_lang: 'zh' };
const descendants = node => [node, ...node.children.flatMap(descendants)];
const byClass = (node, className) => descendants(node).filter(child => child.className === className);
let propertiesRead = 0;
const blocks = Array.from({ length: 2000 }, (_, index) => ({
node_id: index, type: 'KSampler', widgets_values: [123, 'fixed', 20, 7, 'euler', 'normal', 1],
get properties() { propertiesRead++; return { long: 'x'.repeat(16000) }; },
}));
blocks.unshift({ node_id: 'prompt', type: 'CLIPTextEncode', widgets_values: [' exact prompt\n'] });
const list = renderDetailedNodeCards(new Element('main'), blocks);
assert.equal(list.children.length, 2001);
assert.equal(list.children.filter(card => card.open).length, 1);
assert.equal(byClass(list, 'anomalous-material-node-parameter-content').length, 1);
assert.equal(propertiesRead, 0, 'closed nodes must not render/format their properties');
assert.equal(byClass(list.children[0], 'anomalous-material-prompt-body')[0].textContent, ' exact prompt\n');
const sampler = list.children[1];
sampler.toggle(true);
assert.equal(byClass(sampler, 'anomalous-material-node-parameter-content').length, 1);
assert.equal(propertiesRead, 1);
sampler.toggle(false);
sampler.toggle(true);
assert.equal(propertiesRead, 1, 'reopening must reuse the rendered content');
assert.equal(byClass(sampler, 'anomalous-material-node-parameter-content').length, 1);
const selectable = renderDetailedNodeCards(new Element('main'), blocks.slice(0, 2), { selectable: true });
assert.equal(selectable.children.filter(card => card.open).length, 0, 'workbench selection cards stay closed');
assert.equal(byClass(selectable, 'anomalous-material-node-parameter-content').length, 0);
const copy = byClass(list.children[0], 'anomalous-material-copy-prompt-btn')[0];
Object.defineProperty(globalThis, 'navigator', { configurable: true, value: {
clipboard: { writeText: async () => { throw new Error('denied'); } },
} });
await copy.onclick({ stopPropagation() {} });
assert.equal(copy.textContent, '复制失败,请重试');
navigator.clipboard.writeText = async value => assert.equal(value, ' exact prompt\n');
await copy.onclick({ stopPropagation() {} });
assert.notEqual(copy.textContent, '复制失败,请重试');
console.log('material inspector: 2001 cards, one initial body, lazy toggles and clipboard feedback OK');