What the agent does
The agent first measures where the tests are, instead of starting with whichever file it opens first. In a Vite React app like the apps/react-vite workspace of alan2207/bulletproof-react it reads package.json and finds Vitest 2, @testing-library/react 15, @testing-library/user-event 14, MSW 2 with @mswjs/data, yarn.lock, and a test script that is plain vitest. vite.config.ts sets globals: true, the jsdom environment and src/testing/setup-tests.ts, which starts the MSW server with onUnhandledRequest: 'error', mocks zustand through __mocks__/zustand.ts, and seeds and resets the in-memory database around each test. Listing the 12 __tests__ files against src/features, it finds that auth has tests for its login and register forms and discussions is covered through the route tests in src/app/routes/app/discussions/__tests__, while comments, users and teams have none.
So those three are the target, comments and users first because they have components. In features/comments/components/comments-list.tsx the agent finds a Spinner while useInfiniteComments is loading, a No Comments Found heading for an empty list, a Load More Comments button shown only when hasNextPage is true, and a delete button wrapped in <Authorization> with the comment:delete policy from src/lib/authorization.tsx, which allows an ADMIN or the comment's own author. The MSW handler in src/testing/mocks/handlers/comments.ts pages comments ten at a time, so a test that seeds eleven comments reaches the second page through the real query and the real handler. In features/users it finds users-list.tsx with its own loading Spinner, delete-user.tsx with a confirmation and a pending state, and update-profile.tsx, a form that PATCHes /users/profile; the /app/users route is only rendered for ROLES.ADMIN.
It writes tests beside the components, in features/comments/components/__tests__/ and features/users/components/__tests__/, using the repository's own helpers in src/testing/test-utils.tsx: createUser, createDiscussion, loginAsUser, renderApp and waitForLoadingToFinish. Each component is tested for its loading state, its empty state, its populated state, and its error state, which the test produces by overriding one handler with server.use(...) to answer 500, then checking that the error notification src/lib/api-client.ts adds through useNotifications appears. Permission cases render the same list as an admin, as the author and as another user, and check whether the delete button is there.
yarn test starts Vitest in watch mode, so the agent runs yarn test --run to get one pass that exits, then yarn check-types and yarn lint, because the ESLint config includes eslint-plugin-testing-library and eslint-plugin-vitest rules that the new files must follow.
Start it with one API call
POST https://api.prix.dev/o/{org}/sessions with this body:
{
"agent": "claude",
"input": "In apps/react-vite, find the feature modules with the fewest tests and write Vitest and React Testing Library tests for their components and hooks, covering loading, error and empty states. List each feature with its existing test files before writing anything. Use the repository's own test utilities, data generators and MSW handlers; produce error states by overriding a handler for one test, not by mocking the feature's hooks. Cover permission-dependent UI for each role the authorization policies name. Put the tests in __tests__ folders beside the components, like the existing ones. Run yarn test --run, yarn check-types and yarn lint, and fix what they report in the new files. Do not change application code. Open a pull request with the before and after list of tested components.",
"resources": [
{
"url": "https://github.com/your-org/your-app"
}
]
}- Get an API key. In the console, open Settings, then API keys, and create a key for your org. Send it in the Authorization header as a Bearer token.
- Send the request. POST the body in request.json to https://api.prix.dev/o/{org}/sessions, with your repository as the resources URL. The run starts in its own cloud sandbox.
- Read what came back. Follow the run on its live stream or wait for the run.completed webhook, then read the result and its trajectory.
What comes back
The pull request adds test files only, for a codebase shaped like bulletproof-react under src/features/comments/components/__tests__/ and src/features/users/components/__tests__/, with tests for comments-list, create-comment, delete-comment, users-list, delete-user and update-profile. The description opens with the list of features and the test files each had before and has after, and says why teams is left with no component tests: it has only the get-teams API module, whose one caller is the register route in src/app/routes/auth/register.tsx.
The evidence is the run output. The session keeps the agent's yarn test --run, yarn check-types and yarn lint from its own sandbox in full, so the reviewer sees every test name pass with no unhandled MSW request, which the setup file turns into an error. If the repository's CI runs Vitest, its check on the pull request runs the same suite.
A reviewer checks that the tests query by role and label rather than by class name or test id where the component offers one, that each error-state override is reset by the server.resetHandlers() call in the setup file, and that the permission cases match how the team wants the policies to behave rather than only how they behave today.
Run it on every new feature folder
Bulletproof React's layout puts each feature in its own folder under src/features, which makes new untested code easy to spot. Have a GitHub pull_request webhook send this request when a pull request adds files under src/features/ and none under a __tests__ folder, with the pull request URL in resources, so the agent writes the tests on that branch in its own session and sandbox.
With a webhook URL on the API key, Rush POSTs run.completed or run.failed when the turn finishes, with the session URL; your server can then comment on the feature pull request with a link to the tests.
The use case this job follows, with its automation and trigger: Write the missing tests.
Why run it on Rush?
Writing tests for six components means running Vitest over and over while each one goes from failing to passing, alongside the type check and the linter, and doing that on your laptop ties up the working tree you were building the feature in. On Rush the agent installs with Yarn in its own sandbox, runs the suite there as often as it needs, and nothing on your machine changes until you pull the branch.
The tests come with the output that proves them: every vitest pass the agent ran is kept in the session, so a reviewer can see the error-state tests pass for the right reason and not because a request went unhandled. And because the request names states to cover rather than files, the same request works on any Vite React app, and can run on several of them at once, each in its own sandbox.
Frequently asked questions
How does it decide which features need tests most?
It lists each feature folder with the test files that exercise it, including route-level tests elsewhere in src/app, and starts with the folders that have components and no tests. The pull request shows that list, so you can see why comments and users came before discussions.
Will it mock my hooks or API calls?
It mocks the network at the same place your existing tests do. In bulletproof-react that is MSW, with the handlers in src/testing/mocks/handlers, so components run their real React Query hooks and axios client against those handlers. An error state comes from overriding one handler for one test, not from replacing the hook.
Does it need my backend running?
No. The tests run in jsdom against the MSW handlers and the @mswjs/data database the setup file seeds and resets for each test, so there is no server to start and no network call leaves the sandbox.
What about Playwright end-to-end tests?
This job writes component and hook tests only. The app's e2e folder and test-e2e script, which starts the mock server with pm2 and then Playwright, are left alone. Ask for end-to-end coverage in the prompt and the agent runs that script in its sandbox instead.