@jterrazz/test 16.1.1 → 17.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +45 -171
- package/dist/browser/index.js +2 -2980
- package/dist/browser/services.js +2 -0
- package/dist/browser/stubs.js +2 -0
- package/dist/checker.d.ts +6 -1
- package/dist/checker.js +4534 -2628
- package/dist/config.d.ts +40 -0
- package/dist/config.js +309 -0
- package/dist/in-place.global-setup.d.ts +11 -0
- package/dist/in-place.global-setup.js +21 -0
- package/dist/index.d.ts +1653 -3004
- package/dist/index.js +5226 -3845
- package/dist/mobile-session.global-setup.d.ts +14 -0
- package/dist/mobile-session.global-setup.js +31 -0
- package/dist/oxlint.cjs +6069 -5634
- package/dist/oxlint.d.cts +43 -7212
- package/dist/oxlint.d.ts +43 -7212
- package/dist/oxlint.js +6127 -5693
- package/dist/route-coverage.global-setup.d.ts +42 -0
- package/dist/route-coverage.global-setup.js +2 -0
- package/dist/services.d.ts +3 -0
- package/dist/services.global-setup.d.ts +43 -0
- package/dist/services.global-setup.js +2 -0
- package/dist/services.js +597 -0
- package/dist/shared-appium.adapter.js +428 -0
- package/dist/shared-artifacts.js +32 -0
- package/dist/shared-chapter.js +7 -0
- package/dist/shared-container.port.d.ts +31 -0
- package/dist/shared-derived-projects.js +58 -0
- package/dist/shared-device.port.d.ts +189 -0
- package/dist/shared-exchange.js +447 -0
- package/dist/shared-in-place.js +172 -0
- package/dist/shared-index.browser.js +3191 -0
- package/dist/shared-index.services.d.ts +346 -0
- package/dist/shared-index.stubs.d.ts +287 -0
- package/dist/shared-json-shape.js +17 -0
- package/dist/shared-landmarks.js +71 -0
- package/dist/shared-ledger.js +68 -0
- package/dist/shared-literate-plugin.js +183 -0
- package/dist/shared-manifest.js +1321 -0
- package/dist/shared-pause.js +11 -0
- package/dist/shared-peer.js +88 -0
- package/dist/shared-playwright.adapter.js +330 -0
- package/dist/shared-process.adapter.js +74 -0
- package/dist/shared-refusal.js +35 -0
- package/dist/shared-resolve.js +24 -0
- package/dist/shared-route-coverage.global-setup.js +187 -0
- package/dist/shared-scenario-failure.js +198 -0
- package/dist/shared-serve.js +365 -0
- package/dist/shared-service-images.js +10 -0
- package/dist/shared-services.global-setup.js +179 -0
- package/dist/shared-session-ledger.js +301 -0
- package/dist/shared-setup-entry.js +12 -0
- package/dist/shared-source-scan.js +133 -0
- package/dist/shared-spec-document.js +365 -0
- package/dist/shared-specifications.js +26 -0
- package/dist/shared-stream-body.js +130 -0
- package/dist/shared-stub-server.js +190 -0
- package/dist/shared-temp-root.global-setup.js +80 -0
- package/dist/shared-test-baseline.js +26 -0
- package/dist/shared-test-section.js +58 -0
- package/dist/shared-testcontainers.adapter.js +203 -0
- package/dist/shared-tokens.js +55 -0
- package/dist/shared-yaml-document.js +119 -0
- package/dist/stubs.d.ts +2 -0
- package/dist/stubs.js +657 -0
- package/dist/temp-root.global-setup.d.ts +7 -0
- package/dist/temp-root.global-setup.js +2 -0
- package/dist/vitest.d.ts +97 -163
- package/dist/vitest.js +373 -525
- package/docs/README.md +82 -36
- package/docs/concepts/architecture.md +118 -0
- package/docs/concepts/conventions.md +333 -0
- package/docs/guides/api-specs.md +374 -0
- package/docs/guides/cli-specs.md +564 -0
- package/docs/guides/component-specs.md +252 -0
- package/docs/guides/develop.md +94 -0
- package/docs/guides/integration-specs.md +181 -0
- package/docs/guides/job-specs.md +180 -0
- package/docs/guides/lint-specs.md +174 -0
- package/docs/guides/mobile-specs.md +319 -0
- package/docs/guides/module-tests.md +149 -0
- package/docs/guides/release.md +43 -0
- package/docs/guides/spec-documents.md +189 -0
- package/docs/guides/website-specs.md +413 -0
- package/docs/reference/assertions.md +345 -0
- package/docs/reference/glossary.md +20 -0
- package/docs/reference/linting.md +483 -0
- package/docs/reference/services.md +236 -0
- package/docs/reference/stubs.md +498 -0
- package/docs/reference/time-and-doubles.md +137 -0
- package/docs/reference/tokens.md +258 -0
- package/docs/reference/visitor.md +221 -0
- package/docs/start/getting-started.md +187 -0
- package/docs/start/set-up-a-project.md +214 -0
- package/package.json +50 -30
- package/schema/spec.schema.json +43 -17
- package/dist/appium.adapter.js +0 -489
- package/dist/artifacts.js +0 -32
- package/dist/fixtures.js +0 -110
- package/dist/literate.js +0 -1747
- package/dist/match.js +0 -804
- package/dist/playwright.adapter.js +0 -438
- package/dist/server.js +0 -160
- package/dist/stream.js +0 -321
- package/dist/substring-warning.js +0 -282
- package/docs/01-architecture.md +0 -95
- package/docs/02-developing.md +0 -351
- package/docs/03-testing.md +0 -495
- package/docs/04-operating.md +0 -101
- package/docs/05-module-tests.md +0 -152
- package/docs/06-integration.md +0 -180
- package/docs/07-component.md +0 -246
- package/docs/08-website.md +0 -404
- package/docs/09-mobile.md +0 -270
- package/docs/10-api.md +0 -264
- package/docs/11-jobs.md +0 -157
- package/docs/12-cli.md +0 -677
- package/docs/13-elements.md +0 -248
- package/docs/14-assertions.md +0 -314
- package/docs/15-tokens.md +0 -253
- package/docs/16-contracts.md +0 -438
- package/docs/17-services.md +0 -188
- package/docs/18-conventions.md +0 -332
- package/docs/19-linting.md +0 -512
- package/docs/decisions/001-v9-conventions-on-three-channels.md +0 -50
- package/docs/decisions/002-the-statique-channel-holds-only-what-upstream-does-not.md +0 -49
- package/docs/decisions/003-a-rendered-component-is-a-facet-on-vitest-browser-mode.md +0 -84
- package/docs/decisions/004-a-module-against-real-services-or-a-golden-is-the-integration-facet.md +0 -81
- package/docs/decisions/005-the-doubles-ladder-is-closed.md +0 -86
- package/docs/decisions/006-the-catalogue-reaches-a-file-by-its-role.md +0 -245
- package/docs/decisions/007-compose-mode-leaves-the-package.md +0 -53
- package/docs/decisions/008-vitest-5-node-24-and-the-native-seams-as-optional-peers.md +0 -75
- package/docs/decisions/_template.md +0 -22
- package/docs/reference/classes/Matcher.md +0 -120
- package/docs/reference/classes/ProcessHandle.md +0 -368
- package/docs/reference/functions/defineContract.md +0 -39
- package/docs/reference/functions/defineContracts.md +0 -36
- package/docs/reference/functions/disabled.md +0 -28
- package/docs/reference/functions/enabled.md +0 -20
- package/docs/reference/functions/focused.md +0 -26
- package/docs/reference/functions/mockOf.md +0 -79
- package/docs/reference/functions/postgres.md +0 -31
- package/docs/reference/functions/process.md +0 -33
- package/docs/reference/functions/redis.md +0 -28
- package/docs/reference/functions/required.md +0 -41
- package/docs/reference/functions/selected.md +0 -26
- package/docs/reference/functions/sqlite.md +0 -33
- package/docs/reference/functions/testId.md +0 -20
- package/docs/reference/functions/text.md +0 -40
- package/docs/reference/functions/valued.md +0 -28
- package/docs/reference/functions/waitUntil.md +0 -46
- package/docs/reference/functions/within.md +0 -29
- package/docs/reference/index.md +0 -196
- package/docs/reference/interfaces/BaseResult.md +0 -87
- package/docs/reference/interfaces/CallResult.md +0 -160
- package/docs/reference/interfaces/CaptureScope.md +0 -82
- package/docs/reference/interfaces/CliResult.md +0 -253
- package/docs/reference/interfaces/ContainerAccessor.md +0 -166
- package/docs/reference/interfaces/DirectoryAccessor.md +0 -33
- package/docs/reference/interfaces/FetchResult.md +0 -180
- package/docs/reference/interfaces/FilesystemAccessor.md +0 -46
- package/docs/reference/interfaces/HttpResult.md +0 -126
- package/docs/reference/interfaces/JsonAccessor.md +0 -46
- package/docs/reference/interfaces/PageResult.md +0 -356
- package/docs/reference/interfaces/RenderResult.md +0 -72
- package/docs/reference/interfaces/RenderedText.md +0 -110
- package/docs/reference/interfaces/ResponseAccessor.md +0 -47
- package/docs/reference/interfaces/ScreenResult.md +0 -128
- package/docs/reference/interfaces/TableAccessor.md +0 -26
- package/docs/reference/interfaces/TextAccessor.md +0 -96
- package/docs/reference/type-aliases/ApiHandle.md +0 -68
- package/docs/reference/type-aliases/ApiSpecification.md +0 -213
- package/docs/reference/type-aliases/ApiSpecificationOptions.md +0 -68
- package/docs/reference/type-aliases/BrowserConsoleMessage.md +0 -30
- package/docs/reference/type-aliases/BrowserLinkElement.md +0 -50
- package/docs/reference/type-aliases/BrowserMetaElement.md +0 -40
- package/docs/reference/type-aliases/BrowserOpenOptions.md +0 -87
- package/docs/reference/type-aliases/BrowserPage.md +0 -132
- package/docs/reference/type-aliases/BrowserPort.md +0 -51
- package/docs/reference/type-aliases/CliEnv.md +0 -11
- package/docs/reference/type-aliases/CliHandle.md +0 -80
- package/docs/reference/type-aliases/CliInput.md +0 -36
- package/docs/reference/type-aliases/CliOutput.md +0 -46
- package/docs/reference/type-aliases/CliPort.md +0 -58
- package/docs/reference/type-aliases/CliSpecification.md +0 -144
- package/docs/reference/type-aliases/CliSpecificationOptions.md +0 -134
- package/docs/reference/type-aliases/ComponentChain.md +0 -156
- package/docs/reference/type-aliases/ComponentScenario.md +0 -20
- package/docs/reference/type-aliases/ComponentUi.md +0 -47
- package/docs/reference/type-aliases/ComponentVisitor.md +0 -10
- package/docs/reference/type-aliases/ContainerPort.md +0 -113
- package/docs/reference/type-aliases/Contract.md +0 -68
- package/docs/reference/type-aliases/ContractInput.md +0 -13
- package/docs/reference/type-aliases/ContractRequest.md +0 -95
- package/docs/reference/type-aliases/ContractResponder.md +0 -23
- package/docs/reference/type-aliases/ContractResponse.md +0 -78
- package/docs/reference/type-aliases/ContractResponseValue.md +0 -13
- package/docs/reference/type-aliases/Contracts.md +0 -48
- package/docs/reference/type-aliases/DatabaseKeys.md +0 -16
- package/docs/reference/type-aliases/DatabasePort.md +0 -72
- package/docs/reference/type-aliases/DeviceOpenOptions.md +0 -46
- package/docs/reference/type-aliases/DevicePort.md +0 -51
- package/docs/reference/type-aliases/DeviceScreen.md +0 -32
- package/docs/reference/type-aliases/DeviceTimeouts.md +0 -39
- package/docs/reference/type-aliases/DockerSpecConfig.md +0 -43
- package/docs/reference/type-aliases/DomMount.md +0 -21
- package/docs/reference/type-aliases/ElementKind.md +0 -27
- package/docs/reference/type-aliases/ElementMatch.md +0 -74
- package/docs/reference/type-aliases/ElementOptions.md +0 -29
- package/docs/reference/type-aliases/ElementRef.md +0 -128
- package/docs/reference/type-aliases/ExecOptions.md +0 -37
- package/docs/reference/type-aliases/FileAccessor.md +0 -54
- package/docs/reference/type-aliases/HonoApp.md +0 -31
- package/docs/reference/type-aliases/HttpContractFilter.md +0 -49
- package/docs/reference/type-aliases/HttpResponseInit.md +0 -46
- package/docs/reference/type-aliases/HttpStreamInit.md +0 -58
- package/docs/reference/type-aliases/IntegrationHandle.md +0 -46
- package/docs/reference/type-aliases/IntegrationSpecification.md +0 -111
- package/docs/reference/type-aliases/IntegrationSpecificationOptions.md +0 -42
- package/docs/reference/type-aliases/Intercept.md +0 -19
- package/docs/reference/type-aliases/InterceptOptions.md +0 -28
- package/docs/reference/type-aliases/InterceptScope.md +0 -11
- package/docs/reference/type-aliases/IsolationStrategy.md +0 -75
- package/docs/reference/type-aliases/JobHandle.md +0 -34
- package/docs/reference/type-aliases/JobsHandle.md +0 -57
- package/docs/reference/type-aliases/JobsResult.md +0 -16
- package/docs/reference/type-aliases/JobsSpecification.md +0 -94
- package/docs/reference/type-aliases/JobsSpecificationOptions.md +0 -56
- package/docs/reference/type-aliases/LandmarkKind.md +0 -20
- package/docs/reference/type-aliases/LiterateRunFlags.md +0 -27
- package/docs/reference/type-aliases/LiterateServeRegistration.md +0 -70
- package/docs/reference/type-aliases/MatchFixtureOptions.md +0 -27
- package/docs/reference/type-aliases/MatchableRequest.md +0 -61
- package/docs/reference/type-aliases/MatcherKind.md +0 -34
- package/docs/reference/type-aliases/MobileBackendOptions.md +0 -28
- package/docs/reference/type-aliases/MobileElementKind.md +0 -14
- package/docs/reference/type-aliases/MobileElementMatch.md +0 -60
- package/docs/reference/type-aliases/MobileElementRef.md +0 -20
- package/docs/reference/type-aliases/MobileHandle.md +0 -65
- package/docs/reference/type-aliases/MobileScenario.md +0 -20
- package/docs/reference/type-aliases/MobileSpecification.md +0 -51
- package/docs/reference/type-aliases/MobileSpecificationOptions.md +0 -126
- package/docs/reference/type-aliases/MobileVisitor.md +0 -81
- package/docs/reference/type-aliases/MockOfOptions.md +0 -26
- package/docs/reference/type-aliases/MockPort.md +0 -10
- package/docs/reference/type-aliases/PinnedClock.md +0 -10
- package/docs/reference/type-aliases/PostgresOptions.md +0 -32
- package/docs/reference/type-aliases/ProcessOptions.md +0 -112
- package/docs/reference/type-aliases/RedisOptions.md +0 -20
- package/docs/reference/type-aliases/RenderSubject.md +0 -12
- package/docs/reference/type-aliases/ScreenNode.md +0 -67
- package/docs/reference/type-aliases/ServerPort.md +0 -36
- package/docs/reference/type-aliases/ServerResponse.md +0 -46
- package/docs/reference/type-aliases/ServerSpec.md +0 -21
- package/docs/reference/type-aliases/ServiceHandle.md +0 -216
- package/docs/reference/type-aliases/ServiceRecord.md +0 -12
- package/docs/reference/type-aliases/SpecDocument.md +0 -84
- package/docs/reference/type-aliases/SpecEnvToken.md +0 -21
- package/docs/reference/type-aliases/SpecFileAssertion.md +0 -27
- package/docs/reference/type-aliases/SpecFixture.md +0 -30
- package/docs/reference/type-aliases/SpecKind.md +0 -8
- package/docs/reference/type-aliases/SpecRun.md +0 -108
- package/docs/reference/type-aliases/SpecServeEntry.md +0 -40
- package/docs/reference/type-aliases/SpecStream.md +0 -32
- package/docs/reference/type-aliases/SpecificationConfig.md +0 -290
- package/docs/reference/type-aliases/SqliteOptions.md +0 -36
- package/docs/reference/type-aliases/SseEvent.md +0 -58
- package/docs/reference/type-aliases/StreamBody.md +0 -61
- package/docs/reference/type-aliases/TextFilter.md +0 -22
- package/docs/reference/type-aliases/VisitScenario.md +0 -20
- package/docs/reference/type-aliases/Visitor.md +0 -215
- package/docs/reference/type-aliases/WaitUntilOptions.md +0 -46
- package/docs/reference/type-aliases/WebsiteBackendOptions.md +0 -37
- package/docs/reference/type-aliases/WebsiteHandle.md +0 -51
- package/docs/reference/type-aliases/WebsiteSpecification.md +0 -118
- package/docs/reference/type-aliases/WebsiteSpecificationOptions.md +0 -40
- package/docs/reference/variables/anthropic.md +0 -19
- package/docs/reference/variables/banner.md +0 -10
- package/docs/reference/variables/button.md +0 -10
- package/docs/reference/variables/clock.md +0 -16
- package/docs/reference/variables/complementary.md +0 -10
- package/docs/reference/variables/component.md +0 -10
- package/docs/reference/variables/content.md +0 -10
- package/docs/reference/variables/contentinfo.md +0 -10
- package/docs/reference/variables/dialog.md +0 -10
- package/docs/reference/variables/field.md +0 -10
- package/docs/reference/variables/form.md +0 -10
- package/docs/reference/variables/heading.md +0 -10
- package/docs/reference/variables/http.md +0 -40
- package/docs/reference/variables/intercept.md +0 -11
- package/docs/reference/variables/link.md +0 -10
- package/docs/reference/variables/listitem.md +0 -10
- package/docs/reference/variables/main.md +0 -10
- package/docs/reference/variables/match.md +0 -8
- package/docs/reference/variables/navigation.md +0 -10
- package/docs/reference/variables/openai.md +0 -21
- package/docs/reference/variables/option.md +0 -17
- package/docs/reference/variables/region.md +0 -10
- package/docs/reference/variables/row.md +0 -10
- package/docs/reference/variables/search.md +0 -10
- package/docs/reference/variables/specification.md +0 -8
- package/docs/reference/variables/status.md +0 -10
- package/docs/reference/variables/table.md +0 -10
package/docs/01-architecture.md
DELETED
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
# 01 — Architecture
|
|
2
|
-
|
|
3
|
-
What this package IS: one runner model behind six constructors, one chain that needs none, five source trees with declared edges, and seven channels through which its conventions are enforced — each row of the catalogue naming exactly one. The chapters that follow explain how to USE each facet; this one draws the lines they sit inside.
|
|
4
|
-
|
|
5
|
-
| The shape | Held below |
|
|
6
|
-
| -------------------------- | -------------------------------------------------------------------------------- |
|
|
7
|
-
| The runner model | [The runner model](#the-runner-model) — constructor, handle, chain, result |
|
|
8
|
-
| The source trees | [The five trees](#the-five-trees) — `model`, `facets`, `seams`, `runner`, `lint` |
|
|
9
|
-
| How a convention is held | [The seven enforcement channels](#the-seven-enforcement-channels) |
|
|
10
|
-
| What ships out of the tree | [What the tree publishes](#what-the-tree-publishes) |
|
|
11
|
-
|
|
12
|
-
The rules themselves — what each family says, what a reviewer must judge — are the constitution, [18 — Conventions](18-conventions.md); their normative sentences are the generated catalogue, [19 — Linting](19-linting.md).
|
|
13
|
-
|
|
14
|
-
## The runner model
|
|
15
|
-
|
|
16
|
-
Every facet is the same four steps. A **constructor** takes options and returns a **handle**; the handle opens a **chain** of zero or more setups closed by exactly one terminal action; the action executes and resolves to a **typed result**; the result is asserted through vitest's `expect()`. Nothing else is public — there is no imperative escape hatch, because a spec that can do anything proves nothing in particular.
|
|
17
|
-
|
|
18
|
-
Six constructors exist and the list is closed (`src/facets/specification.ts`):
|
|
19
|
-
|
|
20
|
-
| Constructor | Handle destructures to | Subject under test |
|
|
21
|
-
| ----------------------------- | ---------------------------------- | ------------------------------------------------ |
|
|
22
|
-
| `specification.api()` | `{ api, cleanup, docker }` | An HTTP API, built and served in this process |
|
|
23
|
-
| `specification.jobs()` | `{ jobs, cleanup }` | A background pipeline, triggered by name |
|
|
24
|
-
| `specification.cli()` | `{ cli, cleanup, docker }` | A command binary in a fresh temp directory |
|
|
25
|
-
| `specification.integration()` | `{ integration, cleanup, docker }` | A module against real services, or a golden |
|
|
26
|
-
| `specification.website()` | `{ website, cleanup, url }` | A rendered page — fetched, or driven in chromium |
|
|
27
|
-
| `specification.mobile()` | `{ mobile, cleanup, udid }` | A native screen on an iOS simulator |
|
|
28
|
-
|
|
29
|
-
The asymmetry in that column is the model, not an oversight: `jobs` never spawns a container, so it is handed no `docker`; `website` and `mobile` drive a browser and a simulator rather than an orchestrated stack, so they carry neither.
|
|
30
|
-
|
|
31
|
-
**One facet has no constructor**, and the same reason explains it: a rendered component starts nothing. `component` ([07 — Component specs](07-component.md)) is a chain the package exports directly — `component.intercept(…).render(<X />)` — so there is no handle to destructure and no `cleanup` to pass to `afterAll`. What every render of a project shares (the providers, the Vite pipeline, the viewport) is the PROJECT's, stated once by `component()` in `vitest.config.ts`.
|
|
32
|
-
|
|
33
|
-
A runner is created **once per suite**, in a `*.specification.ts` file, and imported by the test files beside it. That split is what makes the container lifecycle affordable — one Postgres per suite, not one per test — and it is why `afterAll(cleanup)` belongs in the specification file and nowhere else.
|
|
34
|
-
|
|
35
|
-
### The seam under the chain
|
|
36
|
-
|
|
37
|
-
`SpecificationBuilder` (`src/model/chain/builder.ts`) holds the chain for every facet; each facet contributes its own setups and its own terminal actions on top. The infrastructure a chain needs is reached through **ports** — `src/model/ports/` declares eight of them (`browser`, `cli`, `container`, `database`, `device`, `isolation`, `server`, `service`) — and an integration implements one. So the chain knows "a database exists"; it never knows Postgres.
|
|
38
|
-
|
|
39
|
-
Every seam is opened lazily rather than imported, and each one is an optional peer loaded by the one module that owns it: `playwright` for `.visit()`, `appium`/`webdriverio` for `.open()`, `@vitest/browser-playwright` + `vitest-browser-react` + `react` for `component.render()`, and — since 16.0 — `better-sqlite3`, `pg`, `redis` and `testcontainers` for the services a record declares. A project that tests no page, no component and no database installs none of them; `src/seams/peer.ts` owns the one message a missing peer produces, which names the facet that asked, the command, and under pnpm the `onlyBuiltDependencies` line a native binding needs.
|
|
40
|
-
|
|
41
|
-
## The five trees
|
|
42
|
-
|
|
43
|
-
The source tree is five trees with declared, one-directional edges. The map is not the linter's to assume — `i1-layer-boundaries` ships inert — so this package declares its own as `FRAMEWORK_LAYERS` in `oxlint.config.ts`, and that declaration is the enforced statement of what follows.
|
|
44
|
-
|
|
45
|
-
| Tree | May import | Holds |
|
|
46
|
-
| --------- | --------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
47
|
-
| `model/` | itself, the docker/process/yaml seams, the msw seam through two named lazy imports, `runner/update`, and each facet's `*.result` module | What every facet is made of: the chain and the orchestrator, the results and their accessors, the element vocabulary, the contracts and `intercept()`, the `{{token}}` engine, the goldens, the clock, the doubles, the ports |
|
|
48
|
-
| `facets/` | `model/`, itself, the runner's project shape, and the one seam each constructor opens | Seven folders, the SAME four files in each: `<facet>.specification.ts` (the constructor), `<facet>.chain.ts`, `<facet>.result.ts`, `<facet>.project.ts` (the vitest helper), plus what is unique to the facet |
|
|
49
|
-
| `seams/` | its OWN external dependency, plus `model/` and the facet vocabulary an adapter projects into | One folder per dependency: `postgres`, `redis`, `sqlite`, `testcontainers`, `docker`, `hono`, `playwright`, `vitest-browser`, `appium`, `msw`, `openai`, `anthropic`, `yaml`, `process` |
|
|
50
|
-
| `runner/` | `vitest`, `vite`, `@vitest/browser-playwright`, `model/`, `facets/` | The CONFIG side of the runner coupling: the preset, update mode, the `literate()` plugin, the shape a node facet's project takes, and the project index |
|
|
51
|
-
| `lint/` | itself, plus a short list of PURE `model/` modules | The tool-facing channel: the oxlint plugin, the conventions checker, the catalogue manifest and generator |
|
|
52
|
-
|
|
53
|
-
**Every facet folder carries the same four files, whether or not it owns all four.** The setups and terminal actions of the six node facets are ONE builder, because a second copy of "declare a contract, seed a table, run the thing" is how two facets drift — so `api.chain.ts` and its five siblings are a named re-export of that builder rather than an implementation, and `jobs.result.ts` re-exports the shape the builder states. The shape is uniform so that a reader who has opened one facet has opened all seven; the re-export says, in the folder itself, which of the four a facet does not own.
|
|
54
|
-
|
|
55
|
-
Three of those edges carry their reason in the declaration itself. `model/` reaches `seams/docker`, `seams/process` and the yaml parser because none of the three has a dependency of its own to leak, and it reaches each facet's `*.result` module because the ONE builder constructs all seven results — a value edge, stated rather than hidden. `lint/` reaches exactly the pure modules the runner also uses — the token list, the ground names, the root walk, the `<case>.spec.yaml` parser — so that the file the lint accepts is the file the runner runs, from ONE parser. And the runner is named in exactly TWO places outside a test, one per side of it: `runner/` for the config side — the preset, the project shape, the plugin — and `seams/vitest-browser/` for the page side, where the locators, the visitor, the golden commands and the page's own `vitest` primitives live. Swapping the runner would be a rewrite of those two folders and of nothing else; `model/` states what a render IS and never which runner performs it, which is why the layer map exempts that seam from the prod-import ban (F2) by name.
|
|
56
|
-
|
|
57
|
-
The table's May-import column is a reading of `FRAMEWORK_LAYERS`, never a second statement of it: the declaration in `oxlint.config.ts` is what the linter enforces and what a change edits.
|
|
58
|
-
|
|
59
|
-
There are TWO composition roots and neither names a layer. `src/index.ts` is node's: it wires the container integrations into the registry seam and re-exports the public surface. `src/browser/index.ts` is the page's, and `src/surface.ts` is what they agree on — see [What the tree publishes](#what-the-tree-publishes).
|
|
60
|
-
|
|
61
|
-
## The seven enforcement channels
|
|
62
|
-
|
|
63
|
-
A convention this package states is held by a **channel**, and every row of the catalogue names exactly one of seven. The guiding aim is that most enforcement is programmatic, not review-borne.
|
|
64
|
-
|
|
65
|
-
- **statique** — the `jterrazz/*` oxlint plugin: one file per rule under `src/lint/rules/model/` or `src/lint/rules/<facet>/` — the tree the rule reaches, which is also the `facet` field of its manifest row — AST analysis, one diagnostic per file.
|
|
66
|
-
- **upstream** — a convention an oxlint vitest-plugin rule already holds, set as an OPTION from the `testing` fragment rather than duplicated as a rule of ours ([ADR-005](decisions/005-the-doubles-ladder-is-closed.md)).
|
|
67
|
-
- **checker** — passes of the `jterrazz-test-check` binary reading what an AST cannot: the `{{token}}` grammar of `_requests/` and `_expected/` fixtures, the `<case>.spec.yaml` document family, cross-file analyses that cross a `*.specification.ts` with its tests, and the member pass that judges a package rather than a tree.
|
|
68
|
-
- **runtime** — the framework refuses incorrect usage as it executes, where static analysis abstains (a non-literal argument) or cannot reach (the network, a container lifecycle).
|
|
69
|
-
- **type** — what the compiler refuses with no rule at all, proven by `src/type-channel.test-d.ts`.
|
|
70
|
-
- **meta** — what the framework's own suite holds about itself: the catalogue's freshness, its completeness, the reach of its rules — [03 — Testing](03-testing.md) owns the channel.
|
|
71
|
-
- **process** — the judgement no single channel settles: asset-driven grouping, golden-file discipline, retro-propagation.
|
|
72
|
-
|
|
73
|
-
The channel a rule sits on is not prose here: it is a field of `src/lint/manifest.ts`, the single source of truth from which the catalogue in [19 — Linting](19-linting.md) is generated. A rule and its normative sentence live together in the code, so the two cannot drift.
|
|
74
|
-
|
|
75
|
-
## What the tree publishes
|
|
76
|
-
|
|
77
|
-
The bundle is built by `tsdown` into `dist/` and its shape follows how each entry is consumed. `index` and `vitest`, plus the `checker`/`catalog` CLIs, are ESM-only — vitest is ESM-only and the CLIs are invoked as `node dist/*.js`. The `oxlint` plugin ships dual, because oxlint loads it from a consumer project that may itself be CommonJS.
|
|
78
|
-
|
|
79
|
-
**There are FOUR entries and no more**: `@jterrazz/test` for everything a spec uses, `@jterrazz/test/vitest` for what `vitest.config.ts` needs, `@jterrazz/test/oxlint` for the lint plugin, `@jterrazz/test/schema` for an editor validating a `<case>.spec.yaml`. That is rule F1 rather than a convention of taste, and the exemption is derived from the manifest's own `exports` map (`src/lint/package-exports.ts`), so a subpath is exempt the moment it is published and stops being exempt the moment it is withdrawn.
|
|
80
|
-
|
|
81
|
-
**The root entry has two runtimes.** A component test renders in a real browser, and the node entry cannot load there: it wires `pg`, `better-sqlite3`, `testcontainers` and `node:fs` at import. So `exports["."]` carries a `browser` condition resolving to `dist/browser/index.js` — the same public surface, built for a page, where every node-only name (the six constructors, the services, the docker accessors, the results that walk a disk) is a stub that throws where it was called, naming the facet and saying it runs under node. The split is the RUNTIME's and never the facets': one specifier, so no spec has to remember which entry it may import.
|
|
82
|
-
|
|
83
|
-
`types` is stated once, and it is the node build's — so the two runtimes are ONE type surface and a name cannot exist on one side only. What both agree on lives in `src/surface.ts`, and the `package-exports` meta-test compares the two builds' exported names on every run.
|
|
84
|
-
|
|
85
|
-
Two committed projections leave the code and land in the corpus: the API reference under `docs/reference/` (typedoc, through `typescript docs`) and the rule catalogue spliced into [19 — Linting](19-linting.md) and `skills/jterrazz-test/references/rules.md`. A third, `schema/spec.schema.json`, is generated from the document grammar's own constants. Both `docs/` and `schema/` ship in the tarball — the reference included, since the corpus links into it — so a consumer reads the manual where the messages send them, inside the install ([04 — Operating](04-operating.md#what-leaves-the-tree)). All three are regenerated by one gesture and sync-checked — see [02 — Developing](02-developing.md).
|
|
86
|
-
|
|
87
|
-
## Pitfalls
|
|
88
|
-
|
|
89
|
-
- **Reaching for a dependency from `model/`.** A new external package belongs in a folder of `seams/` that imports it and nothing else; `model/` importing it directly is the one boundary this package cannot afford to blur, and `i1-layer-boundaries` refuses it.
|
|
90
|
-
- **Adding a seventh constructor.** The six are the closed vocabulary the conventions, the linter and the documentation are all shaped around. A new SUBJECT to specify is a design decision, and it earns a record in [`decisions/`](decisions/) before it earns a constructor — as `integration` did in [ADR-004](decisions/004-a-module-against-real-services-or-a-golden-is-the-integration-facet.md).
|
|
91
|
-
- **Writing a rule's normative sentence into a chapter.** It belongs in `src/lint/manifest.ts` beside the implementation; a chapter that restates it is the second copy the whole design exists to prevent.
|
|
92
|
-
|
|
93
|
-
## Related
|
|
94
|
-
|
|
95
|
-
[02 — Developing](02-developing.md) · [03 — Testing](03-testing.md) · [18 — Conventions](18-conventions.md) · [19 — Linting](19-linting.md)
|
package/docs/02-developing.md
DELETED
|
@@ -1,351 +0,0 @@
|
|
|
1
|
-
# 02 — Developing
|
|
2
|
-
|
|
3
|
-
How a change is made. The first half is this repository's own loop — the toolchain, the order its gates demand, which file a change opens and what it owes when it lands. The second half is the walkthrough a consuming project follows, from `npm install` to two passing specs.
|
|
4
|
-
|
|
5
|
-
## Changing this package
|
|
6
|
-
|
|
7
|
-
### The loop
|
|
8
|
-
|
|
9
|
-
| Task | Command |
|
|
10
|
-
| -------------------------------- | --------------------------------- |
|
|
11
|
-
| Install dependencies | `npm install` (or `make install`) |
|
|
12
|
-
| Build the bundle | `npm run build` |
|
|
13
|
-
| Lint + format + typecheck + knip | `npm run lint` |
|
|
14
|
-
| Auto-fix what a fixer can | `npm run lint:fix` |
|
|
15
|
-
| Run every suite | `npm test` |
|
|
16
|
-
| Run the fast suite only | `npx vitest --run --project unit` |
|
|
17
|
-
| Regenerate the projections | `npm run docs` (or `make docs`) |
|
|
18
|
-
|
|
19
|
-
Each has a `make` alias that installs first, which is what CI calls — [03 — Testing](03-testing.md) § What CI runs.
|
|
20
|
-
|
|
21
|
-
`better-sqlite3` is a native dependency, and an npm that does not run install scripts by default leaves it unbuilt: the `sqlite()` specs of the `unit` project then fail on a missing binding. `npm rebuild better-sqlite3` after the install fixes it, once per checkout.
|
|
22
|
-
|
|
23
|
-
### The build comes first
|
|
24
|
-
|
|
25
|
-
`oxlint.config.ts` loads this package's OWN plugin from `./dist/oxlint.js`, and the end-to-end lint specs load it too. Node's type-stripping does not resolve a `.js` specifier back to its `.ts` source, so **`npm run build` must precede `npm run lint`** and must precede the `unit` project. A lint run on a stale bundle judges the previous build's rules.
|
|
26
|
-
|
|
27
|
-
That config is also where this repository DECLARES its own architecture: `i1-layer-boundaries` ships inert, and `FRAMEWORK_LAYERS` in `oxlint.config.ts` is the enforced statement of the five trees [01 — Architecture](01-architecture.md) describes.
|
|
28
|
-
|
|
29
|
-
### Which file a change opens
|
|
30
|
-
|
|
31
|
-
| Changing… | Opens |
|
|
32
|
-
| ------------------------------------------------- | ------------------------------------------------------------------------------------------ |
|
|
33
|
-
| A facet's chain, setups or terminal actions | `src/facets/<facet>/`, over `model/chain/builder.ts` |
|
|
34
|
-
| What a result exposes | `src/model/result/` — accessors stay READ-ONLY |
|
|
35
|
-
| A matcher, or update-mode behaviour | `src/model/goldens/` and `src/runner/` — the two sides of the runner coupling |
|
|
36
|
-
| An external dependency's adapter | `src/seams/<dep>/`, which imports that dep and `model/` and no more |
|
|
37
|
-
| A `{{token}}` or the structural comparison | `src/model/matching/` |
|
|
38
|
-
| The `<case>.spec.yaml` grammar | `src/model/literate/` — read by BOTH the runner and the checker |
|
|
39
|
-
| A mechanized rule | `src/lint/manifest.ts` **and** its implementation under `src/lint/rules/<facet-or-model>/` |
|
|
40
|
-
| A principle, or a criterion no machine can settle | [18 — Conventions](18-conventions.md), the constitution |
|
|
41
|
-
|
|
42
|
-
### What a change owes
|
|
43
|
-
|
|
44
|
-
Four things land in the SAME commit as the change that makes them true.
|
|
45
|
-
|
|
46
|
-
- **The guard.** Every defect class discovered — in review, from a bug, during a migration — grows the thing that stops it recurring: a static rule, a meta-test, or a runtime refusal. That is rule K1, and it is what keeps the other channels growing instead of decaying. When no channel is possible, the change says so explicitly.
|
|
47
|
-
- **The regenerated projections.** `npm run docs` rewrites them all at once — the API reference under `docs/reference/`, the rule catalogue spliced into [19 — Linting](19-linting.md) and `skills/jterrazz-test/references/rules.md`, the capability matrix, the domain vocabulary and the siblingless table in [03 — Testing](03-testing.md), the fork in [18 — Conventions](18-conventions.md), the signature cards, and `schema/spec.schema.json`. Never edit one by hand: `npm run lint` runs the sync check and the freshness meta-tests, and both fail on a hand edit.
|
|
48
|
-
|
|
49
|
-
**`docs/reference/` is regenerated from a plain CLONE, never from a worktree.** typedoc writes a `Defined in:` link per symbol from whatever the git origin resolves to, and a worktree under `home/<brand>/work/worktrees/` resolves it to a local path — which turns every `https://github.com/jterrazz/package-test/blob/main/…` into a bare path, a whole-file diff that looks like content and is only an address. So `Docs (sync)` is red on `docs/reference/` in a worktree BY CONSTRUCTION, and the projection is refreshed by regenerating it in a clone and committing what that writes.
|
|
50
|
-
|
|
51
|
-
- **The chapter the behaviour falsified.** A page that still describes the old behaviour is a defect that ships. The corpus is mapped by [`docs/README.md`](README.md).
|
|
52
|
-
- **The skill, when the public surface moved.** `skills/jterrazz-test/` routes agents into these chapters; `README.md` is the vitrine and moves with a public API change too.
|
|
53
|
-
|
|
54
|
-
## Using the framework
|
|
55
|
-
|
|
56
|
-
The rest of this chapter takes a consuming project from `npm install` to two passing specs: one HTTP API spec backed by a real Postgres container, and one CLI spec running a binary in a fresh temp directory. It also explains the one framework environment variable (`TEST_UPDATE`).
|
|
57
|
-
|
|
58
|
-
### Install
|
|
59
|
-
|
|
60
|
-
```bash
|
|
61
|
-
npm install -D @jterrazz/test vitest
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
Peer dependencies:
|
|
65
|
-
|
|
66
|
-
| Package | Required | Needed for |
|
|
67
|
-
| -------- | -------- | ------------------------------------------------------------- |
|
|
68
|
-
| `vitest` | yes | Everything — the framework registers its matchers into vitest |
|
|
69
|
-
|
|
70
|
-
`msw` (outgoing HTTP interception for `.intercept()`) ships as a direct dependency — no separate install. API specs pass your web app to `server`; the adapter only needs an object with a `request()` method, so bring your own web framework (e.g. `hono`) in your project.
|
|
71
|
-
|
|
72
|
-
**Docker** must be running for container-backed services (`postgres()`, `redis()`). `sqlite()` and plain CLI specs need no Docker.
|
|
73
|
-
|
|
74
|
-
Trying an unreleased branch of the framework: install a `npm pack` tarball, never a `file:` link — a link makes the consumer resolve `vitest`'s types twice, and the matcher augmentation then lands on one copy and not the other, so `toMatch` types while `toBeEmpty` does not.
|
|
75
|
-
|
|
76
|
-
Everything a spec needs imports from the single package root; the tool subpaths beside it are the ones the `exports` map publishes, and they are listed once — [01 § What the tree publishes](01-architecture.md#what-the-tree-publishes). F1 and F3 read that map, so a specifier it does not publish is refused:
|
|
77
|
-
|
|
78
|
-
```typescript
|
|
79
|
-
// the runners, the services, and what a chain stands on
|
|
80
|
-
import { postgres, process, redis, specification, sqlite } from '@jterrazz/test';
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
```typescript
|
|
84
|
-
// what a test says: contracts, dynamic values, doubles, time, assertions
|
|
85
|
-
import { anthropic, clock, defineContract, http, intercept } from '@jterrazz/test';
|
|
86
|
-
import { match, mockOf, openai, required, waitUntil } from '@jterrazz/test';
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
### The shape of every test
|
|
90
|
-
|
|
91
|
-
A **specification file** (`*.specification.ts`, under `specs/`) creates a runner once per suite. A **test file** imports the runner and writes specs. Every spec is one chain: zero or more setups, then exactly one terminal action, resolving to a typed result you assert on with `expect()`.
|
|
92
|
-
|
|
93
|
-
```
|
|
94
|
-
specification.api(…) → { api, cleanup, docker }
|
|
95
|
-
specification.jobs(…) → { jobs, cleanup } // no docker — jobs never spawn containers
|
|
96
|
-
specification.cli(…) → { cli, cleanup, docker }
|
|
97
|
-
specification.integration(…) → { integration, cleanup } // a module, against real services or a golden
|
|
98
|
-
specification.website(…) → { website, cleanup, url } // no docker — a browser, not a container
|
|
99
|
-
specification.mobile(…) → { mobile, cleanup, udid } // no docker — a simulator, not a container
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
The destructured names are canonical — no aliasing (`{ api: myApi }` is an error, rule A3) — and every specification file registers `afterAll(cleanup)` (rule A4).
|
|
103
|
-
|
|
104
|
-
### First API spec
|
|
105
|
-
|
|
106
|
-
```typescript
|
|
107
|
-
// specs/api/api.specification.ts
|
|
108
|
-
import { afterAll } from 'vitest';
|
|
109
|
-
import { specification, postgres } from '@jterrazz/test';
|
|
110
|
-
import { createApp } from '../../src/app.js';
|
|
111
|
-
|
|
112
|
-
export const { api, cleanup } = await specification.api({
|
|
113
|
-
services: {
|
|
114
|
-
db: postgres(), // reported as "db"; its init reads docker/db/ (chapter 17)
|
|
115
|
-
},
|
|
116
|
-
server: ({ db }) => createApp({ databaseUrl: db.connectionString }),
|
|
117
|
-
// root: absent — auto-discovered by walking up to the nearest package.json
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
afterAll(cleanup);
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
```http
|
|
124
|
-
### specs/api/users/_requests/create-user.http — the COMPLETE request
|
|
125
|
-
POST /users
|
|
126
|
-
Content-Type: application/json
|
|
127
|
-
|
|
128
|
-
{ "name": "Alice" }
|
|
129
|
-
```
|
|
130
|
-
|
|
131
|
-
```http
|
|
132
|
-
### specs/api/users/_expected/user-created.http — status + header subset + body
|
|
133
|
-
HTTP/1.1 201 Created
|
|
134
|
-
Content-Type: application/json
|
|
135
|
-
|
|
136
|
-
{ "id": "{{uuid}}", "name": "Alice" }
|
|
137
|
-
```
|
|
138
|
-
|
|
139
|
-
```typescript
|
|
140
|
-
// specs/api/users/users.spec.ts
|
|
141
|
-
import { expect, test } from 'vitest';
|
|
142
|
-
import { api } from '../api.specification.js';
|
|
143
|
-
|
|
144
|
-
test('creates a user', async () => {
|
|
145
|
-
// Given - empty database
|
|
146
|
-
const result = await api.request('create-user.http');
|
|
147
|
-
|
|
148
|
-
// Then - response matches the fixture; row landed in the database
|
|
149
|
-
expect(result.response).toMatch('user-created.http');
|
|
150
|
-
await expect(result.table('users')).toMatchRows({
|
|
151
|
-
columns: ['name'],
|
|
152
|
-
rows: [['Alice']],
|
|
153
|
-
});
|
|
154
|
-
});
|
|
155
|
-
```
|
|
156
|
-
|
|
157
|
-
`{{uuid}}` is a placeholder from the unified [token grammar](15-tokens.md) — the response body must contain _a_ UUID there, whatever its value.
|
|
158
|
-
|
|
159
|
-
### First CLI spec
|
|
160
|
-
|
|
161
|
-
```typescript
|
|
162
|
-
// specs/cli/cli.specification.ts
|
|
163
|
-
import { resolve } from 'node:path';
|
|
164
|
-
import { afterAll } from 'vitest';
|
|
165
|
-
import { specification } from '@jterrazz/test';
|
|
166
|
-
|
|
167
|
-
export const { cli, cleanup } = await specification.cli(
|
|
168
|
-
resolve(import.meta.dirname, '../../bin/my-cli.sh'),
|
|
169
|
-
);
|
|
170
|
-
|
|
171
|
-
afterAll(cleanup);
|
|
172
|
-
```
|
|
173
|
-
|
|
174
|
-
```typescript
|
|
175
|
-
// specs/cli/help/help.spec.ts
|
|
176
|
-
import { expect, test } from 'vitest';
|
|
177
|
-
import { cli } from '../cli.specification.js';
|
|
178
|
-
|
|
179
|
-
test('shows help', async () => {
|
|
180
|
-
// Given
|
|
181
|
-
const result = await cli.exec('--help');
|
|
182
|
-
|
|
183
|
-
// Then - full snapshot of stdout against _expected/help.txt
|
|
184
|
-
expect(result.exitCode).toBe(0);
|
|
185
|
-
expect(result.stdout).toMatch('help.txt');
|
|
186
|
-
});
|
|
187
|
-
```
|
|
188
|
-
|
|
189
|
-
```
|
|
190
|
-
### specs/cli/help/_expected/help.txt — tokens work in text snapshots too
|
|
191
|
-
my-cli v{{semver}}
|
|
192
|
-
Started at {{iso8601}} in {{workdir}}
|
|
193
|
-
Done in {{duration}}
|
|
194
|
-
```
|
|
195
|
-
|
|
196
|
-
Each CLI spec runs in a fresh, empty temp directory. ANSI escape sequences are stripped before comparison by default (rule D6) — you never snapshot color codes.
|
|
197
|
-
|
|
198
|
-
### vitest config: the preset
|
|
199
|
-
|
|
200
|
-
`vitest.config.ts` starts from `defineSpecConfig()` — the shared preset, imported from the tool subpath beside `literate()`. What you pass is a plain vite/vitest config merged **over** the defaults, so one call gives you the ecosystem's common ground and you still state whatever you want:
|
|
201
|
-
|
|
202
|
-
```typescript
|
|
203
|
-
// vitest.config.ts
|
|
204
|
-
import { api, component, defineSpecConfig, unit, website } from '@jterrazz/test/vitest';
|
|
205
|
-
|
|
206
|
-
export default defineSpecConfig({
|
|
207
|
-
test: {
|
|
208
|
-
projects: [
|
|
209
|
-
unit(), // `**/*.test.ts` outside specs/ — no browser, no pipeline
|
|
210
|
-
component({ vite: './vite.config.ts', wrap: './src/providers.tsx' }),
|
|
211
|
-
website(), // `specs/website/**` — the served product, in its own group
|
|
212
|
-
api(), // `specs/api/**/*.spec.ts` — the app through HTTP, in-process
|
|
213
|
-
],
|
|
214
|
-
},
|
|
215
|
-
});
|
|
216
|
-
```
|
|
217
|
-
|
|
218
|
-
What every helper accepts, on top of the project it already is:
|
|
219
|
-
|
|
220
|
-
| Option | Does |
|
|
221
|
-
| --------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
|
|
222
|
-
| `include` / `exclude` | Replace the canonical globs. A default `exclude` guards the default `include`, so state both or neither |
|
|
223
|
-
| `timeout` | Raise (or lower) the preset's 30 s for this project alone |
|
|
224
|
-
| `serial` | `fileParallelism: false` — the project's files run one at a time, for a facet whose files share one server, one database file, one app |
|
|
225
|
-
|
|
226
|
-
`component()` takes four more that are the APP's — `vite`, `wrap`, `viewport`, `timezone`/`locale`, and the `root` its relative paths are read against — and [07 — Component specs](07-component.md) owns them; `unit()` takes `roots`.
|
|
227
|
-
|
|
228
|
-
`unit()` and `component()` are a pair by construction: `unit()` collects `**/*.test.ts` outside `specs/` and excludes `**/*.test.tsx`, `component()` collects exactly those, so the suffix beside a file decides which project runs it and which rules judge it. `unit()` takes `roots?` where the modules are not at the repository root, or `include?` for the globs outright; the facet helpers collect `specs/<facet>/**/*.spec.ts` — the word for the assembled product (C12). All three carry `sequence.groupOrder` — node 0, `website` 1, `component` 2 — so the two browser projects never open two Chromiums at once on a 2-vCPU runner. What `component()` sets — the provider pinned to the runner's exact version, the msw worker served from this package's own install, the JSX transform the current Vite uses, the dependencies a cold cache must pre-bundle, the artefact directories, the group order that keeps two Chromiums apart — is [07 — Component specs](07-component.md)'s. `component()` is async, and a project may be a promise: `projects: [component({ … })]` needs no `await`.
|
|
229
|
-
|
|
230
|
-
#### What the preset sets
|
|
231
|
-
|
|
232
|
-
| Setting | Value | Why |
|
|
233
|
-
| -------------------------------- | ---------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
234
|
-
| `cacheDir` | `.artifacts/vitest` | The artefact convention below — vite's transform cache leaves `node_modules/` |
|
|
235
|
-
| `test.coverage.reportsDirectory` | [the artefact path](#artefacts-live-under-artifacts) | Where a tool writes what it generates is the toolchain's convention ([`@jterrazz/typescript` — Developing](https://github.com/jterrazz/package-typescript/blob/main/docs/02-developing.md)); the preset sets it so no consumer writes it. The **provider is yours to install** (`@vitest/coverage-v8`) |
|
|
236
|
-
| `test.testTimeout` | `30_000` | Vitest's 5s never survived a container boot, a `prisma db push` or a `next build` |
|
|
237
|
-
| `test.hookTimeout` | `30_000` | Same reason, for the `beforeAll` that starts the infrastructure |
|
|
238
|
-
| `test.exclude` | vitest's defaults + `**/_fixtures/**` | What a spec stands on is an input, never a suite — a repository must not run its own counter-examples |
|
|
239
|
-
| `test.attachmentsDir` | `.artifacts/vitest/attachments` | Vitest 5 writes `context.annotate()` attachments to `.vitest/` at the repository root |
|
|
240
|
-
| `test.outputFile` | `.artifacts/vitest/{json,junit,html,blob}` | Vitest 5 turned the json and junit reporters into FILE writers; all four are artefacts |
|
|
241
|
-
| `test.retry` | `0` | A flaky test is fixed or deleted — a retry turns a real defect into a slow one and hides it |
|
|
242
|
-
| `test.restoreMocks` | `true` | A `vi.spyOn` a test forgets to restore fails a LATER file, one that did nothing wrong |
|
|
243
|
-
| `test.unstubGlobals` | `true` | Same for `vi.stubGlobal` — and it is what lets a test be written without an `afterEach` (rule J6w) |
|
|
244
|
-
| `test.unstubEnvs` | `true` | Same for `vi.stubEnv`, which is the sanctioned way to state a module's env contract (rule E9w) |
|
|
245
|
-
| `plugins` | `literate()`, when `literate:` is given | Turns every matching `<case>.spec.yaml` into a test file (see [12 — CLI specs](12-cli.md)) |
|
|
246
|
-
|
|
247
|
-
It deliberately sets **nothing else**. `fileParallelism` is a per-project truth (a container-lifecycle suite is serial, an isolated one is not), stated by the project rather than by the preset — a helper states it for you when you pass `serial`, and a hand-written project states it itself; so do `reporters`, `environment`, `env`, `globalSetup` and every `include` — a preset that guessed those would be wrong more often than right.
|
|
248
|
-
|
|
249
|
-
**Why the artefact paths are the preset's.** Every one of them is a path the TOOL pins and a consumer never writes: a repository does not choose where `context.annotate()` lands, it discovers it one CI run later, as an untracked `.vitest/` beside `.artifacts/`. Moving them here is the same rule the estate applies to every tool — what a tool generates lives at `.artifacts/<tool>/` — applied once instead of in fourteen configs.
|
|
250
|
-
|
|
251
|
-
Two behaviours worth knowing:
|
|
252
|
-
|
|
253
|
-
- **Projects inherit the defaults too.** Vitest resolves each project as its own config and it inherits nothing from the root, so the preset merges the budgets, the exclusions and the cache dir into every inline project. A project declared as a glob string, a promise or a function is handed back untouched.
|
|
254
|
-
- **Each glob is stated once.** A project's `exclude` REPLACES vitest's own defaults, so every project carries the preset's list in full; the root's list, which vitest concatenates into each project on top of that, then holds only what YOU stated there. The no-tests banner is the one place a consumer ever reads that list, and it read as three merges of the same three globs.
|
|
255
|
-
- **Arrays are ADDITIVE.** Vite's merge concatenates them, so your `exclude` adds to the preset's — you never spread `configDefaults.exclude` again — and your `plugins` join `literate()` rather than replacing it. Scalars (`testTimeout`, `name`, …) are plain overrides: what you state wins.
|
|
256
|
-
|
|
257
|
-
With `projects`, put `literate()` in the ONE project that collects those documents — its glob has to join that project's include — rather than in the top-level `literate:` key:
|
|
258
|
-
|
|
259
|
-
```typescript
|
|
260
|
-
import { defineSpecConfig, literate } from '@jterrazz/test/vitest';
|
|
261
|
-
|
|
262
|
-
export default defineSpecConfig({
|
|
263
|
-
test: {
|
|
264
|
-
projects: [
|
|
265
|
-
{
|
|
266
|
-
plugins: [literate({ specification: './specs/cli/cli.specification.ts' })],
|
|
267
|
-
test: { name: 'cli', include: ['specs/cli/**/*.spec.ts'] },
|
|
268
|
-
},
|
|
269
|
-
],
|
|
270
|
-
},
|
|
271
|
-
});
|
|
272
|
-
```
|
|
273
|
-
|
|
274
|
-
#### Migrating a hand-rolled config
|
|
275
|
-
|
|
276
|
-
Swap the import, drop what the preset already says, keep what is yours:
|
|
277
|
-
|
|
278
|
-
```diff
|
|
279
|
-
-import { configDefaults, defineConfig } from 'vitest/config';
|
|
280
|
-
-import { literate } from '@jterrazz/test/vitest';
|
|
281
|
-
+import { defineSpecConfig, literate } from '@jterrazz/test/vitest';
|
|
282
|
-
|
|
283
|
-
-export default defineConfig({
|
|
284
|
-
+export default defineSpecConfig({
|
|
285
|
-
test: {
|
|
286
|
-
- testTimeout: 30_000,
|
|
287
|
-
- hookTimeout: 30_000,
|
|
288
|
-
|
|
289
|
-
projects: [
|
|
290
|
-
{
|
|
291
|
-
test: {
|
|
292
|
-
name: 'api',
|
|
293
|
-
include: ['specs/api/**/*.spec.ts'],
|
|
294
|
-
- exclude: [...configDefaults.exclude, '**/_fixtures/**', 'specs/api/heavy/**'],
|
|
295
|
-
+ exclude: ['specs/api/heavy/**'],
|
|
296
|
-
- testTimeout: 30_000,
|
|
297
|
-
},
|
|
298
|
-
},
|
|
299
|
-
],
|
|
300
|
-
},
|
|
301
|
-
});
|
|
302
|
-
```
|
|
303
|
-
|
|
304
|
-
Then add `.artifacts/` to `.gitignore` and drop `node_modules/.vite` from it if it was listed. A project whose timeouts were LOWER than 30s, or higher, keeps stating them — the preset is a floor to start from, not a ceiling.
|
|
305
|
-
|
|
306
|
-
### Artefacts live under `.artifacts/`
|
|
307
|
-
|
|
308
|
-
The `.artifacts/<tool>/` convention is [`@jterrazz/typescript`](https://github.com/jterrazz/package-typescript/blob/main/docs/02-developing.md)'s, and its `check` enforces it: one folder per tool at the project root, one line in `.gitignore`, one `rm -rf .artifacts` for a clean slate. What this framework writes there is the only part this chapter answers for.
|
|
309
|
-
|
|
310
|
-
What this framework writes there:
|
|
311
|
-
|
|
312
|
-
| Path | Written by | Lifetime |
|
|
313
|
-
| ------------------------------------------------ | ---------------------------- | ------------------------------- |
|
|
314
|
-
| `.artifacts/vitest/` | vite's transform cache | Reused across runs |
|
|
315
|
-
| `.artifacts/vitest/coverage/` | v8, which the preset selects | Rewritten per coverage run |
|
|
316
|
-
| `.artifacts/vitest/sqlite/template-<key>.sqlite` | `sqlite()`'s schema template | Reused until the schema changes |
|
|
317
|
-
|
|
318
|
-
What it does **not** write there: the fresh temp directory each CLI spec runs in, the per-worker SQLite copies, the profile dirs a browser or a simulator needs. Those are per-RUN scratch, they stay in the OS temp dir, and moving them into the project would only put a `package.json` above a spec that must not see one.
|
|
319
|
-
|
|
320
|
-
### Framework environment variables
|
|
321
|
-
|
|
322
|
-
You set exactly one variable, prefixed `TEST_` (rule E1): `TEST_UPDATE=1` turns on update mode, which [15 — Tokens § Update mode](15-tokens.md#update-mode-tokens-are-preserved) owns whole — what it writes, what it preserves, and the discipline it asks of a reviewer. The framework also reads vitest's own `VITEST_POOL_ID` (set by vitest, not you) to isolate each parallel worker's database schema and index.
|
|
323
|
-
|
|
324
|
-
### Directory layout at a glance
|
|
325
|
-
|
|
326
|
-
```
|
|
327
|
-
specs/
|
|
328
|
-
├── api/
|
|
329
|
-
│ ├── api.specification.ts
|
|
330
|
-
│ └── users/
|
|
331
|
-
│ ├── users.spec.ts # <aspect>.spec.ts inside its domain (rule C1)
|
|
332
|
-
│ ├── _seeds/ # *.sql
|
|
333
|
-
│ ├── _requests/ # *.http — complete requests (inputs)
|
|
334
|
-
│ ├── contracts/ # <name>.contracts.ts facade + <provider>/<name>.ts units + their data
|
|
335
|
-
│ └── _expected/ # all expected fixtures, flat — incl. response *.http (a slash in the name creates a subfolder)
|
|
336
|
-
└── cli/
|
|
337
|
-
├── cli.specification.ts # runner at the facet root (rule C1)
|
|
338
|
-
└── help/
|
|
339
|
-
└── help.spec.ts # under specs/, the suffix is .spec.ts (rule C12)
|
|
340
|
-
```
|
|
341
|
-
|
|
342
|
-
## Pitfalls
|
|
343
|
-
|
|
344
|
-
- **Renaming the destructured runner** (`const { api: usersApi } = …`). The canonical names `api`, `jobs`, `cli`, `website`, `mobile` are enforced (rule A3).
|
|
345
|
-
- **Forgetting `afterAll(cleanup)`.** Infrastructure leaks across suites; rule A4 requires it in every specification file.
|
|
346
|
-
- **Importing from a subpath the `exports` map does not publish** (`@jterrazz/test/services`). Everything a spec needs comes from `@jterrazz/test` (rule F1).
|
|
347
|
-
- **Writing `// Given` without `// Then`** (or vice versa). Every test carries both comments (rule B4); `// When` only when the action is not obvious — the chain _is_ the when.
|
|
348
|
-
|
|
349
|
-
## Related
|
|
350
|
-
|
|
351
|
-
[10 — API specs](10-api.md) · [12 — CLI specs](12-cli.md) · [14 — Assertions](14-assertions.md) · [18 — Conventions](18-conventions.md) · [08 — Website specs](08-website.md) · [09 — Mobile specs](09-mobile.md)
|