@appium/base-driver 8.5.2 → 8.5.5

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 (141) hide show
  1. package/build/lib/basedriver/capabilities.d.ts.map +1 -1
  2. package/build/lib/basedriver/capabilities.js +1 -1
  3. package/build/lib/basedriver/commands/event.js +1 -1
  4. package/build/lib/basedriver/commands/find.d.ts.map +1 -1
  5. package/build/lib/basedriver/commands/find.js +1 -1
  6. package/build/lib/basedriver/commands/index.js +1 -1
  7. package/build/lib/basedriver/commands/log.d.ts.map +1 -1
  8. package/build/lib/basedriver/commands/log.js +1 -1
  9. package/build/lib/basedriver/commands/session.js +1 -1
  10. package/build/lib/basedriver/commands/settings.d.ts.map +1 -1
  11. package/build/lib/basedriver/commands/settings.js +1 -1
  12. package/build/lib/basedriver/commands/timeout.d.ts.map +1 -1
  13. package/build/lib/basedriver/commands/timeout.js +1 -1
  14. package/build/lib/basedriver/core.d.ts +120 -139
  15. package/build/lib/basedriver/core.d.ts.map +1 -1
  16. package/build/lib/basedriver/core.js +1 -49
  17. package/build/lib/basedriver/desired-caps.d.ts +5 -2
  18. package/build/lib/basedriver/desired-caps.d.ts.map +1 -1
  19. package/build/lib/basedriver/desired-caps.js +14 -18
  20. package/build/lib/basedriver/device-settings.d.ts +9 -9
  21. package/build/lib/basedriver/device-settings.d.ts.map +1 -1
  22. package/build/lib/basedriver/device-settings.js +4 -4
  23. package/build/lib/basedriver/driver.d.ts +43 -38
  24. package/build/lib/basedriver/driver.d.ts.map +1 -1
  25. package/build/lib/basedriver/driver.js +58 -11
  26. package/build/lib/basedriver/helpers.d.ts +8 -3
  27. package/build/lib/basedriver/helpers.d.ts.map +1 -1
  28. package/build/lib/basedriver/helpers.js +4 -6
  29. package/build/lib/basedriver/logger.d.ts +1 -1
  30. package/build/lib/basedriver/logger.d.ts.map +1 -1
  31. package/build/lib/basedriver/logger.js +1 -1
  32. package/build/lib/constants.js +1 -1
  33. package/build/lib/express/crash.d.ts.map +1 -1
  34. package/build/lib/express/crash.js +1 -1
  35. package/build/lib/express/express-logging.d.ts.map +1 -1
  36. package/build/lib/express/express-logging.js +1 -1
  37. package/build/lib/express/idempotency.js +1 -1
  38. package/build/lib/express/logger.d.ts +1 -1
  39. package/build/lib/express/logger.d.ts.map +1 -1
  40. package/build/lib/express/logger.js +1 -1
  41. package/build/lib/express/middleware.d.ts.map +1 -1
  42. package/build/lib/express/middleware.js +1 -1
  43. package/build/lib/express/server.d.ts +21 -0
  44. package/build/lib/express/server.d.ts.map +1 -1
  45. package/build/lib/express/server.js +4 -9
  46. package/build/lib/express/static.d.ts.map +1 -1
  47. package/build/lib/express/static.js +2 -2
  48. package/build/lib/express/websocket.d.ts +14 -11
  49. package/build/lib/express/websocket.d.ts.map +1 -1
  50. package/build/lib/express/websocket.js +2 -2
  51. package/build/lib/helpers/capabilities.d.ts.map +1 -1
  52. package/build/lib/helpers/capabilities.js +1 -1
  53. package/build/lib/index.d.ts +2 -1
  54. package/build/lib/index.js +7 -1
  55. package/build/lib/jsonwp-proxy/protocol-converter.d.ts.map +1 -1
  56. package/build/lib/jsonwp-proxy/protocol-converter.js +2 -2
  57. package/build/lib/jsonwp-proxy/proxy.d.ts +30 -5
  58. package/build/lib/jsonwp-proxy/proxy.d.ts.map +1 -1
  59. package/build/lib/jsonwp-proxy/proxy.js +20 -4
  60. package/build/lib/jsonwp-status/status.d.ts.map +1 -1
  61. package/build/lib/jsonwp-status/status.js +2 -2
  62. package/build/lib/protocol/errors.d.ts +17 -8
  63. package/build/lib/protocol/errors.d.ts.map +1 -1
  64. package/build/lib/protocol/errors.js +9 -5
  65. package/build/lib/protocol/helpers.js +1 -1
  66. package/build/lib/protocol/index.js +1 -1
  67. package/build/lib/protocol/protocol.d.ts.map +1 -1
  68. package/build/lib/protocol/protocol.js +1 -1
  69. package/build/lib/protocol/routes.d.ts +17 -3
  70. package/build/lib/protocol/routes.d.ts.map +1 -1
  71. package/build/lib/protocol/routes.js +1 -1
  72. package/build/lib/protocol/validators.js +1 -1
  73. package/build/test/basedriver/driver-e2e-tests.js +1 -1
  74. package/build/test/basedriver/driver-tests.js +1 -1
  75. package/build/test/basedriver/index.js +1 -1
  76. package/build/test/e2e/basedriver/driver.e2e.spec.js +1 -1
  77. package/build/test/e2e/basedriver/helpers.e2e.spec.js +1 -1
  78. package/build/test/e2e/basedriver/websockets.e2e.spec.js +1 -1
  79. package/build/test/e2e/express/server.e2e.spec.js +1 -1
  80. package/build/test/e2e/jsonwp-proxy/proxy.e2e.spec.js +1 -1
  81. package/build/test/e2e/protocol/fake-driver.js +1 -1
  82. package/build/test/e2e/protocol/helpers.js +1 -1
  83. package/build/test/e2e/protocol/protocol.e2e.spec.js +13 -13
  84. package/build/test/helpers.js +1 -1
  85. package/build/test/unit/basedriver/capabilities.spec.js +12 -12
  86. package/build/test/unit/basedriver/capability.spec.js +15 -15
  87. package/build/test/unit/basedriver/commands/event.spec.js +1 -1
  88. package/build/test/unit/basedriver/commands/log.spec.js +1 -1
  89. package/build/test/unit/basedriver/device-settings.spec.js +1 -1
  90. package/build/test/unit/basedriver/driver.spec.js +1 -1
  91. package/build/test/unit/basedriver/helpers.spec.js +33 -33
  92. package/build/test/unit/basedriver/timeout.spec.js +1 -1
  93. package/build/test/unit/express/server.spec.js +1 -1
  94. package/build/test/unit/express/static.spec.js +2 -2
  95. package/build/test/unit/jsonwp-proxy/mock-request.js +1 -1
  96. package/build/test/unit/jsonwp-proxy/protocol-converter.spec.js +1 -1
  97. package/build/test/unit/jsonwp-proxy/proxy.spec.js +2 -2
  98. package/build/test/unit/jsonwp-proxy/url.spec.js +1 -1
  99. package/build/test/unit/jsonwp-status/status.spec.js +1 -1
  100. package/build/test/unit/protocol/errors.spec.js +1 -1
  101. package/build/test/unit/protocol/routes.spec.js +1 -1
  102. package/build/test/unit/protocol/validator.spec.js +1 -1
  103. package/build/tsconfig.tsbuildinfo +1 -1
  104. package/lib/basedriver/capabilities.js +95 -47
  105. package/lib/basedriver/commands/event.js +4 -4
  106. package/lib/basedriver/commands/find.js +12 -26
  107. package/lib/basedriver/commands/index.js +7 -7
  108. package/lib/basedriver/commands/log.js +5 -7
  109. package/lib/basedriver/commands/session.js +3 -3
  110. package/lib/basedriver/commands/settings.js +3 -5
  111. package/lib/basedriver/commands/timeout.js +18 -23
  112. package/lib/basedriver/core.js +150 -229
  113. package/lib/basedriver/desired-caps.js +30 -29
  114. package/lib/basedriver/device-settings.js +21 -20
  115. package/lib/basedriver/driver.js +131 -96
  116. package/lib/basedriver/helpers.js +124 -81
  117. package/lib/basedriver/logger.js +1 -1
  118. package/lib/constants.js +2 -6
  119. package/lib/express/crash.js +4 -6
  120. package/lib/express/express-logging.js +26 -24
  121. package/lib/express/idempotency.js +16 -16
  122. package/lib/express/logger.js +1 -1
  123. package/lib/express/middleware.js +49 -33
  124. package/lib/express/server.js +68 -44
  125. package/lib/express/static.js +11 -12
  126. package/lib/express/websocket.js +26 -16
  127. package/lib/helpers/capabilities.js +11 -16
  128. package/lib/index.js +50 -33
  129. package/lib/jsonwp-proxy/protocol-converter.js +85 -69
  130. package/lib/jsonwp-proxy/proxy.js +116 -53
  131. package/lib/jsonwp-status/status.js +36 -29
  132. package/lib/protocol/errors.js +469 -292
  133. package/lib/protocol/helpers.js +5 -8
  134. package/lib/protocol/index.js +22 -15
  135. package/lib/protocol/protocol.js +103 -55
  136. package/lib/protocol/routes.js +430 -273
  137. package/lib/protocol/validators.js +5 -5
  138. package/package.json +9 -11
  139. package/test/basedriver/driver-e2e-tests.js +92 -66
  140. package/test/basedriver/driver-tests.js +90 -33
  141. package/test/basedriver/index.js +1 -1
@@ -2,18 +2,14 @@ import _ from 'lodash';
2
2
  import path from 'path';
3
3
  import url from 'url';
4
4
  import logger from './logger';
5
- import { tempDir, fs, util, zip, net, timing, node } from '@appium/support';
5
+ import {tempDir, fs, util, zip, net, timing, node} from '@appium/support';
6
6
  import LRU from 'lru-cache';
7
7
  import AsyncLock from 'async-lock';
8
8
  import axios from 'axios';
9
9
 
10
10
  const IPA_EXT = '.ipa';
11
11
  const ZIP_EXTS = ['.zip', IPA_EXT];
12
- const ZIP_MIME_TYPES = [
13
- 'application/zip',
14
- 'application/x-zip-compressed',
15
- 'multipart/x-zip',
16
- ];
12
+ const ZIP_MIME_TYPES = ['application/zip', 'application/x-zip-compressed', 'multipart/x-zip'];
17
13
  const CACHED_APPS_MAX_AGE = 1000 * 60 * 60 * 24; // ms
18
14
  const MAX_CACHED_APPS = 1024;
19
15
  const APPLICATIONS_CACHE = new LRU({
@@ -21,8 +17,10 @@ const APPLICATIONS_CACHE = new LRU({
21
17
  ttl: CACHED_APPS_MAX_AGE, // expire after 24 hours
22
18
  updateAgeOnGet: true,
23
19
  dispose: (app, {fullPath}) => {
24
- logger.info(`The application '${app}' cached at '${fullPath}' has ` +
25
- `expired after ${CACHED_APPS_MAX_AGE}ms`);
20
+ logger.info(
21
+ `The application '${app}' cached at '${fullPath}' has ` +
22
+ `expired after ${CACHED_APPS_MAX_AGE}ms`
23
+ );
26
24
  if (fullPath) {
27
25
  fs.rimraf(fullPath);
28
26
  }
@@ -39,10 +37,11 @@ process.on('exit', () => {
39
37
  return;
40
38
  }
41
39
 
42
- const appPaths = [...APPLICATIONS_CACHE.values()]
43
- .map(({fullPath}) => fullPath);
44
- logger.debug(`Performing cleanup of ${appPaths.length} cached ` +
45
- util.pluralize('application', appPaths.length));
40
+ const appPaths = [...APPLICATIONS_CACHE.values()].map(({fullPath}) => fullPath);
41
+ logger.debug(
42
+ `Performing cleanup of ${appPaths.length} cached ` +
43
+ util.pluralize('application', appPaths.length)
44
+ );
46
45
  for (const appPath of appPaths) {
47
46
  try {
48
47
  // Asynchronous calls are not supported in onExit handler
@@ -53,21 +52,22 @@ process.on('exit', () => {
53
52
  }
54
53
  });
55
54
 
56
-
57
- async function retrieveHeaders (link) {
55
+ async function retrieveHeaders(link) {
58
56
  try {
59
- return (await axios({
60
- url: link,
61
- method: 'HEAD',
62
- timeout: 5000,
63
- })).headers;
57
+ return (
58
+ await axios({
59
+ url: link,
60
+ method: 'HEAD',
61
+ timeout: 5000,
62
+ })
63
+ ).headers;
64
64
  } catch (e) {
65
65
  logger.info(`Cannot send HEAD request to '${link}'. Original error: ${e.message}`);
66
66
  }
67
67
  return {};
68
68
  }
69
69
 
70
- function getCachedApplicationPath (link, currentAppProps = {}, cachedAppInfo = {}) {
70
+ function getCachedApplicationPath(link, currentAppProps = {}, cachedAppInfo = {}) {
71
71
  const refresh = () => {
72
72
  logger.debug(`A fresh copy of the application is going to be downloaded from ${link}`);
73
73
  return null;
@@ -108,7 +108,9 @@ function getCachedApplicationPath (link, currentAppProps = {}, cachedAppInfo = {
108
108
  if (currentMaxAge && timestamp) {
109
109
  const msLeft = timestamp + currentMaxAge * 1000 - Date.now();
110
110
  if (msLeft > 0) {
111
- logger.debug(`The cached application '${path.basename(fullPath)}' will expire in ${msLeft / 1000}s`);
111
+ logger.debug(
112
+ `The cached application '${path.basename(fullPath)}' will expire in ${msLeft / 1000}s`
113
+ );
112
114
  return fullPath;
113
115
  }
114
116
  logger.debug(`The cached application '${path.basename(fullPath)}' has expired`);
@@ -116,25 +118,27 @@ function getCachedApplicationPath (link, currentAppProps = {}, cachedAppInfo = {
116
118
  return refresh();
117
119
  }
118
120
 
119
- function verifyAppExtension (app, supportedAppExtensions) {
121
+ function verifyAppExtension(app, supportedAppExtensions) {
120
122
  if (supportedAppExtensions.map(_.toLower).includes(_.toLower(path.extname(app)))) {
121
123
  return app;
122
124
  }
123
- throw new Error(`New app path '${app}' did not have ` +
124
- `${util.pluralize('extension', supportedAppExtensions.length, false)}: ` +
125
- supportedAppExtensions);
125
+ throw new Error(
126
+ `New app path '${app}' did not have ` +
127
+ `${util.pluralize('extension', supportedAppExtensions.length, false)}: ` +
128
+ supportedAppExtensions
129
+ );
126
130
  }
127
131
 
128
- async function calculateFolderIntegrity (folderPath) {
132
+ async function calculateFolderIntegrity(folderPath) {
129
133
  return (await fs.glob('**/*', {cwd: folderPath, strict: false, nosort: true})).length;
130
134
  }
131
135
 
132
- async function calculateFileIntegrity (filePath) {
136
+ async function calculateFileIntegrity(filePath) {
133
137
  return await fs.hash(filePath);
134
138
  }
135
139
 
136
- async function isAppIntegrityOk (currentPath, expectedIntegrity = {}) {
137
- if (!await fs.exists(currentPath)) {
140
+ async function isAppIntegrityOk(currentPath, expectedIntegrity = {}) {
141
+ if (!(await fs.exists(currentPath))) {
138
142
  return false;
139
143
  }
140
144
 
@@ -146,8 +150,8 @@ async function isAppIntegrityOk (currentPath, expectedIntegrity = {}) {
146
150
  // more precise, but we don't need to be very precise here and also don't want to
147
151
  // overuse RAM and have a performance drop.
148
152
  return (await fs.stat(currentPath)).isDirectory()
149
- ? await calculateFolderIntegrity(currentPath) >= expectedIntegrity?.folder
150
- : await calculateFileIntegrity(currentPath) === expectedIntegrity?.file;
153
+ ? (await calculateFolderIntegrity(currentPath)) >= expectedIntegrity?.folder
154
+ : (await calculateFileIntegrity(currentPath)) === expectedIntegrity?.file;
151
155
  }
152
156
 
153
157
  /**
@@ -178,7 +182,7 @@ async function isAppIntegrityOk (currentPath, expectedIntegrity = {}) {
178
182
 
179
183
  /**
180
184
  * @typedef ConfigureAppOptions
181
- * @property {(obj: PostProcessOptions) => (Promise<PostProcessResult|undefined>|PostProcessResult|undefined)} onPostProcess
185
+ * @property {(obj: PostProcessOptions) => (Promise<PostProcessResult|undefined>|PostProcessResult|undefined)} [onPostProcess]
182
186
  * Optional function, which should be applied
183
187
  * to the application after it is downloaded/preprocessed. This function may be async
184
188
  * and is expected to accept single object parameter.
@@ -200,16 +204,16 @@ async function isAppIntegrityOk (currentPath, expectedIntegrity = {}) {
200
204
  * @param {string|string[]|ConfigureAppOptions} options
201
205
  * @returns The full path to the resulting application bundle
202
206
  */
203
- async function configureApp (app, options = {}) {
207
+ async function configureApp(app, options = /** @type {ConfigureAppOptions} */ ({})) {
204
208
  if (!_.isString(app)) {
205
209
  // immediately shortcircuit if not given an app
206
210
  return;
207
211
  }
208
212
 
209
213
  let supportedAppExtensions;
210
- const {
211
- onPostProcess,
212
- } = _.isPlainObject(options) ? options : {};
214
+ const onPostProcess =
215
+ !_.isString(options) && !_.isArray(options) ? options.onPostProcess : undefined;
216
+
213
217
  if (_.isString(options)) {
214
218
  supportedAppExtensions = [options];
215
219
  } else if (_.isArray(options)) {
@@ -225,13 +229,14 @@ async function configureApp (app, options = {}) {
225
229
  let shouldUnzipApp = false;
226
230
  let packageHash = null;
227
231
  let headers = null;
232
+ /** @type {RemoteAppProps} */
228
233
  const remoteAppProps = {
229
234
  lastModified: null,
230
235
  immutable: false,
231
236
  maxAge: null,
232
237
  };
233
238
  const {protocol, pathname} = url.parse(newApp);
234
- const isUrl = ['http:', 'https:'].includes(protocol);
239
+ const isUrl = protocol === null ? false : ['http:', 'https:'].includes(protocol);
235
240
 
236
241
  const cachedAppInfo = APPLICATIONS_CACHE.get(app);
237
242
 
@@ -260,14 +265,16 @@ async function configureApp (app, options = {}) {
260
265
  logger.info(`Reusing previously downloaded application at '${cachedPath}'`);
261
266
  return verifyAppExtension(cachedPath, supportedAppExtensions);
262
267
  }
263
- logger.info(`The application at '${cachedPath}' does not exist anymore ` +
264
- `or its integrity has been damaged. Deleting it from the internal cache`);
268
+ logger.info(
269
+ `The application at '${cachedPath}' does not exist anymore ` +
270
+ `or its integrity has been damaged. Deleting it from the internal cache`
271
+ );
265
272
  APPLICATIONS_CACHE.delete(app);
266
273
  }
267
274
 
268
275
  let fileName = null;
269
- const basename = fs.sanitizeName(path.basename(decodeURIComponent(pathname)), {
270
- replacement: SANITIZE_REPLACEMENT
276
+ const basename = fs.sanitizeName(path.basename(decodeURIComponent(pathname ?? '')), {
277
+ replacement: SANITIZE_REPLACEMENT,
271
278
  });
272
279
  const extname = path.extname(basename);
273
280
  // to determine if we need to unzip the app, we have a number of places
@@ -280,7 +287,11 @@ async function configureApp (app, options = {}) {
280
287
  const ct = headers['content-type'];
281
288
  logger.debug(`Content-Type: ${ct}`);
282
289
  // the filetype may not be obvious for certain urls, so check the mime type too
283
- if (ZIP_MIME_TYPES.some((mimeType) => new RegExp(`\\b${_.escapeRegExp(mimeType)}\\b`).test(ct))) {
290
+ if (
291
+ ZIP_MIME_TYPES.some((mimeType) =>
292
+ new RegExp(`\\b${_.escapeRegExp(mimeType)}\\b`).test(ct)
293
+ )
294
+ ) {
284
295
  if (!fileName) {
285
296
  fileName = `${DEFAULT_BASENAME}.zip`;
286
297
  }
@@ -292,7 +303,7 @@ async function configureApp (app, options = {}) {
292
303
  const match = /filename="([^"]+)/i.exec(headers['content-disposition']);
293
304
  if (match) {
294
305
  fileName = fs.sanitizeName(match[1], {
295
- replacement: SANITIZE_REPLACEMENT
306
+ replacement: SANITIZE_REPLACEMENT,
296
307
  });
297
308
  shouldUnzipApp = shouldUnzipApp || ZIP_EXTS.includes(path.extname(fileName));
298
309
  }
@@ -304,9 +315,11 @@ async function configureApp (app, options = {}) {
304
315
  : DEFAULT_BASENAME;
305
316
  let resultingExt = extname;
306
317
  if (!supportedAppExtensions.includes(resultingExt)) {
307
- logger.info(`The current file extension '${resultingExt}' is not supported. ` +
308
- `Defaulting to '${_.first(supportedAppExtensions)}'`);
309
- resultingExt = _.first(supportedAppExtensions);
318
+ logger.info(
319
+ `The current file extension '${resultingExt}' is not supported. ` +
320
+ `Defaulting to '${_.first(supportedAppExtensions)}'`
321
+ );
322
+ resultingExt = /** @type {string} */ (_.first(supportedAppExtensions));
310
323
  }
311
324
  fileName = `${resultingName}${resultingExt}`;
312
325
  }
@@ -323,7 +336,8 @@ async function configureApp (app, options = {}) {
323
336
  let errorMessage = `The application at '${newApp}' does not exist or is not accessible`;
324
337
  // protocol value for 'C:\\temp' is 'c:', so we check the length as well
325
338
  if (_.isString(protocol) && protocol.length > 2) {
326
- errorMessage = `The protocol '${protocol}' used in '${newApp}' is not supported. ` +
339
+ errorMessage =
340
+ `The protocol '${protocol}' used in '${newApp}' is not supported. ` +
327
341
  `Only http: and https: protocols are supported`;
328
342
  }
329
343
  throw new Error(errorMessage);
@@ -345,8 +359,10 @@ async function configureApp (app, options = {}) {
345
359
  logger.info(`Will reuse previously cached application at '${fullPath}'`);
346
360
  return verifyAppExtension(fullPath, supportedAppExtensions);
347
361
  }
348
- logger.info(`The application at '${fullPath}' does not exist anymore ` +
349
- `or its integrity has been damaged. Deleting it from the cache`);
362
+ logger.info(
363
+ `The application at '${fullPath}' does not exist anymore ` +
364
+ `or its integrity has been damaged. Deleting it from the cache`
365
+ );
350
366
  APPLICATIONS_CACHE.delete(app);
351
367
  }
352
368
  const tmpRoot = await tempDir.openDir();
@@ -360,8 +376,10 @@ async function configureApp (app, options = {}) {
360
376
  logger.info(`Unzipped local app to '${newApp}'`);
361
377
  } else if (!path.isAbsolute(newApp)) {
362
378
  newApp = path.resolve(process.cwd(), newApp);
363
- logger.warn(`The current application path '${app}' is not absolute ` +
364
- `and has been rewritten to '${newApp}'. Consider using absolute paths rather than relative`);
379
+ logger.warn(
380
+ `The current application path '${app}' is not absolute ` +
381
+ `and has been rewritten to '${newApp}'. Consider using absolute paths rather than relative`
382
+ );
365
383
  app = newApp;
366
384
  }
367
385
 
@@ -393,19 +411,19 @@ async function configureApp (app, options = {}) {
393
411
  headers: _.clone(headers),
394
412
  appPath: newApp,
395
413
  });
396
- return (!result?.appPath || app === result?.appPath || !await fs.exists(result?.appPath))
414
+ return !result?.appPath || app === result?.appPath || !(await fs.exists(result?.appPath))
397
415
  ? newApp
398
416
  : await storeAppInCache(result.appPath);
399
417
  }
400
418
 
401
419
  verifyAppExtension(newApp, supportedAppExtensions);
402
- return (app !== newApp && (packageHash || _.values(remoteAppProps).some(Boolean)))
420
+ return app !== newApp && (packageHash || _.values(remoteAppProps).some(Boolean))
403
421
  ? await storeAppInCache(newApp)
404
422
  : newApp;
405
423
  });
406
424
  }
407
425
 
408
- async function downloadApp (app, targetPath) {
426
+ async function downloadApp(app, targetPath) {
409
427
  const {href} = url.parse(app);
410
428
  try {
411
429
  await net.downloadFile(href, targetPath, {
@@ -426,11 +444,11 @@ async function downloadApp (app, targetPath) {
426
444
  * @param {Array<string>|string} supportedAppExtensions The list of extensions
427
445
  * the target application bundle supports, for example ['.apk', '.apks'] for
428
446
  * Android packages
429
- * @returns {string} Full path to the bundle in the destination folder
447
+ * @returns {Promise<string>} Full path to the bundle in the destination folder
430
448
  * @throws {Error} If the given archive is invalid or no application bundles
431
449
  * have been found inside
432
450
  */
433
- async function unzipApp (zipPath, dstRoot, supportedAppExtensions) {
451
+ async function unzipApp(zipPath, dstRoot, supportedAppExtensions) {
434
452
  await zip.assertValidZip(zipPath);
435
453
 
436
454
  if (!_.isArray(supportedAppExtensions)) {
@@ -442,8 +460,8 @@ async function unzipApp (zipPath, dstRoot, supportedAppExtensions) {
442
460
  logger.debug(`Unzipping '${zipPath}'`);
443
461
  const timer = new timing.Timer().start();
444
462
  const useSystemUnzipEnv = process.env.APPIUM_PREFER_SYSTEM_UNZIP;
445
- const useSystemUnzip = _.isEmpty(useSystemUnzipEnv)
446
- || !['0', 'false'].includes(_.toLower(useSystemUnzipEnv));
463
+ const useSystemUnzip =
464
+ _.isEmpty(useSystemUnzipEnv) || !['0', 'false'].includes(_.toLower(useSystemUnzipEnv));
447
465
  /**
448
466
  * Attempt to use use the system `unzip` (e.g., `/usr/bin/unzip`) due
449
467
  * to the significant performance improvement it provides over the native
@@ -453,25 +471,41 @@ async function unzipApp (zipPath, dstRoot, supportedAppExtensions) {
453
471
  const extractionOpts = {useSystemUnzip};
454
472
  // https://github.com/appium/appium/issues/14100
455
473
  if (path.extname(zipPath) === IPA_EXT) {
456
- logger.debug(`Enforcing UTF-8 encoding on the extracted file names for '${path.basename(zipPath)}'`);
474
+ logger.debug(
475
+ `Enforcing UTF-8 encoding on the extracted file names for '${path.basename(zipPath)}'`
476
+ );
457
477
  extractionOpts.fileNamesEncoding = 'utf8';
458
478
  }
459
479
  await zip.extractAllTo(zipPath, tmpRoot, extractionOpts);
460
- const globPattern = `**/*.+(${supportedAppExtensions.map((ext) => ext.replace(/^\./, '')).join('|')})`;
461
- const sortedBundleItems = (await fs.glob(globPattern, {
462
- cwd: tmpRoot,
463
- strict: false,
464
- // Get the top level match
465
- })).sort((a, b) => a.split(path.sep).length - b.split(path.sep).length);
480
+ const globPattern = `**/*.+(${supportedAppExtensions
481
+ .map((ext) => ext.replace(/^\./, ''))
482
+ .join('|')})`;
483
+ const sortedBundleItems = (
484
+ await fs.glob(globPattern, {
485
+ cwd: tmpRoot,
486
+ strict: false,
487
+ // Get the top level match
488
+ })
489
+ ).sort((a, b) => a.split(path.sep).length - b.split(path.sep).length);
466
490
  if (_.isEmpty(sortedBundleItems)) {
467
- logger.errorAndThrow(`App unzipped OK, but we could not find any '${supportedAppExtensions}' ` +
468
- util.pluralize('bundle', supportedAppExtensions.length, false) +
469
- ` in it. Make sure your archive contains at least one package having ` +
470
- `'${supportedAppExtensions}' ${util.pluralize('extension', supportedAppExtensions.length, false)}`);
491
+ logger.errorAndThrow(
492
+ `App unzipped OK, but we could not find any '${supportedAppExtensions}' ` +
493
+ util.pluralize('bundle', supportedAppExtensions.length, false) +
494
+ ` in it. Make sure your archive contains at least one package having ` +
495
+ `'${supportedAppExtensions}' ${util.pluralize(
496
+ 'extension',
497
+ supportedAppExtensions.length,
498
+ false
499
+ )}`
500
+ );
471
501
  }
472
- logger.debug(`Extracted ${util.pluralize('bundle item', sortedBundleItems.length, true)} ` +
473
- `from '${zipPath}' in ${Math.round(timer.getDuration().asMilliSeconds)}ms: ${sortedBundleItems}`);
474
- const matchedBundle = _.first(sortedBundleItems);
502
+ logger.debug(
503
+ `Extracted ${util.pluralize('bundle item', sortedBundleItems.length, true)} ` +
504
+ `from '${zipPath}' in ${Math.round(
505
+ timer.getDuration().asMilliSeconds
506
+ )}ms: ${sortedBundleItems}`
507
+ );
508
+ const matchedBundle = /** @type {string} */ (_.first(sortedBundleItems));
475
509
  logger.info(`Assuming '${matchedBundle}' is the correct bundle`);
476
510
  const dstPath = path.resolve(dstRoot, path.basename(matchedBundle));
477
511
  await fs.mv(path.resolve(tmpRoot, matchedBundle), dstPath, {mkdirp: true});
@@ -481,8 +515,8 @@ async function unzipApp (zipPath, dstRoot, supportedAppExtensions) {
481
515
  }
482
516
  }
483
517
 
484
- function isPackageOrBundle (app) {
485
- return (/^([a-zA-Z0-9\-_]+\.[a-zA-Z0-9\-_]+)+$/).test(app);
518
+ function isPackageOrBundle(app) {
519
+ return /^([a-zA-Z0-9\-_]+\.[a-zA-Z0-9\-_]+)+$/.test(app);
486
520
  }
487
521
 
488
522
  /**
@@ -495,7 +529,7 @@ function isPackageOrBundle (app) {
495
529
  * @param {String} firstKey The first key to duplicate
496
530
  * @param {String} secondKey The second key to duplicate
497
531
  */
498
- function duplicateKeys (input, firstKey, secondKey) {
532
+ function duplicateKeys(input, firstKey, secondKey) {
499
533
  // If array provided, recursively call on all elements
500
534
  if (_.isArray(input)) {
501
535
  return input.map((item) => duplicateKeys(item, firstKey, secondKey));
@@ -526,7 +560,7 @@ function duplicateKeys (input, firstKey, secondKey) {
526
560
  *
527
561
  * @param {string|Array<String>} cap A desired capability
528
562
  */
529
- function parseCapsArray (cap) {
563
+ function parseCapsArray(cap) {
530
564
  if (_.isArray(cap)) {
531
565
  return cap;
532
566
  }
@@ -553,15 +587,24 @@ function parseCapsArray (cap) {
553
587
  * @param {string?} sessionId session identifier (if exists)
554
588
  * @returns {string}
555
589
  */
556
- function generateDriverLogPrefix (obj, sessionId = null) {
590
+ function generateDriverLogPrefix(obj, sessionId = null) {
557
591
  const instanceName = `${obj.constructor.name}@${node.getObjectId(obj).substring(0, 4)}`;
558
592
  return sessionId ? `${instanceName} (${sessionId.substring(0, 8)})` : instanceName;
559
593
  }
560
594
 
561
595
  /** @type {import('@appium/types').DriverHelpers} */
562
596
  export default {
563
- configureApp, isPackageOrBundle, duplicateKeys, parseCapsArray, generateDriverLogPrefix
564
- };
565
- export {
566
- configureApp, isPackageOrBundle, duplicateKeys, parseCapsArray, generateDriverLogPrefix
597
+ configureApp,
598
+ isPackageOrBundle,
599
+ duplicateKeys,
600
+ parseCapsArray,
601
+ generateDriverLogPrefix,
567
602
  };
603
+ export {configureApp, isPackageOrBundle, duplicateKeys, parseCapsArray, generateDriverLogPrefix};
604
+
605
+ /**
606
+ * @typedef RemoteAppProps
607
+ * @property {Date?} lastModified
608
+ * @property {boolean} immutable
609
+ * @property {number?} maxAge
610
+ */
@@ -1,4 +1,4 @@
1
- import { logger } from '@appium/support';
1
+ import {logger} from '@appium/support';
2
2
 
3
3
  const log = logger.getLogger('BaseDriver');
4
4
  export default log;
package/lib/constants.js CHANGED
@@ -1,4 +1,4 @@
1
- import { util } from '@appium/support';
1
+ import {util} from '@appium/support';
2
2
 
3
3
  // The default maximum length of a single log record
4
4
  // containing http request/response body
@@ -17,8 +17,4 @@ const PROTOCOLS = {
17
17
  // Before Appium 2.0, this default value was '/wd/hub' by historical reasons.
18
18
  const DEFAULT_BASE_PATH = '';
19
19
 
20
-
21
- export {
22
- MAX_LOG_BODY_LENGTH, MJSONWP_ELEMENT_KEY, W3C_ELEMENT_KEY,
23
- PROTOCOLS, DEFAULT_BASE_PATH
24
- };
20
+ export {MAX_LOG_BODY_LENGTH, MJSONWP_ELEMENT_KEY, W3C_ELEMENT_KEY, PROTOCOLS, DEFAULT_BASE_PATH};
@@ -1,13 +1,11 @@
1
- import { errors } from '../protocol';
1
+ import {errors} from '../protocol';
2
2
 
3
-
4
- function produceError () {
3
+ function produceError() {
5
4
  throw new errors.UnknownCommandError('Produced generic error for testing');
6
5
  }
7
6
 
8
- function produceCrash () {
7
+ function produceCrash() {
9
8
  throw new Error('We just tried to crash Appium!');
10
9
  }
11
10
 
12
-
13
- export { produceError, produceCrash };
11
+ export {produceError, produceCrash};
@@ -2,23 +2,21 @@ import _ from 'lodash';
2
2
  import '@colors/colors';
3
3
  import morgan from 'morgan';
4
4
  import log from './logger';
5
- import { MAX_LOG_BODY_LENGTH } from '../constants';
6
-
5
+ import {MAX_LOG_BODY_LENGTH} from '../constants';
7
6
 
8
7
  // Copied the morgan compile function over so that cooler formats
9
8
  // may be configured
10
- function compile (fmt) {
9
+ function compile(fmt) {
11
10
  // escape quotes
12
11
  fmt = fmt.replace(/"/g, '\\"');
13
- fmt = fmt.replace(/:([-\w]{2,})(?:\[([^\]]+)\])?/g,
14
- function replace (_, name, arg) {
15
- return `"\n + (tokens["${name}"](req, res, "${arg}") || "-") + "`;
16
- });
12
+ fmt = fmt.replace(/:([-\w]{2,})(?:\[([^\]]+)\])?/g, function replace(_, name, arg) {
13
+ return `"\n + (tokens["${name}"](req, res, "${arg}") || "-") + "`;
14
+ });
17
15
  let js = ` return "${fmt}";`;
18
16
  return new Function('tokens, req, res', js);
19
17
  }
20
18
 
21
- function requestEndLoggingFormat (tokens, req, res) {
19
+ function requestEndLoggingFormat(tokens, req, res) {
22
20
  let status = res.statusCode;
23
21
  let statusStr = ':status';
24
22
  if (status >= 500) {
@@ -30,28 +28,32 @@ function requestEndLoggingFormat (tokens, req, res) {
30
28
  } else {
31
29
  statusStr = statusStr.green;
32
30
  }
33
- let fn = compile(`${'<-- :method :url '.white}${statusStr} ${':response-time ms - :res[content-length]'.grey}`);
31
+ let fn = compile(
32
+ `${'<-- :method :url '.white}${statusStr} ${':response-time ms - :res[content-length]'.grey}`
33
+ );
34
34
  return fn(tokens, req, res);
35
35
  }
36
36
 
37
37
  const endLogFormatter = morgan((tokens, req, res) => {
38
- log.info(requestEndLoggingFormat(tokens, req, res),
39
- (res.jsonResp || '').grey);
38
+ log.info(requestEndLoggingFormat(tokens, req, res), (res.jsonResp || '').grey);
40
39
  });
41
40
 
42
41
  const requestStartLoggingFormat = compile(`${'-->'.white} ${':method'.white} ${':url'.white}`);
43
42
 
44
- const startLogFormatter = morgan((tokens, req, res) => {
45
- // morgan output is redirected straight to winston
46
- let reqBody = '';
47
- if (req.body) {
48
- try {
49
- reqBody = _.truncate(_.isString(req.body) ? req.body : JSON.stringify(req.body), {
50
- length: MAX_LOG_BODY_LENGTH,
51
- });
52
- } catch (ign) {}
53
- }
54
- log.info(requestStartLoggingFormat(tokens, req, res), reqBody.grey);
55
- }, {immediate: true});
43
+ const startLogFormatter = morgan(
44
+ (tokens, req, res) => {
45
+ // morgan output is redirected straight to winston
46
+ let reqBody = '';
47
+ if (req.body) {
48
+ try {
49
+ reqBody = _.truncate(_.isString(req.body) ? req.body : JSON.stringify(req.body), {
50
+ length: MAX_LOG_BODY_LENGTH,
51
+ });
52
+ } catch (ign) {}
53
+ }
54
+ log.info(requestStartLoggingFormat(tokens, req, res), reqBody.grey);
55
+ },
56
+ {immediate: true}
57
+ );
56
58
 
57
- export { endLogFormatter, startLogFormatter };
59
+ export {endLogFormatter, startLogFormatter};
@@ -1,16 +1,15 @@
1
1
  import log from './logger';
2
2
  import LRU from 'lru-cache';
3
- import { fs, util } from '@appium/support';
3
+ import {fs, util} from '@appium/support';
4
4
  import os from 'os';
5
5
  import path from 'path';
6
- import { EventEmitter } from 'events';
7
-
6
+ import {EventEmitter} from 'events';
8
7
 
9
8
  const CACHE_SIZE = 1024;
10
9
  const IDEMPOTENT_RESPONSES = new LRU({
11
10
  max: CACHE_SIZE,
12
11
  updateAgeOnGet: true,
13
- dispose (key, {response}) {
12
+ dispose(key, {response}) {
14
13
  if (response) {
15
14
  fs.rimrafSync(response);
16
15
  }
@@ -20,9 +19,7 @@ const MONITORED_METHODS = ['POST', 'PATCH'];
20
19
  const IDEMPOTENCY_KEY_HEADER = 'x-idempotency-key';
21
20
 
22
21
  process.on('exit', () => {
23
- const resPaths = [...IDEMPOTENT_RESPONSES.values()]
24
- .map(({response}) => response)
25
- .filter(Boolean);
22
+ const resPaths = [...IDEMPOTENT_RESPONSES.values()].map(({response}) => response).filter(Boolean);
26
23
  for (const resPath of resPaths) {
27
24
  try {
28
25
  // Asynchronous calls are not supported in onExit handler
@@ -31,8 +28,7 @@ process.on('exit', () => {
31
28
  }
32
29
  });
33
30
 
34
-
35
- function cacheResponse (key, req, res) {
31
+ function cacheResponse(key, req, res) {
36
32
  const responseStateListener = new EventEmitter();
37
33
  IDEMPOTENT_RESPONSES.set(key, {
38
34
  method: req.method,
@@ -72,8 +68,10 @@ function cacheResponse (key, req, res) {
72
68
  }
73
69
 
74
70
  if (!IDEMPOTENT_RESPONSES.has(key)) {
75
- log.info(`Could not cache the response identified by '${key}'. ` +
76
- `Cache consistency has been damaged`);
71
+ log.info(
72
+ `Could not cache the response identified by '${key}'. ` +
73
+ `Cache consistency has been damaged`
74
+ );
77
75
  return responseStateListener.emit('ready', null);
78
76
  }
79
77
  if (writeError) {
@@ -82,8 +80,10 @@ function cacheResponse (key, req, res) {
82
80
  return responseStateListener.emit('ready', null);
83
81
  }
84
82
  if (!isResponseFullySent) {
85
- log.info(`Could not cache the response identified by '${key}', ` +
86
- `because it has not been completed`);
83
+ log.info(
84
+ `Could not cache the response identified by '${key}', ` +
85
+ `because it has not been completed`
86
+ );
87
87
  log.info('Does the client terminate connections too early?');
88
88
  IDEMPOTENT_RESPONSES.delete(key);
89
89
  return responseStateListener.emit('ready', null);
@@ -94,7 +94,7 @@ function cacheResponse (key, req, res) {
94
94
  });
95
95
  }
96
96
 
97
- async function handleIdempotency (req, res, next) {
97
+ async function handleIdempotency(req, res, next) {
98
98
  const key = req.headers[IDEMPOTENCY_KEY_HEADER];
99
99
  if (!key) {
100
100
  return next();
@@ -124,7 +124,7 @@ async function handleIdempotency (req, res, next) {
124
124
  }
125
125
 
126
126
  const rerouteCachedResponse = async (cachedResPath) => {
127
- if (!await fs.exists(cachedResPath)) {
127
+ if (!(await fs.exists(cachedResPath))) {
128
128
  IDEMPOTENT_RESPONSES.delete(key);
129
129
  log.warn(`Could not read the cached response identified by key '${key}'`);
130
130
  log.warn('The temporary storage is not accessible anymore');
@@ -149,4 +149,4 @@ async function handleIdempotency (req, res, next) {
149
149
  }
150
150
  }
151
151
 
152
- export { handleIdempotency };
152
+ export {handleIdempotency};
@@ -1,4 +1,4 @@
1
- import { logger } from '@appium/support';
1
+ import {logger} from '@appium/support';
2
2
 
3
3
  const log = logger.getLogger('HTTP');
4
4
  export default log;