From 41b50260fee37c4920f3f594f46695623b4a04c7 Mon Sep 17 00:00:00 2001 From: Victor Giers Date: Thu, 23 Apr 2026 08:10:22 +0200 Subject: [PATCH] auto-git: [add] tests/unit/interaction-links.inspector.integration.test.tsx --- ...ction-links.inspector.integration.test.tsx | 266 ++++++++++++++++++ 1 file changed, 266 insertions(+) create mode 100644 tests/unit/interaction-links.inspector.integration.test.tsx diff --git a/tests/unit/interaction-links.inspector.integration.test.tsx b/tests/unit/interaction-links.inspector.integration.test.tsx new file mode 100644 index 00000000..c231ef96 --- /dev/null +++ b/tests/unit/interaction-links.inspector.integration.test.tsx @@ -0,0 +1,266 @@ +import { fireEvent, render, screen, waitFor } from "@testing-library/react"; +import { act } from "react"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; + +const { MockViewportHost, viewportHostInstances } = vi.hoisted(() => { + const viewportHostInstances: Array<{ + mount: ReturnType; + dispose: ReturnType; + updateWorld: ReturnType; + updateSimulation: ReturnType; + updateAssets: ReturnType; + updateDocument: ReturnType; + setPanelId: ReturnType; + setRenderEnabled: ReturnType; + setViewMode: ReturnType; + setDisplayMode: ReturnType; + setGridVisible: ReturnType; + setCameraState: ReturnType; + setBrushSelectionChangeHandler: ReturnType; + setCameraStateChangeHandler: ReturnType; + setCreationPreviewChangeHandler: ReturnType; + setCreationCommitHandler: ReturnType; + setTransformSessionChangeHandler: ReturnType; + setTransformCommitHandler: ReturnType; + setTransformCancelHandler: ReturnType; + setWhiteboxHoverLabelChangeHandler: ReturnType; + setWhiteboxSelectionMode: ReturnType; + setWhiteboxSnapSettings: ReturnType; + setToolMode: ReturnType; + setCreationPreview: ReturnType; + setTransformSession: ReturnType; + focusSelection: ReturnType; + }> = []; + + class MockViewportHost { + mount = vi.fn(); + dispose = vi.fn(); + updateWorld = vi.fn(); + updateSimulation = vi.fn(); + updateAssets = vi.fn(); + updateDocument = vi.fn(); + setPanelId = vi.fn(); + setRenderEnabled = vi.fn(); + setViewMode = vi.fn(); + setDisplayMode = vi.fn(); + setGridVisible = vi.fn(); + setCameraState = vi.fn(); + setBrushSelectionChangeHandler = vi.fn(); + setCameraStateChangeHandler = vi.fn(); + setCreationPreviewChangeHandler = vi.fn(); + setCreationCommitHandler = vi.fn(); + setTransformSessionChangeHandler = vi.fn(); + setTransformCommitHandler = vi.fn(); + setTransformCancelHandler = vi.fn(); + setWhiteboxHoverLabelChangeHandler = vi.fn(); + setWhiteboxSelectionMode = vi.fn(); + setWhiteboxSnapSettings = vi.fn(); + setToolMode = vi.fn(); + setCreationPreview = vi.fn(); + setTransformSession = vi.fn(); + focusSelection = vi.fn(); + + constructor() { + viewportHostInstances.push(this); + } + } + + return { + MockViewportHost, + viewportHostInstances + }; +}); + +vi.mock("../../src/viewport-three/viewport-host", () => ({ + ViewportHost: MockViewportHost +})); + +vi.mock("../../src/assets/project-asset-storage", () => ({ + getBrowserProjectAssetStorageAccess: vi.fn(async () => ({ + storage: null, + diagnostic: null + })) +})); + +import { App } from "../../src/app/App"; +import { createEditorStore } from "../../src/app/editor-store"; +import { createEmptySceneDocument } from "../../src/document/scene-document"; +import { + createCameraRigEntity, + createCameraRigWorldPointTargetRef, + createInteractableEntity, + createTriggerVolumeEntity +} from "../../src/entities/entity-instances"; + +describe("Interaction link inspector", () => { + beforeEach(() => { + viewportHostInstances.length = 0; + vi.spyOn(HTMLCanvasElement.prototype, "getContext").mockImplementation( + () => ({}) as never + ); + }); + + afterEach(() => { + vi.restoreAllMocks(); + }); + + it("authors trigger-volume control links for camera rig overrides", async () => { + const triggerVolume = createTriggerVolumeEntity({ + id: "entity-trigger-camera-link" + }); + const cameraRig = createCameraRigEntity({ + id: "entity-camera-rig-trigger-link", + target: createCameraRigWorldPointTargetRef({ + x: 0, + y: 1.5, + z: 0 + }) + }); + const store = createEditorStore({ + initialDocument: { + ...createEmptySceneDocument({ name: "Trigger Control Link Scene" }), + entities: { + [triggerVolume.id]: triggerVolume, + [cameraRig.id]: cameraRig + } + } + }); + + render(); + + await waitFor(() => { + expect(viewportHostInstances.length).toBeGreaterThan(0); + }); + + act(() => { + store.setSelection({ + kind: "entities", + ids: [triggerVolume.id] + }); + }); + + fireEvent.click(screen.getByRole("button", { name: "Add Control Link" })); + + await waitFor(() => { + expect(Object.keys(store.getState().document.interactionLinks)).toHaveLength(1); + }); + + const createdLink = Object.values(store.getState().document.interactionLinks)[0]; + + if (createdLink === undefined || createdLink.action.type !== "control") { + throw new Error("Expected a control interaction link to be created."); + } + + expect(screen.getByTestId(`interaction-link-action-${createdLink.id}`)).toHaveValue( + "control" + ); + expect( + screen.getByTestId(`interaction-link-control-target-${createdLink.id}`) + ).toHaveValue(`entity:cameraRig:${cameraRig.id}`); + expect( + screen.getByTestId(`interaction-link-control-effect-${createdLink.id}`) + ).toHaveValue("camera.activate"); + + fireEvent.change( + screen.getByTestId(`interaction-link-control-effect-${createdLink.id}`), + { + target: { + value: "camera.clear" + } + } + ); + + await waitFor(() => { + expect( + store.getState().document.interactionLinks[createdLink.id] + ).toMatchObject({ + action: { + type: "control", + effect: { + type: "clearCameraRigOverride", + target: { + kind: "entity", + entityKind: "cameraRig", + entityId: cameraRig.id + } + } + } + }); + }); + }); + + it("authors interactable control links with click-triggered camera rig activation", async () => { + const interactable = createInteractableEntity({ + id: "entity-interactable-camera-link", + prompt: "Use Camera Console" + }); + const cameraRig = createCameraRigEntity({ + id: "entity-camera-rig-interactable-link", + target: createCameraRigWorldPointTargetRef({ + x: 0, + y: 1.5, + z: 0 + }) + }); + const store = createEditorStore({ + initialDocument: { + ...createEmptySceneDocument({ name: "Interactable Control Link Scene" }), + entities: { + [interactable.id]: interactable, + [cameraRig.id]: cameraRig + } + } + }); + + render(); + + await waitFor(() => { + expect(viewportHostInstances.length).toBeGreaterThan(0); + }); + + act(() => { + store.setSelection({ + kind: "entities", + ids: [interactable.id] + }); + }); + + fireEvent.click(screen.getByRole("button", { name: "Add Control Link" })); + + await waitFor(() => { + expect(Object.keys(store.getState().document.interactionLinks)).toHaveLength(1); + }); + + const createdLink = Object.values(store.getState().document.interactionLinks)[0]; + + if (createdLink === undefined || createdLink.action.type !== "control") { + throw new Error("Expected a control interaction link to be created."); + } + + expect( + screen.getByTestId(`interaction-link-trigger-${createdLink.id}`) + ).toHaveValue("On Click"); + expect( + screen.getByTestId(`interaction-link-control-effect-${createdLink.id}`) + ).toHaveValue("camera.activate"); + + await waitFor(() => { + expect( + store.getState().document.interactionLinks[createdLink.id] + ).toMatchObject({ + trigger: "click", + action: { + type: "control", + effect: { + type: "activateCameraRigOverride", + target: { + kind: "entity", + entityKind: "cameraRig", + entityId: cameraRig.id + } + } + } + }); + }); + }); +});