import { cleanup, fireEvent, screen } from "@testing-library/react";
import * as React from "react";
import { afterEach, describe, expect, it } from "vitest";
import { useToolcraftDispatch } from "../app-shell/use-toolcraft";
import { createSchema, renderCanvasShell } from "./canvas-shell-test-utils";
afterEach(() => {
cleanup();
});
describe("CanvasShell media rendering", () => {
function InfinityToggle(): React.JSX.Element {
const dispatch = useToolcraftDispatch();
return (
);
}
it("hides media when a parent layer group is hidden", () => {
renderCanvasShell({
initialState: {
layers: [
{
collapsed: false,
displayName: "Scene Group",
id: "group-1",
kind: "group",
name: "scene-group",
visible: false,
},
{
displayName: "Layer 1",
id: "layer-1",
kind: "layer",
name: "layer-1",
parentGroupId: "group-1",
visible: true,
},
],
mediaAssets: [
{
sourceSize: { height: 200, unit: "px", width: 300 },
assetKind: "image",
lifecycle: "ready",
resourceRef: "data:image/png;base64,test",
fileName: "material.png",
id: "media-1",
layerId: "layer-1",
mimeType: "image/png",
position: { x: 0, y: 0 },
size: { height: 200, unit: "px", width: 300 },
},
],
selectedLayerId: "layer-1",
},
schema: createSchema(),
});
expect(
screen.queryByRole("button", { name: "Select material.png" }),
).toBeNull();
});
it("hides unavailable image resources", () => {
renderCanvasShell({
initialState: {
layers: [
{
displayName: "Unavailable",
id: "unavailable-layer",
kind: "layer",
name: "unavailable",
visible: true,
},
],
mediaAssets: [
{
assetKind: "image",
error: {
code: "binary-resource-unavailable",
message: "The image resource is unavailable.",
},
fileName: "unavailable.png",
id: "unavailable-image",
layerId: "unavailable-layer",
lifecycle: "unavailable",
mimeType: "image/png",
position: { x: 0, y: 0 },
resourceRef: `media:image:sha256:${"b".repeat(64)}`,
size: { height: 200, unit: "px", width: 300 },
sourceSize: { height: 200, unit: "px", width: 300 },
},
],
},
schema: createSchema(),
});
expect(
screen.queryByRole("button", { name: "Select unavailable.png" }),
).toBeNull();
});
it("keeps one canonical image layer across finite and infinite modes", () => {
renderCanvasShell({
afterCanvas: ,
initialState: {
layers: [
{
displayName: "Layer 1",
id: "layer-1",
kind: "layer",
name: "layer-1",
visible: true,
},
],
mediaAssets: [
{
assetKind: "image",
fileName: "material.png",
id: "media-1",
layerId: "layer-1",
lifecycle: "ready",
mimeType: "image/png",
position: { x: 48, y: -24 },
resourceRef: `media:image:sha256:${"a".repeat(64)}`,
size: { height: 200, unit: "px", width: 300 },
sourceSize: { height: 100, unit: "px", width: 400 },
transform: { flipHorizontal: true, rotationDeg: 90 },
},
],
selectedLayerId: "layer-1",
},
schema: createSchema(),
});
const finiteLayer = screen.getByRole("button", {
name: "Select material.png",
});
expect(finiteLayer.style.cssText).toContain("height: 200px");
expect(finiteLayer.style.cssText).toContain("left: -102px");
expect(finiteLayer.style.cssText).toContain("top: -124px");
expect(finiteLayer.style.cssText).toContain("width: 300px");
expect(finiteLayer.style.transform).toBe("rotate(90deg) scale(-1, 1)");
expect(finiteLayer.dataset.selected).toBe("true");
const finiteStyle = finiteLayer.style.cssText;
expect(
document.querySelector("[data-toolcraft-canvas-surface]")
?.dataset.toolcraftCanvasMode,
).toBe("finite");
fireEvent.click(screen.getByRole("button", { name: "Enable Infinity" }));
const infiniteLayer = screen.getByRole("button", {
name: "Select material.png",
});
expect(infiniteLayer).toBe(finiteLayer);
expect(infiniteLayer.style.cssText).toBe(finiteStyle);
expect(infiniteLayer.dataset.selected).toBe("true");
expect(
document.querySelector("[data-toolcraft-canvas-surface]")
?.dataset.toolcraftCanvasMode,
).toBe("infinite");
});
it("positions uploaded images by their own frame in infinite mode", () => {
renderCanvasShell({
initialState: {
canvas: {
mode: "infinite",
offset: { x: 0, y: 0 },
size: { height: 200, unit: "px", width: 300 },
zoom: 100,
},
layers: [
{
displayName: "Layer 1",
id: "layer-1",
kind: "layer",
name: "layer-1",
visible: true,
},
],
mediaAssets: [
{
sourceSize: { height: 120, unit: "px", width: 240 },
assetKind: "image",
lifecycle: "ready",
resourceRef: "data:image/png;base64,test",
fileName: "material.png",
id: "media-1",
layerId: "layer-1",
mimeType: "image/png",
position: { x: 48, y: -24 },
size: { height: 120, unit: "px", width: 240 },
transform: { flipHorizontal: true, rotationDeg: 90 },
},
],
},
schema: createSchema(),
});
const mediaButton = screen.getByRole("button", {
name: "Select material.png",
});
const mediaImage = screen.getByRole("img", { name: "material.png" });
expect(mediaButton.style.left).toBe("-72px");
expect(mediaButton.style.top).toBe("-84px");
expect(mediaButton.style.width).toBe("240px");
expect(mediaButton.style.height).toBe("120px");
expect(mediaButton.style.transform).toContain("rotate(90deg) scale(-1, 1)");
expect(mediaImage.getAttribute("style")).toBeNull();
});
});