verify-deliverable-separation.mjs 3.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182
  1. #!/usr/bin/env node
  2. /**
  3. * 成果归属实测(2026-11-10)
  4. *
  5. * 验证点:用户自己上传的文件**只出现在「文件」页签**,不出现在「成果」。
  6. *
  7. * 做法:
  8. * 1. 打开开发态登录的桌面端
  9. * 2. 进入 lj-test 会话(`$approot_mwagplwv…`,里面有用户上传的 image.png)
  10. * 3. 切到右栏「文件」→ 截图 + 计数
  11. * 4. 切到右栏「成果」→ 截图 + 计数
  12. *
  13. * 用法:node scripts/verify-deliverable-separation.mjs
  14. */
  15. import { chromium } from 'playwright';
  16. import { mkdir } from 'node:fs/promises';
  17. import { dirname, resolve } from 'node:path';
  18. import { fileURLToPath } from 'node:url';
  19. const here = dirname(fileURLToPath(import.meta.url));
  20. const root = resolve(here, '..');
  21. const outDir = resolve(root, 'test-results/deliverable-separation');
  22. const BASE = process.env.BASE_URL || 'http://127.0.0.1:4300';
  23. /** 实测有用户上传图片的会话标题 */
  24. const SESSION_TITLE = '使用HTML根据我这个图片做一个页面出来';
  25. await mkdir(outDir, { recursive: true });
  26. const browser = await chromium.launch({
  27. /* 本机已装 Chrome,不必下载 Playwright 自带的 chromium */
  28. channel: 'chrome',
  29. });
  30. const page = await browser.newPage({ viewport: { width: 1440, height: 900 } });
  31. const errors = [];
  32. page.on('pageerror', (e) => errors.push(String(e)));
  33. /* 🔴 不能用 networkidle:Matrix `/sync` 是长轮询,页面永远「不空闲」 */
  34. await page.goto(`${BASE}/desktop/chat`, { waitUntil: 'domcontentloaded' });
  35. await page.waitForTimeout(8000);
  36. // ① 找到目标会话并打开
  37. const item = page.locator('text=' + SESSION_TITLE.slice(0, 12)).first();
  38. if (await item.count()) {
  39. await item.click();
  40. await page.waitForTimeout(4000);
  41. } else {
  42. console.log('⚠ 左栏没找到目标会话,截当前画面');
  43. }
  44. // ② 右栏「文件」页签
  45. const filesTab = page.getByRole('tab', { name: /文件/ }).first();
  46. const resultsTab = page.getByRole('tab', { name: /成果/ }).first();
  47. async function tabReport(tab, name) {
  48. if (!(await tab.count())) return { name, error: '找不到页签' };
  49. await tab.click();
  50. await page.waitForTimeout(1500);
  51. await page.screenshot({ path: `${outDir}/${name}.png` });
  52. const pane = page.locator('aside, [class*="rail"]').last();
  53. const text = (await pane.innerText().catch(() => '')) || '';
  54. const hasImage = /image\.png/.test(text);
  55. const isEmpty = /暂无/.test(text);
  56. return { name, hasImage, isEmpty, textLen: text.length, snippet: text.slice(0, 200) };
  57. }
  58. const files = await tabReport(filesTab, 'rail-files');
  59. const results = await tabReport(resultsTab, 'rail-results');
  60. console.log('\n=== 右栏「文件」===');
  61. console.log(JSON.stringify(files, null, 2));
  62. console.log('\n=== 右栏「成果」===');
  63. console.log(JSON.stringify(results, null, 2));
  64. const pass = files.hasImage && !results.hasImage;
  65. console.log(`\n${pass ? '✓ PASS' : '✗ FAIL'}:用户上传的 image.png ` +
  66. (files.hasImage ? '在「文件」中' : '**不在**「文件」中') + ',' +
  67. (results.hasImage ? '**仍在**「成果」中' : '不在「成果」中'));
  68. if (errors.length) console.log('页面错误:', errors.slice(0, 3));
  69. await browser.close();
  70. process.exit(pass ? 0 : 1);