Skip to content

feat(templates): add a test, lint, and format setup to every starter - #44

Open
Bccorb wants to merge 1 commit into
mainfrom
feat/template-test-lint-strategy
Open

feat(templates): add a test, lint, and format setup to every starter#44
Bccorb wants to merge 1 commit into
mainfrom
feat/template-test-lint-strategy

Conversation

@Bccorb

@Bccorb Bccorb commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

A scaffolded project could be built but not verified. Every starter had a lint script and nothing
else, so a user's first move after seamless init was to pick a test runner and wire it up
themselves. This gives all four starters the same working setup on the first npm install.

One command

Every template now declares the same script names, so npm run check is the whole gate:

npm run check   # typecheck && lint && format:check && test

Plus typecheck, lint, lint:fix, format, format:check, test, test:watch, and
test:coverage.

Tests

Vitest, colocated as *.test.ts / *.test.tsx. They cover the configuration and startup logic that
decides whether a fresh scaffold runs at all, and need no database, auth server, or network.

Template Tests
Express, Fastify src/lib/databaseUrl.test.ts (which connection string wins, credential escaping, sslmode becoming a driver option), src/lib/env.test.ts (the boot-time check, including the USER and PASSWORD placeholders seamless init writes into a managed DATABASE_URL)
react-vite src/lib/runtimeConfig.test.ts, src/lib/api.test.ts, src/components/ConfigurationError.test.tsx
react-oauth the above, plus src/pages/OAuthCallback.test.tsx covering the callback route with the SDK and router stubbed

Web tests are configured in the test block of each vite.config.ts, so they share the app's Vite
resolution and plugins. The API starters need no Vitest config; the defaults already fit.

These are a starting point a user extends, not exhaustive coverage. Anything crossing the network
belongs in an integration or end-to-end test against the real stack.

Lint and format

Prettier now owns formatting, with eslint-config-prettier last in each flat config so the two
cannot disagree about the same line.

ESLint in the API starters now covers the whole project rather than src only, and uses Node
globals instead of browser globals. That surfaced a real finding: an unused catch binding in
scripts/runMigrations.js in both API starters, now fixed.

The API starters build with a tsconfig.build.json that keeps tests out of dist/, while
npm run typecheck still checks them.

CI

The per-template job now runs typecheck, lint, format check, tests, and build. Each step is
--if-present, so a template that has not adopted one is skipped rather than failed, but a declared
script has to pass.

The job renamed from Build <id> to Check <id>. If those names are pinned as required status
checks in branch protection, they need updating.

Note on the diff

Making format:check meaningful took a one-time prettier --write pass. That is most of the diff
outside the new files: quote style in main.tsx, README table alignment, JSX wrapping, indentation
in package.json. It changes no behavior.

Verification

npm run validate passes. npm run check and npm run build pass in all four templates, including
from a clean node_modules.

A scaffolded project could be built but not verified: there was a lint
script and nothing else, so a user's first move after `seamless init` was
to pick a test runner and wire it up themselves.

All four starters now declare the same scripts (typecheck, lint, lint:fix,
format, format:check, test, test:watch, test:coverage) plus a `check` that
runs the gate in one command, and ship tests that pass on a fresh install.

Tests are Vitest, colocated as *.test.ts / *.test.tsx, and cover the
configuration and startup logic that decides whether a fresh scaffold runs
at all, with no database, auth server, or network:

- API starters: connection-string resolution (which source wins, credential
  escaping, sslmode becoming a driver option) and the boot-time environment
  check, including the placeholders `seamless init` writes into a managed
  DATABASE_URL.
- Web starters: API origin resolution across the container-injected config
  and VITE_API_URL, URL joining and error handling in apiFetch, and a
  component test. The OAuth starter also covers its callback route with the
  SDK and router stubbed.

Web tests are configured in the `test` block of each vite.config.ts so they
share the app's resolution and plugins. The API starters need no Vitest
config; the defaults already fit.

Also here:

- ESLint covers the whole project in the API starters rather than src only,
  and uses Node globals instead of browser globals. That surfaced an unused
  catch binding in scripts/runMigrations.js, now fixed.
- eslint-config-prettier is wired in last so lint and format cannot disagree
  about the same line. Making format:check meaningful took a one-time
  `prettier --write` pass, which is the bulk of the diff outside the new
  files and changes no behavior.
- The API starters build with a tsconfig.build.json that keeps tests out of
  dist/, while `npm run typecheck` still checks them.
- CI runs typecheck, lint, format check, tests, and build for every template.
  Each step is --if-present, so a template that has not adopted one is
  skipped rather than failed. The job is now `Check <id>` rather than
  `Build <id>`.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant