@jterrazz/test 15.2.0 → 16.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +120 -418
- package/dist/ambiguity.js +7 -6
- package/dist/appium.adapter.js +12 -11
- package/dist/artifacts.js +25 -1698
- package/dist/browser/index.js +1484 -1142
- package/dist/checker.d.ts +1 -1
- package/dist/checker.js +1308 -76
- package/dist/fixtures.js +110 -0
- package/dist/index.d.ts +1482 -1011
- package/dist/index.js +1715 -1173
- package/dist/literate.js +1747 -0
- package/dist/match.js +625 -84
- package/dist/oxlint.cjs +3534 -1469
- package/dist/oxlint.d.cts +60 -48
- package/dist/oxlint.d.ts +60 -48
- package/dist/oxlint.js +3534 -1469
- package/dist/playwright.adapter.js +101 -6
- package/dist/{intercept.js → server.js} +11 -3
- package/dist/{queue.js → stream.js} +63 -4
- package/dist/vitest.d.ts +85 -36
- package/dist/vitest.js +177 -43
- package/package.json +31 -11
- package/dist/spec-document.js +0 -651
|
@@ -3,12 +3,76 @@ import { mkdtempSync } from "node:fs";
|
|
|
3
3
|
import { resolve } from "node:path";
|
|
4
4
|
import { tmpdir } from "node:os";
|
|
5
5
|
import { setTimeout } from "node:timers/promises";
|
|
6
|
-
//#region src/
|
|
6
|
+
//#region src/model/elements/substring-warning.ts
|
|
7
|
+
/**
|
|
8
|
+
* The transitional warning for a name that matches only as a SUBSTRING.
|
|
9
|
+
*
|
|
10
|
+
* Until 16.0 a descriptor's name matched any element whose accessible name
|
|
11
|
+
* CONTAINED it, so `link('Articles')` also designated "Read Articles" — and a
|
|
12
|
+
* test could pass for years against the wrong element without anyone noticing,
|
|
13
|
+
* because the wrong element was usually next to the right one. Exact is the
|
|
14
|
+
* default now.
|
|
15
|
+
*
|
|
16
|
+
* A lint rule cannot carry this: whether a name matched whole or in part is a
|
|
17
|
+
* fact about a RUN, and a verdict fed by a previous run's artefact is not
|
|
18
|
+
* deterministic. So it is a runtime warning, printed by the adapter that did
|
|
19
|
+
* the looking, at the moment it has both answers in hand — nothing found under
|
|
20
|
+
* exact, something found under substring.
|
|
21
|
+
*
|
|
22
|
+
* It is printed ONCE per descriptor per process: a scenario that clicks the
|
|
23
|
+
* same link in ten tests has one thing to fix, not ten lines to read.
|
|
24
|
+
*/
|
|
25
|
+
/** How long the two-release window lasts, stated in the message itself. */
|
|
26
|
+
const WINDOW = "16.0 and 16.1";
|
|
27
|
+
/** Descriptors already reported in this process. */
|
|
28
|
+
const reported = /* @__PURE__ */ new Set();
|
|
29
|
+
/** The identity a descriptor is deduplicated by — kind, name, and its scope chain. */
|
|
30
|
+
function identityOf(element) {
|
|
31
|
+
const own = `${element.kind}:${element.name ?? ""}`;
|
|
32
|
+
return element.scope === void 0 ? own : `${identityOf(element.scope)}>${own}`;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Report that `element` designated nothing as a whole name, but would have
|
|
36
|
+
* designated something as a substring. Returns whether a line was printed,
|
|
37
|
+
* which is what the adapters' own tests assert on.
|
|
38
|
+
*/
|
|
39
|
+
function warnSubstringOnly(element, where) {
|
|
40
|
+
const identity = identityOf(element);
|
|
41
|
+
if (reported.has(identity)) return false;
|
|
42
|
+
reported.add(identity);
|
|
43
|
+
console.warn(`@jterrazz/test: ${element.kind}('${element.name ?? ""}') matched nothing as a whole accessible name, and matched only as a SUBSTRING of a longer one (${where}). Names are exact from 16.0: write the name in full, scope it with \`within(…)\`, or state \`{ exact: false }\` to keep the old behaviour. This warning ships for ${WINDOW}; after that the substring match is gone and this is simply not found.`);
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
//#endregion
|
|
47
|
+
//#region src/seams/playwright/playwright.adapter.ts
|
|
7
48
|
/** How many candidates the ambiguity error enumerates before truncating. */
|
|
8
49
|
const MAX_REPORTED_MATCHES = 10;
|
|
9
50
|
const VALUE_TIMEOUT_MS = 3e4;
|
|
10
51
|
const VALUE_POLL_MS = 100;
|
|
11
52
|
/**
|
|
53
|
+
* The roles a FIELD can carry — what `field('Name')` designates.
|
|
54
|
+
*
|
|
55
|
+
* A field is named by its ACCESSIBLE name, not by the text of the label
|
|
56
|
+
* element that happens to wrap it: `<label><span>Channel</span><select>…` has
|
|
57
|
+
* a label whose text carries every option of the select, while the control's
|
|
58
|
+
* accessible name is `Channel` — which is what the ARIA tree shows and what a
|
|
59
|
+
* screen reader reads. Resolving by label text alone made the ordinary
|
|
60
|
+
* wrapping-label form unreachable under the 16.0 whole-name default.
|
|
61
|
+
*
|
|
62
|
+
* The label-text locator stays in the union for the controls ARIA gives no
|
|
63
|
+
* role at all (a date or colour input), which nothing else would find.
|
|
64
|
+
*/
|
|
65
|
+
const FIELD_ROLES = [
|
|
66
|
+
"checkbox",
|
|
67
|
+
"combobox",
|
|
68
|
+
"radio",
|
|
69
|
+
"searchbox",
|
|
70
|
+
"slider",
|
|
71
|
+
"spinbutton",
|
|
72
|
+
"switch",
|
|
73
|
+
"textbox"
|
|
74
|
+
];
|
|
75
|
+
/**
|
|
12
76
|
* Translate a user-facing descriptor into a playwright locator, resolving
|
|
13
77
|
* `scope` outside-in so `within(navigation(), link('X'))` searches the nav
|
|
14
78
|
* subtree. No `.first()` anywhere: playwright's strict mode is the mechanism
|
|
@@ -17,9 +81,16 @@ const VALUE_POLL_MS = 100;
|
|
|
17
81
|
*/
|
|
18
82
|
function locate(root, element) {
|
|
19
83
|
const scope = element.scope ? locate(root, element.scope) : root;
|
|
20
|
-
const exact = element.exact ??
|
|
84
|
+
const exact = element.exact ?? true;
|
|
21
85
|
const name = element.name ?? "";
|
|
22
|
-
if (element.kind === "field")
|
|
86
|
+
if (element.kind === "field") {
|
|
87
|
+
let field = scope.getByLabel(name, { exact });
|
|
88
|
+
for (const role of FIELD_ROLES) field = field.or(scope.getByRole(role, {
|
|
89
|
+
exact,
|
|
90
|
+
name
|
|
91
|
+
}));
|
|
92
|
+
return field;
|
|
93
|
+
}
|
|
23
94
|
if (element.kind === "testId") return scope.getByTestId(name);
|
|
24
95
|
if (element.kind === "text") return scope.getByText(name, { exact });
|
|
25
96
|
return scope.getByRole(element.kind, element.name === void 0 ? { exact } : {
|
|
@@ -62,15 +133,37 @@ async function act(page, element, action) {
|
|
|
62
133
|
try {
|
|
63
134
|
return await action(locate(page, element));
|
|
64
135
|
} catch (error) {
|
|
65
|
-
if (!isStrictViolation(error))
|
|
136
|
+
if (!isStrictViolation(error)) {
|
|
137
|
+
await reportSubstringOnly(page, element);
|
|
138
|
+
throw error;
|
|
139
|
+
}
|
|
66
140
|
const culprit = await findAmbiguousLevel(page, element);
|
|
141
|
+
const matches = await captureMatches(locate(page, culprit));
|
|
67
142
|
throw new AmbiguousElementError(describeAmbiguity({
|
|
68
143
|
element: culprit,
|
|
69
|
-
matches
|
|
144
|
+
matches,
|
|
70
145
|
url: page.url()
|
|
71
146
|
}));
|
|
72
147
|
}
|
|
73
148
|
}
|
|
149
|
+
/**
|
|
150
|
+
* Say so when a descriptor found NOTHING as a whole name but would have found
|
|
151
|
+
* something as a substring — the one shape the whole-name default changes.
|
|
152
|
+
*
|
|
153
|
+
* Asked only on the failure path, and only for a descriptor that stated no
|
|
154
|
+
* `exact` of its own: an author who wrote `{ exact: false }` chose the
|
|
155
|
+
* substring, and one who wrote `{ exact: true }` was already exact.
|
|
156
|
+
*/
|
|
157
|
+
async function reportSubstringOnly(page, element) {
|
|
158
|
+
if (element.exact !== void 0 || element.name === void 0) return;
|
|
159
|
+
try {
|
|
160
|
+
if (await locate(page, element).count() > 0) return;
|
|
161
|
+
if (await locate(page, {
|
|
162
|
+
...element,
|
|
163
|
+
exact: false
|
|
164
|
+
}).count() > 0) warnSubstringOnly(element, page.url());
|
|
165
|
+
} catch {}
|
|
166
|
+
}
|
|
74
167
|
/** Walk the scope chain outside-in; the outermost ambiguous level is the fault. */
|
|
75
168
|
async function findAmbiguousLevel(page, element) {
|
|
76
169
|
const chain = [];
|
|
@@ -222,6 +315,7 @@ var PlaywrightAdapter = class {
|
|
|
222
315
|
}
|
|
223
316
|
try {
|
|
224
317
|
const page = await context.newPage();
|
|
318
|
+
if (options.clock !== void 0) await page.clock.setFixedTime(new Date(options.clock));
|
|
225
319
|
const consoleMessages = [];
|
|
226
320
|
page.on("console", (message) => {
|
|
227
321
|
consoleMessages.push({
|
|
@@ -278,7 +372,8 @@ var PlaywrightAdapter = class {
|
|
|
278
372
|
/** Screenshot the failing state into a temp file; never masks the original error. */
|
|
279
373
|
async captureEvidence(page) {
|
|
280
374
|
try {
|
|
281
|
-
const
|
|
375
|
+
const dir = mkdtempSync(resolve(tmpdir(), "spec-website-"));
|
|
376
|
+
const path = resolve(dir, "failure.png");
|
|
282
377
|
await page.screenshot({
|
|
283
378
|
fullPage: true,
|
|
284
379
|
path
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
//#region src/
|
|
1
|
+
import { a as isStreamBody, n as ContractQueue, t as toReadableStream } from "./stream.js";
|
|
2
|
+
//#region src/seams/msw/handlers.ts
|
|
3
3
|
/**
|
|
4
4
|
* What a chain with zero contracts gets where the facet's scope is KNOWN but
|
|
5
5
|
* not total — the node engine, which shares a process with everything else the
|
|
@@ -31,6 +31,14 @@ function toMswResponse(msw, response) {
|
|
|
31
31
|
},
|
|
32
32
|
status
|
|
33
33
|
});
|
|
34
|
+
if (isStreamBody(body)) return new msw.HttpResponse(toReadableStream(body), {
|
|
35
|
+
headers: {
|
|
36
|
+
...NEVER_CACHED,
|
|
37
|
+
"content-type": body.contentType,
|
|
38
|
+
...headers
|
|
39
|
+
},
|
|
40
|
+
status
|
|
41
|
+
});
|
|
34
42
|
if (typeof body === "string") return new msw.HttpResponse(body, {
|
|
35
43
|
headers: {
|
|
36
44
|
...NEVER_CACHED,
|
|
@@ -107,7 +115,7 @@ async function observe(request) {
|
|
|
107
115
|
};
|
|
108
116
|
}
|
|
109
117
|
//#endregion
|
|
110
|
-
//#region src/
|
|
118
|
+
//#region src/seams/msw/server.ts
|
|
111
119
|
let mswModule = null;
|
|
112
120
|
let mswHttp = null;
|
|
113
121
|
async function loadMsw() {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { t as CaptureScope } from "./match.js";
|
|
2
|
-
import {
|
|
3
|
-
//#region src/
|
|
2
|
+
import { A as placeholderPatternSource, D as hasPlaceholders, P as textEquals } from "./literate.js";
|
|
3
|
+
//#region src/model/contracts/contract.ts
|
|
4
4
|
/**
|
|
5
5
|
* Declare one contract. Identity function — its value is the enforced shape
|
|
6
6
|
* and the naming convention:
|
|
@@ -86,7 +86,36 @@ function contractsOf(input) {
|
|
|
86
86
|
return flatten([input]);
|
|
87
87
|
}
|
|
88
88
|
//#endregion
|
|
89
|
-
//#region src/
|
|
89
|
+
//#region src/model/contracts/types.ts
|
|
90
|
+
/**
|
|
91
|
+
* The marker that tells a STREAMED body from a value that happens to be an
|
|
92
|
+
* object. A body reaches three different engines (msw's server, msw's worker,
|
|
93
|
+
* the `node:http` stub) and each of them serialises "an object" as JSON, so a
|
|
94
|
+
* stream declared as a plain value arrives as `{}`. The tag is what each
|
|
95
|
+
* engine recognises before it reaches for `JSON.stringify`.
|
|
96
|
+
*/
|
|
97
|
+
const STREAM_BODY = "@jterrazz/test:stream";
|
|
98
|
+
/** Is this body a declared stream rather than a value to serialise? */
|
|
99
|
+
function isStreamBody(body) {
|
|
100
|
+
return typeof body === "object" && body !== null && "kind" in body && body.kind === "@jterrazz/test:stream";
|
|
101
|
+
}
|
|
102
|
+
//#endregion
|
|
103
|
+
//#region src/model/timing/pause.ts
|
|
104
|
+
/**
|
|
105
|
+
* One answer to "wait" — the only `setTimeout` the model owns.
|
|
106
|
+
*
|
|
107
|
+
* Two things in the framework genuinely have to wait: the spacing between the
|
|
108
|
+
* pieces of a declared stream, and the poll behind `waitUntil`. Everything
|
|
109
|
+
* else that waits is a spec doing it by hand, which is what J2 refuses. Stated
|
|
110
|
+
* once so there is one place to look when time stops behaving.
|
|
111
|
+
*/
|
|
112
|
+
async function pause(ms) {
|
|
113
|
+
await new Promise((resolve) => {
|
|
114
|
+
setTimeout(resolve, ms);
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
//#endregion
|
|
118
|
+
//#region src/model/contracts/queue.ts
|
|
90
119
|
/**
|
|
91
120
|
* The ONE contract queue — pure, engine-agnostic (no MSW, no node:http).
|
|
92
121
|
* Both engines consume it: the MSW integration on api/jobs chains, and the
|
|
@@ -259,4 +288,34 @@ var ContractQueue = class {
|
|
|
259
288
|
}
|
|
260
289
|
};
|
|
261
290
|
//#endregion
|
|
262
|
-
|
|
291
|
+
//#region src/model/contracts/stream.ts
|
|
292
|
+
/**
|
|
293
|
+
* A declared stream, made real — ONE serialisation for all three engines.
|
|
294
|
+
*
|
|
295
|
+
* msw's server and msw's worker take a `ReadableStream` body verbatim
|
|
296
|
+
* (https://mswjs.io/docs/http/mocking-responses/streaming) and the `node:http`
|
|
297
|
+
* stub pipes one into its response, so what "arriving in pieces" MEANS —
|
|
298
|
+
* the order, the spacing, the close after the last chunk — is stated here and
|
|
299
|
+
* nowhere else. A second copy of it would be a second answer to "what does the
|
|
300
|
+
* subject read", and the facets would drift.
|
|
301
|
+
*/
|
|
302
|
+
/**
|
|
303
|
+
* The pieces of `body`, in order, `delayBetweenChunks` milliseconds apart,
|
|
304
|
+
* closing after the last one.
|
|
305
|
+
*/
|
|
306
|
+
function toReadableStream(body) {
|
|
307
|
+
const encoder = new TextEncoder();
|
|
308
|
+
let index = 0;
|
|
309
|
+
return new ReadableStream({ async pull(controller) {
|
|
310
|
+
const chunk = body.chunks[index];
|
|
311
|
+
if (chunk === void 0) {
|
|
312
|
+
controller.close();
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
if (body.delayBetweenChunks > 0 && index > 0) await pause(body.delayBetweenChunks);
|
|
316
|
+
index += 1;
|
|
317
|
+
controller.enqueue(encoder.encode(chunk));
|
|
318
|
+
} });
|
|
319
|
+
}
|
|
320
|
+
//#endregion
|
|
321
|
+
export { isStreamBody as a, defineContracts as c, STREAM_BODY as i, isContract as l, ContractQueue as n, contractsOf as o, pause as r, defineContract as s, toReadableStream as t, isContracts as u };
|
package/dist/vitest.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { TestProjectInlineConfiguration, ViteUserConfig } from "vitest/config";
|
|
2
2
|
import { UserConfig, UserConfigFn } from "vite";
|
|
3
|
-
//#region src/
|
|
3
|
+
//#region src/runner/literate-plugin.d.ts
|
|
4
4
|
type LiterateOptions = {
|
|
5
5
|
/**
|
|
6
6
|
* Globs added to the project's test include. Default: every `.spec.yaml`
|
|
@@ -40,7 +40,7 @@ type LiteratePlugin = {
|
|
|
40
40
|
};
|
|
41
41
|
declare function literate(options: LiterateOptions): LiteratePlugin;
|
|
42
42
|
//#endregion
|
|
43
|
-
//#region src/
|
|
43
|
+
//#region src/runner/preset.d.ts
|
|
44
44
|
/**
|
|
45
45
|
* Options are a vite/vitest config plus one key of the framework's own.
|
|
46
46
|
* Everything stated here wins over the preset's defaults.
|
|
@@ -58,24 +58,26 @@ type SpecConfig = {
|
|
|
58
58
|
} & ViteUserConfig;
|
|
59
59
|
declare function defineSpecConfig(config?: SpecConfig): ViteUserConfig;
|
|
60
60
|
//#endregion
|
|
61
|
-
//#region src/
|
|
61
|
+
//#region src/runner/facet-project.d.ts
|
|
62
62
|
/**
|
|
63
|
-
* The
|
|
64
|
-
* kind runs in.
|
|
63
|
+
* The node facets' canonical projects.
|
|
65
64
|
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
65
|
+
* Each one collects the facet's own tree — `specs/<facet>/**\/*.spec.ts`, the
|
|
66
|
+
* suffix that says "the assembled product" — carries the preset's budgets and
|
|
67
|
+
* artefact directory, and runs in group 0 — the browser facets follow, so two
|
|
68
|
+
* Chromiums never share a slot on a two-vCPU runner. `{ include, exclude,
|
|
69
|
+
* timeout, serial }` is what a repository states when its tree, its budget or
|
|
70
|
+
* its parallelism differs;
|
|
71
|
+
* everything else is the framework's.
|
|
72
72
|
*/
|
|
73
73
|
/** What every helper accepts on top of its canonical project. */
|
|
74
|
-
type
|
|
74
|
+
type FacetProjectOptions = {
|
|
75
75
|
/**
|
|
76
|
-
*
|
|
77
|
-
* `
|
|
78
|
-
* `
|
|
76
|
+
* Globs this project stays out of. On a facet helper they ADD to the
|
|
77
|
+
* preset's (`_fixtures/` and vitest's own), because vite concatenates. On
|
|
78
|
+
* `unit()` and `component()`, whose canonical `exclude` exists only to
|
|
79
|
+
* guard their canonical `include`, stating one replaces it — so state
|
|
80
|
+
* both or neither there.
|
|
79
81
|
*/
|
|
80
82
|
exclude?: string[];
|
|
81
83
|
/** Replace the canonical `include` globs. */
|
|
@@ -89,16 +91,31 @@ type CommonOptions = {
|
|
|
89
91
|
/** Raise (or lower) the 30s budget for this project alone. */
|
|
90
92
|
timeout?: number;
|
|
91
93
|
};
|
|
92
|
-
|
|
93
|
-
|
|
94
|
+
//#endregion
|
|
95
|
+
//#region src/facets/api/api.project.d.ts
|
|
96
|
+
/** `api()` — the app met through HTTP. */
|
|
97
|
+
declare function api(options?: FacetProjectOptions): TestProjectInlineConfiguration;
|
|
98
|
+
//#endregion
|
|
99
|
+
//#region src/facets/cli/cli.project.d.ts
|
|
100
|
+
/** `cli()` — a binary, and the documents that specify it. */
|
|
101
|
+
type CliProjectOptions = FacetProjectOptions & {
|
|
94
102
|
/**
|
|
95
|
-
* The
|
|
96
|
-
*
|
|
103
|
+
* The literate door, wired by default: every `specs/cli/**\/*.spec.yaml`
|
|
104
|
+
* becomes a test file bound to `specs/cli/cli.specification.ts`. State
|
|
105
|
+
* `specification` to name another runner, `include` to narrow the glob, or
|
|
106
|
+
* `false` to collect no documents at all.
|
|
97
107
|
*/
|
|
98
|
-
|
|
108
|
+
literate?: false | Partial<LiterateOptions>;
|
|
99
109
|
};
|
|
100
|
-
/**
|
|
101
|
-
|
|
110
|
+
/**
|
|
111
|
+
* A cli project collects the facet's `.test.ts` files AND its documents: a
|
|
112
|
+
* `<case>.spec.yaml` is a test file of its own (docs/07 § the literate door),
|
|
113
|
+
* so the plugin's glob has to join the include of the project that collects
|
|
114
|
+
* them — which is this one, never the root config.
|
|
115
|
+
*/
|
|
116
|
+
declare function cli(options?: CliProjectOptions): TestProjectInlineConfiguration;
|
|
117
|
+
//#endregion
|
|
118
|
+
//#region src/facets/component/component.project.d.ts
|
|
102
119
|
/** `component()` — rendered units, beside the components they cover. */
|
|
103
120
|
type ComponentProjectOptions = {
|
|
104
121
|
/** Freeze the page's `Date` for every render of the project. */
|
|
@@ -130,14 +147,35 @@ type ComponentProjectOptions = {
|
|
|
130
147
|
* provider). A router belongs on the chain instead: it is a test's Given.
|
|
131
148
|
*/
|
|
132
149
|
wrap?: string;
|
|
133
|
-
} &
|
|
150
|
+
} & FacetProjectOptions;
|
|
134
151
|
/**
|
|
135
|
-
* A
|
|
136
|
-
*
|
|
137
|
-
*
|
|
138
|
-
*
|
|
152
|
+
* A component test renders in a real Chromium — the browser the website facet
|
|
153
|
+
* already drives through the same `playwright` peer.
|
|
154
|
+
*
|
|
155
|
+
* The default `include` reaches the whole project, so the default `exclude`
|
|
156
|
+
* keeps it out of `specs/`, where the assembled product is specified. State
|
|
157
|
+
* your own `include` and the exclusion is yours to state too.
|
|
139
158
|
*/
|
|
140
|
-
declare function
|
|
159
|
+
declare function component(options?: ComponentProjectOptions): Promise<TestProjectInlineConfiguration>;
|
|
160
|
+
//#endregion
|
|
161
|
+
//#region src/facets/integration/integration.project.d.ts
|
|
162
|
+
/** `integration()` — a module against real services, or against a golden. */
|
|
163
|
+
declare function integration(options?: FacetProjectOptions): TestProjectInlineConfiguration;
|
|
164
|
+
//#endregion
|
|
165
|
+
//#region src/facets/jobs/jobs.project.d.ts
|
|
166
|
+
/** `jobs()` — what a name triggers in-process. */
|
|
167
|
+
declare function jobs(options?: FacetProjectOptions): TestProjectInlineConfiguration;
|
|
168
|
+
//#endregion
|
|
169
|
+
//#region src/facets/mobile/mobile.project.d.ts
|
|
170
|
+
/**
|
|
171
|
+
* `mobile()` — the app on a simulator. A simulator is the one thing that
|
|
172
|
+
* cannot share a machine with itself, so it runs last and alone (group 3).
|
|
173
|
+
*/
|
|
174
|
+
declare function mobile(options?: FacetProjectOptions): TestProjectInlineConfiguration;
|
|
175
|
+
//#endregion
|
|
176
|
+
//#region src/facets/website/website.project.d.ts
|
|
177
|
+
/** `website()` — the assembled product, met through a served page. */
|
|
178
|
+
type WebsiteProjectOptions = FacetProjectOptions;
|
|
141
179
|
/**
|
|
142
180
|
* A website spec meets the assembled product through a served page, in the
|
|
143
181
|
* Chromium playwright drives. It opens a browser, so it is scheduled before the
|
|
@@ -145,14 +183,25 @@ declare function unit(options?: UnitProjectOptions): TestProjectInlineConfigurat
|
|
|
145
183
|
* a 2-vCPU runner.
|
|
146
184
|
*/
|
|
147
185
|
declare function website(options?: WebsiteProjectOptions): TestProjectInlineConfiguration;
|
|
186
|
+
//#endregion
|
|
187
|
+
//#region src/runner/projects.d.ts
|
|
188
|
+
/** `unit()` — module tests, beside the modules they cover (CONVENTIONS I2). */
|
|
189
|
+
type UnitProjectOptions = FacetProjectOptions & {
|
|
190
|
+
/**
|
|
191
|
+
* The trees module tests live in, when they do not live in `src/`. Each
|
|
192
|
+
* root becomes `<root>/**\/*.test.ts`; `include` states the globs outright.
|
|
193
|
+
*/
|
|
194
|
+
roots?: string[];
|
|
195
|
+
};
|
|
148
196
|
/**
|
|
149
|
-
* A
|
|
150
|
-
*
|
|
151
|
-
*
|
|
152
|
-
*
|
|
153
|
-
*
|
|
154
|
-
*
|
|
197
|
+
* A module test runs beside the module it covers: no DOM, no browser, no
|
|
198
|
+
* pipeline. Its suffix is `.test.ts` and its place is OUTSIDE `specs/`, where
|
|
199
|
+
* the assembled product is specified in `.spec.ts` — the two exclusions say
|
|
200
|
+
* exactly that. `.test.tsx` is the third exclusion because a rendered thing is
|
|
201
|
+
* a component test: a project's dependency scan reads every file its globs
|
|
202
|
+
* match, and a `.tsx` collected here would be optimised for a page that never
|
|
203
|
+
* opens.
|
|
155
204
|
*/
|
|
156
|
-
declare function
|
|
205
|
+
declare function unit(options?: UnitProjectOptions): TestProjectInlineConfiguration;
|
|
157
206
|
//#endregion
|
|
158
|
-
export { type ComponentProjectOptions, type LiterateOptions, type LiteratePlugin, type SpecConfig, type UnitProjectOptions, type WebsiteProjectOptions, component, defineSpecConfig, literate, unit, website };
|
|
207
|
+
export { type CliProjectOptions, type ComponentProjectOptions, type FacetProjectOptions, type LiterateOptions, type LiteratePlugin, type SpecConfig, type UnitProjectOptions, type WebsiteProjectOptions, api, cli, component, defineSpecConfig, integration, jobs, literate, mobile, unit, website };
|