@duckduckgo/autoconsent 16.28.0 → 16.30.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 +34 -0
- package/dist/addon-firefox/compact-rules.json +1 -1
- package/dist/addon-firefox/content.bundle.js +17 -5
- 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/compact-rules.json +1 -1
- package/dist/addon-mv3/content.bundle.js +17 -5
- 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 +17 -5
- package/dist/autoconsent.esm.js +17 -5
- package/dist/autoconsent.playwright.js +17 -5
- package/dist/autoconsent.standalone.js +18 -6
- package/dist/types/cmps/base.d.ts +1 -1
- package/dist/types/dom-actions.d.ts +2 -1
- package/dist/types/rules.d.ts +10 -0
- package/dist/types/types.d.ts +1 -1
- package/docs/rule-syntax.md +12 -1
- package/lib/cmps/base.ts +3 -3
- package/lib/dom-actions.ts +26 -2
- package/lib/rules.ts +10 -0
- package/lib/types.ts +7 -1
- package/package.json +1 -1
- package/rules/autoconsent/bankmillennium-pl.json +26 -0
- package/rules/autoconsent/bibliotheek-nl.json +33 -0
- package/rules/autoconsent/ecbeing.json +10 -0
- package/rules/autoconsent/r42-cookiebar.json +37 -0
- package/rules/autoconsent/stright.json +29 -0
- package/rules/compact-rules.json +1 -1
- package/rules/rule-index.json +1 -1
- package/rules/rules.json +1 -1
- package/tests/bankmillennium-pl.spec.ts +5 -0
- package/tests/bibliotheek-nl.spec.ts +3 -0
- package/tests/ecbeing.spec.ts +3 -0
- package/tests/r42-cookiebar.spec.ts +7 -0
- package/tests/stright.spec.ts +8 -0
- package/tests-wtr/dom-actions/dom-actions.wait-for-then-click.html +14 -0
- package/tests-wtr/dom-actions/dom-actions.wait-for-then-click.ts +106 -0
- package/tests-wtr/rules/cmp-test-urls.json +0 -2
- package/rules/generated/auto_NL_averoachmea.nl_fxj.json +0 -40
- package/rules/generated/auto_NL_centraalbeheer.nl_ooy.json +0 -40
- package/tests/generated/auto_NL_averoachmea.nl_fxj.spec.ts +0 -6
- package/tests/generated/auto_NL_centraalbeheer.nl_ooy.spec.ts +0 -6
|
@@ -29,7 +29,7 @@ export default class AutoConsentCMPBase implements AutoCMP, DomActionsProvider {
|
|
|
29
29
|
elementVisible(selector: ElementSelector, check?: VisibilityCheck): boolean;
|
|
30
30
|
waitForElement(selector: ElementSelector, timeout?: number): Promise<boolean>;
|
|
31
31
|
waitForVisible(selector: ElementSelector, timeout?: number, check?: VisibilityCheck): Promise<boolean>;
|
|
32
|
-
waitForThenClick(selector: ElementSelector, timeout?: number, all?: boolean): Promise<boolean>;
|
|
32
|
+
waitForThenClick(selector: ElementSelector, timeout?: number, all?: boolean, retries?: number, retryInterval?: number): Promise<boolean>;
|
|
33
33
|
wait(ms: number): Promise<true>;
|
|
34
34
|
hide(selector: string, method?: HideMethod): boolean;
|
|
35
35
|
stylesheet(cssRule: string, stylesheetId?: string): boolean;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ElementSelector, HideMethod, VisibilityCheck } from './rules';
|
|
2
2
|
import { DomActionsProvider } from './types';
|
|
3
3
|
import AutoConsent from './web';
|
|
4
|
+
export declare const DEFAULT_CLICK_RETRY_INTERVAL = 300;
|
|
4
5
|
export declare class DomActions implements DomActionsProvider {
|
|
5
6
|
autoconsentInstance: AutoConsent;
|
|
6
7
|
constructor(autoconsentInstance: AutoConsent);
|
|
@@ -10,7 +11,7 @@ export declare class DomActions implements DomActionsProvider {
|
|
|
10
11
|
elementVisible(selector: ElementSelector, check?: VisibilityCheck): boolean;
|
|
11
12
|
waitForElement(selector: ElementSelector, timeout?: number): Promise<boolean>;
|
|
12
13
|
waitForVisible(selector: ElementSelector, timeout?: number, check?: VisibilityCheck): Promise<boolean>;
|
|
13
|
-
waitForThenClick(selector: ElementSelector, timeout?: number, all?: boolean): Promise<boolean>;
|
|
14
|
+
waitForThenClick(selector: ElementSelector, timeout?: number, all?: boolean, retries?: number, retryInterval?: number): Promise<boolean>;
|
|
14
15
|
wait(ms: number): Promise<true>;
|
|
15
16
|
cookieContains(substring: string): boolean;
|
|
16
17
|
hide(selector: string, method?: HideMethod): boolean;
|
package/dist/types/rules.d.ts
CHANGED
|
@@ -68,6 +68,16 @@ export type WaitForThenClickRule = {
|
|
|
68
68
|
waitForThenClick: ElementSelector;
|
|
69
69
|
timeout?: number;
|
|
70
70
|
all?: boolean;
|
|
71
|
+
/**
|
|
72
|
+
* Number of additional click attempts if the element is still visible after the first click.
|
|
73
|
+
* Defaults to 0 (no retries). Only use this on elements that are expected to disappear once the
|
|
74
|
+
* click is handled, otherwise the extra clicks are wasted (or harmful).
|
|
75
|
+
*/
|
|
76
|
+
retry?: number;
|
|
77
|
+
/**
|
|
78
|
+
* How long to wait (in ms) for the element to disappear before clicking it again.
|
|
79
|
+
*/
|
|
80
|
+
retryInterval?: number;
|
|
71
81
|
};
|
|
72
82
|
export type WaitRule = {
|
|
73
83
|
wait: number;
|
package/dist/types/types.d.ts
CHANGED
|
@@ -26,7 +26,7 @@ export interface DomActionsProvider {
|
|
|
26
26
|
elementVisible(selector: ElementSelector, check: VisibilityCheck): boolean;
|
|
27
27
|
waitForElement(selector: ElementSelector, timeout?: number): Promise<boolean>;
|
|
28
28
|
waitForVisible(selector: ElementSelector, timeout?: number, check?: VisibilityCheck): Promise<boolean>;
|
|
29
|
-
waitForThenClick(selector: ElementSelector, timeout?: number, all?: boolean): Promise<boolean>;
|
|
29
|
+
waitForThenClick(selector: ElementSelector, timeout?: number, all?: boolean, retries?: number, retryInterval?: number): Promise<boolean>;
|
|
30
30
|
wait(ms: number): Promise<true>;
|
|
31
31
|
hide(selector: string, method: HideMethod): boolean;
|
|
32
32
|
removeClass(selector: ElementSelector, className: string): boolean;
|
package/docs/rule-syntax.md
CHANGED
|
@@ -103,11 +103,22 @@ Click on an element returned by `selector`. If `all` is `true`, all matching ele
|
|
|
103
103
|
{
|
|
104
104
|
"waitForThenClick": ElementSelector,
|
|
105
105
|
"timeout": 1000,
|
|
106
|
-
"all": true | false
|
|
106
|
+
"all": true | false,
|
|
107
|
+
"retry": 0,
|
|
108
|
+
"retryInterval": 300
|
|
107
109
|
}
|
|
108
110
|
```
|
|
109
111
|
Combines `waitFor` and `click`.
|
|
110
112
|
|
|
113
|
+
`retry` is the number of extra click attempts if the element is still visible after the click, `0` by
|
|
114
|
+
default. It works around CMPs that insert a button before its click handler is attached.
|
|
115
|
+
Between attempts the step waits up to `retryInterval` ms (300 by default) for the element to
|
|
116
|
+
disappear, and stops retrying as soon as it does. Prefer this over an unconditional `wait` before the
|
|
117
|
+
click: nothing is added to the step's duration when the first click works.
|
|
118
|
+
|
|
119
|
+
**Only use `retry` on elements that are expected to go away** once the click is handled — a button that
|
|
120
|
+
stays visible (e.g. a toggle) would be clicked repeatedly.
|
|
121
|
+
|
|
111
122
|
## Unconditional wait
|
|
112
123
|
```javascript
|
|
113
124
|
{
|
package/lib/cmps/base.ts
CHANGED
|
@@ -180,12 +180,12 @@ export default class AutoConsentCMPBase implements AutoCMP, DomActionsProvider {
|
|
|
180
180
|
return this.autoconsent.domActions.waitForVisible(selector, timeout, check);
|
|
181
181
|
}
|
|
182
182
|
|
|
183
|
-
async waitForThenClick(selector: ElementSelector, timeout?: number, all?: boolean) {
|
|
183
|
+
async waitForThenClick(selector: ElementSelector, timeout?: number, all?: boolean, retries?: number, retryInterval?: number) {
|
|
184
184
|
if (this.autoconsent.config.visualTest) {
|
|
185
185
|
await this.highlightElements(this.elementSelector(selector), all);
|
|
186
186
|
}
|
|
187
187
|
this.autoconsent.updateState({ clicks: this.autoconsent.state.clicks + 1 });
|
|
188
|
-
return this.autoconsent.domActions.waitForThenClick(selector, timeout, all);
|
|
188
|
+
return this.autoconsent.domActions.waitForThenClick(selector, timeout, all, retries, retryInterval);
|
|
189
189
|
}
|
|
190
190
|
|
|
191
191
|
wait(ms: number) {
|
|
@@ -336,7 +336,7 @@ export class AutoConsentCMP extends AutoConsentCMPBase {
|
|
|
336
336
|
results.push(this.click(rule.click, rule.all));
|
|
337
337
|
}
|
|
338
338
|
if (rule.waitForThenClick) {
|
|
339
|
-
results.push(this.waitForThenClick(rule.waitForThenClick, rule.timeout, rule.all));
|
|
339
|
+
results.push(this.waitForThenClick(rule.waitForThenClick, rule.timeout, rule.all, rule.retry, rule.retryInterval));
|
|
340
340
|
}
|
|
341
341
|
if (rule.wait) {
|
|
342
342
|
results.push(this.wait(rule.wait));
|
package/lib/dom-actions.ts
CHANGED
|
@@ -3,6 +3,11 @@ import { DomActionsProvider } from './types';
|
|
|
3
3
|
import { getStyleElement, hideElements, appendStylesheetRule, isElementVisible, waitFor } from './utils';
|
|
4
4
|
import AutoConsent from './web';
|
|
5
5
|
|
|
6
|
+
// Default for the `retryInterval` option of `waitForThenClick`.
|
|
7
|
+
export const DEFAULT_CLICK_RETRY_INTERVAL = 300;
|
|
8
|
+
// How often we check if the element is gone while waiting out a `retryInterval`.
|
|
9
|
+
const CLICK_RETRY_POLL_INTERVAL = 50;
|
|
10
|
+
|
|
6
11
|
export class DomActions implements DomActionsProvider {
|
|
7
12
|
constructor(public autoconsentInstance: AutoConsent) {}
|
|
8
13
|
|
|
@@ -66,9 +71,28 @@ export class DomActions implements DomActionsProvider {
|
|
|
66
71
|
return waitFor(() => this.elementVisible(selector, check), times, interval);
|
|
67
72
|
}
|
|
68
73
|
|
|
69
|
-
async waitForThenClick(
|
|
74
|
+
async waitForThenClick(
|
|
75
|
+
selector: ElementSelector,
|
|
76
|
+
timeout = 10000,
|
|
77
|
+
all = false,
|
|
78
|
+
retries = 0,
|
|
79
|
+
retryInterval = DEFAULT_CLICK_RETRY_INTERVAL,
|
|
80
|
+
): Promise<boolean> {
|
|
70
81
|
await this.waitForElement(selector, timeout);
|
|
71
|
-
|
|
82
|
+
let clicked = await this.click(selector, all);
|
|
83
|
+
// Some CMPs insert the button before its click handler is attached, so the first click is
|
|
84
|
+
// silently dropped. There is usually no DOM signal for this, but a click that was handled
|
|
85
|
+
// makes the element go away: if it is still visible, we assume the click was lost and repeat it.
|
|
86
|
+
for (let attempt = 0; clicked && attempt < retries; attempt++) {
|
|
87
|
+
const pollTimes = Math.ceil(retryInterval / CLICK_RETRY_POLL_INTERVAL);
|
|
88
|
+
const isGone = await waitFor(() => !this.elementVisible(selector, 'any'), pollTimes, CLICK_RETRY_POLL_INTERVAL);
|
|
89
|
+
if (isGone) {
|
|
90
|
+
break;
|
|
91
|
+
}
|
|
92
|
+
this.autoconsentInstance.config.logs.rulesteps && console.log('[waitForThenClick] retrying click', selector);
|
|
93
|
+
clicked = await this.click(selector, all);
|
|
94
|
+
}
|
|
95
|
+
return clicked;
|
|
72
96
|
}
|
|
73
97
|
|
|
74
98
|
wait(ms: number): Promise<true> {
|
package/lib/rules.ts
CHANGED
|
@@ -99,6 +99,16 @@ export type WaitForThenClickRule = {
|
|
|
99
99
|
waitForThenClick: ElementSelector;
|
|
100
100
|
timeout?: number;
|
|
101
101
|
all?: boolean;
|
|
102
|
+
/**
|
|
103
|
+
* Number of additional click attempts if the element is still visible after the first click.
|
|
104
|
+
* Defaults to 0 (no retries). Only use this on elements that are expected to disappear once the
|
|
105
|
+
* click is handled, otherwise the extra clicks are wasted (or harmful).
|
|
106
|
+
*/
|
|
107
|
+
retry?: number;
|
|
108
|
+
/**
|
|
109
|
+
* How long to wait (in ms) for the element to disappear before clicking it again.
|
|
110
|
+
*/
|
|
111
|
+
retryInterval?: number;
|
|
102
112
|
};
|
|
103
113
|
|
|
104
114
|
export type WaitRule = {
|
package/lib/types.ts
CHANGED
|
@@ -29,7 +29,13 @@ export interface DomActionsProvider {
|
|
|
29
29
|
elementVisible(selector: ElementSelector, check: VisibilityCheck): boolean;
|
|
30
30
|
waitForElement(selector: ElementSelector, timeout?: number): Promise<boolean>;
|
|
31
31
|
waitForVisible(selector: ElementSelector, timeout?: number, check?: VisibilityCheck): Promise<boolean>;
|
|
32
|
-
waitForThenClick(
|
|
32
|
+
waitForThenClick(
|
|
33
|
+
selector: ElementSelector,
|
|
34
|
+
timeout?: number,
|
|
35
|
+
all?: boolean,
|
|
36
|
+
retries?: number,
|
|
37
|
+
retryInterval?: number,
|
|
38
|
+
): Promise<boolean>;
|
|
33
39
|
wait(ms: number): Promise<true>;
|
|
34
40
|
hide(selector: string, method: HideMethod): boolean;
|
|
35
41
|
removeClass(selector: ElementSelector, className: string): boolean;
|
package/package.json
CHANGED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "bankmillennium.pl",
|
|
3
|
+
"runContext": {
|
|
4
|
+
"urlPattern": "^https?://(www\\.)?bankmillennium\\.pl/"
|
|
5
|
+
},
|
|
6
|
+
"prehideSelectors": ["#cookie_modal_placeholder dialog.bm-modal--cookie"],
|
|
7
|
+
"detectCmp": [{ "exists": "#cookie_modal_placeholder dialog.bm-modal--cookie" }],
|
|
8
|
+
"detectPopup": [{ "visible": "#cookie_modal_placeholder dialog.bm-modal--cookie" }],
|
|
9
|
+
"optIn": [
|
|
10
|
+
{
|
|
11
|
+
"waitForThenClick": [
|
|
12
|
+
"#cookie_modal_placeholder dialog.bm-modal--cookie",
|
|
13
|
+
"xpath///button[contains(normalize-space(.), 'Potwierd')]"
|
|
14
|
+
]
|
|
15
|
+
}
|
|
16
|
+
],
|
|
17
|
+
"optOut": [
|
|
18
|
+
{
|
|
19
|
+
"waitForThenClick": [
|
|
20
|
+
"#cookie_modal_placeholder dialog.bm-modal--cookie",
|
|
21
|
+
"xpath///button[contains(normalize-space(.), 'Odrzu')]"
|
|
22
|
+
]
|
|
23
|
+
}
|
|
24
|
+
],
|
|
25
|
+
"test": [{ "cookieContains": "Analytics_consent=2" }, { "cookieContains": "Marketing_consent=2" }]
|
|
26
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "bibliotheek-nl",
|
|
3
|
+
"_metadata": {
|
|
4
|
+
"vendorUrl": "https://www.bibliotheek.nl/"
|
|
5
|
+
},
|
|
6
|
+
"cosmetic": false,
|
|
7
|
+
"runContext": {
|
|
8
|
+
"main": true,
|
|
9
|
+
"frame": false,
|
|
10
|
+
"urlPattern": "^https?://(?:www\\.)?(?:onlinebibliotheek|bibliotheek)\\.nl/"
|
|
11
|
+
},
|
|
12
|
+
"prehideSelectors": [".modalwindow-container.bar .modalwindow"],
|
|
13
|
+
"detectCmp": [
|
|
14
|
+
{
|
|
15
|
+
"exists": "#bibliotheek-nl-page"
|
|
16
|
+
}
|
|
17
|
+
],
|
|
18
|
+
"detectPopup": [
|
|
19
|
+
{
|
|
20
|
+
"visible": ".modalwindow-container.bar .modalwindow"
|
|
21
|
+
}
|
|
22
|
+
],
|
|
23
|
+
"optIn": [
|
|
24
|
+
{
|
|
25
|
+
"waitForThenClick": ".modalwindow-container.bar .modalwindow button.button.primary.ok"
|
|
26
|
+
}
|
|
27
|
+
],
|
|
28
|
+
"optOut": [
|
|
29
|
+
{
|
|
30
|
+
"waitForThenClick": ".modalwindow-container.bar .modalwindow button.button.primary.notok"
|
|
31
|
+
}
|
|
32
|
+
]
|
|
33
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "ecbeing",
|
|
3
|
+
"vendorUrl": "https://www.ecbeing.net/",
|
|
4
|
+
"cosmetic": true,
|
|
5
|
+
"prehideSelectors": ["#cookieBox .block-cookie-consent"],
|
|
6
|
+
"detectCmp": [{ "exists": "#cookieBox #consentButton.block-cookie-consent--btn" }],
|
|
7
|
+
"detectPopup": [{ "visible": "#cookieBox" }],
|
|
8
|
+
"optIn": [{ "waitForThenClick": "#cookieBox #consentButton" }],
|
|
9
|
+
"optOut": [{ "hide": "#cookieBox" }]
|
|
10
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "r42-cookiebar",
|
|
3
|
+
"cosmetic": false,
|
|
4
|
+
"runContext": {
|
|
5
|
+
"main": true,
|
|
6
|
+
"frame": false
|
|
7
|
+
},
|
|
8
|
+
"prehideSelectors": ["#r42CookieBar"],
|
|
9
|
+
"detectCmp": [
|
|
10
|
+
{
|
|
11
|
+
"exists": "#r42CookieBar .cw-deny"
|
|
12
|
+
}
|
|
13
|
+
],
|
|
14
|
+
"detectPopup": [
|
|
15
|
+
{
|
|
16
|
+
"visible": "#r42CookieBar[open] .cw-deny"
|
|
17
|
+
}
|
|
18
|
+
],
|
|
19
|
+
"optIn": [
|
|
20
|
+
{
|
|
21
|
+
"waitForThenClick": "#r42CookieBar .cw-accept"
|
|
22
|
+
}
|
|
23
|
+
],
|
|
24
|
+
"optOut": [
|
|
25
|
+
{
|
|
26
|
+
"waitForThenClick": "#r42CookieBar .cw-deny",
|
|
27
|
+
"comment": "Weigeren"
|
|
28
|
+
}
|
|
29
|
+
],
|
|
30
|
+
"test": [
|
|
31
|
+
{
|
|
32
|
+
"waitForVisible": "#r42CookieBar .cw-deny",
|
|
33
|
+
"timeout": 1000,
|
|
34
|
+
"check": "none"
|
|
35
|
+
}
|
|
36
|
+
]
|
|
37
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "stright",
|
|
3
|
+
"prehideSelectors": ["#controlled_banner-cookie_consent-consent_banner", "#controlled_banner-cookie_consent-privacy_setting_banner"],
|
|
4
|
+
"detectCmp": [{ "exists": "#controlled_banner-cookie_consent-consent_banner" }],
|
|
5
|
+
"detectPopup": [{ "visible": "#controlled_banner-cookie_consent-consent_banner" }],
|
|
6
|
+
"optIn": [{ "waitForThenClick": "#controlled_submit-cookie_consent-consent_banner-accept_all" }],
|
|
7
|
+
"optOut": [
|
|
8
|
+
{
|
|
9
|
+
"if": {
|
|
10
|
+
"visible": "#controlled_banner-cookie_consent-consent_banner [data-search-key-button=reject_all_text]"
|
|
11
|
+
},
|
|
12
|
+
"then": [
|
|
13
|
+
{
|
|
14
|
+
"waitForThenClick": "#controlled_banner-cookie_consent-consent_banner [data-search-key-button=reject_all_text]"
|
|
15
|
+
}
|
|
16
|
+
],
|
|
17
|
+
"else": [
|
|
18
|
+
{
|
|
19
|
+
"waitForThenClick": "#controlled_submit-cookie_consent-consent_banner-privacy_setting_banner_link"
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"waitForThenClick": "#controlled_banner-cookie_consent-privacy_setting_banner [data-search-key-button=reject_all_text]"
|
|
23
|
+
}
|
|
24
|
+
]
|
|
25
|
+
},
|
|
26
|
+
{ "waitForVisible": "#controlled_banner-cookie_consent-consent_banner", "check": "none" }
|
|
27
|
+
],
|
|
28
|
+
"test": [{ "visible": "#controlled_banner-cookie_consent-consent_banner", "check": "none" }]
|
|
29
|
+
}
|