| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192 |
- #!/usr/bin/env node
- /**
- * 打断场景的 UI 回归验证(2026-11-10)
- *
- * 验证点:
- * ① 图文被容器拆成两轮时,两轮**各自**计时,先结束的那轮定格不倒退
- * ② 同一时间不出现两条「正在思考 / 等待回复」占位
- * ③ 页面无未捕获错误
- *
- * 做法:真实发送「图 + 文字」,每 500ms 采样一次页面上的计时读数与占位文案,
- * 观察读数序列是否**单调不减**(倒退就是本次要修的 bug)。
- */
- import { chromium } from 'playwright';
- import { mkdir } from 'node:fs/promises';
- import { dirname, resolve } from 'node:path';
- import { fileURLToPath } from 'node:url';
- const here = dirname(fileURLToPath(import.meta.url));
- const root = resolve(here, '..');
- const outDir = resolve(root, 'test-results/interrupt-ui');
- const BASE = process.env.BASE_URL || 'http://127.0.0.1:4300';
- const imgPath = process.argv[2];
- await mkdir(outDir, { recursive: true });
- const browser = await chromium.launch({ channel: 'chrome' });
- const page = await browser.newPage({ viewport: { width: 1440, height: 900 } });
- const errors = [];
- page.on('pageerror', (e) => errors.push(String(e)));
- await page.goto(`${BASE}/desktop/chat`, { waitUntil: 'domcontentloaded' });
- await page.waitForTimeout(8000);
- const s = page.locator('text=使用HTML根据我这个').first();
- if (await s.count()) {
- await s.click();
- await page.waitForTimeout(5000);
- }
- // 发送:图 + 文字(复现容器的两轮)
- await page.locator('input[type="file"]').first().setInputFiles(imgPath);
- await page.waitForTimeout(1000);
- const box = page.locator('textarea').first();
- await box.click();
- await box.fill('用终端执行 date 命令,然后用一句话告诉我当前时间。');
- await page.keyboard.press('Enter');
- console.log('已发送图文,开始采样…');
- /** [{t, times:[..], waits:[..]}] */
- const samples = [];
- const t0 = Date.now();
- while (Date.now() - t0 < 120_000) {
- const snap = await page.evaluate(() => ({
- times: [...document.querySelectorAll('.head-time, .seg-time')].map((e) => e.textContent.trim()),
- waits: [...document.querySelectorAll('.seg-wait, .card-wait')].map((e) => e.textContent.trim()),
- }));
- samples.push({ at: ((Date.now() - t0) / 1000).toFixed(1), ...snap });
- await page.waitForTimeout(500);
- if (Date.now() - t0 > 30_000) {
- const noLive = !(await page.getByText('输出中').count());
- if (noLive) break;
- }
- }
- await page.screenshot({ path: `${outDir}/after.png`, fullPage: false });
- // 分析:只看最后一组头部读数(最新回合),检查是否单调不减
- const parse = (s) => parseInt(String(s).replace(/[^\d]/g, ''), 10);
- const series = samples
- .map((x) => x.times.map(parse).filter((n) => Number.isFinite(n)))
- .filter((a) => a.length > 0);
- let regressions = 0;
- for (let i = 1; i < series.length; i++) {
- for (let k = 0; k < Math.min(series[i].length, series[i - 1].length); k++) {
- if (series[i][k] < series[i - 1][k]) {
- regressions++;
- console.log(` ✗ 倒退:${series[i - 1][k]}s → ${series[i][k]}s(${samples[i].at}s 处)`);
- }
- }
- }
- // 占位重复:同一时刻出现多于 1 条「正在思考/等待回复」且文案相同
- const dup = samples.filter((x) => x.waits.length >= 2 && new Set(x.waits).size === 1).length;
- console.log(`\n采样 ${samples.length} 次,读数组 ${series.length} 组`);
- console.log(`读数倒退次数:${regressions}`);
- console.log(`重复占位采样数:${dup}`);
- console.log(`页面错误:${errors.length ? errors.slice(0, 3).join(' | ') : '无'}`);
- const pass = regressions === 0 && errors.length === 0;
- console.log(`\n${pass ? '✓ PASS' : '✗ FAIL'}:计时不退步 / 无页面错误`);
- await browser.close();
- process.exit(pass ? 0 : 1);
|