puppeteer-pro 1.5.1 → 1.5.4

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 (64) hide show
  1. package/.env.example +2 -0
  2. package/.eslintignore +6 -0
  3. package/.eslintrc.json +39 -0
  4. package/.github/dependabot.yml +16 -0
  5. package/.github/workflows/master.cron.code-analyze.yml +58 -0
  6. package/.github/workflows/master.cron.publish.yml +34 -0
  7. package/.github/workflows/master.pr.build.yml +31 -0
  8. package/package.json +10 -10
  9. package/src/index.ts +279 -0
  10. package/src/plugins/anonymize.user.agent/index.ts +66 -0
  11. package/src/plugins/anonymize.user.agent/test.js +35 -0
  12. package/src/plugins/avoid.detection/index.ts +19 -0
  13. package/{plugins → src/plugins}/avoid.detection/injections/chrome.runtime.js +0 -0
  14. package/{plugins → src/plugins}/avoid.detection/injections/console.debug.js +0 -0
  15. package/{plugins → src/plugins}/avoid.detection/injections/hairline.js +0 -0
  16. package/{plugins → src/plugins}/avoid.detection/injections/navigator.languages.js +0 -0
  17. package/{plugins → src/plugins}/avoid.detection/injections/navigator.permissions.js +0 -0
  18. package/{plugins → src/plugins}/avoid.detection/injections/navigator.plugins.js +0 -0
  19. package/{plugins → src/plugins}/avoid.detection/injections/navigator.webdriver.js +0 -0
  20. package/{plugins → src/plugins}/avoid.detection/injections/webgl.js +0 -0
  21. package/{plugins → src/plugins}/avoid.detection/injections/window.js +0 -0
  22. package/src/plugins/avoid.detection/test.js +40 -0
  23. package/src/plugins/block.resources/index.ts +23 -0
  24. package/src/plugins/block.resources/test.js +48 -0
  25. package/src/plugins/disable.dialogs/index.ts +22 -0
  26. package/src/plugins/disable.dialogs/test.js +43 -0
  27. package/src/plugins/manage.cookies/index.ts +174 -0
  28. package/src/plugins/manage.cookies/test.js +90 -0
  29. package/src/plugins/solve.recaptcha/index.ts +95 -0
  30. package/{plugins → src/plugins}/solve.recaptcha/injections/utils.js +0 -0
  31. package/src/plugins/solve.recaptcha/test.js +42 -0
  32. package/test/1.default.js +14 -0
  33. package/test/2.methods.js +187 -0
  34. package/test/3.plugins.js +91 -0
  35. package/tsconfig.json +35 -0
  36. package/tslint.json +21 -0
  37. package/index.d.ts +0 -45
  38. package/index.d.ts.map +0 -1
  39. package/index.js +0 -244
  40. package/index.js.map +0 -1
  41. package/plugins/anonymize.user.agent/index.d.ts +0 -13
  42. package/plugins/anonymize.user.agent/index.d.ts.map +0 -1
  43. package/plugins/anonymize.user.agent/index.js +0 -50
  44. package/plugins/anonymize.user.agent/index.js.map +0 -1
  45. package/plugins/avoid.detection/index.d.ts +0 -8
  46. package/plugins/avoid.detection/index.d.ts.map +0 -1
  47. package/plugins/avoid.detection/index.js +0 -22
  48. package/plugins/avoid.detection/index.js.map +0 -1
  49. package/plugins/block.resources/index.d.ts +0 -10
  50. package/plugins/block.resources/index.d.ts.map +0 -1
  51. package/plugins/block.resources/index.js +0 -20
  52. package/plugins/block.resources/index.js.map +0 -1
  53. package/plugins/disable.dialogs/index.d.ts +0 -8
  54. package/plugins/disable.dialogs/index.d.ts.map +0 -1
  55. package/plugins/disable.dialogs/index.js +0 -18
  56. package/plugins/disable.dialogs/index.js.map +0 -1
  57. package/plugins/manage.cookies/index.d.ts +0 -36
  58. package/plugins/manage.cookies/index.d.ts.map +0 -1
  59. package/plugins/manage.cookies/index.js +0 -140
  60. package/plugins/manage.cookies/index.js.map +0 -1
  61. package/plugins/solve.recaptcha/index.d.ts +0 -11
  62. package/plugins/solve.recaptcha/index.d.ts.map +0 -1
  63. package/plugins/solve.recaptcha/index.js +0 -82
  64. package/plugins/solve.recaptcha/index.js.map +0 -1
package/.env.example ADDED
@@ -0,0 +1,2 @@
1
+ # wit.ai
2
+ WIT_AI_ACCESS_TOKEN=
package/.eslintignore ADDED
@@ -0,0 +1,6 @@
1
+ *.js
2
+ !index.js
3
+ !src/plugins/*/injections/*.js
4
+
5
+ !test/*.js
6
+ !src/plugins/*/*.js
package/.eslintrc.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "extends": "eslint:recommended",
3
+ // "extends": "google",
4
+ "parserOptions": {
5
+ "ecmaVersion": 2018
6
+ },
7
+ "env": {
8
+ "node": true,
9
+ "browser": true,
10
+ "es6": true,
11
+ "mocha": true
12
+ },
13
+ "rules": {
14
+ "comma-dangle": [
15
+ "error",
16
+ {
17
+ "arrays": "only-multiline"
18
+ }
19
+ ],
20
+ "linebreak-style": [
21
+ "error",
22
+ "windows"
23
+ ],
24
+ "max-len": [
25
+ "error",
26
+ {
27
+ "code": 1e10
28
+ }
29
+ ],
30
+ "no-console": "off",
31
+ "require-await": [
32
+ "error"
33
+ ],
34
+ "semi": [
35
+ "error",
36
+ "always"
37
+ ]
38
+ }
39
+ }
@@ -0,0 +1,16 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: npm
4
+ directory: "/"
5
+ schedule:
6
+ interval: daily
7
+ time: "03:00"
8
+ timezone: America/New_York
9
+ open-pull-requests-limit: 10
10
+ versioning-strategy: increase
11
+ labels:
12
+ - dependencies
13
+ commit-message:
14
+ prefix: fix
15
+ prefix-development: chore
16
+ include: scope
@@ -0,0 +1,58 @@
1
+ name: CodeQL
2
+
3
+ on:
4
+ push:
5
+ branches: [ master ]
6
+ pull_request:
7
+ branches: [ master ]
8
+ schedule:
9
+ - cron: '0 9 * * 1'
10
+
11
+ jobs:
12
+ analyze:
13
+ name: Analyze
14
+ runs-on: ubuntu-latest
15
+ permissions:
16
+ actions: read
17
+ contents: read
18
+ security-events: write
19
+
20
+ strategy:
21
+ fail-fast: false
22
+ matrix:
23
+ language: [ 'javascript' ]
24
+ # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
25
+ # Learn more about CodeQL language support at https://git.io/codeql-language-support
26
+
27
+ steps:
28
+ - name: Checkout repository
29
+ uses: actions/checkout@v2
30
+
31
+ # Initializes the CodeQL tools for scanning.
32
+ - name: Initialize CodeQL
33
+ uses: github/codeql-action/init@v1
34
+ with:
35
+ languages: ${{ matrix.language }}
36
+ # If you wish to specify custom queries, you can do so here or in a config file.
37
+ # By default, queries listed here will override any specified in a config file.
38
+ # Prefix the list here with "+" to use these queries and those in the config file.
39
+ # queries: ./path/to/local/query, your-org/your-repo/queries@main
40
+
41
+ # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
42
+ # If this step fails, then you should remove it and run the build manually (see below)
43
+ - name: Autobuild
44
+ uses: github/codeql-action/autobuild@v1
45
+
46
+ # ℹ️ Command-line programs to run using the OS shell.
47
+ # 📚 https://git.io/JvXDl
48
+
49
+ # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
50
+ # and modify them (or add more) to build your code if your project
51
+ # uses a compiled language
52
+
53
+ #- run: |
54
+ # make bootstrap
55
+ # make release
56
+
57
+ - name: Perform CodeQL Analysis
58
+ uses: github/codeql-action/analyze@v1
@@ -0,0 +1,34 @@
1
+ name: Publish
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ schedule:
6
+ - cron: '0 9 * * *'
7
+
8
+ jobs:
9
+ run:
10
+ name: Publish
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - uses: actions/checkout@v1
14
+ - uses: actions/setup-node@v1
15
+ with:
16
+ node-version: 16
17
+ - run: npm install
18
+
19
+ - uses: DamianReeves/write-file-action@v1.0
20
+ with:
21
+ path: .env
22
+ contents: ${{ secrets.ENV }}
23
+ write-mode: overwrite
24
+
25
+ - run: npm run build --if-present
26
+ - run: npm test
27
+
28
+ - run: cd dist
29
+
30
+ - uses: codfish/semantic-release-action@v1
31
+ env:
32
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
33
+ NPM_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }}
34
+ continue-on-error: true
@@ -0,0 +1,31 @@
1
+ name: Build on PR
2
+
3
+ on:
4
+ pull_request_target:
5
+ branches:
6
+ - master
7
+
8
+ jobs:
9
+ run:
10
+ name: Build on PR
11
+ runs-on: ubuntu-latest
12
+ if: github.actor == 'dependabot[bot]'
13
+ steps:
14
+ - name: Checkout
15
+ uses: actions/checkout@v2
16
+ with:
17
+ ref: ${{ github.event.pull_request.head.sha }}
18
+
19
+ - uses: actions/setup-node@v1
20
+ with:
21
+ node-version: 16
22
+ - run: npm install
23
+
24
+ - uses: DamianReeves/write-file-action@v1.0
25
+ with:
26
+ path: .env
27
+ contents: ${{ secrets.ENV }}
28
+ write-mode: overwrite
29
+
30
+ - run: npm run build --if-present
31
+ - run: npm test
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "puppeteer-pro",
3
- "version": "1.5.1",
3
+ "version": "1.5.4",
4
4
  "description": "A simple puppeteer wrapper to enable useful plugins with ease",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -35,21 +35,21 @@
35
35
  "node": ">=10.0.0"
36
36
  },
37
37
  "dependencies": {
38
- "axios": "^0.25.0",
39
- "ghost-cursor": "^1.1.6",
40
- "user-agents": "^1.0.906"
38
+ "axios": "^0.27.2",
39
+ "ghost-cursor": "^1.1.11",
40
+ "user-agents": "^1.0.1042"
41
41
  },
42
42
  "devDependencies": {
43
- "@types/node": "^17.0.10",
43
+ "@types/node": "^17.0.41",
44
44
  "@types/user-agents": "^1.0.2",
45
- "chai": "^4.3.4",
45
+ "chai": "^4.3.6",
46
46
  "copyfiles": "^2.4.1",
47
47
  "dotenv-safe": "^8.2.0",
48
- "eslint": "^8.7.0",
49
- "mocha": "^9.2.0",
50
- "puppeteer": "^13.1.1",
48
+ "eslint": "^8.17.0",
49
+ "mocha": "^10.0.0",
50
+ "puppeteer": "^14.3.0",
51
51
  "rimraf": "^3.0.2",
52
52
  "tslint": "^6.1.3",
53
- "typescript": "^4.5.5"
53
+ "typescript": "^4.7.3"
54
54
  }
55
55
  }
package/src/index.ts ADDED
@@ -0,0 +1,279 @@
1
+ import * as events from 'events';
2
+
3
+ // Puppeteer Defaults
4
+ import * as Puppeteer from 'puppeteer';
5
+
6
+ const browserEvents = new events.EventEmitter();
7
+
8
+ export async function connect(options?: Puppeteer.ConnectOptions): Promise<Puppeteer.Browser> {
9
+ const browser = await Puppeteer.connect(options || {});
10
+
11
+ for (const plugin of plugins) {
12
+ await plugin.init(browser);
13
+ }
14
+
15
+ const _close = browser.close;
16
+ browser.close = async () => {
17
+ await _close.apply(browser);
18
+ browserEvents.emit('close');
19
+ };
20
+
21
+ return browser;
22
+ }
23
+
24
+ /** The method launches a browser instance with given arguments. The browser will be closed when the parent node.js process is closed. */
25
+ export async function launch(options?: Puppeteer.LaunchOptions & Puppeteer.BrowserLaunchArgumentOptions & Puppeteer.BrowserConnectOptions): Promise<Puppeteer.Browser> {
26
+ if (!options) options = {};
27
+
28
+ const browser = await Puppeteer.launch({ defaultViewport: undefined, ...options });
29
+
30
+ for (const plugin of plugins) {
31
+ await plugin.init(browser);
32
+ }
33
+
34
+ const _close = browser.close;
35
+ browser.close = async () => {
36
+ await _close.apply(browser);
37
+ browserEvents.emit('close');
38
+ };
39
+
40
+ return browser;
41
+ }
42
+
43
+ // PuppeteerPro
44
+ let interceptions = 0;
45
+ export class Plugin {
46
+ protected browser: Puppeteer.Browser | null = null;
47
+ private initialized = false;
48
+ private startCounter = 0;
49
+ protected dependencies: Plugin[] = [];
50
+ protected requiresInterception = false;
51
+
52
+ get isInitialized() { return this.initialized; }
53
+ get isStopped() { return this.startCounter === 0; }
54
+
55
+ protected async addDependency(plugin: Plugin) {
56
+ this.dependencies.push(plugin);
57
+ }
58
+
59
+ async init(browser: Puppeteer.Browser) {
60
+ if (this.initialized) return;
61
+
62
+ this.browser = browser;
63
+
64
+ const offOnClose: (() => void)[] = [];
65
+ browserEvents.once('close', async () => {
66
+ offOnClose.forEach(fn => fn());
67
+
68
+ this.browser = null;
69
+ this.initialized = false;
70
+ this.startCounter = 0;
71
+
72
+ await this.onClose();
73
+ });
74
+
75
+ this.startCounter++;
76
+
77
+ const thisOnTargetCreated = this.onTargetCreated.bind(this);
78
+ browser.on('targetcreated', thisOnTargetCreated);
79
+ offOnClose.push(() => browser.off('targetcreated', thisOnTargetCreated));
80
+
81
+ this.initialized = true;
82
+
83
+ this.dependencies.forEach(x => x.init(browser));
84
+
85
+ return this.afterLaunch(browser);
86
+ }
87
+
88
+ protected async afterLaunch(_browser: Puppeteer.Browser) { }
89
+ protected async onClose() { }
90
+
91
+ protected async onTargetCreated(target: Puppeteer.Target) {
92
+ if (this.isStopped) return;
93
+
94
+ if (target.type() !== 'page') return;
95
+ const page = await target.page() as Puppeteer.Page;
96
+ if (page.isClosed()) return;
97
+
98
+ const offOnClose: (() => void)[] = [];
99
+ page.once('close', async () => {
100
+ offOnClose.forEach(fn => fn());
101
+ });
102
+
103
+ const requestHandlers: ((request: any) => void)[] = [];
104
+ page.on('request', request => {
105
+ const _respond = request.respond;
106
+ let responded = 0;
107
+ let respondArgs: IArguments;
108
+
109
+ const _abort = request.abort;
110
+ let aborted = 0;
111
+ let abortArgs: IArguments;
112
+
113
+ const _continue = request.continue;
114
+ let continued = 0;
115
+ let continueArgs: IArguments;
116
+
117
+ // tslint:disable-next-line: only-arrow-functions
118
+ const handleRequest = async function () {
119
+ const total = responded + aborted + continued;
120
+
121
+ if (!(request as any)._interceptionHandled) {
122
+ if (responded === 1) await _respond.apply(request, respondArgs);
123
+ else if (responded === 0 && aborted >= 1 && total === requestHandlers.length) await _abort.apply(request, abortArgs);
124
+ else if (continued === requestHandlers.length) await _continue.apply(request, continueArgs);
125
+ }
126
+ };
127
+
128
+ // tslint:disable-next-line: only-arrow-functions
129
+ request.respond = async function () { responded++; respondArgs = respondArgs || arguments; await handleRequest(); };
130
+ // tslint:disable-next-line: only-arrow-functions
131
+ request.abort = async function () { aborted++; abortArgs = abortArgs || arguments; await handleRequest(); };
132
+ // tslint:disable-next-line: only-arrow-functions
133
+ request.continue = async function () { continued++; continueArgs = continueArgs || arguments; await handleRequest(); };
134
+
135
+ requestHandlers.forEach(handler => handler(request));
136
+ });
137
+
138
+ const _pageOn = page.on;
139
+ page.on = function async(eventName, handler) {
140
+ if (eventName === 'request') {
141
+ requestHandlers.push(handler);
142
+
143
+ return page;
144
+ } else {
145
+ return _pageOn.call(page, eventName, handler);
146
+ }
147
+ };
148
+
149
+ if (this.requiresInterception) {
150
+ await page.setRequestInterception(true);
151
+
152
+ const thisOnRequest = this.onRequest.bind(this);
153
+ page.on('request', thisOnRequest);
154
+ offOnClose.push(() => page.off('request', thisOnRequest));
155
+ }
156
+
157
+ const thisOnDialog = this.onDialog.bind(this);
158
+ page.on('dialog', thisOnDialog);
159
+ offOnClose.push(() => page.off('dialog', thisOnDialog));
160
+
161
+ await this.onPageCreated(page);
162
+ }
163
+ protected async onPageCreated(_page: Puppeteer.Page) { }
164
+
165
+ protected async onRequest(request: Puppeteer.HTTPRequest) {
166
+ const interceptionHandled = (request as any)._interceptionHandled;
167
+ if (interceptionHandled) return;
168
+ if (this.isStopped) return request.continue();
169
+
170
+ await this.processRequest(request);
171
+ }
172
+ protected async processRequest(_request: Puppeteer.HTTPRequest) { }
173
+
174
+ protected async onDialog(dialog: Puppeteer.Dialog) {
175
+ const handled = (dialog as any)._handled;
176
+
177
+ if (handled) return;
178
+ if (this.isStopped) return;
179
+
180
+ await this.processDialog(dialog);
181
+ }
182
+ protected async processDialog(_dialog: Puppeteer.Dialog) { }
183
+
184
+ protected async beforeRestart() { }
185
+ async restart() {
186
+ await this.beforeRestart();
187
+
188
+ this.startCounter++;
189
+ if (this.requiresInterception) interceptions++;
190
+
191
+ this.dependencies.forEach(x => x.restart());
192
+
193
+ await this.afterRestart();
194
+ }
195
+ protected async afterRestart() { }
196
+
197
+ protected async beforeStop() { }
198
+ async stop() {
199
+ await this.beforeStop();
200
+
201
+ this.startCounter--;
202
+ if (this.requiresInterception) interceptions--;
203
+
204
+ if (interceptions === 0 && this.browser) {
205
+ const pages = await this.browser.pages();
206
+
207
+ pages.filter(x => !x.isClosed()).forEach(async (page: Puppeteer.Page) => {
208
+ await page.setRequestInterception(false);
209
+ });
210
+ }
211
+
212
+ this.dependencies.forEach(x => x.stop());
213
+
214
+ await this.afterStop();
215
+ }
216
+ protected async afterStop() { }
217
+
218
+ protected async getFirstPage() {
219
+ if (!this.browser) return null;
220
+
221
+ const pages = await this.browser.pages();
222
+ const openPages = pages.filter(x => !x.isClosed());
223
+ const activePages = pages.filter(x => x.url() !== 'about:blank');
224
+
225
+ return activePages[0] || openPages[0];
226
+ }
227
+ }
228
+
229
+ let plugins: Plugin[] = [];
230
+ export function addPlugin(plugin: Plugin) { plugins.push(plugin); }
231
+ export async function clearPlugins() {
232
+ plugins.forEach(async plugin => {
233
+ await plugin.stop();
234
+ });
235
+
236
+ plugins = [];
237
+ }
238
+
239
+ import { AnonymizeUserAgentPlugin } from './plugins/anonymize.user.agent/index';
240
+ export function anonymizeUserAgent(): AnonymizeUserAgentPlugin {
241
+ const plugin = new AnonymizeUserAgentPlugin();
242
+ plugins.push(plugin);
243
+ return plugin;
244
+ }
245
+
246
+ import { AvoidDetectionPlugin } from './plugins/avoid.detection';
247
+ export function avoidDetection(): AvoidDetectionPlugin {
248
+ const plugin = new AvoidDetectionPlugin();
249
+ plugins.push(plugin);
250
+ return plugin;
251
+ }
252
+
253
+ import { BlockResourcesPlugin, Resource } from './plugins/block.resources';
254
+ export function blockResources(...resources: Resource[]): BlockResourcesPlugin {
255
+ const plugin = new BlockResourcesPlugin(resources);
256
+ plugins.push(plugin);
257
+ return plugin;
258
+ }
259
+
260
+ import { DisableDialogsPlugin } from './plugins/disable.dialogs';
261
+ export function disableDialogs(logMessages = false): DisableDialogsPlugin {
262
+ const plugin = new DisableDialogsPlugin(logMessages);
263
+ plugins.push(plugin);
264
+ return plugin;
265
+ }
266
+
267
+ import { ManageCookiesPlugin, ManageCookiesOption } from './plugins/manage.cookies';
268
+ export function manageCookies(opts: ManageCookiesOption): ManageCookiesPlugin {
269
+ const plugin = new ManageCookiesPlugin(opts);
270
+ plugins.push(plugin);
271
+ return plugin;
272
+ }
273
+
274
+ import { SolveRecaptchaPlugin } from './plugins/solve.recaptcha';
275
+ export function solveRecaptchas(accessToken: string): SolveRecaptchaPlugin {
276
+ const plugin = new SolveRecaptchaPlugin(accessToken);
277
+ plugins.push(plugin);
278
+ return plugin;
279
+ }
@@ -0,0 +1,66 @@
1
+ import * as Puppeteer from 'puppeteer';
2
+ import UserAgent = require('user-agents');
3
+
4
+ import { Plugin } from '../../index';
5
+
6
+ const sleep = (time: number) => { return new Promise(resolve => { setTimeout(resolve, time); }); };
7
+
8
+ interface PageUserAgent {
9
+ target: Puppeteer.Page;
10
+ userAgent: string;
11
+ newUserAgent: string;
12
+ }
13
+
14
+ export class AnonymizeUserAgentPlugin extends Plugin {
15
+ private pages: PageUserAgent[] = [];
16
+ private userAgent?: string;
17
+
18
+ constructor() {
19
+ super();
20
+
21
+ try {
22
+ this.userAgent = new UserAgent({ vendor: 'Google Inc.', platform: 'Win32' }).toString();
23
+ }
24
+ catch (ex) {
25
+ console.warn('Could not create a random user agent');
26
+ }
27
+ }
28
+
29
+ protected async afterLaunch(browser: Puppeteer.Browser) {
30
+ const _newPage = browser.newPage;
31
+ browser.newPage = async (): Promise<Puppeteer.Page> => {
32
+ const page = await _newPage.apply(browser);
33
+ await sleep(100); // Sleep to allow user agent to set
34
+ return page;
35
+ };
36
+ }
37
+
38
+ protected async onClose() {
39
+ this.pages = [];
40
+ }
41
+
42
+ protected async onPageCreated(page: Puppeteer.Page) {
43
+ const userAgent = await page.browser().userAgent();
44
+ const newUserAgent = this.userAgent || userAgent.replace('HeadlessChrome/', 'Chrome/').replace(/\(([^)]+)\)/, '(Windows NT 10.0; Win64; x64)');
45
+
46
+ this.pages.push({ target: page, userAgent, newUserAgent });
47
+
48
+ await page.setUserAgent(newUserAgent);
49
+ }
50
+
51
+ protected async beforeRestart() {
52
+ for (const page of this.pages) {
53
+ if (page.target.isClosed()) continue;
54
+
55
+ await page.target.setUserAgent(page.newUserAgent);
56
+ }
57
+ }
58
+
59
+ protected async afterStop() {
60
+ for (const page of this.pages) {
61
+ if (page.target.isClosed()) continue;
62
+
63
+ await page.target.setUserAgent(page.userAgent);
64
+ }
65
+ }
66
+ }
@@ -0,0 +1,35 @@
1
+ const PuppeteerPro = require('../../../dist/index');
2
+ const chai = require('chai');
3
+
4
+ const expect = chai.expect;
5
+
6
+ const sleep = time => { return new Promise(resolve => { setTimeout(resolve, time); }); };
7
+
8
+ module.exports = plugin => async browserWSEndpoint => {
9
+ const browser = browserWSEndpoint ? await PuppeteerPro.connect({ browserWSEndpoint }) : await PuppeteerPro.launch();
10
+ let page;
11
+
12
+ try {
13
+ page = await browser.newPage();
14
+
15
+ const getResult = async () => {
16
+ await page.goto('https://httpbin.org/headers');
17
+ await sleep(100);
18
+
19
+ const data = await page.evaluate(() => JSON.parse(document.body.innerText));
20
+ return data.headers['User-Agent'];
21
+ };
22
+
23
+ expect(await getResult()).to.not.contain('Headless');
24
+
25
+ await plugin.stop();
26
+ expect(await getResult()).to.contain('Headless');
27
+
28
+ await plugin.restart();
29
+ expect(await getResult()).to.not.contain('Headless');
30
+ }
31
+ finally {
32
+ if (page) await page.close();
33
+ if (browser) await browser.close();
34
+ }
35
+ };
@@ -0,0 +1,19 @@
1
+ import * as fs from 'fs';
2
+ import * as path from 'path';
3
+ import * as Puppeteer from 'puppeteer';
4
+
5
+ import { Plugin } from '../../index';
6
+ import { AnonymizeUserAgentPlugin } from './../anonymize.user.agent/index';
7
+
8
+ const injectionsFolder = path.resolve(`${__dirname}/injections`);
9
+ const injections = fs.readdirSync(injectionsFolder).map(fileName => require(`${injectionsFolder}/${fileName}`));
10
+
11
+ export class AvoidDetectionPlugin extends Plugin {
12
+ dependencies = [new AnonymizeUserAgentPlugin()];
13
+
14
+ protected async onPageCreated(page: Puppeteer.Page) {
15
+ for (const injection of injections) {
16
+ if (!this.isStopped && !page.isClosed()) await page.evaluateOnNewDocument(injection);
17
+ }
18
+ }
19
+ }