Files
Mujoco_WASM/web_platform/e2e/website.production.spec.ts
T
chenlin 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
feat: release v1.0.1 CADWorld 网站与 LeKiwi 智能抓放
集成同源 BYOK 会话隔离、精简模型设置、官方订阅入口和 HTTPS 发布运维;保留本地训练/调参与控制能力。同步 npm 版本及 CHANGELOG,记录公网真实 API 验收仍待用户凭据。
2026-09-24 09:57:41 +08:00

142 lines
6.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { expect, test } from '@playwright/test';
import { resolve } from 'node:path';
import { existsSync } from 'node:fs';
import { readFile, writeFile } from 'node:fs/promises';
test.skip(
process.env.CADWORLD_PRODUCTION_TEST !== '1',
'真实站点检查须显式 opt-in;不使用任何现存密钥',
);
test('生产 HTTPS:模型导入、Pyodide、精简设置、安全会话与订阅不可用边界', async ({
page,
}, info) => {
const errors: string[] = [];
const external: string[] = [];
page.on('pageerror', (e) => errors.push(e.message));
page.on('request', (r) => {
if (
r.url().startsWith('http') &&
new URL(r.url()).origin !== 'https://cadworld-sim.robotquan.com'
)
external.push(r.url());
});
const response = await page.goto('/');
expect(response?.status()).toBe(200);
await expect(page).toHaveTitle(/CADWorld Sim/);
await page
.locator('input[type=file]')
.first()
.setInputFiles(resolve('web_platform/fixtures/python_controller/model.xml'));
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 page.getByRole('button', { name: /Python 脚本控制/ }).click();
await page
.locator('input[accept=".py,text/x-python"]')
.setInputFiles(resolve('web_platform/fixtures/python_controller/balance.py'));
await expect(page.getByText('倒立摆 PD 平衡控制', { exact: true })).toBeVisible({
timeout: 90000,
});
await page.getByRole('button', { name: '启用', exact: true }).click();
await expect(page.getByRole('button', { name: /Python 脚本控制/ })).toContainText('运行');
await page.getByRole('button', { name: 'LeKiwi 智能抓放', exact: true }).click();
await page.getByText('模型设置 · API_KEY / ChatGPT 订阅', { exact: true }).click();
await expect(page.getByRole('button', { name: '保存设置' })).toBeEnabled({ timeout: 20000 });
await expect(page.getByLabel('本机服务令牌')).toHaveCount(0);
const cookie = (await page.context().cookies()).find((c) => c.name === '__Host-cadworld-session');
expect(cookie?.secure).toBe(true);
expect(cookie?.httpOnly).toBe(true);
expect(cookie?.sameSite).toBe('Strict');
await page.screenshot({ path: info.outputPath('website-api-settings.png') });
await page.getByRole('button', { name: 'ChatGPT 订阅', exact: true }).click();
// No login or model invocation. Availability must be explicit; user's login stays interactive.
await expect(page.getByText(/当前服务器可能无法连接官方服务/)).toBeVisible();
await expect(page.getByRole('button', { name: '登录 ChatGPT 订阅', exact: true })).toBeVisible();
await page.screenshot({ path: info.outputPath('website-subscription-entry.png') });
await page.getByText('连接测试与凭据管理', { exact: true }).click();
await page.getByRole('button', { name: '清除本次凭据', exact: true }).click();
await expect(page.getByText(/本次凭据已清除/)).toBeVisible();
expect(errors).toEqual([]);
expect(external).toEqual([]);
});
test('官方订阅入口网络探测:不登录账号、不推理,完成后销毁会话', async ({ page }, info) => {
await page.goto('/');
const evidence = await page.evaluate(async () => {
const prefix = '/api/decision/v1';
const headers = { 'Content-Type': 'application/json' };
const bootstrap = await fetch(prefix + '/session', { method: 'POST', headers, body: '{}' });
const session = await bootstrap.json();
const protectedHeaders = {
...headers,
'X-CSRF-Token': session.csrfToken,
'X-Config-Version': String(session.configVersion),
};
const response = await fetch(prefix + '/codex/login', {
method: 'POST',
headers: protectedHeaders,
body: '{}',
});
const result = await response.json();
const cleared = await fetch(prefix + '/session', {
method: 'DELETE',
headers: protectedHeaders,
body: '{}',
});
return {
status: response.status,
error: typeof result.error === 'string' ? result.error : undefined,
deviceFlowOffered: Boolean(result.verificationUrl && result.userCode),
cleared: cleared.status,
accountLoginPerformed: false,
inferenceCalls: 0,
};
});
expect(evidence.cleared).toBe(200);
expect([200, 409, 429, 502, 504]).toContain(evidence.status);
if (evidence.status !== 200) expect(evidence.error).toMatch(/^[a-z0-9_]+$/);
await writeFile(
info.outputPath('subscription-network-status.json'),
JSON.stringify(evidence, null, 2),
);
});
test('公网真实 WASM A 抓放(明确 mock,不调用付费 API)', async ({ page }, info) => {
test.skip(!existsSync('build/lekiwi/lekiwi-v1.zip'), '无本地 A 资产');
let modelCalls = 0;
page.on('request', (r) => {
if (/\/api\/decision\/v1\/(plan|decide|test)$/.test(r.url())) modelCalls++;
});
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: 90000 });
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 panel.getByRole('button', { name: '开始任务(mock)', exact: true }).click();
await expect(panel.getByTestId('agent-task-state')).toContainText('物理验收成功', {
timeout: 180000,
});
await panel.getByText('高级操作与证据导出', { exact: true }).click();
const download = page.waitForEvent('download');
await panel.getByRole('button', { name: '导出任务证据', exact: true }).click();
const data = JSON.parse(await readFile((await (await download).path())!, 'utf8'));
expect(data.succeeded).toBe(true);
expect(data.transported).toBeGreaterThanOrEqual(0.5);
expect(data.mode).toBe('mock');
expect(modelCalls).toBe(0);
await writeFile(
info.outputPath('production-mock-physical-evidence.json'),
JSON.stringify(data, null, 2),
);
await page.screenshot({ path: info.outputPath('production-physical-success.png') });
});