@dreamdata_io/verify-tracking 0.1.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/LICENSE +21 -0
- package/README.md +756 -0
- package/dist/ai/checker.js +222 -0
- package/dist/ai/checker.js.map +1 -0
- package/dist/ai/checklist.js +190 -0
- package/dist/ai/checklist.js.map +1 -0
- package/dist/ai/cli-presets.js +27 -0
- package/dist/ai/cli-presets.js.map +1 -0
- package/dist/ai/evidence-view.js +143 -0
- package/dist/ai/evidence-view.js.map +1 -0
- package/dist/ai/local-cli.js +58 -0
- package/dist/ai/local-cli.js.map +1 -0
- package/dist/ai/openai-compatible.js +78 -0
- package/dist/ai/openai-compatible.js.map +1 -0
- package/dist/ai/response-parser.js +105 -0
- package/dist/ai/response-parser.js.map +1 -0
- package/dist/ai/types.js +2 -0
- package/dist/ai/types.js.map +1 -0
- package/dist/analysis/consent-mode.js +225 -0
- package/dist/analysis/consent-mode.js.map +1 -0
- package/dist/analysis/deployment.js +2658 -0
- package/dist/analysis/deployment.js.map +1 -0
- package/dist/analysis/diagnosis/copy.en.js +303 -0
- package/dist/analysis/diagnosis/copy.en.js.map +1 -0
- package/dist/analysis/diagnosis/diagnose.js +172 -0
- package/dist/analysis/diagnosis/diagnose.js.map +1 -0
- package/dist/analysis/diagnosis/docs.js +118 -0
- package/dist/analysis/diagnosis/docs.js.map +1 -0
- package/dist/analysis/diagnosis/index.js +5 -0
- package/dist/analysis/diagnosis/index.js.map +1 -0
- package/dist/analysis/diagnosis/rules.js +656 -0
- package/dist/analysis/diagnosis/rules.js.map +1 -0
- package/dist/analysis/diagnosis/types.js +2 -0
- package/dist/analysis/diagnosis/types.js.map +1 -0
- package/dist/cli.js +344 -0
- package/dist/cli.js.map +1 -0
- package/dist/config/env.js +100 -0
- package/dist/config/env.js.map +1 -0
- package/dist/config/preflight.js +167 -0
- package/dist/config/preflight.js.map +1 -0
- package/dist/config/user-config.js +79 -0
- package/dist/config/user-config.js.map +1 -0
- package/dist/evidence/collect.js +527 -0
- package/dist/evidence/collect.js.map +1 -0
- package/dist/evidence/consent-accept.js +377 -0
- package/dist/evidence/consent-accept.js.map +1 -0
- package/dist/evidence/consent.js +340 -0
- package/dist/evidence/consent.js.map +1 -0
- package/dist/evidence/constants.js +615 -0
- package/dist/evidence/constants.js.map +1 -0
- package/dist/evidence/globals.js +132 -0
- package/dist/evidence/globals.js.map +1 -0
- package/dist/evidence/gtm-container.js +432 -0
- package/dist/evidence/gtm-container.js.map +1 -0
- package/dist/evidence/heuristic-banner.js +107 -0
- package/dist/evidence/heuristic-banner.js.map +1 -0
- package/dist/evidence/http-probe.js +139 -0
- package/dist/evidence/http-probe.js.map +1 -0
- package/dist/evidence/init-script.js +112 -0
- package/dist/evidence/init-script.js.map +1 -0
- package/dist/evidence/network.js +365 -0
- package/dist/evidence/network.js.map +1 -0
- package/dist/evidence/preference-center.js +368 -0
- package/dist/evidence/preference-center.js.map +1 -0
- package/dist/evidence/script.js +425 -0
- package/dist/evidence/script.js.map +1 -0
- package/dist/evidence/second-page.js +332 -0
- package/dist/evidence/second-page.js.map +1 -0
- package/dist/evidence/selector-probe.js +53 -0
- package/dist/evidence/selector-probe.js.map +1 -0
- package/dist/evidence/stealth.js +21 -0
- package/dist/evidence/stealth.js.map +1 -0
- package/dist/evidence/types.js +14 -0
- package/dist/evidence/types.js.map +1 -0
- package/dist/interactive/prompt.js +31 -0
- package/dist/interactive/prompt.js.map +1 -0
- package/dist/mcp/server.js +308 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/summary.js +41 -0
- package/dist/mcp/summary.js.map +1 -0
- package/dist/report/context.js +184 -0
- package/dist/report/context.js.map +1 -0
- package/dist/report/projection.js +125 -0
- package/dist/report/projection.js.map +1 -0
- package/dist/report/quote-check.js +90 -0
- package/dist/report/quote-check.js.map +1 -0
- package/dist/report/render.js +178 -0
- package/dist/report/render.js.map +1 -0
- package/dist/report/serializers.js +124 -0
- package/dist/report/serializers.js.map +1 -0
- package/dist/report/store.js +139 -0
- package/dist/report/store.js.map +1 -0
- package/dist/run/verify.js +77 -0
- package/dist/run/verify.js.map +1 -0
- package/dist/utils/duration.js +10 -0
- package/dist/utils/duration.js.map +1 -0
- package/dist/utils/errors.js +17 -0
- package/dist/utils/errors.js.map +1 -0
- package/dist/utils/logger.js +59 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/runTimingsLog.js +27 -0
- package/dist/utils/runTimingsLog.js.map +1 -0
- package/dist/utils/slug.js +27 -0
- package/dist/utils/slug.js.map +1 -0
- package/dist/version.js +10 -0
- package/dist/version.js.map +1 -0
- package/package.json +57 -0
- package/scripts/install.sh +314 -0
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
import { logger } from "../utils/logger.js";
|
|
2
|
+
import { clickFirstMatching } from "./selector-probe.js";
|
|
3
|
+
import { BANNER_CONTEXT_KEYWORDS, ESSENTIAL_CATEGORY_PATTERN, GENERIC_ACCEPT_PHRASES, GENERIC_PREFERENCE_OPEN_PHRASES, GENERIC_REJECT_PHRASES, GENERIC_SAVE_PREFERENCES_PHRASES, PREFERENCE_CENTER_OPEN_SELECTORS, PREFERENCE_CENTER_SAVE_SELECTORS, PREFERENCE_PANEL_CONTEXT_KEYWORDS, PREFERENCE_PANEL_MAX_CONTEXT_CHARS, } from "./constants.js";
|
|
4
|
+
/**
|
|
5
|
+
* Runs INSIDE the page via page.evaluate — must stay a single self-contained
|
|
6
|
+
* function with no imports and no closures over outer scope (Playwright
|
|
7
|
+
* serialises it with Function.prototype.toString()); everything it needs
|
|
8
|
+
* arrives through `args`. Same shape as heuristic-banner.ts, and for the same
|
|
9
|
+
* reason: one function backs both the read-only poll that waits for the panel
|
|
10
|
+
* to render and the click that acts on it, so the two cannot disagree about
|
|
11
|
+
* what they are looking at.
|
|
12
|
+
*/
|
|
13
|
+
export function runPreferenceToggles(args) {
|
|
14
|
+
const { mode, target, savePhrases, shortcutPhrases, contextKeywords, maxContextChars, essentialPattern } = args;
|
|
15
|
+
const essential = new RegExp(essentialPattern, "i");
|
|
16
|
+
const NONE = { toggleCount: 0, essentialCount: 0, stuck: [], applied: [], saveLabel: null, saveClicked: false };
|
|
17
|
+
const normalize = (s) => (s ?? "").replace(/\s+/g, " ").trim().toLowerCase();
|
|
18
|
+
const isVisible = (el) => {
|
|
19
|
+
const style = window.getComputedStyle(el);
|
|
20
|
+
if (style.display === "none" || style.visibility === "hidden")
|
|
21
|
+
return false;
|
|
22
|
+
const rect = el.getBoundingClientRect();
|
|
23
|
+
return rect.width > 0 && rect.height > 0;
|
|
24
|
+
};
|
|
25
|
+
// A category switch is very often a zero-opacity <input> behind a styled
|
|
26
|
+
// label, so the input itself failing isVisible says nothing — what has to
|
|
27
|
+
// be on screen is the thing the visitor actually sees and clicks.
|
|
28
|
+
const isReachable = (el) => {
|
|
29
|
+
let node = el;
|
|
30
|
+
for (let depth = 0; depth < 4 && node; depth++) {
|
|
31
|
+
if (isVisible(node))
|
|
32
|
+
return true;
|
|
33
|
+
node = node.parentElement;
|
|
34
|
+
}
|
|
35
|
+
return false;
|
|
36
|
+
};
|
|
37
|
+
const readState = (el) => {
|
|
38
|
+
if (el instanceof HTMLInputElement)
|
|
39
|
+
return el.checked;
|
|
40
|
+
const checked = el.getAttribute("aria-checked") ?? el.getAttribute("aria-pressed");
|
|
41
|
+
return checked === "true";
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* Everything nearby that might name the category this toggle governs: its
|
|
45
|
+
* own attributes, its label, and the smallest ancestor whose text is short
|
|
46
|
+
* enough to be a heading rather than the category's description paragraph.
|
|
47
|
+
*/
|
|
48
|
+
const describe = (el) => {
|
|
49
|
+
const parts = [
|
|
50
|
+
el.id,
|
|
51
|
+
el.getAttribute("name") ?? "",
|
|
52
|
+
el.getAttribute("value") ?? "",
|
|
53
|
+
el.getAttribute("aria-label") ?? "",
|
|
54
|
+
el.getAttribute("data-category") ?? "",
|
|
55
|
+
el.getAttribute("data-purpose") ?? "",
|
|
56
|
+
typeof el.className === "string" ? el.className : "",
|
|
57
|
+
];
|
|
58
|
+
const labelledBy = el.getAttribute("aria-labelledby");
|
|
59
|
+
if (labelledBy)
|
|
60
|
+
parts.push(document.getElementById(labelledBy)?.textContent ?? "");
|
|
61
|
+
if (el.id)
|
|
62
|
+
parts.push(document.querySelector(`label[for="${CSS.escape(el.id)}"]`)?.textContent ?? "");
|
|
63
|
+
let node = el.parentElement;
|
|
64
|
+
for (let depth = 0; depth < 5 && node; depth++) {
|
|
65
|
+
const text = normalize(node.textContent);
|
|
66
|
+
if (text.length > 0 && text.length <= 120) {
|
|
67
|
+
parts.push(text);
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
70
|
+
node = node.parentElement;
|
|
71
|
+
}
|
|
72
|
+
return normalize(parts.join(" "));
|
|
73
|
+
};
|
|
74
|
+
/**
|
|
75
|
+
* A toggle counts as a consent category only when a container SMALL enough
|
|
76
|
+
* to be the panel itself names cookies or consent.
|
|
77
|
+
*
|
|
78
|
+
* Both halves are load-bearing. A marketing form's "I agree to the Terms
|
|
79
|
+
* and Privacy Policy" checkbox is why "privacy" is not one of the keywords
|
|
80
|
+
* here; the page-level <main> that holds both that form and the cookie
|
|
81
|
+
* banner is why the size bound exists, since it satisfies any keyword going
|
|
82
|
+
* via the banner it contains. Without them the toggle set picks up
|
|
83
|
+
* unrelated form controls, the panel is computed as the smallest element
|
|
84
|
+
* containing all of them — the whole page — and the save-button search is
|
|
85
|
+
* scoped to the whole page with it.
|
|
86
|
+
*/
|
|
87
|
+
const hasContext = (el) => {
|
|
88
|
+
let node = el;
|
|
89
|
+
for (let depth = 0; depth < 12 && node; depth++) {
|
|
90
|
+
const text = normalize(node.textContent);
|
|
91
|
+
if (text.length <= maxContextChars && contextKeywords.some((k) => text.includes(k)))
|
|
92
|
+
return true;
|
|
93
|
+
node = node.parentElement;
|
|
94
|
+
}
|
|
95
|
+
return false;
|
|
96
|
+
};
|
|
97
|
+
const all = Array.from(document.querySelectorAll("input[type='checkbox'], [role='switch'], [aria-checked], button[aria-pressed]")).filter((el) => isReachable(el) && hasContext(el));
|
|
98
|
+
if (all.length === 0)
|
|
99
|
+
return NONE;
|
|
100
|
+
const essentialToggles = all.filter((el) => essential.test(describe(el)));
|
|
101
|
+
// A disabled control is the other way an always-on essential category shows
|
|
102
|
+
// up; either way it is not ours to move.
|
|
103
|
+
const adjustable = all.filter((el) => !essential.test(describe(el)) && !(el instanceof HTMLInputElement && el.disabled) && el.getAttribute("aria-disabled") !== "true");
|
|
104
|
+
if (adjustable.length === 0)
|
|
105
|
+
return { ...NONE, essentialCount: essentialToggles.length };
|
|
106
|
+
// The panel is the smallest element containing every category toggle;
|
|
107
|
+
// that is what scopes the save-button search, so a bare "Save" elsewhere
|
|
108
|
+
// on the page can never be the thing we click.
|
|
109
|
+
let panel = adjustable[0];
|
|
110
|
+
while (panel.parentElement && !adjustable.every((t) => panel.contains(t)))
|
|
111
|
+
panel = panel.parentElement;
|
|
112
|
+
const findSave = () => {
|
|
113
|
+
let scope = panel;
|
|
114
|
+
// The save control frequently lives in a footer that is a sibling of the
|
|
115
|
+
// category list rather than inside it, so widen a few levels — but only
|
|
116
|
+
// after the tighter scopes have been ruled out.
|
|
117
|
+
for (let depth = 0; depth < 8 && scope; depth++) {
|
|
118
|
+
const controls = Array.from(scope.querySelectorAll("button, a, [role='button'], input[type='submit']")).filter(isVisible);
|
|
119
|
+
let best = null;
|
|
120
|
+
for (const el of controls) {
|
|
121
|
+
const text = normalize(el.textContent) || normalize(el.getAttribute("value")) || normalize(el.getAttribute("aria-label"));
|
|
122
|
+
if (shortcutPhrases.includes(text))
|
|
123
|
+
continue;
|
|
124
|
+
const rank = savePhrases.indexOf(text);
|
|
125
|
+
if (rank === -1)
|
|
126
|
+
continue;
|
|
127
|
+
if (!best || rank < best.rank)
|
|
128
|
+
best = { el, rank };
|
|
129
|
+
}
|
|
130
|
+
if (best)
|
|
131
|
+
return best.el;
|
|
132
|
+
scope = scope.parentElement;
|
|
133
|
+
}
|
|
134
|
+
return null;
|
|
135
|
+
};
|
|
136
|
+
const saveEl = findSave();
|
|
137
|
+
const saveLabel = saveEl ? normalize(saveEl.textContent) || normalize(saveEl.getAttribute("aria-label")) : null;
|
|
138
|
+
if (mode !== "toggle") {
|
|
139
|
+
let saveClicked = false;
|
|
140
|
+
if (mode === "save" && saveEl) {
|
|
141
|
+
saveEl.click();
|
|
142
|
+
saveClicked = true;
|
|
143
|
+
}
|
|
144
|
+
return {
|
|
145
|
+
toggleCount: adjustable.length,
|
|
146
|
+
essentialCount: essentialToggles.length,
|
|
147
|
+
stuck: [],
|
|
148
|
+
applied: [],
|
|
149
|
+
saveLabel,
|
|
150
|
+
saveClicked,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
const stuck = [];
|
|
154
|
+
const applied = [];
|
|
155
|
+
for (const el of adjustable) {
|
|
156
|
+
const name = describe(el).slice(0, 40) || "unnamed category";
|
|
157
|
+
if (readState(el) !== target) {
|
|
158
|
+
el.click();
|
|
159
|
+
// A CMP that styles the real input away wires its handler to the label;
|
|
160
|
+
// clicking the input then changes nothing the CMP will read back.
|
|
161
|
+
if (readState(el) !== target && el.id) {
|
|
162
|
+
document.querySelector(`label[for="${CSS.escape(el.id)}"]`)?.click();
|
|
163
|
+
}
|
|
164
|
+
if (readState(el) !== target) {
|
|
165
|
+
el.closest("label")?.click();
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
if (readState(el) === target)
|
|
169
|
+
applied.push(name);
|
|
170
|
+
else
|
|
171
|
+
stuck.push(name);
|
|
172
|
+
}
|
|
173
|
+
return { toggleCount: adjustable.length, essentialCount: essentialToggles.length, stuck, applied, saveLabel, saveClicked: false };
|
|
174
|
+
}
|
|
175
|
+
/** Clicks whatever opens the preference centre; returns what it clicked. */
|
|
176
|
+
async function openPreferenceCenter(page, timeoutMs) {
|
|
177
|
+
const matched = await clickFirstMatching(page, PREFERENCE_CENTER_OPEN_SELECTORS, timeoutMs);
|
|
178
|
+
if (matched)
|
|
179
|
+
return matched;
|
|
180
|
+
// No recognised vendor control. Fall back to text, reusing the banner
|
|
181
|
+
// heuristic's own safeguard: the settings link only counts when it sits in
|
|
182
|
+
// a container that reads like a cookie notice AND holds an accept/reject
|
|
183
|
+
// control, so a page's unrelated "Settings" link is never clicked.
|
|
184
|
+
return await page
|
|
185
|
+
.evaluate(({ openPhrases, acceptPhrases, rejectPhrases, contextKeywords }) => {
|
|
186
|
+
const normalize = (s) => (s ?? "").replace(/\s+/g, " ").trim().toLowerCase();
|
|
187
|
+
const isVisible = (el) => {
|
|
188
|
+
const style = window.getComputedStyle(el);
|
|
189
|
+
if (style.display === "none" || style.visibility === "hidden" || Number(style.opacity) === 0)
|
|
190
|
+
return false;
|
|
191
|
+
const rect = el.getBoundingClientRect();
|
|
192
|
+
return rect.width > 0 && rect.height > 0;
|
|
193
|
+
};
|
|
194
|
+
// Same shadow-root walk as heuristic-banner.ts, and inlined for the
|
|
195
|
+
// same reason: this function is serialised into the page, so it can
|
|
196
|
+
// hold no import and close over nothing.
|
|
197
|
+
const collect = () => {
|
|
198
|
+
const found = [];
|
|
199
|
+
const walk = (root, depth) => {
|
|
200
|
+
for (const el of Array.from(root.querySelectorAll("button, a, [role='button']")))
|
|
201
|
+
found.push(el);
|
|
202
|
+
if (depth >= 5)
|
|
203
|
+
return;
|
|
204
|
+
for (const el of Array.from(root.querySelectorAll("*"))) {
|
|
205
|
+
const shadow = el.shadowRoot;
|
|
206
|
+
if (shadow)
|
|
207
|
+
walk(shadow, depth + 1);
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
walk(document, 0);
|
|
211
|
+
return found;
|
|
212
|
+
};
|
|
213
|
+
const climb = (node) => node.parentElement ?? (node.getRootNode().host ?? null);
|
|
214
|
+
const controls = collect().filter(isVisible);
|
|
215
|
+
const answers = controls.filter((el) => {
|
|
216
|
+
const text = normalize(el.textContent);
|
|
217
|
+
return acceptPhrases.includes(text) || rejectPhrases.includes(text);
|
|
218
|
+
});
|
|
219
|
+
if (answers.length === 0)
|
|
220
|
+
return null;
|
|
221
|
+
// A link that leaves the page is not a preference centre, it is a
|
|
222
|
+
// different page that may or may not have one. factorial.es puts
|
|
223
|
+
// "Manage cookies" (an <a href="/cookies" target="_blank">) in its
|
|
224
|
+
// banner text and "Customize" (a button that reveals the toggles
|
|
225
|
+
// already sitting hidden in the banner) among its buttons; ranking on
|
|
226
|
+
// phrase specificity alone picks the link, lands on a page with no
|
|
227
|
+
// toggles on it, and reports the refusal as undrivable on a banner
|
|
228
|
+
// that can be refused. Opening in place is the stronger signal than
|
|
229
|
+
// any wording, so it is compared first.
|
|
230
|
+
const navigatesAway = (el) => {
|
|
231
|
+
if (el.tagName !== "A")
|
|
232
|
+
return false;
|
|
233
|
+
const anchor = el;
|
|
234
|
+
if ((anchor.getAttribute("target") ?? "").toLowerCase() === "_blank")
|
|
235
|
+
return true;
|
|
236
|
+
const raw = (anchor.getAttribute("href") ?? "").trim();
|
|
237
|
+
if (raw === "" || raw.startsWith("#") || /^javascript:/i.test(raw))
|
|
238
|
+
return false;
|
|
239
|
+
try {
|
|
240
|
+
return new URL(anchor.href).pathname !== window.location.pathname;
|
|
241
|
+
}
|
|
242
|
+
catch {
|
|
243
|
+
return false;
|
|
244
|
+
}
|
|
245
|
+
};
|
|
246
|
+
let best = null;
|
|
247
|
+
for (const el of controls) {
|
|
248
|
+
const text = normalize(el.textContent);
|
|
249
|
+
const rank = openPhrases.indexOf(text);
|
|
250
|
+
if (rank === -1)
|
|
251
|
+
continue;
|
|
252
|
+
// Same container test as heuristic-banner.ts: an ancestor that both
|
|
253
|
+
// mentions cookies and contains a real accept/reject control.
|
|
254
|
+
let node = el;
|
|
255
|
+
let contextOk = false;
|
|
256
|
+
let hasAnswer = false;
|
|
257
|
+
for (let depth = 0; depth < 8 && node && !(contextOk && hasAnswer); depth++) {
|
|
258
|
+
const nodeText = normalize(node.textContent);
|
|
259
|
+
if (!contextOk && contextKeywords.some((k) => nodeText.includes(k)))
|
|
260
|
+
contextOk = true;
|
|
261
|
+
if (!hasAnswer && answers.some((a) => node.contains(a)))
|
|
262
|
+
hasAnswer = true;
|
|
263
|
+
node = climb(node);
|
|
264
|
+
}
|
|
265
|
+
if (!contextOk || !hasAnswer)
|
|
266
|
+
continue;
|
|
267
|
+
const away = navigatesAway(el);
|
|
268
|
+
if (!best || (best.away && !away) || (best.away === away && rank < best.rank)) {
|
|
269
|
+
best = { el, rank, away };
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
if (!best)
|
|
273
|
+
return null;
|
|
274
|
+
const label = normalize(best.el.textContent);
|
|
275
|
+
best.el.click();
|
|
276
|
+
return label;
|
|
277
|
+
}, {
|
|
278
|
+
openPhrases: GENERIC_PREFERENCE_OPEN_PHRASES,
|
|
279
|
+
acceptPhrases: GENERIC_ACCEPT_PHRASES,
|
|
280
|
+
rejectPhrases: GENERIC_REJECT_PHRASES,
|
|
281
|
+
contextKeywords: BANNER_CONTEXT_KEYWORDS,
|
|
282
|
+
})
|
|
283
|
+
.catch((err) => {
|
|
284
|
+
logger.debug("Preference-centre open heuristic failed", err);
|
|
285
|
+
return null;
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Commits the toggles. The vendor's own save button is tried first: its text
|
|
290
|
+
* can be anything ("Allow selection", a localised string), and matching the
|
|
291
|
+
* element beats guessing at the wording.
|
|
292
|
+
*/
|
|
293
|
+
async function savePreferences(page, timeoutMs) {
|
|
294
|
+
const matched = await clickFirstMatching(page, PREFERENCE_CENTER_SAVE_SELECTORS, timeoutMs);
|
|
295
|
+
if (matched)
|
|
296
|
+
return matched;
|
|
297
|
+
const viaText = await page
|
|
298
|
+
.evaluate(runPreferenceToggles, {
|
|
299
|
+
mode: "save",
|
|
300
|
+
target: false,
|
|
301
|
+
savePhrases: GENERIC_SAVE_PREFERENCES_PHRASES,
|
|
302
|
+
shortcutPhrases: [...GENERIC_ACCEPT_PHRASES, ...GENERIC_REJECT_PHRASES],
|
|
303
|
+
contextKeywords: PREFERENCE_PANEL_CONTEXT_KEYWORDS,
|
|
304
|
+
maxContextChars: PREFERENCE_PANEL_MAX_CONTEXT_CHARS,
|
|
305
|
+
essentialPattern: ESSENTIAL_CATEGORY_PATTERN.source,
|
|
306
|
+
})
|
|
307
|
+
.catch((err) => {
|
|
308
|
+
logger.debug("Preference-centre save heuristic failed", err);
|
|
309
|
+
return null;
|
|
310
|
+
});
|
|
311
|
+
return viaText?.saveClicked ? `"${viaText.saveLabel}"` : null;
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* Opens the preference centre and answers it in `direction`.
|
|
315
|
+
*
|
|
316
|
+
* Called only after the vendor API, the vendor buttons and the accept/reject
|
|
317
|
+
* text heuristic have all failed, so a false return here means the banner
|
|
318
|
+
* genuinely could not be answered — which is what the report should say.
|
|
319
|
+
*/
|
|
320
|
+
export async function drivePreferenceCenter(page, direction, timeoutMs) {
|
|
321
|
+
const opened = await openPreferenceCenter(page, timeoutMs);
|
|
322
|
+
if (!opened)
|
|
323
|
+
return { succeeded: false, detail: "no preference-centre control could be reached" };
|
|
324
|
+
const toggleArgs = {
|
|
325
|
+
target: direction === "accept",
|
|
326
|
+
savePhrases: GENERIC_SAVE_PREFERENCES_PHRASES,
|
|
327
|
+
// Never let an accept-all/reject-all shortcut be mistaken for the save
|
|
328
|
+
// control: clicking one discards the toggle states we just set.
|
|
329
|
+
shortcutPhrases: [...GENERIC_ACCEPT_PHRASES, ...GENERIC_REJECT_PHRASES],
|
|
330
|
+
contextKeywords: PREFERENCE_PANEL_CONTEXT_KEYWORDS,
|
|
331
|
+
maxContextChars: PREFERENCE_PANEL_MAX_CONTEXT_CHARS,
|
|
332
|
+
essentialPattern: ESSENTIAL_CATEGORY_PATTERN.source,
|
|
333
|
+
};
|
|
334
|
+
// The panel animates in, so the toggles are not in the DOM the instant the
|
|
335
|
+
// settings link is clicked. Poll rather than sleep, and only act once both
|
|
336
|
+
// halves of the interaction — the toggles and something to commit them
|
|
337
|
+
// with — are actually present.
|
|
338
|
+
const deadline = Date.now() + Math.min(6000, timeoutMs);
|
|
339
|
+
let inspected = null;
|
|
340
|
+
while (Date.now() < deadline) {
|
|
341
|
+
inspected = await page.evaluate(runPreferenceToggles, { ...toggleArgs, mode: "inspect" }).catch(() => null);
|
|
342
|
+
if (inspected && inspected.toggleCount > 0)
|
|
343
|
+
break;
|
|
344
|
+
await page.waitForTimeout(200);
|
|
345
|
+
}
|
|
346
|
+
if (!inspected || inspected.toggleCount === 0) {
|
|
347
|
+
return { succeeded: false, detail: `opened the preference centre via ${opened} but found no category toggles in it` };
|
|
348
|
+
}
|
|
349
|
+
const result = await page.evaluate(runPreferenceToggles, { ...toggleArgs, mode: "toggle" }).catch((err) => {
|
|
350
|
+
logger.debug("Preference-centre toggle pass failed", err);
|
|
351
|
+
return null;
|
|
352
|
+
});
|
|
353
|
+
if (!result)
|
|
354
|
+
return { succeeded: false, detail: `opened the preference centre via ${opened} but could not set its toggles` };
|
|
355
|
+
const state = direction === "accept" ? "on" : "off";
|
|
356
|
+
const summary = `opened the preference centre via ${opened}, set ${result.applied.length}/${result.toggleCount} non-essential categories ${state}`;
|
|
357
|
+
if (result.stuck.length > 0) {
|
|
358
|
+
return { succeeded: false, detail: `${summary}; ${result.stuck.length} would not move (${result.stuck.join(", ")})` };
|
|
359
|
+
}
|
|
360
|
+
const saved = await savePreferences(page, timeoutMs);
|
|
361
|
+
if (!saved) {
|
|
362
|
+
// Refusing to claim success here is the point: the toggles are set but
|
|
363
|
+
// nothing has been submitted, so the site was never actually told.
|
|
364
|
+
return { succeeded: false, detail: `${summary}, but no save control was found to submit them` };
|
|
365
|
+
}
|
|
366
|
+
return { succeeded: true, detail: `${summary}, then clicked ${saved}` };
|
|
367
|
+
}
|
|
368
|
+
//# sourceMappingURL=preference-center.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preference-center.js","sourceRoot":"","sources":["../../src/evidence/preference-center.ts"],"names":[],"mappings":"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"}
|