
Sanity 仓库实战playwright-cli 浏览器自动化命令行完全指南【免费下载链接】sanitySanity Studio – Rapidly configure content workspaces powered by structured content项目地址: https://gitcode.com/GitHub_Trending/sa/sanity本篇技术指南以 .agents/skills/playwright-cli/SKILL.md 为核心主体系统讲解 playwright-cli 这一面向 AI Agent 与开发者的浏览器自动化 CLI 工具从打开浏览器、定位元素、填表提交到会话隔离、存储状态复用、网络请求 Mock、Trace 与视频录制、测试代码生成等完整能力。本仓库的 e2e 目录正是基于 Playwright 构建的 Sanity Studio 端到端测试体系见 e2e/package.json 中的playwright test脚本与playwright/test依赖读完本文你将掌握一套可复制、可运行的浏览器自动化命令集并能将其沉淀为真实的 Playwright 测试代码。快速上手五条命令完成一次浏览器交互playwright-cli 的核心设计是每次命令执行后返回当前页面的结构化快照Snapshot后续操作通过快照中的 ref 编号精准定位元素从而避免脆弱的 CSS 选择器。# 打开新浏览器 playwright-cli open # 导航到页面 playwright-cli goto https://playwright.dev # 使用快照中的 ref 与页面交互 playwright-cli click e15 playwright-cli type page.click playwright-cli press Enter # 截图很少用快照更常用 playwright-cli screenshot # 关闭浏览器 playwright-cli close若环境中没有全局安装的playwright-cli可执行文件可用npx playwright-cli以本地方式运行相同命令例如npx playwright-cli open https://example.com npx playwright-cli click e1命令全景核心交互命令参考打开与导航playwright-cli open # 打开新浏览器 playwright-cli open https://example.com/ # 打开并立即导航 playwright-cli goto https://playwright.dev # 跳转页面 playwright-cli go-back # 后退 playwright-cli go-forward # 前进 playwright-cli reload # 刷新表单与页面交互playwright-cli type search query # 键入文本 playwright-cli click e3 # 单击 playwright-cli dblclick e7 # 双击 playwright-cli fill e5 userexample.com # 填充输入框 playwright-cli drag e2 e8 # 拖拽源 ref → 目标 ref playwright-cli hover e4 # 悬停 playwright-cli select e9 option-value # 下拉框选择 playwright-cli upload ./document.pdf # 文件上传 playwright-cli check e12 # 勾选复选框 playwright-cli uncheck e12 # 取消勾选 playwright-cli eval document.title # 在页面执行表达式 playwright-cli eval el el.textContent e5 # 针对指定元素求值 playwright-cli dialog-accept # 接受弹窗 playwright-cli dialog-accept confirmation text # 接受弹窗并输入文本 playwright-cli dialog-dismiss # 关闭弹窗 playwright-cli resize 1920 1080 # 调整视口尺寸 playwright-cli close # 关闭浏览器键盘与鼠标playwright-cli press Enter # 按键 playwright-cli press ArrowDown playwright-cli keydown Shift # 按下并保持 playwright-cli keyup Shift # 释放按键 playwright-cli mousemove 150 300 playwright-cli mousedown # 默认左键right 为右键 playwright-cli mousedown right playwright-cli mouseup playwright-cli mouseup right playwright-cli mousewheel 0 100 # 滚动deltaX deltaY多标签页playwright-cli tab-list # 列出所有标签页 playwright-cli tab-new # 新建空白标签页 playwright-cli tab-new https://example.com/page # 新建并导航 playwright-cli tab-close # 关闭当前标签页 playwright-cli tab-close 2 # 关闭指定索引标签页 playwright-cli tab-select 0 # 切换到指定标签页截图与 PDFplaywright-cli screenshot # 全页截图 playwright-cli screenshot e5 # 截取指定元素 playwright-cli screenshot --filenamepage.png # 指定文件名 playwright-cli pdf --filenamepage.pdf # 导出为 PDF快照机制基于 ref 的元素定位每一条命令执行后playwright-cli 都会输出当前浏览器状态的结构化快照这是整个工具工作流的核心 playwright-cli goto https://example.com ### Page - Page URL: https://example.com/ - Page Title: Example Domain ### Snapshot Snapshot快照被保存为 YAML 文件默认存放在.playwright-cli/目录按时间戳自动命名其中每个可交互元素都带有e1、e2这样的 ref 编号。后续的click、fill、hover等命令都直接引用这些 ref无需手写任何选择器。也可以随时通过playwright-cli snapshot主动获取快照playwright-cli snapshot playwright-cli snapshot --filenameafter-click.yaml--filename参数的使用原则是默认使用时间戳自动命名当快照/产物属于工作流结果的一部分、需要被后续步骤引用时才用--filename指定固定名称。浏览器会话管理多会话隔离与持久化命名会话通过-s参数可以为每个浏览器会话命名不同会话之间的 Cookies、LocalStorage/SessionStorage、IndexedDB、缓存、浏览历史与打开的标签页完全隔离# 会话1认证流程 playwright-cli -sauth open https://app.example.com/login # 会话2公共浏览独立的 cookie 与存储 playwright-cli -spublic open https://example.com # 命令按会话隔离 playwright-cli -sauth fill e1 userexample.com playwright-cli -spublic snapshot会话管理命令playwright-cli list # 列出所有浏览器会话 playwright-cli close # 关闭默认会话 playwright-cli -smysession close # 关闭命名会话 playwright-cli close-all # 关闭全部会话 playwright-cli kill-all # 强制杀掉全部后台进程清理僵尸进程 playwright-cli delete-data # 删除默认会话用户数据 playwright-cli -smysession delete-data # 删除命名会话的用户数据profile 目录默认会话与环境变量省略-s时所有命令作用于同一个默认会话playwright-cli open https://example.com playwright-cli snapshot playwright-cli close # 关闭默认浏览器也可以设置环境变量指定默认会话名export PLAYWRIGHT_CLI_SESSIONmysession playwright-cli open example.com # 自动使用 mysession持久化 Profile默认情况下浏览器 profile 仅保存在内存中需要持久化时使用--persistent或--profile参数playwright-cli open https://example.com --persistent # 自动生成位置 playwright-cli open https://example.com --profile/path/to/profile # 自定义目录实战模式一并发抓取多个站点# 同时启动三个站点会话 playwright-cli -ssite1 open https://site1.com playwright-cli -ssite2 open https://site2.com playwright-cli -ssite3 open https://site3.com wait # 分别获取快照 playwright-cli -ssite1 snapshot playwright-cli -ssite2 snapshot playwright-cli -ssite3 snapshot # 统一清理 playwright-cli close-all实战模式二A/B 测试对比playwright-cli -svariant-a open https://app.com?varianta playwright-cli -svariant-b open https://app.com?variantb playwright-cli -svariant-a screenshot playwright-cli -svariant-b screenshot最佳实践包括语义化命名会话如github-auth、docs-scrape避免s1这类通用名、用后即清理close/close-all卡死时用kill-all、及时删除过期数据delete-data释放磁盘空间。存储管理Cookies、localStorage 与 sessionStorage存储状态的保存与恢复# 保存到自动命名文件 storage-state-{timestamp}.json playwright-cli state-save # 保存到指定文件 playwright-cli state-save my-auth-state.json # 恢复存储状态 playwright-cli state-load my-auth-state.json # 重新打开页面以应用 cookies playwright-cli open https://example.com保存的存储状态文件格式与 Playwright 的storageState完全一致{ cookies: [ { name: session_id, value: abc123, domain: example.com, path: /, expires: 1735689600, httpOnly: true, secure: true, sameSite: Lax } ], origins: [ { origin: https://example.com, localStorage: [ {name: theme, value: dark}, {name: user_id, value: 12345} ] } ] }Cookies 操作playwright-cli cookie-list # 列出全部 playwright-cli cookie-list --domainexample.com # 按域名过滤 playwright-cli cookie-list --path/api # 按路径过滤 playwright-cli cookie-get session_id # 获取单个 playwright-cli cookie-set session abc123 # 设置基础 cookie playwright-cli cookie-set session abc123 --domainexample.com --path/ --httpOnly --secure --sameSiteLax playwright-cli cookie-set remember_me token123 --expires1735689600 # Unix 时间戳过期 playwright-cli cookie-delete session_id # 删除单个 playwright-cli cookie-clear # 清空全部localStorage 操作playwright-cli localstorage-list playwright-cli localstorage-get theme playwright-cli localstorage-set theme dark playwright-cli localstorage-set user_settings {theme:dark,language:en} # JSON 值 playwright-cli localstorage-delete theme playwright-cli localstorage-clearsessionStorage 操作playwright-cli sessionstorage-list playwright-cli sessionstorage-get form_data playwright-cli sessionstorage-set step 3 playwright-cli sessionstorage-delete step playwright-cli sessionstorage-clear复杂场景多 Cookie / 多 Key 操作一次性写入多个 Cookie 或多个 localStorage 键值可借助run-code直接调用 Playwright APIplaywright-cli run-code async page { await page.context().addCookies([ { name: session_id, value: sess_abc123, domain: example.com, path: /, httpOnly: true }, { name: preferences, value: JSON.stringify({ theme: dark }), domain: example.com, path: / } ]); } playwright-cli run-code async page { await page.evaluate(() { localStorage.setItem(token, jwt_abc123); localStorage.setItem(user_id, 12345); localStorage.setItem(expires_at, Date.now() 3600000); }); }IndexedDB 的枚举与删除同样通过run-code完成indexedDB.databases()/indexedDB.deleteDatabase(myDatabase)。认证状态复用经典模式# 第一步登录并保存状态 playwright-cli open https://app.example.com/login playwright-cli snapshot playwright-cli fill e1 userexample.com playwright-cli fill e2 password123 playwright-cli click e3 playwright-cli state-save auth.json # 第二步恢复状态跳过登录直达业务页 playwright-cli state-load auth.json playwright-cli open https://app.example.com/dashboard # 已自动登录安全注意事项切勿将含认证令牌的存储状态文件提交到版本库将*.auth-state.json加入.gitignore自动化结束后删除状态文件敏感数据使用环境变量传递默认的内存模式in-memory session对敏感操作更安全。网络请求拦截与 MockCLI 路由命令# 自定义状态码 Mock playwright-cli route **/*.jpg --status404 # JSON 响应体 Mock playwright-cli route **/api/users --body[{id:1,name:Alice}] --content-typeapplication/json # 自定义响应头 playwright-cli route **/api/data --body{ok:true} --headerX-Custom: value # 移除请求头如 cookie、authorization playwright-cli route **/* --remove-headercookie,authorization # 列出当前生效的路由 playwright-cli route-list # 移除单条路由或全部路由 playwright-cli unroute **/*.jpg playwright-cli unrouteURL 模式语法**/api/users - 精确路径匹配 **/api/*/details - 路径通配 **/*.{png,jpg,jpeg} - 按扩展名匹配 **/search?q* - 匹配查询参数高级 Mockrun-code 实现条件响应、响应改写、网络故障与延迟# 条件响应根据请求体分发 playwright-cli run-code async page { await page.route(**/api/login, route { const body route.request().postDataJSON(); if (body.username admin) { route.fulfill({ body: JSON.stringify({ token: mock-token }) }); } else { route.fulfill({ status: 401, body: JSON.stringify({ error: Invalid }) }); } }); } # 改写真实响应 playwright-cli run-code async page { await page.route(**/api/user, async route { const response await route.fetch(); const json await response.json(); json.isPremium true; await route.fulfill({ response, json }); }); } # 模拟网络故障 playwright-cli run-code async page { await page.route(**/api/offline, route route.abort(internetdisconnected)); } # abort 可选原因connectionrefused、timedout、connectionreset、internetdisconnected # 延迟响应模拟慢接口 playwright-cli run-code async page { await page.route(**/api/slow, async route { await new Promise(r setTimeout(r, 3000)); route.fulfill({ body: JSON.stringify({ data: loaded }) }); }); }完整的路由用法参见 .agents/skills/playwright-cli/references/request-mocking.md。高级能力run-code 执行任意 Playwright 代码当 CLI 命令无法覆盖某些场景时run-code允许直接注入一段接收page对象的异步函数内部可访问page.context()做浏览器上下文级操作。语法playwright-cli run-code async page { // 此处编写任意 Playwright 代码 }地理位置与权限# 授予定位权限并设置位置旧金山 playwright-cli run-code async page { await page.context().grantPermissions([geolocation]); await page.context().setGeolocation({ latitude: 37.7749, longitude: -122.4194 }); } # 设置伦敦位置 playwright-cli run-code async page { await page.context().grantPermissions([geolocation]); await page.context().setGeolocation({ latitude: 51.5074, longitude: -0.1278 }); } # 清除权限 playwright-cli run-code async page { await page.context().clearPermissions(); }# 批量授予权限 playwright-cli run-code async page { await page.context().grantPermissions([ geolocation, notifications, camera, microphone ]); } # 针对特定源授予权限 playwright-cli run-code async page { await page.context().grantPermissions([clipboard-read], { origin: https://example.com }); }媒体模拟# 深色 / 浅色配色 playwright-cli run-code async page { await page.emulateMedia({ colorScheme: dark }); } playwright-cli run-code async page { await page.emulateMedia({ colorScheme: light }); } # 减弱动效 playwright-cli run-code async page { await page.emulateMedia({ reducedMotion: reduce }); } # 打印媒体类型 playwright-cli run-code async page { await page.emulateMedia({ media: print }); }等待策略# 等待网络空闲 playwright-cli run-code async page { await page.waitForLoadState(networkidle); } # 等待元素隐藏 playwright-cli run-code async page { await page.waitForSelector(.loading, { state: hidden }); } # 等待函数返回 true playwright-cli run-code async page { await page.waitForFunction(() window.appReady true); } # 带超时等待 playwright-cli run-code async page { await page.waitForSelector(.result, { timeout: 10000 }); }iframe、下载与剪贴板# 操作 iframe 内部元素 playwright-cli run-code async page { const frame page.locator(iframe#my-iframe).contentFrame(); await frame.locator(button).click(); } # 列出全部 frame 的 URL playwright-cli run-code async page { const frames page.frames(); return frames.map(f f.url()); } # 处理文件下载 playwright-cli run-code async page { const [download] await Promise.all([ page.waitForEvent(download), page.click(a.download-link) ]); await download.saveAs(./downloaded-file.pdf); return download.suggestedFilename(); } # 读取 / 写入剪贴板需 clipboard-read 权限 playwright-cli run-code async page { await page.context().grantPermissions([clipboard-read]); return await page.evaluate(() navigator.clipboard.readText()); } playwright-cli run-code async page { await page.evaluate(text navigator.clipboard.writeText(text), Hello clipboard!); }页面信息与 JavaScript 执行playwright-cli run-code async page { return await page.title(); } playwright-cli run-code async page { return page.url(); } playwright-cli run-code async page { return await page.content(); } playwright-cli run-code async page { return page.viewportSize(); } # 在页面内执行并返回结构化结果 playwright-cli run-code async page { return await page.evaluate(() { return { userAgent: navigator.userAgent, language: navigator.language, cookiesEnabled: navigator.cookieEnabled }; }); } # 向 evaluate 传参 playwright-cli run-code async page { const multiplier 5; return await page.evaluate(m document.querySelectorAll(li).length * m, multiplier); }错误处理与复杂工作流# try-catch 容错 playwright-cli run-code async page { try { await page.click(.maybe-missing, { timeout: 1000 }); return clicked; } catch (e) { return element not found; } } # 登录并保存状态 playwright-cli run-code async page { await page.goto(https://example.com/login); await page.fill(input[nameemail], userexample.com); await page.fill(input[namepassword], secret); await page.click(button[typesubmit]); await page.waitForURL(**/dashboard); await page.context().storageState({ path: auth.json }); return Login successful; } # 多页面数据抓取 playwright-cli run-code async page { const results []; for (let i 1; i 3; i) { await page.goto(\https://example.com/page/\${i}\); const items await page.locator(.item).allTextContents(); results.push(...items); } return results; }更多场景地理位置、权限、媒体模拟、等待、iframe、下载、剪贴板、页面信息、JS 执行、错误处理、复杂工作流见 .agents/skills/playwright-cli/references/running-code.md。调试与证据采集DevTools、Tracing 与视频录制DevTools 命令playwright-cli console # 查看控制台日志 playwright-cli console warning # 按级别过滤warning 等 playwright-cli network # 查看网络请求 # 示例授权定位权限 playwright-cli run-code async page await page.context().grantPermissions([geolocation])Tracing 追踪启动追踪后Playwright 会在traces/目录生成三类产物文件内容trace-{timestamp}.trace动作日志每次点击/填写/导航、动作前后 DOM 快照、分步截图、时间信息、控制台消息、源码位置trace-{timestamp}.network网络日志所有 HTTP 请求/响应、请求与响应头与体、各阶段耗时DNS、连接、TLS、TTFB、下载、资源大小、失败请求resources/资源缓存图片、字体、样式、脚本、响应体等用于重放页面状态# 开始追踪 playwright-cli tracing-start # 执行动作 playwright-cli open https://example.com playwright-cli click e1 playwright-cli fill e2 test # 停止追踪 playwright-cli tracing-stopTrace 捕获内容的完整清单类别详情动作点击、填写、悬停、键盘输入、导航DOM每个动作前后的完整 DOM 快照截图每个步骤的视觉状态网络全部请求、响应、头、体与耗时控制台全部 console.log / warn / error时间每个操作的精确耗时典型用途排查失败点击查看动作尝试时的 DOM 状态、性能分析网络瀑布流定位慢资源、沉淀文档证据完整录制 checkout 流程。清理旧 Trace 可用find .playwright-cli/traces -mtime 7 -delete。注意 Trace 会给自动化带来额外开销并占用磁盘空间且部分动态内容无法完美重放。视频录制# 开始录制 playwright-cli video-start # 执行动作 playwright-cli open https://example.com playwright-cli snapshot playwright-cli click e1 playwright-cli fill e2 test input # 停止并保存为 WebMVP8/VP9 编码 playwright-cli video-stop demo.webm建议使用带上下文的描述性文件名playwright-cli video-stop recordings/login-flow-2024-01-15.webm。Trace vs Video vs Screenshot 选型特性Trace视频截图格式.trace 文件.webm 视频.png/.jpeg 图片DOM 检视支持不支持不支持网络详情支持不支持不支持回放方式逐步回放连续播放单帧文件体积中等大小适用场景调试演示快速捕获参考文档.agents/skills/playwright-cli/references/tracing.md、.agents/skills/playwright-cli/references/video-recording.md。测试代码生成从交互到 Playwright 测试playwright-cli 的每个动作都会同步生成对应的 Playwright TypeScript 代码并显示在输出中可直接复制进测试文件# 开始会话 playwright-cli open https://example.com/login # 获取快照输出类似e1 [textbox Email], e2 [textbox Password], e3 [button Sign In] playwright-cli snapshot # 填充表单——自动生成代码 playwright-cli fill e1 userexample.com # Ran Playwright code: # await page.getByRole(textbox, { name: Email }).fill(userexample.com); playwright-cli fill e2 password123 # await page.getByRole(textbox, { name: Password }).fill(password123); playwright-cli click e3 # await page.getByRole(button, { name: Sign In }).click();将生成的代码收集成完整的 Playwright 测试import {test, expect} from playwright/test test(login flow, async ({page}) { // Generated code from playwright-cli session: await page.goto(https://example.com/login) await page.getByRole(textbox, {name: Email}).fill(userexample.com) await page.getByRole(textbox, {name: Password}).fill(password123) await page.getByRole(button, {name: Sign In}).click() // Add assertions await expect(page).toHaveURL(/.*dashboard/) })最佳实践优先语义化定位器生成代码尽量使用基于 role 的定位器如getByRole(button, {name: Submit})远比#submit-btn这类 CSS 选择器健壮。本仓库的 E2E 测试也遵循同一原则例如 e2e/tests/auth/cookieAuth.spec.ts 在无法使用getByRole(heading)时改用># 指定浏览器内核 playwright-cli open --browserchrome playwright-cli open --browserfirefox playwright-cli open --browserwebkit playwright-cli open --browsermsedge # 通过浏览器扩展连接 playwright-cli open --extension # 持久化 profile默认内存中 playwright-cli open --persistent # 自定义 profile 目录 playwright-cli open --profile/path/to/profile # 使用配置文件启动 playwright-cli open --configmy-config.json # 有头模式 playwright-cli open https://example.com --headed # 关闭浏览器 / 删除默认会话用户数据 playwright-cli close playwright-cli delete-data注意--profile仅在用户明确指定时才使用默认场景优先使用--persistent的自动生成位置。综合实战三个开箱即用的完整流程表单提交流程playwright-cli open https://example.com/form playwright-cli snapshot playwright-cli fill e1 userexample.com playwright-cli fill e2 password123 playwright-cli click e3 playwright-cli snapshot playwright-cli close多标签页工作流playwright-cli open https://example.com playwright-cli tab-new https://example.com/other playwright-cli tab-list playwright-cli tab-select 0 playwright-cli snapshot playwright-cli close结合 DevTools 与 Trace 的调试流程# 方式一console network 检查 playwright-cli open https://example.com playwright-cli click e4 playwright-cli fill e7 test playwright-cli console playwright-cli network playwright-cli close # 方式二完整 Trace 追踪 playwright-cli open https://example.com playwright-cli tracing-start playwright-cli click e4 playwright-cli fill e7 test playwright-cli tracing-stop playwright-cli close总结playwright-cli 把 Playwright 的能力封装成一组可组合的 CLI 原语快照 ref 定位解决了选择器脆弱问题命名会话与持久化 profile提供了多身份隔离与状态复用route 系列命令实现了零代码的网络 Mockrun-code保留了任意 Playwright API 的逃生通道而Trace、视频与测试代码生成则让每次自动化既可用于调试取证也能直接沉淀为仓库中的正式测试用例——这正是 Sanity 仓库 e2e 体系所依赖的同一套 Playwright 生态。深入研读本文引用的 7 份参考文档即可把命令级用法进阶到完整的工作流编排。延伸阅读.agents/skills/playwright-cli/references/request-mocking.md —— 请求拦截、Mock、改写与故障注入.agents/skills/playwright-cli/references/running-code.md —— 任意 Playwright 代码执行.agents/skills/playwright-cli/references/session-management.md —— 多会话管理与持久化.agents/skills/playwright-cli/references/storage-state.md —— 存储状态cookies、localStorage 等.agents/skills/playwright-cli/references/test-generation.md —— 测试代码生成.agents/skills/playwright-cli/references/tracing.md —— Trace 追踪.agents/skills/playwright-cli/references/video-recording.md —— 视频录制【免费下载链接】sanitySanity Studio – Rapidly configure content workspaces powered by structured content项目地址: https://gitcode.com/GitHub_Trending/sa/sanity创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考