# Find Untested Code in an Angular App

Yes. An agent runs your Angular app's Vitest suite under coverage in its own sandbox and reports the riskiest untested interceptors, directives and pages.

[Run this on Rush](https://cloud.getrush.ai/run/test-coverage-gaps/angular/), in a Project that has your repository.

## What the agent does

The agent reads the suite and the app side by side, because the gap between them is the report. In an app like `realworld-apps/angular-realworld-example-app`, `package.json` pins Angular 21.2 with `vitest ^4` through `@analogjs/vite-plugin-angular`, `jsdom` as the environment and `@vitest/coverage-v8` reporters for text, JSON and HTML, and `vitest.config.ts` includes `src/**/*.spec.ts` with a `src/test-setup.ts` setup file. There are six spec files, and every one of them tests a service: `articles.service`, `comments.service`, `tags.service`, `profile.service`, `user.service` and `jwt.service`. The other forty-two TypeScript files under `src/app` have no spec at all.

It also reads what CI covers, because untested code that CI never runs is the riskiest kind. `.github/workflows` holds `lint.yml`, which runs `bun run format:check`, `playwright.yml`, which runs `bun run test:e2e` against the RealWorld specs in the `realworld` submodule, `security-tests.yml` for the `@security` tagged specs, and `deploy.yml`; no workflow runs `bun run test`, so the six unit specs pass or fail only on a developer's machine. `tsconfig.spec.json` still lists `jasmine` in its `types`, a leftover from the Karma days that the Vitest setup no longer needs, and the report notes it as a sign the unit suite has been neglected.

Then it measures rather than guesses. In the sandbox it runs `bun install`, then `bun run test:coverage`, reads the text table and the per-file uncovered line ranges, and ranks what is missing by what it protects. First come the three interceptors in `src/app/core/interceptors`: `token.interceptor.ts` attaches the JWT to every request, `error.interceptor.ts` decides what the user sees on a failure, and `api.interceptor.ts` rewrites URLs, so a regression in any of them breaks every page at once. Next the `if-authenticated.directive.ts` that hides and shows controls by login state, the `auth.component.ts` that handles both login and registration, `settings.component.ts`, `editor.component.ts`, the `favorite-button` and `follow-button` components that fire writes, and the `markdown.pipe.ts` that renders article bodies with `marked`.

For each gap it writes a test sketch in the project's own idiom: a `TestBed` with `provideHttpClient` and `provideHttpClientTesting`, `HttpTestingController` to assert the header the token interceptor adds, a host component for the directive, and the `@rx-angular/template` directives the components use, all under the existing `vitest.config.ts` so no new runner is involved. It closes with two suggestions the team can take or leave: a `test.yml` workflow that runs `bun run test` beside the three existing ones, and dropping `jasmine` from `tsconfig.spec.json`. The result is `coverage-gaps.md`; the agent changes no code and opens no pull request.

## Start it with one API call

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

```json
{
  "agent": "claude",
  "input": "Report the untested code in this Angular app without changing anything. Read package.json, vitest.config.ts, tsconfig.spec.json, src/test-setup.ts, every .spec.ts file, src/app/app.config.ts, src/app/app.routes.ts, every file under src/app/core, the components and pages under src/app/features, src/app/shared, and the workflows under .github/workflows first. Run bun install and bun run test:coverage, and produce the text table with uncovered lines and the HTML report. Write coverage-gaps.md: the exact command and the measured line and branch coverage, the per-file table, then the ten riskiest untested units ranked by what they protect, with the HTTP interceptors, the authentication directive and component, the settings and editor pages, the favorite and follow buttons and the markdown pipe considered first, each with its uncovered lines and a short test sketch using TestBed, provideHttpClient with provideHttpClientTesting and HttpTestingController where a request is involved, all runnable under the existing vitest.config.ts. Add a section on what CI runs today and whether the unit suite is part of it, and note any stale test configuration you find. Change no code and open no pull request.",
  "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 `coverage-gaps.md` as an artifact on the session, which the session read returns with a download link good for an hour. It opens with the `bun run test:coverage` command, the overall line and branch percentages it printed, and the per-file table with uncovered line ranges, so every number in the report was measured against the suite as it is.

Then come the ten riskiest gaps. Each names the file, the lines no test executes, what goes wrong silently if that code regresses, and a sketch of the test: which `TestBed` providers to configure, which request to expect on the `HttpTestingController`, which host template exercises the directive, and what to assert. A closing section says which workflows run on a pull request today, that none of them runs the unit suite, and what a `test.yml` that does would contain, along with the `jasmine` entry in `tsconfig.spec.json` that no longer belongs.

Before acting on it, a reviewer checks the ranking against how the app is actually used, picks the sketches worth writing, and decides whether to wire the unit suite into CI first so new specs cannot rot. A follow-up message to the same session asks the agent to write any of the tests and open a pull request, with the sandbox's `node_modules` still installed.

## Run it on every pull request to the default branch

A team that wants the gap list refreshed as the code changes starts one run per pull request with `POST /o/{org}/sessions`, the pull request branch in `resources` as `https://github.com/your-org/your-app/pull/123`, and this prompt. A job in the existing workflows can send that request with the key from its secrets and post the artifact link as a comment on the pull request.

Each run measures in its own sandbox, so two pull requests never share a coverage directory. A webhook on the key receives `run.completed` with the `session_url`, and reading the session returns the artifact's fresh download link.

## Why run it on Rush?

A coverage number from a laptop depends on which `node_modules` happened to be installed and whether the `realworld` submodule was initialised; `bun run setup` does both, and skipping it changes the result. In a Rush sandbox the agent starts from a clean checkout, installs exactly what `bun.lock` says, and runs the suite the way a CI job would, so the report describes the code rather than one machine.

The report carries its own proof. The `test:coverage` output is in the transcript beside the table in the report, so a reviewer can check any percentage or line range against the raw run, and the ranking is tied to named files and lines, the interceptors and the authentication directive first, rather than to an impression of where the risk is.

## Frequently asked questions

### Does it write the tests or change the suite?

No. The result is a report with test sketches, and the repository is left as it was. If you want the tests written, a follow-up message to the same session names the sketches and the agent opens a pull request with them under the existing `vitest.config.ts`.

### Why do the interceptors rank above the components?

Because every page goes through them. `token.interceptor.ts` attaches the JWT to each request, `error.interceptor.ts` decides what a failure looks like to the user, and `api.interceptor.ts` rewrites the URL, so a regression there breaks the whole app at once, while a component bug breaks one screen. Code that writes, such as the follow and favorite buttons, ranks above code that only renders.

### Does the unit suite run in CI today?

Not in this repository. The three test workflows run Prettier and the Playwright end-to-end specs, and `bun run test` is run by hand. The report says so and sketches the workflow that would add it, but adding it is the team's decision.

### Are the sketches specific to this app?

Yes. They use the `TestBed` and `provideHttpClient` with `provideHttpClientTesting` setup the existing service specs already use, name the exact header or URL to expect on the `HttpTestingController`, and account for the `@rx-angular/template` directives the components render with, so each one is a few minutes from being a real test.
