Image Compare A/B drew a chip over the top-left of the picture carrying the resolution and a hint about how to use the panel - covering part of what you came to look at in order to say something you need once. The resolution moved to a caption centred under the stage, and when the two sides are different sizes both are named there: the panel scales them to fit, so nothing else on screen would reveal the mismatch. The empty and error states still take the middle of the stage, where there is no picture to hide. HOLD became an A/B toggle. Press-and-hold meant the comparison only existed while a mouse button was down, so the two images could never be held side by side in memory. Each click now swaps the panel and the button names the side you are looking at; flicking between them in place is how a small difference actually becomes visible. A workflow saved on "hold" opens on the toggle it became rather than quietly dropping back to slide. The example workflow was stale in ways that would have shipped to Civitai: its overview credited SimpleWatermarkRemover - the deprecated compatibility id, not the node the graph actually uses - still called the node Refine Mask, and linked to github.com/auboss, which does not exist. It gained model-setup and requirements sections, consistent node titles, and the mask settings its single-frame test was missing: that path shipped with every Mask Refine control at zero, so the frame it previewed was not the result the full run would produce, which is the one thing that test exists to show.
84 lines
3.2 KiB
JavaScript
84 lines
3.2 KiB
JavaScript
import assert from "node:assert/strict";
|
||
import test from "node:test";
|
||
|
||
import {
|
||
clipFraction,
|
||
compareClip,
|
||
compareSizeLabel,
|
||
findCompareImages,
|
||
normalizeCompareMode,
|
||
} from "../js/shared/compare.mjs";
|
||
|
||
test("clip fraction follows the pointer and clamps to the panel", () => {
|
||
assert.equal(clipFraction(150, 100, 200), 0.25);
|
||
assert.equal(clipFraction(100, 100, 200), 0);
|
||
assert.equal(clipFraction(40, 100, 200), 0);
|
||
assert.equal(clipFraction(900, 100, 200), 1);
|
||
});
|
||
|
||
test("degenerate panels and junk input resolve to all-A", () => {
|
||
assert.equal(clipFraction(150, 100, 0), 0);
|
||
assert.equal(clipFraction(150, 100, -5), 0);
|
||
assert.equal(clipFraction(NaN, 100, 200), 0);
|
||
assert.equal(clipFraction(150, 100, "wide"), 0);
|
||
});
|
||
|
||
test("mode normalization", () => {
|
||
assert.equal(normalizeCompareMode("slide"), "slide");
|
||
assert.equal(normalizeCompareMode("toggle"), "toggle");
|
||
assert.equal(normalizeCompareMode("wiggle"), "slide");
|
||
assert.equal(normalizeCompareMode(undefined), "slide");
|
||
});
|
||
|
||
test("a workflow saved on the old hold mode lands on the toggle it became", () => {
|
||
// Dropping it to slide instead would silently change how a saved node
|
||
// behaves, which is exactly what someone reopening their graph notices.
|
||
assert.equal(normalizeCompareMode("hold"), "toggle");
|
||
});
|
||
|
||
test("the caption names the compared resolution, once", () => {
|
||
assert.equal(
|
||
compareSizeLabel({ a: { width: 576, height: 1024 }, b: { width: 576, height: 1024 } }),
|
||
"576×1024",
|
||
);
|
||
// One side only is the common case mid-load; A speaks for the pair.
|
||
assert.equal(compareSizeLabel({ a: { width: 512, height: 512 } }), "512×512");
|
||
});
|
||
|
||
test("a size mismatch is stated rather than hidden behind A", () => {
|
||
// Comparing images of different sizes is usually a wiring mistake, and the
|
||
// panel stretches both to fit, so nothing else on screen would show it.
|
||
assert.equal(
|
||
compareSizeLabel({ a: { width: 512, height: 512 }, b: { width: 1024, height: 1024 } }),
|
||
"A 512×512 · B 1024×1024",
|
||
);
|
||
});
|
||
|
||
test("nothing loaded means no caption at all", () => {
|
||
assert.equal(compareSizeLabel(null), "");
|
||
assert.equal(compareSizeLabel({}), "");
|
||
assert.equal(compareSizeLabel({ a: { width: 0, height: 0 } }), "");
|
||
assert.equal(compareSizeLabel({ a: { filename: "x.png" } }), "");
|
||
});
|
||
|
||
test("clip CSS keeps B left of the seam and hides the seam at the edges", () => {
|
||
assert.deepEqual(compareClip(0.25), {
|
||
clipPath: "inset(0 75.00% 0 0)",
|
||
seamLeft: "25.00%",
|
||
seamVisible: true,
|
||
});
|
||
assert.equal(compareClip(0).seamVisible, false);
|
||
assert.equal(compareClip(1).seamVisible, false);
|
||
assert.equal(compareClip(2).clipPath, "inset(0 0.00% 0 0)");
|
||
assert.equal(compareClip("junk").clipPath, "inset(0 100.00% 0 0)");
|
||
});
|
||
|
||
test("compare previews are pulled from the execution payload as a pair", () => {
|
||
const a = { filename: "a.png", subfolder: "", type: "temp" };
|
||
const b = { filename: "b.png", subfolder: "", type: "temp" };
|
||
assert.deepEqual(findCompareImages({ a_images: [a], b_images: [b] }), { a, b });
|
||
assert.equal(findCompareImages({ a_images: [a] }), null);
|
||
assert.equal(findCompareImages({ a_images: [{}], b_images: [b] }), null);
|
||
assert.equal(findCompareImages(null), null);
|
||
});
|