# Run in CI

> For the complete documentation index, see [llms.txt](/llms.txt)

Because tests are plain `.js` in your repo with no proprietary format, CI is
straightforward.

## A minimal job

```yaml
# .github/workflows/e2e.yml
jobs:
  e2e:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: 20 }
      - run: npx @unotest/web install-chromium
      - run: npx @unotest/web collection smoke --workers=4
        env:
          APP_BASE_URL: ${{ secrets.APP_BASE_URL }}
          TEST_USER_EMAIL: ${{ secrets.TEST_USER_EMAIL }}
          TEST_PASSWORD: ${{ secrets.TEST_PASSWORD }}
```

## Tips

- **Browsers:** run the bundled Chromium in CI; you can widen `browsers` to
  Firefox/WebKit in your [config](/reference/config/).
- **Retries:** turn on `retry.count` for CI (off in dev) to absorb transient
  flakiness — assertion failures still never retry.
- **Secrets:** inject [variables](/concepts/variables/) from your CI secret store;
  the runner masks them in logs and artifacts.
- **Split:** `smoke` on every push, `regress` nightly.
- **iOS:** `unotest mobile` needs macOS runners with Xcode.

## Failure artifacts

On failure the [bundle](/concepts/failure-bundles/) (screenshot, console,
semantic DOM, trace) is written under `.unotest/failures/` — upload it as a CI
artifact for inspection.
