> For AI agents: the complete documentation index is available at /llms.txt, the full documentation bundle is available at /llms-full.txt.

# CI

Rstest uses the same CLI in CI as it does locally. The main difference is that CI should run tests in run mode with `rstest`, then upload any reports that other tools need to read.

## Basic workflow

Use `rstest` in CI so the process exits after one test pass. If your project already has a `test` script, keep CI calling that script and make the script run `rstest`.

```json title="package.json"
{
  "scripts": {
    "test": "rstest"
  }
}
```

A minimal GitHub Actions workflow installs dependencies, restores the package-manager cache, and runs the test script:

```yaml title=".github/workflows/test.yml"
name: Test

on:
  push:
  pull_request:

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: pnpm/action-setup@v4
        with:
          version: 11

      - uses: actions/setup-node@v4
        with:
          node-version: 22.12.0
          cache: pnpm

      - run: pnpm install --frozen-lockfile
      - run: pnpm test
```

When Rstest detects GitHub Actions and no reporter is manually configured, it automatically enables the `github-actions` reporter. Failed assertions are turned into GitHub annotations, and a Markdown summary is appended to the workflow summary. See [reporters](/guide/basic/reporters.md#github-actions-reporter) for reporter details.

Other CI systems can use the same commands. The important part is to run `pnpm install --frozen-lockfile` before `pnpm rstest` or your package script.

## Add coverage

Coverage collection is opt-in. Install the provider you want to use before enabling `--coverage`:

- [@rstest/coverage-istanbul](https://github.com/web-infra-dev/rstest/tree/main/packages/coverage-istanbul): the default Istanbul provider, works in Node mode and Browser Mode.
- [@rstest/coverage-v8](https://github.com/web-infra-dev/rstest/tree/main/packages/coverage-v8): V8-based provider for Node mode and headless, non-watch Chromium Browser Mode runs.

```bash
pnpm add @rstest/coverage-istanbul -D
pnpm rstest --coverage
```

Upload the generated `coverage/` directory if your CI needs to keep the HTML report or pass coverage data to another service:

```yaml
- run: pnpm rstest --coverage --coverage.reportOnFailure

- uses: actions/upload-artifact@v4
  if: always()
  with:
    name: coverage
    path: coverage
```

Use `if: always()` when the report is useful for failed runs too. For available providers, reporters, thresholds, and output paths, see [coverage](/config/test/coverage.md).

## Run browser tests in CI

[Rstest Browser Mode](/guide/browser-testing.md) and [@rstest/playwright](https://github.com/web-infra-dev/rstest/tree/main/packages/playwright) both use [Playwright](https://github.com/microsoft/playwright) to launch browsers. Installing the `playwright` npm package provides the automation API, but the browser executable must be provided separately.

### Choose how to provide the browser

The portable default is to install the Playwright browser that matches the package version:

```bash
pnpm exec playwright install --with-deps chromium
```

Choose the setup that matches the coverage and reproducibility your workflow needs:

| Requirement                                          | Browser setup                                                                                                                                          |
| ---------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Fast Chrome runs on a standard GitHub-hosted runner  | Skip `playwright install` and launch the preinstalled Chrome with `channel: 'chrome'`.                                                                 |
| A Chromium version pinned to the Playwright package  | Run `playwright install --with-deps chromium`. For headless-only runs without a channel, add `--only-shell` to skip the full headed Chromium download. |
| Firefox or WebKit coverage                           | Install the matching Playwright browser with `playwright install --with-deps firefox` or `playwright install --with-deps webkit`.                      |
| A self-hosted runner, container job, or custom image | Install the Playwright browser, or provision Chrome in the image before selecting `channel: 'chrome'`.                                                 |

The standard [GitHub-hosted runner images](https://github.com/actions/runner-images#available-images), including `ubuntu-latest`, provide Google Chrome. Selecting its `chrome` channel lets both Rstest integrations use that executable without downloading Playwright Chromium. GitHub updates runner software regularly, so this trades a pinned browser revision for faster setup. The workflow's **Set up job → Runner Image → Included Software** link shows the [exact software for a run](https://docs.github.com/en/actions/concepts/runners/github-hosted-runners#preinstalled-software-for-github-owned-images).

Firefox and WebKit still require Playwright's browser downloads. Playwright relies on patched builds for those engines and cannot substitute the Firefox or Safari applications installed on the runner.

### Browser mode

Install [@rstest/browser](https://github.com/web-infra-dev/rstest/tree/main/packages/browser) and the Playwright API:

```bash
pnpm add @rstest/browser playwright -D
```

On a standard GitHub-hosted runner, pass the Chrome channel through the Browser Mode CLI and omit the browser installation step:

```yaml
- run: pnpm install --frozen-lockfile
- run: pnpm rstest --browser --browser.providerOptions.launch.channel=chrome
```

To keep the same optimization behind an environment check in `rstest.config.ts`, configure the provider instead:

```ts title="rstest.config.ts"
import { defineConfig } from '@rstest/core';

export default defineConfig({
  browser: {
    enabled: true,
    provider: 'playwright',
    providerOptions:
      process.env.GITHUB_ACTIONS === 'true'
        ? {
            launch: {
              channel: 'chrome',
            },
          }
        : undefined,
  },
});
```

In CI, `browser.headless` defaults to `true`, so no additional headless configuration is needed. See [Browser Mode getting started](/guide/browser-testing/getting-started.md) and [browser configuration](/config/test/browser.md) for other browser and provider options.

### @rstest/playwright

Install the Rstest fixtures and the Playwright API:

```bash
pnpm add @rstest/playwright playwright -D
```

Configure [@rstest/playwright](/guide/basic/e2e-testing.md) launch defaults in `rstest.config.ts`:

```ts title="rstest.config.ts"
import { defineConfig } from '@rstest/core';
import { definePlaywrightConfig } from '@rstest/playwright/config';

export default defineConfig({
  extends: definePlaywrightConfig({
    browserName: 'chromium',
    launchOptions:
      process.env.GITHUB_ACTIONS === 'true' ? { channel: 'chrome' } : undefined,
  }),
});
```

With this fixture, a standard GitHub-hosted workflow only needs to install npm dependencies and run Rstest; do not add a `playwright install` step. See the [@rstest/playwright guide](/guide/basic/e2e-testing.md) for fixture usage and trace artifacts.

## Split tests with shards

Use `--shard <index>/<count>` when the full suite is stable but too slow for one CI machine. Each shard runs a different subset of test files. To combine results and coverage afterward, run every shard with the `blob` reporter, upload the blob files, then merge them in a follow-up job.

```yaml
jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        shard: [1, 2, 3]
    steps:
      - uses: actions/checkout@v4
      - uses: pnpm/action-setup@v4
        with:
          version: 11
      - uses: actions/setup-node@v4
        with:
          node-version: 22.12.0
          cache: pnpm
      - run: pnpm install --frozen-lockfile
      - run: pnpm rstest --shard ${{ matrix.shard }}/3 --reporters=blob --coverage
      - uses: actions/upload-artifact@v4
        if: always()
        with:
          name: rstest-blob-${{ matrix.shard }}
          path: .rstest-reports
          include-hidden-files: true

  merge-reports:
    runs-on: ubuntu-latest
    needs: test
    if: always()
    steps:
      - uses: actions/checkout@v4
      - uses: pnpm/action-setup@v4
        with:
          version: 11
      - uses: actions/setup-node@v4
        with:
          node-version: 22.12.0
          cache: pnpm
      - run: pnpm install --frozen-lockfile
      - uses: actions/download-artifact@v4
        with:
          pattern: rstest-blob-*
          path: .rstest-reports
          merge-multiple: true
      - run: pnpm rstest merge-reports --coverage --cleanup
```

Each shard writes `.rstest-reports/blob-{index}-{count}.json` with its test results and collected coverage. With coverage providers that support deferred finalization, these blob-reporting shards do not generate coverage reports or check thresholds; a shard without the blob reporter still finalizes coverage normally. Older providers retain their existing per-run finalization behavior. The merge job collects those files into one `.rstest-reports/` directory, then `rstest merge-reports` adds untested files, runs the configured reporters, and checks coverage thresholds against the unified result. Put coverage finalization options in the shared configuration, or pass the same `--coverage.*` options to the merge command because shard-only CLI options are not stored in blobs. See [sharding](/guide/basic/cli.md#sharding-tests) and [`rstest merge-reports`](/guide/basic/cli.md#rstest-merge-reports) for details.

## Publish machine-readable reports

CI tools often need structured report files in addition to terminal output. Add reporters when you need XML, JSON, Markdown, or blob output:

```ts title="rstest.config.ts"
import { defineConfig } from '@rstest/core';

export default defineConfig({
  reporters: [
    'default',
    ['junit', { outputPath: './reports/junit.xml' }],
    ['json', { outputPath: './reports/rstest.json' }],
  ],
});
```

Then upload the report directory:

```yaml
- run: pnpm rstest

- uses: actions/upload-artifact@v4
  if: always()
  with:
    name: rstest-reports
    path: reports
```

Use `junit` for CI test-result integrations, `json` for custom tooling, `md` for Markdown summaries, `github-actions` for GitHub annotations, and `blob` for sharded report merging. See [reporters](/guide/basic/reporters.md) for the complete list and options.

## Cache dependencies

Start with the package-manager cache because it is safe and usually gives the biggest CI improvement. In GitHub Actions, `actions/setup-node` with `cache: pnpm` restores the pnpm store based on the lockfile.

Do not cache Playwright browser binaries by default. Restoring the cache can take about as long as downloading the browser, and Linux system dependencies are not included in that cache. Prefer the preinstalled GitHub Chrome when its version policy fits your workflow; otherwise install only the required browser and use `--only-shell` for headless-only Chromium runs.

## Recommended checklist

- Use Node.js `^20.19.0` or `>=22.12.0` to match Rstest's supported runtime range.
- Run `rstest` in CI, either directly or through a package script.
- Install coverage and browser packages only when the workflow needs those features.
- Upload coverage, JUnit, JSON, or blob artifacts with `if: always()` if they help debug failures.
- Add sharding only after the single-machine workflow is stable.
- Choose explicitly between a version-matched Playwright browser and a preinstalled system Chrome, and keep the selected browser, channel, and installation step aligned.
