@duckduckgo/autoconsent 14.97.0 → 15.0.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/CHANGELOG.md +18 -0
- package/dist/addon-firefox/background.bundle.js +10 -1
- package/dist/addon-firefox/compact-rules.json +1 -1
- package/dist/addon-firefox/content.bundle.js +142 -33
- package/dist/addon-firefox/manifest.json +1 -1
- package/dist/addon-firefox/popup.bundle.js +10 -1
- package/dist/addon-firefox/rules.json +1 -1
- package/dist/addon-mv3/background.bundle.js +10 -1
- package/dist/addon-mv3/compact-rules.json +1 -1
- package/dist/addon-mv3/content.bundle.js +142 -33
- package/dist/addon-mv3/manifest.json +1 -1
- package/dist/addon-mv3/popup.bundle.js +10 -1
- package/dist/addon-mv3/rules.json +1 -1
- package/dist/autoconsent.cjs.js +142 -33
- package/dist/autoconsent.esm.js +142 -33
- package/dist/autoconsent.extra.cjs.js +142 -33
- package/dist/autoconsent.extra.esm.js +142 -33
- package/dist/autoconsent.playwright.js +142 -33
- package/dist/types/cmps/base.d.ts +4 -2
- package/dist/types/heuristic-patterns.d.ts +3 -0
- package/dist/types/heuristics.d.ts +4 -7
- package/dist/types/types.d.ts +18 -2
- package/lib/cmps/base.ts +20 -7
- package/lib/heuristic-patterns.ts +68 -1
- package/lib/heuristics.ts +76 -27
- package/lib/types.ts +20 -2
- package/lib/utils.ts +2 -1
- package/lib/web.ts +3 -1
- package/package.json +1 -1
- package/rules/autoconsent/twitch.json +1 -1
- package/rules/compact-rules.json +1 -1
- package/rules/rules.json +1 -1
- package/tests-wtr/heuristics/get-actionable-popups.html +16 -0
- package/tests-wtr/heuristics/get-actionable-popups.ts +85 -6
- package/tests-wtr/heuristics/heuristic-cmp.html +69 -0
- package/tests-wtr/heuristics/heuristic-cmp.ts +178 -0
- package/tests-wtr/heuristics/heuristics-utils.test.ts +96 -28
- package/tests-wtr/lifecycle/find-cmp.html +14 -0
- package/tests-wtr/lifecycle/find-cmp.ts +44 -2
|
@@ -92,5 +92,21 @@
|
|
|
92
92
|
<button>Accept All</button>
|
|
93
93
|
<button>Reject All</button>
|
|
94
94
|
</dialog>
|
|
95
|
+
|
|
96
|
+
<div id="popup-accept-only" class="popup">
|
|
97
|
+
<p>We use cookies to improve your experience.</p>
|
|
98
|
+
<button>Accept All</button>
|
|
99
|
+
</div>
|
|
100
|
+
|
|
101
|
+
<div id="popup-acknowledge-only" class="popup">
|
|
102
|
+
<p>We use cookies to improve your experience.</p>
|
|
103
|
+
<button>OK</button>
|
|
104
|
+
</div>
|
|
105
|
+
|
|
106
|
+
<div id="popup-multiple-reject" class="popup">
|
|
107
|
+
<p>We use cookies to improve your experience.</p>
|
|
108
|
+
<button>Reject All</button>
|
|
109
|
+
<button>Deny</button>
|
|
110
|
+
</div>
|
|
95
111
|
</body>
|
|
96
112
|
</html>
|
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
import { expect } from '@esm-bundle/chai';
|
|
2
2
|
import { getActionablePopups } from '../../lib/heuristics';
|
|
3
|
+
import { ButtonData, PopupHandlingModes } from '../../lib/types';
|
|
4
|
+
|
|
5
|
+
function rejectButtons(buttons: ButtonData[]) {
|
|
6
|
+
return buttons.filter((b) => b.regexClassification === 'reject');
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function nonRejectButtons(buttons: ButtonData[]) {
|
|
10
|
+
return buttons.filter((b) => b.regexClassification !== 'reject');
|
|
11
|
+
}
|
|
3
12
|
|
|
4
13
|
// must be run from get-actionable-popups.html
|
|
5
14
|
describe('getActionablePopups', () => {
|
|
@@ -21,8 +30,8 @@ describe('getActionablePopups', () => {
|
|
|
21
30
|
const popups = getActionablePopups();
|
|
22
31
|
|
|
23
32
|
expect(popups.length).to.equal(1);
|
|
24
|
-
expect(popups[0].
|
|
25
|
-
expect(popups[0].
|
|
33
|
+
expect(rejectButtons(popups[0].buttons)).to.have.length(1);
|
|
34
|
+
expect(rejectButtons(popups[0].buttons)[0].text).to.equal('Reject All');
|
|
26
35
|
});
|
|
27
36
|
|
|
28
37
|
describe('does not match non-cookie popups', () => {
|
|
@@ -34,13 +43,83 @@ describe('getActionablePopups', () => {
|
|
|
34
43
|
expect(popups.length).to.equal(0);
|
|
35
44
|
});
|
|
36
45
|
|
|
37
|
-
it('ignores popup
|
|
46
|
+
it('ignores popup with accept and settings buttons', () => {
|
|
38
47
|
showPopup('popup-no-reject');
|
|
39
48
|
|
|
40
|
-
const popups = getActionablePopups();
|
|
49
|
+
const popups = getActionablePopups(PopupHandlingModes.Tier2);
|
|
50
|
+
|
|
51
|
+
expect(popups.length).to.equal(0);
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
describe('tier button candidates', () => {
|
|
56
|
+
it('finds accept-only popup with no reject buttons', () => {
|
|
57
|
+
showPopup('popup-accept-only');
|
|
58
|
+
|
|
59
|
+
const popups = getActionablePopups(PopupHandlingModes.Tier2);
|
|
60
|
+
|
|
61
|
+
expect(popups.length).to.equal(1);
|
|
62
|
+
expect(rejectButtons(popups[0].buttons)).to.have.length(0);
|
|
63
|
+
expect(popups[0].buttons.filter((b) => b.regexClassification === 'accept')).to.have.length(1);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('finds acknowledge-only popup with no reject buttons', () => {
|
|
67
|
+
showPopup('popup-acknowledge-only');
|
|
68
|
+
|
|
69
|
+
const popups = getActionablePopups(PopupHandlingModes.Tier2);
|
|
70
|
+
|
|
71
|
+
expect(popups.length).to.equal(1);
|
|
72
|
+
expect(rejectButtons(popups[0].buttons)).to.have.length(0);
|
|
73
|
+
expect(popups[0].buttons.filter((b) => b.regexClassification === 'acknowledge')).to.have.length(1);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('finds popup with multiple reject buttons', () => {
|
|
77
|
+
showPopup('popup-multiple-reject');
|
|
78
|
+
|
|
79
|
+
const popups = getActionablePopups(PopupHandlingModes.Tier2);
|
|
80
|
+
|
|
81
|
+
expect(popups.length).to.equal(1);
|
|
82
|
+
expect(popups[0].regexClassification).to.equal(PopupHandlingModes.Reject);
|
|
83
|
+
expect(rejectButtons(popups[0].buttons)).to.have.length(2);
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
describe('mode filtering', () => {
|
|
88
|
+
it('does not return accept-only popup when mode is Reject', () => {
|
|
89
|
+
showPopup('popup-accept-only');
|
|
90
|
+
|
|
91
|
+
const popups = getActionablePopups(PopupHandlingModes.Reject);
|
|
92
|
+
|
|
93
|
+
expect(popups.length).to.equal(0);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('returns acknowledge-only popup when mode is Tier1', () => {
|
|
97
|
+
showPopup('popup-acknowledge-only');
|
|
98
|
+
|
|
99
|
+
const popups = getActionablePopups(PopupHandlingModes.Tier1);
|
|
100
|
+
|
|
101
|
+
expect(popups.length).to.equal(1);
|
|
102
|
+
expect(popups[0].regexClassification).to.equal(PopupHandlingModes.Tier1);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('does not return accept-only popup when mode is Tier1', () => {
|
|
106
|
+
showPopup('popup-accept-only');
|
|
107
|
+
|
|
108
|
+
const popups = getActionablePopups(PopupHandlingModes.Tier1);
|
|
41
109
|
|
|
42
110
|
expect(popups.length).to.equal(0);
|
|
43
111
|
});
|
|
112
|
+
|
|
113
|
+
it('prefers reject popup when reject and accept popups are visible', () => {
|
|
114
|
+
showPopup('popup-reject-all');
|
|
115
|
+
showPopup('popup-accept-only');
|
|
116
|
+
|
|
117
|
+
const popups = getActionablePopups(PopupHandlingModes.Tier2);
|
|
118
|
+
|
|
119
|
+
expect(popups.length).to.equal(2);
|
|
120
|
+
expect(popups[0].regexClassification).to.equal(PopupHandlingModes.Reject);
|
|
121
|
+
expect(popups[1].regexClassification).to.equal(PopupHandlingModes.Tier2);
|
|
122
|
+
});
|
|
44
123
|
});
|
|
45
124
|
|
|
46
125
|
describe('positioning', () => {
|
|
@@ -112,8 +191,8 @@ describe('getActionablePopups', () => {
|
|
|
112
191
|
const popups = getActionablePopups();
|
|
113
192
|
|
|
114
193
|
expect(popups.length).to.equal(1);
|
|
115
|
-
expect(popups[0].
|
|
116
|
-
expect(popups[0].
|
|
194
|
+
expect(rejectButtons(popups[0].buttons)).to.have.length(1);
|
|
195
|
+
expect(nonRejectButtons(popups[0].buttons)).to.have.length(3);
|
|
117
196
|
});
|
|
118
197
|
});
|
|
119
198
|
});
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
<html>
|
|
2
|
+
<head>
|
|
3
|
+
<style>
|
|
4
|
+
.popup {
|
|
5
|
+
position: fixed;
|
|
6
|
+
top: 0;
|
|
7
|
+
left: 0;
|
|
8
|
+
width: 300px;
|
|
9
|
+
height: 200px;
|
|
10
|
+
background: white;
|
|
11
|
+
display: none;
|
|
12
|
+
}
|
|
13
|
+
.popup.visible {
|
|
14
|
+
display: block;
|
|
15
|
+
}
|
|
16
|
+
.popup.tier-preference-accept {
|
|
17
|
+
top: 0;
|
|
18
|
+
left: 320px;
|
|
19
|
+
}
|
|
20
|
+
</style>
|
|
21
|
+
</head>
|
|
22
|
+
<body>
|
|
23
|
+
<script type="module">
|
|
24
|
+
import { runTests } from '@web/test-runner-mocha';
|
|
25
|
+
|
|
26
|
+
runTests(async () => {
|
|
27
|
+
await import('./heuristic-cmp');
|
|
28
|
+
});
|
|
29
|
+
</script>
|
|
30
|
+
|
|
31
|
+
<div id="popup-reject" class="popup">
|
|
32
|
+
<p>We use cookies to improve your experience.</p>
|
|
33
|
+
<button>Accept All</button>
|
|
34
|
+
<button>Reject All</button>
|
|
35
|
+
</div>
|
|
36
|
+
|
|
37
|
+
<div id="popup-acknowledge-only" class="popup">
|
|
38
|
+
<p>We use cookies to improve your experience.</p>
|
|
39
|
+
<button>OK</button>
|
|
40
|
+
</div>
|
|
41
|
+
|
|
42
|
+
<div id="popup-accept-only" class="popup">
|
|
43
|
+
<p>We use cookies to improve your experience.</p>
|
|
44
|
+
<button>Accept All</button>
|
|
45
|
+
</div>
|
|
46
|
+
|
|
47
|
+
<div id="popup-accept-settings" class="popup">
|
|
48
|
+
<p>We use cookies to improve your experience.</p>
|
|
49
|
+
<button>Accept All</button>
|
|
50
|
+
<button>Settings</button>
|
|
51
|
+
</div>
|
|
52
|
+
|
|
53
|
+
<div id="popup-multiple-reject" class="popup">
|
|
54
|
+
<p>We use cookies to improve your experience.</p>
|
|
55
|
+
<button>Reject All</button>
|
|
56
|
+
<button>Deny</button>
|
|
57
|
+
</div>
|
|
58
|
+
|
|
59
|
+
<div id="popup-tier-preference-reject" class="popup">
|
|
60
|
+
<p>We use cookies to improve your experience.</p>
|
|
61
|
+
<button>Reject All</button>
|
|
62
|
+
</div>
|
|
63
|
+
|
|
64
|
+
<div id="popup-tier-preference-accept" class="popup tier-preference-accept">
|
|
65
|
+
<p>We use cookies to improve your experience.</p>
|
|
66
|
+
<button>Accept All</button>
|
|
67
|
+
</div>
|
|
68
|
+
</body>
|
|
69
|
+
</html>
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { expect } from '@esm-bundle/chai';
|
|
2
|
+
import Autoconsent from '../../lib/web';
|
|
3
|
+
import { AutoConsentHeuristicCMP } from '../../lib/cmps/base';
|
|
4
|
+
import { PopupHandlingMode, PopupHandlingModes } from '../../lib/types';
|
|
5
|
+
|
|
6
|
+
// must be run from heuristic-cmp.html
|
|
7
|
+
describe('AutoConsentHeuristicCMP', () => {
|
|
8
|
+
function createAutoconsent(heuristicMode: PopupHandlingMode) {
|
|
9
|
+
return new Autoconsent(() => Promise.resolve(), {
|
|
10
|
+
enabled: false,
|
|
11
|
+
autoAction: null,
|
|
12
|
+
enableHeuristicAction: true,
|
|
13
|
+
heuristicMode,
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function showPopup(...ids: string[]) {
|
|
18
|
+
hideAllPopups();
|
|
19
|
+
for (const id of ids) {
|
|
20
|
+
document.getElementById(id)?.classList.add('visible');
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function hideAllPopups() {
|
|
25
|
+
document.querySelectorAll('.popup').forEach((el) => el.classList.remove('visible'));
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
afterEach(() => {
|
|
29
|
+
hideAllPopups();
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
describe('detectCmp', () => {
|
|
33
|
+
it('detects reject popup as HEURISTIC-TIER0', async () => {
|
|
34
|
+
const autoconsent = createAutoconsent(PopupHandlingModes.Tier2);
|
|
35
|
+
const cmp = new AutoConsentHeuristicCMP(autoconsent, PopupHandlingModes.Tier2);
|
|
36
|
+
showPopup('popup-reject');
|
|
37
|
+
|
|
38
|
+
const detected = await cmp.detectCmp();
|
|
39
|
+
|
|
40
|
+
expect(detected).to.be.true;
|
|
41
|
+
expect(cmp.name).to.equal('HEURISTIC-TIER0');
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('detects acknowledge-only popup as HEURISTIC-TIER1', async () => {
|
|
45
|
+
const autoconsent = createAutoconsent(PopupHandlingModes.Tier2);
|
|
46
|
+
const cmp = new AutoConsentHeuristicCMP(autoconsent, PopupHandlingModes.Tier2);
|
|
47
|
+
showPopup('popup-acknowledge-only');
|
|
48
|
+
|
|
49
|
+
const detected = await cmp.detectCmp();
|
|
50
|
+
|
|
51
|
+
expect(detected).to.be.true;
|
|
52
|
+
expect(cmp.name).to.equal('HEURISTIC-TIER1');
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('detects accept-only popup as HEURISTIC-TIER2', async () => {
|
|
56
|
+
const autoconsent = createAutoconsent(PopupHandlingModes.Tier2);
|
|
57
|
+
const cmp = new AutoConsentHeuristicCMP(autoconsent, PopupHandlingModes.Tier2);
|
|
58
|
+
showPopup('popup-accept-only');
|
|
59
|
+
|
|
60
|
+
const detected = await cmp.detectCmp();
|
|
61
|
+
|
|
62
|
+
expect(detected).to.be.true;
|
|
63
|
+
expect(cmp.name).to.equal('HEURISTIC-TIER2');
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('does not detect popup with accept and settings buttons', async () => {
|
|
67
|
+
const autoconsent = createAutoconsent(PopupHandlingModes.Tier2);
|
|
68
|
+
const cmp = new AutoConsentHeuristicCMP(autoconsent, PopupHandlingModes.Tier2);
|
|
69
|
+
showPopup('popup-accept-settings');
|
|
70
|
+
|
|
71
|
+
const detected = await cmp.detectCmp();
|
|
72
|
+
|
|
73
|
+
expect(detected).to.be.false;
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('detects popup with multiple reject buttons as HEURISTIC-TIER0', async () => {
|
|
77
|
+
const autoconsent = createAutoconsent(PopupHandlingModes.Tier2);
|
|
78
|
+
const cmp = new AutoConsentHeuristicCMP(autoconsent, PopupHandlingModes.Tier2);
|
|
79
|
+
showPopup('popup-multiple-reject');
|
|
80
|
+
|
|
81
|
+
const detected = await cmp.detectCmp();
|
|
82
|
+
|
|
83
|
+
expect(detected).to.be.true;
|
|
84
|
+
expect(cmp.name).to.equal('HEURISTIC-TIER0');
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('prefers reject popup when reject and accept popups are visible', async () => {
|
|
88
|
+
const autoconsent = createAutoconsent(PopupHandlingModes.Tier2);
|
|
89
|
+
const cmp = new AutoConsentHeuristicCMP(autoconsent, PopupHandlingModes.Tier2);
|
|
90
|
+
showPopup('popup-tier-preference-reject', 'popup-tier-preference-accept');
|
|
91
|
+
|
|
92
|
+
const detected = await cmp.detectCmp();
|
|
93
|
+
|
|
94
|
+
expect(detected).to.be.true;
|
|
95
|
+
expect(cmp.name).to.equal('HEURISTIC-TIER0');
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
describe('heuristicMode cap', () => {
|
|
100
|
+
it('does not detect accept-only popup when mode is Reject', async () => {
|
|
101
|
+
const autoconsent = createAutoconsent(PopupHandlingModes.Reject);
|
|
102
|
+
const cmp = new AutoConsentHeuristicCMP(autoconsent, PopupHandlingModes.Reject);
|
|
103
|
+
showPopup('popup-accept-only');
|
|
104
|
+
|
|
105
|
+
const detected = await cmp.detectCmp();
|
|
106
|
+
|
|
107
|
+
expect(detected).to.be.false;
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it('detects acknowledge-only popup when mode is Tier1', async () => {
|
|
111
|
+
const autoconsent = createAutoconsent(PopupHandlingModes.Tier1);
|
|
112
|
+
const cmp = new AutoConsentHeuristicCMP(autoconsent, PopupHandlingModes.Tier1);
|
|
113
|
+
showPopup('popup-acknowledge-only');
|
|
114
|
+
|
|
115
|
+
const detected = await cmp.detectCmp();
|
|
116
|
+
|
|
117
|
+
expect(detected).to.be.true;
|
|
118
|
+
expect(cmp.name).to.equal('HEURISTIC-TIER1');
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it('does not detect accept-only popup when mode is Tier1', async () => {
|
|
122
|
+
const autoconsent = createAutoconsent(PopupHandlingModes.Tier1);
|
|
123
|
+
const cmp = new AutoConsentHeuristicCMP(autoconsent, PopupHandlingModes.Tier1);
|
|
124
|
+
showPopup('popup-accept-only');
|
|
125
|
+
|
|
126
|
+
const detected = await cmp.detectCmp();
|
|
127
|
+
|
|
128
|
+
expect(detected).to.be.false;
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
describe('optOut', () => {
|
|
133
|
+
it('clicks the reject button for TIER0 popup', async () => {
|
|
134
|
+
const autoconsent = createAutoconsent(PopupHandlingModes.Tier2);
|
|
135
|
+
const cmp = new AutoConsentHeuristicCMP(autoconsent, PopupHandlingModes.Tier2);
|
|
136
|
+
showPopup('popup-reject');
|
|
137
|
+
await cmp.detectCmp();
|
|
138
|
+
|
|
139
|
+
const target = cmp.getTargetButton();
|
|
140
|
+
expect(target?.regexClassification).to.equal('reject');
|
|
141
|
+
|
|
142
|
+
const result = await cmp.optOut();
|
|
143
|
+
|
|
144
|
+
expect(result).to.be.true;
|
|
145
|
+
expect(autoconsent.state.clicks).to.equal(1);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it('clicks the acknowledge button for TIER1 popup', async () => {
|
|
149
|
+
const autoconsent = createAutoconsent(PopupHandlingModes.Tier2);
|
|
150
|
+
const cmp = new AutoConsentHeuristicCMP(autoconsent, PopupHandlingModes.Tier2);
|
|
151
|
+
showPopup('popup-acknowledge-only');
|
|
152
|
+
await cmp.detectCmp();
|
|
153
|
+
|
|
154
|
+
const target = cmp.getTargetButton();
|
|
155
|
+
expect(target?.regexClassification).to.equal('acknowledge');
|
|
156
|
+
|
|
157
|
+
const result = await cmp.optOut();
|
|
158
|
+
|
|
159
|
+
expect(result).to.be.true;
|
|
160
|
+
expect(autoconsent.state.clicks).to.equal(1);
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it('clicks the accept button for TIER2 popup', async () => {
|
|
164
|
+
const autoconsent = createAutoconsent(PopupHandlingModes.Tier2);
|
|
165
|
+
const cmp = new AutoConsentHeuristicCMP(autoconsent, PopupHandlingModes.Tier2);
|
|
166
|
+
showPopup('popup-accept-only');
|
|
167
|
+
await cmp.detectCmp();
|
|
168
|
+
|
|
169
|
+
const target = cmp.getTargetButton();
|
|
170
|
+
expect(target?.regexClassification).to.equal('accept');
|
|
171
|
+
|
|
172
|
+
const result = await cmp.optOut();
|
|
173
|
+
|
|
174
|
+
expect(result).to.be.true;
|
|
175
|
+
expect(autoconsent.state.clicks).to.equal(1);
|
|
176
|
+
});
|
|
177
|
+
});
|
|
178
|
+
});
|
|
@@ -2,13 +2,22 @@ import { expect } from '@esm-bundle/chai';
|
|
|
2
2
|
import {
|
|
3
3
|
checkHeuristicPatterns,
|
|
4
4
|
cleanButtonText,
|
|
5
|
-
|
|
5
|
+
classifyButtonTextRegex,
|
|
6
6
|
classifyButtons,
|
|
7
7
|
isDisabled,
|
|
8
8
|
excludeContainers,
|
|
9
9
|
getButtonData,
|
|
10
10
|
isDialogLikeElement,
|
|
11
11
|
} from '../../lib/heuristics';
|
|
12
|
+
import { ButtonData } from '../../lib/types';
|
|
13
|
+
|
|
14
|
+
function rejectButtons(buttons: ButtonData[]) {
|
|
15
|
+
return buttons.filter((b) => b.regexClassification === 'reject');
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function nonRejectButtons(buttons: ButtonData[]) {
|
|
19
|
+
return buttons.filter((b) => b.regexClassification !== 'reject');
|
|
20
|
+
}
|
|
12
21
|
|
|
13
22
|
describe('checkHeuristicPatterns', () => {
|
|
14
23
|
it('detects cookie-related text', () => {
|
|
@@ -66,69 +75,128 @@ describe('cleanButtonText', () => {
|
|
|
66
75
|
});
|
|
67
76
|
});
|
|
68
77
|
|
|
69
|
-
describe('
|
|
78
|
+
describe('classifyButtonTextRegex', () => {
|
|
70
79
|
it('matches reject patterns', () => {
|
|
71
|
-
expect(
|
|
80
|
+
expect(classifyButtonTextRegex('Reject All')).to.equal('reject');
|
|
72
81
|
});
|
|
73
82
|
|
|
74
83
|
it('does not match never match patterns', () => {
|
|
75
|
-
expect(
|
|
84
|
+
expect(classifyButtonTextRegex('reject and pay')).to.equal('other');
|
|
76
85
|
});
|
|
77
86
|
|
|
78
87
|
it('matches "except strictly necessary" qualifier', () => {
|
|
79
88
|
// OneTrust on apnews.com labels the reject button "I Reject All (except Strictly Necessary)"
|
|
80
|
-
expect(
|
|
81
|
-
expect(
|
|
82
|
-
expect(
|
|
83
|
-
expect(
|
|
84
|
-
expect(
|
|
89
|
+
expect(classifyButtonTextRegex('I Reject All (except Strictly Necessary)')).to.equal('reject');
|
|
90
|
+
expect(classifyButtonTextRegex('Reject All (except Strictly Necessary)')).to.equal('reject');
|
|
91
|
+
expect(classifyButtonTextRegex('Reject All (except Necessary)')).to.equal('reject');
|
|
92
|
+
expect(classifyButtonTextRegex('Deny All (except Strictly Essential)')).to.equal('reject');
|
|
93
|
+
expect(classifyButtonTextRegex('I Reject All except necessary')).to.equal('reject');
|
|
85
94
|
});
|
|
86
95
|
|
|
87
|
-
it('returns
|
|
88
|
-
expect(
|
|
96
|
+
it('returns other for empty string', () => {
|
|
97
|
+
expect(classifyButtonTextRegex('')).to.equal('other');
|
|
89
98
|
});
|
|
90
99
|
|
|
91
|
-
it('supports exact
|
|
92
|
-
expect(
|
|
100
|
+
it('supports exact string patterns', () => {
|
|
101
|
+
expect(classifyButtonTextRegex('no')).to.equal('reject');
|
|
93
102
|
});
|
|
94
103
|
|
|
95
|
-
it('does not match
|
|
96
|
-
expect(
|
|
104
|
+
it('does not match partial exact string patterns', () => {
|
|
105
|
+
expect(classifyButtonTextRegex('no problem')).to.equal('other');
|
|
97
106
|
});
|
|
98
107
|
});
|
|
99
108
|
|
|
100
109
|
describe('classifyButtons', () => {
|
|
101
110
|
it('separates reject buttons from other buttons', () => {
|
|
102
|
-
const buttons = [
|
|
111
|
+
const buttons: ButtonData[] = [
|
|
103
112
|
{ text: 'Accept All', element: document.createElement('button') },
|
|
104
113
|
{ text: 'Reject All', element: document.createElement('button') },
|
|
105
114
|
{ text: 'Settings', element: document.createElement('button') },
|
|
106
115
|
];
|
|
107
116
|
|
|
108
|
-
|
|
117
|
+
classifyButtons(buttons);
|
|
109
118
|
|
|
110
|
-
expect(rejectButtons).to.have.length(1);
|
|
111
|
-
expect(rejectButtons[0].text).to.equal('Reject All');
|
|
112
|
-
expect(
|
|
119
|
+
expect(rejectButtons(buttons)).to.have.length(1);
|
|
120
|
+
expect(rejectButtons(buttons)[0].text).to.equal('Reject All');
|
|
121
|
+
expect(nonRejectButtons(buttons)).to.have.length(2);
|
|
113
122
|
});
|
|
114
123
|
|
|
115
|
-
it('
|
|
116
|
-
const
|
|
124
|
+
it('handles empty input', () => {
|
|
125
|
+
const buttons: ButtonData[] = [];
|
|
126
|
+
|
|
127
|
+
classifyButtons(buttons);
|
|
117
128
|
|
|
118
|
-
expect(rejectButtons).to.have.length(0);
|
|
119
|
-
expect(
|
|
129
|
+
expect(rejectButtons(buttons)).to.have.length(0);
|
|
130
|
+
expect(nonRejectButtons(buttons)).to.have.length(0);
|
|
120
131
|
});
|
|
121
132
|
|
|
122
133
|
it('handles multiple reject buttons', () => {
|
|
123
|
-
const buttons = [
|
|
134
|
+
const buttons: ButtonData[] = [
|
|
124
135
|
{ text: 'Reject All', element: document.createElement('button') },
|
|
125
136
|
{ text: 'Deny', element: document.createElement('button') },
|
|
126
137
|
];
|
|
127
138
|
|
|
128
|
-
|
|
139
|
+
classifyButtons(buttons);
|
|
140
|
+
|
|
141
|
+
expect(rejectButtons(buttons)).to.have.length(2);
|
|
142
|
+
expect(nonRejectButtons(buttons)).to.have.length(0);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
it('sets regexClassification on all buttons', () => {
|
|
146
|
+
const buttons: ButtonData[] = [
|
|
147
|
+
{ text: 'Accept All', element: document.createElement('button') },
|
|
148
|
+
{ text: 'Reject All', element: document.createElement('button') },
|
|
149
|
+
{ text: 'Settings', element: document.createElement('button') },
|
|
150
|
+
];
|
|
151
|
+
|
|
152
|
+
classifyButtons(buttons);
|
|
153
|
+
|
|
154
|
+
expect(buttons.every((b) => b.regexClassification)).to.be.true;
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
it('classifies accept buttons', () => {
|
|
158
|
+
const buttons: ButtonData[] = [{ text: 'Accept All', element: document.createElement('button') }];
|
|
159
|
+
|
|
160
|
+
classifyButtons(buttons);
|
|
161
|
+
|
|
162
|
+
expect(rejectButtons(buttons)).to.have.length(0);
|
|
163
|
+
expect(buttons).to.have.length(1);
|
|
164
|
+
expect(buttons[0].regexClassification).to.equal('accept');
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
it('classifies acknowledge buttons', () => {
|
|
168
|
+
const buttons: ButtonData[] = [
|
|
169
|
+
{ text: 'OK', element: document.createElement('button') },
|
|
170
|
+
{ text: 'I understand', element: document.createElement('button') },
|
|
171
|
+
];
|
|
172
|
+
|
|
173
|
+
classifyButtons(buttons);
|
|
174
|
+
|
|
175
|
+
expect(rejectButtons(buttons)).to.have.length(0);
|
|
176
|
+
expect(buttons).to.have.length(2);
|
|
177
|
+
expect(buttons.every((b) => b.regexClassification === 'acknowledge')).to.be.true;
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
it('classifies settings buttons', () => {
|
|
181
|
+
const buttons: ButtonData[] = [
|
|
182
|
+
{ text: 'Settings', element: document.createElement('button') },
|
|
183
|
+
{ text: 'Cookie preferences', element: document.createElement('button') },
|
|
184
|
+
];
|
|
185
|
+
|
|
186
|
+
classifyButtons(buttons);
|
|
187
|
+
|
|
188
|
+
expect(rejectButtons(buttons)).to.have.length(0);
|
|
189
|
+
expect(buttons).to.have.length(2);
|
|
190
|
+
expect(buttons.every((b) => b.regexClassification === 'settings')).to.be.true;
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
it('classifies reject buttons with regexClassification', () => {
|
|
194
|
+
const buttons: ButtonData[] = [{ text: 'Reject All', element: document.createElement('button') }];
|
|
195
|
+
|
|
196
|
+
classifyButtons(buttons);
|
|
129
197
|
|
|
130
|
-
expect(rejectButtons).to.have.length(
|
|
131
|
-
expect(
|
|
198
|
+
expect(rejectButtons(buttons)).to.have.length(1);
|
|
199
|
+
expect(rejectButtons(buttons)[0].regexClassification).to.equal('reject');
|
|
132
200
|
});
|
|
133
201
|
});
|
|
134
202
|
|
|
@@ -19,6 +19,20 @@
|
|
|
19
19
|
<button>Accept All</button>
|
|
20
20
|
<button>Reject All</button>
|
|
21
21
|
</div>
|
|
22
|
+
<div
|
|
23
|
+
id="heuristic-popup-acknowledge-only"
|
|
24
|
+
style="position: fixed; top: 0; left: 0; width: 300px; height: 200px; background: white; display: none"
|
|
25
|
+
>
|
|
26
|
+
<p>We use cookies to improve your experience.</p>
|
|
27
|
+
<button>OK</button>
|
|
28
|
+
</div>
|
|
29
|
+
<div
|
|
30
|
+
id="heuristic-popup-accept-only"
|
|
31
|
+
style="position: fixed; top: 0; left: 0; width: 300px; height: 200px; background: white; display: none"
|
|
32
|
+
>
|
|
33
|
+
<p>We use cookies to improve your experience.</p>
|
|
34
|
+
<button>Accept All</button>
|
|
35
|
+
</div>
|
|
22
36
|
<div id="onetrust-pc-sdk" style="display: none">Cookie Preference Center</div>
|
|
23
37
|
</body>
|
|
24
38
|
</html>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { expect } from '@esm-bundle/chai';
|
|
2
2
|
import Autoconsent from '../../lib/web';
|
|
3
3
|
import Onetrust from '../../lib/cmps/onetrust';
|
|
4
|
+
import { PopupHandlingModes } from '../../lib/types';
|
|
4
5
|
|
|
5
6
|
describe('Autoconsent.findCmp', () => {
|
|
6
7
|
let autoconsent: Autoconsent;
|
|
@@ -145,7 +146,12 @@ describe('Autoconsent.findCmp', () => {
|
|
|
145
146
|
});
|
|
146
147
|
|
|
147
148
|
afterEach(() => {
|
|
148
|
-
document.getElementById('heuristic-popup')
|
|
149
|
+
const heuristicPopup = document.getElementById('heuristic-popup');
|
|
150
|
+
if (heuristicPopup) {
|
|
151
|
+
heuristicPopup.style.display = '';
|
|
152
|
+
}
|
|
153
|
+
document.getElementById('heuristic-popup-acknowledge-only')!.style.display = 'none';
|
|
154
|
+
document.getElementById('heuristic-popup-accept-only')!.style.display = 'none';
|
|
149
155
|
});
|
|
150
156
|
|
|
151
157
|
it('returns heuristic CMP when no declarative rules match', async () => {
|
|
@@ -161,7 +167,7 @@ describe('Autoconsent.findCmp', () => {
|
|
|
161
167
|
const found = await autoconsent.findCmp(1);
|
|
162
168
|
|
|
163
169
|
expect(found).to.have.length(1);
|
|
164
|
-
expect(found[0].name).to.equal('HEURISTIC');
|
|
170
|
+
expect(found[0].name).to.equal('HEURISTIC-TIER0');
|
|
165
171
|
});
|
|
166
172
|
|
|
167
173
|
it('prefers declarative rules over heuristic CMP', async () => {
|
|
@@ -178,5 +184,41 @@ describe('Autoconsent.findCmp', () => {
|
|
|
178
184
|
expect(found).to.have.length(1);
|
|
179
185
|
expect(found[0].name).to.equal('test');
|
|
180
186
|
});
|
|
187
|
+
|
|
188
|
+
it('returns HEURISTIC-TIER1 for acknowledge-only popup when mode is Tier1', async () => {
|
|
189
|
+
document.getElementById('heuristic-popup')!.style.display = 'none';
|
|
190
|
+
document.getElementById('heuristic-popup-acknowledge-only')!.style.display = 'block';
|
|
191
|
+
|
|
192
|
+
autoconsent = new Autoconsent((msg) => Promise.resolve(), {
|
|
193
|
+
enabled: false,
|
|
194
|
+
autoAction: null,
|
|
195
|
+
enableHeuristicAction: true,
|
|
196
|
+
heuristicMode: PopupHandlingModes.Tier1,
|
|
197
|
+
});
|
|
198
|
+
autoconsent.state.findCmpAttempts = 1;
|
|
199
|
+
|
|
200
|
+
const found = await autoconsent.findCmp(1);
|
|
201
|
+
|
|
202
|
+
expect(found).to.have.length(1);
|
|
203
|
+
expect(found[0].name).to.equal('HEURISTIC-TIER1');
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
it('returns HEURISTIC-TIER2 for accept-only popup when mode is Tier2', async () => {
|
|
207
|
+
document.getElementById('heuristic-popup')!.style.display = 'none';
|
|
208
|
+
document.getElementById('heuristic-popup-accept-only')!.style.display = 'block';
|
|
209
|
+
|
|
210
|
+
autoconsent = new Autoconsent((msg) => Promise.resolve(), {
|
|
211
|
+
enabled: false,
|
|
212
|
+
autoAction: null,
|
|
213
|
+
enableHeuristicAction: true,
|
|
214
|
+
heuristicMode: PopupHandlingModes.Tier2,
|
|
215
|
+
});
|
|
216
|
+
autoconsent.state.findCmpAttempts = 1;
|
|
217
|
+
|
|
218
|
+
const found = await autoconsent.findCmp(1);
|
|
219
|
+
|
|
220
|
+
expect(found).to.have.length(1);
|
|
221
|
+
expect(found[0].name).to.equal('HEURISTIC-TIER2');
|
|
222
|
+
});
|
|
181
223
|
});
|
|
182
224
|
});
|