appium-remote-debugger 11.4.1 → 11.5.0

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.
Files changed (68) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/build/lib/mixins/connect.d.ts +53 -56
  3. package/build/lib/mixins/connect.d.ts.map +1 -1
  4. package/build/lib/mixins/connect.js +72 -96
  5. package/build/lib/mixins/connect.js.map +1 -1
  6. package/build/lib/mixins/cookies.d.ts +22 -0
  7. package/build/lib/mixins/cookies.d.ts.map +1 -0
  8. package/build/lib/mixins/cookies.js +48 -0
  9. package/build/lib/mixins/cookies.js.map +1 -0
  10. package/build/lib/mixins/events.d.ts +17 -2
  11. package/build/lib/mixins/events.d.ts.map +1 -1
  12. package/build/lib/mixins/events.js +29 -2
  13. package/build/lib/mixins/events.js.map +1 -1
  14. package/build/lib/mixins/execute.d.ts +5 -12
  15. package/build/lib/mixins/execute.d.ts.map +1 -1
  16. package/build/lib/mixins/execute.js +18 -30
  17. package/build/lib/mixins/execute.js.map +1 -1
  18. package/build/lib/mixins/message-handlers.d.ts +13 -23
  19. package/build/lib/mixins/message-handlers.d.ts.map +1 -1
  20. package/build/lib/mixins/message-handlers.js +56 -27
  21. package/build/lib/mixins/message-handlers.js.map +1 -1
  22. package/build/lib/mixins/misc.d.ts +51 -0
  23. package/build/lib/mixins/misc.d.ts.map +1 -0
  24. package/build/lib/mixins/misc.js +132 -0
  25. package/build/lib/mixins/misc.js.map +1 -0
  26. package/build/lib/mixins/navigate.d.ts +20 -29
  27. package/build/lib/mixins/navigate.d.ts.map +1 -1
  28. package/build/lib/mixins/navigate.js +25 -23
  29. package/build/lib/mixins/navigate.js.map +1 -1
  30. package/build/lib/mixins/screenshot.d.ts +13 -0
  31. package/build/lib/mixins/screenshot.d.ts.map +1 -0
  32. package/build/lib/mixins/screenshot.js +31 -0
  33. package/build/lib/mixins/screenshot.js.map +1 -0
  34. package/build/lib/remote-debugger-real-device.d.ts +16 -2
  35. package/build/lib/remote-debugger-real-device.d.ts.map +1 -1
  36. package/build/lib/remote-debugger-real-device.js +11 -1
  37. package/build/lib/remote-debugger-real-device.js.map +1 -1
  38. package/build/lib/remote-debugger.d.ts +155 -174
  39. package/build/lib/remote-debugger.d.ts.map +1 -1
  40. package/build/lib/remote-debugger.js +149 -210
  41. package/build/lib/remote-debugger.js.map +1 -1
  42. package/build/lib/rpc/rpc-client.d.ts +14 -13
  43. package/build/lib/rpc/rpc-client.d.ts.map +1 -1
  44. package/build/lib/rpc/rpc-client.js +8 -6
  45. package/build/lib/rpc/rpc-client.js.map +1 -1
  46. package/build/lib/utils.d.ts +20 -16
  47. package/build/lib/utils.d.ts.map +1 -1
  48. package/build/lib/utils.js +33 -25
  49. package/build/lib/utils.js.map +1 -1
  50. package/build/tsconfig.tsbuildinfo +1 -1
  51. package/lib/mixins/connect.js +77 -111
  52. package/lib/mixins/cookies.js +45 -0
  53. package/lib/mixins/events.js +25 -1
  54. package/lib/mixins/execute.js +18 -39
  55. package/lib/mixins/message-handlers.js +67 -36
  56. package/lib/mixins/misc.js +128 -0
  57. package/lib/mixins/navigate.js +26 -30
  58. package/lib/mixins/screenshot.js +34 -0
  59. package/lib/remote-debugger-real-device.js +14 -1
  60. package/lib/remote-debugger.js +138 -302
  61. package/lib/rpc/rpc-client.js +9 -7
  62. package/lib/utils.js +42 -38
  63. package/package.json +1 -1
  64. package/build/lib/mixins/index.d.ts +0 -5
  65. package/build/lib/mixins/index.d.ts.map +0 -1
  66. package/build/lib/mixins/index.js +0 -16
  67. package/build/lib/mixins/index.js.map +0 -1
  68. package/lib/mixins/index.js +0 -14
@@ -1,7 +1,7 @@
1
- import log from '../logger';
2
1
  import {
3
- appInfoFromDict, pageArrayFromDict, getDebuggerAppKey,
4
- getPossibleDebuggerAppKeys, simpleStringify, deferredPromise
2
+ pageArrayFromDict,
3
+ getPossibleDebuggerAppKeys,
4
+ simpleStringify,
5
5
  } from '../utils';
6
6
  import events from './events';
7
7
  import { timing } from '@appium/support';
@@ -32,62 +32,60 @@ const BLANK_PAGE_URL = 'about:blank';
32
32
  /**
33
33
  *
34
34
  * @this {import('../remote-debugger').RemoteDebugger}
35
+ * @returns {Promise<void>}
35
36
  */
36
- async function setConnectionKey () {
37
- log.debug('Sending connection key request');
38
- if (!this.rpcClient) {
39
- throw new Error('rpcClient is undefined. Is the debugger connected?');
40
- }
37
+ export async function setConnectionKey () {
38
+ this.log.debug('Sending connection key request');
41
39
 
42
40
  // send but only wait to make sure the socket worked
43
41
  // as response from Web Inspector can take a long time
44
- await this.rpcClient.send('setConnectionKey', {}, false);
42
+ await this.requireRpcClient().send('setConnectionKey', {}, false);
45
43
  }
46
44
 
47
45
  /**
48
46
  *
49
47
  * @this {import('../remote-debugger').RemoteDebugger}
48
+ * @param {number} [timeout=APP_CONNECT_TIMEOUT_MS]
49
+ * @returns {Promise<import('@appium/types').StringRecord>}
50
50
  */
51
- async function connect (timeout = APP_CONNECT_TIMEOUT_MS) {
51
+ export async function connect (timeout = APP_CONNECT_TIMEOUT_MS) {
52
52
  this.setup();
53
53
 
54
54
  // initialize the rpc client
55
55
  this.initRpcClient();
56
56
 
57
- if (!this.rpcClient) {
58
- throw new Error('rpcClient is undefined. Is the debugger connected?');
59
- }
57
+ const rpcClient = this.requireRpcClient();
60
58
 
61
59
  // listen for basic debugger-level events
62
- this.rpcClient.on('_rpc_reportSetup:', _.noop);
63
- this.rpcClient.on('_rpc_forwardGetListing:', this.onPageChange.bind(this));
64
- this.rpcClient.on('_rpc_reportConnectedApplicationList:', this.onConnectedApplicationList.bind(this));
65
- this.rpcClient.on('_rpc_applicationConnected:', this.onAppConnect.bind(this));
66
- this.rpcClient.on('_rpc_applicationDisconnected:', this.onAppDisconnect.bind(this));
67
- this.rpcClient.on('_rpc_applicationUpdated:', this.onAppUpdate.bind(this));
68
- this.rpcClient.on('_rpc_reportConnectedDriverList:', this.onConnectedDriverList.bind(this));
69
- this.rpcClient.on('_rpc_reportCurrentState:', this.onCurrentState.bind(this));
70
- this.rpcClient.on('Page.frameDetached', this.frameDetached.bind(this));
71
-
72
- await this.rpcClient.connect();
60
+ rpcClient.on('_rpc_reportSetup:', _.noop);
61
+ rpcClient.on('_rpc_forwardGetListing:', this.onPageChange.bind(this));
62
+ rpcClient.on('_rpc_reportConnectedApplicationList:', this.onConnectedApplicationList.bind(this));
63
+ rpcClient.on('_rpc_applicationConnected:', this.onAppConnect.bind(this));
64
+ rpcClient.on('_rpc_applicationDisconnected:', this.onAppDisconnect.bind(this));
65
+ rpcClient.on('_rpc_applicationUpdated:', this.onAppUpdate.bind(this));
66
+ rpcClient.on('_rpc_reportConnectedDriverList:', this.onConnectedDriverList.bind(this));
67
+ rpcClient.on('_rpc_reportCurrentState:', this.onCurrentState.bind(this));
68
+ rpcClient.on('Page.frameDetached', this.frameDetached.bind(this));
69
+
70
+ await rpcClient.connect();
73
71
 
74
72
  // get the connection information about the app
75
73
  try {
76
74
  this.setConnectionKey();
77
75
  if (timeout) {
78
- log.debug(`Waiting up to ${timeout}ms for applications to be reported`);
76
+ this.log.debug(`Waiting up to ${timeout}ms for applications to be reported`);
79
77
  try {
80
78
  await waitForCondition(() => !_.isEmpty(this.appDict), {
81
79
  waitMs: timeout,
82
80
  intervalMs: APP_CONNECT_INTERVAL_MS,
83
81
  });
84
82
  } catch (err) {
85
- log.debug(`Timed out waiting for applications to be reported`);
83
+ this.log.debug(`Timed out waiting for applications to be reported`);
86
84
  }
87
85
  }
88
86
  return this.appDict || {};
89
87
  } catch (err) {
90
- log.error(`Error setting connection key: ${err.message}`);
88
+ this.log.error(`Error setting connection key: ${err.message}`);
91
89
  await this.disconnect();
92
90
  throw err;
93
91
  }
@@ -98,7 +96,7 @@ async function connect (timeout = APP_CONNECT_TIMEOUT_MS) {
98
96
  * @this {import('../remote-debugger').RemoteDebugger}
99
97
  * @returns {Promise<void>}
100
98
  */
101
- async function disconnect () {
99
+ export async function disconnect () {
102
100
  if (this.rpcClient) {
103
101
  await this.rpcClient.disconnect();
104
102
  }
@@ -106,50 +104,58 @@ async function disconnect () {
106
104
  this.teardown();
107
105
  }
108
106
 
107
+ /**
108
+ * @typedef {Object} Page
109
+ * @property {string} url
110
+ * @property {string} title
111
+ * @property {number} id
112
+ * @property {boolean} isKey
113
+ * @property {string} [bundleId]
114
+ */
115
+
109
116
  /**
110
117
  *
111
118
  * @this {import('../remote-debugger').RemoteDebugger}
112
119
  * @param {string?} currentUrl
113
120
  * @param {number} [maxTries]
114
121
  * @param {boolean} [ignoreAboutBlankUrl]
115
- * @returns {Promise<AppPages[]>}
122
+ * @returns {Promise<Page[]>}
116
123
  */
117
- async function selectApp (currentUrl = null, maxTries = SELECT_APP_RETRIES, ignoreAboutBlankUrl = false) {
118
- log.debug('Selecting application');
119
- if (!this.rpcClient) {
120
- throw new Error('rpcClient is undefined. Is the debugger connected?');
121
- }
124
+ export async function selectApp (currentUrl = null, maxTries = SELECT_APP_RETRIES, ignoreAboutBlankUrl = false) {
125
+ this.log.debug('Selecting application');
126
+ const rpcClient = this.requireRpcClient();
122
127
 
123
- const shouldCheckForTarget = this.rpcClient.shouldCheckForTarget;
124
- this.rpcClient.shouldCheckForTarget = false;
128
+ const shouldCheckForTarget = rpcClient.shouldCheckForTarget;
129
+ rpcClient.shouldCheckForTarget = false;
125
130
  try {
126
131
  const timer = new timing.Timer().start();
127
132
  if (!this.appDict || _.isEmpty(this.appDict)) {
128
- log.debug('No applications currently connected.');
133
+ this.log.debug('No applications currently connected.');
129
134
  return [];
130
135
  }
131
136
 
132
- const {appIdKey, pageDict} = await this.searchForApp(currentUrl, maxTries, ignoreAboutBlankUrl);
137
+ const {appIdKey, pageDict} = await this.searchForApp(currentUrl, maxTries, ignoreAboutBlankUrl) ?? {};
133
138
 
134
139
  // if, after all this, we have no dictionary, we have failed
135
140
  if (!appIdKey || !pageDict) {
136
- log.errorAndThrow(`Could not connect to a valid app after ${maxTries} tries.`);
141
+ throw this.log.errorWithException(`Could not connect to a valid app after ${maxTries} tries.`);
137
142
  }
138
143
 
139
144
  if (this.appIdKey !== appIdKey) {
140
- log.debug(`Received altered app id, updating from '${this.appIdKey}' to '${appIdKey}'`);
145
+ this.log.debug(`Received altered app id, updating from '${this.appIdKey}' to '${appIdKey}'`);
141
146
  this.appIdKey = appIdKey;
142
147
  }
143
148
 
144
- logApplicationDictionary(this.appDict);
149
+ logApplicationDictionary.bind(this)(this.appDict);
145
150
 
146
151
  // translate the dictionary into a useful form, and return to sender
147
152
  const pageArray = _.isEmpty(this.appDict[appIdKey].pageArray)
148
153
  ? pageArrayFromDict(pageDict)
149
154
  : this.appDict[appIdKey].pageArray;
150
- log.debug(`Finally selecting app ${this.appIdKey}: ${simpleStringify(pageArray)}`);
155
+ this.log.debug(`Finally selecting app ${this.appIdKey}: ${simpleStringify(pageArray)}`);
151
156
 
152
- let fullPageArray = [];
157
+ /** @type {Page[]} */
158
+ const fullPageArray = [];
153
159
  for (const [app, info] of _.toPairs(this.appDict)) {
154
160
  if (!_.isArray(info.pageArray) || !info.isActive) {
155
161
  continue;
@@ -157,7 +163,7 @@ async function selectApp (currentUrl = null, maxTries = SELECT_APP_RETRIES, igno
157
163
  const id = app.replace('PID:', '');
158
164
  for (const page of info.pageArray) {
159
165
  if (!(ignoreAboutBlankUrl && page.url === BLANK_PAGE_URL)) {
160
- let pageDict = _.clone(page);
166
+ const pageDict = _.clone(page);
161
167
  pageDict.id = `${id}.${pageDict.id}`;
162
168
  pageDict.bundleId = info.bundleId;
163
169
  fullPageArray.push(pageDict);
@@ -165,10 +171,10 @@ async function selectApp (currentUrl = null, maxTries = SELECT_APP_RETRIES, igno
165
171
  }
166
172
  }
167
173
 
168
- log.debug(`Selected app after ${timer.getDuration().asMilliSeconds.toFixed(0)}ms`);
174
+ this.log.debug(`Selected app after ${timer.getDuration().asMilliSeconds.toFixed(0)}ms`);
169
175
  return fullPageArray;
170
176
  } finally {
171
- this.rpcClient.shouldCheckForTarget = shouldCheckForTarget;
177
+ rpcClient.shouldCheckForTarget = shouldCheckForTarget;
172
178
  }
173
179
  }
174
180
 
@@ -180,32 +186,28 @@ async function selectApp (currentUrl = null, maxTries = SELECT_APP_RETRIES, igno
180
186
  * @param {boolean} ignoreAboutBlankUrl
181
187
  * @returns {Promise<AppPages?>}
182
188
  */
183
- async function searchForApp (currentUrl, maxTries, ignoreAboutBlankUrl) {
189
+ export async function searchForApp (currentUrl, maxTries, ignoreAboutBlankUrl) {
184
190
  const bundleIds = this.includeSafari && !this.isSafari
185
191
  ? [this.bundleId, ...this.additionalBundleIds, SAFARI_BUNDLE_ID]
186
192
  : [this.bundleId, ...this.additionalBundleIds];
187
193
  let retryCount = 0;
188
194
  try {
189
195
  return await retryInterval(maxTries, SELECT_APP_RETRY_SLEEP_MS, async () => {
190
- if (!this.rpcClient) {
191
- throw new Error('rpcClient is undefined. Is the debugger connected?');
192
- }
193
-
194
- logApplicationDictionary(this.appDict);
196
+ logApplicationDictionary.bind(this)(this.appDict);
195
197
  const possibleAppIds = getPossibleDebuggerAppKeys(/** @type {string[]} */ (bundleIds), this.appDict);
196
- log.debug(`Trying out the possible app ids: ${possibleAppIds.join(', ')} (try #${retryCount + 1} of ${maxTries})`);
198
+ this.log.debug(`Trying out the possible app ids: ${possibleAppIds.join(', ')} (try #${retryCount + 1} of ${maxTries})`);
197
199
  for (const attemptedAppIdKey of possibleAppIds) {
198
200
  try {
199
201
  if (!this.appDict[attemptedAppIdKey].isActive) {
200
- log.debug(`Skipping app '${attemptedAppIdKey}' because it is not active`);
202
+ this.log.debug(`Skipping app '${attemptedAppIdKey}' because it is not active`);
201
203
  continue;
202
204
  }
203
- log.debug(`Attempting app '${attemptedAppIdKey}'`);
204
- const [appIdKey, pageDict] = await this.rpcClient.selectApp(attemptedAppIdKey);
205
+ this.log.debug(`Attempting app '${attemptedAppIdKey}'`);
206
+ const [appIdKey, pageDict] = await this.requireRpcClient().selectApp(attemptedAppIdKey);
205
207
  // in iOS 8.2 the connect logic happens, but with an empty dictionary
206
208
  // which leads to the remote debugger getting disconnected, and into a loop
207
209
  if (_.isEmpty(pageDict)) {
208
- log.debug('Empty page dictionary received. Trying again.');
210
+ this.log.debug('Empty page dictionary received. Trying again.');
209
211
  continue;
210
212
  }
211
213
 
@@ -221,19 +223,19 @@ async function searchForApp (currentUrl, maxTries, ignoreAboutBlankUrl) {
221
223
  }
222
224
 
223
225
  if (currentUrl) {
224
- log.debug(`Received app, but expected url ('${currentUrl}') was not found. Trying again.`);
226
+ this.log.debug(`Received app, but expected url ('${currentUrl}') was not found. Trying again.`);
225
227
  } else {
226
- log.debug('Received app, but no match was found. Trying again.');
228
+ this.log.debug('Received app, but no match was found. Trying again.');
227
229
  }
228
230
  } catch (err) {
229
- log.debug(`Error checking application: '${err.message}'. Retrying connection`);
231
+ this.log.debug(`Error checking application: '${err.message}'. Retrying connection`);
230
232
  }
231
233
  }
232
234
  retryCount++;
233
235
  throw new Error('Failed to find an app to select');
234
236
  });
235
237
  } catch (ign) {
236
- log.errorAndThrow(`Could not connect to a valid app after ${maxTries} tries.`);
238
+ this.log.errorAndThrow(`Could not connect to a valid app after ${maxTries} tries.`);
237
239
  }
238
240
  return null;
239
241
  }
@@ -246,7 +248,7 @@ async function searchForApp (currentUrl, maxTries, ignoreAboutBlankUrl) {
246
248
  * @param {boolean} [ignoreAboutBlankUrl]
247
249
  * @returns {AppPages?}
248
250
  */
249
- function searchForPage (appsDict, currentUrl = null, ignoreAboutBlankUrl = false) {
251
+ export function searchForPage (appsDict, currentUrl = null, ignoreAboutBlankUrl = false) {
250
252
  for (const appDict of _.values(appsDict)) {
251
253
  if (!appDict || !appDict.isActive || !appDict.pageArray || appDict.pageArray.promise) {
252
254
  continue;
@@ -265,34 +267,31 @@ function searchForPage (appsDict, currentUrl = null, ignoreAboutBlankUrl = false
265
267
  /**
266
268
  *
267
269
  * @this {import('../remote-debugger').RemoteDebugger}
268
- * @param {string} appIdKey
269
- * @param {string} pageIdKey
270
+ * @param {string|number} appIdKey
271
+ * @param {string|number} pageIdKey
270
272
  * @param {boolean} [skipReadyCheck]
271
273
  * @returns {Promise<void>}
272
274
  */
273
- async function selectPage (appIdKey, pageIdKey, skipReadyCheck = false) {
274
- this.appIdKey = `PID:${appIdKey}`;
275
+ export async function selectPage (appIdKey, pageIdKey, skipReadyCheck = false) {
276
+ this.appIdKey = _.startsWith(`${appIdKey}`, 'PID:') ? `${appIdKey}` : `PID:${appIdKey}`;
275
277
  this.pageIdKey = pageIdKey;
276
278
 
277
- log.debug(`Selecting page '${pageIdKey}' on app '${this.appIdKey}' and forwarding socket setup`);
278
- if (!this.rpcClient) {
279
- throw new Error('rpcClient is undefined. Is the debugger connected?');
280
- }
279
+ this.log.debug(`Selecting page '${pageIdKey}' on app '${this.appIdKey}' and forwarding socket setup`);
281
280
 
282
281
  const timer = new timing.Timer().start();
283
282
 
284
- await this.rpcClient.selectPage(this.appIdKey, pageIdKey);
283
+ await this.requireRpcClient().selectPage(this.appIdKey, pageIdKey);
285
284
 
286
285
  // make sure everything is ready to go
287
286
  if (!skipReadyCheck && !await this.checkPageIsReady()) {
288
287
  await this.waitForDom();
289
288
  }
290
289
 
291
- log.debug(`Selected page after ${timer.getDuration().asMilliSeconds.toFixed(0)}ms`);
290
+ this.log.debug(`Selected page after ${timer.getDuration().asMilliSeconds.toFixed(0)}ms`);
292
291
  }
293
292
 
294
293
  /**
295
- *
294
+ * @this {import('../remote-debugger').RemoteDebugger}
296
295
  * @param {Record<string, any>} apps
297
296
  * @returns {void}
298
297
  */
@@ -308,56 +307,23 @@ function logApplicationDictionary (apps) {
308
307
  return JSON.stringify(value);
309
308
  }
310
309
 
311
- log.debug('Current applications available:');
310
+ this.log.debug('Current applications available:');
312
311
  for (const [app, info] of _.toPairs(apps)) {
313
- log.debug(` Application: "${app}"`);
312
+ this.log.debug(` Application: "${app}"`);
314
313
  for (const [key, value] of _.toPairs(info)) {
315
314
  if (key === 'pageArray' && Array.isArray(value) && value.length) {
316
- log.debug(` ${key}:`);
315
+ this.log.debug(` ${key}:`);
317
316
  for (const page of value) {
318
317
  let prefix = '- ';
319
318
  for (const [k, v] of _.toPairs(page)) {
320
- log.debug(` ${prefix}${k}: ${JSON.stringify(v)}`);
319
+ this.log.debug(` ${prefix}${k}: ${JSON.stringify(v)}`);
321
320
  prefix = ' ';
322
321
  }
323
322
  }
324
323
  } else {
325
324
  const valueString = getValueString(key, value);
326
- log.debug(` ${key}: ${valueString}`);
325
+ this.log.debug(` ${key}: ${valueString}`);
327
326
  }
328
327
  }
329
328
  }
330
329
  }
331
-
332
- /**
333
- *
334
- * @this {import('../remote-debugger').RemoteDebugger}
335
- * @param {Record<string, any>} dict
336
- * @returns {void}
337
- */
338
- function updateAppsWithDict (dict) {
339
- // get the dictionary entry into a nice form, and add it to the
340
- // application dictionary
341
- this.appDict = this.appDict || {};
342
- let [id, entry] = appInfoFromDict(dict);
343
- if (this.appDict[id]) {
344
- // preserve the page dictionary for this entry
345
- entry.pageArray = this.appDict[id].pageArray;
346
- }
347
- this.appDict[id] = entry;
348
-
349
- // add a promise to get the page dictionary
350
- if (_.isUndefined(entry.pageArray)) {
351
- entry.pageArray = deferredPromise();
352
- }
353
-
354
- // try to get the app id from our connected apps
355
- if (!this.appIdKey) {
356
- this.appIdKey = getDebuggerAppKey(/** @type {string} */ (this.bundleId), this.appDict);
357
- }
358
- }
359
-
360
- export default {
361
- setConnectionKey, connect, disconnect, selectApp,
362
- searchForApp, searchForPage, selectPage, updateAppsWithDict
363
- };
@@ -0,0 +1,45 @@
1
+
2
+ /**
3
+ *
4
+ * @this {import('../remote-debugger').RemoteDebugger}
5
+ * @returns {Promise<void>}
6
+ */
7
+ export async function getCookies () {
8
+ this.log.debug('Getting cookies');
9
+ return await this.requireRpcClient().send('Page.getCookies', {
10
+ appIdKey: this.appIdKey,
11
+ pageIdKey: this.pageIdKey
12
+ });
13
+ }
14
+
15
+ /**
16
+ *
17
+ * @this {import('../remote-debugger').RemoteDebugger}
18
+ * @param {any} cookie
19
+ * @returns {Promise<any>}
20
+ */
21
+ export async function setCookie (cookie) {
22
+ this.log.debug('Setting cookie');
23
+ return await this.requireRpcClient().send('Page.setCookie', {
24
+ appIdKey: this.appIdKey,
25
+ pageIdKey: this.pageIdKey,
26
+ cookie
27
+ });
28
+ }
29
+
30
+ /**
31
+ *
32
+ * @this {import('../remote-debugger').RemoteDebugger}
33
+ * @param {string} cookieName
34
+ * @param {string} url
35
+ * @returns {Promise<any>}
36
+ */
37
+ export async function deleteCookie (cookieName, url) {
38
+ this.log.debug(`Deleting cookie '${cookieName}' on '${url}'`);
39
+ return await this.requireRpcClient().send('Page.deleteCookie', {
40
+ appIdKey: this.appIdKey,
41
+ pageIdKey: this.pageIdKey,
42
+ cookieName,
43
+ url,
44
+ });
45
+ }
@@ -1,9 +1,33 @@
1
1
  // event emitted publically
2
- const events = {
2
+ export const events = {
3
3
  EVENT_PAGE_CHANGE: 'remote_debugger_page_change',
4
4
  EVENT_FRAMES_DETACHED: 'remote_debugger_frames_detached',
5
5
  EVENT_DISCONNECT: 'remote_debugger_disconnect',
6
6
  };
7
7
 
8
+ /**
9
+ * Keep track of the client event listeners so they can be removed
10
+ *
11
+ * @this {import('../remote-debugger').RemoteDebugger}
12
+ * @param {string} eventName
13
+ * @param {(event: import('@appium/types').StringRecord) => any} listener
14
+ * @returns {void}
15
+ */
16
+ export function addClientEventListener (eventName, listener) {
17
+ this._clientEventListeners[eventName] ??= [];
18
+ this._clientEventListeners[eventName].push(listener);
19
+ this.requireRpcClient().on(eventName, listener);
20
+ }
21
+
22
+ /**
23
+ * @this {import('../remote-debugger').RemoteDebugger}
24
+ * @param {string} eventName
25
+ * @returns {void}
26
+ */
27
+ export function removeClientEventListener (eventName) {
28
+ for (const listener of (this._clientEventListeners[eventName] || [])) {
29
+ this.requireRpcClient().off(eventName, listener);
30
+ }
31
+ }
8
32
 
9
33
  export default events;
@@ -1,4 +1,3 @@
1
- import log from '../logger';
2
1
  import { errors } from '@appium/base-driver';
3
2
  import { checkParams, simpleStringify, convertResult, RESPONSE_LOG_LENGTH } from '../utils';
4
3
  import { getScriptForAtom } from '../atoms';
@@ -15,17 +14,13 @@ const RPC_RESPONSE_TIMEOUT_MS = 5000;
15
14
  * @param {string} atom Name of Selenium atom (see atoms/ directory)
16
15
  * @param {any[]} args Arguments passed to the atom
17
16
  * @param {string[]} frames
18
- * @returns {Promise<string>} The result received from the atom
17
+ * @returns {Promise<any>} The result received from the atom
19
18
  */
20
- async function executeAtom (atom, args = [], frames = []) {
21
- if (!this.rpcClient?.isConnected) {
22
- throw new Error('Remote debugger is not connected');
23
- }
24
-
25
- log.debug(`Executing atom '${atom}' with 'args=${JSON.stringify(args)}; frames=${frames}'`);
19
+ export async function executeAtom (atom, args = [], frames = []) {
20
+ this.log.debug(`Executing atom '${atom}' with 'args=${JSON.stringify(args)}; frames=${frames}'`);
26
21
  const script = await getScriptForAtom(atom, args, frames);
27
22
  const value = await this.execute(script, true);
28
- log.debug(`Received result for atom '${atom}' execution: ${_.truncate(simpleStringify(value), {length: RESPONSE_LOG_LENGTH})}`);
23
+ this.log.debug(`Received result for atom '${atom}' execution: ${_.truncate(simpleStringify(value), {length: RESPONSE_LOG_LENGTH})}`);
29
24
  return value;
30
25
  }
31
26
 
@@ -36,18 +31,13 @@ async function executeAtom (atom, args = [], frames = []) {
36
31
  * @param {string[]} [frames]
37
32
  * @returns {Promise<any>}
38
33
  */
39
- async function executeAtomAsync (atom, args = [], frames = []) {
34
+ export async function executeAtomAsync (atom, args = [], frames = []) {
40
35
  // helper to send directly to the web inspector
41
- const evaluate = async (method, opts) => {
42
- if (!this.rpcClient?.isConnected) {
43
- throw new Error('Remote debugger is not connected');
44
- }
45
- return await this.rpcClient.send(method, Object.assign({
46
- appIdKey: this.appIdKey,
47
- pageIdKey: this.pageIdKey,
48
- returnByValue: false,
49
- }, opts));
50
- };
36
+ const evaluate = async (method, opts) => await this.requireRpcClient(true).send(method, Object.assign({
37
+ appIdKey: this.appIdKey,
38
+ pageIdKey: this.pageIdKey,
39
+ returnByValue: false,
40
+ }, opts));
51
41
 
52
42
  // first create a Promise on the page, saving the resolve/reject functions
53
43
  // as properties
@@ -94,7 +84,7 @@ async function executeAtomAsync (atom, args = [], frames = []) {
94
84
  // if the timeout math turns up 0 retries, make sure it happens once
95
85
  const retries = parseInt(`${timeout / retryWait}`, 10) || 1;
96
86
  const timer = new timing.Timer().start();
97
- log.debug(`Waiting up to ${timeout}ms for async execute to finish`);
87
+ this.log.debug(`Waiting up to ${timeout}ms for async execute to finish`);
98
88
  res = await retryInterval(retries, retryWait, async () => {
99
89
  // the atom _will_ return, either because it finished or an error
100
90
  // including a timeout error
@@ -131,10 +121,10 @@ async function executeAtomAsync (atom, args = [], frames = []) {
131
121
  * @param {boolean} [override]
132
122
  * @returns {Promise<any>}
133
123
  */
134
- async function execute (command, override) {
124
+ export async function execute (command, override) {
135
125
  // if the page is not loaded yet, wait for it
136
126
  if (this.pageLoading && !override) {
137
- log.debug('Trying to execute but page is not loaded.');
127
+ this.log.debug('Trying to execute but page is not loaded.');
138
128
  await this.waitForDom();
139
129
  }
140
130
 
@@ -149,17 +139,13 @@ async function execute (command, override) {
149
139
  await this.garbageCollect();
150
140
  }
151
141
 
152
- log.debug(`Sending javascript command: '${_.truncate(command, {length: 50})}'`);
153
- if (!this.rpcClient?.isConnected) {
154
- throw new Error('Remote debugger is not connected');
155
- }
156
- const res = await this.rpcClient.send('Runtime.evaluate', {
142
+ this.log.debug(`Sending javascript command: '${_.truncate(command, {length: 50})}'`);
143
+ const res = await this.requireRpcClient(true).send('Runtime.evaluate', {
157
144
  expression: command,
158
145
  returnByValue: true,
159
146
  appIdKey: this.appIdKey,
160
147
  pageIdKey: this.pageIdKey,
161
148
  });
162
-
163
149
  return convertResult(res);
164
150
  }
165
151
 
@@ -169,19 +155,15 @@ async function execute (command, override) {
169
155
  * @param {any} fn
170
156
  * @param {any[]} [args]
171
157
  */
172
- async function callFunction (objectId, fn, args) {
173
- if (!this.rpcClient?.isConnected) {
174
- throw new Error('Remote debugger is not connected');
175
- }
176
-
158
+ export async function callFunction (objectId, fn, args) {
177
159
  checkParams({appIdKey: this.appIdKey, pageIdKey: this.pageIdKey});
178
160
 
179
161
  if (this.garbageCollectOnExecute) {
180
162
  await this.garbageCollect();
181
163
  }
182
164
 
183
- log.debug('Calling javascript function');
184
- const res = await this.rpcClient.send('Runtime.callFunctionOn', {
165
+ this.log.debug('Calling javascript function');
166
+ const res = await this.requireRpcClient(true).send('Runtime.callFunctionOn', {
185
167
  objectId,
186
168
  functionDeclaration: fn,
187
169
  arguments: args,
@@ -192,6 +174,3 @@ async function callFunction (objectId, fn, args) {
192
174
 
193
175
  return convertResult(res);
194
176
  }
195
-
196
-
197
- export default { executeAtom, executeAtomAsync, execute, callFunction };