@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.
- package/dist/a11y.d.ts +13 -5
- package/dist/a11y.d.ts.map +1 -1
- package/dist/a11y.js +20 -11
- package/dist/a11y.js.map +1 -1
- package/dist/clock-preload.d.ts +2 -0
- package/dist/clock-preload.d.ts.map +1 -0
- package/dist/clock-preload.js +37 -0
- package/dist/clock-preload.js.map +1 -0
- package/dist/eslint-library.d.ts.map +1 -1
- package/dist/eslint-library.js +6 -1
- package/dist/eslint-library.js.map +1 -1
- package/dist/eslint-runtime.d.ts +11 -0
- package/dist/eslint-runtime.d.ts.map +1 -0
- package/dist/eslint-runtime.js +68 -0
- package/dist/eslint-runtime.js.map +1 -0
- package/dist/eslint.d.ts.map +1 -1
- package/dist/eslint.js +6 -1
- package/dist/eslint.js.map +1 -1
- package/dist/layout.d.ts +34 -16
- package/dist/layout.d.ts.map +1 -1
- package/dist/layout.js +228 -56
- package/dist/layout.js.map +1 -1
- package/dist/payload-view.d.ts +16 -0
- package/dist/payload-view.d.ts.map +1 -0
- package/dist/payload-view.js +35 -0
- package/dist/payload-view.js.map +1 -0
- package/dist/playwright.d.ts.map +1 -1
- package/dist/playwright.js +115 -12
- package/dist/playwright.js.map +1 -1
- package/dist/scanner.d.ts +99 -0
- package/dist/scanner.d.ts.map +1 -0
- package/dist/scanner.js +278 -0
- package/dist/scanner.js.map +1 -0
- package/dist/settle.d.ts +8 -6
- package/dist/settle.d.ts.map +1 -1
- package/dist/settle.js +55 -42
- package/dist/settle.js.map +1 -1
- package/dist/sweep-disclosures.d.ts +10 -0
- package/dist/sweep-disclosures.d.ts.map +1 -0
- package/dist/sweep-disclosures.js +179 -0
- package/dist/sweep-disclosures.js.map +1 -0
- package/dist/sweep-discovery.d.ts +15 -0
- package/dist/sweep-discovery.d.ts.map +1 -0
- package/dist/sweep-discovery.js +336 -0
- package/dist/sweep-discovery.js.map +1 -0
- package/dist/sweep-interaction.d.ts +19 -0
- package/dist/sweep-interaction.d.ts.map +1 -0
- package/dist/sweep-interaction.js +351 -0
- package/dist/sweep-interaction.js.map +1 -0
- package/dist/sweep-queue.d.ts +24 -0
- package/dist/sweep-queue.d.ts.map +1 -0
- package/dist/sweep-queue.js +56 -0
- package/dist/sweep-queue.js.map +1 -0
- package/dist/sweep-reflow.d.ts +17 -0
- package/dist/sweep-reflow.d.ts.map +1 -0
- package/dist/sweep-reflow.js +210 -0
- package/dist/sweep-reflow.js.map +1 -0
- package/dist/sweep-resting.d.ts +25 -0
- package/dist/sweep-resting.d.ts.map +1 -0
- package/dist/sweep-resting.js +98 -0
- package/dist/sweep-resting.js.map +1 -0
- package/dist/sweep-shared.d.ts +353 -0
- package/dist/sweep-shared.d.ts.map +1 -0
- package/dist/sweep-shared.js +434 -0
- package/dist/sweep-shared.js.map +1 -0
- package/dist/sweep.d.ts +14 -0
- package/dist/sweep.d.ts.map +1 -0
- package/dist/sweep.js +157 -0
- package/dist/sweep.js.map +1 -0
- package/dist/vitest-core.d.ts +1 -1
- package/dist/vitest-core.js +1 -1
- package/dist/vitest-library.d.ts.map +1 -1
- package/dist/vitest-library.js +2 -1
- package/dist/vitest-library.js.map +1 -1
- 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
|
|
7
|
-
*
|
|
8
|
-
* the
|
|
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
|
|
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<
|
|
15
|
+
export declare const settleForScan: (page: Page) => Promise<SettleOutcome>;
|
|
14
16
|
//# sourceMappingURL=settle.d.ts.map
|
package/dist/settle.d.ts.map
CHANGED
|
@@ -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;
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
83
|
-
|
|
84
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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([
|
|
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
|
|
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
|
|
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
|
|
151
|
+
return await hasCleared();
|
|
143
152
|
};
|
|
144
|
-
await Promise.race([
|
|
145
|
-
|
|
153
|
+
const isRevealed = await Promise.race([
|
|
154
|
+
hasCleared(),
|
|
146
155
|
new Promise((resolve) => {
|
|
147
|
-
setTimeout(
|
|
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
|
|
157
|
-
*
|
|
158
|
-
* the
|
|
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
|
|
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(
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
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.
|
|
174
|
-
|
|
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
|
package/dist/settle.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"settle.js","sourceRoot":"","sources":["../src/settle.ts"],"names":[],"mappings":"AAGA,oGAAoG;AACpG,oGAAoG;AACpG,oGAAoG;AACpG,uEAAuE;AACvE,EAAE;AACF,qGAAqG;AACrG,oGAAoG;AACpG,oGAAoG;AACpG,qGAAqG;AACrG,uGAAuG;AACvG,gGAAgG;AAChG,wDAAwD;AACxD,sGAAsG;AACtG,sGAAsG;AACtG,kGAAkG;AAClG,0BAA0B;AAC1B,EAAE;AACF,uGAAuG;AACvG,mGAAmG;AACnG,gGAAgG;AAChG,mGAAmG;AACnG,qGAAqG;AACrG,sGAAsG;AACtG,uGAAuG;AACvG,8FAA8F;AAC9F,EAAE;AACF,mGAAmG;AACnG,uGAAuG;AACvG,oGAAoG;AACpG,qGAAqG;AACrG,mGAAmG;AACnG,+FAA+F;AAC/F,EAAE;AACF,qGAAqG;AACrG,mGAAmG;AACnG,yFAAyF;AACzF,EAAE;AACF,mGAAmG;AACnG,uGAAuG;AACvG,gEAAgE;AAChE,EAAE;AACF,mGAAmG;AACnG,qGAAqG;AACrG,sGAAsG;AACtG,+DAA+D;AAC/D,EAAE;AACF,8FAA8F;AAC9F,uGAAuG;AACvG,4FAA4F;AAC5F,kGAAkG;AAClG,uBAAuB;AACvB,MAAM,gBAAgB,GAAW,
|
|
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":"AAAA,uGAAuG;AACvG,YAAY;AACZ,OAAO,EAAE,MAAM,EAA2B,IAAI,EAAE,MAAM,kBAAkB,CAAA;AACxE,OAAO,EAEL,iBAAiB,EACjB,eAAe,EAGf,SAAS,GACV,MAAM,sBAAsB,CAAA;AAC7B,OAAO,EAAoB,WAAW,EAAE,MAAM,aAAa,CAAA;AAC3D,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAkB,cAAc,EAAE,MAAM,cAAc,CAAA;AAC7F,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAC3C,OAAO,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAA;AAChD,OAAO,EACL,SAAS,EAET,oBAAoB,EAIpB,WAAW,EACX,UAAU,EACV,QAAQ,EACR,UAAU,EACV,SAAS,EACT,YAAY,EACZ,cAAc,EACd,MAAM,EAEN,OAAO,EACP,WAAW,EACX,cAAc,EACd,aAAa,EAIb,cAAc,EAEd,QAAQ,GACT,MAAM,mBAAmB,CAAA;AAO1B,MAAM,WAAW,GACf,sGAAsG,CAAA;AAExG,uGAAuG;AACvG,6DAA6D;AAC7D,MAAM,qBAAqB,GAAG,CAAC,CAAC,QAAQ,EAAE,SAAS,CAA4B,EAAU,EAAE;IACzF,MAAM,IAAI,GAA0B,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,gBAAgB,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CACxF,CAAC,OAAmB,EAAW,EAAE;QAC/B,MAAM,GAAG,GAAW,OAAO,CAAC,OAAO,CAAC,WAAW,EAAE,CAAA;QACjD,MAAM,IAAI,GAAkB,OAAO,CAAC,YAAY,CAAC,MAAM,CAAC,CAAA;QACxD,MAAM,YAAY,GAAY,GAAG,KAAK,GAAG,IAAI,IAAI,KAAK,IAAI,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAA;QACnF,MAAM,YAAY,GAChB,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC,GAAG,KAAK,QAAQ,IAAI,OAAO,CAAC,YAAY,CAAC,MAAM,CAAC,KAAK,QAAQ,CAAC,CAAA;QACtG,OAAO,OAAO,CAAC,eAAe,EAAE,IAAI,CAAC,YAAY,IAAI,CAAC,YAAY,CAAA;IACpE,CAAC,CACF,CAAA;IACD,KAAK,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,CAAC;QAC9C,OAAO,CAAC,YAAY,CAAC,SAAS,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAA;IAChD,CAAC;IACD,OAAO,IAAI,CAAC,MAAM,CAAA;AACpB,CAAC,CAAA;AAYD,MAAM,qBAAqB,GAAG,CAAC,QAAgB,EAAmB,EAAE;IAClE,MAAM,OAAO,GAAsB,QAAQ,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAA;IACnE,MAAM,KAAK,GAAsB,QAAQ,CAAC,aAAa,CACrD,2FAA2F,CAC5F,CAAA;IACD,MAAM,WAAW,GAAY,KAAK,EAAE,eAAe,EAAE,KAAK,IAAI,CAAA;IAC9D,MAAM,MAAM,GAAsB,QAAQ,CAAC,aAAa,CAAA;IACxD,MAAM,WAAW,GAAG,CAAC,IAAY,EAAiB,EAAE,CAAC,OAAO,EAAE,YAAY,CAAC,IAAI,CAAC,IAAI,IAAI,CAAA;IACxF,MAAM,aAAa,GAAG,CAAC,SAA4B,EAAW,EAAE,CAC9D,SAAS,KAAK,IAAI,IAAI,MAAM,KAAK,IAAI,IAAI,CAAC,SAAS,KAAK,MAAM,IAAI,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAA;IAC/F,OAAO;QACL,GAAG,EAAE,QAAQ,CAAC,IAAI;QAClB,OAAO,EAAE,OAAO,CAAC,MAAM;QACvB,QAAQ,EAAE,WAAW,CAAC,eAAe,CAAC;QACtC,OAAO,EAAE,CAAC,WAAW,CAAC,eAAe,CAAC,IAAI,OAAO,CAAC,KAAK,OAAO;QAC9D,WAAW;QACX,cAAc,EAAE,WAAW,IAAI,aAAa,CAAC,KAAK,CAAC;QACnD,gBAAgB,EAAE,aAAa,CAAC,OAAO,CAAC;KACzC,CAAA;AACH,CAAC,CAAA;AAED,MAAM,cAAc,GAAW,EAAE,CAAA;AAEjC,uGAAuG;AACvG,MAAM,uBAAuB,GAAG,CAAC,QAAgB,EAAW,EAAE;IAC5D,MAAM,OAAO,GAAsB,QAAQ,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAA;IACnE,MAAM,MAAM,GAAsB,QAAQ,CAAC,aAAa,CAAA;IACxD,MAAM,KAAK,GAAsB,QAAQ,CAAC,aAAa,CACrD,2FAA2F,CAC5F,CAAA;IACD,OAAO,CACL,OAAO,KAAK,IAAI;QAChB,MAAM,KAAK,IAAI;QACf,CAAC,OAAO,KAAK,MAAM,IAAI,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QAChD,OAAO,CAAC,YAAY,CAAC,eAAe,CAAC,KAAK,MAAM;QAChD,KAAK,EAAE,eAAe,EAAE,KAAK,IAAI,CAClC,CAAA;AACH,CAAC,CAAA;AAED,oGAAoG;AACpG,oGAAoG;AACpG,MAAM,eAAe,GAAW,IAAI,CAAA;AAEpC,MAAM,iBAAiB,GAAG,KAAK,EAAE,IAAU,EAAE,QAAgB,EAAE,aAAsB,EAA4B,EAAE;IACjH,IAAI,CAAC,aAAa,EAAE,CAAC;QACnB,OAAO,MAAM,IAAI,CAAC,QAAQ,CAAC,qBAAqB,EAAE,QAAQ,CAAC,CAAA;IAC7D,CAAC;IACD,IAAI,CAAC;QACH,MAAM,IAAI,CAAC,eAAe,CAAC,uBAAuB,EAAE,QAAQ,EAAE,EAAE,OAAO,EAAE,eAAe,EAAE,CAAC,CAAA;IAC7F,CAAC;IAAC,OAAO,KAAc,EAAE,CAAC;QACxB,qFAAqF;QACrF,IAAI,CAAC,CAAC,KAAK,YAAY,MAAM,CAAC,YAAY,CAAC,EAAE,CAAC;YAC5C,MAAM,KAAK,CAAA;QACb,CAAC;IACH,CAAC;IACD,OAAO,MAAM,IAAI,CAAC,QAAQ,CAAC,qBAAqB,EAAE,QAAQ,CAAC,CAAA;AAC7D,CAAC,CAAA;AAED,MAAM,SAAS,GAAG,KAAK,EAAE,IAAU,EAAE,QAAgB,EAAE,SAAiB,EAAoB,EAAE;IAC5F,IAAI,SAAS,IAAI,CAAC,EAAE,CAAC;QACnB,OAAO,IAAI,CAAA;IACb,CAAC;IACD,MAAM,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;IAChC,MAAM,KAAK,GAAoB,MAAM,IAAI,CAAC,QAAQ,CAAC,qBAAqB,EAAE,QAAQ,CAAC,CAAA;IACnF,OAAO,KAAK,CAAC,cAAc,IAAI,CAAC,MAAM,SAAS,CAAC,IAAI,EAAE,QAAQ,EAAE,SAAS,GAAG,CAAC,CAAC,CAAC,CAAA;AACjF,CAAC,CAAA;AAYD,MAAM,iBAAiB,GAAG,CAAC,KAA8B,EAAU,EAAE,CAAC,KAAK,EAAE,KAAK,IAAI,cAAc,CAAA;AAEpG,wGAAwG;AACxG,sEAAsE;AACtE,MAAM,iBAAiB,GAAG,KAAK,EAAE,UAA4B,EAA6B,EAAE;IAC1F,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,GAAqB,UAAU,CAAA;IAC9F,MAAM,cAAc,GAAY,MAAM,CAAC,WAAW,IAAI,CAAC,MAAM,SAAS,CAAC,IAAI,EAAE,QAAQ,EAAE,cAAc,CAAC,CAAC,CAAA;IACvG,MAAM,aAAa,GAAY,MAAM,CAAC,OAAO,IAAI,MAAM,CAAC,WAAW,CAAA;IACnE,IAAI,aAAa,EAAE,CAAC;QAClB,MAAM,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAA;IACrC,CAAC;IACD,MAAM,MAAM,GAAoB,MAAM,iBAAiB,CAAC,IAAI,EAAE,QAAQ,EAAE,aAAa,CAAC,CAAA;IACtF,MAAM,gBAAgB,GAAY,aAAa,IAAI,CAAC,MAAM,CAAC,WAAW,IAAI,MAAM,CAAC,QAAQ,KAAK,MAAM,CAAA;IACpG,IAAI,CAAC,gBAAgB,IAAI,CAAC,MAAM,OAAO,CAAC,SAAS,EAAE,CAAC,EAAE,CAAC;QACrD,MAAM,OAAO,CAAC,KAAK,EAAE,CAAA;IACvB,CAAC;IACD,MAAM,aAAa,CAAC,IAAI,CAAC,CAAA;IACzB,OAAO,iBAAiB,CAAC;QACvB,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,cAAc,EAAE,gBAAgB;QAChC,aAAa,EAAE,MAAM,CAAC,gBAAgB;QACtC,OAAO,EAAE,MAAM,CAAC,WAAW;QAC3B,YAAY,EAAE,cAAc;KAC7B,CAAC,CAAC,GAAG,CACJ,CAAC,MAAc,EAAS,EAAE,CACxB,OAAO,CAAC,SAAS,CAAC,KAAK,EAAE,qBAAqB,EAAE,GAAG,iBAAiB,CAAC,KAAK,CAAC,KAAK,MAAM,EAAE,CAAC,EAAE,KAAK,CAAC,CACpG,CAAA;AACH,CAAC,CAAA;AAED,MAAM,WAAW,GAAG,KAAK,EAAE,IAAU,EAAE,KAAmB,EAAkB,EAAE;IAC5E,MAAM,SAAS,GAA8B,MAAM,cAAc,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;IAC9E,MAAM,MAAM,GAAc,MAAM,iBAAiB,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;IAC9D,MAAM,GAAG,GAA8B,MAAM,WAAW,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;IACrE,OAAO,EAAE,GAAG,MAAM,EAAE,QAAQ,EAAE,CAAC,GAAG,SAAS,EAAE,GAAG,MAAM,CAAC,QAAQ,EAAE,GAAG,GAAG,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,CAAA;AACvF,CAAC,CAAA;AAOD,MAAM,eAAe,GAAG,KAAK,EAAE,IAAU,EAAE,IAAe,EAAE,KAAa,EAA6B,EAAE;IACtG,MAAM,QAAQ,GAAW,IAAI,oBAAoB,KAAK,MAAM,CAAC,KAAK,CAAC,IAAI,CAAA;IACvE,MAAM,OAAO,GAAY,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAA;IAC/C,IAAI,CAAC,CAAC,MAAM,OAAO,CAAC,SAAS,EAAE,CAAC,EAAE,CAAC;QACjC,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,KAAK,EAAE,CAAA;IACjD,CAAC;IACD,MAAM,MAAM,GAAoB,MAAM,IAAI,CAAC,QAAQ,CAAC,qBAAqB,EAAE,QAAQ,CAAC,CAAA;IACpF,MAAM,KAAK,GAA4B,MAAM,WAAW,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAA;IACxE,MAAM,OAAO,CAAC,KAAK,EAAE,CAAA;IACrB,MAAM,MAAM,GAAoB,MAAM,IAAI,CAAC,QAAQ,CAAC,qBAAqB,EAAE,QAAQ,CAAC,CAAA;IACpF,IAAI,MAAM,CAAC,GAAG,KAAK,MAAM,CAAC,GAAG,IAAI,MAAM,CAAC,OAAO,KAAK,MAAM,CAAC,OAAO,EAAE,CAAC;QACnE,OAAO;YACL,KAAK,EAAE;gBACL,GAAG,WAAW;gBACd,KAAK,EAAE;oBACL,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,aAAa,iBAAiB,CAAC,KAAK,CAAC,uCAAuC;wBACnG,yBAAyB;iBAC5B;aACF;YACD,SAAS,EAAE,IAAI;SAChB,CAAA;IACH,CAAC;IACD,MAAM,KAAK,GAAiB,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,cAAc,iBAAiB,CAAC,KAAK,CAAC,EAAE,CAAC,CAAA;IACjH,MAAM,QAAQ,GAAU,MAAM,WAAW,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;IACtD,MAAM,QAAQ,GAAqB,MAAM,iBAAiB,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAA;IACrH,OAAO,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC,QAAQ,EAAE,GAAG,QAAQ,CAAC,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE,CAAA;AACrE,CAAC,CAAA;AAED,MAAM,gBAAgB,GAAG,KAAK,EAC5B,IAAU,EACV,IAAe,EACf,KAAa,EACb,KAAa,EACc,EAAE;IAC7B,IAAI,KAAK,IAAI,KAAK,EAAE,CAAC;QACnB,OAAO,EAAE,CAAA;IACX,CAAC;IACD,MAAM,MAAM,GAAqB,MAAM,eAAe,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,CAAC,CAAA;IACzE,OAAO,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,EAAE,GAAG,CAAC,MAAM,gBAAgB,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,GAAG,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,CAAA;AACtH,CAAC,CAAA;AAED,MAAM,eAAe,GAAG,KAAK,EAAE,IAAU,EAAE,IAAe,EAAkB,EAAE;IAC5E,MAAM,KAAK,GAAiB,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAA;IAClF,MAAM,MAAM,GAA8B,MAAM,YAAY,CAAC,IAAI,EAAE,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAA;IACvG,MAAM,aAAa,CAAC,IAAI,CAAC,CAAA;IACzB,MAAM,KAAK,GAAW,MAAM,IAAI,CAAC,QAAQ,CAAC,qBAAqB,EAAE,CAAC,WAAW,EAAE,oBAAoB,CAAU,CAAC,CAAA;IAC9G,MAAM,MAAM,GAAqB,MAAM,gBAAgB,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,EAAE,KAAK,CAAC,CAAA;IAC7E,OAAO,MAAM,CAAC,CAAC,EAAE,GAAG,WAAW,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,GAAG,MAAM,EAAE,MAAM,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,CAAA;AAC/F,CAAC,CAAA;AAED,MAAM,gBAAgB,GAAsB,CAAC,GAAG,cAAc,EAAE,qBAAqB,CAAC,CAAA;AAEtF;;;;;GAKG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,KAAK,EAAE,IAAU,EAAE,KAAY,EAAiB,EAAE;IACtF,IAAI,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,sCAAsC,CAAC,CAAA;IAC3E,MAAM,SAAS,GAAc,aAAa,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAA;IACvD,MAAM,KAAK,GAAyB,SAAS,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,QAAuB,EAAwB,EAAE,CAChH,eAAe,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,QAAyB,EAAa,EAAE,CAAC,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,CAAC,CAC1G,CAAA;IACD,MAAM,MAAM,GAAW,SAAS,CAAC,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,CAAA;IAC1E,MAAM,WAAW,CAAC,IAAI,CAAC,CAAA;IACvB,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,GAA2B,MAAM,QAAQ,CAAC,KAAK,EAAE;QACtE,MAAM;QACN,OAAO,EAAE,SAAS;QAClB,KAAK,EAAE,KAAK,EAAE,IAAe,EAAkB,EAAE,CAAC,MAAM,eAAe,CAAC,IAAI,EAAE,IAAI,CAAC;KACpF,CAAC,CAAA;IACF,UAAU,CAAC,IAAI,EAAE;QACf,MAAM;QACN,IAAI,EAAE,aAAa;QACnB,MAAM;QACN,WAAW,EAAE,gBAAgB;QAC7B,SAAS,EAAE,cAAc,CAAC,KAAK,EAAE,cAAc,CAAC;KACjD,CAAC,CAAA;AACJ,CAAC,CAAA"}
|
|
@@ -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"}
|