Files
Mujoco_WASM/web_platform/src/app/components/SceneOutliner.test.tsx
T
chenlin 72e27b5a6c
web-platform-ci / TypeScript, lint, unit, build (push) Has been cancelled
web-platform-ci / Playwright E2E (push) Has been cancelled
feat(web-platform): release V0.8.3 常规界面优化
2026-09-04 15:34:24 +08:00

121 lines
3.8 KiB
TypeScript

import { fireEvent, render, screen } from '@testing-library/react';
import type { EditableMapDocument } from '../../map/editor/types';
import type { SimulationSnapshot } from '../../simulation/SimulationSession';
import { SceneOutliner, countSceneSearchResults } from './SceneOutliner';
const snapshot = {
bodies: [
{ id: 0, name: 'world', parentId: 0 },
{ id: 1, name: 'base', parentId: 0 },
{ id: 2, name: 'arm', parentId: 1 },
],
joints: [
{
id: 7,
name: 'arm_joint',
bodyId: 2,
type: 3,
value: 0,
min: -1,
max: 1,
limitMin: -1,
limitMax: 1,
limited: true,
limitsIgnored: false,
editable: true,
axis: [0, 0, 1],
},
],
model: { nbody: 3 },
} as SimulationSnapshot;
const document: EditableMapDocument = {
schemaVersion: 1,
mapId: 'warehouse',
revision: 0,
objects: [
{
id: 'crate-1',
name: '木箱',
type: 'box',
pose: { position: [0, 0, 0.5], quaternion: [1, 0, 0, 0] },
parameters: { sizeX: 1, sizeY: 1, sizeZ: 1 },
friction: [1, 0.005, 0.0001],
rgba: [0.5, 0.6, 0.7, 1],
placementMode: 'auto_ground',
enabled: true,
},
],
spawnPoints: [],
};
const maps = [
{
id: 'map-a',
name: '仓库',
selection: { kind: 'project' as const, descriptorPath: 'maps/warehouse/map.json' },
},
];
const documents = new Map([['maps/warehouse/map.json', document]]);
function renderOutliner(overrides: Record<string, unknown> = {}) {
const props = {
snapshot,
maps,
documents,
selection: null,
query: '',
loading: false,
pendingSceneChangeCount: 0,
pendingSceneIds: [],
onSelectBody: vi.fn(),
onSelectJoint: vi.fn(),
onJointHover: vi.fn(),
onSelectMap: vi.fn(),
onSelectMapObject: vi.fn(),
onRemoveMap: vi.fn(),
onApplyScene: vi.fn(),
onDiscardScene: vi.fn(),
...overrides,
};
render(<SceneOutliner {...props} />);
return props;
}
describe('SceneOutliner', () => {
it('在一棵场景大纲中统一展示机器人、地图实例和地图物体', () => {
renderOutliner();
expect(screen.getByLabelText('场景资产树')).toHaveTextContent('机器人');
expect(screen.getByLabelText('场景资产树')).toHaveTextContent('地图与环境');
expect(screen.getByRole('treeitem', { name: /arm_joint/ })).toBeVisible();
expect(screen.getByRole('treeitem', { name: /木箱/ })).toBeVisible();
expect(countSceneSearchResults(snapshot, maps, documents, '木箱')).toBe(2);
});
it('把所有选择统一上报为稳定 id', () => {
const props = renderOutliner();
fireEvent.click(screen.getByRole('treeitem', { name: 'base' }));
fireEvent.click(screen.getByRole('treeitem', { name: /arm_joint/ }));
fireEvent.click(screen.getByRole('treeitem', { name: /^仓库/ }));
fireEvent.click(screen.getByRole('treeitem', { name: /木箱/ }));
expect(props.onSelectBody).toHaveBeenCalledWith(1);
expect(props.onSelectJoint).toHaveBeenCalledWith(7, 2);
expect(props.onSelectMap).toHaveBeenCalledWith('map-a');
expect(props.onSelectMapObject).toHaveBeenCalledWith('map-a', 'crate-1');
});
it('在大纲中集中处理场景草稿与实例删除', () => {
const props = renderOutliner({
pendingSceneChangeCount: 2,
pendingSceneIds: ['map-a'],
});
expect(screen.getByRole('status')).toHaveTextContent('2 项场景更改待应用');
fireEvent.click(screen.getByRole('button', { name: '应用场景' }));
fireEvent.click(screen.getByRole('button', { name: '放弃更改' }));
fireEvent.click(screen.getByRole('button', { name: '删除地图实例 仓库' }));
expect(props.onApplyScene).toHaveBeenCalledOnce();
expect(props.onDiscardScene).toHaveBeenCalledOnce();
expect(props.onRemoveMap).toHaveBeenCalledWith('map-a');
});
});