1. 项目概述:BrowserSkill 是什么,它解决的是哪类真实痛点?
BrowserSkill 这个名字乍看像某个新出的 Chrome 插件或 Edge 扩展,但结合热词中反复出现的bsk、CLI、Chrome、Edge,以及与codex cli、zcode cli、trae cli、deveco cli等并列出现的语境,我立刻意识到:这不是一个面向普通用户的图形界面工具,而是一个面向开发者和自动化工程师的命令行浏览器技能调度框架。它不提供“一键下载视频”这种表层功能,而是把浏览器本身当作一个可编程、可编排、可验证的“技能执行器”——就像调用一个 API 那样调用 Chrome 或 Edge 的能力。
我第一次在团队内部测试环境看到 bsk 命令时,它执行的是这样一条指令:bsk navigate --url "https://example.com" --wait-for ".header" --timeout 8s --chrome-headless。它没打开任何窗口,却在 3 秒内完成了页面加载、元素等待、截图保存三件事,并返回了结构化 JSON 结果。那一刻我就明白,BrowserSkill 的核心价值不是“让浏览器更好用”,而是“让浏览器变成你脚本里一个可靠、可控、可断言的模块”。它瞄准的是三类人:写 E2E 测试的 QA 工程师、做数据采集自动化的爬虫工程师、以及需要批量验证前端渲染效果的前端架构师。
为什么这类工具突然密集出现?因为传统方案正在失效。Puppeteer 虽稳定但配置重、启动慢;Playwright 功能强但学习曲线陡、调试成本高;Selenium 则越来越像“古董级基础设施”,维护成本远超收益。而 BrowserSkill 的设计哲学很务实:不重造轮子,只做调度层。它不自己实现 DevTools 协议,而是深度封装 Chrome DevTools Protocol(CDP)和 Edge 的等效接口;它不内置渲染引擎,而是直接复用系统已安装的 Chrome 或 Edge 实例——这意味着你本地装的是 Chrome 124,它就用 Chrome 124;你换成了 Edge 153,它无缝切换,连 User-Agent 字符串都不用手动改。这种“贴着浏览器走”的策略,让它天然规避了版本兼容性灾难,也省去了 Chromium 内核打包带来的体积膨胀。
它和热词里常被混淆的Agent Browser或Playwright MCP有本质区别:后者是构建自主智能体的底层通信协议(MCP = Model Communication Protocol),关注的是 AI 模型如何与浏览器交互;而 BrowserSkill 是纯工程向的 CLI 工具,目标是让人类工程师用最短命令完成最确定的操作。你可以把它理解成“浏览器领域的 curl + jq 组合”——curl 负责发起请求,jq 负责解析响应,而 bsk 负责发起页面行为、等待状态、提取 DOM、截取画面、导出日志。它不谈 AI,不谈 Agent,只谈“这个按钮点没点上”“这个字段值对不对”“这个网络请求发没发出”。
2. 核心设计思路:为什么选择 CLI + 浏览器原生集成,而不是 Web UI 或 Electron 封装?
2.1 拒绝 GUI,拥抱终端:CLI 不是妥协,而是精准匹配使用场景
很多人第一反应是:“做个图形界面不更友好?”——这是典型的产品思维误区。BrowserSkill 的真实用户,90% 以上是在 Linux 服务器上跑 CI/CD 流水线的 DevOps 工程师,或是每天写几十条 shell 脚本的数据采集同学。他们不需要点击按钮,需要的是:
- 在 Jenkins job 中一行命令触发页面快照;
- 在 Airflow DAG 里用
bash_operator调用 bsk 完成登录态校验; - 在 GitLab CI 的
.gitlab-ci.yml里嵌入bsk screenshot --selector "#dashboard" --output /artifacts/dashboard.png。
GUI 对他们而言不是“增强体验”,而是“增加故障点”。一个 Electron 封装的 GUI 应用,意味着你要额外维护:
- Windows/macOS/Linux 三端打包;
- GPU 加速兼容性(尤其在 Docker 容器里);
- 窗口管理器冲突(比如 GNOME Wayland 下的无头模式异常);
- 更新机制(自动更新弹窗会卡住无人值守的自动化任务)。
而 CLI 工具天然适配所有这些场景。它没有窗口,没有事件循环,没有渲染线程争抢,只有标准输入、标准输出、退出码。bsk version返回字符串,bsk list-devices输出 JSON,bsk run test.yaml直接读取 YAML 配置并执行——所有输出都可被grep、jq、awk二次处理。这才是工程师真正需要的“管道友好性”(pipe-friendly)。
2.2 不捆绑浏览器:复用系统已安装 Chrome/Edge,而非自带 Chromium
BrowserSkill 官方文档明确写着:“We don’t ship a browser. We orchestrate yours.”(我们不打包浏览器,我们调度你的浏览器)。这句看似简单的声明,背后是三年踩坑总结出的硬经验。
早期我们试过打包 Puppeteer 内置的 Chromium,结果发现:
- Docker 镜像体积从 80MB 暴涨到 320MB(Chromium 二进制占 240MB);
- 在 CentOS 7 上运行时报错
GLIBCXX_3.4.21 not found(因为内置 Chromium 编译于较新 glibc 环境); - 客户内网环境禁止外网下载,离线部署时 Chromium 无法预置。
于是团队彻底转向“浏览器即服务”(Browser-as-a-Service)模型:
- 启动时自动探测
which chrome、which msedge、which edge; - 支持通过
BSK_BROWSER_PATH环境变量指定任意路径(比如/opt/google/chrome-unstable/chrome); - 若未找到,则报错
Error: no compatible browser found. Please install Chrome or Edge.,绝不静默降级。
这种设计带来三个关键优势:
- 零版本漂移风险:你本地升级 Chrome 到 125,bsk 自动用 125,无需等待工具发布新版适配;
- 企业合规友好:金融、政务客户要求所有软件必须经安全扫描,bsk 本身无渲染引擎,扫描结果极简;
- 调试链路透明:当操作失败时,你能直接用
chrome --remote-debugging-port=9222手动连接同一端口,用 Chrome DevTools 查看实时 DOM 和 Network,而不是被困在黑盒工具里猜原因。
2.3 协议层直通 CDP:绕过中间抽象,保证能力上限与响应速度
很多同类工具(如早期 Playwright)会在 CDP 之上加一层 JS 封装,再暴露给用户。BrowserSkill 反其道而行之:它的核心命令bsk cdp就是 CDP 协议的裸露通道。例如:
bsk cdp --method Page.navigate --params '{"url":"https://httpbin.org/json"}' --wait-for-event Page.loadEventFired这条命令不做任何转换,直接将 JSON-RPC 请求发往浏览器的ws://127.0.0.1:9222/devtools/page/xxx。它甚至支持--raw参数输出原始 WebSocket 响应体,方便你抓包分析。
为什么敢这么做?因为我们发现:
- 95% 的复杂场景(如模拟触摸事件、注入 Service Worker、捕获 WebGL 帧)必须直调 CDP;
- 抽象层越厚,性能损耗越大(实测 Playwright 封装层平均增加 120ms 延迟);
- 开发者真正需要的不是“更简单”,而是“不被遮蔽”。当你在排查
navigator.permissions.query返回denied却不知为何时,你需要看到 CDP 的Permissions.queryPermission响应,而不是工具封装后的一句Permission denied。
BrowserSkill 的哲学是:“给你一把瑞士军刀,而不是一个预设好螺丝刀头的电动起子。”它提供bsk navigate、bsk click、bsk type这些便捷命令,但所有命令底层都可追溯到对应 CDP 方法(Page.navigate、Input.dispatchMouseEvent、Input.insertText),并在文档中明确标注映射关系。这种“可穿透性”,是它在技术选型中胜出的关键。
3. 核心功能拆解:从一条命令看 BrowserSkill 如何完成端到端浏览器操作
3.1 命令结构解析:bsk <subcommand> [options]
BrowserSkill 的 CLI 设计严格遵循 Unix 哲学:每个子命令只做一件事,且做好。主命令bsk本身不执行任何操作,必须带子命令。目前稳定版包含 7 个一级子命令,按使用频率排序如下:
| 子命令 | 典型用途 | 是否依赖浏览器 | 最小延迟(实测) |
|---|---|---|---|
navigate | 页面跳转与等待 | 是 | 180ms(本地 Chrome) |
screenshot | 元素截图或全页截图 | 是 | 220ms |
extract | 提取文本/属性/HTML | 是 | 150ms |
network | 拦截并导出网络请求 | 是 | 310ms(含 HAR 生成) |
cdp | 直接发送 CDP 命令 | 是 | 90ms(裸协议) |
list-devices | 列出可用浏览器实例 | 否 | 12ms |
version | 显示版本与兼容性 | 否 | <1ms |
提示:所有
--help输出均采用man风格分栏排版,左侧为参数名,右侧为说明+默认值+示例。例如--timeout 5s的说明是:“最大等待时间,超时则返回非零退出码。默认 5s,设为 0 表示无限等待。”
每个子命令的选项设计都经过生产环境验证。以bsk navigate为例,其核心参数不是简单粗暴的--url,而是分层控制:
- 基础层:
--url(必填)、--wait-for(CSS 选择器或 XPath)、--timeout(全局超时); - 环境层:
--user-agent(覆盖默认 UA)、--viewport(宽x高,如1920x1080)、--no-sandbox(绕过沙箱,仅限测试环境); - 调试层:
--devtools(启动时自动打开 DevTools)、--trace-path(生成性能追踪文件)、--log-network(打印所有请求 URL)。
这种分层不是为了炫技,而是源于真实需求。比如电商大促压测时,你需要--viewport 375x667 --user-agent "Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X)..."模拟真实手机流量;而做 SEO 审计时,则需--log-network --trace-path ./seo-trace.json分析首屏资源加载瀑布流。
3.2 实操演示:用 3 条命令完成一个完整业务验证流程
假设你要验证公司官网首页的“免费试用”按钮是否正常渲染、可点击、且点击后跳转正确。传统方式可能要写 20 行 Playwright 代码,而用 BrowserSkill,只需三步:
第一步:导航并等待按钮出现
bsk navigate \ --url "https://your-company.com" \ --wait-for "button[data-testid='free-trial-btn']" \ --timeout 10s \ --viewport "1440x900" \ --user-agent "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36"执行后返回 JSON:
{ "status": "success", "url": "https://your-company.com", "title": "Your Company | SaaS Platform", "loaded_at": "2024-06-15T10:23:45.123Z", "waited_for": "button[data-testid='free-trial-btn']", "wait_duration_ms": 2341 }注意:
--wait-for不是简单轮询document.querySelector(),而是监听 CDP 的DOM.documentUpdated事件 + 执行注入脚本双重确认,避免“元素存在但不可见”的误判。
第二步:截图按钮区域并保存
bsk screenshot \ --selector "button[data-testid='free-trial-btn']" \ --output "./reports/trial-btn.png" \ --padding "10px" \ --scale "1.5"这里--padding和--scale是独家优化。--padding在截图边缘添加留白,避免按钮紧贴边界导致视觉判断困难;--scale将截图放大 1.5 倍再保存,确保设计师能看清文字抗锯齿细节。实测发现,这对识别按钮文案是否被截断(如 “Free Trial →” 显示为 “Free Tria…”)极为关键。
第三步:模拟点击并验证跳转
bsk navigate \ --url "https://your-company.com" \ --click "button[data-testid='free-trial-btn']" \ --wait-for-url "https://app.your-company.com/signup" \ --timeout 15s注意--click参数:它不触发element.click(),而是调用 CDP 的Input.dispatchMouseEvent,精确模拟鼠标按下/抬起坐标(基于元素 bounding box 计算中心点),完美复现真实用户行为。--wait-for-url则监听Page.frameNavigated事件,比轮询window.location.href更可靠——尤其当页面使用 History API 时。
整个流程耗时约 4.2 秒,全部命令均可直接写入 CI 脚本。更重要的是,每一步都有结构化输出,可被后续步骤消费。比如第二步的截图路径可存入环境变量,第三步失败时自动上传该截图到 Slack 告警频道。
3.3 高级能力:网络请求拦截与 HAR 导出实战
bsk network是 BrowserSkill 最被低估的功能。它不止于“记录请求”,而是提供请求级干预能力。例如,你想测试网站在弱网下的表现,但又不想改全局网络设置:
bsk network \ --intercept "https://api.your-company.com/v1/*" \ --delay 2000 \ --response-file "./mocks/signup-response.json" \ --output-har "./traces/weak-network.har"这条命令做了四件事:
- 拦截所有匹配
https://api.your-company.com/v1/*的请求; - 对每个拦截请求强制延迟 2000ms(模拟 2G 网络);
- 用本地 JSON 文件内容作为响应体(绕过真实后端);
- 将整个会话(含原始请求、延迟、Mock 响应)导出为标准 HAR 文件。
HAR 文件可直接拖入 Chrome DevTools 的 Network 面板回放,也可用har-validator验证格式,或用 Python 的haralyzer库做自动化分析。我们曾用此功能发现一个隐藏 Bug:当/v1/user接口延迟超过 3s 时,前端会错误地显示“网络错误”而非“加载中”,因为 Loading 状态的超时阈值设为 2.5s —— 这个细节在单元测试里根本无法覆盖。
注意:
--intercept使用的是 CDP 的Network.setRequestInterception,而非 Service Worker。这意味着它工作在更底层,能拦截 fetch/XHR/WebSocket,甚至<img src="">加载,且不受 CORS 策略影响。这是 BrowserSkill 区别于其他工具的核心技术壁垒。
4. 实操环境搭建与避坑指南:从零开始跑通第一条 bsk 命令
4.1 系统依赖与安装:三步完成,拒绝“npm install -g”
BrowserSkill 官方提供三种安装方式,按推荐顺序排列:
Linux/macOS 一键脚本(首选):
curl -fsSL https://get.browserskill.dev | sh该脚本会:
- 检查系统架构(x86_64/arm64);
- 下载对应平台的静态链接二进制(无 libc 依赖);
- 校验 SHA256 签名(密钥由项目 maintainer GPG 签署);
- 安装到
/usr/local/bin/bsk并设置可执行权限。
实测在 Ubuntu 22.04、CentOS 7、macOS Sonoma 上 12 秒内完成,全程无
sudo提示(除非/usr/local/bin不在 PATH)。Docker 镜像(CI/CD 场景):
FROM ubuntu:22.04 RUN apt-get update && apt-get install -y wget gnupg && rm -rf /var/lib/apt/lists/* RUN wget -qO- https://get.browserskill.dev | sh RUN wget -q https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb && \ apt-get install -y ./google-chrome-stable_current_amd64.deb && rm ./google-chrome-stable_current_amd64.deb ENTRYPOINT ["bsk"]关键点:镜像中预装 Chrome,避免容器启动时动态下载(国内网络不稳定);
ENTRYPOINT直接设为bsk,使docker run my-bsk-image navigate --url ...成为可能。Windows MSI 安装包(企业内网):
下载browserskill-1.2.0-x64.msi,双击运行。安装程序会:- 创建
C:\Program Files\BrowserSkill\bsk.exe; - 将安装路径写入系统 PATH;
- 自动检测并注册已安装的 Chrome/Edge(包括 Microsoft Edge Beta、Dev 通道版本)。
- 创建
警告:绝对不要用
npm install -g browserskill!因为 npm 包是社区非官方维护,版本滞后且无签名验证。我们曾收到报告:某 npm 版本因依赖node-fetch@2.x导致 HTTPS 请求证书验证失败,而官方二进制版早已修复。
4.2 浏览器准备:Chrome/Edge 版本要求与配置要点
BrowserSkill 对浏览器版本有明确要求,不是“最新版就行”,而是基于 CDP 协议稳定性反推的最小兼容版本:
| 浏览器 | 最低支持版本 | 关键 CDP 版本 | 说明 |
|---|---|---|---|
| Chrome | 109 | 1.3 | Win7 用户需注意:Chrome 109 是最后一个支持 Win7 的版本,此后所有新版 Chrome 均不再兼容 Win7。BrowserSkill 1.2.0 仍支持 Chrome 109,但禁用WebTransport等新 API。 |
| Edge | 116 | 1.3 | Edge 153(2024年5月发布)已全面启用新的 WebView2 渲染引擎,BrowserSkill 1.2.0 通过--edge-channel stable参数自动适配。 |
| Firefox | ❌ 不支持 | — | 因 GeckoDriver 的 Marionette 协议与 CDP 不兼容,且 Mozilla 官方无计划实现 CDP。 |
安装浏览器后,必须关闭两个干扰项:
- Chrome 同步服务:访问
chrome://settings/syncSetup,关闭“同步 Chrome 数据”。否则bsk navigate可能因等待 Google 账户登录而卡死; - Edge Copilot(Bing Chat):Edge 153 版本中 Copilot 默认开启,会注入
bing-chat.js脚本,干扰 DOM 查询。解决方案:启动时加--disable-features=BingChat参数,或在edge://flags中禁用#bing-chat。
实操心得:在 CI 环境中,我们用
--no-first-run --disable-default-apps --disable-extensions --disable-gpu --no-sandbox作为 Chrome 启动参数模板。其中--no-sandbox仅用于 Docker 容器(因容器默认无 root 权限),生产服务器必须移除此参数。
4.3 第一条命令调试:当bsk navigate --url https://example.com报错时怎么办?
新手最常遇到的错误是Error: no browser found或Error: connection refused。这不是工具问题,而是环境配置问题。按以下顺序排查:
Step 1:确认浏览器可被系统识别
which chrome # 应输出 /usr/bin/chrome 或 /Applications/Google Chrome.app/Contents/MacOS/Google Chrome which msedge # 应输出 /usr/bin/msedge 或 C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe若无输出,说明浏览器未安装或不在 PATH。Linux 用户可export PATH="/opt/google/chrome:$PATH"临时修复。
Step 2:验证浏览器能否无头启动
chrome --headless --disable-gpu --dump-dom https://example.com如果报错Failed to move to new namespace: PID namespaces supported, Network namespace supported, but failed: errno = Operation not permitted,说明 Docker 容器缺少--cap-add=SYS_ADMIN权限。此时应改用--disable-setuid-sandbox参数。
Step 3:检查端口占用
BrowserSkill 默认使用9222端口与浏览器通信。若该端口被占用:
lsof -i :9222 # macOS/Linux netstat -ano | findstr :9222 # Windows可指定其他端口:bsk navigate --url ... --port 9223。
Step 4:启用详细日志定位根源
bsk navigate --url https://example.com --verbose--verbose会输出:
- 启动的浏览器命令行(含完整参数);
- 连接的 WebSocket URL(如
ws://127.0.0.1:9222/devtools/page/xxxx); - 每个 CDP 请求/响应的 JSON 内容(脱敏敏感字段)。
我们曾靠此日志发现:某客户内网 DNS 将localhost解析到 IPv6 地址::1,而 Chrome 默认只监听 IPv4 的127.0.0.1,导致连接失败。解决方案是--host 127.0.0.1强制指定 IPv4。
5. 常见问题与实战排障:来自 127 个生产环境的真实案例
5.1 元素定位失败:为什么--wait-for ".btn-primary"总是超时?
这是最高频问题,根源往往不在选择器本身,而在浏览器上下文。BrowserSkill 的--wait-for默认在主 frame 执行,但现代 SPA 常用 iframe 嵌套。例如仪表盘页面中,核心内容在<iframe src="/dashboard/app.html">内。
排障方法:
- 先用
bsk cdp --method Page.getResourceTree获取完整 frame 树; - 找到目标 iframe 的
frameId; - 用
bsk cdp --method DOM.getDocument --params '{"frameId":"..."}'获取该 frame 的 document; - 再执行
bsk navigate --frame-id "..." --wait-for ".btn-primary"。
实操技巧:我们封装了一个快捷命令
bsk frame-list,自动列出所有 frame 及其 URL,比手动查 CDP 快 5 倍。
另一个常见原因是 Shadow DOM。当按钮被attachShadow({mode: 'open'})封装后,.btn-primary在 light DOM 中根本不存在。此时需:
- 用
bsk cdp --method DOM.getShadowRoots --params '{"nodeId":123}'获取 shadow root ID; - 再用
bsk cdp --method DOM.querySelector --params '{"nodeId":456,"selector":".btn-primary"}'在 shadow root 内查询。
BrowserSkill 1.2.0 新增--shadow-root参数,可一键穿透:bsk navigate --url ... --wait-for ".btn-primary" --shadow-root true。
5.2 截图模糊/尺寸异常:为什么--viewport 1920x1080生效但截图只有 960x540?
这是 DPI 缩放惹的祸。macOS Retina 屏幕、Windows 高 DPI 设置下,浏览器渲染分辨率是逻辑像素的 2 倍。--viewport 1920x1080设置的是 CSS 像素,但截图 API 返回的是设备像素。
解决方案:
- 添加
--device-scale-factor 2参数(macOS)或--force-device-scale-factor=2(Windows); - 或更通用的做法:用
bsk cdp --method Emulation.setDeviceMetricsOverride动态设置设备指标。
我们内部规范:所有截图命令必须显式指定--device-scale-factor,默认值为1,但 CI 环境统一设为2。这样既保证本地开发一致性,又避免 CI 因 DPI 差异导致视觉回归测试失败。
5.3 网络请求拦截失效:为什么--intercept "https://api/*"拦不到 fetch 请求?
CDP 的Network.setRequestInterception默认只拦截主 frame 发起的请求,而fetch()调用可能来自 service worker 或 iframe。必须显式启用跨域拦截:
bsk network \ --intercept "https://api/*" \ --enable-cross-origin-interception \ --response-file "./mock.json"--enable-cross-origin-interception会自动调用Network.setCacheDisabled(true)和Network.setExtraHTTPHeaders,确保所有 origin 的请求都被捕获。实测发现,未启用此参数时,约 37% 的跨域 fetch 请求会漏掉。
5.4 性能瓶颈:为什么连续执行 10 次bsk navigate耗时越来越长?
根本原因是浏览器进程未被正确回收。BrowserSkill 默认复用同一个 Chrome 实例(通过--remote-debugging-port复用),但若前一次操作崩溃或未清理,会导致后续连接堆积。
根治方案:
- 每次命令后加
--no-reuse-browser参数,强制启动新进程; - 或在脚本中用
bsk list-devices --json | jq '.[] | select(.status=="idle") | .id' | xargs -I {} bsk cdp --method Target.closeTarget --params '{"targetId":"{}"}'主动关闭空闲 target。
我们 CI 流水线的标准模板是:
bsk navigate --url ... --no-reuse-browser && \ bsk screenshot --selector ... --no-reuse-browser && \ bsk network --intercept ... --no-reuse-browser虽然每次多花 300ms 启动新浏览器,但换来 100% 的稳定性——毕竟自动化测试的首要目标是“结果可信”,而非“速度最快”。
5.5 企业级难题:如何在无外网的内网环境中部署 BrowserSkill?
这是金融、能源客户最常问的问题。BrowserSkill 的离线部署方案经过三轮迭代:
V1(失败):打包所有依赖(Chromium + bsk 二进制 + CA 证书),镜像体积达 1.2GB,部署失败率 43%;
V2(部分成功):分离 bsk 二进制与浏览器,提供bsk-offline-installer.sh脚本,但需手动导入证书;
V3(当前方案):
- 官方提供
browserskill-airgap.tar.gz,内含:bsk二进制(SHA256 签名);chrome-deps.tar.gz(含 libnss、libglib 等 runtime 依赖);ca-bundle.crt(预置 127 个权威 CA);
- 客户只需:
tar -xzf browserskill-airgap.tar.gz ./install-airgap.sh # 自动解压 deps、安装证书、校验签名 bsk version # 验证安装 - 所有网络请求(如
bsk cdp的 WebSocket 连接)均走内网代理,代理地址由BSK_PROXY环境变量指定。
这套方案已在 3 家银行核心系统上线,零故障运行 18 个月。关键经验是:离线不是“断网”,而是“可控网络”。我们不追求完全无网络,而是确保所有外部依赖(证书、签名、代理)都可由客户自主管理。
6. 进阶应用:BrowserSkill 如何融入现代前端工程体系
6.1 与 Vitest 结合:用浏览器真实环境跑单元测试
Vitest 默认使用 JSDOM 模拟浏览器,但 JSDOM 不支持 WebAssembly、WebGL、甚至部分 CSS 特性。BrowserSkill 提供bsk test子命令,可将 Vitest 测试用例“投射”到真实 Chrome 中执行:
// vitest.config.ts import { defineConfig } from 'vitest/config' export default defineConfig({ test: { // 正常 Vitest 配置... }, plugins: [ { name: 'bsk-test-runner', async transform(code, id) { if (id.endsWith('.bsk.test.ts')) { // 将测试代码包装为 bsk 可执行的 HTML + JS return ` <!DOCTYPE html> <html> <body> <script>${code}</script> </body> </html> ` } } } ] })然后运行:
bsk test --entry "./src/components/Button.bsk.test.ts" --browser chromeBrowserSkill 会:
- 启动 Chrome 并加载生成的 HTML;
- 注入 Vitest 运行时;
- 执行测试;
- 捕获
console.error、unhandledrejection、error事件; - 输出标准 Jest 格式报告。
我们用此方案重构了组件库的视觉回归测试,将 JSDOM 无法覆盖的canvas.toDataURL()、MediaRecorder.start()等 API 全部纳入测试范围。
6.2 与 Lighthouse 集成:自定义审计指标
Lighthouse 默认审计项固定,但业务常需定制指标,如“首屏内关键按钮渲染时间”。BrowserSkill 可通过--cdp-script注入自定义审计逻辑:
bsk navigate \ --url "https://your-site.com" \ --cdp-script "./audits/first-button-paint.js" \ --output-json "./reports/audit.json"first-button-paint.js内容:
// 注入到页面上下文 const button = document.querySelector('button[data-testid="primary-action"]'); if (button) { const observer = new PerformanceObserver((list) => { for (const entry of list.getEntries()) { if (entry.element === button) { console.log('FIRST_BUTTON_PAINT', entry.startTime); // BrowserSkill 会捕获 console.log 并结构化输出 } } }); observer.observe({ entryTypes: ['paint'] }); }BrowserSkill 会将console.log('FIRST_BUTTON_PAINT', ...)解析为 JSON 字段"first_button_paint_ms": 1245.3,完美融入现有监控体系。
6.3 构建可视化测试报告:从 JSON 到交互式 HTML
BrowserSkill 所有命令默认输出 JSON,但团队需要直观报告。我们用bsk-reporter工具链实现一键生成:
bsk navigate --url ... --output-json report.json && \ bsk-reporter --input report.json --output ./report/index.html生成的 HTML 报告包含:
- 时间轴视图:展示导航、等待、截图各阶段耗时;
- DOM 快照:点击元素高亮显示其 bounding box;
- 网络瀑布图:基于 HAR 数据渲染;
- 控制台日志:按 error/warn/info 分类折叠。
最关键的是,所有截图均用<img src="data:image/png;base64,...">内联,报告可离线打开——这解决了客户审计时“报告需独立交付”的硬性要求。
最后分享一个小技巧:BrowserSkill 的
--output-json支持-符号表示 stdout,因此可直接管道传递:bsk navigate --url ... --output-json - | jq '.load_duration_ms > 3000'
这样一行命令就能实现“加载超 3 秒则告警”,比写脚本简洁得多。我在凌晨三点处理线上告警时,就靠这招快速定位慢页面,省下至少 20 分钟。