@jterrazz/test 16.1.1 → 18.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.
Files changed (309) hide show
  1. package/README.md +45 -171
  2. package/dist/browser/index.js +1 -2980
  3. package/dist/browser/services.js +1 -0
  4. package/dist/browser/stubs.js +1 -0
  5. package/dist/browser-shared-index.browser.js +3233 -0
  6. package/dist/checker.d.ts +6 -1
  7. package/dist/checker.js +3721 -2624
  8. package/dist/config.d.ts +40 -0
  9. package/dist/config.js +309 -0
  10. package/dist/in-place.global-setup.d.ts +12 -0
  11. package/dist/in-place.global-setup.js +21 -0
  12. package/dist/index.d.ts +1641 -3004
  13. package/dist/index.js +5095 -3835
  14. package/dist/mobile-session.global-setup.d.ts +15 -0
  15. package/dist/mobile-session.global-setup.js +31 -0
  16. package/dist/oxlint.cjs +6069 -5634
  17. package/dist/oxlint.d.cts +43 -7212
  18. package/dist/oxlint.d.ts +43 -7212
  19. package/dist/oxlint.js +3979 -5799
  20. package/dist/route-coverage.global-setup.d.ts +43 -0
  21. package/dist/route-coverage.global-setup.js +1 -0
  22. package/dist/services.d.ts +3 -0
  23. package/dist/services.global-setup.d.ts +43 -0
  24. package/dist/services.global-setup.js +1 -0
  25. package/dist/services.js +597 -0
  26. package/dist/shared-appium.adapter.js +428 -0
  27. package/dist/shared-artifacts.js +34 -0
  28. package/dist/shared-chapter.js +7 -0
  29. package/dist/shared-container.port.d.ts +31 -0
  30. package/dist/shared-derived-projects.js +73 -0
  31. package/dist/shared-device.port.d.ts +189 -0
  32. package/dist/shared-exchange.js +447 -0
  33. package/dist/shared-in-place.js +172 -0
  34. package/dist/shared-index.services.d.ts +346 -0
  35. package/dist/shared-index.stubs.d.ts +287 -0
  36. package/dist/shared-json-shape.js +17 -0
  37. package/dist/shared-landmarks.js +71 -0
  38. package/dist/shared-ledger.js +68 -0
  39. package/dist/shared-listened-port.js +19 -0
  40. package/dist/shared-literate-plugin.js +184 -0
  41. package/dist/shared-manifest.js +1321 -0
  42. package/dist/shared-pause.js +22 -0
  43. package/dist/shared-peer.js +88 -0
  44. package/dist/shared-playwright.adapter.js +328 -0
  45. package/dist/shared-process-alive.js +13 -0
  46. package/dist/shared-process.adapter.js +74 -0
  47. package/dist/shared-record-key.js +13 -0
  48. package/dist/shared-refusal.js +35 -0
  49. package/dist/shared-resolve.js +24 -0
  50. package/dist/shared-route-coverage.global-setup.js +187 -0
  51. package/dist/shared-scenario-failure.js +198 -0
  52. package/dist/shared-serve.js +407 -0
  53. package/dist/shared-service-images.js +10 -0
  54. package/dist/shared-services.global-setup.js +174 -0
  55. package/dist/shared-session-ledger.js +305 -0
  56. package/dist/shared-setup-entry.js +12 -0
  57. package/dist/shared-source-scan.js +133 -0
  58. package/dist/shared-spec-document.js +303 -0
  59. package/dist/shared-spec-grammar.js +66 -0
  60. package/dist/shared-specifications.js +26 -0
  61. package/dist/shared-specs-modules.js +1033 -0
  62. package/dist/shared-stub-reply.js +167 -0
  63. package/dist/shared-stub-server.js +142 -0
  64. package/dist/shared-temp-root.global-setup.js +73 -0
  65. package/dist/shared-test-baseline.js +26 -0
  66. package/dist/shared-test-section.js +58 -0
  67. package/dist/shared-testcontainers.adapter.js +306 -0
  68. package/dist/shared-tokens.js +55 -0
  69. package/dist/shared-yaml-document.js +119 -0
  70. package/dist/stubs.d.ts +2 -0
  71. package/dist/stubs.js +657 -0
  72. package/dist/temp-root.global-setup.d.ts +7 -0
  73. package/dist/temp-root.global-setup.js +1 -0
  74. package/dist/vitest.d.ts +93 -164
  75. package/dist/vitest.js +382 -550
  76. package/docs/README.md +82 -36
  77. package/docs/concepts/architecture.md +118 -0
  78. package/docs/concepts/conventions.md +333 -0
  79. package/docs/guides/api-specs.md +374 -0
  80. package/docs/guides/cli-specs.md +566 -0
  81. package/docs/guides/component-specs.md +252 -0
  82. package/docs/guides/develop.md +97 -0
  83. package/docs/guides/integration-specs.md +181 -0
  84. package/docs/guides/job-specs.md +180 -0
  85. package/docs/guides/lint-specs.md +174 -0
  86. package/docs/guides/mobile-specs.md +319 -0
  87. package/docs/guides/module-tests.md +149 -0
  88. package/docs/guides/release.md +44 -0
  89. package/docs/guides/spec-documents.md +189 -0
  90. package/docs/guides/website-specs.md +413 -0
  91. package/docs/reference/assertions.md +345 -0
  92. package/docs/reference/glossary.md +20 -0
  93. package/docs/reference/linting.md +483 -0
  94. package/docs/reference/services.md +236 -0
  95. package/docs/reference/stubs.md +500 -0
  96. package/docs/reference/time-and-doubles.md +137 -0
  97. package/docs/reference/tokens.md +258 -0
  98. package/docs/reference/visitor.md +221 -0
  99. package/docs/start/getting-started.md +187 -0
  100. package/docs/start/set-up-a-project.md +228 -0
  101. package/package.json +56 -36
  102. package/schema/spec.schema.json +43 -17
  103. package/dist/appium.adapter.js +0 -489
  104. package/dist/artifacts.js +0 -32
  105. package/dist/fixtures.js +0 -110
  106. package/dist/literate.js +0 -1747
  107. package/dist/match.js +0 -804
  108. package/dist/playwright.adapter.js +0 -438
  109. package/dist/server.js +0 -160
  110. package/dist/stream.js +0 -321
  111. package/dist/substring-warning.js +0 -282
  112. package/docs/01-architecture.md +0 -95
  113. package/docs/02-developing.md +0 -351
  114. package/docs/03-testing.md +0 -495
  115. package/docs/04-operating.md +0 -101
  116. package/docs/05-module-tests.md +0 -152
  117. package/docs/06-integration.md +0 -180
  118. package/docs/07-component.md +0 -246
  119. package/docs/08-website.md +0 -404
  120. package/docs/09-mobile.md +0 -270
  121. package/docs/10-api.md +0 -264
  122. package/docs/11-jobs.md +0 -157
  123. package/docs/12-cli.md +0 -677
  124. package/docs/13-elements.md +0 -248
  125. package/docs/14-assertions.md +0 -314
  126. package/docs/15-tokens.md +0 -253
  127. package/docs/16-contracts.md +0 -438
  128. package/docs/17-services.md +0 -188
  129. package/docs/18-conventions.md +0 -332
  130. package/docs/19-linting.md +0 -512
  131. package/docs/decisions/001-v9-conventions-on-three-channels.md +0 -50
  132. package/docs/decisions/002-the-statique-channel-holds-only-what-upstream-does-not.md +0 -49
  133. package/docs/decisions/003-a-rendered-component-is-a-facet-on-vitest-browser-mode.md +0 -84
  134. package/docs/decisions/004-a-module-against-real-services-or-a-golden-is-the-integration-facet.md +0 -81
  135. package/docs/decisions/005-the-doubles-ladder-is-closed.md +0 -86
  136. package/docs/decisions/006-the-catalogue-reaches-a-file-by-its-role.md +0 -245
  137. package/docs/decisions/007-compose-mode-leaves-the-package.md +0 -53
  138. package/docs/decisions/008-vitest-5-node-24-and-the-native-seams-as-optional-peers.md +0 -75
  139. package/docs/decisions/_template.md +0 -22
  140. package/docs/reference/classes/Matcher.md +0 -120
  141. package/docs/reference/classes/ProcessHandle.md +0 -368
  142. package/docs/reference/functions/defineContract.md +0 -39
  143. package/docs/reference/functions/defineContracts.md +0 -36
  144. package/docs/reference/functions/disabled.md +0 -28
  145. package/docs/reference/functions/enabled.md +0 -20
  146. package/docs/reference/functions/focused.md +0 -26
  147. package/docs/reference/functions/mockOf.md +0 -79
  148. package/docs/reference/functions/postgres.md +0 -31
  149. package/docs/reference/functions/process.md +0 -33
  150. package/docs/reference/functions/redis.md +0 -28
  151. package/docs/reference/functions/required.md +0 -41
  152. package/docs/reference/functions/selected.md +0 -26
  153. package/docs/reference/functions/sqlite.md +0 -33
  154. package/docs/reference/functions/testId.md +0 -20
  155. package/docs/reference/functions/text.md +0 -40
  156. package/docs/reference/functions/valued.md +0 -28
  157. package/docs/reference/functions/waitUntil.md +0 -46
  158. package/docs/reference/functions/within.md +0 -29
  159. package/docs/reference/index.md +0 -196
  160. package/docs/reference/interfaces/BaseResult.md +0 -87
  161. package/docs/reference/interfaces/CallResult.md +0 -160
  162. package/docs/reference/interfaces/CaptureScope.md +0 -82
  163. package/docs/reference/interfaces/CliResult.md +0 -253
  164. package/docs/reference/interfaces/ContainerAccessor.md +0 -166
  165. package/docs/reference/interfaces/DirectoryAccessor.md +0 -33
  166. package/docs/reference/interfaces/FetchResult.md +0 -180
  167. package/docs/reference/interfaces/FilesystemAccessor.md +0 -46
  168. package/docs/reference/interfaces/HttpResult.md +0 -126
  169. package/docs/reference/interfaces/JsonAccessor.md +0 -46
  170. package/docs/reference/interfaces/PageResult.md +0 -356
  171. package/docs/reference/interfaces/RenderResult.md +0 -72
  172. package/docs/reference/interfaces/RenderedText.md +0 -110
  173. package/docs/reference/interfaces/ResponseAccessor.md +0 -47
  174. package/docs/reference/interfaces/ScreenResult.md +0 -128
  175. package/docs/reference/interfaces/TableAccessor.md +0 -26
  176. package/docs/reference/interfaces/TextAccessor.md +0 -96
  177. package/docs/reference/type-aliases/ApiHandle.md +0 -68
  178. package/docs/reference/type-aliases/ApiSpecification.md +0 -213
  179. package/docs/reference/type-aliases/ApiSpecificationOptions.md +0 -68
  180. package/docs/reference/type-aliases/BrowserConsoleMessage.md +0 -30
  181. package/docs/reference/type-aliases/BrowserLinkElement.md +0 -50
  182. package/docs/reference/type-aliases/BrowserMetaElement.md +0 -40
  183. package/docs/reference/type-aliases/BrowserOpenOptions.md +0 -87
  184. package/docs/reference/type-aliases/BrowserPage.md +0 -132
  185. package/docs/reference/type-aliases/BrowserPort.md +0 -51
  186. package/docs/reference/type-aliases/CliEnv.md +0 -11
  187. package/docs/reference/type-aliases/CliHandle.md +0 -80
  188. package/docs/reference/type-aliases/CliInput.md +0 -36
  189. package/docs/reference/type-aliases/CliOutput.md +0 -46
  190. package/docs/reference/type-aliases/CliPort.md +0 -58
  191. package/docs/reference/type-aliases/CliSpecification.md +0 -144
  192. package/docs/reference/type-aliases/CliSpecificationOptions.md +0 -134
  193. package/docs/reference/type-aliases/ComponentChain.md +0 -156
  194. package/docs/reference/type-aliases/ComponentScenario.md +0 -20
  195. package/docs/reference/type-aliases/ComponentUi.md +0 -47
  196. package/docs/reference/type-aliases/ComponentVisitor.md +0 -10
  197. package/docs/reference/type-aliases/ContainerPort.md +0 -113
  198. package/docs/reference/type-aliases/Contract.md +0 -68
  199. package/docs/reference/type-aliases/ContractInput.md +0 -13
  200. package/docs/reference/type-aliases/ContractRequest.md +0 -95
  201. package/docs/reference/type-aliases/ContractResponder.md +0 -23
  202. package/docs/reference/type-aliases/ContractResponse.md +0 -78
  203. package/docs/reference/type-aliases/ContractResponseValue.md +0 -13
  204. package/docs/reference/type-aliases/Contracts.md +0 -48
  205. package/docs/reference/type-aliases/DatabaseKeys.md +0 -16
  206. package/docs/reference/type-aliases/DatabasePort.md +0 -72
  207. package/docs/reference/type-aliases/DeviceOpenOptions.md +0 -46
  208. package/docs/reference/type-aliases/DevicePort.md +0 -51
  209. package/docs/reference/type-aliases/DeviceScreen.md +0 -32
  210. package/docs/reference/type-aliases/DeviceTimeouts.md +0 -39
  211. package/docs/reference/type-aliases/DockerSpecConfig.md +0 -43
  212. package/docs/reference/type-aliases/DomMount.md +0 -21
  213. package/docs/reference/type-aliases/ElementKind.md +0 -27
  214. package/docs/reference/type-aliases/ElementMatch.md +0 -74
  215. package/docs/reference/type-aliases/ElementOptions.md +0 -29
  216. package/docs/reference/type-aliases/ElementRef.md +0 -128
  217. package/docs/reference/type-aliases/ExecOptions.md +0 -37
  218. package/docs/reference/type-aliases/FileAccessor.md +0 -54
  219. package/docs/reference/type-aliases/HonoApp.md +0 -31
  220. package/docs/reference/type-aliases/HttpContractFilter.md +0 -49
  221. package/docs/reference/type-aliases/HttpResponseInit.md +0 -46
  222. package/docs/reference/type-aliases/HttpStreamInit.md +0 -58
  223. package/docs/reference/type-aliases/IntegrationHandle.md +0 -46
  224. package/docs/reference/type-aliases/IntegrationSpecification.md +0 -111
  225. package/docs/reference/type-aliases/IntegrationSpecificationOptions.md +0 -42
  226. package/docs/reference/type-aliases/Intercept.md +0 -19
  227. package/docs/reference/type-aliases/InterceptOptions.md +0 -28
  228. package/docs/reference/type-aliases/InterceptScope.md +0 -11
  229. package/docs/reference/type-aliases/IsolationStrategy.md +0 -75
  230. package/docs/reference/type-aliases/JobHandle.md +0 -34
  231. package/docs/reference/type-aliases/JobsHandle.md +0 -57
  232. package/docs/reference/type-aliases/JobsResult.md +0 -16
  233. package/docs/reference/type-aliases/JobsSpecification.md +0 -94
  234. package/docs/reference/type-aliases/JobsSpecificationOptions.md +0 -56
  235. package/docs/reference/type-aliases/LandmarkKind.md +0 -20
  236. package/docs/reference/type-aliases/LiterateRunFlags.md +0 -27
  237. package/docs/reference/type-aliases/LiterateServeRegistration.md +0 -70
  238. package/docs/reference/type-aliases/MatchFixtureOptions.md +0 -27
  239. package/docs/reference/type-aliases/MatchableRequest.md +0 -61
  240. package/docs/reference/type-aliases/MatcherKind.md +0 -34
  241. package/docs/reference/type-aliases/MobileBackendOptions.md +0 -28
  242. package/docs/reference/type-aliases/MobileElementKind.md +0 -14
  243. package/docs/reference/type-aliases/MobileElementMatch.md +0 -60
  244. package/docs/reference/type-aliases/MobileElementRef.md +0 -20
  245. package/docs/reference/type-aliases/MobileHandle.md +0 -65
  246. package/docs/reference/type-aliases/MobileScenario.md +0 -20
  247. package/docs/reference/type-aliases/MobileSpecification.md +0 -51
  248. package/docs/reference/type-aliases/MobileSpecificationOptions.md +0 -126
  249. package/docs/reference/type-aliases/MobileVisitor.md +0 -81
  250. package/docs/reference/type-aliases/MockOfOptions.md +0 -26
  251. package/docs/reference/type-aliases/MockPort.md +0 -10
  252. package/docs/reference/type-aliases/PinnedClock.md +0 -10
  253. package/docs/reference/type-aliases/PostgresOptions.md +0 -32
  254. package/docs/reference/type-aliases/ProcessOptions.md +0 -112
  255. package/docs/reference/type-aliases/RedisOptions.md +0 -20
  256. package/docs/reference/type-aliases/RenderSubject.md +0 -12
  257. package/docs/reference/type-aliases/ScreenNode.md +0 -67
  258. package/docs/reference/type-aliases/ServerPort.md +0 -36
  259. package/docs/reference/type-aliases/ServerResponse.md +0 -46
  260. package/docs/reference/type-aliases/ServerSpec.md +0 -21
  261. package/docs/reference/type-aliases/ServiceHandle.md +0 -216
  262. package/docs/reference/type-aliases/ServiceRecord.md +0 -12
  263. package/docs/reference/type-aliases/SpecDocument.md +0 -84
  264. package/docs/reference/type-aliases/SpecEnvToken.md +0 -21
  265. package/docs/reference/type-aliases/SpecFileAssertion.md +0 -27
  266. package/docs/reference/type-aliases/SpecFixture.md +0 -30
  267. package/docs/reference/type-aliases/SpecKind.md +0 -8
  268. package/docs/reference/type-aliases/SpecRun.md +0 -108
  269. package/docs/reference/type-aliases/SpecServeEntry.md +0 -40
  270. package/docs/reference/type-aliases/SpecStream.md +0 -32
  271. package/docs/reference/type-aliases/SpecificationConfig.md +0 -290
  272. package/docs/reference/type-aliases/SqliteOptions.md +0 -36
  273. package/docs/reference/type-aliases/SseEvent.md +0 -58
  274. package/docs/reference/type-aliases/StreamBody.md +0 -61
  275. package/docs/reference/type-aliases/TextFilter.md +0 -22
  276. package/docs/reference/type-aliases/VisitScenario.md +0 -20
  277. package/docs/reference/type-aliases/Visitor.md +0 -215
  278. package/docs/reference/type-aliases/WaitUntilOptions.md +0 -46
  279. package/docs/reference/type-aliases/WebsiteBackendOptions.md +0 -37
  280. package/docs/reference/type-aliases/WebsiteHandle.md +0 -51
  281. package/docs/reference/type-aliases/WebsiteSpecification.md +0 -118
  282. package/docs/reference/type-aliases/WebsiteSpecificationOptions.md +0 -40
  283. package/docs/reference/variables/anthropic.md +0 -19
  284. package/docs/reference/variables/banner.md +0 -10
  285. package/docs/reference/variables/button.md +0 -10
  286. package/docs/reference/variables/clock.md +0 -16
  287. package/docs/reference/variables/complementary.md +0 -10
  288. package/docs/reference/variables/component.md +0 -10
  289. package/docs/reference/variables/content.md +0 -10
  290. package/docs/reference/variables/contentinfo.md +0 -10
  291. package/docs/reference/variables/dialog.md +0 -10
  292. package/docs/reference/variables/field.md +0 -10
  293. package/docs/reference/variables/form.md +0 -10
  294. package/docs/reference/variables/heading.md +0 -10
  295. package/docs/reference/variables/http.md +0 -40
  296. package/docs/reference/variables/intercept.md +0 -11
  297. package/docs/reference/variables/link.md +0 -10
  298. package/docs/reference/variables/listitem.md +0 -10
  299. package/docs/reference/variables/main.md +0 -10
  300. package/docs/reference/variables/match.md +0 -8
  301. package/docs/reference/variables/navigation.md +0 -10
  302. package/docs/reference/variables/openai.md +0 -21
  303. package/docs/reference/variables/option.md +0 -17
  304. package/docs/reference/variables/region.md +0 -10
  305. package/docs/reference/variables/row.md +0 -10
  306. package/docs/reference/variables/search.md +0 -10
  307. package/docs/reference/variables/specification.md +0 -8
  308. package/docs/reference/variables/status.md +0 -10
  309. package/docs/reference/variables/table.md +0 -10
@@ -1,495 +0,0 @@
1
- # 03 — Testing
2
-
3
- What proves a change here: this package specifies itself with itself. The suites under `specs/` are written with `@jterrazz/test` against fixture apps, the module tests sit beside the modules they cover, and a family of meta-tests runs the framework on its own output. This chapter says which suite answers for what, and what a change owes each of them.
4
-
5
- **The suffix says the kind.** `.test.ts` is the UNIT's word and sits beside the module it covers; `.test.tsx` is the same law for a rendered unit; `.spec.ts` is the ASSEMBLED product's word and lives under `specs/<facet>/`; a literate document stays `<case>.spec.yaml`. C12 holds both directions and its `--fix` renames with `git mv`, so a reader can tell what a file proves without opening it.
6
-
7
- | Ground | Where | Proves |
8
- | --------------- | ----------------------------------------- | ---------------------------------------------------------------------- |
9
- | Module tests | `src/**/<file>.test.ts` | One module's behaviour, beside it (rule I2) |
10
- | Component tests | `specs/component-app/<file>.test.tsx` | A rendered unit, beside it, in a real Chromium ([07](07-component.md)) |
11
- | Product specs | `specs/<facet>/<domain>/<aspect>.spec.ts` | The framework's own facets, through the public surface |
12
- | Spec documents | `specs/cli/literate/*.spec.yaml` | The document format, collected as test files by `literate()` |
13
- | Meta-tests | `src/lint/*.test.ts` | The framework applied to itself and to its own projections |
14
-
15
- ## The seven projects
16
-
17
- `test.projects` in `vitest.config.ts` declares seven, and which one you can run is decided by what is installed and running on the machine.
18
-
19
- | Project | Collects | Needs |
20
- | ------------- | ----------------------------------------------------------------------------------------------- | -------------------------------------------------------------------- |
21
- | `unit` | `src/**/*.test.ts` + `specs/lint/**/*.test.ts`, built by `unit()` | Nothing — the lint specs need `npm run build` first |
22
- | `cli` | `specs/cli/**`, built by `cli()` — its documents included, through `literate()` | Nothing — the Docker specs self-skip |
23
- | `api` | `specs/api/**/*.spec.ts`, built by `api()` (node mode, in-process Hono) | Docker |
24
- | `jobs` | `specs/jobs/**`, built by `jobs()` | Docker |
25
- | `integration` | `specs/integration/**`, built by `integration({ serial: true })` — the container seams included | Docker |
26
- | `website` | `specs/website/**`, built by `website()` | playwright + `npx playwright install chromium`; no Docker |
27
- | `component` | `specs/component-app/**/*.test.tsx`, built by `component()` | the same chromium; no Docker. Runs in its own group, after `website` |
28
-
29
- Every project the package runs comes from a helper, so `--project api` means the same tree here as in any consumer — `unit()` included, with the globs it takes naming the one specs tree that is no facet's. A member without `"type": "module"` writes its config as `vitest.config.mts`: the helpers ship ESM only, a CJS config would have to `require()` them, and `roleOf` already reads `.mts` as the config role — so the rules that judge a config still find it.
30
-
31
- Why a file is reached by its ROLE and named by its suffix, and what was weighed against it: [ADR-006](decisions/006-the-catalogue-reaches-a-file-by-its-role.md).
32
-
33
- **`specs/lint/` is a repository suite, and it keeps `.test.ts`.** A repository suite covers a TREE rather than one assembled product: these files run the plugin and the checker over fixture PROJECTS, one per rule, and what they specify is a convention over a repository's shape. Its first level is not one of the six facets, so C12's rename clause never reaches it, C1's declared depth is what judges it, and `unit()` is what collects it — the shape the constitution calls [the third door](18-conventions.md#the-third-door-a-repository-suite).
34
-
35
- **The `unit` project's budget is the whole suite's floor.** It collects the module tests and the lint suite and runs with no service, no browser and no container, so it is the one project a developer runs on every save: the same tree takes between nine and eleven seconds on consecutive runs of a developer machine, and **the budget it may not pass is twelve**. The budget is set above the range and not at the best measurement, because a budget set at the best measurement is a budget that fails on noise. The meta-test that proves every rule's reach (`k4-reach-per-path`) runs inside it on ONE oxlint run for the same reason.
36
-
37
- Every FACET folder under `specs/` belongs to a constructor, and the one folder that is not a facet says so in its name: `specs/component-app/` is the rendered subject this package ships, and its component tests sit beside its components because that is what a rendered unit's test does (A2, I2). A probe of a container seam — the postgres and redis handles — is a module against a real service, so it is an integration spec: `specs/integration/<seam>/`, on `specification.integration({ services })` and its one terminal action. What the seam answers BEFORE it reaches a container, and the one adapter the public entry does not publish (`TestcontainersAdapter`), are module tests beside their modules under `src/seams/` — a spec reaches the framework through its public entry, and a probe that cannot is telling you where it belongs (rule F3).
38
-
39
- ```bash
40
- npm test # every project — Docker and chromium both required
41
- npx vitest --run --project unit # the loop: no infrastructure, after npm run build
42
- npx vitest --run --project website # after npx playwright install chromium
43
- npx vitest --run --project component # the same chromium, a mounted unit at a time
44
- ```
45
-
46
- There is no mobile tree under `specs/`, and that is a hole this chapter states rather than hides: an iOS simulator is not something CI provisions, so the mobile facet is proven by module tests under `src/facets/mobile/` — the simulator resolution, the page-source projection, the ambiguity messages — and by nothing end-to-end.
47
-
48
- A second hole is the RENDERER's, and it is not this repository's to close: a React Native screen never reaches the real Chromium the component facet mounts in, so jest stays on that surface until the `react-native-web` spike says otherwise — [07 — Component specs](07-component.md) lists it with the other three shapes the facet does not take.
49
-
50
- ## How a spec tree is laid out
51
-
52
- The layout is the one the conventions enforce on every consumer (rule C1), and this repository is its first consumer. A facet carries its runners at its ROOT and its tests one level down, in domain folders:
53
-
54
- ```
55
- specs/
56
- ├── api/
57
- │ ├── api.specification.ts # runners at the facet root
58
- │ ├── intercepts.specification.ts
59
- │ └── requests/ # a domain
60
- │ ├── requests.spec.ts
61
- │ ├── _requests/ # ground: complete requests, *.http
62
- │ └── _expected/ # ground: every expected fixture, flat
63
- └── _fixtures/ # the SHARED pool, reached as $FIXTURES/…
64
- ```
65
-
66
- **A domain shared by two facets carries the same name in both.** Six
67
- capabilities belong to no facet in particular — a runner's `lifecycle`, the
68
- declared network (`intercepts`), the ground a chain loads (`seeding`), what a
69
- result answers (`assertions`), the `{{token}}` engine (`tokens`) and the pinned
70
- calendar (`clock`) — and a facet that gives one of them a FOLDER calls it by
71
- that name, so `specs/api/clock/` and `specs/website/clock/` are the same
72
- question asked of two constructors. Everything else is one facet's own, and a
73
- name that is one facet's own is never another's: two names for one capability
74
- is the drift the vocabulary exists to stop.
75
-
76
- It says nothing about where a METHOD may appear. `.seed()` is used inside jobs'
77
- `triggering/` scenarios and integration's `call/` ones, because seeding is the
78
- Given there and not the subject; a capability earns a folder when it is what
79
- the specs in it are ABOUT.
80
-
81
- The vocabulary is declared in `src/lint/domains.ts` and `domains.test.ts` holds
82
- the tree equal to it in both directions — a folder nothing declares fails, and
83
- a declared name nothing carries fails too.
84
-
85
- <!-- GENERATED:domains — do not edit by hand; run `npm run docs`. Source: src/lint/domains.ts -->
86
-
87
- | Facet | Shared domains it carries | Its own |
88
- | ------------- | ---------------------------------------------------------------- | ----------------------------------------------------- |
89
- | `api` | `assertions/`, `clock/`, `intercepts/`, `lifecycle/`, `seeding/` | `initiation-errors/`, `requests/`, `responses/` |
90
- | `cli` | `assertions/`, `seeding/`, `tokens/` | `directory/`, `docker/`, `env/`, `exec/`, `literate/` |
91
- | `integration` | `clock/`, `intercepts/` | `call/`, `golden/`, `postgres/`, `redis/` |
92
- | `jobs` | — | `triggering/` |
93
- | `website` | `assertions/`, `clock/` | `console/`, `fetch/`, `services/`, `visit/` |
94
-
95
- <!-- /GENERATED:domains -->
96
-
97
- A test at a facet root is forbidden and a `*.specification.ts` inside a domain is forbidden; a leading underscore means ground, never a domain. Which of the two legal shapes a given tree takes — its own domain, or sibling tests in a named group folder — is decided by the assets, and that judgement is the process channel's ([18 — Conventions](18-conventions.md)).
98
-
99
- The fixture apps the specs drive live in the pool: `app` and `website-app` for the served facets, `cli-app`, `docker-cli`, `checker-cli` and `lint-cli` for the command facets, the `broken-*` trees for the infrastructure failure paths, and `lint-violations/` — a violation/compliant twin per lint rule.
100
-
101
- **The component tree is the one exception, and the exception is the rule it dogfoods.** A rendered unit's test sits BESIDE the unit, so `specs/component-app/` holds the fixture app and its specs as neighbours — a table with a contract, a form carrying every verb, a modal hook with its Host in the test, a routed link, a vanilla-DOM list, a clock stamp, a noisy panel, a responsive note, a control that refuses input and a pair of links whose names overlap. It is not under `_fixtures/`: ground is what a spec stands on from a distance, and here the spec stands next to it. The exception is HELD by rules, not granted by omission: I2 requires every one of those `.test.tsx` files to have the `.tsx` (or the `.ts` of a hook) of the same basename beside it, and C1's row says a `.test.tsx` is out of its reach because a rendered unit never lives in a facet/domain tree.
102
-
103
- ## The layered reading
104
-
105
- The package proves itself in five layers, and they are meant to be read from the inside out: a module against its own exports, a rule against its own fixtures, a facet against its own tree, the built binary against a fixture project, and the CORPUS against what a meta-test says it must keep true. Each layer judges something the one below it cannot — and the meta-tests are counted apart rather than folded into the module tests, because what they judge is the one thing a test beside a module cannot see.
106
-
107
- <!-- GENERATED:layers — do not edit by hand; run `npm run docs`. Source: src/lint/matrix.ts -->
108
-
109
- | Layer | Where | What it judges | Files |
110
- | ----------------------- | ------------------------------------------------------- | ------------------------------------------------------------------------ | ----- |
111
- | Module tests | `src/**/*.test.ts` beside the module | one module, through its own exports, with nothing started | 54 |
112
- | Rule tests | `src/lint/rules/<facet>/<rule>.test.ts` beside the rule | one rule: what it flags, what it leaves alone, and the message it prints | 56 |
113
- | The package’s own specs | `specs/<facet>/` | the framework's own facets, each met through its constructor | 67 |
114
- | The lint suite | `specs/lint/**` | the built binary end to end, over fixture projects | 88 |
115
- | Meta-tests | `src/lint/*.test.ts` | the corpus itself: the catalogue, the matrix, the cards, the floor | 18 |
116
-
117
- <!-- /GENERATED:layers -->
118
-
119
- The counts are generated: a layer that stops growing while the surface does is visible here before it is visible in a bug.
120
-
121
- ## The lint suite is end-to-end
122
-
123
- `specs/lint/**` runs the REAL oxlint binary and the real checker over the fixture projects and goldens their output, grouped by convention family (`runners/`, `chains/`, `files/`, `assertions/`, `imports/`, `architecture/`, `hygiene/`, `website/`, `checker/`, `meta/`). Because it loads `dist/oxlint.js`, `npm run build` must precede it — the same ordering `npm run lint` depends on.
124
-
125
- Its goldens are full snapshots, not greps: `specs/lint/checker/_expected/*.txt` holds the exact lines the checker prints, including the chapter each message points a consumer at. A message that changes its wording moves its golden with it, in the same commit.
126
-
127
- ## Coverage is a ratchet, not a threshold
128
-
129
- `npx vitest --run --coverage` turns on v8 — the runtime's own counter, so there
130
- is no instrumentation pass and the numbers do not move when the bundler does —
131
- and writes three reports where the preset sends them ([02 — Developing §
132
- Artefacts live under `.artifacts/`](02-developing.md#artefacts-live-under-artifacts)):
133
- `text` for the person who ran it, `html` for the one chasing a line, and
134
- `json-summary`, which is the one a machine reads.
135
-
136
- `npm run coverage` records where the suite stands in `coverage.baseline.json`;
137
- `npm run coverage:check` refuses a run that fell below it. Neither runs the
138
- suite: both read the report of the LAST `--coverage` run, which is why
139
- `make test` runs the suite WITH coverage (`npm test -- --coverage`) before the
140
- gate — a plain `npm test` would leave it judging an older run, or, on a fresh
141
- clone where `.artifacts/` does not exist yet, nothing at all. That target is
142
- the one CI calls ([What CI runs](#what-ci-runs)), so the floor is judged on
143
- every push. It only ever rises: lowering it is an edit to a committed file,
144
- with a commit body saying why.
145
-
146
- ### One floor, over the whole suite
147
-
148
- The baseline is keyed by scope and the CLI
149
- takes `--scope`, so a project can keep its own; this repository records `all`
150
- and nothing else, because the projects overlap — the browser projects and the
151
- node ones both execute `model/`, and four floors summing to more than the tree
152
- would ratchet on which project happened to run. A scope missing any of the four
153
- metrics is refused rather than read as a zero, since a floor of zero forbids
154
- nothing.
155
-
156
- ### Why a ratchet and not a threshold
157
-
158
- A fixed threshold would be a number somebody picked. Set it where the project
159
- stands and it forbids nothing; set it where the project should be and every run
160
- is red until it gets there, which is how a gate becomes something people pass
161
- with `--no-verify`. A ratchet asks the only question a gate can answer
162
- honestly: is this change worse than the last one?
163
-
164
- ### What the report excludes
165
-
166
- What the report EXCLUDES is the shape of the answer, not a way to raise the
167
- number: `node_modules/` (Browser Mode instruments the page's bundle, and
168
- react-dom alone is 19 000 statements), `specs/` and `_fixtures/` (a spec is a
169
- test and the app it drives is ground), the type-only and config files.
170
-
171
- ## Modules with no sibling test
172
-
173
- I2 says a module test lives beside its module. It never said every module has
174
- one, and it cannot: a barrel executes nothing, a `*.port.ts` declares a shape,
175
- and a facet's constructor is proven by the tree that constructs it. What was
176
- missing is the LIST of the ones that do not, and why — a module with real
177
- behaviour, no sibling test and nobody able to say what covers it is the silence
178
- this section closes.
179
-
180
- `src/lint/siblings.ts` declares the kinds, each naming the PATHS it claims —
181
- never a prefix, because a kind claiming `src/seams/` would claim every module
182
- written there next year and K7 would be a poster. **K7** fails on a module that
183
- matches none of them, which is what makes a new, unclaimed module fail the
184
- suite on the day it lands.
185
-
186
- <!-- GENERATED:siblings — do not edit by hand; run `npm run docs`. Source: src/lint/siblings.ts -->
187
-
188
- Today **80 modules** have no sibling test, and every one of them is claimed:
189
-
190
- | Kind | Proven instead by |
191
- | --------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
192
- | composition root or barrel (4) | it wires and re-exports; what it publishes is held by `package-exports.test.ts` and by every spec that imports the entry |
193
- | type-only declaration (11) | it declares a shape and executes nothing; the compiler is its test, and `type-channel.test-d.ts` holds what the compiler must refuse |
194
- | constant data (3) | it is a table, not a behaviour; every reader of it asserts on it (`preset.test.ts` for the artefact paths, `plugin.test.ts` for the manifest) |
195
- | the facet file set, as a re-export (12) | the six node facets share one builder, so these name it rather than implement it — `builder.test.ts` and `facet-matrix.test.ts` hold the thing they name |
196
- | a facet's vitest project (7) | `projects.test.ts` builds every one of them and asserts the project it produces |
197
- | a mobile module that needs a device (2) | the mobile facet has no tree under `specs/` and cannot have one here (M1); what a device is NOT needed for — resolving the simulator, projecting the page source, wording the ambiguity — has its own sibling test, and these two are the rest |
198
- | a facet module (8) | the facet is proven end to end by its own tree under `specs/<facet>/`, which is what the constructor exists to make possible |
199
- | a result accessor (7) | an accessor is what a terminal action hands back: it is exercised by every spec that asserts on a result, and `result.test.ts` holds the base |
200
- | a seam adapter (12) | a seam is proven through the facet that drives it — a probe that could only reach it directly is an integration spec under `specs/integration/<seam>/` (chapter 03) |
201
- | a bundled CLI entry (3) | it is argument parsing over a module that has its own tests, and `specs/lint/` runs the built binary end to end |
202
- | a lint-layer module (4) | the lint layer is proven by the rule tests beside each rule and by `specs/lint/`, which runs the real oxlint binary and the real checker over fixture projects |
203
- | a chain or runner internal (7) | it is reached through the chain, so its proof is the facet specs that drive the chain — a direct test would assert on a seam nothing else speaks to |
204
-
205
- <!-- /GENERATED:siblings -->
206
-
207
- A new module that matches no kind fails the meta-test, which is the moment to
208
- write the test — not six months later, when a coverage number moved.
209
-
210
- ## The capability matrix
211
-
212
- The catalogue in [19 — Linting](19-linting.md) answers "which conventions are
213
- mechanized". This answers the other half: for each thing the framework can DO,
214
- which facets declare it, and how many of the package's own test files exercise
215
- it there.
216
-
217
- It is generated by `npm run docs` from the facet declaration
218
- (`src/lint/facet-matrix.ts`), the `{{token}}` list the runner and the checker
219
- already share, and a scan of the trees each column owns. The same table is the
220
- skill's `references/matrix.md`.
221
-
222
- <!-- GENERATED:matrix — do not edit by hand; run `npm run docs`. Source: src/lint/matrix.ts -->
223
-
224
- What the framework can do, and how many of this package’s own test FILES carry the literal that exercises it, per facet (comments stripped, so a sentence about a capability never counts as a test of it). A blank (`—`) is a capability the facet does not declare; a `0` is a declared capability nothing here exercises, and every one of them is named under **Exemptions** with the reason it is accepted. `matrix.test.ts` fails on a `0` that is not.
225
-
226
- ### Constructor option
227
-
228
- | Capability | api | jobs | cli | integration | website | mobile | component | module |
229
- | ----------- | --- | ---- | --- | ----------- | ------- | ------ | --------- | ------ |
230
- | `services` | 2 | 1 | 1 | 2 | 1 | 0 | — | — |
231
- | `root` | 2 | 1 | 0 | 2 | 0 | 0 | 0 | — |
232
- | `server` | 4 | — | — | — | 3 | — | — | — |
233
- | `jobs` | — | 2 | — | — | — | — | — | — |
234
- | `url` | — | — | — | — | 0 | — | — | — |
235
- | `backend` | — | — | — | — | 0 | 0 | — | — |
236
- | `external` | — | — | — | — | 0 | — | — | — |
237
- | `device` | — | — | — | — | — | 0 | — | — |
238
- | `app` | — | — | — | — | — | 0 | — | — |
239
- | `timeouts` | — | — | — | — | — | 0 | — | — |
240
- | `defaults` | — | — | 1 | — | — | — | — | — |
241
- | `env` | — | — | 4 | — | — | — | — | — |
242
- | `docker` | — | — | 1 | — | — | — | — | — |
243
- | `serve` | — | — | 4 | — | — | — | — | — |
244
- | `transform` | — | — | 2 | — | 1 | — | — | — |
245
- | `wrap` | — | — | — | — | — | — | 1 | — |
246
- | `vite` | — | — | — | — | — | — | 1 | — |
247
- | `clock` | — | — | — | — | — | — | 0 | — |
248
- | `locale` | — | — | — | — | — | — | 0 | — |
249
- | `timezone` | — | — | — | — | — | — | 0 | — |
250
- | `viewport` | — | — | — | — | — | — | 0 | — |
251
-
252
- ### Setup
253
-
254
- | Capability | api | jobs | cli | integration | website | mobile | component | module |
255
- | -------------- | --- | ---- | --- | ----------- | ------- | ------ | --------- | ------ |
256
- | `.clock()` | 1 | 0 | — | 1 | 1 | — | 1 | — |
257
- | `.intercept()` | 3 | 1 | — | 1 | 0 | 0 | 4 | — |
258
- | `.seed()` | 4 | 1 | 1 | 2 | — | — | — | — |
259
- | `.headers()` | 1 | — | — | — | 1 | — | — | — |
260
- | `.fixture()` | — | — | 6 | — | — | — | — | — |
261
- | `.env()` | — | — | 1 | — | — | — | — | — |
262
- | `.wrap()` | — | — | — | — | — | — | 2 | — |
263
- | `.viewport()` | — | — | — | — | — | — | 1 | — |
264
-
265
- ### Terminal action
266
-
267
- | Capability | api | jobs | cli | integration | website | mobile | component | module |
268
- | ------------ | --- | ---- | --- | ----------- | ------- | ------ | --------- | ------ |
269
- | `.get()` | 9 | — | — | — | — | — | — | — |
270
- | `.post()` | 1 | — | — | — | — | — | — | — |
271
- | `.put()` | 1 | — | — | — | — | — | — | — |
272
- | `.delete()` | 1 | — | — | — | — | — | — | — |
273
- | `.request()` | 1 | — | — | — | — | — | — | — |
274
- | `.trigger()` | — | 1 | — | — | — | — | — | — |
275
- | `.call()` | — | — | — | 6 | — | — | — | — |
276
- | `.exec()` | — | — | 7 | — | — | — | — | — |
277
- | `.run()` | — | — | 2 | — | — | — | — | — |
278
- | `.visit()` | — | — | — | — | 11 | — | — | — |
279
- | `.fetch()` | — | — | — | — | 2 | — | — | — |
280
- | `.open()` | — | — | — | — | — | 0 | — | — |
281
- | `.render()` | — | — | — | — | — | — | 15 | — |
282
-
283
- ### Verb
284
-
285
- | Capability | api | jobs | cli | integration | website | mobile | component | module |
286
- | ---------- | --- | ---- | --- | ----------- | ------- | ------ | --------- | ------ |
287
- | `see` | — | — | — | — | 6 | 0 | 15 | — |
288
- | `fill` | — | — | — | — | 3 | 0 | 2 | — |
289
- | `click` | — | — | — | — | 4 | — | 5 | — |
290
- | `gone` | — | — | — | — | 2 | — | 4 | — |
291
- | `press` | — | — | — | — | 0 | — | 2 | — |
292
- | `hover` | — | — | — | — | 0 | — | 1 | — |
293
- | `check` | — | — | — | — | 0 | — | 2 | — |
294
- | `select` | — | — | — | — | 1 | — | 2 | — |
295
- | `goto` | — | — | — | — | 0 | — | — | — |
296
- | `tap` | — | — | — | — | — | 0 | — | — |
297
- | `rerender` | — | — | — | — | — | — | 1 | — |
298
- | `unmount` | — | — | — | — | — | — | 1 | — |
299
-
300
- ### Descriptor
301
-
302
- | Capability | api | jobs | cli | integration | website | mobile | component | module |
303
- | --------------- | --- | ---- | --- | ----------- | ------- | ------ | --------- | ------ |
304
- | `button` | — | — | — | — | 4 | 0 | 5 | — |
305
- | `field` | — | — | — | — | 4 | 0 | 3 | — |
306
- | `content` | — | — | — | — | 3 | 0 | 10 | — |
307
- | `testId` | — | — | — | — | 0 | 0 | 1 | — |
308
- | `heading` | — | — | — | — | 1 | — | 1 | — |
309
- | `link` | — | — | — | — | 3 | — | 2 | — |
310
- | `dialog` | — | — | — | — | 0 | — | 1 | — |
311
- | `status` | — | — | — | — | 0 | — | 3 | — |
312
- | `table` | — | — | — | — | 0 | — | 1 | — |
313
- | `row` | — | — | — | — | 0 | — | 1 | — |
314
- | `listitem` | — | — | — | — | 0 | — | 1 | — |
315
- | `option` | — | — | — | — | 1 | — | 1 | — |
316
- | `banner` | — | — | — | — | 0 | — | 0 | — |
317
- | `complementary` | — | — | — | — | 0 | — | 0 | — |
318
- | `contentinfo` | — | — | — | — | 1 | — | 0 | — |
319
- | `form` | — | — | — | — | 0 | — | 0 | — |
320
- | `main` | — | — | — | — | 1 | — | 1 | — |
321
- | `navigation` | — | — | — | — | 1 | — | 2 | — |
322
- | `region` | — | — | — | — | 1 | — | 1 | — |
323
- | `search` | — | — | — | — | 0 | — | 0 | — |
324
- | `within` | — | — | — | — | 2 | — | 5 | — |
325
- | `focused` | — | — | — | — | 1 | — | 1 | — |
326
- | `selected` | — | — | — | — | 1 | — | 1 | — |
327
- | `valued` | — | — | — | — | 1 | — | 2 | — |
328
- | `disabled` | — | — | — | — | 1 | — | 1 | — |
329
- | `enabled` | — | — | — | — | 1 | — | 1 | — |
330
-
331
- ### Result accessor
332
-
333
- | Capability | api | jobs | cli | integration | website | mobile | component | module |
334
- | --------------- | --- | ---- | --- | ----------- | ------- | ------ | --------- | ------ |
335
- | `.response` | 7 | — | — | — | — | — | — | — |
336
- | `.status` | 3 | — | — | — | 2 | — | — | — |
337
- | `.table()` | 4 | 1 | 1 | 1 | — | — | — | — |
338
- | `.file()` | 0 | — | 5 | 0 | — | — | — | — |
339
- | `.directory()` | 0 | — | 2 | 0 | — | — | — | — |
340
- | `.stdout` | — | — | 5 | — | — | — | — | — |
341
- | `.stderr` | — | — | 2 | — | — | — | — | — |
342
- | `.exitCode` | — | — | 8 | — | — | — | — | — |
343
- | `.filesystem` | — | — | 1 | — | — | — | — | — |
344
- | `.container()` | — | — | 1 | — | — | — | — | — |
345
- | `.containerIds` | — | — | 1 | — | — | — | — | — |
346
- | `.value` | — | — | — | 6 | — | — | — | — |
347
- | `.error` | — | — | — | 5 | — | — | — | — |
348
- | `.tree` | — | — | — | — | 2 | — | 4 | — |
349
- | `.content` | — | — | — | — | 4 | 0 | 12 | — |
350
- | `.console` | — | — | — | — | 1 | — | 3 | — |
351
- | `.errors` | — | — | — | — | 4 | — | 5 | — |
352
- | `.html` | — | — | — | — | 0 | — | 5 | — |
353
- | `.title` | — | — | — | — | 0 | — | — | — |
354
- | `.url` | — | — | — | — | 2 | — | — | — |
355
- | `.head` | — | — | — | — | 3 | — | — | — |
356
- | `.jsonLd` | — | — | — | — | 1 | — | — | — |
357
- | `.canonical` | — | — | — | — | 1 | — | — | — |
358
- | `.alternates` | — | — | — | — | 1 | — | — | — |
359
- | `.links` | — | — | — | — | 0 | — | — | — |
360
- | `.meta()` | — | — | — | — | 2 | — | — | — |
361
- | `.body` | — | — | — | — | 1 | — | — | — |
362
- | `.json` | — | — | 2 | — | 1 | — | — | — |
363
- | `.headers` | — | — | — | — | 1 | — | — | — |
364
- | `.location` | — | — | — | — | 1 | — | — | — |
365
- | `.screen` | — | — | — | — | — | 0 | — | — |
366
-
367
- ### Golden
368
-
369
- | Capability | api | jobs | cli | integration | website | mobile | component | module |
370
- | ------------------- | --- | ---- | --- | ----------- | ------- | ------ | --------- | ------ |
371
- | `toMatch('<name>')` | 3 | 0 | 6 | 5 | 5 | — | 1 | 6 |
372
- | `toMatchRows()` | 4 | 1 | 1 | 1 | — | — | 0 | 4 |
373
- | `.http exchange` | 3 | — | — | — | — | — | — | 7 |
374
- | `.aria.yaml tree` | — | — | — | — | 1 | — | 1 | — |
375
- | `.json body` | 0 | — | 2 | 5 | 2 | — | — | 10 |
376
- | `.txt stream` | 1 | — | 8 | 2 | 2 | — | — | 12 |
377
- | `{ frozen }` | 2 | — | 5 | — | — | — | 1 | 3 |
378
-
379
- ### Time & doubles
380
-
381
- | Capability | api | jobs | cli | integration | website | mobile | component | module |
382
- | ------------- | --- | ---- | --- | ----------- | ------- | ------ | --------- | ------ |
383
- | `clock()` | — | — | — | — | — | — | — | 4 |
384
- | `intercept()` | — | — | — | — | — | — | — | 1 |
385
- | `mockOf()` | — | — | — | — | — | — | — | 6 |
386
- | `match.*` | — | — | — | — | — | — | — | 22 |
387
-
388
- ### Token
389
-
390
- | Capability | api | jobs | cli | integration | website | mobile | component | module |
391
- | -------------- | --- | ---- | --- | ----------- | ------- | ------ | --------- | ------ |
392
- | `{{any}}` | 0 | — | 2 | 0 | 0 | — | — | 1 |
393
- | `{{base64}}` | 0 | — | 0 | 0 | 0 | — | — | 2 |
394
- | `{{date}}` | 0 | — | 0 | 0 | 0 | — | — | 1 |
395
- | `{{duration}}` | 0 | — | 1 | 0 | 0 | — | — | 2 |
396
- | `{{email}}` | 0 | — | 0 | 0 | 0 | — | — | 1 |
397
- | `{{float}}` | 0 | — | 0 | 0 | 0 | — | — | 2 |
398
- | `{{hex}}` | 0 | — | 0 | 0 | 0 | — | — | 2 |
399
- | `{{int}}` | 0 | — | 0 | 0 | 0 | — | — | 1 |
400
- | `{{ip}}` | 0 | — | 0 | 0 | 0 | — | — | 1 |
401
- | `{{iso8601}}` | 0 | — | 0 | 0 | 0 | — | — | 2 |
402
- | `{{number}}` | 0 | — | 0 | 0 | 0 | — | — | 1 |
403
- | `{{path}}` | 0 | — | 0 | 0 | 0 | — | — | 1 |
404
- | `{{port}}` | 0 | — | 0 | 0 | 0 | — | — | 2 |
405
- | `{{semver}}` | 0 | — | 2 | 0 | 0 | — | — | 1 |
406
- | `{{sha}}` | 0 | — | 0 | 0 | 0 | — | — | 1 |
407
- | `{{string}}` | 0 | — | 1 | 0 | 0 | — | — | 1 |
408
- | `{{time}}` | 0 | — | 0 | 0 | 0 | — | — | 1 |
409
- | `{{ulid}}` | 0 | — | 0 | 0 | 0 | — | — | 1 |
410
- | `{{url}}` | 0 | — | 2 | 0 | 0 | — | — | 1 |
411
- | `{{uuid}}` | 0 | — | 0 | 0 | 0 | — | — | 3 |
412
- | `{{workdir}}` | 0 | — | 5 | 0 | 0 | — | — | 1 |
413
-
414
- ### Exemptions
415
-
416
- - no mobile tree: an iOS simulator is not something CI provisions (M1) — `services`·mobile, `root`·mobile, `backend`·mobile, `device`·mobile, `app`·mobile, `timeouts`·mobile, `.intercept()`·mobile, `.open()`·mobile, `see`·mobile, `fill`·mobile, `tap`·mobile, `button`·mobile, `field`·mobile, `content`·mobile, `testId`·mobile, `.content`·mobile, `.screen`·mobile
417
- - the surface ships and this package does not specify it here yet — `root`·cli, `root`·website, `root`·component, `backend`·website, `external`·website, `clock`·component, `locale`·component, `timezone`·component, `viewport`·component, `.clock()`·jobs, `.intercept()`·website, `press`·website, `hover`·website, `check`·website, `goto`·website, `testId`·website, `dialog`·website, `status`·website, `table`·website, `row`·website, `listitem`·website, `banner`·website, `banner`·component, `complementary`·website, `complementary`·component, `contentinfo`·component, `form`·website, `form`·component, `search`·website, `search`·component, `.file()`·api, `.file()`·integration, `.directory()`·api, `.directory()`·integration, `.html`·website, `.title`·website, `.links`·website, `toMatchRows()`·component, `.json body`·api
418
- - the package serves its own fixture site, so `server` is what it proves; `url` targets an already-running deployment, which is a consumer shape (A11 states the XOR) — `url`·website
419
- - a job's oracle is the table it wrote (`toMatchRows`): its result carries no file and no directory — `toMatch('<name>')`·jobs
420
- - the engine owns the family (proven in the module column); a facet uses the ones its goldens need — `{{any}}`·api, `{{any}}`·integration, `{{any}}`·website, `{{base64}}`·api, `{{base64}}`·cli, `{{base64}}`·integration, `{{base64}}`·website, `{{date}}`·api, `{{date}}`·cli, `{{date}}`·integration, `{{date}}`·website, `{{duration}}`·api, `{{duration}}`·integration, `{{duration}}`·website, `{{email}}`·api, `{{email}}`·cli, `{{email}}`·integration, `{{email}}`·website, `{{float}}`·api, `{{float}}`·cli, `{{float}}`·integration, `{{float}}`·website, `{{hex}}`·api, `{{hex}}`·cli, `{{hex}}`·integration, `{{hex}}`·website, `{{int}}`·api, `{{int}}`·cli, `{{int}}`·integration, `{{int}}`·website, `{{ip}}`·api, `{{ip}}`·cli, `{{ip}}`·integration, `{{ip}}`·website, `{{iso8601}}`·api, `{{iso8601}}`·cli, `{{iso8601}}`·integration, `{{iso8601}}`·website, `{{number}}`·api, `{{number}}`·cli, `{{number}}`·integration, `{{number}}`·website, `{{path}}`·api, `{{path}}`·cli, `{{path}}`·integration, `{{path}}`·website, `{{port}}`·api, `{{port}}`·cli, `{{port}}`·integration, `{{port}}`·website, `{{semver}}`·api, `{{semver}}`·integration, `{{semver}}`·website, `{{sha}}`·api, `{{sha}}`·cli, `{{sha}}`·integration, `{{sha}}`·website, `{{string}}`·api, `{{string}}`·integration, `{{string}}`·website, `{{time}}`·api, `{{time}}`·cli, `{{time}}`·integration, `{{time}}`·website, `{{ulid}}`·api, `{{ulid}}`·cli, `{{ulid}}`·integration, `{{ulid}}`·website, `{{url}}`·api, `{{url}}`·integration, `{{url}}`·website, `{{uuid}}`·api, `{{uuid}}`·cli, `{{uuid}}`·integration, `{{uuid}}`·website, `{{workdir}}`·api, `{{workdir}}`·integration, `{{workdir}}`·website
421
-
422
- <!-- /GENERATED:matrix -->
423
-
424
- ## The meta-test channel
425
-
426
- Several truths about this package cannot be asserted from outside it, so they are asserted by running it on itself. Each of these exists because a defect class was found once and made unrepeatable (rule K1).
427
-
428
- The table is GENERATED from the files, because the channel grows faster than a hand-kept list: what a row holds is the file's own `describe` titles, so a meta-test that lands is a row the next `npm run docs` writes.
429
-
430
- <!-- GENERATED:meta-tests — do not edit by hand; run `npm run docs`. Source: src/lint/meta-tests.ts -->
431
-
432
- **19 files** are the channel — every `*.test.ts` directly under `src/lint/`, plus the suite that needs the real binary. What each one holds is its own `describe` titles, read from the file:
433
-
434
- | Meta-test | Holds | Tests |
435
- | ------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- |
436
- | `src/lint/ast.test.ts` | specsAnchor | 7 |
437
- | `src/lint/cards.test.ts` | signature cards — generation freshness (meta-test) · signature cards — completeness against the facet declaration (meta-test) · the skill routes to the cards (meta-test) · signature cards — what a card says | 15 |
438
- | `src/lint/checker-crossfile.test.ts` | checker suppression — suppressedLines (checker-disable comments) | 9 |
439
- | `src/lint/checker-member.test.ts` | the member pass — what a workspace member owes | 18 |
440
- | `src/lint/checker-spec.test.ts` | fixSpecDocument — key order and block scalars | 5 |
441
- | `src/lint/checker.test.ts` | conventions checker — findUnknownTokens (D4) · conventions checker — findKnownTokens (D10) · conventions checker — checkConventionFiles (D4) | 8 |
442
- | `src/lint/coverage.test.ts` | the coverage ratchet | 8 |
443
- | `src/lint/docs-typecheck.test.ts` | docs-typecheck — block selection · docs-typecheck — the published samples typecheck · docs-typecheck — the corpus does not await a sync matcher · docs-typecheck — harness | 6 |
444
- | `src/lint/domains.test.ts` | the spec tree’s domain vocabulary | 4 |
445
- | `src/lint/env-allowlist.test.ts` | framework env reads — e1-env-allowlist (meta-test) | 4 |
446
- | `src/lint/facet-matrix.test.ts` | facet capability declaration (K1 guard) | 6 |
447
- | `src/lint/matrix.test.ts` | capability matrix (meta-test K6) | 5 |
448
- | `src/lint/meta-tests.test.ts` | the meta-test channel, read off its own files | 3 |
449
- | `src/lint/oxlint-pin.test.ts` | the oxlint pin — the toolchain owns it and this manifest mirrors it | 2 |
450
- | `src/lint/package-exports.test.ts` | package-exports — F1 reads the published contract · package-exports — the root has two runtimes and one type surface · package-exports — the tarball carries what a message points at | 8 |
451
- | `src/lint/plugin.test.ts` | the composable fragment — what a consumer wires · the upstream options this vocabulary owns (ADR-005) · conventions catalogue — generation freshness (meta-test) · conventions catalogue — completeness (meta-test) · testing fragment — standalone oxlint config · conventions catalogue — the channels answer for themselves (meta-test) · conventions catalogue — E2E inventory (meta-test) · the corpus’ own coordinates (meta-test K8) · the corpus never offers an option the rulebook refuses | 34 |
452
- | `src/lint/role.test.ts` | roleOf — the kind a path states · isTestRole — the three kinds that DECLARE tests · isTestFile — what the test conventions reach | 14 |
453
- | `src/lint/siblings.test.ts` | modules with no sibling test (meta-test K7) | 4 |
454
- | `specs/lint/meta/k4-reach-per-path.test.ts` | lint — k4-reach-per-path (meta-test) | 4 |
455
-
456
- <!-- /GENERATED:meta-tests -->
457
-
458
- One proof of the channel declares no test and appears in no row: `src/type-channel.test-d.ts` states what the COMPILER refuses — A8, A11, B1, B3, D1, M2, W6 — as an `@ts-expect-error` per row, and `typescript check` fails the day the line it marks starts compiling. It runs under the typechecker the whole repository already runs, never under a vitest project.
459
-
460
- `src/lint/plugin.test.ts` also holds the seven-channel contract: every row of
461
- every channel carries the proof that channel can give — a rule file and a
462
- fixture pair, an assertion on a resolved upstream option, a bundled checker
463
- pass, a `// RUNTIME <ID>` marker above the spec that drives the refusal, a line
464
- of the type-channel file, a named meta-test. A row that loses its proof fails
465
- that test rather than going quietly stale.
466
-
467
- The freshness meta-test is the reason a documentation change can turn the suite red: edit the generated catalogue by hand and it fails, correctly. Regenerate instead — the gesture is [02 — Developing](02-developing.md)'s.
468
-
469
- ## Goldens and update mode
470
-
471
- A fixture this repository compares against is regenerated, never hand-tuned, and the gesture here is the `unit` project:
472
-
473
- ```bash
474
- TEST_UPDATE=1 npx vitest --run --project unit # or: npx vitest --run -u
475
- ```
476
-
477
- What update mode writes, what it preserves and the discipline it asks for are [15 — Tokens § Update mode](15-tokens.md#update-mode-tokens-are-preserved)'s, whole.
478
-
479
- ## What CI runs
480
-
481
- The workflow is `.github/workflows/validate.yaml`, on every push to `main` and every pull request, delegating to the estate's shared `validate.yaml`. It restores `.artifacts/`, then runs `make build`, `make lint` and `make test` in that order, with chromium provisioned because the website specs drive a real browser. The same three targets are what a local run owes before a push; the ordering is not decorative, since lint loads what build produced.
482
-
483
- **`make test` carries the floor.** It runs the suite WITH coverage and then `npm run coverage:check`, so the ratchet is judged on every push rather than trusted to whoever remembered to type `make check` — a gate CI never runs is a courtesy, not a gate. `make check` is still the whole verdict locally: build, lint, then that same target.
484
-
485
- ## Pitfalls
486
-
487
- - **Running the lint specs on a stale `dist/`.** `specs/lint/**` and `oxlint.config.ts` both load `dist/oxlint.js`. Without `npm run build`, the suite judges the previous build's rules and reports a green that means nothing.
488
- - **Hand-editing a golden under `specs/lint/checker/_expected/`.** Those are full-output snapshots of a real binary. Change the message in the code and regenerate with `TEST_UPDATE=1`; a hand-tuned golden asserts your typing, not the checker's output.
489
- - **Expecting `npm test` to pass with Docker stopped.** Only `unit` and `cli` are infrastructure-free. The Docker-backed tests self-skip inside them, but `api`, `jobs` and `integration` fail honestly.
490
- - **Adding a test at a facet root.** `specs/<facet>/<aspect>.spec.ts` is refused by `c1-domain-structure` in this repository's default depth — the runner lives at the root, the tests live one level down.
491
- - **Writing a facet spec with the unit’s suffix.** `c12-spec-file-name` refuses it and `jterrazz-test-check specs --fix` renames it with `git mv` — and names every `include`/`exclude` entry of the member the rename broke — the globs whose prefix covers a file it moved and the literals naming one — since an entry the rename leaves behind collects nothing and the run stays green with fewer files. The reverse — a `.spec.ts` with no `specs/` ancestor — is the member pass's finding.
492
-
493
- ## Related
494
-
495
- [01 — Architecture](01-architecture.md) · [02 — Developing](02-developing.md) · [14 — Assertions](14-assertions.md) · [18 — Conventions](18-conventions.md) · [19 — Linting](19-linting.md)