Documentation
Ripplo reviews your pull requests. Run one command, comment @ripplo review on a pull request, and it tests your preview environment and reports what broke.
Using Ripplo
Choose New review from Reviews to check a pull request or create and update coverage. You can also comment @ripplo review on a pull request.
Pull request reviews
A pull request review runs the existing tests against the preview and investigates the behavior changed by the diff. It reports issues with replays and shows the regression results after any fixes to the tests. Those fixes stay with the review. Coverage reviews update the shared tests after changes merge.
Updating tests
Choose New review → Coverage from Reviews. Describe an area to cover, or leave the brief empty for Ripplo to choose. The review checks and repairs existing tests, then adds coverage against the selected app version. Shared tests update when the review finishes successfully.
Debugging a failure
When a step fails, the replay shows the DOM, network, console, and application state at that moment. Copy for agent copies a command that hands the review to your coding agent.
Syncing issues to GitHub or Linear
In the project's settings, open Project context. Under GitHub issues or Linear issues, choose a repository or team to sync Ripplo findings to. A project syncs to one place at a time, and the repository or workspace must be a context source for the project. Each issue Ripplo finds after that opens a matching issue there within a minute, with the same title and description plus a link back to Ripplo. An issue closed before then is skipped until it reopens. Closing or reopening the issue on either side updates the other.
A Linear workspace connected for context can read issues. Choose Allow in Linear to let Ripplo create and update issues in it. Once allowed, Linear keeps that access until the workspace is disconnected. Turning sync off keeps existing links and stops updates. A new destination applies only to issues found after the change.
Coding agents
The Ripplo plugin for Claude Code lets your agent act on a review. /ripplo:review fixes the issues a review found, or one issue when you add issue and its number. /ripplo:cover changes your app so Ripplo can test the workflows a review could not cover. Copy for agent, on a review or an issue, copies the right command.
Setup installs the plugin when Claude Code is installed. To install it yourself, run these two commands.
claude plugin marketplace add ripplo/claude-plugin
claude plugin install ripplo@ripploOther coding agents can use the ripplo command directly, the same way the plugin does. Sign in once, then pull the published issues and uncovered workflows of a review, the failures of a run with nearby evidence, or a snapshot of one frame. Your deploy pipeline can also report which commit is live on an environment.
npx ripplo login # sign in through your browser
npx ripplo logout
npx ripplo review start <prUrl> # review the pull request's latest commit
npx ripplo review status <codeReviewId> # where the review is and which commit it reviewed (JSON)
npx ripplo review <codeReviewId> # published issues and uncovered workflows (JSON)
npx ripplo explain <runId> # failures per step with nearby evidence (JSON)
npx ripplo snapshot <runId> --offset <ms> # PNG and rrweb-tagged HTML of that frame
RIPPLO_TOKEN=<deploy token> npx ripplo deploy \
--environment <name> --sha <sha> # report a deployment from your pipelineCore concepts
What the words in the dashboard mean.
Organizations and projects
An organization is your team's Ripplo workspace, sharing projects, reviewers, runs, and billing. A project maps to one GitHub repository, and Ripplo reviews the pull requests opened against it.
Workflows
A workflow is one thing a user does, written as a click path: go to a page, click, fill, and at each step the checks that must hold on the screen and in your app's data. Ripplo writes and owns them. A workflow can be tested from more than one starting point.
Test data
Date-dependent data, like a payroll due on a given day, is created the same way as any other test data. Ripplo does not simulate the passage of time or run tests that last for hours. If Ripplo cannot create the data a workflow needs, it warns you and asks either for test data or for an API it can use.
How Ripplo creates and removes test data, on the security page
Runs and replays
A run is one execution of a test against the preview environment you pointed Ripplo at. Every run records the pages as rendered, network calls, console output, and the application state checks at each step, and you scrub through it step by step in the dashboard.
Setup
Ripplo tests a deployed copy of your app, such as a staging server or pull request previews. It must be reachable over the internet, and Ripplo will create and change data in it. Do not connect a production environment.
You can set up from the terminal or in the dashboard. Both sign you in with GitHub, and your first sign-in creates your account and an organization.
Create the project
Run setup in your repository. It signs you in, creates the project, and connects the repository to GitHub if it is not connected yet. It then asks for your app's URL, whether to review a staging server, pull request previews, or both, your sign-in page, and your redirect URI.
Last, it prints three values for your app and waits while you do the next step.
npx ripplo setupAdd sign in with Ripplo to your app
Ripplo signs in to your app as an identity provider, the same way sign in with Google works. Add a provider with id ripplo and the scopes openid email profile ripplo to your auth library, using the three values from step 1. Set them as variables where your app runs, then deploy. The client secret is shown once, when the connection is created. If you lose it, rotate it in the project's settings to get a new one.
Each run signs in as a new user, ripplo-<run id>@test.ripplo.ai, so your app must create the user on first sign-in.
Your app also needs a sign-in page: an address that starts sign in with Ripplo as soon as it opens, or a login page that shows one button or link with Ripplo in its text or attributes, which Ripplo clicks. You enter this address in step 1.
RIPPLO_IDP_CLIENT_ID=<client id>
RIPPLO_IDP_CLIENT_SECRET=<client secret>
RIPPLO_IDP_DISCOVERY_URL=<discovery URL>The redirect URI you enter in step 1 must equal your auth library's callback address for the ripplo provider. You can change it later in the project's environment settings.
Examples for common auth libraries.
better-auth
The callback is /api/auth/oauth2/callback/ripplo on your app, so the redirect URI on the connection must be that address. baseUrl is the base URL the app already gives better-auth.
import { genericOAuth } from "better-auth/plugins/generic-oauth";
const RIPPLO_PROVIDER_ID = "ripplo";
function ripploProviderPlugins() {
const clientId = process.env.RIPPLO_IDP_CLIENT_ID;
const clientSecret = process.env.RIPPLO_IDP_CLIENT_SECRET;
const discoveryUrl = process.env.RIPPLO_IDP_DISCOVERY_URL;
if (clientId == null || clientSecret == null || discoveryUrl == null) {
return [];
}
return [
genericOAuth({
config: [
{
authentication: "basic",
clientId,
clientSecret,
discoveryUrl,
pkce: false,
providerId: RIPPLO_PROVIDER_ID,
redirectURI: `${baseUrl}/api/auth/oauth2/callback/${RIPPLO_PROVIDER_ID}`,
scopes: ["openid", "email", "profile", "ripplo"],
},
],
}),
];
}
export const auth = betterAuth({
account: { accountLinking: { enabled: true, trustedProviders: [RIPPLO_PROVIDER_ID] } },
plugins: [...existingPlugins, ...ripploProviderPlugins()],
});The sign-in page, a GET route at your sign-in page address. Express is shown. /dashboard is the page a signed-in user lands on.
import { fromNodeHeaders } from "better-auth/node";
app.get("/api/test-sso", (request, response) => {
void auth.api
.signInWithOAuth2({
body: { callbackURL: "/dashboard", providerId: "ripplo" },
headers: fromNodeHeaders(request.headers),
returnHeaders: true,
})
.then(({ headers, response: started }) => {
headers.getSetCookie().forEach((cookie) => response.append("set-cookie", cookie));
response.redirect(302, started.url);
});
});next-auth and Auth.js
The callback is /api/auth/callback/ripplo on your app, so the redirect URI on the connection must be that address. allowDangerousEmailAccountLinking lets the email in the token create the user on first sign-in.
import NextAuth from "next-auth";
const ripploProvider =
process.env.RIPPLO_IDP_CLIENT_ID != null &&
process.env.RIPPLO_IDP_CLIENT_SECRET != null &&
process.env.RIPPLO_IDP_DISCOVERY_URL != null
? [
{
id: "ripplo",
name: "Ripplo",
type: "oidc" as const,
clientId: process.env.RIPPLO_IDP_CLIENT_ID,
clientSecret: process.env.RIPPLO_IDP_CLIENT_SECRET,
wellKnown: process.env.RIPPLO_IDP_DISCOVERY_URL,
authorization: { params: { scope: "openid email profile ripplo" } },
checks: ["state" as const],
client: { token_endpoint_auth_method: "client_secret_basic" as const },
allowDangerousEmailAccountLinking: true,
},
]
: [];
export const { handlers, auth, signIn } = NextAuth({
providers: [...existingProviders, ...ripploProvider],
});The sign-in page, a route handler at your sign-in page address. /dashboard is the page a signed-in user lands on.
import { signIn } from "@/auth";
export async function GET(): Promise<Response> {
if (process.env.RIPPLO_IDP_CLIENT_ID == null) {
return new Response("Not found", { status: 404 });
}
return signIn("ripplo", { redirectTo: "/dashboard" });
}Clerk
In the Clerk dashboard, open SSO connections, choose Add connection, then For all users, then Custom provider. Set the key to ripplo and enter the discovery URL, client id, and client secret from step 1. Copy the authorized redirect URL Clerk shows into the redirect URI on the Ripplo connection, then turn on Enable connection.
No app code changes. The sign-in page saved on the connection is your login page, for example /login, where Clerk shows the Ripplo button.
Keycloak
No app code changes. In the realm your test app uses, add an OpenID Connect v1.0 identity provider with alias ripplo. Turn on the discovery endpoint with the discovery URL from step 1, send the client secret as basic auth, set the default scopes to openid email profile ripplo, turn on trust email and hide on login page, and keep the default first login flow so the brokered user is created on first sign-in. The redirect URI on the connection is the broker endpoint Keycloak shows on that page, /realms/<realm>/broker/ripplo/endpoint.
The sign-in page saved on the connection is your app's login address with the provider hint, for example /login?kc_idp_hint=ripplo.
Any other library takes the same values through its generic OpenID Connect provider. Apps without single sign-on can use the Ripplo authentication adapter from the project's environment settings instead.
Test authentication
Once your app is deployed with the three values, Ripplo opens a tab on your sign-in page. If the page shows a Ripplo button, click it, as a review run does. Your app then signs in as a test user.
Continue in setup. If you stopped setup, run this command to start from this step.
npx ripplo setup --checkWhen sign-in works, setup installs the Ripplo plugin for Claude Code, if you use Claude Code, so it can read a review's findings and fix them.
Create your first tests
In Ripplo, open Reviews, choose New review, then Coverage. Ripplo explores your app and writes tests for its workflows. Pull request reviews run these tests, so create them before your first pull request review.
Review a pull request
Comment on a pull request into your default branch. Ripplo replies with a link to the review.
@ripplo reviewIf your host deploys each pull request to its own URL (Railway, Vercel, Netlify, Render, Cloudflare Pages, or PullPreview), Ripplo finds that URL in the deploy comment. Otherwise, deploy the branch and add its URL after @ripplo review.
To sign in through a connection other than the project default, add auth=<connection name> at the end. To review every pull request when its preview is ready, turn on Review every push in the project's environment settings.