appium-android-driver 12.4.6 → 12.4.8

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 (50) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/build/lib/commands/app-management.d.ts +167 -113
  3. package/build/lib/commands/app-management.d.ts.map +1 -1
  4. package/build/lib/commands/app-management.js +147 -103
  5. package/build/lib/commands/app-management.js.map +1 -1
  6. package/build/lib/commands/file-actions.d.ts +37 -19
  7. package/build/lib/commands/file-actions.d.ts.map +1 -1
  8. package/build/lib/commands/file-actions.js +44 -58
  9. package/build/lib/commands/file-actions.js.map +1 -1
  10. package/build/lib/commands/find.d.ts +20 -3
  11. package/build/lib/commands/find.d.ts.map +1 -1
  12. package/build/lib/commands/find.js +10 -3
  13. package/build/lib/commands/find.js.map +1 -1
  14. package/build/lib/commands/intent.d.ts +103 -107
  15. package/build/lib/commands/intent.d.ts.map +1 -1
  16. package/build/lib/commands/intent.js +103 -97
  17. package/build/lib/commands/intent.js.map +1 -1
  18. package/build/lib/commands/performance.d.ts +80 -51
  19. package/build/lib/commands/performance.d.ts.map +1 -1
  20. package/build/lib/commands/performance.js +113 -89
  21. package/build/lib/commands/performance.js.map +1 -1
  22. package/build/lib/commands/recordscreen.d.ts +25 -40
  23. package/build/lib/commands/recordscreen.d.ts.map +1 -1
  24. package/build/lib/commands/recordscreen.js +46 -63
  25. package/build/lib/commands/recordscreen.js.map +1 -1
  26. package/build/lib/commands/shell.d.ts +21 -0
  27. package/build/lib/commands/shell.d.ts.map +1 -1
  28. package/build/lib/commands/shell.js +21 -0
  29. package/build/lib/commands/shell.js.map +1 -1
  30. package/build/lib/commands/streamscreen.d.ts +34 -64
  31. package/build/lib/commands/streamscreen.d.ts.map +1 -1
  32. package/build/lib/commands/streamscreen.js +41 -80
  33. package/build/lib/commands/streamscreen.js.map +1 -1
  34. package/build/lib/commands/types.d.ts.map +1 -1
  35. package/build/lib/driver.d.ts +2 -1
  36. package/build/lib/driver.d.ts.map +1 -1
  37. package/build/lib/driver.js.map +1 -1
  38. package/lib/commands/app-management.ts +639 -0
  39. package/lib/commands/{file-actions.js → file-actions.ts} +88 -74
  40. package/lib/commands/find.ts +20 -3
  41. package/lib/commands/intent.ts +422 -0
  42. package/lib/commands/{performance.js → performance.ts} +167 -108
  43. package/lib/commands/{recordscreen.js → recordscreen.ts} +77 -73
  44. package/lib/commands/shell.ts +21 -0
  45. package/lib/commands/{streamscreen.js → streamscreen.ts} +86 -109
  46. package/lib/commands/types.ts +17 -0
  47. package/lib/driver.ts +2 -1
  48. package/package.json +1 -1
  49. package/lib/commands/app-management.js +0 -533
  50. package/lib/commands/intent.js +0 -409
@@ -2,6 +2,8 @@ import _ from 'lodash';
2
2
  import {fs, util, zip, tempDir} from '@appium/support';
3
3
  import path from 'path';
4
4
  import {errors} from 'appium/driver';
5
+ import type {AndroidDriver} from '../driver';
6
+ import type {ADB} from 'appium-adb';
5
7
 
6
8
  const CONTAINER_PATH_MARKER = '@';
7
9
  // https://regex101.com/r/PLdB0G/2
@@ -9,21 +11,31 @@ const CONTAINER_PATH_PATTERN = new RegExp(`^${CONTAINER_PATH_MARKER}([^/]+)/(.+)
9
11
  const ANDROID_MEDIA_RESCAN_INTENT = 'android.intent.action.MEDIA_SCANNER_SCAN_FILE';
10
12
 
11
13
  /**
12
- * @this {import('../driver').AndroidDriver}
13
- * @param {string} remotePath The full path to the remote file or a specially formatted path, which
14
+ * Pulls a file from the remote device.
15
+ *
16
+ * The full path to the remote file or a specially formatted path, which
17
+ * points to an item inside an app bundle, for example `@my.app.id/my/path`.
18
+ * It is mandatory for the app bundle to have debugging enabled in order to
19
+ * use the latter `remotePath` format.
20
+ *
21
+ * @param remotePath The full path to the remote file or a specially formatted path, which
14
22
  * points to an item inside an app bundle, for example `@my.app.id/my/path`.
15
23
  * It is mandatory for the app bundle to have debugging enabled in order to
16
24
  * use the latter `remotePath` format.
17
- * @returns {Promise<string>}
25
+ * @returns Promise that resolves to the file content as a base64-encoded string.
26
+ * @throws {errors.InvalidArgumentError} If the remote path points to a folder instead of a file.
18
27
  */
19
- export async function pullFile(remotePath) {
28
+ export async function pullFile(
29
+ this: AndroidDriver,
30
+ remotePath: string,
31
+ ): Promise<string> {
20
32
  if (remotePath.endsWith('/')) {
21
33
  throw new errors.InvalidArgumentError(
22
34
  `It is expected that remote path points to a file and not to a folder. ` +
23
35
  `'${remotePath}' is given instead`,
24
36
  );
25
37
  }
26
- let tmpDestination = null;
38
+ let tmpDestination: string | null = null;
27
39
  if (remotePath.startsWith(CONTAINER_PATH_MARKER)) {
28
40
  const [packageId, pathInContainer] = parseContainerPath(remotePath);
29
41
  this.log.debug(
@@ -41,7 +53,7 @@ export async function pullFile(remotePath) {
41
53
  throw this.log.errorWithException(
42
54
  `Cannot access the container of '${packageId}' application. ` +
43
55
  `Is the application installed and has 'debuggable' build option set to true? ` +
44
- `Original error: ${/** @type {Error} */ (e).message}`,
56
+ `Original error: ${(e as Error).message}`,
45
57
  );
46
58
  }
47
59
  }
@@ -60,15 +72,27 @@ export async function pullFile(remotePath) {
60
72
  }
61
73
 
62
74
  /**
63
- * @this {import('../driver').AndroidDriver}
64
- * @param {string} remotePath The full path to the remote file or a specially formatted path, which
75
+ * Pushes a file to the remote device.
76
+ *
77
+ * The full path to the remote file or a specially formatted path, which
65
78
  * points to an item inside an app bundle, for example `@my.app.id/my/path`.
66
79
  * It is mandatory for the app bundle to have debugging enabled in order to
67
80
  * use the latter `remotePath` format.
68
- * @param {string} base64Data Base64-encoded content of the file to be pushed.
69
- * @returns {Promise<void>}
81
+ *
82
+ * @param remotePath The full path to the remote file or a specially formatted path, which
83
+ * points to an item inside an app bundle, for example `@my.app.id/my/path`.
84
+ * It is mandatory for the app bundle to have debugging enabled in order to
85
+ * use the latter `remotePath` format.
86
+ * @param base64Data Base64-encoded content of the file to be pushed.
87
+ * Can be a string or an array of numbers (for Java clients that send byte arrays).
88
+ * @returns Promise that resolves when the file is pushed.
89
+ * @throws {errors.InvalidArgumentError} If the remote path points to a folder instead of a file.
70
90
  */
71
- export async function pushFile(remotePath, base64Data) {
91
+ export async function pushFile(
92
+ this: AndroidDriver,
93
+ remotePath: string,
94
+ base64Data: string | number[],
95
+ ): Promise<void> {
72
96
  if (remotePath.endsWith('/')) {
73
97
  throw new errors.InvalidArgumentError(
74
98
  `It is expected that remote path points to a file and not to a folder. ` +
@@ -76,13 +100,16 @@ export async function pushFile(remotePath, base64Data) {
76
100
  );
77
101
  }
78
102
  const localFile = await tempDir.path({prefix: 'appium', suffix: '.tmp'});
103
+ let base64String: string;
79
104
  if (_.isArray(base64Data)) {
80
105
  // some clients (ahem) java, send a byte array encoding utf8 characters
81
106
  // instead of a string, which would be infinitely better!
82
- base64Data = Buffer.from(base64Data).toString('utf8');
107
+ base64String = Buffer.from(base64Data).toString('utf8');
108
+ } else {
109
+ base64String = base64Data;
83
110
  }
84
- const content = Buffer.from(base64Data, 'base64');
85
- let tmpDestination = null;
111
+ const content = Buffer.from(base64String, 'base64');
112
+ let tmpDestination: string | null = null;
86
113
  try {
87
114
  await fs.writeFile(localFile, content.toString('binary'), 'binary');
88
115
  if (remotePath.startsWith(CONTAINER_PATH_MARKER)) {
@@ -110,7 +137,7 @@ export async function pushFile(remotePath, base64Data) {
110
137
  throw this.log.errorWithException(
111
138
  `Cannot access the container of '${packageId}' application. ` +
112
139
  `Is the application installed and has 'debuggable' build option set to true? ` +
113
- `Original error: ${/** @type {Error} */ (e).message}`,
140
+ `Original error: ${(e as Error).message}`,
114
141
  );
115
142
  }
116
143
  } else {
@@ -132,11 +159,15 @@ export async function pushFile(remotePath, base64Data) {
132
159
  }
133
160
 
134
161
  /**
135
- * @this {import('../driver').AndroidDriver}
136
- * @param {string} remotePath The full path to the remote folder
137
- * @returns {Promise<string>}
162
+ * Pulls a folder from the remote device.
163
+ *
164
+ * @param remotePath The full path to the remote folder.
165
+ * @returns Promise that resolves to the folder content as a base64-encoded zip file string.
138
166
  */
139
- export async function pullFolder(remotePath) {
167
+ export async function pullFolder(
168
+ this: AndroidDriver,
169
+ remotePath: string,
170
+ ): Promise<string> {
140
171
  const tmpRoot = await tempDir.openDir();
141
172
  try {
142
173
  await this.adb.pull(remotePath, tmpRoot);
@@ -151,12 +182,17 @@ export async function pullFolder(remotePath) {
151
182
  }
152
183
 
153
184
  /**
154
- * @this {import('../driver').AndroidDriver}
155
- * @param {string} remotePath The full path to the remote file or a file inside an application bundle
156
- * (for example `@my.app.id/path/in/bundle`)
157
- * @returns {Promise<boolean>}
185
+ * Deletes a file from the remote device.
186
+ *
187
+ * @param remotePath The full path to the remote file or a file inside an application bundle
188
+ * (for example `@my.app.id/path/in/bundle`).
189
+ * @returns Promise that resolves to `true` if the file was successfully deleted, `false` if it doesn't exist.
190
+ * @throws {errors.InvalidArgumentError} If the remote path points to a folder instead of a file.
158
191
  */
159
- export async function mobileDeleteFile(remotePath) {
192
+ export async function mobileDeleteFile(
193
+ this: AndroidDriver,
194
+ remotePath: string,
195
+ ): Promise<boolean> {
160
196
  if (remotePath.endsWith('/')) {
161
197
  throw new errors.InvalidArgumentError(
162
198
  `It is expected that remote path points to a folder and not to a file. ` +
@@ -169,21 +205,20 @@ export async function mobileDeleteFile(remotePath) {
169
205
  /**
170
206
  * Deletes the given folder or file from the remote device
171
207
  *
172
- * @param {ADB} adb
173
- * @param {string} remotePath The full path to the remote folder
174
- * or file (folder names must end with a single slash)
175
208
  * @throws {Error} If the provided remote path is invalid or
176
209
  * the package content cannot be accessed
177
- * @returns {Promise<boolean>} `true` if the remote item has been successfully deleted.
210
+ * @returns `true` if the remote item has been successfully deleted.
178
211
  * If the remote path is valid, but the remote path does not exist
179
212
  * this function return `false`.
180
- * @this {import('../driver').AndroidDriver}
181
213
  */
182
- async function deleteFileOrFolder(adb, remotePath) {
214
+ async function deleteFileOrFolder(
215
+ this: AndroidDriver,
216
+ adb: ADB,
217
+ remotePath: string,
218
+ ): Promise<boolean> {
183
219
  const {isDir, isPresent, isFile} = createFSTests(adb);
184
220
  let dstPath = remotePath;
185
- /** @type {string|undefined} */
186
- let pkgId;
221
+ let pkgId: string | undefined;
187
222
  if (remotePath.startsWith(CONTAINER_PATH_MARKER)) {
188
223
  const [packageId, pathInContainer] = parseContainerPath(remotePath);
189
224
  this.log.debug(`Parsed package identifier '${packageId}' from '${remotePath}'`);
@@ -198,7 +233,7 @@ async function deleteFileOrFolder(adb, remotePath) {
198
233
  throw this.log.errorWithException(
199
234
  `Cannot access the container of '${pkgId}' application. ` +
200
235
  `Is the application installed and has 'debuggable' build option set to true? ` +
201
- `Original error: ${/** @type {Error} */ (e).message}`,
236
+ `Original error: ${(e as Error).message}`,
202
237
  );
203
238
  }
204
239
  }
@@ -233,13 +268,11 @@ async function deleteFileOrFolder(adb, remotePath) {
233
268
  /**
234
269
  * Parses the actual destination path from the given value
235
270
  *
236
- * @param {string} remotePath The preformatted remote path, which looks like
237
- * `@my.app.id/my/path`
238
- * @returns {Array<string>} An array, where the first item is the parsed package
271
+ * @returns An array, where the first item is the parsed package
239
272
  * identifier and the second one is the actual destination path inside the package.
240
273
  * @throws {Error} If the given string cannot be parsed
241
274
  */
242
- function parseContainerPath(remotePath) {
275
+ function parseContainerPath(remotePath: string): [string, string] {
243
276
  const match = CONTAINER_PATH_PATTERN.exec(remotePath);
244
277
  if (!match) {
245
278
  throw new Error(
@@ -254,11 +287,11 @@ function parseContainerPath(remotePath) {
254
287
  * Scans the given file/folder on the remote device
255
288
  * and adds matching items to the device's media library.
256
289
  * Exceptions are ignored and written into the log.
257
- *
258
- * @this {import('../driver').AndroidDriver}
259
- * @param {string} remotePath The file/folder path on the remote device
260
290
  */
261
- async function scanMedia(remotePath) {
291
+ async function scanMedia(
292
+ this: AndroidDriver,
293
+ remotePath: string,
294
+ ): Promise<void> {
262
295
  this.log.debug(`Performing media scan of '${remotePath}'`);
263
296
  try {
264
297
  // https://github.com/appium/appium/issues/16184
@@ -275,7 +308,7 @@ async function scanMedia(remotePath) {
275
308
  ]);
276
309
  }
277
310
  } catch (e) {
278
- const err = /** @type {any} */ (e);
311
+ const err = e as {stderr?: string; message?: string};
279
312
  // FIXME: what has a `stderr` prop?
280
313
  this.log.warn(
281
314
  `Ignoring an unexpected error upon media scanning of '${remotePath}': ${
@@ -288,27 +321,20 @@ async function scanMedia(remotePath) {
288
321
  /**
289
322
  * A small helper, which escapes single quotes in paths,
290
323
  * so they are safe to be passed as arguments of shell commands
291
- *
292
- * @param {string} p The initial remote path
293
- * @returns {string} The escaped path value
294
324
  */
295
- function escapePath(p) {
325
+ function escapePath(p: string): string {
296
326
  return p.replace(/'/g, `\\'`);
297
327
  }
298
328
 
299
329
  /**
300
330
  * Factory providing filesystem test functions using ADB
301
- * @param {ADB} adb
302
331
  */
303
- function createFSTests(adb) {
304
- /**
305
- *
306
- * @param {string} p
307
- * @param {'d'|'f'|'e'} op
308
- * @param {string} [runAs]
309
- * @returns
310
- */
311
- const performRemoteFsCheck = async (p, op, runAs) => {
332
+ function createFSTests(adb: ADB) {
333
+ const performRemoteFsCheck = async (
334
+ p: string,
335
+ op: 'd' | 'f' | 'e',
336
+ runAs?: string,
337
+ ): Promise<boolean> => {
312
338
  const passFlag = '__PASS__';
313
339
  const checkCmd = `[ -${op} '${escapePath(p)}' ] && echo ${passFlag}`;
314
340
  const fullCmd = runAs ? `run-as ${runAs} ${checkCmd}` : checkCmd;
@@ -319,27 +345,15 @@ function createFSTests(adb) {
319
345
  }
320
346
  };
321
347
 
322
- /**
323
- * @param {string} p
324
- * @param {string} [runAs]
325
- */
326
- const isFile = async (p, runAs) => await performRemoteFsCheck(p, 'f', runAs);
327
- /**
328
- * @param {string} p
329
- * @param {string} [runAs]
330
- */
331
- const isDir = async (p, runAs) => await performRemoteFsCheck(p, 'd', runAs);
332
- /**
333
- * @param {string} p
334
- * @param {string} [runAs]
335
- */
336
- const isPresent = async (p, runAs) => await performRemoteFsCheck(p, 'e', runAs);
348
+ const isFile = async (p: string, runAs?: string): Promise<boolean> =>
349
+ await performRemoteFsCheck(p, 'f', runAs);
350
+ const isDir = async (p: string, runAs?: string): Promise<boolean> =>
351
+ await performRemoteFsCheck(p, 'd', runAs);
352
+ const isPresent = async (p: string, runAs?: string): Promise<boolean> =>
353
+ await performRemoteFsCheck(p, 'e', runAs);
337
354
 
338
355
  return {isFile, isDir, isPresent};
339
356
  }
340
357
 
341
358
  // #endregion
342
359
 
343
- /**
344
- * @typedef {import('appium-adb').ADB} ADB
345
- */
@@ -1,7 +1,4 @@
1
1
  /* eslint-disable @typescript-eslint/no-unused-vars */
2
- /**
3
- * @module
4
- */
5
2
 
6
3
  import _ from 'lodash';
7
4
  import {errors, isErrorType} from 'appium/driver';
@@ -9,6 +6,16 @@ import type {AndroidDriver} from '../driver';
9
6
  import type {Element} from '@appium/types';
10
7
  import type {FindElementOpts} from './types';
11
8
 
9
+ /**
10
+ * @param strategy The element location strategy to use (e.g., 'id', 'xpath', 'class name').
11
+ * @param selector The selector value to search for.
12
+ * @param mult If `true`, searches for multiple elements; if `false`, searches for a single element.
13
+ * @param context The context (e.g., webview) in which to search. Defaults to empty string (native context).
14
+ * @returns If `mult` is `false`, returns a single `Element` object.
15
+ * If `mult` is `true`, returns an array of `Element` objects (may be empty).
16
+ * @throws {Error} If `selector` is not provided.
17
+ * @throws {errors.NoSuchElementError} If a single element search fails and no element is found.
18
+ */
12
19
  export async function findElOrEls(
13
20
  this: AndroidDriver,
14
21
  strategy: string,
@@ -88,6 +95,16 @@ export async function findElOrEls(
88
95
  return element as Element;
89
96
  }
90
97
 
98
+ /**
99
+ * Performs the actual element search operation.
100
+ *
101
+ * This is an abstract method that must be implemented by subclasses or specific
102
+ * context handlers (e.g., native context, webview context).
103
+ *
104
+ * @param params The search parameters containing strategy, selector, context, and multiple flag.
105
+ * @returns A single `Element` if `params.multiple` is `false`, or an array of `Element` objects if `true`.
106
+ * @throws {errors.NotImplementedError} This method must be implemented by the specific context handler.
107
+ */
91
108
  export async function doFindElementOrEls(
92
109
  this: AndroidDriver,
93
110
  params: FindElementOpts,