ripplo
v0.9.4
Published
CLI for Ripplo — AI-powered end-to-end testing
Readme
ripplo
Describe how your app should behave. Ripplo proves it does.
You declare your app's state model and user flows in TypeScript. Ripplo seeds real data, drives a real browser, and checks the result against the model: the UI you asserted on and the rows that should have changed underneath it.
export const createTask = test("Create a task", () => {
const { me, project } = ownedProject();
return {
given: [me, project],
steps: [
goto`/projects/${project.id}/tasks`.expect(visible(button("New"))),
click(button("New")).expect(visible(textbox("Title"))),
fill(textbox("Title"), "Buy milk"),
click(button("Create")).expect(Task.created({ title: "Buy milk", projectId: project.id })),
],
};
});ownedProject() describes the starting state; Ripplo seeds it fresh for every run. Task.created(...) is checked against your actual database through a small engine you write once per entity.
Setup with Claude Code
If you use Claude Code, this is the shortest path. One command installs the plugin and opens a guided session that handles auth, project creation, scaffolding, and adapter wiring, and adds hooks that keep tests in sync as the agent changes your code.
npx ripplo setupSkip the rest of this section unless you want to know what setup does, or you're not on Claude Code.
Setup by hand
Three steps: authenticate, scaffold, mount the engine adapter in your app server.
npx ripplo auth login # device-code flow, opens a browser
npx ripplo init # scaffolds .ripplo/ and writes RIPPLO_* env varsripplo init creates .ripplo/ (entities, worlds, tests, plus a project.json), appends RIPPLO_APP_URL, RIPPLO_ENGINE_URL, and RIPPLO_WEBHOOK_SECRET to your env file, and installs @ripplo/testing.
Then mount the adapter. This is the one piece of code that lives in your app: a signed-webhook endpoint Ripplo calls to seed and read state, active only when you flip the env var.
// Express
import { createEngineHandler } from "@ripplo/testing/express";
import { engine } from "./test/engine.js";
app.use(
"/ripplo",
createEngineHandler({ enabled: process.env.ENABLE_RIPPLO_TESTING === "true", engine }),
);engine is where you implement seed and read for each entity you declared; TypeScript flags any entity you forgot. More framework adapters are coming; any server that can mount an Express router works today.
Last, preload @ripplo/instrument in your dev server so test runs capture your backend spans alongside browser actions:
node --import @ripplo/instrument server.js # or NODE_OPTIONS="--import @ripplo/instrument" next devIt exports spans to the local daemon during runs and stays dormant otherwise, so it's safe to leave in your dev script permanently.
Running tests
Start the daemon in one terminal and leave it running next to your dev server:
npx ripplo daemonIt keeps a warm browser pool and executes runs in parallel as they come in. Then:
npx ripplo run # tests in scope + tests affected by your changes
npx ripplo run --all # everything
npx ripplo run my-test # one test by idrun connects to the daemon and streams results; if a daemon is absent it starts one for you.
The lockfile
.ripplo/ compiles to .ripplo/ripplo.lock. Commit it. The Ripplo server reads the lockfile on every push, so your dashboard and CI always reflect what's actually in the branch. npx ripplo compile --check in a pre-commit hook (installed by setup) keeps it fresh.
Worktrees
Ripplo is built for parallel feature work via git worktree. Each worktree gets its own dev session, scope, debug artifacts, and lockfile checkout, while auth and project config are shared globally, so a fresh worktree is ready to run as-is.
Two things to handle per worktree: copy your gitignored env file in (or point envFiles in .ripplo/project.json at a shared path outside the tree), and pick a distinct dev-server port, updating RIPPLO_APP_URL and RIPPLO_ENGINE_URL to match. ripplo doctor flags both if you forget.
Other commands
npx ripplo --help lists everything. Beyond setup you'll mostly use lint (compile + typecheck your .ripplo/), doctor (auth, env, and lockfile health), and scope (manage which flows the current session is responsible for).
License
© Ripplo LLC. All rights reserved. Use is subject to Ripplo's Terms of Service.
