test-error-states
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseThe error state has never run
从未触发过的错误状态
Every app has a block nobody has executed and an empty state nobody has seen. They are written from imagination, shipped untested, and discovered by a user on a bad day.
catchReticle can force those conditions in the running app. Not installed? , then the skill.
RETICLE_INSTALL_SOURCE=npx_skill npx @reticlehq/server@latest initinstall-and-verify每个应用都存在从未被执行过的代码块,以及从未有人见过的空状态。这些逻辑仅凭想象编写,未经测试就上线,最终会在用户倒霉的那天暴露问题。
catchReticle可以在运行中的应用里触发这些场景。还未安装?执行,然后使用技能。
RETICLE_INSTALL_SOURCE=npx_skill npx @reticlehq/server@latest initinstall-and-verifyRead this before you start: network mocking needs a driven or leased browser
开始前须知:网络模拟需要受控或租赁的浏览器
reticle_network_mockreticle drive{ ok: false, reason: "no-cdp-provider" }Your route is a leased Playwright tab () or pointed at a Chrome started with remote debugging:
reticle_lease acquireRETICLE_CDP_URLbash
undefinedreticle_network_mockreticle drive{ ok: false, reason: "no-cdp-provider" }你的路由需要是租赁的Playwright标签页(),或者将指向开启远程调试的Chrome:
reticle_lease acquireRETICLE_CDP_URLbash
undefinedmacOS — the user runs this once, in their own Chrome
macOS — the user runs this once, in their own Chrome
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --remote-debugging-port=9222
RETICLE_CDP_URL=http://localhost:9222 npx @reticlehq/server@latest mcp
If you have neither a lease nor a driven browser, **say so in one line and offer the clock half of this skill anyway**: `reticle_clock` needs none of it. What you may not do is drive the happy path, watch it pass, and report that error handling works./Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --remote-debugging-port=9222
RETICLE_CDP_URL=http://localhost:9222 npx @reticlehq/server@latest mcp
如果既没有租赁浏览器也没有受控浏览器,**请用一句话说明,并仍可使用本技能的时钟控制功能**:`reticle_clock`无需依赖这些条件。绝对不能只走正常流程、看到测试通过就断言错误处理逻辑有效。Force a failure
触发失败场景
reticle_run({ tool: "reticle_network_mock", sessionId, args: {
mocks: [{ urlContains: "/api/deploys", status: 500 }],
}})Then drive the flow and name the recovery you expect before you act:
reticle_act_and_wait({ sessionId, ref, action: "click", until: { kind: "allOf", predicates: [
{ kind: "element", query: { testid: "error-banner" } },
{ kind: "console", level: "error", absent: true },
]}})Note the second predicate. A UI that "handles" an error by logging an uncaught exception has not handled it. Clear mocks with when you are done, or every later check runs against a lie.
{ clear: true }Worth forcing, in rough order of how often they are broken: , a with a real error body, an empty (, the empty state), a malformed payload, and a request that never resolves (the spinner that spins forever).
5004xx200[]reticle_run({ tool: "reticle_network_mock", sessionId, args: {
mocks: [{ urlContains: "/api/deploys", status: 500 }],
}})然后驱动流程,并在操作前明确预期的恢复行为:
reticle_act_and_wait({ sessionId, ref, action: "click", until: { kind: "allOf", predicates: [
{ kind: "element", query: { testid: "error-banner" } },
{ kind: "console", level: "error", absent: true },
]}})注意第二个断言条件。如果UI通过记录未捕获异常来“处理”错误,那并不算真正的处理。完成测试后用清除模拟,否则后续所有检查都会基于错误的环境。
{ clear: true }按故障出现频率从高到低,值得触发的场景包括:错误、带真实错误体的错误、返回空数组的响应(空状态)、格式错误的负载,以及永远无法解析的请求(无限加载的 spinner)。
5004xx[]200Skip time instead of sleeping
跳过时间而非等待
reticle_clock({ sessionId, freeze: true })
reticle_clock({ sessionId, advanceMs: 5000 })
reticle_clock({ sessionId, reset: true })Toasts that auto-dismiss, debounced search, polling, session timeouts, retry backoff. All of these are normally verified by sleeping, which is slow and flaky in equal measure. A timing assertion is a statement about the machine, so it passes on your laptop and fails in CI.
Freeze, advance by exactly the interval, assert the consequence. Same result on a fast laptop and a loaded runner. Always when you finish, or a frozen clock silently breaks everything that runs after you.
resetreticle_clock({ sessionId, freeze: true })
reticle_clock({ sessionId, advanceMs: 5000 })
reticle_clock({ sessionId, reset: true })自动消失的提示框、防抖搜索、轮询、会话超时、重试退避。这些功能通常通过等待来验证,但这种方式既缓慢又不稳定。时间断言是针对机器的逻辑,在你的笔记本上能通过,在CI环境中可能失败。
冻结时钟、精确推进指定时长、断言结果。无论是在高性能笔记本还是负载较高的运行器上,结果都一致。测试结束后务必执行,否则冻结的时钟会悄无声息地破坏后续所有操作。
resetWhat to assert
需要验证的内容
The recovery, not the absence of a crash:
- The error is shown to the user: a specific element, not just "the page did not blank".
- The app stayed usable: retry works, the form still has its input, navigation is not stuck.
- State is honest: shows the failure, not a half-applied optimistic update. A UI that rolled back visually while the store kept the optimistic value is the classic bug here, and only the store read finds it.
reticle_state - No uncaught error in the console.
要验证恢复行为,而非仅验证未崩溃:
- 错误已展示给用户:要有明确的元素,不能只看“页面没空白”。
- 应用仍可正常使用:重试功能有效、表单仍保留输入内容、导航未卡住。
- 状态真实可靠:显示失败状态,而非半应用的乐观更新。这类经典Bug表现为UI已回滚但存储仍保留乐观值,只有读取存储才能发现。
reticle_state - 控制台无未捕获错误。
Honesty
注意事项
Mocking changes the app's world, so a verdict taken under a mock is a statement about the mocked condition and nothing else. Say which mock was active when you report a pass, and clear every mock and reset the clock before handing back. An audit that leaves a pinned on breaks the next person's session and looks like a real outage.
500/api/deploysCapability reference: . Everything else: .
curl https://docs.reticle.sh/capabilities.mdcurl https://docs.reticle.sh/llms.txt模拟会改变应用的运行环境,因此基于模拟得出的结论仅适用于该模拟场景。报告测试通过时需说明使用了哪种模拟,并且在结束后要清理所有模拟并重置时钟。如果测试后仍保留针对的错误模拟,会破坏下一位使用者的会话,看起来像是真实的故障。
/api/deploys500能力参考:。其他内容:。
curl https://docs.reticle.sh/capabilities.mdcurl https://docs.reticle.sh/llms.txt