Files
ComfyAssets-ComfyUI_PromptM…/tests/js/scan-progress.test.js
T
Vito Sansevero dc192d5836 fix: output scan runs as a background job that survives the browser going away
Closing the dashboard tab or clicking outside the scan modal raised
ClientConnectionResetError out of scan_images and abandoned the scan
mid-batch, leaving a half-scanned database. Two overlapping scans could
also run at once.

- py/api/scan_job.py: ScanJob fans progress out to any number of SSE
  subscribers (latest snapshot first, then live events)
- POST /prompt_manager/scan starts the scan as an asyncio task, or attaches
  to the one already running; a write to a dead socket ends that stream
  quietly while the scan continues
- GET /prompt_manager/scan/status reports the running flag and last event
- dashboard: dismissing the modal while a scan runs minimizes it to a
  progress pill, the page re-attaches to a running scan after a reload,
  a lost stream reconnects, and leaving the page mid-scan asks first
2026-10-08 18:01:34 -07:00

79 lines
3.5 KiB
JavaScript

// Run with: node --test tests/js/
const test = require("node:test");
const assert = require("node:assert/strict");
const {
initialState,
reduceScanEvent,
fromStatus,
dismissAction,
progressLabel,
} = require("../../web/js/scan-progress.js");
test("initialState is idle with zero counts", () => {
assert.deepEqual(initialState(), {
status: "idle", progress: 0, statusText: "", processed: 0, found: 0, added: 0, linked: 0, message: "",
});
});
test("a progress event moves the scan to running and copies the counts", () => {
const before = initialState();
const after = reduceScanEvent(before, {
type: "progress", progress: 42, status: "Processing file 84/200...", processed: 84, found: 12,
});
assert.equal(after.status, "running");
assert.equal(after.progress, 42);
assert.equal(after.statusText, "Processing file 84/200...");
assert.equal(after.processed, 84);
assert.equal(after.found, 12);
assert.equal(before.status, "idle", "reducer must not mutate its input");
});
test("a complete event finishes the scan with the final totals", () => {
const running = reduceScanEvent(initialState(), { type: "progress", progress: 90, status: "x", processed: 9, found: 1 });
const done = reduceScanEvent(running, { type: "complete", processed: 10, found: 3, added: 2, linked: 1 });
assert.equal(done.status, "done");
assert.equal(done.progress, 100);
assert.deepEqual([done.processed, done.found, done.added, done.linked], [10, 3, 2, 1]);
});
test("an error event fails the scan and keeps the server message", () => {
const failed = reduceScanEvent(initialState(), { type: "error", message: "No output directories found." });
assert.equal(failed.status, "failed");
assert.equal(failed.message, "No output directories found.");
});
test("unknown events leave the state untouched", () => {
const state = reduceScanEvent(initialState(), { type: "progress", progress: 5, status: "s", processed: 1, found: 0 });
assert.equal(reduceScanEvent(state, { type: "mystery" }), state);
assert.equal(reduceScanEvent(state, null), state);
});
test("fromStatus rebuilds a running scan from the status endpoint", () => {
const state = fromStatus({
running: true,
last_event: { type: "progress", progress: 30, status: "Processing file 3/10...", processed: 3, found: 1 },
});
assert.equal(state.status, "running");
assert.equal(state.progress, 30);
assert.equal(state.processed, 3);
});
test("fromStatus is idle when nothing is running, even if a last event exists", () => {
assert.equal(fromStatus({ running: false, last_event: { type: "complete", processed: 10 } }).status, "idle");
assert.equal(fromStatus({ running: false, last_event: null }).status, "idle");
assert.equal(fromStatus(null).status, "idle");
});
test("dismissing the modal minimizes a running scan and closes otherwise", () => {
const running = reduceScanEvent(initialState(), { type: "progress", progress: 1, status: "s", processed: 0, found: 0 });
assert.equal(dismissAction(running), "minimize");
assert.equal(dismissAction(initialState()), "close");
assert.equal(dismissAction(reduceScanEvent(running, { type: "complete" })), "close");
});
test("progressLabel summarises a running scan for the minimized pill", () => {
const running = reduceScanEvent(initialState(), { type: "progress", progress: 42, status: "s", processed: 84, found: 12 });
assert.equal(progressLabel(running), "Scanning 42% · 84 files · 12 prompts");
assert.equal(progressLabel(initialState()), "");
});