appium-remote-debugger 15.0.1 → 15.0.3

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.
@@ -144,7 +144,7 @@ export async function execute (command, override) {
144
144
  }
145
145
 
146
146
  const rpcClient = this.requireRpcClient(true);
147
- await rpcClient.waitForPageInitialization(
147
+ await rpcClient.waitForPage(
148
148
  /** @type {import('../types').AppIdKey} */ (appIdKey),
149
149
  /** @type {import('../types').PageIdKey} */ (pageIdKey)
150
150
  );
@@ -182,7 +182,7 @@ export async function navToUrl (url) {
182
182
 
183
183
  this.log.debug(`Navigating to new URL: '${url}'`);
184
184
  setNavigatingToPage(this, true);
185
- await rpcClient.waitForPageInitialization(
185
+ await rpcClient.waitForPage(
186
186
  /** @type {import('../types').AppIdKey} */ (appIdKey),
187
187
  /** @type {import('../types').PageIdKey} */ (pageIdKey)
188
188
  );
@@ -49,7 +49,6 @@ export class RemoteDebugger extends EventEmitter {
49
49
  protected readonly _platformVersion?: string;
50
50
  protected readonly _isSafari: boolean;
51
51
  protected readonly _includeSafari: boolean;
52
- protected readonly _useNewSafari: boolean;
53
52
  protected readonly _garbageCollectOnExecute: boolean;
54
53
  protected readonly _host?: string;
55
54
  protected readonly _port?: number;
@@ -120,7 +119,6 @@ export class RemoteDebugger extends EventEmitter {
120
119
  platformVersion,
121
120
  isSafari = true,
122
121
  includeSafari = false,
123
- useNewSafari = false,
124
122
  pageLoadMs,
125
123
  host,
126
124
  port = REMOTE_DEBUGGER_PORT,
@@ -142,10 +140,8 @@ export class RemoteDebugger extends EventEmitter {
142
140
  this._platformVersion = platformVersion;
143
141
  this._isSafari = isSafari;
144
142
  this._includeSafari = includeSafari;
145
- this._useNewSafari = useNewSafari;
146
143
  this._pageLoadMs = pageLoadMs;
147
144
  this._allowNavigationWithoutReload = false;
148
- this.log.debug(`useNewSafari --> ${this._useNewSafari}`);
149
145
 
150
146
  this._garbageCollectOnExecute = garbageCollectOnExecute;
151
147
 
@@ -24,6 +24,7 @@ const NO_TARGET_PRESENT_YET_ERRORS = [
24
24
  ];
25
25
  export const NEW_APP_CONNECTED_ERROR = 'New application has connected';
26
26
  export const EMPTY_PAGE_DICTIONARY_ERROR = 'Empty page dictionary received';
27
+ const ON_PAGE_INITIALIZED_EVENT = 'onPageInitialized';
27
28
 
28
29
 
29
30
  export class RpcClient {
@@ -129,6 +130,8 @@ export class RpcClient {
129
130
  this._targets = {};
130
131
  this._targetSubscriptions = new EventEmitter();
131
132
  this._provisionedPages = new Set();
133
+ this._pageSelectionLock = new AsyncLock();
134
+ this._pageSelectionMonitor = new EventEmitter();
132
135
 
133
136
  this.remoteMessages = new RemoteMessages();
134
137
 
@@ -568,6 +571,11 @@ export class RpcClient {
568
571
  });
569
572
  } catch (e) {
570
573
  log.warn(e.message);
574
+ } finally {
575
+ // Target creation is happening after provisioning,
576
+ // which means the above lock would be already released
577
+ // after provisioning is completed.
578
+ this._pageSelectionMonitor.emit(ON_PAGE_INITIALIZED_EVENT, appIdKey, pageIdKey);
571
579
  }
572
580
  }
573
581
 
@@ -668,29 +676,74 @@ export class RpcClient {
668
676
  * @returns {Promise<void>}
669
677
  */
670
678
  async selectPage (appIdKey, pageIdKey, pageReadinessDetector) {
671
- this._pendingTargetNotification = [appIdKey, pageIdKey, pageReadinessDetector];
672
- this._provisionedPages.clear();
679
+ await this._pageSelectionLock.acquire(toPageSelectionKey(appIdKey, pageIdKey), async () => {
680
+ this._pendingTargetNotification = [appIdKey, pageIdKey, pageReadinessDetector];
681
+ this._provisionedPages.clear();
673
682
 
674
- // go through the steps that the Desktop Safari system
675
- // goes through to initialize the Web Inspector session
683
+ if (this.getTarget(appIdKey, pageIdKey)) {
684
+ log.debug(`Page '${pageIdKey}' is already selected for app '${appIdKey}'`);
685
+ return;
686
+ }
676
687
 
677
- const sendOpts = {
678
- appIdKey,
679
- pageIdKey,
680
- };
688
+ // go through the steps that the Desktop Safari system
689
+ // goes through to initialize the Web Inspector session
681
690
 
682
- // highlight and then un-highlight the webview
683
- for (const enabled of [true, false]) {
684
- await this.send('indicateWebView', Object.assign({
685
- enabled,
686
- }, sendOpts), false);
687
- }
691
+ const sendOpts = {
692
+ appIdKey,
693
+ pageIdKey,
694
+ };
688
695
 
689
- await this.send('setSenderKey', sendOpts);
690
- log.debug('Sender key set');
696
+ const timeoutMs = Math.trunc(Math.max(this.pageLoadTimeoutMs ?? 0, PAGE_INIT_TIMEOUT_MS) * 1.2);
697
+ const timer = new timing.Timer().start();
691
698
 
692
- await this.waitForTarget(appIdKey, pageIdKey);
693
- await this.waitForPageInitialization(appIdKey, pageIdKey);
699
+ const setupWebview = async () => {
700
+ // highlight and then un-highlight the webview
701
+ for (const enabled of [true, false]) {
702
+ await this.send('indicateWebView', Object.assign({
703
+ enabled,
704
+ }, sendOpts), false);
705
+ }
706
+ await this.send('setSenderKey', sendOpts);
707
+ };
708
+ await B.resolve(setupWebview())
709
+ .timeout(timeoutMs, `Cannot set up page '${pageIdKey}' for app '${appIdKey}' within ${timeoutMs}ms`);
710
+
711
+ const msLeft = Math.max(timeoutMs - Math.trunc(timer.getDuration().asMilliSeconds), 1000);
712
+ log.debug(
713
+ `Waiting up to ${msLeft}ms for page '${pageIdKey}' of app '${appIdKey}' to be selected`
714
+ );
715
+ await new Promise((resolve) => {
716
+ const onPageInitialized = (
717
+ /** @type {import("../types").AppIdKey} */ notifiedAppIdKey,
718
+ /** @type {import("../types").PageIdKey} */ notifiedPageIdKey
719
+ ) => {
720
+ const timeoutHandler = setTimeout(() => {
721
+ this._pageSelectionMonitor.off(ON_PAGE_INITIALIZED_EVENT, onPageInitialized);
722
+ log.warn(
723
+ `Page '${pageIdKey}' for app '${appIdKey}' has not been selected ` +
724
+ `within ${timer.getDuration().asMilliSeconds}ms. Continuing anyway`
725
+ );
726
+ resolve(false);
727
+ }, msLeft);
728
+
729
+ if (notifiedAppIdKey === appIdKey && notifiedPageIdKey === pageIdKey) {
730
+ clearTimeout(timeoutHandler);
731
+ this._pageSelectionMonitor.off(ON_PAGE_INITIALIZED_EVENT, onPageInitialized);
732
+ log.debug(
733
+ `Selected the page ${pageIdKey}@${appIdKey} after ${timer.getDuration().asMilliSeconds}ms`
734
+ );
735
+ resolve(true);
736
+ } else {
737
+ log.debug(
738
+ `Got notified that page ${notifiedPageIdKey}@${notifiedAppIdKey} is initialized, ` +
739
+ `but we are waiting for ${pageIdKey}@${appIdKey}. Continuing to wait`
740
+ );
741
+ }
742
+ };
743
+
744
+ this._pageSelectionMonitor.on(ON_PAGE_INITIALIZED_EVENT, onPageInitialized);
745
+ });
746
+ });
694
747
  }
695
748
 
696
749
  /**
@@ -986,15 +1039,19 @@ export class RpcClient {
986
1039
  *
987
1040
  * @param {import('../types').AppIdKey} appIdKey
988
1041
  * @param {import('../types').PageIdKey} pageIdKey
1042
+ * @returns {Promise<void>}
989
1043
  */
990
- async waitForPageInitialization (appIdKey, pageIdKey) {
1044
+ async waitForPage (appIdKey, pageIdKey) {
991
1045
  const appTargetsMap = this.targets[appIdKey];
992
1046
  if (!appTargetsMap) {
993
1047
  throw new Error(`No targets found for app '${appIdKey}'`);
994
1048
  }
995
1049
  const lock = appTargetsMap.lock;
996
1050
  const timer = new timing.Timer().start();
997
- await lock.acquire(pageIdKey, async () => await B.delay(0));
1051
+ await Promise.all([
1052
+ lock.acquire(pageIdKey, async () => await B.delay(0)),
1053
+ this._pageSelectionLock.acquire(toPageSelectionKey(appIdKey, pageIdKey), async () => await B.delay(0))
1054
+ ]);
998
1055
  const durationMs = timer.getDuration().asMilliSeconds;
999
1056
  if (durationMs > 10) {
1000
1057
  log.debug(`Waited ${durationMs}ms until the page ${pageIdKey}@${appIdKey} is initialized`);
@@ -1002,6 +1059,16 @@ export class RpcClient {
1002
1059
  }
1003
1060
  }
1004
1061
 
1062
+ /**
1063
+ *
1064
+ * @param {import('../types').AppIdKey} appIdKey
1065
+ * @param {import('../types').PageIdKey} pageIdKey
1066
+ * @returns {string}
1067
+ */
1068
+ function toPageSelectionKey(appIdKey, pageIdKey) {
1069
+ return `${appIdKey}:${pageIdKey}`;
1070
+ }
1071
+
1005
1072
  export default RpcClient;
1006
1073
 
1007
1074
  /**
package/lib/types.ts CHANGED
@@ -49,7 +49,7 @@ export interface RemoteDebuggerOptions {
49
49
  platformVersion?: string;
50
50
  isSafari?: boolean;
51
51
  includeSafari?: boolean;
52
- /** for web inspector, whether this is a new Safari instance */
52
+ /** @deprecated - deprecated for removal, not used anywhere */
53
53
  useNewSafari?: boolean;
54
54
  /** the time, in ms, that should be waited for page loading */
55
55
  pageLoadMs?: number;
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "keywords": [
5
5
  "appium"
6
6
  ],
7
- "version": "15.0.1",
7
+ "version": "15.0.3",
8
8
  "author": "Appium Contributors",
9
9
  "license": "Apache-2.0",
10
10
  "repository": {