@jterrazz/test 14.3.0 → 15.1.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 +11 -11
- package/dist/ambiguity.js +2 -2
- package/dist/appium.adapter.js +34 -25
- package/dist/artifacts.js +503 -525
- package/dist/checker.js +74 -73
- package/dist/index.d.ts +587 -660
- package/dist/index.js +191 -188
- package/dist/match.js +6 -16
- package/dist/oxlint.cjs +681 -873
- package/dist/oxlint.d.cts +7066 -17
- package/dist/oxlint.d.ts +7066 -17
- package/dist/oxlint.js +681 -873
- package/dist/playwright.adapter.js +36 -14
- package/dist/queue.js +27 -28
- package/dist/spec-document.js +19 -25
- package/dist/vitest.d.ts +6 -6
- package/dist/vitest.js +2 -2
- package/package.json +10 -6
|
@@ -14,8 +14,8 @@ const MAX_REPORTED_MATCHES = 10;
|
|
|
14
14
|
*/
|
|
15
15
|
function locate(root, element) {
|
|
16
16
|
const scope = element.scope ? locate(root, element.scope) : root;
|
|
17
|
-
const options = {
|
|
18
|
-
exact: element.exact,
|
|
17
|
+
const options = element.name === void 0 ? { exact: element.exact ?? false } : {
|
|
18
|
+
exact: element.exact ?? false,
|
|
19
19
|
name: element.name
|
|
20
20
|
};
|
|
21
21
|
switch (element.kind) {
|
|
@@ -30,9 +30,9 @@ function locate(root, element) {
|
|
|
30
30
|
case "button":
|
|
31
31
|
case "heading":
|
|
32
32
|
case "link": return scope.getByRole(element.kind, options);
|
|
33
|
-
case "field": return scope.getByLabel(element.name ?? "", { exact: element.exact });
|
|
33
|
+
case "field": return scope.getByLabel(element.name ?? "", { exact: element.exact ?? false });
|
|
34
34
|
case "testId": return scope.getByTestId(element.name ?? "");
|
|
35
|
-
case "text": return scope.getByText(element.name ?? "", { exact: element.exact });
|
|
35
|
+
case "text": return scope.getByText(element.name ?? "", { exact: element.exact ?? false });
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
38
|
/** Playwright signals "more than one match" through this error text. */
|
|
@@ -41,7 +41,7 @@ function isStrictViolation(error) {
|
|
|
41
41
|
}
|
|
42
42
|
/** Capture the candidates in-page — the evidence the refusal enumerates. */
|
|
43
43
|
async function captureMatches(locator) {
|
|
44
|
-
return locator.evaluateAll((nodes, limit) => {
|
|
44
|
+
return await locator.evaluateAll((nodes, limit) => {
|
|
45
45
|
const LANDMARKS = "nav, header, footer, main, aside, section, form, [role]";
|
|
46
46
|
return nodes.slice(0, limit).map((node) => {
|
|
47
47
|
const element = node;
|
|
@@ -52,7 +52,7 @@ async function captureMatches(locator) {
|
|
|
52
52
|
context: context ?? void 0,
|
|
53
53
|
detail: detail ?? void 0,
|
|
54
54
|
tag: element.tagName.toLowerCase(),
|
|
55
|
-
text: (element.textContent ?? "").
|
|
55
|
+
text: (element.textContent ?? "").replaceAll(/\s+/gu, " ").trim().slice(0, 80)
|
|
56
56
|
};
|
|
57
57
|
});
|
|
58
58
|
}, MAX_REPORTED_MATCHES);
|
|
@@ -86,17 +86,39 @@ async function findAmbiguousLevel(page, element) {
|
|
|
86
86
|
/** The visitor implementation — every action auto-waits via playwright actionability. */
|
|
87
87
|
function createVisitor(page, baseUrl) {
|
|
88
88
|
return {
|
|
89
|
-
check:
|
|
90
|
-
|
|
91
|
-
|
|
89
|
+
check: async (element) => {
|
|
90
|
+
await act(page, element, async (locator) => {
|
|
91
|
+
await locator.check();
|
|
92
|
+
});
|
|
93
|
+
},
|
|
94
|
+
click: async (element) => {
|
|
95
|
+
await act(page, element, async (locator) => {
|
|
96
|
+
await locator.click();
|
|
97
|
+
});
|
|
98
|
+
},
|
|
99
|
+
fill: async (element, value) => {
|
|
100
|
+
await act(page, element, async (locator) => {
|
|
101
|
+
await locator.fill(value);
|
|
102
|
+
});
|
|
103
|
+
},
|
|
92
104
|
goto: async (path) => {
|
|
93
105
|
await page.goto(`${baseUrl}${path}`, { waitUntil: "load" });
|
|
94
106
|
},
|
|
95
|
-
hover:
|
|
96
|
-
|
|
97
|
-
|
|
107
|
+
hover: async (element) => {
|
|
108
|
+
await act(page, element, async (locator) => {
|
|
109
|
+
await locator.hover();
|
|
110
|
+
});
|
|
111
|
+
},
|
|
112
|
+
press: async (key) => {
|
|
113
|
+
await page.keyboard.press(key);
|
|
114
|
+
},
|
|
115
|
+
see: async (element) => {
|
|
116
|
+
await act(page, element, async (locator) => {
|
|
117
|
+
await locator.waitFor({ state: "visible" });
|
|
118
|
+
});
|
|
119
|
+
},
|
|
98
120
|
select: async (element, option) => {
|
|
99
|
-
await act(page, element, (locator) => locator.selectOption(option));
|
|
121
|
+
await act(page, element, async (locator) => await locator.selectOption(option));
|
|
100
122
|
}
|
|
101
123
|
};
|
|
102
124
|
}
|
|
@@ -119,7 +141,7 @@ var PlaywrightAdapter = class {
|
|
|
119
141
|
}
|
|
120
142
|
}
|
|
121
143
|
async open(url, options) {
|
|
122
|
-
const context = await (await this.launch()).newContext({ extraHTTPHeaders: options.headers });
|
|
144
|
+
const context = await (await this.launch()).newContext({ extraHTTPHeaders: options.headers ?? {} });
|
|
123
145
|
if (options.external === "block") {
|
|
124
146
|
const allowed = /* @__PURE__ */ new Set([new URL(options.baseUrl).origin, ...options.allowedOrigins ?? []]);
|
|
125
147
|
await context.route("**/*", (route) => {
|
package/dist/queue.js
CHANGED
|
@@ -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 "./artifacts.js";
|
|
3
|
+
//#region src/specification/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,7 @@ function contractsOf(input) {
|
|
|
86
86
|
return flatten([input]);
|
|
87
87
|
}
|
|
88
88
|
//#endregion
|
|
89
|
-
//#region src/
|
|
89
|
+
//#region src/specification/contracts/queue.ts
|
|
90
90
|
/**
|
|
91
91
|
* The ONE contract queue — pure, engine-agnostic (no MSW, no node:http).
|
|
92
92
|
* Both engines consume it: the MSW integration on api/jobs chains, and the
|
|
@@ -172,20 +172,27 @@ function contractMatches(contract, request) {
|
|
|
172
172
|
function routePatternOf(declared) {
|
|
173
173
|
if (declared instanceof RegExp) return declared;
|
|
174
174
|
const { base } = splitDeclared(declared);
|
|
175
|
-
if (base.startsWith("/")) return new RegExp(String.raw`^https?:\/\/[^/?#]+${placeholderPatternSource(base)}(?:\?[^#]*)?(?:#.*)
|
|
176
|
-
if (hasPlaceholders(base)) return new RegExp(String.raw`^${placeholderPatternSource(base)}(?:\?[^#]*)?(?:#.*)
|
|
175
|
+
if (base.startsWith("/")) return new RegExp(String.raw`^https?:\/\/[^/?#]+${placeholderPatternSource(base)}(?:\?[^#]*)?(?:#.*)?$`, "u");
|
|
176
|
+
if (hasPlaceholders(base)) return new RegExp(String.raw`^${placeholderPatternSource(base)}(?:\?[^#]*)?(?:#.*)?$`, "u");
|
|
177
177
|
return base;
|
|
178
178
|
}
|
|
179
|
+
/** How a declared contract reads in a failure message: spent, served, or silent. */
|
|
180
|
+
function stateOf({ contract, served }) {
|
|
181
|
+
const { times } = contract;
|
|
182
|
+
if (times !== void 0 && served >= times) return ` (exhausted after ${times})`;
|
|
183
|
+
return served === 0 ? "" : ` (served ${served} time(s))`;
|
|
184
|
+
}
|
|
179
185
|
var ContractQueue = class {
|
|
180
|
-
|
|
181
|
-
served;
|
|
186
|
+
entries;
|
|
182
187
|
constructor(contracts) {
|
|
183
|
-
this.
|
|
184
|
-
|
|
188
|
+
this.entries = contracts.map((contract) => ({
|
|
189
|
+
contract,
|
|
190
|
+
served: 0
|
|
191
|
+
}));
|
|
185
192
|
}
|
|
186
193
|
/** How many contracts the chain declared. */
|
|
187
194
|
get size() {
|
|
188
|
-
return this.
|
|
195
|
+
return this.entries.length;
|
|
189
196
|
}
|
|
190
197
|
/**
|
|
191
198
|
* Routes to register with an out-of-process router, deduped by pattern —
|
|
@@ -193,7 +200,7 @@ var ContractQueue = class {
|
|
|
193
200
|
*/
|
|
194
201
|
get routes() {
|
|
195
202
|
const routes = /* @__PURE__ */ new Map();
|
|
196
|
-
for (const contract of this.
|
|
203
|
+
for (const { contract } of this.entries) {
|
|
197
204
|
const url = routePatternOf(contract.request.url);
|
|
198
205
|
const key = String(url);
|
|
199
206
|
const route = routes.get(key) ?? {
|
|
@@ -207,7 +214,7 @@ var ContractQueue = class {
|
|
|
207
214
|
}
|
|
208
215
|
/** The declared routes, in order — the enumeration a 501 body carries. */
|
|
209
216
|
declaredRoutes() {
|
|
210
|
-
return this.
|
|
217
|
+
return this.entries.map(({ contract }) => describeRoute(contract));
|
|
211
218
|
}
|
|
212
219
|
/**
|
|
213
220
|
* Serve the first contract in declaration order that matches and is not
|
|
@@ -215,13 +222,12 @@ var ContractQueue = class {
|
|
|
215
222
|
* matches is spent") — the strict-violation signal.
|
|
216
223
|
*/
|
|
217
224
|
take(request) {
|
|
218
|
-
for (
|
|
219
|
-
const
|
|
220
|
-
|
|
221
|
-
if (
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
return contract;
|
|
225
|
+
for (const entry of this.entries) {
|
|
226
|
+
const { times } = entry.contract;
|
|
227
|
+
if (times !== void 0 && entry.served >= times) continue;
|
|
228
|
+
if (!contractMatches(entry.contract, request)) continue;
|
|
229
|
+
entry.served += 1;
|
|
230
|
+
return entry.contract;
|
|
225
231
|
}
|
|
226
232
|
return null;
|
|
227
233
|
}
|
|
@@ -232,9 +238,8 @@ var ContractQueue = class {
|
|
|
232
238
|
*/
|
|
233
239
|
requiredError() {
|
|
234
240
|
const unmet = [];
|
|
235
|
-
for (const
|
|
241
|
+
for (const { contract, served } of this.entries) {
|
|
236
242
|
if (!contract.required) continue;
|
|
237
|
-
const served = this.served[i];
|
|
238
243
|
const { times } = contract;
|
|
239
244
|
if (times === void 0) {
|
|
240
245
|
if (served === 0) unmet.push(` - ${describeRoute(contract)} — declared and required but never requested`);
|
|
@@ -249,15 +254,9 @@ var ContractQueue = class {
|
|
|
249
254
|
* and how often it was served.
|
|
250
255
|
*/
|
|
251
256
|
unmatchedError(method, url) {
|
|
252
|
-
const declared = this.
|
|
257
|
+
const declared = this.entries.length === 0 ? " (no contracts declared)" : this.entries.map((entry) => ` - ${describeRoute(entry.contract)}${stateOf(entry)}`).join("\n");
|
|
253
258
|
return /* @__PURE__ */ new Error(`Unmatched outgoing HTTP request during spec: ${method} ${url}\nDeclared contracts:\n${declared}\nEvery outgoing request of a chain that declares contracts must match one — add a contract for it (or raise its \`times\` when it is exhausted).`);
|
|
254
259
|
}
|
|
255
|
-
stateOf(index) {
|
|
256
|
-
const served = this.served[index];
|
|
257
|
-
const { times } = this.contracts[index];
|
|
258
|
-
if (times !== void 0 && served >= times) return ` (exhausted after ${times})`;
|
|
259
|
-
return served === 0 ? "" : ` (served ${served} time(s))`;
|
|
260
|
-
}
|
|
261
260
|
};
|
|
262
261
|
//#endregion
|
|
263
262
|
export { isContract as a, defineContracts as i, contractsOf as n, isContracts as o, defineContract as r, ContractQueue as t };
|
package/dist/spec-document.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { LineCounter, Scalar, Scalar as Scalar$1, isMap, isScalar, isSeq, parseDocument, visit } from "yaml";
|
|
2
2
|
//#region src/integrations/yaml/document.ts
|
|
3
3
|
/** A line that opens a mapping key — ` key:` or ` - key:`, never block-scalar text. */
|
|
4
|
-
const KEY_LINE = /^(?<indent> +)(?:- )?[A-Za-z_$'"][^\n]
|
|
4
|
+
const KEY_LINE = /^(?<indent> +)(?:- )?[A-Za-z_$'"][^\n]*:/u;
|
|
5
5
|
/**
|
|
6
6
|
* The indentation step the file was written with, read from the shallowest
|
|
7
7
|
* mapping key it carries. A rewrite must come back in the author's style — a
|
|
@@ -11,8 +11,8 @@ const KEY_LINE = /^(?<indent> +)(?:- )?[A-Za-z_$'"][^\n]*:/;
|
|
|
11
11
|
function detectIndent(text) {
|
|
12
12
|
let smallest = 0;
|
|
13
13
|
for (const line of text.split("\n")) {
|
|
14
|
-
const
|
|
15
|
-
if (
|
|
14
|
+
const indent = KEY_LINE.exec(line)?.groups?.indent;
|
|
15
|
+
if (indent !== void 0 && (smallest === 0 || indent.length < smallest)) smallest = indent.length;
|
|
16
16
|
}
|
|
17
17
|
return smallest === 0 ? 2 : smallest;
|
|
18
18
|
}
|
|
@@ -37,7 +37,7 @@ const DEEPEST_STATABLE_STEP = 9;
|
|
|
37
37
|
* at the root, which YAML counts from -1.
|
|
38
38
|
*/
|
|
39
39
|
function stateIndentation(written, ctx) {
|
|
40
|
-
if (!/^[|>][1-9]
|
|
40
|
+
if (!/^[|>][1-9]/u.test(written)) return written;
|
|
41
41
|
const step = ctx.indent === "" ? 1 : ctx.indentStep.length;
|
|
42
42
|
if (step > DEEPEST_STATABLE_STEP) throw new Error(`A block scalar indented by ${step} spaces cannot state its indentation: YAML writes that indicator as a single digit, so ${DEEPEST_STATABLE_STEP} spaces is the deepest step a document may be written with.`);
|
|
43
43
|
return `${written[0]}${step}${written.slice(2)}`;
|
|
@@ -96,13 +96,13 @@ function lineStart(text, offset) {
|
|
|
96
96
|
/** Back over the whitespace before `offset`, to just past the `:` or `-` that owns it. */
|
|
97
97
|
function afterOwner(text, offset) {
|
|
98
98
|
let at = offset;
|
|
99
|
-
while (at > 0 && /\s
|
|
99
|
+
while (at > 0 && /\s/u.test(text[at - 1] ?? "")) at -= 1;
|
|
100
100
|
return at;
|
|
101
101
|
}
|
|
102
102
|
/** How many spaces a line opens with. */
|
|
103
103
|
function indentOf(text, offset) {
|
|
104
104
|
const start = lineStart(text, offset);
|
|
105
|
-
return /^
|
|
105
|
+
return /^ */u.exec(text.slice(start))[0].length;
|
|
106
106
|
}
|
|
107
107
|
/**
|
|
108
108
|
* Every OUTERMOST flow collection of a document, in source order. A nested one
|
|
@@ -113,11 +113,13 @@ function flowCollections(text) {
|
|
|
113
113
|
const found = [];
|
|
114
114
|
const take = (node) => {
|
|
115
115
|
if (node.flow !== true || !node.range) return;
|
|
116
|
-
const
|
|
116
|
+
const [start, end] = node.range;
|
|
117
|
+
if (start === void 0 || end === void 0) return;
|
|
118
|
+
const from = afterOwner(text, start);
|
|
117
119
|
found.push({
|
|
118
120
|
ownerIndent: indentOf(text, from === 0 ? 0 : from - 1),
|
|
119
|
-
range: [from,
|
|
120
|
-
text: text.slice(from,
|
|
121
|
+
range: [from, end],
|
|
122
|
+
text: text.slice(from, end),
|
|
121
123
|
value: JSON.stringify(node.toJSON())
|
|
122
124
|
});
|
|
123
125
|
return visit.SKIP;
|
|
@@ -154,10 +156,12 @@ function restoreUntouchedFlow(rendered, text) {
|
|
|
154
156
|
for (const target of flowCollections(rendered)) {
|
|
155
157
|
const at = sources.findIndex((candidate, index) => !taken.has(index) && candidate.value === target.value && candidate.ownerIndent === target.ownerIndent);
|
|
156
158
|
if (at === -1) continue;
|
|
159
|
+
const source = sources[at];
|
|
160
|
+
if (source === void 0) continue;
|
|
157
161
|
taken.add(at);
|
|
158
162
|
splices.push({
|
|
159
163
|
range: target.range,
|
|
160
|
-
text:
|
|
164
|
+
text: source.text
|
|
161
165
|
});
|
|
162
166
|
}
|
|
163
167
|
let out = rendered;
|
|
@@ -179,7 +183,7 @@ function renderYamlSource(source) {
|
|
|
179
183
|
}), source.text);
|
|
180
184
|
}
|
|
181
185
|
//#endregion
|
|
182
|
-
//#region src/
|
|
186
|
+
//#region src/specification/literate/spec-document.ts
|
|
183
187
|
/**
|
|
184
188
|
* The `<case>.spec.yaml` document — one scenario, written as data.
|
|
185
189
|
*
|
|
@@ -256,7 +260,7 @@ function lineOf(context, node) {
|
|
|
256
260
|
}
|
|
257
261
|
function nodeRange(node) {
|
|
258
262
|
if ("range" in node && Array.isArray(node.range)) return node.range;
|
|
259
|
-
const key = node
|
|
263
|
+
const { key } = node;
|
|
260
264
|
return key !== null && typeof key === "object" && "range" in key ? key.range ?? void 0 : void 0;
|
|
261
265
|
}
|
|
262
266
|
/** The line a scalar's CONTENT starts on — one past the `|` for a block scalar. */
|
|
@@ -423,8 +427,8 @@ function readRun(context, node, isLast) {
|
|
|
423
427
|
*/
|
|
424
428
|
function readSpecFile(content, fileName) {
|
|
425
429
|
const source = parseYamlSource(content);
|
|
426
|
-
const syntax = yamlSyntaxErrors(source);
|
|
427
|
-
if (syntax
|
|
430
|
+
const [syntax] = yamlSyntaxErrors(source);
|
|
431
|
+
if (syntax !== void 0) throw new SpecSyntaxError(fileName, syntax.line, syntax.message);
|
|
428
432
|
const context = {
|
|
429
433
|
fileName,
|
|
430
434
|
source
|
|
@@ -533,20 +537,10 @@ const STRING_OR_LIST = { oneOf: [{ type: "string" }, {
|
|
|
533
537
|
minItems: 1,
|
|
534
538
|
type: "array"
|
|
535
539
|
}] };
|
|
536
|
-
/**
|
|
537
|
-
* The JSON Schema editors validate a `<case>.spec.yaml` against, built from the
|
|
538
|
-
* constants above so it cannot describe a grammar the parser does not read. It
|
|
539
|
-
* is committed as `schema/spec.schema.json` by `npm run docs` and published as
|
|
540
|
-
* the `@jterrazz/test/schema` export.
|
|
541
|
-
*
|
|
542
|
-
* A schema states which keys exist, not which ORDER they come in — JSON Schema
|
|
543
|
-
* has no vocabulary for the sequence of an object's members. The canonical
|
|
544
|
-
* order is the `d4b-spec-key-order` lint pass's, which also rewrites it.
|
|
545
|
-
*/
|
|
546
540
|
const SPEC_SCHEMA = {
|
|
547
541
|
$defs: {
|
|
548
542
|
fileAssertion: { oneOf: [{
|
|
549
|
-
enum:
|
|
543
|
+
enum: FILE_STATES,
|
|
550
544
|
type: "string"
|
|
551
545
|
}, {
|
|
552
546
|
additionalProperties: false,
|
package/dist/vitest.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ViteUserConfig } from "vitest/config";
|
|
2
2
|
//#region src/vitest/literate-plugin.d.ts
|
|
3
|
-
|
|
3
|
+
type LiterateOptions = {
|
|
4
4
|
/**
|
|
5
5
|
* Globs added to the project's test include. Default: every `.spec.yaml`
|
|
6
6
|
* file. Narrow it when a tree holds documents that are INPUTS to other
|
|
@@ -17,13 +17,13 @@ interface LiterateOptions {
|
|
|
17
17
|
* scenario to the wrong command.
|
|
18
18
|
*/
|
|
19
19
|
specification: string;
|
|
20
|
-
}
|
|
20
|
+
};
|
|
21
21
|
/**
|
|
22
22
|
* The shape vite consumes. Declared structurally rather than imported from
|
|
23
23
|
* `vite`, so the package's runner coupling stays the one dependency it already
|
|
24
24
|
* has (CONVENTIONS I1) — the object is assignable to vite's `Plugin`.
|
|
25
25
|
*/
|
|
26
|
-
|
|
26
|
+
type LiteratePlugin = {
|
|
27
27
|
config: () => {
|
|
28
28
|
test: {
|
|
29
29
|
include: string[];
|
|
@@ -36,7 +36,7 @@ interface LiteratePlugin {
|
|
|
36
36
|
enforce: 'pre';
|
|
37
37
|
load: (id: string) => null | string;
|
|
38
38
|
name: string;
|
|
39
|
-
}
|
|
39
|
+
};
|
|
40
40
|
declare function literate(options: LiterateOptions): LiteratePlugin;
|
|
41
41
|
//#endregion
|
|
42
42
|
//#region src/vitest/preset.d.ts
|
|
@@ -44,7 +44,7 @@ declare function literate(options: LiterateOptions): LiteratePlugin;
|
|
|
44
44
|
* Options are a vite/vitest config plus one key of the framework's own.
|
|
45
45
|
* Everything stated here wins over the preset's defaults.
|
|
46
46
|
*/
|
|
47
|
-
|
|
47
|
+
type SpecConfig = {
|
|
48
48
|
/**
|
|
49
49
|
* Turns every matching `<case>.spec.yaml` into a test file bound to the
|
|
50
50
|
* named specification — the {@link literate} plugin, added to `plugins`.
|
|
@@ -54,7 +54,7 @@ interface SpecConfig extends ViteUserConfig {
|
|
|
54
54
|
* so the plugin goes in that project's own `plugins` instead.
|
|
55
55
|
*/
|
|
56
56
|
literate?: LiterateOptions;
|
|
57
|
-
}
|
|
57
|
+
} & ViteUserConfig;
|
|
58
58
|
declare function defineSpecConfig(config?: SpecConfig): ViteUserConfig;
|
|
59
59
|
//#endregion
|
|
60
60
|
export { type LiterateOptions, type LiteratePlugin, type SpecConfig, defineSpecConfig, literate };
|
package/dist/vitest.js
CHANGED
|
@@ -24,7 +24,7 @@ import { configDefaults, mergeConfig } from "vitest/config";
|
|
|
24
24
|
* ```
|
|
25
25
|
*/
|
|
26
26
|
/** The `.spec.yaml` extension, matched on the id vite hands the plugin. */
|
|
27
|
-
const SPEC_FILE = /\.spec\.yaml(?:\?.*)
|
|
27
|
+
const SPEC_FILE = /\.spec\.yaml(?:\?.*)?$/u;
|
|
28
28
|
/**
|
|
29
29
|
* The module a `<case>.spec.yaml` becomes: one `test()` whose title is the
|
|
30
30
|
* document's `description:` and whose body runs the whole file — its ground,
|
|
@@ -46,7 +46,7 @@ function literateModule(content, filePath, specificationPath) {
|
|
|
46
46
|
}
|
|
47
47
|
/** Strip vite's query suffix (`?v=…`) from a module id. */
|
|
48
48
|
function cleanId(id) {
|
|
49
|
-
return id.split("?")[0];
|
|
49
|
+
return id.split("?")[0] ?? id;
|
|
50
50
|
}
|
|
51
51
|
function literate(options) {
|
|
52
52
|
const include = options.include ?? ["**/*.spec.yaml"];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jterrazz/test",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "15.1.0",
|
|
4
4
|
"description": "Declarative testing framework for HTTP APIs, CLIs, and background jobs — one entry point, real infrastructure, golden-file assertions, plus an oxlint convention plugin.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"api-testing",
|
|
@@ -41,9 +41,14 @@
|
|
|
41
41
|
"import": "./dist/vitest.js"
|
|
42
42
|
},
|
|
43
43
|
"./oxlint": {
|
|
44
|
-
"
|
|
45
|
-
|
|
46
|
-
|
|
44
|
+
"import": {
|
|
45
|
+
"types": "./dist/oxlint.d.ts",
|
|
46
|
+
"default": "./dist/oxlint.js"
|
|
47
|
+
},
|
|
48
|
+
"require": {
|
|
49
|
+
"types": "./dist/oxlint.d.cts",
|
|
50
|
+
"default": "./dist/oxlint.cjs"
|
|
51
|
+
}
|
|
47
52
|
},
|
|
48
53
|
"./schema": "./schema/spec.schema.json"
|
|
49
54
|
},
|
|
@@ -69,12 +74,11 @@
|
|
|
69
74
|
},
|
|
70
75
|
"devDependencies": {
|
|
71
76
|
"@hono/node-server": "^2.0.8",
|
|
72
|
-
"@jterrazz/typescript": "^
|
|
77
|
+
"@jterrazz/typescript": "^10.0.0",
|
|
73
78
|
"@types/better-sqlite3": "^7.6.13",
|
|
74
79
|
"@types/node": "^26.1.1",
|
|
75
80
|
"@types/pg": "^8.20.0",
|
|
76
81
|
"hono": "^4.12.30",
|
|
77
|
-
"oxlint": "^1.74.0",
|
|
78
82
|
"playwright": "^1.49.0",
|
|
79
83
|
"tsdown": "^0.22.7",
|
|
80
84
|
"vitest": "^4.1.10",
|