appium-xcuitest-driver 10.10.0 → 10.11.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 (75) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/build/lib/app-infos-cache.d.ts +29 -31
  3. package/build/lib/app-infos-cache.d.ts.map +1 -1
  4. package/build/lib/app-infos-cache.js +29 -33
  5. package/build/lib/app-infos-cache.js.map +1 -1
  6. package/build/lib/app-utils.d.ts +30 -59
  7. package/build/lib/app-utils.d.ts.map +1 -1
  8. package/build/lib/app-utils.js +158 -211
  9. package/build/lib/app-utils.js.map +1 -1
  10. package/build/lib/commands/battery.d.ts.map +1 -1
  11. package/build/lib/commands/battery.js +4 -8
  12. package/build/lib/commands/battery.js.map +1 -1
  13. package/build/lib/commands/biometric.d.ts.map +1 -1
  14. package/build/lib/commands/biometric.js +1 -5
  15. package/build/lib/commands/biometric.js.map +1 -1
  16. package/build/lib/commands/condition.js +4 -4
  17. package/build/lib/commands/condition.js.map +1 -1
  18. package/build/lib/commands/content-size.js +1 -1
  19. package/build/lib/commands/content-size.js.map +1 -1
  20. package/build/lib/commands/find.js +2 -2
  21. package/build/lib/commands/find.js.map +1 -1
  22. package/build/lib/commands/increase-contrast.js +1 -1
  23. package/build/lib/commands/increase-contrast.js.map +1 -1
  24. package/build/lib/commands/keychains.d.ts.map +1 -1
  25. package/build/lib/commands/keychains.js +1 -5
  26. package/build/lib/commands/keychains.js.map +1 -1
  27. package/build/lib/commands/localization.d.ts.map +1 -1
  28. package/build/lib/commands/localization.js +1 -5
  29. package/build/lib/commands/localization.js.map +1 -1
  30. package/build/lib/commands/pasteboard.d.ts.map +1 -1
  31. package/build/lib/commands/pasteboard.js +10 -8
  32. package/build/lib/commands/pasteboard.js.map +1 -1
  33. package/build/lib/commands/permissions.js +1 -1
  34. package/build/lib/commands/permissions.js.map +1 -1
  35. package/build/lib/css-converter.d.ts +3 -9
  36. package/build/lib/css-converter.d.ts.map +1 -1
  37. package/build/lib/css-converter.js +41 -52
  38. package/build/lib/css-converter.js.map +1 -1
  39. package/build/lib/device/real-device-management.js +14 -14
  40. package/build/lib/device/real-device-management.js.map +1 -1
  41. package/build/lib/device/simulator-management.d.ts.map +1 -1
  42. package/build/lib/device/simulator-management.js +8 -4
  43. package/build/lib/device/simulator-management.js.map +1 -1
  44. package/build/lib/driver.d.ts.map +1 -1
  45. package/build/lib/driver.js +3 -3
  46. package/build/lib/driver.js.map +1 -1
  47. package/build/lib/logger.d.ts +1 -2
  48. package/build/lib/logger.d.ts.map +1 -1
  49. package/build/lib/logger.js +2 -2
  50. package/build/lib/logger.js.map +1 -1
  51. package/build/lib/utils.d.ts +76 -134
  52. package/build/lib/utils.d.ts.map +1 -1
  53. package/build/lib/utils.js +80 -141
  54. package/build/lib/utils.js.map +1 -1
  55. package/lib/{app-infos-cache.js → app-infos-cache.ts} +44 -46
  56. package/lib/{app-utils.js → app-utils.ts} +215 -245
  57. package/lib/commands/battery.js +3 -4
  58. package/lib/commands/biometric.js +1 -2
  59. package/lib/commands/condition.js +1 -1
  60. package/lib/commands/content-size.js +1 -1
  61. package/lib/commands/find.js +1 -1
  62. package/lib/commands/increase-contrast.js +1 -1
  63. package/lib/commands/keychains.js +1 -2
  64. package/lib/commands/localization.js +1 -2
  65. package/lib/commands/pasteboard.js +9 -8
  66. package/lib/commands/permissions.js +1 -1
  67. package/lib/{css-converter.js → css-converter.ts} +75 -88
  68. package/lib/device/real-device-management.ts +1 -1
  69. package/lib/device/simulator-management.ts +9 -4
  70. package/lib/driver.ts +6 -4
  71. package/lib/logger.ts +3 -0
  72. package/lib/{utils.js → utils.ts} +102 -139
  73. package/npm-shrinkwrap.json +38 -32
  74. package/package.json +3 -3
  75. package/lib/logger.js +0 -5
@@ -12,31 +12,31 @@ exports.buildSafariPreferences = buildSafariPreferences;
12
12
  exports.onDownloadApp = onDownloadApp;
13
13
  exports.onPostConfigureApp = onPostConfigureApp;
14
14
  const lodash_1 = __importDefault(require("lodash"));
15
- const path_1 = __importDefault(require("path"));
15
+ const node_path_1 = __importDefault(require("node:path"));
16
16
  const support_1 = require("appium/support");
17
- const logger_js_1 = __importDefault(require("./logger.js"));
17
+ const logger_1 = require("./logger");
18
18
  const node_os_1 = __importDefault(require("node:os"));
19
19
  const teen_process_1 = require("teen_process");
20
20
  const bluebird_1 = __importDefault(require("bluebird"));
21
21
  const node_child_process_1 = require("node:child_process");
22
22
  const node_assert_1 = __importDefault(require("node:assert"));
23
- const utils_js_1 = require("./utils.js");
24
- const STRINGSDICT_RESOURCE = '.stringsdict';
25
- const STRINGS_RESOURCE = '.strings';
23
+ const utils_1 = require("./utils");
26
24
  exports.SAFARI_BUNDLE_ID = 'com.apple.mobilesafari';
27
25
  exports.APP_EXT = '.app';
28
26
  exports.IPA_EXT = '.ipa';
27
+ exports.SUPPORTED_EXTENSIONS = [exports.IPA_EXT, exports.APP_EXT];
28
+ const STRINGSDICT_RESOURCE = '.stringsdict';
29
+ const STRINGS_RESOURCE = '.strings';
29
30
  const ZIP_EXT = '.zip';
30
- const SAFARI_OPTS_ALIASES_MAP = /** @type {const} */ ({
31
+ const SAFARI_OPTS_ALIASES_MAP = {
31
32
  safariAllowPopups: [
32
33
  ['WebKitJavaScriptCanOpenWindowsAutomatically', 'JavaScriptCanOpenWindowsAutomatically'],
33
34
  (x) => Number(Boolean(x)),
34
35
  ],
35
36
  safariIgnoreFraudWarning: [['WarnAboutFraudulentWebsites'], (x) => Number(!x)],
36
37
  safariOpenLinksInBackground: [['OpenLinksInBackground'], (x) => Number(Boolean(x))],
37
- });
38
+ };
38
39
  const MAX_ARCHIVE_SCAN_DEPTH = 1;
39
- exports.SUPPORTED_EXTENSIONS = [exports.IPA_EXT, exports.APP_EXT];
40
40
  const MACOS_RESOURCE_FOLDER = '__MACOSX';
41
41
  const SANITIZE_REPLACEMENT = '-';
42
42
  const INTEL_ARCH = 'x86_64';
@@ -44,9 +44,7 @@ const INTEL_ARCH = 'x86_64';
44
44
  * Verify whether the given application is compatible to the
45
45
  * platform where it is going to be installed and tested.
46
46
  *
47
- * @this {XCUITestDriver}
48
- * @returns {Promise<void>}
49
- * @throws {Error} If bundle architecture does not match the expected device architecture.
47
+ * @throws If bundle architecture does not match the expected device architecture.
50
48
  */
51
49
  async function verifyApplicationPlatform() {
52
50
  this.log.debug('Verifying application platform');
@@ -54,7 +52,7 @@ async function verifyApplicationPlatform() {
54
52
  return;
55
53
  }
56
54
  const supportedPlatforms = await this.appInfosCache.extractAppPlatforms(this.opts.app);
57
- const isTvOS = (0, utils_js_1.isTvOs)(this.opts.platformName);
55
+ const isTvOS = (0, utils_1.isTvOs)(this.opts.platformName);
58
56
  const prefix = isTvOS ? 'AppleTV' : 'iPhone';
59
57
  const suffix = this.isSimulator() ? 'Simulator' : 'OS';
60
58
  const dstPlatform = `${prefix}${suffix}`;
@@ -65,7 +63,7 @@ async function verifyApplicationPlatform() {
65
63
  if (this.isRealDevice()) {
66
64
  return;
67
65
  }
68
- const executablePath = path_1.default.resolve(this.opts.app, await this.appInfosCache.extractExecutableName(this.opts.app));
66
+ const executablePath = node_path_1.default.resolve(this.opts.app, await this.appInfosCache.extractExecutableName(this.opts.app));
69
67
  const [resFile, resUname] = await bluebird_1.default.all([
70
68
  (0, teen_process_1.exec)('lipo', ['-info', executablePath]),
71
69
  (0, teen_process_1.exec)('uname', ['-m']),
@@ -98,33 +96,8 @@ async function verifyApplicationPlatform() {
98
96
  throw new Error(`The ${this.opts.bundleId} application does not support the ${processArch} Simulator ` +
99
97
  `architecture:\n${bundleExecutableInfo}\n\n${advice}`);
100
98
  }
101
- /**
102
- *
103
- * @param {string} resourcePath
104
- * @returns {Promise<import('@appium/types').StringRecord>}
105
- */
106
- async function readResource(resourcePath) {
107
- const data = await support_1.plist.parsePlistFile(resourcePath);
108
- const result = {};
109
- for (const [key, value] of lodash_1.default.toPairs(data)) {
110
- result[key] = lodash_1.default.isString(value) ? value : JSON.stringify(value);
111
- }
112
- return result;
113
- }
114
- /**
115
- * @typedef {Object} LocalizableStringsOptions
116
- * @property {string} [app]
117
- * @property {string} [language='en']
118
- * @property {string} [localizableStringsDir]
119
- * @property {string} [stringFile]
120
- * @property {boolean} [strictMode]
121
- */
122
99
  /**
123
100
  * Extracts string resources from an app
124
- *
125
- * @this {XCUITestDriver}
126
- * @param {LocalizableStringsOptions} opts
127
- * @returns {Promise<import('@appium/types').StringRecord>}
128
101
  */
129
102
  async function parseLocalizableStrings(opts = {}) {
130
103
  const { app, language = 'en', localizableStringsDir, stringFile, strictMode } = opts;
@@ -144,14 +117,13 @@ async function parseLocalizableStrings(opts = {}) {
144
117
  tmpRoot = await support_1.tempDir.openDir();
145
118
  this.log.info(`Extracting '${app}' into a temporary location to parse its resources`);
146
119
  await support_1.zip.extractAllTo(app, tmpRoot);
147
- const relativeBundleRoot = /** @type {string} */ (lodash_1.default.first(await findApps(tmpRoot, [exports.APP_EXT])));
120
+ const relativeBundleRoot = lodash_1.default.first(await findApps(tmpRoot, [exports.APP_EXT]));
148
121
  this.log.info(`Selecting '${relativeBundleRoot}'`);
149
- bundleRoot = path_1.default.join(tmpRoot, relativeBundleRoot);
122
+ bundleRoot = node_path_1.default.join(tmpRoot, relativeBundleRoot);
150
123
  }
151
- /** @type {string|undefined} */
152
124
  let lprojRoot;
153
125
  for (const subfolder of [`${language}.lproj`, localizableStringsDir, ''].filter(lodash_1.default.isString)) {
154
- lprojRoot = path_1.default.resolve(bundleRoot, /** @type {string} */ (subfolder));
126
+ lprojRoot = node_path_1.default.resolve(bundleRoot, subfolder);
155
127
  if (await support_1.fs.exists(lprojRoot)) {
156
128
  break;
157
129
  }
@@ -167,7 +139,7 @@ async function parseLocalizableStrings(opts = {}) {
167
139
  this.log.info(`Retrieving resource strings from '${lprojRoot}'`);
168
140
  const resourcePaths = [];
169
141
  if (stringFile) {
170
- const dstPath = path_1.default.resolve(/** @type {string} */ (lprojRoot), stringFile);
142
+ const dstPath = node_path_1.default.resolve(lprojRoot, stringFile);
171
143
  if (await support_1.fs.exists(dstPath)) {
172
144
  resourcePaths.push(dstPath);
173
145
  }
@@ -179,10 +151,10 @@ async function parseLocalizableStrings(opts = {}) {
179
151
  this.log.info(message);
180
152
  }
181
153
  }
182
- if (lodash_1.default.isEmpty(resourcePaths) && (await support_1.fs.exists(lprojRoot))) {
154
+ if (lodash_1.default.isEmpty(resourcePaths) && lprojRoot && (await support_1.fs.exists(lprojRoot))) {
183
155
  const resourceFiles = (await support_1.fs.readdir(lprojRoot))
184
156
  .filter((name) => lodash_1.default.some([STRINGS_RESOURCE, STRINGSDICT_RESOURCE], (x) => name.endsWith(x)))
185
- .map((name) => path_1.default.resolve(lprojRoot, name));
157
+ .map((name) => node_path_1.default.resolve(lprojRoot, name));
186
158
  resourcePaths.push(...resourceFiles);
187
159
  }
188
160
  this.log.info(`Got ${support_1.util.pluralize('resource file', resourcePaths.length, true)} in '${lprojRoot}'`);
@@ -190,7 +162,7 @@ async function parseLocalizableStrings(opts = {}) {
190
162
  return {};
191
163
  }
192
164
  const resultStrings = {};
193
- const toAbsolutePath = (/** @type {string} */ p) => path_1.default.isAbsolute(p) ? p : path_1.default.resolve(process.cwd(), p);
165
+ const toAbsolutePath = (p) => node_path_1.default.isAbsolute(p) ? p : node_path_1.default.resolve(process.cwd(), p);
194
166
  for (const resourcePath of resourcePaths) {
195
167
  if (!support_1.util.isSubPath(toAbsolutePath(resourcePath), toAbsolutePath(bundleRoot))) {
196
168
  // security precaution
@@ -214,36 +186,11 @@ async function parseLocalizableStrings(opts = {}) {
214
186
  }
215
187
  }
216
188
  }
217
- /**
218
- * Check whether the given path on the file system points to the .app bundle root
219
- *
220
- * @param {string} appPath Possible .app bundle root
221
- * @returns {Promise<boolean>} Whether the given path points to an .app bundle
222
- */
223
- async function isAppBundle(appPath) {
224
- return (lodash_1.default.endsWith(lodash_1.default.toLower(appPath), exports.APP_EXT) &&
225
- (await support_1.fs.stat(appPath)).isDirectory() &&
226
- (await support_1.fs.exists(path_1.default.join(appPath, 'Info.plist'))));
227
- }
228
- /**
229
- * Check whether the given path on the file system points to the .ipa file
230
- *
231
- * @param {string} appPath Possible .ipa file
232
- * @returns {Promise<boolean>} Whether the given path points to an .ipa bundle
233
- */
234
- async function isIpaBundle(appPath) {
235
- return lodash_1.default.endsWith(lodash_1.default.toLower(appPath), exports.IPA_EXT) && (await support_1.fs.stat(appPath)).isFile();
236
- }
237
- /**
238
- * @typedef {Object} UnzipInfo
239
- * @property {string} rootDir
240
- * @property {number} archiveSize
241
- */
242
189
  /**
243
190
  * Unzips a ZIP archive on the local file system.
244
191
  *
245
- * @param {string} archivePath Full path to a .zip archive
246
- * @returns {Promise<UnzipInfo>} temporary folder root where the archive has been extracted
192
+ * @param archivePath Full path to a .zip archive
193
+ * @returns temporary folder root where the archive has been extracted
247
194
  */
248
195
  async function unzipFile(archivePath) {
249
196
  const useSystemUnzipEnv = process.env.APPIUM_PREFER_SYSTEM_UNZIP;
@@ -270,9 +217,6 @@ async function unzipFile(archivePath) {
270
217
  * Uses bdstar tool for this purpose.
271
218
  * This allows to optimize the time needed to prepare the app under test
272
219
  * to MAX(download, unzip) instead of SUM(download, unzip)
273
- *
274
- * @param {import('node:stream').Readable} zipStream
275
- * @returns {Promise<UnzipInfo>}
276
220
  */
277
221
  async function unzipStream(zipStream) {
278
222
  const tmpRoot = await support_1.tempDir.openDir();
@@ -288,7 +232,7 @@ async function unzipStream(zipStream) {
288
232
  bsdtarProcess.stderr.on('data', (chunk) => {
289
233
  const stderr = chunk.toString();
290
234
  if (lodash_1.default.trim(stderr)) {
291
- logger_js_1.default.warn(stderr);
235
+ logger_1.log.warn(stderr);
292
236
  }
293
237
  });
294
238
  zipStream.on('data', (chunk) => {
@@ -300,9 +244,9 @@ async function unzipStream(zipStream) {
300
244
  zipStream.once('error', reject);
301
245
  bsdtarProcess.once('exit', (code, signal) => {
302
246
  zipStream.unpipe(bsdtarProcess.stdin);
303
- logger_js_1.default.debug(`bsdtar process exited with code ${code}, signal ${signal}`);
247
+ logger_1.log.debug(`bsdtar process exited with code ${code}, signal ${signal}`);
304
248
  if (code === 0) {
305
- resolve();
249
+ resolve(undefined);
306
250
  }
307
251
  else {
308
252
  reject(new Error('Is it a valid ZIP archive?'));
@@ -329,18 +273,128 @@ async function unzipStream(zipStream) {
329
273
  };
330
274
  }
331
275
  /**
332
- * Used to parse the file name value from response headers
276
+ * Builds Safari preferences object based on the given session capabilities
277
+ *
278
+ * @param opts
279
+ * @return
280
+ */
281
+ function buildSafariPreferences(opts) {
282
+ const safariSettings = lodash_1.default.cloneDeep(opts?.safariGlobalPreferences ?? {});
283
+ for (const [name, [aliases, valueConverter]] of lodash_1.default.toPairs(SAFARI_OPTS_ALIASES_MAP)) {
284
+ if (!lodash_1.default.has(opts, name)) {
285
+ continue;
286
+ }
287
+ for (const alias of aliases) {
288
+ safariSettings[alias] = valueConverter(opts[name]);
289
+ }
290
+ }
291
+ return safariSettings;
292
+ }
293
+ /**
294
+ * The callback invoked by configureApp helper
295
+ * when it is necessary to download the remote application.
296
+ * We assume the remote file could be anythingm, but only
297
+ * .zip and .ipa formats are supported.
298
+ * A .zip archive can contain one or more
299
+ */
300
+ async function onDownloadApp(opts) {
301
+ return this.isRealDevice()
302
+ ? await downloadIpa.bind(this)(opts.stream, opts.headers)
303
+ : await unzipApp.bind(this)(opts.stream);
304
+ }
305
+ async function onPostConfigureApp(opts) {
306
+ // Pick the previously cached entry if its integrity has been preserved
307
+ const appInfo = lodash_1.default.isPlainObject(opts.cachedAppInfo) ? opts.cachedAppInfo : undefined;
308
+ const cachedPath = appInfo ? appInfo.fullPath : undefined;
309
+ const shouldUseCachedApp = async () => {
310
+ if (!appInfo || !cachedPath || !await support_1.fs.exists(cachedPath)) {
311
+ return false;
312
+ }
313
+ const isCachedPathAFile = (await support_1.fs.stat(cachedPath)).isFile();
314
+ if (isCachedPathAFile) {
315
+ return await support_1.fs.hash(cachedPath) === appInfo.integrity?.file;
316
+ }
317
+ // If the cached path is a folder then it is expected to be previously extracted from
318
+ // an archive located under appPath whose hash is stored as `cachedAppInfo.packageHash`
319
+ if (!isCachedPathAFile
320
+ && opts.cachedAppInfo?.packageHash
321
+ && opts.appPath
322
+ && await support_1.fs.exists(opts.appPath)
323
+ && (await support_1.fs.stat(opts.appPath)).isFile()
324
+ && opts.cachedAppInfo.packageHash === await support_1.fs.hash(opts.appPath)) {
325
+ const nestedItemsCountInCache = appInfo.integrity?.folder;
326
+ if (nestedItemsCountInCache !== undefined) {
327
+ return (await support_1.fs.glob('**/*', { cwd: cachedPath })).length >= nestedItemsCountInCache;
328
+ }
329
+ }
330
+ return false;
331
+ };
332
+ if (await shouldUseCachedApp()) {
333
+ if (!cachedPath) {
334
+ return false;
335
+ }
336
+ this.log.info(`Using '${cachedPath}' which was cached from '${opts.appPath || 'unknown'}'`);
337
+ return { appPath: cachedPath };
338
+ }
339
+ if (!opts.appPath) {
340
+ return false;
341
+ }
342
+ const isLocalIpa = await isIpaBundle(opts.appPath);
343
+ const isLocalApp = !isLocalIpa && await isAppBundle(opts.appPath);
344
+ const isPackageReadyForInstall = isLocalApp || (this.isRealDevice() && isLocalIpa);
345
+ if (isPackageReadyForInstall) {
346
+ await this.appInfosCache.put(opts.appPath);
347
+ }
348
+ // Only local .app bundles (real device/Simulator)
349
+ // and .ipa packages for real devices should not be cached
350
+ if (!opts.isUrl && isPackageReadyForInstall) {
351
+ return false;
352
+ }
353
+ // Cache the app while unpacking the bundle if necessary
354
+ return {
355
+ appPath: isPackageReadyForInstall
356
+ ? opts.appPath
357
+ : await unzipApp.bind(this)(opts.appPath)
358
+ };
359
+ }
360
+ // Private functions
361
+ async function readResource(resourcePath) {
362
+ const data = await support_1.plist.parsePlistFile(resourcePath);
363
+ return lodash_1.default.toPairs(data).reduce((result, [key, value]) => {
364
+ result[key] = lodash_1.default.isString(value) ? value : JSON.stringify(value);
365
+ return result;
366
+ }, {});
367
+ }
368
+ /**
369
+ * Check whether the given path on the file system points to the .app bundle root
333
370
  *
334
- * @param {import('@appium/types').HTTPHeaders} headers
335
- * @returns {string?}
371
+ * @param appPath Possible .app bundle root
372
+ * @returns Whether the given path points to an .app bundle
373
+ */
374
+ async function isAppBundle(appPath) {
375
+ return (lodash_1.default.endsWith(lodash_1.default.toLower(appPath), exports.APP_EXT) &&
376
+ (await support_1.fs.stat(appPath)).isDirectory() &&
377
+ (await support_1.fs.exists(node_path_1.default.join(appPath, 'Info.plist'))));
378
+ }
379
+ /**
380
+ * Check whether the given path on the file system points to the .ipa file
381
+ *
382
+ * @param appPath Possible .ipa file
383
+ * @returns Whether the given path points to an .ipa bundle
384
+ */
385
+ async function isIpaBundle(appPath) {
386
+ return lodash_1.default.endsWith(lodash_1.default.toLower(appPath), exports.IPA_EXT) && (await support_1.fs.stat(appPath)).isFile();
387
+ }
388
+ /**
389
+ * Used to parse the file name value from response headers
336
390
  */
337
391
  function parseFileName(headers) {
338
392
  const contentDisposition = headers['content-disposition'];
339
393
  if (!lodash_1.default.isString(contentDisposition)) {
340
394
  return null;
341
395
  }
342
- if (/^attachment/i.test(/** @type {string} */ (contentDisposition))) {
343
- const match = /filename="([^"]+)/i.exec(/** @type {string} */ (contentDisposition));
396
+ if (/^attachment/i.test(contentDisposition)) {
397
+ const match = /filename="([^"]+)/i.exec(contentDisposition);
344
398
  if (match) {
345
399
  return support_1.fs.sanitizeName(match[1], { replacement: SANITIZE_REPLACEMENT });
346
400
  }
@@ -349,15 +403,10 @@ function parseFileName(headers) {
349
403
  }
350
404
  /**
351
405
  * Downloads and verifies remote applications for real devices
352
- *
353
- * @this {XCUITestDriver}
354
- * @param {import('node:stream').Readable} stream
355
- * @param {import('@appium/types').HTTPHeaders} headers
356
- * @returns {Promise<string>}
357
406
  */
358
407
  async function downloadIpa(stream, headers) {
359
408
  const timer = new support_1.timing.Timer().start();
360
- const logPerformance = (/** @type {string} */ dstPath, /** @type {number} */ fileSize, /** @type {string} */ action) => {
409
+ const logPerformance = (dstPath, fileSize, action) => {
361
410
  const secondsElapsed = timer.getDuration().asSeconds;
362
411
  this.log.info(`The remote file (${support_1.util.toReadableSizeString(fileSize)}) ` +
363
412
  `has been ${action} to '${dstPath}' in ${secondsElapsed.toFixed(3)}s`);
@@ -375,7 +424,7 @@ async function downloadIpa(stream, headers) {
375
424
  const matchedPaths = await findApps(rootDir, [exports.IPA_EXT]);
376
425
  if (!lodash_1.default.isEmpty(matchedPaths)) {
377
426
  this.log.debug(`Found ${support_1.util.pluralize(`${exports.IPA_EXT} application`, matchedPaths.length, true)} in ` +
378
- `'${path_1.default.basename(rootDir)}': ${matchedPaths}`);
427
+ `'${node_path_1.default.basename(rootDir)}': ${matchedPaths}`);
379
428
  }
380
429
  for (const matchedPath of matchedPaths) {
381
430
  try {
@@ -386,7 +435,7 @@ async function downloadIpa(stream, headers) {
386
435
  continue;
387
436
  }
388
437
  this.log.debug(`Selecting the application at '${matchedPath}'`);
389
- const isolatedPath = path_1.default.join(await support_1.tempDir.openDir(), path_1.default.basename(matchedPath));
438
+ const isolatedPath = node_path_1.default.join(await support_1.tempDir.openDir(), node_path_1.default.basename(matchedPath));
390
439
  await support_1.fs.mv(matchedPath, isolatedPath);
391
440
  return isolatedPath;
392
441
  }
@@ -429,26 +478,26 @@ async function downloadIpa(stream, headers) {
429
478
  /**
430
479
  * Looks for items with given extensions in the given folder
431
480
  *
432
- * @param {string} appPath Full path to an app bundle
433
- * @param {Array<string>} appExtensions List of matching item extensions
434
- * @returns {Promise<string[]>} List of relative paths to matched items
481
+ * @param appPath Full path to an app bundle
482
+ * @param appExtensions List of matching item extensions
483
+ * @returns List of relative paths to matched items
435
484
  */
436
485
  async function findApps(appPath, appExtensions) {
437
486
  const globPattern = `**/*.+(${appExtensions.map((ext) => ext.replace(/^\./, '')).join('|')})`;
438
487
  const sortedBundleItems = (await support_1.fs.glob(globPattern, {
439
488
  cwd: appPath,
440
- })).sort((a, b) => a.split(path_1.default.sep).length - b.split(path_1.default.sep).length);
489
+ })).sort((a, b) => a.split(node_path_1.default.sep).length - b.split(node_path_1.default.sep).length);
441
490
  return sortedBundleItems;
442
491
  }
443
492
  /**
444
493
  * Moves the application bundle to a newly created temporary folder
445
494
  *
446
- * @param {string} appPath Full path to the .app or .ipa bundle
447
- * @returns {Promise<string>} The new path to the app bundle.
495
+ * @param appPath Full path to the .app or .ipa bundle
496
+ * @returns The new path to the app bundle.
448
497
  * The name of the app bundle remains the same
449
498
  */
450
499
  async function isolateApp(appPath) {
451
- const appFileName = path_1.default.basename(appPath);
500
+ const appFileName = node_path_1.default.basename(appPath);
452
501
  if ((await support_1.fs.stat(appPath)).isFile()) {
453
502
  const isolatedPath = await support_1.tempDir.path({
454
503
  prefix: appFileName,
@@ -458,36 +507,17 @@ async function isolateApp(appPath) {
458
507
  return isolatedPath;
459
508
  }
460
509
  const tmpRoot = await support_1.tempDir.openDir();
461
- const isolatedRoot = path_1.default.join(tmpRoot, appFileName);
510
+ const isolatedRoot = node_path_1.default.join(tmpRoot, appFileName);
462
511
  await support_1.fs.mv(appPath, isolatedRoot, { mkdirp: true });
463
512
  return isolatedRoot;
464
513
  }
465
- /**
466
- * Builds Safari preferences object based on the given session capabilities
467
- *
468
- * @param {import('./driver').XCUITestDriverOpts} opts
469
- * @return {import('@appium/types').StringRecord}
470
- */
471
- function buildSafariPreferences(opts) {
472
- const safariSettings = lodash_1.default.cloneDeep(opts?.safariGlobalPreferences ?? {});
473
- for (const [name, [aliases, valueConverter]] of lodash_1.default.toPairs(SAFARI_OPTS_ALIASES_MAP)) {
474
- if (!lodash_1.default.has(opts, name)) {
475
- continue;
476
- }
477
- for (const alias of aliases) {
478
- safariSettings[alias] = valueConverter(opts[name]);
479
- }
480
- }
481
- return safariSettings;
482
- }
483
514
  /**
484
515
  * Unzip the given archive and find a matching .app bundle in it
485
516
  *
486
- * @this {XCUITestDriver}
487
- * @param {string|import('node:stream').Readable} appPathOrZipStream The path to the archive.
488
- * @param {number} depth [0] the current nesting depth. App bundles whose nesting level
517
+ * @param appPathOrZipStream The path to the archive.
518
+ * @param depth [0] the current nesting depth. App bundles whose nesting level
489
519
  * is greater than 1 are not supported.
490
- * @returns {Promise<string>} Full path to the first matching .app bundle..
520
+ * @returns Full path to the first matching .app bundle..
491
521
  * @throws If no matching .app bundles were found in the provided archive.
492
522
  */
493
523
  async function unzipApp(appPathOrZipStream, depth = 0) {
@@ -498,20 +528,17 @@ async function unzipApp(appPathOrZipStream, depth = 0) {
498
528
  throw new Error(errMsg);
499
529
  }
500
530
  const timer = new support_1.timing.Timer().start();
501
- /** @type {string} */
502
531
  let rootDir;
503
- /** @type {number} */
504
532
  let archiveSize;
505
533
  try {
506
534
  if (lodash_1.default.isString(appPathOrZipStream)) {
507
- ({ rootDir, archiveSize } = await unzipFile(/** @type {string} */ (appPathOrZipStream)));
535
+ ({ rootDir, archiveSize } = await unzipFile(appPathOrZipStream));
508
536
  }
509
537
  else {
510
538
  if (depth > 0) {
511
539
  node_assert_1.default.fail('Streaming unzip cannot be invoked for nested archive items');
512
540
  }
513
- ({ rootDir, archiveSize } = await unzipStream(
514
- /** @type {import('node:stream').Readable} */ (appPathOrZipStream)));
541
+ ({ rootDir, archiveSize } = await unzipStream(appPathOrZipStream));
515
542
  }
516
543
  }
517
544
  catch (e) {
@@ -527,7 +554,7 @@ async function unzipApp(appPathOrZipStream, depth = 0) {
527
554
  const bytesPerSec = Math.floor(archiveSize / secondsElapsed);
528
555
  this.log.debug(`Approximate decompression speed: ${support_1.util.toReadableSizeString(bytesPerSec)}/s`);
529
556
  }
530
- const isCompatibleWithCurrentPlatform = async (/** @type {string} */ appPath) => {
557
+ const isCompatibleWithCurrentPlatform = async (appPath) => {
531
558
  let platforms;
532
559
  try {
533
560
  platforms = await this.appInfosCache.extractAppPlatforms(appPath);
@@ -550,15 +577,15 @@ async function unzipApp(appPathOrZipStream, depth = 0) {
550
577
  };
551
578
  const matchedPaths = await findApps(rootDir, exports.SUPPORTED_EXTENSIONS);
552
579
  if (lodash_1.default.isEmpty(matchedPaths)) {
553
- this.log.debug(`'${path_1.default.basename(rootDir)}' has no bundles`);
580
+ this.log.debug(`'${node_path_1.default.basename(rootDir)}' has no bundles`);
554
581
  }
555
582
  else {
556
583
  this.log.debug(`Found ${support_1.util.pluralize('bundle', matchedPaths.length, true)} in ` +
557
- `'${path_1.default.basename(rootDir)}': ${matchedPaths}`);
584
+ `'${node_path_1.default.basename(rootDir)}': ${matchedPaths}`);
558
585
  }
559
586
  try {
560
587
  for (const matchedPath of matchedPaths) {
561
- const fullPath = path_1.default.join(rootDir, matchedPath);
588
+ const fullPath = node_path_1.default.join(rootDir, matchedPath);
562
589
  if ((await isAppBundle(fullPath) || (this.isRealDevice() && await isIpaBundle(fullPath)))
563
590
  && await isCompatibleWithCurrentPlatform(fullPath)) {
564
591
  this.log.debug(`Selecting the application at '${matchedPath}'`);
@@ -571,90 +598,10 @@ async function unzipApp(appPathOrZipStream, depth = 0) {
571
598
  }
572
599
  throw new Error(errMsg);
573
600
  }
574
- /**
575
- * The callback invoked by configureApp helper
576
- * when it is necessary to download the remote application.
577
- * We assume the remote file could be anythingm, but only
578
- * .zip and .ipa formats are supported.
579
- * A .zip archive can contain one or more
580
- *
581
- * @this {XCUITestDriver}
582
- * @param {import('@appium/types').DownloadAppOptions} opts
583
- * @returns {Promise<string>}
584
- */
585
- async function onDownloadApp({ stream, headers }) {
586
- return this.isRealDevice()
587
- ? await downloadIpa.bind(this)(stream, headers)
588
- : await unzipApp.bind(this)(stream);
589
- }
590
- /**
591
- * @this {XCUITestDriver}
592
- * @param {import('@appium/types').PostProcessOptions} opts
593
- * @returns {Promise<import('@appium/types').PostProcessResult|false>}
594
- */
595
- async function onPostConfigureApp({ cachedAppInfo, isUrl, appPath }) {
596
- // Pick the previously cached entry if its integrity has been preserved
597
- /** @type {import('@appium/types').CachedAppInfo|undefined} */
598
- const appInfo = lodash_1.default.isPlainObject(cachedAppInfo) ? cachedAppInfo : undefined;
599
- const cachedPath = appInfo ? /** @type {string} */ (appInfo.fullPath) : undefined;
600
- const shouldUseCachedApp = async () => {
601
- if (!appInfo || !cachedPath || !await support_1.fs.exists(cachedPath)) {
602
- return false;
603
- }
604
- const isCachedPathAFile = (await support_1.fs.stat(cachedPath)).isFile();
605
- if (isCachedPathAFile) {
606
- return await support_1.fs.hash(cachedPath) === /** @type {any} */ (appInfo.integrity)?.file;
607
- }
608
- // If the cached path is a folder then it is expected to be previously extracted from
609
- // an archive located under appPath whose hash is stored as `cachedAppInfo.packageHash`
610
- if (!isCachedPathAFile
611
- && cachedAppInfo?.packageHash
612
- && await support_1.fs.exists(/** @type {string} */ (appPath))
613
- && (await support_1.fs.stat(/** @type {string} */ (appPath))).isFile()
614
- && cachedAppInfo.packageHash === await support_1.fs.hash(/** @type {string} */ (appPath))) {
615
- /** @type {number|undefined} */
616
- const nestedItemsCountInCache = /** @type {any} */ (appInfo.integrity)?.folder;
617
- if (nestedItemsCountInCache !== undefined) {
618
- return (await support_1.fs.glob('**/*', { cwd: cachedPath })).length >= nestedItemsCountInCache;
619
- }
620
- }
621
- return false;
622
- };
623
- if (await shouldUseCachedApp()) {
624
- this.log.info(`Using '${cachedPath}' which was cached from '${appPath}'`);
625
- return { appPath: /** @type {string} */ (cachedPath) };
626
- }
627
- const isLocalIpa = await isIpaBundle(/** @type {string} */ (appPath));
628
- const isLocalApp = !isLocalIpa && await isAppBundle(/** @type {string} */ (appPath));
629
- const isPackageReadyForInstall = isLocalApp || (this.isRealDevice() && isLocalIpa);
630
- if (isPackageReadyForInstall) {
631
- await this.appInfosCache.put(/** @type {string} */ (appPath));
632
- }
633
- // Only local .app bundles (real device/Simulator)
634
- // and .ipa packages for real devices should not be cached
635
- if (!isUrl && isPackageReadyForInstall) {
636
- return false;
637
- }
638
- // Cache the app while unpacking the bundle if necessary
639
- return {
640
- appPath: isPackageReadyForInstall
641
- ? appPath
642
- : await unzipApp.bind(this)(/** @type {string} */ (appPath))
643
- };
644
- }
645
- /**
646
- * @returns {Promise<boolean>}
647
- */
648
601
  async function isRosettaInstalled() {
649
602
  return await support_1.fs.exists('/Library/Apple/usr/share/rosetta/rosetta');
650
603
  }
651
- /**
652
- * @returns {boolean}
653
- */
654
604
  function isAppleSilicon() {
655
605
  return node_os_1.default.cpus()[0].model.includes('Apple');
656
606
  }
657
- /**
658
- * @typedef {import('./driver').XCUITestDriver} XCUITestDriver
659
- */
660
607
  //# sourceMappingURL=app-utils.js.map