appium-remote-debugger 12.2.5 → 12.2.7

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.
@@ -10,10 +10,16 @@ import { ON_TARGET_PROVISIONED_EVENT } from './constants';
10
10
 
11
11
  const DATA_LOG_LENGTH = {length: 200};
12
12
  const WAIT_FOR_TARGET_TIMEOUT_MS = 10000;
13
- const WAIT_FOR_TARGET_INTERVAL_MS = 1000;
13
+ const WAIT_FOR_TARGET_INTERVAL_MS = 100;
14
14
  const MIN_PLATFORM_FOR_TARGET_BASED = '12.2';
15
15
  // `Target.exists` protocol method was removed from WebKit in 13.4
16
16
  const MIN_PLATFORM_NO_TARGET_EXISTS = '13.4';
17
+ const NO_TARGET_SUPPORTED_ERROR = `'target' domain was not found`;
18
+ const NO_TARGET_PRESENT_YET_ERRORS = [
19
+ `domain was not found`,
20
+ `some arguments of method`,
21
+ `missing target`,
22
+ ];
17
23
 
18
24
  /**
19
25
  * @param {boolean} isSafari
@@ -257,17 +263,20 @@ export class RpcClient {
257
263
 
258
264
  /**
259
265
  *
260
- * @param {string} appIdKey
261
- * @param {string|number} pageIdKey
262
- * @param {boolean} [force]
266
+ * @param {import('../types').AppIdKey} appIdKey
267
+ * @param {import('../types').PageIdKey} pageIdKey
263
268
  * @returns {Promise<void>}
264
269
  */
265
- async waitForTarget (appIdKey, pageIdKey, force = false) {
266
- if (!force && !this.needsTarget) {
270
+ async waitForTarget (appIdKey, pageIdKey) {
271
+ if (!this.needsTarget) {
272
+ log.debug(`Target-based communication is not needed, skipping wait for target`);
267
273
  return;
268
274
  }
269
-
270
- if (this.getTarget(appIdKey, pageIdKey)) {
275
+ const target = this.getTarget(appIdKey, pageIdKey);
276
+ if (target) {
277
+ log.debug(
278
+ `The target '${target}' for app '${appIdKey}' and page '${pageIdKey}' already exists, no need to wait`
279
+ );
271
280
  return;
272
281
  }
273
282
 
@@ -289,34 +298,30 @@ export class RpcClient {
289
298
  /**
290
299
  *
291
300
  * @param {string} command
292
- * @param {Record<string, any>} [opts]
301
+ * @param {import('../types').RemoteCommandOpts} opts
293
302
  * @param {boolean} [waitForResponse]
294
303
  * @returns {Promise<any>}
295
304
  */
296
- async send (command, opts = {}, waitForResponse = true) {
305
+ async send (command, opts, waitForResponse = true) {
297
306
  const timer = new timing.Timer().start();
298
307
  const {
299
308
  appIdKey,
300
309
  pageIdKey
301
310
  } = opts;
302
311
  try {
303
- if (!_.isEmpty(appIdKey) && !_.isEmpty(pageIdKey)) {
304
- await this.waitForTarget(appIdKey, pageIdKey);
305
- }
306
312
  return await this.sendToDevice(command, opts, waitForResponse);
307
313
  } catch (err) {
308
- let { message = '' } = err;
309
- message = message.toLowerCase();
310
- if (message.includes(`'target' domain was not found`)) {
311
- log.info('The target device does not support Target based communication. ' +
312
- 'Will follow non-target based communication.');
314
+ const messageLc = (err.message || '').toLowerCase();
315
+ if (messageLc.includes(NO_TARGET_SUPPORTED_ERROR)) {
316
+ log.info(
317
+ 'The target device does not support Target based communication. ' +
318
+ 'Will follow non-target based communication.'
319
+ );
313
320
  this.isTargetBased = false;
314
321
  return await this.sendToDevice(command, opts, waitForResponse);
315
- } else if (message.includes(`domain was not found`) ||
316
- message.includes(`some arguments of method`) ||
317
- message.includes(`missing target`)) {
322
+ } else if (appIdKey && NO_TARGET_PRESENT_YET_ERRORS.some((error) => messageLc.includes(error))) {
318
323
  this.isTargetBased = true;
319
- await this.waitForTarget(appIdKey, pageIdKey);
324
+ await this.waitForTarget(appIdKey, /** @type {import('../types').PageIdKey} */ (pageIdKey));
320
325
  return await this.sendToDevice(command, opts, waitForResponse);
321
326
  }
322
327
  throw err;
@@ -328,11 +333,11 @@ export class RpcClient {
328
333
  /**
329
334
  *
330
335
  * @param {string} command
331
- * @param {Record<string, any>} opts
336
+ * @param {import('../types').RemoteCommandOpts} opts
332
337
  * @param {boolean} [waitForResponse]
333
338
  * @returns {Promise<any>}
334
339
  */
335
- async sendToDevice (command, opts = {}, waitForResponse = true) {
340
+ async sendToDevice (command, opts, waitForResponse = true) {
336
341
  return await new B(async (resolve, reject) => {
337
342
  // promise to be resolved whenever remote debugger
338
343
  // replies to our request
@@ -444,7 +449,7 @@ export class RpcClient {
444
449
  }
445
450
 
446
451
  /**
447
- * @param {string} command
452
+ * @param {import('../types').RemoteCommand} command
448
453
  * @returns {Promise<void>}
449
454
  */
450
455
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
@@ -543,11 +548,10 @@ export class RpcClient {
543
548
  targets[page] = newTargetId;
544
549
  const opts = {appIdKey: app, pageIdKey: parseInt(page, 10)};
545
550
  (async () => {
546
- try {
547
- // This is needed to enable the broadcast of page lifecycle events
548
- await this.sendToDevice('Page.enable', opts, true);
549
- } catch (err) {
550
- log.warn(err.message);
551
+ if (this.fullPageInitialization) {
552
+ await this.initializePageFull(opts.appIdKey, opts.pageIdKey);
553
+ } else {
554
+ await this.initializePage(opts.appIdKey, opts.pageIdKey);
551
555
  }
552
556
  this._targetSubscriptions.emit(ON_TARGET_PROVISIONED_EVENT, {
553
557
  ...opts,
@@ -574,21 +578,24 @@ export class RpcClient {
574
578
  }
575
579
 
576
580
  /**
577
- * @param {string} appIdKey
578
- * @param {string|number} pageIdKey
579
- * @returns {any}
581
+ * @param {import('../types').AppIdKey} [appIdKey]
582
+ * @param {import('../types').PageIdKey} [pageIdKey]
583
+ * @returns {string | undefined}
580
584
  */
581
585
  getTarget (appIdKey, pageIdKey) {
586
+ if (!appIdKey || !pageIdKey) {
587
+ return;
588
+ }
582
589
  return (this.targets[appIdKey] || {})[pageIdKey];
583
590
  }
584
591
 
585
592
  /**
586
- * @param {string} appIdKey
587
- * @param {string|number} pageIdKey
593
+ * @param {import('../types').AppIdKey} appIdKey
594
+ * @param {import('../types').PageIdKey} pageIdKey
588
595
  * @returns {Promise<void>}
589
596
  */
590
597
  async selectPage (appIdKey, pageIdKey) {
591
- /** @type {[string, string|number]} */
598
+ /** @type {[import('../types').AppIdKey, import('../types').PageIdKey]} */
592
599
  this.pendingTargetNotification = [appIdKey, pageIdKey];
593
600
  this.shouldCheckForTarget = false;
594
601
 
@@ -616,6 +623,7 @@ export class RpcClient {
616
623
 
617
624
  this.shouldCheckForTarget = true;
618
625
 
626
+ await this.waitForTarget(appIdKey, pageIdKey);
619
627
  if (this.fullPageInitialization) {
620
628
  await this.initializePageFull(appIdKey, pageIdKey);
621
629
  } else {
@@ -625,8 +633,8 @@ export class RpcClient {
625
633
 
626
634
  /**
627
635
  * Perform the minimal initialization to get the Web Inspector working
628
- * @param {string} appIdKey
629
- * @param {string|number} pageIdKey
636
+ * @param {import('../types').AppIdKey} appIdKey
637
+ * @param {import('../types').PageIdKey} pageIdKey
630
638
  * @returns {Promise<void>}
631
639
  */
632
640
  async initializePage (appIdKey, pageIdKey) {
@@ -635,24 +643,33 @@ export class RpcClient {
635
643
  pageIdKey,
636
644
  };
637
645
 
638
- await this.send('Inspector.enable', sendOpts, false);
639
- await this.send('Page.enable', sendOpts, false);
646
+ // The sequence of domains is important
647
+ for (const domain of [
648
+ 'Inspector.enable',
649
+ 'Page.enable',
650
+ 'Runtime.enable',
651
+
652
+ 'Network.enable',
653
+ 'Heap.enable',
654
+ 'Debugger.enable',
655
+ 'Console.enable',
640
656
 
641
- // go through the tasks to initialize
642
- await this.send('Network.enable', sendOpts, false);
643
- await this.send('Runtime.enable', sendOpts, false);
644
- await this.send('Heap.enable', sendOpts, false);
645
- await this.send('Debugger.enable', sendOpts, false);
646
- await this.send('Console.enable', sendOpts, false);
647
- await this.send('Inspector.initialized', sendOpts, false);
657
+ 'Inspector.initialized',
658
+ ]) {
659
+ try {
660
+ await this.send(domain, sendOpts);
661
+ } catch (err) {
662
+ log.info(`Cannot enable domain '${domain}' during initialization: ${err.message}`);
663
+ }
664
+ }
648
665
  }
649
666
 
650
667
  /**
651
668
  * Mimic every step that Desktop Safari Develop tools uses to initialize a
652
669
  * Web Inspector session
653
670
  *
654
- * @param {string} appIdKey
655
- * @param {string|number} pageIdKey
671
+ * @param {import('../types').AppIdKey} appIdKey
672
+ * @param {import('../types').PageIdKey} pageIdKey
656
673
  * @returns {Promise<void>}
657
674
  */
658
675
  async initializePageFull (appIdKey, pageIdKey) {
@@ -661,66 +678,89 @@ export class RpcClient {
661
678
  pageIdKey,
662
679
  };
663
680
 
664
- await this.send('Inspector.enable', sendOpts, false);
665
- await this.send('Page.enable', sendOpts, false);
666
-
667
- // go through the tasks to initialize
668
- await this.send('Page.getResourceTree', sendOpts, false);
669
- await this.send('Network.enable', sendOpts, false);
670
- await this.send('Network.setResourceCachingDisabled', Object.assign({
671
- disabled: false,
672
- }, sendOpts), false);
673
- await this.send('DOMStorage.enable', sendOpts, false);
674
- await this.send('Database.enable', sendOpts, false);
675
- await this.send('IndexedDB.enable', sendOpts, false);
676
- await this.send('CSS.enable', sendOpts, false);
677
- await this.send('Runtime.enable', sendOpts, false);
678
- await this.send('Heap.enable', sendOpts, false);
679
- await this.send('Memory.enable', sendOpts, false);
680
- await this.send('ApplicationCache.enable', sendOpts, false);
681
- await this.send('ApplicationCache.getFramesWithManifests', sendOpts, false);
682
- await this.send('Timeline.setInstruments', Object.assign({
683
- instruments: ['Timeline', 'ScriptProfiler', 'CPU'],
684
- }, sendOpts), false);
685
- await this.send('Timeline.setAutoCaptureEnabled', Object.assign({
686
- enabled: false,
687
- }, sendOpts), false);
688
- await this.send('Debugger.enable', sendOpts, false);
689
- await this.send('Debugger.setBreakpointsActive', Object.assign({
690
- active: true,
691
- }, sendOpts), false);
692
- await this.send('Debugger.setPauseOnExceptions', Object.assign({
693
- state: 'none',
694
- }, sendOpts), false);
695
- await this.send('Debugger.setPauseOnAssertions', Object.assign({
696
- enabled: false,
697
- }, sendOpts), false);
698
- await this.send('Debugger.setAsyncStackTraceDepth', Object.assign({
699
- depth: 200,
700
- }, sendOpts), false);
701
- await this.send('Debugger.setPauseForInternalScripts', Object.assign({
702
- shouldPause: false,
703
- }, sendOpts), false);
704
-
705
- await this.send('LayerTree.enable', sendOpts, false);
706
- await this.send('Worker.enable', sendOpts, false);
707
- await this.send('Canvas.enable', sendOpts, false);
708
- await this.send('Console.enable', sendOpts, false);
709
- await this.send('DOM.getDocument', sendOpts, false);
710
- const loggingChannels = await this.send('Console.getLoggingChannels', sendOpts);
711
- for (const source of (loggingChannels.channels || []).map((entry) => entry.source)) {
712
- await this.send('Console.setLoggingChannelLevel', Object.assign({
713
- source,
714
- level: 'verbose',
715
- }, sendOpts), false);
716
- }
681
+ // The sequence of commands here is important
682
+ const domainsToOptsMap = {
683
+ 'Inspector.enable': sendOpts,
684
+ 'Page.enable': sendOpts,
685
+ 'Runtime.enable': sendOpts,
686
+
687
+ 'Page.getResourceTree': sendOpts,
688
+ 'Network.enable': sendOpts,
689
+ 'Network.setResourceCachingDisabled': {
690
+ disabled: false,
691
+ ...sendOpts,
692
+ },
693
+ 'DOMStorage.enable': sendOpts,
694
+ 'Database.enable': sendOpts,
695
+ 'IndexedDB.enable': sendOpts,
696
+ 'CSS.enable': sendOpts,
697
+ 'Heap.enable': sendOpts,
698
+ 'Memory.enable': sendOpts,
699
+ 'ApplicationCache.enable': sendOpts,
700
+ 'ApplicationCache.getFramesWithManifests': sendOpts,
701
+ 'Timeline.setInstruments': {
702
+ instruments: ['Timeline', 'ScriptProfiler', 'CPU'],
703
+ ...sendOpts,
704
+ },
705
+ 'Timeline.setAutoCaptureEnabled': {
706
+ enabled: false,
707
+ ...sendOpts,
708
+ },
709
+ 'Debugger.enable': sendOpts,
710
+ 'Debugger.setBreakpointsActive': {
711
+ active: true,
712
+ ...sendOpts,
713
+ },
714
+ 'Debugger.setPauseOnExceptions': {
715
+ state: 'none',
716
+ ...sendOpts,
717
+ },
718
+ 'Debugger.setPauseOnAssertions': {
719
+ enabled: false,
720
+ ...sendOpts,
721
+ },
722
+ 'Debugger.setAsyncStackTraceDepth': {
723
+ depth: 200,
724
+ ...sendOpts,
725
+ },
726
+ 'Debugger.setPauseForInternalScripts': {
727
+ shouldPause: false,
728
+ ...sendOpts,
729
+ },
730
+ 'LayerTree.enable': sendOpts,
731
+ 'Worker.enable': sendOpts,
732
+ 'Canvas.enable': sendOpts,
733
+ 'Console.enable': sendOpts,
734
+ 'DOM.getDocument': sendOpts,
735
+ 'Console.getLoggingChannels': sendOpts,
736
+
737
+ 'Inspector.initialized': sendOpts,
738
+ };
717
739
 
718
- await this.send('Inspector.initialized', sendOpts, false);
740
+ for (const [domain, opts] of Object.entries(domainsToOptsMap)) {
741
+ try {
742
+ const res = await this.send(domain, opts);
743
+ if (domain === 'Console.getLoggingChannels') {
744
+ for (const source of (res?.channels || []).map((/** @type {{ source: any; }} */ entry) => entry.source)) {
745
+ try {
746
+ await this.send('Console.setLoggingChannelLevel', Object.assign({
747
+ source,
748
+ level: 'verbose',
749
+ }, sendOpts));
750
+ } catch (err) {
751
+ log.info(`Cannot set logging channel level for '${source}': ${err.message}`);
752
+ }
753
+ }
754
+ }
755
+ } catch (err) {
756
+ log.info(`Cannot enable domain '${domain}' during full initialization: ${err.message}`);
757
+ }
758
+ }
719
759
  }
720
760
 
721
761
  /**
722
762
  *
723
- * @param {string} appIdKey
763
+ * @param {import('../types').AppIdKey} appIdKey
724
764
  * @returns {Promise<[string, Record<string, any>]>}
725
765
  */
726
766
  async selectApp (appIdKey) {
package/lib/types.ts CHANGED
@@ -19,7 +19,7 @@ export interface AppInfo {
19
19
  }
20
20
 
21
21
  export interface AppPage {
22
- appIdKey: string;
22
+ appIdKey: AppIdKey;
23
23
  pageDict: Page;
24
24
  }
25
25
 
@@ -80,3 +80,29 @@ interface RemoteDebuggerRealDeviceSpecificOptions {
80
80
  }
81
81
 
82
82
  export type RemoteDebuggerRealDeviceOptions = RemoteDebuggerRealDeviceSpecificOptions & RemoteDebuggerOptions;
83
+
84
+ export type AppIdKey = string | number;
85
+ export type PageIdKey = string | number;
86
+
87
+ export interface RemoteCommandOpts {
88
+ appIdKey?: AppIdKey;
89
+ pageIdKey?: PageIdKey;
90
+ id?: string;
91
+ connId?: string;
92
+ senderId?: string;
93
+ targetId?: string;
94
+ bundleId?: string;
95
+ enabled?: boolean;
96
+ [key: string]: any;
97
+ }
98
+
99
+ export interface ProtocolCommandOpts {
100
+ id: string;
101
+ method: string;
102
+ params: StringRecord;
103
+ }
104
+
105
+ export interface RemoteCommand {
106
+ __argument: StringRecord;
107
+ __selector: string;
108
+ }
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "keywords": [
5
5
  "appium"
6
6
  ],
7
- "version": "12.2.5",
7
+ "version": "12.2.7",
8
8
  "author": "Appium Contributors",
9
9
  "license": "Apache-2.0",
10
10
  "repository": {