@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/README.md
CHANGED
|
@@ -1,36 +1,82 @@
|
|
|
1
|
-
# @jterrazz/test
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
|
10
|
-
|
|
|
11
|
-
| [
|
|
12
|
-
| [
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
|
19
|
-
|
|
|
20
|
-
| [
|
|
21
|
-
| [
|
|
22
|
-
| [
|
|
23
|
-
| [
|
|
24
|
-
| [
|
|
25
|
-
| [
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
1
|
+
# @jterrazz/test: the manual
|
|
2
|
+
|
|
3
|
+
How to test with `@jterrazz/test`, and how this repository is built. New here: [Getting started](start/getting-started.md), then the guide of the kind of test you are writing. The front page is the root `README.md`; the brief an agent opens first is the root `AGENTS.md`.
|
|
4
|
+
|
|
5
|
+
The pages follow the package's four export families (what is tested, what surrounds the subject, acting on a screen, judging the result). The first is a guide per kind, the other three are reference.
|
|
6
|
+
|
|
7
|
+
## Start
|
|
8
|
+
|
|
9
|
+
| Page | Answers |
|
|
10
|
+
| --------------------------------------------- | ---------------------------------------------------------------------------------------------------------- |
|
|
11
|
+
| [Getting started](start/getting-started.md) | What the framework is and why, the five ideas behind it, a first spec, and which kind of test to write |
|
|
12
|
+
| [Set up a project](start/set-up-a-project.md) | What to install, the vitest config and its project helpers, turning the conventions on, where a run writes |
|
|
13
|
+
|
|
14
|
+
## Guides
|
|
15
|
+
|
|
16
|
+
### What is tested: one guide per kind
|
|
17
|
+
|
|
18
|
+
| Guide | The subject |
|
|
19
|
+
| ------------------------------------------------ | --------------------------------------------------------------------------------------- |
|
|
20
|
+
| [Module tests](guides/module-tests.md) | One module, in its folder's `__tests__/`, nothing started: `<file>.test.ts` |
|
|
21
|
+
| [Integration specs](guides/integration-specs.md) | A module against real services, or against a golden: `specification.integration()` |
|
|
22
|
+
| [Component specs](guides/component-specs.md) | A rendered unit in a real Chromium, in its folder's `__tests__/`: the `component` chain |
|
|
23
|
+
| [Website specs](guides/website-specs.md) | A served page, fetched or driven in a browser: `specification.website()` |
|
|
24
|
+
| [Mobile specs](guides/mobile-specs.md) | A native app on an iOS simulator: `specification.mobile()` |
|
|
25
|
+
| [API specs](guides/api-specs.md) | An HTTP app, in-process, through complete requests: `specification.api()` |
|
|
26
|
+
| [Job specs](guides/job-specs.md) | A pipeline triggered by name: `specification.job()` |
|
|
27
|
+
| [CLI specs](guides/cli-specs.md) | A binary in a fresh working directory: `specification.cli()` |
|
|
28
|
+
| [Spec documents](guides/spec-documents.md) | A CLI session written as data: the `<case>.spec.yaml` format |
|
|
29
|
+
| [Lint specs](guides/lint-specs.md) | The rules a project ships, over fixture trees: `specification.lint()`, `ruleTester()` |
|
|
30
|
+
|
|
31
|
+
### Developing this repository
|
|
32
|
+
|
|
33
|
+
| Guide | Answers |
|
|
34
|
+
| ---------------------------- | ------------------------------------------------------------------------------------------------------- |
|
|
35
|
+
| [Develop](guides/develop.md) | The loop, which file a change opens, what a change owes, how a page of this manual is written |
|
|
36
|
+
| [Test](guides/test.md) | The vitest projects, the generated capability matrix, coverage, the meta-tests, the package's own rules |
|
|
37
|
+
| [Release](guides/release.md) | The npm release, what leaves the tree, the footprint in a project |
|
|
38
|
+
|
|
39
|
+
## Concepts
|
|
40
|
+
|
|
41
|
+
| Page | Answers |
|
|
42
|
+
| ---------------------------------------- | ------------------------------------------------------------------------------------------------------------- |
|
|
43
|
+
| [Conventions](concepts/conventions.md) | The principles: which test earns its place, one file one subject, the naming of a spec tree, what stays human |
|
|
44
|
+
| [Architecture](concepts/architecture.md) | The runner model, the source trees and their edges, the enforcement channels, what the tarball publishes |
|
|
45
|
+
|
|
46
|
+
## Reference
|
|
47
|
+
|
|
48
|
+
### What surrounds the subject
|
|
49
|
+
|
|
50
|
+
| Page | Holds |
|
|
51
|
+
| ------------------------------------------------- | -------------------------------------------------------------------------------------- |
|
|
52
|
+
| [Stubs](reference/stubs.md) | The network, declared: `defineStub`, the builders, `.intercept()` and `intercept()` |
|
|
53
|
+
| [Services](reference/services.md) | Real databases, caches and processes: `postgres()`, `redis()`, `sqlite()`, `process()` |
|
|
54
|
+
| [Time and doubles](reference/time-and-doubles.md) | `clock`, `mockOf()`, and the doubles ladder |
|
|
55
|
+
|
|
56
|
+
The disk a subject reads is laid by its chain: `.fixture()`, `.link()` and, for what git cannot carry, `.prepare()` on a CLI ([CLI specs](guides/cli-specs.md#file-state-is-the-shape-of-a-fixture-tree)) and a lint spec ([Lint specs](guides/lint-specs.md)). Every path a test names is spelled with `file()` or `directory()` ([Conventions § The naming recap](concepts/conventions.md#the-naming-recap)).
|
|
57
|
+
|
|
58
|
+
### Acting on a screen
|
|
59
|
+
|
|
60
|
+
| Page | Holds |
|
|
61
|
+
| ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
|
|
62
|
+
| [The visitor](reference/visitor.md) | The verbs (`click`, `fill`, `see` ...), the targets (`button()`, `field()` ...), scoping, and how one element is matched |
|
|
63
|
+
|
|
64
|
+
### Judging the result
|
|
65
|
+
|
|
66
|
+
| Page | Holds |
|
|
67
|
+
| ------------------------------------- | -------------------------------------------------------------------------- |
|
|
68
|
+
| [Assertions](reference/assertions.md) | Every result subject and its matchers, goldens, sync and async |
|
|
69
|
+
| [Tokens](reference/tokens.md) | The `{{token}}` grammar for values that move, `#ref` captures, update mode |
|
|
70
|
+
|
|
71
|
+
### The rules and the words
|
|
72
|
+
|
|
73
|
+
| Page | Answers |
|
|
74
|
+
| --------------------------------- | -------------------------------------------------------------------------------------------------- |
|
|
75
|
+
| [Linting](reference/linting.md) | The oxlint plugin, the conventions checker, the ratchet, and the generated catalogue of every rule |
|
|
76
|
+
| [Glossary](reference/glossary.md) | The words this manual uses: kind, runner, chain, oracle, golden, ground, seam, ratchet, door |
|
|
77
|
+
|
|
78
|
+
The examples use made-up products: `shoply`, a shop CLI; a news app with an events feed; and hosts under `.test` and `.example`, which never resolve on the internet.
|
|
79
|
+
|
|
80
|
+
## Decisions
|
|
81
|
+
|
|
82
|
+
The decisions this package alone took are in [`decisions/`](decisions/), numbered in the order they were taken, beside the `_template.md` they follow. Neither [Test](guides/test.md) nor `decisions/` ships in the tarball: from inside an install, read them at <https://github.com/jterrazz/package-test/tree/main/docs>. The API itself is the types the package publishes, and no page restates it: they are browsed as the root `README.md` says, `dist/index.d.ts` first.
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# Architecture
|
|
2
|
+
|
|
3
|
+
How the package is built: the runner model every kind of test shares, the source trees and the one direction their imports go, the channels a convention is enforced through, and what the tarball publishes. What a test looks like from the outside is [Getting started](../start/getting-started.md); this page is for changing the package.
|
|
4
|
+
|
|
5
|
+
The rules themselves (what they say and why) are [Conventions](conventions.md); their normative sentences are the generated catalogue, [Linting](../reference/linting.md).
|
|
6
|
+
|
|
7
|
+
## The runner model
|
|
8
|
+
|
|
9
|
+
Every specification (the code behind one kind of test) 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.
|
|
10
|
+
|
|
11
|
+
Seven constructors exist and the list is closed (`src/specifications/specification.ts`):
|
|
12
|
+
|
|
13
|
+
| Constructor | Handle destructures to | Subject under test |
|
|
14
|
+
| ----------------------------- | --------------------------- | ---------------------------------------------- |
|
|
15
|
+
| `specification.api()` | `{ api, cleanup, docker }` | An HTTP API, built and served in this process |
|
|
16
|
+
| `specification.job()` | `{ job, cleanup }` | A background pipeline, triggered by name |
|
|
17
|
+
| `specification.cli()` | `{ cli, cleanup, docker }` | A command binary in a fresh temp directory |
|
|
18
|
+
| `specification.integration()` | `{ integration, cleanup }` | A module against real services, or a golden |
|
|
19
|
+
| `specification.website()` | `{ website, cleanup, url }` | A rendered page, fetched or driven in chromium |
|
|
20
|
+
| `specification.mobile()` | `{ mobile, cleanup, udid }` | A native screen on an iOS simulator |
|
|
21
|
+
| `specification.lint()` | `{ lint, cleanup }` | The rules a project ships, over a fixture tree |
|
|
22
|
+
|
|
23
|
+
The asymmetry in that column is the model, not an oversight: `docker` reads a container the subject itself started, and neither a pipeline triggered by name nor a module called in-process is specified by the containers it starts, so `job` and `integration` are handed none; `website` and `mobile` drive a browser and a simulator rather than an orchestrated stack, so they carry neither; and `lint` starts nothing at all, its `cleanup` releasing nothing.
|
|
24
|
+
|
|
25
|
+
**One specification has no constructor**, and the same reason explains it: a rendered component starts nothing. `component` ([Component specs](../guides/component-specs.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`.
|
|
26
|
+
|
|
27
|
+
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.
|
|
28
|
+
|
|
29
|
+
### The seam under the chain
|
|
30
|
+
|
|
31
|
+
A specification's chain is COMPOSED: `src/specifications/<specification>/<specification>.composition.ts` lists the setups (each a `*.capability.ts`) and the terminal actions (each a `*.action.ts`) it is made of and the adapter each one is armed through, `defineSpecification` opens the chain, and every action runs through the one pipeline of `src/chain/perform.ts`. Every specification (`job`, `api`, `website`, `integration`, `cli`, `mobile` and `component`) is composed, and there is no other chain. The infrastructure a chain needs is reached through **ports**, each a `*.port.ts` beside the area or the specification that owns it (`database`, `service`, `container`, `isolation` under `src/services/`; `fixture`, `link` under `src/disk/`; `server`, `cli`, `browser`, `device` in their specification), and an adapter implements one. So the chain knows "a database exists"; it never knows Postgres.
|
|
32
|
+
|
|
33
|
+
Every seam is opened lazily rather than imported, and each one is an optional peer loaded by the one module that owns it, so a project that tests no page, no component and no database installs none of them. Which peer each specification and each service needs, and what its refusal says, is [Set up a project § What to install](../start/set-up-a-project.md#what-to-install)'s list; a service's refusal is built by `src/services/peer.ts`, and a specification's by the adapter that loads its peer.
|
|
34
|
+
|
|
35
|
+
## The source trees
|
|
36
|
+
|
|
37
|
+
The code is trees with declared, one-directional edges: the product under `src/`, and `lint/` at the repository root beside it, the rules the package ships (the spine: `src/`, `lint/`, `specs/`, `docs/`, `skills/`). The map is the project's to declare, so this package declares its own as `FRAMEWORK_LAYERS` in `lint/self/checks/layers.ts`; `lint/self/checks/architecture.ts` compiles it, with the library owners, the specification faces and the direction of `lint/`, into the dependency-cruiser rules `jterrazz.config.ts` hands the toolchain's Architecture pass, and that declaration is the enforced statement of what follows, each crossing named at its import.
|
|
38
|
+
|
|
39
|
+
The root reads the sentence of a test. A test opens a **specification** on its subject, sets up what **surrounds** it (the stubs, the services, the disk, the clock, the doubles), has a **visitor** act on it, and judges the result with **expectations**; the **chain** is the machinery that composes all of it, and **vitest** the runner that runs it. Each word is one tree at the root of `src/`, beside the three entries and their tests in `__tests__/`, and a lock of this repository's own holds the list closed.
|
|
40
|
+
|
|
41
|
+
| Tree | May import | Holds |
|
|
42
|
+
| ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
43
|
+
| `chain/` | itself, `lint/kit/`; `paths/` reaches `vitest` | The machinery that composes a specification and knows none: at its root `defineSpecification`, the setup and action contracts, `perform`, the chain state and config, the computed chain type and `pause.ts` (the one wait); `context/` (`.headers()` and `.with()`, what a file shares), `paths/` (the stack reader, where a global setup sits, scratch and temp roots), `refusals/` |
|
|
44
|
+
| `visitor/` | `chain/`, `lint/kit/`, itself | What acts on a screen, one part per section of [The visitor](../reference/visitor.md): `verbs.ts` (`VisitorVerbs`, the verbs every surface shares), `targets/` (the `Target` type, the roles, the landmarks with `within()` and the scope walk, the modifiers, how a target is spelled in messages), `matching/` (each candidate the refusal lists, the tag-to-landmark table, and `ambiguous.ts`, the refusal of a target matching several), `matrix.ts` (which verb and target each surface offers) |
|
|
45
|
+
| `expectations/` | `chain/`, `lint/kit/`, itself, `vitest`; `result.ts` reaches `services/database-option`, the goldens reach how `disk/` names and resolves a path | How a result is judged: the base result and its accessors (`text/`, `disk/`, `table/`, `http/`, `command/`), the goldens and matchers, the `{{token}}` engine (`tokens/`), update mode (`in-place/`), `required` and `waitUntil` (`conditions/`) |
|
|
46
|
+
| `stubs/` | `chain/`, `lint/kit/`, itself, `msw`; a stub's request filter reaches `expectations/tokens/`, one `match.*` grammar | What the outside world replies: public at its top, `stub.ts`, `exchange.ts`, the builders (`kinds/`), the intercept setup and scope (`intercept/`); internal below, the queue and the route grammar (`matching/`), the three deliveries and what they share (`delivery/`: `same-process/` msw's server, `browser-page/` msw's worker, `other-process/` the stub backend; at its root msw's handlers and the stream body on the wire) |
|
|
47
|
+
| `services/` | `chain/`, `lint/kit/`, itself, each service its own library, `vitest`; `.seed()` reaches how `disk/` names and resolves a path | The real infrastructure a run starts: the services record, postgres, redis, sqlite, processes, containers and their startup, seeding, and `database-option.ts` (`database-named-only-when-several-at-runtime`'s check, shared by `.seed()` and `.table()`) |
|
|
48
|
+
| `disk/` | `chain/`, `lint/kit/`, itself | How a test names the disk and what a chain lays on it before its action: `file()` and `directory()` (`spec-path.ts`) and their resolution from the spec file (`resolved-spec-path.ts`), `.fixture()` and `.link()`, their ports and the file-system adapters behind them |
|
|
49
|
+
| `clock/` | `chain/`, `lint/kit/`, itself, `vitest` | The pinned clock and `.clock()`, the instant grammar, the process clock adapter |
|
|
50
|
+
| `mocks/` | `chain/`, `lint/kit/`, itself, `vitest`, `vitest-mock-extended` | `mockOf()`, the one double of a port |
|
|
51
|
+
| `specifications/` | `chain/`, every notion above, `lint/kit/`, each specification its own library; a specification's project reaches the runner config (`vitest/project`) | What is tested, one slice per constructor: `<specification>.face.ts` (the names it publishes), `<specification>.composition.ts` (the composition), `<specification>.handle.ts` (the constructor), `<specification>.project.ts` (its Vitest project), and its concept folders (`request/`, `server/`, `routes/`, `documents/`, `render/`, `device/` ...); the lint specification holds `rule-tester.ts` beside them, `ruleTester()`, the other half of the kit to test a rule; at its root, `specification.ts`, the seven as one record |
|
|
52
|
+
| `vitest/` | itself, `chain/`, `lint/kit/`, update mode's setup, `vitest`, never a specification | What every Vitest project receives, knowing no specification: `preset.ts` (the root config), `project.ts` (the defaults every project is built on, `specificationProject`) and `unit.project.ts` (`unit()`), which wire the global setups their owners provide and resolve (`expectations/in-place/`, `chain/paths/temp-root`); a specification's own setups (`services/containers/`, `specifications/mobile/session/`) ride on its project |
|
|
53
|
+
| `standard/` | `@jterrazz/typescript/config`, nothing of the tree | The defaults of the `test` section, data only: `standard.ts`, which the `test()` preset hands on as written ([`@jterrazz/typescript`'s Configuration § The standard](https://github.com/jterrazz/package-typescript/blob/main/docs/reference/configuration.md#the-standard)) |
|
|
54
|
+
| `lint/` | itself and `src/standard/`, never the rest of `src/`: `self/` alone reads the product it holds to account | At the root, not under `src/`: the tool-facing channel, laid out below. The layer map names its files `lint/**`, and cruises it beside `src/` |
|
|
55
|
+
|
|
56
|
+
**`lint/` is split by audience first.**
|
|
57
|
+
|
|
58
|
+
| Module or folder of `lint/` | Holds |
|
|
59
|
+
| --------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
60
|
+
| `index.oxlint.ts` | what ships at `./oxlint`, where `consumer/` meets `kit/` for the plugin: `recommendedRules`, the plugin as the default export, and `materialAt`/`testPackageOf` |
|
|
61
|
+
| `index.config.ts` | what ships at `./config`: `test()`, the one preset this package hands a project (its section's defaults from `src/standard/`, its oxlint rules, the import and naming rules it hands the toolchain, the sections of `.jterrazz/baseline.json` it owns), and the `TestSettings` type its section merges into `@jterrazz/typescript/config` |
|
|
62
|
+
| `index.checker.ts` | the `#checker` private import, where `consumer/` meets `kit/` for the conventions: the `Contribution` the `test()` preset names as its checks (the conventions pass `kit/checker/` builds from the registry of `consumer/checks/checker.ts`, the `test` section it holds, and its `baseline` command), which `@jterrazz/typescript`'s checker program runs on `typescript check`, `fix` and `baseline` |
|
|
63
|
+
| `consumer/` | the declarations every consumer is held to, and nothing else: `catalogue/` (one row per rule in `manifest.ts`, its family in `families.ts`, the chapter's markers in `chapter.ts`, the fork), `rulebook/` (the `test()` preset, and the overrides it sets on upstream rules, one file per upstream plugin), `rules/` (our oxlint rules, `rules/<family>/<name>.ts` beside its test, assembled by `plugin.ts`), `checks/` (one file per checker pass, listed by `checker.ts`, the registry of the tree, ground, document and member passes and of the fixes a `--fix` applies) |
|
|
64
|
+
| `self/` | the declarations only this repository is held to, never shipped: `catalogue/` (the self rows, the self chapter), `rules/`, `checks/` (the layer map and the rules it compiles to, the library owners, the source locks, and `checker.ts`, their registry), `corpus/` (the meta-tests), and `self.cli.ts`, which composes them with the kit (the self checks the `self()` preset names and the generated chapters), beside `preset.ts`, the `self()` preset, and `plugin.ts`, its plugin's entry |
|
|
65
|
+
| `kit/` | what this package alone runs, on top of the rules kit it reads from `@jterrazz/typescript/rules`. The lesson, the rule rows, the chapter tables, the baseline sections and their judges, the git tree, the members, the ground folders and the suppression directives are the kit's: `project/` (how a rule or a pass reads a spec tree: the module parser, the file role, the subject, the siblings, the spec documents, the specs roots), `baseline/` (the `test` section of `.jterrazz/baseline.json`: the checker's counts, the route key the run keeps), `checker/` (what a check spots, taught into the kit's finding by its row, the oxlint run, the rewrites a fix makes, and the passes handed a registry for the kit's `runChecker`: the tree, ground, document and member runs, the conventions pass and its `baseline` command, the self pass held at zero), `catalogue/` (the row's channels, specifications and reaches, the catalogue chapter those render into, and its page) |
|
|
66
|
+
|
|
67
|
+
Two laws keep the audiences apart, the same in every repository that keeps the tree, and `@jterrazz/typescript`'s Names pass holds both. **Declarations only:** `consumer/` and `self/` hold a rule, a check, a decision on an upstream rule, a catalogue entry, or their tests; a module there hands another module no function but one named for its file, and the code declarations share is `lint/kit/`'s. **The kit knows no declaration:** it is handed the rows, the chapter they are read at, the families and the checks, and an entry at the root of `lint/` (the plugin, the config, the checks) hands them.
|
|
68
|
+
|
|
69
|
+
An import in `lint/` goes one way: `lint/kit/` imports nothing of `consumer/`, `self/` or an entry at the root of `lint/`; `consumer/catalogue/` only `lint/kit/`, since it is the list every rule and pass reads its sentence from; `self/catalogue/` `lint/kit/` and the consumer catalogue it extends; `consumer/rulebook/`, `consumer/rules/` and `consumer/checks/` the catalogue and `lint/kit/`, never each other; the entry any of them. A catalogue's own tests are test material, and read the declarations they audit. That direction is not this package's to state: it is the law of every `lint/` tree, carried by `@jterrazz/typescript`'s base preset, which the Architecture pass holds this repository to with no line of its own. This repository keeps one difference, set aside in the open under `optOuts` in `jterrazz.config.ts`, with its reason and counted by the drift report: the self folders other than the catalogue read each other: the corpus audits the self checker and plugin by importing them.
|
|
70
|
+
|
|
71
|
+
`lint/` and `src/` import each other one way only, and a module both need has one owner. The shipped `lint/` never reads the product: what the runner and the lint both read lives in `lint/kit/` or in the kit it builds on. That is the ground names, the root walk and the git tree it walks, the yaml document and the `<case>.spec.yaml` grammar, the token kinds, the stub kinds, the relative-path law, the settings file's `test` section, the artefact paths, the route-coverage key, and the four-part lesson a runtime refusal and a finding both print (`refusalBlock` and `refusal` of `@jterrazz/typescript/rules`). A runtime refusal is the product's own: `src/chain/refusals/` holds `refusal()`, the four-part block the product throws (built from a refusal row's sentence and the catalogue page it is read at, or from a lesson naming the chapter that explains it), `invariant()`, the one message of what only the framework can cause, and `rows.ts`, the refusal rows and the runtime channel of the catalogue, which the chapter projection reads from there. `src/` imports `lint/kit/` and nothing else of `lint/`. `self/`, never shipped, may read `src/`: it judges the repository's own product; the shipped `lint/` never reads `src/`. The same law holds in `@jterrazz/typescript`, whose `src/` is the command that runs its `lint/`, and the base preset carries it for both: `src-reads-no-declaration` and `shipped-lint-reads-no-self`, printed as `architecture/<law>` at the import.
|
|
72
|
+
|
|
73
|
+
**A setup is defined once, whatever the specification.** A second copy of "declare a stub, seed a table, run the thing" is how two specifications drift, so a specification never implements one: it composes the definition and names the adapter it is armed through, and its chain type is computed from the same list (`src/chain/chain.ts`), each chain published as an interface so a compiler message prints its name. Every specification's `<specification>.composition.ts` names that chain beside its composition, so the specification's own folder says what its constructor hands out; `component`, which has no constructor, is opened once by its own page face, `specifications/component/component.face.browser.ts`, which the page's entry re-exports.
|
|
74
|
+
|
|
75
|
+
The edges point one way: `chain ← visitor · expectations · stubs · services · disk · clock · mocks ← vitest ← specifications ← the entries`: the machinery at the bottom, the notions of the sentence beside each other and apart, the specifications composing them, the entries publishing the specifications. Some carry their reason in the declaration itself. `expectations/result.ts` reaches `services/database-option` because a table read names its database by the rule a seed names it by. `stubs/` reaches `expectations/tokens/` because a stub's request filter IS an expectation on the outgoing request, one `match.*` and `{{token}}` grammar, never a second. A specification's `<specification>.project.ts` reaches `vitest/project` because the runner config runs only in the Vitest main process, never in test code; `vitest/` knows no specification, so the one vite plugin a specification owns, the cli's literate door, reaches the preset through the entry that composes them. The runner reaches `lint/kit/` for exactly what it shares with the lint (the token list, the ground names, the stub kinds, the root walk and the tree it walks, that is the kit's git tree of what git does not ignore, and the `<case>.spec.yaml` parser), so that the file the lint accepts is the file the runner runs, from ONE parser. The map holds more than the layer edges, each a rule of `ARCHITECTURE` named at its import: a specification never imports another (`specification-imports-no-other-specification`), a concept folder is reached only from inside its specification, the entries through its face (`specification-entered-by-its-face*`), and each third-party library is imported by exactly one slice (`library-*-has-one-owner`: `playwright` by `specifications/website/`, `webdriverio` by `specifications/mobile/`, `msw` by `stubs/delivery/`, `pg` by `services/postgres/` ...); a node builtin is no library, and each slice spawns only the program its own concept owns ([ADR-009](../decisions/009-node-builtins-are-not-libraries.md)). What the map cannot express is held by the self checker passes under `lint/self/checks/`, one file per pass over the walk `lint/kit/project/source.ts` shares, each finding at the entry, the import or the folder it names: the source root holds the entries, their tests and the trees of the sentence alone, the stubs are entered by what they publish (a specification reaches a delivery only through an edge `DELIVERY_READERS` declares), and a folder under `src/` is named for a concept, never a role ([Test § The package's own rules](../guides/test.md#the-packages-own-rules) lists each). Swapping the runner would be a rewrite of `vitest/`, its entry, the specifications' projects, the component's page and the few bricks built on a vitest primitive (the clock: `vi`; the scratch directory: `onTestFinished`; the doubles: `vitest-mock-extended`; and the goldens, whose matchers are registered on vitest's `expect`), and of nothing else.
|
|
76
|
+
|
|
77
|
+
This package's product IS test tooling, so `no-test-code-in-production` (which keeps a consumer's production away from test material) is turned off by path wherever that is the job: `vitest/` and its entry, each specification's project, the run's global setups, the component's page, the website's playwright adapter, the stubs' deliveries (`stubs/delivery/`), the clock, the doubles, the scratch directory, the goldens and update mode. Every other module is held to it, the entries and the specification constructors included, which is why a constructor is named `<specification>.handle.ts` and never by a suffix `no-test-code-in-production` reads as test material; and no module a bundle is built from may be test material, which the `package-exports` meta-test reads from the build's source maps.
|
|
78
|
+
|
|
79
|
+
The table's May-import column is a reading of `FRAMEWORK_LAYERS`, never a second statement of it: the declaration in `lint/self/checks/layers.ts` is what the Architecture pass enforces and what a change edits. Where a grant names one subpath of a package (`@jterrazz/typescript/config`), the layer is granted the package: dependency-cruiser judges where an import lands, and a package's `exports` land every subpath in the package's own folder; only a subpath that lands in a folder of its own (`vitest/browser`) keeps an owner apart from its package.
|
|
80
|
+
|
|
81
|
+
The composition roots are the entries, and none names a layer. `src/index.ts` is node's: re-exports only, each specification's face (`src/specifications/<specification>/<specification>.face.ts`, the names that specification contributes) and the modules of the other layers by path. `src/index.services.ts` publishes the services and their types (`@jterrazz/test/services`), and `src/index.stubs.ts` the stubs, their builders and `intercept()` (`@jterrazz/test/stubs`). Each of the three has a page twin, `src/index.browser.ts`, `src/index.services.browser.ts` and `src/index.stubs.browser.ts`: it publishes its node entry's types whole and every value a page can run, refusing the node-only ones (see [What the tree publishes](#what-the-tree-publishes)). The page values of all three live in `src/index.browser.ts`, and the two subpath twins re-export theirs from it: the toolchain writes the chunks two page builds share beside the node build's, under the same names, so page code split across three page builds would be overwritten by its node namesake. `src/index.vitest.ts` is the runner config's, `@jterrazz/test/vitest`: it publishes `unit()` and every specification's project beside the preset, derives `vitestConfig` from the declared projects with those helpers, and hands the preset the cli's literate plugin, so `vitest/` never reaches a specification.
|
|
82
|
+
|
|
83
|
+
## The eight enforcement channels
|
|
84
|
+
|
|
85
|
+
A convention this package states is held by a **channel**, and every row of the catalogue names exactly one of eight. The guiding aim is that most enforcement is programmatic, not review-borne.
|
|
86
|
+
|
|
87
|
+
- **static:** the `jterrazz/*` oxlint plugin: one file per rule under `lint/consumer/rules/<family>/`, the folder of its catalogue family; AST analysis, one diagnostic per file.
|
|
88
|
+
- **upstream:** a convention an oxlint vitest-plugin rule already holds, set as an OPTION by the `test()` preset rather than duplicated as a rule of ours ([ADR-005](../decisions/005-the-doubles-ladder-is-closed.md)).
|
|
89
|
+
- **toolchain:** the import and naming rules the `test()` preset hands `@jterrazz/typescript`'s dependency-cruiser and ls-lint passes, printed as `architecture/<rule>` and `names/<rule>`: a project that declares the preset in its `jterrazz.config.ts` is held to them by `typescript check`.
|
|
90
|
+
- **checker:** passes of the conventions checks `test()` brings to `typescript check`, 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.
|
|
91
|
+
- **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).
|
|
92
|
+
- **type:** what the compiler refuses with no rule at all, proven by a consumer's module compiled against the installed declarations (`specs/cli/tsc/`).
|
|
93
|
+
- **meta:** what the framework's own suite holds about itself: the catalogue's freshness, its completeness, the reach of its rules. [Test](../guides/test.md) owns the channel.
|
|
94
|
+
- **process:** the judgement no single channel settles: asset-driven grouping, golden-file discipline, retro-propagation.
|
|
95
|
+
|
|
96
|
+
The channel a rule sits on is not prose here: it is a field of `lint/consumer/catalogue/manifest.ts`, the single source of truth from which the catalogue in [Linting](../reference/linting.md) is generated. So is its audience: a `consumer` rule is what a project using the package meets and is listed there, a `self` rule holds only this package's own suite, has its row in `lint/self/catalogue/manifest.ts`, and is listed in [Test § The package's own rules](../guides/test.md#the-packages-own-rules). A rule and its normative sentence live together in the code, so the two cannot drift.
|
|
97
|
+
|
|
98
|
+
## What the tree publishes
|
|
99
|
+
|
|
100
|
+
The bundle is built by `typescript build`, from the manifest and the names of the modules loaded by path, into `dist/`, and its shape follows how each entry is consumed. `dist/` carries the entries (`index`, `services`, `stubs` and `vitest`, ESM-only since vitest is), plus `checker` (`lint/index.checker.ts`, the `#checker` private import) and the global setups the specifications hand vitest by path, each a `*.global-setup.ts` shipped under its own name; the package ships no CLI. The repository's own programs (`schema`, `route-split` and the `self` program, `lint/self/self.cli.ts`, that is the self checks `typescript check` runs and the pages), with the `self()` preset and its plugin at the root of `lint/self/`, are built into `.artifacts/dev/` and are absent from `dist/` by construction. The `oxlint` plugin ships dual, because oxlint loads it from a consumer project that may itself be CommonJS.
|
|
101
|
+
|
|
102
|
+
**There are SEVEN entries and no more**: `@jterrazz/test` for what writes a spec (the constructors and their faces, `ruleTester()`, the targets, `match` and the tokens, `clock`, `mockOf()`, `packed()`), `@jterrazz/test/services` for the services a specification starts, `@jterrazz/test/stubs` for what the network replies, `@jterrazz/test/vitest` for the project helpers and the `vitestConfig` the derived `vitest.config.ts` awaits, `@jterrazz/test/oxlint` for the lint plugin, `@jterrazz/test/config` for the `test()` preset a `jterrazz.config.ts` lists, `@jterrazz/test/schema` for an editor validating a `<case>.spec.yaml`. That is rule `no-unpublished-subpath` rather than a convention of taste, and the exemption is derived from the manifest's own `exports` map (the kit's `declaredSubpaths`), so a subpath is exempt the moment it is published and stops being exempt the moment it is withdrawn. A service or a stub imported from the root is rule `service-or-stub-from-its-subpath`, whose fix moves the import: the root then loads no service constructor and no stub builder, though it still carries the startup a specification runs over the services it is handed.
|
|
103
|
+
|
|
104
|
+
**Each spec entry has two runtimes.** A component test renders in a real browser, and the node entry cannot load there: it imports node builtins at load (`node:fs` and `node:child_process` among them), while the native seams (`pg`, `better-sqlite3`, `testcontainers`) are lazy peers, opened on first use. So `exports["."]`, `exports["./services"]` and `exports["./stubs"]` each carry a `browser` condition, resolving to `dist/browser/index.js`, `dist/browser/services.js` and `dist/browser/stubs.js`: the same public surface, built for a page, where every node-only name (the seven constructors, `ruleTester()`, the services, the docker accessors, the results that walk a disk) is a stub that throws where it was called, naming the specification and saying it runs under node. The split is the RUNTIME's and never the specifications': one specifier per entry, so no spec has to remember which build it may import.
|
|
105
|
+
|
|
106
|
+
`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. Each page entry takes its types from its node entry (`export type *`), so they cannot drift, and `specs/cli/node/names.spec.ts`, over the packed tarball, records every build's names and fails a value a page build lacks.
|
|
107
|
+
|
|
108
|
+
The API is the types the package publishes, and nothing re-states it. What the code does project into the corpus is a set of generated tables and `schema/spec.schema.json`, generated from the document grammar's own constants; which tables, and the one gesture that writes them, is [Develop § What a change owes](../guides/develop.md#what-a-change-owes)'s list. Both `docs/` and `schema/` ship in the tarball, `docs/` less the two pages that are this repository's own, so a consumer reads the manual where the messages send them, inside the install ([Release](../guides/release.md#what-leaves-the-tree) names the two). All of them are held fresh by meta-tests.
|
|
109
|
+
|
|
110
|
+
## Pitfalls
|
|
111
|
+
|
|
112
|
+
- **Reaching for a dependency from `chain/`.** A new external package belongs to the ONE slice that needs it (a notion (`stubs/`, `services/` ...) or a specification) and nowhere else. Only a library every layer truly shares sits behind the chain, in the one folder `FRAMEWORK_LAYERS` grants it to: vitest's test hooks in `paths/`; the `yaml` library and the toolchain's config types sit in `lint/kit/project/`, which the chain reaches. Anything else the chain imports is the boundary this package cannot afford to blur, and the layer map and the library-ownership rules refuse it.
|
|
113
|
+
- **Adding an eighth constructor.** The seven 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-specification.md) and `lint` in [ADR-015](../decisions/015-a-rule-a-project-ships-is-the-lint-specification.md).
|
|
114
|
+
- **Writing a rule's normative sentence into a chapter.** It belongs in `lint/consumer/catalogue/manifest.ts` beside the implementation; a chapter that restates it is the second copy the whole design exists to prevent.
|
|
115
|
+
|
|
116
|
+
## Related
|
|
117
|
+
|
|
118
|
+
[Develop](../guides/develop.md) · [Test](../guides/test.md) · [Conventions](conventions.md) · [Linting](../reference/linting.md)
|