# Cypress reporter configuration

> The three registrations of @probara/cypress-reporter, every option with its environment variable, type and default, how they combine, the network, and why cypress open reports nothing.

Every setting of `@probara/cypress-reporter` is an option of the reporter in the Cypress config
(`reporterOptions`), 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 **Cypress** card in **Workspace › Integrations**
   ([get started](https://docs.probara.net/en/developers/ci-integrations/cypress/#1-create-an-app-token)) and store it as a CI
   secret named `PROBARA_API_TOKEN`.
2. Set the Project ID: `projectId` in `reporterOptions`, 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. Register the reporter, the plugin and the support file ([registration](#registration)).
   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.

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

## Registration

The reporter registers in three places, each with its own job:

```js
// cypress.config.js
const { defineConfig } = require('cypress');
const { probaraNodeEvents } = require('@probara/cypress-reporter/setup');

module.exports = defineConfig({
  e2e: {
    reporter: '@probara/cypress-reporter',
    reporterOptions: { projectId: 'SHOP' },
    setupNodeEvents(on, config) {
      return probaraNodeEvents(on, config);
    },
  },
});
```

```js
// cypress/support/e2e.js
import '@probara/cypress-reporter/support';
```

| Registration                                           | What it is                                       | What it turns on                                                                                        |
| ------------------------------------------------------ | ------------------------------------------------ | ------------------------------------------------------------------------------------------------------- |
| `reporter` and `reporterOptions`                       | The Mocha reporter Cypress builds for every spec | Every test event: one result per attempt, the keys, the screenshots of each attempt                     |
| `setupNodeEvents` (`@probara/cypress-reporter/setup`)  | The plugin, in the process that loads the config | The Probara run (created, then closed at the end), the screenshots and the video, the browser parameter |
| The support file (`@probara/cypress-reporter/support`) | The browser side, next to your specs             | The `probara.*` helpers, `captureOutput` and run selection                                              |

The plugin reads the same `reporterOptions` Cypress hands the reporter, so it needs no settings of
its own. Return the config it returns from `setupNodeEvents`, as `probaraNodeEvents(on, config)`
does: that config is how its settings reach the browser.

| What is missing  | What happens                                                                                                                                                                 |
| ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Nothing          | Everything: one run for the whole `cypress run`, closed at the end, with the screenshots of every failed attempt, the video and the `probara.*` helpers                      |
| The plugin       | Every result is still sent, but in **one run per spec**, each closed as its spec ends. No screenshot, no video, and no `probara.*` helper. One warning names what is missing |
| The support file | Results, screenshots and the video as usual. Every `probara.*` call does nothing, `captureOutput` captures nothing, run selection skips nothing                              |
| The reporter     | Nothing is reported at all: Cypress never builds this reporter                                                                                                               |

### With other plugins

Cypress keeps **one** handler per event: when two plugins register `after:run` (or `before:run`,
`before:spec`, `after:spec`, `after:screenshot`) with the same `on`, only the last one runs, and the
other is dropped without a word ([cypress#22428](https://github.com/cypress-io/cypress/issues/22428)).
Tasks are the exception: Cypress merges them. When another plugin registers any of those events,
such as [cypress-split](https://github.com/bahmutov/cypress-split), hand every plugin the `on` of
[cypress-on-fix](https://github.com/bahmutov/cypress-on-fix), which runs every handler in the order
they were registered:

```js
// cypress.config.js
const { defineConfig } = require('cypress');
const { probaraNodeEvents } = require('@probara/cypress-reporter/setup');

module.exports = defineConfig({
  e2e: {
    reporter: '@probara/cypress-reporter',
    reporterOptions: { projectId: 'SHOP' },
    setupNodeEvents(cypressOn, config) {
      const on = require('cypress-on-fix')(cypressOn);
      require('cypress-split')(on, config);
      probaraNodeEvents(on, config);
      on('after:run', () => {
        // Your own handler: it runs too.
      });
      return config;
    },
  },
});
```

`probaraNodeEvents` adds its settings to `config` itself, so returning `config` at the end keeps
them, together with what the other plugins changed. What each lost event costs is in
[troubleshooting](https://docs.probara.net/en/developers/ci-integrations/cypress/troubleshooting/#another-plugin-stopped-working-or-this-one-did).

### With `cypress-multi-reporters`

Cypress runs one reporter, so this one replaces the per-test lines of the default `spec` reporter.
To keep them, or to write JUnit XML at the same time, register
[`cypress-multi-reporters`](https://github.com/YOU54F/cypress-plugins/tree/master/cypress-multi-reporters)
and put this reporter's options under `probaraCypressReporterReporterOptions`, the key it builds
from the reporter's name. The plugin reads them from the same place:

```js
// cypress.config.js
module.exports = defineConfig({
  e2e: {
    reporter: 'cypress-multi-reporters',
    reporterOptions: {
      reporterEnabled: 'spec, @probara/cypress-reporter',
      probaraCypressReporterReporterOptions: { projectId: 'SHOP' },
    },
    setupNodeEvents(on, config) {
      return probaraNodeEvents(on, config);
    },
  },
});
```

## Sources and precedence

The first source that sets a value wins:

| Order | Source                                          | Example                                   |
| ----- | ----------------------------------------------- | ----------------------------------------- |
| 1     | An option of the reporter in the Cypress 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/cypress/ci-providers/).

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

```js
reporterOptions: { projectId: 'SHOP', run: { tags: ['e2e', 'nightly'] } },
```

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

```bash
PROBARA_ENVIRONMENT=staging PROBARA_RUN_NAME=Nightly npx cypress 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/cypress/results-and-retries/#an-existing-run))                    |
| `run.name`               | `PROBARA_RUN_NAME`             | `string`                                | the CI build name, else `Automated run <date> <time> UTC` | Name of a new 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/cypress/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                                                                     |
| `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                                                                                                                        |
| `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                                                                                                                         |
| `keyIncludesFile`        | `PROBARA_KEY_INCLUDES_FILE`    | `boolean`                               | `true`                                                    | Start each automation key with the spec file ([`keyIncludesFile`](#keyincludesfile))                                                            |
| `captureOutput`          | `PROBARA_CAPTURE_OUTPUT`       | `boolean`                               | `false`                                                   | Attach each test's console output as `stdout.log` and `stderr.log` ([`captureOutput`](#captureoutput))                                          |
| `attachScreenshots`      | `PROBARA_ATTACH_SCREENSHOTS`   | `boolean`                               | `true`                                                    | Attach the screenshot of each failed attempt ([`attachScreenshots`](#attachscreenshots))                                                        |
| `attachVideos`           | `PROBARA_ATTACH_VIDEOS`        | `boolean`                               | `false`                                                   | Attach the video of a spec to its failed results ([`attachVideos`](#attachvideos))                                                              |
| `browserAsParameter`     | `PROBARA_BROWSER_AS_PARAMETER` | `boolean`                               | `true`                                                    | Send the browser Cypress runs as a `browser` parameter ([`browserAsParameter`](#browserasparameter))                                            |
| `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/cypress/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/cypress/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 (`ana@example.com,bo@example.com`)                               |
| `debug`                  | `PROBARA_DEBUG`                | `boolean`                               | `false`                                                   | Log every request ([debugging](https://docs.probara.net/en/developers/ci-integrations/cypress/troubleshooting/#debugging))                                              |
| `rootDir`                | —                              | `string`                                | Cypress's `projectRoot`                                   | Directory the spec paths of automation keys are relative to ([`keyIncludesFile`](#keyincludesfile))                                             |
| `chunkSize`              | —                              | `number`                                | `500`                                                     | Results per report request, 1 to 500                                                                                                            |
| `timeoutMs`              | —                              | `number`                                | `30000`                                                   | Timeout of one HTTP attempt, in milliseconds, 1 to 600000                                                                                       |
| `maxRetries`             | —                              | `number`                                | `4`                                                       | Retries of a failed request, 0 to 10                                                                                                            |
| `attachmentConcurrency`  | —                              | `number`                                | `2`                                                       | Results whose attachments upload at the same time, 1 to 8                                                                                       |

The reporter sends its own name and version in the `User-Agent` (such as
`probara-cypress-reporter/0.1.0`); there is no option for it.

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

For code and tests, the reporter also takes the seams of `@probara/core`. They have no variable, and
a Cypress config rarely needs them:

| Option   | Type                           | Default                     | What it does                                             |
| -------- | ------------------------------ | --------------------------- | -------------------------------------------------------- |
| `logger` | `{ debug, info, warn, error }` | `[probara]` lines on stdout | 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                                          |
| `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 |

### Unknown options

An option the reporter does not know is left out with a warning, and reporting goes on:

```js
reporterOptions: { projectId: 'SHOP', captureOutputs: true },
```

```text
$ npx cypress run
[probara] Ignored the unknown option "captureOutputs" of @probara/cypress-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 Cypress reporter has

### `keyIncludesFile`

Each test is matched to its case by an automation key: the spec file relative to `rootDir`
(Cypress's `projectRoot`), then `>` between spaces, then the `describe` titles and the `it` title
joined by spaces, without the case ids: `cypress/e2e/cart.cy.js > Cart adds an item`. That is the
key `probara import junit` gives the same test on the report of Cypress's `junit` reporter or of
cypress-junit.

`keyIncludesFile: false` leaves the spec file out, like a JUnit report written without the `file`
attribute of its root suite. Switch to it when your cases were imported from such reports, so each
test keeps its case. A test then gets the same key as a test with the same `describe` titles and
title in another spec.

```js
reporterOptions: { projectId: 'SHOP', keyIncludesFile: false },
```

```text
Cart adds an item
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 spec and its `describe` blocks
or, with `keyIncludesFile: false`, after its `describe` blocks alone. Neither `--spec` nor
Cypress's folder options change a key
([linking](https://docs.probara.net/en/developers/ci-integrations/cypress/linking/#automation-keys)).

### `captureOutput`

`captureOutput: true` attaches what each test writes to the browser console with `console.log`,
`console.info` and `console.debug` as `stdout.log`, and with `console.warn` and `console.error` as
`stderr.log`. The browser console still prints everything. It needs the support file: without it,
nothing is captured. What is and is not captured is in
[console output](https://docs.probara.net/en/developers/ci-integrations/cypress/steps-and-attachments/#console-output).

### `attachScreenshots`

`attachScreenshots: true` (the default) attaches the screenshot Cypress takes of a failed attempt to
the result of **that** attempt, as an `image/png`. It needs the plugin, which is what receives the
screenshots from Cypress. With `false`, none is attached, and Cypress still takes them where it
always does ([screenshots](https://docs.probara.net/en/developers/ci-integrations/cypress/steps-and-attachments/#screenshots)).

### `attachVideos`

`attachVideos: true` attaches the video of a spec to **every failed result of that spec** (a blocked
one too), under the name of the spec file, as a `video/mp4`. It needs `video: true` in the Cypress
config and the plugin. It is off by default because of the cost: one upload per failed result, and
video files are large ([the video of a spec](https://docs.probara.net/en/developers/ci-integrations/cypress/steps-and-attachments/#the-video-of-a-spec)).

### `browserAsParameter`

`browserAsParameter: true` (the default) sends the name of the browser Cypress runs (`electron`,
`chrome`, `firefox`, `edge`) as a `browser` parameter of every result:

```json
{
  "automationKey": "cypress/e2e/cart.cy.js > Cart adds an item",
  "parameters": { "browser": "electron" }
}
```

It never enters the key, so a suite run on two browsers records into one case. The `browser`
parameter selects a run case only when the case declares a `browser` parameter; otherwise it stays on
the result for display. The browser and the attempt are context: they are shown on the result, and they never become a parameter of a case a report creates. The plugin is what learns the browser: without it there is no `browser`
parameter.

### `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](https://docs.probara.net/en/developers/ci-integrations/cypress/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. It needs both the plugin
and the support file. 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/cypress/run-selection/) has the matching rules and
every fallback.

## 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:

```js
reporterOptions: { projectId: 'SHOP', run: { tags: 'nightly' } },
```

```text
$ npx cypress 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.

## 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. To keep what a run could not send, even with reporting off, set a
[results file](https://docs.probara.net/en/developers/ci-integrations/cypress/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, from Node.js: the browser that runs your specs
never calls Probara.

### Which process sends, and when

| Setup                                   | Process that sends                                             | When                                                                                         |
| --------------------------------------- | -------------------------------------------------------------- | -------------------------------------------------------------------------------------------- |
| The plugin registered (the usual setup) | The plugin's, on the Node.js that starts `cypress`             | Everything when the run ends: the reports, then the attachment uploads, then the close       |
| No plugin                               | The reporter's, inside Cypress, on the Node.js Cypress bundles | Each spec's results when that spec ends, into a run of its own                               |
| `runCasesOnly`                          | The plugin's                                                   | Before the first spec: read the case keys of the run, 200 cases per page until the last page |

Nothing is sent while the tests run. Cypress waits for the plugin before it exits, so the time the
requests take, retries included, is part of the `cypress run` command. With `runCasesOnly`, the
case keys are read once per `cypress run`; when they cannot be read after the retries, every test
runs and is reported, with one warning.

### 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.
- 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.

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`. 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. 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 failed attempts attach their
screenshot costs about 200 more. Without the plugin, every spec is a run of its own and pays these
requests on its own. `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 Cypress ended the specs, each spec's attempts in order. A run case keeps the last outcome
  recorded, so the order matters.
- 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/cypress/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 `cypress run` 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. With the plugin
registered, the requests leave the process of the Node.js that started `cypress`:

| 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 cypress run
```

Node added `fetch` support for `NODE_USE_ENV_PROXY` in 22.21.0 and 24.0.0
([Node's docs](https://nodejs.org/api/cli.html#node_use_env_proxy1)). Cypress reads `HTTPS_PROXY`
and `NO_PROXY` for the browser's traffic too
([Cypress's proxy configuration](https://docs.cypress.io/app/references/proxy-configuration)): put
the hosts your tests reach directly in `NO_PROXY`. On older Node versions, upgrade Node or allow the
base URL through the firewall. Behind a proxy, register the plugin: without it, the requests leave
Cypress's own process instead.

### 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 cypress 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.

## Interactive mode

**`cypress open` reports nothing.** Cypress builds the `reporter` of its config
[only during `cypress run`](https://docs.cypress.io/app/references/configuration#Global): the app
shows its own command log instead. A `cypress open` session with the reporter, the plugin and the
support file registered runs its tests as usual and creates no run in Probara. To report the specs
you are working on, run them with `cypress run --spec`:

```bash
PROBARA_API_TOKEN=… PROBARA_PROJECT=SHOP npx cypress run --spec 'cypress/e2e/cart.cy.js'
```

To collect a few local `cypress run` commands in one run, create it once and pass its ULID to each;
a run the reporter did not create is never closed by it:

```bash
PROBARA_RUN_ULID=$(npx @probara/cli run create --run-name "Cart rework")
export PROBARA_RUN_ULID
npx cypress run --spec 'cypress/e2e/cart.cy.js'
npx cypress run --spec 'cypress/e2e/checkout.cy.js'
npx @probara/cli run close
```

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