Files
Mujoco_WASM/web_platform/src/map/MapEditorPanel.test.tsx
T
chenlin fa5485049a
web-platform-release / Build and publish release (push) Has been cancelled
web-platform-ci / TypeScript, lint, unit, build (push) Has been cancelled
web-platform-ci / Playwright E2E (push) Has been cancelled
web-platform-ci / TypeScript, lint, unit, build (pull_request) Has been cancelled
web-platform-ci / Playwright E2E (pull_request) Has been cancelled
feat(web-platform): release V0.8.3 常规界面优化
2026-09-04 15:55:36 +08:00

213 lines
7.0 KiB
TypeScript

import { act, fireEvent, render, screen, waitFor } from '@testing-library/react';
import type { EditableMapDocument, MapEditorInteractionCallbacks } from './editor/types';
import { MapEditorPanel } from './MapEditorPanel';
const interactionProps = {
descriptorPath: 'maps/map/map.json',
onDraftChange: () => {},
onConvert: async () => true,
canConvert: true,
onBindInteraction: () => {},
onSelectPreview: () => {},
onSessionStateChange: () => {},
};
const document: EditableMapDocument = {
schemaVersion: 1,
mapId: 'map',
revision: 0,
objects: [],
spawnPoints: [],
};
describe('MapEditorPanel', () => {
it('新增并编辑对象后应用递增 revision', async () => {
const apply = vi.fn(async (documentValue: EditableMapDocument) => Boolean(documentValue)),
preview = vi.fn();
render(
<MapEditorPanel
{...interactionProps}
document={document}
loading={false}
onPreview={preview}
onApply={apply}
onExport={() => {}}
/>,
);
fireEvent.click(screen.getByRole('button', { name: '新增' }));
fireEvent.change(screen.getByLabelText('对象位置X'), { target: { value: '2' } });
fireEvent.change(screen.getByLabelText('对象滑动摩擦'), { target: { value: '0.8' } });
fireEvent.change(screen.getByLabelText('对象颜色'), { target: { value: '#ff0000' } });
fireEvent.click(screen.getByRole('button', { name: '应用并重新编译' }));
await waitFor(() => expect(apply).toHaveBeenCalled());
expect(preview).not.toHaveBeenLastCalledWith(null);
expect(apply.mock.calls[0][0]).toEqual(
expect.objectContaining({
revision: 1,
objects: [
expect.objectContaining({
pose: expect.objectContaining({ position: [2, 0, 0.5] }),
friction: [0.8, 0.005, 0.0001],
rgba: [1, 0, 0, 1],
}),
],
}),
);
});
it('把认证资产编辑实时上报为场景级草稿', () => {
const onDraftChange = vi.fn();
render(
<MapEditorPanel
{...interactionProps}
document={document}
loading={false}
onPreview={() => {}}
onDraftChange={onDraftChange}
onApply={async () => true}
onExport={() => {}}
/>,
);
fireEvent.click(screen.getByRole('button', { name: '新增' }));
expect(onDraftChange).toHaveBeenLastCalledWith(
'maps/map/map.json',
expect.objectContaining({ objects: [expect.objectContaining({ type: 'box' })] }),
true,
);
});
it('把外部拾取同步到属性表单和视口操纵器', async () => {
const onSelectPreview = vi.fn();
let interaction: MapEditorInteractionCallbacks | null = null;
render(
<MapEditorPanel
{...interactionProps}
document={{
...document,
objects: [
{
id: 'box_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.55, 0.6, 0.68, 1],
placementMode: 'auto_ground',
enabled: true,
},
],
}}
loading={false}
onPreview={() => {}}
onSelectPreview={onSelectPreview}
onBindInteraction={(callbacks) => {
interaction = callbacks;
}}
onApply={async () => true}
onExport={() => {}}
/>,
);
act(() => interaction?.onSelect('box_1'));
await waitFor(() => expect(onSelectPreview).toHaveBeenLastCalledWith('box_1'));
expect(screen.getByLabelText('对象名称')).toHaveValue('认证方盒');
});
it('视口贴地命令通过会话端口锁定位姿编辑', async () => {
let interaction: MapEditorInteractionCallbacks | null = null;
let selectedId: string | null = null;
render(
<MapEditorPanel
{...interactionProps}
document={document}
loading={false}
onPreview={() => {}}
onSelectPreview={(id) => {
selectedId = id;
}}
onBindInteraction={(callbacks) => {
interaction = callbacks;
}}
onApply={async () => true}
onExport={() => {}}
/>,
);
fireEvent.click(screen.getByRole('button', { name: '新增' }));
expect(selectedId).toBeTruthy();
act(() =>
(interaction as unknown as MapEditorInteractionCallbacks).onSetPlacementMode(
selectedId!,
'locked',
),
);
await waitFor(() => expect(screen.getByText('锁定位姿')).toBeVisible());
expect(screen.getByLabelText('对象位置X')).toBeDisabled();
expect(screen.getByLabelText('对象绕Z旋转')).toBeDisabled();
});
it('把表面材质预设与三轴摩擦写入同一对象草稿', () => {
const onDraftChange = vi.fn();
render(
<MapEditorPanel
{...interactionProps}
document={document}
loading={false}
onPreview={() => {}}
onDraftChange={onDraftChange}
onApply={async () => true}
onExport={() => {}}
/>,
);
fireEvent.click(screen.getByRole('button', { name: '新增' }));
fireEvent.change(screen.getByLabelText('对象表面材质'), { target: { value: 'rubber' } });
expect(screen.getByLabelText('对象滑动摩擦')).toHaveValue(1.5);
expect(onDraftChange).toHaveBeenLastCalledWith(
'maps/map/map.json',
expect.objectContaining({
objects: [expect.objectContaining({ friction: [1.5, 0.008, 0.0002] })],
}),
true,
);
});
it('编辑出生点并包含在应用文档中', async () => {
const apply = vi.fn(async (value: EditableMapDocument) => Boolean(value));
render(
<MapEditorPanel
{...interactionProps}
document={document}
loading={false}
onPreview={() => {}}
onApply={apply}
onExport={() => {}}
/>,
);
fireEvent.click(screen.getByRole('button', { name: '新增出生点' }));
const name = screen.getByLabelText(/出生点名称/);
fireEvent.change(name, { target: { value: '装卸区' } });
const yaw = screen.getByLabelText(/出生点.*朝向/);
fireEvent.change(yaw, { target: { value: '90' } });
fireEvent.click(screen.getByRole('button', { name: '应用并重新编译' }));
await waitFor(() => expect(apply).toHaveBeenCalled());
expect(apply.mock.calls[0][0].spawnPoints[0]).toMatchObject({ name: '装卸区', yawDeg: 90 });
});
it('没有 authoring 文档时可请求创建可编辑副本', async () => {
const convert = vi.fn(async () => true);
render(
<MapEditorPanel
{...interactionProps}
onConvert={convert}
document={null}
loading={false}
onPreview={() => {}}
onApply={async () => true}
onExport={() => {}}
/>,
);
expect(screen.getByText(/保持只读/)).toBeInTheDocument();
fireEvent.click(screen.getByRole('button', { name: '创建可编辑副本' }));
await waitFor(() => expect(convert).toHaveBeenCalledOnce());
});
});