187 lines
6.0 KiB
TypeScript
187 lines
6.0 KiB
TypeScript
// SimpleSyrup - workflow-focused ComfyUI extensions for image generation
|
|
// Copyright (C) 2026 Artificial Sweetener and contributors
|
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
|
|
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
|
|
import {
|
|
configureOrderedMediaNode,
|
|
registerOrderedMediaNode
|
|
} from "../../src/orderedMediaNode";
|
|
import type { ComfyExtension } from "../../src/types";
|
|
import {
|
|
CONFIG,
|
|
configured,
|
|
createFixture,
|
|
references,
|
|
resetOrderedMediaFixtures,
|
|
selectFiles,
|
|
widget
|
|
} from "./orderedMediaNodeTestSupport";
|
|
|
|
afterEach(resetOrderedMediaFixtures);
|
|
|
|
describe("ordered-media node integration", () => {
|
|
it.each(["Nodes 1.0", "Nodes 2.0"])(
|
|
"publishes through Comfy's native preview under %s",
|
|
async () => {
|
|
const fixture = createFixture(["one.png"]);
|
|
configureOrderedMediaNode(fixture.node, fixture.app, fixture.api, CONFIG);
|
|
|
|
expect(fixture.imageWidget.hidden).toBe(true);
|
|
expect(fixture.uploadWidget.hidden).toBe(true);
|
|
expect(fixture.imageWidget.computeSize?.()).toEqual([0, -4]);
|
|
expect(
|
|
fixture.node.widgets.some((candidate) =>
|
|
[
|
|
"simple_syrup_selected_media",
|
|
"simple_syrup_move_earlier",
|
|
"simple_syrup_move_later",
|
|
"simple_syrup_remove_media"
|
|
].includes(candidate.name)
|
|
)
|
|
).toBe(false);
|
|
expect(widget(fixture, "simple_syrup_replace_media").label).toBe(
|
|
"Replace images..."
|
|
);
|
|
expect(widget(fixture, "simple_syrup_add_media").label).toBe(
|
|
"Add images..."
|
|
);
|
|
expect("addDOMWidget" in fixture.node).toBe(false);
|
|
await vi.waitFor(() => {
|
|
expect(fixture.app.nodeOutputs?.["7"]?.images).toEqual(
|
|
references("one.png")
|
|
);
|
|
});
|
|
expect(fixture.executed).toHaveBeenCalledWith(
|
|
expect.objectContaining({ node: "7", display_node: "7" })
|
|
);
|
|
}
|
|
);
|
|
|
|
it("appends native multi-upload results and preserves duplicates", async () => {
|
|
const fixture = configured(["one.png", "same.png"]);
|
|
|
|
widget(fixture, "simple_syrup_add_media").callback?.();
|
|
fixture.imageWidget.value = "two.png";
|
|
fixture.imageWidget.callback?.("two.png");
|
|
fixture.imageWidget.value = ["two.png", "same.png"];
|
|
fixture.imageWidget.callback?.(["two.png", "same.png"]);
|
|
|
|
expect(fixture.nativeUpload).toHaveBeenCalledOnce();
|
|
expect(fixture.imageWidget.value).toEqual([
|
|
"one.png",
|
|
"same.png",
|
|
"two.png",
|
|
"same.png"
|
|
]);
|
|
await vi.waitFor(() => {
|
|
expect(fixture.app.nodeOutputs?.["7"]?.images).toEqual(
|
|
references("one.png", "same.png", "two.png", "same.png")
|
|
);
|
|
});
|
|
});
|
|
|
|
it("treats pasted and dropped files as append operations", () => {
|
|
const fixture = configured(["existing.png"]);
|
|
|
|
fixture.node.onDragDrop();
|
|
selectFiles(fixture, ["dropped-a.png", "dropped-b.png"]);
|
|
expect(fixture.imageWidget.value).toEqual([
|
|
"existing.png",
|
|
"dropped-a.png",
|
|
"dropped-b.png"
|
|
]);
|
|
|
|
fixture.node.pasteFiles();
|
|
selectFiles(fixture, ["pasted.png"]);
|
|
expect(fixture.imageWidget.value).toEqual([
|
|
"existing.png",
|
|
"dropped-a.png",
|
|
"dropped-b.png",
|
|
"pasted.png"
|
|
]);
|
|
});
|
|
|
|
it("normalizes scalar workflows and survives Nodes 2.0 reactive assignments", async () => {
|
|
const fixture = configured("saved.png", true);
|
|
|
|
const onGraphConfigured = fixture.node.onGraphConfigured;
|
|
if (!onGraphConfigured) throw new Error("Expected graph configuration callback.");
|
|
onGraphConfigured();
|
|
|
|
expect(fixture.imageWidget.value).toEqual(["saved.png"]);
|
|
await vi.waitFor(() => {
|
|
expect(fixture.app.nodeOutputs?.["7"]?.images).toEqual(
|
|
references("saved.png")
|
|
);
|
|
});
|
|
});
|
|
|
|
it("retains array-valued media when the legacy graph lifecycle clears the widget", async () => {
|
|
const fixture = createFixture(["one.png", "two.png"]);
|
|
fixture.node.onGraphConfigured = () => {
|
|
fixture.imageWidget.value = undefined;
|
|
};
|
|
configureOrderedMediaNode(fixture.node, fixture.app, fixture.api, CONFIG);
|
|
fixture.node.onGraphConfigured();
|
|
|
|
expect(fixture.imageWidget.value).toEqual(["one.png", "two.png"]);
|
|
await vi.waitFor(() => {
|
|
expect(fixture.app.nodeOutputs?.["7"]?.images).toEqual(
|
|
references("one.png", "two.png")
|
|
);
|
|
});
|
|
});
|
|
|
|
it("restores persisted widget values when Comfy clears the live media widget", async () => {
|
|
const fixture = createFixture(undefined);
|
|
fixture.node.widgets_values = [
|
|
["one.png", "two.png"],
|
|
"image",
|
|
"ordered_media",
|
|
"ordered_media"
|
|
];
|
|
configureOrderedMediaNode(fixture.node, fixture.app, fixture.api, CONFIG);
|
|
fixture.imageWidget.value = undefined;
|
|
|
|
fixture.node.onGraphConfigured?.();
|
|
|
|
expect(fixture.imageWidget.value).toEqual(["one.png", "two.png"]);
|
|
await vi.waitFor(() => {
|
|
expect(fixture.app.nodeOutputs?.["7"]?.images).toEqual(
|
|
references("one.png", "two.png")
|
|
);
|
|
});
|
|
});
|
|
|
|
it("restores native handlers and clears preview on node removal", () => {
|
|
const fixture = createFixture(["one.png"]);
|
|
const paste = fixture.node.pasteFiles;
|
|
const drop = fixture.node.onDragDrop;
|
|
configureOrderedMediaNode(fixture.node, fixture.app, fixture.api, CONFIG);
|
|
|
|
fixture.node.onRemoved();
|
|
|
|
expect(fixture.node.pasteFiles).toBe(paste);
|
|
expect(fixture.node.onDragDrop).toBe(drop);
|
|
expect(fixture.originalRemoved).toHaveBeenCalledOnce();
|
|
expect(fixture.app.nodeOutputs?.["7"]?.images).toEqual([]);
|
|
});
|
|
|
|
it("registers a guarded extension and ignores unrelated nodes", async () => {
|
|
const fixture = createFixture([]);
|
|
let extension: ComfyExtension | undefined;
|
|
fixture.app.registerExtension = (value: ComfyExtension) => {
|
|
extension = value;
|
|
};
|
|
registerOrderedMediaNode(fixture.app, fixture.api, "test.extension", CONFIG);
|
|
fixture.node.constructor.comfyClass = "Other.Node";
|
|
|
|
await extension?.nodeCreated?.(fixture.node);
|
|
|
|
expect(extension?.name).toBe("test.extension");
|
|
expect(fixture.node.addWidget).not.toHaveBeenCalled();
|
|
});
|
|
});
|