appium-remote-debugger 14.0.2 → 14.0.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.
- package/CHANGELOG.md +12 -0
- package/build/lib/mixins/connect.js +1 -1
- package/build/lib/mixins/connect.js.map +1 -1
- package/build/lib/mixins/execute.d.ts +1 -1
- package/build/lib/mixins/execute.d.ts.map +1 -1
- package/build/lib/mixins/execute.js +3 -7
- package/build/lib/mixins/execute.js.map +1 -1
- package/build/lib/mixins/navigate.d.ts.map +1 -1
- package/build/lib/mixins/navigate.js +15 -14
- package/build/lib/mixins/navigate.js.map +1 -1
- package/build/lib/rpc/rpc-client.d.ts +3 -10
- package/build/lib/rpc/rpc-client.d.ts.map +1 -1
- package/build/lib/rpc/rpc-client.js +67 -54
- package/build/lib/rpc/rpc-client.js.map +1 -1
- package/build/tsconfig.tsbuildinfo +1 -1
- package/lib/mixins/connect.js +1 -1
- package/lib/mixins/execute.js +3 -9
- package/lib/mixins/navigate.js +19 -15
- package/lib/rpc/rpc-client.js +75 -54
- package/package.json +1 -1
package/lib/mixins/execute.js
CHANGED
|
@@ -12,7 +12,6 @@ import _ from 'lodash';
|
|
|
12
12
|
import {
|
|
13
13
|
getAppIdKey,
|
|
14
14
|
getPageIdKey,
|
|
15
|
-
getPageLoading,
|
|
16
15
|
getGarbageCollectOnExecute,
|
|
17
16
|
} from './property-accessors';
|
|
18
17
|
|
|
@@ -31,7 +30,7 @@ const RPC_RESPONSE_TIMEOUT_MS = 5000;
|
|
|
31
30
|
export async function executeAtom (atom, args = [], frames = []) {
|
|
32
31
|
this.log.debug(`Executing atom '${atom}' with 'args=${JSON.stringify(args)}; frames=${frames}'`);
|
|
33
32
|
const script = await getScriptForAtom(atom, args, frames);
|
|
34
|
-
const value = await this.execute(script
|
|
33
|
+
const value = await this.execute(script);
|
|
35
34
|
this.log.debug(`Received result for atom '${atom}' execution: ${_.truncate(simpleStringify(value), {length: RESPONSE_LOG_LENGTH})}`);
|
|
36
35
|
return value;
|
|
37
36
|
}
|
|
@@ -130,16 +129,11 @@ export async function executeAtomAsync (atom, args = [], frames = []) {
|
|
|
130
129
|
/**
|
|
131
130
|
* @this {RemoteDebugger}
|
|
132
131
|
* @param {string} command
|
|
133
|
-
* @param {boolean} [override]
|
|
132
|
+
* @param {boolean} [override] - Deprecated and unused
|
|
134
133
|
* @returns {Promise<any>}
|
|
135
134
|
*/
|
|
135
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
136
136
|
export async function execute (command, override) {
|
|
137
|
-
// if the page is not loaded yet, wait for it
|
|
138
|
-
if (getPageLoading(this) && !override) {
|
|
139
|
-
this.log.debug('Trying to execute but page is not loaded.');
|
|
140
|
-
await this.waitForDom();
|
|
141
|
-
}
|
|
142
|
-
|
|
143
137
|
const {appIdKey, pageIdKey} = checkParams({
|
|
144
138
|
appIdKey: getAppIdKey(this),
|
|
145
139
|
pageIdKey: getPageIdKey(this),
|
package/lib/mixins/navigate.js
CHANGED
|
@@ -73,12 +73,9 @@ export function isPageLoadingCompleted (readyState) {
|
|
|
73
73
|
* @returns {Promise<void>}
|
|
74
74
|
*/
|
|
75
75
|
export async function waitForDom (startPageLoadTimer) {
|
|
76
|
-
this.log.debug('Waiting for page readiness');
|
|
77
76
|
const readinessTimeoutMs = this.pageLoadMs;
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
startPageLoadTimer = new timing.Timer().start();
|
|
81
|
-
}
|
|
77
|
+
this.log.debug(`Waiting up to ${readinessTimeoutMs}ms for the page to be ready`);
|
|
78
|
+
const timer = startPageLoadTimer ?? new timing.Timer().start();
|
|
82
79
|
|
|
83
80
|
let isPageLoading = true;
|
|
84
81
|
setPageLoading(this, true);
|
|
@@ -88,8 +85,7 @@ export async function waitForDom (startPageLoadTimer) {
|
|
|
88
85
|
let retry = 0;
|
|
89
86
|
while (isPageLoading) {
|
|
90
87
|
// if we are ready, or we've spend too much time on this
|
|
91
|
-
|
|
92
|
-
const elapsedMs = startPageLoadTimer.getDuration().asMilliSeconds;
|
|
88
|
+
const elapsedMs = timer.getDuration().asMilliSeconds;
|
|
93
89
|
// exponential retry
|
|
94
90
|
const intervalMs = Math.min(
|
|
95
91
|
PAGE_READINESS_CHECK_INTERVAL_MS * Math.pow(2, retry),
|
|
@@ -105,7 +101,8 @@ export async function waitForDom (startPageLoadTimer) {
|
|
|
105
101
|
return;
|
|
106
102
|
}
|
|
107
103
|
|
|
108
|
-
|
|
104
|
+
const maxWaitMs = (readinessTimeoutMs - elapsedMs) * 0.95;
|
|
105
|
+
if (await this.checkPageIsReady(maxWaitMs)) {
|
|
109
106
|
if (isPageLoading) {
|
|
110
107
|
this.log.debug(`Page is ready in ${elapsedMs}ms`);
|
|
111
108
|
isPageLoading = false;
|
|
@@ -113,7 +110,9 @@ export async function waitForDom (startPageLoadTimer) {
|
|
|
113
110
|
return;
|
|
114
111
|
}
|
|
115
112
|
if (elapsedMs > readinessTimeoutMs) {
|
|
116
|
-
this.log.info(
|
|
113
|
+
this.log.info(
|
|
114
|
+
`Timed out after ${readinessTimeoutMs}ms of waiting for the page readiness. Continuing anyway`
|
|
115
|
+
);
|
|
117
116
|
isPageLoading = false;
|
|
118
117
|
return;
|
|
119
118
|
}
|
|
@@ -145,16 +144,21 @@ export async function checkPageIsReady (timeoutMs) {
|
|
|
145
144
|
const readyCmd = 'document.readyState;';
|
|
146
145
|
const actualTimeoutMs = timeoutMs ?? getPageReadyTimeout(this);
|
|
147
146
|
try {
|
|
148
|
-
const readyState = await B.resolve(this.execute(readyCmd
|
|
147
|
+
const readyState = await B.resolve(this.execute(readyCmd))
|
|
149
148
|
.timeout(actualTimeoutMs);
|
|
150
|
-
this.log.debug(
|
|
151
|
-
|
|
149
|
+
this.log.debug(
|
|
150
|
+
JSON.stringify({
|
|
151
|
+
readyState,
|
|
152
|
+
pageLoadStrategy: getPageLoadStartegy(this) ?? PAGE_LOAD_STRATEGY.NORMAL,
|
|
153
|
+
})
|
|
154
|
+
);
|
|
152
155
|
return this.isPageLoadingCompleted(readyState);
|
|
153
156
|
} catch (err) {
|
|
154
|
-
if (
|
|
155
|
-
|
|
157
|
+
if (err instanceof BTimeoutError) {
|
|
158
|
+
this.log.debug(`Page readiness check timed out after ${actualTimeoutMs}ms`);
|
|
159
|
+
} else {
|
|
160
|
+
this.log.warn(`Page readiness check has failed. Original error: ${err.message}`);
|
|
156
161
|
}
|
|
157
|
-
this.log.debug(`Page readiness check timed out after ${actualTimeoutMs}ms`);
|
|
158
162
|
return false;
|
|
159
163
|
}
|
|
160
164
|
}
|
package/lib/rpc/rpc-client.js
CHANGED
|
@@ -10,6 +10,9 @@ import AsyncLock from 'async-lock';
|
|
|
10
10
|
|
|
11
11
|
const DATA_LOG_LENGTH = {length: 200};
|
|
12
12
|
const MIN_WAIT_FOR_TARGET_TIMEOUT_MS = 30000;
|
|
13
|
+
// Sometimes page initialization can take a long time, especially in slow CI environments,
|
|
14
|
+
// although we still should not allow it to take forever
|
|
15
|
+
const PAGE_INIT_TIMEOUT_MS = 10 * 60 * 1000; // 10 minutes
|
|
13
16
|
const WAIT_FOR_TARGET_INTERVAL_MS = 100;
|
|
14
17
|
const MIN_PLATFORM_FOR_TARGET_BASED = '12.2';
|
|
15
18
|
// `Target.exists` protocol method was removed from WebKit in 13.4
|
|
@@ -409,7 +412,7 @@ export class RpcClient {
|
|
|
409
412
|
messageHandled = false;
|
|
410
413
|
// do not log receipts
|
|
411
414
|
// @ts-ignore messageHandler must be defined
|
|
412
|
-
this.messageHandler.once(msgId.toString(),
|
|
415
|
+
this.messageHandler.once(msgId.toString(), (err) => {
|
|
413
416
|
if (err) {
|
|
414
417
|
// we are not waiting for this, and if it errors it is most likely
|
|
415
418
|
// a protocol change. Log and check during testing
|
|
@@ -426,7 +429,7 @@ export class RpcClient {
|
|
|
426
429
|
// @ts-ignore messageHandler must be defined
|
|
427
430
|
} else if (this.messageHandler.listeners(cmd.__selector).length) {
|
|
428
431
|
// @ts-ignore messageHandler must be defined
|
|
429
|
-
this.messageHandler.prependOnceListener(cmd.__selector,
|
|
432
|
+
this.messageHandler.prependOnceListener(cmd.__selector, (err, ...args) => {
|
|
430
433
|
if (err) {
|
|
431
434
|
return reject(err);
|
|
432
435
|
}
|
|
@@ -436,7 +439,7 @@ export class RpcClient {
|
|
|
436
439
|
});
|
|
437
440
|
} else if (hasSocketData) {
|
|
438
441
|
// @ts-ignore messageHandler must be defined
|
|
439
|
-
this.messageHandler.once(msgId.toString(),
|
|
442
|
+
this.messageHandler.once(msgId.toString(), (err, value) => {
|
|
440
443
|
if (err) {
|
|
441
444
|
return reject(new Error(`Remote debugger error with code '${err.code}': ${err.message}`));
|
|
442
445
|
}
|
|
@@ -521,52 +524,74 @@ export class RpcClient {
|
|
|
521
524
|
return;
|
|
522
525
|
}
|
|
523
526
|
|
|
527
|
+
const pageInitTimeoutMs = Math.max(this.pageLoadTimeoutMs ?? 0, PAGE_INIT_TIMEOUT_MS);
|
|
528
|
+
if (!_.isPlainObject(this.targets[appIdKey])) {
|
|
529
|
+
this.targets[appIdKey] = {
|
|
530
|
+
lock: new AsyncLock({maxOccupationTime: pageInitTimeoutMs}),
|
|
531
|
+
};
|
|
532
|
+
}
|
|
533
|
+
const timer = new timing.Timer().start();
|
|
534
|
+
|
|
524
535
|
if (targetInfo.isProvisional) {
|
|
525
536
|
log.debug(
|
|
526
|
-
`Provisional target created for app '${appIdKey}' and page '${pageIdKey}': '${targetInfo
|
|
537
|
+
`Provisional target created for app '${appIdKey}' and page '${pageIdKey}': '${JSON.stringify(targetInfo)}'`
|
|
527
538
|
);
|
|
528
|
-
if (!targetInfo.isPaused) {
|
|
529
|
-
return;
|
|
530
|
-
}
|
|
531
539
|
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
if (appTargetsMap) {
|
|
535
|
-
await appTargetsMap.lock.acquire(toLockKey(appIdKey, pageIdKey), async () => {
|
|
540
|
+
try {
|
|
541
|
+
await this.targets[appIdKey].lock.acquire(targetInfo.targetId, async () => {
|
|
536
542
|
try {
|
|
537
543
|
await this._initializePage(appIdKey, pageIdKey, targetInfo.targetId);
|
|
538
544
|
} finally {
|
|
539
|
-
|
|
545
|
+
if (targetInfo.isPaused) {
|
|
546
|
+
await this._resumeTarget(appIdKey, pageIdKey, targetInfo.targetId);
|
|
547
|
+
} else {
|
|
548
|
+
log.debug(
|
|
549
|
+
`Provisional target ${targetInfo.targetId}@${appIdKey} is not paused. This might be a problem`
|
|
550
|
+
);
|
|
551
|
+
}
|
|
540
552
|
}
|
|
541
553
|
});
|
|
554
|
+
} catch (e) {
|
|
555
|
+
log.warn(
|
|
556
|
+
`Cannot complete the initialization of the provisional target '${targetInfo.targetId}' ` +
|
|
557
|
+
`after ${timer.getDuration().asMilliSeconds}ms: ${e.message}`
|
|
558
|
+
);
|
|
542
559
|
}
|
|
543
560
|
return;
|
|
544
561
|
}
|
|
545
562
|
|
|
546
563
|
log.debug(`Target created for app '${appIdKey}' and page '${pageIdKey}': ${JSON.stringify(targetInfo)}`);
|
|
547
564
|
if (_.has(this.targets[appIdKey], pageIdKey)) {
|
|
548
|
-
log.debug(
|
|
565
|
+
log.debug(
|
|
566
|
+
`There is already a target for this app and page ('${this.targets[appIdKey][pageIdKey]}'). ` +
|
|
567
|
+
`This might cause problems`
|
|
568
|
+
);
|
|
549
569
|
}
|
|
550
|
-
const lock = new AsyncLock();
|
|
551
|
-
this.targets[app] = this.targets[app] || { lock };
|
|
552
570
|
this.targets[appIdKey][pageIdKey] = targetInfo.targetId;
|
|
553
571
|
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
572
|
+
try {
|
|
573
|
+
await this.targets[appIdKey].lock.acquire(targetInfo.targetId, async () => {
|
|
574
|
+
try {
|
|
575
|
+
await this.send('Target.setPauseOnStart', {
|
|
576
|
+
pauseOnStart: true,
|
|
577
|
+
appIdKey,
|
|
578
|
+
pageIdKey,
|
|
579
|
+
});
|
|
580
|
+
} catch {}
|
|
581
|
+
try {
|
|
582
|
+
await this._initializePage(appIdKey, pageIdKey, targetInfo.targetId);
|
|
583
|
+
} finally {
|
|
584
|
+
if (targetInfo.isPaused) {
|
|
585
|
+
await this._resumeTarget(appIdKey, pageIdKey, targetInfo.targetId);
|
|
586
|
+
}
|
|
567
587
|
}
|
|
568
|
-
}
|
|
569
|
-
})
|
|
588
|
+
});
|
|
589
|
+
} catch (e) {
|
|
590
|
+
log.warn(
|
|
591
|
+
`Cannot complete the initialization of the new target '${targetInfo.targetId}' ` +
|
|
592
|
+
`after ${timer.getDuration().asMilliSeconds}ms: ${e.message}`
|
|
593
|
+
);
|
|
594
|
+
}
|
|
570
595
|
}
|
|
571
596
|
|
|
572
597
|
/**
|
|
@@ -656,7 +681,7 @@ export class RpcClient {
|
|
|
656
681
|
if (!appIdKey || !pageIdKey) {
|
|
657
682
|
return;
|
|
658
683
|
}
|
|
659
|
-
return
|
|
684
|
+
return this.targets[appIdKey]?.[pageIdKey];
|
|
660
685
|
}
|
|
661
686
|
|
|
662
687
|
/**
|
|
@@ -715,6 +740,7 @@ export class RpcClient {
|
|
|
715
740
|
};
|
|
716
741
|
|
|
717
742
|
log.debug(`Initializing page '${pageIdKey}' for app '${appIdKey}'`);
|
|
743
|
+
const timer = new timing.Timer().start();
|
|
718
744
|
if (!this.fullPageInitialization) {
|
|
719
745
|
// The sequence of domains is important
|
|
720
746
|
for (const domain of [
|
|
@@ -735,7 +761,10 @@ export class RpcClient {
|
|
|
735
761
|
log.info(`Cannot enable domain '${domain}' during initialization: ${err.message}`);
|
|
736
762
|
}
|
|
737
763
|
}
|
|
738
|
-
log.debug(
|
|
764
|
+
log.debug(
|
|
765
|
+
`Simple initialization of page '${pageIdKey}' for app '${appIdKey}' completed ` +
|
|
766
|
+
`in ${timer.getDuration().asMilliSeconds}ms`
|
|
767
|
+
);
|
|
739
768
|
return;
|
|
740
769
|
}
|
|
741
770
|
|
|
@@ -817,7 +846,10 @@ export class RpcClient {
|
|
|
817
846
|
log.info(`Cannot enable domain '${domain}' during full initialization: ${err.message}`);
|
|
818
847
|
}
|
|
819
848
|
}
|
|
820
|
-
log.debug(
|
|
849
|
+
log.debug(
|
|
850
|
+
`Full initialization of page '${pageIdKey}' for app '${appIdKey}' completed ` +
|
|
851
|
+
`in ${timer.getDuration().asMilliSeconds}ms`
|
|
852
|
+
);
|
|
821
853
|
}
|
|
822
854
|
|
|
823
855
|
/**
|
|
@@ -904,7 +936,7 @@ export class RpcClient {
|
|
|
904
936
|
*
|
|
905
937
|
* @param {import('../types').AppIdKey} appIdKey
|
|
906
938
|
* @param {import('../types').PageIdKey} pageIdKey
|
|
907
|
-
* @param {import('../types').TargetId}
|
|
939
|
+
* @param {import('../types').TargetId} targetId
|
|
908
940
|
* @returns {Promise<void>}
|
|
909
941
|
*/
|
|
910
942
|
async _resumeTarget (appIdKey, pageIdKey, targetId) {
|
|
@@ -915,7 +947,9 @@ export class RpcClient {
|
|
|
915
947
|
targetId,
|
|
916
948
|
});
|
|
917
949
|
log.debug(`Successfully resumed the target ${targetId}@${appIdKey}`);
|
|
918
|
-
} catch {
|
|
950
|
+
} catch (e) {
|
|
951
|
+
log.warn(`Could not resume the target ${targetId}@${appIdKey}: ${e.message}`);
|
|
952
|
+
}
|
|
919
953
|
}
|
|
920
954
|
|
|
921
955
|
/**
|
|
@@ -928,29 +962,16 @@ export class RpcClient {
|
|
|
928
962
|
if (!appTargetsMap) {
|
|
929
963
|
throw new Error(`No targets found for app '${appIdKey}'`);
|
|
930
964
|
}
|
|
931
|
-
/** @type {AsyncLock | undefined} */
|
|
932
965
|
const lock = appTargetsMap.lock;
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
log.debug(
|
|
938
|
-
`The initalization of the page ${pageIdKey}@${appIdKey} took ${timer.getDuration().asMilliSeconds}ms`
|
|
939
|
-
);
|
|
966
|
+
const timer = new timing.Timer().start();
|
|
967
|
+
await lock.acquire(appTargetsMap[pageIdKey], async () => await B.delay(0));
|
|
968
|
+
const durationMs = timer.getDuration().asMilliSeconds;
|
|
969
|
+
if (durationMs > 10) {
|
|
970
|
+
log.debug(`Waited ${durationMs}ms until the page ${pageIdKey}@${appIdKey} is initialized`);
|
|
940
971
|
}
|
|
941
972
|
}
|
|
942
973
|
}
|
|
943
974
|
|
|
944
|
-
/**
|
|
945
|
-
*
|
|
946
|
-
* @param {import('../types').AppIdKey} appIdKey
|
|
947
|
-
* @param {import('../types').PageIdKey} pageIdKey
|
|
948
|
-
* @returns {string}
|
|
949
|
-
*/
|
|
950
|
-
export function toLockKey(appIdKey, pageIdKey) {
|
|
951
|
-
return `${appIdKey}-${pageIdKey}`;
|
|
952
|
-
}
|
|
953
|
-
|
|
954
975
|
export default RpcClient;
|
|
955
976
|
|
|
956
977
|
/**
|
|
@@ -972,6 +993,6 @@ export default RpcClient;
|
|
|
972
993
|
*/
|
|
973
994
|
|
|
974
995
|
/**
|
|
975
|
-
* @typedef {PageDict & {provisional?: import('../types').ProvisionalTargetInfo, lock:
|
|
996
|
+
* @typedef {PageDict & {provisional?: import('../types').ProvisionalTargetInfo, lock: AsyncLock}} PagesToTargets
|
|
976
997
|
* @typedef {{[key: import('../types').AppIdKey]: PagesToTargets}} AppToTargetsMap
|
|
977
998
|
*/
|