@jterrazz/test 16.0.0 → 16.1.1

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 (211) hide show
  1. package/dist/appium.adapter.js +59 -4
  2. package/dist/browser/index.js +203 -42
  3. package/dist/checker.js +305 -63
  4. package/dist/index.d.ts +35 -4
  5. package/dist/index.js +47 -24
  6. package/dist/oxlint.cjs +123 -28
  7. package/dist/oxlint.js +123 -28
  8. package/dist/playwright.adapter.js +88 -50
  9. package/dist/substring-warning.js +282 -0
  10. package/dist/vitest.js +91 -12
  11. package/docs/01-architecture.md +95 -0
  12. package/docs/02-developing.md +351 -0
  13. package/docs/03-testing.md +495 -0
  14. package/docs/04-operating.md +101 -0
  15. package/docs/05-module-tests.md +152 -0
  16. package/docs/06-integration.md +180 -0
  17. package/docs/07-component.md +246 -0
  18. package/docs/08-website.md +404 -0
  19. package/docs/09-mobile.md +270 -0
  20. package/docs/10-api.md +264 -0
  21. package/docs/11-jobs.md +157 -0
  22. package/docs/12-cli.md +677 -0
  23. package/docs/13-elements.md +248 -0
  24. package/docs/14-assertions.md +314 -0
  25. package/docs/15-tokens.md +253 -0
  26. package/docs/16-contracts.md +438 -0
  27. package/docs/17-services.md +188 -0
  28. package/docs/18-conventions.md +332 -0
  29. package/docs/19-linting.md +512 -0
  30. package/docs/README.md +36 -0
  31. package/docs/decisions/001-v9-conventions-on-three-channels.md +50 -0
  32. package/docs/decisions/002-the-statique-channel-holds-only-what-upstream-does-not.md +49 -0
  33. package/docs/decisions/003-a-rendered-component-is-a-facet-on-vitest-browser-mode.md +84 -0
  34. package/docs/decisions/004-a-module-against-real-services-or-a-golden-is-the-integration-facet.md +81 -0
  35. package/docs/decisions/005-the-doubles-ladder-is-closed.md +86 -0
  36. package/docs/decisions/006-the-catalogue-reaches-a-file-by-its-role.md +245 -0
  37. package/docs/decisions/007-compose-mode-leaves-the-package.md +53 -0
  38. package/docs/decisions/008-vitest-5-node-24-and-the-native-seams-as-optional-peers.md +75 -0
  39. package/docs/decisions/_template.md +22 -0
  40. package/docs/reference/classes/Matcher.md +120 -0
  41. package/docs/reference/classes/ProcessHandle.md +368 -0
  42. package/docs/reference/functions/defineContract.md +39 -0
  43. package/docs/reference/functions/defineContracts.md +36 -0
  44. package/docs/reference/functions/disabled.md +28 -0
  45. package/docs/reference/functions/enabled.md +20 -0
  46. package/docs/reference/functions/focused.md +26 -0
  47. package/docs/reference/functions/mockOf.md +79 -0
  48. package/docs/reference/functions/postgres.md +31 -0
  49. package/docs/reference/functions/process.md +33 -0
  50. package/docs/reference/functions/redis.md +28 -0
  51. package/docs/reference/functions/required.md +41 -0
  52. package/docs/reference/functions/selected.md +26 -0
  53. package/docs/reference/functions/sqlite.md +33 -0
  54. package/docs/reference/functions/testId.md +20 -0
  55. package/docs/reference/functions/text.md +40 -0
  56. package/docs/reference/functions/valued.md +28 -0
  57. package/docs/reference/functions/waitUntil.md +46 -0
  58. package/docs/reference/functions/within.md +29 -0
  59. package/docs/reference/index.md +196 -0
  60. package/docs/reference/interfaces/BaseResult.md +87 -0
  61. package/docs/reference/interfaces/CallResult.md +160 -0
  62. package/docs/reference/interfaces/CaptureScope.md +82 -0
  63. package/docs/reference/interfaces/CliResult.md +253 -0
  64. package/docs/reference/interfaces/ContainerAccessor.md +166 -0
  65. package/docs/reference/interfaces/DirectoryAccessor.md +33 -0
  66. package/docs/reference/interfaces/FetchResult.md +180 -0
  67. package/docs/reference/interfaces/FilesystemAccessor.md +46 -0
  68. package/docs/reference/interfaces/HttpResult.md +126 -0
  69. package/docs/reference/interfaces/JsonAccessor.md +46 -0
  70. package/docs/reference/interfaces/PageResult.md +356 -0
  71. package/docs/reference/interfaces/RenderResult.md +72 -0
  72. package/docs/reference/interfaces/RenderedText.md +110 -0
  73. package/docs/reference/interfaces/ResponseAccessor.md +47 -0
  74. package/docs/reference/interfaces/ScreenResult.md +128 -0
  75. package/docs/reference/interfaces/TableAccessor.md +26 -0
  76. package/docs/reference/interfaces/TextAccessor.md +96 -0
  77. package/docs/reference/type-aliases/ApiHandle.md +68 -0
  78. package/docs/reference/type-aliases/ApiSpecification.md +213 -0
  79. package/docs/reference/type-aliases/ApiSpecificationOptions.md +68 -0
  80. package/docs/reference/type-aliases/BrowserConsoleMessage.md +30 -0
  81. package/docs/reference/type-aliases/BrowserLinkElement.md +50 -0
  82. package/docs/reference/type-aliases/BrowserMetaElement.md +40 -0
  83. package/docs/reference/type-aliases/BrowserOpenOptions.md +87 -0
  84. package/docs/reference/type-aliases/BrowserPage.md +132 -0
  85. package/docs/reference/type-aliases/BrowserPort.md +51 -0
  86. package/docs/reference/type-aliases/CliEnv.md +11 -0
  87. package/docs/reference/type-aliases/CliHandle.md +80 -0
  88. package/docs/reference/type-aliases/CliInput.md +36 -0
  89. package/docs/reference/type-aliases/CliOutput.md +46 -0
  90. package/docs/reference/type-aliases/CliPort.md +58 -0
  91. package/docs/reference/type-aliases/CliSpecification.md +144 -0
  92. package/docs/reference/type-aliases/CliSpecificationOptions.md +134 -0
  93. package/docs/reference/type-aliases/ComponentChain.md +156 -0
  94. package/docs/reference/type-aliases/ComponentScenario.md +20 -0
  95. package/docs/reference/type-aliases/ComponentUi.md +47 -0
  96. package/docs/reference/type-aliases/ComponentVisitor.md +10 -0
  97. package/docs/reference/type-aliases/ContainerPort.md +113 -0
  98. package/docs/reference/type-aliases/Contract.md +68 -0
  99. package/docs/reference/type-aliases/ContractInput.md +13 -0
  100. package/docs/reference/type-aliases/ContractRequest.md +95 -0
  101. package/docs/reference/type-aliases/ContractResponder.md +23 -0
  102. package/docs/reference/type-aliases/ContractResponse.md +78 -0
  103. package/docs/reference/type-aliases/ContractResponseValue.md +13 -0
  104. package/docs/reference/type-aliases/Contracts.md +48 -0
  105. package/docs/reference/type-aliases/DatabaseKeys.md +16 -0
  106. package/docs/reference/type-aliases/DatabasePort.md +72 -0
  107. package/docs/reference/type-aliases/DeviceOpenOptions.md +46 -0
  108. package/docs/reference/type-aliases/DevicePort.md +51 -0
  109. package/docs/reference/type-aliases/DeviceScreen.md +32 -0
  110. package/docs/reference/type-aliases/DeviceTimeouts.md +39 -0
  111. package/docs/reference/type-aliases/DockerSpecConfig.md +43 -0
  112. package/docs/reference/type-aliases/DomMount.md +21 -0
  113. package/docs/reference/type-aliases/ElementKind.md +27 -0
  114. package/docs/reference/type-aliases/ElementMatch.md +74 -0
  115. package/docs/reference/type-aliases/ElementOptions.md +29 -0
  116. package/docs/reference/type-aliases/ElementRef.md +128 -0
  117. package/docs/reference/type-aliases/ExecOptions.md +37 -0
  118. package/docs/reference/type-aliases/FileAccessor.md +54 -0
  119. package/docs/reference/type-aliases/HonoApp.md +31 -0
  120. package/docs/reference/type-aliases/HttpContractFilter.md +49 -0
  121. package/docs/reference/type-aliases/HttpResponseInit.md +46 -0
  122. package/docs/reference/type-aliases/HttpStreamInit.md +58 -0
  123. package/docs/reference/type-aliases/IntegrationHandle.md +46 -0
  124. package/docs/reference/type-aliases/IntegrationSpecification.md +111 -0
  125. package/docs/reference/type-aliases/IntegrationSpecificationOptions.md +42 -0
  126. package/docs/reference/type-aliases/Intercept.md +19 -0
  127. package/docs/reference/type-aliases/InterceptOptions.md +28 -0
  128. package/docs/reference/type-aliases/InterceptScope.md +11 -0
  129. package/docs/reference/type-aliases/IsolationStrategy.md +75 -0
  130. package/docs/reference/type-aliases/JobHandle.md +34 -0
  131. package/docs/reference/type-aliases/JobsHandle.md +57 -0
  132. package/docs/reference/type-aliases/JobsResult.md +16 -0
  133. package/docs/reference/type-aliases/JobsSpecification.md +94 -0
  134. package/docs/reference/type-aliases/JobsSpecificationOptions.md +56 -0
  135. package/docs/reference/type-aliases/LandmarkKind.md +20 -0
  136. package/docs/reference/type-aliases/LiterateRunFlags.md +27 -0
  137. package/docs/reference/type-aliases/LiterateServeRegistration.md +70 -0
  138. package/docs/reference/type-aliases/MatchFixtureOptions.md +27 -0
  139. package/docs/reference/type-aliases/MatchableRequest.md +61 -0
  140. package/docs/reference/type-aliases/MatcherKind.md +34 -0
  141. package/docs/reference/type-aliases/MobileBackendOptions.md +28 -0
  142. package/docs/reference/type-aliases/MobileElementKind.md +14 -0
  143. package/docs/reference/type-aliases/MobileElementMatch.md +60 -0
  144. package/docs/reference/type-aliases/MobileElementRef.md +20 -0
  145. package/docs/reference/type-aliases/MobileHandle.md +65 -0
  146. package/docs/reference/type-aliases/MobileScenario.md +20 -0
  147. package/docs/reference/type-aliases/MobileSpecification.md +51 -0
  148. package/docs/reference/type-aliases/MobileSpecificationOptions.md +126 -0
  149. package/docs/reference/type-aliases/MobileVisitor.md +81 -0
  150. package/docs/reference/type-aliases/MockOfOptions.md +26 -0
  151. package/docs/reference/type-aliases/MockPort.md +10 -0
  152. package/docs/reference/type-aliases/PinnedClock.md +10 -0
  153. package/docs/reference/type-aliases/PostgresOptions.md +32 -0
  154. package/docs/reference/type-aliases/ProcessOptions.md +112 -0
  155. package/docs/reference/type-aliases/RedisOptions.md +20 -0
  156. package/docs/reference/type-aliases/RenderSubject.md +12 -0
  157. package/docs/reference/type-aliases/ScreenNode.md +67 -0
  158. package/docs/reference/type-aliases/ServerPort.md +36 -0
  159. package/docs/reference/type-aliases/ServerResponse.md +46 -0
  160. package/docs/reference/type-aliases/ServerSpec.md +21 -0
  161. package/docs/reference/type-aliases/ServiceHandle.md +216 -0
  162. package/docs/reference/type-aliases/ServiceRecord.md +12 -0
  163. package/docs/reference/type-aliases/SpecDocument.md +84 -0
  164. package/docs/reference/type-aliases/SpecEnvToken.md +21 -0
  165. package/docs/reference/type-aliases/SpecFileAssertion.md +27 -0
  166. package/docs/reference/type-aliases/SpecFixture.md +30 -0
  167. package/docs/reference/type-aliases/SpecKind.md +8 -0
  168. package/docs/reference/type-aliases/SpecRun.md +108 -0
  169. package/docs/reference/type-aliases/SpecServeEntry.md +40 -0
  170. package/docs/reference/type-aliases/SpecStream.md +32 -0
  171. package/docs/reference/type-aliases/SpecificationConfig.md +290 -0
  172. package/docs/reference/type-aliases/SqliteOptions.md +36 -0
  173. package/docs/reference/type-aliases/SseEvent.md +58 -0
  174. package/docs/reference/type-aliases/StreamBody.md +61 -0
  175. package/docs/reference/type-aliases/TextFilter.md +22 -0
  176. package/docs/reference/type-aliases/VisitScenario.md +20 -0
  177. package/docs/reference/type-aliases/Visitor.md +215 -0
  178. package/docs/reference/type-aliases/WaitUntilOptions.md +46 -0
  179. package/docs/reference/type-aliases/WebsiteBackendOptions.md +37 -0
  180. package/docs/reference/type-aliases/WebsiteHandle.md +51 -0
  181. package/docs/reference/type-aliases/WebsiteSpecification.md +118 -0
  182. package/docs/reference/type-aliases/WebsiteSpecificationOptions.md +40 -0
  183. package/docs/reference/variables/anthropic.md +19 -0
  184. package/docs/reference/variables/banner.md +10 -0
  185. package/docs/reference/variables/button.md +10 -0
  186. package/docs/reference/variables/clock.md +16 -0
  187. package/docs/reference/variables/complementary.md +10 -0
  188. package/docs/reference/variables/component.md +10 -0
  189. package/docs/reference/variables/content.md +10 -0
  190. package/docs/reference/variables/contentinfo.md +10 -0
  191. package/docs/reference/variables/dialog.md +10 -0
  192. package/docs/reference/variables/field.md +10 -0
  193. package/docs/reference/variables/form.md +10 -0
  194. package/docs/reference/variables/heading.md +10 -0
  195. package/docs/reference/variables/http.md +40 -0
  196. package/docs/reference/variables/intercept.md +11 -0
  197. package/docs/reference/variables/link.md +10 -0
  198. package/docs/reference/variables/listitem.md +10 -0
  199. package/docs/reference/variables/main.md +10 -0
  200. package/docs/reference/variables/match.md +8 -0
  201. package/docs/reference/variables/navigation.md +10 -0
  202. package/docs/reference/variables/openai.md +21 -0
  203. package/docs/reference/variables/option.md +17 -0
  204. package/docs/reference/variables/region.md +10 -0
  205. package/docs/reference/variables/row.md +10 -0
  206. package/docs/reference/variables/search.md +10 -0
  207. package/docs/reference/variables/specification.md +8 -0
  208. package/docs/reference/variables/status.md +10 -0
  209. package/docs/reference/variables/table.md +10 -0
  210. package/package.json +3 -2
  211. package/dist/ambiguity.js +0 -146
@@ -0,0 +1,157 @@
1
+ # 11 — Jobs specs (`specification.jobs`)
2
+
3
+ `specification.jobs()` tests background pipelines — cron jobs, queue consumers, nightly reports — by triggering them in-process against real databases and contracted external providers. No HTTP server is involved: the subject under test is _what a job writes_, not what an endpoint returns.
4
+
5
+ Use it when the behaviour you care about starts with "when the job runs…". If the behaviour starts with an HTTP request, use [api](10-api.md).
6
+
7
+ ## What it specifies
8
+
9
+ A jobs spec answers one question: **when this name is triggered, what does the pipeline do to the world it was handed?** The subject is the assembled job — its handler, its services, the network it reaches — met through the one door the scheduler would use, `.trigger(name)`. What a single pure function inside that pipeline returns is a module test's ([05](05-module-tests.md)); what the pipeline leaves in a database is this chapter's.
10
+
11
+ A job spec exercises your job function directly, wired to real containers. The pipeline has no network surface of its own, and testing the scheduler that invokes it is out of scope — the job IS the subject, and the trigger name is how a spec reaches it.
12
+
13
+ ## The constructor
14
+
15
+ ```typescript
16
+ // specs/jobs/jobs.specification.ts
17
+ import { afterAll } from 'vitest';
18
+ import { specification, postgres } from '@jterrazz/test';
19
+ import { nightlyReport, supportDrafts } from '../../src/jobs.js';
20
+
21
+ export const { jobs, cleanup } = await specification.jobs({
22
+ services: {
23
+ db: postgres(),
24
+ analyticsDb: postgres(), // → "analytics-db", init from docker/analytics-db/
25
+ },
26
+ jobs: ({ db, analyticsDb }) => [nightlyReport(db, analyticsDb), supportDrafts(db)],
27
+ });
28
+
29
+ afterAll(cleanup);
30
+ ```
31
+
32
+ ### Options
33
+
34
+ | Option | Required | Description |
35
+ | ---------- | ---------------------------- | ------------------------------------------------------------------------------------------------------ |
36
+ | `services` | yes (if the jobs need infra) | Named record of service factories — same semantics as [api](10-api.md), see [services](17-services.md) |
37
+ | `jobs` | yes | `(services) => JobHandle[]`, or a static array. Each handle is a named, triggerable job (rule A8) |
38
+ | `root` | no | Root-resolution override, same walk-up rule as everywhere (rule A9) |
39
+
40
+ There is **no `server`** option: jobs run in-process by definition, and the record of services is the only outside world they meet (rule A2). A `JobHandle` carries the name you pass to `.trigger()` — the handles above respond to `'nightly-report'` and `'support-drafts'`.
41
+
42
+ ## The chain
43
+
44
+ Setups: `.seed()`, `.intercept()`, `.clock()`. Terminal action: `.trigger(name)` (rule B2). No `.headers()` — there is no request.
45
+
46
+ `.clock('2026-03-04T09:30:00Z')` pins the job's own `Date` for the chain and releases it when the action resolves — a nightly report that stamps a window, an expiry the job computes. The primitive and its second depth are [18 — Conventions § Time](18-conventions.md#time--one-primitive-two-depths).
47
+
48
+ ```typescript
49
+ // specs/jobs/reports/reports.spec.ts
50
+ import { expect, test } from 'vitest';
51
+ import { jobs } from '../jobs.specification.js';
52
+ import pipeline from './contracts/pipeline.contracts.js';
53
+
54
+ test('nightly report classifies, prices and drafts', async () => {
55
+ // Given - pending articles + the world the pipeline reaches out to
56
+ const result = await jobs
57
+ .seed('pending-articles.sql', { database: 'db' })
58
+ .intercept(pipeline)
59
+ .trigger('nightly-report');
60
+
61
+ // Then - the pipeline produced a classified, priced report
62
+ await expect(result.table('reports', { database: 'analyticsDb' })).toMatchRows({
63
+ columns: ['category', 'usd_price'],
64
+ rows: [['ELECTRONICS', 109]],
65
+ });
66
+ });
67
+ ```
68
+
69
+ Everything a pipeline reads from the outside world is declared: seeds set the database state, contracts pin the external providers (OpenAI, Anthropic, arbitrary HTTP — see [contracts](16-contracts.md)). Databases reset at the start of every chain, exactly as for API specs (rules B1, B7).
70
+
71
+ Because jobs run in-process by definition, `.intercept()` is always available. It is **strict** (rule D7): once a chain declares one contract, any outgoing request that matches nothing — including one whose matching contracts are all exhausted — fails the spec with an explicit "Unmatched outgoing HTTP request" error naming the method, URL, and every declared route with its consumption state (see [contracts](16-contracts.md#strict-by-construction-rule-d7)). A chain with no contracts is not network-guarded.
72
+
73
+ ### Seeding and sequences for pipelines
74
+
75
+ Selection is **first non-exhausted match wins**, so a multi-call scenario is a finite contract in front of an unlimited tail — `times` says how many calls the first one answers:
76
+
77
+ ```typescript
78
+ test('retries then recovers from provider rate-limit', async () => {
79
+ // Given - the first call is rate-limited, everything after it succeeds
80
+ const result = await jobs
81
+ .seed('pending-articles.sql', { database: 'db' })
82
+ .intercept([
83
+ { request: openai.chat(), response: openai.error(429), times: 1 },
84
+ { request: openai.chat(), response: openai.reply({ category: 'BOOKS' }) },
85
+ ])
86
+ .trigger('nightly-report');
87
+
88
+ // Then - the retry succeeded
89
+ await expect(result.table('reports', { database: 'analyticsDb' })).toMatchRows({
90
+ columns: ['category'],
91
+ rows: [['BOOKS']],
92
+ });
93
+ });
94
+ ```
95
+
96
+ The number in `times` IS the assertion: it says the job retries exactly once. A contract with no `times` is unlimited, which is what you want for a route the pipeline may hit any number of times.
97
+
98
+ ## The result
99
+
100
+ `.trigger()` resolves to a result whose primary subjects are the databases:
101
+
102
+ | Member | Description |
103
+ | --------------------------- | ----------------------------------------------------------------------- |
104
+ | `result.table(name, opts?)` | Table subject for `toMatchRows` / `toBeEmpty` — async, `await expect()` |
105
+
106
+ With ≥ 2 databases, `{ database: 'key' }` is mandatory on every `.seed()` and `.table()`; with one, forbidden (rule A7). See the [assertions reference](14-assertions.md).
107
+
108
+ ## Unique here
109
+
110
+ ### Error-case testing — a provider that fails on purpose
111
+
112
+ Provider failure modes are first-class response builders — this is where jobs specs earn their keep, because these paths are nearly impossible to reproduce against live providers:
113
+
114
+ ```typescript
115
+ test('handles malformed model output', async () => {
116
+ // Given - the model returns something that is not JSON
117
+ const result = await jobs
118
+ .intercept(openai.chat(), openai.malformed('not json at all'))
119
+ .trigger('nightly-report');
120
+
121
+ // Then - nothing half-written
122
+ await expect(result.table('reports', { database: 'analyticsDb' })).toBeEmpty();
123
+ });
124
+
125
+ test('times out gracefully', async () => {
126
+ // Given - a silent provider (delay longer than the job timeout)
127
+ const result = await jobs
128
+ .intercept(anthropic.messages(), anthropic.timeout())
129
+ .trigger('support-drafts');
130
+
131
+ // Then
132
+ await expect(result.table('support_drafts', { database: 'db' })).toBeEmpty();
133
+ });
134
+ ```
135
+
136
+ The three failure families:
137
+
138
+ | Builder | Simulates |
139
+ | ---------------------------------------------------------------------- | ------------------------------------------------------ |
140
+ | `openai.error(429)` / `anthropic.error(status)` / `http.error(status)` | HTTP error status from the provider |
141
+ | `openai.timeout()` / `anthropic.timeout()` | A provider that never answers within the job's timeout |
142
+ | `openai.malformed('…')` | A 200 whose body violates the provider schema |
143
+
144
+ The full builder catalogue lives in [contracts](16-contracts.md).
145
+
146
+ ## Pitfalls
147
+
148
+ - **Looking for a `server` option.** It exists only on `specification.api()` and `specification.website()` — jobs are in-process by definition (rule A2); what a job reads from the outside is its `services` record (rule A8).
149
+ - **Asserting on a job's return value.** The result surface is the observable state (tables); jobs are specified by their effects.
150
+ - **Forgetting `times` on the leading contract of a sequence.** With no `times` the first contract is unlimited, so it answers every call and the recovery contract behind it is never reached.
151
+ - **One giant spec that triggers two jobs.** A chain has exactly one terminal action (rule B1); sequence scenarios are expressed by seeding the state the second job would have found (rule B7).
152
+ - **Forgetting `await` on table expectations.** `expect(result.table(…))` without `await` never runs the SQL — table matchers are IO matchers (rule D2).
153
+ - **Bounding the tail of a sequence.** The last contract of a sequence should stay unlimited; a `times` there turns one extra retry into an "unmatched outgoing request" failure (rule D7).
154
+
155
+ ## Related
156
+
157
+ [10 — API specs](10-api.md) · [14 — Assertions](14-assertions.md) · [16 — Contracts](16-contracts.md) · [17 — Services](17-services.md)