@jterrazz/test 15.0.0 → 15.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/ambiguity.js +1 -1
- package/dist/appium.adapter.js +33 -24
- package/dist/artifacts.js +503 -525
- package/dist/checker.js +60 -59
- package/dist/index.d.ts +587 -660
- package/dist/index.js +191 -188
- package/dist/match.js +6 -16
- package/dist/oxlint.cjs +538 -525
- package/dist/oxlint.d.cts +7064 -15
- package/dist/oxlint.d.ts +7064 -15
- package/dist/oxlint.js +538 -525
- package/dist/playwright.adapter.js +36 -14
- package/dist/queue.js +27 -28
- package/dist/spec-document.js +19 -25
- package/dist/vitest.d.ts +6 -6
- package/dist/vitest.js +2 -2
- package/package.json +10 -7
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Scalar } from "yaml";
|
|
2
2
|
import { DeepMockProxy } from "vitest-mock-extended";
|
|
3
|
-
//#region src/
|
|
3
|
+
//#region src/specification/matching/match.d.ts
|
|
4
4
|
/**
|
|
5
5
|
* Code-side dynamic-value matchers (`match.*`) and the capture scope shared
|
|
6
6
|
* by every assertion of a single spec execution.
|
|
@@ -12,30 +12,36 @@ import { DeepMockProxy } from "vitest-mock-extended";
|
|
|
12
12
|
*/
|
|
13
13
|
/** The frozen token vocabulary (CONVENTIONS D4) plus the code-only kinds. */
|
|
14
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];
|
|
15
19
|
/**
|
|
16
20
|
* A dynamic-value matcher. Created via the {@link match} factories — never
|
|
17
21
|
* constructed directly by user code.
|
|
18
22
|
*/
|
|
19
23
|
declare class Matcher {
|
|
20
24
|
/** For kind 'includes': the substring the actual value must contain. */
|
|
21
|
-
readonly includes?: string;
|
|
25
|
+
readonly includes?: string | undefined;
|
|
22
26
|
readonly kind: MatcherKind;
|
|
23
27
|
/** For kind 'ref': the capture to assert inequality against. */
|
|
24
|
-
readonly notRef?: string;
|
|
28
|
+
readonly notRef?: string | undefined;
|
|
25
29
|
/** For kind 'ref': the capture name. */
|
|
26
|
-
readonly refName?: string;
|
|
30
|
+
readonly refName?: string | undefined;
|
|
27
31
|
/** For kind 'regex': the pattern the actual value must match. */
|
|
28
|
-
readonly regex?: RegExp;
|
|
32
|
+
readonly regex?: RegExp | undefined;
|
|
29
33
|
constructor(kind: MatcherKind, options?: {
|
|
30
|
-
includes?: string;
|
|
31
|
-
notRef?: string;
|
|
32
|
-
refName?: string;
|
|
33
|
-
regex?: RegExp;
|
|
34
|
+
includes?: string | undefined;
|
|
35
|
+
notRef?: string | undefined;
|
|
36
|
+
refName?: string | undefined;
|
|
37
|
+
regex?: RegExp | undefined;
|
|
34
38
|
});
|
|
35
39
|
/** Placeholder-style rendering used in failure diffs and serialization. */
|
|
36
40
|
toString(): string;
|
|
37
41
|
toJSON(): string;
|
|
38
42
|
}
|
|
43
|
+
/** A no-argument matcher factory — what every typed entry of `match` is. */
|
|
44
|
+
type TypedMatcher = () => Matcher;
|
|
39
45
|
/**
|
|
40
46
|
* Dynamic-value matchers for structural comparisons — the code-side mirror of
|
|
41
47
|
* the `{{token}}` fixture grammar (CONVENTIONS D4).
|
|
@@ -46,72 +52,18 @@ declare class Matcher {
|
|
|
46
52
|
* rows: [[match.uuid(), 'Alice']],
|
|
47
53
|
* });
|
|
48
54
|
*/
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
/** Matches a calendar date (`YYYY-MM-DD`). */
|
|
55
|
-
date: () => Matcher;
|
|
56
|
-
/** Matches a human duration (`12ms`, `1.5s`, `2m`, `3h`). */
|
|
57
|
-
duration: () => Matcher;
|
|
58
|
-
/** Matches an email address. */
|
|
59
|
-
email: () => Matcher;
|
|
60
|
-
/**
|
|
61
|
-
* Matches a float. In JSON contexts any finite number passes (JSON does
|
|
62
|
-
* not distinguish `42` from `42.0`); in text contexts the decimal part
|
|
63
|
-
* is required (`4.2`, never `42`).
|
|
64
|
-
*/
|
|
65
|
-
float: () => Matcher;
|
|
66
|
-
/** Matches a hexadecimal string. */
|
|
67
|
-
hex: () => Matcher;
|
|
68
|
-
/**
|
|
69
|
-
* Matches a string CONTAINING the given substring. Code-only, like
|
|
70
|
-
* {@link match.regex} — the `{{token}}` fixture vocabulary does not grow.
|
|
71
|
-
* The explicit escape hatch now that contract string filters
|
|
72
|
-
* (`openai.chat({ user })`) mean exact equality.
|
|
73
|
-
*/
|
|
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> & {
|
|
74
60
|
includes: (substring: string) => Matcher;
|
|
75
|
-
/** Matches an integer (or an integer string in text contexts). */
|
|
76
|
-
int: () => Matcher;
|
|
77
|
-
/** Matches an IPv4 address. */
|
|
78
|
-
ip: () => Matcher;
|
|
79
|
-
/** Matches an ISO-8601 timestamp string. */
|
|
80
|
-
iso8601: () => Matcher;
|
|
81
|
-
/** Matches a number (or a numeric string in text contexts). */
|
|
82
|
-
number: () => Matcher;
|
|
83
|
-
/** Matches a filesystem path (`/...` or `./...`). */
|
|
84
|
-
path: () => Matcher;
|
|
85
|
-
/** Matches a TCP/UDP port number (0-65535). */
|
|
86
|
-
port: () => Matcher;
|
|
87
|
-
/**
|
|
88
|
-
* Capture-and-compare. The first occurrence of `ref(name)` captures the
|
|
89
|
-
* actual value; every later occurrence must strictly equal the capture.
|
|
90
|
-
* `{ not: other }` additionally asserts inequality with the capture named
|
|
91
|
-
* `other`. Scope: the current spec execution (reset per chain).
|
|
92
|
-
*/
|
|
93
61
|
ref: (name: string, options?: {
|
|
94
62
|
not?: string;
|
|
95
63
|
}) => Matcher;
|
|
96
|
-
/** Matches a string against the given regular expression. */
|
|
97
64
|
regex: (regex: RegExp) => Matcher;
|
|
98
|
-
/** Matches a semantic version (`1.2.3`, `2.0.0-rc.1`). */
|
|
99
|
-
semver: () => Matcher;
|
|
100
|
-
/** Matches a git SHA (7-64 hex chars). */
|
|
101
|
-
sha: () => Matcher;
|
|
102
|
-
/** Matches any string. */
|
|
103
|
-
string: () => Matcher;
|
|
104
|
-
/** Matches a wall-clock time (`HH:MM` or `HH:MM:SS`). */
|
|
105
|
-
time: () => Matcher;
|
|
106
|
-
/** Matches a ULID. */
|
|
107
|
-
ulid: () => Matcher;
|
|
108
|
-
/** Matches an http(s) URL. */
|
|
109
|
-
url: () => Matcher;
|
|
110
|
-
/** Matches a UUID string. */
|
|
111
|
-
uuid: () => Matcher;
|
|
112
|
-
/** Matches the exact working directory of the current spec. */
|
|
113
|
-
workdir: () => Matcher;
|
|
114
65
|
};
|
|
66
|
+
declare const match: MatchFactories;
|
|
115
67
|
/**
|
|
116
68
|
* Named captures recorded by `match.ref()` / `{{type#ref}}` placeholders.
|
|
117
69
|
* One scope lives on each spec result — every assertion chained off the same
|
|
@@ -123,59 +75,14 @@ declare class CaptureScope {
|
|
|
123
75
|
* The exact working directory of the current spec, when the framework
|
|
124
76
|
* knows it (cli mode). Drives the `{{workdir}}` token / `match.workdir()`.
|
|
125
77
|
*/
|
|
126
|
-
workdir?: string;
|
|
78
|
+
workdir?: string | undefined;
|
|
127
79
|
constructor(workdir?: string);
|
|
128
80
|
get(name: string): unknown;
|
|
129
81
|
has(name: string): boolean;
|
|
130
82
|
set(name: string, value: unknown): void;
|
|
131
83
|
}
|
|
132
84
|
//#endregion
|
|
133
|
-
//#region src/
|
|
134
|
-
/**
|
|
135
|
-
* Read-only accessor for a directory produced by a spec action.
|
|
136
|
-
*
|
|
137
|
-
* Assertions go through `expect()` (async — they walk the disk):
|
|
138
|
-
* `await expect(result.directory('out')).toMatch('scaffold/out')`
|
|
139
|
-
* compares the tree against the fixture directory `_expected/<name>/`.
|
|
140
|
-
*/
|
|
141
|
-
declare class DirectoryAccessor {
|
|
142
|
-
/** @internal Ref-capture scope shared by the current spec execution. */
|
|
143
|
-
readonly captures: CaptureScope;
|
|
144
|
-
/** @internal Absolute path of the directory under assertion. */
|
|
145
|
-
readonly root: string;
|
|
146
|
-
/** @internal Test-file directory — fixture resolution root for matchers. */
|
|
147
|
-
readonly testDir: string;
|
|
148
|
-
constructor(absPath: string, testDir: string, captures?: CaptureScope);
|
|
149
|
-
/** List all files (recursively) under the directory, sorted. */
|
|
150
|
-
files(options?: {
|
|
151
|
-
ignore?: string[];
|
|
152
|
-
}): Promise<string[]>;
|
|
153
|
-
}
|
|
154
|
-
//#endregion
|
|
155
|
-
//#region src/core/specification/shared/result/filesystem.d.ts
|
|
156
|
-
/**
|
|
157
|
-
* Read-only accessor for the whole temporary working directory used by a
|
|
158
|
-
* command spec.
|
|
159
|
-
*
|
|
160
|
-
* Assertions go through `expect()` (async — they walk the disk):
|
|
161
|
-
* `await expect(result.filesystem).toMatch('scaffolded')` compares the
|
|
162
|
-
* tree against the fixture directory `_expected/<name>/`.
|
|
163
|
-
*/
|
|
164
|
-
declare class FilesystemAccessor {
|
|
165
|
-
/** @internal Ref-capture scope shared by the current spec execution. */
|
|
166
|
-
readonly captures: CaptureScope;
|
|
167
|
-
/** The absolute path of the temporary working directory. */
|
|
168
|
-
readonly cwd: string;
|
|
169
|
-
/** @internal Test-file directory — fixture resolution root for matchers. */
|
|
170
|
-
readonly testDir: string;
|
|
171
|
-
constructor(cwd: string, testDir: string, captures?: CaptureScope);
|
|
172
|
-
/** List all files (recursively) under the working directory, sorted. */
|
|
173
|
-
files(options?: {
|
|
174
|
-
ignore?: string[];
|
|
175
|
-
}): Promise<string[]>;
|
|
176
|
-
}
|
|
177
|
-
//#endregion
|
|
178
|
-
//#region src/core/specification/shared/result/json.d.ts
|
|
85
|
+
//#region src/specification/facets/_common/result/json.d.ts
|
|
179
86
|
/**
|
|
180
87
|
* Read-only accessor for a JSON payload parsed from a text stream (stdout).
|
|
181
88
|
*
|
|
@@ -205,218 +112,14 @@ declare class JsonAccessor {
|
|
|
205
112
|
get value(): unknown;
|
|
206
113
|
}
|
|
207
114
|
//#endregion
|
|
208
|
-
//#region src/
|
|
209
|
-
/**
|
|
210
|
-
* HTTP response returned by a server port, with parsed JSON body.
|
|
211
|
-
*/
|
|
212
|
-
interface ServerResponse {
|
|
213
|
-
/** HTTP status code. */
|
|
214
|
-
status: number;
|
|
215
|
-
/** Parsed JSON response body, or null if parsing failed. */
|
|
216
|
-
body: unknown;
|
|
217
|
-
/** Response headers as a flat key-value map. */
|
|
218
|
-
headers: Record<string, string>;
|
|
219
|
-
}
|
|
220
|
-
/**
|
|
221
|
-
* Abstract server interface for specification runners.
|
|
222
|
-
* Integration mode uses an in-process Hono app; E2E mode uses real HTTP via fetch.
|
|
223
|
-
*/
|
|
224
|
-
interface ServerPort {
|
|
225
|
-
/** Send an HTTP request and return the parsed response. */
|
|
226
|
-
request: (method: string, path: string, body?: unknown, headers?: Record<string, string>) => Promise<ServerResponse>;
|
|
227
|
-
}
|
|
228
|
-
//#endregion
|
|
229
|
-
//#region src/core/specification/shared/result/response.d.ts
|
|
230
|
-
/**
|
|
231
|
-
* Read-only accessor for an HTTP response.
|
|
232
|
-
*
|
|
233
|
-
* Assertions go through `expect()`:
|
|
234
|
-
* `expect(result.response).toMatch('created.http')` compares status,
|
|
235
|
-
* a subset of headers, and the JSON body against `_expected/<name>` —
|
|
236
|
-
* with `{{placeholder}}` support in all three.
|
|
237
|
-
*/
|
|
238
|
-
declare class ResponseAccessor {
|
|
239
|
-
/** The parsed JSON response body (null when parsing failed). */
|
|
240
|
-
readonly body: unknown;
|
|
241
|
-
/** @internal Ref-capture scope shared by the current spec execution. */
|
|
242
|
-
readonly captures: CaptureScope;
|
|
243
|
-
/** Response headers as a flat, lower-cased key-value map. */
|
|
244
|
-
readonly headers: Record<string, string>;
|
|
245
|
-
/** The HTTP response status code. */
|
|
246
|
-
readonly status: number;
|
|
247
|
-
/** @internal Test-file directory — fixture resolution root for matchers. */
|
|
248
|
-
readonly testDir: string;
|
|
249
|
-
constructor(response: ServerResponse, testDir: string, captures?: CaptureScope);
|
|
250
|
-
}
|
|
251
|
-
//#endregion
|
|
252
|
-
//#region src/core/ports/database.port.d.ts
|
|
253
|
-
/**
|
|
254
|
-
* Abstract database interface for specification runners.
|
|
255
|
-
* Implement this to plug in your database stack (e.g. Postgres, SQLite).
|
|
256
|
-
*/
|
|
257
|
-
interface DatabasePort {
|
|
258
|
-
/** Execute raw SQL (for seeding test data). */
|
|
259
|
-
seed: (sql: string) => Promise<void>;
|
|
260
|
-
/** Query a table and return rows as arrays of values. */
|
|
261
|
-
query: (table: string, columns: string[]) => Promise<unknown[][]>;
|
|
262
|
-
/** Reset database to clean state between tests. */
|
|
263
|
-
reset: () => Promise<void>;
|
|
264
|
-
}
|
|
265
|
-
//#endregion
|
|
266
|
-
//#region src/core/specification/shared/result/table.d.ts
|
|
267
|
-
/**
|
|
268
|
-
* Read-only accessor for a database table after a specification run.
|
|
269
|
-
*
|
|
270
|
-
* Assertions go through `expect()` (async — they query the database):
|
|
271
|
-
*
|
|
272
|
-
* await expect(result.table('users', { database: 'db' })).toMatchRows({
|
|
273
|
-
* columns: ['name'],
|
|
274
|
-
* rows: [['Alice']],
|
|
275
|
-
* });
|
|
276
|
-
* await expect(result.table('users', { database: 'db' })).toBeEmpty();
|
|
277
|
-
*/
|
|
278
|
-
declare class TableAccessor {
|
|
279
|
-
/** @internal Ref-capture scope shared by the current spec execution. */
|
|
280
|
-
readonly captures: CaptureScope;
|
|
281
|
-
/** @internal */
|
|
282
|
-
private readonly db;
|
|
283
|
-
/** The table name this accessor reads. */
|
|
284
|
-
readonly name: string;
|
|
285
|
-
constructor(name: string, db: DatabasePort, captures?: CaptureScope);
|
|
286
|
-
/** @internal Query the table — used by the toMatchRows / toBeEmpty matchers. */
|
|
287
|
-
query(columns: string[]): Promise<unknown[][]>;
|
|
288
|
-
}
|
|
289
|
-
//#endregion
|
|
290
|
-
//#region src/core/specification/shared/result/text.d.ts
|
|
291
|
-
/**
|
|
292
|
-
* Read-only accessor for a captured text handle — THE universal one: stdout,
|
|
293
|
-
* stderr, container logs, and file text all surface as a `TextAccessor`.
|
|
294
|
-
*
|
|
295
|
-
* Backed by a primitive string, exposed via {@link text}, but also provides
|
|
296
|
-
* `toString()` / `valueOf()` for string coercion, so common patterns like
|
|
297
|
-
* `String(result.stdout)` and template-literal interpolation still work.
|
|
298
|
-
*
|
|
299
|
-
* ANSI escape sequences are stripped by default before every comparison
|
|
300
|
-
* (CONVENTIONS D6) — `.text` stays raw. The runner `transform` option remains
|
|
301
|
-
* an escape hatch for application noise not covered by the `{{token}}`
|
|
302
|
-
* grammar.
|
|
303
|
-
*
|
|
304
|
-
* Text operations are **closed** over the type: `.grep(pattern)` returns a
|
|
305
|
-
* `TextAccessor` (not a bare string), preserving the `_expected/`-resolution
|
|
306
|
-
* context and the `transform`, so results are chainable
|
|
307
|
-
* (`result.stdout.grep(a).grep(b)`) and snapshot-able
|
|
308
|
-
* (`expect(result.stdout.grep('users.ts')).toMatch('block.txt')`).
|
|
309
|
-
*
|
|
310
|
-
* Assertions go through `expect()` matchers: `expect(result.stdout).toContain(...)`
|
|
311
|
-
* and `expect(result.stdout).toMatch('name.txt')` (resolved against
|
|
312
|
-
* `_expected/<name>`, flat — with `{{token}}` support, CONVENTIONS D4).
|
|
313
|
-
*/
|
|
314
|
-
declare class TextAccessor {
|
|
315
|
-
/** @internal Ref-capture scope shared by the current spec execution. */
|
|
316
|
-
readonly captures: CaptureScope;
|
|
317
|
-
/** @internal Stream label used in failure messages ("stdout" / "stderr"). */
|
|
318
|
-
readonly streamName: string;
|
|
319
|
-
/** @internal Test-file directory — fixture resolution root for matchers. */
|
|
320
|
-
readonly testDir: string;
|
|
321
|
-
/** The raw captured text (never transformed, ANSI preserved). */
|
|
322
|
-
readonly text: string;
|
|
323
|
-
/** @internal Normaliser applied before comparisons, never to fixtures. */
|
|
324
|
-
readonly transform?: (text: string) => string;
|
|
325
|
-
constructor(value: string, streamName: string, testDir: string, options?: {
|
|
326
|
-
captures?: CaptureScope;
|
|
327
|
-
transform?: (text: string) => string;
|
|
328
|
-
});
|
|
329
|
-
/** @internal The text as compared by matchers — ANSI stripped, transform applied. */
|
|
330
|
-
get comparableText(): string;
|
|
331
|
-
/**
|
|
332
|
-
* Keep only the blank-line-separated blocks of the text that contain
|
|
333
|
-
* `pattern` (how linter/compiler output is structured), returned as a new
|
|
334
|
-
* `TextAccessor` — the same subject type, so the result is chainable and
|
|
335
|
-
* snapshot-able. The scalpel for probing large tool outputs; snapshot the
|
|
336
|
-
* whole surface by default, reach for `.grep()` for targeted checks.
|
|
337
|
-
*/
|
|
338
|
-
grep(pattern: string): TextAccessor;
|
|
339
|
-
toString(): string;
|
|
340
|
-
valueOf(): string;
|
|
341
|
-
}
|
|
342
|
-
/**
|
|
343
|
-
* Wrap an arbitrary string into a {@link TextAccessor} anchored on the calling
|
|
344
|
-
* test's directory — the same caller-detection the builders use.
|
|
345
|
-
*
|
|
346
|
-
* The product surface of a test framework is its own error messages, checker
|
|
347
|
-
* output, and reports; those deserve the same goldening as any other output.
|
|
348
|
-
* `text()` makes an ad-hoc string a first-class snapshot subject:
|
|
349
|
-
*
|
|
350
|
-
* ```typescript
|
|
351
|
-
* const message = await catchMessage(() => expect(result.response).toMatch('wrong-body.http'));
|
|
352
|
-
* expect(text(message)).toMatch('wrong-body-error.txt'); // resolves to _expected/
|
|
353
|
-
* ```
|
|
354
|
-
*
|
|
355
|
-
* ANSI is stripped before every comparison (the raw form stays on `.text`),
|
|
356
|
-
* the `{{token}}` grammar applies to the fixture, and `.grep()` composition
|
|
357
|
-
* works exactly as on any stream accessor.
|
|
358
|
-
*/
|
|
359
|
-
declare function text(value: string): TextAccessor;
|
|
360
|
-
//#endregion
|
|
361
|
-
//#region src/core/ports/cli.port.d.ts
|
|
362
|
-
/**
|
|
363
|
-
* Raw output from a command execution, including exit code and captured output streams.
|
|
364
|
-
*/
|
|
365
|
-
interface CliOutput {
|
|
366
|
-
/** Process exit code (0 = success). */
|
|
367
|
-
exitCode: number;
|
|
368
|
-
/** Captured standard output. */
|
|
369
|
-
stdout: string;
|
|
370
|
-
/** Captured standard error. */
|
|
371
|
-
stderr: string;
|
|
372
|
-
}
|
|
373
|
-
/**
|
|
374
|
-
* Options for the long-running form of `.exec()` (CONVENTIONS B2). When
|
|
375
|
-
* either option is present the process is spawned and observed: it resolves
|
|
376
|
-
* as soon as `waitFor` appears in stdout/stderr, and is killed when `timeout`
|
|
377
|
-
* elapses (exit code 124).
|
|
378
|
-
*/
|
|
379
|
-
interface ExecOptions {
|
|
380
|
-
/** Resolve (exit code 0) when stdout/stderr contains this string. */
|
|
381
|
-
waitFor?: string;
|
|
382
|
-
/** Kill the process after this many milliseconds. Defaults to 10 000. */
|
|
383
|
-
timeout?: number;
|
|
384
|
-
}
|
|
385
|
-
/**
|
|
386
|
-
* Extra environment variables to set for the child process.
|
|
387
|
-
* Values are merged on top of process.env. A `null` value unsets the variable.
|
|
388
|
-
*/
|
|
389
|
-
type CliEnv = Record<string, null | string>;
|
|
390
|
-
/**
|
|
391
|
-
* Abstract command interface for specification runners.
|
|
392
|
-
* Implement this to plug in your command execution strategy.
|
|
393
|
-
*/
|
|
394
|
-
/**
|
|
395
|
-
* What one invocation is handed beyond its arguments — the two per-run inputs a
|
|
396
|
-
* spec document may state. Both are absent by default: the child gets an
|
|
397
|
-
* immediately-closed stdin (never a TTY) and the adapter's own timeout.
|
|
398
|
-
*/
|
|
399
|
-
interface CliInput {
|
|
400
|
-
/** Written to the child's stdin, which is then closed. */
|
|
401
|
-
stdin?: string;
|
|
402
|
-
/** Milliseconds after which the run is killed (exit code 124). */
|
|
403
|
-
timeout?: number;
|
|
404
|
-
}
|
|
405
|
-
interface CliPort {
|
|
406
|
-
/** Execute a command with the given arguments in the given working directory. */
|
|
407
|
-
exec: (args: string, cwd: string, env?: CliEnv, input?: CliInput) => Promise<CliOutput>;
|
|
408
|
-
/** Run a long-running process and wait for a pattern or timeout. */
|
|
409
|
-
watch: (args: string, cwd: string, options: ExecOptions, env?: CliEnv) => Promise<CliOutput>;
|
|
410
|
-
}
|
|
411
|
-
//#endregion
|
|
412
|
-
//#region src/core/contracts/types.d.ts
|
|
115
|
+
//#region src/specification/contracts/types.d.ts
|
|
413
116
|
/**
|
|
414
117
|
* The observed outgoing request, reduced to what contract matchers inspect.
|
|
415
118
|
* Built once per request by the engine (MSW on api/jobs, the stub backend on
|
|
416
119
|
* website/mobile) and handed to {@link ContractRequest.match} and to a
|
|
417
120
|
* {@link ContractResponder}.
|
|
418
121
|
*/
|
|
419
|
-
|
|
122
|
+
type MatchableRequest = {
|
|
420
123
|
/** Parsed JSON body when the payload is JSON, the raw text otherwise, or `null` when absent. */
|
|
421
124
|
body: unknown;
|
|
422
125
|
/** Request headers, keyed by lowercased header name. */
|
|
@@ -425,7 +128,7 @@ interface MatchableRequest {
|
|
|
425
128
|
method: string;
|
|
426
129
|
/** The request URL — fully-qualified, or origin-relative for the stub backend. */
|
|
427
130
|
url: string;
|
|
428
|
-
}
|
|
131
|
+
};
|
|
429
132
|
/**
|
|
430
133
|
* The request half of a contract: which outgoing call it speaks for.
|
|
431
134
|
*
|
|
@@ -434,7 +137,7 @@ interface MatchableRequest {
|
|
|
434
137
|
* website/mobile app's own backend calls take. `{{token}}` segments compare
|
|
435
138
|
* structurally, declared query params are a subset of the observed ones.
|
|
436
139
|
*/
|
|
437
|
-
|
|
140
|
+
type ContractRequest = {
|
|
438
141
|
/** Adapter name — `http` | `openai` | `anthropic`. */
|
|
439
142
|
adapter: string;
|
|
440
143
|
/** HTTP method to match. `*` matches any method. */
|
|
@@ -442,23 +145,23 @@ interface ContractRequest {
|
|
|
442
145
|
/** Absolute URL (string | RegExp) or an any-origin path form (`/articles/{{uuid}}`). */
|
|
443
146
|
url: RegExp | string;
|
|
444
147
|
/** Optional request matcher — the contract only fires if this returns true. */
|
|
445
|
-
match?: (request: MatchableRequest) => boolean;
|
|
148
|
+
match?: ((request: MatchableRequest) => boolean) | undefined;
|
|
446
149
|
/** Transform raw data into a provider-specific response envelope. */
|
|
447
150
|
wrap: (data: unknown) => ContractResponse;
|
|
448
|
-
}
|
|
151
|
+
};
|
|
449
152
|
/**
|
|
450
153
|
* The response half of a contract: what to reply when the request matches.
|
|
451
154
|
*/
|
|
452
|
-
|
|
155
|
+
type ContractResponse = {
|
|
453
156
|
/** HTTP status code (default: 200). */
|
|
454
157
|
status?: number;
|
|
455
158
|
/** Response body — an object is JSON, a string is text, `null`/`undefined` is empty. */
|
|
456
159
|
body: unknown;
|
|
457
160
|
/** Response headers. */
|
|
458
|
-
headers?: Record<string, string
|
|
161
|
+
headers?: Record<string, string> | undefined;
|
|
459
162
|
/** Delay in ms before responding (for timeout testing). */
|
|
460
|
-
delay?: number;
|
|
461
|
-
}
|
|
163
|
+
delay?: number | undefined;
|
|
164
|
+
};
|
|
462
165
|
/**
|
|
463
166
|
* A dynamic response: computed from the observed request at the moment the
|
|
464
167
|
* contract is served, rather than fixed ahead of time. Handed the same
|
|
@@ -472,7 +175,7 @@ type ContractResponder = (request: MatchableRequest) => ContractResponse;
|
|
|
472
175
|
*/
|
|
473
176
|
type ContractResponseValue = ContractResponder | ContractResponse;
|
|
474
177
|
//#endregion
|
|
475
|
-
//#region src/
|
|
178
|
+
//#region src/specification/contracts/contract.d.ts
|
|
476
179
|
/**
|
|
477
180
|
* A declared external interaction: what to match (`request`) and what to reply
|
|
478
181
|
* (`response`), together in one named artifact. Contracts live in TypeScript
|
|
@@ -480,7 +183,7 @@ type ContractResponseValue = ContractResponder | ContractResponse;
|
|
|
480
183
|
* payload (prompts, JSON responses) is visible at a glance while the real HTTP
|
|
481
184
|
* call stays mocked underneath.
|
|
482
185
|
*/
|
|
483
|
-
|
|
186
|
+
type Contract = {
|
|
484
187
|
/** Which outgoing call this contract speaks for. */
|
|
485
188
|
request: ContractRequest;
|
|
486
189
|
/**
|
|
@@ -501,12 +204,12 @@ interface Contract {
|
|
|
501
204
|
* silently-unused declaration into a spec failure.
|
|
502
205
|
*/
|
|
503
206
|
required?: boolean;
|
|
504
|
-
}
|
|
207
|
+
};
|
|
505
208
|
/**
|
|
506
209
|
* A composite of contracts — the unit tests import. Flat, ordered, immutable;
|
|
507
210
|
* `.with()` derives a variant without touching the original.
|
|
508
211
|
*/
|
|
509
|
-
|
|
212
|
+
type Contracts = {
|
|
510
213
|
/** The flattened contracts, in selection order. */
|
|
511
214
|
readonly contracts: readonly Contract[];
|
|
512
215
|
/**
|
|
@@ -516,7 +219,7 @@ interface Contracts {
|
|
|
516
219
|
* route it does not replace.
|
|
517
220
|
*/
|
|
518
221
|
with: (...overrides: (Contract | Contract[] | Contracts)[]) => Contracts;
|
|
519
|
-
}
|
|
222
|
+
};
|
|
520
223
|
/** Any accepted contract input: one, a list, or a composite. */
|
|
521
224
|
type ContractInput = Contract | Contract[] | Contracts;
|
|
522
225
|
/**
|
|
@@ -557,7 +260,7 @@ declare function defineContract(contract: Contract): Contract;
|
|
|
557
260
|
*/
|
|
558
261
|
declare function defineContracts(...items: ContractInput[]): Contracts;
|
|
559
262
|
//#endregion
|
|
560
|
-
//#region src/
|
|
263
|
+
//#region src/specification/ports/browser.port.d.ts
|
|
561
264
|
/**
|
|
562
265
|
* The landmark roles — the standard set of page regions, and the only
|
|
563
266
|
* containers a scope can name. Closed on purpose: ARIA defines exactly these,
|
|
@@ -575,7 +278,7 @@ type ElementKind = 'button' | 'field' | 'heading' | 'link' | 'testId' | 'text';
|
|
|
575
278
|
* A descriptor must designate exactly ONE element at action time; see
|
|
576
279
|
* {@link ElementMatch} and CONVENTIONS W3.
|
|
577
280
|
*/
|
|
578
|
-
|
|
281
|
+
type ElementRef = {
|
|
579
282
|
/**
|
|
580
283
|
* Match the accessible name as a whole string rather than a substring.
|
|
581
284
|
* Default (`false`) mirrors playwright: `link('Articles')` also matches
|
|
@@ -590,47 +293,47 @@ interface ElementRef {
|
|
|
590
293
|
* `within(scope, target)`. Chains: a scope may itself carry a scope.
|
|
591
294
|
*/
|
|
592
295
|
scope?: ElementRef;
|
|
593
|
-
}
|
|
296
|
+
};
|
|
594
297
|
/**
|
|
595
298
|
* One candidate captured when a descriptor matched more than one element —
|
|
596
299
|
* the evidence the ambiguity error enumerates so the author can disambiguate
|
|
597
300
|
* without opening a browser.
|
|
598
301
|
*/
|
|
599
|
-
|
|
302
|
+
type ElementMatch = {
|
|
600
303
|
/** Nearest landmark ancestor (`nav`, `footer`, `main`…), when there is one. */
|
|
601
|
-
context?: string;
|
|
304
|
+
context?: string | undefined;
|
|
602
305
|
/** The attribute that disambiguates most — `href` for links, `name` for fields. */
|
|
603
|
-
detail?: string;
|
|
306
|
+
detail?: string | undefined;
|
|
604
307
|
/** Tag name, lower-cased. */
|
|
605
308
|
tag: string;
|
|
606
309
|
/** Text content, whitespace-collapsed and truncated. */
|
|
607
310
|
text: string;
|
|
608
|
-
}
|
|
311
|
+
};
|
|
609
312
|
/** A `<link>` element captured from the rendered document's head. */
|
|
610
|
-
|
|
313
|
+
type BrowserLinkElement = {
|
|
611
314
|
href: string;
|
|
612
|
-
hreflang?: string;
|
|
315
|
+
hreflang?: string | undefined;
|
|
613
316
|
rel: string;
|
|
614
|
-
type?: string;
|
|
615
|
-
}
|
|
317
|
+
type?: string | undefined;
|
|
318
|
+
};
|
|
616
319
|
/** A `<meta>` element captured from the rendered document's head. */
|
|
617
|
-
|
|
320
|
+
type BrowserMetaElement = {
|
|
618
321
|
content: string;
|
|
619
|
-
name?: string;
|
|
620
|
-
property?: string;
|
|
621
|
-
}
|
|
322
|
+
name?: string | undefined;
|
|
323
|
+
property?: string | undefined;
|
|
324
|
+
};
|
|
622
325
|
/** A console message emitted while the page loaded or the scenario ran. */
|
|
623
|
-
|
|
326
|
+
type BrowserConsoleMessage = {
|
|
624
327
|
text: string;
|
|
625
328
|
type: string;
|
|
626
|
-
}
|
|
329
|
+
};
|
|
627
330
|
/**
|
|
628
331
|
* The visitor — the interaction vocabulary handed to a visit scenario.
|
|
629
332
|
* Every action auto-waits (playwright actionability); `see()` is the single
|
|
630
333
|
* synchronization primitive: it retries until the element is visible and
|
|
631
334
|
* fails at the timeout. There is no sleep and no conditional helper.
|
|
632
335
|
*/
|
|
633
|
-
|
|
336
|
+
type Visitor = {
|
|
634
337
|
/** Check a checkbox or radio. */
|
|
635
338
|
check: (element: ElementRef) => Promise<void>;
|
|
636
339
|
/** Click the element. */
|
|
@@ -647,16 +350,16 @@ interface Visitor {
|
|
|
647
350
|
see: (element: ElementRef) => Promise<void>;
|
|
648
351
|
/** Select an option in a select field. */
|
|
649
352
|
select: (element: ElementRef, option: string) => Promise<void>;
|
|
650
|
-
}
|
|
353
|
+
};
|
|
651
354
|
/** The behavior of a visit — the When of the spec; assertions stay in the Then. */
|
|
652
355
|
type VisitScenario = (visitor: Visitor) => Promise<void>;
|
|
653
356
|
/** Per-visit options forwarded to the browser context. */
|
|
654
|
-
|
|
357
|
+
type BrowserOpenOptions = {
|
|
655
358
|
/**
|
|
656
359
|
* Extra origins the `external: 'block'` policy lets through — the
|
|
657
360
|
* declared stub backend the page legitimately fetches from.
|
|
658
361
|
*/
|
|
659
|
-
allowedOrigins?: string[];
|
|
362
|
+
allowedOrigins?: string[] | undefined;
|
|
660
363
|
/**
|
|
661
364
|
* Base URL of the site under test — the origin `goto()` resolves against
|
|
662
365
|
* and the boundary of the `external` policy.
|
|
@@ -669,16 +372,16 @@ interface BrowserOpenOptions {
|
|
|
669
372
|
*/
|
|
670
373
|
external: 'allow' | 'block';
|
|
671
374
|
/** Extra HTTP headers sent with every request of the visit (incl. User-Agent overrides). */
|
|
672
|
-
headers?: Record<string, string
|
|
375
|
+
headers?: Record<string, string> | undefined;
|
|
673
376
|
/** The interaction scenario to run after load; the capture reflects the final state. */
|
|
674
|
-
scenario?: VisitScenario;
|
|
675
|
-
}
|
|
377
|
+
scenario?: undefined | VisitScenario;
|
|
378
|
+
};
|
|
676
379
|
/**
|
|
677
380
|
* The rendered page captured by a browser visit — the FINAL state when a
|
|
678
381
|
* scenario ran. Extraction happens in-page (the browser IS the HTML
|
|
679
382
|
* parser); interpretation of the raw elements belongs to core.
|
|
680
383
|
*/
|
|
681
|
-
|
|
384
|
+
type BrowserPage = {
|
|
682
385
|
/** Console messages emitted while loading and interacting, in order. */
|
|
683
386
|
consoleMessages: BrowserConsoleMessage[];
|
|
684
387
|
/** Serialized DOM after rendering (`document.documentElement.outerHTML`). */
|
|
@@ -697,20 +400,85 @@ interface BrowserPage {
|
|
|
697
400
|
title: string;
|
|
698
401
|
/** Final URL after redirects and scenario navigation. */
|
|
699
402
|
url: string;
|
|
700
|
-
}
|
|
403
|
+
};
|
|
701
404
|
/**
|
|
702
405
|
* Abstract browser interface for the website specification runner.
|
|
703
406
|
* One implementation lives in `integrations/playwright/` — a single shared
|
|
704
407
|
* browser instance per runner; each `open()` gets a fresh, isolated context.
|
|
705
408
|
*/
|
|
706
|
-
|
|
409
|
+
type BrowserPort = {
|
|
707
410
|
/** Close the shared browser instance (idempotent). */
|
|
708
411
|
close: () => Promise<void>;
|
|
709
412
|
/** Load `url` in a fresh context, run the scenario, capture the final page. */
|
|
710
413
|
open: (url: string, options: BrowserOpenOptions) => Promise<BrowserPage>;
|
|
711
|
-
}
|
|
414
|
+
};
|
|
712
415
|
//#endregion
|
|
713
|
-
//#region src/
|
|
416
|
+
//#region src/specification/ports/cli.port.d.ts
|
|
417
|
+
/**
|
|
418
|
+
* Raw output from a command execution, including exit code and captured output streams.
|
|
419
|
+
*/
|
|
420
|
+
type CliOutput = {
|
|
421
|
+
/** Process exit code (0 = success). */
|
|
422
|
+
exitCode: number;
|
|
423
|
+
/** Captured standard output. */
|
|
424
|
+
stdout: string;
|
|
425
|
+
/** Captured standard error. */
|
|
426
|
+
stderr: string;
|
|
427
|
+
};
|
|
428
|
+
/**
|
|
429
|
+
* Options for the long-running form of `.exec()` (CONVENTIONS B2). When
|
|
430
|
+
* either option is present the process is spawned and observed: it resolves
|
|
431
|
+
* as soon as `waitFor` appears in stdout/stderr, and is killed when `timeout`
|
|
432
|
+
* elapses (exit code 124).
|
|
433
|
+
*/
|
|
434
|
+
type ExecOptions = {
|
|
435
|
+
/** Resolve (exit code 0) when stdout/stderr contains this string. */
|
|
436
|
+
waitFor?: string | undefined;
|
|
437
|
+
/** Kill the process after this many milliseconds. Defaults to 10 000. */
|
|
438
|
+
timeout?: number | undefined;
|
|
439
|
+
};
|
|
440
|
+
/**
|
|
441
|
+
* Extra environment variables to set for the child process.
|
|
442
|
+
* Values are merged on top of process.env. A `null` value unsets the variable.
|
|
443
|
+
*/
|
|
444
|
+
type CliEnv = Record<string, null | string>;
|
|
445
|
+
/**
|
|
446
|
+
* Abstract command interface for specification runners.
|
|
447
|
+
* Implement this to plug in your command execution strategy.
|
|
448
|
+
*/
|
|
449
|
+
/**
|
|
450
|
+
* What one invocation is handed beyond its arguments — the two per-run inputs a
|
|
451
|
+
* spec document may state. Both are absent by default: the child gets an
|
|
452
|
+
* immediately-closed stdin (never a TTY) and the adapter's own timeout.
|
|
453
|
+
*/
|
|
454
|
+
type CliInput = {
|
|
455
|
+
/** Written to the child's stdin, which is then closed. */
|
|
456
|
+
stdin?: string | undefined;
|
|
457
|
+
/** Milliseconds after which the run is killed (exit code 124). */
|
|
458
|
+
timeout?: number | undefined;
|
|
459
|
+
};
|
|
460
|
+
type CliPort = {
|
|
461
|
+
/** Execute a command with the given arguments in the given working directory. */
|
|
462
|
+
exec: (args: string, cwd: string, env?: CliEnv, input?: CliInput) => Promise<CliOutput>;
|
|
463
|
+
/** Run a long-running process and wait for a pattern or timeout. */
|
|
464
|
+
watch: (args: string, cwd: string, options: ExecOptions, env?: CliEnv) => Promise<CliOutput>;
|
|
465
|
+
};
|
|
466
|
+
//#endregion
|
|
467
|
+
//#region src/specification/ports/database.port.d.ts
|
|
468
|
+
/**
|
|
469
|
+
* Abstract database interface for specification runners.
|
|
470
|
+
* Implement this to plug in your database stack (e.g. Postgres, SQLite).
|
|
471
|
+
*/
|
|
472
|
+
type DatabasePort = {
|
|
473
|
+
/** Execute raw SQL (for seeding test data). */
|
|
474
|
+
seed: (sql: string) => Promise<void>;
|
|
475
|
+
/** Query a table and return rows as arrays of values. */
|
|
476
|
+
query: (table: string, columns: string[]) => Promise<unknown[][]>;
|
|
477
|
+
/** Reset database to clean state between tests. */
|
|
478
|
+
reset: () => Promise<void>;
|
|
479
|
+
};
|
|
480
|
+
//#endregion
|
|
481
|
+
//#region src/specification/ports/device.port.d.ts
|
|
714
482
|
/**
|
|
715
483
|
* The element kinds a mobile screen can designate — the structural subset of
|
|
716
484
|
* {@link ElementRef} kinds that map onto the XCUITest accessibility tree.
|
|
@@ -738,7 +506,7 @@ type MobileElementRef = ElementRef;
|
|
|
738
506
|
* the evidence the ambiguity error enumerates so the author can disambiguate
|
|
739
507
|
* without opening the simulator.
|
|
740
508
|
*/
|
|
741
|
-
|
|
509
|
+
type MobileElementMatch = {
|
|
742
510
|
/** The accessibility identifier (`testId()` target), when one is set. */
|
|
743
511
|
identifier?: string;
|
|
744
512
|
/** The accessible label, whitespace-collapsed and truncated. */
|
|
@@ -747,7 +515,7 @@ interface MobileElementMatch {
|
|
|
747
515
|
type: string;
|
|
748
516
|
/** The element value (text-field content, adjustable value), when present. */
|
|
749
517
|
value?: string;
|
|
750
|
-
}
|
|
518
|
+
};
|
|
751
519
|
/**
|
|
752
520
|
* The visitor — the interaction vocabulary handed to a mobile scenario.
|
|
753
521
|
* Every verb auto-waits by polling until a visible match exists; acting
|
|
@@ -755,14 +523,14 @@ interface MobileElementMatch {
|
|
|
755
523
|
* synchronization primitive — is satisfied by any visible match. There is
|
|
756
524
|
* no sleep and no conditional helper.
|
|
757
525
|
*/
|
|
758
|
-
|
|
526
|
+
type MobileVisitor = {
|
|
759
527
|
/** Fill a text field with a value. */
|
|
760
528
|
fill: (element: MobileElementRef, value: string) => Promise<void>;
|
|
761
529
|
/** Wait until the element is visible — the only synchronization primitive. */
|
|
762
530
|
see: (element: MobileElementRef) => Promise<void>;
|
|
763
531
|
/** Tap the element. */
|
|
764
532
|
tap: (element: MobileElementRef) => Promise<void>;
|
|
765
|
-
}
|
|
533
|
+
};
|
|
766
534
|
/** The behavior of an open — the When of the spec; assertions stay in the Then. */
|
|
767
535
|
type MobileScenario = (visitor: MobileVisitor) => Promise<void>;
|
|
768
536
|
/**
|
|
@@ -771,7 +539,7 @@ type MobileScenario = (visitor: MobileVisitor) => Promise<void>;
|
|
|
771
539
|
* nodes are dropped and their children hoisted, so the projection stays
|
|
772
540
|
* stable and golden-friendly. Type names lose the `XCUIElementType` prefix.
|
|
773
541
|
*/
|
|
774
|
-
|
|
542
|
+
type ScreenNode = {
|
|
775
543
|
children?: ScreenNode[];
|
|
776
544
|
/** The accessibility identifier, only when it differs from the label. */
|
|
777
545
|
identifier?: string;
|
|
@@ -779,57 +547,78 @@ interface ScreenNode {
|
|
|
779
547
|
/** Element type without the `XCUIElementType` prefix — `Button`, `StaticText`. */
|
|
780
548
|
type: string;
|
|
781
549
|
value?: string;
|
|
782
|
-
}
|
|
550
|
+
};
|
|
783
551
|
/**
|
|
784
552
|
* The screen captured by a device open — the FINAL state when a scenario
|
|
785
553
|
* ran. The tree is the projected page source; `texts` are the visible
|
|
786
554
|
* labels/values in document order, consecutive duplicates collapsed.
|
|
787
555
|
*/
|
|
788
|
-
|
|
556
|
+
type DeviceScreen = {
|
|
789
557
|
texts: string[];
|
|
790
558
|
tree: ScreenNode;
|
|
791
|
-
}
|
|
559
|
+
};
|
|
792
560
|
/** Per-open options forwarded to the device session. */
|
|
793
|
-
|
|
561
|
+
type DeviceOpenOptions = {
|
|
794
562
|
/** The app under test — terminated and relaunched for a fresh, deterministic state. */
|
|
795
563
|
bundleId: string;
|
|
796
564
|
/** Deep link applied after the relaunch (`news://events`); absent opens the app plainly. */
|
|
797
|
-
deepLink?: string;
|
|
565
|
+
deepLink?: string | undefined;
|
|
798
566
|
/** The interaction scenario to run after launch; the capture reflects the final state. */
|
|
799
|
-
scenario?: MobileScenario;
|
|
800
|
-
}
|
|
567
|
+
scenario?: MobileScenario | undefined;
|
|
568
|
+
};
|
|
801
569
|
/**
|
|
802
570
|
* How long a device session waits, in milliseconds. Every field is optional
|
|
803
571
|
* and falls back to the framework's own default, so a runner states only the
|
|
804
572
|
* wait it needs to move — the defaults suit a release build, and a project
|
|
805
573
|
* that drives a DEV build pays for its bundler's cold boot.
|
|
806
574
|
*/
|
|
807
|
-
|
|
575
|
+
type DeviceTimeouts = {
|
|
808
576
|
/**
|
|
809
577
|
* How long every verb polls for a visible match before refusing.
|
|
810
578
|
* Default: `30_000` — enough for a cold app boot on a release bundle.
|
|
811
579
|
*/
|
|
812
|
-
action?: number;
|
|
580
|
+
action?: number | undefined;
|
|
813
581
|
/**
|
|
814
582
|
* How long the FIRST session waits for WebDriverAgent to build and
|
|
815
583
|
* launch on the simulator. Default: `240_000`.
|
|
816
584
|
*/
|
|
817
|
-
launch?: number;
|
|
818
|
-
}
|
|
585
|
+
launch?: number | undefined;
|
|
586
|
+
};
|
|
819
587
|
/**
|
|
820
588
|
* Abstract device interface for the mobile specification runner.
|
|
821
589
|
* One implementation lives in `integrations/appium/` — a single driver
|
|
822
590
|
* session per runner, created on the first `open()` and reused; each open
|
|
823
591
|
* terminates and relaunches the app for a deterministic fresh state.
|
|
824
592
|
*/
|
|
825
|
-
|
|
593
|
+
type DevicePort = {
|
|
826
594
|
/** End the driver session (idempotent). */
|
|
827
595
|
close: () => Promise<void>;
|
|
828
596
|
/** Relaunch the app, apply the deep link, run the scenario, capture the final screen. */
|
|
829
597
|
open: (options: DeviceOpenOptions) => Promise<DeviceScreen>;
|
|
830
|
-
}
|
|
598
|
+
};
|
|
599
|
+
//#endregion
|
|
600
|
+
//#region src/specification/ports/server.port.d.ts
|
|
601
|
+
/**
|
|
602
|
+
* HTTP response returned by a server port, with parsed JSON body.
|
|
603
|
+
*/
|
|
604
|
+
type ServerResponse = {
|
|
605
|
+
/** HTTP status code. */
|
|
606
|
+
status: number;
|
|
607
|
+
/** Parsed JSON response body, or null if parsing failed. */
|
|
608
|
+
body: unknown;
|
|
609
|
+
/** Response headers as a flat key-value map. */
|
|
610
|
+
headers: Record<string, string>;
|
|
611
|
+
};
|
|
612
|
+
/**
|
|
613
|
+
* Abstract server interface for specification runners.
|
|
614
|
+
* Integration mode uses an in-process Hono app; E2E mode uses real HTTP via fetch.
|
|
615
|
+
*/
|
|
616
|
+
type ServerPort = {
|
|
617
|
+
/** Send an HTTP request and return the parsed response. */
|
|
618
|
+
request: (method: string, path: string, body?: unknown, headers?: Record<string, string>) => Promise<ServerResponse>;
|
|
619
|
+
};
|
|
831
620
|
//#endregion
|
|
832
|
-
//#region src/
|
|
621
|
+
//#region src/specification/ports/isolation.port.d.ts
|
|
833
622
|
/**
|
|
834
623
|
* Strategy for isolating service state across parallel test workers.
|
|
835
624
|
*
|
|
@@ -840,7 +629,7 @@ interface DevicePort {
|
|
|
840
629
|
* Implement this interface to support new service types (e.g. MongoDB,
|
|
841
630
|
* Elasticsearch, S3).
|
|
842
631
|
*/
|
|
843
|
-
|
|
632
|
+
type IsolationStrategy = {
|
|
844
633
|
/**
|
|
845
634
|
* Create an isolated namespace for this worker.
|
|
846
635
|
* Called once when the worker starts — e.g. clone a template database,
|
|
@@ -859,14 +648,14 @@ interface IsolationStrategy {
|
|
|
859
648
|
* Called once when the worker shuts down — e.g. drop the cloned database.
|
|
860
649
|
*/
|
|
861
650
|
release: () => Promise<void>;
|
|
862
|
-
}
|
|
651
|
+
};
|
|
863
652
|
//#endregion
|
|
864
|
-
//#region src/
|
|
653
|
+
//#region src/specification/ports/service.port.d.ts
|
|
865
654
|
/**
|
|
866
655
|
* A service handle — returned by factory functions like postgres(), redis().
|
|
867
656
|
* Mutable: connectionString is populated after the orchestrator starts containers.
|
|
868
657
|
*/
|
|
869
|
-
|
|
658
|
+
type ServiceHandle = {
|
|
870
659
|
/** Service type identifier. */
|
|
871
660
|
readonly type: string;
|
|
872
661
|
/**
|
|
@@ -909,63 +698,200 @@ interface ServiceHandle {
|
|
|
909
698
|
reset: () => Promise<void>;
|
|
910
699
|
/** Get the isolation strategy for parallel test execution. */
|
|
911
700
|
isolation: () => IsolationStrategy;
|
|
701
|
+
};
|
|
702
|
+
//#endregion
|
|
703
|
+
//#region src/specification/facets/_common/result/response.d.ts
|
|
704
|
+
/**
|
|
705
|
+
* Read-only accessor for an HTTP response.
|
|
706
|
+
*
|
|
707
|
+
* Assertions go through `expect()`:
|
|
708
|
+
* `expect(result.response).toMatch('created.http')` compares status,
|
|
709
|
+
* a subset of headers, and the JSON body against `_expected/<name>` —
|
|
710
|
+
* with `{{placeholder}}` support in all three.
|
|
711
|
+
*/
|
|
712
|
+
declare class ResponseAccessor {
|
|
713
|
+
/** The parsed JSON response body (null when parsing failed). */
|
|
714
|
+
readonly body: unknown;
|
|
715
|
+
/** @internal Ref-capture scope shared by the current spec execution. */
|
|
716
|
+
readonly captures: CaptureScope;
|
|
717
|
+
/** Response headers as a flat, lower-cased key-value map. */
|
|
718
|
+
readonly headers: Record<string, string>;
|
|
719
|
+
/** The HTTP response status code. */
|
|
720
|
+
readonly status: number;
|
|
721
|
+
/** @internal Test-file directory — fixture resolution root for matchers. */
|
|
722
|
+
readonly testDir: string;
|
|
723
|
+
constructor(response: ServerResponse, testDir: string, captures?: CaptureScope);
|
|
912
724
|
}
|
|
913
725
|
//#endregion
|
|
914
|
-
//#region src/
|
|
915
|
-
/**
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
726
|
+
//#region src/specification/facets/api/result.d.ts
|
|
727
|
+
/** Result from an HTTP action (.request(), .get(), .post(), .put(), .delete()). */
|
|
728
|
+
declare class HttpResult extends BaseResult {
|
|
729
|
+
private readonly responseData;
|
|
730
|
+
constructor(options: {
|
|
731
|
+
config: SpecificationConfig;
|
|
732
|
+
response: ServerResponse;
|
|
733
|
+
testDir: string;
|
|
734
|
+
});
|
|
735
|
+
/** Access the HTTP response (status, headers, body) for assertions. */
|
|
736
|
+
get response(): ResponseAccessor;
|
|
737
|
+
/** The HTTP response status code. */
|
|
738
|
+
get status(): number;
|
|
922
739
|
}
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
740
|
+
//#endregion
|
|
741
|
+
//#region src/specification/facets/_common/result/filesystem.d.ts
|
|
742
|
+
/**
|
|
743
|
+
* Read-only accessor for the whole temporary working directory used by a
|
|
744
|
+
* command spec.
|
|
745
|
+
*
|
|
746
|
+
* Assertions go through `expect()` (async — they walk the disk):
|
|
747
|
+
* `await expect(result.filesystem).toMatch('scaffolded')` compares the
|
|
748
|
+
* tree against the fixture directory `_expected/<name>/`.
|
|
749
|
+
*/
|
|
750
|
+
declare class FilesystemAccessor {
|
|
751
|
+
/** @internal Ref-capture scope shared by the current spec execution. */
|
|
752
|
+
readonly captures: CaptureScope;
|
|
753
|
+
/** The absolute path of the temporary working directory. */
|
|
754
|
+
readonly cwd: string;
|
|
755
|
+
/** @internal Test-file directory — fixture resolution root for matchers. */
|
|
756
|
+
readonly testDir: string;
|
|
757
|
+
constructor(cwd: string, testDir: string, captures?: CaptureScope);
|
|
758
|
+
/** List all files (recursively) under the working directory, sorted. */
|
|
759
|
+
files(options?: {
|
|
760
|
+
ignore?: string[];
|
|
761
|
+
}): Promise<string[]>;
|
|
927
762
|
}
|
|
763
|
+
//#endregion
|
|
764
|
+
//#region src/specification/facets/_common/result/text.d.ts
|
|
928
765
|
/**
|
|
929
|
-
*
|
|
930
|
-
*
|
|
766
|
+
* Read-only accessor for a captured text handle — THE universal one: stdout,
|
|
767
|
+
* stderr, container logs, and file text all surface as a `TextAccessor`.
|
|
931
768
|
*
|
|
932
|
-
*
|
|
933
|
-
*
|
|
769
|
+
* Backed by a primitive string, exposed via {@link text}, but also provides
|
|
770
|
+
* `toString()` / `valueOf()` for string coercion, so common patterns like
|
|
771
|
+
* `String(result.stdout)` and template-literal interpolation still work.
|
|
772
|
+
*
|
|
773
|
+
* ANSI escape sequences are stripped by default before every comparison
|
|
774
|
+
* (CONVENTIONS D6) — `.text` stays raw. The runner `transform` option remains
|
|
775
|
+
* an escape hatch for application noise not covered by the `{{token}}`
|
|
776
|
+
* grammar.
|
|
777
|
+
*
|
|
778
|
+
* Text operations are **closed** over the type: `.grep(pattern)` returns a
|
|
779
|
+
* `TextAccessor` (not a bare string), preserving the `_expected/`-resolution
|
|
780
|
+
* context and the `transform`, so results are chainable
|
|
781
|
+
* (`result.stdout.grep(a).grep(b)`) and snapshot-able
|
|
782
|
+
* (`expect(result.stdout.grep('users.ts')).toMatch('block.txt')`).
|
|
783
|
+
*
|
|
784
|
+
* Assertions go through `expect()` matchers: `expect(result.stdout).toContain(...)`
|
|
785
|
+
* and `expect(result.stdout).toMatch('name.txt')` (resolved against
|
|
786
|
+
* `_expected/<name>`, flat — with `{{token}}` support, CONVENTIONS D4).
|
|
934
787
|
*/
|
|
935
|
-
declare class
|
|
936
|
-
/** @internal Ref-capture scope shared by
|
|
788
|
+
declare class TextAccessor {
|
|
789
|
+
/** @internal Ref-capture scope shared by the current spec execution. */
|
|
937
790
|
readonly captures: CaptureScope;
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
/**
|
|
943
|
-
|
|
944
|
-
/**
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
})
|
|
950
|
-
|
|
791
|
+
/** @internal Stream label used in failure messages ("stdout" / "stderr"). */
|
|
792
|
+
readonly streamName: string;
|
|
793
|
+
/** @internal Test-file directory — fixture resolution root for matchers. */
|
|
794
|
+
readonly testDir: string;
|
|
795
|
+
/** The raw captured text (never transformed, ANSI preserved). */
|
|
796
|
+
readonly text: string;
|
|
797
|
+
/** @internal Normaliser applied before comparisons, never to fixtures. */
|
|
798
|
+
readonly transform?: ((text: string) => string) | undefined;
|
|
799
|
+
constructor(value: string, streamName: string, testDir: string, options?: {
|
|
800
|
+
captures?: CaptureScope | undefined;
|
|
801
|
+
transform?: ((text: string) => string) | undefined;
|
|
802
|
+
});
|
|
803
|
+
/** @internal The text as compared by matchers — ANSI stripped, transform applied. */
|
|
804
|
+
get comparableText(): string;
|
|
805
|
+
/**
|
|
806
|
+
* Keep only the blank-line-separated blocks of the text that contain
|
|
807
|
+
* `pattern` (how linter/compiler output is structured), returned as a new
|
|
808
|
+
* `TextAccessor` — the same subject type, so the result is chainable and
|
|
809
|
+
* snapshot-able. The scalpel for probing large tool outputs; snapshot the
|
|
810
|
+
* whole surface by default, reach for `.grep()` for targeted checks.
|
|
811
|
+
*/
|
|
812
|
+
grep(pattern: string): TextAccessor;
|
|
813
|
+
toString(): string;
|
|
814
|
+
valueOf(): string;
|
|
951
815
|
}
|
|
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;
|
|
952
834
|
//#endregion
|
|
953
|
-
//#region src/
|
|
954
|
-
/**
|
|
955
|
-
|
|
956
|
-
|
|
835
|
+
//#region src/specification/facets/cli/result.d.ts
|
|
836
|
+
/**
|
|
837
|
+
* Result from a command action (`.exec()`).
|
|
838
|
+
*
|
|
839
|
+
* When the runner was configured with a `docker` option, the result also
|
|
840
|
+
* exposes container accessors and participates in `Symbol.asyncDispose`
|
|
841
|
+
* cleanup. The Docker shell-outs are **lazy**: a test that never calls
|
|
842
|
+
* `.container(...)` never queries the Docker daemon, so command-only tests
|
|
843
|
+
* pay zero Docker cost even when the runner is Docker-aware.
|
|
844
|
+
*
|
|
845
|
+
* Dispose always runs one final label-filtered `docker ps` to catch
|
|
846
|
+
* containers that were spawned during `.exec` but never asserted on —
|
|
847
|
+
* tests still get leak-free cleanup even if they forget to reach into
|
|
848
|
+
* a container.
|
|
849
|
+
*/
|
|
850
|
+
declare class CliResult extends BaseResult {
|
|
851
|
+
private readonly commandOutput;
|
|
852
|
+
private containersCache;
|
|
853
|
+
private readonly dockerConfig?;
|
|
854
|
+
private readonly testRunId?;
|
|
855
|
+
private readonly transform?;
|
|
957
856
|
constructor(options: {
|
|
857
|
+
commandOutput: CliOutput;
|
|
958
858
|
config: SpecificationConfig;
|
|
959
|
-
|
|
859
|
+
dockerConfig?: DockerSpecConfig | undefined;
|
|
960
860
|
testDir: string;
|
|
861
|
+
testRunId?: string | undefined;
|
|
862
|
+
transform?: ((text: string) => string) | undefined;
|
|
863
|
+
workDir: string;
|
|
961
864
|
});
|
|
962
|
-
/**
|
|
963
|
-
get
|
|
964
|
-
/**
|
|
965
|
-
get
|
|
865
|
+
/** The process exit code. */
|
|
866
|
+
get exitCode(): number;
|
|
867
|
+
/** Accessor for the captured standard output with file-based assertions. */
|
|
868
|
+
get stdout(): TextAccessor;
|
|
869
|
+
/** Accessor for the captured standard error with file-based assertions. */
|
|
870
|
+
get stderr(): TextAccessor;
|
|
871
|
+
/** Accessor for parsing stdout as JSON and asserting against JSON fixtures. */
|
|
872
|
+
get json(): JsonAccessor;
|
|
873
|
+
/** Accessor for the temporary working directory the command ran in. */
|
|
874
|
+
get filesystem(): FilesystemAccessor;
|
|
875
|
+
/**
|
|
876
|
+
* Look up a container the command spawned during this run by the value of
|
|
877
|
+
* its name-label (as declared in the `docker.nameLabel` of the
|
|
878
|
+
* `specification.cli()` options).
|
|
879
|
+
* First access triggers a one-shot `docker ps` + `docker inspect`
|
|
880
|
+
* query and caches the result for the rest of the result's lifetime.
|
|
881
|
+
* Tests that don't call this never touch Docker.
|
|
882
|
+
*
|
|
883
|
+
* Returns an accessor with `.exists === false` when no container
|
|
884
|
+
* carries that name — callers can still assert absence without a try.
|
|
885
|
+
*/
|
|
886
|
+
container(name: string): ContainerAccessor;
|
|
887
|
+
/** All captured container IDs. Triggers the same lazy query as `container()`. */
|
|
888
|
+
get containerIds(): string[];
|
|
889
|
+
[Symbol.asyncDispose](): Promise<void>;
|
|
890
|
+
private ensureDockerAware;
|
|
891
|
+
private loadContainers;
|
|
966
892
|
}
|
|
967
893
|
//#endregion
|
|
968
|
-
//#region src/
|
|
894
|
+
//#region src/specification/facets/cli/literate.d.ts
|
|
969
895
|
/**
|
|
970
896
|
* Running a `<case>.spec.yaml` document: one scenario, one working directory,
|
|
971
897
|
* one set of servers, every run executed and asserted.
|
|
@@ -983,7 +909,7 @@ declare class HttpResult extends BaseResult {
|
|
|
983
909
|
* A server a document may start by name (`serve: mcp`), registered once per app
|
|
984
910
|
* in the `serve` option of `specification.cli()`.
|
|
985
911
|
*/
|
|
986
|
-
|
|
912
|
+
type LiterateServeRegistration = {
|
|
987
913
|
/** Shell command that starts the server, run from the project root. */
|
|
988
914
|
command: string;
|
|
989
915
|
/** The variable the resolved URL is bound to in every run's child env. */
|
|
@@ -995,9 +921,9 @@ interface LiterateServeRegistration {
|
|
|
995
921
|
ready: RegExp;
|
|
996
922
|
/** Builds the URL bound to {@link env} from the announced port. */
|
|
997
923
|
url: (port: number) => string;
|
|
998
|
-
}
|
|
924
|
+
};
|
|
999
925
|
/** Per-call options for {@link runSpecDocument} / `cli.run()`. */
|
|
1000
|
-
|
|
926
|
+
type LiterateRunFlags = {
|
|
1001
927
|
/**
|
|
1002
928
|
* Opt this document OUT of the update-mode rewrite. A frozen document is
|
|
1003
929
|
* NEVER written under `TEST_UPDATE=1`: its mismatch still throws its diff.
|
|
@@ -1006,10 +932,10 @@ interface LiterateRunFlags {
|
|
|
1006
932
|
* instead of being silently corrected into a passing file. The document's
|
|
1007
933
|
* mirror of `toMatch(name, { frozen: true })`.
|
|
1008
934
|
*/
|
|
1009
|
-
frozen?: boolean;
|
|
1010
|
-
}
|
|
935
|
+
frozen?: boolean | undefined;
|
|
936
|
+
};
|
|
1011
937
|
//#endregion
|
|
1012
|
-
//#region src/
|
|
938
|
+
//#region src/specification/facets/mobile/result.d.ts
|
|
1013
939
|
/** Result from a `.open()` action — the screen as the device saw it, final state. */
|
|
1014
940
|
declare class ScreenResult extends BaseResult {
|
|
1015
941
|
private readonly capturedScreen;
|
|
@@ -1030,13 +956,13 @@ declare class ScreenResult extends BaseResult {
|
|
|
1030
956
|
get screen(): JsonAccessor;
|
|
1031
957
|
}
|
|
1032
958
|
//#endregion
|
|
1033
|
-
//#region src/
|
|
959
|
+
//#region src/specification/facets/website/result.d.ts
|
|
1034
960
|
/** A raw HTTP exchange captured by `.fetch()` — redirects are NOT followed. */
|
|
1035
|
-
|
|
961
|
+
type FetchExchange = {
|
|
1036
962
|
body: string;
|
|
1037
963
|
headers: Record<string, string>;
|
|
1038
964
|
status: number;
|
|
1039
|
-
}
|
|
965
|
+
};
|
|
1040
966
|
/** Result from a raw `.fetch()` action (robots.txt, sitemaps, redirects). */
|
|
1041
967
|
declare class FetchResult extends BaseResult {
|
|
1042
968
|
private readonly exchange;
|
|
@@ -1107,15 +1033,15 @@ declare class PageResult extends BaseResult {
|
|
|
1107
1033
|
get url(): string;
|
|
1108
1034
|
}
|
|
1109
1035
|
//#endregion
|
|
1110
|
-
//#region src/
|
|
1036
|
+
//#region src/specification/facets/_common/stub-backend.d.ts
|
|
1111
1037
|
/** Options for the stub backend server. */
|
|
1112
|
-
|
|
1038
|
+
type StubBackendOptions = {
|
|
1113
1039
|
/**
|
|
1114
1040
|
* Fixed port — pins a stable stub URL across runs (a bundler that inlines
|
|
1115
1041
|
* the URL at serve time survives warm). Default: a free OS-assigned port.
|
|
1116
1042
|
*/
|
|
1117
|
-
port?: number;
|
|
1118
|
-
}
|
|
1043
|
+
port?: number | undefined;
|
|
1044
|
+
};
|
|
1119
1045
|
declare class StubBackend {
|
|
1120
1046
|
/** Guarded once the current chain declared at least one contract. */
|
|
1121
1047
|
private guarded;
|
|
@@ -1149,107 +1075,107 @@ declare class StubBackend {
|
|
|
1149
1075
|
private record;
|
|
1150
1076
|
}
|
|
1151
1077
|
//#endregion
|
|
1152
|
-
//#region src/
|
|
1078
|
+
//#region src/specification/facets/_common/builder.d.ts
|
|
1153
1079
|
/** A named job that can be triggered via jobs.trigger(). */
|
|
1154
|
-
|
|
1080
|
+
type JobHandle = {
|
|
1155
1081
|
name: string;
|
|
1156
1082
|
execute: () => Promise<void>;
|
|
1157
|
-
}
|
|
1083
|
+
};
|
|
1158
1084
|
/**
|
|
1159
1085
|
* Configuration for the docker-aware cli mode. When set on
|
|
1160
1086
|
* {@link SpecificationConfig}, the cli runner generates a test-run id, injects
|
|
1161
1087
|
* it into the child env under `envVar`, then queries Docker for every
|
|
1162
1088
|
* container carrying `testRunLabel=<id>` after the command exits.
|
|
1163
1089
|
*/
|
|
1164
|
-
|
|
1090
|
+
type DockerSpecConfig = {
|
|
1165
1091
|
envVar: string;
|
|
1166
1092
|
nameLabel: string;
|
|
1167
1093
|
testRunLabel: string;
|
|
1168
|
-
}
|
|
1094
|
+
};
|
|
1169
1095
|
/** Adapter configuration passed to the specification facets at setup time. */
|
|
1170
|
-
|
|
1096
|
+
type SpecificationConfig = {
|
|
1171
1097
|
/**
|
|
1172
1098
|
* The declared stub backend (website/mobile facets) — armed with the
|
|
1173
1099
|
* chain's contracts before every terminal action.
|
|
1174
1100
|
*/
|
|
1175
|
-
backend?: StubBackend;
|
|
1101
|
+
backend?: StubBackend | undefined;
|
|
1176
1102
|
/**
|
|
1177
1103
|
* Base URL of the running stub backend — allow-listed through the
|
|
1178
1104
|
* browser's `external: 'block'` policy so client-side fetches reach it.
|
|
1179
1105
|
*/
|
|
1180
|
-
backendUrl?: string;
|
|
1106
|
+
backendUrl?: string | undefined;
|
|
1181
1107
|
/** Base URL of the website under test (website facet only). */
|
|
1182
|
-
baseUrl?: string;
|
|
1108
|
+
baseUrl?: string | undefined;
|
|
1183
1109
|
/**
|
|
1184
1110
|
* Lazy browser accessor (website facet only). The first `.visit()`
|
|
1185
1111
|
* launches the shared browser instance; `.fetch()`-only spec files never
|
|
1186
1112
|
* pay the browser cost.
|
|
1187
1113
|
*/
|
|
1188
|
-
browser?: () => Promise<BrowserPort
|
|
1114
|
+
browser?: (() => Promise<BrowserPort>) | undefined;
|
|
1189
1115
|
/**
|
|
1190
1116
|
* Cross-origin request policy for visits (website facet only): `'block'`
|
|
1191
1117
|
* aborts requests leaving the site under test — the browser-side analog
|
|
1192
1118
|
* of strict intercepts.
|
|
1193
1119
|
*/
|
|
1194
|
-
external?: 'allow' | 'block';
|
|
1120
|
+
external?: 'allow' | 'block' | undefined;
|
|
1195
1121
|
/** Bundle id of the app under test (mobile facet only) — the app `.open()` relaunches. */
|
|
1196
|
-
bundleId?: string;
|
|
1197
|
-
command?: CliPort;
|
|
1198
|
-
database?: DatabasePort;
|
|
1122
|
+
bundleId?: string | undefined;
|
|
1123
|
+
command?: CliPort | undefined;
|
|
1124
|
+
database?: DatabasePort | undefined;
|
|
1199
1125
|
/**
|
|
1200
1126
|
* Keys of the declared services record that are databases. Drives the
|
|
1201
1127
|
* CONVENTIONS A7 rule: with 2+ databases the `database` option is
|
|
1202
1128
|
* mandatory on `.seed()` / `.table()`; with exactly one it is forbidden.
|
|
1203
1129
|
*/
|
|
1204
|
-
databaseKeys?: string[];
|
|
1205
|
-
databases?: Map<string, DatabasePort
|
|
1130
|
+
databaseKeys?: string[] | undefined;
|
|
1131
|
+
databases?: Map<string, DatabasePort> | undefined;
|
|
1206
1132
|
/**
|
|
1207
1133
|
* Lazy device accessor (mobile facet only). The first `.open()` creates
|
|
1208
1134
|
* the shared driver session; the appium/webdriverio integration stays a
|
|
1209
1135
|
* lazy import so the optional peer is only loaded when a spec opens the
|
|
1210
1136
|
* app.
|
|
1211
1137
|
*/
|
|
1212
|
-
device?: () => Promise<DevicePort
|
|
1213
|
-
dockerConfig?: DockerSpecConfig;
|
|
1138
|
+
device?: (() => Promise<DevicePort>) | undefined;
|
|
1139
|
+
dockerConfig?: DockerSpecConfig | undefined;
|
|
1214
1140
|
/**
|
|
1215
1141
|
* Unique id shared by every spec from this runner instance.
|
|
1216
1142
|
* Stable for the runner's lifetime so multi-step tests (spawn in
|
|
1217
1143
|
* one run, inspect in another) see the same container scope. The
|
|
1218
1144
|
* facet factories auto-populate this when `dockerConfig` is present.
|
|
1219
1145
|
*/
|
|
1220
|
-
dockerTestRunId?: string;
|
|
1146
|
+
dockerTestRunId?: string | undefined;
|
|
1221
1147
|
/**
|
|
1222
1148
|
* Named environment SETS a spec document may name by bare word
|
|
1223
1149
|
* (`env: frozen`). Declared once per app in `specification.cli()`.
|
|
1224
1150
|
*/
|
|
1225
|
-
envSets?: Record<string, CliEnv
|
|
1151
|
+
envSets?: Record<string, CliEnv> | undefined;
|
|
1226
1152
|
/**
|
|
1227
1153
|
* When set, `.intercept()` is unavailable on this runner and throws this
|
|
1228
1154
|
* reason immediately (compose mode — MSW is in-process, CONVENTIONS I3).
|
|
1229
1155
|
*/
|
|
1230
|
-
interceptDisabledReason?: string;
|
|
1231
|
-
jobs?: JobHandle[];
|
|
1156
|
+
interceptDisabledReason?: string | undefined;
|
|
1157
|
+
jobs?: JobHandle[] | undefined;
|
|
1232
1158
|
/** The project root — the working directory a document's `serve:` command runs from. */
|
|
1233
|
-
root?: string;
|
|
1234
|
-
server?: ServerPort;
|
|
1159
|
+
root?: string | undefined;
|
|
1160
|
+
server?: ServerPort | undefined;
|
|
1235
1161
|
/**
|
|
1236
1162
|
* Named servers a spec document may start (`serve: [mcp]`). Declared once
|
|
1237
1163
|
* per app in `specification.cli()`.
|
|
1238
1164
|
*/
|
|
1239
|
-
serveRegistry?: Record<string, LiterateServeRegistration
|
|
1165
|
+
serveRegistry?: Record<string, LiterateServeRegistration> | undefined;
|
|
1240
1166
|
/**
|
|
1241
1167
|
* The declared services record. In cli mode, drives the automatic
|
|
1242
1168
|
* connection-URL injection into the child env (CONVENTIONS B6):
|
|
1243
1169
|
* `<KEY>_URL` per service, plus `DATABASE_URL` / `REDIS_URL` when
|
|
1244
1170
|
* unambiguous.
|
|
1245
1171
|
*/
|
|
1246
|
-
services?: Record<string, ServiceHandle
|
|
1172
|
+
services?: Record<string, ServiceHandle> | undefined;
|
|
1247
1173
|
/**
|
|
1248
1174
|
* Optional normaliser applied to command stdout/stderr before every
|
|
1249
1175
|
* comparison. Does not mutate the raw `.text` accessor.
|
|
1250
1176
|
*/
|
|
1251
|
-
transform?: (text: string) => string;
|
|
1252
|
-
}
|
|
1177
|
+
transform?: ((text: string) => string) | undefined;
|
|
1178
|
+
};
|
|
1253
1179
|
/**
|
|
1254
1180
|
* The `.intercept()` overload set, identical on every facet: one contract, a
|
|
1255
1181
|
* list, a composite — or the inline `(request, response)` pair for a one-off.
|
|
@@ -1263,7 +1189,7 @@ type InterceptMethod<T> = ((contracts: ContractInput) => T) & ((request: Contrac
|
|
|
1263
1189
|
* The `DatabaseKey` parameter is the typed vocabulary of `.seed()`: the keys
|
|
1264
1190
|
* of the declared services record that are databases.
|
|
1265
1191
|
*/
|
|
1266
|
-
|
|
1192
|
+
type ApiSpecification<DatabaseKey extends string = string> = {
|
|
1267
1193
|
/** Set HTTP headers for the request. Multiple calls merge. */
|
|
1268
1194
|
headers: (headers: Record<string, string>) => ApiSpecification<DatabaseKey>;
|
|
1269
1195
|
/** Declare outgoing calls — a contract, a list, a composite, or an inline request + response pair. */
|
|
@@ -1282,12 +1208,12 @@ interface ApiSpecification<DatabaseKey extends string = string> {
|
|
|
1282
1208
|
put: (path: string, body?: unknown) => Promise<HttpResult>;
|
|
1283
1209
|
/** Send the complete request described by `_requests/<file>` (.http format). */
|
|
1284
1210
|
request: (file: string) => Promise<HttpResult>;
|
|
1285
|
-
}
|
|
1211
|
+
};
|
|
1286
1212
|
/**
|
|
1287
1213
|
* The `jobs` facet — job chain entry handed out by `specification.jobs()`.
|
|
1288
1214
|
* Jobs run in-process by definition (CONVENTIONS A5/A8).
|
|
1289
1215
|
*/
|
|
1290
|
-
|
|
1216
|
+
type JobsSpecification<DatabaseKey extends string = string> = {
|
|
1291
1217
|
/** Declare outgoing calls — a contract, a list, a composite, or an inline request + response pair. */
|
|
1292
1218
|
intercept: InterceptMethod<JobsSpecification<DatabaseKey>>;
|
|
1293
1219
|
/** Queue a SQL seed file from `_seeds/` to run before the action. */
|
|
@@ -1296,13 +1222,13 @@ interface JobsSpecification<DatabaseKey extends string = string> {
|
|
|
1296
1222
|
}) => JobsSpecification<DatabaseKey>;
|
|
1297
1223
|
/** Execute the named job registered via the `jobs` option and resolve with the result. */
|
|
1298
1224
|
trigger: (name: string) => Promise<BaseResult>;
|
|
1299
|
-
}
|
|
1225
|
+
};
|
|
1300
1226
|
/**
|
|
1301
1227
|
* The `cli` facet — command chain entry handed out by `specification.cli()`.
|
|
1302
1228
|
* Setup methods chain; `.exec()` is the single terminal action (CONVENTIONS
|
|
1303
1229
|
* B2) — `{ waitFor?, timeout? }` covers long-running processes.
|
|
1304
1230
|
*/
|
|
1305
|
-
|
|
1231
|
+
type CliSpecification<DatabaseKey extends string = string> = {
|
|
1306
1232
|
/** Set environment variables on the child process. `$WORKDIR` expands; `null` unsets. */
|
|
1307
1233
|
env: (env: CliEnv) => CliSpecification<DatabaseKey>;
|
|
1308
1234
|
/**
|
|
@@ -1333,14 +1259,14 @@ interface CliSpecification<DatabaseKey extends string = string> {
|
|
|
1333
1259
|
* directory, where the document lives.
|
|
1334
1260
|
*/
|
|
1335
1261
|
run: (file: string, options?: LiterateRunFlags) => Promise<CliResult>;
|
|
1336
|
-
}
|
|
1262
|
+
};
|
|
1337
1263
|
/**
|
|
1338
1264
|
* The `website` facet — page chain entry handed out by
|
|
1339
1265
|
* `specification.website()`. Setup methods chain; action methods are
|
|
1340
1266
|
* terminal. `.visit()` renders the page in the shared browser; `.fetch()`
|
|
1341
1267
|
* performs one raw HTTP exchange and never follows redirects.
|
|
1342
1268
|
*/
|
|
1343
|
-
|
|
1269
|
+
type WebsiteSpecification = {
|
|
1344
1270
|
/** Set HTTP headers for the exchange (incl. User-Agent overrides). Multiple calls merge. */
|
|
1345
1271
|
headers: (headers: Record<string, string>) => WebsiteSpecification;
|
|
1346
1272
|
/**
|
|
@@ -1356,14 +1282,14 @@ interface WebsiteSpecification {
|
|
|
1356
1282
|
* the capture reflects the FINAL page state.
|
|
1357
1283
|
*/
|
|
1358
1284
|
visit: (path: string, scenario?: VisitScenario) => Promise<PageResult>;
|
|
1359
|
-
}
|
|
1285
|
+
};
|
|
1360
1286
|
/**
|
|
1361
1287
|
* The `mobile` facet — screen chain entry handed out by
|
|
1362
1288
|
* `specification.mobile()`. `.open()` is the single, terminal action: it
|
|
1363
1289
|
* terminates and relaunches the app (deterministic fresh state), applies the
|
|
1364
1290
|
* deep link, runs the scenario, and captures the final screen.
|
|
1365
1291
|
*/
|
|
1366
|
-
|
|
1292
|
+
type MobileSpecification = {
|
|
1367
1293
|
/**
|
|
1368
1294
|
* Declare the chain's backend contracts — served by the declared stub
|
|
1369
1295
|
* backend (requires the runner's `backend` option). Multiple calls append.
|
|
@@ -1375,65 +1301,91 @@ interface MobileSpecification {
|
|
|
1375
1301
|
* first (the When) and the capture reflects the FINAL screen state.
|
|
1376
1302
|
*/
|
|
1377
1303
|
open: (deepLink?: string, scenario?: MobileScenario) => Promise<ScreenResult>;
|
|
1304
|
+
};
|
|
1305
|
+
//#endregion
|
|
1306
|
+
//#region src/specification/facets/_common/result/directory.d.ts
|
|
1307
|
+
/**
|
|
1308
|
+
* Read-only accessor for a directory produced by a spec action.
|
|
1309
|
+
*
|
|
1310
|
+
* Assertions go through `expect()` (async — they walk the disk):
|
|
1311
|
+
* `await expect(result.directory('out')).toMatch('scaffold/out')`
|
|
1312
|
+
* compares the tree against the fixture directory `_expected/<name>/`.
|
|
1313
|
+
*/
|
|
1314
|
+
declare class DirectoryAccessor {
|
|
1315
|
+
/** @internal Ref-capture scope shared by the current spec execution. */
|
|
1316
|
+
readonly captures: CaptureScope;
|
|
1317
|
+
/** @internal Absolute path of the directory under assertion. */
|
|
1318
|
+
readonly root: string;
|
|
1319
|
+
/** @internal Test-file directory — fixture resolution root for matchers. */
|
|
1320
|
+
readonly testDir: string;
|
|
1321
|
+
constructor(absPath: string, testDir: string, captures?: CaptureScope);
|
|
1322
|
+
/** List all files (recursively) under the directory, sorted. */
|
|
1323
|
+
files(options?: {
|
|
1324
|
+
ignore?: string[];
|
|
1325
|
+
}): Promise<string[]>;
|
|
1378
1326
|
}
|
|
1379
1327
|
//#endregion
|
|
1380
|
-
//#region src/
|
|
1328
|
+
//#region src/specification/facets/_common/result/table.d.ts
|
|
1381
1329
|
/**
|
|
1382
|
-
*
|
|
1330
|
+
* Read-only accessor for a database table after a specification run.
|
|
1383
1331
|
*
|
|
1384
|
-
*
|
|
1385
|
-
* exposes container accessors and participates in `Symbol.asyncDispose`
|
|
1386
|
-
* cleanup. The Docker shell-outs are **lazy**: a test that never calls
|
|
1387
|
-
* `.container(...)` never queries the Docker daemon, so command-only tests
|
|
1388
|
-
* pay zero Docker cost even when the runner is Docker-aware.
|
|
1332
|
+
* Assertions go through `expect()` (async — they query the database):
|
|
1389
1333
|
*
|
|
1390
|
-
*
|
|
1391
|
-
*
|
|
1392
|
-
*
|
|
1393
|
-
*
|
|
1334
|
+
* await expect(result.table('users', { database: 'db' })).toMatchRows({
|
|
1335
|
+
* columns: ['name'],
|
|
1336
|
+
* rows: [['Alice']],
|
|
1337
|
+
* });
|
|
1338
|
+
* await expect(result.table('users', { database: 'db' })).toBeEmpty();
|
|
1394
1339
|
*/
|
|
1395
|
-
declare class
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
private readonly
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
/** The
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1340
|
+
declare class TableAccessor {
|
|
1341
|
+
/** @internal Ref-capture scope shared by the current spec execution. */
|
|
1342
|
+
readonly captures: CaptureScope;
|
|
1343
|
+
/** @internal */
|
|
1344
|
+
private readonly db;
|
|
1345
|
+
/** The table name this accessor reads. */
|
|
1346
|
+
readonly name: string;
|
|
1347
|
+
constructor(name: string, db: DatabasePort, captures?: CaptureScope);
|
|
1348
|
+
/** @internal Query the table — used by the toMatchRows / toBeEmpty matchers. */
|
|
1349
|
+
query(columns: string[]): Promise<unknown[][]>;
|
|
1350
|
+
}
|
|
1351
|
+
//#endregion
|
|
1352
|
+
//#region src/specification/facets/_common/result/result.d.ts
|
|
1353
|
+
/** Read-only handle to a single file produced by a spec action. */
|
|
1354
|
+
type FileAccessor = {
|
|
1355
|
+
/** The UTF-8 text content. Throws if the file does not exist. */
|
|
1356
|
+
readonly content: string;
|
|
1357
|
+
readonly exists: boolean;
|
|
1358
|
+
/** The file text as a {@link TextAccessor}, keeping only blocks matching `pattern`. */
|
|
1359
|
+
grep: (pattern: string) => TextAccessor;
|
|
1360
|
+
};
|
|
1361
|
+
type BaseResultOptions = {
|
|
1362
|
+
config: SpecificationConfig;
|
|
1363
|
+
testDir: string;
|
|
1364
|
+
workDir?: string | undefined;
|
|
1365
|
+
};
|
|
1366
|
+
/**
|
|
1367
|
+
* Base result - common accessors available after any action type.
|
|
1368
|
+
* Extended by HttpResult, CliResult, and used directly by job results.
|
|
1369
|
+
*
|
|
1370
|
+
* Every result carries a fresh {@link CaptureScope}: `match.ref()` captures
|
|
1371
|
+
* and `{{type#ref}}` placeholders are scoped to one spec execution.
|
|
1372
|
+
*/
|
|
1373
|
+
declare class BaseResult {
|
|
1374
|
+
/** @internal Ref-capture scope shared by every assertion on this result. */
|
|
1375
|
+
readonly captures: CaptureScope;
|
|
1376
|
+
protected config: SpecificationConfig;
|
|
1377
|
+
protected testDir: string;
|
|
1378
|
+
protected workDir?: string | undefined;
|
|
1379
|
+
constructor(options: BaseResultOptions);
|
|
1380
|
+
/** Access a directory (relative to the working directory) for snapshot assertions. */
|
|
1381
|
+
directory(path?: string): DirectoryAccessor;
|
|
1382
|
+
/** Access a single file (relative to the working directory) for content assertions. */
|
|
1383
|
+
file(path: string): FileAccessor;
|
|
1384
|
+
/** Access a database table for row-level assertions via expect() matchers. */
|
|
1385
|
+
table(tableName: string, options?: {
|
|
1386
|
+
database?: string;
|
|
1387
|
+
}): TableAccessor;
|
|
1388
|
+
private resolveDatabase;
|
|
1437
1389
|
}
|
|
1438
1390
|
//#endregion
|
|
1439
1391
|
//#region src/integrations/docker/container-accessor.d.ts
|
|
@@ -1500,12 +1452,12 @@ declare class ContainerAccessor {
|
|
|
1500
1452
|
* survivable across update runs instead of being silently overwritten with the
|
|
1501
1453
|
* actual output.
|
|
1502
1454
|
*/
|
|
1503
|
-
|
|
1455
|
+
type MatchFixtureOptions = {
|
|
1504
1456
|
frozen?: boolean;
|
|
1505
|
-
}
|
|
1457
|
+
};
|
|
1506
1458
|
//#endregion
|
|
1507
|
-
//#region src/
|
|
1508
|
-
|
|
1459
|
+
//#region src/specification/facets/_common/orchestrator.d.ts
|
|
1460
|
+
type OrchestratorOptions = {
|
|
1509
1461
|
/**
|
|
1510
1462
|
* Named infrastructure record. Keys become the database vocabulary of the
|
|
1511
1463
|
* spec (`.seed()` / `.table()` `database` option) and drive the compose
|
|
@@ -1526,17 +1478,17 @@ interface OrchestratorOptions {
|
|
|
1526
1478
|
root: string;
|
|
1527
1479
|
/** Compose project name — used for per-worker stack isolation. */
|
|
1528
1480
|
projectName?: string;
|
|
1529
|
-
}
|
|
1481
|
+
};
|
|
1530
1482
|
/**
|
|
1531
1483
|
* Orchestrator for test infrastructure.
|
|
1532
1484
|
* Integration: starts services via testcontainers.
|
|
1533
1485
|
* E2E: runs full docker compose up.
|
|
1534
1486
|
*/
|
|
1535
1487
|
declare class Orchestrator {
|
|
1536
|
-
private services;
|
|
1537
|
-
private mode;
|
|
1538
|
-
private root;
|
|
1539
|
-
private projectName;
|
|
1488
|
+
private readonly services;
|
|
1489
|
+
private readonly mode;
|
|
1490
|
+
private readonly root;
|
|
1491
|
+
private readonly projectName;
|
|
1540
1492
|
private running;
|
|
1541
1493
|
private composeStack;
|
|
1542
1494
|
private composeHandles;
|
|
@@ -1585,7 +1537,7 @@ declare class Orchestrator {
|
|
|
1585
1537
|
getAppUrl(): null | string;
|
|
1586
1538
|
}
|
|
1587
1539
|
//#endregion
|
|
1588
|
-
//#region src/
|
|
1540
|
+
//#region src/specification/facets/_common/services.d.ts
|
|
1589
1541
|
/**
|
|
1590
1542
|
* Infrastructure services declared as a named record. Keys become the typed
|
|
1591
1543
|
* vocabulary of the whole spec: the server factory receives the same record,
|
|
@@ -1595,7 +1547,7 @@ type ServiceRecord = Record<string, ServiceHandle>;
|
|
|
1595
1547
|
/** Keys of a services record whose handles are databases. */
|
|
1596
1548
|
type DatabaseKeys<Services extends ServiceRecord> = { [K in keyof Services]: Services[K] extends DatabasePort ? K & string : never; }[keyof Services];
|
|
1597
1549
|
//#endregion
|
|
1598
|
-
//#region src/
|
|
1550
|
+
//#region src/specification/facets/api/start-api.d.ts
|
|
1599
1551
|
/** Any object with a request method compatible with Hono's app.request(). */
|
|
1600
1552
|
type HonoApp = {
|
|
1601
1553
|
request: (path: string, init?: RequestInit) => Promise<Response> | Response;
|
|
@@ -1603,7 +1555,7 @@ type HonoApp = {
|
|
|
1603
1555
|
/** Execution mode — exists ONLY on `specification.api()` (CONVENTIONS A5). */
|
|
1604
1556
|
type SpecificationMode = 'compose' | 'node';
|
|
1605
1557
|
/** Options for {@link startApi | specification.api}. */
|
|
1606
|
-
|
|
1558
|
+
type ApiSpecificationOptions<Services extends ServiceRecord = ServiceRecord> = {
|
|
1607
1559
|
/**
|
|
1608
1560
|
* Execution mode override. Resolution: `options.mode` >
|
|
1609
1561
|
* `process.env.TEST_MODE` > `'node'`. Never hardcode this in a
|
|
@@ -1632,14 +1584,14 @@ interface ApiSpecificationOptions<Services extends ServiceRecord = ServiceRecord
|
|
|
1632
1584
|
* its key, else the kebab-case conversion of the key (CONVENTIONS A6).
|
|
1633
1585
|
*/
|
|
1634
1586
|
services?: Services;
|
|
1635
|
-
}
|
|
1587
|
+
};
|
|
1636
1588
|
/**
|
|
1637
1589
|
* The record returned by {@link startApi | specification.api}. Destructure
|
|
1638
1590
|
* with the canonical names (CONVENTIONS A3):
|
|
1639
1591
|
*
|
|
1640
1592
|
* const { api, cleanup, docker } = await specification.api(…);
|
|
1641
1593
|
*/
|
|
1642
|
-
|
|
1594
|
+
type ApiHandle<DatabaseKey extends string = string> = {
|
|
1643
1595
|
api: ApiSpecification<DatabaseKey>;
|
|
1644
1596
|
/** Stop all infrastructure started by this specification. */
|
|
1645
1597
|
cleanup: () => Promise<void>;
|
|
@@ -1650,12 +1602,12 @@ interface ApiHandle<DatabaseKey extends string = string> {
|
|
|
1650
1602
|
docker: (containerId: string) => ContainerAccessor;
|
|
1651
1603
|
/** The orchestrator managing the test infrastructure lifecycle. */
|
|
1652
1604
|
orchestrator: Orchestrator;
|
|
1653
|
-
}
|
|
1605
|
+
};
|
|
1654
1606
|
declare function startApi<Services extends ServiceRecord>(options: ApiSpecificationOptions<Services>): Promise<ApiHandle<DatabaseKeys<Services>>>;
|
|
1655
1607
|
//#endregion
|
|
1656
|
-
//#region src/
|
|
1608
|
+
//#region src/specification/facets/cli/start-cli.d.ts
|
|
1657
1609
|
/** Options for {@link startCli | specification.cli}. */
|
|
1658
|
-
|
|
1610
|
+
type CliSpecificationOptions<Services extends ServiceRecord = ServiceRecord> = {
|
|
1659
1611
|
/**
|
|
1660
1612
|
* Opt-in Docker awareness. When set, every spec generates a unique
|
|
1661
1613
|
* test-run id, injects it into the child process env under `envVar`,
|
|
@@ -1699,15 +1651,15 @@ interface CliSpecificationOptions<Services extends ServiceRecord = ServiceRecord
|
|
|
1699
1651
|
* D6). Does NOT mutate the raw `.text` accessor. Prefer `{{token}}`
|
|
1700
1652
|
* placeholders in fixtures.
|
|
1701
1653
|
*/
|
|
1702
|
-
transform?: (text: string) => string;
|
|
1703
|
-
}
|
|
1654
|
+
transform?: ((text: string) => string) | undefined;
|
|
1655
|
+
};
|
|
1704
1656
|
/**
|
|
1705
1657
|
* The record returned by {@link startCli | specification.cli}. Destructure
|
|
1706
1658
|
* with the canonical names (CONVENTIONS A3):
|
|
1707
1659
|
*
|
|
1708
1660
|
* const { cli, cleanup, docker } = await specification.cli(…);
|
|
1709
1661
|
*/
|
|
1710
|
-
|
|
1662
|
+
type CliHandle<DatabaseKey extends string = string> = {
|
|
1711
1663
|
/** Stop all infrastructure started by this specification. */
|
|
1712
1664
|
cleanup: () => Promise<void>;
|
|
1713
1665
|
cli: CliSpecification<DatabaseKey>;
|
|
@@ -1718,12 +1670,12 @@ interface CliHandle<DatabaseKey extends string = string> {
|
|
|
1718
1670
|
docker: (containerId: string) => ContainerAccessor;
|
|
1719
1671
|
/** The orchestrator managing the test infrastructure lifecycle. */
|
|
1720
1672
|
orchestrator: null | Orchestrator;
|
|
1721
|
-
}
|
|
1673
|
+
};
|
|
1722
1674
|
declare function startCli<Services extends ServiceRecord>(bin: string, options?: CliSpecificationOptions<Services>): Promise<CliHandle<DatabaseKeys<Services>>>;
|
|
1723
1675
|
//#endregion
|
|
1724
|
-
//#region src/
|
|
1676
|
+
//#region src/specification/facets/jobs/start-jobs.d.ts
|
|
1725
1677
|
/** Options for {@link startJobs | specification.jobs}. */
|
|
1726
|
-
|
|
1678
|
+
type JobsSpecificationOptions<Services extends ServiceRecord = ServiceRecord> = {
|
|
1727
1679
|
/**
|
|
1728
1680
|
* Named jobs triggerable via `jobs.trigger(name)` — a factory receiving
|
|
1729
1681
|
* the started services record, or a static array. Jobs run in-process by
|
|
@@ -1734,23 +1686,23 @@ interface JobsSpecificationOptions<Services extends ServiceRecord = ServiceRecor
|
|
|
1734
1686
|
root?: string;
|
|
1735
1687
|
/** Named infrastructure record started via testcontainers. */
|
|
1736
1688
|
services?: Services;
|
|
1737
|
-
}
|
|
1689
|
+
};
|
|
1738
1690
|
/**
|
|
1739
1691
|
* The record returned by {@link startJobs | specification.jobs}. Destructure
|
|
1740
1692
|
* with the canonical names (CONVENTIONS A3):
|
|
1741
1693
|
*
|
|
1742
1694
|
* const { jobs, cleanup } = await specification.jobs(…);
|
|
1743
1695
|
*/
|
|
1744
|
-
|
|
1696
|
+
type JobsHandle<DatabaseKey extends string = string> = {
|
|
1745
1697
|
/** Stop all infrastructure started by this specification. */
|
|
1746
1698
|
cleanup: () => Promise<void>;
|
|
1747
1699
|
jobs: JobsSpecification<DatabaseKey>;
|
|
1748
1700
|
/** The orchestrator managing the test infrastructure lifecycle. */
|
|
1749
1701
|
orchestrator: null | Orchestrator;
|
|
1750
|
-
}
|
|
1702
|
+
};
|
|
1751
1703
|
declare function startJobs<Services extends ServiceRecord>(options: JobsSpecificationOptions<Services>): Promise<JobsHandle<DatabaseKeys<Services>>>;
|
|
1752
1704
|
//#endregion
|
|
1753
|
-
//#region src/
|
|
1705
|
+
//#region src/specification/facets/mobile/start-mobile.d.ts
|
|
1754
1706
|
/**
|
|
1755
1707
|
* The declared stub backend behind the app under test. The framework owns
|
|
1756
1708
|
* the simulator and appium but NOT the JS bundler (Metro belongs to the
|
|
@@ -1758,16 +1710,16 @@ declare function startJobs<Services extends ServiceRecord>(options: JobsSpecific
|
|
|
1758
1710
|
* injected: the handle exposes `backendUrl` and the CALLER wires it into its
|
|
1759
1711
|
* own bundler env.
|
|
1760
1712
|
*/
|
|
1761
|
-
|
|
1713
|
+
type MobileBackendOptions = {
|
|
1762
1714
|
/**
|
|
1763
1715
|
* Fixed port — pins a stable stub URL across runs. Metro inlines
|
|
1764
1716
|
* `EXPO_PUBLIC_*` values at bundle-serve time; a stable port lets a warm
|
|
1765
1717
|
* Metro survive between runs. Default: a free OS-assigned port.
|
|
1766
1718
|
*/
|
|
1767
1719
|
port?: number;
|
|
1768
|
-
}
|
|
1720
|
+
};
|
|
1769
1721
|
/** Options for {@link startMobile | specification.mobile}. */
|
|
1770
|
-
|
|
1722
|
+
type MobileSpecificationOptions = {
|
|
1771
1723
|
/** The app under test — terminated and relaunched by every `.open()`. */
|
|
1772
1724
|
app: {
|
|
1773
1725
|
/** Bundle id of the installed app (`com.example.app`). */
|
|
@@ -1801,31 +1753,31 @@ interface MobileSpecificationOptions {
|
|
|
1801
1753
|
* (`{ action: 45_000 }`) instead of sleeping inside its scenarios.
|
|
1802
1754
|
*/
|
|
1803
1755
|
timeouts?: DeviceTimeouts;
|
|
1804
|
-
}
|
|
1756
|
+
};
|
|
1805
1757
|
/**
|
|
1806
1758
|
* The record returned by {@link startMobile | specification.mobile}.
|
|
1807
1759
|
* Destructure with the canonical names (CONVENTIONS A3):
|
|
1808
1760
|
*
|
|
1809
1761
|
* const { mobile, cleanup, udid } = await specification.mobile(…);
|
|
1810
1762
|
*/
|
|
1811
|
-
|
|
1763
|
+
type MobileHandle = {
|
|
1812
1764
|
/**
|
|
1813
1765
|
* Base URL of the declared stub backend — present only with the
|
|
1814
1766
|
* `backend` option. The caller injects it into its own bundler env
|
|
1815
1767
|
* (e.g. `EXPO_PUBLIC_API_URL`); the framework never touches the bundler.
|
|
1816
1768
|
*/
|
|
1817
|
-
backendUrl?: string;
|
|
1769
|
+
backendUrl?: string | undefined;
|
|
1818
1770
|
/** End the driver session, stop the appium server and the stub backend. */
|
|
1819
1771
|
cleanup: () => Promise<void>;
|
|
1820
1772
|
mobile: MobileSpecification;
|
|
1821
1773
|
/** The resolved simulator UDID the specs run against. */
|
|
1822
1774
|
udid: string;
|
|
1823
|
-
}
|
|
1775
|
+
};
|
|
1824
1776
|
declare function startMobile(options: MobileSpecificationOptions): Promise<MobileHandle>;
|
|
1825
1777
|
//#endregion
|
|
1826
|
-
//#region src/
|
|
1778
|
+
//#region src/specification/facets/website/serve.adapter.d.ts
|
|
1827
1779
|
/** Options for a local server started by the framework. */
|
|
1828
|
-
|
|
1780
|
+
type ServeOptions = {
|
|
1829
1781
|
/** Shell command that starts the site. Receives the chosen port as `PORT`. */
|
|
1830
1782
|
command: string;
|
|
1831
1783
|
/** Fixed port. Default: an OS-assigned free port, injected as `PORT`. */
|
|
@@ -1844,21 +1796,21 @@ interface ServeOptions {
|
|
|
1844
1796
|
ready?: RegExp | string;
|
|
1845
1797
|
/** Readiness budget in milliseconds. Default 30 000. */
|
|
1846
1798
|
timeout?: number;
|
|
1847
|
-
}
|
|
1799
|
+
};
|
|
1848
1800
|
//#endregion
|
|
1849
|
-
//#region src/
|
|
1801
|
+
//#region src/specification/facets/website/start-website.d.ts
|
|
1850
1802
|
/**
|
|
1851
1803
|
* The declared stub backend behind the site under test — started before the
|
|
1852
1804
|
* server command, torn down with the runner. Its URL is injected into the
|
|
1853
1805
|
* server child's environment under `env`; the contracts each chain declares
|
|
1854
1806
|
* via `.intercept(...)` are what it serves.
|
|
1855
1807
|
*/
|
|
1856
|
-
|
|
1808
|
+
type WebsiteBackendOptions = {
|
|
1857
1809
|
/** Env var receiving the stub's URL in the server child (e.g. `'API_URL'`). */
|
|
1858
1810
|
env: string;
|
|
1859
1811
|
/** Fixed port — pins a stable stub URL across runs. Default: a free OS-assigned port. */
|
|
1860
1812
|
port?: number;
|
|
1861
|
-
}
|
|
1813
|
+
};
|
|
1862
1814
|
/**
|
|
1863
1815
|
* Options for {@link startWebsite | specification.website}. `server` (start
|
|
1864
1816
|
* the site locally) and `url` (target a running site) are mutually
|
|
@@ -1904,16 +1856,16 @@ type WebsiteSpecificationOptions = {
|
|
|
1904
1856
|
*
|
|
1905
1857
|
* const { website, cleanup } = await specification.website(…);
|
|
1906
1858
|
*/
|
|
1907
|
-
|
|
1859
|
+
type WebsiteHandle = {
|
|
1908
1860
|
/** Stop the server process and the shared browser instance. */
|
|
1909
1861
|
cleanup: () => Promise<void>;
|
|
1910
1862
|
/** The base URL the specs run against. */
|
|
1911
1863
|
url: string;
|
|
1912
1864
|
website: WebsiteSpecification;
|
|
1913
|
-
}
|
|
1865
|
+
};
|
|
1914
1866
|
declare function startWebsite(options: WebsiteSpecificationOptions): Promise<WebsiteHandle>;
|
|
1915
1867
|
//#endregion
|
|
1916
|
-
//#region src/
|
|
1868
|
+
//#region src/specification/facets/_common/specification.d.ts
|
|
1917
1869
|
/**
|
|
1918
1870
|
* The five specification constructors (CONVENTIONS A2) — created in a
|
|
1919
1871
|
* `*.specification.ts` file under `specs/`, destructured with canonical
|
|
@@ -1938,44 +1890,17 @@ declare function startWebsite(options: WebsiteSpecificationOptions): Promise<Web
|
|
|
1938
1890
|
* export const { cli, cleanup } = await specification.cli('my-cli');
|
|
1939
1891
|
* afterAll(cleanup);
|
|
1940
1892
|
*/
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
* Test an HTTP app. Mode `'node'` (default) starts the declared services
|
|
1944
|
-
* via testcontainers and runs the app in-process; mode `'compose'` runs
|
|
1945
|
-
* `docker compose up` on `docker/compose.test.yaml` and sends real HTTP
|
|
1946
|
-
* requests to the app service. Resolution: `options.mode` > `TEST_MODE`
|
|
1947
|
-
* env var > `'node'`. Only `.api()` has a mode.
|
|
1948
|
-
*/
|
|
1893
|
+
/** The five constructors, and only five — the framework's whole entry surface. */
|
|
1894
|
+
type Specification = {
|
|
1949
1895
|
api: typeof startApi;
|
|
1950
|
-
/**
|
|
1951
|
-
* Test a command binary. Each spec runs in a fresh temp directory.
|
|
1952
|
-
*
|
|
1953
|
-
* @param bin - Path to the binary (resolved from node_modules/.bin or PATH).
|
|
1954
|
-
*/
|
|
1955
1896
|
cli: typeof startCli;
|
|
1956
|
-
/**
|
|
1957
|
-
* Test background jobs. Jobs run in-process by definition — no HTTP
|
|
1958
|
-
* server, no mode. `.trigger(name)` is the terminal action.
|
|
1959
|
-
*/
|
|
1960
1897
|
jobs: typeof startJobs;
|
|
1961
|
-
/**
|
|
1962
|
-
* Test a native app on the iOS simulator. `device` names the simulator
|
|
1963
|
-
* (resolved and booted via `simctl`); `app` names the bundle under
|
|
1964
|
-
* test. `.open(deepLink?, scenario?)` relaunches the app fresh, runs
|
|
1965
|
-
* the scenario, and captures the final screen.
|
|
1966
|
-
*/
|
|
1967
1898
|
mobile: typeof startMobile;
|
|
1968
|
-
/**
|
|
1969
|
-
* Test a deployed or locally-served website. `server` starts the site
|
|
1970
|
-
* (a shell command receiving `PORT`); `url` targets a running one.
|
|
1971
|
-
* `.visit(path)` renders the page in a single shared browser instance;
|
|
1972
|
-
* `.fetch(path)` performs one raw HTTP exchange (redirects never
|
|
1973
|
-
* followed).
|
|
1974
|
-
*/
|
|
1975
1899
|
website: typeof startWebsite;
|
|
1976
1900
|
};
|
|
1901
|
+
declare const specification: Specification;
|
|
1977
1902
|
//#endregion
|
|
1978
|
-
//#region src/
|
|
1903
|
+
//#region src/specification/literate/spec-document.d.ts
|
|
1979
1904
|
/**
|
|
1980
1905
|
* The facets a document may describe. `cli` is the only one implemented; the
|
|
1981
1906
|
* key exists so a second facet can arrive without a second grammar.
|
|
@@ -1994,22 +1919,22 @@ type SpecEnvToken = {
|
|
|
1994
1919
|
name: string;
|
|
1995
1920
|
};
|
|
1996
1921
|
/** One `serve:` entry — a registered server plus the extra env it is started with. */
|
|
1997
|
-
|
|
1922
|
+
type SpecServeEntry = {
|
|
1998
1923
|
env: Record<string, string>;
|
|
1999
1924
|
line: number;
|
|
2000
1925
|
name: string;
|
|
2001
|
-
}
|
|
1926
|
+
};
|
|
2002
1927
|
/** One `fixture:` entry — a path with the line it was declared on. */
|
|
2003
|
-
|
|
1928
|
+
type SpecFixture = {
|
|
2004
1929
|
line: number;
|
|
2005
1930
|
path: string;
|
|
2006
|
-
}
|
|
1931
|
+
};
|
|
2007
1932
|
/** An expected stream: its text, and the first line of that text in the file. */
|
|
2008
|
-
|
|
1933
|
+
type SpecStream = {
|
|
2009
1934
|
/** 1-based line the scalar's content starts on — where a token defect is reported. */
|
|
2010
1935
|
line: number;
|
|
2011
1936
|
text: string;
|
|
2012
|
-
}
|
|
1937
|
+
};
|
|
2013
1938
|
/** One `files:` assertion, keyed by a workdir-relative path. */
|
|
2014
1939
|
type SpecFileAssertion = {
|
|
2015
1940
|
contains: SpecStream[];
|
|
@@ -2027,7 +1952,7 @@ type SpecFileAssertion = {
|
|
|
2027
1952
|
path: string;
|
|
2028
1953
|
};
|
|
2029
1954
|
/** One run: a command, what it must exit with, and what it must have produced. */
|
|
2030
|
-
|
|
1955
|
+
type SpecRun = {
|
|
2031
1956
|
command: string;
|
|
2032
1957
|
/** 1-based line of the `command:` key — the frame a failure points at. */
|
|
2033
1958
|
commandLine: number;
|
|
@@ -2041,9 +1966,9 @@ interface SpecRun {
|
|
|
2041
1966
|
timeout: null | number;
|
|
2042
1967
|
/** Only ever on the LAST run — a long-running command ends the session. */
|
|
2043
1968
|
waitFor: null | string;
|
|
2044
|
-
}
|
|
1969
|
+
};
|
|
2045
1970
|
/** A parsed `<case>.spec.yaml`. */
|
|
2046
|
-
|
|
1971
|
+
type SpecDocument = {
|
|
2047
1972
|
/** The vitest test title. */
|
|
2048
1973
|
description: string;
|
|
2049
1974
|
/** 1-based line of the `description:` key. */
|
|
@@ -2053,7 +1978,7 @@ interface SpecDocument {
|
|
|
2053
1978
|
kind: SpecKind;
|
|
2054
1979
|
runs: SpecRun[];
|
|
2055
1980
|
serve: SpecServeEntry[];
|
|
2056
|
-
}
|
|
1981
|
+
};
|
|
2057
1982
|
//#endregion
|
|
2058
1983
|
//#region src/integrations/docker/docker-lookup.d.ts
|
|
2059
1984
|
/** Return all container IDs (running or stopped) that carry `key=value`. */
|
|
@@ -2063,12 +1988,12 @@ declare function inspectContainer(id: string): unknown;
|
|
|
2063
1988
|
/** Force-remove the given container IDs in a single call. Errors are swallowed. */
|
|
2064
1989
|
declare function removeContainers(ids: string[]): void;
|
|
2065
1990
|
//#endregion
|
|
2066
|
-
//#region src/
|
|
1991
|
+
//#region src/specification/ports/container.port.d.ts
|
|
2067
1992
|
/**
|
|
2068
1993
|
* Abstract container interface.
|
|
2069
1994
|
* Represents a running service (database, cache, etc.)
|
|
2070
1995
|
*/
|
|
2071
|
-
|
|
1996
|
+
type ContainerPort = {
|
|
2072
1997
|
/** Start the container and wait until ready. */
|
|
2073
1998
|
start: () => Promise<void>;
|
|
2074
1999
|
/** Stop and remove the container. */
|
|
@@ -2081,9 +2006,9 @@ interface ContainerPort {
|
|
|
2081
2006
|
getConnectionString: () => string;
|
|
2082
2007
|
/** Get container logs (stdout + stderr). */
|
|
2083
2008
|
getLogs: () => Promise<string>;
|
|
2084
|
-
}
|
|
2009
|
+
};
|
|
2085
2010
|
//#endregion
|
|
2086
|
-
//#region src/
|
|
2011
|
+
//#region src/specification/facets/website/elements.d.ts
|
|
2087
2012
|
/**
|
|
2088
2013
|
* The element vocabulary — user-facing descriptors for visit scenarios.
|
|
2089
2014
|
* Reads like English (`click(link('Articles'))`), translates to
|
|
@@ -2099,41 +2024,45 @@ interface ContainerPort {
|
|
|
2099
2024
|
* substring, when two names genuinely overlap.
|
|
2100
2025
|
*/
|
|
2101
2026
|
/** Options accepted by every named descriptor. */
|
|
2102
|
-
|
|
2027
|
+
type ElementOptions = {
|
|
2103
2028
|
/**
|
|
2104
2029
|
* Match the accessible name as a whole string. Default is substring —
|
|
2105
2030
|
* `link('Articles')` also matches "Read Articles".
|
|
2106
2031
|
*/
|
|
2107
2032
|
exact?: boolean;
|
|
2108
|
-
}
|
|
2033
|
+
};
|
|
2034
|
+
/** A user-facing element factory — what every name in this module is. */
|
|
2035
|
+
type ElementFactory = (name: string, options?: ElementOptions) => ElementRef;
|
|
2109
2036
|
/** A button (or element with the button role), by accessible name. */
|
|
2110
|
-
declare const button:
|
|
2037
|
+
declare const button: ElementFactory;
|
|
2111
2038
|
/** A form field, by label. */
|
|
2112
|
-
declare const field:
|
|
2039
|
+
declare const field: ElementFactory;
|
|
2113
2040
|
/** A heading, by accessible name. */
|
|
2114
|
-
declare const heading:
|
|
2041
|
+
declare const heading: ElementFactory;
|
|
2115
2042
|
/** A link, by accessible name. */
|
|
2116
|
-
declare const link:
|
|
2043
|
+
declare const link: ElementFactory;
|
|
2117
2044
|
/** An element containing the given text. */
|
|
2118
|
-
declare const content:
|
|
2045
|
+
declare const content: ElementFactory;
|
|
2119
2046
|
/** The escape hatch: an element by `data-testid`. Prefer user-facing elements. */
|
|
2120
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;
|
|
2121
2050
|
/** The `banner` landmark — the page header. */
|
|
2122
|
-
declare const banner:
|
|
2051
|
+
declare const banner: LandmarkFactory;
|
|
2123
2052
|
/** The `complementary` landmark — an `<aside>`, a sidebar. */
|
|
2124
|
-
declare const complementary:
|
|
2053
|
+
declare const complementary: LandmarkFactory;
|
|
2125
2054
|
/** The `contentinfo` landmark — the page footer. */
|
|
2126
|
-
declare const contentinfo:
|
|
2055
|
+
declare const contentinfo: LandmarkFactory;
|
|
2127
2056
|
/** The `form` landmark — a form carrying an accessible name. */
|
|
2128
|
-
declare const form:
|
|
2057
|
+
declare const form: LandmarkFactory;
|
|
2129
2058
|
/** The `main` landmark — the primary content of the document. */
|
|
2130
|
-
declare const main:
|
|
2059
|
+
declare const main: LandmarkFactory;
|
|
2131
2060
|
/** The `navigation` landmark — a `<nav>`. Name it when a page has several. */
|
|
2132
|
-
declare const navigation:
|
|
2061
|
+
declare const navigation: LandmarkFactory;
|
|
2133
2062
|
/** The `region` landmark — a `<section>` carrying an accessible name. */
|
|
2134
|
-
declare const region:
|
|
2063
|
+
declare const region: LandmarkFactory;
|
|
2135
2064
|
/** The `search` landmark. */
|
|
2136
|
-
declare const search:
|
|
2065
|
+
declare const search: LandmarkFactory;
|
|
2137
2066
|
/**
|
|
2138
2067
|
* Restrict a descriptor to the inside of another — the answer to ambiguity,
|
|
2139
2068
|
* and the one the framework prefers over a test id.
|
|
@@ -2148,7 +2077,7 @@ declare const search: (name?: string, options?: ElementOptions) => ElementRef;
|
|
|
2148
2077
|
declare const within: (scope: ElementRef, target: ElementRef) => ElementRef;
|
|
2149
2078
|
//#endregion
|
|
2150
2079
|
//#region src/integrations/postgres/postgres.d.ts
|
|
2151
|
-
|
|
2080
|
+
type PostgresOptions = {
|
|
2152
2081
|
/**
|
|
2153
2082
|
* Map to a service in docker/compose.test.yaml. Defaults to the handle's
|
|
2154
2083
|
* key in the declared services record.
|
|
@@ -2158,7 +2087,7 @@ interface PostgresOptions {
|
|
|
2158
2087
|
image?: string;
|
|
2159
2088
|
/** Override environment variables. */
|
|
2160
2089
|
env?: Record<string, string>;
|
|
2161
|
-
}
|
|
2090
|
+
};
|
|
2162
2091
|
declare class PostgresHandle implements DatabasePort, ServiceHandle {
|
|
2163
2092
|
readonly type = "postgres";
|
|
2164
2093
|
composeName: null | string;
|
|
@@ -2195,7 +2124,7 @@ declare class PostgresHandle implements DatabasePort, ServiceHandle {
|
|
|
2195
2124
|
declare function postgres(options?: PostgresOptions): PostgresHandle;
|
|
2196
2125
|
//#endregion
|
|
2197
2126
|
//#region src/integrations/redis/redis.d.ts
|
|
2198
|
-
|
|
2127
|
+
type RedisOptions = {
|
|
2199
2128
|
/**
|
|
2200
2129
|
* Map to a service in docker/compose.test.yaml. Defaults to the handle's
|
|
2201
2130
|
* key in the declared services record.
|
|
@@ -2203,7 +2132,7 @@ interface RedisOptions {
|
|
|
2203
2132
|
composeService?: string;
|
|
2204
2133
|
/** Override image. */
|
|
2205
2134
|
image?: string;
|
|
2206
|
-
}
|
|
2135
|
+
};
|
|
2207
2136
|
declare class RedisHandle implements ServiceHandle {
|
|
2208
2137
|
readonly type = "redis";
|
|
2209
2138
|
composeName: null | string;
|
|
@@ -2232,7 +2161,7 @@ declare class RedisHandle implements ServiceHandle {
|
|
|
2232
2161
|
declare function redis(options?: RedisOptions): RedisHandle;
|
|
2233
2162
|
//#endregion
|
|
2234
2163
|
//#region src/integrations/sqlite/sqlite.d.ts
|
|
2235
|
-
|
|
2164
|
+
type SqliteOptions = {
|
|
2236
2165
|
/**
|
|
2237
2166
|
* Path to a SQL file used to initialize the database schema.
|
|
2238
2167
|
* Mutually exclusive with `prismaSchema`.
|
|
@@ -2245,7 +2174,7 @@ interface SqliteOptions {
|
|
|
2245
2174
|
* Mutually exclusive with `init`.
|
|
2246
2175
|
*/
|
|
2247
2176
|
prismaSchema?: string;
|
|
2248
|
-
}
|
|
2177
|
+
};
|
|
2249
2178
|
declare class SqliteHandle implements DatabasePort, ServiceHandle {
|
|
2250
2179
|
readonly type = "sqlite";
|
|
2251
2180
|
composeName: null | string;
|
|
@@ -2257,8 +2186,8 @@ declare class SqliteHandle implements DatabasePort, ServiceHandle {
|
|
|
2257
2186
|
private db;
|
|
2258
2187
|
private templatePath;
|
|
2259
2188
|
private workerDbPath;
|
|
2260
|
-
private initSql;
|
|
2261
|
-
private prismaSchema;
|
|
2189
|
+
private readonly initSql;
|
|
2190
|
+
private readonly prismaSchema;
|
|
2262
2191
|
private readonly options;
|
|
2263
2192
|
constructor(options?: SqliteOptions);
|
|
2264
2193
|
buildConnectionString(): string;
|
|
@@ -2311,7 +2240,7 @@ declare class SqliteHandle implements DatabasePort, ServiceHandle {
|
|
|
2311
2240
|
*/
|
|
2312
2241
|
declare function sqlite(options?: SqliteOptions): SqliteHandle;
|
|
2313
2242
|
//#endregion
|
|
2314
|
-
//#region src/
|
|
2243
|
+
//#region src/specification/contracts/filters.d.ts
|
|
2315
2244
|
/**
|
|
2316
2245
|
* A text filter on a provider request builder (`openai.chat({ user })`,
|
|
2317
2246
|
* `anthropic.messages({ system })`, …).
|
|
@@ -2330,13 +2259,12 @@ declare function sqlite(options?: SqliteOptions): SqliteHandle;
|
|
|
2330
2259
|
type TextFilter = Matcher | RegExp | string;
|
|
2331
2260
|
//#endregion
|
|
2332
2261
|
//#region src/integrations/anthropic/anthropic.d.ts
|
|
2333
|
-
|
|
2262
|
+
type AnthropicMessagesFilter = {
|
|
2334
2263
|
model?: TextFilter;
|
|
2335
2264
|
system?: TextFilter;
|
|
2336
2265
|
user?: TextFilter;
|
|
2337
2266
|
tools?: string[];
|
|
2338
|
-
}
|
|
2339
|
-
declare function buildReply(data: unknown): ContractResponse;
|
|
2267
|
+
};
|
|
2340
2268
|
/**
|
|
2341
2269
|
* Anthropic API intercept helpers.
|
|
2342
2270
|
*/
|
|
@@ -2355,19 +2283,19 @@ declare const anthropic: {
|
|
|
2355
2283
|
*/
|
|
2356
2284
|
messages(filter?: AnthropicMessagesFilter, url?: string): ContractRequest;
|
|
2357
2285
|
/** Response: wrap data in Anthropic messages format. */
|
|
2358
|
-
reply:
|
|
2286
|
+
reply(data: unknown): ContractResponse;
|
|
2359
2287
|
/** Response: return an Anthropic error. */
|
|
2360
2288
|
error(status: number, message?: string): ContractResponse;
|
|
2361
2289
|
/** Response: simulate a timeout. */
|
|
2362
2290
|
timeout(): ContractResponse;
|
|
2363
2291
|
};
|
|
2364
2292
|
//#endregion
|
|
2365
|
-
//#region src/
|
|
2293
|
+
//#region src/specification/contracts/http.d.ts
|
|
2366
2294
|
/**
|
|
2367
2295
|
* Request filters for the generic HTTP provider. Every field is a subset
|
|
2368
2296
|
* constraint — a request matches when all provided fields match.
|
|
2369
2297
|
*/
|
|
2370
|
-
|
|
2298
|
+
type HttpContractFilter = {
|
|
2371
2299
|
/**
|
|
2372
2300
|
* Body constraint. An object is a deep SUBSET match (toMatchObject-style)
|
|
2373
2301
|
* whose leaf values may be `match.*` matchers; a string is a containment
|
|
@@ -2378,16 +2306,16 @@ interface HttpContractFilter {
|
|
|
2378
2306
|
headers?: Record<string, RegExp | string>;
|
|
2379
2307
|
/** Query-param subset. string = exact value, RegExp = `test()`. */
|
|
2380
2308
|
query?: Record<string, RegExp | string>;
|
|
2381
|
-
}
|
|
2309
|
+
};
|
|
2382
2310
|
/** Init options shared by the response builders. */
|
|
2383
|
-
|
|
2311
|
+
type HttpResponseInit = {
|
|
2384
2312
|
/** Delay in ms before responding (for timeout testing). */
|
|
2385
2313
|
delay?: number;
|
|
2386
2314
|
/** Response headers, merged over the builder's own. */
|
|
2387
2315
|
headers?: Record<string, string>;
|
|
2388
2316
|
/** HTTP status code. */
|
|
2389
2317
|
status?: number;
|
|
2390
|
-
}
|
|
2318
|
+
};
|
|
2391
2319
|
/**
|
|
2392
2320
|
* Generic HTTP contract helpers for any URL. The url is absolute (string or
|
|
2393
2321
|
* RegExp), or a PATH FORM starting with `/` — `http.get('/articles/{{uuid}}')`
|
|
@@ -2418,20 +2346,19 @@ declare const http: {
|
|
|
2418
2346
|
};
|
|
2419
2347
|
//#endregion
|
|
2420
2348
|
//#region src/integrations/openai/openai.d.ts
|
|
2421
|
-
|
|
2349
|
+
type OpenAIChatFilter = {
|
|
2422
2350
|
model?: TextFilter;
|
|
2423
2351
|
system?: TextFilter;
|
|
2424
2352
|
user?: TextFilter;
|
|
2425
2353
|
tools?: string[];
|
|
2426
2354
|
temperature?: number;
|
|
2427
|
-
}
|
|
2428
|
-
|
|
2355
|
+
};
|
|
2356
|
+
type OpenAIResponsesFilter = {
|
|
2429
2357
|
model?: TextFilter;
|
|
2430
2358
|
system?: TextFilter;
|
|
2431
2359
|
user?: TextFilter;
|
|
2432
2360
|
tools?: string[];
|
|
2433
|
-
}
|
|
2434
|
-
declare function buildChatReply(data: unknown): ContractResponse;
|
|
2361
|
+
};
|
|
2435
2362
|
/**
|
|
2436
2363
|
* OpenAI API intercept helpers.
|
|
2437
2364
|
*/
|
|
@@ -2467,7 +2394,7 @@ declare const openai: {
|
|
|
2467
2394
|
* @example
|
|
2468
2395
|
* openai.reply({ categories: ['TECH'] })
|
|
2469
2396
|
*/
|
|
2470
|
-
reply:
|
|
2397
|
+
reply(data: unknown): ContractResponse;
|
|
2471
2398
|
/** Response: return an OpenAI error. */
|
|
2472
2399
|
error(status: number, message?: string): ContractResponse;
|
|
2473
2400
|
/** Response: return malformed content. */
|
|
@@ -2487,12 +2414,12 @@ declare const mockOf: MockPort;
|
|
|
2487
2414
|
//#endregion
|
|
2488
2415
|
//#region src/vitest/mock-of-date.d.ts
|
|
2489
2416
|
/** Interface for freezing and resetting the global Date in tests. */
|
|
2490
|
-
|
|
2417
|
+
type MockDatePort = {
|
|
2491
2418
|
/** Restore the real Date object. */
|
|
2492
2419
|
reset: () => void;
|
|
2493
2420
|
/** Freeze `Date.now()` and `new Date()` to the given value. */
|
|
2494
2421
|
set: (date: Date | number | string) => void;
|
|
2495
|
-
}
|
|
2422
|
+
};
|
|
2496
2423
|
/**
|
|
2497
2424
|
* Freeze or reset the global Date for deterministic time-dependent tests.
|
|
2498
2425
|
* Wraps the `mockdate` package.
|