| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101 |
- #!/usr/bin/env node
- /**
- * 复现「新建会话后立刻发消息,消息不显示」(2026-11-11)
- *
- * 不读代码推断,在真实浏览器里按用户操作顺序跑:
- * 1. 打开聊天页 → 点「新建对话」→ 弹窗里选容器
- * 2. **立刻**在输入框里输入并回车
- * 3. 每 200ms 采样消息区,看消息何时出现、输入框何时清空
- *
- * 用法:node scripts/repro-new-session-send.mjs
- */
- import { chromium } from 'playwright';
- import { readFileSync } from 'node:fs';
- import { dirname, resolve } from 'node:path';
- import { fileURLToPath } from 'node:url';
- const here = dirname(fileURLToPath(import.meta.url));
- const login = JSON.parse(readFileSync(resolve(here, '../.claude/dev-login.json'), 'utf8'));
- const BASE = 'http://localhost:4200';
- const AGENT = process.argv[2] ?? 'lj-test';
- const b = await chromium.launch({ channel: 'chrome' });
- const ctx = await b.newContext({ viewport: { width: 1440, height: 900 } });
- const page = await ctx.newPage();
- await page.addInitScript(
- ([token, me]) => {
- localStorage.setItem('fmode_session_token', token);
- localStorage.setItem('fmode_me', me);
- },
- [login.token, JSON.stringify(login.me)],
- );
- const errors = [];
- page.on('pageerror', (e) => errors.push(String(e).slice(0, 200)));
- page.on('console', (m) => {
- if (m.type() === 'error') errors.push('console: ' + m.text().slice(0, 200));
- });
- console.log('=== 1. 打开聊天页 ===');
- await page.goto(`${BASE}/desktop/chat`, { waitUntil: 'domcontentloaded', timeout: 60_000 });
- await page.waitForTimeout(6000);
- console.log('=== 2. 点「新建对话」 ===');
- await page.locator('text=新建对话').first().click();
- await page.waitForTimeout(2000);
- console.log(`=== 3. 选容器 ${AGENT} ===`);
- const opt = page.locator(`text=${AGENT}`).last();
- await opt.click();
- await page.waitForTimeout(4000);
- console.log(' URL:', page.url());
- /* 确认输入框就绪 */
- const ta = page.locator('textarea').first();
- await ta.waitFor({ state: 'visible', timeout: 20_000 });
- console.log('\n=== 4. 立刻输入并回车(模拟用户"新建完就发")===');
- const TEXT = '测试' + Date.now().toString().slice(-4);
- await ta.fill(TEXT);
- const t0 = Date.now();
- await ta.press('Enter');
- console.log('\n=== 5. 采样(每 200ms,最多 20 秒)===');
- let firstSeenAt = null;
- let inputClearedAt = null;
- for (let i = 0; i < 100; i++) {
- const snap = await page.evaluate(() => {
- const ta = document.querySelector('textarea');
- return {
- inputVal: ta ? ta.value : '(无)',
- bodyText: (document.body.innerText || '').slice(0, 6000),
- };
- });
- const ms = Date.now() - t0;
- if (snap.inputVal === '' && inputClearedAt === null) {
- inputClearedAt = ms;
- console.log(` ${ms}ms 输入框已清空`);
- }
- /* 消息区出现该文本(排除输入框里的那份) */
- if (snap.bodyText.includes(TEXT) && firstSeenAt === null) {
- firstSeenAt = ms;
- console.log(` ${ms}ms ✅ 消息出现在页面上`);
- }
- if (i % 10 === 0 && firstSeenAt === null) {
- console.log(` ${ms}ms 还没出现 | 输入框="${snap.inputVal.slice(0, 20)}"`);
- }
- if (firstSeenAt !== null && inputClearedAt !== null) break;
- await page.waitForTimeout(200);
- }
- console.log('\n=== 结果 ===');
- console.log(`文本: "${TEXT}"`);
- console.log(`输入框清空: ${inputClearedAt === null ? '❌ 从未清空' : inputClearedAt + 'ms'}`);
- console.log(`消息出现: ${firstSeenAt === null ? '❌ 20 秒内从未出现' : firstSeenAt + 'ms'}`);
- if (firstSeenAt !== null && firstSeenAt > 1500) {
- console.log('⚠️ 超过 1.5 秒 —— 用户能感知到卡顿');
- }
- console.log('页面错误:', errors.length ? errors.slice(0, 5) : '无');
- await page.screenshot({ path: 'test-results/repro-new-session.png' });
- await b.close();
|