@setsquare/review-sdk 0.0.1

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.
Files changed (181) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +57 -0
  3. package/README.md +338 -0
  4. package/THIRD-PARTY-NOTICES.md +61 -0
  5. package/dist/autoscan.d.ts +25 -0
  6. package/dist/autoscan.d.ts.map +1 -0
  7. package/dist/autoscan.js +332 -0
  8. package/dist/autoscan.js.map +1 -0
  9. package/dist/axe.d.ts +96 -0
  10. package/dist/axe.d.ts.map +1 -0
  11. package/dist/axe.js +210 -0
  12. package/dist/axe.js.map +1 -0
  13. package/dist/bin/setsquare.d.ts +3 -0
  14. package/dist/bin/setsquare.d.ts.map +1 -0
  15. package/dist/bin/setsquare.js +21 -0
  16. package/dist/bin/setsquare.js.map +1 -0
  17. package/dist/brand.d.ts +84 -0
  18. package/dist/brand.d.ts.map +1 -0
  19. package/dist/brand.js +335 -0
  20. package/dist/brand.js.map +1 -0
  21. package/dist/checks/dynamic.d.ts +57 -0
  22. package/dist/checks/dynamic.d.ts.map +1 -0
  23. package/dist/checks/dynamic.js +184 -0
  24. package/dist/checks/dynamic.js.map +1 -0
  25. package/dist/checks/rule-pack.d.ts +34 -0
  26. package/dist/checks/rule-pack.d.ts.map +1 -0
  27. package/dist/checks/rule-pack.js +270 -0
  28. package/dist/checks/rule-pack.js.map +1 -0
  29. package/dist/checks/versions.d.ts +28 -0
  30. package/dist/checks/versions.d.ts.map +1 -0
  31. package/dist/checks/versions.js +45 -0
  32. package/dist/checks/versions.js.map +1 -0
  33. package/dist/ci.d.ts +37 -0
  34. package/dist/ci.d.ts.map +1 -0
  35. package/dist/ci.js +199 -0
  36. package/dist/ci.js.map +1 -0
  37. package/dist/contract.d.ts +334 -0
  38. package/dist/contract.d.ts.map +1 -0
  39. package/dist/contract.js +35 -0
  40. package/dist/contract.js.map +1 -0
  41. package/dist/dedupe.d.ts +79 -0
  42. package/dist/dedupe.d.ts.map +1 -0
  43. package/dist/dedupe.js +102 -0
  44. package/dist/dedupe.js.map +1 -0
  45. package/dist/document.d.ts +29 -0
  46. package/dist/document.d.ts.map +1 -0
  47. package/dist/document.js +94 -0
  48. package/dist/document.js.map +1 -0
  49. package/dist/envelope.d.ts +124 -0
  50. package/dist/envelope.d.ts.map +1 -0
  51. package/dist/envelope.js +337 -0
  52. package/dist/envelope.js.map +1 -0
  53. package/dist/escape.d.ts +14 -0
  54. package/dist/escape.d.ts.map +1 -0
  55. package/dist/escape.js +20 -0
  56. package/dist/escape.js.map +1 -0
  57. package/dist/findings.d.ts +47 -0
  58. package/dist/findings.d.ts.map +1 -0
  59. package/dist/findings.js +229 -0
  60. package/dist/findings.js.map +1 -0
  61. package/dist/fixture.d.ts +7 -0
  62. package/dist/fixture.d.ts.map +1 -0
  63. package/dist/fixture.js +237 -0
  64. package/dist/fixture.js.map +1 -0
  65. package/dist/group.d.ts +214 -0
  66. package/dist/group.d.ts.map +1 -0
  67. package/dist/group.js +709 -0
  68. package/dist/group.js.map +1 -0
  69. package/dist/html-report.d.ts +68 -0
  70. package/dist/html-report.d.ts.map +1 -0
  71. package/dist/html-report.js +649 -0
  72. package/dist/html-report.js.map +1 -0
  73. package/dist/human.d.ts +65 -0
  74. package/dist/human.d.ts.map +1 -0
  75. package/dist/human.js +111 -0
  76. package/dist/human.js.map +1 -0
  77. package/dist/index.d.ts +34 -0
  78. package/dist/index.d.ts.map +1 -0
  79. package/dist/index.js +33 -0
  80. package/dist/index.js.map +1 -0
  81. package/dist/infer.d.ts +65 -0
  82. package/dist/infer.d.ts.map +1 -0
  83. package/dist/infer.js +67 -0
  84. package/dist/infer.js.map +1 -0
  85. package/dist/insights.d.ts +140 -0
  86. package/dist/insights.d.ts.map +1 -0
  87. package/dist/insights.js +483 -0
  88. package/dist/insights.js.map +1 -0
  89. package/dist/install.d.ts +8 -0
  90. package/dist/install.d.ts.map +1 -0
  91. package/dist/install.js +114 -0
  92. package/dist/install.js.map +1 -0
  93. package/dist/links.d.ts +45 -0
  94. package/dist/links.d.ts.map +1 -0
  95. package/dist/links.js +45 -0
  96. package/dist/links.js.map +1 -0
  97. package/dist/mask.d.ts +85 -0
  98. package/dist/mask.d.ts.map +1 -0
  99. package/dist/mask.js +98 -0
  100. package/dist/mask.js.map +1 -0
  101. package/dist/observe.d.ts +51 -0
  102. package/dist/observe.d.ts.map +1 -0
  103. package/dist/observe.js +233 -0
  104. package/dist/observe.js.map +1 -0
  105. package/dist/options.d.ts +249 -0
  106. package/dist/options.d.ts.map +1 -0
  107. package/dist/options.js +185 -0
  108. package/dist/options.js.map +1 -0
  109. package/dist/output.d.ts +113 -0
  110. package/dist/output.d.ts.map +1 -0
  111. package/dist/output.js +231 -0
  112. package/dist/output.js.map +1 -0
  113. package/dist/perf.d.ts +11 -0
  114. package/dist/perf.d.ts.map +1 -0
  115. package/dist/perf.js +150 -0
  116. package/dist/perf.js.map +1 -0
  117. package/dist/reading-level.d.ts +73 -0
  118. package/dist/reading-level.d.ts.map +1 -0
  119. package/dist/reading-level.js +189 -0
  120. package/dist/reading-level.js.map +1 -0
  121. package/dist/reporter.d.ts +94 -0
  122. package/dist/reporter.d.ts.map +1 -0
  123. package/dist/reporter.js +709 -0
  124. package/dist/reporter.js.map +1 -0
  125. package/dist/rules.d.ts +55 -0
  126. package/dist/rules.d.ts.map +1 -0
  127. package/dist/rules.js +77 -0
  128. package/dist/rules.js.map +1 -0
  129. package/dist/runtime.d.ts +32 -0
  130. package/dist/runtime.d.ts.map +1 -0
  131. package/dist/runtime.js +67 -0
  132. package/dist/runtime.js.map +1 -0
  133. package/dist/sanitise.d.ts +116 -0
  134. package/dist/sanitise.d.ts.map +1 -0
  135. package/dist/sanitise.js +247 -0
  136. package/dist/sanitise.js.map +1 -0
  137. package/dist/scan.d.ts +60 -0
  138. package/dist/scan.d.ts.map +1 -0
  139. package/dist/scan.js +246 -0
  140. package/dist/scan.js.map +1 -0
  141. package/dist/screens.d.ts +98 -0
  142. package/dist/screens.d.ts.map +1 -0
  143. package/dist/screens.js +301 -0
  144. package/dist/screens.js.map +1 -0
  145. package/dist/settle.d.ts +65 -0
  146. package/dist/settle.d.ts.map +1 -0
  147. package/dist/settle.js +162 -0
  148. package/dist/settle.js.map +1 -0
  149. package/dist/shots.d.ts +109 -0
  150. package/dist/shots.d.ts.map +1 -0
  151. package/dist/shots.js +113 -0
  152. package/dist/shots.js.map +1 -0
  153. package/dist/style.d.ts +47 -0
  154. package/dist/style.d.ts.map +1 -0
  155. package/dist/style.js +154 -0
  156. package/dist/style.js.map +1 -0
  157. package/dist/suite.d.ts +218 -0
  158. package/dist/suite.d.ts.map +1 -0
  159. package/dist/suite.js +219 -0
  160. package/dist/suite.js.map +1 -0
  161. package/dist/summary.d.ts +268 -0
  162. package/dist/summary.d.ts.map +1 -0
  163. package/dist/summary.js +1308 -0
  164. package/dist/summary.js.map +1 -0
  165. package/dist/unscannable.d.ts +75 -0
  166. package/dist/unscannable.d.ts.map +1 -0
  167. package/dist/unscannable.js +111 -0
  168. package/dist/unscannable.js.map +1 -0
  169. package/dist/versions.d.ts +13 -0
  170. package/dist/versions.d.ts.map +1 -0
  171. package/dist/versions.js +39 -0
  172. package/dist/versions.js.map +1 -0
  173. package/dist/wcag-map.d.ts +19 -0
  174. package/dist/wcag-map.d.ts.map +1 -0
  175. package/dist/wcag-map.js +52 -0
  176. package/dist/wcag-map.js.map +1 -0
  177. package/dist/with-setsquare.d.ts +26 -0
  178. package/dist/with-setsquare.d.ts.map +1 -0
  179. package/dist/with-setsquare.js +70 -0
  180. package/dist/with-setsquare.js.map +1 -0
  181. package/package.json +81 -0
@@ -0,0 +1,47 @@
1
+ import type { AxeFinding, RuleInapplicable, RulePass } from '@setsquare/schema';
2
+ import type { AxeRunResults } from './axe.js';
3
+ export declare function toFindings(results: AxeRunResults): AxeFinding[];
4
+ /**
5
+ * Rule-level pass rows.
6
+ *
7
+ * Emitted from the first release because P2 computes "regressed" as a JOIN
8
+ * against these, so a run ingested without them leaves a permanent hole in
9
+ * evidence history. `inapplicable` is deliberately excluded: a rule with nothing
10
+ * to check did not pass, and recording it as one would inflate every conformance
11
+ * claim built on this data.
12
+ */
13
+ export declare function toPasses(results: AxeRunResults): RulePass[];
14
+ /**
15
+ * EARL's `inapplicable`: rules that ran and found nothing to check.
16
+ *
17
+ * Rule-level like passes, but never conflated with them — a page with no
18
+ * `<video>` did not pass the captions rule. Carried because "your pages
19
+ * contain nothing this criterion regulates" is coverage a ledger must be able
20
+ * to state; without these rows it can only say "did not run", which
21
+ * understates what the scan did.
22
+ */
23
+ export declare function toInapplicable(results: AxeRunResults): RuleInapplicable[];
24
+ export declare function toWcag(tags: string[]): AxeFinding['wcag'] | undefined;
25
+ /**
26
+ * Bound and clean computed evidence.
27
+ *
28
+ * Three filters, narrowest first.
29
+ *
30
+ * **By name** (`isPermittedComputedValue`): a rule contributes the keys the
31
+ * contract says it may, and an unenumerated rule contributes numbers and
32
+ * booleans only. This is the filter that makes "no page text leaves the
33
+ * customer's CI" a property of the code rather than a habit of its authors — a
34
+ * check that starts putting `node.textContent` under a new key emits nothing
35
+ * until that key is declared in `@setsquare/schema`.
36
+ *
37
+ * **By shape**: nested objects and arrays are dropped, because that is where
38
+ * `relatedNodes` and other page fragments hide, and no rule family needs them
39
+ * for arithmetic.
40
+ *
41
+ * **By size**: strings are stripped of hostile characters and capped, the whole
42
+ * object is size-capped, and anything that will not serialise is dropped rather
43
+ * than risked. URL-valued keys lose their query and fragment first — a path is
44
+ * evidence, a query string is where tokens and addresses live.
45
+ */
46
+ export declare function sanitiseComputed(data: Record<string, unknown>, ruleId: string): Record<string, unknown> | undefined;
47
+ //# sourceMappingURL=findings.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"findings.d.ts","sourceRoot":"","sources":["../src/findings.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAIhF,OAAO,KAAK,EAA0B,aAAa,EAAE,MAAM,UAAU,CAAC;AAoCtE,wBAAgB,UAAU,CAAC,OAAO,EAAE,aAAa,GAAG,UAAU,EAAE,CAK/D;AAED;;;;;;;;GAQG;AACH,wBAAgB,QAAQ,CAAC,OAAO,EAAE,aAAa,GAAG,QAAQ,EAAE,CAc3D;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,aAAa,GAAG,gBAAgB,EAAE,CASzE;AAiDD,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,UAAU,CAAC,MAAM,CAAC,GAAG,SAAS,CAkCrE;AAwCD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,MAAM,EAAE,MAAM,GACb,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAmBrC"}
@@ -0,0 +1,229 @@
1
+ import { isPermittedComputedValue, isUrlValuedComputedKey, stripUrlTail } from '@setsquare/schema';
2
+ import { MAX_RULE_ID_LENGTH } from '@setsquare/schema/limits';
3
+ import { capLength, sanitiseFailureSummary, sanitiseSelectors, stripHostileCharacters, } from './sanitise.js';
4
+ /**
5
+ * Map axe's output onto the contract's shapes.
6
+ *
7
+ * Everything crossing this boundary is untrusted: selectors and class names come
8
+ * from customer-page DOM, which routinely contains third-party embeds and
9
+ * user-generated content. Nothing is copied through without passing the
10
+ * sanitiser, and the two fields axe offers that we deliberately never carry are
11
+ * `node.html` (schema v1 has no snippet field, and this is the field that would
12
+ * otherwise smuggle whole page fragments into evidence) and `relatedNodes`
13
+ * (which contains the same HTML for other elements).
14
+ */
15
+ /** Bounded, because computed evidence is page-derived like everything else. */
16
+ const MAX_COMPUTED_BYTES = 2048;
17
+ const IMPACTS = new Set(['minor', 'moderate', 'serious', 'critical']);
18
+ /**
19
+ * `wcag412` -> `4.1.2`, `wcag1410` -> `1.4.10`.
20
+ *
21
+ * The first two digits are always single, the rest is the criterion number —
22
+ * which is why this cannot be a naive split. 1.4.10 and 1.4.1 would be
23
+ * indistinguishable under one.
24
+ */
25
+ const CRITERION_TAG = /^wcag(\d)(\d)(\d+)$/;
26
+ /** `wcag2a`, `wcag21aa`, `wcag22aa` — the version-and-level tags. */
27
+ const VERSION_TAG = /^wcag(\d)(\d)?(a{1,3})$/;
28
+ export function toFindings(results) {
29
+ return [
30
+ ...results.violations.map((rule) => toFinding(rule, 'violation')),
31
+ ...results.incomplete.map((rule) => toFinding(rule, 'incomplete')),
32
+ ];
33
+ }
34
+ /**
35
+ * Rule-level pass rows.
36
+ *
37
+ * Emitted from the first release because P2 computes "regressed" as a JOIN
38
+ * against these, so a run ingested without them leaves a permanent hole in
39
+ * evidence history. `inapplicable` is deliberately excluded: a rule with nothing
40
+ * to check did not pass, and recording it as one would inflate every conformance
41
+ * claim built on this data.
42
+ */
43
+ export function toPasses(results) {
44
+ return results.passes.map((rule) => {
45
+ const pass = {
46
+ ruleId: capLength(stripHostileCharacters(rule.id), MAX_RULE_ID_LENGTH),
47
+ nodeCount: rule.nodes.length,
48
+ };
49
+ // The same WCAG identity findings carry, so the conformance projection can
50
+ // credit a passing rule to its criteria. Absent on best-practice rules,
51
+ // exactly as it is on findings — a pass row with no wcag block is advice
52
+ // that held, not conformance evidence.
53
+ const wcag = toWcag(rule.tags);
54
+ if (wcag)
55
+ pass.wcag = wcag;
56
+ return pass;
57
+ });
58
+ }
59
+ /**
60
+ * EARL's `inapplicable`: rules that ran and found nothing to check.
61
+ *
62
+ * Rule-level like passes, but never conflated with them — a page with no
63
+ * `<video>` did not pass the captions rule. Carried because "your pages
64
+ * contain nothing this criterion regulates" is coverage a ledger must be able
65
+ * to state; without these rows it can only say "did not run", which
66
+ * understates what the scan did.
67
+ */
68
+ export function toInapplicable(results) {
69
+ return results.inapplicable.map((rule) => {
70
+ const row = {
71
+ ruleId: capLength(stripHostileCharacters(rule.id), MAX_RULE_ID_LENGTH),
72
+ };
73
+ const wcag = toWcag(rule.tags);
74
+ if (wcag)
75
+ row.wcag = wcag;
76
+ return row;
77
+ });
78
+ }
79
+ function toFinding(rule, status) {
80
+ const selectors = rule.nodes.flatMap((node) => selectorsOf(node));
81
+ const targets = sanitiseSelectors(selectors);
82
+ const finding = {
83
+ ruleId: capLength(stripHostileCharacters(rule.id), MAX_RULE_ID_LENGTH),
84
+ status,
85
+ targets: {
86
+ // The TRUE total, even though `selectors` is capped. An agent needs to know
87
+ // the shape of the problem and its size; the cap only bounds the former.
88
+ nodeCount: rule.nodes.length,
89
+ ...targets,
90
+ },
91
+ };
92
+ // Axe reports no impact on some incompletes. Absent means unknown, and must
93
+ // not quietly become "minor".
94
+ if (rule.impact && IMPACTS.has(rule.impact)) {
95
+ finding.impact = rule.impact;
96
+ }
97
+ const wcag = toWcag(rule.tags);
98
+ if (wcag)
99
+ finding.wcag = wcag;
100
+ if (rule.helpUrl) {
101
+ // Axe appends `?application=axeAPI`, which is telemetry-ish noise on a link a
102
+ // human is meant to click.
103
+ const helpUrl = rule.helpUrl.split('?')[0];
104
+ if (helpUrl && helpUrl.startsWith('https://'))
105
+ finding.helpUrl = helpUrl;
106
+ }
107
+ const evidence = toEvidence(rule.nodes, finding.ruleId);
108
+ if (evidence)
109
+ finding.evidence = evidence;
110
+ return finding;
111
+ }
112
+ /** Axe's targets are strings, or arrays of strings for nested frames. */
113
+ function selectorsOf(node) {
114
+ return node.target.flatMap((entry) => {
115
+ if (typeof entry === 'string')
116
+ return [entry];
117
+ if (Array.isArray(entry))
118
+ return entry.filter((part) => typeof part === 'string');
119
+ return [];
120
+ });
121
+ }
122
+ export function toWcag(tags) {
123
+ const criteria = [];
124
+ const versionTags = [];
125
+ let level;
126
+ for (const tag of tags) {
127
+ const criterion = CRITERION_TAG.exec(tag);
128
+ if (criterion) {
129
+ criteria.push(`${criterion[1]}.${criterion[2]}.${criterion[3]}`);
130
+ continue;
131
+ }
132
+ const version = VERSION_TAG.exec(tag);
133
+ if (version) {
134
+ versionTags.push(tag);
135
+ // Keep the STRONGEST level the rule carries. A rule tagged both wcag2a and
136
+ // wcag2aa is an A-level requirement; reporting AA would overstate what
137
+ // failing it means.
138
+ const candidate = version[3]?.toUpperCase();
139
+ if (!level || candidate.length < level.length)
140
+ level = candidate;
141
+ }
142
+ }
143
+ if (criteria.length === 0 && versionTags.length === 0)
144
+ return undefined;
145
+ const wcag = {
146
+ // Sorted and deduplicated so two runs produce byte-identical evidence.
147
+ criteria: [...new Set(criteria)].sort().slice(0, 20),
148
+ versionTags: [...new Set(versionTags)].sort().slice(0, 20),
149
+ };
150
+ if (level)
151
+ wcag.level = level;
152
+ return wcag;
153
+ }
154
+ /**
155
+ * Failure summary plus computed evidence.
156
+ *
157
+ * `data` from the failing check is the "agent can do arithmetic" field — for
158
+ * contrast it holds the measured ratio, the required ratio and both colours,
159
+ * which is the difference between "fix the contrast" and "this token is 2.51:1
160
+ * and needs 4.5:1". Taken only from the check that failed, and only `data`:
161
+ * `relatedNodes` carries HTML for other elements and never crosses this line.
162
+ */
163
+ function toEvidence(nodes, ruleId) {
164
+ const first = nodes[0];
165
+ if (!first)
166
+ return undefined;
167
+ const evidence = {};
168
+ const summary = sanitiseFailureSummary(first.failureSummary);
169
+ if (summary)
170
+ evidence.failureSummary = summary;
171
+ const computed = computedFrom(first, ruleId);
172
+ if (computed)
173
+ evidence.computed = computed;
174
+ return evidence.failureSummary || evidence.computed ? evidence : undefined;
175
+ }
176
+ function computedFrom(node, ruleId) {
177
+ for (const bucket of [node.any, node.all, node.none]) {
178
+ for (const check of bucket ?? []) {
179
+ const data = check.data;
180
+ if (!data || typeof data !== 'object' || Array.isArray(data))
181
+ continue;
182
+ const cleaned = sanitiseComputed(data, ruleId);
183
+ if (cleaned)
184
+ return cleaned;
185
+ }
186
+ }
187
+ return undefined;
188
+ }
189
+ /**
190
+ * Bound and clean computed evidence.
191
+ *
192
+ * Three filters, narrowest first.
193
+ *
194
+ * **By name** (`isPermittedComputedValue`): a rule contributes the keys the
195
+ * contract says it may, and an unenumerated rule contributes numbers and
196
+ * booleans only. This is the filter that makes "no page text leaves the
197
+ * customer's CI" a property of the code rather than a habit of its authors — a
198
+ * check that starts putting `node.textContent` under a new key emits nothing
199
+ * until that key is declared in `@setsquare/schema`.
200
+ *
201
+ * **By shape**: nested objects and arrays are dropped, because that is where
202
+ * `relatedNodes` and other page fragments hide, and no rule family needs them
203
+ * for arithmetic.
204
+ *
205
+ * **By size**: strings are stripped of hostile characters and capped, the whole
206
+ * object is size-capped, and anything that will not serialise is dropped rather
207
+ * than risked. URL-valued keys lose their query and fragment first — a path is
208
+ * evidence, a query string is where tokens and addresses live.
209
+ */
210
+ export function sanitiseComputed(data, ruleId) {
211
+ const cleaned = {};
212
+ for (const [key, value] of Object.entries(data)) {
213
+ if (!isPermittedComputedValue(ruleId, key, value))
214
+ continue;
215
+ if (typeof value === 'string') {
216
+ const bounded = isUrlValuedComputedKey(ruleId, key) ? stripUrlTail(value) : value;
217
+ cleaned[stripHostileCharacters(key)] = capLength(stripHostileCharacters(bounded), 256);
218
+ }
219
+ else if (typeof value === 'number' || typeof value === 'boolean')
220
+ cleaned[stripHostileCharacters(key)] = value;
221
+ }
222
+ if (Object.keys(cleaned).length === 0)
223
+ return undefined;
224
+ const serialised = JSON.stringify(cleaned);
225
+ if (!serialised || serialised.length > MAX_COMPUTED_BYTES)
226
+ return undefined;
227
+ return cleaned;
228
+ }
229
+ //# sourceMappingURL=findings.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"findings.js","sourceRoot":"","sources":["../src/findings.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import type { BrowserContext, TestInfo } from '@playwright/test';
2
+ import type { ResolvedSetsquareOptions } from './options.js';
3
+ export declare function createContextFixture(options: ResolvedSetsquareOptions): ({ context, browserName }: {
4
+ context: BrowserContext;
5
+ browserName: string;
6
+ }, use: (context: BrowserContext) => Promise<void>, testInfo: TestInfo) => Promise<void>;
7
+ //# sourceMappingURL=fixture.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fixture.d.ts","sourceRoot":"","sources":["../src/fixture.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAQ,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAYvE,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,cAAc,CAAC;AAwE7D,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,wBAAwB,IAElE,0BAA0B;IAAE,OAAO,EAAE,cAAc,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,EAC1E,KAAK,CAAC,OAAO,EAAE,cAAc,KAAK,OAAO,CAAC,IAAI,CAAC,EAC/C,UAAU,QAAQ,KACjB,OAAO,CAAC,IAAI,CAAC,CAmFjB"}
@@ -0,0 +1,237 @@
1
+ import { SCAN_RECORD_ATTACHMENT, SCAN_RECORD_VERSION, } from './contract.js';
2
+ import { flushAutoScan, installAutoScan } from './autoscan.js';
3
+ import { observePageRuntime } from './observe.js';
4
+ import { unscannableOmitted, unscannableRows } from './unscannable.js';
5
+ /**
6
+ * The capture half of the SDK: an override of Playwright's `context` fixture
7
+ * that watches every page and records what it saw.
8
+ *
9
+ * It forms no opinions. No dedupe, no inference, no aggregation, no disk — all of
10
+ * that is the reporter's, because the reporter is the only thing that exists once
11
+ * per run. This file runs in N worker processes, and anything stateful here is a
12
+ * cross-process bug in waiting.
13
+ */
14
+ /**
15
+ * Records SPA route changes from inside the page.
16
+ *
17
+ * `history.pushState` and `replaceState` fire no navigation event that Playwright
18
+ * can see, so a `framenavigated` listener alone observes ONE url on a
19
+ * single-page app and misses every route the user actually visited. Patching the
20
+ * two methods and listening for `popstate` is the only way to see them.
21
+ *
22
+ * Deliberately writes to an array on `window` rather than exposing a binding or
23
+ * dispatching events: a binding installs a function the application can see, and
24
+ * this is the customer's page, not ours. The array is read back at teardown.
25
+ *
26
+ * KNOWN LIMIT, and it is why this is capture-only for now: `addInitScript` runs
27
+ * fresh on every real navigation, so the log resets with each document. Route
28
+ * changes made before the last full navigation are not recovered at teardown.
29
+ * That is fine while nothing depends on the timing, and it stops being fine the
30
+ * moment auto-scan needs to fire AT the route change rather than learn about it
31
+ * afterwards. That is the point at which this grows a binding (P1-4.2).
32
+ */
33
+ const NAV_LOG_SCRIPT = `
34
+ (() => {
35
+ const log = [];
36
+ Object.defineProperty(window, '__setsquareNavLog', {
37
+ value: log,
38
+ // Non-enumerable so it does not show up in a customer's own iteration over
39
+ // window, and non-writable so page code cannot clobber it by accident.
40
+ enumerable: false,
41
+ writable: false,
42
+ configurable: true,
43
+ });
44
+
45
+ const record = (kind) => log.push({ kind, url: location.href, at: Date.now() });
46
+
47
+ for (const method of ['pushState', 'replaceState']) {
48
+ const original = history[method];
49
+ history[method] = function (...args) {
50
+ const result = original.apply(this, args);
51
+ try { record(method); } catch {}
52
+ return result;
53
+ };
54
+ }
55
+
56
+ window.addEventListener('popstate', () => { try { record('popstate'); } catch {} });
57
+ })();
58
+ `;
59
+ const NAV_KINDS = new Set(['pushState', 'replaceState', 'popstate']);
60
+ export function createContextFixture(options) {
61
+ return async function setsquareContext({ context, browserName }, use, testInfo) {
62
+ const startedAt = Date.now();
63
+ const observers = [];
64
+ const warnings = [];
65
+ // Applies to pages that already exist and to every page opened later,
66
+ // including ones the test opens itself. A test driving two sessions is
67
+ // ordinary in a real suite, and attributing one page's evidence to another
68
+ // page's context would be worse than capturing nothing.
69
+ await context.addInitScript(NAV_LOG_SCRIPT).catch(() => {
70
+ // A context that refuses init scripts still yields real navigations below.
71
+ // Partial evidence beats none, provided we do not pretend it is complete.
72
+ });
73
+ const observe = (page) => {
74
+ const observer = { page, navigations: [] };
75
+ observers.push(observer);
76
+ /*
77
+ * What the page itself did: console errors, uncaught throws, failed
78
+ * requests, responses at or above 400.
79
+ *
80
+ * Attached here rather than at the context, so it covers pages the test
81
+ * opens itself and so a test that opens none pays nothing — which is the
82
+ * whole reason this is a `context` fixture and not a `page` one.
83
+ */
84
+ observePageRuntime(page, startedAt);
85
+ page.on('framenavigated', (frame) => {
86
+ // Main frame only. An iframe navigating is not the user changing page,
87
+ // and treating it as one would file evidence against the wrong surface.
88
+ if (frame !== page.mainFrame())
89
+ return;
90
+ observer.navigations.push({
91
+ url: frame.url(),
92
+ atMs: Date.now() - startedAt,
93
+ kind: 'navigation',
94
+ });
95
+ });
96
+ };
97
+ for (const page of context.pages())
98
+ observe(page);
99
+ context.on('page', observe);
100
+ if (options.autoScan && !options.disabled) {
101
+ // Installed before the test body runs, so the very first navigation is
102
+ // already being watched. A scanner that starts listening after the page has
103
+ // loaded has already missed the thing it exists to see.
104
+ await installAutoScan(context, options, testInfo, (message) => warnings.push(message)).catch((error) => {
105
+ warnings.push(`Automatic scanning could not be installed: ${error instanceof Error ? error.message : String(error)}. Explicit setsquare.scan() calls are unaffected.`);
106
+ });
107
+ }
108
+ try {
109
+ await use(context);
110
+ }
111
+ finally {
112
+ // Teardown must never fail the test. The suite's verdict is the customer's,
113
+ // and our evidence-gathering has no standing to overturn it.
114
+ try {
115
+ // Before recording observations: whatever the test left on screen is the
116
+ // most reliable scan opportunity in the whole run, and for a fast test it
117
+ // is the only one.
118
+ if (options.autoScan && !options.disabled) {
119
+ await flushAutoScan(context, options, testInfo, (message) => warnings.push(message));
120
+ }
121
+ await attachRecords(observers, browserName, startedAt, testInfo);
122
+ for (const message of warnings) {
123
+ // Surfaced through Playwright's own annotations so a problem appears
124
+ // next to the test it happened in, not buried in a summary.
125
+ testInfo.annotations.push({ type: 'setsquare-warning', description: message });
126
+ }
127
+ }
128
+ catch {
129
+ // Swallowed on purpose. A capture problem is reported through the run's
130
+ // warnings, not by reddening someone's suite.
131
+ }
132
+ }
133
+ };
134
+ }
135
+ async function attachRecords(observers, browserName, startedAt, testInfo) {
136
+ for (const observer of observers) {
137
+ const observed = await observePage(observer, browserName, startedAt, testInfo.project.name);
138
+ if (!observed)
139
+ continue;
140
+ const record = {
141
+ recordVersion: SCAN_RECORD_VERSION,
142
+ kind: 'observation',
143
+ observed,
144
+ };
145
+ /*
146
+ * Surfaces this page reached and we could not scan.
147
+ *
148
+ * Rides the observation record because that is the one thing written per page
149
+ * at teardown, after every scan attempt has had its chance to fail. Absent
150
+ * when nothing was recorded — an empty array would be a claim that nothing
151
+ * went unscanned, which is a different statement.
152
+ */
153
+ const unscanned = unscannableRows(observer.page);
154
+ if (unscanned) {
155
+ record.unscanned = unscanned;
156
+ const omitted = unscannableOmitted(observer.page);
157
+ if (omitted > 0)
158
+ record.unscannedOmitted = omitted;
159
+ }
160
+ await testInfo.attach(SCAN_RECORD_ATTACHMENT, {
161
+ body: Buffer.from(JSON.stringify(record), 'utf8'),
162
+ contentType: 'application/json',
163
+ });
164
+ }
165
+ }
166
+ async function observePage(observer, browserName, startedAt, project) {
167
+ const { page } = observer;
168
+ const navigations = [...observer.navigations];
169
+ // A page closed by the test is not an error — plenty of specs close their own.
170
+ // Whatever was observed before it closed is still evidence.
171
+ if (!page.isClosed()) {
172
+ const routeChanges = await readRouteChanges(page, startedAt);
173
+ navigations.push(...routeChanges);
174
+ }
175
+ // `about:blank` is Playwright opening a context, not the suite visiting
176
+ // anything. Recording it would put a surface in the grid that no test asked for.
177
+ const meaningful = navigations.filter((nav) => nav.url && nav.url !== 'about:blank');
178
+ if (meaningful.length === 0)
179
+ return undefined;
180
+ meaningful.sort((a, b) => a.atMs - b.atMs);
181
+ const context = {
182
+ project,
183
+ engine: normaliseEngine(browserName),
184
+ navigations: meaningful,
185
+ };
186
+ if (!page.isClosed()) {
187
+ const viewport = page.viewportSize();
188
+ if (viewport)
189
+ context.viewport = viewport;
190
+ const theme = await readTheme(page);
191
+ if (theme)
192
+ context.observedTheme = theme;
193
+ }
194
+ return context;
195
+ }
196
+ async function readRouteChanges(page, startedAt) {
197
+ try {
198
+ const raw = await page.evaluate(() => window.__setsquareNavLog ?? []);
199
+ return raw
200
+ .filter((entry) => NAV_KINDS.has(entry.kind))
201
+ .map((entry) => ({
202
+ url: entry.url,
203
+ atMs: entry.at - startedAt,
204
+ kind: entry.kind,
205
+ }));
206
+ }
207
+ catch {
208
+ // Navigating away mid-read, a closed page, a CSP that blocked the init
209
+ // script. None of these is worth a warning on its own.
210
+ return [];
211
+ }
212
+ }
213
+ async function readTheme(page) {
214
+ try {
215
+ return await page.evaluate(() => {
216
+ const root = document.documentElement;
217
+ // Our own attribute first: a customer who has told us explicitly should not
218
+ // be second-guessed by a heuristic. `data-theme` is the widespread
219
+ // convention and is what the reference implementation uses.
220
+ return (root.getAttribute('data-setsquare-theme') ?? root.getAttribute('data-theme') ?? undefined);
221
+ });
222
+ }
223
+ catch {
224
+ return undefined;
225
+ }
226
+ }
227
+ /**
228
+ * Anything that is not one of Playwright's three engines is treated as chromium.
229
+ *
230
+ * Not a guess: `browserName` is a closed set in Playwright, and branded channels
231
+ * (`chrome`, `msedge`) are chromium. If a fourth engine ever appears this becomes
232
+ * wrong, which is what the compatibility matrix is for.
233
+ */
234
+ function normaliseEngine(browserName) {
235
+ return browserName === 'firefox' || browserName === 'webkit' ? browserName : 'chromium';
236
+ }
237
+ //# sourceMappingURL=fixture.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fixture.js","sourceRoot":"","sources":["../src/fixture.ts"],"names":[],"mappings":"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"}