appium-remote-debugger 11.4.2 → 11.5.1
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.d.ts +30 -56
- package/build/lib/mixins/connect.d.ts.map +1 -1
- package/build/lib/mixins/connect.js +57 -90
- package/build/lib/mixins/connect.js.map +1 -1
- package/build/lib/mixins/cookies.d.ts +22 -0
- package/build/lib/mixins/cookies.d.ts.map +1 -0
- package/build/lib/mixins/cookies.js +48 -0
- package/build/lib/mixins/cookies.js.map +1 -0
- package/build/lib/mixins/events.d.ts +17 -2
- package/build/lib/mixins/events.d.ts.map +1 -1
- package/build/lib/mixins/events.js +29 -2
- package/build/lib/mixins/events.js.map +1 -1
- package/build/lib/mixins/execute.d.ts +5 -12
- package/build/lib/mixins/execute.d.ts.map +1 -1
- package/build/lib/mixins/execute.js +18 -30
- package/build/lib/mixins/execute.js.map +1 -1
- package/build/lib/mixins/message-handlers.d.ts +13 -23
- package/build/lib/mixins/message-handlers.d.ts.map +1 -1
- package/build/lib/mixins/message-handlers.js +56 -27
- package/build/lib/mixins/message-handlers.js.map +1 -1
- package/build/lib/mixins/misc.d.ts +51 -0
- package/build/lib/mixins/misc.d.ts.map +1 -0
- package/build/lib/mixins/misc.js +132 -0
- package/build/lib/mixins/misc.js.map +1 -0
- package/build/lib/mixins/navigate.d.ts +20 -29
- package/build/lib/mixins/navigate.d.ts.map +1 -1
- package/build/lib/mixins/navigate.js +25 -23
- package/build/lib/mixins/navigate.js.map +1 -1
- package/build/lib/mixins/screenshot.d.ts +13 -0
- package/build/lib/mixins/screenshot.d.ts.map +1 -0
- package/build/lib/mixins/screenshot.js +31 -0
- package/build/lib/mixins/screenshot.js.map +1 -0
- package/build/lib/remote-debugger-real-device.d.ts +16 -2
- package/build/lib/remote-debugger-real-device.d.ts.map +1 -1
- package/build/lib/remote-debugger-real-device.js +11 -1
- package/build/lib/remote-debugger-real-device.js.map +1 -1
- package/build/lib/remote-debugger.d.ts +153 -174
- package/build/lib/remote-debugger.d.ts.map +1 -1
- package/build/lib/remote-debugger.js +147 -213
- package/build/lib/remote-debugger.js.map +1 -1
- package/build/lib/rpc/rpc-client.d.ts +2 -1
- package/build/lib/rpc/rpc-client.d.ts.map +1 -1
- package/build/lib/rpc/rpc-client.js +2 -0
- package/build/lib/rpc/rpc-client.js.map +1 -1
- package/build/lib/utils.d.ts +20 -16
- package/build/lib/utils.d.ts.map +1 -1
- package/build/lib/utils.js +33 -25
- package/build/lib/utils.js.map +1 -1
- package/build/tsconfig.tsbuildinfo +1 -1
- package/lib/mixins/connect.js +61 -105
- package/lib/mixins/cookies.js +45 -0
- package/lib/mixins/events.js +25 -1
- package/lib/mixins/execute.js +18 -39
- package/lib/mixins/message-handlers.js +67 -36
- package/lib/mixins/misc.js +128 -0
- package/lib/mixins/navigate.js +26 -30
- package/lib/mixins/screenshot.js +34 -0
- package/lib/remote-debugger-real-device.js +14 -1
- package/lib/remote-debugger.js +133 -305
- package/lib/rpc/rpc-client.js +3 -1
- package/lib/utils.js +42 -38
- package/package.json +1 -1
- package/build/lib/mixins/index.d.ts +0 -5
- package/build/lib/mixins/index.d.ts.map +0 -1
- package/build/lib/mixins/index.js +0 -16
- package/build/lib/mixins/index.js.map +0 -1
- package/lib/mixins/index.js +0 -14
|
@@ -1,6 +1,11 @@
|
|
|
1
|
-
import log from '../logger';
|
|
2
1
|
import events from './events';
|
|
3
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
pageArrayFromDict,
|
|
4
|
+
getDebuggerAppKey,
|
|
5
|
+
simpleStringify,
|
|
6
|
+
appInfoFromDict,
|
|
7
|
+
deferredPromise,
|
|
8
|
+
} from '../utils';
|
|
4
9
|
import _ from 'lodash';
|
|
5
10
|
|
|
6
11
|
|
|
@@ -16,14 +21,14 @@ import _ from 'lodash';
|
|
|
16
21
|
* @param {Record<string, any>} pageDict
|
|
17
22
|
* @returns {Promise<void>}
|
|
18
23
|
*/
|
|
19
|
-
async function onPageChange (err, appIdKey, pageDict) {
|
|
24
|
+
export async function onPageChange (err, appIdKey, pageDict) {
|
|
20
25
|
if (_.isEmpty(pageDict)) {
|
|
21
26
|
return;
|
|
22
27
|
}
|
|
23
28
|
|
|
24
29
|
const pageArray = pageArrayFromDict(pageDict);
|
|
25
30
|
|
|
26
|
-
await
|
|
31
|
+
await useAppDictLock.bind(this)((/** @type {() => void} */ done) => {
|
|
27
32
|
try {
|
|
28
33
|
// save the page dict for this app
|
|
29
34
|
if (this.appDict[appIdKey]) {
|
|
@@ -34,7 +39,7 @@ async function onPageChange (err, appIdKey, pageDict) {
|
|
|
34
39
|
} else {
|
|
35
40
|
// we have a pre-existing pageDict
|
|
36
41
|
if (_.isEqual(this.appDict[appIdKey].pageArray, pageArray)) {
|
|
37
|
-
log.debug(`Received page change notice for app '${appIdKey}' ` +
|
|
42
|
+
this.log.debug(`Received page change notice for app '${appIdKey}' ` +
|
|
38
43
|
`but the listing has not changed. Ignoring.`);
|
|
39
44
|
return done();
|
|
40
45
|
}
|
|
@@ -53,7 +58,7 @@ async function onPageChange (err, appIdKey, pageDict) {
|
|
|
53
58
|
return;
|
|
54
59
|
}
|
|
55
60
|
|
|
56
|
-
log.debug(`Page changed: ${simpleStringify(pageDict, true)}`);
|
|
61
|
+
this.log.debug(`Page changed: ${simpleStringify(pageDict, true)}`);
|
|
57
62
|
|
|
58
63
|
this.emit(events.EVENT_PAGE_CHANGE, {
|
|
59
64
|
appIdKey: appIdKey.replace('PID:', ''),
|
|
@@ -67,12 +72,12 @@ async function onPageChange (err, appIdKey, pageDict) {
|
|
|
67
72
|
* @param {Record<string, any>} dict
|
|
68
73
|
* @returns {Promise<void>}
|
|
69
74
|
*/
|
|
70
|
-
async function onAppConnect (err, dict) {
|
|
75
|
+
export async function onAppConnect (err, dict) {
|
|
71
76
|
const appIdKey = dict.WIRApplicationIdentifierKey;
|
|
72
|
-
log.debug(`Notified that new application '${appIdKey}' has connected`);
|
|
73
|
-
await
|
|
77
|
+
this.log.debug(`Notified that new application '${appIdKey}' has connected`);
|
|
78
|
+
await useAppDictLock.bind(this)((/** @type {() => void} */ done) => {
|
|
74
79
|
try {
|
|
75
|
-
|
|
80
|
+
updateAppsWithDict.bind(this)(dict);
|
|
76
81
|
} finally {
|
|
77
82
|
done();
|
|
78
83
|
}
|
|
@@ -85,10 +90,10 @@ async function onAppConnect (err, dict) {
|
|
|
85
90
|
* @param {Record<string, any>} dict
|
|
86
91
|
* @returns {void}
|
|
87
92
|
*/
|
|
88
|
-
function onAppDisconnect (err, dict) {
|
|
93
|
+
export function onAppDisconnect (err, dict) {
|
|
89
94
|
const appIdKey = dict.WIRApplicationIdentifierKey;
|
|
90
|
-
log.debug(`Application '${appIdKey}' disconnected. Removing from app dictionary.`);
|
|
91
|
-
|
|
95
|
+
this.log.debug(`Application '${appIdKey}' disconnected. Removing from app dictionary.`);
|
|
96
|
+
this.log.debug(`Current app is '${this.appIdKey}'`);
|
|
92
97
|
|
|
93
98
|
// get rid of the entry in our app dictionary,
|
|
94
99
|
// since it is no longer available
|
|
@@ -96,13 +101,13 @@ function onAppDisconnect (err, dict) {
|
|
|
96
101
|
|
|
97
102
|
// if the disconnected app is the one we are connected to, try to find another
|
|
98
103
|
if (this.appIdKey === appIdKey) {
|
|
99
|
-
log.debug(`No longer have app id. Attempting to find new one.`);
|
|
104
|
+
this.log.debug(`No longer have app id. Attempting to find new one.`);
|
|
100
105
|
this.appIdKey = getDebuggerAppKey(/** @type {string} */ (this.bundleId), this.appDict);
|
|
101
106
|
}
|
|
102
107
|
|
|
103
108
|
if (!this.appDict) {
|
|
104
109
|
// this means we no longer have any apps. what the what?
|
|
105
|
-
log.debug('Main app disconnected. Disconnecting altogether.');
|
|
110
|
+
this.log.debug('Main app disconnected. Disconnecting altogether.');
|
|
106
111
|
this.connected = false;
|
|
107
112
|
this.emit(events.EVENT_DISCONNECT, true);
|
|
108
113
|
}
|
|
@@ -114,10 +119,10 @@ function onAppDisconnect (err, dict) {
|
|
|
114
119
|
* @param {Record<string, any>} dict
|
|
115
120
|
* @returns {Promise<void>}
|
|
116
121
|
*/
|
|
117
|
-
async function onAppUpdate (err, dict) {
|
|
118
|
-
await
|
|
122
|
+
export async function onAppUpdate (err, dict) {
|
|
123
|
+
await useAppDictLock.bind(this)((/** @type {() => void} */ done) => {
|
|
119
124
|
try {
|
|
120
|
-
|
|
125
|
+
updateAppsWithDict.bind(this)(dict);
|
|
121
126
|
} finally {
|
|
122
127
|
done();
|
|
123
128
|
}
|
|
@@ -130,9 +135,9 @@ async function onAppUpdate (err, dict) {
|
|
|
130
135
|
* @param {Record<string, any>} drivers
|
|
131
136
|
* @returns {void}
|
|
132
137
|
*/
|
|
133
|
-
function onConnectedDriverList (err, drivers) {
|
|
138
|
+
export function onConnectedDriverList (err, drivers) {
|
|
134
139
|
this.connectedDrivers = drivers.WIRDriverDictionaryKey;
|
|
135
|
-
log.debug(`Received connected driver list: ${JSON.stringify(this.connectedDrivers)}`);
|
|
140
|
+
this.log.debug(`Received connected driver list: ${JSON.stringify(this.connectedDrivers)}`);
|
|
136
141
|
}
|
|
137
142
|
|
|
138
143
|
/**
|
|
@@ -141,11 +146,11 @@ function onConnectedDriverList (err, drivers) {
|
|
|
141
146
|
* @param {Record<string, any>} state
|
|
142
147
|
* @returns {void}
|
|
143
148
|
*/
|
|
144
|
-
function onCurrentState (err, state) {
|
|
149
|
+
export function onCurrentState (err, state) {
|
|
145
150
|
this.currentState = state.WIRAutomationAvailabilityKey;
|
|
146
151
|
// This state changes when 'Remote Automation' in 'Settings app' > 'Safari' > 'Advanced' > 'Remote Automation' changes
|
|
147
152
|
// WIRAutomationAvailabilityAvailable or WIRAutomationAvailabilityNotAvailable
|
|
148
|
-
log.debug(`Received connected automation availability state: ${JSON.stringify(this.currentState)}`);
|
|
153
|
+
this.log.debug(`Received connected automation availability state: ${JSON.stringify(this.currentState)}`);
|
|
149
154
|
}
|
|
150
155
|
|
|
151
156
|
/**
|
|
@@ -154,8 +159,8 @@ function onCurrentState (err, state) {
|
|
|
154
159
|
* @param {Record<string, any>} apps
|
|
155
160
|
* @returns {Promise<void>}
|
|
156
161
|
*/
|
|
157
|
-
async function onConnectedApplicationList (err, apps) {
|
|
158
|
-
log.debug(`Received connected applications list: ${_.keys(apps).join(', ')}`);
|
|
162
|
+
export async function onConnectedApplicationList (err, apps) {
|
|
163
|
+
this.log.debug(`Received connected applications list: ${_.keys(apps).join(', ')}`);
|
|
159
164
|
|
|
160
165
|
// translate the received information into an easier-to-manage
|
|
161
166
|
// hash with app id as key, and app info as value
|
|
@@ -168,7 +173,7 @@ async function onConnectedApplicationList (err, apps) {
|
|
|
168
173
|
newDict[id] = entry;
|
|
169
174
|
}
|
|
170
175
|
// update the object's list of apps
|
|
171
|
-
await
|
|
176
|
+
await useAppDictLock.bind(this)((/** @type {() => void} */ done) => {
|
|
172
177
|
try {
|
|
173
178
|
_.defaults(this.appDict, newDict);
|
|
174
179
|
} finally {
|
|
@@ -177,14 +182,40 @@ async function onConnectedApplicationList (err, apps) {
|
|
|
177
182
|
});
|
|
178
183
|
}
|
|
179
184
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
185
|
+
/**
|
|
186
|
+
* @this {import('../remote-debugger').RemoteDebugger}
|
|
187
|
+
* @param {(done: () => any) => any} fn
|
|
188
|
+
* @returns {Promise<any>}
|
|
189
|
+
*/
|
|
190
|
+
async function useAppDictLock (fn) {
|
|
191
|
+
return await this._lock.acquire('appDict', fn);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
*
|
|
197
|
+
* @this {import('../remote-debugger').RemoteDebugger}
|
|
198
|
+
* @param {import('@appium/types').StringRecord} dict
|
|
199
|
+
* @returns {void}
|
|
200
|
+
*/
|
|
201
|
+
function updateAppsWithDict (dict) {
|
|
202
|
+
// get the dictionary entry into a nice form, and add it to the
|
|
203
|
+
// application dictionary
|
|
204
|
+
this.appDict = this.appDict || {};
|
|
205
|
+
let [id, entry] = appInfoFromDict(dict);
|
|
206
|
+
if (this.appDict[id]) {
|
|
207
|
+
// preserve the page dictionary for this entry
|
|
208
|
+
entry.pageArray = this.appDict[id].pageArray;
|
|
209
|
+
}
|
|
210
|
+
this.appDict[id] = entry;
|
|
211
|
+
|
|
212
|
+
// add a promise to get the page dictionary
|
|
213
|
+
if (_.isUndefined(entry.pageArray)) {
|
|
214
|
+
entry.pageArray = deferredPromise();
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// try to get the app id from our connected apps
|
|
218
|
+
if (!this.appIdKey) {
|
|
219
|
+
this.appIdKey = getDebuggerAppKey(/** @type {string} */ (this.bundleId), this.appDict);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { checkParams } from '../utils';
|
|
2
|
+
import B from 'bluebird';
|
|
3
|
+
|
|
4
|
+
const SAFARI_BUNDLE_ID = 'com.apple.mobilesafari';
|
|
5
|
+
const GARBAGE_COLLECT_TIMEOUT_MS = 5000;
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @this {import('../remote-debugger').RemoteDebugger}
|
|
9
|
+
* @returns {Promise<void>}
|
|
10
|
+
*/
|
|
11
|
+
export async function launchSafari () {
|
|
12
|
+
await this.requireRpcClient().send('launchApplication', {
|
|
13
|
+
bundleId: SAFARI_BUNDLE_ID
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* @this {import('../remote-debugger').RemoteDebugger}
|
|
19
|
+
* @param {(event: import('@appium/types').StringRecord) => any} fn
|
|
20
|
+
* @returns {Promise<any>}
|
|
21
|
+
*/
|
|
22
|
+
export async function startTimeline (fn) {
|
|
23
|
+
this.log.debug('Starting to record the timeline');
|
|
24
|
+
this.requireRpcClient().on('Timeline.eventRecorded', fn);
|
|
25
|
+
return await this.requireRpcClient().send('Timeline.start', {
|
|
26
|
+
appIdKey: this.appIdKey,
|
|
27
|
+
pageIdKey: this.pageIdKey,
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* @this {import('../remote-debugger').RemoteDebugger}
|
|
33
|
+
* @returns {Promise<any>}
|
|
34
|
+
*/
|
|
35
|
+
export async function stopTimeline () {
|
|
36
|
+
this.log.debug('Stopping to record the timeline');
|
|
37
|
+
await this.requireRpcClient().send('Timeline.stop', {
|
|
38
|
+
appIdKey: this.appIdKey,
|
|
39
|
+
pageIdKey: this.pageIdKey,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* @this {import('../remote-debugger').RemoteDebugger}
|
|
45
|
+
* @param {(event: import('@appium/types').StringRecord) => any} listener
|
|
46
|
+
* @returns {void}
|
|
47
|
+
*/
|
|
48
|
+
export function startConsole (listener) {
|
|
49
|
+
this.log.debug('Starting to listen for JavaScript console');
|
|
50
|
+
this.addClientEventListener('Console.messageAdded', listener);
|
|
51
|
+
this.addClientEventListener('Console.messageRepeatCountUpdated', listener);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* @this {import('../remote-debugger').RemoteDebugger}
|
|
56
|
+
* @returns {void}
|
|
57
|
+
*/
|
|
58
|
+
export function stopConsole () {
|
|
59
|
+
this.log.debug('Stopping to listen for JavaScript console');
|
|
60
|
+
this.removeClientEventListener('Console.messageAdded');
|
|
61
|
+
this.removeClientEventListener('Console.messageRepeatCountUpdated');
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* @this {import('../remote-debugger').RemoteDebugger}
|
|
66
|
+
* @param {(event: import('@appium/types').StringRecord) => any} listener
|
|
67
|
+
* @returns {void}
|
|
68
|
+
*/
|
|
69
|
+
export function startNetwork (listener) {
|
|
70
|
+
this.log.debug('Starting to listen for network events');
|
|
71
|
+
this.addClientEventListener('NetworkEvent', listener);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* @this {import('../remote-debugger').RemoteDebugger}
|
|
76
|
+
* @returns {void}
|
|
77
|
+
*/
|
|
78
|
+
export function stopNetwork () {
|
|
79
|
+
this.log.debug('Stopping to listen for network events');
|
|
80
|
+
this.removeClientEventListener('NetworkEvent');
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Potentially this does not work for mobile safari
|
|
84
|
+
/**
|
|
85
|
+
* @this {import('../remote-debugger').RemoteDebugger}
|
|
86
|
+
* @param {string} value
|
|
87
|
+
* @returns {Promise<any>}
|
|
88
|
+
*/
|
|
89
|
+
export async function overrideUserAgent (value) {
|
|
90
|
+
this.log.debug('Setting overrideUserAgent');
|
|
91
|
+
return await this.requireRpcClient().send('Page.overrideUserAgent', {
|
|
92
|
+
appIdKey: this.appIdKey,
|
|
93
|
+
pageIdKey: this.pageIdKey,
|
|
94
|
+
value
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* @this {import('../remote-debugger').RemoteDebugger}
|
|
100
|
+
* @param {number} [timeoutMs=GARBAGE_COLLECT_TIMEOUT_MS]
|
|
101
|
+
* @returns {Promise<void>}
|
|
102
|
+
*/
|
|
103
|
+
export async function garbageCollect (timeoutMs = GARBAGE_COLLECT_TIMEOUT_MS) {
|
|
104
|
+
this.log.debug(`Garbage collecting with ${timeoutMs}ms timeout`);
|
|
105
|
+
|
|
106
|
+
try {
|
|
107
|
+
checkParams({appIdKey: this.appIdKey, pageIdKey: this.pageIdKey});
|
|
108
|
+
} catch (err) {
|
|
109
|
+
this.log.debug(`Unable to collect garbage at this time`);
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
try {
|
|
114
|
+
await B.resolve(this.requireRpcClient().send(
|
|
115
|
+
'Heap.gc', {
|
|
116
|
+
appIdKey: this.appIdKey,
|
|
117
|
+
pageIdKey: this.pageIdKey,
|
|
118
|
+
})
|
|
119
|
+
).timeout(timeoutMs);
|
|
120
|
+
this.log.debug(`Garbage collection successful`);
|
|
121
|
+
} catch (e) {
|
|
122
|
+
if (e instanceof B.TimeoutError) {
|
|
123
|
+
this.log.debug(`Garbage collection timed out after ${timeoutMs}ms`);
|
|
124
|
+
} else {
|
|
125
|
+
this.log.debug(`Unable to collect garbage: ${e.message}`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
package/lib/mixins/navigate.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import log from '../logger';
|
|
2
1
|
import { checkParams } from '../utils';
|
|
3
2
|
import events from './events';
|
|
4
3
|
import { timing, util } from '@appium/support';
|
|
@@ -25,7 +24,7 @@ const PAGE_LOAD_STRATEGY = {
|
|
|
25
24
|
* @this {import('../remote-debugger').RemoteDebugger}
|
|
26
25
|
* @returns {void}
|
|
27
26
|
*/
|
|
28
|
-
function frameDetached () {
|
|
27
|
+
export function frameDetached () {
|
|
29
28
|
this.emit(events.EVENT_FRAMES_DETACHED);
|
|
30
29
|
}
|
|
31
30
|
|
|
@@ -33,8 +32,8 @@ function frameDetached () {
|
|
|
33
32
|
* @this {import('../remote-debugger').RemoteDebugger}
|
|
34
33
|
* @returns {void}
|
|
35
34
|
*/
|
|
36
|
-
function cancelPageLoad () {
|
|
37
|
-
log.debug('Unregistering from page readiness notifications');
|
|
35
|
+
export function cancelPageLoad () {
|
|
36
|
+
this.log.debug('Unregistering from page readiness notifications');
|
|
38
37
|
this.pageLoading = false;
|
|
39
38
|
if (this.pageLoadDelay) {
|
|
40
39
|
this.pageLoadDelay.cancel();
|
|
@@ -48,7 +47,7 @@ function cancelPageLoad () {
|
|
|
48
47
|
* @param {string} readyState
|
|
49
48
|
* @returns {boolean}
|
|
50
49
|
*/
|
|
51
|
-
function isPageLoadingCompleted (readyState) {
|
|
50
|
+
export function isPageLoadingCompleted (readyState) {
|
|
52
51
|
const _pageLoadStrategy = _.toLower(this.pageLoadStrategy);
|
|
53
52
|
if (_pageLoadStrategy === PAGE_LOAD_STRATEGY.NONE) {
|
|
54
53
|
return true;
|
|
@@ -65,14 +64,14 @@ function isPageLoadingCompleted (readyState) {
|
|
|
65
64
|
|
|
66
65
|
/**
|
|
67
66
|
* @this {import('../remote-debugger').RemoteDebugger}
|
|
68
|
-
* @param {timing.Timer
|
|
67
|
+
* @param {timing.Timer?} [startPageLoadTimer]
|
|
69
68
|
* @returns {Promise<void>}
|
|
70
69
|
*/
|
|
71
|
-
async function waitForDom (startPageLoadTimer) {
|
|
72
|
-
log.debug('Waiting for page readiness');
|
|
70
|
+
export async function waitForDom (startPageLoadTimer) {
|
|
71
|
+
this.log.debug('Waiting for page readiness');
|
|
73
72
|
const readinessTimeoutMs = this.pageLoadMs || DEFAULT_PAGE_READINESS_TIMEOUT_MS;
|
|
74
73
|
if (!_.isFunction(startPageLoadTimer?.getDuration)) {
|
|
75
|
-
log.debug(`Page load timer not a timer. Creating new timer`);
|
|
74
|
+
this.log.debug(`Page load timer not a timer. Creating new timer`);
|
|
76
75
|
startPageLoadTimer = new timing.Timer().start();
|
|
77
76
|
}
|
|
78
77
|
|
|
@@ -94,7 +93,7 @@ async function waitForDom (startPageLoadTimer) {
|
|
|
94
93
|
await B.delay(intervalMs);
|
|
95
94
|
// we can get this called in the middle of trying to find a new app
|
|
96
95
|
if (!this.appIdKey) {
|
|
97
|
-
log.debug('Not connected to an application. Ignoring page readiess check');
|
|
96
|
+
this.log.debug('Not connected to an application. Ignoring page readiess check');
|
|
98
97
|
return;
|
|
99
98
|
}
|
|
100
99
|
if (!isPageLoading) {
|
|
@@ -103,13 +102,13 @@ async function waitForDom (startPageLoadTimer) {
|
|
|
103
102
|
|
|
104
103
|
if (await this.checkPageIsReady()) {
|
|
105
104
|
if (isPageLoading) {
|
|
106
|
-
log.debug(`Page is ready in ${elapsedMs}ms`);
|
|
105
|
+
this.log.debug(`Page is ready in ${elapsedMs}ms`);
|
|
107
106
|
isPageLoading = false;
|
|
108
107
|
}
|
|
109
108
|
return;
|
|
110
109
|
}
|
|
111
110
|
if (elapsedMs > readinessTimeoutMs) {
|
|
112
|
-
log.info(`Timed out after ${readinessTimeoutMs}ms of waiting for the page readiness. Continuing anyway`);
|
|
111
|
+
this.log.info(`Timed out after ${readinessTimeoutMs}ms of waiting for the page readiness. Continuing anyway`);
|
|
113
112
|
isPageLoading = false;
|
|
114
113
|
return;
|
|
115
114
|
}
|
|
@@ -137,21 +136,21 @@ async function waitForDom (startPageLoadTimer) {
|
|
|
137
136
|
* @param {number} [timeoutMs]
|
|
138
137
|
* @returns {Promise<boolean>}
|
|
139
138
|
*/
|
|
140
|
-
async function checkPageIsReady (timeoutMs) {
|
|
139
|
+
export async function checkPageIsReady (timeoutMs) {
|
|
141
140
|
checkParams({appIdKey: this.appIdKey});
|
|
142
141
|
|
|
143
142
|
const readyCmd = 'document.readyState;';
|
|
144
143
|
try {
|
|
145
144
|
const readyState = await B.resolve(this.execute(readyCmd, true))
|
|
146
145
|
.timeout(timeoutMs ?? this.pageReadyTimeout);
|
|
147
|
-
log.debug(`Document readyState is '${readyState}'. ` +
|
|
146
|
+
this.log.debug(`Document readyState is '${readyState}'. ` +
|
|
148
147
|
`The pageLoadStrategy is '${this.pageLoadStrategy || PAGE_LOAD_STRATEGY.NORMAL}'`);
|
|
149
148
|
return this.isPageLoadingCompleted(readyState);
|
|
150
149
|
} catch (err) {
|
|
151
150
|
if (!(err instanceof B.TimeoutError)) {
|
|
152
151
|
throw err;
|
|
153
152
|
}
|
|
154
|
-
log.debug(`Page readiness check timed out after ${this.pageReadyTimeout}ms`);
|
|
153
|
+
this.log.debug(`Page readiness check timed out after ${this.pageReadyTimeout}ms`);
|
|
155
154
|
return false;
|
|
156
155
|
}
|
|
157
156
|
}
|
|
@@ -161,11 +160,10 @@ async function checkPageIsReady (timeoutMs) {
|
|
|
161
160
|
* @param {string} url
|
|
162
161
|
* @returns {Promise<void>}
|
|
163
162
|
*/
|
|
164
|
-
async function navToUrl (url) {
|
|
163
|
+
export async function navToUrl (url) {
|
|
165
164
|
checkParams({appIdKey: this.appIdKey, pageIdKey: this.pageIdKey});
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
}
|
|
165
|
+
const rpcClient = this.requireRpcClient();
|
|
166
|
+
|
|
169
167
|
try {
|
|
170
168
|
new URL(url);
|
|
171
169
|
} catch (e) {
|
|
@@ -173,7 +171,7 @@ async function navToUrl (url) {
|
|
|
173
171
|
}
|
|
174
172
|
|
|
175
173
|
this._navigatingToPage = true;
|
|
176
|
-
log.debug(`Navigating to new URL: '${url}'`);
|
|
174
|
+
this.log.debug(`Navigating to new URL: '${url}'`);
|
|
177
175
|
const readinessTimeoutMs = this.pageLoadMs || DEFAULT_PAGE_READINESS_TIMEOUT_MS;
|
|
178
176
|
/** @type {(() => void)|undefined} */
|
|
179
177
|
let onPageLoaded;
|
|
@@ -192,7 +190,7 @@ async function navToUrl (url) {
|
|
|
192
190
|
onPageLoadedTimeout = setTimeout(() => {
|
|
193
191
|
if (isPageLoading) {
|
|
194
192
|
isPageLoading = false;
|
|
195
|
-
log.info(
|
|
193
|
+
this.log.info(
|
|
196
194
|
`Timed out after ${start.getDuration().asMilliSeconds.toFixed(0)}ms of waiting ` +
|
|
197
195
|
`for the ${url} page readiness. Continuing anyway`
|
|
198
196
|
);
|
|
@@ -203,7 +201,7 @@ async function navToUrl (url) {
|
|
|
203
201
|
onPageLoaded = () => {
|
|
204
202
|
if (isPageLoading) {
|
|
205
203
|
isPageLoading = false;
|
|
206
|
-
log.debug(`The page ${url} is ready in ${start.getDuration().asMilliSeconds.toFixed(0)}ms`);
|
|
204
|
+
this.log.debug(`The page ${url} is ready in ${start.getDuration().asMilliSeconds.toFixed(0)}ms`);
|
|
207
205
|
}
|
|
208
206
|
if (onPageLoadedTimeout) {
|
|
209
207
|
clearTimeout(onPageLoadedTimeout);
|
|
@@ -213,7 +211,7 @@ async function navToUrl (url) {
|
|
|
213
211
|
return resolve();
|
|
214
212
|
};
|
|
215
213
|
// https://chromedevtools.github.io/devtools-protocol/tot/Page/#event-loadEventFired
|
|
216
|
-
|
|
214
|
+
rpcClient.once('Page.loadEventFired', onPageLoaded);
|
|
217
215
|
// Pages that have no proper DOM structure do not fire the `Page.loadEventFired` event
|
|
218
216
|
// so we rely on the very first event after target change, which is `onTargetProvisioned`
|
|
219
217
|
// and start sending `document.readyState` requests until we either succeed or
|
|
@@ -233,9 +231,9 @@ async function navToUrl (url) {
|
|
|
233
231
|
}
|
|
234
232
|
}
|
|
235
233
|
};
|
|
236
|
-
|
|
234
|
+
rpcClient.targetSubscriptions.once(rpcConstants.ON_TARGET_PROVISIONED_EVENT, onTargetProvisioned);
|
|
237
235
|
|
|
238
|
-
|
|
236
|
+
rpcClient.send('Page.navigate', {
|
|
239
237
|
url,
|
|
240
238
|
appIdKey: this.appIdKey,
|
|
241
239
|
pageIdKey: this.pageIdKey,
|
|
@@ -260,17 +258,17 @@ async function navToUrl (url) {
|
|
|
260
258
|
onPageLoadedTimeout = null;
|
|
261
259
|
}
|
|
262
260
|
if (onTargetProvisioned) {
|
|
263
|
-
|
|
261
|
+
rpcClient.targetSubscriptions.off(rpcConstants.ON_TARGET_PROVISIONED_EVENT, onTargetProvisioned);
|
|
264
262
|
}
|
|
265
263
|
if (onPageLoaded) {
|
|
266
|
-
|
|
264
|
+
rpcClient.off('Page.loadEventFired', onPageLoaded);
|
|
267
265
|
}
|
|
268
266
|
}
|
|
269
267
|
|
|
270
268
|
// enable console logging, so we get the events (otherwise we only
|
|
271
269
|
// get notified when navigating to a local page
|
|
272
270
|
try {
|
|
273
|
-
await B.resolve(
|
|
271
|
+
await B.resolve(rpcClient.send('Console.enable', {
|
|
274
272
|
appIdKey: this.appIdKey,
|
|
275
273
|
pageIdKey: this.pageIdKey,
|
|
276
274
|
}, didPageFinishLoad)).timeout(CONSOLE_ENABLEMENT_TIMEOUT_MS);
|
|
@@ -282,5 +280,3 @@ async function navToUrl (url) {
|
|
|
282
280
|
throw err;
|
|
283
281
|
}
|
|
284
282
|
}
|
|
285
|
-
|
|
286
|
-
export default {frameDetached, cancelPageLoad, waitForDom, checkPageIsReady, navToUrl, isPageLoadingCompleted};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Capture a rect of the page or by default the viewport
|
|
3
|
+
* @this {import('../remote-debugger').RemoteDebugger}
|
|
4
|
+
* @param {ScreenshotCaptureOptions} [opts={}] if rect is null capture the whole
|
|
5
|
+
* coordinate system else capture the rect in the given coordinateSystem
|
|
6
|
+
* @returns {Promise<string>} a base64 encoded string of the screenshot
|
|
7
|
+
*/
|
|
8
|
+
export async function captureScreenshot(opts = {}) {
|
|
9
|
+
const {rect = null, coordinateSystem = 'Viewport'} = opts;
|
|
10
|
+
this.log.debug('Capturing screenshot');
|
|
11
|
+
|
|
12
|
+
const arect = rect ?? /** @type {import('@appium/types').Rect} */ (await this.executeAtom(
|
|
13
|
+
'execute_script',
|
|
14
|
+
['return {x: 0, y: 0, width: window.innerWidth, height: window.innerHeight}', []]
|
|
15
|
+
));
|
|
16
|
+
const response = await this.requireRpcClient().send('Page.snapshotRect', {
|
|
17
|
+
...arect,
|
|
18
|
+
appIdKey: this.appIdKey,
|
|
19
|
+
pageIdKey: this.pageIdKey,
|
|
20
|
+
coordinateSystem,
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
if (response.error) {
|
|
24
|
+
throw new Error(response.error);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return response.dataURL.replace(/^data:image\/png;base64,/, '');
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* @typedef {Object} ScreenshotCaptureOptions
|
|
32
|
+
* @property {import('@appium/types').Rect | null} [rect=null]
|
|
33
|
+
* @property {"Viewport" | "Page"} [coordinateSystem="Viewport"]
|
|
34
|
+
*/
|
|
@@ -1,9 +1,19 @@
|
|
|
1
1
|
import RemoteDebugger from './remote-debugger';
|
|
2
2
|
import { RpcClientRealDevice } from './rpc';
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* @typedef {Object} RemoteDebuggerRealDeviceOptions
|
|
6
|
+
* @property {string} udid Real device UDID
|
|
7
|
+
*/
|
|
4
8
|
|
|
5
9
|
export default class RemoteDebuggerRealDevice extends RemoteDebugger {
|
|
6
|
-
|
|
10
|
+
/** @type {string} */
|
|
11
|
+
udid;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @param {RemoteDebuggerRealDeviceOptions & import('./remote-debugger').RemoteDebuggerOptions} opts
|
|
15
|
+
*/
|
|
16
|
+
constructor (opts) {
|
|
7
17
|
super(opts);
|
|
8
18
|
|
|
9
19
|
this.udid = opts.udid;
|
|
@@ -11,6 +21,9 @@ export default class RemoteDebuggerRealDevice extends RemoteDebugger {
|
|
|
11
21
|
this._skippedApps = ['lockdownd'];
|
|
12
22
|
}
|
|
13
23
|
|
|
24
|
+
/**
|
|
25
|
+
* @override
|
|
26
|
+
*/
|
|
14
27
|
initRpcClient () {
|
|
15
28
|
this.rpcClient = new RpcClientRealDevice({
|
|
16
29
|
bundleId: this.bundleId,
|