@ploaness/config 1.12.0 → 1.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/dist/a11y.d.ts +13 -5
  2. package/dist/a11y.d.ts.map +1 -1
  3. package/dist/a11y.js +20 -11
  4. package/dist/a11y.js.map +1 -1
  5. package/dist/clock-preload.d.ts +2 -0
  6. package/dist/clock-preload.d.ts.map +1 -0
  7. package/dist/clock-preload.js +37 -0
  8. package/dist/clock-preload.js.map +1 -0
  9. package/dist/eslint-library.d.ts.map +1 -1
  10. package/dist/eslint-library.js +6 -1
  11. package/dist/eslint-library.js.map +1 -1
  12. package/dist/eslint-runtime.d.ts +11 -0
  13. package/dist/eslint-runtime.d.ts.map +1 -0
  14. package/dist/eslint-runtime.js +68 -0
  15. package/dist/eslint-runtime.js.map +1 -0
  16. package/dist/eslint.d.ts.map +1 -1
  17. package/dist/eslint.js +6 -1
  18. package/dist/eslint.js.map +1 -1
  19. package/dist/layout.d.ts +34 -16
  20. package/dist/layout.d.ts.map +1 -1
  21. package/dist/layout.js +228 -56
  22. package/dist/layout.js.map +1 -1
  23. package/dist/payload-view.d.ts +16 -0
  24. package/dist/payload-view.d.ts.map +1 -0
  25. package/dist/payload-view.js +35 -0
  26. package/dist/payload-view.js.map +1 -0
  27. package/dist/playwright.d.ts.map +1 -1
  28. package/dist/playwright.js +115 -12
  29. package/dist/playwright.js.map +1 -1
  30. package/dist/scanner.d.ts +99 -0
  31. package/dist/scanner.d.ts.map +1 -0
  32. package/dist/scanner.js +278 -0
  33. package/dist/scanner.js.map +1 -0
  34. package/dist/settle.d.ts +8 -6
  35. package/dist/settle.d.ts.map +1 -1
  36. package/dist/settle.js +55 -42
  37. package/dist/settle.js.map +1 -1
  38. package/dist/sweep-disclosures.d.ts +10 -0
  39. package/dist/sweep-disclosures.d.ts.map +1 -0
  40. package/dist/sweep-disclosures.js +179 -0
  41. package/dist/sweep-disclosures.js.map +1 -0
  42. package/dist/sweep-discovery.d.ts +15 -0
  43. package/dist/sweep-discovery.d.ts.map +1 -0
  44. package/dist/sweep-discovery.js +336 -0
  45. package/dist/sweep-discovery.js.map +1 -0
  46. package/dist/sweep-interaction.d.ts +19 -0
  47. package/dist/sweep-interaction.d.ts.map +1 -0
  48. package/dist/sweep-interaction.js +351 -0
  49. package/dist/sweep-interaction.js.map +1 -0
  50. package/dist/sweep-queue.d.ts +24 -0
  51. package/dist/sweep-queue.d.ts.map +1 -0
  52. package/dist/sweep-queue.js +56 -0
  53. package/dist/sweep-queue.js.map +1 -0
  54. package/dist/sweep-reflow.d.ts +17 -0
  55. package/dist/sweep-reflow.d.ts.map +1 -0
  56. package/dist/sweep-reflow.js +210 -0
  57. package/dist/sweep-reflow.js.map +1 -0
  58. package/dist/sweep-resting.d.ts +25 -0
  59. package/dist/sweep-resting.d.ts.map +1 -0
  60. package/dist/sweep-resting.js +98 -0
  61. package/dist/sweep-resting.js.map +1 -0
  62. package/dist/sweep-shared.d.ts +353 -0
  63. package/dist/sweep-shared.d.ts.map +1 -0
  64. package/dist/sweep-shared.js +434 -0
  65. package/dist/sweep-shared.js.map +1 -0
  66. package/dist/sweep.d.ts +14 -0
  67. package/dist/sweep.d.ts.map +1 -0
  68. package/dist/sweep.js +157 -0
  69. package/dist/sweep.js.map +1 -0
  70. package/dist/vitest-core.d.ts +1 -1
  71. package/dist/vitest-core.js +1 -1
  72. package/dist/vitest-library.d.ts.map +1 -1
  73. package/dist/vitest-library.js +2 -1
  74. package/dist/vitest-library.js.map +1 -1
  75. package/package.json +10 -2
package/dist/settle.d.ts CHANGED
@@ -1,14 +1,16 @@
1
1
  import type { Page } from '@playwright/test';
2
+ /** How a settle ended. */
3
+ export type SettleOutcome = 'settled' | 'ceiling' | 'busy' | 'interrupted';
2
4
  /**
3
5
  * Wait for a page to finish rendering, before anything measures it.
4
6
  *
5
7
  * Call it after a navigation, and after a state change a scan is about to judge. A still document
6
- * costs two animation frames; one that is animating costs however long its animations have left; a
7
- * document that never stops changing costs the budget and no more, so a page nobody can settle slows
8
- * the sweep rather than hanging it. A visible `aria-busy="true"` region is waited for until it clears,
9
- * on a longer ceiling of its own, and the page is then settled again.
8
+ * costs two animation frames; one that is animating costs however long its animations have left. A
9
+ * visible `aria-busy="true"` region is waited for until it clears, on a longer ceiling of its own, and
10
+ * the page is then settled again.
10
11
  * @param page the page about to be scanned, left on whatever route it holds.
11
- * @returns nothing. It resolves whether the page settled or the budget ran out.
12
+ * @returns `settled`, or what stopped it: the ceiling, a region that stayed busy, or a navigation that
13
+ * destroyed the page while it waited. The sweep reports every outcome but `settled` as a finding.
12
14
  */
13
- export declare const settleForScan: (page: Page) => Promise<void>;
15
+ export declare const settleForScan: (page: Page) => Promise<SettleOutcome>;
14
16
  //# sourceMappingURL=settle.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"settle.d.ts","sourceRoot":"","sources":["../src/settle.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAA;AA6L5C;;;;;;;;;;GAUG;AACH,eAAO,MAAM,aAAa,GAAU,MAAM,IAAI,KAAG,OAAO,CAAC,IAAI,CAc5D,CAAA"}
1
+ {"version":3,"file":"settle.d.ts","sourceRoot":"","sources":["../src/settle.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAA;AAuN5C,0BAA0B;AAC1B,MAAM,MAAM,aAAa,GAAG,SAAS,GAAG,SAAS,GAAG,MAAM,GAAG,aAAa,CAAA;AAE1E;;;;;;;;;;GAUG;AACH,eAAO,MAAM,aAAa,GAAU,MAAM,IAAI,KAAG,OAAO,CAAC,aAAa,CAgBrE,CAAA"}
package/dist/settle.js CHANGED
@@ -44,12 +44,19 @@
44
44
  // It is also unusable here in particular: the sweep runs against `next dev`, whose every page holds a
45
45
  // hot-reload socket open, so no page it ever visits goes idle.
46
46
  //
47
+ // WHY IMAGES ARE PART OF IT. A rule that judges an image reads its natural size and whether it loaded,
48
+ // and a lazy image below the fold has not started loading when the page settles. Every lazy image is
49
+ // therefore asked to load now, and every image's decode is awaited. A decode that fails is swallowed
50
+ // here: the image rule reports the broken image, with its element named.
51
+ //
47
52
  // WHY THE CEILING IS A TIMER. `requestAnimationFrame` does not fire in a page the browser has
48
53
  // backgrounded, so a frame-counted ceiling is not a ceiling. A throttled background timer still fires.
49
54
  // The race is inside the page for the same reason the hit-target probe does its work in one
50
55
  // `evaluate`: a Node-side race would leave the in-page promise pending against a context the next
51
56
  // navigation destroys.
52
- const SETTLE_BUDGET_MS = 1000;
57
+ // Reaching it is a finding rather than a timeout the scan proceeds past: a page that has not settled
58
+ // gives every verdict measured on it the standing of a coin toss.
59
+ const SETTLE_BUDGET_MS = 10_000;
53
60
  // WHY A BUSY REGION IS WAITED FOR, AND ON A CEILING OF ITS OWN. `aria-busy="true"` is a page saying
54
61
  // in so many words that a region is still being filled, and a framework's loading fallback says it:
55
62
  // Next streams a route's content into a hidden container and React reveals it in place of the busy
@@ -66,7 +73,7 @@ const BUSY_BUDGET_MS = 30_000;
66
73
  // own source text, so a reference to anything at module scope here would be a ReferenceError in the
67
74
  // page rather than a compile error in this file - the one mistake this shape cannot be checked for.
68
75
  // It is also why the animation wait below is written inline rather than as a helper of its own.
69
- const settleInPage = async (budgetMs) => {
76
+ const isSettledInPage = async (budgetMs) => {
70
77
  const quietFrames = 2;
71
78
  // Recursive rather than a loop over a counter, because there is no mutable binding to count with:
72
79
  // this repository bans `let` and in-place mutation in its own source as well as in a consumer's. Each
@@ -79,101 +86,107 @@ const settleInPage = async (budgetMs) => {
79
86
  });
80
87
  });
81
88
  const current = document.querySelectorAll('*').length;
82
- if (current !== previous) {
83
- await stable(current, quietFrames);
84
- return;
85
- }
86
- if (remaining > 1) {
87
- await stable(current, remaining - 1);
89
+ // A change restarts the count of quiet frames; a quiet frame spends one of them.
90
+ const left = current === previous ? remaining - 1 : quietFrames;
91
+ if (left > 0) {
92
+ await stable(current, left);
88
93
  }
89
94
  };
90
- const settled = async () => {
95
+ const hasSettled = async () => {
91
96
  // The optional call covers a document carrying no font set at all, which must skip the wait
92
97
  // rather than fail the settle.
93
98
  await document.fonts?.ready;
99
+ // Settled rather than all fulfilled: a broken image is the image rule's finding, reported with its
100
+ // element.
101
+ await Promise.allSettled(Array.from(document.images, async (image) => {
102
+ if (image.loading === 'lazy') {
103
+ image.setAttribute('loading', 'eager');
104
+ }
105
+ await image.decode();
106
+ }));
94
107
  // The element count FIRST, and this order is the whole of it. An animation does not exist until
95
108
  // something creates it, and on a hydrating page that something is the mount: measured before the
96
109
  // framework has mounted, `getAnimations` answers an honest empty list, and a settle that believed
97
110
  // it would scan the very frame the animation is about to start from. Waiting for the tree to stop
98
111
  // changing is waiting for the mount, which is what puts the animations on the page to be found.
99
112
  await stable(document.querySelectorAll('*').length, quietFrames);
100
- await Promise.all((document.getAnimations?.() ?? [])
113
+ // Settled rather than all fulfilled: a cancelled animation rejects rather than resolving. The
114
+ // cancellation is a state change like any other and the count below is what notices it.
115
+ await Promise.allSettled((document.getAnimations?.() ?? [])
101
116
  .filter((animation) => animation.playState === 'running' &&
102
117
  animation.effect !== null &&
103
118
  Number.isFinite(animation.effect.getComputedTiming().iterations))
104
- .map(async (animation) => {
105
- try {
106
- await animation.finished;
107
- }
108
- catch {
109
- // A cancelled animation rejects rather than resolving. The cancellation is a state change
110
- // like any other and the count below is what notices it.
111
- }
112
- }));
119
+ .map(async (animation) => await animation.finished));
113
120
  // And again afterwards, because an animation that ends by revealing a panel adds elements as it
114
121
  // finishes. This pass costs two frames on a page where nothing moved.
115
122
  await stable(document.querySelectorAll('*').length, quietFrames);
123
+ return true;
116
124
  };
117
- const deadline = async () => {
125
+ const isSettledByDeadline = async () => {
118
126
  await new Promise((resolve) => {
119
127
  setTimeout(() => {
120
128
  resolve();
121
129
  }, budgetMs);
122
130
  });
131
+ return false;
123
132
  };
124
- await Promise.race([settled(), deadline()]);
133
+ return await Promise.race([hasSettled(), isSettledByDeadline()]);
125
134
  };
126
135
  // Self-contained for the same reason as the settle above. Frame by frame, like the count, so a region
127
136
  // revealed between two checks is seen on the next. Answers whether anything was busy at all, so a page
128
137
  // that never was is not settled a second time.
129
- const wasBusyInPage = async (budgetMs) => {
138
+ const busyInPage = async (budgetMs) => {
130
139
  const busySelector = '[aria-busy="true"]';
131
140
  const isBusy = () => Array.from(document.querySelectorAll(busySelector)).some((element) => element.checkVisibility());
132
141
  const wasBusy = isBusy();
133
- const revealed = async () => {
142
+ const hasCleared = async () => {
134
143
  if (!isBusy()) {
135
- return;
144
+ return true;
136
145
  }
137
146
  await new Promise((resolve) => {
138
147
  requestAnimationFrame(() => {
139
148
  resolve();
140
149
  });
141
150
  });
142
- await revealed();
151
+ return await hasCleared();
143
152
  };
144
- await Promise.race([
145
- revealed(),
153
+ const isRevealed = await Promise.race([
154
+ hasCleared(),
146
155
  new Promise((resolve) => {
147
- setTimeout(resolve, budgetMs);
156
+ setTimeout(() => {
157
+ resolve(false);
158
+ }, budgetMs);
148
159
  }),
149
160
  ]);
150
- return wasBusy;
161
+ return { wasBusy, isRevealed };
151
162
  };
152
163
  /**
153
164
  * Wait for a page to finish rendering, before anything measures it.
154
165
  *
155
166
  * Call it after a navigation, and after a state change a scan is about to judge. A still document
156
- * costs two animation frames; one that is animating costs however long its animations have left; a
157
- * document that never stops changing costs the budget and no more, so a page nobody can settle slows
158
- * the sweep rather than hanging it. A visible `aria-busy="true"` region is waited for until it clears,
159
- * on a longer ceiling of its own, and the page is then settled again.
167
+ * costs two animation frames; one that is animating costs however long its animations have left. A
168
+ * visible `aria-busy="true"` region is waited for until it clears, on a longer ceiling of its own, and
169
+ * the page is then settled again.
160
170
  * @param page the page about to be scanned, left on whatever route it holds.
161
- * @returns nothing. It resolves whether the page settled or the budget ran out.
171
+ * @returns `settled`, or what stopped it: the ceiling, a region that stayed busy, or a navigation that
172
+ * destroyed the page while it waited. The sweep reports every outcome but `settled` as a finding.
162
173
  */
163
174
  export const settleForScan = async (page) => {
164
175
  try {
165
- await page.evaluate(settleInPage, SETTLE_BUDGET_MS);
166
- // Revealing the content changes the tree, so a page that was busy is settled once more after it.
167
- if (await page.evaluate(wasBusyInPage, BUSY_BUDGET_MS)) {
168
- await page.evaluate(settleInPage, SETTLE_BUDGET_MS);
176
+ if (!(await page.evaluate(isSettledInPage, SETTLE_BUDGET_MS))) {
177
+ return 'ceiling';
178
+ }
179
+ const busy = await page.evaluate(busyInPage, BUSY_BUDGET_MS);
180
+ if (!busy.isRevealed) {
181
+ return 'busy';
169
182
  }
183
+ // Revealing the content changes the tree, so a page that was busy is settled once more after it.
184
+ return !busy.wasBusy || (await page.evaluate(isSettledInPage, SETTLE_BUDGET_MS)) ? 'settled' : 'ceiling';
170
185
  }
171
186
  catch {
172
187
  // A route that redirects on mount destroys the execution context while this is in flight, and a
173
- // closed page cannot be asked anything. Neither is an accessibility finding, and neither should end
174
- // a sweep: the scan that follows then runs on whatever the page holds, which is what every call
175
- // site did before this function existed. A page that is genuinely broken still fails at `analyze`,
176
- // with an axe result to read rather than a protocol error naming no route.
188
+ // closed page cannot be asked anything. The caller decides what that means for its scan.
189
+ return 'interrupted';
177
190
  }
178
191
  };
179
192
  //# sourceMappingURL=settle.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"settle.js","sourceRoot":"","sources":["../src/settle.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"settle.js","sourceRoot":"","sources":["../src/settle.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import { type Page } from '@playwright/test';
2
+ import { type Shard } from './sweep-shared.js';
3
+ /**
4
+ * Open every disclosure on one shard of the locations, judge what it reveals, and judge how it answers
5
+ * the keyboard.
6
+ * @param page the test's page.
7
+ * @param shard the part of the pass this test visits.
8
+ */
9
+ export declare const expectDisclosureStates: (page: Page, shard: Shard) => Promise<void>;
10
+ //# sourceMappingURL=sweep-disclosures.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sweep-disclosures.d.ts","sourceRoot":"","sources":["../src/sweep-disclosures.ts"],"names":[],"mappings":"AAEA,OAAO,EAAwB,KAAK,IAAI,EAAQ,MAAM,kBAAkB,CAAA;AAaxE,OAAO,EAoBL,KAAK,KAAK,EAMX,MAAM,mBAAmB,CAAA;AAiN1B;;;;;GAKG;AACH,eAAO,MAAM,sBAAsB,GAAU,MAAM,IAAI,EAAE,OAAO,KAAK,KAAG,OAAO,CAAC,IAAI,CAoBnF,CAAA"}
@@ -0,0 +1,179 @@
1
+ // The sweep's disclosure pass: every disclosure opened, what it reveals judged, and how it answers the
2
+ // keyboard.
3
+ import { errors, test } from '@playwright/test';
4
+ import { disclosureDefects, drivenViewports, findingAt, } from '@ploaness/governance';
5
+ import { targetFacts } from './layout.js';
6
+ import { axeFindings, judgeRestingPlace, settleFindings } from './scanner.js';
7
+ import { settleForScan } from './settle.js';
8
+ import { carried } from './sweep-interaction.js';
9
+ import { armBudget, DISCLOSURE_ATTRIBUTE, EMPTY_VISIT, finishPass, healthOf, isChromium, itemLabel, loadLocation, locationOfItem, merged, placeAt, preparePage, RESTING_JUDGED, readDiscovery, shardLocations, visitAll, } from './sweep-shared.js';
10
+ const DISCLOSURES = '[aria-expanded="false"], [aria-haspopup]:not([aria-haspopup="false"]), details:not([open]) > summary';
11
+ // Mark the disclosures it is safe to open: visible, outside forms unless declared `type="button"`, and
12
+ // never a link that navigates. Answers how many were marked.
13
+ const markDisclosuresInPage = ([selector, attribute]) => {
14
+ const safe = Array.from(document.querySelectorAll(selector)).filter((element) => {
15
+ const tag = element.tagName.toLowerCase();
16
+ const href = element.getAttribute('href');
17
+ const isNavigating = tag === 'a' && href !== null && !href.startsWith('#');
18
+ const isSubmitting = element.closest('form') !== null && !(tag === 'button' && element.getAttribute('type') === 'button');
19
+ return element.checkVisibility() && !isNavigating && !isSubmitting;
20
+ });
21
+ for (const [index, element] of safe.entries()) {
22
+ element.setAttribute(attribute, String(index));
23
+ }
24
+ return safe.length;
25
+ };
26
+ const disclosureFactsInPage = (selector) => {
27
+ const control = document.querySelector(selector);
28
+ const modal = document.querySelector('dialog:modal, [role="dialog"][aria-modal="true"], [role="alertdialog"][aria-modal="true"]');
29
+ const isModalOpen = modal?.checkVisibility() === true;
30
+ const active = document.activeElement;
31
+ const attributeOf = (name) => control?.getAttribute(name) ?? null;
32
+ const containsFocus = (container) => container !== null && active !== null && (container === active || container.contains(active));
33
+ return {
34
+ url: location.href,
35
+ history: history.length,
36
+ expanded: attributeOf('aria-expanded'),
37
+ isPopup: (attributeOf('aria-haspopup') ?? 'false') !== 'false',
38
+ isModalOpen,
39
+ isFocusInModal: isModalOpen && containsFocus(modal),
40
+ isFocusOnControl: containsFocus(control),
41
+ };
42
+ };
43
+ const MAX_TRAP_STEPS = 30;
44
+ // Whether the control a disclosure opened from holds focus again, and nothing it opened is still open.
45
+ const isClosedToControlInPage = (selector) => {
46
+ const control = document.querySelector(selector);
47
+ const active = document.activeElement;
48
+ const modal = document.querySelector('dialog:modal, [role="dialog"][aria-modal="true"], [role="alertdialog"][aria-modal="true"]');
49
+ return (control !== null &&
50
+ active !== null &&
51
+ (control === active || control.contains(active)) &&
52
+ control.getAttribute('aria-expanded') !== 'true' &&
53
+ modal?.checkVisibility() !== true);
54
+ };
55
+ // How long a disclosure may take to close and hand focus back after Escape. Libraries such as Radix
56
+ // close in the key's own task and move focus once their exit has rendered, a tick or a frame later.
57
+ const FOCUS_RETURN_MS = 1000;
58
+ const closedAfterEscape = async (page, selector, isDismissible) => {
59
+ if (!isDismissible) {
60
+ return await page.evaluate(disclosureFactsInPage, selector);
61
+ }
62
+ try {
63
+ await page.waitForFunction(isClosedToControlInPage, selector, { timeout: FOCUS_RETURN_MS });
64
+ }
65
+ catch (error) {
66
+ // Not closed back to its control in time: the facts read next say which half failed.
67
+ if (!(error instanceof errors.TimeoutError)) {
68
+ throw error;
69
+ }
70
+ }
71
+ return await page.evaluate(disclosureFactsInPage, selector);
72
+ };
73
+ const isTrapped = async (page, selector, remaining) => {
74
+ if (remaining <= 0) {
75
+ return true;
76
+ }
77
+ await page.keyboard.press('Tab');
78
+ const facts = await page.evaluate(disclosureFactsInPage, selector);
79
+ return facts.isFocusInModal && (await isTrapped(page, selector, remaining - 1));
80
+ };
81
+ const labelOfDisclosure = (facts) => facts?.label ?? 'a disclosure';
82
+ // Escape for a popup or a modal dialog, then the keyboard judgement, then a click to close whatever the
83
+ // keyboard did not, so the next disclosure starts from a closed page.
84
+ const dismissDisclosure = async (disclosure) => {
85
+ const { page, selector, control, before, opened, place, facts } = disclosure;
86
+ const isFocusTrapped = opened.isModalOpen && (await isTrapped(page, selector, MAX_TRAP_STEPS));
87
+ const isDismissible = before.isPopup || opened.isModalOpen;
88
+ if (isDismissible) {
89
+ await page.keyboard.press('Escape');
90
+ }
91
+ const closed = await closedAfterEscape(page, selector, isDismissible);
92
+ const isClosedOnEscape = isDismissible && !closed.isModalOpen && closed.expanded !== 'true';
93
+ if (!isClosedOnEscape && (await control.isVisible())) {
94
+ await control.click();
95
+ }
96
+ await settleForScan(page);
97
+ return disclosureDefects({
98
+ isPopup: before.isPopup,
99
+ closedOnEscape: isClosedOnEscape,
100
+ focusReturned: closed.isFocusOnControl,
101
+ isModal: opened.isModalOpen,
102
+ focusTrapped: isFocusTrapped,
103
+ }).map((defect) => carried(findingAt(place, 'disclosure-keyboard', `${labelOfDisclosure(facts)}: ${defect}`), facts));
104
+ };
105
+ const judgeOpened = async (page, place) => {
106
+ const unsettled = await settleFindings(page, place);
107
+ const judged = await judgeRestingPlace(page, place);
108
+ const axe = await axeFindings(page, place);
109
+ return { ...judged, findings: [...unsettled, ...judged.findings, ...axe], notes: [] };
110
+ };
111
+ const driveDisclosure = async (page, item, index) => {
112
+ const selector = `[${DISCLOSURE_ATTRIBUTE}="${String(index)}"]`;
113
+ const control = page.locator(selector);
114
+ if (!(await control.isVisible())) {
115
+ return { visit: EMPTY_VISIT, isAborted: false };
116
+ }
117
+ const before = await page.evaluate(disclosureFactsInPage, selector);
118
+ const facts = await targetFacts(page, selector);
119
+ await control.click();
120
+ const opened = await page.evaluate(disclosureFactsInPage, selector);
121
+ if (opened.url !== before.url || opened.history !== before.history) {
122
+ return {
123
+ visit: {
124
+ ...EMPTY_VISIT,
125
+ notes: [
126
+ `${item.location.location}: opening ${labelOfDisclosure(facts)} changed the URL, so the disclosures ` +
127
+ 'after it went unchecked',
128
+ ],
129
+ },
130
+ isAborted: true,
131
+ };
132
+ }
133
+ const place = placeAt(page, item.location, item.viewport, `disclosure ${labelOfDisclosure(facts)}`);
134
+ const revealed = await judgeOpened(page, place);
135
+ const keyboard = await dismissDisclosure({ page, selector, control, before, opened, place, facts });
136
+ return { visit: merged([revealed, ...keyboard]), isAborted: false };
137
+ };
138
+ const driveDisclosures = async (page, item, index, count) => {
139
+ if (index >= count) {
140
+ return [];
141
+ }
142
+ const driven = await driveDisclosure(page, item, index);
143
+ return driven.isAborted ? [driven.visit] : [driven.visit, ...(await driveDisclosures(page, item, index + 1, count))];
144
+ };
145
+ const disclosureVisit = async (page, item) => {
146
+ const place = placeAt(page, item.location, item.viewport, 'resting');
147
+ const loaded = await loadLocation(page, item.location, item.viewport, place);
148
+ await settleForScan(page);
149
+ const count = await page.evaluate(markDisclosuresInPage, [DISCLOSURES, DISCLOSURE_ATTRIBUTE]);
150
+ const driven = await driveDisclosures(page, item, 0, count);
151
+ return merged([{ ...EMPTY_VISIT, findings: loaded }, ...driven, await healthOf(page, place)]);
152
+ };
153
+ const DISCLOSURE_RULES = [...RESTING_JUDGED, 'disclosure-keyboard'];
154
+ /**
155
+ * Open every disclosure on one shard of the locations, judge what it reveals, and judge how it answers
156
+ * the keyboard.
157
+ * @param page the test's page.
158
+ * @param shard the part of the pass this test visits.
159
+ */
160
+ export const expectDisclosureStates = async (page, shard) => {
161
+ test.info().skip(!isChromium(page), 'the disclosure pass runs in Chromium');
162
+ const discovery = readDiscovery(test.info());
163
+ const items = discovery.locations.flatMap((location) => drivenViewports(location.isStory).map((viewport) => ({ location, viewport })));
164
+ const budget = armBudget(page, 'disclosures', shard, items.length);
165
+ await preparePage(page);
166
+ const { visits, taken } = await visitAll(items, {
167
+ budget,
168
+ labelOf: itemLabel,
169
+ visit: async (item) => await disclosureVisit(page, item),
170
+ });
171
+ finishPass(page, {
172
+ budget,
173
+ pass: 'disclosures',
174
+ visits,
175
+ judgedRules: DISCLOSURE_RULES,
176
+ locations: shardLocations(taken, locationOfItem),
177
+ });
178
+ };
179
+ //# sourceMappingURL=sweep-disclosures.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sweep-disclosures.js","sourceRoot":"","sources":["../src/sweep-disclosures.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import { type Page } from '@playwright/test';
2
+ /**
3
+ * Discover every page the sweep scans, and record it with whatever made the discovery incomplete.
4
+ *
5
+ * Routes come from a link crawl from `/`, every same-origin `hreflang` alternate, and `sitemap.xml`;
6
+ * stories from each auxiliary server that answers `/index.json`; breakpoints and colour schemes from
7
+ * each page's stylesheets. A crawled internal link that does not answer is a broken link. Under a
8
+ * `delta` run's change scope the crawl still reaches and checks every route, and the passes are handed
9
+ * only the routes and stories the change reaches, each with the reason it is swept. Every pass depends
10
+ * on this test, so it fails only when it could not write its record; the summary judges the findings
11
+ * and problems it recorded.
12
+ * @param page the test's page.
13
+ */
14
+ export declare const expectDiscoveredSite: (page: Page) => Promise<void>;
15
+ //# sourceMappingURL=sweep-discovery.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sweep-discovery.d.ts","sourceRoot":"","sources":["../src/sweep-discovery.ts"],"names":[],"mappings":"AACA,OAAO,EAA4B,KAAK,IAAI,EAAuB,MAAM,kBAAkB,CAAA;AAoc3F;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,oBAAoB,GAAU,MAAM,IAAI,KAAG,OAAO,CAAC,IAAI,CAgDnE,CAAA"}