| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091 |
- import assert from 'node:assert/strict';
- import { mkdir, readFile, writeFile } from 'node:fs/promises';
- import { chromium } from 'playwright';
- // 准备资料必须来自真实 request_clarification 工具运行;此脚本不替换 HTTP 响应。
- const fixture = JSON.parse(await readFile(new URL('../../.work/frontend-waiting.json', import.meta.url), 'utf8'));
- const started = Date.now();
- const base = process.env.FRONTEND_URL || 'http://127.0.0.1:18762';
- const browser = await chromium.launch({ headless: true, executablePath: process.env.PLAYWRIGHT_CHROMIUM_EXECUTABLE });
- const page = await browser.newPage({ viewport: { width: 390, height: 844 } });
- page.setDefaultTimeout(20000);
- const errors = [], checks = [], resumed = [];
- let token, report, browserRequests = 0, serviceRequests = 0;
- page.on('pageerror', error => errors.push(error.message));
- page.on('request', request => { if (/\/(openapi|admin)\//.test(request.url())) browserRequests++; if (request.method() === 'POST' && request.url().endsWith('/inputs')) resumed.push(request); });
- const content = page.locator('.mobile .content');
- const timeout = setTimeout(() => { void browser.close(); }, 90000);
- async function navigate(target) {
- await page.evaluate(value => { location.hash = `/customer/${value}`; }, target);
- await page.locator(`.mobile[data-page="${target}"]`).waitFor();
- await page.waitForFunction(() => !document.querySelector('.mobile .content[aria-busy="true"]'));
- }
- try {
- await page.goto(`${base}/#/customer/login`);
- await content.getByLabel('账户', { exact: true }).fill(fixture.username);
- await content.getByLabel('密码', { exact: true }).fill(fixture.password);
- await content.getByRole('checkbox').check();
- const login = page.waitForResponse(response => response.request().method() === 'POST' && response.url().endsWith('/auth/sessions'));
- await content.getByRole('button', { name: '登录并继续', exact: true }).click();
- const loginResponse = await login;
- assert.equal(loginResponse.status(), 200);
- token = (await loginResponse.json()).access_token;
- await content.locator('.message').filter({ hasText: fixture.question }).waitFor();
- await content.getByText('请补充助手询问的信息,继续本次咨询。', { exact: false }).waitFor();
- await page.waitForFunction(() => [...document.querySelectorAll('.mobile button')].some(button => button.textContent === '发送' && !button.disabled));
- assert.equal(await content.getByRole('button', { name: '发送', exact: true }).isEnabled(), true);
- checks.push('重新登录读取真实等待运行,显示助手问题并开放原输入控件');
- await navigate('profile');
- await navigate('chat');
- await content.getByText('请补充助手询问的信息,继续本次咨询。', { exact: false }).waitFor();
- const answer = `键盘空格键无法使用,申请数量为 1。浏览器验证 ${Date.now()}`;
- await content.getByLabel('补充问题', { exact: true }).fill(answer);
- const receiving = page.waitForResponse(response => response.request().method() === 'POST' && response.url().endsWith(`/agent/runs/${fixture.run_id}/inputs`));
- await content.getByRole('button', { name: '发送', exact: true }).click();
- const response = await receiving;
- assert.equal(response.status(), 202, await response.text());
- const result = await response.json();
- assert.equal(result.run.run_id, fixture.run_id);
- assert.equal(result.run.status, 'QUEUED');
- assert.equal(result.run.model_calls, 0);
- assert.equal(resumed.length, 1);
- assert.equal(resumed[0].postDataJSON().answer, answer);
- assert.ok(resumed[0].postDataJSON().interrupt_id);
- assert.ok(resumed[0].postDataJSON().expected_run_version >= 1);
- assert.ok(resumed[0].headers()['idempotency-key']);
- await content.locator('.message.customer').filter({ hasText: answer }).waitFor();
- assert.equal(await content.getByRole('button', { name: '发送', exact: true }).isDisabled(), true);
- checks.push('补充资料提交原 run_id、等待标识和版本,真实接口返回 202,SSE 显示本人消息');
- const cancelling = page.waitForResponse(response => response.request().method() === 'POST' && response.url().endsWith(`/agent/runs/${fixture.run_id}/cancel`));
- await content.getByRole('button', { name: '停止本次回复', exact: true }).click();
- assert.equal((await cancelling).status(), 200);
- await content.getByRole('button', { name: '停止本次回复', exact: true }).waitFor({ state: 'hidden' });
- serviceRequests++;
- const current = await page.request.get(`${base}/openapi/v1/agent/runs/${fixture.run_id}`, { headers: { Authorization: `Bearer ${token}` } });
- assert.equal(current.status(), 200);
- const run = await current.json();
- assert.equal(run.status, 'CANCELLED');
- assert.equal(run.model_calls, 0);
- checks.push('恢复后停止原运行,取消事件实时释放输入,生成模型调用为零');
- assert.deepEqual(errors, []);
- report = { date: new Date().toISOString(), checks, browserErrors: errors, inputRequests: resumed.length, generatedModelCalls: run.model_calls };
- console.log(checks.join('\n'));
- } finally {
- clearTimeout(timeout);
- if (token) {
- serviceRequests++;
- const current = await page.request.get(`${base}/openapi/v1/agent/runs/${fixture.run_id}`, { headers: { Authorization: `Bearer ${token}` } });
- if (current.ok() && ['WAITING_USER', 'QUEUED', 'RUNNING'].includes((await current.json()).status)) { serviceRequests++; await page.request.post(`${base}/openapi/v1/agent/runs/${fixture.run_id}/cancel`, { headers: { Authorization: `Bearer ${token}` } }); }
- serviceRequests++;
- await page.request.delete(`${base}/openapi/v1/auth/sessions/current`, { headers: { Authorization: `Bearer ${token}` } });
- }
- await browser.close();
- if (report) {
- await mkdir(new URL('../../.work/frontend-verification/', import.meta.url), { recursive: true });
- await writeFile(new URL('../../.work/frontend-verification/customer-waiting.json', import.meta.url), JSON.stringify({ ...report, seconds: (Date.now() - started) / 1000, browserRequests, serviceRequests, requestCount: browserRequests + serviceRequests, durationLimitSeconds: 90 }, null, 2));
- }
- }
|