| from __future__ import annotations |
|
|
| import subprocess |
| import textwrap |
| from pathlib import Path |
|
|
|
|
| REPO_ROOT = Path(__file__).resolve().parents[2] |
|
|
|
|
| def run_node_scenario(source: str) -> subprocess.CompletedProcess[str]: |
| return subprocess.run( |
| ["node", "--input-type=module"], |
| input=source, |
| cwd=REPO_ROOT, |
| text=True, |
| capture_output=True, |
| check=False, |
| ) |
|
|
|
|
| def test_infer_setup_surface_from_koolook_groups() -> None: |
| script = textwrap.dedent( |
| """ |
| import assert from "node:assert/strict"; |
| import { inferSetupSurface } from "./web/sidebar/published_surface.js"; |
| |
| const surface = inferSetupSurface({ |
| nodes: [ |
| { id: 12, type: "Load Image", title: "Source image", pos: [40, 40], size: [180, 80] }, |
| { id: 20, type: "Preview Image", title: "Preview", pos: [420, 40], size: [180, 80] }, |
| { id: 30, type: "Note", title: "Outside", pos: [800, 40], size: [180, 80] }, |
| ], |
| groups: [ |
| { title: "Koolook Input", bounding: [20, 20, 240, 140] }, |
| { title: "Koolook Output", pos: [400, 20], size: [240, 140] }, |
| ], |
| }); |
| |
| assert.deepEqual(surface, { |
| sourceInputs: [{ |
| group: "Koolook Input", |
| nodes: [{ id: "12", type: "Load Image", title: "Source image" }], |
| }], |
| outputs: [{ |
| group: "Koolook Output", |
| nodes: [{ id: "20", type: "Preview Image", title: "Preview" }], |
| }], |
| controls: [], |
| app: { inputs: [], outputs: [], results: [] }, |
| }); |
| """ |
| ) |
|
|
| result = run_node_scenario(script) |
| assert result.returncode == 0, result.stderr |
|
|
|
|
| def test_infer_setup_surface_app_contract_from_publish_nodes() -> None: |
| script = textwrap.dedent( |
| """ |
| import assert from "node:assert/strict"; |
| import { inferSetupSurface } from "./web/sidebar/published_surface.js"; |
| |
| const surface = inferSetupSurface({ |
| nodes: [ |
| { |
| id: 100, |
| type: "Koolook_PublishInput", |
| title: "Koolook Publish Input", |
| pos: [40, 60], |
| size: [360, 320], |
| widgets_values: [ |
| "Img", |
| "/shots/example/frames", |
| "/shots/example/movie.mov", |
| "/shots/example/image.png", |
| "unused prompt", |
| ], |
| }, |
| { |
| id: 200, |
| type: "Koolook_PublishOutput", |
| title: "Koolook Publish Output", |
| pos: [520, 60], |
| size: [360, 240], |
| widgets_values: [ |
| "/shots/example/output", |
| "publish-OUT", |
| "1", |
| ], |
| }, |
| { |
| id: 300, |
| type: "Koolook_PublishResult", |
| title: "Koolook Publish Result", |
| pos: [520, 340], |
| size: [360, 160], |
| widgets_values: ["/shots/example/output/publish-OUT_v001.mov"], |
| }, |
| ], |
| groups: [ |
| { title: "Koolook Input", bounding: [20, 20, 440, 420] }, |
| { title: "Koolook Output", bounding: [500, 20, 360, 560] }, |
| ], |
| }); |
| |
| assert.deepEqual(surface.app, { |
| inputs: [ |
| { key: "sequence_folder", label: "Sequence folder", visible: true, target: { node: "100", input: "sequence_folder" }, default: "/shots/example/frames" }, |
| { key: "qt_file", label: "QT file", visible: true, target: { node: "100", input: "qt_file" }, default: "/shots/example/movie.mov" }, |
| { key: "single_file", label: "Single file", visible: true, target: { node: "100", input: "single_file" }, default: "/shots/example/image.png" }, |
| { key: "prompt", label: "Prompt", visible: true, standalone: true, multiline: true, target: { node: "100", input: "prompt" }, default: "", placeholder: "unused prompt", help: "Describe the shot in one simple line: subject + action + setting." }, |
| ], |
| outputs: [ |
| { key: "folder", label: "Output folder", visible: true, target: { node: "200", input: "folder" }, default: "/shots/example/output" }, |
| { key: "name", label: "Output name", visible: true, target: { node: "200", input: "name" }, default: "publish-OUT" }, |
| { key: "version", label: "Version", visible: true, target: { node: "200", input: "version" }, default: "1" }, |
| ], |
| results: [ |
| { key: "result", label: "Result", visible: true, target: { node: "300", input: "result" }, default: "/shots/example/output/publish-OUT_v001.mov" }, |
| ], |
| switch: { |
| key: "switch", |
| label: "Input type", |
| visible: true, |
| target: { node: "100", input: "mode" }, |
| default: 2, |
| options: [ |
| { value: 0, label: "EXR", visible: true, input: "sequence_folder" }, |
| { value: 1, label: "QT", visible: true, input: "qt_file" }, |
| { value: 2, label: "Img", visible: true, input: "single_file" }, |
| { value: 3, label: "Prompt", visible: false, input: "prompt" }, |
| ], |
| }, |
| }); |
| """ |
| ) |
|
|
| result = run_node_scenario(script) |
| assert result.returncode == 0, result.stderr |
|
|
|
|
| def test_prompt_is_an_always_on_field_not_a_source_mode() -> None: |
| |
| |
| |
| |
| script = textwrap.dedent( |
| """ |
| import assert from "node:assert/strict"; |
| import { inferSetupSurface } from "./web/sidebar/published_surface.js"; |
| |
| const surface = inferSetupSurface({ |
| nodes: [ |
| { |
| id: 100, |
| type: "Koolook_PublishInput", |
| title: "Koolook Publish Input", |
| pos: [40, 60], |
| size: [360, 320], |
| widgets_values: [ |
| "EXR", |
| "/shots/example/frames", |
| "/shots/example/movie.mov", |
| "/shots/example/image.png", |
| "a bear talking to the camera in a sunny forest", |
| ], |
| }, |
| ], |
| groups: [ |
| { title: "Koolook Input", bounding: [20, 20, 440, 420] }, |
| ], |
| }); |
| |
| const prompt = surface.app.inputs.find(field => field.key === "prompt"); |
| assert.ok(prompt, "prompt field is present"); |
| assert.equal(prompt.visible, true); |
| assert.equal(prompt.standalone, true); |
| assert.equal(prompt.multiline, true); |
| assert.equal(prompt.default, ""); |
| assert.equal(prompt.placeholder, "a bear talking to the camera in a sunny forest"); |
| assert.ok(prompt.help && prompt.help.length > 0, "prompt carries a help hint"); |
| |
| // The prompt is never offered as a source mode in the switch. |
| const promptOption = surface.app.switch.options.find(o => o.input === "prompt"); |
| assert.equal(promptOption.visible, false); |
| const visibleSources = surface.app.switch.options.filter(o => o.visible !== false).map(o => o.input); |
| assert.deepEqual(visibleSources, ["sequence_folder", "qt_file", "single_file"]); |
| """ |
| ) |
|
|
| result = run_node_scenario(script) |
| assert result.returncode == 0, result.stderr |
|
|
|
|
| def test_infer_setup_surface_ignores_malformed_group_geometry() -> None: |
| script = textwrap.dedent( |
| """ |
| import assert from "node:assert/strict"; |
| import { inferSetupSurface } from "./web/sidebar/published_surface.js"; |
| |
| const surface = inferSetupSurface({ |
| nodes: [ |
| { id: 12, type: "Load Image", title: "Source image", pos: [40, 40], size: [180, 80] }, |
| ], |
| groups: [ |
| { title: "Koolook Input", bounding: ["bad", 20, 240, 140] }, |
| ], |
| }); |
| |
| assert.deepEqual(surface.sourceInputs, []); |
| """ |
| ) |
|
|
| result = run_node_scenario(script) |
| assert result.returncode == 0, result.stderr |
|
|