99 lines
3.1 KiB
TypeScript
99 lines
3.1 KiB
TypeScript
import { fireEvent, render, screen } from '@testing-library/react';
|
||
import { ActuatorControl } from './SidebarPanel';
|
||
import type { ActuatorInfo } from '../../simulation/SimulationSession';
|
||
|
||
const actuator: ActuatorInfo = {
|
||
id: 0,
|
||
name: 'shoulder_motor',
|
||
value: 0.5,
|
||
min: -1,
|
||
max: 1,
|
||
limited: true,
|
||
jointId: 0,
|
||
jointName: 'shoulder',
|
||
jointType: 3,
|
||
unit: 'N·m',
|
||
kind: 'motor',
|
||
controlCount: 1,
|
||
gear: 2,
|
||
gain: 1,
|
||
kp: 0,
|
||
kv: 0,
|
||
ctrlLimited: true,
|
||
ctrlMin: -1,
|
||
ctrlMax: 1,
|
||
forceLimited: true,
|
||
forceMin: -20,
|
||
forceMax: 20,
|
||
};
|
||
|
||
describe('ActuatorControl', () => {
|
||
it('显示对应关节和常用力矩单位', () => {
|
||
render(<ActuatorControl actuator={actuator} onControl={() => {}} onParameters={() => {}} />);
|
||
expect(screen.getByText('shoulder_motor')).toBeVisible();
|
||
expect(screen.getByText('关节:shoulder')).toBeVisible();
|
||
expect(screen.getByText('1.000 N·m')).toBeVisible();
|
||
});
|
||
|
||
it('内部按 gear 换算输出,但参数面板只开放 kp、kv 等业务参数', () => {
|
||
const onControl = vi.fn(),
|
||
onParameters = vi.fn();
|
||
render(
|
||
<ActuatorControl actuator={actuator} onControl={onControl} onParameters={onParameters} />,
|
||
);
|
||
fireEvent.change(screen.getByRole('slider'), { target: { value: '2' } });
|
||
expect(onControl).toHaveBeenCalledWith(1);
|
||
fireEvent.click(screen.getByText('常用参数'));
|
||
expect(screen.queryByLabelText('传动比 gear')).not.toBeInTheDocument();
|
||
expect(screen.queryByLabelText('固定增益 gain')).not.toBeInTheDocument();
|
||
const kp = screen.getByLabelText(/kp(MJCF stiffness/);
|
||
fireEvent.change(kp, { target: { value: '3' } });
|
||
fireEvent.blur(kp);
|
||
expect(onParameters).toHaveBeenCalledWith(
|
||
expect.objectContaining({ kp: 3, ctrlLimited: true, forceLimited: true }),
|
||
);
|
||
});
|
||
|
||
it('position 伺服使用角度目标并开放 kp、kv', () => {
|
||
const onParameters = vi.fn();
|
||
render(
|
||
<ActuatorControl
|
||
actuator={{
|
||
...actuator,
|
||
name: 'shoulder_servo',
|
||
kind: 'position',
|
||
unit: '°',
|
||
value: Math.PI / 2,
|
||
min: -Math.PI,
|
||
max: Math.PI,
|
||
gear: 1,
|
||
kp: 100,
|
||
kv: 10,
|
||
gain: 100,
|
||
}}
|
||
onControl={() => {}}
|
||
onParameters={onParameters}
|
||
/>,
|
||
);
|
||
expect(screen.getByText('90.000 °')).toBeVisible();
|
||
fireEvent.click(screen.getByText('常用参数'));
|
||
const kp = screen.getByLabelText(/位置增益 kp/);
|
||
fireEvent.change(kp, { target: { value: '150' } });
|
||
fireEvent.blur(kp);
|
||
expect(onParameters).toHaveBeenCalledWith(expect.objectContaining({ kp: 150, kv: 10 }));
|
||
});
|
||
|
||
it('非 motor 驱动器保持原始控制单位且不开放通用参数编辑', () => {
|
||
render(
|
||
<ActuatorControl
|
||
actuator={{ ...actuator, name: 'custom', kind: 'other', unit: '', value: 0.25 }}
|
||
onControl={() => {}}
|
||
onParameters={() => {}}
|
||
/>,
|
||
);
|
||
expect(screen.getByText('0.250')).toBeVisible();
|
||
expect(screen.queryByText('常用参数')).not.toBeInTheDocument();
|
||
expect(screen.getByText(/不是可直接编辑的 motor\/position/)).toBeVisible();
|
||
});
|
||
});
|