appium-android-driver 12.1.3 → 12.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -76,6 +76,246 @@ const WEBVIEW_WAIT_INTERVAL_MS = 200;
76
76
  const CDP_REQ_TIMEOUT = 2000; // ms
77
77
  const DEVTOOLS_PORTS_RANGE = [10900, 11000];
78
78
  const DEVTOOLS_PORT_ALLOCATION_GUARD = support_1.util.getLockFileGuard(node_path_1.default.resolve(node_os_1.default.tmpdir(), 'android_devtools_port_guard'), { timeout: 7, tryRecovery: true });
79
+ // #region Exported Functions
80
+ /**
81
+ *
82
+ * @param {string} browser
83
+ * @returns {CHROME_BROWSER_PACKAGE_ACTIVITY[keyof CHROME_BROWSER_PACKAGE_ACTIVITY]}
84
+ */
85
+ function getChromePkg(browser) {
86
+ return (exports.CHROME_BROWSER_PACKAGE_ACTIVITY[browser.toLowerCase()] ||
87
+ exports.CHROME_BROWSER_PACKAGE_ACTIVITY.default);
88
+ }
89
+ /**
90
+ * Parse webview names for getContexts
91
+ *
92
+ * @this {AndroidDriver}
93
+ * @param {import('../types').WebviewsMapping[]} webviewsMapping
94
+ * @param {import('../types').GetWebviewsOpts} options
95
+ * @returns {string[]}
96
+ */
97
+ function parseWebviewNames(webviewsMapping, { ensureWebviewsHavePages = true, isChromeSession = false } = {}) {
98
+ if (isChromeSession) {
99
+ return [exports.CHROMIUM_WIN];
100
+ }
101
+ /** @type {string[]} */
102
+ const result = [];
103
+ for (const { webview, pages, proc, webviewName } of webviewsMapping) {
104
+ if (ensureWebviewsHavePages && !pages?.length) {
105
+ this.log.info(`Skipping the webview '${webview}' at '${proc}' ` +
106
+ `since it has reported having zero pages`);
107
+ continue;
108
+ }
109
+ if (webviewName) {
110
+ result.push(webviewName);
111
+ }
112
+ }
113
+ this.log.debug(`Found ${support_1.util.pluralize('webview', result.length, true)}: ${JSON.stringify(result)}`);
114
+ return result;
115
+ }
116
+ /**
117
+ * Get a list of available webviews mapping by introspecting processes with adb,
118
+ * where webviews are listed. It's possible to pass in a 'deviceSocket' arg, which
119
+ * limits the webview possibilities to the one running on the Chromium devtools
120
+ * socket we're interested in (see note on webviewsFromProcs). We can also
121
+ * direct this method to verify whether a particular webview process actually
122
+ * has any pages (if a process exists but no pages are found, Chromedriver will
123
+ * not actually be able to connect to it, so this serves as a guard for that
124
+ * strange failure mode). The strategy for checking whether any pages are
125
+ * active involves sending a request to the remote debug server on the device,
126
+ * hence it is also possible to specify the port on the host machine which
127
+ * should be used for this communication.
128
+ *
129
+ * @this {AndroidDriver}
130
+ * @param {import('../types').GetWebviewsOpts} [opts={}]
131
+ * @returns {Promise<import('../types').WebviewsMapping[]>}
132
+ */
133
+ async function getWebViewsMapping({ androidDeviceSocket = null, ensureWebviewsHavePages = true, webviewDevtoolsPort = null, enableWebviewDetailsCollection = true, waitForWebviewMs = 0, } = {}) {
134
+ this.log.debug(`Getting a list of available webviews`);
135
+ if (!lodash_1.default.isNumber(waitForWebviewMs)) {
136
+ waitForWebviewMs = parseInt(`${waitForWebviewMs}`, 10) || 0;
137
+ }
138
+ /** @type {import('../types').WebviewsMapping[]} */
139
+ let webviewsMapping;
140
+ const timer = new support_1.timing.Timer().start();
141
+ do {
142
+ webviewsMapping = await webviewsFromProcs.bind(this)(androidDeviceSocket);
143
+ if (webviewsMapping.length > 0) {
144
+ break;
145
+ }
146
+ this.log.debug(`No webviews found in ${timer.getDuration().asMilliSeconds.toFixed(0)}ms`);
147
+ await (0, asyncbox_1.sleep)(WEBVIEW_WAIT_INTERVAL_MS);
148
+ } while (timer.getDuration().asMilliSeconds < waitForWebviewMs);
149
+ await collectWebviewsDetails.bind(this)(webviewsMapping, {
150
+ ensureWebviewsHavePages,
151
+ enableWebviewDetailsCollection,
152
+ webviewDevtoolsPort,
153
+ });
154
+ for (const webviewMapping of webviewsMapping) {
155
+ const { webview, info } = webviewMapping;
156
+ webviewMapping.webviewName = null;
157
+ let wvName = webview;
158
+ /** @type {{name: string; id: string | null} | undefined} */
159
+ let process;
160
+ if (!androidDeviceSocket) {
161
+ const pkgMatch = WEBVIEW_PKG_PATTERN.exec(webview);
162
+ try {
163
+ // web view name could either be suffixed with PID or the package name
164
+ // package names could not start with a digit
165
+ const pkg = pkgMatch ? pkgMatch[1] : await procFromWebview.bind(this)(webview);
166
+ wvName = `${exports.WEBVIEW_BASE}${pkg}`;
167
+ const pidMatch = WEBVIEW_PID_PATTERN.exec(webview);
168
+ process = {
169
+ name: pkg,
170
+ id: pidMatch ? pidMatch[1] : null,
171
+ };
172
+ }
173
+ catch (e) {
174
+ this.log.debug(e.stack);
175
+ this.log.warn(e.message);
176
+ continue;
177
+ }
178
+ }
179
+ webviewMapping.webviewName = wvName;
180
+ const key = (0, cache_1.toDetailsCacheKey)(this.adb, wvName);
181
+ if (info || process) {
182
+ cache_1.WEBVIEWS_DETAILS_CACHE.set(key, { info, process });
183
+ }
184
+ else if (cache_1.WEBVIEWS_DETAILS_CACHE.has(key)) {
185
+ cache_1.WEBVIEWS_DETAILS_CACHE.delete(key);
186
+ }
187
+ }
188
+ return webviewsMapping;
189
+ }
190
+ /**
191
+ * @this {AndroidDriver}
192
+ * @param {import('../../driver').AndroidDriverOpts} opts
193
+ * @param {string} curDeviceId
194
+ * @param {string} [context]
195
+ * @returns {Promise<Chromedriver>}
196
+ */
197
+ async function setupNewChromedriver(opts, curDeviceId, context) {
198
+ // @ts-ignore TODO: Remove the legacy
199
+ if (opts.chromeDriverPort) {
200
+ this.log.warn(`The 'chromeDriverPort' capability is deprecated. Please use 'chromedriverPort' instead`);
201
+ // @ts-ignore TODO: Remove the legacy
202
+ opts.chromedriverPort = opts.chromeDriverPort;
203
+ }
204
+ if (opts.chromedriverPort) {
205
+ this.log.debug(`Using user-specified port ${opts.chromedriverPort} for chromedriver`);
206
+ }
207
+ else {
208
+ // if a single port wasn't given, we'll look for a free one
209
+ opts.chromedriverPort = await getChromedriverPort.bind(this)(opts.chromedriverPorts);
210
+ }
211
+ const details = context ? (0, cache_1.getWebviewDetails)(this.adb, context) : undefined;
212
+ if (!lodash_1.default.isEmpty(details)) {
213
+ this.log.debug('Passing web view details to the Chromedriver constructor: ' +
214
+ JSON.stringify(details, null, 2));
215
+ }
216
+ /** @type {import('appium-chromedriver').ChromedriverOpts} */
217
+ const chromedriverOpts = {
218
+ port: lodash_1.default.isNil(opts.chromedriverPort) ? undefined : String(opts.chromedriverPort),
219
+ executable: opts.chromedriverExecutable,
220
+ adb: this.adb,
221
+ cmdArgs: /** @type {string[] | undefined} */ (opts.chromedriverArgs),
222
+ verbose: !!opts.showChromedriverLog,
223
+ executableDir: opts.chromedriverExecutableDir,
224
+ mappingPath: opts.chromedriverChromeMappingFile,
225
+ // @ts-ignore this property exists
226
+ bundleId: opts.chromeBundleId,
227
+ useSystemExecutable: opts.chromedriverUseSystemExecutable,
228
+ disableBuildCheck: opts.chromedriverDisableBuildCheck,
229
+ // @ts-ignore this is ok
230
+ details,
231
+ isAutodownloadEnabled: isChromedriverAutodownloadEnabled.bind(this)(),
232
+ };
233
+ if (this.basePath) {
234
+ chromedriverOpts.reqBasePath = this.basePath;
235
+ }
236
+ const chromedriver = new appium_chromedriver_1.Chromedriver(chromedriverOpts);
237
+ // make sure there are chromeOptions
238
+ opts.chromeOptions = opts.chromeOptions || {};
239
+ // try out any prefixed chromeOptions,
240
+ // and strip the prefix
241
+ for (const opt of lodash_1.default.keys(opts)) {
242
+ if (opt.endsWith(':chromeOptions')) {
243
+ this?.log?.warn(`Merging '${opt}' into 'chromeOptions'. This may cause unexpected behavior`);
244
+ lodash_1.default.merge(opts.chromeOptions, opts[opt]);
245
+ }
246
+ }
247
+ // Ensure there are logging preferences
248
+ opts.chromeLoggingPrefs = opts.chromeLoggingPrefs ?? {};
249
+ // Strip the prefix and store it
250
+ for (const opt of lodash_1.default.keys(opts)) {
251
+ if (opt.endsWith(':loggingPrefs')) {
252
+ this.log.warn(`Merging '${opt}' into 'chromeLoggingPrefs'. This may cause unexpected behavior`);
253
+ lodash_1.default.merge(opts.chromeLoggingPrefs, opts[opt]);
254
+ }
255
+ }
256
+ const caps = /** @type {any} */ (createChromedriverCaps.bind(this)(opts, curDeviceId, details));
257
+ this.log.debug(`Before starting chromedriver, androidPackage is '${caps.chromeOptions.androidPackage}'`);
258
+ const sessionCaps = await chromedriver.start(caps);
259
+ cacheChromedriverCaps.bind(this)(sessionCaps, context);
260
+ return chromedriver;
261
+ }
262
+ /**
263
+ * @this {AndroidDriver}
264
+ * @template {Chromedriver} T
265
+ * @param {T} chromedriver
266
+ * @param {string} context
267
+ * @returns {Promise<T>}
268
+ */
269
+ async function setupExistingChromedriver(chromedriver, context) {
270
+ // check the status by sending a simple window-based command to ChromeDriver
271
+ // if there is an error, we want to recreate the ChromeDriver session
272
+ if (await chromedriver.hasWorkingWebview()) {
273
+ const cachedCaps = this._chromedriverCapsCache.get(context);
274
+ if (cachedCaps) {
275
+ cacheChromedriverCaps.bind(this)(cachedCaps, context);
276
+ }
277
+ }
278
+ else {
279
+ this.log.debug('ChromeDriver is not associated with a window. Re-initializing the session.');
280
+ const sessionCaps = await chromedriver.restart();
281
+ cacheChromedriverCaps.bind(this)(sessionCaps, context);
282
+ }
283
+ return chromedriver;
284
+ }
285
+ /**
286
+ * @this {AndroidDriver}
287
+ * @returns {boolean}
288
+ */
289
+ function shouldDismissChromeWelcome() {
290
+ return (!!this.opts.chromeOptions &&
291
+ lodash_1.default.isArray(this.opts.chromeOptions.args) &&
292
+ this.opts.chromeOptions.args.includes('--no-first-run'));
293
+ }
294
+ /**
295
+ * @this {AndroidDriver}
296
+ * @returns {Promise<void>}
297
+ */
298
+ async function dismissChromeWelcome() {
299
+ this.log.info('Trying to dismiss Chrome welcome');
300
+ let activity = await this.getCurrentActivity();
301
+ if (activity !== 'org.chromium.chrome.browser.firstrun.FirstRunActivity') {
302
+ this.log.info('Chrome welcome dialog never showed up! Continuing');
303
+ return;
304
+ }
305
+ let el = await this.findElOrEls('id', 'com.android.chrome:id/terms_accept', false);
306
+ await this.click(/** @type {string} */ (el.ELEMENT));
307
+ try {
308
+ let el = await this.findElOrEls('id', 'com.android.chrome:id/negative_button', false);
309
+ await this.click(/** @type {string} */ (el.ELEMENT));
310
+ }
311
+ catch (e) {
312
+ // DO NOTHING, THIS DEVICE DIDNT LAUNCH THE SIGNIN DIALOG
313
+ // IT MUST BE A NON GMS DEVICE
314
+ this.log.warn(`This device did not show Chrome SignIn dialog, ${ /** @type {Error} */(e).message}`);
315
+ }
316
+ }
317
+ // #endregion
318
+ // #region Internal Helper Functions
79
319
  /**
80
320
  * @returns {Promise<number>}
81
321
  */
@@ -133,20 +373,11 @@ async function cdpList(host, port) {
133
373
  async function cdpInfo(host, port) {
134
374
  return cdpGetRequest(host, port, '/json/version');
135
375
  }
136
- /**
137
- *
138
- * @param {string} browser
139
- * @returns {CHROME_BROWSER_PACKAGE_ACTIVITY[keyof CHROME_BROWSER_PACKAGE_ACTIVITY]}
140
- */
141
- function getChromePkg(browser) {
142
- return (exports.CHROME_BROWSER_PACKAGE_ACTIVITY[browser.toLowerCase()] ||
143
- exports.CHROME_BROWSER_PACKAGE_ACTIVITY.default);
144
- }
145
376
  /**
146
377
  * Create Chromedriver capabilities based on the provided
147
378
  * Appium capabilities
148
379
  *
149
- * @this {import('../../driver').AndroidDriver}
380
+ * @this {AndroidDriver}
150
381
  * @param {any} opts
151
382
  * @param {string} deviceId
152
383
  * @param {import('../types').WebViewDetails | null} [webViewDetails]
@@ -236,37 +467,10 @@ function createChromedriverCaps(opts, deviceId, webViewDetails) {
236
467
  }
237
468
  return caps;
238
469
  }
239
- /**
240
- * Parse webview names for getContexts
241
- *
242
- * @this {import('../../driver').AndroidDriver}
243
- * @param {import('../types').WebviewsMapping[]} webviewsMapping
244
- * @param {import('../types').GetWebviewsOpts} options
245
- * @returns {string[]}
246
- */
247
- function parseWebviewNames(webviewsMapping, { ensureWebviewsHavePages = true, isChromeSession = false } = {}) {
248
- if (isChromeSession) {
249
- return [exports.CHROMIUM_WIN];
250
- }
251
- /** @type {string[]} */
252
- const result = [];
253
- for (const { webview, pages, proc, webviewName } of webviewsMapping) {
254
- if (ensureWebviewsHavePages && !pages?.length) {
255
- this.log.info(`Skipping the webview '${webview}' at '${proc}' ` +
256
- `since it has reported having zero pages`);
257
- continue;
258
- }
259
- if (webviewName) {
260
- result.push(webviewName);
261
- }
262
- }
263
- this.log.debug(`Found ${support_1.util.pluralize('webview', result.length, true)}: ${JSON.stringify(result)}`);
264
- return result;
265
- }
266
470
  /**
267
471
  * Allocates a local port for devtools communication
268
472
  *
269
- * @this {import('../../driver').AndroidDriver}
473
+ * @this {AndroidDriver}
270
474
  * @param {string} socketName - The remote Unix socket name
271
475
  * @param {number?} [webviewDevtoolsPort=null] - The local port number or null to apply
272
476
  * autodetection
@@ -308,7 +512,7 @@ async function allocateDevtoolsChannel(socketName, webviewDevtoolsPort = null) {
308
512
  * No error is thrown if CDP request fails - in such case no data will be
309
513
  * recorded into the corresponding `webviewsMapping` item.
310
514
  *
311
- * @this {import('../../driver').AndroidDriver}
515
+ * @this {AndroidDriver}
312
516
  * @param {import('../types').WebviewProps[]} webviewsMapping The current webviews mapping
313
517
  * !!! Each item of this array gets mutated (`info`/`pages` properties get added
314
518
  * based on the provided `opts`) if the requested details have been
@@ -369,87 +573,13 @@ async function collectWebviewsDetails(webviewsMapping, opts = {}) {
369
573
  await bluebird_1.default.all(detailCollectors);
370
574
  this.log.debug(`CDP data collection completed`);
371
575
  }
372
- /**
373
- * Get a list of available webviews mapping by introspecting processes with adb,
374
- * where webviews are listed. It's possible to pass in a 'deviceSocket' arg, which
375
- * limits the webview possibilities to the one running on the Chromium devtools
376
- * socket we're interested in (see note on webviewsFromProcs). We can also
377
- * direct this method to verify whether a particular webview process actually
378
- * has any pages (if a process exists but no pages are found, Chromedriver will
379
- * not actually be able to connect to it, so this serves as a guard for that
380
- * strange failure mode). The strategy for checking whether any pages are
381
- * active involves sending a request to the remote debug server on the device,
382
- * hence it is also possible to specify the port on the host machine which
383
- * should be used for this communication.
384
- *
385
- * @this {import('../../driver').AndroidDriver}
386
- * @param {import('../types').GetWebviewsOpts} [opts={}]
387
- * @returns {Promise<import('../types').WebviewsMapping[]>}
388
- */
389
- async function getWebViewsMapping({ androidDeviceSocket = null, ensureWebviewsHavePages = true, webviewDevtoolsPort = null, enableWebviewDetailsCollection = true, waitForWebviewMs = 0, } = {}) {
390
- this.log.debug(`Getting a list of available webviews`);
391
- if (!lodash_1.default.isNumber(waitForWebviewMs)) {
392
- waitForWebviewMs = parseInt(`${waitForWebviewMs}`, 10) || 0;
393
- }
394
- /** @type {import('../types').WebviewsMapping[]} */
395
- let webviewsMapping;
396
- const timer = new support_1.timing.Timer().start();
397
- do {
398
- webviewsMapping = await webviewsFromProcs.bind(this)(androidDeviceSocket);
399
- if (webviewsMapping.length > 0) {
400
- break;
401
- }
402
- this.log.debug(`No webviews found in ${timer.getDuration().asMilliSeconds.toFixed(0)}ms`);
403
- await (0, asyncbox_1.sleep)(WEBVIEW_WAIT_INTERVAL_MS);
404
- } while (timer.getDuration().asMilliSeconds < waitForWebviewMs);
405
- await collectWebviewsDetails.bind(this)(webviewsMapping, {
406
- ensureWebviewsHavePages,
407
- enableWebviewDetailsCollection,
408
- webviewDevtoolsPort,
409
- });
410
- for (const webviewMapping of webviewsMapping) {
411
- const { webview, info } = webviewMapping;
412
- webviewMapping.webviewName = null;
413
- let wvName = webview;
414
- /** @type {{name: string; id: string | null} | undefined} */
415
- let process;
416
- if (!androidDeviceSocket) {
417
- const pkgMatch = WEBVIEW_PKG_PATTERN.exec(webview);
418
- try {
419
- // web view name could either be suffixed with PID or the package name
420
- // package names could not start with a digit
421
- const pkg = pkgMatch ? pkgMatch[1] : await procFromWebview.bind(this)(webview);
422
- wvName = `${exports.WEBVIEW_BASE}${pkg}`;
423
- const pidMatch = WEBVIEW_PID_PATTERN.exec(webview);
424
- process = {
425
- name: pkg,
426
- id: pidMatch ? pidMatch[1] : null,
427
- };
428
- }
429
- catch (e) {
430
- this.log.debug(e.stack);
431
- this.log.warn(e.message);
432
- continue;
433
- }
434
- }
435
- webviewMapping.webviewName = wvName;
436
- const key = (0, cache_1.toDetailsCacheKey)(this.adb, wvName);
437
- if (info || process) {
438
- cache_1.WEBVIEWS_DETAILS_CACHE.set(key, { info, process });
439
- }
440
- else if (cache_1.WEBVIEWS_DETAILS_CACHE.has(key)) {
441
- cache_1.WEBVIEWS_DETAILS_CACHE.delete(key);
442
- }
443
- }
444
- return webviewsMapping;
445
- }
446
576
  /**
447
577
  * Take a webview name like WEBVIEW_4296 and use 'adb shell ps' to figure out
448
578
  * which app package is associated with that webview. One of the reasons we
449
579
  * want to do this is to make sure we're listing webviews for the actual AUT,
450
580
  * not some other running app
451
581
  *
452
- * @this {import('../../driver').AndroidDriver}
582
+ * @this {AndroidDriver}
453
583
  * @param {string} webview
454
584
  * @returns {Promise<string>}
455
585
  */
@@ -471,7 +601,7 @@ async function procFromWebview(webview) {
471
601
  * See https://cs.chromium.org/chromium/src/chrome/browser/devtools/device/android_device_info_query.cc
472
602
  * for more details
473
603
  *
474
- * @this {import('../../driver').AndroidDriver}
604
+ * @this {AndroidDriver}
475
605
  * @returns {Promise<string[]>} a list of matching webview socket names (including the leading '@')
476
606
  */
477
607
  async function getPotentialWebviewProcs() {
@@ -517,7 +647,7 @@ async function getPotentialWebviewProcs() {
517
647
  * that socket name (this is for apps which embed Chromium, which isn't the
518
648
  * same as chrome-backed webviews).
519
649
  *
520
- * @this {import('../../driver').AndroidDriver}
650
+ * @this {AndroidDriver}
521
651
  * @param {string?} [deviceSocket=null] - the explictly-named device socket to use
522
652
  * @returns {Promise<import('../types').WebviewProc[]>}
523
653
  */
@@ -555,7 +685,7 @@ async function webviewsFromProcs(deviceSocket = null) {
555
685
  return webviews;
556
686
  }
557
687
  /**
558
- * @this {import('../../driver').AndroidDriver}
688
+ * @this {AndroidDriver}
559
689
  * @param {import('../types').PortSpec} [portSpec]
560
690
  * @returns {Promise<number>}
561
691
  */
@@ -599,7 +729,7 @@ async function getChromedriverPort(portSpec) {
599
729
  return foundPort;
600
730
  }
601
731
  /**
602
- * @this {import('../../driver').AndroidDriver}
732
+ * @this {AndroidDriver}
603
733
  * @returns {boolean}
604
734
  */
605
735
  function isChromedriverAutodownloadEnabled() {
@@ -611,148 +741,21 @@ function isChromedriverAutodownloadEnabled() {
611
741
  return false;
612
742
  }
613
743
  /**
614
- * @this {import('../../driver').AndroidDriver}
615
- * @param {import('../../driver').AndroidDriverOpts} opts
616
- * @param {string} curDeviceId
617
- * @param {string} [context]
618
- * @returns {Promise<Chromedriver>}
619
- */
620
- async function setupNewChromedriver(opts, curDeviceId, context) {
621
- // @ts-ignore TODO: Remove the legacy
622
- if (opts.chromeDriverPort) {
623
- this.log.warn(`The 'chromeDriverPort' capability is deprecated. Please use 'chromedriverPort' instead`);
624
- // @ts-ignore TODO: Remove the legacy
625
- opts.chromedriverPort = opts.chromeDriverPort;
626
- }
627
- if (opts.chromedriverPort) {
628
- this.log.debug(`Using user-specified port ${opts.chromedriverPort} for chromedriver`);
629
- }
630
- else {
631
- // if a single port wasn't given, we'll look for a free one
632
- opts.chromedriverPort = await getChromedriverPort.bind(this)(opts.chromedriverPorts);
633
- }
634
- const details = context ? (0, cache_1.getWebviewDetails)(this.adb, context) : undefined;
635
- if (!lodash_1.default.isEmpty(details)) {
636
- this.log.debug('Passing web view details to the Chromedriver constructor: ' +
637
- JSON.stringify(details, null, 2));
638
- }
639
- /** @type {import('appium-chromedriver').ChromedriverOpts} */
640
- const chromedriverOpts = {
641
- port: lodash_1.default.isNil(opts.chromedriverPort) ? undefined : String(opts.chromedriverPort),
642
- executable: opts.chromedriverExecutable,
643
- adb: this.adb,
644
- cmdArgs: /** @type {string[] | undefined} */ (opts.chromedriverArgs),
645
- verbose: !!opts.showChromedriverLog,
646
- executableDir: opts.chromedriverExecutableDir,
647
- mappingPath: opts.chromedriverChromeMappingFile,
648
- // @ts-ignore this property exists
649
- bundleId: opts.chromeBundleId,
650
- useSystemExecutable: opts.chromedriverUseSystemExecutable,
651
- disableBuildCheck: opts.chromedriverDisableBuildCheck,
652
- // @ts-ignore this is ok
653
- details,
654
- isAutodownloadEnabled: isChromedriverAutodownloadEnabled.bind(this)(),
655
- };
656
- if (this.basePath) {
657
- chromedriverOpts.reqBasePath = this.basePath;
658
- }
659
- const chromedriver = new appium_chromedriver_1.Chromedriver(chromedriverOpts);
660
- // make sure there are chromeOptions
661
- opts.chromeOptions = opts.chromeOptions || {};
662
- // try out any prefixed chromeOptions,
663
- // and strip the prefix
664
- for (const opt of lodash_1.default.keys(opts)) {
665
- if (opt.endsWith(':chromeOptions')) {
666
- this?.log?.warn(`Merging '${opt}' into 'chromeOptions'. This may cause unexpected behavior`);
667
- lodash_1.default.merge(opts.chromeOptions, opts[opt]);
668
- }
669
- }
670
- // Ensure there are logging preferences
671
- opts.chromeLoggingPrefs = opts.chromeLoggingPrefs ?? {};
672
- // Strip the prefix and store it
673
- for (const opt of lodash_1.default.keys(opts)) {
674
- if (opt.endsWith(':loggingPrefs')) {
675
- this.log.warn(`Merging '${opt}' into 'chromeLoggingPrefs'. This may cause unexpected behavior`);
676
- lodash_1.default.merge(opts.chromeLoggingPrefs, opts[opt]);
677
- }
678
- }
679
- const caps = /** @type {any} */ (createChromedriverCaps.bind(this)(opts, curDeviceId, details));
680
- this.log.debug(`Before starting chromedriver, androidPackage is '${caps.chromeOptions.androidPackage}'`);
681
- const sessionCaps = await chromedriver.start(caps);
682
- updateBidiProxyUrl.bind(this)(sessionCaps, context);
683
- return chromedriver;
684
- }
685
- /**
686
- * @this {import('../../driver').AndroidDriver}
687
- * @template {Chromedriver} T
688
- * @param {T} chromedriver
689
- * @param {string} context
690
- * @returns {Promise<T>}
691
- */
692
- async function setupExistingChromedriver(chromedriver, context) {
693
- // check the status by sending a simple window-based command to ChromeDriver
694
- // if there is an error, we want to recreate the ChromeDriver session
695
- if (await chromedriver.hasWorkingWebview()) {
696
- const cachedBidiProxyUrl = this._bidiProxyUrlCache.get(context);
697
- if (cachedBidiProxyUrl) {
698
- updateBidiProxyUrl.bind(this)({ webSocketUrl: cachedBidiProxyUrl }, context);
699
- }
700
- }
701
- else {
702
- this.log.debug('ChromeDriver is not associated with a window. Re-initializing the session.');
703
- const sessionCaps = await chromedriver.restart();
704
- updateBidiProxyUrl.bind(this)(sessionCaps, context);
705
- }
706
- return chromedriver;
707
- }
708
- /**
709
- * @this {import('../../driver').AndroidDriver}
744
+ * @this {AndroidDriver}
710
745
  * @param {Record<string, any>} sessionCaps
711
746
  * @param {string} context
712
747
  * @returns {void}
713
748
  */
714
- function updateBidiProxyUrl(sessionCaps, context) {
715
- if (!this.opts?.chromedriverForwardBiDi || !sessionCaps?.webSocketUrl) {
716
- return;
717
- }
718
- this._bidiProxyUrlCache.set(context, sessionCaps.webSocketUrl);
719
- if (this._bidiProxyUrl !== sessionCaps.webSocketUrl) {
749
+ function cacheChromedriverCaps(sessionCaps, context) {
750
+ // Store full session capabilities in cache
751
+ this._chromedriverCapsCache.set(context, sessionCaps);
752
+ if (this.opts?.chromedriverForwardBiDi
753
+ && sessionCaps?.webSocketUrl
754
+ && this._bidiProxyUrl !== sessionCaps.webSocketUrl) {
720
755
  this._bidiProxyUrl = sessionCaps.webSocketUrl;
721
756
  this.log.debug(`Updated Bidi Proxy URL to ${this._bidiProxyUrl}`);
722
757
  }
723
758
  }
724
- /**
725
- * @this {import('../../driver').AndroidDriver}
726
- * @returns {boolean}
727
- */
728
- function shouldDismissChromeWelcome() {
729
- return (!!this.opts.chromeOptions &&
730
- lodash_1.default.isArray(this.opts.chromeOptions.args) &&
731
- this.opts.chromeOptions.args.includes('--no-first-run'));
732
- }
733
- /**
734
- * @this {import('../../driver').AndroidDriver}
735
- * @returns {Promise<void>}
736
- */
737
- async function dismissChromeWelcome() {
738
- this.log.info('Trying to dismiss Chrome welcome');
739
- let activity = await this.getCurrentActivity();
740
- if (activity !== 'org.chromium.chrome.browser.firstrun.FirstRunActivity') {
741
- this.log.info('Chrome welcome dialog never showed up! Continuing');
742
- return;
743
- }
744
- let el = await this.findElOrEls('id', 'com.android.chrome:id/terms_accept', false);
745
- await this.click(/** @type {string} */ (el.ELEMENT));
746
- try {
747
- let el = await this.findElOrEls('id', 'com.android.chrome:id/negative_button', false);
748
- await this.click(/** @type {string} */ (el.ELEMENT));
749
- }
750
- catch (e) {
751
- // DO NOTHING, THIS DEVICE DIDNT LAUNCH THE SIGNIN DIALOG
752
- // IT MUST BE A NON GMS DEVICE
753
- this.log.warn(`This device did not show Chrome SignIn dialog, ${ /** @type {Error} */(e).message}`);
754
- }
755
- }
756
759
  /**
757
760
  * https://github.com/puppeteer/puppeteer/issues/2242#issuecomment-544219536
758
761
  *
@@ -764,7 +767,9 @@ function isCompatibleCdpHost(host) {
764
767
  || host.endsWith('.localhost')
765
768
  || Boolean(node_net_1.default.isIP(host));
766
769
  }
770
+ // #endregion
767
771
  /**
768
772
  * @typedef {import('appium-adb').ADB} ADB
773
+ * @typedef {import('../../driver').AndroidDriver} AndroidDriver
769
774
  */
770
775
  //# sourceMappingURL=helpers.js.map