/* Copyright 2026 Marimo. All rights reserved. */ import { fireEvent, render } from "@testing-library/react"; import { describe, expect, it } from "vitest"; import ImageComparisonComponent from "../ImageComparisonComponent"; const baseProps = { beforeSrc: "before.png", afterSrc: "after.png", value: 50, direction: "horizontal" as const, }; describe("ImageComparisonComponent", () => { it("renders the comparison slider with both images", () => { const { getByAltText, container } = render( , ); expect(getByAltText("Before")).toBeTruthy(); expect(getByAltText("After")).toBeTruthy(); expect(container.querySelector("img-comparison-slider")).toBeTruthy(); }); it("shows a visible error instead of collapsing when an image fails to load", () => { const brokenSrc = "https://example.com/does-not-exist.png"; const { getByAltText, queryByText, container } = render( , ); // No error before the image fails. expect(queryByText(/Failed to load/)).toBeNull(); fireEvent.error(getByAltText("Before")); // The slider is replaced with a visible error mentioning the source, so // the output is never a silent blank. expect(container.querySelector("img-comparison-slider")).toBeNull(); const error = queryByText(/Failed to load image/); expect(error).toBeTruthy(); expect(error?.textContent).toContain(brokenSrc); }); it("clears the error when the sources change so new images can load", () => { const brokenSrc = "https://example.com/does-not-exist.png"; const { getByAltText, queryByText, container, rerender } = render( , ); fireEvent.error(getByAltText("Before")); expect(queryByText(/Failed to load/)).toBeTruthy(); // Re-render with a new source (e.g. the user fixed the notebook): the error // should clear and the slider should mount again rather than stay stuck. rerender(); expect(queryByText(/Failed to load/)).toBeNull(); expect(container.querySelector("img-comparison-slider")).toBeTruthy(); }); it("truncates long sources (e.g. data URLs) in the error message", () => { const longSrc = `data:image/png;base64,${"A".repeat(200)}`; const { getByAltText, queryByText } = render( , ); fireEvent.error(getByAltText("After")); const error = queryByText(/Failed to load image/); expect(error?.textContent).toContain("…"); expect(error?.textContent).not.toContain("A".repeat(200)); }); it("sizes both images explicitly when dimensions are given", () => { const { getAllByAltText } = render( , ); for (const img of getAllByAltText(/Before|After/)) { expect((img as HTMLElement).style.width).toBe("300px"); expect((img as HTMLElement).style.height).toBe("300px"); // Letterbox identically so the slider's clip stays aligned. expect((img as HTMLElement).style.objectFit).toBe("contain"); } }); it("applies a single dimension to both images when only one is given", () => { const { getAllByAltText } = render( , ); for (const img of getAllByAltText(/Before|After/)) { expect((img as HTMLElement).style.width).toBe("300px"); expect((img as HTMLElement).style.objectFit).toBe("contain"); } }); it("applies height-only sizing to both images", () => { const { getAllByAltText } = render( , ); for (const img of getAllByAltText(/Before|After/)) { expect((img as HTMLElement).style.height).toBe("300px"); expect((img as HTMLElement).style.objectFit).toBe("contain"); } }); it("treats empty-string dimensions as not provided", () => { const { getAllByAltText } = render( , ); for (const img of getAllByAltText(/Before|After/)) { expect((img as HTMLElement).style.width).toBe(""); expect((img as HTMLElement).style.height).toBe(""); } }); it.each(["horizontal", "vertical"] as const)( "lets the container grow to the images when no height is given (%s)", (direction) => { const { container } = render( , ); // A fixed height would crop tall images and leave the output's expand // button with nothing to reveal. const slider = container.querySelector("img-comparison-slider"); expect(slider).toBeTruthy(); expect(slider?.parentElement?.style.height).toBe("auto"); }, ); it("leaves images unstyled when no dimensions are given", () => { const { getAllByAltText } = render( , ); for (const img of getAllByAltText(/Before|After/)) { // No explicit sizing: the slider keeps its intrinsic-size behavior. expect((img as HTMLElement).style.width).toBe(""); expect((img as HTMLElement).style.height).toBe(""); } }); });