# Write Widget Tests for a Flutter App

Yes. An agent creates your Flutter app's first test/ directory: widget tests for its main screens and go_router navigation, plus a golden test.

[Run this on Rush](https://cloud.getrush.ai/run/write-widget-tests/flutter/), in a Project that has your repository.

## What the agent does

The agent starts by checking what testing the app can do at all. In a Flutter app like gskinnerTeam/flutter-wonderous-app there is no `test/` and no `integration_test/` directory, and `pubspec.yaml` lists only `icons_launcher`, `flutter_lints` and `dependency_validator` under `dev_dependencies`, so `flutter_test` itself is missing. The SDK constraints are `sdk: ^3.11.5` and `flutter: ^3.41.9`, and the repository's web workflows pin `flutter-version: '3.41.9'`, so the agent installs that Flutter release in its sandbox, adds `flutter_test: sdk: flutter` to `dev_dependencies`, and runs `flutter pub get` against the committed `pubspec.lock`.

Then it works out what the app needs before any screen can build. `main.dart` calls `registerSingletons()`, which registers ten lazy singletons with `GetIt`, from `AppLogic` and `WondersLogic` to `CollectiblesLogic` and `NativeWidgetService`, and then `appLogic.bootstrap()`, which loads settings and collectibles through `JsonPrefsFile` on top of `shared_preferences` and only then sets `isBootstrapComplete`. The router's `_handleRedirect` in `lib/router.dart` sends every path back to `/` until that flag is true. So the shared helper in `test/helpers.dart` registers the same singletons, calls `SharedPreferences.setMockInitialValues({})` (the plugin's own test hook, so no platform channel is needed), runs `bootstrap()`, resets `GetIt` after each test, and pumps `WondersApp` itself, so the tests go through the real `MaterialApp.router`, localization delegates and redirect.

The widget tests follow the routes `ScreenPaths` defines. After bootstrap, a test checks that `/` redirects to `/home` and that `HomeScreen` (in `wonders_home_screen.dart`) shows the first wonder from the `WonderType` enum, which has eight values from `chichenItza` to `tajMahal`. A navigation test opens one wonder's details through `ScreenPaths.wonderDetails(type, tabIndex: 0)`, which builds `/home/wonder/<name>?t=0`, checks that `WonderDetailsScreen` and its `WonderDetailsTabMenu` render, and checks that an unknown wonder name falls back to `chichenItza`, as `_parseWonderType` does. Because the app runs long `flutter_animate` sequences, the agent advances time with `pump` and explicit durations rather than relying on `pumpAndSettle` alone.

For the golden test it picks `TimelineEventCard` from `lib/ui/common/timeline_event_card.dart`, a stateless card with a `year`, `text` and `darkMode`, and renders both modes inside `WondersAppScaffold` so `$styles` resolves. Flutter tests use a placeholder font unless real ones are loaded, so the test loads the app's Cinzel, Yeseva and Tenor fonts from `assets/fonts/` with `FontLoader` first, then runs `matchesGoldenFile`. It generates the images once with `flutter test --update-goldens`, then runs `flutter test` without the flag to prove they match, and keeps that output for the pull request.

## Start it with one API call

`POST https://api.prix.dev/o/{org}/sessions`

```json
{
  "agent": "claude",
  "input": "This app has no tests. Add widget tests for the home screen, one detail screen and the go_router navigation between them, plus a golden test for one card widget. Read pubspec.yaml, main.dart and the router first, install the Flutter version the repository pins, and add flutter_test to dev_dependencies if it is missing. Start the app the way main.dart does, with its real service registration and bootstrap, using only the plugins' own test hooks such as SharedPreferences.setMockInitialValues; reset the service locator between tests. Load the app's fonts before the golden test, generate the golden once and then run flutter test again without --update-goldens. Do not change code under lib/. Run flutter test, and open a pull request with the output.",
  "resources": [
    {
      "url": "https://github.com/your-org/your-flutter-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

The pull request creates `test/` with a shared `helpers.dart`, `home_screen_test.dart`, `navigation_test.dart`, and `timeline_event_card_golden_test.dart` with its PNGs under `test/goldens/`, and adds `flutter_test` to `dev_dependencies` with the matching `pubspec.lock` change. Nothing under `lib/` changes. The description lists each test, the route or widget it covers, and the Flutter version it ran on, and includes the `flutter test` output the prompt asks for.

The session keeps both golden runs, the one that wrote the images and the one that compared against them, and every other `flutter test` and `flutter pub get` the agent ran, so a reviewer can see the tests pass without the update flag. Wonderous's own workflows build for the web and do not run tests, so the pull request notes that and suggests a `flutter test` step for CI as a follow-up rather than adding one itself.

Before merging, a reviewer opens the golden PNGs to check that they show the real fonts and colours rather than placeholder boxes, checks that the bootstrap in the test helper matches `main.dart` closely enough that a change to startup breaks the tests, and decides whether goldens generated on Linux suit a team that may regenerate them on macOS, where font rendering differs.

## Run it on every Flutter SDK upgrade

Widget tests and goldens are most useful when the toolkit underneath changes. When a pull request bumps the `flutter` constraint in `pubspec.yaml` or the `flutter-version` in a workflow, have a GitHub `pull_request` webhook send a request with the pull request URL in `resources`, asking the agent to run `flutter test` on the new version and report any golden that moved. Each pull request gets its own session in its own sandbox.

Set a webhook URL on the API key and Rush POSTs `run.completed` or `run.failed` when the turn ends, with the session URL, so your server can post the result on the upgrade pull request.

## Why run it on Rush?

Golden tests are sensitive to the machine that renders them: the Flutter version, the operating system and the fonts all change pixels. Generating them on one developer's laptop ties every future comparison to that laptop. On Rush the agent installs the exact Flutter release the repository pins in its own Linux sandbox, generates the goldens there and runs the comparison there, and the session records which version and commands produced them.

The first test directory for an app is also the hardest to set up, because the service locator, the bootstrap and the router redirect all have to work in a test before a single screen renders. That trial and error happens in the sandbox, with every failed `flutter test` kept in the session, and what reaches you is a pull request with passing tests and the output that shows it.

## Frequently asked questions

### My app uses GetIt. Will the tests replace my services with fakes?

No. The helper registers the same singletons `main.dart` registers and runs the real bootstrap, then resets `GetIt` after each test so state does not leak between them. Only platform plugins get their own test hooks, such as `SharedPreferences.setMockInitialValues`, because there is no device behind a widget test.

### Will the goldens match on my Mac?

Not always. Font rasterising differs between Linux and macOS, so goldens made on one can fail on the other by a few pixels. Most teams generate and compare goldens in one place, usually CI on Linux; the agent generates them on Linux and says so in the pull request.

### Does it test on a real device or emulator?

No. `flutter test` runs widget tests in the Flutter test environment with no device, which is what makes them fast and runnable in a sandbox. Tests in `integration_test/` that need a device or a browser are a separate job.

### Does it work with Riverpod or Bloc instead of GetIt?

Yes. The agent reads how `main.dart` sets up state, wraps the app in the same `ProviderScope` or `BlocProvider` tree in its test helper, and overrides only what touches a platform, so the screens build with the state they use in the app.
