@duckduckgo/autoconsent 16.47.0 → 16.49.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/.agents/skills/optimize-button-patterns/SKILL.md +145 -0
- package/.agents/skills/oxylabs-testing/SKILL.md +1 -1
- package/.claude/skills/optimize-button-patterns/SKILL.md +7 -0
- package/AGENTS.md +5 -0
- package/CHANGELOG.md +56 -0
- package/data/Readme.md +6 -0
- package/data/labelled-button-texts.csv +7006 -0
- package/dist/addon-firefox/background.bundle.js +2 -0
- package/dist/addon-firefox/compact-rules.json +1 -1
- package/dist/addon-firefox/content.bundle.js +6 -7
- package/dist/addon-firefox/manifest.json +1 -1
- package/dist/addon-firefox/rule-index.json +1 -1
- package/dist/addon-firefox/rules.json +1 -1
- package/dist/addon-mv3/background.bundle.js +2 -0
- package/dist/addon-mv3/compact-rules.json +1 -1
- package/dist/addon-mv3/content.bundle.js +6 -7
- package/dist/addon-mv3/manifest.json +1 -1
- package/dist/addon-mv3/rule-index.json +1 -1
- package/dist/addon-mv3/rules.json +1 -1
- package/dist/autoconsent.cjs.js +6 -7
- package/dist/autoconsent.esm.js +6 -7
- package/dist/autoconsent.playwright.js +6 -7
- package/dist/autoconsent.standalone.js +7 -8
- package/dist/types/eval-snippets.d.ts +1 -0
- package/lib/eval-snippets.ts +3 -0
- package/lib/heuristic-patterns.ts +4 -8
- package/package.json +3 -1
- package/rules/autoconsent/borlabs.json +5 -0
- package/rules/autoconsent/burpee-com.json +13 -7
- package/rules/autoconsent/consentmo.json +51 -7
- package/rules/autoconsent/cookie-popup-close-cookies.json +37 -0
- package/rules/autoconsent/cookieconsent2.json +6 -1
- package/rules/autoconsent/costco.json +27 -0
- package/rules/autoconsent/datasign.json +85 -0
- package/rules/autoconsent/ensembleartsphilly.json +33 -0
- package/rules/autoconsent/evium.de.json +33 -0
- package/rules/autoconsent/iubenda.json +13 -7
- package/rules/autoconsent/pandectes.json +48 -15
- package/rules/autoconsent/passle.json +32 -0
- package/rules/autoconsent/pmc-privacy-banner.json +9 -0
- package/rules/autoconsent/primeconsent.json +39 -0
- package/rules/autoconsent/shinden.pl.json +34 -0
- package/rules/autoconsent/tealium.json +1 -1
- package/rules/autoconsent/tri-table.json +14 -0
- package/rules/autoconsent/truevault-polaris.json +63 -0
- package/rules/autoconsent/webflow.json +1 -1
- package/rules/autoconsent/wix.json +90 -8
- package/rules/compact-rules.json +1 -1
- package/rules/rule-index.json +1 -1
- package/rules/rules.json +1 -1
- package/scripts/benchmark-button-classification.ts +75 -0
- package/scripts/button-text-benchmark.ts +102 -0
- package/tests/borlabs.spec.ts +7 -3
- package/tests/burpee-com.spec.ts +10 -4
- package/tests/consentmo.spec.ts +1 -0
- package/tests/cookie-popup-close-cookies.spec.ts +10 -0
- package/tests/cookieconsent2.spec.ts +6 -1
- package/tests/costco.spec.ts +5 -0
- package/tests/datasign.spec.ts +8 -0
- package/tests/ensembleartsphilly.spec.ts +6 -0
- package/tests/evium.de.spec.ts +5 -0
- package/tests/iubenda.spec.ts +6 -0
- package/tests/pandectes.spec.ts +20 -1
- package/tests/passle.spec.ts +8 -0
- package/tests/pmc-privacy-banner.spec.ts +8 -0
- package/tests/primeconsent.spec.ts +11 -0
- package/tests/shinden.pl.spec.ts +5 -0
- package/tests/tealium.spec.ts +8 -6
- package/tests/tri-table.spec.ts +5 -0
- package/tests/truevault-polaris.spec.ts +9 -0
- package/tests/webflow.spec.ts +7 -1
- package/tests/wix.spec.ts +8 -1
- package/tests-wtr/heuristics/button-classification-accuracy.test.ts +41 -0
- package/tests-wtr/heuristics/heuristics-utils.test.ts +9 -0
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Benchmark classifyButtonTextRegex against labelled button texts (exact label match, occurrence-weighted).
|
|
3
|
+
* New labelled rows are produced by the tracker-radar-collector post-processing scripts.
|
|
4
|
+
*/
|
|
5
|
+
import fs from 'fs';
|
|
6
|
+
import path from 'path';
|
|
7
|
+
import { parseArgs } from 'util';
|
|
8
|
+
import { classifyButtonTextRegex } from '../lib/heuristics';
|
|
9
|
+
import { buildLabelBenchmarks, classifyRows, formatExample, LabelBenchmark, parseButtonTextCsv, pct } from './button-text-benchmark';
|
|
10
|
+
|
|
11
|
+
const DEFAULT_CSV_PATH = path.join(__dirname, '../data/labelled-button-texts.csv');
|
|
12
|
+
const TOP_FAILURES = 25;
|
|
13
|
+
const USAGE = `Usage: npm run benchmark-buttons -- [--input <path>] [--top <n>]
|
|
14
|
+
|
|
15
|
+
Benchmark classifyButtonTextRegex against labelled button texts (exact label match, occurrence-weighted).
|
|
16
|
+
|
|
17
|
+
-i, --input <path> path to labelled button text CSV (default: ${path.relative(process.cwd(), DEFAULT_CSV_PATH)})
|
|
18
|
+
--top <n> number of failure examples to print per label (default: ${TOP_FAILURES})
|
|
19
|
+
-h, --help show this help`;
|
|
20
|
+
|
|
21
|
+
const { values } = parseArgs({
|
|
22
|
+
options: {
|
|
23
|
+
input: { type: 'string', short: 'i', default: DEFAULT_CSV_PATH },
|
|
24
|
+
top: { type: 'string', default: String(TOP_FAILURES) },
|
|
25
|
+
help: { type: 'boolean', short: 'h' },
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
if (values.help) {
|
|
29
|
+
console.log(USAGE);
|
|
30
|
+
process.exit(0);
|
|
31
|
+
}
|
|
32
|
+
const opts = { input: values.input, top: parseInt(values.top, 10) };
|
|
33
|
+
|
|
34
|
+
function printLabelBenchmark(b: LabelBenchmark) {
|
|
35
|
+
console.log(`\n ${b.label}`);
|
|
36
|
+
console.log(
|
|
37
|
+
` correctly labelled: ${b.rowCorrect}/${b.rowSupport} rows (${pct(b.rowCorrect, b.rowSupport)}), ${b.weightedCorrect}/${b.weightedSupport} weighted (${pct(b.weightedCorrect, b.weightedSupport)})`,
|
|
38
|
+
);
|
|
39
|
+
console.log(
|
|
40
|
+
` false positives (predicted ${b.label}, labelled otherwise): ${b.falsePositiveExamples.length} rows, ${b.weightedFalsePositives} weighted`,
|
|
41
|
+
);
|
|
42
|
+
for (const [title, examples] of [
|
|
43
|
+
['top false positives', b.falsePositiveExamples],
|
|
44
|
+
['top missed', b.missedExamples],
|
|
45
|
+
] as const) {
|
|
46
|
+
console.log(` ${title}:${examples.length === 0 ? ' (none)' : ''}`);
|
|
47
|
+
for (const example of examples.slice(0, opts.top)) {
|
|
48
|
+
console.log(` ${formatExample(example)}`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function main() {
|
|
54
|
+
const inputPath = path.resolve(opts.input);
|
|
55
|
+
const rows = parseButtonTextCsv(fs.readFileSync(inputPath, 'utf8'));
|
|
56
|
+
if (rows.length === 0) {
|
|
57
|
+
console.error('no labelled rows found in', inputPath);
|
|
58
|
+
process.exit(1);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const benchmarks = buildLabelBenchmarks(classifyRows(rows, classifyButtonTextRegex));
|
|
62
|
+
const total = (key: 'rowSupport' | 'weightedSupport' | 'rowCorrect' | 'weightedCorrect' | 'weightedFalsePositives') =>
|
|
63
|
+
benchmarks.reduce((sum, b) => sum + b[key], 0);
|
|
64
|
+
|
|
65
|
+
console.log(`Input: ${inputPath} (${rows.length} labelled rows)`);
|
|
66
|
+
console.log('\nclassifyButtonTextRegex benchmark (excluding other)');
|
|
67
|
+
console.log(
|
|
68
|
+
` correctly labelled: ${total('rowCorrect')}/${total('rowSupport')} rows, ${total('weightedCorrect')}/${total('weightedSupport')} weighted (${pct(total('weightedCorrect'), total('weightedSupport'))})`,
|
|
69
|
+
);
|
|
70
|
+
console.log(` false positives (weighted): ${total('weightedFalsePositives')}`);
|
|
71
|
+
console.log(` missed (weighted): ${total('weightedSupport') - total('weightedCorrect')}`);
|
|
72
|
+
benchmarks.forEach(printLabelBenchmark);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
main();
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helpers for scoring the button classifier against data/labelled-button-texts.csv.
|
|
3
|
+
* Shared by the WTR accuracy test and scripts/benchmark-button-classification.ts, so must not use Node or DOM APIs.
|
|
4
|
+
*/
|
|
5
|
+
// browser ESM build, so the same import works in WTR and in Node
|
|
6
|
+
import { parse } from 'csv-parse/browser/esm/sync';
|
|
7
|
+
import { ButtonRegexClassification } from '../lib/types';
|
|
8
|
+
|
|
9
|
+
const VALID_LABELS: readonly ButtonRegexClassification[] = ['settings', 'accept', 'reject', 'acknowledge', 'other'];
|
|
10
|
+
|
|
11
|
+
export const BENCHMARK_LABELS = VALID_LABELS.filter((label) => label !== 'other');
|
|
12
|
+
|
|
13
|
+
export type ButtonTextRow = {
|
|
14
|
+
buttonText: string;
|
|
15
|
+
occurences: number;
|
|
16
|
+
label: ButtonRegexClassification;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export type ClassifiedButtonTextRow = ButtonTextRow & {
|
|
20
|
+
predicted: ButtonRegexClassification;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export type LabelBenchmark = {
|
|
24
|
+
label: ButtonRegexClassification;
|
|
25
|
+
rowSupport: number;
|
|
26
|
+
weightedSupport: number;
|
|
27
|
+
rowCorrect: number;
|
|
28
|
+
weightedCorrect: number;
|
|
29
|
+
weightedFalsePositives: number;
|
|
30
|
+
falsePositiveExamples: ClassifiedButtonTextRow[];
|
|
31
|
+
missedExamples: ClassifiedButtonTextRow[];
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Parse labelled-button-texts.csv (columns: button_text, occurences, label), keeping only rows with a valid label.
|
|
36
|
+
* Options match the reader in tracker-radar-collector, which also writes this file.
|
|
37
|
+
*/
|
|
38
|
+
export function parseButtonTextCsv(content: string): ButtonTextRow[] {
|
|
39
|
+
const records: Record<string, string>[] = parse(content, {
|
|
40
|
+
columns: true,
|
|
41
|
+
skip_empty_lines: true,
|
|
42
|
+
relax_column_count: true,
|
|
43
|
+
trim: true,
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
const rows: ButtonTextRow[] = [];
|
|
47
|
+
for (const record of records) {
|
|
48
|
+
const buttonText = record.button_text;
|
|
49
|
+
const occurences = Number.parseInt(record.occurences, 10);
|
|
50
|
+
const label = record.label?.toLowerCase() as ButtonRegexClassification;
|
|
51
|
+
if (!buttonText || Number.isNaN(occurences) || !VALID_LABELS.includes(label)) {
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
rows.push({ buttonText, occurences, label });
|
|
55
|
+
}
|
|
56
|
+
return rows;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function classifyRows(
|
|
60
|
+
rows: ButtonTextRow[],
|
|
61
|
+
classifier: (buttonText: string) => ButtonRegexClassification,
|
|
62
|
+
): ClassifiedButtonTextRow[] {
|
|
63
|
+
return rows.map((row) => ({ ...row, predicted: classifier(row.buttonText) }));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function sumOccurences(rows: ButtonTextRow[]): number {
|
|
67
|
+
return rows.reduce((sum, row) => sum + row.occurences, 0);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function byOccurence(a: ButtonTextRow, b: ButtonTextRow): number {
|
|
71
|
+
return b.occurences - a.occurences || a.buttonText.localeCompare(b.buttonText);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Per-label stats, in BENCHMARK_LABELS order. Rows labelled 'other' are excluded, matching the original collector benchmark.
|
|
76
|
+
*/
|
|
77
|
+
export function buildLabelBenchmarks(results: ClassifiedButtonTextRow[]): LabelBenchmark[] {
|
|
78
|
+
const benchmarkResults = results.filter((r) => r.label !== 'other');
|
|
79
|
+
return BENCHMARK_LABELS.map((label) => {
|
|
80
|
+
const support = benchmarkResults.filter((r) => r.label === label);
|
|
81
|
+
const correct = support.filter((r) => r.predicted === label);
|
|
82
|
+
const falsePositives = benchmarkResults.filter((r) => r.predicted === label && r.label !== label);
|
|
83
|
+
return {
|
|
84
|
+
label,
|
|
85
|
+
rowSupport: support.length,
|
|
86
|
+
weightedSupport: sumOccurences(support),
|
|
87
|
+
rowCorrect: correct.length,
|
|
88
|
+
weightedCorrect: sumOccurences(correct),
|
|
89
|
+
weightedFalsePositives: sumOccurences(falsePositives),
|
|
90
|
+
falsePositiveExamples: falsePositives.sort(byOccurence),
|
|
91
|
+
missedExamples: support.filter((r) => r.predicted !== label).sort(byOccurence),
|
|
92
|
+
};
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export function pct(numerator: number, denominator: number): string {
|
|
97
|
+
return denominator === 0 ? 'n/a' : `${((numerator / denominator) * 100).toFixed(1)}%`;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function formatExample(row: ClassifiedButtonTextRow): string {
|
|
101
|
+
return `[${row.label} -> ${row.predicted}] ${JSON.stringify(row.buttonText)} (x${row.occurences})`;
|
|
102
|
+
}
|
package/tests/borlabs.spec.ts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import generateCMPTests from '../playwright/runner';
|
|
2
2
|
|
|
3
|
-
generateCMPTests(
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
generateCMPTests(
|
|
4
|
+
'borlabs',
|
|
5
|
+
['https://www.kesselheld.de/', 'https://cocoon-hotels.com/de/cocoon-salzburg/', 'https://www.headphonecheck.com/'],
|
|
6
|
+
{
|
|
7
|
+
skipRegions: ['US'],
|
|
8
|
+
},
|
|
9
|
+
);
|
package/tests/burpee-com.spec.ts
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
import generateCMPTests from '../playwright/runner';
|
|
2
2
|
|
|
3
3
|
generateCMPTests('burpee.com', [
|
|
4
|
-
|
|
5
|
-
'https://www.
|
|
6
|
-
'https://www.
|
|
7
|
-
'https://www.
|
|
4
|
+
// Decline button
|
|
5
|
+
'https://www.discountsurgical.com/',
|
|
6
|
+
'https://www.lymphrx.com/',
|
|
7
|
+
'https://www.cutmy.co.uk/',
|
|
8
|
+
'https://www.workingperson.com/',
|
|
9
|
+
// Manage Cookies button
|
|
10
|
+
'https://www.hobbies.co.uk/',
|
|
11
|
+
// Allow button only: the banner is hidden
|
|
12
|
+
'https://www.oxfordproducts.com/',
|
|
13
|
+
'https://www.testequipmentdepot.com/',
|
|
8
14
|
]);
|
package/tests/consentmo.spec.ts
CHANGED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import generateCMPTests from '../playwright/runner';
|
|
2
|
+
|
|
3
|
+
generateCMPTests('cookie-popup-close-cookies', [
|
|
4
|
+
'https://catflooringaccessories.com/product/fpb10x3/',
|
|
5
|
+
'https://www.coba.com/',
|
|
6
|
+
'https://www.copely.com/',
|
|
7
|
+
'https://www.movingedge.com/',
|
|
8
|
+
'https://www.arvitec.com/',
|
|
9
|
+
'https://www.cobaflooring.com/',
|
|
10
|
+
]);
|
|
@@ -1,3 +1,8 @@
|
|
|
1
1
|
import generateCMPTests from '../playwright/runner';
|
|
2
2
|
|
|
3
|
-
generateCMPTests('cookieconsent2', [
|
|
3
|
+
generateCMPTests('cookieconsent2', [
|
|
4
|
+
'https://alpha-heidelberg.de/',
|
|
5
|
+
'https://antitec.fi/',
|
|
6
|
+
'https://servero.nl/onze-producten/',
|
|
7
|
+
'https://www.goaexperience.co.uk/guide/festivals-and-events',
|
|
8
|
+
]);
|
package/tests/iubenda.spec.ts
CHANGED
|
@@ -3,3 +3,9 @@ import generateCMPTests from '../playwright/runner';
|
|
|
3
3
|
generateCMPTests('iubenda', ['https://www.rossignol.com/us/', 'https://www.lofficielusa.com/', 'https://www.3bmeteo.com/'], {
|
|
4
4
|
skipRegions: ['AU'],
|
|
5
5
|
});
|
|
6
|
+
|
|
7
|
+
// accept-only banner without reject or customize buttons
|
|
8
|
+
generateCMPTests('iubenda', ['https://www.yogurtland.com/', 'https://www.greenplanet.net/'], {
|
|
9
|
+
skipRegions: ['AU'],
|
|
10
|
+
testSelfTest: false,
|
|
11
|
+
});
|
package/tests/pandectes.spec.ts
CHANGED
|
@@ -3,6 +3,25 @@ import generateCMPTests from '../playwright/runner';
|
|
|
3
3
|
generateCMPTests('pandectes', [
|
|
4
4
|
'https://hollandscountryclothing.co.uk/',
|
|
5
5
|
'https://cluse.com/',
|
|
6
|
-
'https://www.parent.com/',
|
|
7
6
|
'https://swedishstockings.com/', // full-page overlay ("blocking") banner variant
|
|
8
7
|
]);
|
|
8
|
+
|
|
9
|
+
// shadow DOM <pandectes-cmp> banner variant
|
|
10
|
+
generateCMPTests(
|
|
11
|
+
'pandectes',
|
|
12
|
+
[
|
|
13
|
+
'https://enron.com/',
|
|
14
|
+
'https://www.parent.com/',
|
|
15
|
+
'https://wetnwildbeauty.com/',
|
|
16
|
+
'https://www.yakima.com/',
|
|
17
|
+
'https://us.select-sport.com/collections/soccer-balls-training-series',
|
|
18
|
+
],
|
|
19
|
+
{
|
|
20
|
+
testOptIn: false,
|
|
21
|
+
},
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
// shadow DOM variant without a decline button: opt out through preferences
|
|
25
|
+
generateCMPTests('pandectes', ['https://asmc.de/', 'https://crossrope.com/'], {
|
|
26
|
+
testOptIn: false,
|
|
27
|
+
});
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import generateCMPTests from '../playwright/runner';
|
|
2
|
+
|
|
3
|
+
generateCMPTests('passle', [
|
|
4
|
+
'https://technologylaw.fkks.com/post/102jge9/california-requires-browsers-and-mobile-devices-to-support-opt-out-preference-sig',
|
|
5
|
+
'https://www.insurtechworld.org/',
|
|
6
|
+
'https://news.fintech.io/',
|
|
7
|
+
'https://www.bbeb.com/',
|
|
8
|
+
]);
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import generateCMPTests from '../playwright/runner';
|
|
2
|
+
|
|
3
|
+
generateCMPTests('pmc-privacy-banner', [
|
|
4
|
+
'https://www.theverge.com/news/999211/microsoft-surface-mouse-haptic-feedback',
|
|
5
|
+
'https://www.eater.com/',
|
|
6
|
+
'https://www.sbnation.com/',
|
|
7
|
+
'https://punchdrink.com/',
|
|
8
|
+
]);
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import generateCMPTests from '../playwright/runner';
|
|
2
|
+
|
|
3
|
+
generateCMPTests('primeconsent', [
|
|
4
|
+
'https://www.jclewislincoln.com/2025-lincoln-aviator-dimensions',
|
|
5
|
+
'https://jclewisford.net/',
|
|
6
|
+
'https://www.experiencepoint.com/',
|
|
7
|
+
'https://www.valenciaautocenter.com/',
|
|
8
|
+
'https://www.upl-ltd.com/',
|
|
9
|
+
'https://www.cornhuskernissan.com/',
|
|
10
|
+
'https://privacypillar.com/',
|
|
11
|
+
]);
|
package/tests/tealium.spec.ts
CHANGED
|
@@ -2,11 +2,13 @@ import generateCMPTests from '../playwright/runner';
|
|
|
2
2
|
|
|
3
3
|
generateCMPTests('Tealium', [
|
|
4
4
|
// 'https://www.bahn.de/', // uses shadow DOM, see https://app.asana.com/0/1201844467387842/1202635343225979/f
|
|
5
|
-
'https://www.forcepoint.com/',
|
|
6
|
-
'https://www.lufthansa.com/de/en/homepage',
|
|
5
|
+
// 'https://www.forcepoint.com/', // custom region picker must be answered before the Tealium prompt shows
|
|
7
6
|
'http://www.tui.com/',
|
|
8
|
-
'
|
|
9
|
-
'
|
|
10
|
-
'
|
|
11
|
-
'
|
|
7
|
+
'https://www.nsandi.com/',
|
|
8
|
+
'https://www.telenor.com/',
|
|
9
|
+
'https://www.allaboutvision.com/',
|
|
10
|
+
'https://www.chs.net/',
|
|
11
|
+
'https://www.nito.no/',
|
|
12
|
+
'https://www.about.hsbc.pl/pl-pl/hsbc-service-delivery',
|
|
13
|
+
'https://www.hsbc.com/',
|
|
12
14
|
]);
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import generateCMPTests from '../playwright/runner';
|
|
2
|
+
|
|
3
|
+
generateCMPTests('truevault-polaris', [
|
|
4
|
+
'https://www.mcgeeandco.com/products/calvert-brass-tissue-box-cover',
|
|
5
|
+
'https://www.studio-mcgee.com/',
|
|
6
|
+
'https://www.thorne.com/',
|
|
7
|
+
'https://www.crossfit.com/',
|
|
8
|
+
'https://www.philzcoffee.com/',
|
|
9
|
+
]);
|
package/tests/webflow.spec.ts
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
1
|
import generateCMPTests from '../playwright/runner';
|
|
2
2
|
|
|
3
|
-
generateCMPTests('webflow', [
|
|
3
|
+
generateCMPTests('webflow', [
|
|
4
|
+
'https://www.ctgt.ai/research/distillation-censorship-transfer',
|
|
5
|
+
'https://www.dock.io/',
|
|
6
|
+
'https://www.talkabroad.com/',
|
|
7
|
+
'https://www.pocoyo.com/',
|
|
8
|
+
'https://www.storypark.com/',
|
|
9
|
+
]);
|
package/tests/wix.spec.ts
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
1
|
import generateCMPTests from '../playwright/runner';
|
|
2
2
|
|
|
3
|
-
generateCMPTests('wix', [
|
|
3
|
+
generateCMPTests('wix', [
|
|
4
|
+
'https://www.wix.com/',
|
|
5
|
+
'https://www.poconopoolandspallc.com/',
|
|
6
|
+
'https://www.karlswarehousing.com/',
|
|
7
|
+
'https://www.stormkingpw.com/',
|
|
8
|
+
'https://www.iconicoffices.com/',
|
|
9
|
+
'https://www.steigertools.nl/',
|
|
10
|
+
]);
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { expect } from '@esm-bundle/chai';
|
|
2
|
+
import { classifyButtonTextRegex } from '../../lib/heuristics';
|
|
3
|
+
import {
|
|
4
|
+
BENCHMARK_LABELS,
|
|
5
|
+
buildLabelBenchmarks,
|
|
6
|
+
classifyRows,
|
|
7
|
+
formatExample,
|
|
8
|
+
LabelBenchmark,
|
|
9
|
+
parseButtonTextCsv,
|
|
10
|
+
pct,
|
|
11
|
+
} from '../../scripts/button-text-benchmark';
|
|
12
|
+
|
|
13
|
+
// Minimum occurrence-weighted accuracy required per label.
|
|
14
|
+
const MIN_WEIGHTED_ACCURACY = 0.9;
|
|
15
|
+
|
|
16
|
+
describe('classifyButtonTextRegex against labelled button texts', () => {
|
|
17
|
+
let benchmarks: LabelBenchmark[];
|
|
18
|
+
|
|
19
|
+
before(async () => {
|
|
20
|
+
const response = await fetch('/data/labelled-button-texts.csv');
|
|
21
|
+
const rows = parseButtonTextCsv(await response.text());
|
|
22
|
+
expect(rows.length).to.be.greaterThan(0, 'expected labelled rows in the CSV');
|
|
23
|
+
benchmarks = buildLabelBenchmarks(classifyRows(rows, classifyButtonTextRegex));
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
BENCHMARK_LABELS.forEach((label, i) => {
|
|
27
|
+
it(`never predicts "${label}" for a button labelled differently`, () => {
|
|
28
|
+
const examples = benchmarks[i].falsePositiveExamples.slice(0, 10).map(formatExample).join('\n ');
|
|
29
|
+
expect(benchmarks[i].falsePositiveExamples.length).to.equal(0, `false positives for "${label}":\n ${examples}\n`);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it(`classifies >${MIN_WEIGHTED_ACCURACY * 100}% of weighted "${label}" buttons correctly`, () => {
|
|
33
|
+
const { weightedCorrect, weightedSupport } = benchmarks[i];
|
|
34
|
+
expect(weightedSupport).to.be.greaterThan(0, `expected labelled rows for "${label}"`);
|
|
35
|
+
expect(weightedCorrect / weightedSupport).to.be.greaterThan(
|
|
36
|
+
MIN_WEIGHTED_ACCURACY,
|
|
37
|
+
`weighted accuracy for "${label}" was ${pct(weightedCorrect, weightedSupport)} (${weightedCorrect}/${weightedSupport})`,
|
|
38
|
+
);
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
});
|
|
@@ -207,6 +207,7 @@ describe('classifyButtonTextRegex', () => {
|
|
|
207
207
|
expect(classifyButtonTextRegex('Essential Only')).to.equal('reject');
|
|
208
208
|
expect(classifyButtonTextRegex('Only use essential cookies')).to.equal('reject');
|
|
209
209
|
expect(classifyButtonTextRegex('Only accept necessary cookies')).to.equal('reject');
|
|
210
|
+
expect(classifyButtonTextRegex('Essential only cookies')).to.equal('reject');
|
|
210
211
|
});
|
|
211
212
|
|
|
212
213
|
it('does not classify bare essential category labels as reject choices', () => {
|
|
@@ -282,6 +283,14 @@ describe('classifyButtonTextRegex', () => {
|
|
|
282
283
|
it('does not treat revoke links as reject buttons', () => {
|
|
283
284
|
expect(classifyButtonTextRegex('Widerrufen')).to.equal('other');
|
|
284
285
|
});
|
|
286
|
+
|
|
287
|
+
it('classifies accept-and-close buttons as accept', () => {
|
|
288
|
+
expect(classifyButtonTextRegex('Akzeptieren & Schließen')).to.equal('accept');
|
|
289
|
+
expect(classifyButtonTextRegex('Akzeptieren und schließen')).to.equal('accept');
|
|
290
|
+
expect(classifyButtonTextRegex('Zustimmen schliessen')).to.equal('accept');
|
|
291
|
+
expect(classifyButtonTextRegex('Accepter fermer')).to.equal('accept');
|
|
292
|
+
expect(classifyButtonTextRegex('Accepteren sluiten')).to.equal('accept');
|
|
293
|
+
});
|
|
285
294
|
});
|
|
286
295
|
|
|
287
296
|
describe('classifyButtons', () => {
|