@jterrazz/test 16.1.1 → 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/README.md +45 -171
- package/dist/browser/index.js +1 -2980
- package/dist/browser/services.js +1 -0
- package/dist/browser/stubs.js +1 -0
- package/dist/browser-shared-index.browser.js +3233 -0
- package/dist/checker.d.ts +6 -1
- package/dist/checker.js +3721 -2624
- package/dist/config.d.ts +40 -0
- package/dist/config.js +309 -0
- package/dist/in-place.global-setup.d.ts +12 -0
- package/dist/in-place.global-setup.js +21 -0
- package/dist/index.d.ts +1641 -3004
- package/dist/index.js +5095 -3835
- package/dist/mobile-session.global-setup.d.ts +15 -0
- package/dist/mobile-session.global-setup.js +31 -0
- package/dist/oxlint.cjs +6069 -5634
- package/dist/oxlint.d.cts +43 -7212
- package/dist/oxlint.d.ts +43 -7212
- package/dist/oxlint.js +3979 -5799
- package/dist/route-coverage.global-setup.d.ts +43 -0
- package/dist/route-coverage.global-setup.js +1 -0
- package/dist/services.d.ts +3 -0
- package/dist/services.global-setup.d.ts +43 -0
- package/dist/services.global-setup.js +1 -0
- package/dist/services.js +597 -0
- package/dist/shared-appium.adapter.js +428 -0
- package/dist/shared-artifacts.js +34 -0
- package/dist/shared-chapter.js +7 -0
- package/dist/shared-container.port.d.ts +31 -0
- package/dist/shared-derived-projects.js +73 -0
- package/dist/shared-device.port.d.ts +189 -0
- package/dist/shared-exchange.js +447 -0
- package/dist/shared-in-place.js +172 -0
- package/dist/shared-index.services.d.ts +346 -0
- package/dist/shared-index.stubs.d.ts +287 -0
- package/dist/shared-json-shape.js +17 -0
- package/dist/shared-landmarks.js +71 -0
- package/dist/shared-ledger.js +68 -0
- package/dist/shared-listened-port.js +19 -0
- package/dist/shared-literate-plugin.js +184 -0
- package/dist/shared-manifest.js +1321 -0
- package/dist/shared-pause.js +22 -0
- package/dist/shared-peer.js +88 -0
- package/dist/shared-playwright.adapter.js +328 -0
- package/dist/shared-process-alive.js +13 -0
- package/dist/shared-process.adapter.js +74 -0
- package/dist/shared-record-key.js +13 -0
- package/dist/shared-refusal.js +35 -0
- package/dist/shared-resolve.js +24 -0
- package/dist/shared-route-coverage.global-setup.js +187 -0
- package/dist/shared-scenario-failure.js +198 -0
- package/dist/shared-serve.js +407 -0
- package/dist/shared-service-images.js +10 -0
- package/dist/shared-services.global-setup.js +174 -0
- package/dist/shared-session-ledger.js +305 -0
- package/dist/shared-setup-entry.js +12 -0
- package/dist/shared-source-scan.js +133 -0
- package/dist/shared-spec-document.js +303 -0
- package/dist/shared-spec-grammar.js +66 -0
- package/dist/shared-specifications.js +26 -0
- package/dist/shared-specs-modules.js +1033 -0
- package/dist/shared-stub-reply.js +167 -0
- package/dist/shared-stub-server.js +142 -0
- package/dist/shared-temp-root.global-setup.js +73 -0
- package/dist/shared-test-baseline.js +26 -0
- package/dist/shared-test-section.js +58 -0
- package/dist/shared-testcontainers.adapter.js +306 -0
- package/dist/shared-tokens.js +55 -0
- package/dist/shared-yaml-document.js +119 -0
- package/dist/stubs.d.ts +2 -0
- package/dist/stubs.js +657 -0
- package/dist/temp-root.global-setup.d.ts +7 -0
- package/dist/temp-root.global-setup.js +1 -0
- package/dist/vitest.d.ts +93 -164
- package/dist/vitest.js +382 -550
- package/docs/README.md +82 -36
- package/docs/concepts/architecture.md +118 -0
- package/docs/concepts/conventions.md +333 -0
- package/docs/guides/api-specs.md +374 -0
- package/docs/guides/cli-specs.md +566 -0
- package/docs/guides/component-specs.md +252 -0
- package/docs/guides/develop.md +97 -0
- package/docs/guides/integration-specs.md +181 -0
- package/docs/guides/job-specs.md +180 -0
- package/docs/guides/lint-specs.md +174 -0
- package/docs/guides/mobile-specs.md +319 -0
- package/docs/guides/module-tests.md +149 -0
- package/docs/guides/release.md +44 -0
- package/docs/guides/spec-documents.md +189 -0
- package/docs/guides/website-specs.md +413 -0
- package/docs/reference/assertions.md +345 -0
- package/docs/reference/glossary.md +20 -0
- package/docs/reference/linting.md +483 -0
- package/docs/reference/services.md +236 -0
- package/docs/reference/stubs.md +500 -0
- package/docs/reference/time-and-doubles.md +137 -0
- package/docs/reference/tokens.md +258 -0
- package/docs/reference/visitor.md +221 -0
- package/docs/start/getting-started.md +187 -0
- package/docs/start/set-up-a-project.md +228 -0
- package/package.json +56 -36
- package/schema/spec.schema.json +43 -17
- package/dist/appium.adapter.js +0 -489
- package/dist/artifacts.js +0 -32
- package/dist/fixtures.js +0 -110
- package/dist/literate.js +0 -1747
- package/dist/match.js +0 -804
- package/dist/playwright.adapter.js +0 -438
- package/dist/server.js +0 -160
- package/dist/stream.js +0 -321
- package/dist/substring-warning.js +0 -282
- package/docs/01-architecture.md +0 -95
- package/docs/02-developing.md +0 -351
- package/docs/03-testing.md +0 -495
- package/docs/04-operating.md +0 -101
- package/docs/05-module-tests.md +0 -152
- package/docs/06-integration.md +0 -180
- package/docs/07-component.md +0 -246
- package/docs/08-website.md +0 -404
- package/docs/09-mobile.md +0 -270
- package/docs/10-api.md +0 -264
- package/docs/11-jobs.md +0 -157
- package/docs/12-cli.md +0 -677
- package/docs/13-elements.md +0 -248
- package/docs/14-assertions.md +0 -314
- package/docs/15-tokens.md +0 -253
- package/docs/16-contracts.md +0 -438
- package/docs/17-services.md +0 -188
- package/docs/18-conventions.md +0 -332
- package/docs/19-linting.md +0 -512
- package/docs/decisions/001-v9-conventions-on-three-channels.md +0 -50
- package/docs/decisions/002-the-statique-channel-holds-only-what-upstream-does-not.md +0 -49
- package/docs/decisions/003-a-rendered-component-is-a-facet-on-vitest-browser-mode.md +0 -84
- package/docs/decisions/004-a-module-against-real-services-or-a-golden-is-the-integration-facet.md +0 -81
- package/docs/decisions/005-the-doubles-ladder-is-closed.md +0 -86
- package/docs/decisions/006-the-catalogue-reaches-a-file-by-its-role.md +0 -245
- package/docs/decisions/007-compose-mode-leaves-the-package.md +0 -53
- package/docs/decisions/008-vitest-5-node-24-and-the-native-seams-as-optional-peers.md +0 -75
- package/docs/decisions/_template.md +0 -22
- package/docs/reference/classes/Matcher.md +0 -120
- package/docs/reference/classes/ProcessHandle.md +0 -368
- package/docs/reference/functions/defineContract.md +0 -39
- package/docs/reference/functions/defineContracts.md +0 -36
- package/docs/reference/functions/disabled.md +0 -28
- package/docs/reference/functions/enabled.md +0 -20
- package/docs/reference/functions/focused.md +0 -26
- package/docs/reference/functions/mockOf.md +0 -79
- package/docs/reference/functions/postgres.md +0 -31
- package/docs/reference/functions/process.md +0 -33
- package/docs/reference/functions/redis.md +0 -28
- package/docs/reference/functions/required.md +0 -41
- package/docs/reference/functions/selected.md +0 -26
- package/docs/reference/functions/sqlite.md +0 -33
- package/docs/reference/functions/testId.md +0 -20
- package/docs/reference/functions/text.md +0 -40
- package/docs/reference/functions/valued.md +0 -28
- package/docs/reference/functions/waitUntil.md +0 -46
- package/docs/reference/functions/within.md +0 -29
- package/docs/reference/index.md +0 -196
- package/docs/reference/interfaces/BaseResult.md +0 -87
- package/docs/reference/interfaces/CallResult.md +0 -160
- package/docs/reference/interfaces/CaptureScope.md +0 -82
- package/docs/reference/interfaces/CliResult.md +0 -253
- package/docs/reference/interfaces/ContainerAccessor.md +0 -166
- package/docs/reference/interfaces/DirectoryAccessor.md +0 -33
- package/docs/reference/interfaces/FetchResult.md +0 -180
- package/docs/reference/interfaces/FilesystemAccessor.md +0 -46
- package/docs/reference/interfaces/HttpResult.md +0 -126
- package/docs/reference/interfaces/JsonAccessor.md +0 -46
- package/docs/reference/interfaces/PageResult.md +0 -356
- package/docs/reference/interfaces/RenderResult.md +0 -72
- package/docs/reference/interfaces/RenderedText.md +0 -110
- package/docs/reference/interfaces/ResponseAccessor.md +0 -47
- package/docs/reference/interfaces/ScreenResult.md +0 -128
- package/docs/reference/interfaces/TableAccessor.md +0 -26
- package/docs/reference/interfaces/TextAccessor.md +0 -96
- package/docs/reference/type-aliases/ApiHandle.md +0 -68
- package/docs/reference/type-aliases/ApiSpecification.md +0 -213
- package/docs/reference/type-aliases/ApiSpecificationOptions.md +0 -68
- package/docs/reference/type-aliases/BrowserConsoleMessage.md +0 -30
- package/docs/reference/type-aliases/BrowserLinkElement.md +0 -50
- package/docs/reference/type-aliases/BrowserMetaElement.md +0 -40
- package/docs/reference/type-aliases/BrowserOpenOptions.md +0 -87
- package/docs/reference/type-aliases/BrowserPage.md +0 -132
- package/docs/reference/type-aliases/BrowserPort.md +0 -51
- package/docs/reference/type-aliases/CliEnv.md +0 -11
- package/docs/reference/type-aliases/CliHandle.md +0 -80
- package/docs/reference/type-aliases/CliInput.md +0 -36
- package/docs/reference/type-aliases/CliOutput.md +0 -46
- package/docs/reference/type-aliases/CliPort.md +0 -58
- package/docs/reference/type-aliases/CliSpecification.md +0 -144
- package/docs/reference/type-aliases/CliSpecificationOptions.md +0 -134
- package/docs/reference/type-aliases/ComponentChain.md +0 -156
- package/docs/reference/type-aliases/ComponentScenario.md +0 -20
- package/docs/reference/type-aliases/ComponentUi.md +0 -47
- package/docs/reference/type-aliases/ComponentVisitor.md +0 -10
- package/docs/reference/type-aliases/ContainerPort.md +0 -113
- package/docs/reference/type-aliases/Contract.md +0 -68
- package/docs/reference/type-aliases/ContractInput.md +0 -13
- package/docs/reference/type-aliases/ContractRequest.md +0 -95
- package/docs/reference/type-aliases/ContractResponder.md +0 -23
- package/docs/reference/type-aliases/ContractResponse.md +0 -78
- package/docs/reference/type-aliases/ContractResponseValue.md +0 -13
- package/docs/reference/type-aliases/Contracts.md +0 -48
- package/docs/reference/type-aliases/DatabaseKeys.md +0 -16
- package/docs/reference/type-aliases/DatabasePort.md +0 -72
- package/docs/reference/type-aliases/DeviceOpenOptions.md +0 -46
- package/docs/reference/type-aliases/DevicePort.md +0 -51
- package/docs/reference/type-aliases/DeviceScreen.md +0 -32
- package/docs/reference/type-aliases/DeviceTimeouts.md +0 -39
- package/docs/reference/type-aliases/DockerSpecConfig.md +0 -43
- package/docs/reference/type-aliases/DomMount.md +0 -21
- package/docs/reference/type-aliases/ElementKind.md +0 -27
- package/docs/reference/type-aliases/ElementMatch.md +0 -74
- package/docs/reference/type-aliases/ElementOptions.md +0 -29
- package/docs/reference/type-aliases/ElementRef.md +0 -128
- package/docs/reference/type-aliases/ExecOptions.md +0 -37
- package/docs/reference/type-aliases/FileAccessor.md +0 -54
- package/docs/reference/type-aliases/HonoApp.md +0 -31
- package/docs/reference/type-aliases/HttpContractFilter.md +0 -49
- package/docs/reference/type-aliases/HttpResponseInit.md +0 -46
- package/docs/reference/type-aliases/HttpStreamInit.md +0 -58
- package/docs/reference/type-aliases/IntegrationHandle.md +0 -46
- package/docs/reference/type-aliases/IntegrationSpecification.md +0 -111
- package/docs/reference/type-aliases/IntegrationSpecificationOptions.md +0 -42
- package/docs/reference/type-aliases/Intercept.md +0 -19
- package/docs/reference/type-aliases/InterceptOptions.md +0 -28
- package/docs/reference/type-aliases/InterceptScope.md +0 -11
- package/docs/reference/type-aliases/IsolationStrategy.md +0 -75
- package/docs/reference/type-aliases/JobHandle.md +0 -34
- package/docs/reference/type-aliases/JobsHandle.md +0 -57
- package/docs/reference/type-aliases/JobsResult.md +0 -16
- package/docs/reference/type-aliases/JobsSpecification.md +0 -94
- package/docs/reference/type-aliases/JobsSpecificationOptions.md +0 -56
- package/docs/reference/type-aliases/LandmarkKind.md +0 -20
- package/docs/reference/type-aliases/LiterateRunFlags.md +0 -27
- package/docs/reference/type-aliases/LiterateServeRegistration.md +0 -70
- package/docs/reference/type-aliases/MatchFixtureOptions.md +0 -27
- package/docs/reference/type-aliases/MatchableRequest.md +0 -61
- package/docs/reference/type-aliases/MatcherKind.md +0 -34
- package/docs/reference/type-aliases/MobileBackendOptions.md +0 -28
- package/docs/reference/type-aliases/MobileElementKind.md +0 -14
- package/docs/reference/type-aliases/MobileElementMatch.md +0 -60
- package/docs/reference/type-aliases/MobileElementRef.md +0 -20
- package/docs/reference/type-aliases/MobileHandle.md +0 -65
- package/docs/reference/type-aliases/MobileScenario.md +0 -20
- package/docs/reference/type-aliases/MobileSpecification.md +0 -51
- package/docs/reference/type-aliases/MobileSpecificationOptions.md +0 -126
- package/docs/reference/type-aliases/MobileVisitor.md +0 -81
- package/docs/reference/type-aliases/MockOfOptions.md +0 -26
- package/docs/reference/type-aliases/MockPort.md +0 -10
- package/docs/reference/type-aliases/PinnedClock.md +0 -10
- package/docs/reference/type-aliases/PostgresOptions.md +0 -32
- package/docs/reference/type-aliases/ProcessOptions.md +0 -112
- package/docs/reference/type-aliases/RedisOptions.md +0 -20
- package/docs/reference/type-aliases/RenderSubject.md +0 -12
- package/docs/reference/type-aliases/ScreenNode.md +0 -67
- package/docs/reference/type-aliases/ServerPort.md +0 -36
- package/docs/reference/type-aliases/ServerResponse.md +0 -46
- package/docs/reference/type-aliases/ServerSpec.md +0 -21
- package/docs/reference/type-aliases/ServiceHandle.md +0 -216
- package/docs/reference/type-aliases/ServiceRecord.md +0 -12
- package/docs/reference/type-aliases/SpecDocument.md +0 -84
- package/docs/reference/type-aliases/SpecEnvToken.md +0 -21
- package/docs/reference/type-aliases/SpecFileAssertion.md +0 -27
- package/docs/reference/type-aliases/SpecFixture.md +0 -30
- package/docs/reference/type-aliases/SpecKind.md +0 -8
- package/docs/reference/type-aliases/SpecRun.md +0 -108
- package/docs/reference/type-aliases/SpecServeEntry.md +0 -40
- package/docs/reference/type-aliases/SpecStream.md +0 -32
- package/docs/reference/type-aliases/SpecificationConfig.md +0 -290
- package/docs/reference/type-aliases/SqliteOptions.md +0 -36
- package/docs/reference/type-aliases/SseEvent.md +0 -58
- package/docs/reference/type-aliases/StreamBody.md +0 -61
- package/docs/reference/type-aliases/TextFilter.md +0 -22
- package/docs/reference/type-aliases/VisitScenario.md +0 -20
- package/docs/reference/type-aliases/Visitor.md +0 -215
- package/docs/reference/type-aliases/WaitUntilOptions.md +0 -46
- package/docs/reference/type-aliases/WebsiteBackendOptions.md +0 -37
- package/docs/reference/type-aliases/WebsiteHandle.md +0 -51
- package/docs/reference/type-aliases/WebsiteSpecification.md +0 -118
- package/docs/reference/type-aliases/WebsiteSpecificationOptions.md +0 -40
- package/docs/reference/variables/anthropic.md +0 -19
- package/docs/reference/variables/banner.md +0 -10
- package/docs/reference/variables/button.md +0 -10
- package/docs/reference/variables/clock.md +0 -16
- package/docs/reference/variables/complementary.md +0 -10
- package/docs/reference/variables/component.md +0 -10
- package/docs/reference/variables/content.md +0 -10
- package/docs/reference/variables/contentinfo.md +0 -10
- package/docs/reference/variables/dialog.md +0 -10
- package/docs/reference/variables/field.md +0 -10
- package/docs/reference/variables/form.md +0 -10
- package/docs/reference/variables/heading.md +0 -10
- package/docs/reference/variables/http.md +0 -40
- package/docs/reference/variables/intercept.md +0 -11
- package/docs/reference/variables/link.md +0 -10
- package/docs/reference/variables/listitem.md +0 -10
- package/docs/reference/variables/main.md +0 -10
- package/docs/reference/variables/match.md +0 -8
- package/docs/reference/variables/navigation.md +0 -10
- package/docs/reference/variables/openai.md +0 -21
- package/docs/reference/variables/option.md +0 -17
- package/docs/reference/variables/region.md +0 -10
- package/docs/reference/variables/row.md +0 -10
- package/docs/reference/variables/search.md +0 -10
- package/docs/reference/variables/specification.md +0 -8
- package/docs/reference/variables/status.md +0 -10
- package/docs/reference/variables/table.md +0 -10
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
import { a as RUN_KEYS, c as isRelativePath, i as PRESENCE, n as FILE_CONTENT_KEYS, r as FILE_STATES, s as SpecSyntaxError, t as DOCUMENT_KEYS } from "./shared-spec-grammar.js";
|
|
2
|
+
import { a as isPair, c as nodeItems, f as yamlSyntaxErrors, i as isNode, l as nodePairs, o as isScalar, r as isMap, s as isSeq, u as parseYamlSource } from "./shared-yaml-document.js";
|
|
3
|
+
import { GROUND_FIXTURES, isGroundFolder } from "@jterrazz/typescript/rules";
|
|
4
|
+
//#region lint/kit/project/dedent.ts
|
|
5
|
+
function indentOf(line) {
|
|
6
|
+
return /^[\t ]*/u.exec(line)?.[0].length ?? 0;
|
|
7
|
+
}
|
|
8
|
+
/** A text opening on a line break loses that break, the closing line's whitespace and the shared indentation. */
|
|
9
|
+
function dedent(text) {
|
|
10
|
+
if (!text.startsWith("\n")) return text;
|
|
11
|
+
const lines = text.slice(1).split("\n");
|
|
12
|
+
if (lines.at(-1)?.trim() === "") lines[lines.length - 1] = "";
|
|
13
|
+
const indents = lines.filter((line) => line.trim() !== "").map((line) => indentOf(line));
|
|
14
|
+
const common = indents.length === 0 ? 0 : Math.min(...indents);
|
|
15
|
+
return lines.map((line) => line.slice(common)).join("\n");
|
|
16
|
+
}
|
|
17
|
+
//#endregion
|
|
18
|
+
//#region lint/kit/project/spec-node.ts
|
|
19
|
+
function fail(context, node, message) {
|
|
20
|
+
throw new SpecSyntaxError(context.fileName, lineOf(context, node), message);
|
|
21
|
+
}
|
|
22
|
+
/** A pair sits on its key's line; a node the parser never placed, on the first. */
|
|
23
|
+
function lineOf(context, node) {
|
|
24
|
+
const placed = isPair(node) ? node.key : node;
|
|
25
|
+
return isNode(placed) && placed.range ? context.source.lineAt(placed.range[0]) : 1;
|
|
26
|
+
}
|
|
27
|
+
function contentLine(context, node) {
|
|
28
|
+
const start = lineOf(context, node);
|
|
29
|
+
return isScalar(node) && (node.type ?? "").startsWith("BLOCK") ? start + 1 : start;
|
|
30
|
+
}
|
|
31
|
+
function requireMap(context, node, what) {
|
|
32
|
+
if (!isMap(node)) fail(context, node ?? void 0, `${what} must be a mapping of keys to values`);
|
|
33
|
+
return node;
|
|
34
|
+
}
|
|
35
|
+
function closedPairs(context, map, allowed, what) {
|
|
36
|
+
const pairs = [];
|
|
37
|
+
for (const pair of nodePairs(map)) {
|
|
38
|
+
const key = scalarString(pair.key);
|
|
39
|
+
if (key === null) fail(context, pair, `${what}: a key must be a plain word`);
|
|
40
|
+
if (!allowed.includes(key)) fail(context, pair, `unknown key "${key}:" in ${what}; known keys: ${allowed.join(", ")}`);
|
|
41
|
+
pairs.push(pair);
|
|
42
|
+
}
|
|
43
|
+
return pairs;
|
|
44
|
+
}
|
|
45
|
+
function keyed(pairs) {
|
|
46
|
+
return new Map(pairs.map((pair) => [scalarString(pair.key) ?? "", pair]));
|
|
47
|
+
}
|
|
48
|
+
function holdPresence(context, found, { line, shape, subject = "" }) {
|
|
49
|
+
const at = (message, where = line) => {
|
|
50
|
+
throw new SpecSyntaxError(context.fileName, where, message);
|
|
51
|
+
};
|
|
52
|
+
for (const law of PRESENCE[shape]) if (law.law === "each") {
|
|
53
|
+
const missing = law.keys.find((key) => !found.has(key));
|
|
54
|
+
if (missing !== void 0) at(law.refusal(subject, missing));
|
|
55
|
+
} else if (law.law === "some") {
|
|
56
|
+
if (!law.keys.some((key) => found.has(key))) at(law.refusal(subject));
|
|
57
|
+
} else if (found.has(law.with)) {
|
|
58
|
+
const beside = law.keys.find((key) => found.has(key));
|
|
59
|
+
if (beside !== void 0) at(law.refusal(subject, beside), lineOf(context, found.get(beside)));
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
function held(found, key) {
|
|
63
|
+
const pair = found.get(key);
|
|
64
|
+
if (pair === void 0) throw new Error(`"${key}:" is required by a presence law and was not found`);
|
|
65
|
+
return pair;
|
|
66
|
+
}
|
|
67
|
+
function scalarString(node) {
|
|
68
|
+
return isScalar(node) && typeof node.value === "string" ? node.value : null;
|
|
69
|
+
}
|
|
70
|
+
function requireString(context, node, what) {
|
|
71
|
+
const value = scalarString(node);
|
|
72
|
+
if (value === null) fail(context, node, `${what} must be text`);
|
|
73
|
+
return value;
|
|
74
|
+
}
|
|
75
|
+
function requireInteger(context, node, what) {
|
|
76
|
+
const value = isScalar(node) ? node.value : void 0;
|
|
77
|
+
if (!Number.isInteger(value)) fail(context, node, `${what} must be a literal integer`);
|
|
78
|
+
return Number(value);
|
|
79
|
+
}
|
|
80
|
+
function stringList(context, node, what) {
|
|
81
|
+
if (isSeq(node)) return nodeItems(node).map((item) => ({
|
|
82
|
+
line: lineOf(context, item),
|
|
83
|
+
value: requireString(context, item, `each entry of ${what}`)
|
|
84
|
+
}));
|
|
85
|
+
return [{
|
|
86
|
+
line: lineOf(context, node),
|
|
87
|
+
value: requireString(context, node, what)
|
|
88
|
+
}];
|
|
89
|
+
}
|
|
90
|
+
//#endregion
|
|
91
|
+
//#region lint/kit/project/spec-fields.ts
|
|
92
|
+
function readStream(context, node, what) {
|
|
93
|
+
return {
|
|
94
|
+
line: contentLine(context, node),
|
|
95
|
+
text: requireString(context, node, what)
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
function readEnv(context, node) {
|
|
99
|
+
return stringList(context, node, "env:").map(({ line, value }) => {
|
|
100
|
+
const separator = value.indexOf("=");
|
|
101
|
+
if (separator === -1) return {
|
|
102
|
+
kind: "set",
|
|
103
|
+
line,
|
|
104
|
+
name: value
|
|
105
|
+
};
|
|
106
|
+
if (separator === 0) throw new SpecSyntaxError(context.fileName, line, `env: "${value}" has no variable name before the "="`);
|
|
107
|
+
return {
|
|
108
|
+
key: value.slice(0, separator),
|
|
109
|
+
kind: "pair",
|
|
110
|
+
line,
|
|
111
|
+
value: value.slice(separator + 1)
|
|
112
|
+
};
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
function readServeEntry(context, node) {
|
|
116
|
+
const name = scalarString(node);
|
|
117
|
+
if (name !== null) return {
|
|
118
|
+
env: {},
|
|
119
|
+
line: lineOf(context, node),
|
|
120
|
+
name
|
|
121
|
+
};
|
|
122
|
+
const map = requireMap(context, node, "a serve: entry");
|
|
123
|
+
if (map.items.length !== 1) fail(context, node, "a serve: entry names ONE server: `- <name>: { KEY: value }`");
|
|
124
|
+
const [pair] = nodePairs(map);
|
|
125
|
+
if (pair === void 0) fail(context, node, "a serve: entry names ONE server: `- <name>: { KEY: value }`");
|
|
126
|
+
const serverName = scalarString(pair.key);
|
|
127
|
+
if (serverName === null) fail(context, pair, "a serve: entry must name its server with a plain word");
|
|
128
|
+
const env = {};
|
|
129
|
+
const envMap = requireMap(context, pair.value, `serve: ${serverName}`);
|
|
130
|
+
for (const entry of nodePairs(envMap)) {
|
|
131
|
+
const key = scalarString(entry.key);
|
|
132
|
+
if (key === null) fail(context, entry, `serve: ${serverName} is no variable name, which must be a plain word`);
|
|
133
|
+
env[key] = requireString(context, entry.value, `serve: ${serverName}.${key}`);
|
|
134
|
+
}
|
|
135
|
+
return {
|
|
136
|
+
env,
|
|
137
|
+
line: lineOf(context, pair),
|
|
138
|
+
name: serverName
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
function readServe(context, node) {
|
|
142
|
+
return (isSeq(node) ? nodeItems(node) : [node]).map((item) => readServeEntry(context, item));
|
|
143
|
+
}
|
|
144
|
+
function readFiles(context, node) {
|
|
145
|
+
const map = requireMap(context, node, "files:");
|
|
146
|
+
return nodePairs(map).map((pair) => {
|
|
147
|
+
const path = scalarString(pair.key);
|
|
148
|
+
if (path === null) fail(context, pair, "files: each key is a path relative to the working directory");
|
|
149
|
+
const line = lineOf(context, pair);
|
|
150
|
+
const state = scalarString(pair.value);
|
|
151
|
+
if (state !== null) {
|
|
152
|
+
const kind = FILE_STATES.find((known) => known === state);
|
|
153
|
+
if (kind === void 0) fail(context, pair, `files: "${path}: ${state}" is not a state: write ${FILE_STATES.join(" or ")}, or a { ${FILE_CONTENT_KEYS.join(", ")} } assertion`);
|
|
154
|
+
return {
|
|
155
|
+
kind,
|
|
156
|
+
line,
|
|
157
|
+
path
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
const pairs = closedPairs(context, requireMap(context, pair.value, `files: ${path}`), FILE_CONTENT_KEYS, `files: ${path}`);
|
|
161
|
+
holdPresence(context, keyed(pairs), {
|
|
162
|
+
line,
|
|
163
|
+
shape: "files",
|
|
164
|
+
subject: path
|
|
165
|
+
});
|
|
166
|
+
const contains = [];
|
|
167
|
+
let equals = null;
|
|
168
|
+
for (const entry of pairs) {
|
|
169
|
+
if (scalarString(entry.key) === "contains") {
|
|
170
|
+
if (isSeq(entry.value)) for (const item of nodeItems(entry.value)) contains.push(readStream(context, item, `files: ${path}.contains`));
|
|
171
|
+
else contains.push(readStream(context, entry.value, `files: ${path}.contains`));
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
equals = readStream(context, entry.value, `files: ${path}.equals`);
|
|
175
|
+
}
|
|
176
|
+
return {
|
|
177
|
+
contains,
|
|
178
|
+
equals,
|
|
179
|
+
kind: "content",
|
|
180
|
+
line,
|
|
181
|
+
path
|
|
182
|
+
};
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
function readRun(context, node, isLast) {
|
|
186
|
+
const found = keyed(closedPairs(context, requireMap(context, node, "each entry of runs:"), RUN_KEYS, "a run"));
|
|
187
|
+
holdPresence(context, found, {
|
|
188
|
+
line: lineOf(context, node),
|
|
189
|
+
shape: "run"
|
|
190
|
+
});
|
|
191
|
+
const commandPair = found.get("command");
|
|
192
|
+
const exitPair = found.get("exit");
|
|
193
|
+
if (commandPair === void 0 || exitPair === void 0) fail(context, node, "a run states its command: and its exit:");
|
|
194
|
+
const command = requireString(context, commandPair.value, "command:");
|
|
195
|
+
const waitForPair = found.get("waitFor");
|
|
196
|
+
if (waitForPair !== void 0 && !isLast) fail(context, waitForPair, "waitFor: is only allowed on the LAST run, since a long-running command ends the session");
|
|
197
|
+
const stdout = found.get("stdout");
|
|
198
|
+
const stderr = found.get("stderr");
|
|
199
|
+
const stdin = found.get("stdin");
|
|
200
|
+
const timeout = found.get("timeout");
|
|
201
|
+
const files = found.get("files");
|
|
202
|
+
return {
|
|
203
|
+
command,
|
|
204
|
+
commandLine: lineOf(context, commandPair),
|
|
205
|
+
exitCode: requireInteger(context, exitPair.value, "exit:"),
|
|
206
|
+
files: files === void 0 ? [] : readFiles(context, files.value),
|
|
207
|
+
stderr: stderr === void 0 ? null : readStream(context, stderr.value, "stderr:"),
|
|
208
|
+
stdin: stdin === void 0 ? null : requireString(context, stdin.value, "stdin:"),
|
|
209
|
+
stdout: stdout === void 0 ? null : readStream(context, stdout.value, "stdout:"),
|
|
210
|
+
timeout: timeout === void 0 ? null : requireInteger(context, timeout.value, "timeout:"),
|
|
211
|
+
waitFor: waitForPair === void 0 ? null : requireString(context, waitForPair.value, "waitFor:")
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
function groundOf(path) {
|
|
215
|
+
return path.split("/").find((segment) => isGroundFolder(segment));
|
|
216
|
+
}
|
|
217
|
+
/** A fixture is laid from `__fixtures__/`, a file or a whole directory. */
|
|
218
|
+
function readFixtures(context, node) {
|
|
219
|
+
return stringList(context, node, "fixture:").map(({ line, value }) => {
|
|
220
|
+
if (!isRelativePath(value)) throw new SpecSyntaxError(context.fileName, line, `fixture: "${value}" is not a relative path: name it from this document (./... or ../...)`);
|
|
221
|
+
const found = groundOf(value);
|
|
222
|
+
if (found !== GROUND_FIXTURES) {
|
|
223
|
+
const where = `./${GROUND_FIXTURES}/ beside the document, or in the ${GROUND_FIXTURES}/ of the lowest folder the leaves share (specs/${GROUND_FIXTURES}/ across specifications)`;
|
|
224
|
+
throw new SpecSyntaxError(context.fileName, line, `fixture: reads ${GROUND_FIXTURES}/, and "${value}" names ${found === void 0 ? `no ${GROUND_FIXTURES}/ folder` : `${found}/`}, so put the file in ${where} (verb-reads-its-ground-folder)`);
|
|
225
|
+
}
|
|
226
|
+
return {
|
|
227
|
+
line,
|
|
228
|
+
path: value
|
|
229
|
+
};
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
//#endregion
|
|
233
|
+
//#region lint/kit/project/spec-document.ts
|
|
234
|
+
/** The grammar alone: no path resolution, no command, no filesystem. The runner, the lint checker and the JSON Schema read the same constants. */
|
|
235
|
+
/** `fileName` is used verbatim in refusals. Update mode rewrites the returned YAML source, never a re-serialisation. */
|
|
236
|
+
function readSpecFile(content, fileName) {
|
|
237
|
+
const source = parseYamlSource(content);
|
|
238
|
+
const [syntax] = yamlSyntaxErrors(source);
|
|
239
|
+
if (syntax !== void 0) throw new SpecSyntaxError(fileName, syntax.line, syntax.message);
|
|
240
|
+
const context = {
|
|
241
|
+
fileName,
|
|
242
|
+
source
|
|
243
|
+
};
|
|
244
|
+
const root = source.document.contents;
|
|
245
|
+
if (root === null || isScalar(root) && root.value === null) throw new SpecSyntaxError(fileName, 1, "the document is empty");
|
|
246
|
+
const map = requireMap(context, root, "a spec document");
|
|
247
|
+
const found = keyed(closedPairs(context, map, DOCUMENT_KEYS, "a spec document"));
|
|
248
|
+
holdPresence(context, found, {
|
|
249
|
+
line: lineOf(context, map),
|
|
250
|
+
shape: "document"
|
|
251
|
+
});
|
|
252
|
+
return {
|
|
253
|
+
document: {
|
|
254
|
+
description: requireString(context, held(found, "description").value, "description:"),
|
|
255
|
+
descriptionLine: lineOf(context, found.get("description")),
|
|
256
|
+
...readCliFields(context, found)
|
|
257
|
+
},
|
|
258
|
+
source
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
function readCliFields(context, found) {
|
|
262
|
+
const runsNode = held(found, "runs");
|
|
263
|
+
if (!isSeq(runsNode.value) || runsNode.value.items.length === 0) fail(context, runsNode, "runs: is a list of at least one run");
|
|
264
|
+
const runItems = nodeItems(runsNode.value);
|
|
265
|
+
const fixturePair = found.get("fixture");
|
|
266
|
+
const envPair = found.get("env");
|
|
267
|
+
const servePair = found.get("serve");
|
|
268
|
+
return {
|
|
269
|
+
env: envPair === void 0 ? [] : readEnv(context, envPair.value),
|
|
270
|
+
fixtures: fixturePair === void 0 ? [] : readFixtures(context, fixturePair.value),
|
|
271
|
+
runs: runItems.map((item, index) => readRun(context, item, index === runItems.length - 1)),
|
|
272
|
+
serve: servePair === void 0 ? [] : readServe(context, servePair.value)
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
/** `stdin:` is an input and a description or command is prose: none appears here. */
|
|
276
|
+
function assertedStreams(document) {
|
|
277
|
+
const streams = [];
|
|
278
|
+
for (const run of document.runs) {
|
|
279
|
+
if (run.stdout !== null) streams.push(run.stdout);
|
|
280
|
+
if (run.stderr !== null) streams.push(run.stderr);
|
|
281
|
+
for (const assertion of run.files) {
|
|
282
|
+
if (assertion.kind !== "content") continue;
|
|
283
|
+
streams.push(...assertion.contains);
|
|
284
|
+
if (assertion.equals !== null) streams.push(assertion.equals);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
return streams;
|
|
288
|
+
}
|
|
289
|
+
function documentPaths(document) {
|
|
290
|
+
return document.fixtures.map(({ line, path }) => ({
|
|
291
|
+
key: "fixture",
|
|
292
|
+
kind: path.endsWith("/") ? "directory" : "file",
|
|
293
|
+
line,
|
|
294
|
+
path
|
|
295
|
+
}));
|
|
296
|
+
}
|
|
297
|
+
function parseSpecDocument(content, fileName) {
|
|
298
|
+
return readSpecFile(content, fileName).document;
|
|
299
|
+
}
|
|
300
|
+
//#endregion
|
|
301
|
+
export { scalarString as a, readSpecFile as i, documentPaths as n, dedent as o, parseSpecDocument as r, assertedStreams as t };
|
|
302
|
+
|
|
303
|
+
//# sourceMappingURL=shared-spec-document.js.map
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
//#region lint/kit/project/relative-path.ts
|
|
2
|
+
function isRelativePath(value) {
|
|
3
|
+
return value.startsWith("./") || value.startsWith("../");
|
|
4
|
+
}
|
|
5
|
+
//#endregion
|
|
6
|
+
//#region lint/kit/project/spec-grammar.ts
|
|
7
|
+
const SPEC_EXTENSION = ".spec.yaml";
|
|
8
|
+
const DOCUMENT_KEYS = [
|
|
9
|
+
"description",
|
|
10
|
+
"fixture",
|
|
11
|
+
"env",
|
|
12
|
+
"serve",
|
|
13
|
+
"runs"
|
|
14
|
+
];
|
|
15
|
+
const RUN_KEYS = [
|
|
16
|
+
"command",
|
|
17
|
+
"stdin",
|
|
18
|
+
"timeout",
|
|
19
|
+
"waitFor",
|
|
20
|
+
"exit",
|
|
21
|
+
"stdout",
|
|
22
|
+
"stderr",
|
|
23
|
+
"files"
|
|
24
|
+
];
|
|
25
|
+
const FILE_CONTENT_KEYS = ["contains", "equals"];
|
|
26
|
+
const FILE_STATES = ["absent", "exists"];
|
|
27
|
+
const PRESENCE = {
|
|
28
|
+
document: [{
|
|
29
|
+
keys: ["description"],
|
|
30
|
+
law: "each",
|
|
31
|
+
refusal: () => "missing \"description:\": every document is one test, and this is its title"
|
|
32
|
+
}, {
|
|
33
|
+
keys: ["runs"],
|
|
34
|
+
law: "each",
|
|
35
|
+
refusal: () => "missing \"runs:\": description and runs are mandatory"
|
|
36
|
+
}],
|
|
37
|
+
files: [{
|
|
38
|
+
keys: FILE_CONTENT_KEYS,
|
|
39
|
+
law: "some",
|
|
40
|
+
refusal: (path) => `files: ${path} asserts nothing: give it contains: or equals:`
|
|
41
|
+
}],
|
|
42
|
+
run: [{
|
|
43
|
+
keys: ["command", "exit"],
|
|
44
|
+
law: "each",
|
|
45
|
+
refusal: (_subject, key) => `a run is missing "${key}:": command and exit are mandatory`
|
|
46
|
+
}, {
|
|
47
|
+
keys: ["waitFor"],
|
|
48
|
+
law: "apart",
|
|
49
|
+
refusal: () => "waitFor: and stdin: cannot share a run: the pattern is watched on a process nobody closed the input of",
|
|
50
|
+
with: "stdin"
|
|
51
|
+
}]
|
|
52
|
+
};
|
|
53
|
+
var SpecSyntaxError = class extends Error {
|
|
54
|
+
line;
|
|
55
|
+
reason;
|
|
56
|
+
constructor(fileName, line, message) {
|
|
57
|
+
super(`${fileName}:${line}: ${message}`);
|
|
58
|
+
this.line = line;
|
|
59
|
+
this.reason = message;
|
|
60
|
+
this.name = "SpecSyntaxError";
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
//#endregion
|
|
64
|
+
export { RUN_KEYS as a, isRelativePath as c, PRESENCE as i, FILE_CONTENT_KEYS as n, SPEC_EXTENSION as o, FILE_STATES as r, SpecSyntaxError as s, DOCUMENT_KEYS as t };
|
|
65
|
+
|
|
66
|
+
//# sourceMappingURL=shared-spec-grammar.js.map
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { basename } from "node:path";
|
|
2
|
+
//#region lint/kit/project/specifications.ts
|
|
3
|
+
const SPECIFICATIONS = /* @__PURE__ */ new Set([
|
|
4
|
+
"api",
|
|
5
|
+
"cli",
|
|
6
|
+
"integration",
|
|
7
|
+
"job",
|
|
8
|
+
"lint",
|
|
9
|
+
"mobile",
|
|
10
|
+
"website"
|
|
11
|
+
]);
|
|
12
|
+
const IMPORT_SOURCE = /(?:\bfrom|\bimport|\brequire)\s*\(?\s*['"](?<source>[^'"]+)['"]/gu;
|
|
13
|
+
const CONSTRUCTS = /\bspecification\.[a-z]+\s*\(/u;
|
|
14
|
+
/** Constructing the runner in the file counts: a refusal spec proves the constructor that must fail to start, and has no specification module to import. */
|
|
15
|
+
function reachesRunner(text) {
|
|
16
|
+
for (const match of text.matchAll(IMPORT_SOURCE)) {
|
|
17
|
+
const { source = "" } = match.groups ?? {};
|
|
18
|
+
if (source === "@jterrazz/test" || source.startsWith("@jterrazz/test/")) return true;
|
|
19
|
+
if (basename(source).includes(".specification")) return true;
|
|
20
|
+
}
|
|
21
|
+
return CONSTRUCTS.test(text);
|
|
22
|
+
}
|
|
23
|
+
//#endregion
|
|
24
|
+
export { reachesRunner as n, SPECIFICATIONS as t };
|
|
25
|
+
|
|
26
|
+
//# sourceMappingURL=shared-specifications.js.map
|