Each slot lists the nodes it links to as chips that jump to them, and flags required inputs with nothing connected. Links to a subgraph's own input or output show as plain chips. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
80 lines
4.6 KiB
TypeScript
80 lines
4.6 KiB
TypeScript
import { describe, it, expect } from 'vitest';
|
|
import { getSlotLinks } from '../../src/info-panel/NodeDataExtractor';
|
|
|
|
describe('getSlotLinks', () => {
|
|
// Nodes and links as LiteGraph keeps them; getInputNode follows an input's link to its origin
|
|
const makeGraph = () => {
|
|
const graph: any = { _nodes: [], links: new Map() };
|
|
graph.getNodeById = (id: number) => graph._nodes.find((n: any) => n.id === id) ?? null;
|
|
return graph;
|
|
};
|
|
const addNode = (graph: any, id: number, title: string, inputs: any[] = [], outputs: any[] = [], required: string[] = []) => {
|
|
class Node {
|
|
static nodeData = { input: { required: Object.fromEntries(required.map(name => [name, ['IMAGE']])) } };
|
|
id = id;
|
|
title = title;
|
|
graph = graph;
|
|
inputs = inputs;
|
|
outputs = outputs;
|
|
getInputNode(slot: number) {
|
|
const link = graph.links.get(this.inputs[slot].link);
|
|
return link ? graph.getNodeById(link.origin_id) : null;
|
|
}
|
|
}
|
|
const node = new Node();
|
|
graph._nodes.push(node);
|
|
return node;
|
|
};
|
|
const link = (graph: any, id: number, origin: any, originSlot: number, target: any, targetSlot: number) => {
|
|
graph.links.set(id, { origin_id: origin.id, origin_slot: originSlot, target_id: target.id, target_slot: targetSlot });
|
|
(origin.outputs[originSlot].links ??= []).push(id);
|
|
target.inputs[targetSlot].link = id;
|
|
};
|
|
|
|
it('names the nodes at the other end of each link, flags required inputs with nothing connected, and leaves out unlinked widget inputs', () => {
|
|
const graph = makeGraph();
|
|
const loader = addNode(graph, 1, 'Load Checkpoint', [], [
|
|
{ name: 'MODEL', type: 'MODEL', links: null },
|
|
{ name: 'CLIP', type: 'CLIP', localized_name: 'CLIP', links: null }
|
|
]);
|
|
const sampler = addNode(graph, 2, 'KSampler', [
|
|
{ name: 'model', type: 'MODEL', link: null },
|
|
{ name: 'positive', type: 'CONDITIONING', label: 'Positive prompt', link: null },
|
|
{ name: 'latent_image', type: 'LATENT', link: null },
|
|
{ name: 'seed', type: 'INT', widget: { name: 'seed' }, link: null },
|
|
{ name: 'cfg', type: 'FLOAT', widget: { name: 'cfg' }, link: null }
|
|
], [], ['model', 'positive', 'latent_image']);
|
|
const encode = addNode(graph, 3, '', [{ name: 'clip', type: 'CLIP', link: null }]);
|
|
const prim = addNode(graph, 4, 'CFG', [], [{ name: 'FLOAT', type: 'FLOAT', links: null }]);
|
|
link(graph, 10, loader, 0, sampler, 0);
|
|
link(graph, 11, loader, 1, encode, 0);
|
|
link(graph, 12, prim, 0, sampler, 4);
|
|
// A subgraph's own input and output nodes are not in graph._nodes
|
|
graph.inputs = [{ name: 'positive', label: 'Prompt', type: 'CONDITIONING' }];
|
|
graph.outputs = [{ name: 'LATENT', type: 'LATENT' }];
|
|
sampler.inputs[1].link = 13;
|
|
graph.links.set(13, { origin_id: -10, origin_slot: 0, target_id: 2, target_slot: 1 });
|
|
|
|
expect(getSlotLinks(sampler as any)).toEqual({
|
|
inputs: [
|
|
{ name: 'model', type: 'MODEL', linked: [{ id: 1, title: 'Load Checkpoint', slot: 'MODEL' }], missing: false },
|
|
{ name: 'Positive prompt', type: 'CONDITIONING', linked: [{ title: 'Subgraph input', slot: 'Prompt' }], missing: false },
|
|
{ name: 'latent_image', type: 'LATENT', linked: [], missing: true },
|
|
{ name: 'cfg', type: 'FLOAT', linked: [{ id: 4, title: 'CFG', slot: 'FLOAT' }], missing: false }
|
|
],
|
|
outputs: []
|
|
});
|
|
|
|
link(graph, 14, loader, 0, addNode(graph, 5, 'Upscale', [{ name: 'model', label: 'Model in', type: 'MODEL', link: null }]), 0);
|
|
loader.outputs[0].links.push(15);
|
|
graph.links.set(15, { origin_id: 1, origin_slot: 0, target_id: -20, target_slot: 0 });
|
|
expect(getSlotLinks(loader as any).outputs).toEqual([
|
|
{ name: 'MODEL', type: 'MODEL', linked: [{ id: 2, title: 'KSampler', slot: 'model' }, { id: 5, title: 'Upscale', slot: 'Model in' }, { title: 'Subgraph output', slot: 'LATENT' }] },
|
|
{ name: 'CLIP', type: 'CLIP', linked: [{ id: 3, title: 'Untitled', slot: 'clip' }] }
|
|
]);
|
|
// Without a node definition, such as a subgraph node, nothing is required
|
|
const subgraphNode = { id: 6, title: 'Subgraph', graph, inputs: [{ name: 'image', type: 'IMAGE', link: null }], outputs: [], getInputNode: () => null };
|
|
expect(getSlotLinks(subgraphNode as any).inputs).toEqual([{ name: 'image', type: 'IMAGE', linked: [], missing: false }]);
|
|
});
|
|
});
|