Files
DemonGatanjieu-Anomalous_Mo…/tests/param_compare.mjs
T
DemonGatanjieuandClaude Opus 5.5 a127987348 Current node: compare the node alone, the models page's cards in the picker, no click-through
Three fixes from the user's use of the current-node panel:

- ⇄ Compare on a saved set listed the whole workflow's values. It now
  compares that node alone: a copy of the node holding the set's values,
  nothing wired, against the canvas, under the node's id so the two pair
  (a set's workflow numbered otherwise, with two prompt nodes, paired
  neither). compareParams takes `only` for this; the comparison window
  takes `nodeId` and leaves out the structure badge. Every set can now be
  compared; the unused "could not read the set's workflow" text is gone.
- The Change model picker drew cards of its own: full-size covers and
  videos without a still, so most showed blank, and only file names. It
  now shows the models page's card face (modelCardFace, split out of
  ui_grid.js): the cover thumbnail, a video's still until pointed at, the
  base badge, version, readable name and file name; "current" and the
  chosen outline stay. Versions are not folded. Its type and base chips
  and their texts are gone.
- In the generated-pictures gallery (current node, View all), once
  scrolled, "Close gallery" opened the picture scrolled up under it:
  Chrome's hit-testing again (lessons.md). Its scroll box and the
  picker's card area get clip-path: inset(0).

Checked in an isolated ComfyUI: the prompt node's compare lists only its
text; the picker shows covers and names, selects, replaces; the models
page's cards keep their buttons and open the details; a 13-step scroll
of the gallery leaves every click on the close button. Full suite and
structure check pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 02:41:52 +08:00

99 lines
6.4 KiB
JavaScript

// node tests/param_compare.mjs — two sets of workflow parameters side by side.
import assert from 'node:assert/strict';
import { compareParams, readParams, shownValue, textDifference } from '../web/modules/param_compare.js';
const node = (id, type, widgets, extra = {}) => ({
id, type, title: type, mode: 0, inputs: [],
widgets: Object.entries(widgets).map(([name, value]) => ({ name, value })), ...extra,
});
const graph = (nodes, links = {}) => ({ _nodes: nodes, links });
const imageA = graph([
node(3, 'KSampler', { seed: 11, steps: 20, cfg: 7 }, { inputs: [{ name: 'model', link: 1 }] }),
node(4, 'CheckpointLoaderSimple', { ckpt_name: 'a.safetensors' }),
node(6, 'CLIPTextEncode', { text: '1girl, red dress, smile, outdoors' }),
], { 1: [1, 4, 0, 3, 0, 'MODEL'] });
const imageB = graph([
node(3, 'KSampler', { seed: 99, steps: 30, cfg: 7 }, { inputs: [{ name: 'model', link: 1 }] }),
node(4, 'CheckpointLoaderSimple', { ckpt_name: 'a.safetensors' }),
node(6, 'CLIPTextEncode', { text: '1girl, white dress, smile, outdoors' }),
], { 1: [1, 4, 0, 3, 0, 'MODEL'] });
const same = compareParams(readParams(imageA), readParams(imageB));
assert.equal(same.sameStructure, true);
assert.equal(same.sameCount, 2); // cfg and ckpt_name
assert.deepEqual(same.nodes.map(n => [n.id, n.rows.map(r => [r.name, r.aText, r.bText, r.volatile])]), [
['3', [['seed', '11', '99', true], ['steps', '20', '30', false]]],
['6', [['text', '1girl, red dress, smile, outdoors', '1girl, white dress, smile, outdoors', false]]],
]);
assert.equal(same.nodes[0].rows[1].index, 1); // the widget's place on the B side, to write it
// A node bypassed on one side, one only on one side, and an input wired elsewhere.
const imageC = graph([
node(3, 'KSampler', { seed: 11, steps: 20, cfg: 7 }, { mode: 4, inputs: [{ name: 'model', link: 2 }] }),
node(4, 'CheckpointLoaderSimple', { ckpt_name: 'a.safetensors' }),
node(9, 'LoraLoader', { lora_name: 'x.safetensors' }),
], new Map([[2, { origin_id: 9, origin_slot: 0 }]]));
const changed = compareParams(readParams(imageA), readParams(imageC));
assert.equal(changed.sameStructure, false);
assert.deepEqual(changed.onlyA.map(n => n.id), ['6']);
assert.deepEqual(changed.onlyB.map(n => n.id), ['9']);
assert.deepEqual(changed.rewired.map(n => n.id), ['3']);
assert.deepEqual(changed.nodes[0].rows[0], { name: '', mode: true, a: 0, b: 4 });
// A node of another type under the same id is not compared value by value.
const retyped = compareParams(readParams(graph([node(5, 'A', { x: 1 })])), readParams(graph([node(5, 'B', { x: 2 })])));
assert.deepEqual([retyped.onlyA.map(n => n.type), retyped.onlyB.map(n => n.type), retyped.nodes], [['A'], ['B'], []]);
// Starred values stand apart, equal ones too, and count as compared like the rest.
const starred = compareParams(readParams(imageA), readParams(imageB), { isKey: (type, widget) => type === 'KSampler' && ['cfg', 'steps'].includes(widget) });
assert.deepEqual(starred.keys.map(({ node, row }) => [node.id, row.name, row.same]), [['3', 'steps', false], ['3', 'cfg', true]]);
assert.deepEqual(starred.nodes.map(n => [n.id, n.rows.map(r => r.name)]), [['3', ['seed']], ['6', ['text']]]);
assert.deepEqual([starred.sameCount, starred.compared], [2, 5]);
// Equal values listed on request, marked same, counted the same way.
const all = compareParams(readParams(imageA), readParams(imageB), { includeSame: true });
assert.deepEqual(all.nodes.map(n => [n.id, n.rows.map(r => [r.name, r.same])]), [
['3', [['seed', false], ['steps', false], ['cfg', true]]],
['4', [['ckpt_name', true]]],
['6', [['text', false]]],
]);
assert.deepEqual([all.sameCount, all.compared], [2, 5]);
// Two workflows numbered differently: a type found once on each side still pairs, by type.
const other = compareParams(
readParams(graph([node(3, 'KSampler', { steps: 20 }), node(6, 'CLIPTextEncode', { text: 'a' }), node(7, 'CLIPTextEncode', { text: 'b' })])),
readParams(graph([node(70, 'KSampler', { steps: 30 }), node(71, 'CLIPTextEncode', { text: 'c' }), node(72, 'CLIPTextEncode', { text: 'd' })])),
);
assert.equal(other.sameStructure, false);
assert.deepEqual(other.nodes.map(n => [n.aId, n.bId, n.id, n.rows[0].aText, n.rows[0].bText]), [['3', '70', '70', '20', '30']]);
assert.deepEqual([other.onlyA.map(n => n.id), other.onlyB.map(n => n.id), other.compared], [['6', '7'], ['71', '72'], 1]);
assert.equal(compareParams(readParams(graph([node(1, 'A', {})])), readParams(graph([node(2, 'B', {})]))).compared, 0);
// One node alone (the current node's ⇄): its pair only, though every node still pairs.
const one = compareParams(readParams(imageA), readParams(imageB), { only: { side: 'b', id: '6' } });
assert.deepEqual(one.nodes.map(n => [n.id, n.rows.map(r => r.name)]), [['6', ['text']]]);
assert.deepEqual([one.sameCount, one.compared, one.keys], [0, 1, []]);
const renumbered = compareParams(readParams(imageA), readParams(graph([node(70, 'KSampler', { steps: 30 })])), { only: { side: 'b', id: '70' } });
assert.deepEqual(renumbered.nodes.map(n => [n.aId, n.bId]), [['3', '70']], 'paired by type, then kept');
assert.deepEqual([renumbered.onlyA, renumbered.onlyB], [[], []], 'the other unpaired nodes are not this one');
const alone = compareParams(readParams(imageA), readParams(imageC), { only: { side: 'a', id: '6' } });
assert.deepEqual([alone.nodes, alone.onlyA.map(n => n.id), alone.onlyB, alone.rewired], [[], ['6'], [], []]);
const rewiredOne = compareParams(readParams(imageA), readParams(imageC), { only: { side: 'a', id: '3' } });
assert.deepEqual([rewiredOne.nodes.map(n => n.id), rewiredOne.rewired], [['3'], []], 'one node: its values, not its wiring');
// Prompts as tags: the tags only one side has, each once.
assert.deepEqual(textDifference('1girl, red dress, smile, red dress', '1girl, smile, white dress, hat'),
{ tags: true, onlyA: ['red dress'], onlyB: ['white dress', 'hat'] });
// Other text: the changed part with a little around it.
const long = `${'a'.repeat(50)} cat ${'b'.repeat(50)}`;
assert.deepEqual(textDifference(long, long.replace('cat', 'dog')), {
tags: false, before: `…${'a'.repeat(39)} `, aPart: 'cat', bPart: 'dog', after: ` ${'b'.repeat(39)}…`,
});
assert.deepEqual(textDifference('steps 20', 'steps 30'), { tags: false, before: 'steps ', aPart: '2', bPart: '3', after: '0' });
assert.equal(shownValue(0.30000000000000004), '0.3');
assert.equal(shownValue(''), '—');
assert.equal(shownValue(['x', 1]), '["x",1]');
console.log('param_compare ok');