SimMan

Your E2E tests break because of selectors, not bugs.
A vision model is the eyes. A real cursor is the hands.

alpha 0.2 Apache-2.0 npm i -g simman model-agnostic

How it works

๐Ÿ“ธScreenshotreal browser, real pixels
โ†’
๐ŸŽฏGroundmodel finds pixel coordinates
โ†’
๐Ÿ–ฑ๏ธClickreal cursor, no selectors
โ†’
โœ…Judgedid the expected result happen?

Why

No brittle selectors

CSS selectors break on refactors, not bugs. SimMan clicks what a user sees โ€” so tests survive markup churn and catch visual failures DOM tools are blind to.

Deterministic core

No LLM inside the engine. A structured scenario drives Playwright deterministically; vision is used only where selectors would be. 99 tests, headless, no API key needed.

Truth signals

Declare your app's own ground truth per step โ€” a network call observed (kind: network) or an element actually enabled (kind: element_enabled). When declared, it overrides the vision judge. Hybrid verification, not vibes.

Local-first option

Run grounding fully offline on Apple Silicon with Qwen2.5-VL via MLX โ€” screenshots never leave your machine. Or use hosted Gemini with zero setup.

Readable by anyone

Scenarios are plain YAML in user language โ€” action / expected per step. A PM can review your test plan.

Human-gated autonomy

simman synthesize: describe a goal, the agent explores the page and proposes a plan โ€” and a human must approve it (approve / edit / reject) before any browser action runs. Agent proposes, human disposes.

Quick start

# install
npm i -g simman
npx playwright install chromium
export GEMINI_API_KEY=...

# preflight, then run a scenario
simman doctor
simman run scenarios/example.yaml --base-url http://localhost:3100
# or: describe the goal, approve the plan, get a repeatable test
simman synthesize http://localhost:3100 "compose a post and publish it"

#   โ†’ agent explores the page and proposes a plan
#   โ†’ gate: [a]pprove ยท [e]dit in $EDITOR ยท [r]eject   (nothing runs unapproved)
#   โ†’ saved scenarios/compose-post.yaml โ€” re-runs forever, no LLM needed
id: compose-and-post
steps:
  - action: "Type a greeting into the message box"
    expected: "The message box contains the greeting"
  - action: "Click the Post button"
    expected: "A confirmation appears"
    truth:            # your app's ground truth โ€” overrides the vision judge
      kind: network
      match: /api/posts
      method: POST
Alpha honesty: the deterministic engine is hardened (116 tests) and the full synthesize โ†’ gate โ†’ run loop ships as of 0.2 โ€” verified end-to-end with a fully local model. Still alpha: scenario schema may change between minor releases, and hosted OpenAI/Anthropic adapters are code-complete but less battle-tested than Gemini and local MLX.