Files
Mujoco_WASM/web_platform/e2e/website.production.spec.ts
T
chenlin 0d986f60bd
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
feat: release v1.0.2 LeKiwi 语言控制与网站嵌入
集成服务器托管模型、自然语言移动与有界抓放、内置 LeKiwi URL 导入和双摄像头;同步部署契约与指定域名 iframe 白名单,保留原有物理安全、会话及调用预算防护。

更新 npm 包及锁文件版本、CHANGELOG 与发布文档。提交前 typecheck、120 项定向前端测试和 44 项后端测试通过(3 项可选跳过);真实 v2 云模型抓放仍待单独验收,不包含运行密钥或构建产物。
2026-09-24 15:29:49 +08:00

121 lines
5.5 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';
test.skip(process.env.CADWORLD_PRODUCTION_TEST !== '1', '真实站点非付费检查须显式启用');
test('生产 HTTPS:Pyodide 与精简语言界面,不触发推理', async ({ page }) => {
let inference = 0;
const errors: string[] = [];
page.on('pageerror', (e) => errors.push(e.message));
page.on('request', (r) => {
if (/\/api\/decision\/v1\/(command|plan|decide|test)$/.test(r.url())) inference++;
});
expect((await page.goto('/'))?.status()).toBe(200);
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: '机器人语言控制', exact: true }).click();
await expect(page.locator('input[type=password]')).toHaveCount(0);
await expect(page.getByRole('textbox', { name: '机器人指令' })).toBeEditable();
await page.getByRole('button', { name: '执行指令', exact: true }).click();
await expect(page.getByText('请先导入并加载 LeKiwi v1 机器人', { exact: true })).toBeVisible();
expect(inference).toBe(0);
expect(errors).toEqual([]);
});
test('生产 v2:空工作台载入预设、文字目标及双相机,无付费推理', 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.getByRole('tab', { name: '控制台', exact: true }).click();
await page.getByRole('button', { name: '机器人语言控制', exact: true }).click();
const panel = page.getByLabel('机器人语言控制', { exact: true });
await expect(panel.getByRole('combobox', { name: '预设场景' })).toBeVisible();
await expect(panel.locator('input[type=password], details')).toHaveCount(0);
await panel.getByRole('button', { name: '载入预设场景' }).click();
await expect(panel.getByRole('status')).toContainText('预设已载入并暂停', { timeout: 120000 });
await expect(page.getByRole('button', { name: '▶ 播放' })).toBeEnabled();
await expect(page.getByRole('combobox', { name: '摄像头视角' }).locator('option')).toHaveCount(2);
await panel.getByRole('textbox', { name: '机器人指令' }).fill('把方块搬到 (0.25,0.50) 米');
await expect(panel.getByRole('button', { name: '执行指令', exact: true })).toBeEnabled();
await panel.getByRole('textbox', { name: '机器人指令' }).fill('停止');
await panel.getByRole('button', { name: '执行指令', exact: true }).click();
await expect(panel.getByRole('status')).toContainText('用户停止');
expect(inference).toBe(0);
expect(errors).toEqual([]);
await page.screenshot({ path: info.outputPath('language-v2-production.png') });
});
test('生产默认配置受保护、订阅关闭、静态目录无密钥;退出销毁会话,无推理', async ({ page }) => {
await page.goto('/');
const result = 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 secured = {
...headers,
'X-CSRF-Token': session.csrfToken,
'X-Config-Version': String(session.configVersion),
};
const config = await fetch(prefix + '/configuration', {
method: 'PUT',
headers: secured,
body: '{}',
});
const subscription = await fetch(prefix + '/codex/login', {
method: 'POST',
headers: secured,
body: '{}',
});
const env = await fetch('/.env');
const secret = await fetch('/run/secrets/model-keys.env');
const destroy = await fetch(prefix + '/session', {
method: 'DELETE',
headers: secured,
body: '{}',
});
const expired = await fetch(prefix + '/status');
return {
ready: session.ready,
llm: session.configuration.llm.model,
keyTypes: [typeof session.configuration.llm.hasKey, typeof session.configuration.jev.hasKey],
locked: config.status,
subscription: subscription.status,
env: env.status,
secret: secret.status,
destroyed: destroy.status,
expired: expired.status,
};
});
expect(result).toEqual({
ready: true,
llm: 'deepseek-flash',
keyTypes: ['boolean', 'boolean'],
locked: 403,
subscription: 403,
env: 404,
secret: 404,
destroyed: 200,
expired: 401,
});
});