Files
Mujoco_WASM/web_platform/src/map/PhysicalMapPanel.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

216 lines
7.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { fireEvent, render, screen } from '@testing-library/react';
import { DEFAULT_PHYSICAL_MAP_CONFIG } from './types';
import { PhysicalMapPanel } from './PhysicalMapPanel';
const common = {
maps: [],
rootBodies: ['robot'],
loading: false,
nativeUrdf: false,
showVisualMap: true,
showMapCollision: false,
editorDocument: null,
onEditorPreview: () => {},
onEditorDraftChange: () => {},
onEditorApply: async () => true,
onEditorExport: () => {},
onEditorConvert: async () => true,
onEditorBindInteraction: () => {},
onEditorSelect: () => {},
onEditorSessionStateChange: () => {},
onMapDisplay: () => {},
onDraft: () => {},
};
describe('PhysicalMapPanel', () => {
it('无选中实例时显示引导空态', () => {
render(<PhysicalMapPanel {...common} value={{ kind: 'none' }} onApply={() => {}} />);
expect(screen.getByText('未选择地图实例')).toBeInTheDocument();
expect(screen.getByLabelText('地图来源')).toHaveValue('none');
});
it('已有场景草稿时无需再次改参数也可一次编译', () => {
const onApply = vi.fn();
render(
<PhysicalMapPanel
{...common}
value={{ kind: 'builtin', config: { ...DEFAULT_PHYSICAL_MAP_CONFIG, preset: 'rough' } }}
sceneDirty
onApply={onApply}
/>,
);
expect(screen.getByText(/场景草稿正在轻量预览/)).toBeInTheDocument();
const apply = screen.getByRole('button', { name: '应用并重新编译' });
expect(apply).toBeEnabled();
fireEvent.click(apply);
expect(onApply).toHaveBeenCalledTimes(1);
});
it('选择楼梯参数后请求重新编译', () => {
const onApply = vi.fn();
render(
<PhysicalMapPanel
{...common}
value={{ kind: 'builtin', config: { ...DEFAULT_PHYSICAL_MAP_CONFIG, preset: 'flat' } }}
onApply={onApply}
/>,
);
fireEvent.change(screen.getByLabelText('物理地图预设'), { target: { value: 'stairs' } });
fireEvent.change(screen.getByLabelText('台阶数量'), { target: { value: '6' } });
fireEvent.click(screen.getByRole('button', { name: '应用并重新编译' }));
expect(onApply).toHaveBeenCalledWith({
kind: 'builtin',
config: expect.objectContaining({ preset: 'stairs', stairCount: 6 }),
});
});
it('在右侧参数面板调整系统地形并提交', () => {
const onApply = vi.fn();
render(
<PhysicalMapPanel
{...common}
value={{ kind: 'builtin', config: { ...DEFAULT_PHYSICAL_MAP_CONFIG, preset: 'flat' } }}
onApply={onApply}
/>,
);
fireEvent.change(screen.getByLabelText('物理地图预设'), { target: { value: 'rough' } });
fireEvent.change(screen.getByLabelText('地形难度(01'), { target: { value: '0.8' } });
fireEvent.change(screen.getByLabelText('随机种子'), { target: { value: '9' } });
fireEvent.click(screen.getByRole('button', { name: '应用并重新编译' }));
expect(onApply).toHaveBeenCalledWith({
kind: 'builtin',
config: expect.objectContaining({
preset: 'rough',
terrainDifficulty: 0.8,
seed: 9,
}),
});
});
it('参数地形属性不再承载视口变换工具', () => {
render(
<PhysicalMapPanel
{...common}
value={{ kind: 'builtin', config: { ...DEFAULT_PHYSICAL_MAP_CONFIG, preset: 'rough' } }}
onApply={() => {}}
/>,
);
expect(screen.queryByLabelText('地图变换模式')).not.toBeInTheDocument();
expect(screen.getByText(/已统一到视口浮动工具条/)).toBeInTheDocument();
});
it('表单参数与外部视口变换合并到同一份草稿', () => {
const onDraft = vi.fn();
const onApply = vi.fn();
const initial = {
kind: 'builtin' as const,
config: { ...DEFAULT_PHYSICAL_MAP_CONFIG, preset: 'rough' as const },
};
const view = render(
<PhysicalMapPanel
{...common}
value={initial}
sceneDirty
onDraft={onDraft}
onApply={onApply}
/>,
);
fireEvent.change(screen.getByLabelText('地形边长(m'), { target: { value: '7' } });
const staged = onDraft.mock.calls.at(-1)?.[0];
expect(staged).toMatchObject({ kind: 'builtin', config: { size: 7 } });
const moved = {
...staged,
config: { ...staged.config, positionX: 2.4, positionY: -1.2 },
};
view.rerender(
<PhysicalMapPanel {...common} value={moved} sceneDirty onDraft={onDraft} onApply={onApply} />,
);
expect(screen.getByLabelText('地形边长(m')).toHaveValue(7);
expect(screen.getByLabelText('位置 Xm')).toHaveValue(2.4);
fireEvent.click(screen.getByRole('button', { name: '应用并重新编译' }));
expect(onApply).toHaveBeenLastCalledWith(
expect.objectContaining({
kind: 'builtin',
config: expect.objectContaining({ size: 7, positionX: 2.4, positionY: -1.2 }),
}),
);
});
it('可以移动并旋转资产地图', () => {
const onApply = vi.fn();
render(
<PhysicalMapPanel
{...common}
value={{ kind: 'builtin', config: { ...DEFAULT_PHYSICAL_MAP_CONFIG, preset: 'flat' } }}
onApply={onApply}
/>,
);
fireEvent.change(screen.getByLabelText('位置 Xm'), { target: { value: '2.5' } });
fireEvent.change(screen.getByLabelText('位置 Ym'), { target: { value: '-1.5' } });
fireEvent.change(screen.getByLabelText('旋转 Z(°)'), { target: { value: '45' } });
fireEvent.click(screen.getByRole('button', { name: '应用并重新编译' }));
expect(onApply).toHaveBeenCalledWith({
kind: 'builtin',
config: expect.objectContaining({ positionX: 2.5, positionY: -1.5, yawDeg: 45 }),
});
});
it('显示工程地图、实例变换、出生点和根 Body', () => {
const onApply = vi.fn();
const onDraft = vi.fn();
render(
<PhysicalMapPanel
{...common}
value={{ kind: 'project', descriptorPath: 'maps/warehouse/map.json' }}
maps={[
{
descriptorPath: 'maps/warehouse/map.json',
schemaVersion: 1 as const,
id: 'warehouse',
name: '仓库',
spawnPoints: [{ id: 'door', name: '入口' }],
},
]}
rootBodies={['robot', 'payload']}
onDraft={onDraft}
onApply={onApply}
/>,
);
fireEvent.change(screen.getByLabelText('位置 Xm'), { target: { value: '2.5' } });
fireEvent.change(screen.getByLabelText('位置 Ym'), { target: { value: '-1.5' } });
fireEvent.change(screen.getByLabelText('旋转 Z(°)'), { target: { value: '45' } });
fireEvent.change(screen.getByLabelText('地图出生点'), { target: { value: 'door' } });
expect(screen.getByLabelText('地图出生点')).toHaveValue('door');
fireEvent.change(screen.getByLabelText('机器人根 Body'), { target: { value: 'robot' } });
fireEvent.click(screen.getByRole('button', { name: '应用并重新编译' }));
expect(onApply).toHaveBeenCalledWith(
expect.objectContaining({
kind: 'project',
descriptorPath: 'maps/warehouse/map.json',
positionX: 2.5,
positionY: -1.5,
yawDeg: 45,
spawnPointId: 'door',
robotRootBody: 'robot',
}),
);
expect(onDraft).toHaveBeenLastCalledWith(
expect.objectContaining({ kind: 'project', robotRootBody: 'robot' }),
);
});
it('原生 URDF 模式只允许移除地图', () => {
render(
<PhysicalMapPanel
{...common}
value={{ kind: 'builtin', config: { ...DEFAULT_PHYSICAL_MAP_CONFIG, preset: 'flat' } }}
nativeUrdf
onApply={() => {}}
/>,
);
expect(screen.getByRole('status')).toHaveTextContent('原生 URDF');
expect(screen.getByRole('button', { name: '应用并重新编译' })).toBeDisabled();
expect(screen.getByLabelText('地图来源')).not.toBeDisabled();
});
});