Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Tw9fjX7o2YfeGQE18MEtPu
79 lines
4.6 KiB
JavaScript
79 lines
4.6 KiB
JavaScript
import test, { mock } from "node:test";
|
|
import assert from "node:assert/strict";
|
|
import { setupDom, teardownDom } from "../../composer/tests/_dom.js";
|
|
import { createDom } from "../../shell/dom.js";
|
|
|
|
let hub;
|
|
test.before(async () => { setupDom(); hub = (await import("./index.js")).default; });
|
|
test.after(() => teardownDom());
|
|
|
|
// A project with one rendered scene and the Upscale button on: pressing it runs one job with the model file or with RTX.
|
|
function rig(model, upscaleJob) {
|
|
const media = { filename: "a.mp4", subfolder: "", type: "output" };
|
|
const doc = { id: "P", scenes: [{ id: "a", text: "x" }], scene_renders: { a: { media } }, editor_settings: { upscaleMode: "button", upscaleModel: model } };
|
|
const project = { project: doc, get scenes() { return doc.scenes; }, selected: doc.scenes[0], edit: (fn) => fn(doc), editFor: async (_id, fn) => fn(doc),
|
|
pref: (k, d) => (doc.editor_settings[k] ?? d), setPref() {}, onChange: () => () => {}, subscribe: () => () => {} };
|
|
const has = new Set();
|
|
const app = { dom: createDom(), project, selection: { focus: "a", ids: ["a"] }, has, api: { upscaleModels: async () => ({ models: [model], rtx: null }), upscale: async () => ({ job_id: "j" }), upscaleJob: upscaleJob, stopJob() {} },
|
|
editorSettings: [], on: () => () => {}, openSettings() {} };
|
|
const host = document.createElement("div");
|
|
hub.setup({ host, app });
|
|
return { app, host, has };
|
|
}
|
|
|
|
test("the upscale-model flag is up while a model job runs and down after it, also when it fails", async () => {
|
|
let release, seen = [];
|
|
const { host, has } = rig("4x.safetensors", () => new Promise((r) => { release = r; }));
|
|
const press = () => host.querySelector("button");
|
|
press().click();
|
|
await new Promise((r) => setTimeout(r, 1300)); // the job is polled after a second
|
|
seen.push(has.has("upscale.model"));
|
|
release({ state: "error", detail: "boom" });
|
|
await new Promise((r) => setTimeout(r, 100));
|
|
seen.push(has.has("upscale.model"));
|
|
assert.deepEqual(seen, [true, false]);
|
|
});
|
|
|
|
// "Always": a render that appears while ComfyUI is generating is tried again every 15 s, for the same project and render only.
|
|
function always(refused) {
|
|
const first = { filename: "a.mp4", subfolder: "", type: "output" };
|
|
const doc = { id: "P", scenes: [{ id: "a" }, { id: "b" }], scene_renders: { a: { media: first } }, editor_settings: { upscaleMode: "always", upscaleModel: "4x.safetensors" } };
|
|
const project = { project: doc, get scenes() { return doc.scenes; }, selected: null, edit: (fn) => fn(doc), editFor: async (_id, fn) => fn(doc),
|
|
pref: (k, d) => (doc.editor_settings[k] ?? d), setPref() {}, };
|
|
let calls = 0, listener = () => {};
|
|
const api = { upscaleModels: async () => ({ models: ["4x.safetensors"], rtx: null }), upscale: async () => { calls += 1; throw new Error(refused); }, upscaleJob: async () => ({}), stopJob() {} };
|
|
const app = { dom: createDom(), project, selection: { focus: null, ids: [] }, has: new Set(), api, editorSettings: [], on: (fn) => { listener = fn; return () => {}; }, openSettings() {} };
|
|
hub.setup({ host: document.createElement("div"), app });
|
|
const render = (id, name) => { doc.scene_renders = { ...doc.scene_renders, [id]: { media: { filename: name, subfolder: "", type: "output" } } }; listener(); };
|
|
return { doc, render, calls: () => calls };
|
|
}
|
|
const flush = async () => { for (let i = 0; i < 6; i += 1) await Promise.resolve(); };
|
|
const BUSY = "ComfyUI is generating right now.";
|
|
|
|
test("Always tries a render again after a running generation, for as long as it is the same render", async () => {
|
|
mock.timers.enable({ apis: ["setTimeout"] });
|
|
try {
|
|
const { render, calls } = always(BUSY);
|
|
render("b", "b.mp4"); await flush();
|
|
assert.equal(calls(), 1);
|
|
mock.timers.tick(15000); await flush();
|
|
assert.equal(calls(), 2, "tried again");
|
|
for (let i = 0; i < 600; i += 1) { mock.timers.tick(15000); await flush(); }
|
|
assert.equal(calls(), 481, "one first try and 480 more, then it gives up");
|
|
} finally { mock.timers.reset(); }
|
|
});
|
|
|
|
test("Always does not try again when the render was replaced, the mode was switched off or another project is open", async () => {
|
|
for (const change of [(d) => { d.scene_renders = { a: d.scene_renders.a }; }, (d) => { d.editor_settings.upscaleMode = "never"; }, (d) => { d.id = "Q"; }]) {
|
|
mock.timers.enable({ apis: ["setTimeout"] });
|
|
try {
|
|
const { doc, render, calls } = always(BUSY);
|
|
render("b", "b.mp4"); await flush();
|
|
assert.equal(calls(), 1);
|
|
change(doc);
|
|
mock.timers.tick(15000); await flush();
|
|
assert.equal(calls(), 1);
|
|
} finally { mock.timers.reset(); }
|
|
}
|
|
});
|