@jterrazz/test 17.0.0 → 18.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/dist/browser/index.js +1 -2
- package/dist/browser/services.js +1 -2
- package/dist/browser/stubs.js +1 -2
- package/dist/{shared-index.browser.js → browser-shared-index.browser.js} +82 -40
- package/dist/checker.js +15 -824
- package/dist/in-place.global-setup.d.ts +1 -0
- package/dist/in-place.global-setup.js +1 -1
- package/dist/index.d.ts +2 -14
- package/dist/index.js +83 -204
- package/dist/mobile-session.global-setup.d.ts +1 -0
- package/dist/oxlint.cjs +2 -2
- package/dist/oxlint.js +67 -2321
- package/dist/route-coverage.global-setup.d.ts +1 -0
- package/dist/route-coverage.global-setup.js +1 -2
- package/dist/services.global-setup.d.ts +1 -1
- package/dist/services.global-setup.js +1 -2
- package/dist/services.js +1 -1
- package/dist/shared-appium.adapter.js +14 -14
- package/dist/shared-artifacts.js +7 -5
- package/dist/shared-derived-projects.js +23 -8
- package/dist/shared-in-place.js +1 -1
- package/dist/shared-ledger.js +1 -1
- package/dist/shared-listened-port.js +19 -0
- package/dist/shared-literate-plugin.js +2 -1
- package/dist/shared-pause.js +12 -1
- package/dist/shared-playwright.adapter.js +17 -19
- package/dist/shared-process-alive.js +13 -0
- package/dist/shared-record-key.js +13 -0
- package/dist/shared-serve.js +107 -65
- package/dist/shared-services.global-setup.js +4 -9
- package/dist/shared-session-ledger.js +10 -6
- package/dist/shared-spec-document.js +2 -64
- package/dist/shared-spec-grammar.js +66 -0
- package/dist/shared-specs-modules.js +1033 -0
- package/dist/{shared-stream-body.js → shared-stub-reply.js} +40 -3
- package/dist/shared-stub-server.js +30 -78
- package/dist/shared-temp-root.global-setup.js +3 -10
- package/dist/shared-testcontainers.adapter.js +111 -8
- package/dist/temp-root.global-setup.js +1 -2
- package/dist/vitest.d.ts +17 -22
- package/dist/vitest.js +37 -53
- package/docs/concepts/architecture.md +20 -20
- package/docs/concepts/conventions.md +2 -2
- package/docs/guides/cli-specs.md +2 -0
- package/docs/guides/develop.md +20 -17
- package/docs/guides/mobile-specs.md +3 -3
- package/docs/guides/release.md +4 -3
- package/docs/guides/website-specs.md +1 -1
- package/docs/reference/linting.md +2 -2
- package/docs/reference/services.md +9 -9
- package/docs/reference/stubs.md +4 -2
- package/docs/start/set-up-a-project.md +31 -17
- package/package.json +10 -10
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { i as writePendingRewrites } from "./shared-in-place.js";
|
|
2
|
-
import {
|
|
2
|
+
import { u as VITEST_ARTIFACTS_DIR } from "./shared-artifacts.js";
|
|
3
3
|
import { t as setupEntry } from "./shared-setup-entry.js";
|
|
4
4
|
import { join } from "node:path";
|
|
5
5
|
import { randomUUID } from "node:crypto";
|
package/dist/index.d.ts
CHANGED
|
@@ -514,9 +514,6 @@ interface ApiSpecification<DatabaseKey extends string = string> extends ChainOf<
|
|
|
514
514
|
}, ApiSpecification<DatabaseKey>> {}
|
|
515
515
|
//#endregion
|
|
516
516
|
//#region src/specifications/api/api.handle.d.ts
|
|
517
|
-
type ApiConfig = ServicesConfig & {
|
|
518
|
-
server?: ServerPort | undefined;
|
|
519
|
-
};
|
|
520
517
|
/** Options for {@link startApi | specification.api}. */
|
|
521
518
|
type ApiSpecificationOptions<Services extends ServiceRecord = ServiceRecord> = {
|
|
522
519
|
/** Project root override; by default the nearest `package.json` above the specification file (`no-redundant-root`). */
|
|
@@ -1005,9 +1002,6 @@ type Job = {
|
|
|
1005
1002
|
name: string;
|
|
1006
1003
|
execute: () => Promise<void>;
|
|
1007
1004
|
};
|
|
1008
|
-
type JobConfig = ServicesConfig & {
|
|
1009
|
-
jobs?: Job[] | undefined;
|
|
1010
|
-
};
|
|
1011
1005
|
/** Options for {@link startJob | specification.job}. */
|
|
1012
1006
|
type JobSpecificationOptions<Services extends ServiceRecord = ServiceRecord> = {
|
|
1013
1007
|
/** Named jobs `job.trigger(name)` runs: a factory of the started services, or an array. */
|
|
@@ -1239,10 +1233,6 @@ interface MobileSpecification extends ChainOf<typeof composition$2, {
|
|
|
1239
1233
|
}, MobileSpecification> {}
|
|
1240
1234
|
//#endregion
|
|
1241
1235
|
//#region src/specifications/mobile/mobile.handle.d.ts
|
|
1242
|
-
type MobileConfig = StubsConfig & {
|
|
1243
|
-
bundleId?: string | undefined;
|
|
1244
|
-
device?: (() => Promise<DevicePort>) | undefined;
|
|
1245
|
-
};
|
|
1246
1236
|
/** The stub backend behind the app; nothing is injected: wire `backendUrl` into your own bundler env. */
|
|
1247
1237
|
type MobileBackendOptions = {
|
|
1248
1238
|
/** Fixed port: Metro inlines `EXPO_PUBLIC_*` at serve time, so a stable port keeps a warm Metro valid. */
|
|
@@ -1562,9 +1552,6 @@ type WebsiteHandle = {
|
|
|
1562
1552
|
declare function startWebsite<Services extends ServiceRecord>(options: WebsiteSpecificationOptions<Services>): Promise<WebsiteHandle>;
|
|
1563
1553
|
//#endregion
|
|
1564
1554
|
//#region src/specifications/specification.d.ts
|
|
1565
|
-
/** Every specification's configuration, as one. */
|
|
1566
|
-
type SpecificationConfig = ApiConfig & CliConfig & JobConfig & MobileConfig & WebsiteConfig;
|
|
1567
|
-
/** Created in a `*.specification.ts` under `specs/`, destructured with canonical names, cleaned up via `afterAll(cleanup)` (`runner-in-specification-file`, `no-runner-alias`, `cleanup-passed-to-after-all`). */
|
|
1568
1555
|
/** The seven constructors, and only seven: the framework's whole entry surface. */
|
|
1569
1556
|
type Specification = {
|
|
1570
1557
|
api: typeof startApi;
|
|
@@ -1575,6 +1562,7 @@ type Specification = {
|
|
|
1575
1562
|
mobile: typeof startMobile;
|
|
1576
1563
|
website: typeof startWebsite;
|
|
1577
1564
|
};
|
|
1565
|
+
/** Created in a `*.specification.ts` under `specs/`, destructured with canonical names, cleaned up via `afterAll(cleanup)` (`runner-in-specification-file`, `no-runner-alias`, `cleanup-passed-to-after-all`). */
|
|
1578
1566
|
export declare const specification: Specification;
|
|
1579
1567
|
//#endregion
|
|
1580
1568
|
//#region src/specifications/component/render/scenario.d.ts
|
|
@@ -1924,5 +1912,5 @@ declare module 'vitest' {
|
|
|
1924
1912
|
}
|
|
1925
1913
|
}
|
|
1926
1914
|
//#endregion
|
|
1927
|
-
export { type ApiHandle, type ApiSpecification, type ApiSpecificationOptions, type BaseResult, type BrowserConsoleMessage, type BrowserLinkElement, type BrowserMetaElement, type BrowserOpenOptions, type BrowserPage, type BrowserPort, type CallResult, type CaptureScope, type CliEnv, type CliHandle, type CliInput, type CliOutput, type CliPort, type CliResult, type CliSpecification, type CliSpecificationOptions, type ComponentChain, type ComponentScenario, type ComponentUi, type ComponentVisitor, type ContainerAccessor, type DeviceOpenOptions, type DevicePort, type DeviceScreen, type DeviceTimeouts, type DirectoryAccessor, type DiskDirectory, type DiskFile, type DiskTarget, type DockerSpecConfig, type DomMount, type ElementKind, type ElementMatch, type ExecOptions, type FetchResult, type FileAccessor, type FileAt, type FilesystemAccessor, type HonoApp, type HttpResult, type IntegrationHandle, type IntegrationSpecification, type IntegrationSpecificationOptions, type Job, type JobHandle, type JobResult, type JobSpecification, type JobSpecificationOptions, type JsonAccessor, type LandmarkKind, type LintConfig, type LintHandle, type LintResult, type LintRules, type LintRunOptions, type LintSpecification, type LintSpecificationOptions, type LiterateRunFlags, type LiterateServeRegistration, type MatchFixtureOptions, Matcher, type MatcherKind, type MobileBackendOptions, type MobileElementKind, type MobileElementMatch, type MobileHandle, type MobileScenario, type MobileSpecification, type MobileSpecificationOptions, type MobileVisitor, type MockOfOptions, type MockPort, type PackedPackage, type PageResult, type PinnedClock, type PositionedCases, type PositionedRuleTester, type PrepareStep, type RelativePath, type RenderResult, type RenderSubject, type RenderedText, type ResponseAccessor, type ScreenNode, type ScreenResult, type ServerPort, type ServerRequestOptions, type ServerResponse, type ServerSpec, type SpecDocument, type SpecEnvToken, type SpecFileAssertion, type SpecFixture, type SpecRun, type SpecServeEntry, type SpecStream, type
|
|
1915
|
+
export { type ApiHandle, type ApiSpecification, type ApiSpecificationOptions, type BaseResult, type BrowserConsoleMessage, type BrowserLinkElement, type BrowserMetaElement, type BrowserOpenOptions, type BrowserPage, type BrowserPort, type CallResult, type CaptureScope, type CliEnv, type CliHandle, type CliInput, type CliOutput, type CliPort, type CliResult, type CliSpecification, type CliSpecificationOptions, type ComponentChain, type ComponentScenario, type ComponentUi, type ComponentVisitor, type ContainerAccessor, type DeviceOpenOptions, type DevicePort, type DeviceScreen, type DeviceTimeouts, type DirectoryAccessor, type DiskDirectory, type DiskFile, type DiskTarget, type DockerSpecConfig, type DomMount, type ElementKind, type ElementMatch, type ExecOptions, type FetchResult, type FileAccessor, type FileAt, type FilesystemAccessor, type HonoApp, type HttpResult, type IntegrationHandle, type IntegrationSpecification, type IntegrationSpecificationOptions, type Job, type JobHandle, type JobResult, type JobSpecification, type JobSpecificationOptions, type JsonAccessor, type LandmarkKind, type LintConfig, type LintHandle, type LintResult, type LintRules, type LintRunOptions, type LintSpecification, type LintSpecificationOptions, type LiterateRunFlags, type LiterateServeRegistration, type MatchFixtureOptions, Matcher, type MatcherKind, type MobileBackendOptions, type MobileElementKind, type MobileElementMatch, type MobileHandle, type MobileScenario, type MobileSpecification, type MobileSpecificationOptions, type MobileVisitor, type MockOfOptions, type MockPort, type PackedPackage, type PageResult, type PinnedClock, type PositionedCases, type PositionedRuleTester, type PrepareStep, type RelativePath, type RenderResult, type RenderSubject, type RenderedText, type ResponseAccessor, type ScreenNode, type ScreenResult, type ServerPort, type ServerRequestOptions, type ServerResponse, type ServerSpec, type SpecDocument, type SpecEnvToken, type SpecFileAssertion, type SpecFixture, type SpecRun, type SpecServeEntry, type SpecStream, type TableAccessor, type Target, type TargetOptions, type TextAccessor, type VisitScenario, type WaitUntilOptions, type WebsiteBackendOptions, type WebsiteHandle, type WebsiteSpecification, type WebsiteSpecificationOptions, type WebsiteVisitor, directory, file, match };
|
|
1928
1916
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -1,16 +1,19 @@
|
|
|
1
|
-
import { a as
|
|
1
|
+
import { a as endGroup, i as SIGNALS_GROUPS, o as signalGroup, r as KILL_GRACE_MS, t as ServedProcess } from "./shared-serve.js";
|
|
2
2
|
import { i as safeRealpath, r as resolveRoot } from "./shared-resolve.js";
|
|
3
|
-
import {
|
|
3
|
+
import { a as getCallerDir, n as deferRewritesTo, r as writeInPlace, t as callSiteOf } from "./shared-in-place.js";
|
|
4
4
|
import { t as isRecord } from "./shared-json-shape.js";
|
|
5
5
|
import { n as refusal$1, t as invariant } from "./shared-refusal.js";
|
|
6
|
+
import { a as field$1, c as containerLogs, d as findContainersByLabel, f as inspectContainer, h as requireDockerDaemon, l as execInContainer, m as removeContainers, p as readContainerFile, r as TestcontainersAdapter, u as fieldOf } from "./shared-testcontainers.adapter.js";
|
|
6
7
|
import { _ as Matcher, d as structuralEquals, g as placeholderStringMatches, h as hasPlaceholders, l as isPlainObject, m as textEquals, n as isStreamBody, p as textContains, r as declared, u as renderExpected, v as match, y as CaptureScope } from "./shared-exchange.js";
|
|
7
|
-
import {
|
|
8
|
+
import { a as RUN_KEYS, s as SpecSyntaxError } from "./shared-spec-grammar.js";
|
|
8
9
|
import { a as renderValue, c as file, d as isDiskTarget, f as requireDiskFile, i as renderText, l as isDiskDirectory, o as resolveSpecPath, p as requireDiskTarget, r as renderRows, s as directory, t as grammarRefusal, u as isDiskFile } from "./shared-literate-plugin.js";
|
|
9
10
|
import { c as writeLedger, l as missingHint, o as routesLedger, u as shouldUpdateSnapshots } from "./shared-ledger.js";
|
|
10
|
-
import {
|
|
11
|
+
import { i as readSpecFile, o as dedent } from "./shared-spec-document.js";
|
|
12
|
+
import { n as untilDeadline } from "./shared-pause.js";
|
|
13
|
+
import { t as listenedPort } from "./shared-listened-port.js";
|
|
11
14
|
import { t as ProcessHandle } from "./shared-process.adapter.js";
|
|
12
|
-
import {
|
|
13
|
-
import { n as StubQueue, t as
|
|
15
|
+
import { n as toKebabCase, t as toConstantCase } from "./shared-record-key.js";
|
|
16
|
+
import { a as toReadableStream, i as unmatchedSummary, n as replyOf, o as StubQueue, r as requestBody, t as refusedReply } from "./shared-stub-reply.js";
|
|
14
17
|
import { d as renderYamlSource, n as blockScalar, o as isScalar, r as isMap, s as isSeq } from "./shared-yaml-document.js";
|
|
15
18
|
import { a as sessionLedger, i as mobileSessions, o as mobileServer, s as reachAppium } from "./shared-session-ledger.js";
|
|
16
19
|
import { S as testId, _ as listitem, a as main, b as status, d as button, f as content, g as link, h as heading, i as form, l as search, m as field, n as complementary, o as navigation, p as dialog, r as contentinfo, s as region, t as banner, u as within, v as option, x as table, y as row } from "./shared-landmarks.js";
|
|
@@ -18,8 +21,8 @@ import { createRequire } from "node:module";
|
|
|
18
21
|
import { LESSON_PARTS, lessonParts, presetChecks } from "@jterrazz/typescript/rules";
|
|
19
22
|
import { cpSync, existsSync, lstatSync, mkdirSync, mkdtempSync, readFileSync, rmSync, statSync, symlinkSync, writeFileSync } from "node:fs";
|
|
20
23
|
import { basename, dirname, isAbsolute, join, normalize, relative, resolve, sep } from "node:path";
|
|
24
|
+
import { execFileSync, spawn, spawnSync } from "node:child_process";
|
|
21
25
|
import { readdir } from "node:fs/promises";
|
|
22
|
-
import { execFileSync, execSync, spawn, spawnSync } from "node:child_process";
|
|
23
26
|
import { randomUUID } from "node:crypto";
|
|
24
27
|
import { createServer } from "node:http";
|
|
25
28
|
import { tmpdir } from "node:os";
|
|
@@ -177,7 +180,7 @@ var TableAccessor = class {
|
|
|
177
180
|
const ANSI_RE = /\u001B\[[0-9;]*m/gu;
|
|
178
181
|
/** rule `no-transform-duplicating-tokens`: a golden compares the text, never its colour. */
|
|
179
182
|
function stripAnsiCodes(value) {
|
|
180
|
-
return value.
|
|
183
|
+
return value.replaceAll(ANSI_RE, "");
|
|
181
184
|
}
|
|
182
185
|
//#endregion
|
|
183
186
|
//#region src/expectations/text/grep.ts
|
|
@@ -338,110 +341,6 @@ var JsonAccessor = class {
|
|
|
338
341
|
}
|
|
339
342
|
};
|
|
340
343
|
//#endregion
|
|
341
|
-
//#region src/expectations/command/docker-cli.ts
|
|
342
|
-
const DEFAULT_TIMEOUT = 1e4;
|
|
343
|
-
/** Docker-aware mode reads what the daemon answers: a run where none answers is refused, never passed. */
|
|
344
|
-
function requireDockerDaemon() {
|
|
345
|
-
try {
|
|
346
|
-
execSync("docker version --format \"{{.Server.Version}}\"", {
|
|
347
|
-
stdio: "ignore",
|
|
348
|
-
timeout: DEFAULT_TIMEOUT
|
|
349
|
-
});
|
|
350
|
-
} catch (error) {
|
|
351
|
-
throw new Error(refusal$1({
|
|
352
|
-
fix: "start Docker (Docker Desktop, OrbStack, colima or another) and run again",
|
|
353
|
-
found: "specification.cli({ docker }) asked for Docker, and no Docker daemon answered",
|
|
354
|
-
read: "docs/guides/cli-specs.md#docker-aware-mode",
|
|
355
|
-
why: "Docker-aware mode reads the containers a run spawned, so it needs a Docker daemon."
|
|
356
|
-
}), { cause: error });
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
function findContainersByLabel(key, value) {
|
|
360
|
-
try {
|
|
361
|
-
return execSync(`docker ps -aq -f label=${key}=${value}`, {
|
|
362
|
-
encoding: "utf8",
|
|
363
|
-
timeout: DEFAULT_TIMEOUT
|
|
364
|
-
}).split("\n").map((line) => line.trim()).filter((line) => line.length > 0);
|
|
365
|
-
} catch {
|
|
366
|
-
return [];
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
/** A field of a value read off docker's JSON or a failed process: absent wherever the value does not hold it. */
|
|
370
|
-
function fieldOf(value, ...keys) {
|
|
371
|
-
if (typeof value !== "object" || value === null) return;
|
|
372
|
-
const key = keys.find((candidate) => candidate in value);
|
|
373
|
-
return key === void 0 ? void 0 : Reflect.get(value, key);
|
|
374
|
-
}
|
|
375
|
-
const streamOf = (error, key) => {
|
|
376
|
-
const stream = fieldOf(error, key);
|
|
377
|
-
return typeof stream === "string" ? stream : void 0;
|
|
378
|
-
};
|
|
379
|
-
function inspectContainer(id) {
|
|
380
|
-
const raw = execSync(`docker inspect ${id}`, {
|
|
381
|
-
encoding: "utf8",
|
|
382
|
-
timeout: DEFAULT_TIMEOUT
|
|
383
|
-
});
|
|
384
|
-
const parsed = JSON.parse(raw);
|
|
385
|
-
if (!Array.isArray(parsed)) return parsed;
|
|
386
|
-
return parsed[0];
|
|
387
|
-
}
|
|
388
|
-
function removeContainers(ids) {
|
|
389
|
-
if (ids.length === 0) return;
|
|
390
|
-
try {
|
|
391
|
-
execSync(`docker rm -f ${ids.join(" ")}`, {
|
|
392
|
-
encoding: "utf8",
|
|
393
|
-
stdio: "pipe",
|
|
394
|
-
timeout: DEFAULT_TIMEOUT
|
|
395
|
-
});
|
|
396
|
-
} catch {}
|
|
397
|
-
}
|
|
398
|
-
function containerLogs(id) {
|
|
399
|
-
try {
|
|
400
|
-
return execSync(`docker logs ${id}`, {
|
|
401
|
-
encoding: "utf8",
|
|
402
|
-
stdio: [
|
|
403
|
-
"ignore",
|
|
404
|
-
"pipe",
|
|
405
|
-
"pipe"
|
|
406
|
-
],
|
|
407
|
-
timeout: DEFAULT_TIMEOUT
|
|
408
|
-
});
|
|
409
|
-
} catch (error) {
|
|
410
|
-
return streamOf(error, "stdout") ?? streamOf(error, "stderr") ?? "";
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
|
-
function readContainerFile(id, path) {
|
|
414
|
-
return execSync(`docker exec ${id} cat ${JSON.stringify(path)}`, {
|
|
415
|
-
encoding: "utf8",
|
|
416
|
-
timeout: DEFAULT_TIMEOUT
|
|
417
|
-
});
|
|
418
|
-
}
|
|
419
|
-
function execInContainer(id, argv) {
|
|
420
|
-
try {
|
|
421
|
-
const quoted = argv.map((a) => JSON.stringify(a)).join(" ");
|
|
422
|
-
return {
|
|
423
|
-
exitCode: 0,
|
|
424
|
-
stderr: "",
|
|
425
|
-
stdout: execSync(`docker exec ${id} ${quoted}`, {
|
|
426
|
-
encoding: "utf8",
|
|
427
|
-
stdio: [
|
|
428
|
-
"ignore",
|
|
429
|
-
"pipe",
|
|
430
|
-
"pipe"
|
|
431
|
-
],
|
|
432
|
-
timeout: DEFAULT_TIMEOUT
|
|
433
|
-
})
|
|
434
|
-
};
|
|
435
|
-
} catch (error) {
|
|
436
|
-
const status = fieldOf(error, "status");
|
|
437
|
-
return {
|
|
438
|
-
exitCode: typeof status === "number" ? status : 1,
|
|
439
|
-
stderr: streamOf(error, "stderr") ?? (error instanceof Error ? error.message : ""),
|
|
440
|
-
stdout: streamOf(error, "stdout") ?? ""
|
|
441
|
-
};
|
|
442
|
-
}
|
|
443
|
-
}
|
|
444
|
-
//#endregion
|
|
445
344
|
//#region src/expectations/command/container.ts
|
|
446
345
|
function readInspectState(inspect) {
|
|
447
346
|
const state = fieldOf(inspect, "State", "state");
|
|
@@ -1693,16 +1592,6 @@ function declaredDatabaseKeys(services) {
|
|
|
1693
1592
|
return Object.entries(services).filter(([, service]) => service.createDatabaseAdapter() !== null).map(([key]) => key);
|
|
1694
1593
|
}
|
|
1695
1594
|
//#endregion
|
|
1696
|
-
//#region lint/kit/project/record-key.ts
|
|
1697
|
-
/** `analyticsDb` gives `analytics-db`. */
|
|
1698
|
-
function toKebabCase(key) {
|
|
1699
|
-
return key.replaceAll(/(?<lower>[a-z0-9])(?<upper>[A-Z])/gu, "$<lower>-$<upper>").toLowerCase();
|
|
1700
|
-
}
|
|
1701
|
-
/** `analyticsDb` gives `ANALYTICS_DB`, `db-main` gives `DB_MAIN`. */
|
|
1702
|
-
function toConstantCase(key) {
|
|
1703
|
-
return key.replaceAll(/(?<lower>[a-z0-9])(?<upper>[A-Z])/gu, "$<lower>_$<upper>").replaceAll(/[^A-Za-z0-9]/gu, "_").toUpperCase();
|
|
1704
|
-
}
|
|
1705
|
-
//#endregion
|
|
1706
1595
|
//#region src/services/containers/run-setup-container.adapter.ts
|
|
1707
1596
|
function answered(value) {
|
|
1708
1597
|
const error = field$1(value, "error");
|
|
@@ -1970,12 +1859,12 @@ var Orchestrator = class {
|
|
|
1970
1859
|
function getWorkerId() {
|
|
1971
1860
|
return process.env.VITEST_POOL_ID ?? "0";
|
|
1972
1861
|
}
|
|
1973
|
-
async function acquireIsolation(services) {
|
|
1862
|
+
async function acquireIsolation(services, acquired) {
|
|
1974
1863
|
const workerId = getWorkerId();
|
|
1975
|
-
for (const service of Object.values(services))
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1864
|
+
for (const service of Object.values(services)) {
|
|
1865
|
+
await service.isolation().acquire(workerId);
|
|
1866
|
+
acquired.push(service);
|
|
1867
|
+
}
|
|
1979
1868
|
}
|
|
1980
1869
|
function splitProcesses(services) {
|
|
1981
1870
|
const infrastructure = {};
|
|
@@ -2012,16 +1901,30 @@ async function startServices(services, root, subject) {
|
|
|
2012
1901
|
subject
|
|
2013
1902
|
});
|
|
2014
1903
|
await orchestrator.start();
|
|
2015
|
-
|
|
1904
|
+
const acquired = [];
|
|
1905
|
+
const stopInfrastructure = async () => {
|
|
1906
|
+
for (const service of acquired.splice(0)) await service.isolation().release();
|
|
1907
|
+
await orchestrator.stop();
|
|
1908
|
+
};
|
|
2016
1909
|
const runId = randomUUID();
|
|
2017
|
-
|
|
1910
|
+
let stopProcesses;
|
|
1911
|
+
try {
|
|
1912
|
+
await acquireIsolation(infrastructure, acquired);
|
|
1913
|
+
stopProcesses = await startProcessServices(services, root, runId);
|
|
1914
|
+
} catch (error) {
|
|
1915
|
+
await stopInfrastructure();
|
|
1916
|
+
throw error;
|
|
1917
|
+
}
|
|
2018
1918
|
const databases = orchestrator.getDatabases();
|
|
2019
1919
|
return {
|
|
2020
1920
|
database: orchestrator.getDatabase() ?? void 0,
|
|
2021
1921
|
databases: databases.size > 0 ? databases : void 0,
|
|
2022
1922
|
orchestrator,
|
|
2023
1923
|
runId,
|
|
2024
|
-
|
|
1924
|
+
stop: async () => {
|
|
1925
|
+
await stopProcesses();
|
|
1926
|
+
await stopInfrastructure();
|
|
1927
|
+
}
|
|
2025
1928
|
};
|
|
2026
1929
|
}
|
|
2027
1930
|
//#endregion
|
|
@@ -2656,7 +2559,7 @@ async function startApi(options) {
|
|
|
2656
2559
|
const root = resolveRoot(options.root, callerDir);
|
|
2657
2560
|
const services = declaredServices(options.services);
|
|
2658
2561
|
const databaseKeys = declaredDatabaseKeys(services);
|
|
2659
|
-
const { database, databases,
|
|
2562
|
+
const { database, databases, stop } = await startServices(services, root);
|
|
2660
2563
|
const server = new HonoAdapter(options.server(services));
|
|
2661
2564
|
const config = {
|
|
2662
2565
|
database,
|
|
@@ -2672,9 +2575,7 @@ async function startApi(options) {
|
|
|
2672
2575
|
declared: server.declared(),
|
|
2673
2576
|
reached: server.reached
|
|
2674
2577
|
});
|
|
2675
|
-
await
|
|
2676
|
-
await releaseIsolation(services);
|
|
2677
|
-
await orchestrator.stop();
|
|
2578
|
+
await stop();
|
|
2678
2579
|
},
|
|
2679
2580
|
docker: createDockerReader(callerDir)
|
|
2680
2581
|
};
|
|
@@ -2699,15 +2600,10 @@ var StubBackend = class {
|
|
|
2699
2600
|
this.handle(request, response);
|
|
2700
2601
|
});
|
|
2701
2602
|
this.server = server;
|
|
2702
|
-
await
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
});
|
|
2707
|
-
});
|
|
2708
|
-
const address = server.address();
|
|
2709
|
-
if (address === null || typeof address === "string") throw invariant("the stub backend listens on no port");
|
|
2710
|
-
return `http://127.0.0.1:${address.port}`;
|
|
2603
|
+
return `http://127.0.0.1:${await listenedPort(server, {
|
|
2604
|
+
host: "127.0.0.1",
|
|
2605
|
+
port: this.options.port ?? 0
|
|
2606
|
+
})}`;
|
|
2711
2607
|
}
|
|
2712
2608
|
async stop() {
|
|
2713
2609
|
const { server } = this;
|
|
@@ -2765,18 +2661,18 @@ var StubBackend = class {
|
|
|
2765
2661
|
const stub = this.queue.take(observed);
|
|
2766
2662
|
if (!stub) {
|
|
2767
2663
|
this.record(method, url);
|
|
2664
|
+
const error = refusedReply(unmatchedSummary(method, url));
|
|
2768
2665
|
response.writeHead(501, {
|
|
2769
2666
|
...cors,
|
|
2770
2667
|
"content-type": "application/json"
|
|
2771
2668
|
});
|
|
2772
2669
|
response.end(JSON.stringify({
|
|
2773
2670
|
declared: this.queue.declaredRoutes(),
|
|
2774
|
-
error
|
|
2671
|
+
error
|
|
2775
2672
|
}));
|
|
2776
2673
|
return;
|
|
2777
2674
|
}
|
|
2778
|
-
const reply =
|
|
2779
|
-
await pause(reply.delay ?? 0);
|
|
2675
|
+
const reply = await replyOf(stub.response, observed);
|
|
2780
2676
|
if (reply.transport === "network-error") {
|
|
2781
2677
|
request.socket.destroy();
|
|
2782
2678
|
return;
|
|
@@ -2835,13 +2731,7 @@ async function writeReply(response, reply, cors) {
|
|
|
2835
2731
|
async function readBody(request) {
|
|
2836
2732
|
const chunks = [];
|
|
2837
2733
|
for await (const chunk of request) chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk)));
|
|
2838
|
-
|
|
2839
|
-
if (raw.length === 0) return null;
|
|
2840
|
-
try {
|
|
2841
|
-
return JSON.parse(raw);
|
|
2842
|
-
} catch {
|
|
2843
|
-
return raw;
|
|
2844
|
-
}
|
|
2734
|
+
return requestBody(Buffer.concat(chunks).toString("utf8"));
|
|
2845
2735
|
}
|
|
2846
2736
|
function corsHeaders(request) {
|
|
2847
2737
|
return {
|
|
@@ -3903,12 +3793,6 @@ async function observeProcess(child, options) {
|
|
|
3903
3793
|
});
|
|
3904
3794
|
});
|
|
3905
3795
|
}
|
|
3906
|
-
function endGroup(child) {
|
|
3907
|
-
signalGroup(child);
|
|
3908
|
-
setTimeout(() => {
|
|
3909
|
-
signalGroup(child, "SIGKILL");
|
|
3910
|
-
}, KILL_GRACE_MS).unref();
|
|
3911
|
-
}
|
|
3912
3796
|
/** Spawned, never `spawnSync`: the test's event loop keeps running while the binary does, so a server the test started (a collector, a stub) answers it. stdout and stderr are captured whatever the exit code, and many CLIs write banners to stderr. */
|
|
3913
3797
|
var ExecAdapter = class {
|
|
3914
3798
|
command;
|
|
@@ -3923,8 +3807,8 @@ var ExecAdapter = class {
|
|
|
3923
3807
|
env,
|
|
3924
3808
|
shell: true
|
|
3925
3809
|
});
|
|
3926
|
-
onTestFinished(() => {
|
|
3927
|
-
endGroup(child);
|
|
3810
|
+
onTestFinished(async () => {
|
|
3811
|
+
await endGroup(child);
|
|
3928
3812
|
});
|
|
3929
3813
|
return await new Promise((resolve) => {
|
|
3930
3814
|
const output = {
|
|
@@ -3980,7 +3864,7 @@ var ExecAdapter = class {
|
|
|
3980
3864
|
function dockerTestRunId() {
|
|
3981
3865
|
return `t-${Math.random().toString(36).slice(2, 10)}-${Date.now().toString(36)}`;
|
|
3982
3866
|
}
|
|
3983
|
-
async function startedBackend(declared, defaults, started) {
|
|
3867
|
+
async function startedBackend$1(declared, defaults, started) {
|
|
3984
3868
|
if (declared === void 0) return {
|
|
3985
3869
|
backend: null,
|
|
3986
3870
|
defaults
|
|
@@ -3995,7 +3879,7 @@ async function startedBackend(declared, defaults, started) {
|
|
|
3995
3879
|
}
|
|
3996
3880
|
};
|
|
3997
3881
|
} catch (error) {
|
|
3998
|
-
await started?.
|
|
3882
|
+
await started?.stop();
|
|
3999
3883
|
throw error;
|
|
4000
3884
|
}
|
|
4001
3885
|
}
|
|
@@ -4009,7 +3893,7 @@ async function startCli(bin, options = {}) {
|
|
|
4009
3893
|
if (options.docker !== void 0) requireDockerDaemon();
|
|
4010
3894
|
let started = null;
|
|
4011
3895
|
if (Object.keys(services).length > 0) started = await startServices(services, root, { type: "none" });
|
|
4012
|
-
const { backend, defaults } = await startedBackend(options.backend, options.defaults, started);
|
|
3896
|
+
const { backend, defaults } = await startedBackend$1(options.backend, options.defaults, started);
|
|
4013
3897
|
const dockerTestRunIdOf = options.docker === void 0 ? void 0 : dockerTestRunId();
|
|
4014
3898
|
const declaredServicesOf = Object.keys(services).length > 0 ? services : void 0;
|
|
4015
3899
|
const config = {
|
|
@@ -4029,10 +3913,8 @@ async function startCli(bin, options = {}) {
|
|
|
4029
3913
|
};
|
|
4030
3914
|
return {
|
|
4031
3915
|
cleanup: async () => {
|
|
3916
|
+
await started?.stop();
|
|
4032
3917
|
await backend?.stop();
|
|
4033
|
-
await started?.stopProcesses();
|
|
4034
|
-
await releaseIsolation(services);
|
|
4035
|
-
if (started) await started.orchestrator.stop();
|
|
4036
3918
|
},
|
|
4037
3919
|
cli: cliDefinition.open(config),
|
|
4038
3920
|
docker: createDockerReader(callerDir),
|
|
@@ -4178,9 +4060,7 @@ async function startIntegration(options = {}) {
|
|
|
4178
4060
|
};
|
|
4179
4061
|
return {
|
|
4180
4062
|
cleanup: async () => {
|
|
4181
|
-
await started?.
|
|
4182
|
-
await releaseIsolation(services);
|
|
4183
|
-
if (started) await started.orchestrator.stop();
|
|
4063
|
+
await started?.stop();
|
|
4184
4064
|
},
|
|
4185
4065
|
integration: integrationDefinition.open(config)
|
|
4186
4066
|
};
|
|
@@ -4278,9 +4158,7 @@ async function startJob(options) {
|
|
|
4278
4158
|
};
|
|
4279
4159
|
return {
|
|
4280
4160
|
cleanup: async () => {
|
|
4281
|
-
await started?.
|
|
4282
|
-
await releaseIsolation(services);
|
|
4283
|
-
if (started) await started.orchestrator.stop();
|
|
4161
|
+
await started?.stop();
|
|
4284
4162
|
},
|
|
4285
4163
|
job: jobDefinition.open(config),
|
|
4286
4164
|
orchestrator: started?.orchestrator ?? null
|
|
@@ -4956,19 +4834,30 @@ async function startBesideAppium(services, root, appium) {
|
|
|
4956
4834
|
throw error;
|
|
4957
4835
|
}
|
|
4958
4836
|
}
|
|
4837
|
+
async function startedBackend(declared, started, appium) {
|
|
4838
|
+
if (!declared) return {
|
|
4839
|
+
backend: null,
|
|
4840
|
+
backendUrl: void 0
|
|
4841
|
+
};
|
|
4842
|
+
const backend = new StubBackend({ port: declared.port });
|
|
4843
|
+
try {
|
|
4844
|
+
return {
|
|
4845
|
+
backend,
|
|
4846
|
+
backendUrl: await backend.start()
|
|
4847
|
+
};
|
|
4848
|
+
} catch (error) {
|
|
4849
|
+
await started?.stop();
|
|
4850
|
+
await appium.stop();
|
|
4851
|
+
throw error;
|
|
4852
|
+
}
|
|
4853
|
+
}
|
|
4959
4854
|
async function startMobile(options) {
|
|
4960
4855
|
const callerDir = getCallerDir();
|
|
4961
4856
|
await registerMatchers();
|
|
4962
4857
|
const root = resolveRoot(options.root, callerDir);
|
|
4963
4858
|
const { appium, device: driven, outlivesFile } = await reachAppium(options, root, await mobileServer());
|
|
4964
|
-
const
|
|
4965
|
-
const
|
|
4966
|
-
let backend = null;
|
|
4967
|
-
let backendUrl;
|
|
4968
|
-
if (options.backend) {
|
|
4969
|
-
backend = new StubBackend({ port: options.backend.port });
|
|
4970
|
-
backendUrl = await backend.start();
|
|
4971
|
-
}
|
|
4859
|
+
const started = await startBesideAppium(declaredServices(options.services), root, appium);
|
|
4860
|
+
const { backend, backendUrl } = await startedBackend(options.backend, started, appium);
|
|
4972
4861
|
const device = lazyDevice({
|
|
4973
4862
|
driven,
|
|
4974
4863
|
ledger: async () => await runLedger(outlivesFile),
|
|
@@ -4985,11 +4874,7 @@ async function startMobile(options) {
|
|
|
4985
4874
|
backendUrl,
|
|
4986
4875
|
cleanup: async () => {
|
|
4987
4876
|
await device.close();
|
|
4988
|
-
|
|
4989
|
-
await started.stopProcesses();
|
|
4990
|
-
await releaseIsolation(services);
|
|
4991
|
-
await started.orchestrator.stop();
|
|
4992
|
-
}
|
|
4877
|
+
await started?.stop();
|
|
4993
4878
|
await appium.stop();
|
|
4994
4879
|
if (backend) await backend.stop();
|
|
4995
4880
|
},
|
|
@@ -5449,8 +5334,7 @@ async function startSite(options, context) {
|
|
|
5449
5334
|
serve
|
|
5450
5335
|
};
|
|
5451
5336
|
} catch (error) {
|
|
5452
|
-
await context.started?.
|
|
5453
|
-
await context.started?.orchestrator.stop();
|
|
5337
|
+
await context.started?.stop();
|
|
5454
5338
|
await context.backend?.stop();
|
|
5455
5339
|
throw error;
|
|
5456
5340
|
}
|
|
@@ -5533,11 +5417,7 @@ async function startWebsite(options) {
|
|
|
5533
5417
|
cleanup: async () => {
|
|
5534
5418
|
await browser.close();
|
|
5535
5419
|
if (serve) await serve.stop();
|
|
5536
|
-
|
|
5537
|
-
await started.stopProcesses();
|
|
5538
|
-
await releaseIsolation(services);
|
|
5539
|
-
await started.orchestrator.stop();
|
|
5540
|
-
}
|
|
5420
|
+
await started?.stop();
|
|
5541
5421
|
if (backend) await backend.stop();
|
|
5542
5422
|
},
|
|
5543
5423
|
url: baseUrl,
|
|
@@ -5546,6 +5426,7 @@ async function startWebsite(options) {
|
|
|
5546
5426
|
}
|
|
5547
5427
|
//#endregion
|
|
5548
5428
|
//#region src/specifications/specification.ts
|
|
5429
|
+
/** Created in a `*.specification.ts` under `specs/`, destructured with canonical names, cleaned up via `afterAll(cleanup)` (`runner-in-specification-file`, `no-runner-alias`, `cleanup-passed-to-after-all`). */
|
|
5549
5430
|
const specification = {
|
|
5550
5431
|
/** Test an HTTP app: services via testcontainers, the app in this process. */
|
|
5551
5432
|
api: startApi,
|
|
@@ -5676,17 +5557,15 @@ const DEFAULT_INTERVAL_MS = 50;
|
|
|
5676
5557
|
async function waitUntil(predicate, options = {}) {
|
|
5677
5558
|
const timeout = options.timeout ?? DEFAULT_TIMEOUT_MS;
|
|
5678
5559
|
const interval = options.interval ?? DEFAULT_INTERVAL_MS;
|
|
5679
|
-
|
|
5680
|
-
|
|
5681
|
-
|
|
5682
|
-
|
|
5683
|
-
|
|
5684
|
-
|
|
5685
|
-
|
|
5686
|
-
|
|
5687
|
-
|
|
5688
|
-
await pause(interval);
|
|
5689
|
-
}
|
|
5560
|
+
if (await untilDeadline(async () => await predicate() || void 0, {
|
|
5561
|
+
interval,
|
|
5562
|
+
timeout
|
|
5563
|
+
}) === void 0) throw new Error(refusal$1({
|
|
5564
|
+
fix: "raise the budget if the wait is legitimate, or wait on what actually changes",
|
|
5565
|
+
found: `waitUntil(): ${options.why ?? "the condition"} did not hold within ${timeout}ms`,
|
|
5566
|
+
read: "docs/reference/assertions.md#two-helpers-that-are-not-matchers",
|
|
5567
|
+
why: "`waitUntil()` waits on a condition, within a budget."
|
|
5568
|
+
}));
|
|
5690
5569
|
}
|
|
5691
5570
|
//#endregion
|
|
5692
5571
|
export { Matcher, banner, button, clock, complementary, component, content, contentinfo, dialog, directory, disabled, enabled, field, file, focused, form, heading, link, listitem, main, match, mockOf, navigation, option, packed, region, required, row, ruleTester, search, selected, specification, status, table, testId, text, valued, waitUntil, within };
|
|
@@ -11,4 +11,5 @@ export declare function mobileSessionSetup(): string;
|
|
|
11
11
|
/** One driver session per run: the first runner records it in the provided folder, later files attach, the teardown ends them. With `mobile({ device, root })` it also owns the Appium server. */
|
|
12
12
|
export default function setup(project: SessionProject): Promise<() => Promise<void>>;
|
|
13
13
|
//#endregion
|
|
14
|
+
export {};
|
|
14
15
|
//# sourceMappingURL=mobile-session.global-setup.d.ts.map
|
package/dist/oxlint.cjs
CHANGED
|
@@ -5628,14 +5628,14 @@ function literalOf(property) {
|
|
|
5628
5628
|
value
|
|
5629
5629
|
};
|
|
5630
5630
|
}
|
|
5631
|
-
/** Only under `test` or in `projects
|
|
5631
|
+
/** Only under `test` or in `projects`: `optimizeDeps.include` and its kin list package specifiers, not globs over a tree. */
|
|
5632
5632
|
function includedGlobs(program) {
|
|
5633
5633
|
const globs = [];
|
|
5634
5634
|
walk(program, (node) => {
|
|
5635
5635
|
const key = propertyKeyName(node);
|
|
5636
5636
|
if (key !== "projects" && key !== "test") return;
|
|
5637
5637
|
const value = child(node, "value");
|
|
5638
|
-
const projects = key === "test" ? [value] :
|
|
5638
|
+
const projects = key === "test" ? [value] : childList(value, "properties").map((property) => child(property, "value"));
|
|
5639
5639
|
for (const project of projects) {
|
|
5640
5640
|
const include = child(findProperty(project, "include"), "value");
|
|
5641
5641
|
for (const element of childList(include, "elements")) {
|