@jterrazz/test 15.1.1 → 15.3.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 +38 -29
- package/dist/ambiguity.js +23 -4
- package/dist/artifacts.js +108 -163
- package/dist/browser/index.js +2796 -0
- package/dist/checker.js +470 -29
- package/dist/index.d.ts +1434 -609
- package/dist/index.js +3696 -2745
- package/dist/intercept.js +117 -59
- package/dist/oxlint.cjs +481 -35
- package/dist/oxlint.d.cts +13 -1
- package/dist/oxlint.d.ts +13 -1
- package/dist/oxlint.js +481 -35
- package/dist/playwright.adapter.js +96 -22
- package/dist/{queue.js → stream.js} +61 -2
- package/dist/vitest.d.ts +130 -2
- package/dist/vitest.js +433 -3
- package/package.json +37 -5
package/dist/index.d.ts
CHANGED
|
@@ -1,117 +1,7 @@
|
|
|
1
1
|
import { Scalar } from "yaml";
|
|
2
|
-
import { DeepMockProxy } from "vitest-mock-extended";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
* Code-side dynamic-value matchers (`match.*`) and the capture scope shared
|
|
6
|
-
* by every assertion of a single spec execution.
|
|
7
|
-
*
|
|
8
|
-
* Matchers are used inside `toMatchRows` rows and any structural comparison;
|
|
9
|
-
* their file-side twins are the `{{placeholder}}` forms in `_expected/*.http`
|
|
10
|
-
* fixtures (body AND headers), `_expected/*.json` files, and text snapshots
|
|
11
|
-
* (`_expected/*.txt`). One grammar, one vocabulary (CONVENTIONS D4).
|
|
12
|
-
*/
|
|
13
|
-
/** The frozen token vocabulary (CONVENTIONS D4) plus the code-only kinds. */
|
|
14
|
-
type MatcherKind = 'any' | 'base64' | 'date' | 'duration' | 'email' | 'float' | 'hex' | 'includes' | 'int' | 'ip' | 'iso8601' | 'number' | 'path' | 'port' | 'ref' | 'regex' | 'semver' | 'sha' | 'string' | 'time' | 'ulid' | 'url' | 'uuid' | 'workdir';
|
|
15
|
-
/** Every kind usable as a `{{token}}` in fixture files (all but ref/regex/includes). */
|
|
16
|
-
declare const TOKEN_KINDS: readonly ["any", "base64", "date", "duration", "email", "float", "hex", "int", "ip", "iso8601", "number", "path", "port", "semver", "sha", "string", "time", "ulid", "url", "uuid", "workdir"];
|
|
17
|
-
/** A token kind valid inside a fixture `{{...}}` placeholder. */
|
|
18
|
-
type TokenKind = (typeof TOKEN_KINDS)[number];
|
|
19
|
-
/**
|
|
20
|
-
* A dynamic-value matcher. Created via the {@link match} factories — never
|
|
21
|
-
* constructed directly by user code.
|
|
22
|
-
*/
|
|
23
|
-
declare class Matcher {
|
|
24
|
-
/** For kind 'includes': the substring the actual value must contain. */
|
|
25
|
-
readonly includes?: string | undefined;
|
|
26
|
-
readonly kind: MatcherKind;
|
|
27
|
-
/** For kind 'ref': the capture to assert inequality against. */
|
|
28
|
-
readonly notRef?: string | undefined;
|
|
29
|
-
/** For kind 'ref': the capture name. */
|
|
30
|
-
readonly refName?: string | undefined;
|
|
31
|
-
/** For kind 'regex': the pattern the actual value must match. */
|
|
32
|
-
readonly regex?: RegExp | undefined;
|
|
33
|
-
constructor(kind: MatcherKind, options?: {
|
|
34
|
-
includes?: string | undefined;
|
|
35
|
-
notRef?: string | undefined;
|
|
36
|
-
refName?: string | undefined;
|
|
37
|
-
regex?: RegExp | undefined;
|
|
38
|
-
});
|
|
39
|
-
/** Placeholder-style rendering used in failure diffs and serialization. */
|
|
40
|
-
toString(): string;
|
|
41
|
-
toJSON(): string;
|
|
42
|
-
}
|
|
43
|
-
/** A no-argument matcher factory — what every typed entry of `match` is. */
|
|
44
|
-
type TypedMatcher = () => Matcher;
|
|
45
|
-
/**
|
|
46
|
-
* Dynamic-value matchers for structural comparisons — the code-side mirror of
|
|
47
|
-
* the `{{token}}` fixture grammar (CONVENTIONS D4).
|
|
48
|
-
*
|
|
49
|
-
* @example
|
|
50
|
-
* await expect(result.table('users', { database: 'db' })).toMatchRows({
|
|
51
|
-
* columns: ['id', 'name'],
|
|
52
|
-
* rows: [[match.uuid(), 'Alice']],
|
|
53
|
-
* });
|
|
54
|
-
*/
|
|
55
|
-
/**
|
|
56
|
-
* The matcher roster: one no-argument factory per token kind, plus the three
|
|
57
|
-
* code-only forms the `{{token}}` grammar does not carry.
|
|
58
|
-
*/
|
|
59
|
-
type MatchFactories = Record<TokenKind, TypedMatcher> & {
|
|
60
|
-
includes: (substring: string) => Matcher;
|
|
61
|
-
ref: (name: string, options?: {
|
|
62
|
-
not?: string;
|
|
63
|
-
}) => Matcher;
|
|
64
|
-
regex: (regex: RegExp) => Matcher;
|
|
65
|
-
};
|
|
66
|
-
declare const match: MatchFactories;
|
|
67
|
-
/**
|
|
68
|
-
* Named captures recorded by `match.ref()` / `{{type#ref}}` placeholders.
|
|
69
|
-
* One scope lives on each spec result — every assertion chained off the same
|
|
70
|
-
* result shares it, and a new chain starts fresh.
|
|
71
|
-
*/
|
|
72
|
-
declare class CaptureScope {
|
|
73
|
-
private readonly values;
|
|
74
|
-
/**
|
|
75
|
-
* The exact working directory of the current spec, when the framework
|
|
76
|
-
* knows it (cli mode). Drives the `{{workdir}}` token / `match.workdir()`.
|
|
77
|
-
*/
|
|
78
|
-
workdir?: string | undefined;
|
|
79
|
-
constructor(workdir?: string);
|
|
80
|
-
get(name: string): unknown;
|
|
81
|
-
has(name: string): boolean;
|
|
82
|
-
set(name: string, value: unknown): void;
|
|
83
|
-
}
|
|
84
|
-
//#endregion
|
|
85
|
-
//#region src/specification/facets/_common/result/json.d.ts
|
|
86
|
-
/**
|
|
87
|
-
* Read-only accessor for a JSON payload parsed from a text stream (stdout).
|
|
88
|
-
*
|
|
89
|
-
* Lazily parses the JSON on first use; parse errors are deferred until the
|
|
90
|
-
* value is read so that callers can still read the raw stream text without
|
|
91
|
-
* triggering a throw.
|
|
92
|
-
*
|
|
93
|
-
* ANSI escape sequences are stripped by default before `JSON.parse`
|
|
94
|
-
* (CONVENTIONS D6). When a `transform` is configured on the spec runner, it
|
|
95
|
-
* runs on the stripped text — an escape hatch for application noise not
|
|
96
|
-
* covered by the `{{token}}` grammar.
|
|
97
|
-
*
|
|
98
|
-
* Assertions go through `expect()`: `expect(result.json).toMatch('name.json')`
|
|
99
|
-
* (deep-equal against `_expected/<name>`, with `{{placeholder}}` support).
|
|
100
|
-
*/
|
|
101
|
-
declare class JsonAccessor {
|
|
102
|
-
/** @internal Ref-capture scope shared by the current spec execution. */
|
|
103
|
-
readonly captures: CaptureScope;
|
|
104
|
-
/** @internal */
|
|
105
|
-
private readonly rawText;
|
|
106
|
-
/** @internal Test-file directory — fixture resolution root for matchers. */
|
|
107
|
-
readonly testDir: string;
|
|
108
|
-
/** @internal */
|
|
109
|
-
private readonly transform?;
|
|
110
|
-
constructor(rawText: string, testDir: string, transform?: (text: string) => string, captures?: CaptureScope);
|
|
111
|
-
/** The parsed JSON value. Throws if the text is not valid JSON. */
|
|
112
|
-
get value(): unknown;
|
|
113
|
-
}
|
|
114
|
-
//#endregion
|
|
2
|
+
import { DeepMockProxy, MockProxy } from "vitest-mock-extended";
|
|
3
|
+
import { ReactNode as ComponentUi } from "react";
|
|
4
|
+
import "vitest/browser";
|
|
115
5
|
//#region src/specification/contracts/types.d.ts
|
|
116
6
|
/**
|
|
117
7
|
* The observed outgoing request, reduced to what contract matchers inspect.
|
|
@@ -155,12 +45,51 @@ type ContractRequest = {
|
|
|
155
45
|
type ContractResponse = {
|
|
156
46
|
/** HTTP status code (default: 200). */
|
|
157
47
|
status?: number;
|
|
158
|
-
/**
|
|
48
|
+
/**
|
|
49
|
+
* Response body — an object is JSON, a string is text, `null`/`undefined`
|
|
50
|
+
* is empty, and a {@link StreamBody} (built by `http.stream()` /
|
|
51
|
+
* `http.sse()`) arrives in pieces.
|
|
52
|
+
*/
|
|
159
53
|
body: unknown;
|
|
160
54
|
/** Response headers. */
|
|
161
55
|
headers?: Record<string, string> | undefined;
|
|
162
56
|
/** Delay in ms before responding (for timeout testing). */
|
|
163
57
|
delay?: number | undefined;
|
|
58
|
+
/**
|
|
59
|
+
* A transport failure instead of a reply: the request never reached a
|
|
60
|
+
* server, so `fetch` rejects. `status`, `body` and `headers` say nothing
|
|
61
|
+
* here — there is no response to carry them.
|
|
62
|
+
*
|
|
63
|
+
* The one thing a status code cannot express. A component's "the server did
|
|
64
|
+
* not answer, check that it is running" branch is reached by a rejected
|
|
65
|
+
* `fetch`, and a 503 stand-in tests the other branch entirely.
|
|
66
|
+
*/
|
|
67
|
+
transport?: 'network-error' | undefined;
|
|
68
|
+
};
|
|
69
|
+
/**
|
|
70
|
+
* The marker that tells a STREAMED body from a value that happens to be an
|
|
71
|
+
* object. A body reaches three different engines (msw's server, msw's worker,
|
|
72
|
+
* the `node:http` stub) and each of them serialises "an object" as JSON, so a
|
|
73
|
+
* stream declared as a plain value arrives as `{}`. The tag is what each
|
|
74
|
+
* engine recognises before it reaches for `JSON.stringify`.
|
|
75
|
+
*/
|
|
76
|
+
declare const STREAM_BODY = "@jterrazz/test:stream";
|
|
77
|
+
/**
|
|
78
|
+
* A body that arrives in pieces — what `http.stream()` and `http.sse()` build.
|
|
79
|
+
*
|
|
80
|
+
* The chunks are written in order, `delayBetweenChunks` milliseconds apart,
|
|
81
|
+
* and the connection closes after the last one. It is the chunks and their
|
|
82
|
+
* ORDER that a streaming subject is specified against: a client that renders
|
|
83
|
+
* tokens as they land, or one that reconnects on a half-read body.
|
|
84
|
+
*/
|
|
85
|
+
type StreamBody = {
|
|
86
|
+
/** The pieces, written in order. */
|
|
87
|
+
chunks: readonly string[];
|
|
88
|
+
/** The `content-type` the stream is served under. */
|
|
89
|
+
contentType: string;
|
|
90
|
+
/** Milliseconds between two chunks. Default 0 — everything at once. */
|
|
91
|
+
delayBetweenChunks: number;
|
|
92
|
+
kind: typeof STREAM_BODY;
|
|
164
93
|
};
|
|
165
94
|
/**
|
|
166
95
|
* A dynamic response: computed from the observed request at the moment the
|
|
@@ -260,6 +189,144 @@ declare function defineContract(contract: Contract): Contract;
|
|
|
260
189
|
*/
|
|
261
190
|
declare function defineContracts(...items: ContractInput[]): Contracts;
|
|
262
191
|
//#endregion
|
|
192
|
+
//#region src/integrations/msw/scope.d.ts
|
|
193
|
+
/**
|
|
194
|
+
* One block's worth of declared network. Disposed at the end of the scope that
|
|
195
|
+
* declared it: the handlers are dropped, and an unmatched request is thrown.
|
|
196
|
+
*/
|
|
197
|
+
type InterceptScope = AsyncDisposable;
|
|
198
|
+
/**
|
|
199
|
+
* The module-scope network double. Resolves once the engine is listening —
|
|
200
|
+
* which is why it is a promise, and why the canonical form awaits it:
|
|
201
|
+
*
|
|
202
|
+
* ```typescript
|
|
203
|
+
* await using _ = await intercept(http.get(url), http.json({ ok: true }));
|
|
204
|
+
* ```
|
|
205
|
+
*
|
|
206
|
+
* Without that `await` the subject could reach the real network before the
|
|
207
|
+
* first handler is in place, so the shape refuses the racy spelling: a promise
|
|
208
|
+
* is not an `AsyncDisposable`, and the compiler says so.
|
|
209
|
+
*/
|
|
210
|
+
type Intercept = ((contracts: ContractInput) => Promise<InterceptScope>) & ((request: ContractRequest, response: ContractResponder | ContractResponse) => Promise<InterceptScope>);
|
|
211
|
+
//#endregion
|
|
212
|
+
//#region src/specification/matching/match.d.ts
|
|
213
|
+
/**
|
|
214
|
+
* Code-side dynamic-value matchers (`match.*`) and the capture scope shared
|
|
215
|
+
* by every assertion of a single spec execution.
|
|
216
|
+
*
|
|
217
|
+
* Matchers are used inside `toMatchRows` rows and any structural comparison;
|
|
218
|
+
* their file-side twins are the `{{placeholder}}` forms in `_expected/*.http`
|
|
219
|
+
* fixtures (body AND headers), `_expected/*.json` files, and text snapshots
|
|
220
|
+
* (`_expected/*.txt`). One grammar, one vocabulary (CONVENTIONS D4).
|
|
221
|
+
*/
|
|
222
|
+
/** The frozen token vocabulary (CONVENTIONS D4) plus the code-only kinds. */
|
|
223
|
+
type MatcherKind = 'any' | 'base64' | 'date' | 'duration' | 'email' | 'float' | 'hex' | 'includes' | 'int' | 'ip' | 'iso8601' | 'number' | 'path' | 'port' | 'ref' | 'regex' | 'semver' | 'sha' | 'string' | 'time' | 'ulid' | 'url' | 'uuid' | 'workdir';
|
|
224
|
+
/** Every kind usable as a `{{token}}` in fixture files (all but ref/regex/includes). */
|
|
225
|
+
declare const TOKEN_KINDS: readonly ["any", "base64", "date", "duration", "email", "float", "hex", "int", "ip", "iso8601", "number", "path", "port", "semver", "sha", "string", "time", "ulid", "url", "uuid", "workdir"];
|
|
226
|
+
/** A token kind valid inside a fixture `{{...}}` placeholder. */
|
|
227
|
+
type TokenKind = (typeof TOKEN_KINDS)[number];
|
|
228
|
+
/**
|
|
229
|
+
* A dynamic-value matcher. Created via the {@link match} factories — never
|
|
230
|
+
* constructed directly by user code.
|
|
231
|
+
*/
|
|
232
|
+
declare class Matcher {
|
|
233
|
+
/** For kind 'includes': the substring the actual value must contain. */
|
|
234
|
+
readonly includes?: string | undefined;
|
|
235
|
+
readonly kind: MatcherKind;
|
|
236
|
+
/** For kind 'ref': the capture to assert inequality against. */
|
|
237
|
+
readonly notRef?: string | undefined;
|
|
238
|
+
/** For kind 'ref': the capture name. */
|
|
239
|
+
readonly refName?: string | undefined;
|
|
240
|
+
/** For kind 'regex': the pattern the actual value must match. */
|
|
241
|
+
readonly regex?: RegExp | undefined;
|
|
242
|
+
constructor(kind: MatcherKind, options?: {
|
|
243
|
+
includes?: string | undefined;
|
|
244
|
+
notRef?: string | undefined;
|
|
245
|
+
refName?: string | undefined;
|
|
246
|
+
regex?: RegExp | undefined;
|
|
247
|
+
});
|
|
248
|
+
/** Placeholder-style rendering used in failure diffs and serialization. */
|
|
249
|
+
toString(): string;
|
|
250
|
+
toJSON(): string;
|
|
251
|
+
}
|
|
252
|
+
/** A no-argument matcher factory — what every typed entry of `match` is. */
|
|
253
|
+
type TypedMatcher = () => Matcher;
|
|
254
|
+
/**
|
|
255
|
+
* Dynamic-value matchers for structural comparisons — the code-side mirror of
|
|
256
|
+
* the `{{token}}` fixture grammar (CONVENTIONS D4).
|
|
257
|
+
*
|
|
258
|
+
* @example
|
|
259
|
+
* await expect(result.table('users', { database: 'db' })).toMatchRows({
|
|
260
|
+
* columns: ['id', 'name'],
|
|
261
|
+
* rows: [[match.uuid(), 'Alice']],
|
|
262
|
+
* });
|
|
263
|
+
*/
|
|
264
|
+
/**
|
|
265
|
+
* The matcher roster: one no-argument factory per token kind, plus the three
|
|
266
|
+
* code-only forms the `{{token}}` grammar does not carry.
|
|
267
|
+
*/
|
|
268
|
+
type MatchFactories = Record<TokenKind, TypedMatcher> & {
|
|
269
|
+
includes: (substring: string) => Matcher;
|
|
270
|
+
ref: (name: string, options?: {
|
|
271
|
+
not?: string;
|
|
272
|
+
}) => Matcher;
|
|
273
|
+
regex: (regex: RegExp) => Matcher;
|
|
274
|
+
};
|
|
275
|
+
declare const match: MatchFactories;
|
|
276
|
+
/**
|
|
277
|
+
* Named captures recorded by `match.ref()` / `{{type#ref}}` placeholders.
|
|
278
|
+
* One scope lives on each spec result — every assertion chained off the same
|
|
279
|
+
* result shares it, and a new chain starts fresh.
|
|
280
|
+
*/
|
|
281
|
+
declare class CaptureScope {
|
|
282
|
+
private readonly values;
|
|
283
|
+
/**
|
|
284
|
+
* The exact working directory of the current spec, when the framework
|
|
285
|
+
* knows it (cli mode). Drives the `{{workdir}}` token / `match.workdir()`.
|
|
286
|
+
*/
|
|
287
|
+
workdir?: string | undefined;
|
|
288
|
+
constructor(workdir?: string);
|
|
289
|
+
get(name: string): unknown;
|
|
290
|
+
has(name: string): boolean;
|
|
291
|
+
set(name: string, value: unknown): void;
|
|
292
|
+
}
|
|
293
|
+
//#endregion
|
|
294
|
+
//#region src/specification/facets/_common/result/json.d.ts
|
|
295
|
+
/**
|
|
296
|
+
* Read-only accessor for a JSON payload parsed from a text stream (stdout).
|
|
297
|
+
*
|
|
298
|
+
* Lazily parses the JSON on first use; parse errors are deferred until the
|
|
299
|
+
* value is read so that callers can still read the raw stream text without
|
|
300
|
+
* triggering a throw.
|
|
301
|
+
*
|
|
302
|
+
* ANSI escape sequences are stripped by default before `JSON.parse`
|
|
303
|
+
* (CONVENTIONS D6). When a `transform` is configured on the spec runner, it
|
|
304
|
+
* runs on the stripped text — an escape hatch for application noise not
|
|
305
|
+
* covered by the `{{token}}` grammar.
|
|
306
|
+
*
|
|
307
|
+
* Assertions go through `expect()`: `expect(result.json).toMatch('name.json')`
|
|
308
|
+
* (deep-equal against `_expected/<name>`, with `{{placeholder}}` support).
|
|
309
|
+
*/
|
|
310
|
+
declare class JsonAccessor<Value = unknown> {
|
|
311
|
+
/** @internal Ref-capture scope shared by the current spec execution. */
|
|
312
|
+
readonly captures: CaptureScope;
|
|
313
|
+
/** @internal */
|
|
314
|
+
private readonly rawText;
|
|
315
|
+
/** @internal Test-file directory — fixture resolution root for matchers. */
|
|
316
|
+
readonly testDir: string;
|
|
317
|
+
/** @internal */
|
|
318
|
+
private readonly transform?;
|
|
319
|
+
constructor(rawText: string, testDir: string, transform?: (text: string) => string, captures?: CaptureScope);
|
|
320
|
+
/**
|
|
321
|
+
* The parsed JSON value. Throws if the text is not valid JSON.
|
|
322
|
+
*
|
|
323
|
+
* `Value` is `unknown` unless the subject that produced this accessor
|
|
324
|
+
* knows better — `.call<T>()` does, and hands its T straight through so a
|
|
325
|
+
* one-field reading needs no cast in the test.
|
|
326
|
+
*/
|
|
327
|
+
get value(): Value;
|
|
328
|
+
}
|
|
329
|
+
//#endregion
|
|
263
330
|
//#region src/specification/ports/browser.port.d.ts
|
|
264
331
|
/**
|
|
265
332
|
* The landmark roles — the standard set of page regions, and the only
|
|
@@ -267,8 +334,15 @@ declare function defineContracts(...items: ContractInput[]): Contracts;
|
|
|
267
334
|
* so `within()` never becomes a second selector language.
|
|
268
335
|
*/
|
|
269
336
|
type LandmarkKind = 'banner' | 'complementary' | 'contentinfo' | 'form' | 'main' | 'navigation' | 'region' | 'search';
|
|
270
|
-
/**
|
|
271
|
-
|
|
337
|
+
/**
|
|
338
|
+
* The interactive/textual element kinds — what a visitor actually acts on.
|
|
339
|
+
*
|
|
340
|
+
* `dialog`, `status`, `table`, `row`, `listitem` and `option` are ARIA roles
|
|
341
|
+
* like the landmarks, but they are not CONTAINERS of a page: they are things a
|
|
342
|
+
* visitor reads and acts on, and they are the roles the estate's UI specs were
|
|
343
|
+
* already reaching for through `getByRole` or, worse, through `.first()`.
|
|
344
|
+
*/
|
|
345
|
+
type ElementKind = 'button' | 'dialog' | 'field' | 'heading' | 'link' | 'listitem' | 'option' | 'row' | 'status' | 'table' | 'testId' | 'text';
|
|
272
346
|
/**
|
|
273
347
|
* A user-facing element descriptor — pure data, built by the element
|
|
274
348
|
* vocabulary (`button()`, `link()`, `field()`, …) and translated into
|
|
@@ -285,9 +359,37 @@ type ElementRef = {
|
|
|
285
359
|
* "Read Articles".
|
|
286
360
|
*/
|
|
287
361
|
exact?: boolean;
|
|
362
|
+
/**
|
|
363
|
+
* Assert about the element's FOCUS rather than its presence — built by
|
|
364
|
+
* `focused(element)`. The accessibility tree carries no focus state, so
|
|
365
|
+
* where the keyboard is cannot be goldened; it is asserted by a verb.
|
|
366
|
+
*/
|
|
367
|
+
focused?: boolean;
|
|
368
|
+
/**
|
|
369
|
+
* Narrows to "and it is (not) accepting input" — built by `disabled(x)` /
|
|
370
|
+
* `enabled(x)`. Like `focused`, a STATE the vocabulary can already name the
|
|
371
|
+
* element of; unlike it, one the ARIA tree carries too (`[disabled]`), so
|
|
372
|
+
* this modifier is what names the ONE control a test is about, and it
|
|
373
|
+
* reaches the direction a behavioural substitute cannot — clicking a
|
|
374
|
+
* disabled control is a timeout, never an answer.
|
|
375
|
+
*/
|
|
376
|
+
disabled?: boolean;
|
|
288
377
|
kind: ElementKind | LandmarkKind;
|
|
289
378
|
/** Landmarks may be anonymous (`main()`, `banner()`); everything else is named. */
|
|
290
379
|
name?: string;
|
|
380
|
+
/**
|
|
381
|
+
* Narrows to "and it is (not) the chosen one" — built by `selected(x)`.
|
|
382
|
+
* An option of a COLLAPSED select is in the tree and in the document, and
|
|
383
|
+
* it has no box on the screen, so this facet of it is read from the node
|
|
384
|
+
* (`:checked`) rather than from what a visitor could point at.
|
|
385
|
+
*/
|
|
386
|
+
selected?: boolean;
|
|
387
|
+
/**
|
|
388
|
+
* The value the field must hold — built by `valued(field('Title'), '…')`.
|
|
389
|
+
* A field's value is a PROPERTY of the node: a controlled input's `value`
|
|
390
|
+
* attribute never moves, so nothing but the live value answers for it.
|
|
391
|
+
*/
|
|
392
|
+
value?: string;
|
|
291
393
|
/**
|
|
292
394
|
* Restrict the search to the elements of another descriptor — built by
|
|
293
395
|
* `within(scope, target)`. Chains: a scope may itself carry a scope.
|
|
@@ -300,6 +402,12 @@ type ElementRef = {
|
|
|
300
402
|
* without opening a browser.
|
|
301
403
|
*/
|
|
302
404
|
type ElementMatch = {
|
|
405
|
+
/**
|
|
406
|
+
* The accessible name, when it differs from the text — an `aria-label` is
|
|
407
|
+
* what a role descriptor MATCHED on, and printing the text alone sends the
|
|
408
|
+
* author to disambiguate a name they cannot see.
|
|
409
|
+
*/
|
|
410
|
+
accessibleName?: string | undefined;
|
|
303
411
|
/** Nearest landmark ancestor (`nav`, `footer`, `main`…), when there is one. */
|
|
304
412
|
context?: string | undefined;
|
|
305
413
|
/** The attribute that disambiguates most — `href` for links, `name` for fields. */
|
|
@@ -340,7 +448,13 @@ type Visitor = {
|
|
|
340
448
|
click: (element: ElementRef) => Promise<void>;
|
|
341
449
|
/** Fill a form field with a value. */
|
|
342
450
|
fill: (element: ElementRef, value: string) => Promise<void>;
|
|
343
|
-
/**
|
|
451
|
+
/**
|
|
452
|
+
* Wait until the element is GONE — hidden, or never in the document. The
|
|
453
|
+
* absence primitive: `see()` cannot answer "the dialog closed", because an
|
|
454
|
+
* element that never appears and one that disappeared read the same to it.
|
|
455
|
+
*/
|
|
456
|
+
gone: (element: ElementRef) => Promise<void>;
|
|
457
|
+
/** Navigate to a path of the site under test. */
|
|
344
458
|
goto: (path: string) => Promise<void>;
|
|
345
459
|
/** Hover the element. */
|
|
346
460
|
hover: (element: ElementRef) => Promise<void>;
|
|
@@ -365,6 +479,11 @@ type BrowserOpenOptions = {
|
|
|
365
479
|
* and the boundary of the `external` policy.
|
|
366
480
|
*/
|
|
367
481
|
baseUrl: string;
|
|
482
|
+
/**
|
|
483
|
+
* Pin the page's own `Date` at this instant before the first byte is
|
|
484
|
+
* parsed — the calendar the site's scripts read. `.clock()` on the chain.
|
|
485
|
+
*/
|
|
486
|
+
clock?: string | undefined;
|
|
368
487
|
/**
|
|
369
488
|
* Cross-origin request policy. `'block'` aborts every request leaving
|
|
370
489
|
* the site under test (analytics, CDNs) — the browser-side analog of
|
|
@@ -396,6 +515,8 @@ type BrowserPage = {
|
|
|
396
515
|
status: number;
|
|
397
516
|
/** Rendered `document.body.innerText`. */
|
|
398
517
|
text: string;
|
|
518
|
+
/** The ARIA snapshot of the rendered `<body>` — the outline a golden pins. */
|
|
519
|
+
tree: string;
|
|
399
520
|
/** `document.title` after rendering. */
|
|
400
521
|
title: string;
|
|
401
522
|
/** Final URL after redirects and scenario navigation. */
|
|
@@ -813,24 +934,6 @@ declare class TextAccessor {
|
|
|
813
934
|
toString(): string;
|
|
814
935
|
valueOf(): string;
|
|
815
936
|
}
|
|
816
|
-
/**
|
|
817
|
-
* Wrap an arbitrary string into a {@link TextAccessor} anchored on the calling
|
|
818
|
-
* test's directory — the same caller-detection the builders use.
|
|
819
|
-
*
|
|
820
|
-
* The product surface of a test framework is its own error messages, checker
|
|
821
|
-
* output, and reports; those deserve the same goldening as any other output.
|
|
822
|
-
* `text()` makes an ad-hoc string a first-class snapshot subject:
|
|
823
|
-
*
|
|
824
|
-
* ```typescript
|
|
825
|
-
* const message = await catchMessage(() => expect(result.response).toMatch('wrong-body.http'));
|
|
826
|
-
* expect(text(message)).toMatch('wrong-body-error.txt'); // resolves to _expected/
|
|
827
|
-
* ```
|
|
828
|
-
*
|
|
829
|
-
* ANSI is stripped before every comparison (the raw form stays on `.text`),
|
|
830
|
-
* the `{{token}}` grammar applies to the fixture, and `.grep()` composition
|
|
831
|
-
* works exactly as on any stream accessor.
|
|
832
|
-
*/
|
|
833
|
-
declare function text(value: string): TextAccessor;
|
|
834
937
|
//#endregion
|
|
835
938
|
//#region src/specification/facets/cli/result.d.ts
|
|
836
939
|
/**
|
|
@@ -935,6 +1038,62 @@ type LiterateRunFlags = {
|
|
|
935
1038
|
frozen?: boolean | undefined;
|
|
936
1039
|
};
|
|
937
1040
|
//#endregion
|
|
1041
|
+
//#region src/specification/facets/integration/result.d.ts
|
|
1042
|
+
/** What `.call()` produced: the value it returned, or the error it threw. */
|
|
1043
|
+
type CallOutcome = {
|
|
1044
|
+
error: unknown;
|
|
1045
|
+
threw: boolean;
|
|
1046
|
+
value: unknown;
|
|
1047
|
+
};
|
|
1048
|
+
type CallResultOptions = BaseResultOptions & {
|
|
1049
|
+
outcome: CallOutcome;
|
|
1050
|
+
};
|
|
1051
|
+
/**
|
|
1052
|
+
* What `result.value` is, decided by what the call returned: a string reads as
|
|
1053
|
+
* a stream, anything else as JSON. `.call<T>()` already knows T, so the test
|
|
1054
|
+
* reads one field (`result.value.value.ok`) without narrowing a union first.
|
|
1055
|
+
*/
|
|
1056
|
+
type CallValue<Returned> = Returned extends string ? TextAccessor : JsonAccessor<Returned>;
|
|
1057
|
+
/**
|
|
1058
|
+
* The result of an in-process call — the integration facet's whole surface.
|
|
1059
|
+
*
|
|
1060
|
+
* Two readings, never both: `value` is what the call RETURNED, `error` is
|
|
1061
|
+
* what it THREW. A module that refuses is specified the same way a module
|
|
1062
|
+
* that answers is, and neither needs a `try`/`catch` in the test — which is
|
|
1063
|
+
* the shape that used to make a refusal spec three times the size of the
|
|
1064
|
+
* happy one, and the shape that quietly passes when nothing throws at all.
|
|
1065
|
+
*
|
|
1066
|
+
* Both are the package's ordinary accessors, so the golden mechanism reaches
|
|
1067
|
+
* them, and `Returned` is what `.call<T>()` returned — `result.value.value` is
|
|
1068
|
+
* that type, not `unknown`, so a one-field reading needs no golden file: `toMatch('<name>.json'|'<name>.txt')`, the `{{token}}` grammar,
|
|
1069
|
+
* `{ frozen }` and `TEST_UPDATE=1` all work here exactly as on an HTTP
|
|
1070
|
+
* response or a command's stdout.
|
|
1071
|
+
*/
|
|
1072
|
+
declare class CallResult<Returned = unknown> extends BaseResult {
|
|
1073
|
+
private readonly outcome;
|
|
1074
|
+
constructor(options: CallResultOptions);
|
|
1075
|
+
/**
|
|
1076
|
+
* What the call THREW, as text — the message of an `Error`, the value
|
|
1077
|
+
* itself when something else was thrown, and empty when it returned.
|
|
1078
|
+
*
|
|
1079
|
+
* Empty rather than absent on purpose: `await expect(result.error).toBeEmpty()`
|
|
1080
|
+
* is how a spec says "and it did not refuse" — awaited, because the
|
|
1081
|
+
* matcher answers a promise on every subject it takes, and a dropped one
|
|
1082
|
+
* passes the test while the failure surfaces as an unhandled rejection
|
|
1083
|
+
* (D2 refuses the bare form).
|
|
1084
|
+
*/
|
|
1085
|
+
get error(): TextAccessor;
|
|
1086
|
+
/**
|
|
1087
|
+
* What the call RETURNED. A string reads as a stream (`<name>.txt`),
|
|
1088
|
+
* anything else as JSON (`<name>.json`) — the same two subjects every
|
|
1089
|
+
* other facet's results are built from.
|
|
1090
|
+
*
|
|
1091
|
+
* A call that threw returns the empty value: what it produced is
|
|
1092
|
+
* {@link CallResult.error}.
|
|
1093
|
+
*/
|
|
1094
|
+
get value(): CallValue<Returned>;
|
|
1095
|
+
}
|
|
1096
|
+
//#endregion
|
|
938
1097
|
//#region src/specification/facets/mobile/result.d.ts
|
|
939
1098
|
/** Result from a `.open()` action — the screen as the device saw it, final state. */
|
|
940
1099
|
declare class ScreenResult extends BaseResult {
|
|
@@ -1027,6 +1186,13 @@ declare class PageResult extends BaseResult {
|
|
|
1027
1186
|
get status(): number;
|
|
1028
1187
|
/** Rendered body text as a text accessor. */
|
|
1029
1188
|
get content(): TextAccessor;
|
|
1189
|
+
/**
|
|
1190
|
+
* The ARIA snapshot of the rendered `<body>` — the page's outline, as the
|
|
1191
|
+
* accessibility tree draws it, and the one golden a rendered surface pins.
|
|
1192
|
+
* Deterministic where a screenshot is not, and the SAME dialect a component
|
|
1193
|
+
* spec asserts, so a page and the components inside it read alike.
|
|
1194
|
+
*/
|
|
1195
|
+
get tree(): TextAccessor;
|
|
1030
1196
|
/** The document title as a text accessor. */
|
|
1031
1197
|
get title(): TextAccessor;
|
|
1032
1198
|
/** Final URL after redirects. */
|
|
@@ -1144,11 +1310,23 @@ type SpecificationConfig = {
|
|
|
1144
1310
|
* facet factories auto-populate this when `dockerConfig` is present.
|
|
1145
1311
|
*/
|
|
1146
1312
|
dockerTestRunId?: string | undefined;
|
|
1313
|
+
/**
|
|
1314
|
+
* Environment applied to every run of the declared binary
|
|
1315
|
+
* (`specification.cli({ defaults })`) — under a chain's own `.env()` and
|
|
1316
|
+
* under a document's `env:`, both of which win.
|
|
1317
|
+
*/
|
|
1318
|
+
defaultEnv?: CliEnv | undefined;
|
|
1147
1319
|
/**
|
|
1148
1320
|
* Named environment SETS a spec document may name by bare word
|
|
1149
1321
|
* (`env: frozen`). Declared once per app in `specification.cli()`.
|
|
1150
1322
|
*/
|
|
1151
1323
|
envSets?: Record<string, CliEnv> | undefined;
|
|
1324
|
+
/**
|
|
1325
|
+
* When set, `.clock()` is unavailable on this runner and throws this
|
|
1326
|
+
* reason immediately (compose mode — the calendar it would pin is this
|
|
1327
|
+
* process's, and the app runs in a container).
|
|
1328
|
+
*/
|
|
1329
|
+
clockDisabledReason?: string | undefined;
|
|
1152
1330
|
/**
|
|
1153
1331
|
* When set, `.intercept()` is unavailable on this runner and throws this
|
|
1154
1332
|
* reason immediately (compose mode — MSW is in-process, CONVENTIONS I3).
|
|
@@ -1190,6 +1368,8 @@ type InterceptMethod<T> = ((contracts: ContractInput) => T) & ((request: Contrac
|
|
|
1190
1368
|
* of the declared services record that are databases.
|
|
1191
1369
|
*/
|
|
1192
1370
|
type ApiSpecification<DatabaseKey extends string = string> = {
|
|
1371
|
+
/** Pin the app's `Date` at `iso` for this chain — the calendar the app reads. */
|
|
1372
|
+
clock: (iso: string) => ApiSpecification<DatabaseKey>;
|
|
1193
1373
|
/** Set HTTP headers for the request. Multiple calls merge. */
|
|
1194
1374
|
headers: (headers: Record<string, string>) => ApiSpecification<DatabaseKey>;
|
|
1195
1375
|
/** Declare outgoing calls — a contract, a list, a composite, or an inline request + response pair. */
|
|
@@ -1214,6 +1394,8 @@ type ApiSpecification<DatabaseKey extends string = string> = {
|
|
|
1214
1394
|
* Jobs run in-process by definition (CONVENTIONS A5/A8).
|
|
1215
1395
|
*/
|
|
1216
1396
|
type JobsSpecification<DatabaseKey extends string = string> = {
|
|
1397
|
+
/** Pin the job's `Date` at `iso` for this chain — the calendar the job reads. */
|
|
1398
|
+
clock: (iso: string) => JobsSpecification<DatabaseKey>;
|
|
1217
1399
|
/** Declare outgoing calls — a contract, a list, a composite, or an inline request + response pair. */
|
|
1218
1400
|
intercept: InterceptMethod<JobsSpecification<DatabaseKey>>;
|
|
1219
1401
|
/** Queue a SQL seed file from `_seeds/` to run before the action. */
|
|
@@ -1223,6 +1405,31 @@ type JobsSpecification<DatabaseKey extends string = string> = {
|
|
|
1223
1405
|
/** Execute the named job registered via the `jobs` option and resolve with the result. */
|
|
1224
1406
|
trigger: (name: string) => Promise<BaseResult>;
|
|
1225
1407
|
};
|
|
1408
|
+
/**
|
|
1409
|
+
* The `integration` facet — the in-process chain handed out by
|
|
1410
|
+
* `specification.integration()`.
|
|
1411
|
+
*
|
|
1412
|
+
* The subject is a MODULE, not an entry: no HTTP, no binary, no page. What
|
|
1413
|
+
* makes it a spec rather than a module test is what it stands on — real
|
|
1414
|
+
* services, declared contracts, a golden — so it takes the same setups as
|
|
1415
|
+
* every other facet and ends on the one action a module has: being called.
|
|
1416
|
+
*/
|
|
1417
|
+
type IntegrationSpecification<Services extends Record<string, unknown> = Record<string, unknown>, DatabaseKey extends string = string> = {
|
|
1418
|
+
/** Pin the module's `Date` at `iso` for this chain. */
|
|
1419
|
+
clock: (iso: string) => IntegrationSpecification<Services, DatabaseKey>;
|
|
1420
|
+
/** Declare outgoing calls — a contract, a list, a composite, or an inline request + response pair. */
|
|
1421
|
+
intercept: InterceptMethod<IntegrationSpecification<Services, DatabaseKey>>;
|
|
1422
|
+
/** Queue a SQL seed file from `_seeds/` to run before the call. */
|
|
1423
|
+
seed: (file: string, options?: {
|
|
1424
|
+
database?: DatabaseKey;
|
|
1425
|
+
}) => IntegrationSpecification<Services, DatabaseKey>;
|
|
1426
|
+
/**
|
|
1427
|
+
* Call the module and resolve with what it produced. The started services
|
|
1428
|
+
* record is handed in, so the subject is constructed with the real
|
|
1429
|
+
* connection strings rather than with a double.
|
|
1430
|
+
*/
|
|
1431
|
+
call: <T>(subject: (services: Services) => Promise<T> | T) => Promise<CallResult<T>>;
|
|
1432
|
+
};
|
|
1226
1433
|
/**
|
|
1227
1434
|
* The `cli` facet — command chain entry handed out by `specification.cli()`.
|
|
1228
1435
|
* Setup methods chain; `.exec()` is the single terminal action (CONVENTIONS
|
|
@@ -1267,6 +1474,11 @@ type CliSpecification<DatabaseKey extends string = string> = {
|
|
|
1267
1474
|
* performs one raw HTTP exchange and never follows redirects.
|
|
1268
1475
|
*/
|
|
1269
1476
|
type WebsiteSpecification = {
|
|
1477
|
+
/**
|
|
1478
|
+
* Pin the PAGE's `Date` at `iso` for this chain — what the site's own
|
|
1479
|
+
* scripts read. A `.fetch()` opens no page and refuses the setup.
|
|
1480
|
+
*/
|
|
1481
|
+
clock: (iso: string) => WebsiteSpecification;
|
|
1270
1482
|
/** Set HTTP headers for the exchange (incl. User-Agent overrides). Multiple calls merge. */
|
|
1271
1483
|
headers: (headers: Record<string, string>) => WebsiteSpecification;
|
|
1272
1484
|
/**
|
|
@@ -1441,7 +1653,16 @@ declare class ContainerAccessor {
|
|
|
1441
1653
|
private containerExec;
|
|
1442
1654
|
}
|
|
1443
1655
|
//#endregion
|
|
1444
|
-
//#region src/
|
|
1656
|
+
//#region src/specification/facets/_common/result/match-options.d.ts
|
|
1657
|
+
/**
|
|
1658
|
+
* The per-call option both `toMatch` implementations honour.
|
|
1659
|
+
*
|
|
1660
|
+
* A golden is read from disk under node and through a server command from
|
|
1661
|
+
* inside a page, and BOTH builds must answer `{ frozen: true }` the same way —
|
|
1662
|
+
* so the option is declared here, where the node matchers and the page's
|
|
1663
|
+
* matchers can each reach it, rather than in the runner's own folder, which a
|
|
1664
|
+
* seam may not import (rule I1).
|
|
1665
|
+
*/
|
|
1445
1666
|
/**
|
|
1446
1667
|
* Per-call options for the fixture-file `toMatch` subjects. `frozen` opts a
|
|
1447
1668
|
* single fixture OUT of update-mode rewriting: a frozen fixture is NEVER
|
|
@@ -1456,180 +1677,875 @@ type MatchFixtureOptions = {
|
|
|
1456
1677
|
frozen?: boolean;
|
|
1457
1678
|
};
|
|
1458
1679
|
//#endregion
|
|
1459
|
-
//#region src/
|
|
1460
|
-
type OrchestratorOptions = {
|
|
1461
|
-
/**
|
|
1462
|
-
* Named infrastructure record. Keys become the database vocabulary of the
|
|
1463
|
-
* spec (`.seed()` / `.table()` `database` option) and drive the compose
|
|
1464
|
-
* binding: a handle with no explicit `composeService` links to the compose
|
|
1465
|
-
* service named exactly like its key, else the kebab-case conversion of the
|
|
1466
|
-
* key (`analyticsDb` → `analytics-db`) — see {@link resolveComposeBinding}.
|
|
1467
|
-
*/
|
|
1468
|
-
services: Record<string, ServiceHandle>;
|
|
1469
|
-
mode: 'e2e' | 'integration';
|
|
1470
|
-
/**
|
|
1471
|
-
* The project root, already resolved. REQUIRED: the constructors derive it
|
|
1472
|
-
* by walking up from the calling specification file (CONVENTIONS A9), and
|
|
1473
|
-
* the orchestrator is handed the answer. Defaulting to `process.cwd()` here
|
|
1474
|
-
* meant one silent second opinion about what the project is — the runner
|
|
1475
|
-
* resolving from the caller, this from wherever the runner happened to be
|
|
1476
|
-
* launched, which in a workspace is rarely the same directory.
|
|
1477
|
-
*/
|
|
1478
|
-
root: string;
|
|
1479
|
-
/** Compose project name — used for per-worker stack isolation. */
|
|
1480
|
-
projectName?: string;
|
|
1481
|
-
};
|
|
1680
|
+
//#region src/integrations/vitest-browser/ui.d.ts
|
|
1482
1681
|
/**
|
|
1483
|
-
*
|
|
1484
|
-
*
|
|
1485
|
-
* E2E: runs full docker compose up.
|
|
1682
|
+
* A vanilla DOM subject: a function handed the container it must fill. Its
|
|
1683
|
+
* optional return is the teardown, the way a DOM library states one.
|
|
1486
1684
|
*/
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
private resolveBindings;
|
|
1505
|
-
/**
|
|
1506
|
-
* Start declared services via testcontainers (integration mode).
|
|
1507
|
-
* Phase 1: start all containers in parallel (the slow part).
|
|
1508
|
-
* Phase 2: wire connections, healthcheck, and init sequentially (fast).
|
|
1509
|
-
*/
|
|
1510
|
-
start(): Promise<void>;
|
|
1511
|
-
/**
|
|
1512
|
-
* Stop testcontainers (integration mode).
|
|
1513
|
-
*/
|
|
1514
|
-
stop(): Promise<void>;
|
|
1515
|
-
/**
|
|
1516
|
-
* Start full docker compose stack (e2e mode).
|
|
1517
|
-
* Auto-detects infra services and creates handles for them.
|
|
1518
|
-
*/
|
|
1519
|
-
startCompose(): Promise<void>;
|
|
1520
|
-
/**
|
|
1521
|
-
* Stop docker compose stack (e2e mode).
|
|
1522
|
-
*/
|
|
1523
|
-
stopCompose(): Promise<void>;
|
|
1524
|
-
/**
|
|
1525
|
-
* Get the default database — the first declared handle that is one.
|
|
1526
|
-
*/
|
|
1527
|
-
getDatabase(): DatabasePort | null;
|
|
1528
|
-
/**
|
|
1529
|
-
* Get all database services keyed by their record key (declared services)
|
|
1530
|
-
* or compose service name (stack-detected services).
|
|
1531
|
-
*/
|
|
1532
|
-
getDatabases(): Map<string, DatabasePort>;
|
|
1533
|
-
private allHandles;
|
|
1534
|
-
/**
|
|
1535
|
-
* Get app URL from compose (e2e mode).
|
|
1536
|
-
*/
|
|
1537
|
-
getAppUrl(): null | string;
|
|
1685
|
+
type DomMount = (container: HTMLElement) => (() => void) | void;
|
|
1686
|
+
/**
|
|
1687
|
+
* The `server.commands` this seam registers, declared in the module every part
|
|
1688
|
+
* of the seam already imports.
|
|
1689
|
+
*
|
|
1690
|
+
* A `declare module` merges only as an interface, and only from a file that is
|
|
1691
|
+
* itself a module and part of the program — a lone `.d.ts` would have to be
|
|
1692
|
+
* pulled in by an empty import, and its single `export {}` is exactly what a
|
|
1693
|
+
* formatter is free to drop, turning the augmentation into an ambient
|
|
1694
|
+
* declaration that SHADOWS the module it meant to extend.
|
|
1695
|
+
*/
|
|
1696
|
+
declare module 'vitest/browser' {
|
|
1697
|
+
interface BrowserCommands {
|
|
1698
|
+
ariaTree: () => Promise<string>;
|
|
1699
|
+
goldenRead: (name: string) => Promise<null | string>;
|
|
1700
|
+
goldenWrite: (name: string, content: string) => Promise<string>;
|
|
1701
|
+
}
|
|
1538
1702
|
}
|
|
1539
1703
|
//#endregion
|
|
1540
|
-
//#region src/
|
|
1704
|
+
//#region src/integrations/vitest-browser/vitest-browser.adapter.d.ts
|
|
1705
|
+
/** What a mounted subject offers the chain, React or vanilla DOM alike. */
|
|
1706
|
+
type MountedSurface = {
|
|
1707
|
+
/** The element the subject was mounted into. */
|
|
1708
|
+
container: HTMLElement;
|
|
1709
|
+
/** Replace the rendered tree — a prop change, as a parent makes one. */
|
|
1710
|
+
rerender: (ui: ComponentUi) => Promise<void>;
|
|
1711
|
+
/** Take it off the screen, as a parent does. */
|
|
1712
|
+
unmount: () => Promise<void>;
|
|
1713
|
+
};
|
|
1714
|
+
/** The verbs a rendered surface shares with a page, plus the two a parent owns. */
|
|
1715
|
+
declare function componentVerbs(surface: MountedSurface): {
|
|
1716
|
+
check: (element: ElementRef) => Promise<void>;
|
|
1717
|
+
click: (element: ElementRef) => Promise<void>;
|
|
1718
|
+
fill: (element: ElementRef, value: string) => Promise<void>;
|
|
1719
|
+
gone: (element: ElementRef) => Promise<void>;
|
|
1720
|
+
hover: (element: ElementRef) => Promise<void>;
|
|
1721
|
+
press: (key: string) => Promise<void>;
|
|
1722
|
+
rerender: (ui: ComponentUi) => Promise<void>;
|
|
1723
|
+
see: (element: ElementRef) => Promise<void>;
|
|
1724
|
+
select: (element: ElementRef, option: string) => Promise<void>;
|
|
1725
|
+
unmount: () => Promise<void>;
|
|
1726
|
+
};
|
|
1727
|
+
//#endregion
|
|
1728
|
+
//#region src/specification/facets/component/rendered-text.d.ts
|
|
1541
1729
|
/**
|
|
1542
|
-
*
|
|
1543
|
-
*
|
|
1544
|
-
*
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1730
|
+
* A stream captured INSIDE the page.
|
|
1731
|
+
*
|
|
1732
|
+
* Structurally identical to any other {@link TextAccessor} — the same
|
|
1733
|
+
* `{{token}}` grammar, the same ANSI stripping, the same `.grep()` — and
|
|
1734
|
+
* marked so the matcher augmentation can say the one thing that differs: its
|
|
1735
|
+
* golden lives on the server, so `toMatch` is awaited here where it is
|
|
1736
|
+
* synchronous everywhere else.
|
|
1737
|
+
*/
|
|
1738
|
+
declare class RenderedText extends TextAccessor {
|
|
1739
|
+
/** The marker the `toMatch` overload reads. Never asserted on directly. */
|
|
1740
|
+
readonly capturedInPage: true;
|
|
1741
|
+
}
|
|
1549
1742
|
//#endregion
|
|
1550
|
-
//#region src/specification/facets/
|
|
1551
|
-
/**
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1743
|
+
//#region src/specification/facets/component/component.result.d.ts
|
|
1744
|
+
/**
|
|
1745
|
+
* What a render resolves to — the component as the page saw it, after the
|
|
1746
|
+
* scenario ran.
|
|
1747
|
+
*
|
|
1748
|
+
* Five accessors and no more: the ARIA outline a golden pins, the two
|
|
1749
|
+
* renderings a probe reads, and the two streams a spec wants silent. Nothing
|
|
1750
|
+
* here reaches back into React — a spec that needs the component's internals
|
|
1751
|
+
* is asserting the implementation, not the surface a user meets.
|
|
1752
|
+
*/
|
|
1753
|
+
declare class RenderResult {
|
|
1754
|
+
/** The browser console as a stream — one `[type] text` line per message. */
|
|
1755
|
+
readonly console: RenderedText;
|
|
1756
|
+
/** Rendered text of the whole document body (`innerText`). */
|
|
1757
|
+
readonly content: RenderedText;
|
|
1758
|
+
/** Console messages of type `error` only, plus any uncaught page error. */
|
|
1759
|
+
readonly errors: RenderedText;
|
|
1760
|
+
/** The mounted markup, serialised — the escape hatch for a class or an attribute. */
|
|
1761
|
+
readonly html: RenderedText;
|
|
1762
|
+
/** The ARIA snapshot of the rendered body — the outline a golden pins. */
|
|
1763
|
+
readonly tree: RenderedText;
|
|
1764
|
+
constructor(streams: {
|
|
1765
|
+
console: string;
|
|
1766
|
+
content: string;
|
|
1767
|
+
errors: string;
|
|
1768
|
+
html: string;
|
|
1769
|
+
tree: string;
|
|
1770
|
+
});
|
|
1771
|
+
}
|
|
1772
|
+
//#endregion
|
|
1773
|
+
//#region src/specification/facets/component/component.types.d.ts
|
|
1774
|
+
/**
|
|
1775
|
+
* The component facet's shape, stated where BOTH runtimes can read it.
|
|
1776
|
+
*
|
|
1777
|
+
* The page gets the working chain; node gets a stub under the same name and
|
|
1778
|
+
* the same type, so a component test that ran in the wrong project fails on
|
|
1779
|
+
* the line that rendered rather than on a missing export. The published types
|
|
1780
|
+
* are the node build's, so this file is the one description of the facet.
|
|
1781
|
+
*/
|
|
1782
|
+
/** The interaction vocabulary a render scenario is handed — the When. */
|
|
1783
|
+
type ComponentVisitor = ReturnType<typeof componentVerbs>;
|
|
1784
|
+
/** The behaviour of a render; assertions stay in the Then, on the result (W1). */
|
|
1785
|
+
type ComponentScenario = (visitor: ComponentVisitor) => Promise<void>;
|
|
1786
|
+
/**
|
|
1787
|
+
* What `.render()` mounts. A React tree is an element (`<PostTable />`); a
|
|
1788
|
+
* vanilla DOM subject is the FUNCTION that fills a container, which is how a
|
|
1789
|
+
* DOM-only module is called in production too.
|
|
1790
|
+
*/
|
|
1791
|
+
type RenderSubject = ComponentUi | DomMount;
|
|
1792
|
+
/**
|
|
1793
|
+
* The chain. Every setup returns a new one, so the handle a spec imports never
|
|
1794
|
+
* carries the previous test's contracts, wrapper or clock.
|
|
1795
|
+
*/
|
|
1796
|
+
type ComponentChain = {
|
|
1797
|
+
/** Pin the page's `Date` for this render — the clock the component reads. */
|
|
1798
|
+
clock: (iso: string) => ComponentChain;
|
|
1566
1799
|
/**
|
|
1567
|
-
*
|
|
1568
|
-
*
|
|
1569
|
-
*
|
|
1570
|
-
* `docker/compose.test.yaml`, else the first containing `package.json`
|
|
1571
|
-
* (CONVENTIONS A9).
|
|
1800
|
+
* Declare what the network replies — the same contracts, the same queue
|
|
1801
|
+
* and the same strictness as every other facet (D7), served by msw's
|
|
1802
|
+
* worker instead of its node interceptor.
|
|
1572
1803
|
*/
|
|
1573
|
-
|
|
1804
|
+
intercept: {
|
|
1805
|
+
(contracts: ContractInput): ComponentChain;
|
|
1806
|
+
(request: ContractRequest, response: ContractResponder | ContractResponse): ComponentChain;
|
|
1807
|
+
};
|
|
1808
|
+
/** Mount the subject, run the scenario, and capture what the page shows. */
|
|
1809
|
+
render: (subject: RenderSubject, scenario?: ComponentScenario) => Promise<RenderResult>;
|
|
1574
1810
|
/**
|
|
1575
|
-
* The
|
|
1576
|
-
*
|
|
1577
|
-
*
|
|
1811
|
+
* The page size THIS render gets — the Given of a component that reads
|
|
1812
|
+
* `matchMedia` or a container query. The project's size is restored when
|
|
1813
|
+
* the test ends, so a narrow render never leaks into the next one.
|
|
1578
1814
|
*/
|
|
1579
|
-
|
|
1815
|
+
viewport: (size: {
|
|
1816
|
+
height: number;
|
|
1817
|
+
width: number;
|
|
1818
|
+
}) => ComponentChain;
|
|
1580
1819
|
/**
|
|
1581
|
-
*
|
|
1582
|
-
*
|
|
1583
|
-
*
|
|
1584
|
-
* its key, else the kebab-case conversion of the key (CONVENTIONS A6).
|
|
1820
|
+
* Wrap every render of this chain — the test-local Given a router stub or
|
|
1821
|
+
* a `<StrictMode>` is. The project's own `wrap` stays outermost: it is the
|
|
1822
|
+
* app's frame, and one test does not get to sit outside it.
|
|
1585
1823
|
*/
|
|
1586
|
-
|
|
1824
|
+
wrap: (wrapper: (ui: ComponentUi) => ComponentUi) => ComponentChain;
|
|
1587
1825
|
};
|
|
1826
|
+
//#endregion
|
|
1827
|
+
//#region src/specification/facets/website/elements.d.ts
|
|
1588
1828
|
/**
|
|
1589
|
-
* The
|
|
1590
|
-
*
|
|
1829
|
+
* The element vocabulary — user-facing descriptors for visit scenarios.
|
|
1830
|
+
* Reads like English (`click(link('Articles'))`), translates to
|
|
1831
|
+
* accessibility-first locators in the browser integration. CSS/XPath is
|
|
1832
|
+
* deliberately not expressible; `testId()` is the single escape hatch.
|
|
1591
1833
|
*
|
|
1592
|
-
*
|
|
1834
|
+
* A descriptor must designate exactly ONE element (CONVENTIONS W3). Two knobs
|
|
1835
|
+
* narrow it, in this order of preference:
|
|
1836
|
+
*
|
|
1837
|
+
* 1. `within(scope, target)` — search inside a landmark, the way a person
|
|
1838
|
+
* would say "the Articles link *in the nav*";
|
|
1839
|
+
* 2. `{ exact: true }` — match the accessible name whole rather than as a
|
|
1840
|
+
* substring, when two names genuinely overlap.
|
|
1593
1841
|
*/
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
/** Stop all infrastructure started by this specification. */
|
|
1597
|
-
cleanup: () => Promise<void>;
|
|
1842
|
+
/** Options accepted by every named descriptor. */
|
|
1843
|
+
type ElementOptions = {
|
|
1598
1844
|
/**
|
|
1599
|
-
*
|
|
1600
|
-
*
|
|
1845
|
+
* Match the accessible name as a whole string. Default is substring —
|
|
1846
|
+
* `link('Articles')` also matches "Read Articles".
|
|
1601
1847
|
*/
|
|
1602
|
-
|
|
1603
|
-
/** The orchestrator managing the test infrastructure lifecycle. */
|
|
1604
|
-
orchestrator: Orchestrator;
|
|
1848
|
+
exact?: boolean;
|
|
1605
1849
|
};
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1850
|
+
/** A user-facing element factory — what every name in this module is. */
|
|
1851
|
+
type ElementFactory = (name: string, options?: ElementOptions) => ElementRef;
|
|
1852
|
+
/** A button (or element with the button role), by accessible name. */
|
|
1853
|
+
declare const button: ElementFactory;
|
|
1854
|
+
/** A form field, by label. */
|
|
1855
|
+
declare const field: ElementFactory;
|
|
1856
|
+
/** A heading, by accessible name. */
|
|
1857
|
+
declare const heading: ElementFactory;
|
|
1858
|
+
/** A link, by accessible name. */
|
|
1859
|
+
declare const link: ElementFactory;
|
|
1860
|
+
/** An element containing the given text. */
|
|
1861
|
+
declare const content: ElementFactory;
|
|
1862
|
+
/** The escape hatch: an element by `data-testid`. Prefer user-facing elements. */
|
|
1863
|
+
declare const testId: (id: string) => ElementRef;
|
|
1864
|
+
/** A landmark factory — the optionally-named containers `within()` accepts. */
|
|
1865
|
+
type LandmarkFactory = (name?: string, options?: ElementOptions) => ElementRef;
|
|
1866
|
+
/** The `banner` landmark — the page header. */
|
|
1867
|
+
declare const banner: LandmarkFactory;
|
|
1868
|
+
/** The `complementary` landmark — an `<aside>`, a sidebar. */
|
|
1869
|
+
declare const complementary: LandmarkFactory;
|
|
1870
|
+
/** The `contentinfo` landmark — the page footer. */
|
|
1871
|
+
declare const contentinfo: LandmarkFactory;
|
|
1872
|
+
/** The `form` landmark — a form carrying an accessible name. */
|
|
1873
|
+
declare const form: LandmarkFactory;
|
|
1874
|
+
/** The `main` landmark — the primary content of the document. */
|
|
1875
|
+
declare const main: LandmarkFactory;
|
|
1876
|
+
/** The `navigation` landmark — a `<nav>`. Name it when a page has several. */
|
|
1877
|
+
declare const navigation: LandmarkFactory;
|
|
1878
|
+
/** The `region` landmark — a `<section>` carrying an accessible name. */
|
|
1879
|
+
declare const region: LandmarkFactory;
|
|
1880
|
+
/** The `search` landmark. */
|
|
1881
|
+
declare const search: LandmarkFactory;
|
|
1882
|
+
/**
|
|
1883
|
+
* Restrict a descriptor to the inside of another — the answer to ambiguity,
|
|
1884
|
+
* and the one the framework prefers over a test id.
|
|
1885
|
+
*
|
|
1886
|
+
* click(within(navigation(), link('Articles')))
|
|
1887
|
+
*
|
|
1888
|
+
* Composes outside-in: the scope may itself be scoped, so a deeply nested
|
|
1889
|
+
* target reads `within(main(), within(region('Series'), link('Part 2')))`.
|
|
1890
|
+
* Any descriptor works as a scope, including `testId()` when a container has
|
|
1891
|
+
* no landmark role to stand on.
|
|
1892
|
+
*/
|
|
1893
|
+
declare const within: (scope: ElementRef, target: ElementRef) => ElementRef;
|
|
1894
|
+
/** A modal or non-modal `<dialog>`, by accessible name. A closed one is absent. */
|
|
1895
|
+
declare const dialog: LandmarkFactory;
|
|
1896
|
+
/** A live region announcing a result — `role="status"`, an `<output>`. */
|
|
1897
|
+
declare const status: LandmarkFactory;
|
|
1898
|
+
/** A table, by its caption or accessible name. */
|
|
1899
|
+
declare const table: LandmarkFactory;
|
|
1900
|
+
/** A row of a table or grid, by the text of its cells. */
|
|
1901
|
+
declare const row: LandmarkFactory;
|
|
1902
|
+
/** An item of a list, by its text. Retires the `.first()` of an unnamed `<li>`. */
|
|
1903
|
+
declare const listitem: LandmarkFactory;
|
|
1904
|
+
/**
|
|
1905
|
+
* An option of a select or a listbox, by its label — named inside the field
|
|
1906
|
+
* that holds it: `within(field('Channel'), option('LinkedIn'))`.
|
|
1907
|
+
*
|
|
1908
|
+
* Asserted by PRESENCE, not by visibility: the options of a collapsed
|
|
1909
|
+
* `<select>` are in the document and in the accessibility tree, and none of
|
|
1910
|
+
* them has a box on the screen until it is opened. `see(option('LinkedIn'))`
|
|
1911
|
+
* therefore asks "is it offered", and `see(selected(option('LinkedIn')))`
|
|
1912
|
+
* asks which one the field is on.
|
|
1913
|
+
*/
|
|
1914
|
+
declare const option: ElementFactory;
|
|
1915
|
+
/**
|
|
1916
|
+
* Where the keyboard is: `see(focused(button('Open')))` asserts the element has
|
|
1917
|
+
* focus, `gone(focused(x))` that it does not.
|
|
1918
|
+
*
|
|
1919
|
+
* A modifier rather than a descriptor of its own, because focus is a STATE of
|
|
1920
|
+
* an element the vocabulary can already name. It is a verb's business and never
|
|
1921
|
+
* a golden's: the accessibility tree carries no focus, so a tree snapshot of a
|
|
1922
|
+
* dialog that took the keyboard and one that did not are byte-identical.
|
|
1923
|
+
*/
|
|
1924
|
+
declare const focused: (element: ElementRef) => ElementRef;
|
|
1925
|
+
/**
|
|
1926
|
+
* Whether the element accepts input: `see(disabled(button('Delete')))` asserts
|
|
1927
|
+
* it refuses, `see(enabled(button('Delete')))` that it takes it.
|
|
1928
|
+
*
|
|
1929
|
+
* Modifiers rather than descriptors, for the same reason as `focused`: being
|
|
1930
|
+
* disabled is a state of an element the vocabulary already names. Unlike focus,
|
|
1931
|
+
* the ARIA tree carries it, so a golden pins a whole outline's enablement and
|
|
1932
|
+
* these name the ONE control a test is about. They also buy the direction a
|
|
1933
|
+
* behavioural substitute cannot reach — clicking a disabled control is a
|
|
1934
|
+
* timeout, never an assertion.
|
|
1935
|
+
*/
|
|
1936
|
+
declare const disabled: (element: ElementRef) => ElementRef;
|
|
1937
|
+
/** The other direction of {@link disabled} — the element takes input. */
|
|
1938
|
+
declare const enabled: (element: ElementRef) => ElementRef;
|
|
1939
|
+
/**
|
|
1940
|
+
* Which option the field is on: `see(selected(option('LinkedIn')))`, and
|
|
1941
|
+
* `gone(selected(option('X')))` for the one it left.
|
|
1942
|
+
*
|
|
1943
|
+
* A modifier for the same reason as {@link disabled}: being chosen is a state
|
|
1944
|
+
* of an element the vocabulary already names. No matcher answers for it — a
|
|
1945
|
+
* `<option>` is not a checkbox — so both adapters read the `:checked` the CSS
|
|
1946
|
+
* spec gives a selected option.
|
|
1947
|
+
*/
|
|
1948
|
+
declare const selected: (element: ElementRef) => ElementRef;
|
|
1949
|
+
/**
|
|
1950
|
+
* What the field holds: `see(valued(field('Title'), 'Launch teaser'))`, and
|
|
1951
|
+
* `gone(valued(field('Title'), '…'))` for a value it no longer holds.
|
|
1952
|
+
*
|
|
1953
|
+
* A modifier for the same reason as {@link disabled}: what a field holds is a
|
|
1954
|
+
* state of an element the vocabulary already names, and this is how the ONE
|
|
1955
|
+
* field a test is about is waited for — a golden pins a whole outline instead.
|
|
1956
|
+
* Read from the node's live value, never from the `value` ATTRIBUTE, which a
|
|
1957
|
+
* controlled input never moves.
|
|
1958
|
+
*/
|
|
1959
|
+
declare const valued: (element: ElementRef, value: string) => ElementRef;
|
|
1960
|
+
//#endregion
|
|
1961
|
+
//#region src/specification/contracts/filters.d.ts
|
|
1962
|
+
/**
|
|
1963
|
+
* A text filter on a provider request builder (`openai.chat({ user })`,
|
|
1964
|
+
* `anthropic.messages({ system })`, …).
|
|
1965
|
+
*
|
|
1966
|
+
* A **string is EXACT**. That is deliberate: a substring filter silently
|
|
1967
|
+
* cross-matches — two prompts sharing a preamble both satisfy the first
|
|
1968
|
+
* contract, and the spec goes green on the wrong reply. Looser matching is
|
|
1969
|
+
* explicit, and says which looseness it means:
|
|
1970
|
+
*
|
|
1971
|
+
* - a `RegExp` — pattern matching;
|
|
1972
|
+
* - `match.includes('…')` — containment, the code-only matcher (it never joins
|
|
1973
|
+
* the `{{token}}` file vocabulary, CONVENTIONS D4);
|
|
1974
|
+
* - any other `match.*` matcher, evaluated by the same structural engine the
|
|
1975
|
+
* assertions use.
|
|
1976
|
+
*/
|
|
1977
|
+
type TextFilter = Matcher | RegExp | string;
|
|
1978
|
+
//#endregion
|
|
1979
|
+
//#region src/integrations/anthropic/anthropic.d.ts
|
|
1980
|
+
type AnthropicMessagesFilter = {
|
|
1981
|
+
model?: TextFilter;
|
|
1982
|
+
system?: TextFilter;
|
|
1983
|
+
user?: TextFilter;
|
|
1984
|
+
tools?: string[];
|
|
1985
|
+
};
|
|
1986
|
+
/**
|
|
1987
|
+
* Anthropic API intercept helpers.
|
|
1988
|
+
*/
|
|
1989
|
+
declare const anthropic: {
|
|
1990
|
+
/**
|
|
1991
|
+
* Request: match Messages API calls, optionally routed through a
|
|
1992
|
+
* custom gateway URL. When used with a JSON fixture file, the data is
|
|
1993
|
+
* returned as-is (no wrapping) because Anthropic fixtures are typically
|
|
1994
|
+
* already in the Messages API response shape.
|
|
1995
|
+
*
|
|
1996
|
+
* @example
|
|
1997
|
+
* anthropic.messages()
|
|
1998
|
+
* anthropic.messages({ system: /classify/ })
|
|
1999
|
+
* anthropic.messages({ user: buildPrompt() }) // string = EXACT equality
|
|
2000
|
+
* anthropic.messages({ user: /classify/ }, GATEWAY)
|
|
2001
|
+
*/
|
|
2002
|
+
messages(filter?: AnthropicMessagesFilter, url?: string): ContractRequest;
|
|
2003
|
+
/** Response: wrap data in Anthropic messages format. */
|
|
2004
|
+
reply(data: unknown): ContractResponse;
|
|
2005
|
+
/** Response: return an Anthropic error. */
|
|
2006
|
+
error(status: number, message?: string): ContractResponse;
|
|
2007
|
+
/** Response: simulate a timeout. */
|
|
2008
|
+
timeout(): ContractResponse;
|
|
2009
|
+
};
|
|
2010
|
+
//#endregion
|
|
2011
|
+
//#region src/specification/contracts/http.d.ts
|
|
2012
|
+
/**
|
|
2013
|
+
* Request filters for the generic HTTP provider. Every field is a subset
|
|
2014
|
+
* constraint — a request matches when all provided fields match.
|
|
2015
|
+
*/
|
|
2016
|
+
type HttpContractFilter = {
|
|
2017
|
+
/**
|
|
2018
|
+
* Body constraint. An object is a deep SUBSET match (toMatchObject-style)
|
|
2019
|
+
* whose leaf values may be `match.*` matchers; a string is a containment
|
|
2020
|
+
* test and a RegExp a `test()` over the raw text body.
|
|
2021
|
+
*/
|
|
2022
|
+
body?: object | RegExp | string;
|
|
2023
|
+
/** Header subset. Names are case-insensitive; string = exact value, RegExp = `test()`. */
|
|
2024
|
+
headers?: Record<string, RegExp | string>;
|
|
2025
|
+
/** Query-param subset. string = exact value, RegExp = `test()`. */
|
|
2026
|
+
query?: Record<string, RegExp | string>;
|
|
2027
|
+
};
|
|
2028
|
+
/** Init options for a streamed reply. */
|
|
2029
|
+
type HttpStreamInit = {
|
|
2030
|
+
/** The `content-type` the stream is served under. Default `application/octet-stream`. */
|
|
2031
|
+
contentType?: string;
|
|
2032
|
+
/** Milliseconds between two chunks. Default 0 — every chunk at once. */
|
|
2033
|
+
delay?: number;
|
|
2034
|
+
/** Response headers, merged over the builder's own. */
|
|
2035
|
+
headers?: Record<string, string>;
|
|
2036
|
+
/** HTTP status code. Default 200. */
|
|
2037
|
+
status?: number;
|
|
2038
|
+
};
|
|
2039
|
+
/** One server-sent event, as `http.sse()` frames it. */
|
|
2040
|
+
type SseEvent = {
|
|
2041
|
+
/** The `data:` payload. An object is serialised as JSON; a string is sent as-is. */
|
|
2042
|
+
data: unknown;
|
|
2043
|
+
/** The `event:` name. Omitted for an unnamed (default `message`) event. */
|
|
2044
|
+
event?: string;
|
|
2045
|
+
/** The `id:` the client echoes as `Last-Event-ID` when it reconnects. */
|
|
2046
|
+
id?: string;
|
|
2047
|
+
/** The `retry:` hint, in milliseconds. */
|
|
2048
|
+
retry?: number;
|
|
2049
|
+
};
|
|
2050
|
+
/** Init options shared by the response builders. */
|
|
2051
|
+
type HttpResponseInit = {
|
|
2052
|
+
/** Delay in ms before responding (for timeout testing). */
|
|
2053
|
+
delay?: number;
|
|
2054
|
+
/** Response headers, merged over the builder's own. */
|
|
2055
|
+
headers?: Record<string, string>;
|
|
2056
|
+
/** HTTP status code. */
|
|
2057
|
+
status?: number;
|
|
2058
|
+
};
|
|
2059
|
+
/**
|
|
2060
|
+
* Generic HTTP contract helpers for any URL. The url is absolute (string or
|
|
2061
|
+
* RegExp), or a PATH FORM starting with `/` — `http.get('/articles/{{uuid}}')`
|
|
2062
|
+
* matches that path on ANY origin, which is what an app calling its own
|
|
2063
|
+
* backend needs. An optional {@link HttpContractFilter} narrows matching by
|
|
2064
|
+
* body, headers, or query — a request that hits the URL/method but fails the
|
|
2065
|
+
* filter counts as unmatched (strict contracts, CONVENTIONS D7).
|
|
2066
|
+
*
|
|
2067
|
+
* @example
|
|
2068
|
+
* defineContract({ request: http.get('/articles/{{uuid}}'), response: http.json(article) })
|
|
2069
|
+
* defineContract({ request: http.post(URL, { body: { user: 'alice' } }), response: http.empty() })
|
|
2070
|
+
*/
|
|
2071
|
+
declare const http: {
|
|
2072
|
+
any(url: RegExp | string, filter?: HttpContractFilter): ContractRequest;
|
|
2073
|
+
delete(url: RegExp | string, filter?: HttpContractFilter): ContractRequest;
|
|
2074
|
+
get(url: RegExp | string, filter?: HttpContractFilter): ContractRequest;
|
|
2075
|
+
patch(url: RegExp | string, filter?: HttpContractFilter): ContractRequest;
|
|
2076
|
+
post(url: RegExp | string, filter?: HttpContractFilter): ContractRequest;
|
|
2077
|
+
put(url: RegExp | string, filter?: HttpContractFilter): ContractRequest;
|
|
2078
|
+
/** Response: a body-less reply (204 by default). */
|
|
2079
|
+
empty(status?: number): ContractResponse;
|
|
2080
|
+
/** Response: an error status. Without a body, `{ error: 'HTTP <status>' }`. */
|
|
2081
|
+
error(status: number, body?: unknown): ContractResponse;
|
|
2082
|
+
/** Response: a JSON body (200 by default). */
|
|
2083
|
+
json(body: unknown, init?: HttpResponseInit): ContractResponse;
|
|
2084
|
+
/**
|
|
2085
|
+
* Response: no response at all — the request fails in transport, the way a
|
|
2086
|
+
* server that is not running fails it. `fetch` rejects; no status is served.
|
|
2087
|
+
*/
|
|
2088
|
+
unreachable(): ContractResponse;
|
|
2089
|
+
/**
|
|
2090
|
+
* Response: a body that arrives in PIECES, written in order.
|
|
2091
|
+
*
|
|
2092
|
+
* A subject that reads a response as it lands — a token-by-token render, a
|
|
2093
|
+
* progress bar, a reconnect on a half-read body — is specified by the
|
|
2094
|
+
* chunks and their order, which a single serialised body cannot state.
|
|
2095
|
+
* Honoured by all three engines: msw's server, msw's worker, and the
|
|
2096
|
+
* `node:http` stub the website and mobile facets serve from.
|
|
2097
|
+
*
|
|
2098
|
+
* @example
|
|
2099
|
+
* http.stream(['Hel', 'lo, ', 'world'], { contentType: 'text/plain', delay: 10 })
|
|
2100
|
+
*/
|
|
2101
|
+
stream(chunks: readonly string[], init?: HttpStreamInit): ContractResponse;
|
|
2102
|
+
/**
|
|
2103
|
+
* Response: a server-sent event stream — one chunk per event, framed the
|
|
2104
|
+
* way an `EventSource` reads them, served as `text/event-stream`.
|
|
2105
|
+
*
|
|
2106
|
+
* @example
|
|
2107
|
+
* http.sse([{ data: { token: 'Hel' } }, { data: { token: 'lo' } }, { event: 'done', data: '' }])
|
|
2108
|
+
*/
|
|
2109
|
+
sse(events: readonly SseEvent[], init?: Omit<HttpStreamInit, "contentType">): ContractResponse;
|
|
2110
|
+
/** Response: a text body, served as `text/plain` (200 by default). */
|
|
2111
|
+
text(body: string, init?: HttpResponseInit): ContractResponse;
|
|
2112
|
+
};
|
|
2113
|
+
//#endregion
|
|
2114
|
+
//#region src/integrations/openai/openai.d.ts
|
|
2115
|
+
type OpenAIChatFilter = {
|
|
2116
|
+
model?: TextFilter;
|
|
2117
|
+
system?: TextFilter;
|
|
2118
|
+
user?: TextFilter;
|
|
2119
|
+
tools?: string[];
|
|
2120
|
+
temperature?: number;
|
|
2121
|
+
};
|
|
2122
|
+
type OpenAIResponsesFilter = {
|
|
2123
|
+
model?: TextFilter;
|
|
2124
|
+
system?: TextFilter;
|
|
2125
|
+
user?: TextFilter;
|
|
2126
|
+
tools?: string[];
|
|
2127
|
+
};
|
|
2128
|
+
/**
|
|
2129
|
+
* OpenAI API intercept helpers.
|
|
2130
|
+
*/
|
|
2131
|
+
declare const openai: {
|
|
2132
|
+
/**
|
|
2133
|
+
* Request: match Chat Completions API calls. STRING filters mean EXACT
|
|
2134
|
+
* equality (pass the app's own prompt builder); loosen deliberately with a
|
|
2135
|
+
* RegExp or `match.includes(...)`.
|
|
2136
|
+
*
|
|
2137
|
+
* @example
|
|
2138
|
+
* openai.chat() // any chat call
|
|
2139
|
+
* openai.chat({ model: 'gpt-4o' }) // exact model
|
|
2140
|
+
* openai.chat({ system: buildPrompt() }) // exact system prompt
|
|
2141
|
+
* openai.chat({ system: /classify/ }) // pattern
|
|
2142
|
+
*/
|
|
2143
|
+
chat(filter?: OpenAIChatFilter): ContractRequest;
|
|
2144
|
+
/**
|
|
2145
|
+
* Request: match Responses API calls (AI SDK v5+) with auto-wrapping.
|
|
2146
|
+
* String filters mean EXACT equality (see {@link openai.chat}).
|
|
2147
|
+
* When used with a JSON file, the data is automatically wrapped in the
|
|
2148
|
+
* Responses API envelope.
|
|
2149
|
+
*
|
|
2150
|
+
* @param filter - Optional body filters.
|
|
2151
|
+
* @param url - Custom gateway URL (default: api.openai.com).
|
|
2152
|
+
*
|
|
2153
|
+
* @example
|
|
2154
|
+
* openai.responses({ user: /Report Ingestion/ }, GATEWAY)
|
|
2155
|
+
*/
|
|
2156
|
+
responses(filter?: OpenAIResponsesFilter, url?: string): ContractRequest;
|
|
2157
|
+
/**
|
|
2158
|
+
* Response: wrap data in Chat Completions format.
|
|
2159
|
+
*
|
|
2160
|
+
* @example
|
|
2161
|
+
* openai.reply({ categories: ['TECH'] })
|
|
2162
|
+
*/
|
|
2163
|
+
reply(data: unknown): ContractResponse;
|
|
2164
|
+
/** Response: return an OpenAI error. */
|
|
2165
|
+
error(status: number, message?: string): ContractResponse;
|
|
2166
|
+
/** Response: return malformed content. */
|
|
2167
|
+
malformed(content: string): ContractResponse;
|
|
2168
|
+
/** Response: simulate a timeout. */
|
|
2169
|
+
timeout(): ContractResponse;
|
|
2170
|
+
};
|
|
2171
|
+
//#endregion
|
|
2172
|
+
//#region src/vitest/mock-of.d.ts
|
|
2173
|
+
/** How much of the port the double answers for. */
|
|
2174
|
+
type MockOfOptions = {
|
|
2175
|
+
/**
|
|
2176
|
+
* Deep (the default): reading a member that was never stubbed hands back
|
|
2177
|
+
* another double, however far down the port nests. `false` gives the flat
|
|
2178
|
+
* proxy — every member of `T` and nothing beneath them, which is what a
|
|
2179
|
+
* port with a nested namespace wants when the test only ever touches the
|
|
2180
|
+
* first level.
|
|
2181
|
+
*/
|
|
2182
|
+
deep?: boolean;
|
|
2183
|
+
};
|
|
2184
|
+
/**
|
|
2185
|
+
* A typed double for an injected port — the fourth rung of the doubles ladder
|
|
2186
|
+
* (docs/12-conventions.md), and the only one a module test may build itself.
|
|
2187
|
+
*
|
|
2188
|
+
* The type argument is the port: `T` is constrained to `object` and has no
|
|
2189
|
+
* default, so a double is always asked for something. A call with no argument
|
|
2190
|
+
* answers for `object`, which satisfies no port and fails where it is used.
|
|
2191
|
+
*
|
|
2192
|
+
* @example
|
|
2193
|
+
* const gateway = mockOf<PaymentGateway>(); // deep
|
|
2194
|
+
* const clock = mockOf<ClockPort>({ deep: false }); // flat
|
|
2195
|
+
*/
|
|
2196
|
+
declare function mockOf<T extends object>(options: {
|
|
2197
|
+
deep: false;
|
|
2198
|
+
}): MockProxy<T>;
|
|
2199
|
+
declare function mockOf<T extends object>(options?: MockOfOptions): DeepMockProxy<T>;
|
|
2200
|
+
/** The factory's own shape — kept as a name so a consumer can annotate with it. */
|
|
2201
|
+
type MockPort = typeof mockOf;
|
|
2202
|
+
//#endregion
|
|
2203
|
+
//#region src/vitest/mock-of-date.d.ts
|
|
2204
|
+
/** Interface for freezing and resetting the global Date in tests. */
|
|
2205
|
+
type MockDatePort = {
|
|
2206
|
+
/** Restore the real Date object. */
|
|
2207
|
+
reset: () => void;
|
|
2208
|
+
/** Freeze `Date.now()` and `new Date()` to the given value. */
|
|
2209
|
+
set: (date: Date | number | string) => void;
|
|
2210
|
+
};
|
|
2211
|
+
/**
|
|
2212
|
+
* Freeze or reset the global Date for deterministic time-dependent tests.
|
|
2213
|
+
* Wraps the `mockdate` package.
|
|
2214
|
+
*/
|
|
2215
|
+
declare const mockOfDate: MockDatePort;
|
|
2216
|
+
//#endregion
|
|
2217
|
+
//#region src/vitest/clock.d.ts
|
|
2218
|
+
/**
|
|
2219
|
+
* The one time primitive.
|
|
2220
|
+
*
|
|
2221
|
+
* Time is the determinism the framework owns (CONVENTIONS D16): a test that
|
|
2222
|
+
* samples `Date.now()` and asserts on what comes back is a test that fails on
|
|
2223
|
+
* the next leap second, and a test that reaches for `vi.useFakeTimers()` owns a
|
|
2224
|
+
* teardown it will one day forget. `clock` is both answers in one name — the
|
|
2225
|
+
* instant is stated, and the scope restores itself.
|
|
2226
|
+
*
|
|
2227
|
+
* ```typescript
|
|
2228
|
+
* using _ = clock.at('2026-03-04T09:30:00Z'); // Date is pinned until the scope ends
|
|
2229
|
+
* ```
|
|
2230
|
+
*
|
|
2231
|
+
* `at()` takes the CALENDAR and leaves the scheduler alone: a UI framework's
|
|
2232
|
+
* own loop, a `setTimeout` inside the subject and every promise still resolve
|
|
2233
|
+
* on their own. `run()` takes the scheduler too, for a subject whose behaviour
|
|
2234
|
+
* IS the passage of time — a debounce, a retry backoff, a poll.
|
|
2235
|
+
*/
|
|
2236
|
+
/** A pinned clock — released when the `using` scope that declared it ends. */
|
|
2237
|
+
type PinnedClock = Disposable;
|
|
2238
|
+
declare const clock: {
|
|
2239
|
+
/**
|
|
2240
|
+
* Move the pinned clock forward by `ms` — and, under {@link clock.run},
|
|
2241
|
+
* fire every callback that came due, awaiting what each of them started.
|
|
2242
|
+
*
|
|
2243
|
+
* Only a pinned clock moves: called with no clock taken, it says so rather
|
|
2244
|
+
* than silently doing nothing to the real one.
|
|
2245
|
+
*/
|
|
2246
|
+
advance(ms: number): Promise<void>;
|
|
2247
|
+
/**
|
|
2248
|
+
* Pin `Date` at `iso` for the current scope. `Date` ONLY: the scheduler
|
|
2249
|
+
* stays real, so a subject that awaits, renders or polls keeps running.
|
|
2250
|
+
*
|
|
2251
|
+
* @example
|
|
2252
|
+
* using _ = clock.at('2026-03-04T09:30:00Z');
|
|
2253
|
+
* expect(stampedAt()).toBe('2026-03-04T09:30:00.000Z');
|
|
2254
|
+
*/
|
|
2255
|
+
at(iso: string): PinnedClock;
|
|
2256
|
+
/**
|
|
2257
|
+
* Take the scheduler as well as the calendar: `setTimeout`, `setInterval`
|
|
2258
|
+
* and their friends queue instead of firing, and {@link clock.advance} is
|
|
2259
|
+
* what makes them due. For a subject whose behaviour IS elapsed time — a
|
|
2260
|
+
* debounce, a retry backoff, a poll — where waiting for real would be the
|
|
2261
|
+
* arbitrary sleep J2 forbids.
|
|
2262
|
+
*
|
|
2263
|
+
* @example
|
|
2264
|
+
* using _ = clock.run('2026-03-04T09:30:00Z');
|
|
2265
|
+
* const pending = retrying(failsTwice);
|
|
2266
|
+
* await clock.advance(5_000);
|
|
2267
|
+
* await expect(pending).resolves.toBe('ok');
|
|
2268
|
+
*/
|
|
2269
|
+
run(iso?: string): PinnedClock;
|
|
2270
|
+
};
|
|
2271
|
+
//#endregion
|
|
2272
|
+
//#region src/specification/assertions/required.d.ts
|
|
2273
|
+
/**
|
|
2274
|
+
* The value, or a failure that says what was missing and why it mattered.
|
|
2275
|
+
*
|
|
2276
|
+
* A test reaching into a captured structure — a row, a header, the third
|
|
2277
|
+
* element of a list — meets `T | undefined` and has to do something about it.
|
|
2278
|
+
* The two things it usually does are both wrong: `!` erases the question, and
|
|
2279
|
+
* an `if (!x) throw new Error('missing')` re-states the same three lines in
|
|
2280
|
+
* every repository (the estate had two hand-written copies of exactly this).
|
|
2281
|
+
* `required` is that gesture with the reason attached, so the failure reads as
|
|
2282
|
+
* a sentence rather than as a `TypeError` two frames further down.
|
|
2283
|
+
*
|
|
2284
|
+
* @example
|
|
2285
|
+
* const id = required(result.response.body.id, 'the creation reply carries the new id');
|
|
2286
|
+
*/
|
|
2287
|
+
declare function required<T>(value: null | T | undefined, why: string): T;
|
|
2288
|
+
//#endregion
|
|
2289
|
+
//#region src/specification/assertions/wait-until.d.ts
|
|
2290
|
+
/** How long to wait, and how often to look. */
|
|
2291
|
+
type WaitUntilOptions = {
|
|
2292
|
+
/** Milliseconds between two readings. Default 50. */
|
|
2293
|
+
interval?: number;
|
|
2294
|
+
/** How long the condition has to become true. Default 5 000 ms. */
|
|
2295
|
+
timeout?: number;
|
|
2296
|
+
/** What the condition is, named in the failure. Default: the predicate's source. */
|
|
2297
|
+
why?: string;
|
|
2298
|
+
};
|
|
2299
|
+
/**
|
|
2300
|
+
* Wait until a condition holds — the sanctioned answer to "the thing I am
|
|
2301
|
+
* specifying is not ready yet".
|
|
2302
|
+
*
|
|
2303
|
+
* A spec that sleeps for a guessed duration is a spec that is slow on a fast
|
|
2304
|
+
* machine and flaky on a slow one, which is why J2 refuses `setTimeout` under
|
|
2305
|
+
* `specs/`. Every facet that drives something already waits on a CONDITION —
|
|
2306
|
+
* a served page, a banner on stdout, an element the visitor can see — and this
|
|
2307
|
+
* is that primitive where no facet owns the waiting: a background write to
|
|
2308
|
+
* land, a queue to drain, a file to appear.
|
|
2309
|
+
*
|
|
2310
|
+
* The failure names the condition and how long it was given, so a timeout is a
|
|
2311
|
+
* sentence rather than "expected true, got false".
|
|
2312
|
+
*
|
|
2313
|
+
* The budget is measured on `performance.now()`, not on `Date` — `clock.at()`
|
|
2314
|
+
* freezes the calendar, and a deadline read off a frozen calendar never
|
|
2315
|
+
* arrives: the poll would run until vitest killed the test with a sentence
|
|
2316
|
+
* about the file rather than about the condition.
|
|
2317
|
+
*
|
|
2318
|
+
* Not for a subject under `clock.run()`: there the scheduler is the test's, and
|
|
2319
|
+
* `clock.advance(ms)` is what makes time pass.
|
|
2320
|
+
*
|
|
2321
|
+
* @example
|
|
2322
|
+
* await waitUntil(() => outbox.length === 1, { why: 'the job wrote its one message' });
|
|
2323
|
+
*/
|
|
2324
|
+
declare function waitUntil(predicate: () => boolean | Promise<boolean>, options?: WaitUntilOptions): Promise<void>;
|
|
2325
|
+
//#endregion
|
|
2326
|
+
//#region src/surface.d.ts
|
|
2327
|
+
declare module 'vitest' {
|
|
2328
|
+
interface Assertion<T = any> {
|
|
2329
|
+
/**
|
|
2330
|
+
* Assert the subject is empty — zero rows for a table (async), an
|
|
2331
|
+
* empty stream for a text accessor (console, errors, stdout).
|
|
2332
|
+
*/
|
|
2333
|
+
toBeEmpty: T extends TableAccessor ? () => Promise<void> : T extends {
|
|
2334
|
+
readonly comparableText: string;
|
|
2335
|
+
} ? () => Promise<void> : never;
|
|
2336
|
+
/** Assert the container is running. Async — docker-backed subject. */
|
|
2337
|
+
toBeRunning: T extends ContainerAccessor ? () => Promise<void> : never;
|
|
2338
|
+
/**
|
|
2339
|
+
* On `@jterrazz/test` accessors: assert the subject matches a fixture
|
|
2340
|
+
* file under `_expected/<name>` (flat — a slash creates a subfolder;
|
|
2341
|
+
* `.http` format for `result.response`). Async for filesystem/directory
|
|
2342
|
+
* subjects (tree compare on disk), and for a subject captured INSIDE a
|
|
2343
|
+
* page (`result.tree`, `result.html`, … — the file crosses the browser
|
|
2344
|
+
* seam through a server command).
|
|
2345
|
+
*
|
|
2346
|
+
* Pass `{ frozen: true }` to opt a single fixture OUT of update-mode
|
|
2347
|
+
* rewriting: a frozen fixture is never overwritten under `TEST_UPDATE=1`
|
|
2348
|
+
* (or vitest `-u`), and a frozen mismatch/missing fixture still throws.
|
|
2349
|
+
* Use it for a deliberately-wrong fixture whose diff/error rendering is
|
|
2350
|
+
* the behaviour under test.
|
|
2351
|
+
*/
|
|
2352
|
+
toMatch: T extends DirectoryAccessor | FilesystemAccessor ? (name: RegExp | string, options?: MatchFixtureOptions) => Promise<void> : T extends {
|
|
2353
|
+
readonly capturedInPage: true;
|
|
2354
|
+
} ? (name: RegExp | string, options?: MatchFixtureOptions) => Promise<void> : T extends JsonAccessor | ResponseAccessor | TextAccessor ? (name: RegExp | string, options?: MatchFixtureOptions) => void : (expected: RegExp | string, options?: MatchFixtureOptions) => void;
|
|
2355
|
+
/**
|
|
2356
|
+
* Assert the table contains exactly the given rows for the given
|
|
2357
|
+
* columns. Cells accept `match.*` dynamic-value matchers. Async —
|
|
2358
|
+
* queries the database.
|
|
2359
|
+
*/
|
|
2360
|
+
toMatchRows: T extends TableAccessor ? (expected: {
|
|
2361
|
+
columns: string[];
|
|
2362
|
+
rows: readonly (readonly unknown[])[];
|
|
2363
|
+
}) => Promise<void> : never;
|
|
2364
|
+
}
|
|
2365
|
+
}
|
|
2366
|
+
//#endregion
|
|
2367
|
+
//#region src/specification/facets/_common/orchestrator.d.ts
|
|
2368
|
+
type OrchestratorOptions = {
|
|
2369
|
+
/**
|
|
2370
|
+
* Named infrastructure record. Keys become the database vocabulary of the
|
|
2371
|
+
* spec (`.seed()` / `.table()` `database` option) and drive the compose
|
|
2372
|
+
* binding: a handle with no explicit `composeService` links to the compose
|
|
2373
|
+
* service named exactly like its key, else the kebab-case conversion of the
|
|
2374
|
+
* key (`analyticsDb` → `analytics-db`) — see {@link resolveComposeBinding}.
|
|
2375
|
+
*/
|
|
2376
|
+
services: Record<string, ServiceHandle>;
|
|
2377
|
+
mode: 'e2e' | 'integration';
|
|
2378
|
+
/**
|
|
2379
|
+
* The project root, already resolved. REQUIRED: the constructors derive it
|
|
2380
|
+
* by walking up from the calling specification file (CONVENTIONS A9), and
|
|
2381
|
+
* the orchestrator is handed the answer. Defaulting to `process.cwd()` here
|
|
2382
|
+
* meant one silent second opinion about what the project is — the runner
|
|
2383
|
+
* resolving from the caller, this from wherever the runner happened to be
|
|
2384
|
+
* launched, which in a workspace is rarely the same directory.
|
|
2385
|
+
*/
|
|
2386
|
+
root: string;
|
|
2387
|
+
/** Compose project name — used for per-worker stack isolation. */
|
|
2388
|
+
projectName?: string;
|
|
2389
|
+
};
|
|
2390
|
+
/**
|
|
2391
|
+
* Orchestrator for test infrastructure.
|
|
2392
|
+
* Integration: starts services via testcontainers.
|
|
2393
|
+
* E2E: runs full docker compose up.
|
|
2394
|
+
*/
|
|
2395
|
+
declare class Orchestrator {
|
|
2396
|
+
private readonly services;
|
|
2397
|
+
private readonly mode;
|
|
2398
|
+
private readonly root;
|
|
2399
|
+
private readonly projectName;
|
|
2400
|
+
private running;
|
|
2401
|
+
private composeStack;
|
|
2402
|
+
private composeHandles;
|
|
2403
|
+
private started;
|
|
2404
|
+
constructor(options: OrchestratorOptions);
|
|
2405
|
+
/**
|
|
2406
|
+
* Bind each declared handle to a compose service (CONVENTIONS A6): a record
|
|
2407
|
+
* key resolves to the service named exactly like it, else the kebab-case
|
|
2408
|
+
* conversion of the key; an explicit `composeService` wins. Throws on an
|
|
2409
|
+
* ambiguous binding (both names present). Runs once compose config is known
|
|
2410
|
+
* so the two-step resolution can consult the real service list.
|
|
2411
|
+
*/
|
|
2412
|
+
private resolveBindings;
|
|
2413
|
+
/**
|
|
2414
|
+
* Start declared services via testcontainers (integration mode).
|
|
2415
|
+
* Phase 1: start all containers in parallel (the slow part).
|
|
2416
|
+
* Phase 2: wire connections, healthcheck, and init sequentially (fast).
|
|
2417
|
+
*/
|
|
2418
|
+
start(): Promise<void>;
|
|
2419
|
+
/**
|
|
2420
|
+
* Stop testcontainers (integration mode).
|
|
2421
|
+
*/
|
|
2422
|
+
stop(): Promise<void>;
|
|
2423
|
+
/**
|
|
2424
|
+
* Start full docker compose stack (e2e mode).
|
|
2425
|
+
* Auto-detects infra services and creates handles for them.
|
|
2426
|
+
*/
|
|
2427
|
+
startCompose(): Promise<void>;
|
|
2428
|
+
/**
|
|
2429
|
+
* Stop docker compose stack (e2e mode).
|
|
2430
|
+
*/
|
|
2431
|
+
stopCompose(): Promise<void>;
|
|
2432
|
+
/**
|
|
2433
|
+
* Get the default database — the first declared handle that is one.
|
|
2434
|
+
*/
|
|
2435
|
+
getDatabase(): DatabasePort | null;
|
|
2436
|
+
/**
|
|
2437
|
+
* Get all database services keyed by their record key (declared services)
|
|
2438
|
+
* or compose service name (stack-detected services).
|
|
2439
|
+
*/
|
|
2440
|
+
getDatabases(): Map<string, DatabasePort>;
|
|
2441
|
+
private allHandles;
|
|
2442
|
+
/**
|
|
2443
|
+
* Get app URL from compose (e2e mode).
|
|
2444
|
+
*/
|
|
2445
|
+
getAppUrl(): null | string;
|
|
2446
|
+
}
|
|
2447
|
+
//#endregion
|
|
2448
|
+
//#region src/specification/facets/_common/services.d.ts
|
|
2449
|
+
/**
|
|
2450
|
+
* Infrastructure services declared as a named record. Keys become the typed
|
|
2451
|
+
* vocabulary of the whole spec: the server factory receives the same record,
|
|
2452
|
+
* and `.seed()` / `.table()` target databases by key.
|
|
2453
|
+
*/
|
|
2454
|
+
type ServiceRecord = Record<string, ServiceHandle>;
|
|
2455
|
+
/** Keys of a services record whose handles are databases. */
|
|
2456
|
+
type DatabaseKeys<Services extends ServiceRecord> = { [K in keyof Services]: Services[K] extends DatabasePort ? K & string : never; }[keyof Services];
|
|
2457
|
+
//#endregion
|
|
2458
|
+
//#region src/specification/facets/api/start-api.d.ts
|
|
2459
|
+
/** Any object with a request method compatible with Hono's app.request(). */
|
|
2460
|
+
type HonoApp = {
|
|
2461
|
+
request: (path: string, init?: RequestInit) => Promise<Response> | Response;
|
|
2462
|
+
};
|
|
2463
|
+
/** Execution mode — exists ONLY on `specification.api()` (CONVENTIONS A5). */
|
|
2464
|
+
type SpecificationMode = 'compose' | 'node';
|
|
2465
|
+
/** Options for {@link startApi | specification.api}. */
|
|
2466
|
+
type ApiSpecificationOptions<Services extends ServiceRecord = ServiceRecord> = {
|
|
2467
|
+
/**
|
|
2468
|
+
* Execution mode override. Resolution: `options.mode` >
|
|
2469
|
+
* `process.env.TEST_MODE` > `'node'`. Never hardcode this in a
|
|
2470
|
+
* specification file when `server` is defined — set it per vitest
|
|
2471
|
+
* project via `env: { TEST_MODE: 'compose' }` (CONVENTIONS A5).
|
|
2472
|
+
*/
|
|
2473
|
+
mode?: SpecificationMode;
|
|
2474
|
+
/**
|
|
2475
|
+
* Project root override for compose detection and init scripts. When
|
|
2476
|
+
* absent, the root is auto-discovered by walking up from the calling
|
|
2477
|
+
* specification file to the first directory containing
|
|
2478
|
+
* `docker/compose.test.yaml`, else the first containing `package.json`
|
|
2479
|
+
* (CONVENTIONS A9).
|
|
2480
|
+
*/
|
|
2481
|
+
root?: string;
|
|
2482
|
+
/**
|
|
2483
|
+
* The app factory — receives the started services record (fully typed)
|
|
2484
|
+
* and returns the Hono app. Required in node mode, ignored in compose
|
|
2485
|
+
* mode (the app runs as a compose service there).
|
|
2486
|
+
*/
|
|
2487
|
+
server?: (services: Services) => HonoApp;
|
|
2488
|
+
/**
|
|
2489
|
+
* Named infrastructure record. Keys become the `database` vocabulary of
|
|
2490
|
+
* `.seed()` / `.table()` and drive the compose binding: a handle with no
|
|
2491
|
+
* `composeService` option links to the compose service named exactly like
|
|
2492
|
+
* its key, else the kebab-case conversion of the key (CONVENTIONS A6).
|
|
2493
|
+
*/
|
|
2494
|
+
services?: Services;
|
|
2495
|
+
};
|
|
2496
|
+
/**
|
|
2497
|
+
* The record returned by {@link startApi | specification.api}. Destructure
|
|
2498
|
+
* with the canonical names (CONVENTIONS A3):
|
|
2499
|
+
*
|
|
2500
|
+
* const { api, cleanup, docker } = await specification.api(…);
|
|
2501
|
+
*/
|
|
2502
|
+
type ApiHandle<DatabaseKey extends string = string> = {
|
|
2503
|
+
api: ApiSpecification<DatabaseKey>;
|
|
2504
|
+
/** Stop all infrastructure started by this specification. */
|
|
2505
|
+
cleanup: () => Promise<void>;
|
|
2506
|
+
/**
|
|
2507
|
+
* Read a running container by id — returns a {@link ContainerAccessor}
|
|
2508
|
+
* usable with `await expect(...).toBeRunning()` and read accessors.
|
|
2509
|
+
*/
|
|
2510
|
+
docker: (containerId: string) => ContainerAccessor;
|
|
2511
|
+
/** The orchestrator managing the test infrastructure lifecycle. */
|
|
2512
|
+
orchestrator: Orchestrator;
|
|
2513
|
+
};
|
|
2514
|
+
declare function startApi<Services extends ServiceRecord>(options: ApiSpecificationOptions<Services>): Promise<ApiHandle<DatabaseKeys<Services>>>;
|
|
2515
|
+
//#endregion
|
|
2516
|
+
//#region src/specification/facets/cli/start-cli.d.ts
|
|
2517
|
+
/** Options for {@link startCli | specification.cli}. */
|
|
2518
|
+
type CliSpecificationOptions<Services extends ServiceRecord = ServiceRecord> = {
|
|
2519
|
+
/**
|
|
2520
|
+
* Opt-in Docker awareness. When set, every spec generates a unique
|
|
2521
|
+
* test-run id, injects it into the child process env under `envVar`,
|
|
2522
|
+
* and exposes `.container(name)` accessors on the result that lazily
|
|
2523
|
+
* query Docker. Always declare results with `await using` so leaked
|
|
2524
|
+
* containers get force-removed at scope exit (CONVENTIONS B5).
|
|
2525
|
+
*/
|
|
2526
|
+
docker?: DockerSpecConfig;
|
|
2527
|
+
/**
|
|
2528
|
+
* Environment applied to EVERY run of this binary — the variables the
|
|
2529
|
+
* product needs to be deterministic at all (`TZ`, `NO_COLOR`, `LANG`,
|
|
2530
|
+
* a config home under the spec's own workdir). Stated once per app
|
|
2531
|
+
* instead of repeated on every chain and in every document; a chained
|
|
2532
|
+
* `.env()` and a document's `env:` both win over it.
|
|
2533
|
+
*/
|
|
2534
|
+
defaults?: CliEnv;
|
|
2535
|
+
/**
|
|
2536
|
+
* Named environment SETS for `<case>.spec.yaml` documents. An `env:` entry
|
|
2537
|
+
* `frozen` applies the whole `frozen` record; `$WORKDIR` expands and `null`
|
|
2538
|
+
* unsets, exactly as in `.env()`. Declared once per app so a document
|
|
2539
|
+
* states WHICH ground it stands on, not how to build it.
|
|
2540
|
+
*/
|
|
2541
|
+
env?: Record<string, CliEnv>;
|
|
2542
|
+
/**
|
|
2543
|
+
* Named servers a `<case>.spec.yaml` document may start (`serve: [mcp]`, or
|
|
2544
|
+
* `- mcp: { KEY: value }` to add env to that one). Each entry names the
|
|
2545
|
+
* shell `command`, the `ready` regex whose capture group holds the port the
|
|
2546
|
+
* server announces, the `url(port)` builder, and the `env` variable that
|
|
2547
|
+
* URL is bound to in every run's child.
|
|
2548
|
+
*/
|
|
1633
2549
|
serve?: Record<string, LiterateServeRegistration>;
|
|
1634
2550
|
/**
|
|
1635
2551
|
* Project-root override (CONVENTIONS A9) — the single meaning of `root`:
|
|
@@ -1673,6 +2589,42 @@ type CliHandle<DatabaseKey extends string = string> = {
|
|
|
1673
2589
|
};
|
|
1674
2590
|
declare function startCli<Services extends ServiceRecord>(bin: string, options?: CliSpecificationOptions<Services>): Promise<CliHandle<DatabaseKeys<Services>>>;
|
|
1675
2591
|
//#endregion
|
|
2592
|
+
//#region src/specification/facets/integration/start-integration.d.ts
|
|
2593
|
+
/** Options for {@link startIntegration | specification.integration}. */
|
|
2594
|
+
type IntegrationSpecificationOptions<Services extends ServiceRecord = ServiceRecord> = {
|
|
2595
|
+
/** Project-root override — see the `root` option of `specification.api()`. */
|
|
2596
|
+
root?: string;
|
|
2597
|
+
/**
|
|
2598
|
+
* Named infrastructure the module is specified AGAINST — a real database,
|
|
2599
|
+
* a real cache, a `process()` it talks to. Omit it for the other half of
|
|
2600
|
+
* the facet: a pure module whose oracle is a golden.
|
|
2601
|
+
*/
|
|
2602
|
+
services?: Services;
|
|
2603
|
+
};
|
|
2604
|
+
/**
|
|
2605
|
+
* The record returned by {@link startIntegration | specification.integration}.
|
|
2606
|
+
* Destructure with the canonical names (CONVENTIONS A3):
|
|
2607
|
+
*
|
|
2608
|
+
* const { integration, cleanup } = await specification.integration(…);
|
|
2609
|
+
*/
|
|
2610
|
+
type IntegrationHandle<Services extends ServiceRecord = ServiceRecord, DatabaseKey extends string = string> = {
|
|
2611
|
+
/** Stop all infrastructure started by this specification. */
|
|
2612
|
+
cleanup: () => Promise<void>;
|
|
2613
|
+
integration: IntegrationSpecification<Services, DatabaseKey>;
|
|
2614
|
+
};
|
|
2615
|
+
/**
|
|
2616
|
+
* Specify a module the way the assembled product is specified: against real
|
|
2617
|
+
* services and declared contracts, with the golden mechanism on what it
|
|
2618
|
+
* produces.
|
|
2619
|
+
*
|
|
2620
|
+
* The fork is the SUBJECT. A module alone is a module test, beside its code,
|
|
2621
|
+
* with no runner at all. A module that needs a real database, or whose oracle
|
|
2622
|
+
* is a golden file, is this — and it used to be neither: a `specs/` folder
|
|
2623
|
+
* with plain vitest, a hand-built sqlite template, a `beforeAll` that started
|
|
2624
|
+
* a container, and a `toMatchSnapshot` where the package has a golden engine.
|
|
2625
|
+
*/
|
|
2626
|
+
declare function startIntegration<Services extends ServiceRecord>(options?: IntegrationSpecificationOptions<Services>): Promise<IntegrationHandle<Services, DatabaseKeys<Services>>>;
|
|
2627
|
+
//#endregion
|
|
1676
2628
|
//#region src/specification/facets/jobs/start-jobs.d.ts
|
|
1677
2629
|
/** Options for {@link startJobs | specification.jobs}. */
|
|
1678
2630
|
type JobsSpecificationOptions<Services extends ServiceRecord = ServiceRecord> = {
|
|
@@ -1719,7 +2671,7 @@ type MobileBackendOptions = {
|
|
|
1719
2671
|
port?: number;
|
|
1720
2672
|
};
|
|
1721
2673
|
/** Options for {@link startMobile | specification.mobile}. */
|
|
1722
|
-
type MobileSpecificationOptions = {
|
|
2674
|
+
type MobileSpecificationOptions<Services extends ServiceRecord = ServiceRecord> = {
|
|
1723
2675
|
/** The app under test — terminated and relaunched by every `.open()`. */
|
|
1724
2676
|
app: {
|
|
1725
2677
|
/** Bundle id of the installed app (`com.example.app`). */
|
|
@@ -1752,6 +2704,14 @@ type MobileSpecificationOptions = {
|
|
|
1752
2704
|
* driving a DEV build states the wait its bundler's cold boot needs
|
|
1753
2705
|
* (`{ action: 45_000 }`) instead of sleeping inside its scenarios.
|
|
1754
2706
|
*/
|
|
2707
|
+
/**
|
|
2708
|
+
* Named services started with the runner and stopped with it — a
|
|
2709
|
+
* `process()` for the bundler the app loads from, a database a dev build
|
|
2710
|
+
* reads. Metro stops being a `beforeAll` the repository maintains: it is
|
|
2711
|
+
* `process({ command: 'expo start', ready: /Metro waiting on .*:(\\d+)/ })`
|
|
2712
|
+
* like every other external process the framework owns.
|
|
2713
|
+
*/
|
|
2714
|
+
services?: Services;
|
|
1755
2715
|
timeouts?: DeviceTimeouts;
|
|
1756
2716
|
};
|
|
1757
2717
|
/**
|
|
@@ -1773,13 +2733,35 @@ type MobileHandle = {
|
|
|
1773
2733
|
/** The resolved simulator UDID the specs run against. */
|
|
1774
2734
|
udid: string;
|
|
1775
2735
|
};
|
|
1776
|
-
declare function startMobile(options: MobileSpecificationOptions): Promise<MobileHandle>;
|
|
2736
|
+
declare function startMobile<Services extends ServiceRecord>(options: MobileSpecificationOptions<Services>): Promise<MobileHandle>;
|
|
1777
2737
|
//#endregion
|
|
1778
2738
|
//#region src/specification/facets/website/serve.adapter.d.ts
|
|
1779
|
-
/**
|
|
1780
|
-
|
|
1781
|
-
|
|
2739
|
+
/**
|
|
2740
|
+
* The ONE shape an external process takes — the site under test, a backend a
|
|
2741
|
+
* page talks to, a bundler a simulator loads from. Built by `process()`
|
|
2742
|
+
* (chapter 11) and accepted wherever a facet starts something outside this
|
|
2743
|
+
* process: `website({ server })`, `services: { api: process(…) }`, and the
|
|
2744
|
+
* literate `serve:` registry.
|
|
2745
|
+
*/
|
|
2746
|
+
type ProcessOptions = {
|
|
2747
|
+
/**
|
|
2748
|
+
* A one-shot command run ONCE per specification, before the process is
|
|
2749
|
+
* spawned, which must exit 0 — a build, a migration, a fixture load. Its
|
|
2750
|
+
* failure is the specification's, and it says so.
|
|
2751
|
+
*/
|
|
2752
|
+
before?: string;
|
|
2753
|
+
/** Shell command that starts the process. Receives the chosen port as `PORT`. */
|
|
1782
2754
|
command: string;
|
|
2755
|
+
/** Working directory, relative to the project root. Default: the root itself. */
|
|
2756
|
+
cwd?: string;
|
|
2757
|
+
/**
|
|
2758
|
+
* Environment for the child, on top of the runner's own. A function
|
|
2759
|
+
* receives the services already started beside this one, so a process can
|
|
2760
|
+
* be handed a sibling's connection string.
|
|
2761
|
+
*/
|
|
2762
|
+
env?: ((services: Record<string, {
|
|
2763
|
+
connectionString: string;
|
|
2764
|
+
}>) => Record<string, string>) | Record<string, string>;
|
|
1783
2765
|
/** Fixed port. Default: an OS-assigned free port, injected as `PORT`. */
|
|
1784
2766
|
port?: number;
|
|
1785
2767
|
/**
|
|
@@ -1797,8 +2779,96 @@ type ServeOptions = {
|
|
|
1797
2779
|
/** Readiness budget in milliseconds. Default 30 000. */
|
|
1798
2780
|
timeout?: number;
|
|
1799
2781
|
};
|
|
2782
|
+
/**
|
|
2783
|
+
* What `ProcessOptions` was called in 15.2, when the only process the
|
|
2784
|
+
* framework owned was a website's server.
|
|
2785
|
+
*
|
|
2786
|
+
* @deprecated Renamed to {@link ProcessOptions} in 15.3 — the shape is the
|
|
2787
|
+
* one external process every facet starts, not a website's alone. The alias
|
|
2788
|
+
* is removed in 16.0.
|
|
2789
|
+
*/
|
|
2790
|
+
type ServeOptions = ProcessOptions;
|
|
2791
|
+
//#endregion
|
|
2792
|
+
//#region src/specification/facets/_common/process.d.ts
|
|
2793
|
+
/**
|
|
2794
|
+
* An external process, declared like any other service.
|
|
2795
|
+
*
|
|
2796
|
+
* Before this there were three shapes for the same thing: `website({ server })`
|
|
2797
|
+
* took a `ServeOptions`, a literate document took a `serve:` entry, and a
|
|
2798
|
+
* project that needed a second process — an API behind the site, Metro behind
|
|
2799
|
+
* the simulator — started one by hand in a `beforeAll` and killed it in an
|
|
2800
|
+
* `afterAll` it sometimes forgot. `process()` is the one shape, and the
|
|
2801
|
+
* framework owns the lifetime.
|
|
2802
|
+
*
|
|
2803
|
+
* It sits in a `services` record beside `postgres()`, `redis()` and
|
|
2804
|
+
* `sqlite()`, so it starts after them and can be handed their connection
|
|
2805
|
+
* strings through `env`, and it is stopped with the specification.
|
|
2806
|
+
*/
|
|
2807
|
+
declare class ProcessHandle implements ServiceHandle {
|
|
2808
|
+
/** Embedded: nothing to pull, nothing to run in a container. */
|
|
2809
|
+
readonly defaultImage = "";
|
|
2810
|
+
readonly defaultPort = 0;
|
|
2811
|
+
readonly environment: Record<string, string>;
|
|
2812
|
+
readonly type = "process";
|
|
2813
|
+
composeName: null | string;
|
|
2814
|
+
connectionString: string;
|
|
2815
|
+
started: boolean;
|
|
2816
|
+
private adapter;
|
|
2817
|
+
private readonly options;
|
|
2818
|
+
constructor(options: ProcessOptions);
|
|
2819
|
+
buildConnectionString(host: string, port: number): string;
|
|
2820
|
+
createDatabaseAdapter(): DatabasePort | null;
|
|
2821
|
+
healthcheck(): Promise<void>;
|
|
2822
|
+
/**
|
|
2823
|
+
* A process is not a container, so the orchestrator never starts it: the
|
|
2824
|
+
* facet does, once its siblings are up and their connection strings are
|
|
2825
|
+
* readable ({@link startProcessServices}).
|
|
2826
|
+
*/
|
|
2827
|
+
initialize(): Promise<void>;
|
|
2828
|
+
/**
|
|
2829
|
+
* Isolation is the RUN's, not the worker's — and a run here is one
|
|
2830
|
+
* EVALUATION of the specification module. Under vitest's default
|
|
2831
|
+
* isolation that is once per test file, so two files that import the same
|
|
2832
|
+
* specification get a child each, with the id the facet minted for it.
|
|
2833
|
+
* Nothing is acquired or reset between tests: the id is what tells two
|
|
2834
|
+
* runs apart, never a label a spec samples.
|
|
2835
|
+
*/
|
|
2836
|
+
isolation(): IsolationStrategy;
|
|
2837
|
+
reset(): Promise<void>;
|
|
2838
|
+
/**
|
|
2839
|
+
* Spawn it and wait until it answers. `services` is every handle declared
|
|
2840
|
+
* beside this one, already started — what an `env` function reads.
|
|
2841
|
+
*
|
|
2842
|
+
* @internal
|
|
2843
|
+
*/
|
|
2844
|
+
spawnWith(root: string, services: Record<string, {
|
|
2845
|
+
connectionString: string;
|
|
2846
|
+
}>, runId: string): Promise<void>;
|
|
2847
|
+
/** Terminate the process group. Idempotent. */
|
|
2848
|
+
shutdown(): Promise<void>;
|
|
2849
|
+
/** What it was declared as — what a facet reads to start it in a slot of its own. */
|
|
2850
|
+
get spec(): ProcessOptions;
|
|
2851
|
+
/** The base URL it came up on — known only once it is started. */
|
|
2852
|
+
get url(): string;
|
|
2853
|
+
}
|
|
2854
|
+
/**
|
|
2855
|
+
* Declare an external process the framework owns for the life of the
|
|
2856
|
+
* specification.
|
|
2857
|
+
*
|
|
2858
|
+
* @example
|
|
2859
|
+
* const { website, cleanup } = await specification.website({
|
|
2860
|
+
* services: { api: process({ command: 'bin/server web', ready: '/health' }) },
|
|
2861
|
+
* server: ({ api }) => process({
|
|
2862
|
+
* command: 'next dev',
|
|
2863
|
+
* env: { NEXT_PUBLIC_API_URL: api.connectionString },
|
|
2864
|
+
* }),
|
|
2865
|
+
* });
|
|
2866
|
+
*/
|
|
2867
|
+
declare function processService(options: ProcessOptions): ProcessHandle;
|
|
1800
2868
|
//#endregion
|
|
1801
2869
|
//#region src/specification/facets/website/start-website.d.ts
|
|
2870
|
+
/** What `server` may be handed as, and what it resolves to. */
|
|
2871
|
+
type ServerSpec<Services extends ServiceRecord> = ((services: Services) => ProcessHandle | ProcessOptions) | ProcessHandle | ProcessOptions;
|
|
1802
2872
|
/**
|
|
1803
2873
|
* The declared stub backend behind the site under test — started before the
|
|
1804
2874
|
* server command, torn down with the runner. Its URL is injected into the
|
|
@@ -1819,7 +2889,7 @@ type WebsiteBackendOptions = {
|
|
|
1819
2889
|
* mode for the same reason: a deployed site cannot be pointed at a local
|
|
1820
2890
|
* stub.
|
|
1821
2891
|
*/
|
|
1822
|
-
type WebsiteSpecificationOptions = {
|
|
2892
|
+
type WebsiteSpecificationOptions<Services extends ServiceRecord = ServiceRecord> = {
|
|
1823
2893
|
/**
|
|
1824
2894
|
* Cross-origin request policy for visits. Default: `'block'` with a
|
|
1825
2895
|
* local `server` (deterministic — analytics and CDNs never leave the
|
|
@@ -1831,6 +2901,14 @@ type WebsiteSpecificationOptions = {
|
|
|
1831
2901
|
* `server` command. Auto-discovered from the calling file when absent.
|
|
1832
2902
|
*/
|
|
1833
2903
|
root?: string;
|
|
2904
|
+
/**
|
|
2905
|
+
* Named services started with the runner, before the site is: a database
|
|
2906
|
+
* the site reads, a `process()` backend it calls. They are started in
|
|
2907
|
+
* declaration order and stopped with the specification, and `server` may
|
|
2908
|
+
* be a function of them — which is how a site is handed the URL of the
|
|
2909
|
+
* API it was started beside.
|
|
2910
|
+
*/
|
|
2911
|
+
services?: Services;
|
|
1834
2912
|
} & ({
|
|
1835
2913
|
/**
|
|
1836
2914
|
* Declared stub backend: started BEFORE the server command, its
|
|
@@ -1839,10 +2917,11 @@ type WebsiteSpecificationOptions = {
|
|
|
1839
2917
|
*/
|
|
1840
2918
|
backend?: WebsiteBackendOptions;
|
|
1841
2919
|
/**
|
|
1842
|
-
* Start the site locally: a shell command receiving
|
|
1843
|
-
* as `PORT`, polled on `ready` (default `/`) until it
|
|
2920
|
+
* Start the site locally: a `process()` — a shell command receiving
|
|
2921
|
+
* a free port as `PORT`, polled on `ready` (default `/`) until it
|
|
2922
|
+
* answers — or a function of the started services returning one.
|
|
1844
2923
|
*/
|
|
1845
|
-
server:
|
|
2924
|
+
server: ServerSpec<Services>;
|
|
1846
2925
|
url?: never;
|
|
1847
2926
|
} | {
|
|
1848
2927
|
backend?: never;
|
|
@@ -1863,11 +2942,11 @@ type WebsiteHandle = {
|
|
|
1863
2942
|
url: string;
|
|
1864
2943
|
website: WebsiteSpecification;
|
|
1865
2944
|
};
|
|
1866
|
-
declare function startWebsite(options: WebsiteSpecificationOptions): Promise<WebsiteHandle>;
|
|
2945
|
+
declare function startWebsite<Services extends ServiceRecord>(options: WebsiteSpecificationOptions<Services>): Promise<WebsiteHandle>;
|
|
1867
2946
|
//#endregion
|
|
1868
2947
|
//#region src/specification/facets/_common/specification.d.ts
|
|
1869
2948
|
/**
|
|
1870
|
-
* The
|
|
2949
|
+
* The six specification constructors (CONVENTIONS A2) — created in a
|
|
1871
2950
|
* `*.specification.ts` file under `specs/`, destructured with canonical
|
|
1872
2951
|
* names, and cleaned up via `afterAll(cleanup)` (A1/A3/A4).
|
|
1873
2952
|
*
|
|
@@ -1890,16 +2969,21 @@ declare function startWebsite(options: WebsiteSpecificationOptions): Promise<Web
|
|
|
1890
2969
|
* export const { cli, cleanup } = await specification.cli('my-cli');
|
|
1891
2970
|
* afterAll(cleanup);
|
|
1892
2971
|
*/
|
|
1893
|
-
/** The
|
|
2972
|
+
/** The six constructors, and only six — the framework's whole entry surface. */
|
|
1894
2973
|
type Specification = {
|
|
1895
2974
|
api: typeof startApi;
|
|
1896
2975
|
cli: typeof startCli;
|
|
2976
|
+
integration: typeof startIntegration;
|
|
1897
2977
|
jobs: typeof startJobs;
|
|
1898
2978
|
mobile: typeof startMobile;
|
|
1899
2979
|
website: typeof startWebsite;
|
|
1900
2980
|
};
|
|
1901
2981
|
declare const specification: Specification;
|
|
1902
2982
|
//#endregion
|
|
2983
|
+
//#region src/specification/facets/component/component.node.d.ts
|
|
2984
|
+
/** Present, typed, and refusing — every setup and the terminal action alike. */
|
|
2985
|
+
declare const component: ComponentChain;
|
|
2986
|
+
//#endregion
|
|
1903
2987
|
//#region src/specification/literate/spec-document.d.ts
|
|
1904
2988
|
/**
|
|
1905
2989
|
* The facets a document may describe. `cli` is the only one implemented; the
|
|
@@ -1988,93 +3072,51 @@ declare function inspectContainer(id: string): unknown;
|
|
|
1988
3072
|
/** Force-remove the given container IDs in a single call. Errors are swallowed. */
|
|
1989
3073
|
declare function removeContainers(ids: string[]): void;
|
|
1990
3074
|
//#endregion
|
|
1991
|
-
//#region src/specification/
|
|
1992
|
-
/**
|
|
1993
|
-
*
|
|
1994
|
-
*
|
|
1995
|
-
*/
|
|
1996
|
-
type ContainerPort = {
|
|
1997
|
-
/** Start the container and wait until ready. */
|
|
1998
|
-
start: () => Promise<void>;
|
|
1999
|
-
/** Stop and remove the container. */
|
|
2000
|
-
stop: () => Promise<void>;
|
|
2001
|
-
/** Get the mapped host port for a container port. */
|
|
2002
|
-
getMappedPort: (containerPort: number) => number;
|
|
2003
|
-
/** Get the host to connect to. */
|
|
2004
|
-
getHost: () => string;
|
|
2005
|
-
/** Get a full connection string for this service. */
|
|
2006
|
-
getConnectionString: () => string;
|
|
2007
|
-
/** Get container logs (stdout + stderr). */
|
|
2008
|
-
getLogs: () => Promise<string>;
|
|
2009
|
-
};
|
|
2010
|
-
//#endregion
|
|
2011
|
-
//#region src/specification/facets/website/elements.d.ts
|
|
2012
|
-
/**
|
|
2013
|
-
* The element vocabulary — user-facing descriptors for visit scenarios.
|
|
2014
|
-
* Reads like English (`click(link('Articles'))`), translates to
|
|
2015
|
-
* accessibility-first locators in the browser integration. CSS/XPath is
|
|
2016
|
-
* deliberately not expressible; `testId()` is the single escape hatch.
|
|
2017
|
-
*
|
|
2018
|
-
* A descriptor must designate exactly ONE element (CONVENTIONS W3). Two knobs
|
|
2019
|
-
* narrow it, in this order of preference:
|
|
2020
|
-
*
|
|
2021
|
-
* 1. `within(scope, target)` — search inside a landmark, the way a person
|
|
2022
|
-
* would say "the Articles link *in the nav*";
|
|
2023
|
-
* 2. `{ exact: true }` — match the accessible name whole rather than as a
|
|
2024
|
-
* substring, when two names genuinely overlap.
|
|
2025
|
-
*/
|
|
2026
|
-
/** Options accepted by every named descriptor. */
|
|
2027
|
-
type ElementOptions = {
|
|
2028
|
-
/**
|
|
2029
|
-
* Match the accessible name as a whole string. Default is substring —
|
|
2030
|
-
* `link('Articles')` also matches "Read Articles".
|
|
2031
|
-
*/
|
|
2032
|
-
exact?: boolean;
|
|
2033
|
-
};
|
|
2034
|
-
/** A user-facing element factory — what every name in this module is. */
|
|
2035
|
-
type ElementFactory = (name: string, options?: ElementOptions) => ElementRef;
|
|
2036
|
-
/** A button (or element with the button role), by accessible name. */
|
|
2037
|
-
declare const button: ElementFactory;
|
|
2038
|
-
/** A form field, by label. */
|
|
2039
|
-
declare const field: ElementFactory;
|
|
2040
|
-
/** A heading, by accessible name. */
|
|
2041
|
-
declare const heading: ElementFactory;
|
|
2042
|
-
/** A link, by accessible name. */
|
|
2043
|
-
declare const link: ElementFactory;
|
|
2044
|
-
/** An element containing the given text. */
|
|
2045
|
-
declare const content: ElementFactory;
|
|
2046
|
-
/** The escape hatch: an element by `data-testid`. Prefer user-facing elements. */
|
|
2047
|
-
declare const testId: (id: string) => ElementRef;
|
|
2048
|
-
/** A landmark factory — the optionally-named containers `within()` accepts. */
|
|
2049
|
-
type LandmarkFactory = (name?: string, options?: ElementOptions) => ElementRef;
|
|
2050
|
-
/** The `banner` landmark — the page header. */
|
|
2051
|
-
declare const banner: LandmarkFactory;
|
|
2052
|
-
/** The `complementary` landmark — an `<aside>`, a sidebar. */
|
|
2053
|
-
declare const complementary: LandmarkFactory;
|
|
2054
|
-
/** The `contentinfo` landmark — the page footer. */
|
|
2055
|
-
declare const contentinfo: LandmarkFactory;
|
|
2056
|
-
/** The `form` landmark — a form carrying an accessible name. */
|
|
2057
|
-
declare const form: LandmarkFactory;
|
|
2058
|
-
/** The `main` landmark — the primary content of the document. */
|
|
2059
|
-
declare const main: LandmarkFactory;
|
|
2060
|
-
/** The `navigation` landmark — a `<nav>`. Name it when a page has several. */
|
|
2061
|
-
declare const navigation: LandmarkFactory;
|
|
2062
|
-
/** The `region` landmark — a `<section>` carrying an accessible name. */
|
|
2063
|
-
declare const region: LandmarkFactory;
|
|
2064
|
-
/** The `search` landmark. */
|
|
2065
|
-
declare const search: LandmarkFactory;
|
|
2066
|
-
/**
|
|
2067
|
-
* Restrict a descriptor to the inside of another — the answer to ambiguity,
|
|
2068
|
-
* and the one the framework prefers over a test id.
|
|
3075
|
+
//#region src/specification/facets/_common/result/text-subject.d.ts
|
|
3076
|
+
/**
|
|
3077
|
+
* Wrap an arbitrary string into a {@link TextAccessor} anchored on the calling
|
|
3078
|
+
* test's directory — the same caller-detection the builders use.
|
|
2069
3079
|
*
|
|
2070
|
-
*
|
|
3080
|
+
* The product surface of a test framework is its own error messages, checker
|
|
3081
|
+
* output, and reports; those deserve the same goldening as any other output.
|
|
3082
|
+
* `text()` makes an ad-hoc string a first-class snapshot subject:
|
|
2071
3083
|
*
|
|
2072
|
-
*
|
|
2073
|
-
*
|
|
2074
|
-
*
|
|
2075
|
-
*
|
|
3084
|
+
* ```typescript
|
|
3085
|
+
* const message = await catchMessage(() => expect(result.response).toMatch('wrong-body.http'));
|
|
3086
|
+
* expect(text(message)).toMatch('wrong-body-error.txt'); // resolves to _expected/
|
|
3087
|
+
* ```
|
|
3088
|
+
*
|
|
3089
|
+
* ANSI is stripped before every comparison (the raw form stays on `.text`),
|
|
3090
|
+
* the `{{token}}` grammar applies to the fixture, and `.grep()` composition
|
|
3091
|
+
* works exactly as on any stream accessor.
|
|
3092
|
+
*
|
|
3093
|
+
* The ANCHOR is why this sits apart from the accessor it builds: reading the
|
|
3094
|
+
* caller's frame means reading a stack and a real path, which is node's alone.
|
|
3095
|
+
* {@link TextAccessor} itself is a pure projection over a captured string, so
|
|
3096
|
+
* it loads in a page — and the browser entry builds its own `text()`, where
|
|
3097
|
+
* the golden commands resolve `_expected/` from the test's path server-side.
|
|
2076
3098
|
*/
|
|
2077
|
-
declare
|
|
3099
|
+
declare function text(value: string): TextAccessor;
|
|
3100
|
+
//#endregion
|
|
3101
|
+
//#region src/specification/ports/container.port.d.ts
|
|
3102
|
+
/**
|
|
3103
|
+
* Abstract container interface.
|
|
3104
|
+
* Represents a running service (database, cache, etc.)
|
|
3105
|
+
*/
|
|
3106
|
+
type ContainerPort = {
|
|
3107
|
+
/** Start the container and wait until ready. */
|
|
3108
|
+
start: () => Promise<void>;
|
|
3109
|
+
/** Stop and remove the container. */
|
|
3110
|
+
stop: () => Promise<void>;
|
|
3111
|
+
/** Get the mapped host port for a container port. */
|
|
3112
|
+
getMappedPort: (containerPort: number) => number;
|
|
3113
|
+
/** Get the host to connect to. */
|
|
3114
|
+
getHost: () => string;
|
|
3115
|
+
/** Get a full connection string for this service. */
|
|
3116
|
+
getConnectionString: () => string;
|
|
3117
|
+
/** Get container logs (stdout + stderr). */
|
|
3118
|
+
getLogs: () => Promise<string>;
|
|
3119
|
+
};
|
|
2078
3120
|
//#endregion
|
|
2079
3121
|
//#region src/integrations/postgres/postgres.d.ts
|
|
2080
3122
|
type PostgresOptions = {
|
|
@@ -2240,228 +3282,11 @@ declare class SqliteHandle implements DatabasePort, ServiceHandle {
|
|
|
2240
3282
|
*/
|
|
2241
3283
|
declare function sqlite(options?: SqliteOptions): SqliteHandle;
|
|
2242
3284
|
//#endregion
|
|
2243
|
-
//#region src/
|
|
2244
|
-
/**
|
|
2245
|
-
* A text filter on a provider request builder (`openai.chat({ user })`,
|
|
2246
|
-
* `anthropic.messages({ system })`, …).
|
|
2247
|
-
*
|
|
2248
|
-
* A **string is EXACT**. That is deliberate: a substring filter silently
|
|
2249
|
-
* cross-matches — two prompts sharing a preamble both satisfy the first
|
|
2250
|
-
* contract, and the spec goes green on the wrong reply. Looser matching is
|
|
2251
|
-
* explicit, and says which looseness it means:
|
|
2252
|
-
*
|
|
2253
|
-
* - a `RegExp` — pattern matching;
|
|
2254
|
-
* - `match.includes('…')` — containment, the code-only matcher (it never joins
|
|
2255
|
-
* the `{{token}}` file vocabulary, CONVENTIONS D4);
|
|
2256
|
-
* - any other `match.*` matcher, evaluated by the same structural engine the
|
|
2257
|
-
* assertions use.
|
|
2258
|
-
*/
|
|
2259
|
-
type TextFilter = Matcher | RegExp | string;
|
|
2260
|
-
//#endregion
|
|
2261
|
-
//#region src/integrations/anthropic/anthropic.d.ts
|
|
2262
|
-
type AnthropicMessagesFilter = {
|
|
2263
|
-
model?: TextFilter;
|
|
2264
|
-
system?: TextFilter;
|
|
2265
|
-
user?: TextFilter;
|
|
2266
|
-
tools?: string[];
|
|
2267
|
-
};
|
|
2268
|
-
/**
|
|
2269
|
-
* Anthropic API intercept helpers.
|
|
2270
|
-
*/
|
|
2271
|
-
declare const anthropic: {
|
|
2272
|
-
/**
|
|
2273
|
-
* Request: match Messages API calls, optionally routed through a
|
|
2274
|
-
* custom gateway URL. When used with a JSON fixture file, the data is
|
|
2275
|
-
* returned as-is (no wrapping) because Anthropic fixtures are typically
|
|
2276
|
-
* already in the Messages API response shape.
|
|
2277
|
-
*
|
|
2278
|
-
* @example
|
|
2279
|
-
* anthropic.messages()
|
|
2280
|
-
* anthropic.messages({ system: /classify/ })
|
|
2281
|
-
* anthropic.messages({ user: buildPrompt() }) // string = EXACT equality
|
|
2282
|
-
* anthropic.messages({ user: /classify/ }, GATEWAY)
|
|
2283
|
-
*/
|
|
2284
|
-
messages(filter?: AnthropicMessagesFilter, url?: string): ContractRequest;
|
|
2285
|
-
/** Response: wrap data in Anthropic messages format. */
|
|
2286
|
-
reply(data: unknown): ContractResponse;
|
|
2287
|
-
/** Response: return an Anthropic error. */
|
|
2288
|
-
error(status: number, message?: string): ContractResponse;
|
|
2289
|
-
/** Response: simulate a timeout. */
|
|
2290
|
-
timeout(): ContractResponse;
|
|
2291
|
-
};
|
|
2292
|
-
//#endregion
|
|
2293
|
-
//#region src/specification/contracts/http.d.ts
|
|
2294
|
-
/**
|
|
2295
|
-
* Request filters for the generic HTTP provider. Every field is a subset
|
|
2296
|
-
* constraint — a request matches when all provided fields match.
|
|
2297
|
-
*/
|
|
2298
|
-
type HttpContractFilter = {
|
|
2299
|
-
/**
|
|
2300
|
-
* Body constraint. An object is a deep SUBSET match (toMatchObject-style)
|
|
2301
|
-
* whose leaf values may be `match.*` matchers; a string is a containment
|
|
2302
|
-
* test and a RegExp a `test()` over the raw text body.
|
|
2303
|
-
*/
|
|
2304
|
-
body?: object | RegExp | string;
|
|
2305
|
-
/** Header subset. Names are case-insensitive; string = exact value, RegExp = `test()`. */
|
|
2306
|
-
headers?: Record<string, RegExp | string>;
|
|
2307
|
-
/** Query-param subset. string = exact value, RegExp = `test()`. */
|
|
2308
|
-
query?: Record<string, RegExp | string>;
|
|
2309
|
-
};
|
|
2310
|
-
/** Init options shared by the response builders. */
|
|
2311
|
-
type HttpResponseInit = {
|
|
2312
|
-
/** Delay in ms before responding (for timeout testing). */
|
|
2313
|
-
delay?: number;
|
|
2314
|
-
/** Response headers, merged over the builder's own. */
|
|
2315
|
-
headers?: Record<string, string>;
|
|
2316
|
-
/** HTTP status code. */
|
|
2317
|
-
status?: number;
|
|
2318
|
-
};
|
|
2319
|
-
/**
|
|
2320
|
-
* Generic HTTP contract helpers for any URL. The url is absolute (string or
|
|
2321
|
-
* RegExp), or a PATH FORM starting with `/` — `http.get('/articles/{{uuid}}')`
|
|
2322
|
-
* matches that path on ANY origin, which is what an app calling its own
|
|
2323
|
-
* backend needs. An optional {@link HttpContractFilter} narrows matching by
|
|
2324
|
-
* body, headers, or query — a request that hits the URL/method but fails the
|
|
2325
|
-
* filter counts as unmatched (strict contracts, CONVENTIONS D7).
|
|
2326
|
-
*
|
|
2327
|
-
* @example
|
|
2328
|
-
* defineContract({ request: http.get('/articles/{{uuid}}'), response: http.json(article) })
|
|
2329
|
-
* defineContract({ request: http.post(URL, { body: { user: 'alice' } }), response: http.empty() })
|
|
2330
|
-
*/
|
|
2331
|
-
declare const http: {
|
|
2332
|
-
any(url: RegExp | string, filter?: HttpContractFilter): ContractRequest;
|
|
2333
|
-
delete(url: RegExp | string, filter?: HttpContractFilter): ContractRequest;
|
|
2334
|
-
get(url: RegExp | string, filter?: HttpContractFilter): ContractRequest;
|
|
2335
|
-
patch(url: RegExp | string, filter?: HttpContractFilter): ContractRequest;
|
|
2336
|
-
post(url: RegExp | string, filter?: HttpContractFilter): ContractRequest;
|
|
2337
|
-
put(url: RegExp | string, filter?: HttpContractFilter): ContractRequest;
|
|
2338
|
-
/** Response: a body-less reply (204 by default). */
|
|
2339
|
-
empty(status?: number): ContractResponse;
|
|
2340
|
-
/** Response: an error status. Without a body, `{ error: 'HTTP <status>' }`. */
|
|
2341
|
-
error(status: number, body?: unknown): ContractResponse;
|
|
2342
|
-
/** Response: a JSON body (200 by default). */
|
|
2343
|
-
json(body: unknown, init?: HttpResponseInit): ContractResponse;
|
|
2344
|
-
/** Response: a text body, served as `text/plain` (200 by default). */
|
|
2345
|
-
text(body: string, init?: HttpResponseInit): ContractResponse;
|
|
2346
|
-
};
|
|
2347
|
-
//#endregion
|
|
2348
|
-
//#region src/integrations/openai/openai.d.ts
|
|
2349
|
-
type OpenAIChatFilter = {
|
|
2350
|
-
model?: TextFilter;
|
|
2351
|
-
system?: TextFilter;
|
|
2352
|
-
user?: TextFilter;
|
|
2353
|
-
tools?: string[];
|
|
2354
|
-
temperature?: number;
|
|
2355
|
-
};
|
|
2356
|
-
type OpenAIResponsesFilter = {
|
|
2357
|
-
model?: TextFilter;
|
|
2358
|
-
system?: TextFilter;
|
|
2359
|
-
user?: TextFilter;
|
|
2360
|
-
tools?: string[];
|
|
2361
|
-
};
|
|
2362
|
-
/**
|
|
2363
|
-
* OpenAI API intercept helpers.
|
|
2364
|
-
*/
|
|
2365
|
-
declare const openai: {
|
|
2366
|
-
/**
|
|
2367
|
-
* Request: match Chat Completions API calls. STRING filters mean EXACT
|
|
2368
|
-
* equality (pass the app's own prompt builder); loosen deliberately with a
|
|
2369
|
-
* RegExp or `match.includes(...)`.
|
|
2370
|
-
*
|
|
2371
|
-
* @example
|
|
2372
|
-
* openai.chat() // any chat call
|
|
2373
|
-
* openai.chat({ model: 'gpt-4o' }) // exact model
|
|
2374
|
-
* openai.chat({ system: buildPrompt() }) // exact system prompt
|
|
2375
|
-
* openai.chat({ system: /classify/ }) // pattern
|
|
2376
|
-
*/
|
|
2377
|
-
chat(filter?: OpenAIChatFilter): ContractRequest;
|
|
2378
|
-
/**
|
|
2379
|
-
* Request: match Responses API calls (AI SDK v5+) with auto-wrapping.
|
|
2380
|
-
* String filters mean EXACT equality (see {@link openai.chat}).
|
|
2381
|
-
* When used with a JSON file, the data is automatically wrapped in the
|
|
2382
|
-
* Responses API envelope.
|
|
2383
|
-
*
|
|
2384
|
-
* @param filter - Optional body filters.
|
|
2385
|
-
* @param url - Custom gateway URL (default: api.openai.com).
|
|
2386
|
-
*
|
|
2387
|
-
* @example
|
|
2388
|
-
* openai.responses({ user: /Report Ingestion/ }, GATEWAY)
|
|
2389
|
-
*/
|
|
2390
|
-
responses(filter?: OpenAIResponsesFilter, url?: string): ContractRequest;
|
|
2391
|
-
/**
|
|
2392
|
-
* Response: wrap data in Chat Completions format.
|
|
2393
|
-
*
|
|
2394
|
-
* @example
|
|
2395
|
-
* openai.reply({ categories: ['TECH'] })
|
|
2396
|
-
*/
|
|
2397
|
-
reply(data: unknown): ContractResponse;
|
|
2398
|
-
/** Response: return an OpenAI error. */
|
|
2399
|
-
error(status: number, message?: string): ContractResponse;
|
|
2400
|
-
/** Response: return malformed content. */
|
|
2401
|
-
malformed(content: string): ContractResponse;
|
|
2402
|
-
/** Response: simulate a timeout. */
|
|
2403
|
-
timeout(): ContractResponse;
|
|
2404
|
-
};
|
|
2405
|
-
//#endregion
|
|
2406
|
-
//#region src/vitest/mock-of.d.ts
|
|
2407
|
-
/** Factory signature that creates a deep mock proxy for any interface. */
|
|
2408
|
-
type MockPort = <T>() => DeepMockProxy<T>;
|
|
2409
|
-
/**
|
|
2410
|
-
* Create a deep mock proxy for a given type.
|
|
2411
|
-
* Wraps `vitest-mock-extended`'s `mockDeep` for convenient port mocking.
|
|
2412
|
-
*/
|
|
2413
|
-
declare const mockOf: MockPort;
|
|
2414
|
-
//#endregion
|
|
2415
|
-
//#region src/vitest/mock-of-date.d.ts
|
|
2416
|
-
/** Interface for freezing and resetting the global Date in tests. */
|
|
2417
|
-
type MockDatePort = {
|
|
2418
|
-
/** Restore the real Date object. */
|
|
2419
|
-
reset: () => void;
|
|
2420
|
-
/** Freeze `Date.now()` and `new Date()` to the given value. */
|
|
2421
|
-
set: (date: Date | number | string) => void;
|
|
2422
|
-
};
|
|
3285
|
+
//#region src/index.d.ts
|
|
2423
3286
|
/**
|
|
2424
|
-
*
|
|
2425
|
-
*
|
|
3287
|
+
* The module-scope network double, on msw's node server — what a test with no
|
|
3288
|
+
* chain to hang contracts on reaches for (chapter 10).
|
|
2426
3289
|
*/
|
|
2427
|
-
declare const
|
|
2428
|
-
//#endregion
|
|
2429
|
-
//#region src/index.d.ts
|
|
2430
|
-
declare module 'vitest' {
|
|
2431
|
-
interface Assertion<T = any> {
|
|
2432
|
-
/**
|
|
2433
|
-
* Assert the subject is empty — zero rows for a table (async), an
|
|
2434
|
-
* empty stream for a text accessor (console, errors, stdout).
|
|
2435
|
-
*/
|
|
2436
|
-
toBeEmpty: T extends TableAccessor ? () => Promise<void> : T extends {
|
|
2437
|
-
readonly comparableText: string;
|
|
2438
|
-
} ? () => Promise<void> : never;
|
|
2439
|
-
/** Assert the container is running. Async — docker-backed subject. */
|
|
2440
|
-
toBeRunning: T extends ContainerAccessor ? () => Promise<void> : never;
|
|
2441
|
-
/**
|
|
2442
|
-
* On `@jterrazz/test` accessors: assert the subject matches a fixture
|
|
2443
|
-
* file under `_expected/<name>` (flat — a slash creates a subfolder;
|
|
2444
|
-
* `.http` format for `result.response`). Async for filesystem/directory
|
|
2445
|
-
* subjects (tree compare on disk). Other subjects keep vitest-native
|
|
2446
|
-
* `toMatch` semantics (string substring / regexp).
|
|
2447
|
-
*
|
|
2448
|
-
* Pass `{ frozen: true }` to opt a single fixture OUT of update-mode
|
|
2449
|
-
* rewriting: a frozen fixture is never overwritten under `TEST_UPDATE=1`
|
|
2450
|
-
* (or vitest `-u`), and a frozen mismatch/missing fixture still throws.
|
|
2451
|
-
* Use it for a deliberately-wrong fixture whose diff/error rendering is
|
|
2452
|
-
* the behaviour under test.
|
|
2453
|
-
*/
|
|
2454
|
-
toMatch: T extends DirectoryAccessor | FilesystemAccessor ? (name: RegExp | string, options?: MatchFixtureOptions) => Promise<void> : T extends JsonAccessor | ResponseAccessor | TextAccessor ? (name: RegExp | string, options?: MatchFixtureOptions) => void : (expected: RegExp | string, options?: MatchFixtureOptions) => void;
|
|
2455
|
-
/**
|
|
2456
|
-
* Assert the table contains exactly the given rows for the given
|
|
2457
|
-
* columns. Cells accept `match.*` dynamic-value matchers. Async —
|
|
2458
|
-
* queries the database.
|
|
2459
|
-
*/
|
|
2460
|
-
toMatchRows: T extends TableAccessor ? (expected: {
|
|
2461
|
-
columns: string[];
|
|
2462
|
-
rows: readonly (readonly unknown[])[];
|
|
2463
|
-
}) => Promise<void> : never;
|
|
2464
|
-
}
|
|
2465
|
-
}
|
|
3290
|
+
declare const intercept: Intercept;
|
|
2466
3291
|
//#endregion
|
|
2467
|
-
export { type ApiHandle, type ApiSpecification, type ApiSpecificationOptions, BaseResult, type BrowserConsoleMessage, type BrowserLinkElement, type BrowserMetaElement, type BrowserOpenOptions, type BrowserPage, type BrowserPort, type CaptureScope, type CliEnv, type CliHandle, type CliInput, type CliOutput, type CliPort, CliResult, type CliSpecification, type CliSpecificationOptions, ContainerAccessor, type ContainerPort, type Contract, type ContractInput, type ContractRequest, type ContractResponder, type ContractResponse, type ContractResponseValue, type Contracts, type DatabaseKeys, type DatabasePort, type DeviceOpenOptions, type DevicePort, type DeviceScreen, type DeviceTimeouts, DirectoryAccessor, type DockerSpecConfig, type ElementKind, type ElementMatch, type ElementOptions, type ElementRef, type ExecOptions, FetchResult, type FileAccessor, FilesystemAccessor, type HonoApp, type HttpContractFilter, type HttpResponseInit, HttpResult, type IsolationStrategy, type JobHandle, type JobsHandle, type JobsSpecification, type JobsSpecificationOptions, JsonAccessor, type LandmarkKind, type LiterateRunFlags, type LiterateServeRegistration, type MatchFixtureOptions, type MatchableRequest, Matcher, type MatcherKind, type MobileBackendOptions, type MobileElementKind, type MobileElementMatch, type MobileElementRef, type MobileHandle, type MobileScenario, type MobileSpecification, type MobileSpecificationOptions, type MobileVisitor, type MockDatePort, type MockPort, Orchestrator, PageResult, type PostgresOptions, type RedisOptions, ResponseAccessor, type ScreenNode, ScreenResult, type ServeOptions, type ServerPort, type ServerResponse, type ServiceHandle, type ServiceRecord, type SpecDocument, type SpecEnvToken, type SpecFileAssertion, type SpecFixture, type SpecKind, type SpecRun, type SpecServeEntry, type SpecStream, type SpecificationConfig, type SpecificationMode, type SqliteOptions, TableAccessor, TextAccessor, type TextFilter, type VisitScenario, type Visitor, type WebsiteBackendOptions, type WebsiteHandle, type WebsiteSpecification, type WebsiteSpecificationOptions, anthropic, banner, button, complementary, content, contentinfo, defineContract, defineContracts, field, findContainersByLabel, form, heading, http, inspectContainer, link, main, match, mockOf, mockOfDate, navigation, openai, postgres, redis, region, removeContainers, search, specification, sqlite, testId, text, within };
|
|
3292
|
+
export { type ApiHandle, type ApiSpecification, type ApiSpecificationOptions, BaseResult, type BrowserConsoleMessage, type BrowserLinkElement, type BrowserMetaElement, type BrowserOpenOptions, type BrowserPage, type BrowserPort, CallResult, type CaptureScope, type CliEnv, type CliHandle, type CliInput, type CliOutput, type CliPort, CliResult, type CliSpecification, type CliSpecificationOptions, type ComponentChain, type ComponentScenario, type ComponentUi, type ComponentVisitor, ContainerAccessor, type ContainerPort, type Contract, type ContractInput, type ContractRequest, type ContractResponder, type ContractResponse, type ContractResponseValue, type Contracts, type DatabaseKeys, type DatabasePort, type DeviceOpenOptions, type DevicePort, type DeviceScreen, type DeviceTimeouts, DirectoryAccessor, type DockerSpecConfig, type DomMount, type ElementKind, type ElementMatch, type ElementOptions, type ElementRef, type ExecOptions, FetchResult, type FileAccessor, FilesystemAccessor, type HonoApp, type HttpContractFilter, type HttpResponseInit, HttpResult, type HttpStreamInit, type IntegrationHandle, type IntegrationSpecification, type IntegrationSpecificationOptions, type Intercept, type InterceptScope, type IsolationStrategy, type JobHandle, type JobsHandle, type JobsSpecification, type JobsSpecificationOptions, JsonAccessor, type LandmarkKind, type LiterateRunFlags, type LiterateServeRegistration, type MatchFixtureOptions, type MatchableRequest, Matcher, type MatcherKind, type MobileBackendOptions, type MobileElementKind, type MobileElementMatch, type MobileElementRef, type MobileHandle, type MobileScenario, type MobileSpecification, type MobileSpecificationOptions, type MobileVisitor, type MockDatePort, type MockOfOptions, type MockPort, Orchestrator, PageResult, type PinnedClock, type PostgresOptions, ProcessHandle, type ProcessOptions, type RedisOptions, type RenderResult, type RenderSubject, type RenderedText, ResponseAccessor, type ScreenNode, ScreenResult, type ServeOptions, type ServerPort, type ServerResponse, type ServerSpec, type ServiceHandle, type ServiceRecord, type SpecDocument, type SpecEnvToken, type SpecFileAssertion, type SpecFixture, type SpecKind, type SpecRun, type SpecServeEntry, type SpecStream, type SpecificationConfig, type SpecificationMode, type SqliteOptions, type SseEvent, type StreamBody, TableAccessor, TextAccessor, type TextFilter, type VisitScenario, type Visitor, type WaitUntilOptions, type WebsiteBackendOptions, type WebsiteHandle, type WebsiteSpecification, type WebsiteSpecificationOptions, anthropic, banner, button, clock, complementary, component, content, contentinfo, defineContract, defineContracts, dialog, disabled, enabled, field, findContainersByLabel, focused, form, heading, http, inspectContainer, intercept, link, listitem, main, match, mockOf, mockOfDate, navigation, openai, option, postgres, processService as process, redis, region, removeContainers, required, row, search, selected, specification, sqlite, status, table, testId, text, valued, waitUntil, within };
|