287 lines
9.6 KiB
TypeScript
287 lines
9.6 KiB
TypeScript
import { render, waitFor } from "@testing-library/react";
|
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
|
|
import {
|
|
createInactiveTransformSession,
|
|
type ActiveTransformSession,
|
|
type TransformSessionState
|
|
} from "../../src/core/transform-session";
|
|
import { createEmptySceneDocument } from "../../src/document/scene-document";
|
|
import { createRuntimeClockState } from "../../src/runtime-three/runtime-project-time";
|
|
import { buildRuntimeSceneFromDocument } from "../../src/runtime-three/runtime-scene-build";
|
|
import { ViewportCanvas } from "../../src/viewport-three/ViewportCanvas";
|
|
import {
|
|
createDefaultViewportPanelCameraState,
|
|
type ViewportPanelCameraState
|
|
} from "../../src/viewport-three/viewport-layout";
|
|
import type {
|
|
CreationViewportToolPreview,
|
|
ViewportToolPreview
|
|
} from "../../src/viewport-three/viewport-transient-state";
|
|
|
|
const { MockViewportHost, viewportHostInstances } = vi.hoisted(() => {
|
|
const viewportHostInstances: Array<{
|
|
mount: ReturnType<typeof vi.fn>;
|
|
dispose: ReturnType<typeof vi.fn>;
|
|
setRenderEnabled: ReturnType<typeof vi.fn>;
|
|
updateWorld: ReturnType<typeof vi.fn>;
|
|
updateSimulation: ReturnType<typeof vi.fn>;
|
|
updateAssets: ReturnType<typeof vi.fn>;
|
|
updateDocument: ReturnType<typeof vi.fn>;
|
|
setViewMode: ReturnType<typeof vi.fn>;
|
|
setDisplayMode: ReturnType<typeof vi.fn>;
|
|
setCameraState: ReturnType<typeof vi.fn>;
|
|
setBrushSelectionChangeHandler: ReturnType<typeof vi.fn>;
|
|
setCameraStateChangeHandler: ReturnType<typeof vi.fn>;
|
|
setCreationPreviewChangeHandler: ReturnType<typeof vi.fn>;
|
|
setCreationCommitHandler: ReturnType<typeof vi.fn>;
|
|
setTransformSessionChangeHandler: ReturnType<typeof vi.fn>;
|
|
setTransformCommitHandler: ReturnType<typeof vi.fn>;
|
|
setTransformCancelHandler: ReturnType<typeof vi.fn>;
|
|
setWhiteboxHoverLabelChangeHandler: ReturnType<typeof vi.fn>;
|
|
setWhiteboxSelectionMode: ReturnType<typeof vi.fn>;
|
|
setWhiteboxSnapSettings: ReturnType<typeof vi.fn>;
|
|
setGridVisible: ReturnType<typeof vi.fn>;
|
|
setToolMode: ReturnType<typeof vi.fn>;
|
|
setCreationPreview: ReturnType<typeof vi.fn>;
|
|
setTransformSession: ReturnType<typeof vi.fn>;
|
|
setPanelId: ReturnType<typeof vi.fn>;
|
|
focusSelection: ReturnType<typeof vi.fn>;
|
|
}> = [];
|
|
|
|
class MockViewportHost {
|
|
mount = vi.fn();
|
|
dispose = vi.fn();
|
|
setRenderEnabled = vi.fn();
|
|
updateWorld = vi.fn();
|
|
updateSimulation = vi.fn();
|
|
updateAssets = vi.fn();
|
|
updateDocument = vi.fn();
|
|
setViewMode = vi.fn();
|
|
setDisplayMode = 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();
|
|
setGridVisible = vi.fn();
|
|
setToolMode = vi.fn();
|
|
setCreationPreview = vi.fn();
|
|
setTransformSession = vi.fn();
|
|
setPanelId = vi.fn();
|
|
focusSelection = vi.fn();
|
|
|
|
constructor() {
|
|
viewportHostInstances.push(this);
|
|
}
|
|
}
|
|
|
|
return {
|
|
MockViewportHost,
|
|
viewportHostInstances
|
|
};
|
|
});
|
|
|
|
vi.mock("../../src/viewport-three/viewport-host", () => ({
|
|
ViewportHost: MockViewportHost
|
|
}));
|
|
|
|
describe("ViewportCanvas", () => {
|
|
beforeEach(() => {
|
|
viewportHostInstances.length = 0;
|
|
vi.spyOn(HTMLCanvasElement.prototype, "getContext").mockImplementation(
|
|
() => ({}) as never
|
|
);
|
|
});
|
|
|
|
afterEach(() => {
|
|
vi.restoreAllMocks();
|
|
});
|
|
|
|
it("wires the creation commit handler into the viewport host", async () => {
|
|
const sceneDocument = createEmptySceneDocument();
|
|
const cameraState = createDefaultViewportPanelCameraState();
|
|
const toolPreview: CreationViewportToolPreview = {
|
|
kind: "create",
|
|
sourcePanelId: "topLeft",
|
|
target: {
|
|
kind: "box-brush"
|
|
},
|
|
center: null
|
|
};
|
|
const onCommitCreation = vi.fn(() => true);
|
|
const onCameraStateChange = vi.fn(
|
|
(_cameraState: ViewportPanelCameraState) => undefined
|
|
);
|
|
const onToolPreviewChange = vi.fn(
|
|
(_toolPreview: ViewportToolPreview) => undefined
|
|
);
|
|
const onTransformSessionChange = vi.fn(
|
|
(_transformSession: TransformSessionState) => undefined
|
|
);
|
|
const onTransformCommit = vi.fn(
|
|
(_transformSession: ActiveTransformSession) => undefined
|
|
);
|
|
const onTransformCancel = vi.fn(() => undefined);
|
|
const onSelectionChange = vi.fn();
|
|
|
|
render(
|
|
<ViewportCanvas
|
|
panelId="topLeft"
|
|
world={sceneDocument.world}
|
|
sceneDocument={sceneDocument}
|
|
editorSimulationScene={null}
|
|
editorSimulationClock={null}
|
|
projectAssets={sceneDocument.assets}
|
|
loadedModelAssets={{}}
|
|
loadedImageAssets={{}}
|
|
whiteboxSelectionMode="object"
|
|
whiteboxSnapEnabled
|
|
whiteboxSnapStep={1}
|
|
viewportGridVisible={true}
|
|
selection={{ kind: "none" }}
|
|
toolMode="create"
|
|
toolPreview={toolPreview}
|
|
transformSession={createInactiveTransformSession()}
|
|
cameraState={cameraState}
|
|
viewMode="perspective"
|
|
displayMode="authoring"
|
|
layoutMode="single"
|
|
isActivePanel
|
|
focusRequestId={0}
|
|
focusSelection={{ kind: "none" }}
|
|
onSelectionChange={onSelectionChange}
|
|
onCommitCreation={onCommitCreation}
|
|
onCameraStateChange={onCameraStateChange}
|
|
onToolPreviewChange={onToolPreviewChange}
|
|
onTransformSessionChange={onTransformSessionChange}
|
|
onTransformCommit={onTransformCommit}
|
|
onTransformCancel={onTransformCancel}
|
|
/>
|
|
);
|
|
|
|
await waitFor(() => {
|
|
expect(viewportHostInstances).toHaveLength(1);
|
|
expect(
|
|
viewportHostInstances[0].setCreationCommitHandler
|
|
).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
const registeredHandler = viewportHostInstances[0].setCreationCommitHandler
|
|
.mock.calls[0][0] as (
|
|
toolPreview: CreationViewportToolPreview
|
|
) => boolean;
|
|
|
|
expect(registeredHandler(toolPreview)).toBe(true);
|
|
expect(onCommitCreation).toHaveBeenCalledWith(toolPreview);
|
|
});
|
|
|
|
it("applies and subscribes to persisted camera state through the viewport host", async () => {
|
|
const sceneDocument = createEmptySceneDocument();
|
|
const cameraState = createDefaultViewportPanelCameraState();
|
|
const onCameraStateChange = vi.fn(
|
|
(_cameraState: ViewportPanelCameraState) => undefined
|
|
);
|
|
|
|
render(
|
|
<ViewportCanvas
|
|
panelId="topLeft"
|
|
world={sceneDocument.world}
|
|
sceneDocument={sceneDocument}
|
|
editorSimulationScene={null}
|
|
editorSimulationClock={null}
|
|
projectAssets={sceneDocument.assets}
|
|
loadedModelAssets={{}}
|
|
loadedImageAssets={{}}
|
|
whiteboxSelectionMode="object"
|
|
whiteboxSnapEnabled
|
|
whiteboxSnapStep={1}
|
|
viewportGridVisible={true}
|
|
selection={{ kind: "none" }}
|
|
toolMode="select"
|
|
toolPreview={{ kind: "none" }}
|
|
transformSession={createInactiveTransformSession()}
|
|
cameraState={cameraState}
|
|
viewMode="perspective"
|
|
displayMode="normal"
|
|
layoutMode="single"
|
|
isActivePanel
|
|
focusRequestId={0}
|
|
focusSelection={{ kind: "none" }}
|
|
onSelectionChange={vi.fn()}
|
|
onCommitCreation={vi.fn(() => true)}
|
|
onCameraStateChange={onCameraStateChange}
|
|
onToolPreviewChange={vi.fn()}
|
|
onTransformSessionChange={vi.fn()}
|
|
onTransformCommit={vi.fn()}
|
|
onTransformCancel={vi.fn()}
|
|
/>
|
|
);
|
|
|
|
await waitFor(() => {
|
|
expect(viewportHostInstances).toHaveLength(1);
|
|
expect(viewportHostInstances[0].setCameraState).toHaveBeenCalledWith(
|
|
cameraState
|
|
);
|
|
expect(
|
|
viewportHostInstances[0].setCameraStateChangeHandler
|
|
).toHaveBeenCalledTimes(1);
|
|
});
|
|
});
|
|
|
|
it("pushes editor simulation scene state into the viewport host", async () => {
|
|
const sceneDocument = createEmptySceneDocument();
|
|
const editorSimulationScene = buildRuntimeSceneFromDocument(sceneDocument);
|
|
const editorSimulationClock = createRuntimeClockState(sceneDocument.time);
|
|
|
|
render(
|
|
<ViewportCanvas
|
|
panelId="topLeft"
|
|
world={sceneDocument.world}
|
|
sceneDocument={sceneDocument}
|
|
editorSimulationScene={editorSimulationScene}
|
|
editorSimulationClock={editorSimulationClock}
|
|
projectAssets={sceneDocument.assets}
|
|
loadedModelAssets={{}}
|
|
loadedImageAssets={{}}
|
|
whiteboxSelectionMode="object"
|
|
whiteboxSnapEnabled
|
|
whiteboxSnapStep={1}
|
|
viewportGridVisible={true}
|
|
selection={{ kind: "none" }}
|
|
toolMode="select"
|
|
toolPreview={{ kind: "none" }}
|
|
transformSession={createInactiveTransformSession()}
|
|
cameraState={createDefaultViewportPanelCameraState()}
|
|
viewMode="perspective"
|
|
displayMode="normal"
|
|
layoutMode="single"
|
|
isActivePanel
|
|
focusRequestId={0}
|
|
focusSelection={{ kind: "none" }}
|
|
onSelectionChange={vi.fn()}
|
|
onCommitCreation={vi.fn(() => true)}
|
|
onCameraStateChange={vi.fn()}
|
|
onToolPreviewChange={vi.fn()}
|
|
onTransformSessionChange={vi.fn()}
|
|
onTransformCommit={vi.fn()}
|
|
onTransformCancel={vi.fn()}
|
|
/>
|
|
);
|
|
|
|
await waitFor(() => {
|
|
expect(viewportHostInstances).toHaveLength(1);
|
|
expect(viewportHostInstances[0].updateSimulation).toHaveBeenCalledWith(
|
|
editorSimulationScene,
|
|
editorSimulationClock
|
|
);
|
|
});
|
|
});
|
|
});
|