Write a test the way you'd describe it
No selectors, no scripting, no flaky page objects. Describe the outcome in plain Markdown and let the agent drive the browser.
# Test Wikipedia English Language Banner
## Success Condition
- [] Wikipedia is reachable.
- [] The English language entry point is clickable.
- [] The banner displays "Welcome to Wikipedia".
## Steps
1. Go to https://www.wikipedia.org.
2. Click the English language link.
3. Confirm the "Welcome to Wikipedia" banner is shown.
- 1
Describe it
Drop a Markdown file into your
tasksfolder that states the steps and success conditions in plain language. - 2
Run it
One
run-qacommand and the agent opens a real browser, follows your steps, and reasons about the page. - 3
Read the result
Get a clear pass/fail report with a full diagnostic trail when something breaks — no screenshots to decode.
- 4
Grow the suite
Chain dependent tests so a value discovered in one flows into the next, and pull the steps they share into a test skill.
Teach it once, reuse it everywhere
A test skill is a Markdown file holding a named piece of instruction — a login flow, or the exact procedure for an interaction the agent otherwise gets wrong. Tests refer to it by name instead of repeating the prose.
- Loaded on demand. The agent sees only a skill's name and description until a test needs it, so a thorough skill costs nothing until it is used.
- Batteries included. Three built-in skills ship in the image — clipboard capture, forms and dialogs, and screenshots — under a reserved
ww:prefix that can never collide with your own names. - Swappable per environment. Point
SKILLS_DIRat another folder and the same tests resolve to a different implementation.
---
name: login-test-site
description: Log a given user into the test site. Use
whenever a task requires an authenticated session.
---
# Login to Test Site
1. Verify the given user exists in the context manager.
If not, fail with "Test user credential isn't found."
2. Open a browser tab, navigate to `LOGIN_URL`, and submit
`${context.<user>.username}` and its password.
3. Verify the browser URL becomes `TEST_URL`.
Any test that needs an authenticated session is now two lines:
1. Load the `login-test-site` skill and log into the
test site with user `testuser`.
2. Verify the account menu shows "testuser".
Built for real-world QA
Everything you need to automate front-end testing without the usual maintenance tax.
Plain Text Testing
All test tasks are plain Markdown files — no scripting required. Write automation in plain language. Manage Test Tasks
Reusable Test Skills
New in 1.4.0Describe a login flow or a tricky interaction once in a SKILL.md, and every test that needs it loads the instructions on demand. Create Test Skills
Tests That Chain
Declare dependencies between tests and pass values from one to the next through global, preset, and runtime context. Chain Tests Together
Minimal Token Usage
Handles complex scenarios and large chained test suites without hitting token limits. Efficient token handling reduces real-world test costs to pennies per test case. AI Benchmark
Fully Harnessed
It runs in your infrastructure — there is no Waterwheel backend — and is isolated by layers of permission controls, from the container down to individual tools. Agent Permissions
Your Key, Your Provider
Claude, OpenAI, Google Gemini, DeepSeek, or Ollama-hosted Gemma 4. You bring the key, and the agent talks only to the provider you pick. Provider Guide
Easy to Use
Delivered as a Docker image. Configure once and integrate seamlessly into your development, CI/CD, or QA pipeline. Waterwheel Docker Page
Nested Agent Loops
Pair the agent with a code agent to wrap an autonomous browser test loop in a test-and-fix coding loop. Agent Loops
Skip the QA bottleneck
Run it yourself
Pull the Docker image, point it at your site, and ship with confidence. Free to run on your own machine.
docker pull taojdcn/duotail-waterwheel:1.4.0
Let your code agent drive
Add the skills to your code agent to run the full test-and-fix loop automatically.
npx skills add taodong/duotail-waterwheel-skills --skill '*'