What the agent does
An end-to-end test of a Laravel app needs the whole app running, so the agent reads what that takes first. In koel/koel, composer.json requires PHP 8.3 and laravel/framework ^13.0, and runs PHPUnit with php artisan test --parallel. The front end is Vue 3 built by Vite (pnpm-lock.yaml, pnpm run build), with component specs beside each .vue file under resources/assets/js/components, and nothing that drives a real browser against a running server. .env.example defaults to DB_CONNECTION=mysql with an empty APP_KEY and an empty MEDIA_PATH.
So it builds a test environment the app supports without MySQL: an .env.e2e with DB_CONNECTION=sqlite-persistent (defined in config/database.php) pointing at a throwaway file, php artisan key:generate, then php artisan koel:init --no-assets --no-scheduler, which migrates and seeds the first admin, admin@koel.dev with the password KoelIsCool from User::FIRST_ADMIN_*. It points MEDIA_PATH at the MP3 and FLAC files the PHPUnit suite already ships in tests/songs and runs php artisan koel:scan, so the library has real tracks to play. It builds the assets once with pnpm run build.
The Playwright config in e2e/ starts that app with php artisan serve on a fixed port as its webServer, after the setup script, so npx playwright test works from a clean checkout. The specs cover three flows. Sign-in fills the form CredentialsLoginForm.vue renders under data-testid="login-form", submits with data-testid="submit", and checks a wrong password keeps the user on the form. Playing a song opens the library, plays a track scanned from tests/songs, and checks the footer AudioPlayer reports playback. Creating a playlist goes through CreatePlaylistForm.vue, names it, adds a song and reloads to check it was saved.
Headless Chromium plays audio with no sound device, so the agent checks playback through the player's state and the request to the stream endpoint, not through sound. It runs the suite headless several times; a failure caused by the app rather than the test stays in the suite marked test.fail(), with its cause written down.
Start it with one API call
POST https://api.prix.dev/o/{org}/sessions with this body:
{
"agent": "claude",
"input": "Write Playwright end-to-end tests for sign-in, playing a song and creating a playlist in this Laravel app. Put them in e2e/ with a playwright.config that starts the dev server, run them headless, and open a pull request with the passing suite. If a test exposes a real bug, mark it expected to fail and explain it in the description.",
"resources": [
{
"url": "https://github.com/your-org/your-laravel-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
What comes back is a pull request that adds e2e/auth.spec.ts, e2e/playback.spec.ts and e2e/playlist.spec.ts, a playwright.config.ts whose webServer runs php artisan serve, a setup script that prepares the SQLite database, seeds the admin and scans the bundled songs, and .env.e2e. @playwright/test is added to package.json and pnpm-lock.yaml; the PHP code and PHPUnit suite are unchanged.
The description explains how to run the suite from a clean checkout in one command, what the setup script changes (a separate SQLite file, never your .env), and every test marked test.fail() with the behavior it found and where. The session's trajectory keeps the koel:init and koel:scan output and every npx playwright test run with its full output.
Before merging, a reviewer checks that .env.e2e holds no real secrets, that SQLite is close enough to the production database for these flows, and whether each expected failure is a bug to fix. Running the suite in CI needs PHP, pnpm and Chromium on the runner; a follow-up on the same session can add that workflow.
Run it on every release branch cut
When a release branch is created, a GitHub create webhook can send this request with the branch in resources, asking the agent to run the e2e suite there and update any spec the release broke. The agent runs it against that exact code in its own sandbox and pushes fixes to a branch of its own.
Set a webhook URL on the API key and Rush POSTs run.completed or run.failed when the turn finishes, so the release channel hears whether sign-in, playback and playlists still work before the release ships.
The use case this job follows, with its automation and trigger: Write the missing tests.
Why run it on Rush?
Running a Laravel app end to end on a laptop means PHP, Composer, Node, a database, a built front end and a browser, all on versions that match the app, and a .env you do not want a test to overwrite. In its own sandbox the agent sets all of that up from nothing, seeds a known admin, and scans fixture media, so the tests run against a real server rather than a mocked API.
What it hands back has been run: the trajectory shows every setup command and every Playwright run with its output. Each run is isolated, so php artisan serve and the SQLite file never collide with another job, and the same request can add end-to-end tests to several Laravel apps at once.
Frequently asked questions
Will it touch my .env or my real database?
No. The suite uses its own .env.e2e and a separate SQLite file the setup script creates, and the sandbox has no access to your databases unless you give it one. Your .env.example and .env stay as they are.
Does my app need to support SQLite?
It helps, because the setup needs no database server. Koel defines a sqlite-persistent connection, so the agent uses it. If your app only works on MySQL or Postgres, the agent runs that server in the sandbox instead and says so in the description.
How does it test audio playback in a headless browser?
It checks the player's state and the request the browser makes for the track, not the sound. That proves the click reached the player and the server streamed the file, which is the part a regression breaks.
Will it replace my PHPUnit tests?
No. PHPUnit stays for the server code and the Vue component specs stay beside their components. Playwright adds the one thing neither covers: a real browser against a running app.