Skip to content
carbonmeaiPublic

Latest commit

 

History

1,059 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
dánwò: Drive the UI. Judge everything behind it.

dánwò

The open-source end-to-end testing framework for web and mobile apps.
Drive your app through its UI like a user, and check what happened behind it when you need to.
Learn more »

English · 简体中文

Quickstart · Docs · What you can test · Fault matrix · Coding agents · Contributing

Licence: Apache-2.0 Node 22.22.3+ or 24.11.1+ npm @danwo/cli TypeScript


danwo is a complete UI testing framework on its own: locators, plain-English agent steps that replay with no model calls, and screenshot comparison, in a browser, on Android or on iOS. When your app takes payments, sends email or handles webhooks, turn on the backend layer: emulated providers, invariants and built-in fault injection (the fault matrix).

A test

With locators: no model needed

import { test } from "@danwo/sdk"

test("signs up", async ({ app, screen, expect }) => {
  await app.open("/sign-up")
  await screen.getByLabel("Email").fill("ada@example.com")
  await screen.getByLabel("Password").fill("correct horse battery staple")
  await screen.getByRole("button", { name: "Create account" }).click()

  await expect(screen.getByRole("heading", { name: "Welcome, Ada" })).toBeVisible()
})

This runs with no model and no API key.

In plain English: a model on the first run, then cached

import { test } from "@danwo/sdk"

test("buys two mugs", async ({ app, agent, screen, expect }) => {
  await app.open("/")
  await agent.act("add two blue mugs to the basket and go to checkout")
  await agent.assert("the basket shows two blue mugs")

  await expect(screen).toHaveScreenshot("checkout")
})

The first run needs a model and its API key: each step goes to the model, which works out the clicks, and danwo records them. Later runs replay that recording with no model call. Commit the recording and CI replays it too; danwo test --replay-only makes sure no model is called there. A step goes back to the model only when the page has changed enough that its recording no longer fits.

Quickstart

You need Node.js 22.22.3 or a later 22, or 24.11.1 or newer, on macOS, Linux or Windows (web tests; mobile tests need macOS or Linux). In your app's folder:

npx @danwo/cli init
npm run test:danwo

init asks what to test, which model and providers to use (pick none for a UI-only start) and which coding agents to set up. It writes a config and a first test that passes, and installs the browser. See the quickstart.

Add the backend layer when you need it

Emulates the providers Stripe, Clerk, Resend, Google, Slack and 20 more answer from local emulators behind a proxy. No sandbox keys, no code changes.
Judges the backend An invariant reads your database and the provider after the test: one row, one charge, one email.
Breaks it on purpose danwo matrix reruns a test once per fault: 500s, declines, duplicate webhooks, lost database commits. The invariant judges each run.
test("joins the waitlist", async ({ app, screen, expect }) => {
  await app.open("/")
  await screen.getByLabel("Work email").fill(EMAIL)
  await screen.getByRole("button", { name: "Join the waitlist" }).click()

  await expect(screen.getByRole("status")).toContainText("You're on the list")
}).invariant(async ({ databaseUrl, resend, expect }) => {
  // Whatever went wrong on the way: one row, one welcome email.
  expect(await sql(databaseUrl, "select id from signups where email = $1", [EMAIL])).toHaveLength(1)
  expect(resendInbox(resend).list(EMAIL)).toHaveLength(1)
})
npx @danwo/cli matrix tests/waitlist.danwo.ts
PASS  status 429 on POST api.resend.com/emails
PASS  reset-after on POST api.resend.com/emails
PASS  serialization_failure on INSERT signups (postgres)
PASS  double-submit on POST /api/join
…
faults: 16 passed

Remove the app's idempotency key and two trials turn red: a second welcome email went out. The faults are planned from what the test called and injected at danwo's proxy, so there is nothing to set up and every result reproduces. See the fault matrix.

Every run, with or without the backend layer, leaves a flight record: video, steps, and the calls behind them, in a dashboard, a PR comment and over MCP.

Examples

Each one runs with no real keys. Clone, install, test.

Example What it shows
commerce A shop on Next.js, Hono, Postgres and Stripe Checkout. 86 faults held.
saas Workspaces, a trial and billing, Slack and Google, Inngest jobs, live notes.
clerk-login The real Clerk components, Google sign-in and org invites, on the emulator.
better-auth Self-hosted Better Auth: email codes, Google and GitHub, linked accounts.
with-next The smallest one: a Next.js waitlist on Postgres and Resend.
with-swiftui The same waitlist as a native SwiftUI app on the iOS Simulator.
with-compose The same waitlist as a native Jetpack Compose app on an Android emulator.
with-flutter The same waitlist as a Flutter app, on an Android emulator or iOS Simulator.

More in the examples gallery.

Where it runs

Web apps Any framework, started by danwo or already running.
Android Expo, React Native, Jetpack Compose and Views apps on an emulator.
iOS Expo, React Native, SwiftUI and UIKit apps on an iOS Simulator.
Playwright Test Keep your tests, add emulators and the matrix.
From Cypress Move a Cypress suite over one spec at a time.
From Detox Move a Detox suite over one test file at a time.
Real stacks Several apps, workers, databases and background jobs behind one proxy.
CI A GitHub Check and a PR comment that say what broke and whose fault it is.
Coding agents A skill and an MCP server for Claude Code, Cursor and Codex.

Contributing

Node 22.22.3+ or 24.11.1+, and bun:

bun install
bun run check

See CONTRIBUTING.md, the Code of Conduct, and SECURITY.md for reporting a vulnerability. danwo is early: APIs will change.

The name

Dánwò is Yorùbá for "to test". It comes from dán … wò, "try it and see": you put something to the test and watch what happens. Say it DAHN-woh, high then low.

Licence

The code is under the Apache License 2.0. The dánwò name and logo are trademarks of Carbonme Inc; see TRADEMARKS.md.

Releases

Packages

Contributors

Languages