关于 local-testing
local-testing 是用于在 macOS 上测试本地应用和 Bot 的技能。它通过 CDP 使用 agent-browser 自动化 Electron 和 Web 应用,并用 osascript 控制原生应用,同时提供环境管理、各渠道参考资料、脚本和验证流程。
主要功能
- 01通过 CDP 自动化 Electron 和 Web 应用
- 02使用 osascript 控制原生应用
- 03提供按渠道划分的 Bot 测试参考
- 04包含 Electron 生命周期与录制脚本
- 05支持截图和界面验证
适用场景
- 01通过界面测试 Electron 应用
- 02向桌面 Bot 发送测试消息
- 03截图验证应用行为
- 04运行 iMessage bridge 回归检查
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ lobehub/ lobehub --skill local-testing安装后在 Claude Code 中输入/local-testing确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
bot/capture-app-window.shosascript-common.mdbot/discord/index.mdtest-discord-bot.shbot/imessage/index.mdsend-imessage-test.shtest-imessage-bridge.shbot/lark/index.mdtest-lark-bot.shbot/qq/index.mdtest-qq-bot.shbot/slack/index.mdtest-slack-bot.shbot/telegram/index.mdtest-telegram-bot.shbot/wechat/index.mdtest-wechat-bot.shreferences/agent-browser-login.mdagent-gateway.mdrecord-app-screen.mdscripts/electron-dev.shscripts/agent-gateway/analyze-events.tsanalyze.mjsprobe-dump.jsprobe-dump.tsprobe-events.tsprobe.jsrun.tstab-switch.jstypes.ts详细介绍
本地应用和机器人测试
在 macOS 上进行本地测试的两种方式:
| 方式 | 工具 | 适用场景 |
|---|---|---|
| agent-browser + CDP | agent-browser CLI | Electron 应用、Web 应用(DOM 访问、JS 求值) |
| osascript (AppleScript) | osascript -e | macOS 原生应用(微信、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 更新。
核心工作流
所有浏览器自动化都遵循以下模式:
- 导航:
agent-browser open <url> - 快照:
agent-browser snapshot -i(获取@e1、@e2等元素引用) - 交互:使用引用进行点击、填写和选择
- 重新获取快照:导航或 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_PORT | 9222 | Chrome DevTools Protocol 端口 |
ELECTRON_LOG | /tmp/electron-dev.log | Electron 进程日志 |
ELECTRON_WAIT_S | 60 | 等待 Electron 进程的最长秒数 |
RENDERER_WAIT_S | 60 | 等待 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。
所有平台的操作模式相同:
- 激活应用(
tell application "X" to activate) - 导航到频道或聊天(快速切换器
Cmd+K或搜索Cmd+F) - 发送消息(粘贴剪贴板内容
Cmd+V,然后按 Enter) - 等待机器人回复
- 截屏以进行验证(
screencapture+Read工具)
各平台参考
选择目标平台对应的文件——每个文件都包含该应用专用的激活、导航、发送消息和验证代码片段:
每个频道都有自己的文件夹 bot/<channel>/,其中包含 index.md
(该应用专用的激活、导航、发送消息和验证代码片段)以及测试脚本:
| 平台 | 参考文件 | 快速切换器 |
|---|---|---|
| Discord | bot/discord/index.md | Cmd+K |
| Slack | bot/slack/index.md | Cmd+K |
| Telegram | bot/telegram/index.md | Cmd+F |
| WeChat / 微信 | bot/wechat/index.md | Cmd+F |
| Lark / 飞书 | bot/lark/index.md | Cmd+K |
| bot/qq/index.md | Cmd+F |
有关通用 osascript 模式(激活、输入、粘贴、截屏、读取辅助功能信息、通用工作流模板和注意事项),请参阅 bot/osascript-common.md。如果你刚开始使用 osascript 自动化,请先阅读此文档。
基于桥接的频道(无原生应用)
有些频道没有可通过 osascript 控制的原生应用——它们通过桌面应用内部的本地桥接进行连接。这些频道使用 agent-browser(IPC + UI)和桥接自身的 HTTP/REST 端点进行测试,而非使用 osascript:
| 频道 | 参考文件 | 控制对象 |
|---|---|---|
| iMessage | bot/imessage/index.md | imessageBridge.* 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 -Cfill对 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。