appium-adb 14.3.4 → 14.4.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 (134) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/build/lib/adb.d.ts +9 -4
  3. package/build/lib/adb.d.ts.map +1 -1
  4. package/build/lib/adb.js +8 -9
  5. package/build/lib/adb.js.map +1 -1
  6. package/build/lib/index.d.ts +1 -1
  7. package/build/lib/index.d.ts.map +1 -1
  8. package/build/lib/index.js +2 -2
  9. package/build/lib/index.js.map +1 -1
  10. package/build/lib/logcat.d.ts.map +1 -1
  11. package/build/lib/logcat.js +6 -14
  12. package/build/lib/logcat.js.map +1 -1
  13. package/build/lib/tools/aab-utils.d.ts.map +1 -1
  14. package/build/lib/tools/aab-utils.js +2 -2
  15. package/build/lib/tools/aab-utils.js.map +1 -1
  16. package/build/lib/tools/android-manifest.d.ts.map +1 -1
  17. package/build/lib/tools/android-manifest.js +11 -12
  18. package/build/lib/tools/android-manifest.js.map +1 -1
  19. package/build/lib/tools/apk-signing.d.ts.map +1 -1
  20. package/build/lib/tools/apk-signing.js +18 -19
  21. package/build/lib/tools/apk-signing.js.map +1 -1
  22. package/build/lib/tools/apk-utils.d.ts.map +1 -1
  23. package/build/lib/tools/apk-utils.js +39 -40
  24. package/build/lib/tools/apk-utils.js.map +1 -1
  25. package/build/lib/tools/apks-utils.d.ts.map +1 -1
  26. package/build/lib/tools/apks-utils.js +8 -9
  27. package/build/lib/tools/apks-utils.js.map +1 -1
  28. package/build/lib/tools/app-commands.d.ts.map +1 -1
  29. package/build/lib/tools/app-commands.js +37 -36
  30. package/build/lib/tools/app-commands.js.map +1 -1
  31. package/build/lib/tools/device-settings.d.ts.map +1 -1
  32. package/build/lib/tools/device-settings.js +17 -18
  33. package/build/lib/tools/device-settings.js.map +1 -1
  34. package/build/lib/tools/emulator-commands.d.ts.map +1 -1
  35. package/build/lib/tools/emulator-commands.js +22 -23
  36. package/build/lib/tools/emulator-commands.js.map +1 -1
  37. package/build/lib/tools/fs-commands.d.ts.map +1 -1
  38. package/build/lib/tools/fs-commands.js +5 -4
  39. package/build/lib/tools/fs-commands.js.map +1 -1
  40. package/build/lib/tools/general-commands.d.ts.map +1 -1
  41. package/build/lib/tools/general-commands.js +29 -29
  42. package/build/lib/tools/general-commands.js.map +1 -1
  43. package/build/lib/tools/keyboard-commands.d.ts.map +1 -1
  44. package/build/lib/tools/keyboard-commands.js +6 -4
  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 +17 -20
  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 +5 -9
  51. package/build/lib/tools/logcat-commands.js.map +1 -1
  52. package/build/lib/tools/network-commands.d.ts.map +1 -1
  53. package/build/lib/tools/network-commands.js +1 -5
  54. package/build/lib/tools/network-commands.js.map +1 -1
  55. package/build/lib/tools/process-commands.d.ts.map +1 -1
  56. package/build/lib/tools/process-commands.js +11 -12
  57. package/build/lib/tools/process-commands.js.map +1 -1
  58. package/build/lib/tools/system-calls.d.ts +12 -5
  59. package/build/lib/tools/system-calls.d.ts.map +1 -1
  60. package/build/lib/tools/system-calls.js +56 -50
  61. package/build/lib/tools/system-calls.js.map +1 -1
  62. package/build/lib/tools/types.d.ts +1 -1
  63. package/build/lib/tools/types.d.ts.map +1 -1
  64. package/build/lib/utils/helpers/constants.d.ts +6 -0
  65. package/build/lib/utils/helpers/constants.d.ts.map +1 -0
  66. package/build/lib/utils/helpers/constants.js +9 -0
  67. package/build/lib/utils/helpers/constants.js.map +1 -0
  68. package/build/lib/utils/helpers/index.d.ts +6 -0
  69. package/build/lib/utils/helpers/index.d.ts.map +1 -0
  70. package/build/lib/utils/helpers/index.js +21 -0
  71. package/build/lib/utils/helpers/index.js.map +1 -0
  72. package/build/lib/utils/helpers/install.d.ts +16 -0
  73. package/build/lib/utils/helpers/install.d.ts.map +1 -0
  74. package/build/lib/utils/helpers/install.js +39 -0
  75. package/build/lib/utils/helpers/install.js.map +1 -0
  76. package/build/lib/utils/helpers/manifest.d.ts +10 -0
  77. package/build/lib/utils/helpers/manifest.d.ts.map +1 -0
  78. package/build/lib/utils/helpers/manifest.js +124 -0
  79. package/build/lib/utils/helpers/manifest.js.map +1 -0
  80. package/build/lib/utils/helpers/resource.d.ts +11 -0
  81. package/build/lib/utils/helpers/resource.d.ts.map +1 -0
  82. package/build/lib/utils/helpers/resource.js +59 -0
  83. package/build/lib/utils/helpers/resource.js.map +1 -0
  84. package/build/lib/utils/helpers/sdk.d.ts +20 -0
  85. package/build/lib/utils/helpers/sdk.d.ts.map +1 -0
  86. package/build/lib/utils/helpers/sdk.js +80 -0
  87. package/build/lib/utils/helpers/sdk.js.map +1 -0
  88. package/build/lib/utils/index.d.ts +40 -0
  89. package/build/lib/utils/index.d.ts.map +1 -0
  90. package/build/lib/utils/index.js +58 -0
  91. package/build/lib/utils/index.js.map +1 -0
  92. package/build/lib/utils/lodash/core.d.ts +9 -0
  93. package/build/lib/utils/lodash/core.d.ts.map +1 -0
  94. package/build/lib/utils/lodash/core.js +60 -0
  95. package/build/lib/utils/lodash/core.js.map +1 -0
  96. package/build/lib/utils/lodash/index.d.ts +2 -0
  97. package/build/lib/utils/lodash/index.d.ts.map +1 -0
  98. package/build/lib/utils/lodash/index.js +18 -0
  99. package/build/lib/utils/lodash/index.js.map +1 -0
  100. package/lib/adb.ts +14 -6
  101. package/lib/index.ts +1 -1
  102. package/lib/logcat.ts +6 -11
  103. package/lib/tools/aab-utils.ts +1 -1
  104. package/lib/tools/android-manifest.ts +4 -4
  105. package/lib/tools/apk-signing.ts +11 -7
  106. package/lib/tools/apk-utils.ts +41 -42
  107. package/lib/tools/apks-utils.ts +4 -5
  108. package/lib/tools/app-commands.ts +40 -34
  109. package/lib/tools/device-settings.ts +17 -15
  110. package/lib/tools/emulator-commands.ts +22 -23
  111. package/lib/tools/fs-commands.ts +5 -4
  112. package/lib/tools/general-commands.ts +32 -27
  113. package/lib/tools/keyboard-commands.ts +6 -4
  114. package/lib/tools/lockmgmt.ts +17 -13
  115. package/lib/tools/logcat-commands.ts +5 -6
  116. package/lib/tools/network-commands.ts +1 -2
  117. package/lib/tools/process-commands.ts +11 -9
  118. package/lib/tools/system-calls.ts +54 -46
  119. package/lib/tools/types.ts +1 -1
  120. package/lib/utils/helpers/constants.ts +6 -0
  121. package/lib/utils/helpers/index.ts +10 -0
  122. package/lib/utils/helpers/install.ts +50 -0
  123. package/lib/utils/helpers/manifest.ts +127 -0
  124. package/lib/utils/helpers/resource.ts +63 -0
  125. package/lib/utils/helpers/sdk.ts +81 -0
  126. package/lib/utils/index.ts +25 -0
  127. package/lib/utils/lodash/core.ts +65 -0
  128. package/lib/utils/lodash/index.ts +1 -0
  129. package/package.json +3 -5
  130. package/build/lib/helpers.d.ts +0 -47
  131. package/build/lib/helpers.d.ts.map +0 -1
  132. package/build/lib/helpers.js +0 -283
  133. package/build/lib/helpers.js.map +0 -1
  134. package/lib/helpers.ts +0 -323
@@ -56,7 +56,9 @@ export async function isSoftKeyboardPresent(this: ADB): Promise<KeyboardState> {
56
56
  canCloseKeyboard: !!(inputViewShownMatch && inputViewShownMatch[1] === 'true'),
57
57
  };
58
58
  } catch (e) {
59
- throw new Error(`Error finding softkeyboard. Original error: ${(e as Error).message}`);
59
+ throw new Error(`Error finding softkeyboard. Original error: ${(e as Error).message}`, {
60
+ cause: e,
61
+ });
60
62
  }
61
63
  }
62
64
 
@@ -81,7 +83,7 @@ export async function availableIMEs(this: ADB): Promise<string[]> {
81
83
  return getIMEListFromOutput(await this.shell(['ime', 'list', '-a']));
82
84
  } catch (e) {
83
85
  const err = e as Error;
84
- throw new Error(`Error getting available IME's. Original error: ${err.message}`);
86
+ throw new Error(`Error getting available IME's. Original error: ${err.message}`, {cause: e});
85
87
  }
86
88
  }
87
89
 
@@ -95,7 +97,7 @@ export async function enabledIMEs(this: ADB): Promise<string[]> {
95
97
  return getIMEListFromOutput(await this.shell(['ime', 'list']));
96
98
  } catch (e) {
97
99
  const err = e as Error;
98
- throw new Error(`Error getting enabled IME's. Original error: ${err.message}`);
100
+ throw new Error(`Error getting enabled IME's. Original error: ${err.message}`, {cause: e});
99
101
  }
100
102
  }
101
103
 
@@ -140,7 +142,7 @@ export async function defaultIME(this: ADB): Promise<string | null> {
140
142
  return engine.trim();
141
143
  } catch (e) {
142
144
  const err = e as Error;
143
- throw new Error(`Error getting default IME. Original error: ${err.message}`);
145
+ throw new Error(`Error getting default IME. Original error: ${err.message}`, {cause: e});
144
146
  }
145
147
  }
146
148
 
@@ -1,7 +1,7 @@
1
1
  import {log} from '../logger';
2
- import _ from 'lodash';
3
2
  import {sleep, waitForCondition} from 'asyncbox';
4
3
  import type {ADB} from '../adb';
4
+ import {util} from '@appium/support';
5
5
 
6
6
  const CREDENTIAL_CANNOT_BE_NULL_OR_EMPTY_ERROR = `Credential can't be null or empty`;
7
7
  const CREDENTIAL_DID_NOT_MATCH_ERROR = `didn't match`;
@@ -17,13 +17,13 @@ const HIDE_KEYBOARD_WAIT_TIME = 100;
17
17
  * @return True if the management is supported. The result is cached per ADB instance
18
18
  */
19
19
  export async function isLockManagementSupported(this: ADB): Promise<boolean> {
20
- if (!_.isBoolean(this._isLockManagementSupported)) {
20
+ if (typeof this._isLockManagementSupported !== 'boolean') {
21
21
  const passFlag = '__PASS__';
22
22
  let output = '';
23
23
  try {
24
24
  output = await this.shell([`locksettings help && echo ${passFlag}`]);
25
25
  } catch {}
26
- this._isLockManagementSupported = _.includes(output, passFlag);
26
+ this._isLockManagementSupported = output.includes(passFlag);
27
27
  log.debug(
28
28
  `Extended lock settings management is ` +
29
29
  `${this._isLockManagementSupported ? '' : 'not '}supported`,
@@ -52,12 +52,12 @@ export async function verifyLockCredential(
52
52
  const {stdout, stderr} = await this.shell(buildCommand('verify', credential), {
53
53
  outputFormat: this.EXEC_OUTPUT_FORMAT.FULL,
54
54
  });
55
- if (_.includes(stdout, 'verified successfully')) {
55
+ if (stdout?.includes('verified successfully')) {
56
56
  return true;
57
57
  }
58
58
  if (
59
59
  [`didn't match`, CREDENTIAL_CANNOT_BE_NULL_OR_EMPTY_ERROR].some((x) =>
60
- _.includes(stderr || stdout, x),
60
+ (stderr || stdout)?.includes(x),
61
61
  )
62
62
  ) {
63
63
  return false;
@@ -71,6 +71,7 @@ export async function verifyLockCredential(
71
71
  (e as Error & {stderr?: string; stdout?: string}).stdout ||
72
72
  (e as Error).message
73
73
  }`,
74
+ {cause: e},
74
75
  );
75
76
  }
76
77
  }
@@ -97,7 +98,7 @@ export async function clearLockCredential(
97
98
  });
98
99
  if (
99
100
  !['user has no password', 'Lock credential cleared'].some((x) =>
100
- _.includes(stderr || stdout, x),
101
+ (stderr || stdout)?.includes(x),
101
102
  )
102
103
  ) {
103
104
  throw new Error(stderr || stdout);
@@ -110,6 +111,7 @@ export async function clearLockCredential(
110
111
  (e as Error & {stderr?: string; stdout?: string}).stdout ||
111
112
  (e as Error).message
112
113
  }`,
114
+ {cause: e},
113
115
  );
114
116
  }
115
117
  }
@@ -129,7 +131,7 @@ export async function isLockEnabled(this: ADB): Promise<boolean> {
129
131
  if (
130
132
  /\bfalse\b/.test(stdout) ||
131
133
  [CREDENTIAL_DID_NOT_MATCH_ERROR, CREDENTIAL_CANNOT_BE_NULL_OR_EMPTY_ERROR].some((x) =>
132
- _.includes(stderr || stdout, x),
134
+ (stderr || stdout)?.includes(x),
133
135
  )
134
136
  ) {
135
137
  return true;
@@ -141,6 +143,7 @@ export async function isLockEnabled(this: ADB): Promise<boolean> {
141
143
  } catch (e) {
142
144
  throw new Error(
143
145
  `Cannot check if device lock is enabled. Original error: ${(e as Error).message}`,
146
+ {cause: e},
144
147
  );
145
148
  }
146
149
  }
@@ -174,7 +177,7 @@ export async function setLockCredential(
174
177
  `Only the following credential types are supported: ${SUPPORTED_LOCK_CREDENTIAL_TYPES}`,
175
178
  );
176
179
  }
177
- if (_.isEmpty(credential) && !_.isInteger(credential)) {
180
+ if (util.isEmpty(credential) && !Number.isInteger(credential)) {
178
181
  throw new Error('Device lock credential cannot be empty');
179
182
  }
180
183
  const cmd = buildCommand(`set-${credentialType}`, oldCredential, credential);
@@ -182,7 +185,7 @@ export async function setLockCredential(
182
185
  const {stdout, stderr} = await this.shell(cmd, {
183
186
  outputFormat: this.EXEC_OUTPUT_FORMAT.FULL,
184
187
  });
185
- if (!_.includes(stdout, 'set to')) {
188
+ if (!stdout?.includes('set to')) {
186
189
  throw new Error(stderr || stdout);
187
190
  }
188
191
  } catch (e) {
@@ -193,6 +196,7 @@ export async function setLockCredential(
193
196
  (e as Error & {stderr?: string; stdout?: string}).stdout ||
194
197
  (e as Error).message
195
198
  }`,
199
+ {cause: e},
196
200
  );
197
201
  }
198
202
  }
@@ -295,9 +299,9 @@ export async function lock(this: ADB): Promise<void> {
295
299
  */
296
300
  export function isShowingLockscreen(dumpsys: string): boolean {
297
301
  return (
298
- _.some(['mShowingLockscreen=true', 'mDreamingLockscreen=true'], (x) => dumpsys.includes(x)) ||
302
+ ['mShowingLockscreen=true', 'mDreamingLockscreen=true'].some((x) => dumpsys.includes(x)) ||
299
303
  // `mIsShowing` and `mInputRestricted` are `true` in lock condition. `false` is unlock condition.
300
- _.every([/KeyguardStateMonitor[\n\s]+mIsShowing=true/, /\s+mInputRestricted=true/], (x) =>
304
+ [/KeyguardStateMonitor[\n\s]+mIsShowing=true/, /\s+mInputRestricted=true/].every((x) =>
301
305
  x.test(dumpsys),
302
306
  )
303
307
  );
@@ -327,10 +331,10 @@ function buildCommand(
327
331
  ...args: string[]
328
332
  ): string[] {
329
333
  const cmd = ['locksettings', verb];
330
- if (oldCredential && !_.isEmpty(oldCredential)) {
334
+ if (oldCredential && !util.isEmpty(oldCredential)) {
331
335
  cmd.push('--old', oldCredential);
332
336
  }
333
- if (!_.isEmpty(args)) {
337
+ if (args.length > 0) {
334
338
  cmd.push(...args);
335
339
  }
336
340
  return cmd;
@@ -1,4 +1,3 @@
1
- import _ from 'lodash';
2
1
  import {Logcat} from '../logcat';
3
2
  import type {ADB} from '../adb';
4
3
  import type {LogcatOpts, LogEntry, LogcatListener} from './types';
@@ -10,7 +9,7 @@ import type {LogcatOpts, LogEntry, LogcatListener} from './types';
10
9
  * @throws {Error} If restart fails.
11
10
  */
12
11
  export async function startLogcat(this: ADB, opts: LogcatOpts = {}): Promise<void> {
13
- if (!_.isEmpty(this.logcat)) {
12
+ if (this.logcat) {
14
13
  throw new Error("Trying to start logcat capture but it's already started!");
15
14
  }
16
15
 
@@ -30,7 +29,7 @@ export async function startLogcat(this: ADB, opts: LogcatOpts = {}): Promise<voi
30
29
  */
31
30
  export async function stopLogcat(this: ADB): Promise<void> {
32
31
  const logcat = this.logcat;
33
- if (!logcat || _.isEmpty(this.logcat)) {
32
+ if (!logcat) {
34
33
  return;
35
34
  }
36
35
  try {
@@ -49,7 +48,7 @@ export async function stopLogcat(this: ADB): Promise<void> {
49
48
  */
50
49
  export function getLogcatLogs(this: ADB): LogEntry[] {
51
50
  const logcat = this.logcat;
52
- if (!logcat || _.isEmpty(this.logcat)) {
51
+ if (!logcat) {
53
52
  throw new Error(`Can't get logcat logs since logcat hasn't started`);
54
53
  }
55
54
  return logcat.getLogs();
@@ -63,7 +62,7 @@ export function getLogcatLogs(this: ADB): LogEntry[] {
63
62
  */
64
63
  export function setLogcatListener(this: ADB, listener: LogcatListener): void {
65
64
  const logcat = this.logcat;
66
- if (!logcat || _.isEmpty(this.logcat)) {
65
+ if (!logcat) {
67
66
  throw new Error("Logcat process hasn't been started");
68
67
  }
69
68
  logcat.on('output', listener);
@@ -79,7 +78,7 @@ export function setLogcatListener(this: ADB, listener: LogcatListener): void {
79
78
  */
80
79
  export function removeLogcatListener(this: ADB, listener: LogcatListener): void {
81
80
  const logcat = this.logcat;
82
- if (!logcat || _.isEmpty(this.logcat)) {
81
+ if (!logcat) {
83
82
  throw new Error("Logcat process hasn't been started");
84
83
  }
85
84
  logcat.removeListener('output', listener);
@@ -1,5 +1,4 @@
1
1
  import {EOL} from 'node:os';
2
- import _ from 'lodash';
3
2
  import {log} from '../logger';
4
3
  import type {ADB} from '../adb';
5
4
  import type {PortFamily, PortInfo} from './types';
@@ -126,7 +125,7 @@ export async function listPorts(this: ADB, family: PortFamily = '4'): Promise<Po
126
125
  const sourceProcName = `/proc/net/tcp${family === '6' ? '6' : ''}`;
127
126
  const output = await this.shell(['cat', sourceProcName]);
128
127
  const lines = output.split('\n');
129
- if (_.isEmpty(lines)) {
128
+ if (!lines.length) {
130
129
  log.debug(output);
131
130
  throw new Error(`Cannot parse the payload of ${sourceProcName}`);
132
131
  }
@@ -1,5 +1,5 @@
1
- import _ from 'lodash';
2
1
  import {log} from '../logger';
2
+ import {util} from '@appium/support';
3
3
  import type {ExecError} from 'teen_process';
4
4
  import type {ADB} from '../adb';
5
5
 
@@ -19,7 +19,7 @@ const PS_TITLE_PATTERN: RegExp = new RegExp(
19
19
  * @returns the output of `ps` command where all processes are included
20
20
  */
21
21
  export async function listProcessStatus(this: ADB): Promise<string> {
22
- if (!_.isBoolean(this._doesPsSupportAOption)) {
22
+ if (typeof this._doesPsSupportAOption !== 'boolean') {
23
23
  try {
24
24
  this._doesPsSupportAOption = /^-A\b/m.test(await this.shell(['ps', '--help']));
25
25
  } catch (e: unknown) {
@@ -108,7 +108,7 @@ export async function getProcessIdsByName(this: ADB, name: string): Promise<numb
108
108
  }
109
109
  }
110
110
 
111
- return _.uniq(pids);
111
+ return util.uniq(pids);
112
112
  }
113
113
 
114
114
  /**
@@ -126,14 +126,16 @@ export async function killProcessesByName(
126
126
  try {
127
127
  log.debug(`Attempting to kill all ${name} processes`);
128
128
  const pids: number[] = await this.getProcessIdsByName(name);
129
- if (_.isEmpty(pids)) {
129
+ if (pids.length === 0) {
130
130
  log.info(`No '${name}' process has been found`);
131
131
  } else {
132
132
  await Promise.all(pids.map((p: number) => this.killProcessByPID(p, signal)));
133
133
  }
134
134
  } catch (e: unknown) {
135
135
  const err: Error = e as Error;
136
- throw new Error(`Unable to kill ${name} processes. Original error: ${err.message}`);
136
+ throw new Error(`Unable to kill ${name} processes. Original error: ${err.message}`, {
137
+ cause: e,
138
+ });
137
139
  }
138
140
  }
139
141
 
@@ -158,10 +160,10 @@ export async function killProcessByPID(
158
160
  await this.shell(['kill', `-${signal}`, `${pid}`]);
159
161
  } catch (e: unknown) {
160
162
  const err: ExecError = e as ExecError;
161
- if (_.includes(err.stderr, noProcessFlag)) {
163
+ if ((err.stderr ?? '').includes(noProcessFlag)) {
162
164
  return;
163
165
  }
164
- if (!_.includes(err.stderr, 'Operation not permitted')) {
166
+ if (!(err.stderr ?? '').includes('Operation not permitted')) {
165
167
  throw err;
166
168
  }
167
169
  log.info(`Cannot kill PID ${pid} due to insufficient permissions. Retrying as root`);
@@ -171,7 +173,7 @@ export async function killProcessByPID(
171
173
  });
172
174
  } catch (e1: unknown) {
173
175
  const err1: ExecError = e1 as ExecError;
174
- if (_.includes(err1.stderr, noProcessFlag)) {
176
+ if ((err1.stderr ?? '').includes(noProcessFlag)) {
175
177
  return;
176
178
  }
177
179
  throw err1;
@@ -188,5 +190,5 @@ export async function killProcessByPID(
188
190
  * @throws {Error} If the given process name is not a valid class name.
189
191
  */
190
192
  export async function processExists(this: ADB, processName: string): Promise<boolean> {
191
- return !_.isEmpty(await this.getProcessIdsByName(processName));
193
+ return (await this.getProcessIdsByName(processName)).length > 0;
192
194
  }
@@ -1,13 +1,12 @@
1
1
  import path from 'node:path';
2
2
  import {log} from '../logger';
3
3
  import {system, fs, util, tempDir, timing} from '@appium/support';
4
- import {DEFAULT_ADB_EXEC_TIMEOUT, getSdkRootFromEnv} from '../helpers';
5
4
  import {exec, SubProcess} from 'teen_process';
6
5
  import type {ExecError, TeenProcessExecResult} from 'teen_process';
7
6
  import {asyncmap, retry, retryInterval, sleep, waitForCondition} from 'asyncbox';
8
- import _ from 'lodash';
9
7
  import * as semver from 'semver';
10
8
  import type {ADB} from '../adb';
9
+ import {DEFAULT_ADB_EXEC_TIMEOUT, cloneDeep, getSdkRootFromEnv, memoize, zip} from '../utils';
11
10
  import type {
12
11
  ConnectedDevicesOptions,
13
12
  Device,
@@ -81,7 +80,7 @@ function _getBinaryNameForOS(binaryName: string): string {
81
80
  */
82
81
  function getSdkBinaryLocationCandidates(sdkRoot: string, fullBinaryName: string): string[] {
83
82
  return SDK_BINARY_ROOTS.map((x) =>
84
- path.resolve(sdkRoot, ...(_.isArray(x) ? x : [x]), fullBinaryName),
83
+ path.resolve(sdkRoot, ...(Array.isArray(x) ? x : [x]), fullBinaryName),
85
84
  );
86
85
  }
87
86
 
@@ -100,7 +99,7 @@ async function getOpenSslForOs(): Promise<string> {
100
99
  }
101
100
  }
102
101
 
103
- export const getBinaryNameForOS = _.memoize(_getBinaryNameForOS);
102
+ export const getBinaryNameForOS = memoize(_getBinaryNameForOS);
104
103
 
105
104
  /**
106
105
  * Retrieve full path to the given binary and caches it into `binaries`
@@ -124,19 +123,16 @@ export async function getBinaryFromSdkRoot(this: ADB, binaryName: string): Promi
124
123
  let buildToolsDirs = await getBuildToolsDirs(this.sdkRoot);
125
124
  if (this.buildToolsVersion) {
126
125
  buildToolsDirs = buildToolsDirs.filter((x) => path.basename(x) === this.buildToolsVersion);
127
- if (_.isEmpty(buildToolsDirs)) {
126
+ if (util.isEmpty(buildToolsDirs)) {
128
127
  log.info(`Found no build tools whose version matches to '${this.buildToolsVersion}'`);
129
128
  } else {
130
129
  log.info(`Using build tools at '${buildToolsDirs}'`);
131
130
  }
132
131
  }
133
132
  binaryLocs.push(
134
- ..._.flatten(
135
- buildToolsDirs.map((dir) => [
136
- path.resolve(dir, fullBinaryName),
137
- path.resolve(dir, 'lib', fullBinaryName),
138
- ]),
139
- ),
133
+ ...buildToolsDirs
134
+ .map((dir) => [path.resolve(dir, fullBinaryName), path.resolve(dir, 'lib', fullBinaryName)])
135
+ .flat(),
140
136
  );
141
137
 
142
138
  let binaryLoc: string | null = null;
@@ -146,7 +142,7 @@ export async function getBinaryFromSdkRoot(this: ADB, binaryName: string): Promi
146
142
  break;
147
143
  }
148
144
  }
149
- if (_.isNull(binaryLoc)) {
145
+ if (binaryLoc === null) {
150
146
  throw new Error(
151
147
  `Could not find '${fullBinaryName}' in ${JSON.stringify(binaryLocs)}. ` +
152
148
  `Do you have Android Build Tools ${this.buildToolsVersion ? `v ${this.buildToolsVersion} ` : ''}` +
@@ -235,7 +231,9 @@ export async function getConnectedDevices(
235
231
  ({stdout} = await exec(this.executable.path, args));
236
232
  } catch (e: unknown) {
237
233
  const error = e as Error;
238
- throw new Error(`Error while getting connected devices. Original error: ${error.message}`);
234
+ throw new Error(`Error while getting connected devices. Original error: ${error.message}`, {
235
+ cause: e,
236
+ });
239
237
  }
240
238
  const listHeader = 'List of devices';
241
239
  // expecting adb devices to return output as
@@ -253,7 +251,7 @@ export async function getConnectedDevices(
253
251
  }
254
252
  const devices = stdout
255
253
  .split('\n')
256
- .map(_.trim)
254
+ .map((x) => x.trim())
257
255
  .filter((line) => line && !excludedLines.some((x) => line.includes(x)))
258
256
  .map((line) => {
259
257
  // state is "device", afaic
@@ -271,7 +269,7 @@ export async function getConnectedDevices(
271
269
  }
272
270
  return device;
273
271
  });
274
- if (_.isEmpty(devices)) {
272
+ if (util.isEmpty(devices)) {
275
273
  log.debug('No connected devices have been detected');
276
274
  } else {
277
275
  log.debug(`Connected devices: ${JSON.stringify(devices)}`);
@@ -322,7 +320,9 @@ export async function getDevicesWithRetry(this: ADB, timeoutMs: number = 20000):
322
320
  } catch (e: unknown) {
323
321
  const error = e as Error;
324
322
  if (/Condition unmet/.test(error.message)) {
325
- throw new Error(`Could not find a connected Android device in ${timeoutMs}ms`);
323
+ throw new Error(`Could not find a connected Android device in ${timeoutMs}ms`, {
324
+ cause: e,
325
+ });
326
326
  } else {
327
327
  throw e;
328
328
  }
@@ -346,7 +346,9 @@ export async function reconnect(this: ADB, target: string | null = 'offline'): P
346
346
  await this.adbExec(args);
347
347
  } catch (e: unknown) {
348
348
  const error = e as ExecError;
349
- throw new Error(`Cannot reconnect adb. Original error: ${error.stderr || error.message}`);
349
+ throw new Error(`Cannot reconnect adb. Original error: ${error.stderr || error.message}`, {
350
+ cause: e,
351
+ });
350
352
  }
351
353
  }
352
354
 
@@ -384,7 +386,7 @@ export async function killServer(this: ADB): Promise<void> {
384
386
  *
385
387
  * @returns True if token was reset successfully, false otherwise
386
388
  */
387
- export const resetTelnetAuthToken = _.memoize(
389
+ export const resetTelnetAuthToken = memoize(
388
390
  async function resetTelnetAuthToken(): Promise<boolean> {
389
391
  // The methods is used to remove telnet auth token
390
392
  //
@@ -451,14 +453,14 @@ export async function adbExec<
451
453
  throw new Error('You need to pass in a command to adbExec()');
452
454
  }
453
455
 
454
- const optsCopy = _.cloneDeep(opts ?? {}) as TExecOpts;
456
+ const optsCopy = cloneDeep(opts ?? {}) as TExecOpts;
455
457
  // setting default timeout for each command to prevent infinite wait.
456
458
  optsCopy.timeout = optsCopy.timeout || this.adbExecTimeout || DEFAULT_ADB_EXEC_TIMEOUT;
457
459
  optsCopy.timeoutCapName = optsCopy.timeoutCapName || 'adbExecTimeout'; // For error message
458
460
 
459
461
  const {outputFormat = this.EXEC_OUTPUT_FORMAT.STDOUT} = optsCopy;
460
462
 
461
- cmd = _.isArray(cmd) ? cmd : [cmd];
463
+ cmd = Array.isArray(cmd) ? cmd : [cmd];
462
464
  let adbRetried = false;
463
465
  const execFunc = async (): Promise<string | ExecResult> => {
464
466
  try {
@@ -495,7 +497,7 @@ export async function adbExec<
495
497
  return error.stdout.replace(LINKER_WARNING_REGEXP, '').trim();
496
498
  }
497
499
 
498
- if (_.isNull(error.code)) {
500
+ if (error.code === null) {
499
501
  error.message =
500
502
  `Error executing adbExec. Original error: '${error.message}'. ` +
501
503
  `Try to increase the ${optsCopy.timeout}ms adb execution timeout ` +
@@ -544,7 +546,7 @@ export async function shell<TShellExecOpts extends ShellExecOptions = ShellExecO
544
546
  ): Promise<TShellExecOpts extends TFullOutputOption ? ExecResult : string> {
545
547
  const {privileged} = opts ?? ({} as TShellExecOpts);
546
548
 
547
- const cmdArr = _.isArray(cmd) ? cmd : [cmd];
549
+ const cmdArr = Array.isArray(cmd) ? cmd : [cmd];
548
550
  const fullCmd: string[] = ['shell'];
549
551
  if (privileged) {
550
552
  log.info(`'adb shell ${util.quote(cmdArr)}' requires root access`);
@@ -592,7 +594,7 @@ export function getAdbServerPort(this: ADB): number {
592
594
  */
593
595
  export async function getEmulatorPort(this: ADB): Promise<number> {
594
596
  log.debug('Getting running emulator port');
595
- if (!_.isNil(this.emulatorPort)) {
597
+ if (this.emulatorPort != null) {
596
598
  return this.emulatorPort;
597
599
  }
598
600
  try {
@@ -605,7 +607,7 @@ export async function getEmulatorPort(this: ADB): Promise<number> {
605
607
  }
606
608
  } catch (e: unknown) {
607
609
  const error = e as Error;
608
- throw new Error(`No devices connected. Original error: ${error.message}`);
610
+ throw new Error(`No devices connected. Original error: ${error.message}`, {cause: e});
609
611
  }
610
612
  }
611
613
 
@@ -647,7 +649,7 @@ export async function getConnectedEmulators(
647
649
  return emulators;
648
650
  } catch (e: unknown) {
649
651
  const error = e as Error;
650
- throw new Error(`Error getting emulators. Original error: ${error.message}`);
652
+ throw new Error(`Error getting emulators. Original error: ${error.message}`, {cause: e});
651
653
  }
652
654
  }
653
655
 
@@ -684,7 +686,7 @@ export function setDeviceId(this: ADB, deviceId: string): void {
684
686
  export function setDevice(this: ADB, deviceObj: Device): void {
685
687
  const deviceId = deviceObj.udid;
686
688
  const emPort = this.getPortFromEmulatorString(deviceId);
687
- if (_.isNumber(emPort)) {
689
+ if (typeof emPort === 'number' && !Number.isNaN(emPort)) {
688
690
  this.setEmulatorPort(emPort);
689
691
  }
690
692
  this.setDeviceId(deviceId);
@@ -705,7 +707,7 @@ export async function getRunningAVD(this: ADB, avdName: string): Promise<Device
705
707
  try {
706
708
  const emulators = await this.getConnectedEmulators();
707
709
  for (const emulator of emulators) {
708
- if (_.isNumber(emulator.port)) {
710
+ if (typeof emulator.port === 'number' && !Number.isNaN(emulator.port)) {
709
711
  this.setEmulatorPort(emulator.port);
710
712
  }
711
713
  const runningAVDName = await this.execEmuConsoleCommand(['avd', 'name'], {
@@ -713,7 +715,7 @@ export async function getRunningAVD(this: ADB, avdName: string): Promise<Device
713
715
  execTimeout: 5000,
714
716
  connTimeout: 1000,
715
717
  });
716
- if (_.toLower(avdName) === _.toLower(runningAVDName.trim())) {
718
+ if (avdName.toLowerCase() === runningAVDName.trim().toLowerCase()) {
717
719
  log.debug(`Found emulator '${avdName}' on port ${emulator.port}`);
718
720
  this.setDeviceId(emulator.udid);
719
721
  return emulator;
@@ -723,7 +725,7 @@ export async function getRunningAVD(this: ADB, avdName: string): Promise<Device
723
725
  return null;
724
726
  } catch (e: unknown) {
725
727
  const error = e as Error;
726
- throw new Error(`Error getting AVD. Original error: ${error.message}`);
728
+ throw new Error(`Error getting AVD. Original error: ${error.message}`, {cause: e});
727
729
  }
728
730
  }
729
731
 
@@ -758,7 +760,9 @@ export async function getRunningAVDWithRetry(
758
760
  )) as Device | null;
759
761
  } catch (e: unknown) {
760
762
  const error = e as Error;
761
- throw new Error(`Error getting AVD with retry. Original error: ${error.message}`);
763
+ throw new Error(`Error getting AVD with retry. Original error: ${error.message}`, {
764
+ cause: e,
765
+ });
762
766
  }
763
767
  }
764
768
 
@@ -781,7 +785,7 @@ export async function killAllEmulators(this: ADB): Promise<void> {
781
785
  await exec(cmd, args);
782
786
  } catch (e: unknown) {
783
787
  const error = e as Error;
784
- throw new Error(`Error killing emulators. Original error: ${error.message}`);
788
+ throw new Error(`Error killing emulators. Original error: ${error.message}`, {cause: e});
785
789
  }
786
790
  }
787
791
 
@@ -906,12 +910,12 @@ export async function launchAVD(
906
910
  log.info('The -delay-adb emulator startup detection feature has been explicitly disabled');
907
911
  }
908
912
 
909
- if (!_.isEmpty(args)) {
910
- launchArgs.push(...(_.isArray(args) ? args : util.shellParse(`${args}`)));
913
+ if (!util.isEmpty(args)) {
914
+ launchArgs.push(...(Array.isArray(args) ? args : util.shellParse(`${args}`)));
911
915
  }
912
916
 
913
917
  log.debug(`Running '${emulatorBinaryPath}' with args: ${util.quote(launchArgs)}`);
914
- if (!_.isEmpty(env)) {
918
+ if (!util.isEmpty(env)) {
915
919
  log.debug(`Customized emulator environment: ${JSON.stringify(env)}`);
916
920
  }
917
921
  const proc = new SubProcess(emulatorBinaryPath, launchArgs, {
@@ -939,6 +943,7 @@ export async function launchAVD(
939
943
  const error = e as ExecError;
940
944
  throw new Error(
941
945
  `'${processedAvdName}' Emulator has failed to boot: ${error.stderr || error.message}`,
946
+ {cause: e},
942
947
  );
943
948
  }
944
949
  }
@@ -952,13 +957,13 @@ export async function launchAVD(
952
957
  * @returns Version information object
953
958
  * @throws {Error} If error occurs while getting adb version
954
959
  */
955
- export const getVersion = _.memoize(async function getVersion(this: ADB): Promise<Version> {
960
+ export const getVersion = memoize(async function getVersion(this: ADB): Promise<Version> {
956
961
  let stdout: string;
957
962
  try {
958
963
  stdout = await this.adbExec('version');
959
964
  } catch (e: unknown) {
960
965
  const error = e as ExecError;
961
- throw new Error(`Error getting adb version: ${error.stderr || error.message}`);
966
+ throw new Error(`Error getting adb version: ${error.stderr || error.message}`, {cause: e});
962
967
  }
963
968
 
964
969
  const result: Partial<Version> = {};
@@ -1044,7 +1049,7 @@ export async function waitForEmulatorReady(this: ADB, timeoutMs: number = 20000)
1044
1049
  let suffix = '';
1045
1050
  const servicesValue = services;
1046
1051
  if (servicesValue) {
1047
- const missingServices = _.zip(REQUIRED_SERVICES, requiredServicesRe)
1052
+ const missingServices = zip(REQUIRED_SERVICES, requiredServicesRe)
1048
1053
  .filter(([, pattern]) => !(pattern as RegExp).test(servicesValue))
1049
1054
  .map(([name]) => name);
1050
1055
  suffix = ` (${missingServices} service${missingServices.length === 1 ? ' is' : 's are'} not running)`;
@@ -1133,6 +1138,7 @@ export async function reboot(
1133
1138
  throw new Error(
1134
1139
  `Could not reboot device. Rebooting requires root access and ` +
1135
1140
  `attempt to get root access on device failed with error: '${message}'`,
1141
+ {cause: e},
1136
1142
  );
1137
1143
  }
1138
1144
  throw error;
@@ -1293,6 +1299,7 @@ export async function installMitmCertificate(this: ADB, cert: Buffer | string):
1293
1299
  `Is the certificate properly encoded into base64-string? ` +
1294
1300
  `Do you have root permissions on the device? ` +
1295
1301
  `Original error: ${error.message}`,
1302
+ {cause: err},
1296
1303
  );
1297
1304
  } finally {
1298
1305
  await fs.rimraf(tmpRoot);
@@ -1325,6 +1332,7 @@ export async function isMitmCertificateInstalled(
1325
1332
  `Cannot retrieve the certificate hash. ` +
1326
1333
  `Is the certificate properly encoded into base64-string? ` +
1327
1334
  `Original error: ${error.message}`,
1335
+ {cause: err},
1328
1336
  );
1329
1337
  } finally {
1330
1338
  await fs.rimraf(tmpRoot);
@@ -1353,10 +1361,10 @@ export async function shellChunks(
1353
1361
  let cmdChunk: string[] = [];
1354
1362
  for (const arg of args) {
1355
1363
  const nextCmd = argTransformer(arg);
1356
- if (!_.isArray(nextCmd)) {
1364
+ if (!Array.isArray(nextCmd)) {
1357
1365
  throw new Error('Argument transformer must result in an array');
1358
1366
  }
1359
- if (_.last(nextCmd) !== ';') {
1367
+ if (nextCmd.at(-1) !== ';') {
1360
1368
  nextCmd.push(';');
1361
1369
  }
1362
1370
  if (nextCmd.join(' ').length + cmdChunk.join(' ').length >= MAX_SHELL_BUFFER_LENGTH) {
@@ -1365,7 +1373,7 @@ export async function shellChunks(
1365
1373
  }
1366
1374
  cmdChunk = [...cmdChunk, ...nextCmd];
1367
1375
  }
1368
- if (!_.isEmpty(cmdChunk)) {
1376
+ if (!util.isEmpty(cmdChunk)) {
1369
1377
  commands.push(cmdChunk);
1370
1378
  }
1371
1379
  log.debug(`Got the following command chunks to execute: ${JSON.stringify(commands)}`);
@@ -1392,18 +1400,18 @@ export async function shellChunks(
1392
1400
  */
1393
1401
  export function toAvdLocaleArgs(language: string | null, country: string | null): string[] {
1394
1402
  const result: string[] = [];
1395
- if (language && _.isString(language)) {
1403
+ if (language && typeof language === 'string') {
1396
1404
  result.push('-prop', `persist.sys.language=${language.toLowerCase()}`);
1397
1405
  }
1398
- if (country && _.isString(country)) {
1406
+ if (country && typeof country === 'string') {
1399
1407
  result.push('-prop', `persist.sys.country=${country.toUpperCase()}`);
1400
1408
  }
1401
1409
  let locale: string | undefined;
1402
- if (_.isString(language) && _.isString(country) && language && country) {
1410
+ if (typeof language === 'string' && typeof country === 'string' && language && country) {
1403
1411
  locale = language.toLowerCase() + '-' + country.toUpperCase();
1404
- } else if (language && _.isString(language)) {
1412
+ } else if (language && typeof language === 'string') {
1405
1413
  locale = language.toLowerCase();
1406
- } else if (country && _.isString(country)) {
1414
+ } else if (country && typeof country === 'string') {
1407
1415
  locale = country;
1408
1416
  }
1409
1417
  if (locale) {
@@ -1419,7 +1427,7 @@ export function toAvdLocaleArgs(language: string | null, country: string | null)
1419
1427
  * @param sdkRoot - The Android SDK root directory path
1420
1428
  * @returns Array of build-tools directory paths (newest first)
1421
1429
  */
1422
- export const getBuildToolsDirs = _.memoize(async function getBuildToolsDirs(
1430
+ export const getBuildToolsDirs = memoize(async function getBuildToolsDirs(
1423
1431
  sdkRoot: string,
1424
1432
  ): Promise<string[]> {
1425
1433
  let buildToolsDirs = await fs.glob('*/', {
@@ -1,4 +1,4 @@
1
- import * as emuConstants from './emu-constants';
1
+ import type * as emuConstants from './emu-constants';
2
2
 
3
3
  export type StringRecord<T = any> = Record<string, T>;
4
4
 
@@ -0,0 +1,6 @@
1
+ export const APKS_EXTENSION = '.apks';
2
+ export const APK_EXTENSION = '.apk';
3
+ export const APK_INSTALL_TIMEOUT = 60000;
4
+ export const DEFAULT_ADB_EXEC_TIMEOUT = 20000; // in milliseconds
5
+
6
+ export const MODULE_NAME = 'appium-adb';