@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,198 @@
1
+ /**
2
+ * This code is in most parts copied from https://github.com/cavi-au/Consent-O-Matic/blob/master/Extension/Tools.js
3
+ * which is licened under the MIT.
4
+ */
5
+ export default class Tools {
6
+ static base: HTMLElement = null;
7
+
8
+ static setBase(base: HTMLElement) {
9
+ Tools.base = base;
10
+ }
11
+
12
+ static findElement(options: any, parent: any = null, multiple = false): HTMLElement[] | HTMLElement {
13
+ let possibleTargets = null;
14
+
15
+ if (parent != null) {
16
+ possibleTargets = Array.from(parent.querySelectorAll(options.selector));
17
+ } else {
18
+ if (Tools.base != null) {
19
+ possibleTargets = Array.from(
20
+ Tools.base.querySelectorAll(options.selector)
21
+ );
22
+ } else {
23
+ possibleTargets = Array.from(
24
+ document.querySelectorAll(options.selector)
25
+ );
26
+ }
27
+ }
28
+
29
+ if (options.textFilter != null) {
30
+ possibleTargets = possibleTargets.filter(possibleTarget => {
31
+ let textContent = possibleTarget.textContent.toLowerCase();
32
+
33
+ if (Array.isArray(options.textFilter)) {
34
+ let foundText = false;
35
+
36
+ for (let text of options.textFilter) {
37
+ if (textContent.indexOf(text.toLowerCase()) !== -1) {
38
+ foundText = true;
39
+ break;
40
+ }
41
+ }
42
+
43
+ return foundText;
44
+ } else if (options.textFilter != null) {
45
+ return textContent.indexOf(options.textFilter.toLowerCase()) !== -1;
46
+ }
47
+ });
48
+ }
49
+
50
+ if (options.styleFilters != null) {
51
+ possibleTargets = possibleTargets.filter(possibleTarget => {
52
+ let styles = window.getComputedStyle(possibleTarget);
53
+
54
+ let keep = true;
55
+
56
+ for (let styleFilter of options.styleFilters) {
57
+ let option = styles[styleFilter.option];
58
+
59
+ if (styleFilter.negated) {
60
+ keep = keep && option !== styleFilter.value;
61
+ } else {
62
+ keep = keep && option === styleFilter.value;
63
+ }
64
+ }
65
+
66
+ return keep;
67
+ });
68
+ }
69
+
70
+ if (options.displayFilter != null) {
71
+ possibleTargets = possibleTargets.filter(possibleTarget => {
72
+ if (options.displayFilter) {
73
+ //We should be displayed
74
+ return possibleTarget.offsetHeight !== 0;
75
+ } else {
76
+ //We should not be displayed
77
+ return possibleTarget.offsetHeight === 0;
78
+ }
79
+ });
80
+ }
81
+
82
+ if (options.iframeFilter != null) {
83
+ possibleTargets = possibleTargets.filter(possibleTarget => {
84
+ if (options.iframeFilter) {
85
+ //We should be inside an iframe
86
+ return window.location !== window.parent.location;
87
+ } else {
88
+ //We should not be inside an iframe
89
+ return window.location === window.parent.location;
90
+ }
91
+ });
92
+ }
93
+
94
+ if (options.childFilter != null) {
95
+ possibleTargets = possibleTargets.filter(possibleTarget => {
96
+ let oldBase = Tools.base;
97
+ Tools.setBase(possibleTarget);
98
+ let childResults = Tools.find(options.childFilter);
99
+ Tools.setBase(oldBase);
100
+ return childResults.target != null;
101
+ });
102
+ }
103
+
104
+ if (multiple) {
105
+ return possibleTargets;
106
+ } else {
107
+ if (possibleTargets.length > 1) {
108
+ console.warn(
109
+ "Multiple possible targets: ",
110
+ possibleTargets,
111
+ options,
112
+ parent
113
+ );
114
+ }
115
+
116
+ return possibleTargets[0];
117
+ }
118
+ }
119
+
120
+ static find(options: any, multiple = false) {
121
+ let results: any[] = [];
122
+ if (options.parent != null) {
123
+ let parent = Tools.findElement(options.parent, null, multiple);
124
+ if (parent != null) {
125
+ if (parent instanceof Array) {
126
+ parent.forEach(p => {
127
+ let targets = Tools.findElement(options.target, p, multiple);
128
+ if (targets instanceof Array) {
129
+ targets.forEach(target => {
130
+ results.push({
131
+ parent: p,
132
+ target: target
133
+ });
134
+ });
135
+ } else {
136
+ results.push({
137
+ parent: p,
138
+ target: targets
139
+ });
140
+ }
141
+ });
142
+
143
+ return results;
144
+ } else {
145
+ let targets = Tools.findElement(options.target, parent, multiple);
146
+ if (targets instanceof Array) {
147
+ targets.forEach(target => {
148
+ results.push({
149
+ parent: parent,
150
+ target: target
151
+ });
152
+ });
153
+ } else {
154
+ results.push({
155
+ parent: parent,
156
+ target: targets
157
+ });
158
+ }
159
+ }
160
+ }
161
+ } else {
162
+ let targets = Tools.findElement(options.target, null, multiple);
163
+ if (targets instanceof Array) {
164
+ targets.forEach(target => {
165
+ results.push({
166
+ parent: null,
167
+ target: target
168
+ });
169
+ });
170
+ } else {
171
+ results.push({
172
+ parent: null,
173
+ target: targets
174
+ });
175
+ }
176
+ }
177
+
178
+ if (results.length === 0) {
179
+ results.push({
180
+ parent: null,
181
+ target: null
182
+ });
183
+ }
184
+
185
+ if (multiple) {
186
+ return results;
187
+ } else {
188
+ if (results.length !== 1) {
189
+ console.warn(
190
+ "Multiple results found, even though multiple false",
191
+ results
192
+ );
193
+ }
194
+
195
+ return results[0];
196
+ }
197
+ }
198
+ }
@@ -0,0 +1,91 @@
1
+ import { matches, executeAction } from "./consentomatic/index";
2
+ let actionQueue = Promise.resolve(null);
3
+ const styleOverrideElementId = "autoconsent-css-rules";
4
+ const styleSelector = `style#${styleOverrideElementId}`;
5
+ export default function handleMessage(message, debug = false) {
6
+ if (message.type === "click") {
7
+ const elem = document.querySelectorAll(message.selector);
8
+ debug && console.log("[click]", message.selector, elem);
9
+ if (elem.length > 0) {
10
+ if (message.all === true) {
11
+ elem.forEach(e => e.click());
12
+ }
13
+ else {
14
+ elem[0].click();
15
+ }
16
+ }
17
+ return elem.length > 0;
18
+ }
19
+ else if (message.type === "elemExists") {
20
+ const exists = document.querySelector(message.selector) !== null;
21
+ debug && console.log("[exists?]", message.selector, exists);
22
+ return exists;
23
+ }
24
+ else if (message.type === "elemVisible") {
25
+ const elem = document.querySelectorAll(message.selector);
26
+ const results = new Array(elem.length);
27
+ elem.forEach((e, i) => {
28
+ results[i] = e.offsetParent !== null || window.getComputedStyle(e).display !== "none" || e.style?.display !== "none";
29
+ });
30
+ if (results.length === 0) {
31
+ return false;
32
+ }
33
+ else if (message.check === "any") {
34
+ return results.some(r => r);
35
+ }
36
+ else if (message.check === "none") {
37
+ return results.every(r => !r);
38
+ }
39
+ // all
40
+ return results.every(r => r);
41
+ }
42
+ else if (message.type === "getAttribute") {
43
+ const elem = document.querySelector(message.selector);
44
+ if (!elem) {
45
+ return false;
46
+ }
47
+ return elem.getAttribute(message.attribute);
48
+ }
49
+ else if (message.type === "eval") {
50
+ // TODO: chrome support
51
+ const result = window.eval(message.script); // eslint-disable-line no-eval
52
+ debug && console.log("[eval]", message.script, result);
53
+ return result;
54
+ }
55
+ else if (message.type === "hide") {
56
+ const parent = document.head ||
57
+ document.getElementsByTagName("head")[0] ||
58
+ document.documentElement;
59
+ const rule = `${message.selectors.join(",")} { display: none !important; z-index: -1 !important; } `;
60
+ const existingElement = document.querySelector(styleSelector);
61
+ debug && console.log("[hide]", message.selectors, !!existingElement);
62
+ if (existingElement && existingElement instanceof HTMLStyleElement) {
63
+ existingElement.innerText += rule;
64
+ }
65
+ else {
66
+ const css = document.createElement("style");
67
+ css.type = "text/css";
68
+ css.id = styleOverrideElementId;
69
+ css.appendChild(document.createTextNode(rule));
70
+ parent.appendChild(css);
71
+ }
72
+ return message.selectors.length > 0;
73
+ }
74
+ else if (message.type === "undohide") {
75
+ const existingElement = document.querySelector(styleSelector);
76
+ debug && console.log("[unhide]", !!existingElement);
77
+ if (existingElement) {
78
+ existingElement.remove();
79
+ }
80
+ return !!existingElement;
81
+ }
82
+ else if (message.type === "matches") {
83
+ const matched = matches(message.config);
84
+ return matched;
85
+ }
86
+ else if (message.type === "executeAction") {
87
+ actionQueue = actionQueue.then(() => executeAction(message.config, message.param));
88
+ return true;
89
+ }
90
+ return null;
91
+ }
@@ -0,0 +1,83 @@
1
+ import { matches, executeAction } from "./consentomatic/index";
2
+ import { ContentScriptMessage } from "../messages";
3
+
4
+ let actionQueue = Promise.resolve(null);
5
+ const styleOverrideElementId = "autoconsent-css-rules";
6
+ const styleSelector = `style#${styleOverrideElementId}`;
7
+
8
+ export default function handleMessage(message: ContentScriptMessage, debug = false) {
9
+ if (message.type === "click") {
10
+ const elem = document.querySelectorAll<HTMLElement>(message.selector);
11
+ debug && console.log("[click]", message.selector, elem);
12
+ if (elem.length > 0) {
13
+ if (message.all === true) {
14
+ elem.forEach(e => e.click());
15
+ } else {
16
+ elem[0].click();
17
+ }
18
+ }
19
+ return elem.length > 0;
20
+ } else if (message.type === "elemExists") {
21
+ const exists = document.querySelector(message.selector) !== null;
22
+ debug && console.log("[exists?]", message.selector, exists);
23
+ return exists;
24
+ } else if (message.type === "elemVisible") {
25
+ const elem = document.querySelectorAll<HTMLElement>(message.selector);
26
+ const results = new Array(elem.length);
27
+ elem.forEach((e, i) => {
28
+ results[i] = e.offsetParent !== null || window.getComputedStyle(e).display !== "none" || e.style?.display !== "none";
29
+ });
30
+ if (results.length === 0) {
31
+ return false;
32
+ } else if (message.check === "any") {
33
+ return results.some(r => r);
34
+ } else if (message.check === "none") {
35
+ return results.every(r => !r);
36
+ }
37
+ // all
38
+ return results.every(r => r);
39
+ } else if (message.type === "getAttribute") {
40
+ const elem = document.querySelector(message.selector);
41
+ if (!elem) {
42
+ return false;
43
+ }
44
+ return elem.getAttribute(message.attribute);
45
+ } else if (message.type === "eval") {
46
+ // TODO: chrome support
47
+ const result = window.eval(message.script); // eslint-disable-line no-eval
48
+ debug && console.log("[eval]", message.script, result);
49
+ return result;
50
+ } else if (message.type === "hide") {
51
+ const parent =
52
+ document.head ||
53
+ document.getElementsByTagName("head")[0] ||
54
+ document.documentElement;
55
+ const rule = `${message.selectors.join(",")} { display: none !important; z-index: -1 !important; } `;
56
+ const existingElement = document.querySelector(styleSelector);
57
+ debug && console.log("[hide]", message.selectors, !!existingElement);
58
+ if (existingElement && existingElement instanceof HTMLStyleElement) {
59
+ existingElement.innerText += rule;
60
+ } else {
61
+ const css = document.createElement("style");
62
+ css.type = "text/css";
63
+ css.id = styleOverrideElementId;
64
+ css.appendChild(document.createTextNode(rule));
65
+ parent.appendChild(css);
66
+ }
67
+ return message.selectors.length > 0;
68
+ } else if (message.type === "undohide") {
69
+ const existingElement = document.querySelector(styleSelector);
70
+ debug && console.log("[unhide]", !!existingElement);
71
+ if (existingElement) {
72
+ existingElement.remove();
73
+ }
74
+ return !!existingElement
75
+ } else if (message.type === "matches") {
76
+ const matched = matches(message.config);
77
+ return matched;
78
+ } else if (message.type === "executeAction") {
79
+ actionQueue = actionQueue.then(() => executeAction(message.config, message.param));
80
+ return true;
81
+ }
82
+ return null;
83
+ }
package/lib/web/tab.js ADDED
@@ -0,0 +1,106 @@
1
+ import { waitFor } from "../cmps/base";
2
+ export default class TabActions {
3
+ constructor(tabId, frame, sendContentMessage, browser) {
4
+ this.frame = frame;
5
+ this.sendContentMessage = sendContentMessage;
6
+ this.browser = browser;
7
+ this.id = tabId;
8
+ }
9
+ async elementExists(selector, frameId = 0) {
10
+ console.log(`check for ${selector} in tab ${this.id}, frame ${frameId}`);
11
+ return this.sendContentMessage(this.id, {
12
+ type: "elemExists",
13
+ selector
14
+ }, {
15
+ frameId
16
+ });
17
+ }
18
+ async clickElement(selector, frameId = 0) {
19
+ console.log(`click element ${selector} in tab ${this.id}`);
20
+ return this.sendContentMessage(this.id, {
21
+ type: "click",
22
+ selector
23
+ }, {
24
+ frameId
25
+ });
26
+ }
27
+ async clickElements(selector, frameId = 0) {
28
+ console.log(`click elements ${selector} in tab ${this.id}`);
29
+ return this.sendContentMessage(this.id, {
30
+ type: "click",
31
+ all: true,
32
+ selector
33
+ }, {
34
+ frameId
35
+ });
36
+ }
37
+ async elementsAreVisible(selector, check, frameId = 0) {
38
+ return this.sendContentMessage(this.id, {
39
+ type: "elemVisible",
40
+ selector,
41
+ check
42
+ }, {
43
+ frameId
44
+ });
45
+ }
46
+ async getAttribute(selector, attribute, frameId = 0) {
47
+ return this.sendContentMessage(this.id, {
48
+ type: "getAttribute",
49
+ selector,
50
+ attribute
51
+ }, { frameId });
52
+ }
53
+ async eval(script, frameId = 0) {
54
+ // console.log(`run ${script} in tab ${this.id}`);
55
+ return await this.sendContentMessage(this.id, {
56
+ type: "eval",
57
+ script
58
+ }, { frameId });
59
+ }
60
+ async waitForElement(selector, timeout, frameId = 0) {
61
+ const interval = 200;
62
+ const times = Math.ceil(timeout / interval);
63
+ return waitFor(() => this.elementExists(selector, frameId), times, interval);
64
+ }
65
+ async waitForThenClick(selector, timeout, frameId = 0) {
66
+ if (await this.waitForElement(selector, timeout, frameId)) {
67
+ return await this.clickElement(selector, frameId);
68
+ }
69
+ return false;
70
+ }
71
+ async hideElements(selectors, frameId = 0) {
72
+ return this.sendContentMessage(this.id, {
73
+ type: "hide",
74
+ selectors
75
+ }, { frameId });
76
+ }
77
+ async undoHideElements(frameId = 0) {
78
+ return this.sendContentMessage(this.id, {
79
+ type: "undohide",
80
+ }, { frameId });
81
+ }
82
+ async getBrowserTab() {
83
+ return this.browser.tabs.get(this.id);
84
+ }
85
+ async goto(url) {
86
+ return this.browser.tabs.update(this.id, { url });
87
+ }
88
+ wait(ms) {
89
+ return new Promise(resolve => {
90
+ setTimeout(() => resolve(true), ms);
91
+ });
92
+ }
93
+ matches(matcherConfig) {
94
+ return this.sendContentMessage(this.id, {
95
+ type: "matches",
96
+ config: matcherConfig
97
+ }, { frameId: 0 });
98
+ }
99
+ executeAction(config, param) {
100
+ return this.sendContentMessage(this.id, {
101
+ type: "executeAction",
102
+ config,
103
+ param
104
+ }, { frameId: 0 });
105
+ }
106
+ }
package/lib/web/tab.ts ADDED
@@ -0,0 +1,171 @@
1
+ import { waitFor } from "../cmps/base";
2
+ import { TabActor, MessageSender, Browser } from "../types";
3
+
4
+ export default class TabActions implements TabActor {
5
+ id: number;
6
+
7
+ constructor(
8
+ tabId: number,
9
+ public frame: any,
10
+ private sendContentMessage: MessageSender,
11
+ private browser: Browser
12
+ ) {
13
+ this.id = tabId;
14
+ }
15
+
16
+ async elementExists(selector: string, frameId = 0) {
17
+ console.log(`check for ${selector} in tab ${this.id}, frame ${frameId}`);
18
+ return this.sendContentMessage(
19
+ this.id,
20
+ {
21
+ type: "elemExists",
22
+ selector
23
+ },
24
+ {
25
+ frameId
26
+ }
27
+ );
28
+ }
29
+
30
+ async clickElement(selector: string, frameId = 0) {
31
+ console.log(`click element ${selector} in tab ${this.id}`);
32
+ return this.sendContentMessage(
33
+ this.id,
34
+ {
35
+ type: "click",
36
+ selector
37
+ },
38
+ {
39
+ frameId
40
+ }
41
+ );
42
+ }
43
+
44
+ async clickElements(selector: string, frameId = 0) {
45
+ console.log(`click elements ${selector} in tab ${this.id}`);
46
+ return this.sendContentMessage(
47
+ this.id,
48
+ {
49
+ type: "click",
50
+ all: true,
51
+ selector
52
+ },
53
+ {
54
+ frameId
55
+ }
56
+ );
57
+ }
58
+
59
+ async elementsAreVisible(selector: string, check?: 'all' | 'any' | 'none', frameId = 0) {
60
+ return this.sendContentMessage(
61
+ this.id,
62
+ {
63
+ type: "elemVisible",
64
+ selector,
65
+ check
66
+ },
67
+ {
68
+ frameId
69
+ }
70
+ );
71
+ }
72
+
73
+ async getAttribute(selector: string, attribute: string, frameId = 0) {
74
+ return this.sendContentMessage(
75
+ this.id,
76
+ {
77
+ type: "getAttribute",
78
+ selector,
79
+ attribute
80
+ },
81
+ { frameId }
82
+ );
83
+ }
84
+
85
+ async eval(script: string, frameId = 0) {
86
+ // console.log(`run ${script} in tab ${this.id}`);
87
+ return await this.sendContentMessage(
88
+ this.id,
89
+ {
90
+ type: "eval",
91
+ script
92
+ },
93
+ { frameId }
94
+ );
95
+ }
96
+
97
+ async waitForElement(selector: string, timeout: number, frameId = 0) {
98
+ const interval = 200;
99
+ const times = Math.ceil(timeout / interval);
100
+ return waitFor(
101
+ () => this.elementExists(selector, frameId),
102
+ times,
103
+ interval
104
+ );
105
+ }
106
+
107
+ async waitForThenClick(selector: string, timeout: number, frameId = 0) {
108
+ if (await this.waitForElement(selector, timeout, frameId)) {
109
+ return await this.clickElement(selector, frameId);
110
+ }
111
+ return false;
112
+ }
113
+
114
+ async hideElements(selectors: string[], frameId = 0) {
115
+ return this.sendContentMessage(
116
+ this.id,
117
+ {
118
+ type: "hide",
119
+ selectors
120
+ },
121
+ { frameId }
122
+ );
123
+ }
124
+
125
+ async undoHideElements(frameId = 0): Promise<boolean> {
126
+ return this.sendContentMessage(
127
+ this.id,
128
+ {
129
+ type: "undohide",
130
+ },
131
+ { frameId }
132
+ );
133
+ }
134
+
135
+ async getBrowserTab() {
136
+ return this.browser.tabs.get(this.id);
137
+ }
138
+
139
+ async goto(url: string) {
140
+ return this.browser.tabs.update(this.id, { url });
141
+ }
142
+
143
+ wait(ms: number): Promise<true> {
144
+ return new Promise(resolve => {
145
+ setTimeout(() => resolve(true), ms);
146
+ });
147
+ }
148
+
149
+ matches(matcherConfig: any) {
150
+ return this.sendContentMessage(
151
+ this.id,
152
+ {
153
+ type: "matches",
154
+ config: matcherConfig
155
+ },
156
+ { frameId: 0 }
157
+ );
158
+ }
159
+
160
+ executeAction(config: any, param?: any): Promise<boolean> {
161
+ return this.sendContentMessage(
162
+ this.id,
163
+ {
164
+ type: "executeAction",
165
+ config,
166
+ param
167
+ },
168
+ { frameId: 0 }
169
+ );
170
+ }
171
+ }