mcpField
L
local-testing

local-testing

github.com/lobehub测试与质量83.6k77 次安装今天更新
代码仓库

关于 local-testing

local-testing 是用于在 macOS 上测试本地应用和 Bot 的技能。它通过 CDP 使用 agent-browser 自动化 Electron 和 Web 应用,并用 osascript 控制原生应用,同时提供环境管理、各渠道参考资料、脚本和验证流程。

主要功能

  1. 01通过 CDP 自动化 Electron 和 Web 应用
  2. 02使用 osascript 控制原生应用
  3. 03提供按渠道划分的 Bot 测试参考
  4. 04包含 Electron 生命周期与录制脚本
  5. 05支持截图和界面验证

适用场景

  1. 01通过界面测试 Electron 应用
  2. 02向桌面 Bot 发送测试消息
  3. 03截图验证应用行为
  4. 04运行 iMessage bridge 回归检查

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/lobehub/lobehub --skill local-testing

安装后在 Claude Code 中输入/local-testing确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

bot/
capture-app-window.sh
osascript-common.md
bot/discord/
index.md
test-discord-bot.sh
bot/imessage/
index.md
send-imessage-test.sh
test-imessage-bridge.sh
bot/lark/
index.md
test-lark-bot.sh
bot/qq/
index.md
test-qq-bot.sh
bot/slack/
index.md
test-slack-bot.sh
bot/telegram/
index.md
test-telegram-bot.sh
bot/wechat/
index.md
test-wechat-bot.sh
references/
agent-browser-login.md
agent-gateway.md
record-app-screen.md
scripts/
electron-dev.sh
scripts/agent-gateway/
analyze-events.ts
analyze.mjs
probe-dump.js
probe-dump.ts
probe-events.ts
probe.js
run.ts
tab-switch.js
types.ts

详细介绍

本地应用和机器人测试

在 macOS 上进行本地测试的两种方式:

方式工具适用场景
agent-browser + CDPagent-browser CLIElectron 应用、Web 应用(DOM 访问、JS 求值)
osascript (AppleScript)osascript -emacOS 原生应用(微信、Discord、Telegram、Slack)

第 1 部分:agent-browser(Electron / Web 应用)

使用 agent-browser 通过 Chrome DevTools Protocol 自动化基于 Chromium 的应用。

通过 npm i -g agent-browser、brew install agent-browser 或 cargo install agent-browser 安装。运行 agent-browser install 下载 Chrome。运行 agent-browser upgrade 更新。

核心工作流

所有浏览器自动化都遵循以下模式:

  1. 导航:agent-browser open <url>
  2. 快照:agent-browser snapshot -i(获取 @e1、@e2 等元素引用)
  3. 交互:使用引用进行点击、填写和选择
  4. 重新获取快照:导航或 DOM 发生变化后,获取新的引用
agent-browser open https://example.com/form
agent-browser snapshot -i
# Output: @e1 [input type="email"], @e2 [input type="password"], @e3 [button] "Submit"

agent-browser fill @e1 "[email protected]"
agent-browser fill @e2 "password123"
agent-browser click @e3
agent-browser wait --load networkidle
agent-browser snapshot -i # Check result

命令串联

# Chain open + wait + snapshot in one call
agent-browser open https://example.com && agent-browser wait --load networkidle && agent-browser snapshot -i

如果不需要读取中间输出,请使用 &&。如果需要先解析输出(例如,先获取快照以发现引用,再进行交互),请分别运行命令。

基本命令

# Navigation
agent-browser open <url>              # Navigate (aliases: goto, navigate)
agent-browser close                   # Close browser
agent-browser close --all             # Close all active sessions

# Snapshot
agent-browser snapshot -i             # Interactive elements with refs (recommended)
agent-browser snapshot -s "#selector" # Scope to CSS selector

# Interaction (use @refs from snapshot)
agent-browser click @e1               # Click element
agent-browser click @e1 --new-tab     # Click and open in new tab
agent-browser fill @e2 "text"         # Clear and type text
agent-browser type @e2 "text"         # Type without clearing
agent-browser select @e1 "option"     # Select dropdown option
agent-browser check @e1               # Check checkbox
agent-browser press Enter             # Press key
agent-browser keyboard type "text"    # Type at current focus (no selector)
agent-browser keyboard inserttext "text"  # Insert without key events
agent-browser scroll down 500         # Scroll page
agent-browser scroll down 500 --selector "div.content"  # Scroll within container

# Get information
agent-browser get text @e1            # Get element text
agent-browser get url                 # Get current URL
agent-browser get title               # Get page title
agent-browser get cdp-url             # Get CDP WebSocket URL

# Wait
agent-browser wait @e1                # Wait for element
agent-browser wait --load networkidle # Wait for network idle
agent-browser wait --url "**/page"    # Wait for URL pattern
agent-browser wait 2000               # Wait milliseconds
agent-browser wait --text "Welcome"   # Wait for text to appear
agent-browser wait --fn "!document.body.innerText.includes('Loading...')"  # Wait for text to disappear
agent-browser wait "#spinner" --state hidden  # Wait for element to disappear

# Downloads
agent-browser download @e1 ./file.pdf          # Click element to trigger download
agent-browser wait --download ./output.zip     # Wait for any download to complete

# Network
agent-browser network requests                 # Inspect tracked requests
agent-browser network requests --type xhr,fetch  # Filter by resource type
agent-browser network requests --method POST   # Filter by HTTP method
agent-browser network route "**/api/*" --abort # Block matching requests
agent-browser network har start                # Start HAR recording
agent-browser network har stop ./capture.har   # Stop and save HAR file

# Viewport & Device Emulation
agent-browser set viewport 1920 1080          # Set viewport size (default: 1280x720)
agent-browser set viewport 1920 1080 2        # 2x retina
agent-browser set device "iPhone 14"          # Emulate device (viewport + user agent)

# Capture
agent-browser screenshot              # Screenshot to temp dir
agent-browser screenshot --full       # Full page screenshot
agent-browser screenshot --annotate   # Annotated screenshot with numbered element labels
agent-browser pdf output.pdf          # Save as PDF

# Clipboard
agent-browser clipboard read          # Read text from clipboard
agent-browser clipboard write "text"  # Write text to clipboard
agent-browser clipboard copy          # Copy current selection
agent-browser clipboard paste         # Paste from clipboard

# Dialogs (alert, confirm, prompt, beforeunload)
agent-browser dialog accept           # Accept dialog
agent-browser dialog accept "input"   # Accept prompt dialog with text
agent-browser dialog dismiss          # Dismiss/cancel dialog
agent-browser dialog status           # Check if dialog is open

# Diff (compare page states)
agent-browser diff snapshot                        # Compare current vs last snapshot
agent-browser diff screenshot --baseline before.png  # Visual pixel diff
agent-browser diff url <url1> <url2>               # Compare two pages

# Streaming
agent-browser stream enable           # Start WebSocket streaming
agent-browser stream status           # Inspect streaming state
agent-browser stream disable          # Stop streaming

批量执行

echo '[
  ["open", "https://example.com"],
  ["snapshot", "-i"],
  ["click", "@e1"],
  ["screenshot", "result.png"]
]' | agent-browser batch --json

身份验证

# Option 1: Auth vault (credentials stored encrypted)
echo "$PASSWORD" | agent-browser auth save myapp --url https://app.example.com/login --username user --password-stdin
agent-browser auth login myapp

# Option 2: Session name (auto-save/restore cookies + localStorage)
agent-browser --session-name myapp open https://app.example.com/login
agent-browser close                                                       # State auto-saved
agent-browser --session-name myapp open https://app.example.com/dashboard # Auto-restored

# Option 3: Persistent profile
agent-browser --profile ~/.myapp open https://app.example.com/login

# Option 4: State file
agent-browser state save auth.json
agent-browser state load auth.json
LobeHub 开发服务器 — 注入 better-auth Cookie

在 macOS 上使用 agent-browser --headed 可能会创建一个屏幕外的 Chromium 窗口,导致无法手动登录。对于本地 LobeHub 开发服务器(例如 localhost:3011),请从用户自己的 Chrome DevTools 中的 Network 请求复制 better-auth.session_token Cookie,然后通过 state load 加载。完整步骤请参阅 references/agent-browser-login.md。

语义定位器(Refs 的替代方案)

agent-browser find text "Sign In" click
agent-browser find label "Email" fill "[email protected]"
agent-browser find role button click --name "Submit"
agent-browser find placeholder "Search" type "query"
agent-browser find testid "submit-btn" click

JavaScript 求值(eval)

# Simple expressions
agent-browser eval 'document.title'

# Complex JS: use --stdin with heredoc (RECOMMENDED)
agent-browser eval --stdin << 'EVALEOF'
JSON.stringify(
  Array.from(document.querySelectorAll("img"))
    .filter(i => !i.alt)
    .map(i => ({ src: i.src.split("/").pop(), width: i.width }))
)
EVALEOF

# Base64 encoding (avoids all shell escaping issues)
agent-browser eval -b "$(echo -n 'document.title' | base64)"

Ref 生命周期

页面发生变化时,Refs(@e1、@e2 等)会失效。点击会触发导航的链接或按钮、提交表单或动态内容加载后,务必重新获取快照。

带注释的屏幕截图(视觉模式)

agent-browser screenshot --annotate
# Output includes the image path and a legend:
#   [1] @e1 button "Submit"
#   [2] @e2 link "Home"
agent-browser click @e2 # Click using ref from annotated screenshot

并行会话

agent-browser --session site1 open https://site-a.com
agent-browser --session site2 open https://site-b.com
agent-browser session list

连接到现有 Chrome

agent-browser --auto-connect snapshot # Auto-discover running Chrome
agent-browser --cdp 9222 snapshot     # Explicit CDP port

iOS 模拟器(移动版 Safari)

agent-browser device list
agent-browser -p ios --device "iPhone 16 Pro" open https://example.com
agent-browser -p ios snapshot -i
agent-browser -p ios tap @e1
agent-browser -p ios swipe up
agent-browser -p ios screenshot mobile.png
agent-browser -p ios close

可观测性仪表板

agent-browser dashboard install
agent-browser dashboard start # Background server on port 4848
agent-browser dashboard stop

云服务提供商

使用 -p <provider> 在云端浏览器中运行:agentcore、browserbase、browserless、browseruse、kernel。

浏览器引擎选择

agent-browser --engine lightpanda open example.com # 10x faster, 10x less memory

Electron(LobeHub 桌面版)

启动 / 清理

使用 electron-dev.sh 脚本管理 Electron 开发环境。它负责处理进程生命周期、等待 SPA 就绪,并可靠地终止所有子进程(主进程 + 辅助进程 + vite)。

SCRIPT=".agents/skills/local-testing/scripts/electron-dev.sh"

# Start Electron dev with CDP (idempotent — skips if already running)
$SCRIPT start

# Check if Electron is running and CDP is reachable
$SCRIPT status

# Kill all Electron-related processes (main + helper + vite)
$SCRIPT stop

# Force fresh restart
$SCRIPT restart

start 成功后,使用 agent-browser --cdp 9222 snapshot -i 连接。

测试完成后务必运行 $SCRIPT stop — 仅运行 pkill -f "Electron" 无法捕获所有辅助进程。

环境变量
变量默认值说明
CDP_PORT9222Chrome DevTools Protocol 端口
ELECTRON_LOG/tmp/electron-dev.logElectron 进程日志
ELECTRON_WAIT_S60等待 Electron 进程的最长秒数
RENDERER_WAIT_S60等待 SPA 加载的最长秒数
LobeHub 专用模式
访问 Zustand Store 状态
agent-browser --cdp 9222 eval --stdin << 'EVALEOF'
(function() {
  var chat = window.__LOBE_STORES.chat();
  var ops = Object.values(chat.operations);
  return JSON.stringify({
    ops: ops.map(function(o) { return { type: o.type, status: o.status }; }),
    activeAgent: chat.activeAgentId,
    activeTopic: chat.activeTopicId,
  });
})()
EVALEOF
查找并使用聊天输入框
# The chat input is contenteditable — must use -C flag
agent-browser --cdp 9222 snapshot -i -C 2>&1 | grep "editable"

agent-browser --cdp 9222 click @e48
agent-browser --cdp 9222 type @e48 "Hello world"
agent-browser --cdp 9222 press Enter
等待 Agent 完成
agent-browser --cdp 9222 eval --stdin << 'EVALEOF'
(function() {
  var chat = window.__LOBE_STORES.chat();
  var ops = Object.values(chat.operations);
  var running = ops.filter(function(o) { return o.status === 'running'; });
  return running.length === 0 ? 'done' : 'running: ' + running.length;
})()
EVALEOF
安装错误拦截器
agent-browser --cdp 9222 eval --stdin << 'EVALEOF'
(function() {
  window.__CAPTURED_ERRORS = [];
  var orig = console.error;
  console.error = function() {
    var msg = Array.from(arguments).map(function(a) {
      if (a instanceof Error) return a.message;
      return typeof a === 'object' ? JSON.stringify(a) : String(a);
    }).join(' ');
    window.__CAPTURED_ERRORS.push(msg);
    orig.apply(console, arguments);
  };
  return 'installed';
})()
EVALEOF

# Later, check captured errors:
agent-browser --cdp 9222 eval "JSON.stringify(window.__CAPTURED_ERRORS)"

Chrome / Web 应用

/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome \
  --remote-debugging-port=9222 \
  --user-data-dir=/tmp/chrome-test-profile \
  "<URL>" &
sleep 5
agent-browser --cdp 9222 snapshot -i

# Or auto-discover running Chrome with remote debugging
agent-browser --auto-connect snapshot -i

第 2 部分:osascript(原生 macOS 应用机器人测试)

使用 osascript 通过 AppleScript 控制原生 macOS 桌面应用,以测试机器人。适用于任何支持 macOS 辅助功能的应用,无需 CDP 或 Chromium。

所有平台的操作模式相同:

  1. 激活应用(tell application "X" to activate)
  2. 导航到频道或聊天(快速切换器 Cmd+K 或搜索 Cmd+F)
  3. 发送消息(粘贴剪贴板内容 Cmd+V,然后按 Enter)
  4. 等待机器人回复
  5. 截屏以进行验证(screencapture + Read 工具)

各平台参考

选择目标平台对应的文件——每个文件都包含该应用专用的激活、导航、发送消息和验证代码片段:

每个频道都有自己的文件夹 bot/<channel>/,其中包含 index.md (该应用专用的激活、导航、发送消息和验证代码片段)以及测试脚本:

平台参考文件快速切换器
Discordbot/discord/index.mdCmd+K
Slackbot/slack/index.mdCmd+K
Telegrambot/telegram/index.mdCmd+F
WeChat / 微信bot/wechat/index.mdCmd+F
Lark / 飞书bot/lark/index.mdCmd+K
QQbot/qq/index.mdCmd+F

有关通用 osascript 模式(激活、输入、粘贴、截屏、读取辅助功能信息、通用工作流模板和注意事项),请参阅 bot/osascript-common.md。如果你刚开始使用 osascript 自动化,请先阅读此文档。

基于桥接的频道(无原生应用)

有些频道没有可通过 osascript 控制的原生应用——它们通过桌面应用内部的本地桥接进行连接。这些频道使用 agent-browser(IPC + UI)和桥接自身的 HTTP/REST 端点进行测试,而非使用 osascript:

频道参考文件控制对象
iMessagebot/imessage/index.mdimessageBridge.* IPC + 本地桥接 + BlueBubbles REST

对于 iMessage,有一个一次性回归脚本——请参阅下方的 test-imessage-bridge.sh。

脚本

应用 / 录制脚本位于 .agents/skills/local-testing/scripts/:

脚本用途
electron-dev.sh管理 Electron 开发环境(启动/停止/状态/重启)
record-electron-demo.sh使用 ffmpeg 录制 Electron 应用演示
record-app-screen.sh录制应用屏幕(视频 + 截图,启动/停止)

机器人脚本位于 .agents/skills/local-testing/bot/ 下,每个频道一个文件夹(与该频道的 index.md 并列)。共享的 capture-app-window.sh 位于 bot/ 根目录:

脚本用途
capture-app-window.sh捕获特定应用窗口的截图(供机器人测试使用)
discord/test-discord-bot.sh通过 osascript 向 Discord 机器人发送消息
slack/test-slack-bot.sh通过 osascript 向 Slack 机器人发送消息
telegram/test-telegram-bot.sh通过 osascript 向 Telegram 机器人发送消息
wechat/test-wechat-bot.sh通过 osascript 向 WeChat 机器人发送消息
lark/test-lark-bot.sh通过 osascript 向 Lark / 飞书机器人发送消息
qq/test-qq-bot.sh通过 osascript 向 QQ 机器人发送消息
imessage/test-imessage-bridge.sh对 iMessage BlueBubbles 桥接进行回归测试(IPC + HTTP)
imessage/send-imessage-test.sh发送一条真实的 iMessage(桌面端 → BB → iMessage)并验证是否发送成功
窗口截图工具

capture-app-window.sh 使用 screencapture -l <windowID> 捕获特定应用窗口的截图。它使用 Swift + CGWindowList 按进程名称查找窗口,因此即使窗口位于外接显示器上或被其他窗口遮挡,截图也能正常工作。

# Standalone usage
./.agents/skills/local-testing/bot/capture-app-window.sh "Discord" /tmp/discord.png
./.agents/skills/local-testing/bot/capture-app-window.sh "Slack" /tmp/slack.png
./.agents/skills/local-testing/bot/capture-app-window.sh "WeChat" /tmp/wechat.png

所有机器人测试脚本都会自动使用此工具截图。

机器人测试脚本

所有机器人测试脚本使用相同的接口:

./scripts/test-<platform>-bot.sh <channel_or_contact> <message> [wait_seconds] [screenshot_path]

示例:

# Discord — test a bot in #bot-testing channel
./.agents/skills/local-testing/bot/discord/test-discord-bot.sh "bot-testing" "!ping"
./.agents/skills/local-testing/bot/discord/test-discord-bot.sh "bot-testing" "/ask Tell me a joke" 30

# Slack — test a bot in #bot-testing channel
./.agents/skills/local-testing/bot/slack/test-slack-bot.sh "bot-testing" "@mybot hello"
./.agents/skills/local-testing/bot/slack/test-slack-bot.sh "bot-testing" "/ask What is 2+2?" 20

# Telegram — test a bot by username
./.agents/skills/local-testing/bot/telegram/test-telegram-bot.sh "MyTestBot" "/start"
./.agents/skills/local-testing/bot/telegram/test-telegram-bot.sh "GPTBot" "Hello" 60

# WeChat — test a bot or send to a contact
./.agents/skills/local-testing/bot/wechat/test-wechat-bot.sh "文件传输助手" "test message" 5
./.agents/skills/local-testing/bot/wechat/test-wechat-bot.sh "MyBot" "Tell me a joke" 30

# Lark/飞书 — test a bot in a group chat
./.agents/skills/local-testing/bot/lark/test-lark-bot.sh "bot-testing" "@MyBot hello"
./.agents/skills/local-testing/bot/lark/test-lark-bot.sh "bot-testing" "Help me with this" 30

# QQ — test a bot in a group or direct chat
./.agents/skills/local-testing/bot/qq/test-qq-bot.sh "bot-testing" "Hello bot" 15
./.agents/skills/local-testing/bot/qq/test-qq-bot.sh "MyBot" "/help" 10

每个脚本都会激活应用、导航到频道/联系人、通过剪贴板粘贴消息、发送消息、等待,然后截取屏幕截图。使用 Read 工具查看截图并进行视觉验证。

iMessage 桥接回归脚本

test-imessage-bridge.sh 不遵循 osascript 机器人接口,而是驱动桌面桥接的 IPC + HTTP 层并断言结果,然后自行清理。需要 BlueBubbles 正在运行,且 Electron 已启动并启用 CDP。

./.agents/skills/local-testing/bot/imessage/test-imessage-bridge.sh '<bluebubbles_password>' [bb_url] [cdp_port]
# defaults: bb_url=http://127.0.0.1:1234  cdp_port=9222 — exit 0 = all green

该脚本会检查连接/配置流程(testConfig 成功和拒绝路径、首次 upsertConfig 保存、桥接运行并已注册 webhook、本地服务端密钥强制校验)。完整的手动 UI 流程和已知问题,请参阅 bot/imessage/index.md。


屏幕录制

使用 record-app-screen.sh 录制自动化演示(启动/停止流程、CDP 截图 + ffmpeg 合成)。完整文档请参阅 references/record-app-screen.md。

./.agents/skills/local-testing/scripts/electron-dev.sh start
./.agents/skills/local-testing/scripts/record-app-screen.sh start my-demo
# ... run automation ...
./.agents/skills/local-testing/scripts/record-app-screen.sh stop

输出到 .records/ 目录(已添加到 gitignore):<name>.mp4(视频)+ <name>/(每 3s 一张截图)。


注意事项

agent-browser
  • 守护进程可能会卡住 — 如果命令挂起,请运行 agent-browser close --all 或 pkill -f agent-browser 进行重置
  • HMR 会使所有内容失效 — 代码更改后,引用会失效。请重新生成快照或重启
  • snapshot -i 找不到 contenteditable — 对于富文本编辑器,请使用 snapshot -i -C
  • fill 对 contenteditable 无效 — 对于聊天输入框,请使用 type
  • 截图会保存到 ~/.agent-browser/tmp/screenshots/ — 使用 Read 工具读取
  • 对话框会阻塞所有命令 — 如果命令超时,请检查 agent-browser dialog status
  • 默认超时时间为 25s — 使用 AGENT_BROWSER_DEFAULT_TIMEOUT(毫秒)覆盖,或使用显式等待
  • Shell 引号会破坏 eval — 对于复杂的 JS,请使用 eval --stdin <<'EVALEOF'
Electron 专用
  • 务必使用 electron-dev.sh stop 进行清理 — pkill -f "Electron" 只会终止主进程;辅助进程(GPU、渲染器、网络)仍会保留。该脚本会根据项目的 electron 二进制文件路径匹配 PID,并查找和终止所有相关进程。
  • 必须在 apps/desktop/ 中运行 npx electron-vite dev — 从项目根目录运行会静默失败。electron-dev.sh 脚本会自动处理。
  • 加载后不要调整 Electron 窗口大小 — 调整大小会触发完整的 SPA 重新加载
  • Store 位于 window.__LOBE_STORES,而不是 window.__ZUSTAND_STORES__
osascript

完整的 osascript 注意事项列表(辅助功能权限、keystroke 的非 ASCII 问题、本地化的应用名称、速率限制等)请参阅 bot/osascript-common.md。

评论

支持 Markdown