appium-adb 14.1.0 → 14.1.2

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 (84) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/build/lib/adb.d.ts.map +1 -1
  3. package/build/lib/adb.js.map +1 -1
  4. package/build/lib/helpers.d.ts.map +1 -1
  5. package/build/lib/helpers.js +3 -3
  6. package/build/lib/helpers.js.map +1 -1
  7. package/build/lib/logcat.d.ts.map +1 -1
  8. package/build/lib/logcat.js +16 -6
  9. package/build/lib/logcat.js.map +1 -1
  10. package/build/lib/logger.js.map +1 -1
  11. package/build/lib/tools/aab-utils.d.ts.map +1 -1
  12. package/build/lib/tools/aab-utils.js +25 -18
  13. package/build/lib/tools/aab-utils.js.map +1 -1
  14. package/build/lib/tools/android-manifest.d.ts.map +1 -1
  15. package/build/lib/tools/android-manifest.js +36 -24
  16. package/build/lib/tools/android-manifest.js.map +1 -1
  17. package/build/lib/tools/apk-signing.d.ts.map +1 -1
  18. package/build/lib/tools/apk-signing.js +58 -37
  19. package/build/lib/tools/apk-signing.js.map +1 -1
  20. package/build/lib/tools/apk-utils.d.ts.map +1 -1
  21. package/build/lib/tools/apk-utils.js +46 -38
  22. package/build/lib/tools/apk-utils.js.map +1 -1
  23. package/build/lib/tools/apks-utils.d.ts.map +1 -1
  24. package/build/lib/tools/apks-utils.js +21 -14
  25. package/build/lib/tools/apks-utils.js.map +1 -1
  26. package/build/lib/tools/app-commands.d.ts.map +1 -1
  27. package/build/lib/tools/app-commands.js +44 -45
  28. package/build/lib/tools/app-commands.js.map +1 -1
  29. package/build/lib/tools/device-settings.d.ts.map +1 -1
  30. package/build/lib/tools/device-settings.js +38 -32
  31. package/build/lib/tools/device-settings.js.map +1 -1
  32. package/build/lib/tools/emu-constants.d.ts.map +1 -1
  33. package/build/lib/tools/emu-constants.js +4 -4
  34. package/build/lib/tools/emu-constants.js.map +1 -1
  35. package/build/lib/tools/emulator-commands.d.ts.map +1 -1
  36. package/build/lib/tools/emulator-commands.js +20 -17
  37. package/build/lib/tools/emulator-commands.js.map +1 -1
  38. package/build/lib/tools/fs-commands.d.ts.map +1 -1
  39. package/build/lib/tools/fs-commands.js +2 -1
  40. package/build/lib/tools/fs-commands.js.map +1 -1
  41. package/build/lib/tools/general-commands.d.ts.map +1 -1
  42. package/build/lib/tools/general-commands.js +11 -15
  43. package/build/lib/tools/general-commands.js.map +1 -1
  44. package/build/lib/tools/keyboard-commands.d.ts.map +1 -1
  45. package/build/lib/tools/keyboard-commands.js.map +1 -1
  46. package/build/lib/tools/lockmgmt.d.ts.map +1 -1
  47. package/build/lib/tools/lockmgmt.js +24 -23
  48. package/build/lib/tools/lockmgmt.js.map +1 -1
  49. package/build/lib/tools/logcat-commands.d.ts.map +1 -1
  50. package/build/lib/tools/logcat-commands.js.map +1 -1
  51. package/build/lib/tools/network-commands.d.ts.map +1 -1
  52. package/build/lib/tools/network-commands.js +0 -1
  53. package/build/lib/tools/network-commands.js.map +1 -1
  54. package/build/lib/tools/process-commands.d.ts.map +1 -1
  55. package/build/lib/tools/process-commands.js +1 -1
  56. package/build/lib/tools/process-commands.js.map +1 -1
  57. package/build/lib/tools/system-calls.d.ts.map +1 -1
  58. package/build/lib/tools/system-calls.js +30 -36
  59. package/build/lib/tools/system-calls.js.map +1 -1
  60. package/build/lib/tools/types.d.ts +6 -6
  61. package/build/lib/tools/types.d.ts.map +1 -1
  62. package/lib/adb.ts +20 -25
  63. package/lib/helpers.ts +48 -36
  64. package/lib/logcat.ts +32 -22
  65. package/lib/logger.ts +1 -2
  66. package/lib/tools/aab-utils.js +42 -33
  67. package/lib/tools/android-manifest.js +78 -54
  68. package/lib/tools/apk-signing.ts +123 -89
  69. package/lib/tools/apk-utils.ts +206 -109
  70. package/lib/tools/apks-utils.js +50 -40
  71. package/lib/tools/app-commands.ts +220 -135
  72. package/lib/tools/device-settings.ts +154 -124
  73. package/lib/tools/emu-constants.ts +4 -5
  74. package/lib/tools/emulator-commands.ts +113 -65
  75. package/lib/tools/fs-commands.js +9 -8
  76. package/lib/tools/general-commands.js +48 -51
  77. package/lib/tools/keyboard-commands.js +22 -20
  78. package/lib/tools/lockmgmt.js +80 -51
  79. package/lib/tools/logcat-commands.js +6 -6
  80. package/lib/tools/network-commands.js +11 -12
  81. package/lib/tools/process-commands.ts +28 -15
  82. package/lib/tools/system-calls.ts +319 -221
  83. package/lib/tools/types.ts +19 -10
  84. package/package.json +4 -3
@@ -1,17 +1,19 @@
1
1
  import {
2
- APKS_EXTENSION, buildInstallArgs,
3
- APK_INSTALL_TIMEOUT, DEFAULT_ADB_EXEC_TIMEOUT,
4
- readPackageManifest
2
+ APKS_EXTENSION,
3
+ buildInstallArgs,
4
+ APK_INSTALL_TIMEOUT,
5
+ DEFAULT_ADB_EXEC_TIMEOUT,
6
+ readPackageManifest,
5
7
  } from '../helpers.js';
6
- import { exec, type ExecError } from 'teen_process';
7
- import { log } from '../logger.js';
8
+ import {exec, type ExecError} from 'teen_process';
9
+ import {log} from '../logger.js';
8
10
  import path from 'path';
9
11
  import _ from 'lodash';
10
- import { fs, util, mkdirp, timing } from '@appium/support';
12
+ import {fs, util, mkdirp, timing} from '@appium/support';
11
13
  import * as semver from 'semver';
12
14
  import os from 'os';
13
- import { LRUCache } from 'lru-cache';
14
- import type { ADB } from '../adb.js';
15
+ import {LRUCache} from 'lru-cache';
16
+ import type {ADB} from '../adb.js';
15
17
  import type {
16
18
  UninstallOptions,
17
19
  ShellExecOptions,
@@ -22,7 +24,7 @@ import type {
22
24
  ApkStrings,
23
25
  AppInfo,
24
26
  InstallState,
25
- StringRecord
27
+ StringRecord,
26
28
  } from './types.js';
27
29
 
28
30
  export const REMOTE_CACHE_ROOT = '/data/local/tmp/appium_cache';
@@ -35,9 +37,13 @@ export const REMOTE_CACHE_ROOT = '/data/local/tmp/appium_cache';
35
37
  * @returns True if the package was found on the device and
36
38
  * successfully uninstalled.
37
39
  */
38
- export async function uninstallApk (this: ADB, pkg: string, options: UninstallOptions = {}): Promise<boolean> {
40
+ export async function uninstallApk(
41
+ this: ADB,
42
+ pkg: string,
43
+ options: UninstallOptions = {},
44
+ ): Promise<boolean> {
39
45
  log.debug(`Uninstalling ${pkg}`);
40
- if (!options.skipInstallCheck && !await this.isAppInstalled(pkg)) {
46
+ if (!options.skipInstallCheck && !(await this.isAppInstalled(pkg))) {
41
47
  log.info(`${pkg} was not uninstalled, because it was not present on the device`);
42
48
  return false;
43
49
  }
@@ -72,7 +78,11 @@ export async function uninstallApk (this: ADB, pkg: string, options: UninstallOp
72
78
  * @param opts - Additional exec options.
73
79
  * @throws If there was a failure during application install.
74
80
  */
75
- export async function installFromDevicePath (this: ADB, apkPathOnDevice: string, opts: ShellExecOptions = {}): Promise<void> {
81
+ export async function installFromDevicePath(
82
+ this: ADB,
83
+ apkPathOnDevice: string,
84
+ opts: ShellExecOptions = {},
85
+ ): Promise<void> {
76
86
  const stdout = await this.shell(['pm', 'install', '-r', apkPathOnDevice], opts);
77
87
  if (stdout.includes('Failure')) {
78
88
  throw new Error(`Remote install failed: ${stdout}`);
@@ -87,7 +97,11 @@ export async function installFromDevicePath (this: ADB, apkPathOnDevice: string,
87
97
  * @returns Full path to the cached apk on the remote file system
88
98
  * @throws if there was a failure while caching the app
89
99
  */
90
- export async function cacheApk (this: ADB, apkPath: string, options: CachingOptions = {}): Promise<string> {
100
+ export async function cacheApk(
101
+ this: ADB,
102
+ apkPath: string,
103
+ options: CachingOptions = {},
104
+ ): Promise<string> {
91
105
  const appHash = await fs.hash(apkPath);
92
106
  const remotePath = path.posix.join(REMOTE_CACHE_ROOT, `${appHash}.apk`);
93
107
  const remoteCachedFiles: string[] = [];
@@ -95,13 +109,21 @@ export async function cacheApk (this: ADB, apkPath: string, options: CachingOpti
95
109
  try {
96
110
  const errorMarker = '_ERROR_';
97
111
  let lsOutput: string | null = null;
98
- if (this._areExtendedLsOptionsSupported === true || !_.isBoolean(this._areExtendedLsOptionsSupported)) {
112
+ if (
113
+ this._areExtendedLsOptionsSupported === true ||
114
+ !_.isBoolean(this._areExtendedLsOptionsSupported)
115
+ ) {
99
116
  lsOutput = await this.shell([`ls -t -1 ${REMOTE_CACHE_ROOT} 2>&1 || echo ${errorMarker}`]);
100
117
  }
101
- if (!_.isString(lsOutput) || (lsOutput.includes(errorMarker) && !lsOutput.includes(REMOTE_CACHE_ROOT))) {
118
+ if (
119
+ !_.isString(lsOutput) ||
120
+ (lsOutput.includes(errorMarker) && !lsOutput.includes(REMOTE_CACHE_ROOT))
121
+ ) {
102
122
  if (!_.isBoolean(this._areExtendedLsOptionsSupported)) {
103
- log.debug('The current Android API does not support extended ls options. ' +
104
- 'Defaulting to no-options call');
123
+ log.debug(
124
+ 'The current Android API does not support extended ls options. ' +
125
+ 'Defaulting to no-options call',
126
+ );
105
127
  }
106
128
  lsOutput = await this.shell([`ls ${REMOTE_CACHE_ROOT} 2>&1 || echo ${errorMarker}`]);
107
129
  this._areExtendedLsOptionsSupported = false;
@@ -111,15 +133,18 @@ export async function cacheApk (this: ADB, apkPath: string, options: CachingOpti
111
133
  if (lsOutput.includes(errorMarker)) {
112
134
  throw new Error(lsOutput.substring(0, lsOutput.indexOf(errorMarker)));
113
135
  }
114
- remoteCachedFiles.push(...(
115
- lsOutput.split('\n')
136
+ remoteCachedFiles.push(
137
+ ...lsOutput
138
+ .split('\n')
116
139
  .map((x) => x.trim())
117
- .filter(Boolean)
118
- ));
140
+ .filter(Boolean),
141
+ );
119
142
  } catch (e) {
120
143
  const err = e as Error;
121
- log.debug(`Got an error '${err.message.trim()}' while getting the list of files in the cache. ` +
122
- `Assuming the cache does not exist yet`);
144
+ log.debug(
145
+ `Got an error '${err.message.trim()}' while getting the list of files in the cache. ` +
146
+ `Assuming the cache does not exist yet`,
147
+ );
123
148
  await this.shell(['mkdir', '-p', REMOTE_CACHE_ROOT]);
124
149
  }
125
150
  log.debug(`The count of applications in the cache: ${remoteCachedFiles.length}`);
@@ -136,8 +161,10 @@ export async function cacheApk (this: ADB, apkPath: string, options: CachingOpti
136
161
  const timer = new timing.Timer().start();
137
162
  await this.push(apkPath, remotePath, {timeout: options.timeout});
138
163
  const {size} = await fs.stat(apkPath);
139
- log.info(`The upload of '${path.basename(apkPath)}' (${util.toReadableSizeString(size)}) ` +
140
- `took ${timer.getDuration().asMilliSeconds.toFixed(0)}ms`);
164
+ log.info(
165
+ `The upload of '${path.basename(apkPath)}' (${util.toReadableSizeString(size)}) ` +
166
+ `took ${timer.getDuration().asMilliSeconds.toFixed(0)}ms`,
167
+ );
141
168
  }
142
169
  if (!this.remoteAppsCache) {
143
170
  this.remoteAppsCache = new LRUCache({
@@ -145,8 +172,9 @@ export async function cacheApk (this: ADB, apkPath: string, options: CachingOpti
145
172
  });
146
173
  }
147
174
  // Cleanup the invalid entries from the cache
148
- _.difference([...this.remoteAppsCache.keys()], remoteCachedFiles.map(toHash))
149
- .forEach((hash) => (this.remoteAppsCache as LRUCache<string, string>).delete(hash));
175
+ _.difference([...this.remoteAppsCache.keys()], remoteCachedFiles.map(toHash)).forEach((hash) =>
176
+ (this.remoteAppsCache as LRUCache<string, string>).delete(hash),
177
+ );
150
178
  // Bump the cache record for the recently cached item
151
179
  this.remoteAppsCache.set(appHash, remotePath);
152
180
  // If the remote cache exceeds this.remoteAppsCacheLimit, remove the least recently used entries
@@ -160,8 +188,10 @@ export async function cacheApk (this: ADB, apkPath: string, options: CachingOpti
160
188
  log.debug(`Deleted ${entriesToCleanup.length} expired application cache entries`);
161
189
  } catch (e) {
162
190
  const err = e as Error;
163
- log.warn(`Cannot delete ${entriesToCleanup.length} expired application cache entries. ` +
164
- `Original error: ${err.message}`);
191
+ log.warn(
192
+ `Cannot delete ${entriesToCleanup.length} expired application cache entries. ` +
193
+ `Original error: ${err.message}`,
194
+ );
165
195
  }
166
196
  }
167
197
  return remotePath;
@@ -174,7 +204,11 @@ export async function cacheApk (this: ADB, apkPath: string, options: CachingOpti
174
204
  * @param options - The set of installation options.
175
205
  * @throws If an unexpected error happens during install.
176
206
  */
177
- export async function install (this: ADB, appPath: string, options: InstallOptions = {}): Promise<void> {
207
+ export async function install(
208
+ this: ADB,
209
+ appPath: string,
210
+ options: InstallOptions = {},
211
+ ): Promise<void> {
178
212
  if (appPath.endsWith(APKS_EXTENSION)) {
179
213
  return await this.installApks(appPath, options);
180
214
  }
@@ -182,12 +216,13 @@ export async function install (this: ADB, appPath: string, options: InstallOptio
182
216
  options = _.cloneDeep(options);
183
217
  _.defaults(options, {
184
218
  replace: true,
185
- timeout: this.adbExecTimeout === DEFAULT_ADB_EXEC_TIMEOUT ? APK_INSTALL_TIMEOUT : this.adbExecTimeout,
219
+ timeout:
220
+ this.adbExecTimeout === DEFAULT_ADB_EXEC_TIMEOUT ? APK_INSTALL_TIMEOUT : this.adbExecTimeout,
186
221
  timeoutCapName: 'androidInstallTimeout',
187
222
  });
188
223
 
189
224
  const installArgs = buildInstallArgs(await this.getApiLevel(), options);
190
- if (options.noIncremental && await this.isIncrementalInstallSupported()) {
225
+ if (options.noIncremental && (await this.isIncrementalInstallSupported())) {
191
226
  // Adb throws an error if it does not know about an arg,
192
227
  // which is the case here for older adb versions.
193
228
  installArgs.push('--no-incremental');
@@ -196,19 +231,17 @@ export async function install (this: ADB, appPath: string, options: InstallOptio
196
231
  timeout: options.timeout,
197
232
  timeoutCapName: options.timeoutCapName,
198
233
  };
199
- const installCmd = [
200
- 'install',
201
- ...installArgs,
202
- appPath,
203
- ];
234
+ const installCmd = ['install', ...installArgs, appPath];
204
235
  let performAppInstall = async () => await this.adbExec(installCmd, installOpts);
205
236
  // this.remoteAppsCacheLimit <= 0 means no caching should be applied
206
237
  let shouldCacheApp = (this.remoteAppsCacheLimit as number) > 0;
207
238
  if (shouldCacheApp) {
208
239
  shouldCacheApp = !(await this.isStreamedInstallSupported());
209
240
  if (!shouldCacheApp) {
210
- log.info(`The application at '${appPath}' will not be cached, because the device under test has ` +
211
- `confirmed the support of streamed installs`);
241
+ log.info(
242
+ `The application at '${appPath}' will not be cached, because the device under test has ` +
243
+ `confirmed the support of streamed installs`,
244
+ );
212
245
  }
213
246
  }
214
247
  if (shouldCacheApp) {
@@ -216,25 +249,31 @@ export async function install (this: ADB, appPath: string, options: InstallOptio
216
249
  log.info(`Clearing the cache at '${REMOTE_CACHE_ROOT}'`);
217
250
  await this.shell(['rm', '-rf', `${REMOTE_CACHE_ROOT}/*`]);
218
251
  };
219
- const cacheApp = async () => await this.cacheApk(appPath, {
220
- timeout: options.timeout,
221
- });
252
+ const cacheApp = async () =>
253
+ await this.cacheApk(appPath, {
254
+ timeout: options.timeout,
255
+ });
222
256
  try {
223
257
  const cachedAppPath = await cacheApp();
224
258
  performAppInstall = async () => {
225
259
  const pmInstallCmdByRemotePath = (remotePath: string) => [
226
- 'pm', 'install',
260
+ 'pm',
261
+ 'install',
227
262
  ...installArgs,
228
263
  remotePath,
229
264
  ];
230
265
  const output = await this.shell(pmInstallCmdByRemotePath(cachedAppPath), installOpts);
231
266
  // https://github.com/appium/appium/issues/13970
232
267
  if (/\bINSTALL_FAILED_INSUFFICIENT_STORAGE\b/.test(output)) {
233
- log.warn(`There was a failure while installing '${appPath}' ` +
234
- `because of the insufficient device storage space`);
268
+ log.warn(
269
+ `There was a failure while installing '${appPath}' ` +
270
+ `because of the insufficient device storage space`,
271
+ );
235
272
  await clearCache();
236
- log.info(`Consider decreasing the maximum amount of cached apps ` +
237
- `(currently ${this.remoteAppsCacheLimit}) to avoid such issues in the future`);
273
+ log.info(
274
+ `Consider decreasing the maximum amount of cached apps ` +
275
+ `(currently ${this.remoteAppsCacheLimit}) to avoid such issues in the future`,
276
+ );
238
277
  const newCachedAppPath = await cacheApp();
239
278
  return await this.shell(pmInstallCmdByRemotePath(newCachedAppPath), installOpts);
240
279
  }
@@ -251,9 +290,13 @@ export async function install (this: ADB, appPath: string, options: InstallOptio
251
290
  try {
252
291
  const timer = new timing.Timer().start();
253
292
  const output = await performAppInstall();
254
- log.info(`The installation of '${path.basename(appPath)}' took ${timer.getDuration().asMilliSeconds.toFixed(0)}ms`);
255
- const truncatedOutput = (!_.isString(output) || output.length <= 300) ?
256
- output : `${output.substring(0, 150)}...${output.substring(output.length - 150)}`;
293
+ log.info(
294
+ `The installation of '${path.basename(appPath)}' took ${timer.getDuration().asMilliSeconds.toFixed(0)}ms`,
295
+ );
296
+ const truncatedOutput =
297
+ !_.isString(output) || output.length <= 300
298
+ ? output
299
+ : `${output.substring(0, 150)}...${output.substring(output.length - 150)}`;
257
300
  log.debug(`Install command stdout: ${truncatedOutput}`);
258
301
  if (/\[INSTALL[A-Z_]+FAILED[A-Z_]+\]/.test(output)) {
259
302
  if (this.isTestPackageOnlyError(output)) {
@@ -282,7 +325,11 @@ export async function install (this: ADB, appPath: string, options: InstallOptio
282
325
  * try to extract it on its own
283
326
  * @returns One of `APP_INSTALL_STATE` constants
284
327
  */
285
- export async function getApplicationInstallState (this: ADB, appPath: string, pkg: string | null = null): Promise<InstallState> {
328
+ export async function getApplicationInstallState(
329
+ this: ADB,
330
+ appPath: string,
331
+ pkg: string | null = null,
332
+ ): Promise<InstallState> {
286
333
  let apkInfo: AppInfo | null = null;
287
334
  if (!pkg) {
288
335
  apkInfo = (await this.getApkInfo(appPath)) as AppInfo;
@@ -319,30 +366,48 @@ export async function getApplicationInstallState (this: ADB, appPath: string, pk
319
366
  }
320
367
  if (_.isInteger(apkVersionCode) && _.isInteger(pkgVersionCode)) {
321
368
  if ((pkgVersionCode as number) > (apkVersionCode as number)) {
322
- log.debug(`The version code of the installed '${pkg}' is greater than the application version code (${pkgVersionCode} > ${apkVersionCode})`);
369
+ log.debug(
370
+ `The version code of the installed '${pkg}' is greater than the application version code (${pkgVersionCode} > ${apkVersionCode})`,
371
+ );
323
372
  return this.APP_INSTALL_STATE.NEWER_VERSION_INSTALLED;
324
373
  }
325
374
  // Version codes might not be maintained. Check version names.
326
375
  if (pkgVersionCode === apkVersionCode) {
327
- if (_.isString(apkVersionName) && _.isString(pkgVersionName) && semver.satisfies(pkgVersionName, `>=${apkVersionName}`)) {
328
- log.debug(`The version name of the installed '${pkg}' is greater or equal to the application version name ('${pkgVersionName}' >= '${apkVersionName}')`);
376
+ if (
377
+ _.isString(apkVersionName) &&
378
+ _.isString(pkgVersionName) &&
379
+ semver.satisfies(pkgVersionName, `>=${apkVersionName}`)
380
+ ) {
381
+ log.debug(
382
+ `The version name of the installed '${pkg}' is greater or equal to the application version name ('${pkgVersionName}' >= '${apkVersionName}')`,
383
+ );
329
384
  return semver.satisfies(pkgVersionName, `>${apkVersionName}`)
330
385
  ? this.APP_INSTALL_STATE.NEWER_VERSION_INSTALLED
331
386
  : this.APP_INSTALL_STATE.SAME_VERSION_INSTALLED;
332
387
  }
333
388
  if (!_.isString(apkVersionName) || !_.isString(pkgVersionName)) {
334
- log.debug(`The version name of the installed '${pkg}' is equal to application version name (${pkgVersionCode} === ${apkVersionCode})`);
389
+ log.debug(
390
+ `The version name of the installed '${pkg}' is equal to application version name (${pkgVersionCode} === ${apkVersionCode})`,
391
+ );
335
392
  return this.APP_INSTALL_STATE.SAME_VERSION_INSTALLED;
336
393
  }
337
394
  }
338
- } else if (_.isString(apkVersionName) && _.isString(pkgVersionName) && semver.satisfies(pkgVersionName, `>=${apkVersionName}`)) {
339
- log.debug(`The version name of the installed '${pkg}' is greater or equal to the application version name ('${pkgVersionName}' >= '${apkVersionName}')`);
395
+ } else if (
396
+ _.isString(apkVersionName) &&
397
+ _.isString(pkgVersionName) &&
398
+ semver.satisfies(pkgVersionName, `>=${apkVersionName}`)
399
+ ) {
400
+ log.debug(
401
+ `The version name of the installed '${pkg}' is greater or equal to the application version name ('${pkgVersionName}' >= '${apkVersionName}')`,
402
+ );
340
403
  return semver.satisfies(pkgVersionName, `>${apkVersionName}`)
341
404
  ? this.APP_INSTALL_STATE.NEWER_VERSION_INSTALLED
342
405
  : this.APP_INSTALL_STATE.SAME_VERSION_INSTALLED;
343
406
  }
344
407
 
345
- log.debug(`The installed '${pkg}' package is older than '${appPath}' (${pkgVersionCode} < ${apkVersionCode} or '${pkgVersionName}' < '${apkVersionName}')'`);
408
+ log.debug(
409
+ `The installed '${pkg}' package is older than '${appPath}' (${pkgVersionCode} < ${apkVersionCode} or '${pkgVersionName}' < '${apkVersionName}')'`,
410
+ );
346
411
  return this.APP_INSTALL_STATE.OLDER_VERSION_INSTALLED;
347
412
  }
348
413
 
@@ -356,7 +421,12 @@ export async function getApplicationInstallState (this: ADB, appPath: string, pk
356
421
  * @param options - Set of install options.
357
422
  * @throws If an unexpected error happens during install.
358
423
  */
359
- export async function installOrUpgrade (this: ADB, appPath: string, pkg: string | null = null, options: InstallOrUpgradeOptions = {}): Promise<InstallOrUpgradeResult> {
424
+ export async function installOrUpgrade(
425
+ this: ADB,
426
+ appPath: string,
427
+ pkg: string | null = null,
428
+ options: InstallOrUpgradeOptions = {},
429
+ ): Promise<InstallOrUpgradeResult> {
360
430
  if (!pkg) {
361
431
  const apkInfo = await this.getApkInfo(appPath);
362
432
  if ('name' in apkInfo) {
@@ -364,18 +434,16 @@ export async function installOrUpgrade (this: ADB, appPath: string, pkg: string
364
434
  } else {
365
435
  log.warn(
366
436
  `Cannot determine the package name of '${appPath}'. ` +
367
- `Continuing with the install anyway`
437
+ `Continuing with the install anyway`,
368
438
  );
369
439
  }
370
440
  }
371
441
 
372
- const {
373
- enforceCurrentBuild,
374
- } = options;
442
+ const {enforceCurrentBuild} = options;
375
443
  const appState = await this.getApplicationInstallState(appPath, pkg);
376
444
  let wasUninstalled = false;
377
445
  const uninstallPackage = async () => {
378
- if (!await this.uninstallApk(pkg as string, {skipInstallCheck: true})) {
446
+ if (!(await this.uninstallApk(pkg as string, {skipInstallCheck: true}))) {
379
447
  throw new Error(`'${pkg}' package cannot be uninstalled`);
380
448
  }
381
449
  wasUninstalled = true;
@@ -420,7 +488,9 @@ export async function installOrUpgrade (this: ADB, appPath: string, pkg: string
420
488
  await this.install(appPath, {...options, replace: true});
421
489
  } catch (err) {
422
490
  const error = err as Error;
423
- log.warn(`Cannot install/upgrade '${pkg}' because of '${error.message}'. Trying full reinstall`);
491
+ log.warn(
492
+ `Cannot install/upgrade '${pkg}' because of '${error.message}'. Trying full reinstall`,
493
+ );
424
494
  await uninstallPackage();
425
495
  await this.install(appPath, {...options, replace: false});
426
496
  }
@@ -440,11 +510,11 @@ export async function installOrUpgrade (this: ADB, appPath: string, pkg: string
440
510
  * store the extracted file to. If not provided then the `localPath` property in the returned object
441
511
  * will be undefined.
442
512
  */
443
- export async function extractStringsFromApk (
513
+ export async function extractStringsFromApk(
444
514
  this: ADB,
445
515
  appPath: string,
446
516
  language: string | null = null,
447
- outRoot: string | null = null
517
+ outRoot: string | null = null,
448
518
  ): Promise<ApkStrings> {
449
519
  log.debug(`Extracting strings from for language: ${language || 'default'}`);
450
520
  const originalAppPath = appPath;
@@ -457,49 +527,73 @@ export async function extractStringsFromApk (
457
527
  try {
458
528
  await this.initAapt();
459
529
 
460
- configMarker = await formatConfigMarker(async () => {
461
- const {stdout} = await exec((this.binaries as StringRecord).aapt as string, [
462
- 'd', 'configurations', appPath,
463
- ]);
464
- return _.uniq(stdout.split(os.EOL));
465
- }, language, '(default)');
530
+ configMarker = await formatConfigMarker(
531
+ async () => {
532
+ const {stdout} = await exec((this.binaries as StringRecord).aapt as string, [
533
+ 'd',
534
+ 'configurations',
535
+ appPath,
536
+ ]);
537
+ return _.uniq(stdout.split(os.EOL));
538
+ },
539
+ language,
540
+ '(default)',
541
+ );
466
542
 
467
543
  const {stdout} = await exec((this.binaries as StringRecord).aapt as string, [
468
- 'd', '--values', 'resources', appPath,
544
+ 'd',
545
+ '--values',
546
+ 'resources',
547
+ appPath,
469
548
  ]);
470
549
  apkStrings = parseAaptStrings(stdout, configMarker);
471
550
  } catch (e) {
472
551
  const err = e as ExecError;
473
- log.debug('Cannot extract resources using aapt. Trying aapt2. ' +
474
- `Original error: ${err.stderr || err.message}`);
552
+ log.debug(
553
+ 'Cannot extract resources using aapt. Trying aapt2. ' +
554
+ `Original error: ${err.stderr || err.message}`,
555
+ );
475
556
 
476
557
  await this.initAapt2();
477
558
 
478
- configMarker = await formatConfigMarker(async () => {
479
- const {stdout} = await exec((this.binaries as StringRecord).aapt2 as string, [
480
- 'd', 'configurations', appPath,
481
- ]);
482
- return _.uniq(stdout.split(os.EOL));
483
- }, language, '');
559
+ configMarker = await formatConfigMarker(
560
+ async () => {
561
+ const {stdout} = await exec((this.binaries as StringRecord).aapt2 as string, [
562
+ 'd',
563
+ 'configurations',
564
+ appPath,
565
+ ]);
566
+ return _.uniq(stdout.split(os.EOL));
567
+ },
568
+ language,
569
+ '',
570
+ );
484
571
 
485
572
  try {
486
573
  const {stdout} = await exec((this.binaries as StringRecord).aapt2 as string, [
487
- 'd', 'resources', appPath,
574
+ 'd',
575
+ 'resources',
576
+ appPath,
488
577
  ]);
489
578
  apkStrings = parseAapt2Strings(stdout, configMarker);
490
579
  } catch (e) {
491
580
  const error = e as Error;
492
- throw new Error(`Cannot extract resources from '${originalAppPath}'. ` +
493
- `Original error: ${error.message}`);
581
+ throw new Error(
582
+ `Cannot extract resources from '${originalAppPath}'. ` + `Original error: ${error.message}`,
583
+ );
494
584
  }
495
585
  }
496
586
 
497
587
  if (_.isEmpty(apkStrings)) {
498
- log.warn(`No strings have been found in '${originalAppPath}' resources ` +
499
- `for '${configMarker || 'default'}' configuration`);
588
+ log.warn(
589
+ `No strings have been found in '${originalAppPath}' resources ` +
590
+ `for '${configMarker || 'default'}' configuration`,
591
+ );
500
592
  } else {
501
- log.info(`Successfully extracted ${_.keys(apkStrings).length} strings from ` +
502
- `'${originalAppPath}' resources for '${configMarker || 'default'}' configuration`);
593
+ log.info(
594
+ `Successfully extracted ${_.keys(apkStrings).length} strings from ` +
595
+ `'${originalAppPath}' resources for '${configMarker || 'default'}' configuration`,
596
+ );
503
597
  }
504
598
 
505
599
  if (!outRoot) {
@@ -519,8 +613,8 @@ export async function extractStringsFromApk (
519
613
  * file system.
520
614
  * @returns The parsed application information.
521
615
  */
522
- export async function getApkInfo (this: ADB, appPath: string): Promise<AppInfo | {}> {
523
- if (!await fs.exists(appPath)) {
616
+ export async function getApkInfo(this: ADB, appPath: string): Promise<AppInfo | {}> {
617
+ if (!(await fs.exists(appPath))) {
524
618
  throw new Error(`The file at path ${appPath} does not exist or is not accessible`);
525
619
  }
526
620
 
@@ -554,7 +648,11 @@ export async function getApkInfo (this: ADB, appPath: string): Promise<AppInfo |
554
648
  * @param defaultMarker The default config marker value
555
649
  * @returns The formatted config marker
556
650
  */
557
- async function formatConfigMarker (configsGetter: () => Promise<string[]>, desiredMarker: string | null, defaultMarker: string): Promise<string> {
651
+ async function formatConfigMarker(
652
+ configsGetter: () => Promise<string[]>,
653
+ desiredMarker: string | null,
654
+ defaultMarker: string,
655
+ ): Promise<string> {
558
656
  let configMarker = desiredMarker || defaultMarker;
559
657
  if (configMarker.includes('-') && !configMarker.includes('-r')) {
560
658
  configMarker = configMarker.replace('-', '-r');
@@ -562,10 +660,14 @@ async function formatConfigMarker (configsGetter: () => Promise<string[]>, desir
562
660
  const configs = await configsGetter();
563
661
  log.debug(`Resource configurations: ${JSON.stringify(configs)}`);
564
662
  // Assume the 'en' configuration is the default one
565
- if (configMarker.toLowerCase().startsWith('en')
566
- && !configs.some((x) => x.trim() === configMarker)) {
567
- log.debug(`Resource configuration name '${configMarker}' is unknown. ` +
568
- `Replacing it with '${defaultMarker}'`);
663
+ if (
664
+ configMarker.toLowerCase().startsWith('en') &&
665
+ !configs.some((x) => x.trim() === configMarker)
666
+ ) {
667
+ log.debug(
668
+ `Resource configuration name '${configMarker}' is unknown. ` +
669
+ `Replacing it with '${defaultMarker}'`,
670
+ );
569
671
  configMarker = defaultMarker;
570
672
  } else {
571
673
  log.debug(`Selected configuration: '${configMarker}'`);
@@ -583,9 +685,9 @@ async function formatConfigMarker (configsGetter: () => Promise<string[]>, desir
583
685
  * values are represented as arrays. If no config found for the
584
686
  * given marker then an empty mapping is returned.
585
687
  */
586
- export function parseAapt2Strings (rawOutput: string, configMarker: string): StringRecord {
688
+ export function parseAapt2Strings(rawOutput: string, configMarker: string): StringRecord {
587
689
  const allLines = rawOutput.split(os.EOL);
588
- function extractContent (startIdx: number): [string | null, number] {
690
+ function extractContent(startIdx: number): [string | null, number] {
589
691
  let idx = startIdx;
590
692
  const startCharPos = allLines[startIdx].indexOf('"');
591
693
  if (startCharPos < 0) {
@@ -597,15 +699,9 @@ export function parseAapt2Strings (rawOutput: string, configMarker: string): Str
597
699
  if (terminationCharMatch) {
598
700
  const terminationCharPos = terminationCharMatch.index;
599
701
  if (startIdx === idx) {
600
- return [
601
- allLines[idx].substring(startCharPos + 1, terminationCharPos),
602
- idx
603
- ];
702
+ return [allLines[idx].substring(startCharPos + 1, terminationCharPos), idx];
604
703
  }
605
- return [
606
- `${result}\\n${_.trimStart(allLines[idx].substring(0, terminationCharPos))}`,
607
- idx,
608
- ];
704
+ return [`${result}\\n${_.trimStart(allLines[idx].substring(0, terminationCharPos))}`, idx];
609
705
  }
610
706
  if (idx > startIdx) {
611
707
  result += `\\n${_.trimStart(allLines[idx])}`;
@@ -670,7 +766,9 @@ export function parseAapt2Strings (rawOutput: string, configMarker: string): Str
670
766
  lineIndex = idx;
671
767
  if (_.isString(content)) {
672
768
  apkStrings[currentResourceId] = [
673
- ...(Array.isArray(apkStrings[currentResourceId]) ? apkStrings[currentResourceId] : []),
769
+ ...(Array.isArray(apkStrings[currentResourceId])
770
+ ? apkStrings[currentResourceId]
771
+ : []),
674
772
  content,
675
773
  ];
676
774
  }
@@ -699,7 +797,7 @@ export function parseAapt2Strings (rawOutput: string, configMarker: string): Str
699
797
  * values are represented as arrays. If no config found for the
700
798
  * given marker then an empty mapping is returned.
701
799
  */
702
- export function parseAaptStrings (rawOutput: string, configMarker: string): StringRecord {
800
+ export function parseAaptStrings(rawOutput: string, configMarker: string): StringRecord {
703
801
  const normalizeStringMatch = function (s: string) {
704
802
  return s.replace(/"$/, '').replace(/^"/, '').replace(/\\"/g, '"');
705
803
  };
@@ -770,4 +868,3 @@ export function parseAaptStrings (rawOutput: string, configMarker: string): Stri
770
868
  }
771
869
 
772
870
  // #endregion
773
-