From 6170745da903eb1fd1c51568ff222c0855d4b2f3 Mon Sep 17 00:00:00 2001 From: shrimbly Date: Tue, 13 Jan 2026 20:18:33 +1300 Subject: [PATCH] test(22-01): extend GenerateVideoNode dynamic handle tests Add comprehensive tests for dynamic input handles: - Multiple image handles for multi-frame video models - Placeholder handles with dimmed opacity - Handle ordering (images before text) - Gap spacing between handle groups - Schema integration tests Co-Authored-By: Claude Opus 4.5 --- .../__tests__/GenerateVideoNode.test.tsx | 160 ++++++++++++++++++ 1 file changed, 160 insertions(+) diff --git a/src/components/__tests__/GenerateVideoNode.test.tsx b/src/components/__tests__/GenerateVideoNode.test.tsx index 3845ed64..1be4adcf 100644 --- a/src/components/__tests__/GenerateVideoNode.test.tsx +++ b/src/components/__tests__/GenerateVideoNode.test.tsx @@ -645,6 +645,166 @@ describe("GenerateVideoNode", () => { expect(imageHandle).toBeInTheDocument(); expect(textHandle).toBeInTheDocument(); }); + + describe("Multiple Image Inputs (common for video)", () => { + it("should render multiple image handles for multi-frame video models", () => { + const { container } = render( + + + + ); + + // Should have two image INPUT handles (exclude any output handles) + const imageInputHandles = container.querySelectorAll('[data-handletype="image"][class*="target"]'); + expect(imageInputHandles.length).toBe(2); + + // Check schema names are set + const schemaNames = Array.from(imageInputHandles).map(h => h.getAttribute('data-schema-name')); + expect(schemaNames).toContain('start_image'); + expect(schemaNames).toContain('end_image'); + }); + + it("should show labels like 'Start Frame', 'End Frame' from schema", () => { + render( + + + + ); + + expect(screen.getByText("Start Frame")).toBeInTheDocument(); + expect(screen.getByText("End Frame")).toBeInTheDocument(); + }); + }); + + describe("Placeholder Handles", () => { + it("should show dimmed image handle when video model only needs text", () => { + const { container } = render( + + + + ); + + // Image handle should exist with dimmed opacity + const imageHandle = container.querySelector('[data-handletype="image"]') as HTMLElement; + expect(imageHandle).toBeInTheDocument(); + expect(imageHandle.style.opacity).toBe("0.3"); + }); + + it("should show dimmed text handle when video model only needs images", () => { + const { container } = render( + + + + ); + + // Text handle should exist with dimmed opacity + const textHandle = container.querySelector('[data-handletype="text"]') as HTMLElement; + expect(textHandle).toBeInTheDocument(); + expect(textHandle.style.opacity).toBe("0.3"); + }); + + it("should show 'Not used by this model' description for placeholder handles", () => { + const { container } = render( + + + + ); + + // Image handle should have the placeholder title + const imageHandle = container.querySelector('[data-handletype="image"]'); + expect(imageHandle).toHaveAttribute("title", "Not used by this model"); + }); + }); + + describe("Schema Integration", () => { + it("should position handles correctly with image handles before text handles", () => { + const { container } = render( + + + + ); + + const imageHandle = container.querySelector('[data-handletype="image"]') as HTMLElement; + const textHandle = container.querySelector('[data-handletype="text"]') as HTMLElement; + + // Image handle should be positioned above (lower %) text handle + const imageTop = parseFloat(imageHandle.style.top); + const textTop = parseFloat(textHandle.style.top); + expect(imageTop).toBeLessThan(textTop); + }); + + it("should maintain gap between image and text handle groups", () => { + const { container } = render( + + + + ); + + // Get all input handles in order (exclude output handle) + const imageInputHandles = container.querySelectorAll('[data-handletype="image"][class*="target"]'); + const textHandle = container.querySelector('[data-handletype="text"]') as HTMLElement; + + expect(imageInputHandles.length).toBe(2); + + const image0 = imageInputHandles[0] as HTMLElement; + const image1 = imageInputHandles[1] as HTMLElement; + + const top0 = parseFloat(image0.style.top); + const top1 = parseFloat(image1.style.top); + const topText = parseFloat(textHandle.style.top); + + // Gap between image-1 and text should be larger than gap between image-0 and image-1 + const imageDiff = top1 - top0; + const gapDiff = topText - top1; + + // The gap should account for the spacing slot + expect(gapDiff).toBeGreaterThan(imageDiff * 0.9); + }); + }); }); describe("Custom Title and Comment", () => {