npm package: @rpcbase/test
Description: `@rpcbase/test` ships the shared Playwright wiring we use across RPCBase packages: a preconfigured `test`/`expect`, automatic V8 coverage collection, and the `rb-test` CLI.
Latest version: 0.360.0

# @rpcbase/test helpers

`@rpcbase/test` ships the shared Playwright wiring we use across RPCBase packages: a preconfigured `test`/`expect`, automatic V8 coverage collection, and the `rb-test` CLI.

## 1. Declare your coverage config

Create `spec/coverage.config.ts` (or `.js` / `.json`) in your package:

```ts
export default {
  thresholds: {
    statements: 75,
    lines: 75,
    functions: 75,
    branches: 60,
  },
}
```

Only `RB_DISABLE_COVERAGE=1` skips the hooks; every other option lives inside this config file.
Coverage artifacts are written to `build/playwright/coverage/` and `build/vitest/coverage/`, with the report in `build/coverage/report/`.

### Default file collection

If you omit `collectCoverageFrom`, `rb-test` uses a reasonable default:

- `src/**/*.{ts,tsx,js,jsx,mjs,cjs}` when `src/` exists
- if `src/` is missing, `rb-test` throws unless you set `collectCoverageFrom`

Unit test files are excluded by default: `!**/*.test.{ts,tsx,js,jsx,mjs,cjs}`.

`collectCoverageFrom` supports negated globs (prefix with `!`) to exclude files.

### Per-folder thresholds

Need stricter coverage in a sub-tree? Extend the same `thresholds` object with glob keys (mirroring Jest's `coverageThreshold` syntax):

```ts
export default {
  thresholds: {
    global: {
      statements: 90,
      lines: 85,
      functions: 85,
      branches: 70,
    },
    "src/core/**": {
      statements: 98,
      lines: 95,
    },
    "src/components/**": {
      functions: 92,
    },
  },
}
```

- When `thresholds` only has metric keys, it behaves exactly like before.
- Adding `thresholds.global` lets you keep a default floor while overriding specific directories.
- Globs run against POSIX-style paths relative to the package root (parent folder of `spec/`). Absolute paths are not supported.
- Metrics you omit inside an override inherit from the global thresholds (or the 75/75/75/60 defaults).
- If a glob matches no files you'll get a warning and the override is skipped, so typos are easy to spot.

## 2. Import `test` / `expect` directly

```ts
// spec/my-component.spec.ts
import { test, expect } from "@rpcbase/test"

test("renders", async ({ page }) => {
  await page.goto("/playground")
  await expect(page.locator("button"))..toBeVisible()
})
```

The exported `test` already records coverage via CDP and writes per-test payloads automatically.
Because CDP is Chromium-only, V8 coverage is collected only when running on Chromium (Chrome); other browsers skip coverage collection.

## 3. Build your Playwright config via the shared wrapper

```ts
// playwright.config.ts
import { defineConfig, devices } from "@rpcbase/test"

export default defineConfig({
  testDir: "./spec",
  reporter: [["list"]],
  use: {
    baseURL: "http://localhost:9198",
    launchOptions: { headless: true },
  },
  projects: [
    {
      name: "chromium",
      use: { ...devices["Desktop Chrome"] },
    },
  ],
})
```

Whenever coverage is enabled, the wrapper appends the shared reporter so Istanbul aggregation + threshold enforcement run after the suite.

### Spec file conventions (desktop vs mobile)

The wrapper also applies a default `testMatch` per project:

- `*.spec.ts` runs on all projects
- `*.spec.desktop.ts` runs on non-mobile projects only
- `*.spec.mobile.ts` runs on mobile projects only (`project.use.isMobile === true`)

To run mobile specs on iOS Safari, add a mobile WebKit project, for example:

```ts
projects: [
  { name: "chromium", use: { ...devices["Desktop Chrome"] } },
  { name: "mobile-ios-safari", use: { ...devices["iPhone 15 Pro"] } },
],
```

## 4. Run tests with `rb-test`

Each package uses its `npm test` script as `tsc --noEmit && rb-test`. The CLI runs two stages:

- Playwright, with any CLI flags you pass through untouched; repo defaults are applied unless you provide your own `--config`.

Coverage is enforced separately:

- Playwright uses `spec/coverage.config.*` via the shared reporter and will fail the run if thresholds aren’t met.
- Vitest only reads `src/coverage.json` (JSON object). If that file is missing, Vitest coverage is skipped.

Need to debug without coverage? Set `RB_DISABLE_COVERAGE=1 npm test` and the Playwright hooks short-circuit.