TestSprite
Run your first end-to-end test in five minutes, then the reference for everything else.
Local Drive's end-to-end tests run on TestSprite:
a deployed server and a real browser, driven the way a person would drive them.
It is one layer of the testing pyramid, and everything below it
stays as it was. go test and flutter test are still the fast loop.
Recommended, never required. No account is needed to build, test or send a pull request, and CI does not run it.
One thing before you start. TestSprite drives a browser from the cloud, so
the target has to be publicly reachable. The CLI rejects localhost and private
addresses, so you need a deployment: a staging server, a preview, or your own
instance on a domain.
Working on a laptop only? Skip to AI agents and MCP. The MCP server can reach a local app, which is the way round this.
Quick start
Five minutes, four steps. You need Node.js 20.19+, 22.13+ or 24+ (odd numbered releases are not supported), a TestSprite account, and a deployment holding nothing you would miss.
1. Install and sign in.
npm install -g @testsprite/testsprite-cli
testsprite setupsetup asks for your API key, which comes from the TestSprite dashboard under
Settings, then API Keys. It stores it in ~/.testsprite/credentials and never
touches this repository.
2. Point it at your deployment.
testsprite project create --type frontend --name "Local Drive web" \
--url https://your-deployment.example
export TESTSPRITE_PROJECT_ID=prj_...3. Run one test.
testsprite test create \
--plan-from ./e2e/testsprite/frontend/01-sign-in-opens-the-file-list.plan.json \
--run --wait --target-url https://your-deployment.exampleReplace PROJECT_ID in that plan file with your own id first, or pass
--project "$TESTSPRITE_PROJECT_ID".
4. Read what happened.
Exit 0 means it passed. If it did not:
testsprite test failure get <test-id> --out ./.testsprite/failure --failed-onlyThat writes the failing step, the DOM around it and a hypothesis. Then fix the application and run it again.
That is the whole loop. Everything below is reference.
How the loop works
The part worth internalising, because a test that was written and never run has verified nothing.
change ──▶ run the test ──▶ read the failure bundle ──▶ decide what is wrong
▲ │
└──────────────── fix, then rerun ◀────────────┘Deciding what is wrong is the step that matters, and it has three answers:
- The application is wrong. Fix the application. This is the common case and the reason the layer exists.
- The product changed on purpose. Update the plan to describe the new intended behaviour, in the same commit as the change that caused it.
- The environment is wrong. A deployment that had not finished, a fixture account that was removed, a network fault. Fix the environment and run again; changing the test would hide it.
Never edit a test to make it pass. The intended behaviour is the source of truth, and a suite that is edited until it is green is worth less than no suite, because it still costs time to run and no longer tells anyone anything.
Why this layer exists
The layers below run against source. This one runs against a deployment, and the interesting failures are the ones no unit test can see: a control that moved, a screen that renders before its data arrives, a redirect that loops. Writing those as browser scripts produces a suite that breaks whenever a class name changes and gets deleted within a year.
TestSprite takes the workflow in sentences and drives the browser itself, so a test survives the refactor that renames the button. When one fails it returns a bundle: the failing step, the DOM around it, and a hypothesis. That is the part that makes a failure worth having.
It is a development dependency and nothing more. The server does not call it, the
client does not know it exists. If this collaboration ended, e2e/testsprite/
would be deleted and nothing else in the repository would change.
CLI reference
The two projects
Local Drive has two, because it is two things:
| Project | Type | Points at | Covers |
|---|---|---|---|
| Local Drive web | frontend |
The web client's public URL | The workflows in e2e/testsprite/frontend/ |
| Local Drive API | backend |
The server's public URL | The contracts in e2e/testsprite/backend/ |
testsprite project create --type frontend --name "Local Drive web" \
--url https://your-deployment.example \
--instruction "A self hosted file server. Sign in first; the file list is the main screen."
testsprite project create --type backend --name "Local Drive API"Running the committed suite
The plans and API tests live in e2e/testsprite/. See
its README for
what each file covers.
| What you want | Command |
|---|---|
| Check the plans are valid, offline, no key | testsprite test lint --plan-from-dir ./e2e/testsprite/frontend |
| Create and run every frontend workflow | testsprite test create-batch --plan-from-dir ./e2e/testsprite/frontend --run --wait --target-url <url> |
| Create and run one backend contract | testsprite test create --type backend --project "$TESTSPRITE_PROJECT_ID" --code-file ./e2e/testsprite/backend/<file>.py --run --wait --target-url <url> |
| Run everything already created | testsprite test run --all --project "$TESTSPRITE_PROJECT_ID" --wait |
| Replay one after a fix | testsprite test rerun <test-id> --wait |
| Score a test that fails intermittently | testsprite test flaky <test-id> |
--wait blocks until there is a verdict. --output json gives a machine
readable envelope, which is what an agent or a script should read.
A frontend rerun replays the saved script and is free. A backend rerun dispatches the whole dependency closure, so producers and teardowns run too.
The linting one is worth knowing: it needs no key, no account and no network, so it is the check to run before opening a pull request that touches the suite.
Investigating a failure
Start with the triage card:
testsprite test result <test-id> --include-analysisThen pull the bundle. It holds the failing step, the DOM around it, the test source and a root cause hypothesis, all anchored to one snapshot:
testsprite test failure get <test-id> --out ./.testsprite/failure--failed-only trims it to the failing step and its neighbours, which is usually
what you want when handing it to an agent. To pin an exact run rather than the
latest failure, which matters when several run at once:
testsprite test artifact get <run-id> --out ./.testsprite/runs/<run-id>.testsprite/ is ignored by git. A bundle contains screenshots of whatever was
on screen, so it stays local.
AI agents and MCP
The agent skill
The mechanics are covered by TestSprite's own skill, which the CLI installs and keeps in step with its own version:
testsprite agent install --target claudeclaude is the mature target. cursor, cline, windsurf, copilot, kiro,
codex and antigravity are also supported. Run testsprite agent list for
where each one lands, and testsprite agent status to check an installed skill
is not stale.
Those generated files are ignored by git, so each contributor installs the ones for their own tool and no single editor's copy sits in the repository going out of date.
What is specific to this project, meaning when the layer is worth reaching for
and what a failure means here, is in
.ai/skills/verify-with-testsprite.md.
Read that with AI agents.
The MCP server, and when to prefer it
The CLI is the primary interface. The MCP server is a second way in, aimed at an assistant working inside an editor, and it earns its place here for one reason: it can test an app that is only running locally, which the CLI cannot. For a self hosted project where the normal state of a change is "running on my laptop", it is often the only interface that can verify anything at all.
claude mcp add TestSprite --env API_KEY=your_api_key -- npx @testsprite/testsprite-mcp@latestCursor, Trae, Windsurf, VS Code and GitHub Copilot are also supported; they take
the same package in their own MCP settings, with API_KEY in the environment.
See TestSprite's
MCP installation guide
for each client's exact configuration, and the
tool reference for what it exposes.
| Situation | Use |
|---|---|
| Change is deployed somewhere public | The CLI |
| Change is only running locally | The MCP |
Running the committed suite in e2e/testsprite/ |
The CLI |
| Exploring what a new area should even test | The MCP |
| Scripting, or anything that needs an exit code | The CLI |
The MCP writes testsprite_tests/ into the working directory, including
generated cases, reports, a standard_prd.json and a tmp/ holding
config.json, code_summary.json and test_results.json. Some of that carries
an API key and a map of the codebase, so the whole tree is ignored here. See
Security.
The Web Portal is the third piece and holds the account: keys, project credentials, the credit balance, and the dashboard a run links back to. Some settings exist only there, and this repository does not try to reproduce them.
Advanced configuration
Where the API key lives
The key is never committed, never pasted into a file in this repository, and never printed into a log. Nothing here reads it; the CLI finds it itself.
| Where | How |
|---|---|
| Your machine | testsprite setup writes ~/.testsprite/credentials, mode 0600 |
| A shell, one off | TESTSPRITE_API_KEY=… in the environment |
| A pipeline | A secret named TESTSPRITE_API_KEY |
| More than one account | Profiles: --profile <name>, or TESTSPRITE_PROFILE |
To configure credentials without installing the agent skills:
testsprite setup --no-agentRunning it from a pipeline
There is no TestSprite job in this repository's CI, deliberately. Runs cost credits and need a deployment to point at, so they happen when somebody decides they are worth it rather than on every push.
If you wire it into a pipeline of your own, the exit code is the contract:
| Code | Meaning |
|---|---|
0 |
Passed |
1 |
Failed, blocked or cancelled |
3 |
Auth: key missing, revoked, or the wrong scope |
5 |
Validation: a bad field, or a missing flag |
7 |
Timed out. Inconclusive rather than a regression; resume with test wait |
11 |
Rate limited. Honour Retry-After |
12 |
Out of credits |
14 |
The CLI is too old for the backend |
Treat 7 as inconclusive rather than red. The plan lint needs no key and no
network, which is the part worth automating first.
Troubleshooting
localhost is rejected. Working as intended. Use a deployment, or the MCP
server for a local app.
auth status fails. Run testsprite setup, or export
TESTSPRITE_API_KEY. testsprite doctor says which part is missing.
A frontend test passes but asserts nothing useful. Usually a step that says "verify the page loads". Assert the thing that would be wrong if the feature broke: a specific name in the listing, not visibility of a container.
A test fails only sometimes. Score it before trusting it:
testsprite test flaky <test-id>. A plan that depends on state left by an
earlier run is the usual cause.
A run timed out (exit 7). The run is still going server side. Resume with
testsprite test wait <run-id> rather than triggering a second one.
Security
Nothing either tool generates is committed. Only the files written by hand in
e2e/testsprite/ are tracked. Everything below is ignored, and the rules are
deliberately broader than the paths in use today, because a key cannot be
un-leaked:
| Path | Why it stays out |
|---|---|
testsprite_tests/ |
Written by the MCP. Holds generated cases and reports |
testsprite_tests/tmp/config.json |
Can carry the API key |
testsprite_tests/tmp/code_summary.json |
A map of the codebase |
testsprite_tests/standard_prd.json |
Describes behaviour that may be unreleased |
.testsprite/ |
CLI state, and failure bundles |
.testsprite/failure/, .testsprite/runs/ |
Screenshots and DOM snapshots of the account the run used |
TC001_*.py and siblings |
Generated test cases |
.claude/skills/testsprite-*/ and the equivalents |
Generated agent skills, one per editor |
If you need to share a generated artifact, force add it deliberately after reading it. The default is out.
The rest:
- No key, token or password is committed anywhere. Placeholders only.
- Backend tests read the injected credential block and never hardcode one. A
hardcoded token expires and cannot be rotated, and
test createwarns when it sees one. - Tests run against a dedicated account on a deployment holding nothing anyone would miss. The frontend plans create folders, rename files and use the trash.
- Never point this suite at a server holding real files. A run uploads, renames and trashes things, and a failure bundle photographs whatever was on screen.
- Testing sends the application under test to a third party service. Treat that as you would any hosted tool, and read TestSprite's own documentation for what it retains.
Keeping this current
TestSprite is moving quickly, so this page holds Local Drive's own workflow and
links out for anything that changes underneath it. Commands here were verified
against the CLI and the official documentation on 2026-08-11, with
@testsprite/testsprite-cli v0.5.0 on Node 24.
If something no longer matches, TestSprite's documentation wins:
testsprite --version
testsprite doctor
testsprite --helpThe CLI reference and llms.txt are the sources to check against. Update the verification date above when you do.