All jobs

Write System Tests for a Rails App

Yes. An agent writes Capybara system tests that drive your Rails and Hotwire app in headless Chrome, runs bin/rails test:system and opens a pull request.

Run this on RushIn a Project that has your repository.

What the agent does

The agent clones the app into its own cloud sandbox and reads how its system tests already work, because a new test should look like the ones beside it. In a Rails 8 app like basecamp/once-campfire, .ruby-version says 4.0.7, the Gemfile takes rails from the main branch with turbo-rails, stimulus-rails, sqlite3, redis and resque, and the test group has capybara and selenium-webdriver. test/application_system_test_case.rb uses driven_by :selenium, using: :headless_chrome, screen_size: [ 1400, 1400 ] and disables WebMock, so real Action Cable traffic flows between browser sessions.

It reads the seven files already in test/system (sending_messages_test.rb, composer_test.rb, unread_rooms_test.rb, boosting_messages_test.rb and others) to see what is covered and how. test/test_helpers/system_test_helper.rb gives sign_in, join_room (which waits for three connected turbo-cable-stream-source elements), send_message, assert_message_text, assert_room_read and assert_room_unread. Fixtures in test/fixtures name rooms such as designers and hq and users such as david, jz and kevin, all signing in with secret123456. Sending between two users and inserting a mention are tested already; creating a room is not, even though config/routes.rb defines rooms/opens, rooms/closeds and rooms/directs and each has its own controller.

So the new tests cover the gaps. A room test signs in, creates an open room and a closed room with chosen members, and checks that a member in a second using_session sees the room appear in their sidebar without reloading, which is a Turbo Stream broadcast. A direct-message test starts a direct room with another user. A mention test posts a message that mentions Kevin in one session and checks, in Kevin's session, that the message arrives with its .mention and that the room shows as unread for him. Each uses the existing helpers rather than raw Capybara calls, and waits on selectors rather than sleeping.

Then the agent runs what the test_system job in .github/workflows/ci.yml runs: it starts Redis, installs Chrome, sets REDIS_URL=redis://localhost:6379/0, and runs bin/rails db:setup test:system. When a test fails, it reads the failure screenshot Rails saves under tmp/screenshots and fixes the test, or reports the app's behavior when the app is what is wrong.

Start it with one API call

POST https://api.prix.dev/o/{org}/sessions with this body:

{
  "agent": "claude",
  "input": "Write Rails system tests with Capybara and headless Chrome for this app's room and messaging flows that have none: creating an open room and a closed room, starting a direct room, and mentioning a user so the message and the unread state reach them in their own session. Read test/application_system_test_case.rb, test/test_helpers and the existing tests in test/system first, reuse their helpers and fixtures, and wait on selectors rather than sleeping. Start Redis and set REDIS_URL, then run bin/rails db:setup test:system until every test passes. Open a pull request listing each new test, what it covers, and any behavior a test found that looks wrong.",
  "resources": [
    {
      "url": "https://github.com/your-org/your-app"
    }
  ]
}
  1. 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.
  2. 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.
  3. 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

What comes back is a pull request with new files in test/system, for example creating_rooms_test.rb, direct_rooms_test.rb and mentions_test.rb, and any helper they share added to test/test_helpers/system_test_helper.rb rather than copied between files. The description lists each test with the flow it drives and the sessions involved.

The checks are the repository's own CI: test_system runs every system test with a Redis service, and bin/rubocop checks the new files against rubocop-rails-omakase. The session's transcript keeps each bin/rails test:system run in the sandbox with its output.

Before merging, a reviewer runs the new tests a few times on CI or reads them for waits that could race, since a test that checks a broadcast before the cable connects will pass locally and fail sometimes on CI. They also check that the tests assert what a person sees, a room name in the sidebar or a mention in a message, rather than database rows.

Run it on every pull request that adds a controller

A repository webhook on your server can send this request when a pull request adds a file under app/controllers, with the prompt naming the new controller and the branch in resources as https://github.com/your-org/your-app/tree/<branch>. The tests then land on the same branch as the feature, before it merges.

With a webhook URL on the API key, Rush posts run.completed or run.failed when the turn ends. Your handler can comment on the feature's pull request with the link to the test changes.

The use case this job follows, with its automation and trigger: Write the missing tests.

Why run it on Rush?

System tests are the slowest and most fragile tests to write by hand, because each one needs a real browser, a running app, Redis for Action Cable and two signed-in sessions. In a Rush sandbox the agent sets that up on its own machine, runs headless Chrome against it, and repeats a failing test with its screenshot until the test is right.

The proof is in the record and the checks: every bin/rails test:system run and its output is in the transcript, and CI's test_system job runs the new tests again on the pull request. Several flows can be covered at once in separate runs, each with its own browser and database, without one test run's data leaking into another.

Frequently asked questions

Does it need a real browser?

Yes. Rails system tests drive Chrome through Selenium, and the agent installs Chrome in its sandbox and runs it headless, as the app's ApplicationSystemTestCase asks. No screen or display server is needed.

Can it test Turbo Stream and Action Cable updates?

Yes, by opening two sessions with using_session and checking in one that a change made in the other arrives. The helpers in this app already wait for the cable connection before acting, and the new tests use them.

Will it use RSpec instead of Minitest?

It follows the project. This app uses Minitest with ActionDispatch::SystemTestCase, so the tests do too. In an RSpec project the agent writes spec/system files with the project's existing drivers.

What if a flow is broken in the app?

The agent does not change app code to make a test pass. It keeps the failing test out of the suite, or marks it skipped with the reason, and describes the broken behavior in the pull request so you decide.