# Vitest reporter configuration

> Every option of @probara/vitest-reporter with its environment variable, type and default, how they combine, and how the reporter behaves on the network and in watch mode.

Every setting of `@probara/vitest-reporter` is an option of the reporter in the Vitest config, and
most have a `PROBARA_*` environment variable too. Keep the token in the environment, the project in
the config (or the environment), and set the rest where it fits your pipeline.

## Quick path

1. Create an app token from the **Vitest** card in **Workspace › Integrations**
   ([get started](https://docs.probara.net/en/developers/ci-integrations/vitest/#1-create-an-app-token)) and store it as a
   CI secret named `PROBARA_API_TOKEN` (each
   [CI provider](https://docs.probara.net/en/developers/ci-integrations/vitest/ci-providers/) shows where).
2. Set the Project ID: `projectId` in the reporter options, or `PROBARA_PROJECT`. A Project ID is a
   capital letter, then capital letters or digits (`SHOP`, `E2E`): any other value turns reporting
   off with an error.
3. Everything else has a default. The run is named after the CI build, and its branch, commit and
   build URL come from the CI on their own.

```ts
// vitest.config.mts
import { defineConfig } from 'vitest/config';

export default defineConfig({
  test: {
    reporters: ['default', ['@probara/vitest-reporter', { projectId: 'SHOP' }]],
  },
});
```

Keep `'default'` in the list: a `reporters` list replaces Vitest's own, and `default` is the one you
read in the terminal. The reporter entry is the same in any file Vitest reads its config from
(`vitest.config.ts`, the `test` section of `vite.config.ts`...). The reporter adds its own setup
file and runner to every Vitest project of the config, so there is nothing to add to `setupFiles`
or `runner`.

Do not put the token in the Vitest config: the config is committed. `apiToken` exists for code that
reads the token from its own secret store.

## Sources and precedence

The first source that sets a value wins:

| Order | Source                                         | Example                                   |
| ----- | ---------------------------------------------- | ----------------------------------------- |
| 1     | An option of the reporter in the Vitest config | `{ run: { name: 'Release 2.4' } }`        |
| 2     | A `PROBARA_*` variable                         | `PROBARA_RUN_NAME=Nightly`                |
| 3     | CI detection                                   | `CI #42` from the workflow and run number |
| 4     | The default                                    | `Automated run 2026-09-29 14:05 UTC`      |

- An option set to `undefined` never hides its variable, and a blank variable counts as unset. To
  let a variable override an option, leave the option out (or pass `process.env.X ?? default`).
- Booleans accept `true`, `1`, `yes`, `on` and `false`, `0`, `no`, `off`. Lists are
  comma-separated in variables (`PROBARA_RUN_TAGS=nightly,smoke`).
- CI detection only fills the run's name and its source (branch, commit, build URL, and in
  GitHub Actions the workflow run that groups parallel jobs). What each CI
  fills in is in [CI providers](https://docs.probara.net/en/developers/ci-integrations/vitest/ci-providers/).

Options and variables can be mixed. This config sets the project and tags:

```ts
reporters: [
  'default',
  ['@probara/vitest-reporter', { projectId: 'SHOP', run: { tags: ['unit', 'nightly'] } }],
],
```

and the pipeline adds the run's environment and name:

```bash
PROBARA_ENVIRONMENT=staging PROBARA_RUN_NAME=Nightly npx vitest run
```

## Options

| Option                   | Variable                       | Type                                    | Default                                        | What it does                                                                                                                                                                               |
| ------------------------ | ------------------------------ | --------------------------------------- | ---------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `enabled`                | `PROBARA_ENABLED`              | `boolean`                               | on                                             | `false` turns reporting off: nothing is sent, and nothing is logged                                                                                                                        |
| `apiToken`               | `PROBARA_API_TOKEN`            | `string`                                | none (required)                                | The app token. Keep it in the environment                                                                                                                                                  |
| `projectId`              | `PROBARA_PROJECT`              | `string`                                | none (required)                                | The Project ID (capital letters and digits), such as `SHOP`                                                                                                                                |
| `baseUrl`                | `PROBARA_BASE_URL`             | `string`                                | `https://app.probara.net`                      | The Probara to report to ([self-hosted](#self-hosted-probara))                                                                                                                             |
| `run.ulid`               | `PROBARA_RUN_ULID`             | `string`                                | none: a new run                                | Report into an existing run ([an existing run](https://docs.probara.net/en/developers/ci-integrations/vitest/results-and-retries/#an-existing-run))                                                                |
| `run.name`               | `PROBARA_RUN_NAME`             | `string`                                | the CI build name, else `Automated run <date>` | Name of a new run ([the run](https://docs.probara.net/en/developers/ci-integrations/vitest/results-and-retries/#the-run))                                                                                          |
| `run.description`        | `PROBARA_RUN_DESCRIPTION`      | `string`                                | none                                           | Description of a new run                                                                                                                                                                   |
| `run.environment`        | `PROBARA_ENVIRONMENT`          | `string`                                | none                                           | Environment of a new run by name, created when none matches                                                                                                                                |
| `run.environmentId`      | `PROBARA_ENVIRONMENT_ID`       | `string`                                | none                                           | Environment of a new run by ULID                                                                                                                                                           |
| `run.milestone`          | `PROBARA_MILESTONE`            | `string`                                | none                                           | Milestone of a new run by display id (`M-3`) or name                                                                                                                                       |
| `run.milestoneId`        | `PROBARA_MILESTONE_ID`         | `string`                                | none                                           | Milestone of a new run by ULID                                                                                                                                                             |
| `run.plan`               | `PROBARA_PLAN`                 | `string`                                | none                                           | Test plan of a new run by display id (`PLAN-2`) or name: the run starts with its cases                                                                                                     |
| `run.configurations`     | `PROBARA_CONFIGURATIONS`       | `{ group, name }[]`                     | none                                           | Configurations of a new run by name (`Browser=Chrome,OS=Linux`), each group once                                                                                                           |
| `run.configurationUlids` | `PROBARA_CONFIGURATION_ULIDS`  | `string[]`                              | none                                           | Configurations of a new run by ULID                                                                                                                                                        |
| `run.tags`               | `PROBARA_RUN_TAGS`             | `string[]`                              | none                                           | Tags of a new run                                                                                                                                                                          |
| `run.ulids`              | `PROBARA_RUN_ULIDS`            | `Record<string, string>`                | none                                           | The run to reuse in each project (`WEB=<ulid>,API=<ulid>`)                                                                                                                                 |
| `projects`               | `PROBARA_PROJECTS`             | `string[]`                              | none                                           | Other projects results may go to (`WEB,API`) ([several projects](https://docs.probara.net/en/developers/ci-integrations/vitest/sharding/#several-probara-projects))                                                |
| `source`                 | —                              | `false \| object (GitHub fields below)` | detected from the CI                           | `false` sends no source; an object overrides the detected fields one by one ([run source](https://docs.probara.net/en/developers/ci-integrations/vitest/results-and-retries/#run-source))                          |
| `source.branch`          | `PROBARA_BRANCH`               | `string`                                | detected from the CI                           | Branch of the run source                                                                                                                                                                   |
| `source.commit`          | `PROBARA_COMMIT`               | `string`                                | detected from the CI                           | Commit of the run source                                                                                                                                                                   |
| `source.buildUrl`        | `PROBARA_BUILD_URL`            | `string`                                | detected from the CI                           | CI build URL of the run source                                                                                                                                                             |
| `createMissingCases`     | `PROBARA_CREATE_MISSING_CASES` | `boolean`                               | `true`                                         | Create a case for a test that matches none                                                                                                                                                 |
| `suiteUlid`              | `PROBARA_SUITE_ULID`           | `string`                                | the project root                               | Suite that created cases go under                                                                                                                                                          |
| `closeRun`               | `PROBARA_CLOSE_RUN`            | `boolean`                               | `true` for a new run, `false` for a reused one | Close the run at the end (never in watch mode) ([closing](https://docs.probara.net/en/developers/ci-integrations/vitest/results-and-retries/#closing))                                                             |
| `closeRuns`              | —                              | `Record<string, boolean>`               | none                                           | Close the run of each project (`{ SHOP: true, WEB: false }`) when `closeRun` is not set                                                                                                    |
| `uploadAttachments`      | `PROBARA_UPLOAD_ATTACHMENTS`   | `boolean`                               | `true`                                         | `false` uploads no file ([attachments](https://docs.probara.net/en/developers/ci-integrations/vitest/steps-and-attachments/#probaraattach))                                                                        |
| `keyIncludesFile`        | `PROBARA_KEY_INCLUDES_FILE`    | `boolean`                               | `true`                                         | Start each automation key with the test file ([`keyIncludesFile`](#keyincludesfile))                                                                                                       |
| `captureOutput`          | `PROBARA_CAPTURE_OUTPUT`       | `boolean`                               | `false`                                        | Attach each attempt's console output as `stdout.log` and `stderr.log` ([`captureOutput`](#captureoutput))                                                                                  |
| `attachScreenshots`      | `PROBARA_ATTACH_SCREENSHOTS`   | `boolean`                               | `true`                                         | Attach a browser test's failure screenshots to its attempt; `false` turns them off ([`attachScreenshots`](#attachscreenshots))                                                             |
| `issueUrlTemplate`       | `PROBARA_ISSUE_URL_TEMPLATE`   | `string`                                | none                                           | The URL each `probara.issue(id)` becomes, with `%s` for the id ([`issueUrlTemplate`](#issueurltemplate))                                                                                   |
| `runCasesOnly`           | `PROBARA_RUN_CASES_ONLY`       | `boolean`                               | `false`                                        | Run only the tests of the cases of `run.ulid` ([`runCasesOnly`](#runcasesonly))                                                                                                            |
| `statusMapping`          | `PROBARA_STATUS_MAPPING`       | `{ passed, failed, skipped, blocked }`  | none                                           | Send a status as another (`failed=blocked`) ([statuses](https://docs.probara.net/en/developers/ci-integrations/vitest/results-and-retries/#status-mapping-and-filter))                                             |
| `statusFilter`           | `PROBARA_STATUS_FILTER`        | `string[]`                              | none                                           | Send no result with these statuses, after the mapping (`skipped`)                                                                                                                          |
| `resultsFile`            | `PROBARA_RESULTS_FILE`         | `string`                                | none                                           | JSON file for what could not be sent ([results file](https://docs.probara.net/en/developers/ci-integrations/vitest/results-and-retries/#results-file))                                                             |
| `assignFailedTo`         | `PROBARA_ASSIGN_FAILED_TO`     | `string[]`                              | none                                           | Assign each failed result without an assignee to one of these members, in turn ([assign failed results](https://docs.probara.net/en/developers/ci-integrations/vitest/results-and-retries/#assign-failed-results)) |
| `debug`                  | `PROBARA_DEBUG`                | `boolean`                               | `false`                                        | Log every request ([`PROBARA_DEBUG`](https://docs.probara.net/en/developers/ci-integrations/vitest/troubleshooting/#probara_debug))                                                                                |
| `rootDir`                | —                              | `string`                                | the Vitest root                                | Directory the file paths of automation keys are relative to                                                                                                                                |
| `chunkSize`              | —                              | `number`                                | `500`                                          | Results per report request, 1 to 500                                                                                                                                                       |
| `timeoutMs`              | —                              | `number`                                | `30000`                                        | Timeout of one HTTP attempt, in milliseconds, 1 to 600000 ([timeouts](#timeouts))                                                                                                          |
| `maxRetries`             | —                              | `number`                                | `4`                                            | Retries of a failed request, 0 to 10 ([retries and backoff](#retries-and-backoff))                                                                                                         |
| `attachmentConcurrency`  | —                              | `number`                                | `2`                                            | Results whose attachments upload at the same time, 1 to 8                                                                                                                                  |

`rootDir` defaults to Vitest's `root`, which is the directory `vitest` runs in unless the config or
`--root` sets another.

In GitHub Actions the source also gets these fields, detected from `GITHUB_*` variables:

| Option                     | Variable | Type     | Default                      | What it does                                                  |
| -------------------------- | -------- | -------- | ---------------------------- | ------------------------------------------------------------- |
| `source.provider`          | —        | `string` | detected from GitHub Actions | `github_actions` (detected) or `other`                        |
| `source.repository`        | —        | `string` | detected from GitHub Actions | Repository of the run source, `owner/name`                    |
| `source.commitAuthor`      | —        | `string` | detected from GitHub Actions | Who triggered the build                                       |
| `source.pullRequestNumber` | —        | `number` | detected from GitHub Actions | Pull request of the build                                     |
| `source.workflow`          | —        | `string` | detected from GitHub Actions | CI workflow of the run source                                 |
| `source.ciRunId`           | —        | `string` | detected from GitHub Actions | CI run id: the jobs of one GitHub Actions run share one run   |
| `source.ciRunAttempt`      | —        | `number` | detected from GitHub Actions | Attempt of the CI run: a re-run reports into a run of its own |
| `source.ciJob`             | —        | `string` | detected from GitHub Actions | CI job of the run source                                      |

### Runtime options

These are seams for code (a test of the reporter, a custom logger), not settings of a job. They have
no variable:

| Option   | Type                           | Default                     | What it does                                                    |
| -------- | ------------------------------ | --------------------------- | --------------------------------------------------------------- |
| `logger` | `{ debug, info, warn, error }` | `[probara]` lines on stderr | Where the reporter logs                                         |
| `env`    | `Record<string, string>`       | `process.env`               | Where `PROBARA_*` and CI variables are read from                |
| `fetch`  | `function`                     | the global `fetch`          | The HTTP client ([your own HTTP client](#your-own-http-client)) |
| `sleep`  | `function`                     | `setTimeout`                | Waits between retries                                           |
| `random` | `function`                     | `Math.random`               | Jitter of the retry backoff                                     |
| `now`    | `function`                     | the current time            | Clock of the default run name and of `Retry-After` dates        |

The default logger writes through Vitest's own logger, on stderr: stdout stays Vitest's, so a
reporter that prints JSON there never gets a line of the reporter's.

### Unknown options

An option the reporter does not know is left out with a warning, and the run still reports:

```ts
reporters: ['default', ['@probara/vitest-reporter', { captureOutputs: true }]],
```

```text
$ npx vitest run
[probara] Ignored the unknown option "captureOutputs" of @probara/vitest-reporter
[probara] Sending 3 results of 3 tests (3 passed, 0 failed, 0 skipped, 0 blocked)
[probara] Recorded 3 results (2 new cases, 0 unmatched) in R-1 (closed): https://app.probara.net/projects/SHOP/runs/R-1
```

## Settings only the Vitest reporter has

### `keyIncludesFile`

Each test is matched to its case by an automation key: the test file relative to `rootDir` (the
Vitest root), then the `describe` blocks and the title, each after `>` between spaces, without the
case ids: `tests/cart.test.ts > cart > adds an item`. That is the key `probara import junit` gives
the same test on Vitest's JUnit output, which always names the file.

`keyIncludesFile: false` (`PROBARA_KEY_INCLUDES_FILE=false`) leaves the file out of every key. Use
it when your cases already have keys without the file, made by hand or by another tool, so each
test keeps its case. A test then gets the same key as a test with the same `describe` blocks and
title in another file.

```ts
reporters: ['default', ['@probara/vitest-reporter', { keyIncludesFile: false }]],
```

```json
[
  { "automationKey": "cart > adds an item", "caseDisplayId": "SHOP-12" },
  { "automationKey": "cart > removes an item" },
  { "automationKey": "login > logs in with a valid password" }
]
```

Changing `keyIncludesFile` changes every key: tests that name no case in their titles then create
new cases. A case the report creates goes in a suite named after the test file or, with
`keyIncludesFile: false`, after its outermost `describe`, when it has one. Vitest projects add
nothing to the key ([linking](https://docs.probara.net/en/developers/ci-integrations/vitest/linking/#automation-keys)).

### `captureOutput`

`captureOutput: true` (`PROBARA_CAPTURE_OUTPUT=true`) attaches what each attempt writes with
`console.log`, `console.info` and `console.debug` as `stdout.log`, and with `console.warn` and
`console.error` as `stderr.log`. Vitest still prints the output as usual. It needs nothing else: no
setup file, no change to the tests.

```ts
reporters: ['default', ['@probara/vitest-reporter', { captureOutput: true }]],
```

What is and is not captured is in
[console output](https://docs.probara.net/en/developers/ci-integrations/vitest/steps-and-attachments/#console-output).

### `attachScreenshots`

In browser mode, Vitest takes a screenshot when a test fails, and the reporter attaches it to the
failed attempt, as `image/png`. `attachScreenshots: false` (`PROBARA_ATTACH_SCREENSHOTS=false`)
leaves the screenshots out. A test that runs in Node has none
([failure screenshots](https://docs.probara.net/en/developers/ci-integrations/vitest/steps-and-attachments/#failure-screenshots)).

### `issueUrlTemplate`

`probara.issue(id)` names an issue of your tracker: `issueUrlTemplate` turns it into a link of the
result, named by the id, with the URL-encoded id in place of `%s`
(`https://jira.example.com/browse/%s`). The template is an `http` or `https` URL with `%s`; any
other value turns reporting off with an error. Without a template, issues are left out with a
warning ([links and issues](https://docs.probara.net/en/developers/ci-integrations/vitest/metadata/#links-and-issues)).

### `runCasesOnly`

`runCasesOnly: true` (or `PROBARA_RUN_CASES_ONLY=true`) runs only the tests of the cases of the run
`run.ulid` (`PROBARA_RUN_ULID`), and reports into that run, leaving it open. Nothing else goes in
the config: the setup file the reporter adds to every Vitest project does the skipping. Turn it on
per job with the variables rather than in the config: with the option in the config, every job
without a run ULID warns and runs every test.
[Run selection](https://docs.probara.net/en/developers/ci-integrations/vitest/run-selection/) has the matching rules and
every fallback, including browser mode and shards that write a blob.

## What happens with each setup

| Setup                                        | What the reporter does                                                      |
| -------------------------------------------- | --------------------------------------------------------------------------- |
| No token and no project, or `enabled: false` | Stays off and quiet: nothing is sent, and nothing is logged (but at debug). |
| Only one of them, or an invalid value        | Reporting is off; each problem is logged, and the tests run as usual.       |
| A token and a project, every value valid     | Reports.                                                                    |

A value of the wrong type (such as `run.tags: 'nightly'` instead of a list) is an invalid value:

```ts
reporters: ['default', ['@probara/vitest-reporter', { run: { tags: 'nightly' } }]],
```

```text
$ npx vitest run
[probara] Probara reporting is off: run.tags must be a list of strings
```

Problems name the option or the variable at fault, never its value, so a token never shows up. The
reporter never fails the run: whatever goes wrong while reporting is a `[probara]` line on stderr,
and the exit code of `vitest` stays the one the tests give.

## Turn reporting off

Set `enabled: false` or `PROBARA_ENABLED=false`, or leave the token out: the reporter sends nothing
and stays quiet. Builds of pull requests from forks get no secrets, so they report nothing on their
own. The reporter still adds its setup file, so the `probara.*` helpers keep recording, and nothing
is sent ([how metadata travels](https://docs.probara.net/en/developers/ci-integrations/vitest/metadata/#how-metadata-travels)).
To keep what a run could not send, even with reporting off, set a
[results file](https://docs.probara.net/en/developers/ci-integrations/vitest/results-and-retries/#results-file).

## Self-hosted Probara

Point the reporter at another Probara with `baseUrl` or `PROBARA_BASE_URL`: the URL of the app,
without a path. Requests go to that host and nowhere else. For proxies and private certificate
authorities, see [network](#network).

## Network

The reporter talks HTTPS to one host, the base URL (`https://app.probara.net` by default), from
Vitest's main process: the test workers and the browsers of browser mode never call Probara.

### When requests happen

| When                                                                   | Requests                                                                          |
| ---------------------------------------------------------------------- | --------------------------------------------------------------------------------- |
| When a run starts, before any test file runs, only with `runCasesOnly` | `GET` the case keys of the run, 200 cases per page until the last page            |
| When the run ends, after the last test file                            | The reports, then the attachment uploads, then the close of the run (when needed) |

Nothing is sent while the tests run: the reporter collects the results and sends them once Vitest
is done. Vitest waits for the reporter before it exits, so the time the requests take, retries
included, is part of the `vitest run` command. In [watch mode](#watch-mode), each re-run makes its
own requests when it ends. A shard that writes a blob sends nothing: the `vitest --merge-reports`
run makes the requests for every shard
([sharding](https://docs.probara.net/en/developers/ci-integrations/vitest/sharding/)).

With `runCasesOnly`, the case keys are read once per run (and once per re-run in watch mode). When
they cannot be read after the retries, every test runs and is reported, with one warning
([run selection](https://docs.probara.net/en/developers/ci-integrations/vitest/run-selection/)); the retries of that read
are logged at debug only.

### Timeouts

| Setting     | Default                            | Range       | What it bounds                                                    |
| ----------- | ---------------------------------- | ----------- | ----------------------------------------------------------------- |
| `timeoutMs` | 30000 ms                           | 1 to 600000 | One attempt of a request, the response body included              |
| uploads     | 120000 ms or `timeoutMs` if larger | —           | One attempt of an attachment upload (a large file on a slow link) |

A timed-out attempt is retried like a network error.

### Retries and backoff

| Answer                                                         | What happens                            |
| -------------------------------------------------------------- | --------------------------------------- |
| Network error, timeout, 408, 429, 500, 502, 503, 504           | Retried                                 |
| 409 with a `Retry-After` (the same request is still in flight) | Retried                                 |
| A `201` whose body never arrived                               | Retried; the idempotency key replays it |
| Any other answer (400, 401, 403, 404, 409, 413, 422...)        | Not retried: the request fails          |

- `maxRetries` (0 to 10, default 4) sets the retries after the first attempt: 5 attempts in all by
  default.
- The wait before retry _n_ is 1 s × 2ⁿ⁻¹, at most 30 s, plus up to 20 % of random jitter. The
  backoff is fixed: no option changes it.
- A `Retry-After` header (seconds or an HTTP date) replaces that wait, capped at 120 s.
- Each retry is logged, with the attempt and the wait:
  `Report attempt 1 of 5 got 503; retrying in 1000 ms`.

With the defaults, a report that keeps failing gives up after about 15 s of waiting (1 + 2 + 4 + 8
s, up to 18 s with the jitter), plus the attempts themselves, each at most `timeoutMs` (30 s by
default). The worst case is a `Retry-After` on every answer: 120 s before each retry, so 8 minutes
of waiting with the default 4 retries (20 minutes with `maxRetries: 10`), plus up to 5 attempts of
30 s. That is per request, and a failed report stops the later ones of its project. Vitest waits
for the reporter before it exits: lower `maxRetries` or `timeoutMs` to bound a CI step.

### Rate limit

Probara limits API requests per organization (60 requests per minute on Free, 1,200 on Pro, and 3,000 on Enterprise),
for every token of the organization together. What a run costs:

| Request           | How many                                                                   |
| ----------------- | -------------------------------------------------------------------------- |
| Case keys         | 1 per 200 cases of the run, only with `runCasesOnly`                       |
| Report            | 1 per chunk of up to 500 results (`chunkSize`)                             |
| Attachment stage  | 1 per result with files (per 20 files or 64 MiB)                           |
| Attachment commit | 1 per result with files                                                    |
| Close             | 1, when attachments were queued (otherwise the last report closes the run) |

A run of 2000 attempts without files costs 4 requests; one where 100 attempts attach a file (or
their console output, with `captureOutput`) costs about 200 more. `429` answers are retried after
their `Retry-After`. To spread uploads, lower `attachmentConcurrency`; to send fewer files, attach
only what matters, or set `uploadAttachments: false`.

### Chunks and ordering

- Results go out in chunks of `chunkSize` (1 to 500, default 500), **one after another**, in the
  order of Vitest's test files, each file's attempts in order. A run case keeps the last outcome
  recorded, so the order matters. A chunk also stays within Probara's totals of steps and tags per
  report.
- The first chunk creates the run (unless `run.ulid` names one); the others reuse it.
- When a chunk fails after its retries, the later chunks of that project are not sent and the run is
  left open; a [results file](https://docs.probara.net/en/developers/ci-integrations/vitest/results-and-retries/#results-file)
  keeps them.
- Uploads start once their report is recorded, `attachmentConcurrency` results at a time (1 to 8,
  default 2), and the run is closed after the uploads.

### Idempotency

Every report, run creation, run close and attachment commit carries an
[`Idempotency-Key`](https://docs.probara.net/en/developers/api/idempotency/), the same on each retry of that request. A
retried report whose first attempt was recorded after all is replayed by Probara, never recorded
twice. Running `vitest` again is a new run, with every result recorded again.

### Proxies

The reporter uses Node's built-in `fetch`, and has no proxy option of its own:

| Node.js                     | Proxy support                                                                           |
| --------------------------- | --------------------------------------------------------------------------------------- |
| 22.21 or later, 24 or later | Set `NODE_USE_ENV_PROXY=1` with `HTTPS_PROXY` (and `NO_PROXY`); `fetch` goes through it |
| 22.12 to 22.20              | Not supported: `fetch` ignores `HTTPS_PROXY` and `HTTP_PROXY`                           |

```bash
NODE_USE_ENV_PROXY=1 HTTPS_PROXY=http://proxy.example.com:8080 npx vitest run
```

Node added `fetch` support for `NODE_USE_ENV_PROXY` in
[22.21.0](https://github.com/nodejs/node/blob/main/doc/changelogs/CHANGELOG_V22.md#22.21.0) and
24.0.0 ([Node's docs](https://nodejs.org/api/cli.html#node_use_env_proxy1)). The variables reach
Vitest's workers too: put `localhost` in `NO_PROXY` if your tests reach a local server through
`fetch`. On older Node versions, upgrade Node, give the reporter a `fetch` of your own
([below](#your-own-http-client)), or allow the base URL through the firewall.

### Custom certificate authorities

For a self-hosted Probara behind a private certificate authority, give Node the CA certificate
(PEM) when it starts:

```bash
NODE_EXTRA_CA_CERTS=/etc/ssl/certs/company-ca.pem npx vitest run
```

Node reads `NODE_EXTRA_CA_CERTS` once at startup, so set it in the environment of the step. On Node
22.19 and later, `NODE_USE_SYSTEM_CA=1` trusts the operating system's certificate store instead.
There is no option to turn TLS verification off.

### Your own HTTP client

`fetch` replaces the HTTP client: a function with the signature of the global `fetch`, which every
request of the reporter goes through. Use it for what the environment variables above cannot do,
such as a proxy library or a header a company gateway asks for:

```ts
// vitest.config.mts
import { defineConfig } from 'vitest/config';

/** The global fetch, with the header the company gateway asks for. */
const gatewayFetch: typeof fetch = (input, init) => {
  // Headers takes every form of init.headers: a plain object, a Headers or a list of pairs.
  const headers = new Headers(init?.headers);
  headers.set('X-Gateway', 'ci');
  return fetch(input, { ...init, headers });
};

export default defineConfig({
  test: {
    reporters: [
      'default',
      ['@probara/vitest-reporter', { projectId: 'SHOP', fetch: gatewayFetch }],
    ],
  },
});
```

The reporter still sets the timeouts, the retries and its own headers (the token, the
`Idempotency-Key`); keep them as they reach your function.

### Not supported: a network profiler

Qase's network profiler records the HTTP requests a test makes as steps of its result. The reporter
records no requests: they would fill each result with steps of noise, up to the step limit, and
they cannot be told apart when tests run in parallel. Attach what matters with
[`probara.attach()`](https://docs.probara.net/en/developers/ci-integrations/vitest/steps-and-attachments/#probaraattach).
What else other tools have and the reporter does not is in
[not supported](https://docs.probara.net/en/developers/ci-integrations/vitest/migrating/#not-supported).

## Watch mode

`vitest --watch` (and `vitest` alone in a terminal, which starts watch mode) re-runs tests as you
save files. The reporter gives the whole session **one** Probara run: the first run creates it,
every re-run reports into it, and it stays open, since a closed run would refuse the next re-run.

```text
$ npx vitest --watch
[probara] Sending 3 results of 3 tests (3 passed, 0 failed, 0 skipped, 0 blocked)
[probara] Recorded 3 results (2 new cases, 0 unmatched) in R-1 (open): https://app.probara.net/projects/SHOP/runs/R-1
[probara] Watch mode: every re-run reports into R-1 of SHOP, which stays open: close it in Probara, or with probara run close --project SHOP --run-ulid 01J9Z3K4M5N6P7Q8R9S0T1V2W3
[probara] Sending 2 results of 2 tests (2 passed, 0 failed, 0 skipped, 0 blocked)
[probara] Recorded 2 results (0 new cases, 0 unmatched) in R-1 (open): https://app.probara.net/projects/SHOP/runs/R-1
```

Above, the first run created `R-1` and said so once; then `tests/cart.test.ts` was saved, and the
re-run of its 2 tests reported into the same run. Each `Sending` line counts its own run or re-run.

| In a watch session                     | What the reporter does                                                          |
| -------------------------------------- | ------------------------------------------------------------------------------- |
| The first run (or re-run) with results | Creates the run, and logs the `Watch mode` line with the command that closes it |
| Every later re-run                     | Reports the tests it ran into that run, and counts them in its own summary line |
| A change to the Vitest config          | Vitest restarts, and the session keeps its run                                  |
| The end of the session (`q`, `Ctrl+C`) | Nothing: the run stays open. Close it in Probara, or with the logged command    |
| `closeRun`, `closeRuns`                | Ignored: a watch session never closes its runs                                  |
| `run.ulid` (`PROBARA_RUN_ULID`)        | Every re-run reports into that run, which stays open                            |
| `projects`                             | One run per Probara project, created with its first result                      |
| `runCasesOnly`                         | The run's cases are read again before each re-run                               |
| A warning about the tests of a re-run  | Logged once in that re-run                                                      |

Close the session's run when you are done:

```bash
npx @probara/cli run close --project SHOP --run-ulid 01J9Z3K4M5N6P7Q8R9S0T1V2W3
```

Watch mode is for your own machine: the reporter reports only with a token and a project, so a
local session sends nothing unless you set them. The `probara.*` helpers and the setup file work in
watch mode as in any run. With
[several Probara projects](https://docs.probara.net/en/developers/ci-integrations/vitest/sharding/#several-probara-projects),
each project gets its own run, created with its first result in the session, and its own
`Watch mode` line; a project whose tests have not reported yet has no run until they do.

### When the run is closed during the session

Someone may close (or delete) the session's run in Probara while you keep working. The next re-run
is refused, so the reporter sends that re-run's results into a new run at once, and every later
re-run reports into the new one:

```text
$ npx vitest --watch
[probara] Sending 3 results of 3 tests (3 passed, 0 failed, 0 skipped, 0 blocked)
[probara] Recorded 3 results (2 new cases, 0 unmatched) in R-1 (open): https://app.probara.net/projects/SHOP/runs/R-1
[probara] Watch mode: every re-run reports into R-1 of SHOP, which stays open: close it in Probara, or with probara run close --project SHOP --run-ulid 01J9Z3K4M5N6P7Q8R9S0T1V2W3
[probara] Sending 2 results of 2 tests (2 passed, 0 failed, 0 skipped, 0 blocked)
[probara] 2 results were not sent: Probara answered 409 conflict: <message from Probara>. No run was created or updated
[probara] The run R-1 of SHOP was closed: sent the 2 results of this re-run into a new run
[probara] Recorded 2 results (0 new cases, 0 unmatched) in R-2 (open): https://app.probara.net/projects/SHOP/runs/R-2
[probara] Watch mode: every re-run reports into R-2 of SHOP, which stays open: close it in Probara, or with probara run close --project SHOP --run-ulid 01J9Z3M8Q2R4T6V8X0Y2Z4A6B8
```

- **Only a refusal moves the session.** A closed run (`409 conflict`) or a deleted one
  (`404 not_found`) refuses the re-run for good, so its results go into a new run. Any other failure
  (Probara unreachable, a `409` retried because the same report was still in flight) may have
  recorded the results after all: they are never sent twice, and the session keeps its run.
- **A run closed halfway through a re-run.** When the run refuses a re-run after part of it was
  recorded, only the results it did not record go into the new run; the rest stays in the old run,
  and the log line names both.
- **With a results file**, a refused re-run is kept in the file, which still names the refused run,
  and the next re-run reports into a new run. The log line gives the command that sends the file
  into an open run instead (`--run-ulids WEB=<ulid>` for another project). When the file cannot be
  written, the re-run goes into a new run as above.

```ts
reporters: ['default', ['@probara/vitest-reporter', { resultsFile: 'probara-results.json' }]],
```

```text
$ npx vitest --watch
[probara] Sending 3 results of 3 tests (3 passed, 0 failed, 0 skipped, 0 blocked)
[probara] Recorded 3 results (2 new cases, 0 unmatched) in R-1 (open): https://app.probara.net/projects/SHOP/runs/R-1
[probara] Watch mode: every re-run reports into R-1 of SHOP, which stays open: close it in Probara, or with probara run close --project SHOP --run-ulid 01J9Z3K4M5N6P7Q8R9S0T1V2W3
[probara] Sending 2 results of 2 tests (2 passed, 0 failed, 0 skipped, 0 blocked)
[probara] 2 results were not sent: Probara answered 409 conflict: <message from Probara>. No run was created or updated
[probara] Wrote the 2 results that were not sent to /work/shop/probara-results.json: send them with probara import results /work/shop/probara-results.json
[probara] The run R-1 of SHOP was closed: the results file /work/shop/probara-results.json keeps this re-run, but names R-1, which refuses it: send it with probara import results /work/shop/probara-results.json --run-ulid <ulid of an open run>; the next re-run reports into a new run
```

Related: [Get started](https://docs.probara.net/en/developers/ci-integrations/vitest/) ·
[Results and retries](https://docs.probara.net/en/developers/ci-integrations/vitest/results-and-retries/) ·
[Troubleshooting](https://docs.probara.net/en/developers/ci-integrations/vitest/troubleshooting/) · Source:
[configuration.md](https://github.com/cynch-me/probara-sdk/blob/main/packages/vitest-reporter/docs/configuration.md),
[network.md](https://github.com/cynch-me/probara-sdk/blob/main/packages/vitest-reporter/docs/network.md),
[watch.md](https://github.com/cynch-me/probara-sdk/blob/main/packages/vitest-reporter/docs/watch.md)
