Files

161 lines
5.8 KiB
JavaScript

import assert from "node:assert/strict";
import test from "node:test";
import {
PATH_WIDGETS,
alreadyProjectPathed,
detectNodeMedia,
inspectSaveNodesPatchStatus,
isSaveLikeNode,
patchSingleNode,
previewPatchSingleNode,
stampGraphProjectSignature,
} from "../../web/js/mjr/patch.js";
test("patchSingleNode patches split path and filename widgets", () => {
PATH_WIDGETS.splice(0, PATH_WIDGETS.length, "output_path");
const node = {
type: "SaveImage",
widgets: [
{ name: "output_path", value: "" },
{ name: "filename_prefix", value: "" },
],
};
assert.equal(isSaveLikeNode(node), true);
assert.equal(patchSingleNode(node, "PROJECTS/Demo/02_OUT/IMAGES", "260526_Test"), true);
assert.equal(node.widgets[0].value, "PROJECTS/Demo/02_OUT/IMAGES");
assert.equal(node.widgets[1].value, "260526_Test");
});
test("previewPatchSingleNode shows old and new widget values", () => {
PATH_WIDGETS.splice(0, PATH_WIDGETS.length, "output_path");
const node = {
title: "Preview Save",
type: "SaveImage",
widgets: [
{ name: "output_path", value: "old/path" },
{ name: "filename_prefix", value: "old_prefix" },
],
};
const preview = previewPatchSingleNode(node, "PROJECTS/Demo/02_OUT/IMAGES", "260526_Test");
assert.equal(preview.length, 2);
assert.equal(preview[0].nodeLabel, "Preview Save");
assert.equal(preview[0].oldValue, "old/path");
assert.equal(preview[0].newValue, "PROJECTS/Demo/02_OUT/IMAGES");
assert.equal(preview[1].oldValue, "old_prefix");
assert.equal(preview[1].newValue, "260526_Test");
});
test("patchSingleNode falls back to filename_prefix only nodes", () => {
PATH_WIDGETS.splice(0, PATH_WIDGETS.length, "output_path");
const node = {
type: "SaveImage",
widgets: [{ name: "filename_prefix", value: "" }],
};
assert.equal(patchSingleNode(node, "PROJECTS/Demo/02_OUT/IMAGES", "260526_Test"), true);
assert.equal(node.widgets[0].value, "PROJECTS/Demo/02_OUT/IMAGES/260526_Test");
});
test("detectNodeMedia and alreadyProjectPathed cover common save node hints", () => {
assert.equal(detectNodeMedia({ type: "VHS_VideoCombine", widgets: [] }), "videos");
assert.equal(
detectNodeMedia({ type: "CustomSave", widgets: [{ name: "fps", value: 24 }] }),
"videos"
);
assert.equal(alreadyProjectPathed("PROJECTS/Demo/02_OUT/IMAGES"), true);
assert.equal(alreadyProjectPathed("plain/output"), false);
});
test("stampGraphProjectSignature stamps output metadata and project identifiers", () => {
const fakeApp = { graph: { extra: {} } };
const fakeState = {
projectId: "proj_123",
projectFolder: "MyProject",
pathTemplate: "{BASE}/{MEDIA}/{DATE}/{NAME}",
name: "hero_shot",
kind: "shot",
media: "videos",
lastRelDir: "PROJECTS/MyProject/02_OUT/videos/260914/hero_shot",
lastPrefix: "260914_hero_shot_",
};
stampGraphProjectSignature(fakeApp, fakeState);
assert.equal(fakeApp.graph.extra.mjr_project.project_id, "proj_123");
assert.equal(fakeApp.graph.extra.mjr_project.project_folder, "MyProject");
assert.equal(fakeApp.graph.extra.mjr_project.name, "hero_shot");
assert.equal(fakeApp.graph.extra.mjr_project.kind, "shot");
assert.equal(fakeApp.graph.extra.mjr_project.media, "videos");
assert.equal(fakeApp.graph.extra.mjr_project.rel_dir, "PROJECTS/MyProject/02_OUT/videos/260914/hero_shot");
assert.equal(fakeApp.graph.extra.mjr_project.prefix, "260914_hero_shot_");
});
test("isSaveLikeNode excludes non-file outputs like ConditioningOutput and ConditioningCombine", () => {
assert.equal(isSaveLikeNode({ type: "ConditioningOutput", widgets: [] }), false);
assert.equal(isSaveLikeNode({ type: "ConditioningCombine", widgets: [] }), false);
assert.equal(isSaveLikeNode({ type: "ModelOutput", widgets: [] }), false);
// Still accepts real SaveImage with filename_prefix
assert.equal(
isSaveLikeNode({ type: "SaveImage", widgets: [{ name: "filename_prefix", value: "ComfyUI" }] }),
true
);
});
test("patchSingleNode skips when node already matches target values (early exit)", () => {
const node = {
type: "SaveImage",
widgets: [
{ name: "output_path", value: "PROJECTS/Demo/02_OUT/IMAGES" },
{ name: "filename_prefix", value: "260526_Test" },
],
};
// Already matches -> returns false, no changes
assert.equal(patchSingleNode(node, "PROJECTS/Demo/02_OUT/IMAGES", "260526_Test"), false);
// Different target -> returns true, updates widgets
assert.equal(patchSingleNode(node, "PROJECTS/Demo/02_OUT/IMAGES", "260527_New"), true);
assert.equal(node.widgets[1].value, "260527_New");
});
test("patchSingleNode with empty prefix preserves trailing slash and correctly early exits on repeat", () => {
const node = {
type: "SaveImage",
widgets: [{ name: "filename_prefix", value: "old_val" }],
};
// First patch with empty prefix -> should append trailing slash to directory
assert.equal(patchSingleNode(node, "PROJECTS/Demo/02_OUT/IMAGES", ""), true);
assert.equal(node.widgets[0].value, "PROJECTS/Demo/02_OUT/IMAGES/");
// Second patch with same target -> early-exit should return false!
assert.equal(patchSingleNode(node, "PROJECTS/Demo/02_OUT/IMAGES", ""), false);
assert.equal(node.widgets[0].value, "PROJECTS/Demo/02_OUT/IMAGES/");
});
test("inspectSaveNodesPatchStatus differentiates nodes needing update from synced nodes", () => {
const nodeSynced = {
type: "SaveImage",
widgets: [{ name: "filename_prefix", value: "PROJECTS/Demo/02_OUT/IMAGES/260526_Test_" }],
};
const nodeOutdated = {
type: "SaveImage",
widgets: [{ name: "filename_prefix", value: "ComfyUI" }],
};
const fakeApp = {
graph: {
_nodes: [nodeSynced, nodeOutdated],
},
};
const summary = inspectSaveNodesPatchStatus(fakeApp, "PROJECTS/Demo/02_OUT/IMAGES", "260526_Test_");
assert.equal(summary.total, 2);
assert.equal(summary.synced, 1);
assert.equal(summary.toUpdate, 1);
});