Files
ComfyUI-LM-Remote/tests/frontend/lora_manager_sidebar_utils.test.js
T

196 lines
5.6 KiB
JavaScript

import test from "node:test";
import assert from "node:assert/strict";
import {
buildExternalLinks,
extractLoraNames,
getSelectedGraphNodes,
matchModelItems,
normalizeLoraIdentifier,
normalizeUsageTips,
} from "../../web/comfyui/lora_manager_sidebar_utils.js";
test("normalizes loader paths and weight extensions", () => {
assert.equal(
normalizeLoraIdentifier("Styles\\Portrait.safetensors"),
"styles/portrait"
);
});
test("formats Manager usage presets and hides empty JSON", () => {
assert.deepEqual(normalizeUsageTips("{}"), []);
assert.deepEqual(
normalizeUsageTips('{"strength_min":0.7,"clipStrength":1}'),
[
{ label: "Strength min", value: "0.7" },
{ label: "Clip Strength", value: "1" },
]
);
assert.deepEqual(normalizeUsageTips("Use at low strength"), [
{ label: "Note", value: "Use at low strength" },
]);
});
test("extracts stock and numbered LoRA loader widgets", () => {
const node = {
comfyClass: "Power Lora Loader",
widgets: [
{ name: "lora_name", value: "styles/portrait.safetensors" },
{ name: "lora_01", value: "characters/alice.safetensors" },
{ name: "strength_model", value: 0.8 },
],
};
assert.deepEqual(extractLoraNames(node), [
"styles/portrait.safetensors",
"characters/alice.safetensors",
]);
});
test("treats active Manager entries as authoritative over synchronized text", () => {
const node = {
comfyClass: "Lora Loader (Remote, LoraManager)",
lorasWidget: {
value: [
{ name: "one", active: true },
{ name: "two", active: false },
],
},
widgets: [{ name: "text", value: "<lora:one:1> <lora:two:0.7>" }],
};
assert.deepEqual(extractLoraNames(node), ["one"]);
});
test("extracts LoRA syntax from text loaders without a Manager widget", () => {
const node = {
comfyClass: "LoRA Text Loader",
widgets: [{ name: "text", value: "<lora:one:1> <lora:three:0.7>" }],
};
assert.deepEqual(extractLoraNames(node), ["one", "three"]);
});
test("extracts third-party generic selectors and keyed LoRA maps", () => {
const node = {
type: "ThirdPartyLoraLoader",
widgets: [
{ name: "model", value: "styles/four.safetensors" },
{
name: "loras",
value: {
"five.safetensors": 0.7,
"disabled.safetensors": false,
},
},
],
};
assert.deepEqual(extractLoraNames(node), [
"styles/four.safetensors",
"five.safetensors",
]);
});
test("supports dynamic stack widget names and their enable switches", () => {
const node = {
type: "LoRAStackDynamic",
widgets: [
{ name: "input_mode", value: "text" },
{ name: "lora_count", value: 2 },
{ name: "lora_name_1", value: "stale-one.safetensors" },
{ name: "lora_name_text_1", value: "one.safetensors" },
{ name: "enabled_1", value: true },
{ name: "lora_name_2", value: "stale-two.safetensors" },
{ name: "lora_name_text_2", value: "two.safetensors" },
{ name: "enabled_2", value: false },
{ name: "lora_name_text_3", value: "three.safetensors" },
{ name: "enabled_3", value: true },
],
};
assert.deepEqual(extractLoraNames(node), ["one.safetensors"]);
node.widgets.find((widget) => widget.name === "input_mode").value = "dropdown";
node.widgets.find((widget) => widget.name === "lora_count").value = 1;
assert.deepEqual(extractLoraNames(node), ["stale-one.safetensors"]);
});
test("reads current selectedItems with selected_nodes fallback", () => {
const selected = { id: 4, type: "LoraLoader", widgets: [] };
assert.deepEqual(
getSelectedGraphNodes({ selectedItems: new Set([selected]) }),
[selected]
);
assert.deepEqual(
getSelectedGraphNodes({ selected_nodes: { 4: selected } }),
[selected]
);
});
test("prefers exact relative paths and reports ambiguous basenames", () => {
const items = [
{
file_name: "portrait.safetensors",
model_name: "Portrait",
folder: "styles",
file_path: "/models/loras/styles/portrait.safetensors",
},
{
file_name: "portrait.safetensors",
model_name: "Portrait Alt",
folder: "people",
file_path: "/models/loras/people/portrait.safetensors",
},
];
const exact = matchModelItems("styles/portrait.safetensors", items);
assert.equal(exact.found, true);
assert.equal(exact.model.folder, "styles");
const ambiguous = matchModelItems("portrait.safetensors", items);
assert.equal(ambiguous.ambiguous, true);
assert.equal(ambiguous.candidates.length, 2);
const absolute = matchModelItems(
"/models/loras/people/portrait.safetensors",
items
);
assert.equal(absolute.found, true);
assert.equal(absolute.model.folder, "people");
});
test("builds exact Civitai mirrors and hash-based CivArchive search", () => {
const links = buildExternalLinks("portrait", {
model_name: "Portrait",
sha256: "abc123",
civitai: { modelId: 42, id: 84 },
});
assert.equal(
links.civitai,
"https://civitai.com/models/42?modelVersionId=84"
);
assert.equal(
links.civitaiRed,
"https://civitai.red/models/42?modelVersionId=84"
);
assert.equal(links.civArchive, "https://civarchive.com/search?q=abc123");
});
test("builds encoded name searches when the Manager has no card", () => {
const links = buildExternalLinks("Krea 2 portrait");
assert.equal(
links.civitai,
"https://civitai.com/search/models?query=Krea%202%20portrait"
);
assert.equal(
links.civitaiRed,
"https://civitai.red/search/models?query=Krea%202%20portrait"
);
assert.equal(
links.civArchive,
"https://civarchive.com/search?q=Krea%202%20portrait"
);
});