f3a8a38acd
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) (pull_request) 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
lekiwi-compatibility / cpu-compatibility (pull_request) Has been cancelled
集成同源 BYOK 会话隔离、精简模型设置、官方订阅入口和 HTTPS 发布运维;保留本地训练/调参与控制能力。同步 npm 版本及 CHANGELOG,记录公网真实 API 验收仍待用户凭据。
71 lines
4.0 KiB
TypeScript
71 lines
4.0 KiB
TypeScript
import { expect, test } from '@playwright/test';
|
||
import { resolve } from 'node:path';
|
||
import { readFile, writeFile } from 'node:fs/promises';
|
||
|
||
test('主工作台:A 导入、mock 单技能暂停/继续、物理成功及证据导出', async ({ page }, info) => {
|
||
const errors: string[] = [];
|
||
let modelRequests = 0;
|
||
page.on('pageerror', (e) => errors.push(e.message));
|
||
page.on('request', (r) => {
|
||
if (/\/api\/decision\/v1\/(plan|decide)$/.test(r.url())) modelRequests++;
|
||
});
|
||
await page.goto('/');
|
||
await page
|
||
.locator('input[type="file"]')
|
||
.first()
|
||
.setInputFiles(resolve('build/lekiwi/lekiwi-v1.zip'));
|
||
const dialog = page.getByRole('dialog', { name: '配置 URDF 仿真组件' });
|
||
await expect(dialog).toBeVisible({ timeout: 30000 });
|
||
await dialog.getByLabel('URDF 机器人 profile').selectOption('lekiwi-v1');
|
||
await dialog.getByRole('button', { name: '转换并加载' }).click();
|
||
await expect(page.getByRole('button', { name: '▶ 播放' })).toBeEnabled({ timeout: 60000 });
|
||
await page.getByRole('tab', { name: '控制台', exact: true }).click();
|
||
await page.getByRole('button', { name: 'LeKiwi 智能抓放', exact: true }).click();
|
||
const panel = page.getByLabel('LeKiwi 智能抓放任务', { exact: true });
|
||
await expect(panel).toContainText('MOCK(不调用模型 API)');
|
||
await panel.getByRole('button', { name: '加载 A 任务场景', exact: true }).click();
|
||
await expect(panel).toContainText('A 抓放场景已加载并暂停', { timeout: 60000 });
|
||
await panel.getByRole('button', { name: '单技能步进', exact: true }).click();
|
||
const status = panel.getByTestId('agent-task-state');
|
||
await expect(status).toContainText('任务已暂停', { timeout: 60000 });
|
||
await expect(status).toContainText('当前技能:open');
|
||
await expect(status).toContainText('Jev 1/60');
|
||
await panel.getByRole('button', { name: '继续任务', exact: true }).click();
|
||
await expect(status).toContainText('物理验收成功', { timeout: 180000 });
|
||
await expect(status).toContainText('Jev 11/60');
|
||
const download = page.waitForEvent('download');
|
||
await panel.getByRole('button', { name: '导出任务证据', exact: true }).click();
|
||
const downloaded = await download;
|
||
const evidence = JSON.parse(await readFile((await downloaded.path())!, 'utf8'));
|
||
await writeFile(
|
||
info.outputPath('workbench-physical-evidence.json'),
|
||
JSON.stringify(evidence, null, 2),
|
||
);
|
||
expect(evidence.mode).toBe('mock');
|
||
expect(evidence.succeeded).toBe(true);
|
||
expect(evidence.transported).toBeGreaterThanOrEqual(0.5);
|
||
expect(evidence.sample.onGoalSupport).toBe(true);
|
||
expect(modelRequests).toBe(0);
|
||
expect(errors).toEqual([]);
|
||
await page.screenshot({ path: info.outputPath('agent-workbench.png') });
|
||
await panel.getByRole('button', { name: '重置任务场景', exact: true }).click();
|
||
await expect(panel.getByTestId('agent-task-state')).toHaveCount(0, { timeout: 60000 });
|
||
await panel.getByLabel('目标 Y', { exact: true }).fill('0.617');
|
||
await panel.getByRole('button', { name: '单技能步进', exact: true }).click();
|
||
await expect(status).toContainText('任务已暂停', { timeout: 60000 });
|
||
const changedDownload = page.waitForEvent('download');
|
||
await panel.getByRole('button', { name: '导出任务证据', exact: true }).click();
|
||
const changed = JSON.parse(await readFile((await (await changedDownload).path())!, 'utf8'));
|
||
expect(changed.goal).toEqual([0.257, 0.617, 0.128]);
|
||
expect(changed.runId).not.toBe(evidence.runId);
|
||
expect(changed.sample.time).toBeLessThan(2.01);
|
||
expect(changed.sample.object[1]).toBeLessThan(0.02); // New source episode, not the old placed cube.
|
||
await writeFile(info.outputPath('edited-goal-episode.json'), JSON.stringify(changed, null, 2));
|
||
await panel.getByLabel('目标 Y', { exact: true }).fill('0.615');
|
||
await expect(status).toContainText('已取消');
|
||
await expect(status).toContainText('目标编辑使旧计划失效');
|
||
expect(modelRequests).toBe(0);
|
||
expect(errors).toEqual([]);
|
||
await expect(page.getByRole('button', { name: '强化学习任务', exact: true })).toBeVisible();
|
||
});
|