# Write Component Tests for a Svelte App

Yes. An agent adds Vitest with jsdom and Svelte Testing Library, tests the five components that hold the most state as a user would, and opens a pull request.

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

## What the agent does

The agent starts by learning how the repository already tests Svelte. In a monorepo like huntabyte/shadcn-svelte it reads `pnpm-workspace.yaml` (packages under `packages/*`, `docs` and `sv-addons/*`, with Svelte 5.54 and Vite 7 pinned in the catalog), the root `package.json` (`packageManager` pnpm 10.32, Node 20 or newer, and a `test` script that runs only `packages/*` and `sv-addons/*`, never `docs`), and the one package that tests components today: `packages/primitives`. Its `vitest.config.ts` runs Vitest 4 in jsdom with the `svelte` Vite plugin, a `$lib` alias and `conditions: ["browser"]`, and excludes `*.browser.test.*`, which a second config runs headless in Chromium through `@vitest/browser-playwright`. Its tests mount components with `mount` and `unmount` from `svelte`, flush with `flushSync`, and drive them through small fixture components in `src/lib/questionnaire/fixtures/`.

Then it finds components worth testing. The published UI lives in `docs/src/lib/registry/ui/<name>/`, and most of those files are thin wrappers over bits-ui with no state of their own; the agent greps for `$state(` and `{#if` and ranks what it finds. Two registry components carry real logic: `code-block/code-block-copy-button.svelte` (a `copied` flag set after `navigator.clipboard.writeText`, cleared by a two-second timeout, an `aria-label` that flips between Copy and Copied, and a `sr-only` live region) and `sidebar/context.svelte.ts` (a `SidebarState` class with `open`, `openMobile`, a `toggle` that picks one or the other from the `IsMobile` hook, and a keyboard shortcut on meta or ctrl plus `SIDEBAR_KEYBOARD_SHORTCUT`). The docs site's own components in `docs/src/lib/components/` add more: `command-menu/command-menu.svelte`, `theme-customizer.svelte`, `mobile-nav.svelte`, `docs-toc.svelte`, and the demo cards under `cards/` such as `team-members.svelte` and `report-issue.svelte`. The agent picks five from this list by how many branches a user can reach.

The `docs` package has `jsdom` and `@types/jsdom` in its devDependencies but no Vitest and no test file, so the agent adds `vitest`, `@testing-library/svelte` and `@testing-library/jest-dom` there, plus a `docs/vitest.config.ts` modelled on the primitives config: the `svelte` plugin rather than the full SvelteKit plugin, because `docs/vite.config.ts` carries a `flattenedRegistry` plugin, Tailwind and `enhancedImages` that a unit test does not need, and the `$lib` alias pointing at `docs/src/lib`. Components authored in `.md` through mdsx are left out. Each test renders the component with `render`, acts with `userEvent` or `fireEvent`, and asserts on what is visible: the copy button's label changes to Copied after a click and back after the timer, the sidebar trigger toggles `data-state` from expanded to collapsed and answers the keyboard shortcut, the command menu opens on its shortcut and filters its list as you type, the mobile nav closes when a link is chosen. Clipboard and `matchMedia` are stubbed with `vi.stubGlobal`.

The agent adds a `test` script to `docs/package.json`, runs `pnpm -F docs test`, then `pnpm -F docs check` (svelte-check with `--fail-on-warnings`) and the root `pnpm lint` (`oxfmt --check` and ESLint), since CI runs both. It does not touch the components themselves and does not change the root `test` script, so the published packages' tests still run exactly as before.

## Start it with one API call

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

```json
{
  "agent": "claude",
  "input": "Find the five Svelte components in this repository with the most conditional rendering or local state. Set up Vitest with jsdom and Svelte Testing Library in the package that owns them, following the Vitest config that already exists in packages/primitives, and write tests for each component that render it, interact with it as a user would and assert on visible output rather than internals. Add a test script, run the tests, run svelte-check and the lint script, and open a pull request that lists each component and what its tests cover. Change no component code.",
  "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 on a new branch with a `vitest.config.ts` and a `test` script in the package that owns the components, three new devDependencies, and one `*.test.ts` file beside each of the five components, for example `code-block-copy-button.test.ts` next to `code-block-copy-button.svelte` and `sidebar.test.ts` beside the sidebar's `context.svelte.ts`. The lockfile changes accordingly, and nothing else in `pnpm-lock.yaml` moves.

The description names the five components, says which user behaviours each test covers and which it stubs (clipboard, `matchMedia`, the keyboard shortcut), and quotes the commands it ran: the Vitest run, `svelte-check` and the lint script. The trajectory keeps every command and its output, so a reviewer can see the first failing attempt at mounting a component as well as the fix.

Before merging, a reviewer checks that assertions are on roles, labels and text rather than on class names or `$state` internals, that the new config does not import the docs site's Vite config and drag in its registry plugin, and that the root `pnpm test` still runs only the published packages. In a project that follows this repository's contributing guide, the reviewer also rewrites the pull request description in their own words before it goes upstream, since the guide asks that descriptions not be machine written.

## Run it on every new component in the registry

Add an automation with a `github` trigger on pull requests whose files touch the component directory, and this prompt narrowed to the components the pull request adds or changes. Each run opens its own pull request against the feature branch with tests for just those components, so coverage lands with the feature rather than months later.

For the backlog, a `schedule` trigger once a week with the same prompt picks the next five untested components each time. Give the API key a webhook URL and read `run.completed` to post each pull request link where the team reviews tests.

## Why run it on Rush?

Component tests in a Svelte 5 monorepo fail for reasons that have nothing to do with the component: the wrong Vite plugin, a missing `conditions` entry so the server build of Svelte loads in jsdom, an alias that resolves in the app but not in the test. On Rush the agent works through those in a sandbox with the repository's own pnpm and Node, and the transcript shows every failed mount and the config change that fixed it, so you do not spend your afternoon on them.

Because the run is a pull request with the Vitest and svelte-check output in its record, the review is about whether the tests describe the component well, not whether they run. The same request can run on each package of the workspace at once, each in its own sandbox, and nothing is installed on a laptop.

## Frequently asked questions

### Does it use Svelte Testing Library or the browser mode the primitives package uses?

Svelte Testing Library on jsdom, because the prompt asks for it and the components chosen do not need a real layout engine. Where a component depends on `matchMedia` or the clipboard, the test stubs them. If a component only makes sense with real geometry, the agent says so in the description rather than forcing a jsdom test.

### Will it change the components to make them testable?

No. The prompt says to change no component code, and the agent follows that. If a component is hard to reach without a `data-testid`, the test queries by role or label instead and the description notes the limitation.

### Does it work with pnpm workspaces and the catalog protocol?

Yes. The agent adds devDependencies to the one package it tests and lets pnpm update the lockfile; `catalog:` entries in `pnpm-workspace.yaml` stay as they are. The root `test` script, which runs only the published packages, is left alone.

### Which Svelte version does the sandbox use?

The one the lockfile resolves, Svelte 5 here, since the agent installs from `pnpm-lock.yaml`. It does not bump Svelte, Vite or SvelteKit to make a test pass.

### What if a test finds a real bug?

The test stays red, the agent leaves the component untouched, and the pull request description names the component, the behaviour and the failing assertion so a person can decide whether the component or the expectation is wrong.
