Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,10 @@ jobs:
run: pnpm exec playwright install --with-deps chromium

- name: Run tests
run: pnpm coverage:ci
run: JABTERM_SKIP_BUILD=1 pnpm coverage:ci

- name: Run docs scenario tests (docs assets)
run: pnpm run test:docs

- name: Upload coverage artifacts
if: always()
Expand Down
6 changes: 6 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Agents

If you are an AI agent (or automation) working on this repository, follow the test contract described in:

- [`agents/testing-strategy.md`](agents/testing-strategy.md)

13 changes: 10 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,15 @@
"scripts": {
"build": "tsc -p tsconfig.server.json && tsc -p tsconfig.react.json",
"prepack": "pnpm build",
"test": "pnpm exec playwright test",
"test:client": "pnpm exec vitest run",
"test:server:coverage": "rm -rf .cache/v8-coverage && NODE_V8_COVERAGE=.cache/v8-coverage pnpm test",
"test": "pnpm run build && test-runner all",
"test:unit": "test-runner unit",
"test:e2e": "pnpm run build && test-runner e2e",
"test:scenario": "pnpm run build && test-runner scenario",
"test:smoke": "pnpm run build && test-runner scenario --smoke",
"test:integration": "node -e \"console.log('No integration tests in this repo. (intentional)')\"",
"test:docs": "pnpm run build && test-runner e2e --project docs",
"test:client": "pnpm run test:unit",
"test:server:coverage": "rm -rf .cache/v8-coverage && pnpm run build && NODE_V8_COVERAGE=.cache/v8-coverage test-runner scenario && NODE_V8_COVERAGE=.cache/v8-coverage test-runner e2e",
"test:client:coverage": "rm -rf .cache/coverage/client && pnpm exec vitest run --coverage",
"coverage:server:report": "rm -rf .cache/coverage/server && pnpm exec c8 report --temp-directory .cache/v8-coverage --reporter=text-summary --reporter=json-summary --report-dir .cache/coverage/server --all --src src/server --exclude \"**/playwright.config.ts\" --exclude \"tests/**\" --exclude \"src/react/**\" && node scripts/normalize-coverage-summary.mjs .cache/coverage/server/coverage-summary.json",
"coverage:merge": "node scripts/merge-coverage-summaries.mjs .cache/coverage/server/coverage-summary.json .cache/coverage/client/coverage-summary.json coverage/coverage-summary.json",
Expand Down Expand Up @@ -59,6 +65,7 @@
"@testing-library/jest-dom": "^6.9.0",
"@testing-library/react": "^16.3.0",
"@vitest/coverage-v8": "^3.2.4",
"test-runner": "file:packages/test-runner",
"c8": "^10.1.3",
"jsdom": "^26.1.0",
"react": "^19.2.0",
Expand Down
69 changes: 69 additions & 0 deletions packages/test-runner/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# test-runner

Small, policy-light test runner intended to be copied between repos (or published).

## CLI

```bash
test-runner <suite> [--smoke] [--human] [--pkg <name>...] [--project <name>] [--allow-missing-project]
```

Suites:

- `unit`: run unit tests (Vitest if installed; otherwise `node --test`)
- `scenario`: run Playwright project `scenario` (recommended naming: `*.scenario.e2e.ts`)
- `e2e`: run Playwright project `e2e` (recommended naming: `*.e2e.ts`, ignoring scenario)
- `integration`: run Playwright project `integration` (optional)
- `all`: `unit` + `scenario` + `e2e` (never runs `integration`)

Smoke rules (`--smoke`) apply to Playwright suites only:

- per-test timeout via `SMOKE_PER_TEST_TIMEOUT_MS` (default 30000)
- total timeout via `SMOKE_TOTAL_TIMEOUT_MS` (default 180000)
- stop on first failure
- dot reporter output goes to `run.log`
- terminal output: **one line only** on success / failure

Human execution (`--human`) is orthogonal:

- Playwright: `--headed`, `--workers=1`, `--trace=on`
- Exposes `TEST_RUNNER_HUMAN=1` for test utilities (see `test-runner/human`)

## Artifacts

Each run writes to:

`.cache/tests/<run-id>/` (cleaned before each run)

With:

- `run.log`: full raw output
- `pw-output/`: Playwright output directory (traces/videos/screenshots if enabled by config)

## Playwright config convention (recommended)

Define projects so the runner doesn’t need tags or discovery logic:

```ts
import { defineConfig } from "@playwright/test";

const E2E = /.*\.e2e\.(ts|js)x?$/;
const SCENARIO = /.*\.scenario\.e2e\.(ts|js)x?$/;

export default defineConfig({
testDir: "tests",
projects: [
{ name: "e2e", testMatch: E2E, testIgnore: [SCENARIO] },
{ name: "scenario", testMatch: SCENARIO },
],
});
```

## Test helper: `breath()`

```js
import { breath } from "test-runner/human";

await breath(300);
```

Loading