点击走流程
一个真实浏览器在你的应用上打开。登录、导航、填表 —— 正常使用即可。
LLM 只规划一次浏览器操作。从第二次运行起,Windup 进行确定性回放,零 LLM 调用 —— ~1 秒、$0,结果稳定。你编辑的是测试场景,而非选择器。
问题所在
手写的 E2E 测试,每当选择器变动就会失效。在每次运行时都驱动浏览器的 AI 智能体则速度慢、不确定,且每个测试都要向你收取一次 LLM 调用的费用。
Windup 只规划一次,并缓存一份经过验证的操作计划。回放是确定性的,且免费。当应用发生变化、某个检查失败时,计划会失效并自动重新规划 —— 自愈,而不是悄无声息地出错。
工作原理
最昂贵的部分 —— 弄清楚该执行哪些浏览器操作 —— 只发生一次,并被转化为缓存的、可验证的数据。
一个自然语言任务交给规划器。LLM 产出一份 JSON 操作计划 —— 一次调用,仅在首次运行时发生。
一个确定性执行器(Playwright)运行该计划。廉价的 DOM/URL 后置条件验证每个操作 —— 无需 LLM。
轨迹被缓存下来。之后每次运行都回放它:零 LLM 调用、~1 秒、$0,每次都是同一份计划。
1.0 新功能
与其描述,不如演示?windup record 在你的应用上打开一个有头浏览器 —— 你点击走一遍流程,标记要验证什么,然后完成。Windup 会写出场景并缓存录制的计划,于是它以 $0、无 LLM 的方式确定性回放。
npx windup record --url http://localhost:3000 一个真实浏览器在你的应用上打开。登录、导航、填表 —— 正常使用即可。
底部有一个浮动工具栏:点 ◉ 然后点击测试应检查的元素 —— 或者什么都不标记以验证最终 URL。
“■ finalizar”(或 Ctrl-C)。Windup 写出场景 + 缓存计划。windup run <id> 以 $0 回放。
为什么选 Windup
手写脚本运行成本低,但维护成本高。每次运行都调用 AI 的智能体易于编写,但速度慢且不确定。Windup 各取二者之长。
| 维度 | 手写脚本 | 每次运行都用 AI 智能体 | Windup |
|---|---|---|---|
| 编写方式 | 代码 + 选择器 | 自然语言 | 自然语言 |
| 每次运行成本 | $0 | 每次运行都调用 LLM | 仅首次运行调用 LLM |
| 速度 | 快 | 慢(模型在回路中) | ~1s 回放 |
| 确定性 | 高 | 低(每次即兴发挥) | 高(每次都是同一份计划) |
| 应用发生变化 | 你去修脚本 | 可能悄无声息地做错事 | 检查失败 → 重新规划 |
功能特性
一款为真实项目打造的 CLI:以文字或演示编写、依赖关系、动态值(OTP/magic-link)、设备模拟、性能预算、请求打桩、运行时健康门禁、CI 护栏、只读诊断与报告器 —— QA 工作流所需的一切。
用纯自然语言描述测试。无需选择器、无需页面对象、无需维护测试代码。
从第二次运行起:llm_calls=0、~1s、$0。缓存的计划每次都以相同方式运行。
Playwright 配合可信输入事件(isTrusted)—— 可靠的点击、输入与导航。
每个操作都检查 DOM/URL 后置条件,回路中没有 LLM。可断言可见文本、元素数量、某个属性,或某物已消失 —— 而不只是“某个选择器存在”。
通过演示编写:驱动一个有头浏览器,用工具栏标记验证,完成 —— Windup 写出场景并缓存录制的计划($0 回放)。输入的密码绝不会进入计划。
来自运行 ledger 的只读诊断,无 LLM:每场景的通过率历史、某场景为何重新规划、两次运行之间有何变化。还有 windup badge 生成状态 SVG。
用 --device "iPhone 14" 在某个 Playwright 设备预设下运行场景 —— 视口、UA、移动/触摸。缓存计划按设备分键,因此移动端和桌面端是独立轨迹。
用 --web-vitals 捕获最终页面的 TTFB / FCP / LCP / CLS,并在超过 config.budgets 时让运行失败 —— 一个搭在你已有运行之上的性能门禁。
为请求打桩(一个 500、一个空列表、一次掉线的调用)并冻结时钟/时区 —— 每次运行都应用,绝不缓存。把它们限定到单个场景,于是一个错误状态测试不会泄漏到每次运行。
让在运行期间记录了 console 错误或收到静默 5xx 的场景失败(--fail-on-console / --fail-on-5xx)—— 页面底下坏了,运行就不能“通过”。
重试 flake(--retries)、限制套件挂钟(--max-wall)、首次失败即停(--bail),或隔离一个已知 flaky 的场景让它照常报告而不使构建失败 —— 暴露,而非隐藏。
针对尚无测试的路由,LLM 为每条未覆盖路由起草一个场景 —— 闭合 scan → 覆盖 → 编写 的循环。
LLM 辅助编写:根据你应用的真实页面(站点地图)+ 项目清单生成场景。--validate 会运行并不断改进,直到通过。
直接从源代码(Next.js、react-router)索引你的路由和元素。
凭据从不进入场景、缓存、LLM 提示词或 git —— 只有 ENV:* 引用,在运行时解析。
场景之间的依赖关系(例如「创建发票」依赖「登录」)—— 同一会话,按依赖分别缓存。
AI 撰写一份运行后的复盘,引用真实观察到的值 —— 价格、消息、确认信息。
失败时,AI 阅读真实页面并对你的场景提出修复建议。
JUnit、JSON 和自包含的 HTML(--reporter)。任何失败都会返回非零退出码。
Google Gemini 和 OpenAI,可按次运行选择(--llm openai:gpt-5-mini)。windup costs 按提供商和模型追踪花费。
在一个共享的热浏览器上以隔离的上下文并行运行多个场景 —— 混合套件下约快 2 倍,在有规划或长流程时更快。默认串行。
用 --browser 在 Chromium(默认)、Firefox 或 WebKit 上运行相同的场景。同一份计划可在三者上回放 —— 编写一次,处处运行。
在运行时从作者声明的来源(cmd/http/fn)获取动态值(OTP 验证码、magic-link)。计划通过 value_ref/url_ref 使用它们 —— 解锁无密码登录。resolveFields 以确定性方式绑定字段。
依赖的认证状态(storageState)只捕获一次,并在缓存回放时恢复 —— 因此不必为每个依赖方重新运行登录流程(deps≈0)。套件的重大提速。
在计划运行前注入 localStorage/sessionStorage —— 无需服务器往返即可直达购物车或某个 POS 设备状态。确定且对 CI 安全。
安全禁用名单:针对被禁止的选择器或 URL 的运行会中止 —— 防止更改测试密码、删除数据或持久化配置的 CI 护栏。
将 windup scan 索引的路由与你的场景交叉比对,列出还没有测试的路由 —— 自动发现覆盖缺口。
CI 前的预检:LLM 密钥、浏览器、场景可解析、无孤立片段、站点地图已扫描 —— 优先捕捉常见的「到 CI 就会挂」问题。
对每个场景的最终页面进行一次免费的可访问性审计(axe-core)—— 报告违规项。仅供参考;从不导致运行失败。
将套件以轮询方式拆分到并行的 CI 运行器上(--shard 1/4、2/4、……),每一份都是独立的作业。
增量 CI:只运行受代码或场景改动影响的场景(git diff → 路由归因),并带有安全的全量回退 —— 绝不出现无声的假绿。
按路由 glob 复用的抗抖动就绪判定:在操作前等待应用就绪,只定义一次,而不是在每个场景中作为 hint 重复。
示例
你只写意图。首次运行进行规划并支付几分之一美分;此后每次运行都是缓存回放,成本 $0。
{
"scenario_id": "checkout",
"task": "Log in as the qa account, add 'Backpack' to the cart, check out and verify the order confirmation message appears."
} $ windup run checkout # 1st run
PASS checkout cache=miss llm_calls=1 total=3003ms cost=$0.0024
$ windup run checkout # again — deterministic replay
PASS checkout cache=hit llm_calls=0 total=671ms cost=$0 cache=hit · llm_calls=0 · $0 —— 第二次运行从不触及模型。
CI / CD
在一个热浏览器中运行整个套件,任何场景失败都让构建失败,并产出机器可读或人类可读的报告。
$ npx windup run --all --reporter junit \
--report-file reports/windup.xml 可靠性
回放是实测出来的,不是承诺出来的。缓存的计划每次运行都产出相同结果 —— 没有模型,没有抖动。
快速开始
Node ≥ 20,以及一个 API 密钥 —— GOOGLE_GENERATIVE_AI_API_KEY(Google,默认)或 OPENAI_API_KEY(OpenAI)—— 放在 .env.local 中。密钥仅用于规划;缓存回放从不调用 LLM。
$ npm i -D windupjs $ npx windup init $ npx windup scan $ npx windup new "log in and create an invoice for ACME" $ npx windup run checkout 面向 AI 时代
如今没人再读文档了 —— 大家都把它交给助手。所以 Windup 附带了一份 llms.txt:整套文档,为机器结构化组织。把你的编码智能体指向它,用纯自然语言描述流程,它就会为你编写并运行场景。
将此 URL 粘贴到你的助手(Claude、Cursor、Copilot……)中。
https://windup.run/zh/llms.txt 一段现成可粘贴的起始提示词 —— 填入你的流程即可。
阅读 https://windup.run/zh/llms.txt 并为我的应用搭建 Windup E2E 测试,然后为以下流程编写场景:…… llms-full.txt —— 整套文档汇于一个文件 · 每页 markdown 位于 /zh/docs/<page>.md · llms.txt 标准