Files

96 lines
5.1 KiB
JavaScript

import test from "node:test";
import assert from "node:assert/strict";
import { setupDom, teardownDom } from "../../composer/tests/_dom.js";
let build;
test.before(async () => { setupDom(); ({ build } = await import("./build.js")); });
test.after(() => teardownDom());
const tick = () => new Promise((r) => setTimeout(r, 0));
const LIB = [{ name: "neon", triggers: ["neon"], replacements: ["neon lights"], category: "light" }];
function rig({ expand, stats = { scores: { neon: 2.25 } } } = {}) {
const scenes = [{ id: "a", text: "a fox" }, { id: "b", text: "a cat" }, { id: "c", text: "", gen_unit_id: "b", cut_offset_frames: 8 }, { id: "v", text: "", source: { type: "video" } }];
const heard = new Set(), writes = [];
const p = { project: { scenes }, scenes, selectedId: "a", get selected() { return scenes.find((s) => s.id === this.selectedId); },
anchor: "", postfix: "", postfixEnabled: true, variables: [], flush: async () => {},
setText: (id, t) => { writes.push([id, t]); scenes.find((s) => s.id === id).text = t; heard.forEach((f) => f()); } };
const api = { shortcuts: async () => ({ shortcuts: LIB }), suggestionStats: async () => stats,
expandPrompt: expand || (async (body) => ({ text: body.text.replace("neon", "neon lights") })) };
const page = build({ project: p, api, on: (f) => { heard.add(f); return () => heard.delete(f); } }, () => {});
document.body.replaceChildren(page.node);
const q = (label) => page.node.querySelector(`[aria-label="${label}"]`);
const press = (label) => [...page.node.querySelectorAll("button")].find((b) => b.textContent === label).click();
const select = (id) => { p.selectedId = id; heard.forEach((f) => f()); };
const undo = () => { scenes.splice(0, scenes.length, ...scenes.map((x) => ({ ...x }))); heard.forEach((f) => f()); }; // Undo: copies of the same scenes
return { page, q, press, select, writes, undo };
}
test("Build drafts from the selected scene, shows the full prompt, and Use puts the draft in that scene", async () => {
const { page, q, press, writes } = rig();
assert.equal(q("Idea").value, "a fox");
press("Build"); await tick(); await tick();
assert.equal(q("Draft").value, "a fox, neon");
assert.match(page.node.textContent, /For scene 1\. Added: neon/);
assert.match(page.node.textContent, /a fox, neon lights/);
press("Use in scene");
assert.deepEqual(writes, [["a", "a fox, neon"]]);
});
test("Use before Build, or with an emptied draft, never wipes the scene", async () => {
const { q, press, writes } = rig();
press("Use in scene");
press("Build"); await tick(); await tick();
q("Draft").value = " ";
press("Use in scene");
assert.deepEqual(writes, []);
});
test("the idea follows the selection until typed in; the draft goes to the scene it was built for, a cut's to its root", async () => {
const { q, press, select, writes } = rig();
select("b");
assert.equal(q("Idea").value, "a cat");
q("Idea").value = "my words"; q("Idea").dispatchEvent(new window.Event("input"));
select("b");
assert.equal(q("Idea").value, "my words", "typed: kept while the same scene stays selected");
select("c"); // a cut of b: same root, so the typing stays
assert.equal(q("Idea").value, "my words");
press("Build"); await tick(); await tick();
select("a");
assert.equal(q("Idea").value, "a fox", "another scene starts the idea again");
press("Use in scene");
assert.deepEqual(writes, [["b", "my words, neon"]], "the scene it was built for, not the one selected since");
});
test("an imported video clip is refused: it has no prompt", async () => {
const { page, press, select } = rig();
select("v");
press("Build"); await tick();
assert.match(page.node.textContent, /imported video/);
});
test("a slow full-prompt answer never replaces a newer one", async () => {
const waits = [];
const { page, q } = rig({ expand: (body) => new Promise((r) => waits.push(() => r({ text: `X(${body.text})` }))) });
for (const t of ["first", "second"]) { q("Draft").value = t; q("Draft").dispatchEvent(new window.Event("change")); }
waits[1](); await tick(); waits[0](); await tick();
assert.match(page.node.textContent, /X\(second\)/);
assert.doesNotMatch(page.node.textContent, /X\(first\)/);
});
test("Undo (copies of the same scenes) keeps what was typed in the idea", () => {
const { q, undo } = rig();
q("Idea").value = "typed"; q("Idea").dispatchEvent(new window.Event("input"));
undo();
assert.equal(q("Idea").value, "typed");
});
test("Build says where the like-guess stands: learning, or on with how often it was right", async () => {
const learning = rig({ stats: { scores: {}, guess: { ready: false, right: 3, of: 7, prompts: 4, weights: {} } } });
learning.press("Build"); await tick(); await tick();
assert.match(learning.page.node.textContent, /Like-guess learning: 7 ratings over 4 different prompts/);
const on = rig({ stats: { scores: {}, guess: { ready: true, right: 70, of: 80, prompts: 60, weights: { neon: 2 } } } });
on.press("Build"); await tick(); await tick();
assert.match(on.page.node.textContent, /Like-guess on: it called 70 of your 80 past ratings right/);
assert.equal(on.q("Draft").value, "a fox, neon");
});