Files
aigc-home/e2e/pagination.mjs
T
总工andmultica-agent 949cd2ecad HEL-654: D 批——记录流状态语义、金框结果切换、撤销删除与任务页重皮
- 点选金框滑动 + 检视区 150ms 交叉淡变;↑↓/J/K 即时切换、空格播放、
  Delete 删除、] 检视区、浮层让位;新记录入场/完成金光/失败抖动,减动效只余短淡变
- 单条删除 5 秒撤销(Toast 金色动作钮),窗口过后才提交 DELETE,离页 flush;
  任务页同队列同步;工作流删除保留旧记录并标识「工作流已删除」
- 红/琥珀界线:失败红 + 中文结论展开;done 但文件取不到 → 琥珀「文件缺失」
  + 用相同参数重做/移除记录
- 参数页提交自动切结果页;工作流选择器 ↑↓ 真高亮 + Enter 使用(ShellPop arrowRoving 开关)
- 记录流 80 条分页(220 条专项:50 次轮询 490ms);任务页 §3.5 芯片+搜索+
  行内进度条+ghost 重试;i18n 七语种 +17 键

Co-authored-by: multica-agent <github@multica.ai>
2026-09-30 13:02:51 +08:00

75 lines
2.7 KiB
JavaScript
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.
/* Pagination + perf check (§五): intercept the jobs API and serve 220
* synthetic takes so the >80 path engages without touching the backend. */
import { chromium } from 'playwright';
let failures = 0;
const ok = (name, cond, extra = '') => {
console.log(`${cond ? 'PASS' : 'FAIL'} ${name}${extra ? ' — ' + extra : ''}`);
if (!cond) failures++;
};
const run = async () => {
const browser = await chromium.launch();
const page = await (
await browser.newContext({ viewport: { width: 1440, height: 900 } })
).newPage();
const base = 'http://127.0.0.1:5173';
let realJobs = [];
await page.route('**/api/playground/jobs**', async (route) => {
const r = await route.fetch();
realJobs = await r.json();
// Fan the real list out to 220 takes with distinct ids/timestamps.
const fan = [];
for (let i = 0; i < 220; i++) {
const src = realJobs[i % realJobs.length] ?? realJobs[0];
fan.push({
...src,
id: 9000 + i,
status: 'done',
output_paths: src.output_paths,
created_at: new Date(Date.now() - i * 47 * 60 * 1000).toISOString(),
});
}
await route.fulfill({ json: fan });
});
const t0 = Date.now();
await page.goto(`${base}/image`);
await page.waitForSelector('#desk-prompt', { timeout: 15000 });
await page.waitForSelector('.take');
await page.waitForTimeout(800);
const rendered = await page.locator('.take').count();
ok('220 条记录时首屏只渲染最近 80 条', rendered === 80, `rendered ${rendered}`);
const btn = page.locator('.earlier');
const label = await btn.textContent();
ok('出现「显示更早」按钮并带余量', /140/.test(label || ''), (label || '').trim());
await btn.click();
await page.waitForTimeout(600);
ok('点击后追加 80 条(共 160)', (await page.locator('.take').count()) === 160);
await page.locator('.earlier').click();
await page.waitForTimeout(600);
ok('再点展开全部 220 条', (await page.locator('.take').count()) === 220);
const loadMs = Date.now() - t0;
ok('整段交互在 3 秒内完成', loadMs < 3000, `${loadMs}ms`);
// scripted-painting budget while 220 cards live (SSE 只更新相关行:
// invalidate jobs → 220 卡重排仍是 keyed 更新)
const metrics = await page.evaluate(async () => {
const t = performance.now();
for (let i = 0; i < 50; i++) await fetch('/api/playground/jobs');
return { poll50: Math.round(performance.now() - t) };
});
ok('50 次 jobs 轮询(220 卡在屏)总耗时 < 8 秒', metrics.poll50 < 8000, `${metrics.poll50}ms`);
await page.screenshot({ path: 'shots/13-pagination-220.png' });
await browser.close();
console.log(failures === 0 ? '\nPAGINATION CHECKS PASSED' : `\n${failures} FAILED`);
process.exit(failures ? 1 : 0);
};
run().catch((e) => {
console.error('SCRIPT ERROR:', e);
process.exit(2);
});