Files
EchoChat/.playwright-mcp/task16/scenarios/01-create-and-join-by-code.md
bujinyuan 5ed14c2aac docs(meeting): Task 16 E2E 剧本落盘 + .gitignore 精细化
Playwright MCP 4 场景剧本(.playwright-mcp/task16/):
- README.md:总览 + 账号/环境/资源断言指南
- scenarios/01-create-and-join-by-code.md:创建→会议号加入→媒体打通→结束,含 Redis/MySQL/media-server 三维资源断言
- scenarios/02-invite-link-with-password.md:邀请链接带密码 + P0-4 "密码不走 URL query" 回归
- scenarios/03-notify-quickjoin.md:通知中心「立即加入」跳转 + 过期卡片降级
- scenarios/04-host-toolkit-and-transfer.md:主持人四件套 + 宽限期重连/到期自动转让(P1-1 原子性 + P1-7 处理锁验证)
- scenarios/05-reopen-5times-resource-cleanup.md:反复开关 5 次 + 前端 heap 快照/ Redis 累积对比/ media-server 观测端点对比,验证 Step A(f5ae095)资源清理修复

手工回归点(manual-regression.md):17 项 MCP 难以稳定覆盖的交互场景
(HTTPS 自签 / 跨网段 iOS / 网络抖动 / 物理返回 / 跨 tab 并发 / 摄像头抢占 等)

.gitignore 调整:
- 放行 .playwright-mcp/task16/ 下的 Markdown 剧本文档进入版本控制
- 仍忽略运行时产物(*.log/*.png/*.yml/截图)
- task15/ 保持未跟踪状态,避免历史截图噪声入库

Made-with: Cursor
2026-04-23 17:21:42 +08:00

6.6 KiB
Raw Permalink Blame History

场景 01创建会议 → 会议号加入 → 双向音视频打通 → 主持人结束

目标

  • 验证 POST /api/v1/meeting/rooms(创建)+ POST /api/v1/meeting/rooms/:code/join(加入)+ mediasoup 信令全链路打通
  • 覆盖设计 §6.1REST 建房、§6.3WS 信令 11 事件、§6.6NodeClient 媒体编排)
  • 配套断言 Step A 资源清理:结束会议后 Redis / MySQL / media-server 全部归零

前置条件

../README.md 通用前置;额外确认:

  • 两个浏览器上下文MCP 多 tab分别以 task15_a / task15_b 登录
  • Alice tab 为主持人Bob tab 为参会人

MCP 执行脚本

步骤 1Alice 登录并创建即时会议

  1. browser_navigatehttps://localhost:5173/#/pages/auth/login
  2. browser_fill_form → 账号 task15_a / 密码 Task15@2026 → 点 "登录"
  3. 断言首页进入「消息」tab
  4. 底部切换到「会议」tabpages/meeting/index
  5. 点「创建即时会议」
  6. 设备预览页:默认勾选麦克风 + 摄像头;点「立即进入」
  7. 进入 pages/meeting/room,截图 → 01-alice-created.png
  8. 读取 URL query 得到 roomCode,记录到上下文变量 $ROOM_CODE

步骤 2Bob 通过会议号加入

  1. 开第二个 browser contextbrowser_navigatehttps://localhost:5173/#/pages/auth/login
  2. task15_b 登录
  3. 切到「会议」tab点「通过会议号加入」
  4. 输入 $ROOM_CODE
  5. 预览页 → 「立即加入」
  6. 截图 → 02-bob-joined.png

步骤 3双向音视频打通断言

  • Alice tab 成员面板应展示 2 个成员Bob 的 AV 图标为蓝色(开启状态)
  • Bob tab 成员面板同理
  • Alice 的视频宫格应能看到 Bob 的视频流(VideoTile[user_id=56] video.srcObject 非空)
  • 控制台验证 mediasoup 关键日志:
    • [MediaEngine] producer 创建成功 kind=audio
    • [MediaEngine] producer 创建成功 kind=video
    • [MediaEngine] consumer 创建成功
  • browser_evaluate:
    const store = window.__pinia?.state?.value?.meeting
    return {
      participantCount: store.participants.length,
      localAudio: store.localAudioEnabled,
      localVideo: store.localVideoEnabled,
      remoteUserCount: Object.keys(store.remoteConsumers).length,
    }
    
    期望:participantCount=2localAudio=truelocalVideo=trueremoteUserCount=1

步骤 4资源创建态断言

在第三个终端执行:

# media-server 应有 1 Router + 4 Transport2 users × send/recv+ 4 Producer + 4 Consumer
curl -s -H "X-Internal-Token: $INTERNAL_TOKEN" http://localhost:3300/internal/info | jq .stats

# Redis 应有两个用户的资源追踪 Set
redis-cli KEYS "echo:meeting:resource:$ROOM_CODE:*"
# 期望输出echo:meeting:resource:xxx:55 / echo:meeting:resource:xxx:56
# 每个 SMEMBERS 应有 5 条transport×2 + producer×2 + consumer×2 之类,具体取决于 producer.new 收敛顺序)
redis-cli SMEMBERS "echo:meeting:resource:$ROOM_CODE:55"
redis-cli SMEMBERS "echo:meeting:resource:$ROOM_CODE:56"

步骤 5Alice 结束会议

  1. Alice tab 工具栏 → 「结束会议」→ 弹窗确认「主持人结束」
  2. 截图 → 03-alice-ended.png(页面转为「会议已结束」提示页)
  3. Bob tab 应收到 WS meeting.room.ended 广播展示「会议已结束」toast1.2s 后跳回会议首页
  4. 截图 → 04-bob-received-ended.png

步骤 6资源释放断言核心验证 Step A

# 1. Redis 清理验证P1-4 + Step A B1/B3 的核心断言)
redis-cli KEYS "echo:meeting:resource:$ROOM_CODE:*"         # 期望:(empty array)
redis-cli KEYS "echo:meeting:member_state:$ROOM_CODE:*"     # 期望:(empty array)
redis-cli KEYS "echo:meeting:host_grace:$ROOM_CODE"         # 期望:(nil)
redis-cli KEYS "echo:meeting:empty_ttl:$ROOM_CODE"          # 期望:(nil)

# 2. MySQL 状态验证
mysql -e "SELECT status, ended_reason FROM meeting_rooms WHERE room_code='$ROOM_CODE'" echochat
# 期望status=ended, ended_reason=host_ended

mysql -e "SELECT user_id, left_at, left_reason FROM meeting_participants WHERE room_id=(SELECT id FROM meeting_rooms WHERE room_code='$ROOM_CODE')" echochat
# 期望Alice + Bob 都有 left_at ≠ NULL, left_reason='host_end'

# 3. media-server Router / Transport / Producer / Consumer 全部归零
curl -s -H "X-Internal-Token: $INTERNAL_TOKEN" http://localhost:3300/internal/info | jq '.stats | .routers, .transports, .producers, .consumers'
# 期望:对应 $ROOM_CODE 的 Router 已销毁;整体 stats 可能非 0其他并行测试但本场 Router 必须不存在

步骤 7前端资源释放断言

在 Alice tab 执行 browser_evaluate:

// 结束页停留时的 state 快照
const store = window.__pinia?.state?.value?.meeting
return {
  localState: store.localState,       // 期望:'ended'
  engineExists: !!window.__engine,    // 期望false_cleanupMedia 已置空)
  participantCount: store.participants.length,  // 结束页保留渲染数据,非 0
}

然后手动返回首页(模拟 onUnload 触发):

  1. browser_navigate_back 或点结束页的「返回首页」按钮
  2. 再次 browser_evaluate 断言 pinia state
    const store = window.__pinia?.state?.value?.meeting
    return {
      localState: store.localState,       // 期望:'idle'
      participantCount: store.participants.length,  // 期望0
      currentRoom: store.currentRoom,     // 期望null
    }
    

预期结果汇总

断言维度 预期
WS 信令 room.join / producer.new / consume 全链路无异常
双向媒体 两端视频宫格均能播放对方画面,localAudioEnabled=trueremoteConsumers 含对方
Redis 清理 resource:* / member_state:* / host_grace / empty_ttl 全空
DB 一致 meeting_rooms.status=ended、两行 meeting_participants.left_at 非空
media-server 对应 Router 已删除level transport/producer/consumer 观察端点无残留
前端 _engine=null / _pendingBroadcastTimers.size=0onUnload 后 pinia state 全部 reset

失败排查

现象 可能原因 排查点
Bob 看不到 Alice 的画面 pushExistingRoomState 同步性Task 16 P1-8 修复) logs.Inforoom.join 是否先于 member.joined
结束后 Redis resource:* 仍存 EndRoom 未走 cleanupRoomRedisResidualStep A B1 回归) grep "cleanupRoomRedisResidual" backend/go-service/app/meeting/service/meeting_service.go
localState 卡在 connected _onRoomEnded 未触发 检查 Bob tab 是否收到 meeting.room.ended WS 事件(网络面板 frame