@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,483 @@
1
+ import { axisVariance, documentConsistency, gridCoverage, sharedSelectorCandidates, } from '@setsquare/schema/conformance';
2
+ const plural = (count, one, many) => (count === 1 ? one : many);
3
+ /** Engine per test, so a count is never presented as spanning engines. */
4
+ function engines(envelope) {
5
+ return new Map(envelope.results.map((result) => [result.testId, result.engine]));
6
+ }
7
+ /**
8
+ * What the pages did while the tests were passing.
9
+ *
10
+ * The headline of the whole feature, and the reason it leads with the phrase it
11
+ * does: a 500 on a background request fails no assertion, appears in no
12
+ * Playwright artefact, and until now appeared nowhere at all.
13
+ *
14
+ * Counts are reported PER SURFACE and tagged with their engine, never summed into
15
+ * a run total. The cross-engine lane measured why: on one page Chromium and WebKit
16
+ * report 123 console errors and Firefox 121, because the first two also log a
17
+ * failed-resource error. A total spanning engines would be a number that describes
18
+ * no browser.
19
+ */
20
+ export function runtimeInsight(envelope) {
21
+ const engineOf = engines(envelope);
22
+ const surfaces = [];
23
+ let listened = 0;
24
+ for (const scan of envelope.scans) {
25
+ const runtime = scan.runtime;
26
+ if (!runtime)
27
+ continue;
28
+ listened += 1;
29
+ const counts = runtime.counts;
30
+ const consoleErrors = counts['console-error'] ?? 0;
31
+ const pageErrors = counts['page-error'] ?? 0;
32
+ const failedRequests = counts['request-failed'] ?? 0;
33
+ const serverErrors = counts['response-5xx'] ?? 0;
34
+ const clientErrors = counts['response-4xx'] ?? 0;
35
+ if (consoleErrors + pageErrors + failedRequests + serverErrors + clientErrors === 0) {
36
+ continue;
37
+ }
38
+ const engine = engineOf.get(scan.testId);
39
+ surfaces.push({
40
+ surface: scan.context.surface,
41
+ ...(engine ? { engine } : {}),
42
+ consoleErrors,
43
+ pageErrors,
44
+ failedRequests,
45
+ serverErrors,
46
+ clientErrors,
47
+ omitted: runtime.omitted,
48
+ // Severity-ordered, not first-seen. The accumulator keeps exemplars in
49
+ // arrival order, and on a chatty page that buries the 500 under three
50
+ // console warnings — burying the one thing this block exists to surface.
51
+ exemplars: [...runtime.exemplars]
52
+ .sort((a, b) => severity(b.kind) - severity(a.kind))
53
+ .slice(0, 3)
54
+ .map(describeEvent),
55
+ });
56
+ }
57
+ // Nothing was ever observed: the run predates the capability, or no page was
58
+ // scanned. Silence, not a clean bill of health.
59
+ if (listened === 0)
60
+ return undefined;
61
+ const notes = [];
62
+ if (surfaces.length === 0) {
63
+ /*
64
+ * The honest positive. Worth saying precisely because it is the one case where
65
+ * a zero MEANS something: `observed` was populated, so we listened.
66
+ */
67
+ return {
68
+ heading: 'Page runtime',
69
+ figures: [
70
+ {
71
+ value: 0,
72
+ label: `${plural(listened, 'surface', 'surfaces')} logged errors — ${listened} listened to`,
73
+ notes: [
74
+ 'No console errors, uncaught exceptions, failed requests or 4xx/5xx responses ' +
75
+ 'were seen on any scanned surface.',
76
+ ],
77
+ },
78
+ ],
79
+ notes: [],
80
+ };
81
+ }
82
+ surfaces.sort((a, b) => b.serverErrors - a.serverErrors ||
83
+ b.pageErrors - a.pageErrors ||
84
+ b.consoleErrors - a.consoleErrors ||
85
+ a.surface.localeCompare(b.surface));
86
+ const serverTotal = surfaces.reduce((total, row) => total + row.serverErrors, 0);
87
+ if (serverTotal > 0) {
88
+ notes.push(`Across this run, ${serverTotal} server ${plural(serverTotal, 'error', 'errors')} (5xx) ${plural(serverTotal, 'was', 'were')} returned to a page while the tests ` +
89
+ 'were passing. Nothing asserted on them, so they failed nothing.');
90
+ }
91
+ notes.push('Counts are per surface and per engine. Engines disagree about what reaches the ' +
92
+ 'console for the same page, so these are not comparable across engines and are ' +
93
+ 'deliberately not totalled.');
94
+ return {
95
+ heading: 'Page runtime',
96
+ figures: surfaces.map((row) => ({
97
+ value: row.consoleErrors +
98
+ row.pageErrors +
99
+ row.serverErrors +
100
+ row.clientErrors +
101
+ row.failedRequests,
102
+ label: `${row.surface}${row.engine ? ` · ${row.engine}` : ''}`,
103
+ notes: [describeCounts(row), ...row.exemplars].filter((note) => note.length > 0),
104
+ })),
105
+ notes,
106
+ };
107
+ }
108
+ /** How loudly an event should speak for its surface. */
109
+ const SEVERITY = {
110
+ 'response-5xx': 5,
111
+ 'page-error': 4,
112
+ 'response-4xx': 3,
113
+ 'request-failed': 2,
114
+ 'console-error': 1,
115
+ 'console-warning': 0,
116
+ };
117
+ const severity = (kind) => SEVERITY[kind] ?? 0;
118
+ function describeCounts(row) {
119
+ const parts = [];
120
+ if (row.serverErrors > 0)
121
+ parts.push(`${row.serverErrors} × 5xx`);
122
+ if (row.clientErrors > 0)
123
+ parts.push(`${row.clientErrors} × 4xx`);
124
+ if (row.pageErrors > 0) {
125
+ parts.push(`${row.pageErrors} uncaught ${plural(row.pageErrors, 'error', 'errors')}`);
126
+ }
127
+ if (row.consoleErrors > 0)
128
+ parts.push(`${row.consoleErrors} console errors`);
129
+ if (row.failedRequests > 0)
130
+ parts.push(`${row.failedRequests} failed requests`);
131
+ if (row.omitted > 0)
132
+ parts.push(`${row.omitted} beyond the cap, not counted`);
133
+ return parts.join(', ');
134
+ }
135
+ function describeEvent(event) {
136
+ if (event.status !== undefined && event.urlPattern !== undefined) {
137
+ return `${event.status} ${event.urlPattern}`;
138
+ }
139
+ if (event.message !== undefined)
140
+ return event.message;
141
+ return event.urlPattern ?? event.kind;
142
+ }
143
+ /* -------------------------------------------------------------------------- *
144
+ * The unscannable ledger
145
+ * -------------------------------------------------------------------------- */
146
+ const REASON_WORDS = {
147
+ 'settle-timed-out-empty': 'the test moved on before the page settled',
148
+ 'nav-aborted': 'the page navigated away before the scan ran',
149
+ 'scan-timed-out': 'the scan ran out of its time budget',
150
+ 'scan-failed': 'the scan could not run',
151
+ 'scan-cap-reached': 'the per-page scan ceiling was reached',
152
+ 'page-closed': 'the page was closed first',
153
+ 'autoscan-disabled': 'automatic scanning was off',
154
+ 'not-in-changed-set': 'changed-only mode skipped it',
155
+ unknown: 'the reason could not be determined',
156
+ };
157
+ /**
158
+ * Surfaces this run reached and could not check.
159
+ *
160
+ * §2 of the gap analysis asks for exactly this and says why: "3 surfaces could not
161
+ * be checked" deserves to be a figure in the gutter, not a sentence in a
162
+ * paragraph. Before the ledger it was prose, readable by a person paying
163
+ * attention and countable by nothing.
164
+ *
165
+ * The phrase is "could not be checked", never "failed" — nothing is known about
166
+ * those surfaces in either direction, which is the whole point.
167
+ */
168
+ export function unscannableInsight(envelope) {
169
+ const rows = envelope.unscanned;
170
+ // Absent means the envelope predates the ledger, NEVER that nothing was missed.
171
+ // Silence is the only honest rendering of "we do not know".
172
+ if (!rows || rows.length === 0)
173
+ return undefined;
174
+ const patterns = new Set(rows.map((row) => row.urlPattern));
175
+ const occurrences = rows.reduce((total, row) => total + row.count, 0);
176
+ const byReason = new Map();
177
+ for (const row of rows) {
178
+ byReason.set(row.reason, (byReason.get(row.reason) ?? 0) + row.count);
179
+ }
180
+ return {
181
+ heading: 'Could not be checked',
182
+ figures: [
183
+ {
184
+ value: patterns.size,
185
+ label: `${plural(patterns.size, 'surface', 'surfaces')} the run reached and could not scan`,
186
+ notes: [`${occurrences} ${plural(occurrences, 'occurrence', 'occurrences')} in total.`],
187
+ },
188
+ ...[...byReason.entries()]
189
+ .sort((a, b) => b[1] - a[1])
190
+ .map(([reason, count]) => ({
191
+ value: count,
192
+ label: REASON_WORDS[reason] ?? reason,
193
+ })),
194
+ ],
195
+ notes: [
196
+ 'These surfaces have no evidence in this run — which is different from being ' +
197
+ 'clean. Any coverage figure above is computed over what was scanned, so treat ' +
198
+ 'it as a floor.',
199
+ ],
200
+ };
201
+ }
202
+ /* -------------------------------------------------------------------------- *
203
+ * Coverage on the real grid
204
+ * -------------------------------------------------------------------------- */
205
+ const AXIS_WORDS = {
206
+ surface: 'surfaces',
207
+ theme: 'themes',
208
+ persona: 'personas',
209
+ state: 'states',
210
+ };
211
+ /**
212
+ * Which axes this run's evidence actually spans.
213
+ *
214
+ * The product's unit is surface × theme × persona × state, and coverage was only
215
+ * ever reported per route. Two rules hold this block honest:
216
+ *
217
+ * - **No percentage from `combinations`.** It is a combinatorial product over
218
+ * values that happened to appear and counts cells that cannot exist (an
219
+ * admin-only persona on a public page). The GAP is a real signal; the ratio
220
+ * is not a coverage figure.
221
+ * - **`declared` is the only real denominator**, and it comes from the
222
+ * customer's own config. When it is absent the line is suppressed entirely
223
+ * rather than falling back to "N of N", which would report perfect coverage
224
+ * of whatever happened to run.
225
+ */
226
+ export function coverageInsight(envelope, declared, unmeasuredAxes) {
227
+ if (envelope.scans.length === 0)
228
+ return undefined;
229
+ const grid = gridCoverage(envelope, declared);
230
+ const figures = [];
231
+ figures.push({
232
+ value: grid.cellsWithEvidence,
233
+ label: 'distinct surface × theme × persona × state cells have evidence',
234
+ notes: grid.combinations > grid.cellsWithEvidence
235
+ ? [
236
+ `The observed values combine ${grid.combinations} ways. That is not a target: ` +
237
+ 'many combinations cannot exist, so this is a shape, never a percentage.',
238
+ ]
239
+ : [],
240
+ });
241
+ for (const axis of grid.axes) {
242
+ if (axis.axis === 'surface')
243
+ continue;
244
+ // Only where the customer's config gave us a real denominator.
245
+ if (axis.declared === undefined || axis.declared.length === 0)
246
+ continue;
247
+ figures.push({
248
+ value: `${axis.observed.length} of ${axis.declared.length}`,
249
+ label: `${AXIS_WORDS[axis.axis]} configured have evidence`,
250
+ notes: axis.missing.length > 0 ? [`No evidence for: ${axis.missing.join(', ')}.`] : [],
251
+ });
252
+ }
253
+ const notes = [];
254
+ const undeclared = grid.axes.filter((axis) => axis.axis !== 'surface' && (axis.declared === undefined || axis.declared.length === 0));
255
+ if (undeclared.length > 0) {
256
+ notes.push(`Nothing in the config names the ${undeclared
257
+ .map((axis) => AXIS_WORDS[axis.axis])
258
+ .join(', ')} this suite has, so there is no denominator to compare against — ` +
259
+ 'only what was observed.');
260
+ }
261
+ if (unmeasuredAxes !== undefined && unmeasuredAxes.length > 0) {
262
+ notes.push(`No project sets ${unmeasuredAxes.join(', ')}, so ${plural(unmeasuredAxes.length, 'that axis is', 'those axes are')} unmeasured in this run.`);
263
+ }
264
+ return { heading: 'Coverage', figures, notes };
265
+ }
266
+ /* -------------------------------------------------------------------------- *
267
+ * Cross-surface reasoning
268
+ * -------------------------------------------------------------------------- */
269
+ /**
270
+ * Findings that exist only because one run holds many surfaces.
271
+ *
272
+ * No per-page tool can express any of these, which is the point. All three are
273
+ * carefully worded as evidence rather than conclusions:
274
+ *
275
+ * - variance is a ROOT-CAUSE POINTER ("fails in dark, passes in light" names a
276
+ * token, not a component)
277
+ * - a shared selector shape is a CANDIDATE, never "one fix" — that is a fix
278
+ * claim and sits on the paid side of the free-layer boundary (D10)
279
+ * - document consistency shows its `unknown` bucket, because a surface we never
280
+ * probed is not a surface missing a skip link
281
+ */
282
+ export function crossSurfaceInsight(envelope) {
283
+ const figures = [];
284
+ const notes = [];
285
+ for (const axis of ['theme', 'persona', 'state']) {
286
+ const rows = axisVariance(envelope, axis);
287
+ // Empty means either one value on that axis, or no disagreement. Both render
288
+ // as silence: "no theme-specific failures" is a claim a one-theme run cannot
289
+ // support.
290
+ for (const row of rows.slice(0, 3)) {
291
+ figures.push({
292
+ value: row.failingValues.length,
293
+ label: `${row.ruleId} fails on ${row.surface} in ${row.failingValues.join(', ')}`,
294
+ notes: [
295
+ `Passes there in ${row.passingValues.join(', ')} — so the difference is the ${axis}.`,
296
+ ],
297
+ });
298
+ }
299
+ }
300
+ const shared = sharedSelectorCandidates(envelope).filter((row) => row.surfaces.length >= 2);
301
+ for (const row of shared.slice(0, 3)) {
302
+ figures.push({
303
+ value: row.surfaces.length,
304
+ label: `surfaces share the selector shape ${row.shape} for ${row.ruleId}`,
305
+ });
306
+ }
307
+ if (shared.length > 0) {
308
+ // Said ONCE, as a block note. Repeating it under every row turned the caveat
309
+ // into wallpaper, which is how a caveat stops being read.
310
+ notes.push(`A shared selector shape is a candidate for one shared component, not a ` +
311
+ `conclusion — two unrelated components can carry the same shape` +
312
+ (shared.length > 3 ? `. ${shared.length - 3} more not shown` : '') +
313
+ '.');
314
+ }
315
+ const documents = documentConsistency(envelope);
316
+ if (documents.duplicateTitles.length > 0) {
317
+ // One row for all of them: three consecutive rows reading "2 surfaces share
318
+ // one page title" says the same sentence three times and buries which
319
+ // surfaces, which is the only part that helps.
320
+ figures.push({
321
+ value: documents.duplicateTitles.length,
322
+ label: `${plural(documents.duplicateTitles.length, 'group of surfaces shares', 'groups of surfaces share')} a page title`,
323
+ notes: [
324
+ ...documents.duplicateTitles.slice(0, 4).map((duplicate) => duplicate.surfaces.join(', ')),
325
+ 'Each title is fine on its own page; repeated across surfaces it is 2.4.2.',
326
+ ],
327
+ });
328
+ }
329
+ if (documents.langValues.length > 1) {
330
+ figures.push({
331
+ value: documents.langValues.length,
332
+ label: 'different lang values across surfaces',
333
+ notes: documents.langValues.map((row) => `${row.value}: ${row.surfaces.slice(0, 6).join(', ')}`),
334
+ });
335
+ }
336
+ if (documents.skipLinkAbsent.length > 0 && documents.skipLinkPresent.length > 0) {
337
+ /*
338
+ * Both counts, plainly. An earlier wording said "N surfaces have no skip link
339
+ * where others do", which reads as an anomaly — and on the fixture suite the
340
+ * "anomaly" was 14 of 16 surfaces. Inconsistency is the finding here; which
341
+ * side is the majority is the reader's to judge.
342
+ */
343
+ const present = documents.skipLinkPresent.length;
344
+ const absent = documents.skipLinkAbsent.length;
345
+ figures.push({
346
+ value: `${present}/${present + absent}`,
347
+ label: 'surfaces have a skip link — the rest do not',
348
+ notes: [
349
+ `Present on ${documents.skipLinkPresent.slice(0, 6).join(', ')}. ` +
350
+ `Absent on ${documents.skipLinkAbsent.slice(0, 6).join(', ')}` +
351
+ (absent > 6 ? ` and ${absent - 6} more` : '') +
352
+ '.',
353
+ ],
354
+ });
355
+ }
356
+ /*
357
+ * The unknown bucket qualifies a COMPARISON, so it is only said when there is a
358
+ * comparison to qualify.
359
+ *
360
+ * Emitted unconditionally, it produced a section on an ordinary run whose entire
361
+ * content was "1 surface was not probed for these" — announcing our own gap to a
362
+ * reader who had been shown no cross-surface finding at all. A caveat with
363
+ * nothing to caveat is noise, and noise is how the real caveats stop being read.
364
+ */
365
+ if (documents.unknown.length > 0 && figures.length > 0) {
366
+ notes.push(`${documents.unknown.length} ${plural(documents.unknown.length, 'surface was', 'surfaces were')} not probed for these, so ${plural(documents.unknown.length, 'it is', 'they are')} absent from the comparison rather than counted against it.`);
367
+ }
368
+ // No figures means no cross-surface finding, and this section exists for nothing
369
+ // else. Silence rather than a heading over an empty list.
370
+ if (figures.length === 0)
371
+ return undefined;
372
+ return { heading: 'Across surfaces', figures, notes };
373
+ }
374
+ /* -------------------------------------------------------------------------- *
375
+ * Where the suite's time went, and what we cost
376
+ * -------------------------------------------------------------------------- */
377
+ /**
378
+ * Suite mechanics: the tail, the steps, the workers, and our own overhead.
379
+ *
380
+ * **The overhead wording is the sharpest hazard in this file.** These are summed
381
+ * per-scan durations across workers, and scanning runs concurrently with the
382
+ * customer's own tests — so on the SDK's own grouped fixture run the total spent
383
+ * (28.7s) EXCEEDS the wall clock (27.9s). Rendered as a percentage that is
384
+ * gibberish; rendered as a delta it is a lie. The gap analysis's own example
385
+ * phrasing, "scan cost: 1.8s of 8.5s", implies exactly the subtraction that does
386
+ * not hold.
387
+ *
388
+ * So: time SPENT, never time ADDED, no percentage, no delta, and a pointer at
389
+ * `SETSQUARE_DISABLED=1`, which is the only way to actually measure the
390
+ * difference.
391
+ */
392
+ export function suiteTimingInsight(suite, overhead) {
393
+ if (!suite && !overhead)
394
+ return undefined;
395
+ const figures = [];
396
+ const notes = [];
397
+ const slowest = suite?.slowestTests?.[0];
398
+ if (slowest) {
399
+ figures.push({
400
+ value: `${(slowest.durationMs / 1000).toFixed(1)}s`,
401
+ label: 'slowest test',
402
+ // In the notes rather than the label: a figure LABEL sits on one line beside
403
+ // the gutter and does not wrap, and a full titlePath routinely runs past any
404
+ // terminal. Notes wrap, so the whole id survives.
405
+ notes: [slowest.testId],
406
+ });
407
+ }
408
+ const step = suite?.slowestSteps?.[0];
409
+ if (step) {
410
+ figures.push({
411
+ value: `${(step.durationMs / 1000).toFixed(1)}s`,
412
+ label: `slowest step${step.category ? ` (${step.category})` : ''}`,
413
+ notes: [step.title],
414
+ });
415
+ }
416
+ const workers = suite?.workers;
417
+ /*
418
+ * Imbalance only when the run could actually be imbalanced.
419
+ *
420
+ * Playwright recycles worker PROCESSES — one per project, another after a
421
+ * failure — so a strictly serial suite still reports several worker indices.
422
+ * The first version of this line reported a six-second "imbalance" on a
423
+ * `workers: 1` run, which is the shape of the projects and not a finding.
424
+ */
425
+ if (workers && workers.configured !== undefined && workers.configured > 1) {
426
+ figures.push({
427
+ value: `${(workers.tailMs / 1000).toFixed(1)}s`,
428
+ label: 'busiest worker above the median',
429
+ notes: [
430
+ `${workers.configured} workers configured, ${workers.workers} processes used. ` +
431
+ `Busiest ${(workers.busiestMs / 1000).toFixed(1)}s, quietest ` +
432
+ `${(workers.idlestMs / 1000).toFixed(1)}s. A long tail means one worker holds ` +
433
+ 'the run open while the others are done.',
434
+ ],
435
+ });
436
+ }
437
+ if (overhead) {
438
+ figures.push({
439
+ value: `${(overhead.scanMs / 1000).toFixed(1)}s`,
440
+ label: `spent inside Setsquare across ${overhead.scans} ${plural(overhead.scans, 'scan', 'scans')}`,
441
+ notes: [`${(overhead.axeMs / 1000).toFixed(1)}s of that was axe itself.`],
442
+ });
443
+ notes.push(`Time spent, not time added: scans run alongside your tests and these are summed ` +
444
+ `across workers, so this is not ${(overhead.runDurationMs / 1000).toFixed(1)}s of ` +
445
+ 'wall clock minus anything. To measure what we actually cost, run once with ' +
446
+ 'SETSQUARE_DISABLED=1 and compare.');
447
+ }
448
+ if (figures.length === 0)
449
+ return undefined;
450
+ return { heading: 'Suite timing', figures, notes };
451
+ }
452
+ /**
453
+ * Which tests were flaky, on which attempt, and how they failed.
454
+ *
455
+ * The summary already said "2 flaky". It never said which, and all of it was in
456
+ * the data already — that is the whole complaint in §2. Returned as notes for the
457
+ * Playwright block rather than a section of its own, because it belongs beside the
458
+ * count it explains.
459
+ */
460
+ export function flakeNotes(suite) {
461
+ const flaky = suite?.flaky ?? [];
462
+ if (flaky.length === 0)
463
+ return [];
464
+ return flaky.slice(0, 5).map((test) => {
465
+ const how = test.failure?.step
466
+ ? ` — first failed at ${test.failure.step}`
467
+ : test.failure?.errorClass
468
+ ? ` — first failed with ${test.failure.errorClass}`
469
+ : '';
470
+ return `${test.testId}: passed on attempt ${test.passedOnRetry + 1}${how}.`;
471
+ });
472
+ }
473
+ /** Every block, in the order the renderers present them. */
474
+ export function insightBlocks(envelope, options = {}) {
475
+ return [
476
+ runtimeInsight(envelope),
477
+ unscannableInsight(envelope),
478
+ crossSurfaceInsight(envelope),
479
+ coverageInsight(envelope, options.declaredAxes, options.unmeasuredAxes),
480
+ suiteTimingInsight(options.suite, options.overhead),
481
+ ].filter((block) => block !== undefined);
482
+ }
483
+ //# sourceMappingURL=insights.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"insights.js","sourceRoot":"","sources":["../src/insights.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import type { ResolvedSetsquareOptions } from './options.js';
2
+ export interface InstallResult {
3
+ installed: boolean;
4
+ /** Present when installation was skipped. Written into the run's warnings. */
5
+ reason?: string;
6
+ }
7
+ export declare function installFixture(options: ResolvedSetsquareOptions): InstallResult;
8
+ //# sourceMappingURL=install.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../src/install.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,cAAc,CAAC;AA2D7D,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,OAAO,CAAC;IACnB,8EAA8E;IAC9E,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AA6BD,wBAAgB,cAAc,CAAC,OAAO,EAAE,wBAAwB,GAAG,aAAa,CAwC/E"}
@@ -0,0 +1,114 @@
1
+ import { test as playwrightTest } from '@playwright/test';
2
+ import { createContextFixture } from './fixture.js';
3
+ /**
4
+ * Install the capture fixture into the customer's suite without touching a
5
+ * single test file.
6
+ *
7
+ * ---------------------------------------------------------------------------
8
+ * READ THIS BEFORE CHANGING ANYTHING HERE. It is the one place in the SDK that
9
+ * depends on Playwright internals, and it is a deliberate, contained trade.
10
+ * ---------------------------------------------------------------------------
11
+ *
12
+ * The problem. Fixtures in Playwright come from the `test` object a spec
13
+ * imports. Customers import `test` from `@playwright/test`, so the only
14
+ * supported ways to add a fixture are to make them change every spec's import,
15
+ * or to hand them a `mergeTests()` call — both of which are test edits, and
16
+ * "zero test rewrites" is the product's headline claim, not a nicety.
17
+ *
18
+ * The mechanism. `playwright/test` exports the `test` object itself, a
19
+ * per-process singleton whose implementation hangs off an own symbol and holds a
20
+ * plain `fixtures` array. Playwright loads the config file in EVERY worker
21
+ * process, so `withSetsquare()` — which runs during config load — executes in each
22
+ * worker before any spec is imported. Appending to that array at that moment
23
+ * adds our fixture to the base `test` and to everything later derived from it.
24
+ *
25
+ * Why an OVERRIDE of `context` and not an `auto` fixture. This matters more than
26
+ * it looks. An auto fixture that depends on `page` or `context` forces a browser
27
+ * launch for every test, including ones that never wanted a browser: measured on
28
+ * the fixture suite, a pure-arithmetic test went from 2ms to 185ms. On a real
29
+ * suite with hundreds of non-browser tests that is minutes per run, added
30
+ * silently, and blamed on the customer's own suite. An override instantiates
31
+ * only when something actually asks for a context, so a test that wants no
32
+ * browser still gets none. `fixtures/specs/nopage/pure.spec.ts` is the permanent
33
+ * regression test for it.
34
+ *
35
+ * The risk, stated plainly. This is not public API and a Playwright release can
36
+ * change it. Three things contain that:
37
+ * 1. Everything below is defensive. A shape we do not recognise means we
38
+ * install nothing and say so — never a thrown error, because the SDK must
39
+ * never be the reason a customer's build fails.
40
+ * 2. Degrading is visible, not silent. "No evidence" and "no problems" must
41
+ * never look the same.
42
+ * 3. The compatibility matrix (P1-6.3) runs the suite across the supported
43
+ * Playwright range, so a break surfaces in OUR CI rather than in theirs.
44
+ */
45
+ /**
46
+ * Process-wide guard, keyed on a registered symbol.
47
+ *
48
+ * `Symbol.for` rather than a module-level boolean because a package can be
49
+ * loaded more than once in one process — ESM and CJS resolutions, or two
50
+ * versions in a monorepo — and each copy would carry its own module state.
51
+ * Installing twice would attach two fixtures and duplicate every record, which
52
+ * presents as inflated counts rather than as an error.
53
+ */
54
+ const INSTALLED = Symbol.for('setsquare.playwright.installed');
55
+ /**
56
+ * Find Playwright's internal test-type implementation by shape rather than by
57
+ * name.
58
+ *
59
+ * The symbol is module-local to Playwright and has no stable description, so it
60
+ * cannot be looked up directly. Scanning own symbols for the object carrying a
61
+ * `fixtures` array is both what is actually available and, usefully, a check on
62
+ * the assumption: if the shape ever changes, this returns undefined and we
63
+ * degrade, rather than mutating something that merely happens to be there.
64
+ */
65
+ function findTestTypeImpl(test) {
66
+ if (typeof test !== 'function' && typeof test !== 'object')
67
+ return undefined;
68
+ for (const symbol of Object.getOwnPropertySymbols(test)) {
69
+ const candidate = test[symbol];
70
+ if (candidate &&
71
+ typeof candidate === 'object' &&
72
+ Array.isArray(candidate.fixtures)) {
73
+ return candidate;
74
+ }
75
+ }
76
+ return undefined;
77
+ }
78
+ export function installFixture(options) {
79
+ const globals = globalThis;
80
+ if (globals[INSTALLED])
81
+ return { installed: true };
82
+ try {
83
+ const impl = findTestTypeImpl(playwrightTest);
84
+ if (!impl) {
85
+ return {
86
+ installed: false,
87
+ reason: 'Setsquare could not attach to @playwright/test: its internal fixture ' +
88
+ 'registry was not in the expected shape. Scans will not run. This is ' +
89
+ 'usually a Playwright version outside the supported range.',
90
+ };
91
+ }
92
+ impl.fixtures.push({
93
+ // An OVERRIDE of the built-in `context`, not an auto fixture. See above —
94
+ // the difference is a browser launch per test that does not want one.
95
+ fixtures: { context: createContextFixture(options) },
96
+ // Playwright records where a fixture was declared and shows it in error
97
+ // messages. Naming ourselves honestly here means a fixture problem points
98
+ // at us instead of at a confusing internal frame.
99
+ location: { file: 'node_modules/@setsquare/review-sdk', line: 1, column: 1 },
100
+ });
101
+ globals[INSTALLED] = true;
102
+ return { installed: true };
103
+ }
104
+ catch (error) {
105
+ // Deliberately swallowed. An SDK that throws during config load takes the
106
+ // whole suite with it, which is the one outcome we have promised can never
107
+ // happen.
108
+ return {
109
+ installed: false,
110
+ reason: `Setsquare could not install its capture fixture: ${error instanceof Error ? error.message : String(error)}. Scans will not run; your tests are unaffected.`,
111
+ };
112
+ }
113
+ }
114
+ //# sourceMappingURL=install.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"install.js","sourceRoot":"","sources":["../src/install.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,IAAI,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAE1D,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAGpD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AAEH;;;;;;;;GAQG;AACH,MAAM,SAAS,GAAG,MAAM,CAAC,GAAG,CAAC,gCAAgC,CAAC,CAAC;AAW/D;;;;;;;;;GASG;AACH,SAAS,gBAAgB,CAAC,IAAa;IACrC,IAAI,OAAO,IAAI,KAAK,UAAU,IAAI,OAAO,IAAI,KAAK,QAAQ;QAAE,OAAO,SAAS,CAAC;IAE7E,KAAK,MAAM,MAAM,IAAI,MAAM,CAAC,qBAAqB,CAAC,IAAc,CAAC,EAAE,CAAC;QAClE,MAAM,SAAS,GAAI,IAAgC,CAAC,MAAM,CAAC,CAAC;QAC5D,IACE,SAAS;YACT,OAAO,SAAS,KAAK,QAAQ;YAC7B,KAAK,CAAC,OAAO,CAAE,SAA0B,CAAC,QAAQ,CAAC,EACnD,CAAC;YACD,OAAO,SAAyB,CAAC;QACnC,CAAC;IACH,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,OAAiC;IAC9D,MAAM,OAAO,GAAG,UAAqC,CAAC;IACtD,IAAI,OAAO,CAAC,SAAS,CAAC;QAAE,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;IAEnD,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,gBAAgB,CAAC,cAAc,CAAC,CAAC;QAE9C,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,OAAO;gBACL,SAAS,EAAE,KAAK;gBAChB,MAAM,EACJ,uEAAuE;oBACvE,sEAAsE;oBACtE,2DAA2D;aAC9D,CAAC;QACJ,CAAC;QAED,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC;YACjB,0EAA0E;YAC1E,sEAAsE;YACtE,QAAQ,EAAE,EAAE,OAAO,EAAE,oBAAoB,CAAC,OAAO,CAAC,EAAE;YACpD,wEAAwE;YACxE,0EAA0E;YAC1E,kDAAkD;YAClD,QAAQ,EAAE,EAAE,IAAI,EAAE,oCAAoC,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE;SAC7E,CAAC,CAAC;QAEH,OAAO,CAAC,SAAS,CAAC,GAAG,IAAI,CAAC;QAC1B,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;IAC7B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,0EAA0E;QAC1E,2EAA2E;QAC3E,UAAU;QACV,OAAO;YACL,SAAS,EAAE,KAAK;YAChB,MAAM,EAAE,oDACN,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CACvD,kDAAkD;SACnD,CAAC;IACJ,CAAC;AACH,CAAC"}