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( {}} />, ); 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( {}} 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( {}} 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( {}} 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( {}} 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( {}} 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( {}} onApply={async () => true} onExport={() => {}} />, ); expect(screen.getByText(/保持只读/)).toBeInTheDocument(); fireEvent.click(screen.getByRole('button', { name: '创建可编辑副本' })); await waitFor(() => expect(convert).toHaveBeenCalledOnce()); }); });