@duckduckgo/autoconsent 1.0.8 → 2.1.1

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.
Files changed (182) hide show
  1. package/.eslintrc.cjs +14 -0
  2. package/.vscode/.idea/.vscode.iml +9 -0
  3. package/.vscode/.idea/modules.xml +8 -0
  4. package/.vscode/.idea/workspace.xml +28 -0
  5. package/.vscode/settings.json +7 -0
  6. package/Jenkinsfile +68 -39
  7. package/api.md +104 -0
  8. package/dist/addon-firefox/background.bundle.js +1 -0
  9. package/dist/addon-firefox/content.bundle.js +1 -0
  10. package/dist/addon-firefox/icons/cog.png +0 -0
  11. package/dist/addon-firefox/icons/cookie-idle.png +0 -0
  12. package/dist/addon-firefox/icons/cookie.png +0 -0
  13. package/dist/addon-firefox/icons/party.png +0 -0
  14. package/dist/addon-firefox/icons/tick.png +0 -0
  15. package/dist/addon-firefox/icons/verified.png +0 -0
  16. package/dist/addon-firefox/manifest.json +32 -0
  17. package/dist/addon-firefox/rules.json +4167 -0
  18. package/dist/addon-mv3/background.bundle.js +1 -0
  19. package/dist/addon-mv3/content.bundle.js +1 -0
  20. package/dist/addon-mv3/icons/cog.png +0 -0
  21. package/dist/addon-mv3/icons/cookie-idle.png +0 -0
  22. package/dist/addon-mv3/icons/cookie.png +0 -0
  23. package/dist/addon-mv3/icons/party.png +0 -0
  24. package/dist/addon-mv3/icons/tick.png +0 -0
  25. package/dist/addon-mv3/icons/verified.png +0 -0
  26. package/dist/addon-mv3/manifest.json +34 -0
  27. package/dist/addon-mv3/rules.json +4167 -0
  28. package/dist/autoconsent.cjs.js +1 -1387
  29. package/dist/autoconsent.esm.js +1 -1379
  30. package/dist/autoconsent.playwright.js +1 -0
  31. package/lib/cmps/all.ts +15 -10
  32. package/lib/cmps/base.ts +95 -90
  33. package/lib/cmps/consentmanager.ts +31 -19
  34. package/lib/cmps/consentomatic.ts +89 -0
  35. package/lib/cmps/cookiebot.ts +58 -55
  36. package/lib/cmps/evidon.ts +29 -18
  37. package/lib/cmps/onetrust.ts +32 -20
  38. package/lib/cmps/sourcepoint-frame.ts +104 -0
  39. package/lib/cmps/sourcepoint-top.ts +47 -0
  40. package/lib/cmps/trustarc-frame.ts +115 -0
  41. package/lib/cmps/trustarc-top.ts +97 -0
  42. package/lib/consentomatic/index.ts +233 -70
  43. package/lib/{web/consentomatic → consentomatic}/tools.ts +0 -0
  44. package/lib/eval-handler.ts +58 -0
  45. package/lib/index.ts +0 -2
  46. package/lib/messages.ts +100 -0
  47. package/lib/rule-executors.ts +108 -0
  48. package/lib/rules.ts +82 -0
  49. package/lib/types.ts +35 -0
  50. package/lib/utils.ts +64 -0
  51. package/lib/web.ts +288 -74
  52. package/package.json +20 -16
  53. package/playwright/content.ts +21 -0
  54. package/playwright/runner.ts +162 -0
  55. package/playwright.config.ts +7 -0
  56. package/readme.md +61 -47
  57. package/rollup.config.js +38 -16
  58. package/rules/autoconsent/ausopen.json +2 -1
  59. package/rules/autoconsent/baden-wuerttemberg-de.json +7 -3
  60. package/rules/autoconsent/bundesregierung-de.json +5 -1
  61. package/rules/autoconsent/cc-banner.json +0 -1
  62. package/rules/autoconsent/cookie-law-info.json +1 -1
  63. package/rules/autoconsent/cookie-notice.json +1 -2
  64. package/rules/autoconsent/cookieconsent.json +5 -6
  65. package/rules/autoconsent/destatis-de.json +2 -2
  66. package/rules/autoconsent/dunelm.json +1 -1
  67. package/rules/autoconsent/etsy.json +4 -3
  68. package/rules/autoconsent/eu-cookie-compliance.json +0 -1
  69. package/rules/autoconsent/hl-co-uk.json +8 -9
  70. package/rules/autoconsent/johnlewis.json +5 -2
  71. package/rules/autoconsent/marksandspencer.json +2 -1
  72. package/rules/autoconsent/mediamarkt-de.json +1 -1
  73. package/rules/autoconsent/microsoft.json +1 -1
  74. package/rules/autoconsent/notice-cookie.json +0 -1
  75. package/rules/autoconsent/osano.json +4 -2
  76. package/rules/autoconsent/snigel.json +2 -1
  77. package/rules/autoconsent/tealium.json +4 -5
  78. package/rules/autoconsent/thefreedictionary.json +1 -1
  79. package/rules/rules.json +79 -45
  80. package/tests/192.spec.ts +1 -1
  81. package/tests/arzt-auskunft.spec.ts +1 -1
  82. package/tests/asus.spec.ts +1 -1
  83. package/tests/ausopen.spec.ts +1 -1
  84. package/tests/aws.amazon.spec.ts +1 -1
  85. package/tests/baden-wuerttemberg.spec.ts +1 -1
  86. package/tests/borlabs.spec.ts +1 -1
  87. package/tests/bundesregierung.spec.ts +5 -2
  88. package/tests/ccbanner.spec.ts +1 -1
  89. package/tests/consentmanager.spec.ts +3 -3
  90. package/tests/cookiebot.spec.ts +8 -2
  91. package/tests/cookieconsent.spec.ts +1 -1
  92. package/tests/cookielawinfo.spec.ts +1 -1
  93. package/tests/cookienotice.spec.ts +1 -1
  94. package/tests/corona-in-zahlen.spec.ts +1 -1
  95. package/tests/deepl.spec.ts +1 -1
  96. package/tests/destatis.spec.ts +1 -1
  97. package/tests/didomi.spec.ts +7 -3
  98. package/tests/drupal.spec.ts +8 -0
  99. package/tests/dunelm.spec.ts +1 -1
  100. package/tests/etsy.spec.ts +1 -1
  101. package/tests/eu-cookie-compliance-banner.spec.ts +1 -1
  102. package/tests/evidon.spec.ts +7 -2
  103. package/tests/fundingchoices.spec.ts +2 -1
  104. package/tests/gov-uk.spec.ts +1 -1
  105. package/tests/hl-co-uk.spec.ts +1 -1
  106. package/tests/hubspot.spec.ts +1 -1
  107. package/tests/ionos.spec.ts +1 -1
  108. package/tests/johnlewis.spec.ts +2 -2
  109. package/tests/klaro.spec.ts +1 -1
  110. package/tests/marksandspencer.spec.ts +1 -1
  111. package/tests/mediamarkt.spec.ts +1 -1
  112. package/tests/metoffice-gov-uk.spec.ts +1 -1
  113. package/tests/microsoft.spec.ts +1 -1
  114. package/tests/moneysavingexpert.spec.ts +1 -1
  115. package/tests/motor-talk.spec.ts +1 -1
  116. package/tests/national-lottery.spec.ts +1 -1
  117. package/tests/netflix.spec.ts +1 -1
  118. package/tests/nhs.spec.ts +1 -1
  119. package/tests/notice-cookie.spec.ts +1 -1
  120. package/tests/obi.spec.ts +1 -1
  121. package/tests/oil.spec.ts +2 -2
  122. package/tests/onetrust.spec.ts +27 -2
  123. package/tests/osano.spec.ts +1 -1
  124. package/tests/otto.spec.ts +1 -1
  125. package/tests/paypal.spec.ts +1 -1
  126. package/tests/quantcast.spec.ts +5 -2
  127. package/tests/snigel.spec.ts +1 -1
  128. package/tests/sourcepoint.spec.ts +8 -8
  129. package/tests/springer.spec.ts +2 -2
  130. package/tests/steampowered.spec.ts +1 -1
  131. package/tests/tealium.spec.ts +1 -1
  132. package/tests/testcmp.spec.ts +1 -1
  133. package/tests/thalia.spec.ts +1 -1
  134. package/tests/thefreedictionary.spec.ts +1 -1
  135. package/tests/trustarc.spec.ts +26 -4
  136. package/tests/usercentrics-1.spec.ts +1 -1
  137. package/tests/uswitch.spec.ts +1 -1
  138. package/tests/vodafone.spec.ts +1 -1
  139. package/tests/waitrose.spec.ts +1 -1
  140. package/tests/wetransfer.spec.ts +1 -1
  141. package/tests/wordpressgdpr.spec.ts +2 -2
  142. package/tests/xing.spec.ts +1 -1
  143. package/tsconfig.json +2 -2
  144. package/update_version.js +10 -6
  145. package/.eslintrc +0 -12
  146. package/cosmetics/rules.json +0 -110
  147. package/dist/autoconsent.puppet.js +0 -1078
  148. package/lib/cmps/all.js +0 -19
  149. package/lib/cmps/base.js +0 -174
  150. package/lib/cmps/consentmanager.js +0 -31
  151. package/lib/cmps/cookiebot.js +0 -77
  152. package/lib/cmps/evidon.js +0 -26
  153. package/lib/cmps/onetrust.js +0 -34
  154. package/lib/cmps/sourcepoint.js +0 -82
  155. package/lib/cmps/sourcepoint.ts +0 -95
  156. package/lib/cmps/trustarc.js +0 -106
  157. package/lib/cmps/trustarc.ts +0 -147
  158. package/lib/config.js +0 -1
  159. package/lib/consentomatic/index.js +0 -52
  160. package/lib/detector.js +0 -33
  161. package/lib/detector.ts +0 -34
  162. package/lib/hider.js +0 -13
  163. package/lib/hider.ts +0 -16
  164. package/lib/index.js +0 -4
  165. package/lib/messages.d.ts +0 -61
  166. package/lib/node.js +0 -35
  167. package/lib/node.ts +0 -43
  168. package/lib/puppet/tab.js +0 -121
  169. package/lib/puppet/tab.ts +0 -146
  170. package/lib/rules.d.ts +0 -80
  171. package/lib/tabwrapper.js +0 -67
  172. package/lib/tabwrapper.ts +0 -74
  173. package/lib/types.d.ts +0 -61
  174. package/lib/web/consentomatic/index.ts +0 -249
  175. package/lib/web/content-utils.js +0 -29
  176. package/lib/web/content-utils.ts +0 -31
  177. package/lib/web/content.js +0 -89
  178. package/lib/web/content.ts +0 -80
  179. package/lib/web/tab.js +0 -112
  180. package/lib/web/tab.ts +0 -178
  181. package/lib/web.js +0 -95
  182. package/tests/runner.ts +0 -61
@@ -0,0 +1,162 @@
1
+ import fs from "fs";
2
+ import path from 'path';
3
+ import { test, expect, Page, Frame } from "@playwright/test";
4
+ import { waitFor } from "../lib/utils";
5
+ import { ContentScriptMessage } from "../lib/messages";
6
+ import { enableLogs } from "../lib/config";
7
+ import { AutoAction } from "../lib/types";
8
+
9
+ const testRegion = (process.env.REGION || "NA").trim();
10
+
11
+ type TestOptions = {
12
+ testOptOut: boolean;
13
+ testSelfTest: boolean;
14
+ testOptIn: boolean;
15
+ skipRegions?: string[];
16
+ onlyRegions?: string[];
17
+ };
18
+ const defaultOptions: TestOptions = {
19
+ testOptOut: true,
20
+ testOptIn: true,
21
+ testSelfTest: true,
22
+ skipRegions: [],
23
+ onlyRegions: [],
24
+ };
25
+
26
+ const contentScript = fs.readFileSync(
27
+ path.join(__dirname, "../dist/autoconsent.playwright.js"),
28
+ "utf8"
29
+ );
30
+
31
+ export async function injectContentScript(page: Page | Frame) {
32
+ try {
33
+ await page.evaluate(contentScript);
34
+ } catch (e) {
35
+ // frame was detached
36
+ // console.log(e);
37
+ }
38
+ }
39
+
40
+ export function generateTest(
41
+ url: string,
42
+ expectedCmp: string,
43
+ options: TestOptions = defaultOptions
44
+ ) {
45
+ function genTest(autoAction: AutoAction) {
46
+ test(`${url.split("://")[1]} .${testRegion} ${autoAction}`, async ({ page }) => {
47
+ if (options.onlyRegions && options.onlyRegions.length > 0 && !options.onlyRegions.includes(testRegion)) {
48
+ test.skip();
49
+ }
50
+ if (options.skipRegions && options.skipRegions.includes(testRegion)) {
51
+ test.skip();
52
+ }
53
+ enableLogs && page.on('console', async msg => {
54
+ console.log(` page log:`, msg.text());
55
+ });
56
+ await page.exposeBinding("autoconsentSendMessage", messageCallback);
57
+ await page.goto(url, { waitUntil: "commit" });
58
+
59
+ // set up a messaging function
60
+ const received: ContentScriptMessage[] = [];
61
+
62
+ function isMessageReceived(msg: Partial<ContentScriptMessage>, partial = true) {
63
+ return received.some((m) => {
64
+ const keysMatch = partial || Object.keys(m).length === Object.keys(msg).length;
65
+ return keysMatch && Object.keys(msg).every(
66
+ (k) => (<any>m)[k] === (<any>msg)[k]
67
+ );
68
+ });
69
+ }
70
+
71
+ let selfTestFrame: Frame = null;
72
+ async function messageCallback({ frame }: { frame: Frame }, msg: ContentScriptMessage) {
73
+ enableLogs && msg.type !== 'eval' && console.log(msg);
74
+ received.push(msg);
75
+ switch (msg.type) {
76
+ case 'init': {
77
+ await frame.evaluate(`autoconsentReceiveMessage({ type: "initResp", config: ${JSON.stringify({
78
+ enabled: true,
79
+ autoAction: autoAction,
80
+ disabledCmps: [],
81
+ enablePrehide: true,
82
+ detectRetries: 20,
83
+ })} })`);
84
+ break;
85
+ }
86
+ case 'optInResult':
87
+ case 'optOutResult': {
88
+ if (msg.scheduleSelfTest) {
89
+ selfTestFrame = frame;
90
+ }
91
+ break;
92
+ }
93
+ case 'autoconsentDone': {
94
+ if (selfTestFrame && options.testSelfTest) {
95
+ await selfTestFrame.evaluate(`autoconsentReceiveMessage({ type: "selfTest" })`);
96
+ }
97
+ break;
98
+ }
99
+ case 'eval': {
100
+ const result = await frame.evaluate(msg.code);
101
+ await frame.evaluate(`autoconsentReceiveMessage({ id: "${msg.id}", type: "evalResp", result: ${JSON.stringify(result)} })`);
102
+ break;
103
+ }
104
+ case 'autoconsentError': {
105
+ console.error(url, msg.details);
106
+ break;
107
+ }
108
+ }
109
+ }
110
+
111
+ // inject content scripts into every frame
112
+ await injectContentScript(page);
113
+ page.frames().forEach(injectContentScript);
114
+ page.on("framenavigated", injectContentScript);
115
+
116
+ // wait for all messages and assertions
117
+ await waitFor(() => isMessageReceived({ type: "popupFound", cmp: expectedCmp }), 50, 500);
118
+ expect(isMessageReceived({ type: "popupFound", cmp: expectedCmp })).toBe(true);
119
+
120
+ if (autoAction === 'optOut') {
121
+ await waitFor(() => isMessageReceived({ type: "optOutResult", result: true }), 50, 300);
122
+ expect(isMessageReceived({ type: "optOutResult", result: true })).toBe(true);
123
+ }
124
+ if (autoAction === 'optIn') {
125
+ await waitFor(() => isMessageReceived({ type: "optInResult", result: true }), 50, 300);
126
+ expect(isMessageReceived({ type: "optInResult", result: true })).toBe(true);
127
+ }
128
+ if (options.testSelfTest && selfTestFrame) {
129
+ await waitFor(() => isMessageReceived({ type: "selfTestResult", result: true }), 50, 300);
130
+ expect(isMessageReceived({ type: "selfTestResult", result: true })).toBe(true);
131
+ }
132
+ await waitFor(() => isMessageReceived({ type: "autoconsentDone" }), 10, 500);
133
+ expect(isMessageReceived({ type: "autoconsentDone" })).toBe(true);
134
+
135
+ expect(isMessageReceived({ type: "autoconsentError" })).toBe(false);
136
+ })
137
+ }
138
+
139
+ if (!options.testOptIn && !options.testOptOut) {
140
+ genTest(null);
141
+ }
142
+
143
+ if (options.testOptIn) {
144
+ genTest('optIn');
145
+ }
146
+
147
+ if (options.testOptOut) {
148
+ genTest('optOut');
149
+ }
150
+ }
151
+
152
+ export default function generateCMPTests(
153
+ cmp: string,
154
+ sites: string[],
155
+ options: Partial<TestOptions> = {}
156
+ ) {
157
+ test.describe(cmp, () => {
158
+ sites.forEach((url) => {
159
+ generateTest(url, cmp, Object.assign({}, defaultOptions, options));
160
+ });
161
+ });
162
+ }
@@ -25,6 +25,13 @@ const config: PlaywrightTestConfig = {
25
25
  ...devices['Desktop Firefox'],
26
26
  proxy,
27
27
  },
28
+ },
29
+ {
30
+ name: 'chrome',
31
+ use: {
32
+ ...devices['Desktop Chrome'],
33
+ proxy,
34
+ },
28
35
  }
29
36
  ],
30
37
  };
package/readme.md CHANGED
@@ -1,28 +1,30 @@
1
- ## Autoconsent
1
+ # Autoconsent
2
2
 
3
3
  This is a library of rules for navigating through common consent popups on the web. These rules
4
- can be run in a Firefox webextension, or in a puppeteer orchestrated headless browser. Using
4
+ can be run in a Chrome extension, or in a Playwright-orchestrated headless browser. Using
5
5
  these rules, opt-in and opt-out options can be selected automatically, without requiring
6
6
  user-input.
7
7
 
8
- ### Install Standalone
8
+ ## Browser extension
9
9
 
10
- The standalone addon can be built with the following steps:
10
+ The web extension can be built with the following steps:
11
11
 
12
12
  ```bash
13
13
  # Download dependencies
14
14
  npm ci
15
- # Build JS bundles
16
- npm run bundle
17
15
  # Build consent ruleset
18
16
  npm run build-rules
17
+ # Build JS bundles (rules must be built first)
18
+ npm run bundle
19
19
  ```
20
20
 
21
- The standalone addon can be found in the `addon` directory and can be run with `npm start`.
22
- Alternatively, you can use `web-ext build -s addon/` to generate a packaged addon that can
23
- be installed in an existing Firefox profile.
21
+ The extension-specific code can be found in the `addon` directory. There are two versions of the
22
+ addon (found under `dist/addon` after building), one for `mv3` version for Chromium-based browsers, and a `firefox` version for Firefox.
23
+ You can load these in [Chrome](https://developer.chrome.com/docs/extensions/mv3/getstarted/#unpacked)
24
+ in developer mode, and in [Firefox](https://extensionworkshop.com/documentation/develop/temporary-installation-in-firefox/)
25
+ as a tempoary addon.
24
26
 
25
- ### Rules
27
+ ## Rules
26
28
 
27
29
  The library's functionality is implemented as a set of rules that define how to manage consent on
28
30
  a subset of sites. These generally correspond to specific Consent Management Providers (CMPs)
@@ -41,33 +43,50 @@ There are currently three ways of implementing a CMP:
41
43
  3. As a [Consent-O-Matic](https://github.com/cavi-au/Consent-O-Matic) rule. The `ConsentOMaticCMP` class implements
42
44
  compability with rules written for the Consent-O-Matic extension.
43
45
 
44
- ### Rule Syntax
46
+ ## Intermediate rules
47
+
48
+ Sometimes the opt-out process requires actions that span across multiple pages or iframes. In this case it is necessary to define stages (each corresponding to a separate page context) as separate rulesets. Each one, except the very last stage, must be marked as intermediate using the `intermediate: true` flag. If the `intermediate` flag is not set correctly, autoconsent may report a successful opt-out even if it is not yet finished.
49
+
50
+ ## Context filters
51
+
52
+ By default, rules will be executed in all top-level documents. Some rules are designed for specific contexts (e.g. only nested iframes, or only specific URLs). This can be configured in `runContext` field (see the syntax reference below).
53
+
54
+ ## Rule Syntax Reference
45
55
 
46
56
  An autoconsent CMP rule can be written as either:
47
- * a class implementing the `AutoCMP` interface, or
48
57
  * a JSON file adhering to the `AutoConsentCMPRule` type.
58
+ * a class implementing the `AutoCMP` interface, or
59
+ * common JSON rules are available as reusable functions in [rule-executors.ts](/lib/rule-executors.ts). You can also use existing class-based rules as reference.
49
60
 
50
- In most cases the JSON syntax should be sufficient, unless non-linear logic is required, in which case a class is required.
61
+ In most cases the JSON syntax should be sufficient, unless some complex non-linear logic is required, in which case a class is required.
51
62
 
52
- Both JSON and class implementations require 5 main components:
63
+ Both JSON and class implementations have the following components:
53
64
  * `name` - to identify this CMP.
54
65
  * `detectCMP` - which determines if this CMP is included on the page.
55
66
  * `detectPopup` - which determines if a popup is being shown by the CMP.
56
- * `optOut` - executes actions to do an 'opt-out' from the popup screen. i.e. denying all consents possible.
57
- * `optIn` - execut actions for an 'opt-in' from the popup screen.
67
+ * `optOut` - a list of actions to do an 'opt-out' from the popup screen. i.e. denying all consents possible.
68
+ * `optIn` - a list of actions for an 'opt-in' from the popup screen.
69
+ * (optional) `prehideSelectors` - a list of CSS selectors to "pre-hide" early before detecting a CMP. This helps against flickering. Pre-hiding is done using CSS `opacity` and `z-index`, so be it should be used with care to prevent conflicts with the opt-out process.
70
+ * (optional) `intermediate` - a boolean flag indicating that the ruleset is part of a multi-stage process, see the [Intermediate rules](#intermediate-rules) section. This is `false` by default.
71
+ * (optional) `runContext` - an object describing when this rule should be tried:
72
+ * `main` - boolean, set to `true` if the rule should be executed in top-level documents (default: `true`)
73
+ * `frame` - boolean, set to `true` if the rule should be executed in nested frames (default: `false`)
74
+ * `url` - string, specifies a string prefix that should match the page URL (default: empty)
75
+ * (optional) `test` - a list of actions to verify a successful opt-out. This is currently only used in Playwright tests.
58
76
 
59
- Except for `name` this are defined as a set of checks or actions on the page. In the JSON syntax this is a list of `AutoConsentRuleStep` objects. For `detect` checks, we return true for the check if all steps return true. For opt in and out, we execute actions in order, exiting if one fails. The following checks/actions are supported:
60
77
 
61
- #### Element exists
78
+ `detectCMP`, `detectPopup`, `optOut`, `optIn`, and `test` are defined as a set of checks or actions on the page. In the JSON syntax this is a list of `AutoConsentRuleStep` objects. For `detect` checks, we return true for the check if all steps return true. For opt in and out, we execute actions in order, exiting if one fails. The following checks/actions are supported:
79
+
80
+ ### Element exists
62
81
 
63
82
  ```json
64
83
  {
65
84
  "exists": "selector"
66
85
  }
67
86
  ```
68
- Returns true if `document.querySelect(selector)` returns elements.
87
+ Returns true if `document.querySelector(selector)` returns elements.
69
88
 
70
- #### Element visible
89
+ ### Element visible
71
90
 
72
91
  ```json
73
92
  {
@@ -75,18 +94,9 @@ Returns true if `document.querySelect(selector)` returns elements.
75
94
  "check": "any" | "all" | "none"
76
95
  }
77
96
  ```
78
- Returns true if an element returned from `document.querySelect(selector)` is current visible on the page. If `check` is `all`, every element must be visible. If `check` is `none`, no element should be visible.
97
+ Returns true if elements returned from `document.querySelectorAll(selector)` are currently visible on the page. If `check` is `all`, every element must be visible. If `check` is `none`, no element should be visible. Visibility check is a CSS-based heuristic.
79
98
 
80
- #### Eval
81
-
82
- ```json
83
- {
84
- "eval": "code"
85
- }
86
- ```
87
- Evaluates `code` in the context of the page. NB: the result of this action depends on the truthiness of the evaluated expression, make sure it returns `true` in case of success.
88
-
89
- #### Wait for element
99
+ ### Wait for element
90
100
 
91
101
  ```json
92
102
  {
@@ -96,25 +106,26 @@ Evaluates `code` in the context of the page. NB: the result of this action depen
96
106
  ```
97
107
  Waits until `selector` exists in the page. After `timeout` ms the step fails.
98
108
 
99
- #### Click and element
109
+ ### Click an element
100
110
  ```json
101
111
  {
102
112
  "click": "selector",
103
113
  "all": true | false,
104
114
  }
105
115
  ```
106
- Click on an element returned by `selector`. If `all` is `true`, all matching elements are clicked.
116
+ Click on an element returned by `selector`. If `all` is `true`, all matching elements are clicked. If `all` is `false`, only the first returned value is clicked.
107
117
 
108
- #### Wait for then click
118
+ ### Wait for then click
109
119
  ```json
110
120
  {
111
121
  "waitForThenClick": "selector",
112
- "timeout": 1000
122
+ "timeout": 1000,
123
+ "all": true | false
113
124
  }
114
125
  ```
115
126
  Combines `waitFor` and `click`.
116
127
 
117
- #### Wait
128
+ ### Unconditional wait
118
129
  ```json
119
130
  {
120
131
  "wait": 1000,
@@ -122,30 +133,33 @@ Combines `waitFor` and `click`.
122
133
  ```
123
134
  Wait for the specified number of milliseconds.
124
135
 
125
- #### Go to URL
136
+ ### Hide
126
137
  ```json
127
138
  {
128
- "goto": "url"
139
+ "hide": ["selector", ...],
140
+ "method": "display" | "opacity"
129
141
  }
130
142
  ```
131
- Navigate the page to the given URL.
143
+ Hide the elements matched by the selectors. `method` defines how elements are hidden: "display" sets `display: none`, "opacity" sets `opacity: 0`. Method is "display" by default.
144
+
145
+ ### Eval
132
146
 
133
- #### Hide rule
134
147
  ```json
135
148
  {
136
- "hide": ["selector", ...]
149
+ "eval": "code"
137
150
  }
138
151
  ```
139
- Set the elements matched by the selectors to `display: none`.
152
+ Evaluates `code` in the context of the page. The rule is considered successful if it *evaluates to a truthy value*.
153
+ Eval rules are not 100% reliable because they can be blocked by a CSP policy on the page. Therefore, they should only be used as a last resort when none of the other rules are sufficient.
140
154
 
141
- #### Frames
155
+ ### Optional actions
142
156
 
143
- In some cases, rules have to interact with `iframes` in the page. The CMP rule defintion can optionally include a `frame` component that should be the _prefix_ of the expected frame URL. Checks and actions can then add `"frame": true` to indicate that the check or action should be done on the iframe's document (rather than main frame).
157
+ Any rule can include the `"optional": true` to ignore failure.
144
158
 
145
- #### Optional actions
159
+ ## API
146
160
 
147
- Any rule can include the `"optional": true` to ignore failure.
161
+ See [this document](/api.md) for more details on internal APIs.
148
162
 
149
- ### License
163
+ ## License
150
164
 
151
165
  MPLv2.
package/rollup.config.js CHANGED
@@ -1,14 +1,19 @@
1
+ import json from '@rollup/plugin-json';
1
2
  import typescript from '@rollup/plugin-typescript';
3
+ import copy from 'rollup-plugin-copy';
4
+ import { terser } from "rollup-plugin-terser";
2
5
  import pkg from './package.json';
3
6
 
4
7
  export default [{
5
- input: './lib/node.ts',
8
+ input: './playwright/content.ts',
6
9
  output: [{
7
- file: 'dist/autoconsent.puppet.js',
8
- format: 'cjs'
10
+ file: 'dist/autoconsent.playwright.js',
11
+ format: 'iife'
9
12
  }],
10
13
  plugins: [
14
+ json(),
11
15
  typescript(),
16
+ terser(),
12
17
  ]
13
18
  }, {
14
19
  input: './lib/web.ts',
@@ -22,33 +27,50 @@ export default [{
22
27
  }],
23
28
  plugins: [
24
29
  typescript(),
25
- ]
30
+ terser(),
31
+ ],
26
32
  }, {
27
33
  input: './addon/background.ts',
28
34
  output: [{
29
- file: './addon/background.bundle.js',
35
+ file: './dist/addon-mv3/background.bundle.js',
36
+ format: 'iife',
37
+ }, {
38
+ file: './dist/addon-firefox/background.bundle.js',
30
39
  format: 'iife',
31
40
  }],
32
41
  plugins: [
33
42
  typescript(),
43
+ terser(),
44
+ copy({
45
+ targets: [
46
+ {
47
+ src: ['./addon/icons', './rules/rules.json'],
48
+ dest: ['./dist/addon-firefox/', './dist/addon-mv3/']
49
+ },
50
+ {
51
+ src: './addon/manifest.mv3.json',
52
+ dest: './dist/addon-mv3',
53
+ rename: 'manifest.json',
54
+ },
55
+ {
56
+ src: './addon/manifest.firefox.json',
57
+ dest: './dist/addon-firefox',
58
+ rename: 'manifest.json',
59
+ },
60
+ ]
61
+ })
34
62
  ]
35
63
  }, {
36
64
  input: './addon/content.ts',
37
65
  output: [{
38
- file: './addon/content.bundle.js',
66
+ file: './dist/addon-mv3/content.bundle.js',
39
67
  format: 'iife',
40
- }],
41
- plugins: [
42
- typescript(),
43
- ]
44
- }, {
45
- input: './addon/test.ts',
46
- output: [{
47
- file: './addon/test.bundle.js',
68
+ }, {
69
+ file: './dist/addon-firefox/content.bundle.js',
48
70
  format: 'iife',
49
- external: ['chai', 'mocha']
50
71
  }],
51
72
  plugins: [
52
73
  typescript(),
53
- ]
74
+ terser(),
75
+ ],
54
76
  }];
@@ -3,5 +3,6 @@
3
3
  "isHidingRule": true,
4
4
  "detectCmp": [{ "exists": ".gdpr-popup__message" }],
5
5
  "detectPopup": [{ "visible": ".gdpr-popup__message" }],
6
- "optOut": [{ "hide": [".gdpr-popup__message"]}]
6
+ "optOut": [{ "hide": [".gdpr-popup__message"]}],
7
+ "optIn": [{ "click": [".gdpr-popup__message button"]}]
7
8
  }
@@ -1,9 +1,13 @@
1
1
  {
2
2
  "name": "baden-wuerttemberg.de",
3
- "isHidingRule": true,
4
3
  "prehideSelectors": [".cookie-alert.t-dark"],
5
4
  "detectCmp": [{ "exists": ".cookie-alert.t-dark" }],
6
5
  "detectPopup": [{ "visible": ".cookie-alert.t-dark" }],
7
- "optIn": [{ "click": ".cookie-alert__button" }],
8
- "optOut": []
6
+ "optIn": [
7
+ { "click": ".cookie-alert__form input:not([disabled]):not([checked])" },
8
+ { "click": ".cookie-alert__button button" }
9
+ ],
10
+ "optOut": [
11
+ { "hide": [".cookie-alert.t-dark"] }
12
+ ]
9
13
  }
@@ -6,7 +6,11 @@
6
6
  "optIn": [{ "click": ".bpa-accept-all-button" }],
7
7
  "optOut": [
8
8
  {
9
- "click": ".bpa-close-button"
9
+ "wait": 500,
10
+ "comment": "click is not immediately recognized"
11
+ },
12
+ {
13
+ "waitForThenClick": ".bpa-close-button"
10
14
  }
11
15
  ],
12
16
  "test": [
@@ -1,7 +1,6 @@
1
1
  {
2
2
  "name": "cc_banner",
3
3
  "prehideSelectors": [".cc_banner-wrapper"],
4
- "isHidingRule": true,
5
4
  "detectCmp": [{ "exists": ".cc_banner-wrapper" }],
6
5
  "detectPopup": [{ "visible": ".cc_banner" }],
7
6
  "optIn": [{ "click": ".cc_btn_accept_all" }],
@@ -3,7 +3,7 @@
3
3
  "prehideSelectors": ["#cookie-law-info-bar"],
4
4
  "detectCmp": [{ "exists": "#cookie-law-info-bar" }],
5
5
  "detectPopup": [{ "visible": "#cookie-law-info-bar" }],
6
- "optIn": [{ "click": "[data-cli_action=\"accept\"]" }],
6
+ "optIn": [{ "click": "[data-cli_action=\"accept_all\"]" }],
7
7
  "optOut": [
8
8
  { "hide": ["#cookie-law-info-bar"] },
9
9
  {
@@ -1,9 +1,8 @@
1
1
  {
2
2
  "name": "cookie-notice",
3
3
  "prehideSelectors": ["#cookie-notice"],
4
- "isHidingRule": true,
5
4
  "detectCmp": [{ "exists": "#cookie-notice" }],
6
5
  "detectPopup": [{ "visible": "#cookie-notice" }],
7
- "optIn": [{ "hide": ["#cn-accept-cookie"] }],
6
+ "optIn": [{ "click": ["#cn-accept-cookie"] }],
8
7
  "optOut": [{ "hide": ["#cookie-notice"] }]
9
8
  }
@@ -1,9 +1,8 @@
1
1
  {
2
2
  "name": "cookieconsent",
3
- "prehideSelectors": ["[aria-label=\"cookieconsent\"]"],
4
- "isHidingRule": true,
5
- "detectCmp": [{ "exists": "[aria-label=\"cookieconsent\"]" }],
6
- "detectPopup": [{ "visible": "[aria-label=\"cookieconsent\"]" }],
3
+ "prehideSelectors": ["[aria-label=\"cookieconsent\"][aria-describedby=\"cookieconsent:desc\"]"],
4
+ "detectCmp": [{ "exists": "[aria-label=\"cookieconsent\"][aria-describedby=\"cookieconsent:desc\"]" }],
5
+ "detectPopup": [{ "visible": "[aria-label=\"cookieconsent\"][aria-describedby=\"cookieconsent:desc\"]" }],
7
6
  "optIn": [{ "click": ".cc-dismiss" }],
8
- "optOut": [{ "hide": ["[aria-label=\"cookieconsent\"]"] }]
9
- }
7
+ "optOut": [{ "hide": ["[aria-label=\"cookieconsent\"][aria-describedby=\"cookieconsent:desc\"]"] }]
8
+ }
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "destatis.de",
3
3
  "prehideSelectors": ["div[aria-labelledby=cookiebannerhead]"],
4
- "isHidingRule": true,
5
4
  "detectCmp": [{ "exists": ".cookiebannerbox" }],
6
5
  "detectPopup": [{ "visible": ".cookiebannerbox" }],
7
- "optOut": [{ "hide": [".cookiebannerbox"] }]
6
+ "optOut": [{ "hide": [".cookiebannerbox"] }],
7
+ "optIn": [{ "click": [".cookiebannerbox .close"] }]
8
8
  }
@@ -3,7 +3,7 @@
3
3
  "prehideSelectors": ["div[data-testid=cookie-consent-modal-backdrop]"],
4
4
  "detectCmp": [{ "exists": "div[data-testid=cookie-consent-message-contents]"}],
5
5
  "detectPopup": [{ "visible": "div[data-testid=cookie-consent-message-contents]" }],
6
- "optIn": [{ "click": "" }],
6
+ "optIn": [{ "click": "[data-testid=\"cookie-consent-allow-all\"]" }],
7
7
  "optOut": [
8
8
  {
9
9
  "click": "button[data-testid=cookie-consent-adjust-settings]"
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "name": "etsy",
3
+ "prehideSelectors": ["#gdpr-single-choice-overlay", "#gdpr-privacy-settings"],
3
4
  "detectCmp": [{ "exists": "#gdpr-single-choice-overlay" }],
4
5
  "detectPopup": [{ "visible": "#gdpr-single-choice-overlay" }],
5
6
  "optOut": [
6
- {"hide": ["#gdpr-single-choice-overlay", "#gdpr-privacy-settings"]},
7
7
  {"click": "button[data-gdpr-open-full-settings]"},
8
- {"wait": 500},
9
- {"eval": "document.querySelectorAll('.gdpr-overlay-body input').forEach(toggle => { toggle.checked = false; }) || true"},
8
+ {"waitForVisible": ".gdpr-overlay-body input", "timeout": 3000},
9
+ {"wait": 1000},
10
+ {"eval": "document.querySelectorAll(\".gdpr-overlay-body input\").forEach(toggle => { toggle.checked = false; }) || true"},
10
11
  {"eval": "document.querySelector('.gdpr-overlay-view button[data-wt-overlay-close]').click() || true"}
11
12
  ],
12
13
  "optIn": [{"click": "button[data-gdpr-single-choice-accept]"}]
@@ -1,6 +1,5 @@
1
1
  {
2
2
  "name": "eu-cookie-compliance-banner",
3
- "isHidingRule": true,
4
3
  "detectCmp": [{ "exists": ".eu-cookie-compliance-banner-info" }],
5
4
  "detectPopup": [{ "visible": ".eu-cookie-compliance-banner-info" }],
6
5
  "optIn": [{ "click": ".agree-button" }],
@@ -1,6 +1,6 @@
1
1
  {
2
- "name": "hl.co.uk",
3
- "prehideSelectors": [".cookieModalContent", "#cookie-banner-overlay"],
2
+ "name": "hl.co.uk",
3
+ "prehideSelectors": [".cookieModalContent", "#cookie-banner-overlay"],
4
4
  "detectCmp": [{ "exists": "#cookie-banner-overlay" }],
5
5
  "detectPopup": [{ "exists": "#cookie-banner-overlay" }],
6
6
  "optIn": [{ "click": "#acceptCookieButton" }],
@@ -12,19 +12,18 @@
12
12
  "hide": [".cookieSettingsModal"]
13
13
  },
14
14
  {
15
- "wait": 500
15
+ "waitFor": "#AOCookieToggle"
16
16
  },
17
17
  {
18
- "click": "#AOCookieToggle"
18
+ "click": "#AOCookieToggle[aria-pressed=true]",
19
+ "optional": true
19
20
  },
20
21
  {
21
- "eval": "document.querySelector('#AOCookieToggle').getAttribute('aria-pressed') === 'false'"
22
+ "waitFor": "#TPCookieToggle"
22
23
  },
23
24
  {
24
- "click": "#TPCookieToggle"
25
- },
26
- {
27
- "eval": "document.querySelector('#TPCookieToggle').getAttribute('aria-pressed') === 'false'"
25
+ "click": "#TPCookieToggle[aria-pressed=true]",
26
+ "optional": true
28
27
  },
29
28
  {
30
29
  "click": "#updateCookieButton"
@@ -6,8 +6,11 @@
6
6
  "optOut": [
7
7
  {"click": "button[data-test^=manage-cookies]"},
8
8
  {"wait": "500"},
9
- {"eval": "!!Array.from(document.querySelectorAll('label[data-test^=toggle]')).forEach(e => e.click())", "optional": true },
10
- {"eval": "Array.from(document.querySelectorAll('label[data-test^=toggle]')).filter(e => e.className.match('checked') && !e.className.match('disabled')).length === 0"},
9
+ {
10
+ "click": "label[data-test^=toggle][class*=checked]:not([class*=disabled])",
11
+ "all": true,
12
+ "optional": true
13
+ },
11
14
  {"click": "button[data-test=save-preferences]"}
12
15
  ],
13
16
  "optIn": [{ "click": "button[data-test=allow-all]"}]
@@ -3,5 +3,6 @@
3
3
  "isHidingRule": true,
4
4
  "detectCmp": [{ "exists": ".navigation-cookiebbanner" }],
5
5
  "detectPopup": [{ "visible": ".navigation-cookiebbanner" }],
6
- "optOut": [{ "hide": [".navigation-cookiebbanner"]}]
6
+ "optOut": [{ "hide": [".navigation-cookiebbanner"]}],
7
+ "optIn": [{ "click": [".navigation-cookiebbanner__submit"]}]
7
8
  }