diff --git a/src/components/__tests__/GenerateImageNode.test.tsx b/src/components/__tests__/GenerateImageNode.test.tsx index f899fe8f..c7da245f 100644 --- a/src/components/__tests__/GenerateImageNode.test.tsx +++ b/src/components/__tests__/GenerateImageNode.test.tsx @@ -623,6 +623,241 @@ describe("GenerateImageNode", () => { expect(imageHandle).toBeInTheDocument(); expect(textHandle).toBeInTheDocument(); }); + + describe("Multiple Image Inputs", () => { + it("should render multiple image handles when schema has multiple image inputs", () => { + const { container } = render( + + + + ); + + // Should have two image INPUT handles with data-schema-name attributes (exclude output handle) + 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('first_frame'); + expect(schemaNames).toContain('last_frame'); + }); + + it("should position multiple image handles correctly (spaced evenly)", () => { + const { container } = render( + + + + ); + + // Check handles have style.top set (indicating positioning) - filter to input handles only + const imageInputHandles = container.querySelectorAll('[data-handletype="image"][class*="target"]'); + expect(imageInputHandles.length).toBe(2); + + const image0Handle = imageInputHandles[0] as HTMLElement; + const image1Handle = imageInputHandles[1] as HTMLElement; + + // Both should have top positions set + expect(image0Handle.style.top).toBeTruthy(); + expect(image1Handle.style.top).toBeTruthy(); + + // First handle should be positioned higher (lower percentage) than second + const top0 = parseFloat(image0Handle.style.top); + const top1 = parseFloat(image1Handle.style.top); + expect(top0).toBeLessThan(top1); + }); + + it("should show labels for each image input from schema", () => { + render( + + + + ); + + expect(screen.getByText("First Frame")).toBeInTheDocument(); + expect(screen.getByText("Last Frame")).toBeInTheDocument(); + }); + }); + + describe("Multiple Text Inputs", () => { + it("should render multiple text handles when schema has multiple text inputs", () => { + const { container } = render( + + + + ); + + // Should have two text handles with data-schema-name attributes + const textHandles = container.querySelectorAll('[data-handletype="text"]'); + expect(textHandles.length).toBe(2); + + // Check schema names are set + const schemaNames = Array.from(textHandles).map(h => h.getAttribute('data-schema-name')); + expect(schemaNames).toContain('prompt'); + expect(schemaNames).toContain('negative_prompt'); + }); + + it("should show labels for each text input from schema", () => { + render( + + + + ); + + expect(screen.getByText("Prompt")).toBeInTheDocument(); + expect(screen.getByText("Negative Prompt")).toBeInTheDocument(); + }); + }); + + describe("Placeholder Handle Variations", () => { + it("should show dimmed image handle (opacity 0.3) when schema has only text inputs", () => { + 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 schema has only image inputs", () => { + 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("Handle Ordering", () => { + it("should render 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", () => {