@jterrazz/test 10.0.0 → 11.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.
@@ -1,131 +1,7 @@
1
+ import { n as describeAmbiguity, t as AmbiguousElementError } from "./ambiguity.js";
1
2
  import { mkdtempSync } from "node:fs";
2
3
  import { resolve } from "node:path";
3
4
  import { tmpdir } from "node:os";
4
- //#region src/core/specification/website/ambiguity.ts
5
- /**
6
- * The ambiguity refusal — CONVENTIONS W3.
7
- *
8
- * A visit descriptor must designate exactly one element. Acting on "the
9
- * first match" is the failure mode this module exists to prevent: the spec
10
- * keeps passing while the visitor clicks something else, and nothing ever
11
- * reports it. So when a descriptor matches several elements the framework
12
- * refuses, and the refusal has to carry everything needed to fix it without
13
- * opening a browser — the descriptor in the caller's own vocabulary, every
14
- * candidate, and the concrete rewrites that would resolve it.
15
- *
16
- * Pure string building: it takes captured data and returns a message, so the
17
- * wording is unit-testable and the browser integration stays a thin adapter.
18
- */
19
- /** `kind` → the constructor that builds it, so errors echo the caller's source. */
20
- const CONSTRUCTORS = {
21
- banner: "banner",
22
- button: "button",
23
- complementary: "complementary",
24
- contentinfo: "contentinfo",
25
- field: "field",
26
- form: "form",
27
- heading: "heading",
28
- link: "link",
29
- main: "main",
30
- navigation: "navigation",
31
- region: "region",
32
- search: "search",
33
- testId: "testId",
34
- text: "content"
35
- };
36
- /** The landmark a context string maps back to, for a copy-pasteable suggestion. */
37
- const CONTEXT_LANDMARKS = {
38
- aside: "complementary()",
39
- banner: "banner()",
40
- complementary: "complementary()",
41
- contentinfo: "contentinfo()",
42
- footer: "contentinfo()",
43
- form: "form()",
44
- header: "banner()",
45
- main: "main()",
46
- nav: "navigation()",
47
- navigation: "navigation()",
48
- search: "search()"
49
- };
50
- /**
51
- * Render a descriptor as the source that would build it — `link('Articles')`,
52
- * `within(navigation(), link('Articles', { exact: true }))`. The error speaks
53
- * the vocabulary the author wrote, never playwright's.
54
- */
55
- function formatElement(element) {
56
- const bare = formatBare(element);
57
- return element.scope ? `within(${formatElement(element.scope)}, ${bare})` : bare;
58
- }
59
- function formatBare(element) {
60
- const args = [];
61
- if (element.name !== void 0) args.push(JSON.stringify(element.name));
62
- if (element.exact) args.push("{ exact: true }");
63
- return `${CONSTRUCTORS[element.kind]}(${args.join(", ")})`;
64
- }
65
- /** `1. <a href="/articles">Articles</a> in <nav>` — one evidence line per candidate. */
66
- function formatMatch(match, index) {
67
- const attribute = match.detail ? ` ${quoteDetail(match)}` : "";
68
- const context = match.context ? ` in <${match.context}>` : "";
69
- return ` ${index + 1}. <${match.tag}${attribute}>${match.text}</${match.tag}>${context}`;
70
- }
71
- function quoteDetail(match) {
72
- return `${match.tag === "a" ? "href" : "name"}=${JSON.stringify(match.detail)}`;
73
- }
74
- /**
75
- * The rewrites worth offering, in the order they should be tried. Scoping is
76
- * always available; `exact` only when it would actually narrow the set, and
77
- * the count it would leave is stated so a still-ambiguous suggestion never
78
- * reads as a fix.
79
- */
80
- function formatFixes(element, matches) {
81
- const fixes = [];
82
- const landmarks = [...new Set(matches.map((match) => match.context && CONTEXT_LANDMARKS[match.context]).filter((landmark) => Boolean(landmark)))];
83
- if (landmarks.length > 0 && !element.scope) fixes.push(`scope it within(${landmarks[0]}, ${formatBare(element)})${landmarks.length > 1 ? ` [also here: ${landmarks.slice(1).join(", ")}]` : ""}`);
84
- else if (!element.scope) fixes.push(`scope it within(main(), ${formatBare(element)})`);
85
- if (!element.exact && element.name !== void 0) {
86
- const remaining = matches.filter((match) => match.text === element.name).length;
87
- if (remaining > 0 && remaining < matches.length) fixes.push(`exact name ${formatBare({
88
- ...element,
89
- exact: true
90
- })} [leaves ${remaining} of ${matches.length}]`);
91
- }
92
- fixes.push("other element a heading(), button() or field() may name one thing where this does not");
93
- return fixes;
94
- }
95
- /**
96
- * The W3 refusal. A distinct class so the visit wrapper can recognize an
97
- * already-complete message and let it through instead of nesting it inside
98
- * `visit scenario failed: …`, which would print the whole thing twice.
99
- */
100
- var AmbiguousElementError = class extends Error {
101
- name = "AmbiguousElementError";
102
- };
103
- /**
104
- * Build the refusal thrown when a descriptor matches more than one element.
105
- *
106
- * @param options.element The descriptor as the caller wrote it.
107
- * @param options.matches Every candidate, in DOM order (already truncated).
108
- * @param options.url The page the visitor was on when it happened.
109
- */
110
- function describeAmbiguity(options) {
111
- const { element, matches, url } = options;
112
- const fixes = formatFixes(element, matches).map((fix) => ` • ${fix}`);
113
- return [
114
- `Ambiguous element: ${formatElement(element)} matched ${matches.length} elements on ${url}.`,
115
- "",
116
- "A spec must designate exactly one element. Acting on the first match would let",
117
- "this test keep passing while the visitor interacts with something else.",
118
- "",
119
- "Matched:",
120
- ...matches.map((match, index) => formatMatch(match, index)),
121
- "",
122
- "Disambiguate with one of:",
123
- ...fixes,
124
- "",
125
- "Docs: docs/11-website.md#designating-exactly-one-element (CONVENTIONS W3)"
126
- ].join("\n");
127
- }
128
- //#endregion
129
5
  //#region src/integrations/playwright/playwright.adapter.ts
130
6
  /** How many candidates the ambiguity error enumerates before truncating. */
131
7
  const MAX_REPORTED_MATCHES = 10;
@@ -245,9 +121,9 @@ var PlaywrightAdapter = class {
245
121
  async open(url, options) {
246
122
  const context = await (await this.launch()).newContext({ extraHTTPHeaders: options.headers });
247
123
  if (options.external === "block") {
248
- const origin = new URL(options.baseUrl).origin;
124
+ const allowed = /* @__PURE__ */ new Set([new URL(options.baseUrl).origin, ...options.allowedOrigins ?? []]);
249
125
  await context.route("**/*", (route) => {
250
- if (new URL(route.request().url()).origin === origin) route.continue();
126
+ if (allowed.has(new URL(route.request().url()).origin)) route.continue();
251
127
  else route.abort();
252
128
  });
253
129
  }