fix(website): 移除文件导入中的移动操作训练场景
web-platform-ci / Standalone decision service (no cloud credentials) (push) Has been cancelled
web-platform-ci / TypeScript, lint, unit, build (push) Has been cancelled
web-platform-ci / Playwright E2E (push) Has been cancelled
lekiwi-compatibility / cpu-compatibility (push) Has been cancelled
web-platform-ci / Standalone decision service (no cloud credentials) (push) Has been cancelled
web-platform-ci / TypeScript, lint, unit, build (push) Has been cancelled
web-platform-ci / Playwright E2E (push) Has been cancelled
lekiwi-compatibility / cpu-compatibility (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
import { expect, test } from '@playwright/test';
|
||||
import { resolve } from 'node:path';
|
||||
|
||||
for (const kind of ['URDF', 'ZIP'] as const) {
|
||||
test(`网站 ${kind} 导入不显示移动操作训练场景,普通导入仍可用`, async ({ page }, info) => {
|
||||
let inference = 0;
|
||||
const errors: string[] = [];
|
||||
page.on('pageerror', (error) => errors.push(error.message));
|
||||
await page.route('**/api/decision/v1/**', async (route) => {
|
||||
if (/\/(command|plan|decide|test)$/.test(new URL(route.request().url()).pathname)) {
|
||||
inference++;
|
||||
await route.abort();
|
||||
} else await route.continue();
|
||||
});
|
||||
expect((await page.goto('/'))?.status()).toBe(200);
|
||||
await page
|
||||
.locator('input[type=file]')
|
||||
.first()
|
||||
.setInputFiles(
|
||||
kind === 'ZIP'
|
||||
? resolve('web_platform/assets/robots/lekiwi-v1.zip')
|
||||
: {
|
||||
name: 'robot.urdf',
|
||||
mimeType: 'application/xml',
|
||||
buffer: Buffer.from(`<robot name="import-test"><link name="base">
|
||||
<inertial><mass value="1"/><inertia ixx=".1" iyy=".1" izz=".1" ixy="0" ixz="0" iyz="0"/></inertial>
|
||||
<visual><geometry><box size=".4 .4 .2"/></geometry></visual>
|
||||
</link></robot>`),
|
||||
},
|
||||
);
|
||||
const dialog = page.getByRole('dialog', { name: '配置 URDF 仿真组件' });
|
||||
await expect(dialog).toBeVisible({ timeout: 30000 });
|
||||
const profile = dialog.getByLabel('URDF 机器人 profile');
|
||||
await expect(profile).toHaveValue('');
|
||||
await expect(profile.locator('option')).toHaveText([
|
||||
'不启用(通用转换)',
|
||||
'LeKiwi v1(仿真专用)',
|
||||
]);
|
||||
await expect(dialog.getByText(/移动操作训练场景|强化学习任务即可一键训练/)).toHaveCount(0);
|
||||
await expect(dialog.getByRole('checkbox', { name: /为关节添加驱动器/ })).toBeEnabled();
|
||||
await page.screenshot({ path: info.outputPath('import-options.png') });
|
||||
if (kind === 'ZIP') await profile.selectOption('lekiwi-v1');
|
||||
await dialog.getByRole('button', { name: '转换并加载' }).click();
|
||||
await expect(dialog).not.toBeVisible();
|
||||
await expect(page.getByRole('button', { name: '▶ 播放' })).toBeEnabled({ timeout: 90000 });
|
||||
await page.getByRole('tab', { name: '控制台', exact: true }).click();
|
||||
await expect(page.getByRole('button', { name: '强化学习任务', exact: true })).toHaveCount(0);
|
||||
await expect(page.getByRole('button', { name: '机器人语言控制', exact: true })).toBeVisible();
|
||||
expect(inference).toBe(0);
|
||||
expect(errors).toEqual([]);
|
||||
});
|
||||
}
|
||||
@@ -1,7 +1,11 @@
|
||||
import { defineConfig } from '@playwright/test';
|
||||
export default defineConfig({
|
||||
testDir: './e2e',
|
||||
testMatch: ['website.production.spec.ts', 'website.embed.production.spec.ts'],
|
||||
testMatch: [
|
||||
'website.production.spec.ts',
|
||||
'website.embed.production.spec.ts',
|
||||
...(process.env.CADWORLD_PRODUCTION_TEST === '1' ? ['website.import.spec.ts'] : []),
|
||||
],
|
||||
workers: 1,
|
||||
timeout: 240_000,
|
||||
use: {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { defineConfig } from '@playwright/test';
|
||||
export default defineConfig({
|
||||
testDir: './e2e',
|
||||
testMatch: 'website.spec.ts',
|
||||
testMatch: ['website.spec.ts', 'website.import.spec.ts'],
|
||||
workers: 1,
|
||||
timeout: 240_000,
|
||||
use: {
|
||||
|
||||
@@ -1,7 +1,69 @@
|
||||
import { fireEvent, render, screen } from '@testing-library/react';
|
||||
import { UrdfImportOptionsDialog } from './UrdfImportOptionsDialog';
|
||||
|
||||
const capabilities = vi.hoisted(() => ({ training: true }));
|
||||
vi.mock('../websiteConfig', () => ({ websiteCapabilities: capabilities }));
|
||||
|
||||
const mobileProfiles = ['MobileManipulator-LeKiwi-v1', 'MobileManipulator-LeKiwi-Bundle'];
|
||||
|
||||
describe('UrdfImportOptionsDialog', () => {
|
||||
beforeEach(() => {
|
||||
capabilities.training = true;
|
||||
});
|
||||
|
||||
it.each(mobileProfiles)('本地版保留训练场景 %s', (profileId) => {
|
||||
const onConfirm = vi.fn();
|
||||
render(<UrdfImportOptionsDialog open onConfirm={onConfirm} onSkip={() => {}} />);
|
||||
expect(screen.getAllByRole('option', { name: /移动操作训练场景/ })).toHaveLength(2);
|
||||
fireEvent.change(screen.getByLabelText('URDF 机器人 profile'), {
|
||||
target: { value: profileId },
|
||||
});
|
||||
expect(screen.getByText(/强化学习任务即可一键训练/)).toBeVisible();
|
||||
fireEvent.click(screen.getByRole('button', { name: '转换并加载' }));
|
||||
expect(onConfirm).toHaveBeenCalledWith(
|
||||
{ addActuators: false, addSensors: false, sensorType: 'camera' },
|
||||
profileId,
|
||||
);
|
||||
});
|
||||
|
||||
it('网站版移除训练选项,仍可选择普通 LeKiwi profile', () => {
|
||||
capabilities.training = false;
|
||||
const onConfirm = vi.fn();
|
||||
render(<UrdfImportOptionsDialog open onConfirm={onConfirm} onSkip={() => {}} />);
|
||||
expect(screen.queryAllByRole('option', { name: /移动操作训练场景/ })).toHaveLength(0);
|
||||
expect(screen.queryByText(/强化学习任务即可一键训练/)).not.toBeInTheDocument();
|
||||
expect(screen.getByRole('option', { name: '不启用(通用转换)' })).toBeInTheDocument();
|
||||
fireEvent.change(screen.getByLabelText('URDF 机器人 profile'), {
|
||||
target: { value: 'lekiwi-v1' },
|
||||
});
|
||||
fireEvent.click(screen.getByRole('button', { name: '转换并加载' }));
|
||||
expect(onConfirm).toHaveBeenCalledWith(
|
||||
{ addActuators: false, addSensors: false, sensorType: 'camera' },
|
||||
'lekiwi-v1',
|
||||
);
|
||||
});
|
||||
|
||||
it.each(mobileProfiles)('网站版不沿用已移除的初始训练场景 %s', (profileId) => {
|
||||
capabilities.training = false;
|
||||
const onConfirm = vi.fn();
|
||||
render(
|
||||
<UrdfImportOptionsDialog
|
||||
open
|
||||
initialProfileId={profileId}
|
||||
onConfirm={onConfirm}
|
||||
onSkip={() => {}}
|
||||
/>,
|
||||
);
|
||||
expect(screen.getByLabelText('URDF 机器人 profile')).toHaveValue('');
|
||||
expect(screen.queryByText(/强化学习任务即可一键训练/)).not.toBeInTheDocument();
|
||||
expect(screen.getByRole('checkbox', { name: /为关节添加驱动器/ })).toBeEnabled();
|
||||
expect(screen.getByRole('button', { name: '不添加,直接加载' })).toBeEnabled();
|
||||
fireEvent.click(screen.getByRole('button', { name: '转换并加载' }));
|
||||
expect(onConfirm).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ addActuators: true, addSensors: true }),
|
||||
);
|
||||
});
|
||||
|
||||
it('默认选择关节驱动器和摄像头传感器', () => {
|
||||
const onConfirm = vi.fn();
|
||||
render(
|
||||
|
||||
@@ -3,6 +3,7 @@ import { Camera, Settings2 } from 'lucide-react';
|
||||
import type { CameraDirection, UrdfEnhancementOptions } from '../../project/urdfToMjcf';
|
||||
import { Button, Dialog, Select, Tooltip } from '../../components/ui';
|
||||
import { ROBOT_PROFILES } from '../../robot/registry';
|
||||
import { websiteCapabilities } from '../websiteConfig';
|
||||
|
||||
function OptionCard({
|
||||
checked,
|
||||
@@ -56,7 +57,11 @@ export function UrdfImportOptionsDialog({
|
||||
onConfirm: (options: UrdfEnhancementOptions, profileId?: string) => void;
|
||||
onSkip: () => void;
|
||||
}) {
|
||||
const [profileId, setProfileId] = useState(initialProfileId);
|
||||
const [profileId, setProfileId] = useState(() =>
|
||||
!websiteCapabilities.training && initialProfileId.startsWith('MobileManipulator-')
|
||||
? ''
|
||||
: initialProfileId,
|
||||
);
|
||||
const [options, setOptions] = useState<UrdfEnhancementOptions>(() => ({
|
||||
addActuators: true,
|
||||
addSensors: true,
|
||||
@@ -113,10 +118,14 @@ export function UrdfImportOptionsDialog({
|
||||
onChange={(e) => setProfileId(e.target.value)}
|
||||
>
|
||||
<option value="">不启用(通用转换)</option>
|
||||
<option value="MobileManipulator-LeKiwi-v1">LeKiwi v1 · 移动操作训练场景</option>
|
||||
<option value="MobileManipulator-LeKiwi-Bundle">
|
||||
LeKiwi Bundle · 移动操作训练场景
|
||||
</option>
|
||||
{websiteCapabilities.training && (
|
||||
<>
|
||||
<option value="MobileManipulator-LeKiwi-v1">LeKiwi v1 · 移动操作训练场景</option>
|
||||
<option value="MobileManipulator-LeKiwi-Bundle">
|
||||
LeKiwi Bundle · 移动操作训练场景
|
||||
</option>
|
||||
</>
|
||||
)}
|
||||
{ROBOT_PROFILES.map((p) => (
|
||||
<option key={p.id} value={p.id}>
|
||||
{p.label}
|
||||
@@ -124,7 +133,7 @@ export function UrdfImportOptionsDialog({
|
||||
))}
|
||||
</Select>
|
||||
</label>
|
||||
{profileId.startsWith('MobileManipulator-') && (
|
||||
{websiteCapabilities.training && profileId.startsWith('MobileManipulator-') && (
|
||||
<p className="text-xs text-warning">
|
||||
加载抓取物体与搬运目标;进入控制台 →
|
||||
强化学习任务即可一键训练。移动操作模式不启用外部控制桥。
|
||||
|
||||
Reference in New Issue
Block a user