verify-image-text-send.mjs 2.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374
  1. #!/usr/bin/env node
  2. /**
  3. * 图文同发实测(2026-11-10)
  4. *
  5. * 验证点:选图 + 打字一起发送后,用户侧**一个回合**(图与文字不分裂成两次发言)。
  6. *
  7. * ⚠️ 容器侧合并窗口(见 docs/03-interface/容器侧改造-图文合并处理-20261110.md)
  8. * 尚未上线,所以容器仍可能对图片抢先回话。本脚本只断言 **App 侧渲染**:
  9. * - 用户消息里 image.png 与文字属于同一个回合
  10. * - 不再出现「一个图气泡 + 一个空气泡 + 中间夹着处理提示」的分裂
  11. *
  12. * 用法:node scripts/verify-image-text-send.mjs <图片路径>
  13. */
  14. import { chromium } from 'playwright';
  15. import { mkdir } from 'node:fs/promises';
  16. import { dirname, resolve } from 'node:path';
  17. import { fileURLToPath } from 'node:url';
  18. const here = dirname(fileURLToPath(import.meta.url));
  19. const root = resolve(here, '..');
  20. const outDir = resolve(root, 'test-results/image-text-send');
  21. const BASE = process.env.BASE_URL || 'http://127.0.0.1:4300';
  22. const imgPath = process.argv[2];
  23. if (!imgPath) {
  24. console.error('用法:node scripts/verify-image-text-send.mjs <图片路径>');
  25. process.exit(2);
  26. }
  27. await mkdir(outDir, { recursive: true });
  28. const browser = await chromium.launch({ channel: 'chrome' });
  29. const page = await browser.newPage({ viewport: { width: 1440, height: 900 } });
  30. await page.goto(`${BASE}/desktop/chat`, { waitUntil: 'domcontentloaded' });
  31. await page.waitForTimeout(8000);
  32. // 进一个已有会话(入口页没有输入框)
  33. const s = page.locator('text=使用HTML根据我这个').first();
  34. if (await s.count()) {
  35. await s.click();
  36. await page.waitForTimeout(5000);
  37. }
  38. // 选图(隐藏的 file input)
  39. const input = page.locator('input[type="file"]').first();
  40. await input.setInputFiles(imgPath);
  41. await page.waitForTimeout(1200);
  42. await page.screenshot({ path: `${outDir}/1-staged.png` });
  43. // 打字 + 发送
  44. const box = page.locator('textarea').first();
  45. await box.click();
  46. await box.fill('这张图是什么形状?只回一句。');
  47. await page.keyboard.press('Enter');
  48. // 采集中途状态
  49. for (const [i, wait] of [[2, 1500], [3, 5000], [4, 20_000]]) {
  50. await page.waitForTimeout(wait);
  51. await page.screenshot({ path: `${outDir}/${i}-after-${wait}ms.png` });
  52. }
  53. // 统计「用户回合」数量:自己的气泡里同时含图和文
  54. const stats = await page.evaluate(() => {
  55. const bubbles = [...document.querySelectorAll('app-message-bubble')];
  56. const own = bubbles.filter((b) => b.className.includes('own') || b.innerHTML.includes('image.png'));
  57. return {
  58. bubbles: bubbles.length,
  59. withImage: bubbles.filter((b) => /image\.png/.test(b.innerText)).length,
  60. withText: bubbles.filter((b) => /这张图是什么形状/.test(b.innerText)).length,
  61. sample: own.slice(-3).map((b) => b.innerText.slice(0, 60)),
  62. };
  63. });
  64. console.log(JSON.stringify(stats, null, 2));
  65. console.log(`\n截图:${outDir}`);
  66. await browser.close();