Files
olivv-cs-ComfyUI-FunPack/app/legacy/references.test.js
T
digital-garbageandClaude Sonnet 5 29ac44ffcd feat: v4 UI port Phase A start -- module categories, legacy frontend served live
Two pieces of the "port v4's exact UI onto v5's backend" plan:

- core/contract.py: new optional ModuleSpec.category field (closed set:
  continuity, guidance, conditioning, sampling, post, system), validated in
  core/schema.py, threaded through core/registry.py's FIELDS and
  to_manifest(). Presentation grouping, independent of STAGE (execution
  order) -- a module can be stage="sampling" and category="guidance" at
  once. Only the 4 existing modules that actually render a settings panel
  (alg, sharpen, appearance, full_control) got a category assigned; the
  other 12 have none to categorize (no SETTINGS block) and stay
  uncategorised, which is valid.

- v4's frontend (movie_editor/frontend/*, ~29k lines, 61 files) copied
  verbatim into app/legacy/. core/routes.py's "/" route now serves
  legacy/index.html instead of the composer entry point; a <base> tag in
  that file resolves its own relative script/link tags against
  /funpack/app/legacy/ regardless of what URL it's served at, so every
  file moved as a unit with zero per-file path edits. Verified live: all
  61 assets load 200 OK, the real v4 onboarding screen renders pixel-for-
  pixel (it's the actual DOM/CSS, not a reimplementation), and several v4
  API calls (/api/media, /api/projects, /api/git/status) already succeed
  against v5's real backend by coincidence of shared route naming -- the
  rest 404/405 as expected, since api.js's ~90 functions haven't been
  rewired to v5's actual routes yet (next piece of this plan).

The old composer entry point (app/index.html, app/composer/**) stays in
the tree, unreferenced, until a later cleanup pass once nothing imports it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-14 23:58:51 +03:00

75 lines
3.1 KiB
JavaScript

// Reference numbering (references.js).
// Run: node --test movie_editor/frontend/references.test.js
//
// The rule these guard: the R number shown must be the one a reference slot resolves
// against. builder._reference_by_slot picks the nth mark OF THAT KIND, so a global count
// makes "Reference video 1" point at an item the UI labels R2 — and leaves no way to have
// an R1 video and an R1 image at once.
const test = require("node:test");
const assert = require("node:assert");
const R = require("./references.js");
const BIN = [
{ id: "i1", kind: "image", name: "cat.png" },
{ id: "v1", kind: "video", name: "clip.mp4" },
{ id: "i2", kind: "image", name: "dog.png" },
{ id: "a1", kind: "audio", name: "score.wav" },
];
test("an image and a video marked together are both R1", () => {
const marks = ["i1", "v1"];
assert.equal(R.referenceNumber(marks, BIN, "i1"), 1);
assert.equal(R.referenceNumber(marks, BIN, "v1"), 1);
});
test("marking order still separates two references of the same kind", () => {
const marks = ["i1", "v1", "i2"];
assert.equal(R.referenceNumber(marks, BIN, "i1"), 1);
assert.equal(R.referenceNumber(marks, BIN, "i2"), 2);
assert.equal(R.referenceNumber(marks, BIN, "v1"), 1);
});
test("marking another kind never renumbers the images", () => {
const before = R.referenceNumber(["i1", "i2"], BIN, "i2");
const after = R.referenceNumber(["i1", "a1", "i2"], BIN, "i2");
assert.equal(before, 2);
assert.equal(after, 2);
});
test("the number matches the slot that resolves it", () => {
// builder._reference_by_slot(kind, n) takes the marks of one kind in mark order and
// returns the nth. Same list, same answer, or the badge is pointing at the wrong file.
const marks = ["i1", "v1", "i2"];
const videos = R.referencesOfKind(marks, BIN, "video");
assert.equal(videos[R.referenceNumber(marks, BIN, "v1") - 1].id, "v1");
const images = R.referencesOfKind(marks, BIN, "image");
assert.equal(images[R.referenceNumber(marks, BIN, "i2") - 1].id, "i2");
});
test("unmarked media has no number", () => {
assert.equal(R.referenceNumber(["i1"], BIN, "v1"), 0);
assert.equal(R.referenceNumber([], BIN, "i1"), 0);
});
test("a mark left behind by a deleted file is not a reference", () => {
// The id stays in project.references until something prunes it; it must not occupy a
// number, or every later reference of that kind counts one too high.
const marks = ["gone", "i1"];
assert.equal(R.referenceNumber(marks, BIN, "gone"), 0);
assert.equal(R.referenceNumber(marks, BIN, "i1"), 1);
assert.equal(R.referenceCountOfKind(marks, BIN, "image"), 1);
});
test("counts are per kind", () => {
const marks = ["i1", "v1", "i2"];
assert.equal(R.referenceCountOfKind(marks, BIN, "image"), 2);
assert.equal(R.referenceCountOfKind(marks, BIN, "video"), 1);
assert.equal(R.referenceCountOfKind(marks, BIN, "audio"), 0);
});
test("media with no kind counts as an image, matching the resolver's default", () => {
const bin = [{ id: "x", name: "mystery" }, { id: "i1", kind: "image", name: "cat.png" }];
assert.equal(R.referenceNumber(["x", "i1"], bin, "i1"), 2);
});