Keep the control panel geometry stable when toggling building context, and use accessible switch controls to prevent focus-driven movement. Add browser coverage for camera framing, opacity, responsive layout, and the default 6x pipe scale.
193 lines
5.3 KiB
TypeScript
193 lines
5.3 KiB
TypeScript
import { fireEvent, render, screen } from "@testing-library/react";
|
|
|
|
import { ThreeDimensionalControls } from "./ThreeDimensionalControls";
|
|
import type { SceneRuntimeState } from "./sceneProtocol";
|
|
|
|
const state: SceneRuntimeState = {
|
|
mode: "network",
|
|
status: "管网实体模型",
|
|
contextVisible: true,
|
|
roofVisible: true,
|
|
displayMode: "global",
|
|
style: {
|
|
scale: 6,
|
|
mode: "uniform",
|
|
color: "#098ed0",
|
|
missingColor: "#89949d",
|
|
lowColor: "#2b83ba",
|
|
highColor: "#e66c37",
|
|
opacity: 1,
|
|
roughness: 0.3,
|
|
metalness: 0.22,
|
|
nodes: true,
|
|
direction: "none",
|
|
arrowColor: "#f2b447",
|
|
autoRange: true,
|
|
min: 0,
|
|
max: 3,
|
|
},
|
|
styleSummary: {},
|
|
appearance: {
|
|
preset: "day",
|
|
exposure: 0.95,
|
|
contextOpacity: 0.4,
|
|
shadows: true,
|
|
effects: true,
|
|
quality: "standard",
|
|
},
|
|
camera: {
|
|
active: "overview",
|
|
note: "供水总览",
|
|
views: [
|
|
{
|
|
id: "overview",
|
|
label: "供水总览",
|
|
mode: "network",
|
|
saved: false,
|
|
},
|
|
],
|
|
},
|
|
};
|
|
|
|
describe("ThreeDimensionalControls", () => {
|
|
it("toggles the active toolbar panel and opens a different panel", () => {
|
|
const onOpenChange = jest.fn();
|
|
const onTabChange = jest.fn();
|
|
const { rerender } = render(
|
|
<ThreeDimensionalControls
|
|
open
|
|
activeTab="scene"
|
|
ready
|
|
state={state}
|
|
selection={null}
|
|
timelineOpen
|
|
onOpenChange={onOpenChange}
|
|
onTimelineOpenChange={jest.fn()}
|
|
onTabChange={onTabChange}
|
|
onCommand={jest.fn()}
|
|
/>,
|
|
);
|
|
|
|
fireEvent.click(screen.getByRole("button", { name: "场景与视角" }));
|
|
expect(onOpenChange).toHaveBeenLastCalledWith(false);
|
|
expect(onTabChange).not.toHaveBeenCalled();
|
|
|
|
rerender(
|
|
<ThreeDimensionalControls
|
|
open={false}
|
|
activeTab="scene"
|
|
ready
|
|
state={state}
|
|
selection={null}
|
|
timelineOpen
|
|
onOpenChange={onOpenChange}
|
|
onTimelineOpenChange={jest.fn()}
|
|
onTabChange={onTabChange}
|
|
onCommand={jest.fn()}
|
|
/>,
|
|
);
|
|
|
|
fireEvent.click(screen.getByRole("button", { name: "管网样式" }));
|
|
expect(onTabChange).toHaveBeenLastCalledWith("style");
|
|
expect(onOpenChange).toHaveBeenLastCalledWith(true);
|
|
});
|
|
|
|
it("sends typed scene commands from platform controls", () => {
|
|
const onCommand = jest.fn();
|
|
const onTimelineOpenChange = jest.fn();
|
|
render(
|
|
<ThreeDimensionalControls
|
|
open
|
|
activeTab="scene"
|
|
ready
|
|
state={state}
|
|
selection={null}
|
|
timelineOpen
|
|
onOpenChange={jest.fn()}
|
|
onTimelineOpenChange={onTimelineOpenChange}
|
|
onTabChange={jest.fn()}
|
|
onCommand={onCommand}
|
|
/>,
|
|
);
|
|
|
|
fireEvent.click(screen.getByRole("button", { name: "站房精细版" }));
|
|
expect(onCommand).toHaveBeenCalledWith({ name: "set-mode", mode: "detail" });
|
|
fireEvent.change(screen.getByRole("slider", { name: "建筑背景透明度 60%" }), {
|
|
target: { value: "0.35" },
|
|
});
|
|
expect(onCommand).toHaveBeenCalledWith({
|
|
name: "set-appearance",
|
|
patch: { contextOpacity: 0.65 },
|
|
});
|
|
fireEvent.click(screen.getByRole("switch", { name: "建筑背景" }));
|
|
expect(onCommand).toHaveBeenCalledWith({ name: "toggle-context" });
|
|
fireEvent.click(screen.getByRole("button", { name: "时间轴" }));
|
|
expect(onTimelineOpenChange).toHaveBeenCalledWith(false);
|
|
});
|
|
|
|
it("uses the glass listbox instead of a native select", () => {
|
|
const onCommand = jest.fn();
|
|
render(
|
|
<ThreeDimensionalControls
|
|
open
|
|
activeTab="scene"
|
|
ready
|
|
state={state}
|
|
selection={null}
|
|
timelineOpen
|
|
onOpenChange={jest.fn()}
|
|
onTimelineOpenChange={jest.fn()}
|
|
onTabChange={jest.fn()}
|
|
onCommand={onCommand}
|
|
/>,
|
|
);
|
|
|
|
const displayMode = screen.getByRole("combobox", {
|
|
name: "管网展示比例",
|
|
});
|
|
expect(displayMode.tagName).not.toBe("SELECT");
|
|
|
|
fireEvent.mouseDown(displayMode);
|
|
fireEvent.click(screen.getByRole("option", { name: "泵房协调比例" }));
|
|
expect(onCommand).toHaveBeenCalledWith({
|
|
name: "set-display-mode",
|
|
mode: "coordinated",
|
|
});
|
|
});
|
|
|
|
it("shows selected asset fields and linked assets in the property tab", () => {
|
|
const onCommand = jest.fn();
|
|
render(
|
|
<ThreeDimensionalControls
|
|
open
|
|
activeTab="properties"
|
|
ready
|
|
state={state}
|
|
selection={{
|
|
assetId: "inp:node:J-1",
|
|
elementId: "J-1",
|
|
kind: "节点",
|
|
title: "节点 · J-1",
|
|
sections: [
|
|
{
|
|
title: "运行结果",
|
|
fields: [{ label: "压力", value: "31.200 mH₂O" }],
|
|
},
|
|
],
|
|
neighbors: [{ id: "P-1", label: "P-1" }],
|
|
}}
|
|
timelineOpen
|
|
onOpenChange={jest.fn()}
|
|
onTimelineOpenChange={jest.fn()}
|
|
onTabChange={jest.fn()}
|
|
onCommand={onCommand}
|
|
/>,
|
|
);
|
|
|
|
expect(screen.getByText("节点 · J-1")).toBeInTheDocument();
|
|
expect(screen.getByText("31.200 mH₂O")).toBeInTheDocument();
|
|
fireEvent.click(screen.getByRole("button", { name: "P-1" }));
|
|
expect(onCommand).toHaveBeenCalledWith({ name: "select-asset", assetId: "P-1" });
|
|
});
|
|
});
|