@duckduckgo/autoconsent 1.0.2

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 (99) hide show
  1. package/.eslintrc +12 -0
  2. package/Dockerfile +9 -0
  3. package/Jenkinsfile +50 -0
  4. package/LICENSE +373 -0
  5. package/cosmetics/rules.json +110 -0
  6. package/dist/autoconsent.cjs.js +1316 -0
  7. package/dist/autoconsent.esm.js +1308 -0
  8. package/dist/autoconsent.puppet.js +980 -0
  9. package/lib/cmps/all.js +17 -0
  10. package/lib/cmps/all.ts +21 -0
  11. package/lib/cmps/base.js +170 -0
  12. package/lib/cmps/base.ts +199 -0
  13. package/lib/cmps/consentmanager.js +31 -0
  14. package/lib/cmps/consentmanager.ts +39 -0
  15. package/lib/cmps/cookiebot.js +73 -0
  16. package/lib/cmps/cookiebot.ts +81 -0
  17. package/lib/cmps/evidon.js +26 -0
  18. package/lib/cmps/evidon.ts +32 -0
  19. package/lib/cmps/sourcepoint.js +82 -0
  20. package/lib/cmps/sourcepoint.ts +95 -0
  21. package/lib/cmps/trustarc.js +106 -0
  22. package/lib/cmps/trustarc.ts +147 -0
  23. package/lib/consentomatic/index.js +52 -0
  24. package/lib/consentomatic/index.ts +86 -0
  25. package/lib/detector.js +29 -0
  26. package/lib/detector.ts +30 -0
  27. package/lib/hider.js +13 -0
  28. package/lib/hider.ts +16 -0
  29. package/lib/index.js +4 -0
  30. package/lib/index.ts +6 -0
  31. package/lib/messages.d.ts +58 -0
  32. package/lib/node.js +30 -0
  33. package/lib/node.ts +38 -0
  34. package/lib/puppet/tab.js +114 -0
  35. package/lib/puppet/tab.ts +136 -0
  36. package/lib/rules.d.ts +80 -0
  37. package/lib/tabwrapper.js +61 -0
  38. package/lib/tabwrapper.ts +68 -0
  39. package/lib/types.d.ts +61 -0
  40. package/lib/web/consentomatic/index.js +188 -0
  41. package/lib/web/consentomatic/index.ts +249 -0
  42. package/lib/web/consentomatic/tools.js +177 -0
  43. package/lib/web/consentomatic/tools.ts +198 -0
  44. package/lib/web/content.js +91 -0
  45. package/lib/web/content.ts +83 -0
  46. package/lib/web/tab.js +106 -0
  47. package/lib/web/tab.ts +171 -0
  48. package/lib/web.js +90 -0
  49. package/lib/web.ts +109 -0
  50. package/package.json +41 -0
  51. package/playwright.config.ts +31 -0
  52. package/readme.md +151 -0
  53. package/rollup.config.js +54 -0
  54. package/rules/autoconsent/asus.json +7 -0
  55. package/rules/autoconsent/cc-banner.json +9 -0
  56. package/rules/autoconsent/cookie-law-info.json +14 -0
  57. package/rules/autoconsent/cookie-notice.json +9 -0
  58. package/rules/autoconsent/cookieconsent.json +9 -0
  59. package/rules/autoconsent/drupal.json +7 -0
  60. package/rules/autoconsent/eu-cookie-compliance.json +14 -0
  61. package/rules/autoconsent/fundingchoices.json +12 -0
  62. package/rules/autoconsent/hubspot.json +7 -0
  63. package/rules/autoconsent/klaro.json +10 -0
  64. package/rules/autoconsent/notice-cookie.json +9 -0
  65. package/rules/autoconsent/onetrust.json +24 -0
  66. package/rules/autoconsent/osano.json +11 -0
  67. package/rules/autoconsent/quantcast.json +14 -0
  68. package/rules/autoconsent/tealium.json +19 -0
  69. package/rules/autoconsent/testcmp.json +12 -0
  70. package/rules/build.js +63 -0
  71. package/rules/rules.json +3030 -0
  72. package/tests/asus.spec.ts +5 -0
  73. package/tests/ccbanner.spec.ts +11 -0
  74. package/tests/consentmanager.spec.ts +10 -0
  75. package/tests/cookiebot.spec.ts +9 -0
  76. package/tests/cookieconsent.spec.ts +8 -0
  77. package/tests/cookielawinfo.spec.ts +9 -0
  78. package/tests/cookienotice.spec.ts +6 -0
  79. package/tests/didomi.spec.ts +9 -0
  80. package/tests/eu-cookie-compliance-banner.spec.ts +7 -0
  81. package/tests/evidon.spec.ts +6 -0
  82. package/tests/fundingchoices.spec.ts +10 -0
  83. package/tests/hubspot.spec.ts +6 -0
  84. package/tests/klaro.spec.ts +5 -0
  85. package/tests/notice-cookie.spec.ts +7 -0
  86. package/tests/oil.spec.ts +10 -0
  87. package/tests/onetrust.spec.ts +15 -0
  88. package/tests/optanon.spec.ts +10 -0
  89. package/tests/osano.spec.ts +5 -0
  90. package/tests/quantcast.spec.ts +16 -0
  91. package/tests/runner.ts +61 -0
  92. package/tests/sourcepoint.spec.ts +17 -0
  93. package/tests/springer.spec.ts +11 -0
  94. package/tests/tealium.spec.ts +6 -0
  95. package/tests/testcmp.spec.ts +5 -0
  96. package/tests/trustarc.spec.ts +13 -0
  97. package/tests/wordpressgdpr.spec.ts +9 -0
  98. package/tsconfig.json +14 -0
  99. package/update_version.js +8 -0
@@ -0,0 +1,82 @@
1
+ import AutoConsentBase, { success, waitFor } from "./base";
2
+ export default class SourcePoint extends AutoConsentBase {
3
+ constructor() {
4
+ super("Sourcepoint");
5
+ this.ccpaMode = false;
6
+ this.prehideSelectors = ["div[id^='sp_message_container_'],.message-overlay"];
7
+ }
8
+ detectFrame(_, frame) {
9
+ try {
10
+ const url = new URL(frame.url);
11
+ if (url.searchParams.has('message_id') && url.hostname === 'ccpa-notice.sp-prod.net') {
12
+ this.ccpaMode = true;
13
+ return true;
14
+ }
15
+ return (url.pathname === '/index.html' || url.pathname === '/privacy-manager/index.html')
16
+ && url.searchParams.has('message_id') && url.searchParams.has('requestUUID');
17
+ }
18
+ catch (e) {
19
+ return false;
20
+ }
21
+ }
22
+ async detectCmp(tab) {
23
+ return await tab.elementExists("div[id^='sp_message_container_']") || !!tab.frame;
24
+ }
25
+ async detectPopup(tab) {
26
+ return await tab.elementsAreVisible("div[id^='sp_message_container_']");
27
+ }
28
+ async optIn(tab) {
29
+ return tab.clickElement(".sp_choice_type_11", tab.frame.id);
30
+ }
31
+ isManagerOpen(tab) {
32
+ return tab.frame && new URL(tab.frame.url).pathname === "/privacy-manager/index.html";
33
+ }
34
+ async optOut(tab) {
35
+ try {
36
+ tab.hideElements(["div[id^='sp_message_container_']"]);
37
+ if (!this.isManagerOpen(tab)) {
38
+ if (!await waitFor(() => !!tab.frame, 30, 100)) {
39
+ throw "Frame never opened";
40
+ }
41
+ if (!await tab.elementExists("button.sp_choice_type_12", tab.frame.id)) {
42
+ // do not sell button
43
+ return tab.clickElement('button.sp_choice_type_13', tab.frame.id);
44
+ }
45
+ await success(tab.clickElement("button.sp_choice_type_12", tab.frame.id));
46
+ await waitFor(() => new URL(tab.frame.url).pathname === "/privacy-manager/index.html", 200, 100);
47
+ }
48
+ await tab.waitForElement('.type-modal', 20000, tab.frame.id);
49
+ // reject all button is offered by some sites
50
+ try {
51
+ const path = await Promise.race([
52
+ tab.waitForElement('.sp_choice_type_REJECT_ALL', 2000, tab.frame.id).then(r => 0),
53
+ tab.waitForElement('.reject-toggle', 2000, tab.frame.id).then(() => 1),
54
+ tab.waitForElement('.pm-features', 2000, tab.frame.id).then(r => 2),
55
+ ]);
56
+ if (path === 0) {
57
+ await tab.wait(1000);
58
+ return await success(tab.clickElement('.sp_choice_type_REJECT_ALL', tab.frame.id));
59
+ }
60
+ else if (path === 1) {
61
+ await tab.clickElement('.reject-toggle', tab.frame.id);
62
+ }
63
+ else {
64
+ await tab.waitForElement('.pm-features', 10000, tab.frame.id);
65
+ await tab.clickElements('.checked > span', tab.frame.id);
66
+ if (await tab.elementExists('.chevron', tab.frame.id)) {
67
+ await tab.clickElement('.chevron', tab.frame.id);
68
+ }
69
+ }
70
+ }
71
+ catch (e) { }
72
+ return await tab.clickElement('.sp_choice_type_SAVE_AND_EXIT', tab.frame.id);
73
+ }
74
+ finally {
75
+ tab.undoHideElements();
76
+ }
77
+ }
78
+ async test(tab) {
79
+ await tab.eval("__tcfapi('getTCData', 2, r => window.__rcsResult = r)");
80
+ return tab.eval("Object.values(window.__rcsResult.purpose.consents).every(c => !c)");
81
+ }
82
+ }
@@ -0,0 +1,95 @@
1
+ import AutoConsentBase, { success, waitFor } from "./base";
2
+ import { TabActor } from "../types";
3
+
4
+ export default class SourcePoint extends AutoConsentBase {
5
+
6
+ ccpaMode = false;
7
+ prehideSelectors = ["div[id^='sp_message_container_'],.message-overlay"]
8
+
9
+ constructor() {
10
+ super("Sourcepoint");
11
+ }
12
+
13
+ detectFrame(_: TabActor, frame: { url: string }) {
14
+ try {
15
+ const url = new URL(frame.url);
16
+ if (url.searchParams.has('message_id') && url.hostname === 'ccpa-notice.sp-prod.net') {
17
+ this.ccpaMode = true;
18
+ return true;
19
+ }
20
+ return (url.pathname === '/index.html' || url.pathname === '/privacy-manager/index.html')
21
+ && url.searchParams.has('message_id') && url.searchParams.has('requestUUID');
22
+ } catch (e) {
23
+ return false;
24
+ }
25
+ }
26
+
27
+ async detectCmp(tab: TabActor) {
28
+ return await tab.elementExists("div[id^='sp_message_container_']") || !!tab.frame
29
+ }
30
+
31
+ async detectPopup(tab: TabActor) {
32
+ return await tab.elementsAreVisible("div[id^='sp_message_container_']");
33
+ }
34
+
35
+ async optIn(tab: TabActor) {
36
+ return tab.clickElement(".sp_choice_type_11", tab.frame.id);
37
+ }
38
+
39
+ isManagerOpen(tab: TabActor) {
40
+ return tab.frame && new URL(tab.frame.url).pathname === "/privacy-manager/index.html";
41
+ }
42
+
43
+ async optOut(tab: TabActor) {
44
+ try {
45
+ tab.hideElements(["div[id^='sp_message_container_']"])
46
+ if (!this.isManagerOpen(tab)) {
47
+ if (!await waitFor(() => !!tab.frame, 30, 100)) {
48
+ throw "Frame never opened";
49
+ }
50
+ if (!await tab.elementExists("button.sp_choice_type_12", tab.frame.id)) {
51
+ // do not sell button
52
+ return tab.clickElement('button.sp_choice_type_13', tab.frame.id);
53
+ }
54
+ await success(tab.clickElement("button.sp_choice_type_12", tab.frame.id));
55
+ await waitFor(
56
+ () =>
57
+ new URL(tab.frame.url).pathname === "/privacy-manager/index.html",
58
+ 200,
59
+ 100
60
+ );
61
+ }
62
+ await tab.waitForElement('.type-modal', 20000, tab.frame.id);
63
+ // reject all button is offered by some sites
64
+ try {
65
+ const path = await Promise.race([
66
+ tab.waitForElement('.sp_choice_type_REJECT_ALL', 2000, tab.frame.id).then(r => 0),
67
+ tab.waitForElement('.reject-toggle', 2000, tab.frame.id).then(() => 1),
68
+ tab.waitForElement('.pm-features', 2000, tab.frame.id).then(r => 2),
69
+ ]);
70
+ if (path === 0) {
71
+ await tab.wait(1000);
72
+ return await success(tab.clickElement('.sp_choice_type_REJECT_ALL', tab.frame.id))
73
+ } else if (path === 1) {
74
+ await tab.clickElement('.reject-toggle', tab.frame.id)
75
+ } else {
76
+ await tab.waitForElement('.pm-features', 10000, tab.frame.id);
77
+ await tab.clickElements('.checked > span', tab.frame.id);
78
+ if (await tab.elementExists('.chevron', tab.frame.id)) {
79
+ await tab.clickElement('.chevron', tab.frame.id)
80
+ }
81
+ }
82
+ } catch(e) {}
83
+ return await tab.clickElement('.sp_choice_type_SAVE_AND_EXIT', tab.frame.id);
84
+ } finally {
85
+ tab.undoHideElements();
86
+ }
87
+ }
88
+
89
+ async test(tab: TabActor) {
90
+ await tab.eval("__tcfapi('getTCData', 2, r => window.__rcsResult = r)");
91
+ return tab.eval(
92
+ "Object.values(window.__rcsResult.purpose.consents).every(c => !c)"
93
+ );
94
+ }
95
+ }
@@ -0,0 +1,106 @@
1
+ import AutoConsentBase, { waitFor } from "./base";
2
+ export default class TrustArc extends AutoConsentBase {
3
+ constructor() {
4
+ super("TrustArc");
5
+ this.prehideSelectors = [
6
+ ".trustarc-banner-container",
7
+ ".truste_popframe,.truste_overlay,.truste_box_overlay,#truste-consent-track",
8
+ ];
9
+ }
10
+ detectFrame(_, frame) {
11
+ return frame.url.startsWith("https://consent-pref.trustarc.com/?");
12
+ }
13
+ async detectCmp(tab) {
14
+ if (tab.frame &&
15
+ tab.frame.url.startsWith("https://consent-pref.trustarc.com/?")) {
16
+ return true;
17
+ }
18
+ return tab.elementExists("#truste-show-consent");
19
+ }
20
+ async detectPopup(tab) {
21
+ return ((await tab.elementsAreVisible("#truste-consent-content,#trustarc-banner-overlay")) ||
22
+ (tab.frame &&
23
+ (await tab.waitForElement("#defaultpreferencemanager", 5000, tab.frame.id))));
24
+ }
25
+ async openFrame(tab) {
26
+ if (await tab.elementExists("#truste-show-consent")) {
27
+ await tab.clickElement("#truste-show-consent");
28
+ }
29
+ }
30
+ async navigateToSettings(tab, frameId) {
31
+ // wait for it to load
32
+ await waitFor(async () => {
33
+ return ((await tab.elementExists(".shp", frameId)) ||
34
+ (await tab.elementsAreVisible(".advance", "any", frameId)) ||
35
+ tab.elementExists(".switch span:first-child", frameId));
36
+ }, 10, 500);
37
+ // splash screen -> hit more information
38
+ if (await tab.elementExists(".shp", frameId)) {
39
+ await tab.clickElement(".shp", frameId);
40
+ }
41
+ await tab.waitForElement(".prefPanel", 5000, frameId);
42
+ // go to advanced settings if not yet shown
43
+ if (await tab.elementsAreVisible(".advance", "any", frameId)) {
44
+ await tab.clickElement(".advance", frameId);
45
+ }
46
+ // takes a while to load the opt-in/opt-out buttons
47
+ return await waitFor(() => tab.elementsAreVisible(".switch span:first-child", "any", frameId), 5, 1000);
48
+ }
49
+ async optOut(tab) {
50
+ // await tab.hideElements(['.truste_overlay', '.truste_box_overlay', '.trustarc-banner', '.truste-banner']);
51
+ if (await tab.elementExists("#truste-consent-required")) {
52
+ return tab.clickElement("#truste-consent-required");
53
+ }
54
+ if (!tab.frame) {
55
+ await tab.clickElement("#truste-show-consent");
56
+ await waitFor(async () => !!tab.frame &&
57
+ (await tab.elementsAreVisible(".mainContent", "any", tab.frame.id)), 50, 100);
58
+ }
59
+ const frameId = tab.frame.id;
60
+ await waitFor(() => tab.eval("document.readyState === 'complete'", frameId), 20, 100);
61
+ tab.hideElements([".truste_popframe", ".truste_overlay", ".truste_box_overlay", "#truste-consent-track"]);
62
+ if (await tab.elementExists('.rejectAll', frameId)) {
63
+ return tab.clickElement('.rejectAll', frameId);
64
+ }
65
+ if (await tab.waitForElement('#catDetails0', 1000, frameId)) {
66
+ await tab.clickElement("#catDetails0", frameId);
67
+ return tab.clickElement(".submit", frameId);
68
+ }
69
+ if (await tab.elementExists(".required", frameId)) {
70
+ await tab.clickElement(".required", frameId);
71
+ }
72
+ else {
73
+ await this.navigateToSettings(tab, frameId);
74
+ await tab.clickElements(".switch span:nth-child(1):not(.active)", frameId);
75
+ await tab.clickElement(".submit", frameId);
76
+ }
77
+ try {
78
+ await tab.waitForThenClick("#gwt-debug-close_id", 20000, tab.frame.id);
79
+ }
80
+ catch (e) {
81
+ // ignore frame disappearing
82
+ }
83
+ return true;
84
+ }
85
+ async optIn(tab) {
86
+ if (!tab.frame) {
87
+ await this.openFrame(tab);
88
+ await waitFor(() => !!tab.frame, 10, 200);
89
+ }
90
+ const frameId = tab.frame.id;
91
+ await this.navigateToSettings(tab, frameId);
92
+ await tab.clickElements(".switch span:nth-child(2)", frameId);
93
+ await tab.clickElement(".submit", frameId);
94
+ await waitFor(() => tab.elementExists("#gwt-debug-close_id", frameId), 300, 1000);
95
+ await tab.clickElement("#gwt-debug-close_id", frameId);
96
+ return true;
97
+ }
98
+ async openCmp(tab) {
99
+ await tab.eval("truste.eu.clickListener()");
100
+ return true;
101
+ }
102
+ async test() {
103
+ // TODO: find out how to test TrustArc
104
+ return true;
105
+ }
106
+ }
@@ -0,0 +1,147 @@
1
+ import AutoConsentBase, { waitFor } from "./base";
2
+ import { TabActor } from "../types";
3
+
4
+ export default class TrustArc extends AutoConsentBase {
5
+
6
+ prehideSelectors = [
7
+ ".trustarc-banner-container",
8
+ ".truste_popframe,.truste_overlay,.truste_box_overlay,#truste-consent-track",
9
+ ]
10
+
11
+ constructor() {
12
+ super("TrustArc");
13
+ }
14
+
15
+ detectFrame(_: TabActor, frame: { url: string }) {
16
+ return frame.url.startsWith("https://consent-pref.trustarc.com/?");
17
+ }
18
+
19
+ async detectCmp(tab: TabActor) {
20
+ if (
21
+ tab.frame &&
22
+ tab.frame.url.startsWith("https://consent-pref.trustarc.com/?")
23
+ ) {
24
+ return true;
25
+ }
26
+ return tab.elementExists("#truste-show-consent");
27
+ }
28
+
29
+ async detectPopup(tab: TabActor) {
30
+ return (
31
+ (await tab.elementsAreVisible("#truste-consent-content,#trustarc-banner-overlay")) ||
32
+ (tab.frame &&
33
+ (await tab.waitForElement(
34
+ "#defaultpreferencemanager",
35
+ 5000,
36
+ tab.frame.id
37
+ )))
38
+ );
39
+ }
40
+
41
+ async openFrame(tab: TabActor) {
42
+ if (await tab.elementExists("#truste-show-consent")) {
43
+ await tab.clickElement("#truste-show-consent");
44
+ }
45
+ }
46
+
47
+ async navigateToSettings(tab: TabActor, frameId: number) {
48
+ // wait for it to load
49
+ await waitFor(
50
+ async () => {
51
+ return (
52
+ (await tab.elementExists(".shp", frameId)) ||
53
+ (await tab.elementsAreVisible(".advance", "any", frameId)) ||
54
+ tab.elementExists(".switch span:first-child", frameId)
55
+ );
56
+ },
57
+ 10,
58
+ 500
59
+ );
60
+ // splash screen -> hit more information
61
+ if (await tab.elementExists(".shp", frameId)) {
62
+ await tab.clickElement(".shp", frameId);
63
+ }
64
+ await tab.waitForElement(".prefPanel", 5000, frameId);
65
+ // go to advanced settings if not yet shown
66
+ if (await tab.elementsAreVisible(".advance", "any", frameId)) {
67
+ await tab.clickElement(".advance", frameId);
68
+ }
69
+ // takes a while to load the opt-in/opt-out buttons
70
+ return await waitFor(
71
+ () => tab.elementsAreVisible(".switch span:first-child", "any", frameId),
72
+ 5,
73
+ 1000
74
+ );
75
+ }
76
+
77
+ async optOut(tab: TabActor) {
78
+ // await tab.hideElements(['.truste_overlay', '.truste_box_overlay', '.trustarc-banner', '.truste-banner']);
79
+ if (await tab.elementExists("#truste-consent-required")) {
80
+ return tab.clickElement("#truste-consent-required");
81
+ }
82
+ if (!tab.frame) {
83
+ await tab.clickElement("#truste-show-consent");
84
+ await waitFor(
85
+ async () =>
86
+ !!tab.frame &&
87
+ (await tab.elementsAreVisible(".mainContent", "any", tab.frame.id)),
88
+ 50,
89
+ 100
90
+ );
91
+ }
92
+ const frameId = tab.frame!.id;
93
+ await waitFor(() => tab.eval("document.readyState === 'complete'", frameId), 20, 100);
94
+ tab.hideElements([".truste_popframe", ".truste_overlay", ".truste_box_overlay", "#truste-consent-track"]);
95
+ if (await tab.elementExists('.rejectAll', frameId)) {
96
+ return tab.clickElement('.rejectAll', frameId);
97
+ }
98
+ if (await tab.waitForElement('#catDetails0', 1000, frameId)) {
99
+ await tab.clickElement("#catDetails0", frameId);
100
+ return tab.clickElement(".submit", frameId);
101
+ }
102
+ if (await tab.elementExists(".required", frameId)) {
103
+ await tab.clickElement(".required", frameId);
104
+ } else {
105
+ await this.navigateToSettings(tab, frameId);
106
+ await tab.clickElements(
107
+ ".switch span:nth-child(1):not(.active)",
108
+ frameId
109
+ );
110
+ await tab.clickElement(".submit", frameId);
111
+ }
112
+ try {
113
+ await tab.waitForThenClick("#gwt-debug-close_id", 20000, tab.frame.id);
114
+ } catch (e) {
115
+ // ignore frame disappearing
116
+ }
117
+ return true;
118
+ }
119
+
120
+ async optIn(tab: TabActor) {
121
+ if (!tab.frame) {
122
+ await this.openFrame(tab);
123
+ await waitFor(() => !!tab.frame, 10, 200);
124
+ }
125
+ const frameId = tab.frame!.id;
126
+ await this.navigateToSettings(tab, frameId);
127
+ await tab.clickElements(".switch span:nth-child(2)", frameId);
128
+ await tab.clickElement(".submit", frameId);
129
+ await waitFor(
130
+ () => tab.elementExists("#gwt-debug-close_id", frameId),
131
+ 300,
132
+ 1000
133
+ );
134
+ await tab.clickElement("#gwt-debug-close_id", frameId);
135
+ return true;
136
+ }
137
+
138
+ async openCmp(tab: TabActor) {
139
+ await tab.eval("truste.eu.clickListener()");
140
+ return true;
141
+ }
142
+
143
+ async test() {
144
+ // TODO: find out how to test TrustArc
145
+ return true;
146
+ }
147
+ }
@@ -0,0 +1,52 @@
1
+ export class ConsentOMaticCMP {
2
+ constructor(name, config) {
3
+ this.name = name;
4
+ this.config = config;
5
+ this.methods = new Map();
6
+ config.methods.forEach(methodConfig => {
7
+ if (methodConfig.action) {
8
+ this.methods.set(methodConfig.name, methodConfig.action);
9
+ }
10
+ });
11
+ this.hasSelfTest = this.methods.has("TEST_CONSENT");
12
+ }
13
+ async detectCmp(tab) {
14
+ return (await Promise.all(this.config.detectors.map(detectorConfig => tab.matches(detectorConfig.presentMatcher)))).some(matched => matched);
15
+ }
16
+ async detectPopup(tab) {
17
+ return (await Promise.all(this.config.detectors.map(detectorConfig => tab.matches(detectorConfig.showingMatcher)))).some(matched => matched);
18
+ }
19
+ async executeAction(tab, method, param) {
20
+ if (this.methods.has(method)) {
21
+ return tab.executeAction(this.methods.get(method), param);
22
+ }
23
+ return true;
24
+ }
25
+ async optOut(tab) {
26
+ await this.executeAction(tab, "HIDE_CMP");
27
+ await this.executeAction(tab, "OPEN_OPTIONS");
28
+ await this.executeAction(tab, "HIDE_CMP");
29
+ await this.executeAction(tab, "DO_CONSENT", []);
30
+ await this.executeAction(tab, "SAVE_CONSENT");
31
+ return true;
32
+ }
33
+ async optIn(tab) {
34
+ await this.executeAction(tab, "HIDE_CMP");
35
+ await this.executeAction(tab, "OPEN_OPTIONS");
36
+ await this.executeAction(tab, "HIDE_CMP");
37
+ await this.executeAction(tab, "DO_CONSENT", ['D', 'A', 'B', 'E', 'F', 'X']);
38
+ await this.executeAction(tab, "SAVE_CONSENT");
39
+ return true;
40
+ }
41
+ async openCmp(tab) {
42
+ await this.executeAction(tab, "HIDE_CMP");
43
+ await this.executeAction(tab, "OPEN_OPTIONS");
44
+ return true;
45
+ }
46
+ test(tab) {
47
+ return this.executeAction(tab, "TEST_CONSENT");
48
+ }
49
+ detectFrame(tab, frame) {
50
+ return false;
51
+ }
52
+ }
@@ -0,0 +1,86 @@
1
+ import { AutoCMP, TabActor } from "../types";
2
+
3
+ export type DetectorConfig = {
4
+ presentMatcher: {};
5
+ showingMatcher: {};
6
+ };
7
+ export type MethodConfig = {
8
+ action?: any;
9
+ name: string;
10
+ };
11
+
12
+ export type ConsentOMaticConfig = {
13
+ detectors: DetectorConfig[];
14
+ methods: MethodConfig[];
15
+ };
16
+
17
+ export class ConsentOMaticCMP implements AutoCMP {
18
+ methods = new Map<string, {}>();
19
+ hasSelfTest: boolean;
20
+
21
+ constructor(public name: string, public config: ConsentOMaticConfig) {
22
+ config.methods.forEach(methodConfig => {
23
+ if (methodConfig.action) {
24
+ this.methods.set(methodConfig.name, methodConfig.action);
25
+ }
26
+ });
27
+ this.hasSelfTest = this.methods.has("TEST_CONSENT");
28
+ }
29
+
30
+ async detectCmp(tab: TabActor): Promise<boolean> {
31
+ return (
32
+ await Promise.all(
33
+ this.config.detectors.map(detectorConfig =>
34
+ tab.matches(detectorConfig.presentMatcher)
35
+ )
36
+ )
37
+ ).some(matched => matched);
38
+ }
39
+
40
+ async detectPopup(tab: TabActor): Promise<boolean> {
41
+ return (
42
+ await Promise.all(
43
+ this.config.detectors.map(detectorConfig =>
44
+ tab.matches(detectorConfig.showingMatcher)
45
+ )
46
+ )
47
+ ).some(matched => matched);
48
+ }
49
+
50
+ async executeAction(tab: TabActor, method: string, param?: any) {
51
+ if (this.methods.has(method)) {
52
+ return tab.executeAction(this.methods.get(method), param);
53
+ }
54
+ return true;
55
+ }
56
+
57
+ async optOut(tab: TabActor): Promise<boolean> {
58
+ await this.executeAction(tab, "HIDE_CMP");
59
+ await this.executeAction(tab, "OPEN_OPTIONS");
60
+ await this.executeAction(tab, "HIDE_CMP");
61
+ await this.executeAction(tab, "DO_CONSENT", []);
62
+ await this.executeAction(tab, "SAVE_CONSENT");
63
+ return true;
64
+ }
65
+
66
+ async optIn(tab: TabActor): Promise<boolean> {
67
+ await this.executeAction(tab, "HIDE_CMP");
68
+ await this.executeAction(tab, "OPEN_OPTIONS");
69
+ await this.executeAction(tab, "HIDE_CMP");
70
+ await this.executeAction(tab, "DO_CONSENT", ['D', 'A', 'B', 'E', 'F', 'X']);
71
+ await this.executeAction(tab, "SAVE_CONSENT");
72
+ return true;
73
+ }
74
+ async openCmp(tab: TabActor): Promise<boolean> {
75
+ await this.executeAction(tab, "HIDE_CMP");
76
+ await this.executeAction(tab, "OPEN_OPTIONS");
77
+ return true;
78
+ }
79
+
80
+ test(tab: TabActor): Promise<boolean> {
81
+ return this.executeAction(tab, "TEST_CONSENT");
82
+ }
83
+ detectFrame(tab: TabActor, frame: { url: string }): boolean {
84
+ return false;
85
+ }
86
+ }
@@ -0,0 +1,29 @@
1
+ export default async function detectDialog(tab, retries, rules) {
2
+ let breakEarly = false;
3
+ const found = await new Promise(async (resolve) => {
4
+ let earlyReturn = false;
5
+ await Promise.all(rules.map(async (r, index) => {
6
+ try {
7
+ if (await r.detectCmp(tab)) {
8
+ earlyReturn = true;
9
+ resolve(index);
10
+ }
11
+ }
12
+ catch (e) {
13
+ breakEarly = true;
14
+ }
15
+ }));
16
+ if (!earlyReturn) {
17
+ resolve(-1);
18
+ }
19
+ });
20
+ if (found === -1 && retries > 0 && !breakEarly) {
21
+ return new Promise((resolve) => {
22
+ setTimeout(async () => {
23
+ const result = detectDialog(tab, retries - 1, rules);
24
+ resolve(result);
25
+ }, 500);
26
+ });
27
+ }
28
+ return found > -1 ? rules[found] : null;
29
+ }
@@ -0,0 +1,30 @@
1
+ import { AutoCMP, TabActor } from './types';
2
+
3
+ export default async function detectDialog(tab: TabActor, retries: number, rules: AutoCMP[]): Promise<AutoCMP> {
4
+ let breakEarly = false;
5
+ const found: number = await new Promise(async (resolve) => {
6
+ let earlyReturn = false;
7
+ await Promise.all(rules.map(async (r, index) => {
8
+ try {
9
+ if (await r.detectCmp(tab)) {
10
+ earlyReturn = true;
11
+ resolve(index)
12
+ }
13
+ } catch (e) {
14
+ breakEarly = true;
15
+ }
16
+ }));
17
+ if (!earlyReturn) {
18
+ resolve(-1)
19
+ }
20
+ })
21
+ if (found === -1 && retries > 0 && !breakEarly) {
22
+ return new Promise((resolve) => {
23
+ setTimeout(async () => {
24
+ const result = detectDialog(tab, retries - 1, rules);
25
+ resolve(result);
26
+ }, 500);
27
+ });
28
+ }
29
+ return found > -1 ? rules[found] : null;
30
+ }
package/lib/hider.js ADDED
@@ -0,0 +1,13 @@
1
+ // hide rules not specific to a single CMP rule
2
+ const globalHidden = [
3
+ "#didomi-popup,.didomi-popup-container,.didomi-popup-notice,.didomi-consent-popup-preferences,#didomi-notice,.didomi-popup-backdrop,.didomi-screen-medium",
4
+ ];
5
+ export default async function prehideElements(tab, rules) {
6
+ const selectors = rules.reduce((selectorList, rule) => {
7
+ if (rule.prehideSelectors) {
8
+ return [...selectorList, ...rule.prehideSelectors];
9
+ }
10
+ return selectorList;
11
+ }, globalHidden);
12
+ await tab.hideElements(selectors);
13
+ }
package/lib/hider.ts ADDED
@@ -0,0 +1,16 @@
1
+ import { AutoCMP, TabActor } from './types';
2
+
3
+ // hide rules not specific to a single CMP rule
4
+ const globalHidden = [
5
+ "#didomi-popup,.didomi-popup-container,.didomi-popup-notice,.didomi-consent-popup-preferences,#didomi-notice,.didomi-popup-backdrop,.didomi-screen-medium",
6
+ ]
7
+
8
+ export default async function prehideElements(tab: TabActor, rules: AutoCMP[]): Promise<void> {
9
+ const selectors = rules.reduce((selectorList, rule) => {
10
+ if (rule.prehideSelectors) {
11
+ return [...selectorList, ...rule.prehideSelectors];
12
+ }
13
+ return selectorList;
14
+ }, globalHidden);
15
+ await tab.hideElements(selectors);
16
+ }
package/lib/index.js ADDED
@@ -0,0 +1,4 @@
1
+ import _rules, { createAutoCMP } from './cmps/all';
2
+ export { waitFor } from './cmps/base';
3
+ export { createAutoCMP };
4
+ export const rules = _rules;
package/lib/index.ts ADDED
@@ -0,0 +1,6 @@
1
+ import _rules, { createAutoCMP } from './cmps/all';
2
+
3
+ export { waitFor } from './cmps/base';
4
+
5
+ export { createAutoCMP };
6
+ export const rules = _rules;