@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,25 @@
1
+ import type { BrowserContext, TestInfo } from '@playwright/test';
2
+ import { type ResolvedSetsquareOptions } from './options.js';
3
+ export declare function installAutoScan(context: BrowserContext, options: ResolvedSetsquareOptions, testInfo: TestInfo, warn: (message: string) => void): Promise<void>;
4
+ /**
5
+ * Scan whatever the test left on screen, at teardown.
6
+ *
7
+ * This exists because of a measurement, and it is the single most important
8
+ * thing Stage 5 learned. Auto-scan fires 500ms after a page goes quiet — but a
9
+ * great many real tests finish in less than that. On the fixture suite, only two
10
+ * of ten surfaces were ever scanned: every fast test tore its context down before
11
+ * the settle callback could run, and the evidence for those surfaces simply never
12
+ * existed. A customer would have seen a grid with most of their application
13
+ * missing and no indication why.
14
+ *
15
+ * So teardown sweeps: for every page still open, if its current URL has not been
16
+ * scanned during this test, scan it now. The page has by definition finished
17
+ * settling — the test is over — which makes this the most reliable moment
18
+ * available, not a fallback.
19
+ *
20
+ * The settle heuristic still earns its place: it is what catches the surfaces a
21
+ * test passes THROUGH on its way somewhere else, which teardown can never see
22
+ * because they are no longer on screen.
23
+ */
24
+ export declare function flushAutoScan(context: BrowserContext, options: ResolvedSetsquareOptions, testInfo: TestInfo, warn: (message: string) => void): Promise<void>;
25
+ //# sourceMappingURL=autoscan.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"autoscan.d.ts","sourceRoot":"","sources":["../src/autoscan.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAQ,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAavE,OAAO,EAAqB,KAAK,wBAAwB,EAAE,MAAM,cAAc,CAAC;AA+DhF,wBAAgB,eAAe,CAC7B,OAAO,EAAE,cAAc,EACvB,OAAO,EAAE,wBAAwB,EACjC,QAAQ,EAAE,QAAQ,EAClB,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,GAC9B,OAAO,CAAC,IAAI,CAAC,CAqEf;AA6LD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,aAAa,CACjC,OAAO,EAAE,cAAc,EACvB,OAAO,EAAE,wBAAwB,EACjC,QAAQ,EAAE,QAAQ,EAClB,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,GAC9B,OAAO,CAAC,IAAI,CAAC,CA0Df"}
@@ -0,0 +1,332 @@
1
+ import { engineVersion, rulesetDigest, runAxe } from './axe.js';
2
+ import { SCAN_RECORD_ATTACHMENT, SCAN_RECORD_VERSION } from './contract.js';
3
+ import { toFindings, toInapplicable, toPasses } from './findings.js';
4
+ import { runStatusMessages, runTextSpacing } from './checks/dynamic.js';
5
+ import { setsquareCheckVersions } from './checks/versions.js';
6
+ import { runReadingLevel } from './reading-level.js';
7
+ import { readingLevelEnabled } from './runtime.js';
8
+ import { capturePerf } from './perf.js';
9
+ import { readDocumentFacts } from './document.js';
10
+ import { pageRuntime } from './observe.js';
11
+ import { recordUnscannable } from './unscannable.js';
12
+ import { changedOnlyRoutes } from './options.js';
13
+ import { tagsFor } from './rules.js';
14
+ import { toUrlPattern } from './sanitise.js';
15
+ import { MUTATION_TOTAL, SETTLE_BINDING, settleScript } from './settle.js';
16
+ const state = new WeakMap();
17
+ let visitCounter = 0;
18
+ function stateFor(page, url) {
19
+ const existing = state.get(page);
20
+ if (existing && existing.visitUrl === url)
21
+ return existing;
22
+ const next = {
23
+ queue: existing?.queue ?? Promise.resolve(),
24
+ visitRef: `v${(visitCounter += 1)}`,
25
+ visitUrl: url,
26
+ perfCaptured: false,
27
+ scans: existing?.scans ?? 0,
28
+ scanned: existing?.scanned ?? new Map(),
29
+ };
30
+ state.set(page, next);
31
+ return next;
32
+ }
33
+ /**
34
+ * A per-page ceiling on automatic scans.
35
+ *
36
+ * Without one, a page that mutates in bursts — a poller, a chat widget, an
37
+ * animation that pauses — produces a settle signal per burst and we scan it
38
+ * repeatedly for the whole test. The dedupe in the reporter would collapse the
39
+ * results, but the RUN TIME would already have been spent, and run time is the
40
+ * canary this milestone exists to listen to.
41
+ *
42
+ * Deliberately generous: a genuinely multi-route test should not hit it. When it
43
+ * does, the run says so rather than silently capping.
44
+ */
45
+ const MAX_AUTO_SCANS_PER_PAGE = 25;
46
+ export function installAutoScan(context, options, testInfo, warn) {
47
+ const settle = options.settle;
48
+ const onSettled = async (page, signal) => {
49
+ // Nothing to record: a closed page cannot hold a ledger, and the page's own
50
+ // observation record has already been written or never will be.
51
+ if (page.isClosed())
52
+ return;
53
+ const pageState = stateFor(page, signal.url);
54
+ if (pageState.scans >= MAX_AUTO_SCANS_PER_PAGE) {
55
+ recordUnscannable(page, toUrlPattern(signal.url).pattern, 'scan-cap-reached');
56
+ if (pageState.scans === MAX_AUTO_SCANS_PER_PAGE) {
57
+ pageState.scans += 1;
58
+ warn(`A page settled more than ${MAX_AUTO_SCANS_PER_PAGE} times in one test ` +
59
+ `(${signal.url}); further automatic scans were skipped. This usually means ` +
60
+ `something on the page never stops changing.`);
61
+ }
62
+ return;
63
+ }
64
+ pageState.scans += 1;
65
+ /*
66
+ * Queued, never concurrent.
67
+ *
68
+ * Playwright serialises operations on a page anyway, so a scan running while
69
+ * the customer's test is mid-click does not corrupt anything — but it does
70
+ * make their action WAIT for our axe run. Chaining scans keeps that cost
71
+ * bounded and predictable instead of multiplying it.
72
+ */
73
+ pageState.queue = pageState.queue.then(async () => {
74
+ if (page.isClosed())
75
+ return;
76
+ try {
77
+ await scanSettledPage(page, signal, options, testInfo, warn);
78
+ }
79
+ catch (error) {
80
+ // Never the customer's problem. A failed automatic scan is a warning in
81
+ // our own output and nothing else — plus a counted ledger row, so the
82
+ // coverage figures cannot silently absorb it.
83
+ recordUnscannable(page, toUrlPattern(signal.url).pattern, 'scan-failed');
84
+ warn(`Automatic scan of ${signal.url} failed: ${error instanceof Error ? error.message : String(error)}`);
85
+ }
86
+ });
87
+ await pageState.queue;
88
+ };
89
+ return Promise.all([
90
+ /*
91
+ * A binding rather than polling.
92
+ *
93
+ * The alternative — asking the page every N milliseconds whether it has gone
94
+ * quiet — costs a round-trip per poll per page for the whole run, and still
95
+ * learns about the settle late. The binding costs one function on `window`,
96
+ * which is real pollution of a customer's page and is why it is namespaced
97
+ * and non-enumerable.
98
+ */
99
+ context.exposeBinding(SETTLE_BINDING, ({ page }, signal) => onSettled(page, signal)),
100
+ context.addInitScript(settleScript(settle)),
101
+ ]).then(() => undefined);
102
+ }
103
+ async function scanSettledPage(page, signal, options, testInfo, warn) {
104
+ const url = page.url();
105
+ /*
106
+ * The page moved on between settling and being scanned.
107
+ *
108
+ * Ordinary in a fast suite: a test clicks through two routes quicker than a
109
+ * scan takes. Attributing findings to the URL that settled would file them
110
+ * against a surface they did not come from, so the scan is abandoned instead.
111
+ * The new URL will settle and be scanned on its own.
112
+ */
113
+ if (url !== signal.url) {
114
+ // Counted, not merely skipped. A fast suite that outruns its own scans looks
115
+ // identical to a smaller application unless the misses are on the record.
116
+ recordUnscannable(page, toUrlPattern(signal.url).pattern, 'nav-aborted');
117
+ return;
118
+ }
119
+ const pageState = stateFor(page, url);
120
+ /*
121
+ * Changed-only mode: skip a surface the change cannot reach.
122
+ *
123
+ * The SDK does no route reasoning — it compares a pattern it already computed
124
+ * against a list the CLI wrote. A miss is RECORDED in the ledger rather than
125
+ * silently dropped, because a run that scanned a third of the app must not read
126
+ * like a run of a smaller app.
127
+ *
128
+ * Only a real array licenses skipping: `changedOnlyRoutes` returns undefined for
129
+ * a missing, null or malformed set, and undefined means scan everything.
130
+ */
131
+ const changedOnly = changedOnlyRoutes();
132
+ if (changedOnly !== undefined) {
133
+ const pattern = toUrlPattern(url).pattern;
134
+ if (!changedOnly.has(pattern)) {
135
+ recordUnscannable(page, pattern, 'not-in-changed-set');
136
+ return;
137
+ }
138
+ }
139
+ const scanStartedAt = Date.now();
140
+ const results = await runAxe(page, options.rules, options.scanTimeoutMs);
141
+ const axeMs = Date.now() - scanStartedAt;
142
+ /*
143
+ * The scan ran out of budget, so there is no scan.
144
+ *
145
+ * Recorded as a warning and NOT as an empty result: "we looked and found
146
+ * nothing" and "we ran out of time" are the two facts this product exists to
147
+ * keep apart, and conflating them here would be the confident false pass in its
148
+ * purest form.
149
+ */
150
+ if (!results) {
151
+ recordUnscannable(page, toUrlPattern(url).pattern, 'scan-timed-out');
152
+ warn(`A scan of ${url} was abandoned after ${options.scanTimeoutMs}ms so it could not ` +
153
+ 'hold up the test. That surface has no evidence in this run rather than a clean result.');
154
+ return;
155
+ }
156
+ const axeVersion = engineVersion(results) ?? 'unknown';
157
+ const findings = toFindings(results);
158
+ // Setsquare's own checks, same channel and same never-fail discipline as
159
+ // the explicit-scan path. The digest names the run's configured check set,
160
+ // never which scans happened to execute one — see setsquareCheckVersions for why.
161
+ const setsquareChecks = setsquareCheckVersions(options.rules);
162
+ if (readingLevelEnabled()) {
163
+ const readingLevel = await runReadingLevel(page);
164
+ if (readingLevel)
165
+ findings.push(readingLevel);
166
+ }
167
+ const passes = toPasses(results);
168
+ const inapplicable = toInapplicable(results);
169
+ /*
170
+ * Dynamic checks only at TEARDOWN, never mid-test: the text-spacing probe
171
+ * mutates the live page, and an auto-scan firing between a customer's
172
+ * actions would make us the flake in their screenshot assertion. At
173
+ * teardown the test is over and the page is about to be discarded.
174
+ */
175
+ if (signal.reason === 'teardown') {
176
+ for (const check of [await runTextSpacing(page), await runStatusMessages(page)]) {
177
+ if (check.finding)
178
+ findings.push(check.finding);
179
+ if (check.pass)
180
+ passes.push(check.pass);
181
+ if (check.inapplicable)
182
+ inapplicable.push(check.inapplicable);
183
+ }
184
+ }
185
+ const record = {
186
+ recordVersion: SCAN_RECORD_VERSION,
187
+ kind: 'scan',
188
+ page: { project: testInfo.project.name, engine: engineOf(page), url },
189
+ // Auto-scan states nothing. The surface is inferred by the reporter from the
190
+ // route, and any persona or theme comes from configuration or the live DOM —
191
+ // never from this file guessing.
192
+ declared: { surface: '' },
193
+ visitRef: pageState.visitRef,
194
+ axeVersion,
195
+ rulesetDigest: rulesetDigest(axeVersion, results, tagsFor(options.rules), setsquareChecks),
196
+ wcagTarget: options.rules.target,
197
+ findings,
198
+ passes,
199
+ inapplicable,
200
+ settle: {
201
+ reason: signal.reason,
202
+ ...(signal.via ? { via: signal.via } : {}),
203
+ sinceNavMs: Math.round(signal.sinceNavMs),
204
+ mutations: signal.mutations,
205
+ settleTimedOut: signal.settleTimedOut,
206
+ },
207
+ };
208
+ const viewport = page.viewportSize();
209
+ if (viewport)
210
+ record.page.viewport = viewport;
211
+ const probesStartedAt = Date.now();
212
+ // Theme and document facts in one round-trip. Two `page.evaluate` calls on a
213
+ // path that runs for every scan in the suite is a cost with nothing to show for it.
214
+ const facts = await readDocumentFacts(page);
215
+ if (facts.theme)
216
+ record.page.observedTheme = facts.theme;
217
+ if (facts.document)
218
+ record.document = facts.document;
219
+ /*
220
+ * Perf and page-runtime are both properties of the PAGE LOAD, not of the scan,
221
+ * so both are emitted once per visit and gated together.
222
+ *
223
+ * Scanning one page as its state changes — modal closed, then modal open —
224
+ * produces several scans from a single navigation. Repeating either block would
225
+ * weight pillar 3's averages by how many states a surface happens to have and
226
+ * would report the same 500 four times.
227
+ */
228
+ if (!pageState.perfCaptured) {
229
+ const perf = await capturePerf(page);
230
+ if (perf)
231
+ record.perf = perf;
232
+ const runtime = pageRuntime(page);
233
+ if (runtime)
234
+ record.runtime = runtime;
235
+ // Set regardless of whether either block materialised: the gate means "this
236
+ // visit has had its page-load facts taken", and an engine that can measure
237
+ // neither must not be asked again on every subsequent state.
238
+ pageState.perfCaptured = true;
239
+ }
240
+ const probesMs = Date.now() - probesStartedAt;
241
+ record.cost = {
242
+ totalMs: Date.now() - scanStartedAt,
243
+ axeMs,
244
+ ...(probesMs > 0 ? { probesMs } : {}),
245
+ };
246
+ pageState.scanned.set(url, await mutationTotal(page));
247
+ await testInfo.attach(SCAN_RECORD_ATTACHMENT, {
248
+ body: Buffer.from(JSON.stringify(record), 'utf8'),
249
+ contentType: 'application/json',
250
+ });
251
+ }
252
+ /** How much the page has changed in total. Zero if the counter is unavailable. */
253
+ async function mutationTotal(page) {
254
+ try {
255
+ return await page.evaluate((key) => window[key] ?? 0, MUTATION_TOTAL);
256
+ }
257
+ catch {
258
+ return 0;
259
+ }
260
+ }
261
+ function engineOf(page) {
262
+ const name = page.context().browser()?.browserType().name();
263
+ return name === 'firefox' || name === 'webkit' ? name : 'chromium';
264
+ }
265
+ /**
266
+ * Scan whatever the test left on screen, at teardown.
267
+ *
268
+ * This exists because of a measurement, and it is the single most important
269
+ * thing Stage 5 learned. Auto-scan fires 500ms after a page goes quiet — but a
270
+ * great many real tests finish in less than that. On the fixture suite, only two
271
+ * of ten surfaces were ever scanned: every fast test tore its context down before
272
+ * the settle callback could run, and the evidence for those surfaces simply never
273
+ * existed. A customer would have seen a grid with most of their application
274
+ * missing and no indication why.
275
+ *
276
+ * So teardown sweeps: for every page still open, if its current URL has not been
277
+ * scanned during this test, scan it now. The page has by definition finished
278
+ * settling — the test is over — which makes this the most reliable moment
279
+ * available, not a fallback.
280
+ *
281
+ * The settle heuristic still earns its place: it is what catches the surfaces a
282
+ * test passes THROUGH on its way somewhere else, which teardown can never see
283
+ * because they are no longer on screen.
284
+ */
285
+ export async function flushAutoScan(context, options, testInfo, warn) {
286
+ for (const page of context.pages()) {
287
+ if (page.isClosed())
288
+ continue;
289
+ const url = page.url();
290
+ // `about:blank` is Playwright opening a context, not the suite visiting
291
+ // anything. Scanning it would put a surface in the grid nobody asked for.
292
+ if (!url || url === 'about:blank')
293
+ continue;
294
+ const pageState = stateFor(page, url);
295
+ /*
296
+ * Re-scan a URL already scanned during this test IF the page has changed
297
+ * since — which is the whole reason this tracks mutation counts rather than
298
+ * just which URLs were visited.
299
+ *
300
+ * Measured on the fixture site: `/slow` renders a conformant shell, goes
301
+ * quiet, is scanned at 500ms with zero violations, and only THEN injects its
302
+ * two real violations at 1000ms. Skipping the second scan because the URL was
303
+ * "already done" enshrined the shell and reported a clean page — the exact
304
+ * confident false pass this whole heuristic exists to prevent. The mutation
305
+ * counter distinguishes "already scanned" from "already scanned, and then
306
+ * everything changed".
307
+ */
308
+ const lastScannedAt = pageState.scanned.get(url);
309
+ if (lastScannedAt !== undefined && (await mutationTotal(page)) === lastScannedAt)
310
+ continue;
311
+ try {
312
+ // Chained onto the same queue as settle-triggered scans, so a scan that was
313
+ // already in flight when the test ended is not raced.
314
+ const mutations = await mutationTotal(page);
315
+ pageState.queue = pageState.queue.then(() => scanSettledPage(page, {
316
+ url,
317
+ // Honest about where this came from. A teardown scan is not a settle,
318
+ // and labelling it one would make the diagnostics — whose entire job
319
+ // is telling you how much to trust a result — lie.
320
+ reason: 'teardown',
321
+ sinceNavMs: 0,
322
+ mutations,
323
+ settleTimedOut: false,
324
+ }, options, testInfo, warn));
325
+ await pageState.queue;
326
+ }
327
+ catch (error) {
328
+ warn(`Final scan of ${url} failed: ${error instanceof Error ? error.message : String(error)}`);
329
+ }
330
+ }
331
+ }
332
+ //# sourceMappingURL=autoscan.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"autoscan.js","sourceRoot":"","sources":["../src/autoscan.ts"],"names":[],"mappings":"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"}
package/dist/axe.d.ts ADDED
@@ -0,0 +1,96 @@
1
+ import type { Page } from '@playwright/test';
2
+ import { type RulePolicy } from './rules.js';
3
+ /** The subset of axe's output this SDK reads. */
4
+ export interface AxeCheckResult {
5
+ data?: unknown;
6
+ }
7
+ export interface AxeNode {
8
+ target: unknown[];
9
+ failureSummary?: string;
10
+ any?: AxeCheckResult[];
11
+ all?: AxeCheckResult[];
12
+ none?: AxeCheckResult[];
13
+ }
14
+ export interface AxeRuleResult {
15
+ id: string;
16
+ impact?: string | null;
17
+ tags: string[];
18
+ helpUrl?: string;
19
+ nodes: AxeNode[];
20
+ }
21
+ export interface AxeRunResults {
22
+ violations: AxeRuleResult[];
23
+ incomplete: AxeRuleResult[];
24
+ passes: AxeRuleResult[];
25
+ inapplicable: AxeRuleResult[];
26
+ testEngine?: {
27
+ name?: string;
28
+ version?: string;
29
+ };
30
+ }
31
+ /**
32
+ * Inject axe and run it against the page's current state.
33
+ *
34
+ * Injection happens per scan rather than per page, because a navigation wipes
35
+ * the document and any previously injected script with it. Re-injecting is
36
+ * cheap next to the run itself.
37
+ *
38
+ * Main frame only, and that is a stated limitation rather than an oversight:
39
+ * axe reports cross-origin iframes as `incomplete` unless it is injected into
40
+ * each frame, which needs same-origin access the SDK cannot assume. Reporting
41
+ * them as incomplete is the honest outcome — "we could not check this" — and it
42
+ * reaches the payload as such rather than as a pass.
43
+ *
44
+ * ## Why this does not use `window.axe`
45
+ *
46
+ * Because it did, and it broke a customer's suite.
47
+ *
48
+ * The reference implementation runs `@axe-core/playwright` in its own specs. Both
49
+ * it and the original version of this function loaded axe onto the shared
50
+ * `window.axe`, so an auto-scan firing while one of their scans was in flight hit
51
+ * axe-core's own guard — *"Axe is already running"* — and failed two of THEIR
52
+ * tests, which then passed on retry. The SDK had changed a verdict in the suite
53
+ * of the customer profile most likely to adopt it: the one already running axe.
54
+ *
55
+ * The fix is that our instance is never reachable as `window.axe`. Axe's UMD
56
+ * bundle assigns itself to the global, so the injection captures it, files it
57
+ * under our own key, and puts back whatever was there before — including
58
+ * `undefined`, so a page with no axe of its own is left with no axe of its own.
59
+ * A separate instance carries its own `_running` flag, so neither side can ever
60
+ * trip the other's guard.
61
+ *
62
+ * Politeness on top of correctness: if the customer's axe IS mid-run we wait for
63
+ * it, up to a short budget, rather than traversing the same DOM at the same time.
64
+ * Two concurrent runs are no longer an ERROR, but they are still two full tree
65
+ * walks competing for one main thread, and the customer's run is the one with a
66
+ * person waiting on it.
67
+ */
68
+ export declare function runAxe(page: Page, policy: RulePolicy, timeoutMs?: number): Promise<AxeRunResults | undefined>;
69
+ /** The axe build that produced a result, read from the run rather than assumed. */
70
+ export declare function engineVersion(results: AxeRunResults): string | undefined;
71
+ /**
72
+ * A digest of the rules that actually ran.
73
+ *
74
+ * Evidence claims are relative to a ruleset, so "this finding did not appear last
75
+ * week" is only meaningful if the same rules were evaluated both times. The
76
+ * digest is what makes that checkable rather than assumed.
77
+ *
78
+ * Computed from the union of every outcome bucket — violations, incompletes,
79
+ * passes and inapplicable — because that union IS the resolved rule set. axe
80
+ * ships 105 rules and evaluates ~89 by default; the remainder are experimental or
81
+ * off by default and never appear in any bucket. Digesting `getRules()` instead
82
+ * would name rules that never ran, and would not change when the default set did.
83
+ *
84
+ * Sorted before hashing so two runs of the same configuration agree regardless of
85
+ * the order axe happened to report in.
86
+ */
87
+ export declare function rulesetDigest(axeVersion: string, results: AxeRunResults, tags?: string[],
88
+ /**
89
+ * Setsquare's own checks, when any are enabled — versioned ids like
90
+ * `setsquare-reading-level@1`. Evidence claims are relative to everything that
91
+ * assessed the page, not only axe, so two runs that differ in enabled setsquare
92
+ * checks must not share a digest. Empty (the default) leaves the digest
93
+ * byte-identical to what pre-check SDKs produced.
94
+ */
95
+ setsquareChecks?: string[]): string;
96
+ //# sourceMappingURL=axe.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"axe.d.ts","sourceRoot":"","sources":["../src/axe.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAG7C,OAAO,EAAW,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AA0BtD,iDAAiD;AACjD,MAAM,WAAW,cAAc;IAC7B,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,OAAO;IACtB,MAAM,EAAE,OAAO,EAAE,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,GAAG,CAAC,EAAE,cAAc,EAAE,CAAC;IACvB,GAAG,CAAC,EAAE,cAAc,EAAE,CAAC;IACvB,IAAI,CAAC,EAAE,cAAc,EAAE,CAAC;CACzB;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,OAAO,EAAE,CAAC;CAClB;AAED,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,aAAa,EAAE,CAAC;IAC5B,UAAU,EAAE,aAAa,EAAE,CAAC;IAC5B,MAAM,EAAE,aAAa,EAAE,CAAC;IACxB,YAAY,EAAE,aAAa,EAAE,CAAC;IAC9B,UAAU,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CAClD;AAcD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,wBAAsB,MAAM,CAC1B,IAAI,EAAE,IAAI,EACV,MAAM,EAAE,UAAU,EAClB,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,aAAa,GAAG,SAAS,CAAC,CA6GpC;AAED,mFAAmF;AACnF,wBAAgB,aAAa,CAAC,OAAO,EAAE,aAAa,GAAG,MAAM,GAAG,SAAS,CAExE;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,aAAa,CAC3B,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,aAAa,EACtB,IAAI,GAAE,MAAM,EAAO;AACnB;;;;;;GAMG;AACH,eAAe,GAAE,MAAM,EAAO,GAC7B,MAAM,CAuBR"}