appium-remote-debugger 15.2.9 → 15.2.10
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 +6 -0
- package/build/lib/mixins/connect.d.ts +47 -31
- package/build/lib/mixins/connect.d.ts.map +1 -1
- package/build/lib/mixins/connect.js +105 -86
- package/build/lib/mixins/connect.js.map +1 -1
- package/build/lib/mixins/message-handlers.d.ts +63 -43
- package/build/lib/mixins/message-handlers.d.ts.map +1 -1
- package/build/lib/mixins/message-handlers.js +74 -57
- package/build/lib/mixins/message-handlers.js.map +1 -1
- package/build/lib/mixins/navigate.d.ts +39 -27
- package/build/lib/mixins/navigate.d.ts.map +1 -1
- package/build/lib/mixins/navigate.js +31 -31
- package/build/lib/mixins/navigate.js.map +1 -1
- package/build/tsconfig.tsbuildinfo +1 -1
- package/lib/mixins/{connect.js → connect.ts} +153 -113
- package/lib/mixins/message-handlers.ts +272 -0
- package/lib/mixins/{navigate.js → navigate.ts} +44 -41
- package/package.json +1 -1
- package/lib/mixins/message-handlers.js +0 -224
|
@@ -13,6 +13,8 @@ import {
|
|
|
13
13
|
getPageIdKey,
|
|
14
14
|
setNavigatingToPage,
|
|
15
15
|
} from './property-accessors';
|
|
16
|
+
import type { RemoteDebugger } from '../remote-debugger';
|
|
17
|
+
import type { AppIdKey, PageIdKey } from '../types';
|
|
16
18
|
|
|
17
19
|
export const DEFAULT_PAGE_READINESS_TIMEOUT_MS = 20 * 1000;
|
|
18
20
|
const PAGE_READINESS_CHECK_INTERVAL_MS = 50;
|
|
@@ -27,32 +29,34 @@ const PAGE_LOAD_STRATEGY = Object.freeze({
|
|
|
27
29
|
});
|
|
28
30
|
|
|
29
31
|
/**
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
+
* Emits a frame detached event when a frame is detached from the page.
|
|
33
|
+
* This is typically called by the RPC client when receiving a Page.frameDetached event.
|
|
32
34
|
*/
|
|
33
|
-
export function frameDetached
|
|
35
|
+
export function frameDetached(this: RemoteDebugger): void {
|
|
34
36
|
this.emit(events.EVENT_FRAMES_DETACHED);
|
|
35
37
|
}
|
|
36
38
|
|
|
37
39
|
/**
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
+
* Cancels the current page load operation by unregistering from page readiness
|
|
41
|
+
* notifications and canceling any pending page load delay.
|
|
40
42
|
*/
|
|
41
|
-
export function cancelPageLoad
|
|
43
|
+
export function cancelPageLoad(this: RemoteDebugger): void {
|
|
42
44
|
this.log.debug('Unregistering from page readiness notifications');
|
|
43
45
|
setPageLoading(this, false);
|
|
44
46
|
getPageLoadDelay(this)?.cancel();
|
|
45
47
|
}
|
|
46
48
|
|
|
47
49
|
/**
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
+
* Determines if the current readyState indicates that page loading is completed
|
|
51
|
+
* based on the configured page load strategy.
|
|
50
52
|
*
|
|
51
|
-
* @
|
|
52
|
-
* @
|
|
53
|
-
*
|
|
53
|
+
* @param readyState - The document readyState value ('loading', 'interactive', or 'complete').
|
|
54
|
+
* @returns True if the page load is considered complete for the current strategy:
|
|
55
|
+
* - 'eager': returns true when readyState is not 'loading'
|
|
56
|
+
* - 'none': always returns true
|
|
57
|
+
* - 'normal' (default): returns true only when readyState is 'complete'
|
|
54
58
|
*/
|
|
55
|
-
export function isPageLoadingCompleted
|
|
59
|
+
export function isPageLoadingCompleted(this: RemoteDebugger, readyState: string): boolean {
|
|
56
60
|
const pageLoadStrategy = _.toLower(getPageLoadStartegy(this));
|
|
57
61
|
switch (pageLoadStrategy) {
|
|
58
62
|
case PAGE_LOAD_STRATEGY.EAGER:
|
|
@@ -67,11 +71,14 @@ export function isPageLoadingCompleted (readyState) {
|
|
|
67
71
|
}
|
|
68
72
|
|
|
69
73
|
/**
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
74
|
+
* Waits for the DOM to be ready by periodically checking the page readiness state.
|
|
75
|
+
* Uses exponential backoff for retry intervals and respects the configured page load
|
|
76
|
+
* strategy and timeout settings.
|
|
77
|
+
*
|
|
78
|
+
* @param startPageLoadTimer - Optional timer instance to use for tracking elapsed time.
|
|
79
|
+
* If not provided, a new timer will be created and started.
|
|
73
80
|
*/
|
|
74
|
-
export async function waitForDom
|
|
81
|
+
export async function waitForDom(this: RemoteDebugger, startPageLoadTimer?: timing.Timer): Promise<void> {
|
|
75
82
|
const readinessTimeoutMs = this.pageLoadMs;
|
|
76
83
|
this.log.debug(`Waiting up to ${readinessTimeoutMs}ms for the page to be ready`);
|
|
77
84
|
const timer = startPageLoadTimer ?? new timing.Timer().start();
|
|
@@ -79,7 +86,6 @@ export async function waitForDom (startPageLoadTimer) {
|
|
|
79
86
|
let isPageLoading = true;
|
|
80
87
|
setPageLoading(this, true);
|
|
81
88
|
setPageLoadDelay(this, util.cancellableDelay(readinessTimeoutMs));
|
|
82
|
-
/** @type {B<void>} */
|
|
83
89
|
const pageReadinessPromise = B.resolve((async () => {
|
|
84
90
|
let retry = 0;
|
|
85
91
|
while (isPageLoading) {
|
|
@@ -118,7 +124,6 @@ export async function waitForDom (startPageLoadTimer) {
|
|
|
118
124
|
retry++;
|
|
119
125
|
}
|
|
120
126
|
})());
|
|
121
|
-
/** @type {B<void>} */
|
|
122
127
|
const cancellationPromise = B.resolve((async () => {
|
|
123
128
|
try {
|
|
124
129
|
await getPageLoadDelay(this);
|
|
@@ -135,11 +140,15 @@ export async function waitForDom (startPageLoadTimer) {
|
|
|
135
140
|
}
|
|
136
141
|
|
|
137
142
|
/**
|
|
138
|
-
*
|
|
139
|
-
*
|
|
140
|
-
*
|
|
143
|
+
* Checks if the current page is ready by executing a JavaScript command to
|
|
144
|
+
* retrieve the document readyState and evaluating it against the page load strategy.
|
|
145
|
+
*
|
|
146
|
+
* @param timeoutMs - Optional timeout in milliseconds for the readyState check.
|
|
147
|
+
* If not provided, uses the configured page ready timeout.
|
|
148
|
+
* @returns A promise that resolves to true if the page is ready according to
|
|
149
|
+
* the page load strategy, false otherwise or if the check times out.
|
|
141
150
|
*/
|
|
142
|
-
export async function checkPageIsReady
|
|
151
|
+
export async function checkPageIsReady(this: RemoteDebugger, timeoutMs?: number): Promise<boolean> {
|
|
143
152
|
const readyCmd = 'document.readyState;';
|
|
144
153
|
const actualTimeoutMs = timeoutMs ?? getPageReadyTimeout(this);
|
|
145
154
|
try {
|
|
@@ -152,7 +161,7 @@ export async function checkPageIsReady (timeoutMs) {
|
|
|
152
161
|
})
|
|
153
162
|
);
|
|
154
163
|
return this.isPageLoadingCompleted(readyState);
|
|
155
|
-
} catch (err) {
|
|
164
|
+
} catch (err: any) {
|
|
156
165
|
if (err instanceof BTimeoutError) {
|
|
157
166
|
this.log.debug(`Page readiness check timed out after ${actualTimeoutMs}ms`);
|
|
158
167
|
} else {
|
|
@@ -163,11 +172,14 @@ export async function checkPageIsReady (timeoutMs) {
|
|
|
163
172
|
}
|
|
164
173
|
|
|
165
174
|
/**
|
|
166
|
-
*
|
|
167
|
-
*
|
|
168
|
-
*
|
|
175
|
+
* Navigates to a new URL and waits for the page to be ready.
|
|
176
|
+
* Validates the URL format, waits for the page to be available, sends the navigation
|
|
177
|
+
* command, and monitors for the Page.loadEventFired event or timeout.
|
|
178
|
+
*
|
|
179
|
+
* @param url - The URL to navigate to. Must be a valid URL format.
|
|
180
|
+
* @throws TypeError if the provided URL is not a valid URL format.
|
|
169
181
|
*/
|
|
170
|
-
export async function navToUrl
|
|
182
|
+
export async function navToUrl(this: RemoteDebugger, url: string): Promise<void> {
|
|
171
183
|
const {appIdKey, pageIdKey} = checkParams({
|
|
172
184
|
appIdKey: getAppIdKey(this),
|
|
173
185
|
pageIdKey: getPageIdKey(this),
|
|
@@ -183,22 +195,18 @@ export async function navToUrl (url) {
|
|
|
183
195
|
this.log.debug(`Navigating to new URL: '${url}'`);
|
|
184
196
|
setNavigatingToPage(this, true);
|
|
185
197
|
await rpcClient.waitForPage(
|
|
186
|
-
|
|
187
|
-
|
|
198
|
+
appIdKey as AppIdKey,
|
|
199
|
+
pageIdKey as PageIdKey
|
|
188
200
|
);
|
|
189
201
|
const readinessTimeoutMs = this.pageLoadMs;
|
|
190
|
-
|
|
191
|
-
let
|
|
192
|
-
/** @type {NodeJS.Timeout|undefined|null} */
|
|
193
|
-
let onPageLoadedTimeout;
|
|
202
|
+
let onPageLoaded: (() => void) | undefined;
|
|
203
|
+
let onPageLoadedTimeout: NodeJS.Timeout | undefined | null;
|
|
194
204
|
setPageLoadDelay(this, util.cancellableDelay(readinessTimeoutMs));
|
|
195
205
|
setPageLoading(this, true);
|
|
196
206
|
let isPageLoading = true;
|
|
197
|
-
// /** @type {Promise<void>|null} */
|
|
198
207
|
const start = new timing.Timer().start();
|
|
199
208
|
|
|
200
|
-
|
|
201
|
-
const pageReadinessPromise = new B((resolve) => {
|
|
209
|
+
const pageReadinessPromise = new B<void>((resolve) => {
|
|
202
210
|
onPageLoadedTimeout = setTimeout(() => {
|
|
203
211
|
if (isPageLoading) {
|
|
204
212
|
isPageLoading = false;
|
|
@@ -231,7 +239,6 @@ export async function navToUrl (url) {
|
|
|
231
239
|
pageIdKey,
|
|
232
240
|
});
|
|
233
241
|
});
|
|
234
|
-
/** @type {B<void>} */
|
|
235
242
|
const cancellationPromise = B.resolve((async () => {
|
|
236
243
|
try {
|
|
237
244
|
await getPageLoadDelay(this);
|
|
@@ -254,7 +261,3 @@ export async function navToUrl (url) {
|
|
|
254
261
|
}
|
|
255
262
|
}
|
|
256
263
|
}
|
|
257
|
-
|
|
258
|
-
/**
|
|
259
|
-
* @typedef {import('../remote-debugger').RemoteDebugger} RemoteDebugger
|
|
260
|
-
*/
|
package/package.json
CHANGED
|
@@ -1,224 +0,0 @@
|
|
|
1
|
-
import { events } from './events';
|
|
2
|
-
import {
|
|
3
|
-
pageArrayFromDict,
|
|
4
|
-
appInfoFromDict,
|
|
5
|
-
} from '../utils';
|
|
6
|
-
import _ from 'lodash';
|
|
7
|
-
import {
|
|
8
|
-
setAppIdKey,
|
|
9
|
-
getAppDict,
|
|
10
|
-
getAppIdKey,
|
|
11
|
-
getBundleId,
|
|
12
|
-
getNavigatingToPage,
|
|
13
|
-
setCurrentState,
|
|
14
|
-
setConnectedDrivers,
|
|
15
|
-
getSkippedApps,
|
|
16
|
-
} from './property-accessors';
|
|
17
|
-
|
|
18
|
-
/*
|
|
19
|
-
* Generic callbacks used throughout the lifecycle of the Remote Debugger.
|
|
20
|
-
* These will be added to the prototype.
|
|
21
|
-
*/
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* @this {RemoteDebugger}
|
|
25
|
-
* @param {Error?} err
|
|
26
|
-
* @param {string} appIdKey
|
|
27
|
-
* @param {Record<string, any>} pageDict
|
|
28
|
-
* @returns {Promise<void>}
|
|
29
|
-
*/
|
|
30
|
-
export async function onPageChange (err, appIdKey, pageDict) {
|
|
31
|
-
if (_.isEmpty(pageDict)) {
|
|
32
|
-
return;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const currentPages = pageArrayFromDict(pageDict);
|
|
36
|
-
// save the page dict for this app
|
|
37
|
-
if (getAppDict(this)[appIdKey]) {
|
|
38
|
-
const previousPages = getAppDict(this)[appIdKey].pageArray;
|
|
39
|
-
// we have a pre-existing pageDict
|
|
40
|
-
if (previousPages && _.isEqual(previousPages, currentPages)) {
|
|
41
|
-
this.log.debug(
|
|
42
|
-
`Received page change notice for app '${appIdKey}' ` +
|
|
43
|
-
`but the listing has not changed. Ignoring.`
|
|
44
|
-
);
|
|
45
|
-
return;
|
|
46
|
-
}
|
|
47
|
-
// keep track of the page dictionary
|
|
48
|
-
getAppDict(this)[appIdKey].pageArray = currentPages;
|
|
49
|
-
this.log.debug(
|
|
50
|
-
`Pages changed for ${appIdKey}: ${JSON.stringify(previousPages)} -> ${JSON.stringify(currentPages)}`
|
|
51
|
-
);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
if (getNavigatingToPage(this)) {
|
|
55
|
-
// in the middle of navigating, so reporting a page change will cause problems
|
|
56
|
-
return;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
this.emit(events.EVENT_PAGE_CHANGE, {
|
|
60
|
-
appIdKey: appIdKey.replace('PID:', ''),
|
|
61
|
-
pageArray: currentPages,
|
|
62
|
-
});
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* @this {RemoteDebugger}
|
|
67
|
-
* @param {Error?} err
|
|
68
|
-
* @param {Record<string, any>} dict
|
|
69
|
-
* @returns {Promise<void>}
|
|
70
|
-
*/
|
|
71
|
-
export async function onAppConnect (err, dict) {
|
|
72
|
-
const appIdKey = dict.WIRApplicationIdentifierKey;
|
|
73
|
-
this.log.debug(`Notified that new application '${appIdKey}' has connected`);
|
|
74
|
-
updateAppsWithDict.bind(this)(dict);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* @this {RemoteDebugger}
|
|
79
|
-
* @param {Error?} err
|
|
80
|
-
* @param {import('@appium/types').StringRecord} dict
|
|
81
|
-
* @returns {void}
|
|
82
|
-
*/
|
|
83
|
-
export function onAppDisconnect (err, dict) {
|
|
84
|
-
const appIdKey = dict.WIRApplicationIdentifierKey;
|
|
85
|
-
this.log.debug(`Application '${appIdKey}' disconnected. Removing from app dictionary.`);
|
|
86
|
-
this.log.debug(`Current app is '${getAppIdKey(this)}'`);
|
|
87
|
-
|
|
88
|
-
// get rid of the entry in our app dictionary,
|
|
89
|
-
// since it is no longer available
|
|
90
|
-
delete getAppDict(this)[appIdKey];
|
|
91
|
-
|
|
92
|
-
// if the disconnected app is the one we are connected to, try to find another
|
|
93
|
-
if (getAppIdKey(this) === appIdKey) {
|
|
94
|
-
this.log.debug(`No longer have app id. Attempting to find new one.`);
|
|
95
|
-
setAppIdKey(this, getDebuggerAppKey.bind(this)(/** @type {string} */ (getBundleId(this))));
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
if (_.isEmpty(getAppDict(this))) {
|
|
99
|
-
// this means we no longer have any apps. what the what?
|
|
100
|
-
this.log.debug('Main app disconnected. Disconnecting altogether.');
|
|
101
|
-
this.emit(events.EVENT_DISCONNECT, true);
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* @this {RemoteDebugger}
|
|
107
|
-
* @param {Error?} err
|
|
108
|
-
* @param {Record<string, any>} dict
|
|
109
|
-
* @returns {Promise<void>}
|
|
110
|
-
*/
|
|
111
|
-
export async function onAppUpdate (err, dict) {
|
|
112
|
-
this.log.debug(`Notified that an application has been updated`);
|
|
113
|
-
updateAppsWithDict.bind(this)(dict);
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* @this {RemoteDebugger}
|
|
118
|
-
* @param {Error?} err
|
|
119
|
-
* @param {Record<string, any>} drivers
|
|
120
|
-
* @returns {void}
|
|
121
|
-
*/
|
|
122
|
-
export function onConnectedDriverList (err, drivers) {
|
|
123
|
-
setConnectedDrivers(this, drivers.WIRDriverDictionaryKey);
|
|
124
|
-
this.log.debug(`Received connected driver list: ${JSON.stringify(this.connectedDrivers)}`);
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* @this {RemoteDebugger}
|
|
129
|
-
* @param {Error?} err
|
|
130
|
-
* @param {Record<string, any>} state
|
|
131
|
-
* @returns {void}
|
|
132
|
-
*/
|
|
133
|
-
export function onCurrentState (err, state) {
|
|
134
|
-
setCurrentState(this, state.WIRAutomationAvailabilityKey);
|
|
135
|
-
// This state changes when 'Remote Automation' in 'Settings app' > 'Safari' > 'Advanced' > 'Remote Automation' changes
|
|
136
|
-
// WIRAutomationAvailabilityAvailable or WIRAutomationAvailabilityNotAvailable
|
|
137
|
-
this.log.debug(`Received connected automation availability state: ${JSON.stringify(this.currentState)}`);
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
/**
|
|
141
|
-
* @this {RemoteDebugger}
|
|
142
|
-
* @param {Error?} err
|
|
143
|
-
* @param {Record<string, any>} apps
|
|
144
|
-
* @returns {Promise<void>}
|
|
145
|
-
*/
|
|
146
|
-
export async function onConnectedApplicationList (err, apps) {
|
|
147
|
-
this.log.debug(`Received connected applications list: ${_.keys(apps).join(', ')}`);
|
|
148
|
-
|
|
149
|
-
// translate the received information into an easier-to-manage
|
|
150
|
-
// hash with app id as key, and app info as value
|
|
151
|
-
let newDict = {};
|
|
152
|
-
for (const dict of _.values(apps)) {
|
|
153
|
-
const [id, entry] = appInfoFromDict(dict);
|
|
154
|
-
if (getSkippedApps(this).includes(entry.name)) {
|
|
155
|
-
continue;
|
|
156
|
-
}
|
|
157
|
-
newDict[id] = entry;
|
|
158
|
-
}
|
|
159
|
-
// update the object's list of apps
|
|
160
|
-
_.defaults(getAppDict(this), newDict);
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
/**
|
|
164
|
-
*
|
|
165
|
-
* @this {RemoteDebugger}
|
|
166
|
-
* @param {import('@appium/types').StringRecord} dict
|
|
167
|
-
* @returns {void}
|
|
168
|
-
*/
|
|
169
|
-
function updateAppsWithDict (dict) {
|
|
170
|
-
// get the dictionary entry into a nice form, and add it to the
|
|
171
|
-
// application dictionary
|
|
172
|
-
const [id, entry] = appInfoFromDict(dict);
|
|
173
|
-
if (getAppDict(this)[id]?.pageArray) {
|
|
174
|
-
// preserve the page dictionary for this entry
|
|
175
|
-
entry.pageArray = getAppDict(this)[id].pageArray;
|
|
176
|
-
}
|
|
177
|
-
getAppDict(this)[id] = entry;
|
|
178
|
-
|
|
179
|
-
// try to get the app id from our connected apps
|
|
180
|
-
if (!getAppIdKey(this)) {
|
|
181
|
-
setAppIdKey(this, getDebuggerAppKey.bind(this)(/** @type {string} */ (getBundleId(this))));
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
/**
|
|
186
|
-
* Given a bundle id, finds the correct remote debugger app that is
|
|
187
|
-
* connected.
|
|
188
|
-
*
|
|
189
|
-
* @this {RemoteDebugger}
|
|
190
|
-
* @param {string} bundleId
|
|
191
|
-
* @returns {string|undefined}
|
|
192
|
-
*/
|
|
193
|
-
export function getDebuggerAppKey (bundleId) {
|
|
194
|
-
let appId;
|
|
195
|
-
for (const [key, data] of _.toPairs(getAppDict(this))) {
|
|
196
|
-
if (data.bundleId === bundleId) {
|
|
197
|
-
appId = key;
|
|
198
|
-
break;
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
// now we need to determine if we should pick a proxy for this instead
|
|
202
|
-
if (appId) {
|
|
203
|
-
this.log.debug(`Found app id key '${appId}' for bundle '${bundleId}'`);
|
|
204
|
-
let proxyAppId;
|
|
205
|
-
for (const [key, data] of _.toPairs(getAppDict(this))) {
|
|
206
|
-
if (data.isProxy && data.hostId === appId) {
|
|
207
|
-
this.log.debug(`Found separate bundleId '${data.bundleId}' ` +
|
|
208
|
-
`acting as proxy for '${bundleId}', with app id '${key}'`);
|
|
209
|
-
// set the app id... the last one will be used, so just keep re-assigning
|
|
210
|
-
proxyAppId = key;
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
if (proxyAppId) {
|
|
214
|
-
appId = proxyAppId;
|
|
215
|
-
this.log.debug(`Using proxied app id '${appId}'`);
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
return appId;
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
/**
|
|
223
|
-
* @typedef {import('../remote-debugger').RemoteDebugger} RemoteDebugger
|
|
224
|
-
*/
|