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
1 change: 1 addition & 0 deletions .agents/skills/webjs/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,7 @@ Success is a 303 (PRG); failure re-renders the page at 422 with the result on `a

## Testing Defaults

- `npm run ci` before every push: it runs the `webjs.ci` step list in `package.json` (correctness, project health, types, a dependency audit, then the server, browser, and e2e test layers) with a result line per step, and CI runs the same list, so a green local run predicts the pipeline. `npm run ci -- --only Tests` runs one layer while iterating. See `references/testing.md` and `references/built-ins.md`.
- Prefer server/handler tests first: drive the app with `handle()` from `@webjsdev/server/testing` and assert on the `Response`.
- Add a browser test (`npm run test:browser`) for anything touching hydration, the client router, slots, or custom-element upgrade. A unit test is necessary but NOT sufficient for a browser-facing change.
- Render the app and LOOK for any UI change: `npm run check` and `npm run typecheck` pass even when a layout collapses. Static tools give no signal for a visual defect.
Expand Down
24 changes: 24 additions & 0 deletions .agents/skills/webjs/references/built-ins.md
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,30 @@ An over-limit body responds `413` without buffering the whole payload.

`before` runs to completion first (a non-zero exit aborts the boot). `parallel` (dev only) runs long-lived watchers alongside the server and tears them down on exit. `watch` (dev only) adds extra live-reload directories outside the app tree.

### Local CI (`webjs.ci`)

`webjs ci` runs the step list the block declares, the Rails 8.1 `bin/ci` posture: your machine is the first CI runner, and a cloud pipeline runs the SAME list by calling `npm run ci`, so the two cannot drift. Each step prints a heading, then `✅ <title> passed in 2.11s` or `❌ <title> failed in 0.01s`; the run ends with every failure listed and one total line, and exits 1 on any failure.

```jsonc
{ "webjs": { "ci": { "steps": [
{ "title": "Setup", "run": "webjs db migrate" },
{ "title": "Checks", "parallel": 2, "steps": [ // two at a time
"webjs check", // a string is a command titled by itself
{ "title": "Types", "run": "webjs typecheck" },
{ "title": "Tests", "steps": [ // a nested group takes ONE slot, runs in order
{ "title": "Tests: server", "run": "webjs test --server" },
{ "title": "Tests: e2e", "run": "webjs test --server", "env": { "WEBJS_E2E": "1" } }
] }
] }
] } } }
```

A step is a string, a `{ title, run, env? }` command, or a `{ title, steps, parallel? }` group. `parallel` is a slot count (default 1); a parallel group captures each step's output and replays it whole when the step finishes, so two steps never interleave, and a group nested inside it takes one slot and runs sequentially (it cannot declare `parallel`, which the reader reports rather than honours). `env` is per-step, so the e2e opt-in does not depend on a shell prefix. Every child runs with `CI=true`, `node_modules/.bin` on PATH, and `.env` loaded first (a real env var wins), so a `webjs db migrate` step sees `DATABASE_URL`.

Flags: `-f` / `--fail-fast` stops after the first failure (the default runs everything and lists every failure), `--only <title>` runs one step or group by title (repeatable, an unknown title is an error rather than an empty green run), `--json` emits one document on stdout (`{ ok, seconds, steps: [{ title, run, group, ok, code, seconds, output? }] }`, failed steps carrying their captured output, the human report on stderr) for an agent loop, and `--signoff` runs `gh signoff` after a green run. To hold a merge until a LOCAL run is green, install `basecamp/gh-signoff`, run `gh signoff install` once (a branch-protection rule requiring the `signoff` status), and run `npm run ci -- --signoff`; a red run posts nothing.

Under GitHub Actions each step is a `::group::` in the log, a failed step is an `::error::` annotation, and a step table is appended to the job summary, so a single job running the whole list still names the layer that broke. The scaffold's workflow is exactly that one job; `webjs create --skip-ci` omits it and the local list always ships. A malformed block (a group with no title, a nested `parallel`, an unknown key) refuses to run and names every problem by JSON path, because a silently dropped step is a check that never ran. Nothing declared is exit 1 too, naming any workspace member that declares one, since "ran zero steps" would read as green.

### Bring your own ORM (`webjs.db`)

Drizzle is the scaffold DEFAULT, not lock-in. The runtime never imports it, `db/connection.server.ts` is the app's own file, and `webjs db` is adapter-driven: a `db` block maps each verb to the shell command `webjs db <verb>` runs instead of the drizzle-kit default (node_modules/.bin on PATH like a `before` step, extra CLI args appended).
Expand Down
11 changes: 11 additions & 0 deletions .agents/skills/webjs/references/testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,17 @@ WEBJS_ELIDE=0 npm run test:e2e

A test that passes under one and fails under the other is a wrong verdict, and `webjs elision` tells you which module and on what evidence. If the component's interactivity is genuinely invisible to static analysis, the fix is `static interactive = true` on it; see `components.md` for what that override does and does not rescue.

## One command for every layer (`webjs ci`)

```sh
npm run ci # the webjs.ci list: check, doctor, typecheck, audit, then every test layer
npm run ci -- --only Tests # one step or group by title
npm run ci -- --fail-fast # stop at the first failure
npm run ci -- --json # one JSON document for an agent loop
```

The scaffold declares its gate once, in `package.json` under `webjs.ci`, and `npm run ci` runs it with a timed result line per step (the Rails `bin/ci` model). The generated GitHub workflow runs the same list, so a green local run predicts CI. Run it before every push; the pre-commit hook deliberately runs none of it so a commit stays fast. Browser and e2e steps need a Chromium on the machine (`npx playwright install chromium`, plus `puppeteer-core` for the e2e layer), which the workflow installs for itself. The list, the flags, the `--signoff` merge gate, and the JSON shape are in `references/built-ins.md` under "Local CI".

## Type-checking your tests (`webjs typecheck`)

Your tests are inside the tsconfig `include`, so `npm run typecheck` reads them (#1299). Treat a type error in a test as a failed gate, not a review catch: the checker sees a wrong argument shape or an unannotated parameter in a test the same way it sees one in `app/`.
Expand Down
2 changes: 1 addition & 1 deletion .claude/hooks/block-prose-punctuation.sh
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,7 @@ fi
# positives (a wrongly blocked write), the same tradeoff as the rules above:
# e.g. a sentence-ending "built on webjs." is not flagged (trailing period),
# and the `bin/webjs.js` "webjs commands:" usage banner may rarely trip it.
webjs_cli='create|dev|start|test|check|routes|elision|db|ui|doctor|types|typecheck|mcp|vendor|help|version|add|init|generate|migrate|push|studio|seed|pin|unpin|list|audit|outdated|update|view|diff|info|build'
webjs_cli='create|dev|start|test|check|ci|routes|elision|db|ui|doctor|types|typecheck|mcp|vendor|help|version|add|init|generate|migrate|push|studio|seed|pin|unpin|list|audit|outdated|update|view|diff|info|build'

# Scan copy: drop fenced code blocks, inline code spans, and emphasis markers
# so a `webjs` inside code is never considered and **webjs** still matches.
Expand Down
Loading
Loading