appium-android-driver 12.4.7 → 12.4.9

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 (39) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/build/lib/commands/file-actions.d.ts +37 -19
  3. package/build/lib/commands/file-actions.d.ts.map +1 -1
  4. package/build/lib/commands/file-actions.js +44 -58
  5. package/build/lib/commands/file-actions.js.map +1 -1
  6. package/build/lib/commands/geolocation.d.ts +44 -36
  7. package/build/lib/commands/geolocation.d.ts.map +1 -1
  8. package/build/lib/commands/geolocation.js +38 -32
  9. package/build/lib/commands/geolocation.js.map +1 -1
  10. package/build/lib/commands/intent.d.ts +103 -107
  11. package/build/lib/commands/intent.d.ts.map +1 -1
  12. package/build/lib/commands/intent.js +103 -97
  13. package/build/lib/commands/intent.js.map +1 -1
  14. package/build/lib/commands/log.d.ts +44 -48
  15. package/build/lib/commands/log.d.ts.map +1 -1
  16. package/build/lib/commands/log.js +30 -54
  17. package/build/lib/commands/log.js.map +1 -1
  18. package/build/lib/commands/network.d.ts +59 -39
  19. package/build/lib/commands/network.d.ts.map +1 -1
  20. package/build/lib/commands/network.js +65 -45
  21. package/build/lib/commands/network.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/types.d.ts.map +1 -1
  27. package/build/lib/driver.d.ts +11 -10
  28. package/build/lib/driver.d.ts.map +1 -1
  29. package/build/lib/driver.js.map +1 -1
  30. package/lib/commands/{file-actions.js → file-actions.ts} +88 -74
  31. package/lib/commands/{geolocation.js → geolocation.ts} +85 -54
  32. package/lib/commands/intent.ts +422 -0
  33. package/lib/commands/{log.js → log.ts} +68 -73
  34. package/lib/commands/{network.js → network.ts} +106 -59
  35. package/lib/commands/{recordscreen.js → recordscreen.ts} +77 -73
  36. package/lib/commands/types.ts +17 -0
  37. package/lib/driver.ts +2 -1
  38. package/package.json +1 -1
  39. package/lib/commands/intent.js +0 -409
@@ -0,0 +1,422 @@
1
+ import _ from 'lodash';
2
+ import {errors} from 'appium/driver';
3
+ import {util} from '@appium/support';
4
+ import type {AndroidDriver} from '../driver';
5
+ import type {IntentOpts} from './types';
6
+
7
+ const NO_VALUE_ARG_TYPE = 'sn';
8
+ const SUPPORTED_EXTRA_TYPES = [
9
+ 's',
10
+ NO_VALUE_ARG_TYPE,
11
+ 'z',
12
+ 'i',
13
+ 'l',
14
+ 'f',
15
+ 'u',
16
+ 'cn',
17
+ 'ia',
18
+ 'ial',
19
+ 'la',
20
+ 'lal',
21
+ 'fa',
22
+ 'fal',
23
+ 'sa',
24
+ 'sal',
25
+ ] as const;
26
+
27
+ /**
28
+ * Starts an Android activity.
29
+ *
30
+ * @deprecated Use {@link mobileStartActivity} instead.
31
+ * @param appPackage The package name of the application to start.
32
+ * @param appActivity The activity name to start.
33
+ * @param appWaitPackage The package name to wait for. Defaults to `appPackage` if not provided.
34
+ * @param appWaitActivity The activity name to wait for. Defaults to `appActivity` if not provided.
35
+ * @param intentAction The intent action to use.
36
+ * @param intentCategory The intent category to use.
37
+ * @param intentFlags The intent flags to use.
38
+ * @param optionalIntentArguments Optional intent arguments.
39
+ * @param dontStopAppOnReset If `true`, does not stop the app on reset. If not provided, uses the capability value.
40
+ * @returns Promise that resolves when the activity is started.
41
+ */
42
+ export async function startActivity(
43
+ this: AndroidDriver,
44
+ appPackage: string,
45
+ appActivity: string,
46
+ appWaitPackage?: string,
47
+ appWaitActivity?: string,
48
+ intentAction?: string,
49
+ intentCategory?: string,
50
+ intentFlags?: string,
51
+ optionalIntentArguments?: string,
52
+ dontStopAppOnReset?: boolean,
53
+ ): Promise<void> {
54
+ this.log.debug(`Starting package '${appPackage}' and activity '${appActivity}'`);
55
+
56
+ // dontStopAppOnReset is both an argument here, and a desired capability
57
+ // if the argument is set, use it, otherwise use the cap
58
+ if (!util.hasValue(dontStopAppOnReset)) {
59
+ dontStopAppOnReset = !!this.opts.dontStopAppOnReset;
60
+ }
61
+
62
+ const args = {
63
+ pkg: appPackage,
64
+ activity: appActivity,
65
+ waitPkg: appWaitPackage || appPackage,
66
+ waitActivity: appWaitActivity || appActivity,
67
+ action: intentAction,
68
+ category: intentCategory,
69
+ flags: intentFlags,
70
+ optionalIntentArguments,
71
+ stopApp: !dontStopAppOnReset,
72
+ };
73
+ this._cachedActivityArgs = this._cachedActivityArgs || {};
74
+ this._cachedActivityArgs[`${args.waitPkg}/${args.waitActivity}`] = args;
75
+ await this.adb.startApp(args);
76
+ }
77
+
78
+ /**
79
+ * Starts an Android activity using the activity manager.
80
+ *
81
+ * @param wait Set it to `true` if you want to block the method call
82
+ * until the activity manager's process returns the control to the system.
83
+ * `false` by default.
84
+ * @param stop Set it to `true` to force stop the target
85
+ * app before starting the activity. `false` by default.
86
+ * @param windowingMode The windowing mode to launch the activity into.
87
+ * Check https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/app/WindowConfiguration.java
88
+ * for more details on possible windowing modes (constants starting with `WINDOWING_MODE_`).
89
+ * @param activityType The activity type to launch the activity as.
90
+ * Check https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/app/WindowConfiguration.java
91
+ * for more details on possible activity types (constants starting with `ACTIVITY_TYPE_`).
92
+ * @param display The display identifier to launch the activity into.
93
+ * @param user The user ID for which the activity is started.
94
+ * @param intent The name of the activity intent to start, for example
95
+ * `com.some.package.name/.YourActivitySubClassName`.
96
+ * @param action Action name.
97
+ * @param pkg Package name.
98
+ * @param uri Unified resource identifier.
99
+ * @param mimeType Mime type.
100
+ * @param identifier Optional identifier.
101
+ * @param component Component name.
102
+ * @param categories One or more category names.
103
+ * @param extras Optional intent arguments. Must be represented as array of arrays,
104
+ * where each subarray item contains two or three string items: value type, key name and the value itself.
105
+ * See {@link IntentOpts} for supported value types.
106
+ * @param flags Intent startup-specific flags as a hexadecimal string.
107
+ * @returns Promise that resolves to the command output string.
108
+ */
109
+ export async function mobileStartActivity(
110
+ this: AndroidDriver,
111
+ wait?: boolean,
112
+ stop?: boolean,
113
+ windowingMode?: string | number,
114
+ activityType?: string | number,
115
+ display?: number | string,
116
+ user?: string,
117
+ intent?: string,
118
+ action?: string,
119
+ pkg?: string,
120
+ uri?: string,
121
+ mimeType?: string,
122
+ identifier?: string,
123
+ component?: string,
124
+ categories?: string | string[],
125
+ extras?: string[][],
126
+ flags?: string,
127
+ ): Promise<string> {
128
+ const cmd = [
129
+ 'am',
130
+ 'start-activity',
131
+ ];
132
+ if (!_.isNil(user)) {
133
+ cmd.push('--user', String(user));
134
+ }
135
+ if (wait) {
136
+ cmd.push('-W');
137
+ }
138
+ if (stop) {
139
+ cmd.push('-S');
140
+ }
141
+ if (!_.isNil(windowingMode)) {
142
+ cmd.push('--windowingMode', String(windowingMode));
143
+ }
144
+ if (!_.isNil(activityType)) {
145
+ cmd.push('--activityType', String(activityType));
146
+ }
147
+ if (!_.isNil(display)) {
148
+ cmd.push('--display', String(display));
149
+ }
150
+ cmd.push(...parseIntentSpec({
151
+ intent,
152
+ action,
153
+ package: pkg,
154
+ uri,
155
+ mimeType,
156
+ identifier,
157
+ component,
158
+ categories,
159
+ extras,
160
+ flags,
161
+ }));
162
+ return await this.adb.shell(cmd);
163
+ }
164
+
165
+ /**
166
+ * Sends a broadcast intent to the Android system.
167
+ *
168
+ * @param receiverPermission Require receiver to hold the given permission.
169
+ * @param allowBackgroundActivityStarts Whether the receiver may start activities even if in the background.
170
+ * @param user The user ID for which the broadcast is sent.
171
+ * The `current` alias assumes the current user ID. `all` by default.
172
+ * @param intent The name of the activity intent to broadcast, for example
173
+ * `com.some.package.name/.YourServiceSubClassName`.
174
+ * @param action Action name.
175
+ * @param pkg Package name.
176
+ * @param uri Unified resource identifier.
177
+ * @param mimeType Mime type.
178
+ * @param identifier Optional identifier.
179
+ * @param component Component name.
180
+ * @param categories One or more category names.
181
+ * @param extras Optional intent arguments. Must be represented as array of arrays,
182
+ * where each subarray item contains two or three string items: value type, key name and the value itself.
183
+ * See {@link IntentOpts} for supported value types.
184
+ * @param flags Intent startup-specific flags as a hexadecimal string.
185
+ * @returns Promise that resolves to the command output string.
186
+ */
187
+ export async function mobileBroadcast(
188
+ this: AndroidDriver,
189
+ receiverPermission?: string,
190
+ allowBackgroundActivityStarts?: boolean,
191
+ user?: string | number,
192
+ intent?: string,
193
+ action?: string,
194
+ pkg?: string,
195
+ uri?: string,
196
+ mimeType?: string,
197
+ identifier?: string,
198
+ component?: string,
199
+ categories?: string | string[],
200
+ extras?: string[][],
201
+ flags?: string,
202
+ ): Promise<string> {
203
+ const cmd = ['am', 'broadcast'];
204
+ if (!_.isNil(user)) {
205
+ cmd.push('--user', String(user));
206
+ }
207
+ if (receiverPermission) {
208
+ cmd.push('--receiver-permission', receiverPermission);
209
+ }
210
+ if (allowBackgroundActivityStarts) {
211
+ cmd.push('--allow-background-activity-starts');
212
+ }
213
+ cmd.push(...parseIntentSpec({
214
+ intent,
215
+ action,
216
+ package: pkg,
217
+ uri,
218
+ mimeType,
219
+ identifier,
220
+ component,
221
+ categories,
222
+ extras,
223
+ flags,
224
+ }));
225
+ return await this.adb.shell(cmd);
226
+ }
227
+
228
+ /**
229
+ * Starts an Android service.
230
+ *
231
+ * @param foreground Set it to `true` if your service must be started as foreground service.
232
+ * This option is ignored if the API level of the device under test is below 26 (Android 8).
233
+ * @param user The user ID for which the service is started.
234
+ * The `current` user id is used by default.
235
+ * @param intent The name of the activity intent to start, for example
236
+ * `com.some.package.name/.YourServiceSubClassName`.
237
+ * @param action Action name.
238
+ * @param pkg Package name.
239
+ * @param uri Unified resource identifier.
240
+ * @param mimeType Mime type.
241
+ * @param identifier Optional identifier.
242
+ * @param component Component name.
243
+ * @param categories One or more category names.
244
+ * @param extras Optional intent arguments. Must be represented as array of arrays,
245
+ * where each subarray item contains two or three string items: value type, key name and the value itself.
246
+ * See {@link IntentOpts} for supported value types.
247
+ * @param flags Intent startup-specific flags as a hexadecimal string.
248
+ * @returns Promise that resolves to the command output string.
249
+ */
250
+ export async function mobileStartService(
251
+ this: AndroidDriver,
252
+ foreground?: boolean,
253
+ user?: string,
254
+ intent?: string,
255
+ action?: string,
256
+ pkg?: string,
257
+ uri?: string,
258
+ mimeType?: string,
259
+ identifier?: string,
260
+ component?: string,
261
+ categories?: string | string[],
262
+ extras?: string[][],
263
+ flags?: string,
264
+ ): Promise<string> {
265
+ const cmd = ['am'];
266
+ cmd.push(foreground ? 'start-foreground-service' : 'start-service');
267
+ if (!_.isNil(user)) {
268
+ cmd.push('--user', String(user));
269
+ }
270
+ cmd.push(...parseIntentSpec({
271
+ intent,
272
+ action,
273
+ package: pkg,
274
+ uri,
275
+ mimeType,
276
+ identifier,
277
+ component,
278
+ categories,
279
+ extras,
280
+ flags,
281
+ }));
282
+ return await this.adb.shell(cmd);
283
+ }
284
+
285
+ /**
286
+ * Stops an Android service.
287
+ *
288
+ * @param user The user ID for which the service is stopped.
289
+ * @param intent The name of the activity intent to stop, for example
290
+ * `com.some.package.name/.YourServiceSubClassName`.
291
+ * @param action Action name.
292
+ * @param pkg Package name.
293
+ * @param uri Unified resource identifier.
294
+ * @param mimeType Mime type.
295
+ * @param identifier Optional identifier.
296
+ * @param component Component name.
297
+ * @param categories One or more category names.
298
+ * @param extras Optional intent arguments. Must be represented as array of arrays,
299
+ * where each subarray item contains two or three string items: value type, key name and the value itself.
300
+ * See {@link IntentOpts} for supported value types.
301
+ * @param flags Intent startup-specific flags as a hexadecimal string.
302
+ * @returns Promise that resolves to the command output string.
303
+ * If the service was already stopped, returns the error message.
304
+ */
305
+ export async function mobileStopService(
306
+ this: AndroidDriver,
307
+ user?: string,
308
+ intent?: string,
309
+ action?: string,
310
+ pkg?: string,
311
+ uri?: string,
312
+ mimeType?: string,
313
+ identifier?: string,
314
+ component?: string,
315
+ categories?: string | string[],
316
+ extras?: string[][],
317
+ flags?: string,
318
+ ): Promise<string> {
319
+ const cmd = [
320
+ 'am',
321
+ 'stop-service',
322
+ ];
323
+ if (!_.isNil(user)) {
324
+ cmd.push('--user', String(user));
325
+ }
326
+ cmd.push(...parseIntentSpec({
327
+ intent,
328
+ action,
329
+ package: pkg,
330
+ uri,
331
+ mimeType,
332
+ identifier,
333
+ component,
334
+ categories,
335
+ extras,
336
+ flags,
337
+ }));
338
+ try {
339
+ return await this.adb.shell(cmd);
340
+ } catch (e) {
341
+ // https://github.com/appium/appium-uiautomator2-driver/issues/792
342
+ const err = e as {code?: number; stderr?: string};
343
+ if (err.code === 255 && err.stderr?.includes('Service stopped')) {
344
+ return err.stderr;
345
+ }
346
+ throw e;
347
+ }
348
+ }
349
+
350
+ // #region Internal helpers
351
+
352
+ function parseIntentSpec(opts: IntentOpts = {}): string[] {
353
+ const {intent, action, uri, mimeType, identifier, categories, component, extras, flags} = opts;
354
+ const resultArgs: string[] = [];
355
+ if (intent) {
356
+ resultArgs.push(intent);
357
+ }
358
+ if (action) {
359
+ resultArgs.push('-a', action);
360
+ }
361
+ if (uri) {
362
+ resultArgs.push('-d', uri);
363
+ }
364
+ if (mimeType) {
365
+ resultArgs.push('-t', mimeType);
366
+ }
367
+ if (!_.isNil(identifier)) {
368
+ resultArgs.push('-i', identifier);
369
+ }
370
+ if (categories) {
371
+ if (_.isArray(categories)) {
372
+ resultArgs.push(..._.flatMap(categories.map((cName) => ['-c', cName])));
373
+ } else {
374
+ resultArgs.push('-c', categories);
375
+ }
376
+ }
377
+ if (component) {
378
+ resultArgs.push('-n', component);
379
+ }
380
+ if (opts.package) {
381
+ resultArgs.push('-p', opts.package);
382
+ }
383
+ if (extras) {
384
+ if (!_.isArray(extras)) {
385
+ throw new errors.InvalidArgumentError(`'extras' must be an array`);
386
+ }
387
+ for (const item of extras) {
388
+ if (!_.isArray(item)) {
389
+ throw new errors.InvalidArgumentError(`Extra argument '${item}' must be an array`);
390
+ }
391
+ const [type, key, value] = item;
392
+ if (!SUPPORTED_EXTRA_TYPES.includes(type as any)) {
393
+ throw new errors.InvalidArgumentError(
394
+ `Extra argument type '${type}' is not known. ` +
395
+ `Supported intent argument types are: ${SUPPORTED_EXTRA_TYPES.join(', ')}`,
396
+ );
397
+ }
398
+ if (_.isEmpty(key) || (_.isString(key) && _.trim(key) === '')) {
399
+ throw new errors.InvalidArgumentError(
400
+ `Extra argument's key in '${JSON.stringify(item)}' must be a valid string identifier`,
401
+ );
402
+ }
403
+ if (type === NO_VALUE_ARG_TYPE) {
404
+ resultArgs.push(`--e${type}`, key);
405
+ } else if (_.isUndefined(value)) {
406
+ throw new errors.InvalidArgumentError(
407
+ `Intent argument type '${type}' in '${JSON.stringify(item)}' requires a ` +
408
+ `valid value to be provided`,
409
+ );
410
+ } else {
411
+ resultArgs.push(`--e${type}`, key, value);
412
+ }
413
+ }
414
+ }
415
+ if (flags) {
416
+ resultArgs.push('-f', flags);
417
+ }
418
+ return resultArgs;
419
+ }
420
+
421
+ // #endregion
422
+
@@ -2,51 +2,42 @@ import {DEFAULT_WS_PATHNAME_PREFIX, BaseDriver} from 'appium/driver';
2
2
  import _ from 'lodash';
3
3
  import os from 'node:os';
4
4
  import WebSocket from 'ws';
5
+ import type {AppiumServer, WSServer} from '@appium/types';
6
+ import type {EventEmitter} from 'node:events';
7
+ import type {ADB} from 'appium-adb';
8
+ import type {Chromedriver} from 'appium-chromedriver';
5
9
  import {
6
10
  GET_SERVER_LOGS_FEATURE,
7
11
  toLogRecord,
8
12
  nativeLogEntryToSeleniumEntry,
13
+ type LogEntry,
9
14
  } from '../utils';
10
15
  import { NATIVE_WIN } from './context/helpers';
11
16
  import { BIDI_EVENT_NAME } from './bidi/constants';
12
17
  import { makeLogEntryAddedEvent } from './bidi/models';
18
+ import type {AndroidDriver} from '../driver';
13
19
 
14
20
  export const supportedLogTypes = {
15
21
  logcat: {
16
22
  description: 'Logs for Android applications on real device and emulators via ADB',
17
- /**
18
- *
19
- * @param {import('../driver').AndroidDriver} self
20
- * @returns
21
- */
22
- getter: (self) => /** @type {ADB} */ (self.adb).getLogcatLogs(),
23
+ getter: (self: AndroidDriver) => (self.adb as ADB).getLogcatLogs(),
23
24
  },
24
25
  bugreport: {
25
26
  description: `'adb bugreport' output for advanced issues diagnostic`,
26
- /**
27
- *
28
- * @param {import('../driver').AndroidDriver} self
29
- * @returns
30
- */
31
- getter: async (self) => {
32
- const output = await /** @type {ADB} */ (self.adb).bugreport();
27
+ getter: async (self: AndroidDriver) => {
28
+ const output = await (self.adb as ADB).bugreport();
33
29
  const timestamp = Date.now();
34
30
  return output.split(os.EOL).map((x) => toLogRecord(timestamp, x));
35
31
  },
36
32
  },
37
33
  server: {
38
34
  description: 'Appium server logs',
39
- /**
40
- *
41
- * @param {import('../driver').AndroidDriver} self
42
- * @returns
43
- */
44
- getter: (self) => {
35
+ getter: (self: AndroidDriver) => {
45
36
  self.assertFeatureEnabled(GET_SERVER_LOGS_FEATURE);
46
37
  return self.log.unwrap().record.map(nativeLogEntryToSeleniumEntry);
47
38
  },
48
39
  },
49
- };
40
+ } as const;
50
41
 
51
42
  /**
52
43
  * Starts Android logcat broadcast websocket on the same host and port
@@ -56,12 +47,13 @@ export const supportedLogTypes = {
56
47
  * Each connected websocket listener will receive logcat log lines
57
48
  * as soon as they are visible to Appium.
58
49
  *
59
- * @this {import('../driver').AndroidDriver}
60
- * @returns {Promise<void>}
50
+ * @returns Promise that resolves when the logcat broadcasting websocket is started.
61
51
  */
62
- export async function mobileStartLogsBroadcast() {
63
- const server = /** @type {import('@appium/types').AppiumServer} */ (this.server);
64
- const pathname = WEBSOCKET_ENDPOINT(/** @type {string} */ (this.sessionId));
52
+ export async function mobileStartLogsBroadcast(
53
+ this: AndroidDriver,
54
+ ): Promise<void> {
55
+ const server = this.server as AppiumServer;
56
+ const pathname = WEBSOCKET_ENDPOINT(this.sessionId as string);
65
57
  if (!_.isEmpty(await server.getWebSocketHandlers(pathname))) {
66
58
  this.log.debug(`The logcat broadcasting web socket server is already listening at ${pathname}`);
67
59
  return;
@@ -78,7 +70,7 @@ export async function mobileStartLogsBroadcast() {
78
70
  wss.on('connection', (ws, req) => {
79
71
  if (req) {
80
72
  const remoteIp = _.isEmpty(req.headers['x-forwarded-for'])
81
- ? req.connection?.remoteAddress
73
+ ? req.socket.remoteAddress
82
74
  : req.headers['x-forwarded-for'];
83
75
  this.log.debug(`Established a new logcat listener web socket connection from ${remoteIp}`);
84
76
  } else {
@@ -86,7 +78,7 @@ export async function mobileStartLogsBroadcast() {
86
78
  }
87
79
 
88
80
  if (_.isEmpty(this._logcatWebsocketListener)) {
89
- this._logcatWebsocketListener = (logRecord) => {
81
+ this._logcatWebsocketListener = (logRecord: LogEntry) => {
90
82
  if (ws?.readyState === WebSocket.OPEN) {
91
83
  ws.send(logRecord.message);
92
84
  }
@@ -112,19 +104,20 @@ export async function mobileStartLogsBroadcast() {
112
104
  this.log.debug(closeMsg);
113
105
  });
114
106
  });
115
- await server.addWebSocketHandler(pathname, /** @type {import('@appium/types').WSServer} */ (wss));
107
+ await server.addWebSocketHandler(pathname, wss as WSServer);
116
108
  }
117
109
 
118
110
  /**
119
111
  * Stops the previously started logcat broadcasting wesocket server.
120
112
  * This method will return immediately if no server is running.
121
113
  *
122
- * @this {import('../driver').AndroidDriver}
123
- * @returns {Promise<void>}
114
+ * @returns Promise that resolves when the logcat broadcasting websocket is stopped.
124
115
  */
125
- export async function mobileStopLogsBroadcast() {
126
- const pathname = WEBSOCKET_ENDPOINT(/** @type {string} */ (this.sessionId));
127
- const server = /** @type {import('@appium/types').AppiumServer} */ (this.server);
116
+ export async function mobileStopLogsBroadcast(
117
+ this: AndroidDriver,
118
+ ): Promise<void> {
119
+ const pathname = WEBSOCKET_ENDPOINT(this.sessionId as string);
120
+ const server = this.server as AppiumServer;
128
121
  if (_.isEmpty(await server.getWebSocketHandlers(pathname))) {
129
122
  return;
130
123
  }
@@ -137,40 +130,53 @@ export async function mobileStopLogsBroadcast() {
137
130
  }
138
131
 
139
132
  /**
140
- * @this {import('../driver').AndroidDriver}
141
- * @returns {Promise<string[]>}
133
+ * Gets the list of available log types.
134
+ *
135
+ * @returns Promise that resolves to an array of log type names.
142
136
  */
143
- export async function getLogTypes() {
137
+ export async function getLogTypes(
138
+ this: AndroidDriver,
139
+ ): Promise<string[]> {
144
140
  // XXX why doesn't `super` work here?
145
141
  const nativeLogTypes = await BaseDriver.prototype.getLogTypes.call(this);
146
142
  if (this.isWebContext()) {
147
- const webLogTypes = /** @type {string[]} */ (
148
- await /** @type {import('appium-chromedriver').Chromedriver} */ (
149
- this.chromedriver
150
- ).jwproxy.command('/log/types', 'GET')
151
- );
143
+ const webLogTypes = await (this.chromedriver as Chromedriver).jwproxy.command('/log/types', 'GET') as string[];
152
144
  return [...nativeLogTypes, ...webLogTypes];
153
145
  }
154
146
  return nativeLogTypes;
155
147
  }
156
148
 
149
+ export interface BiDiListenerProperties {
150
+ type: string;
151
+ srcEventName?: string;
152
+ context?: string;
153
+ entryTransformer?: (x: LogEntry) => LogEntry;
154
+ }
155
+
156
+ export type LogListener = (logEntry: LogEntry) => any;
157
+
157
158
  /**
159
+ * Assigns a BiDi log listener to an event emitter.
160
+ *
158
161
  * https://w3c.github.io/webdriver-bidi/#event-log-entryAdded
159
162
  *
160
- * @template {import('node:events').EventEmitter} EE
161
- * @this {import('../driver').AndroidDriver}
162
- * @param {EE} logEmitter
163
- * @param {BiDiListenerProperties} properties
164
- * @returns {[EE, LogListener]}
163
+ * @template EE The event emitter type.
164
+ * @param logEmitter The event emitter to attach the listener to.
165
+ * @param properties The BiDi listener properties.
166
+ * @returns A tuple containing the event emitter and the listener function.
165
167
  */
166
- export function assignBiDiLogListener (logEmitter, properties) {
168
+ export function assignBiDiLogListener<EE extends EventEmitter>(
169
+ this: AndroidDriver,
170
+ logEmitter: EE,
171
+ properties: BiDiListenerProperties,
172
+ ): [EE, LogListener] {
167
173
  const {
168
174
  type,
169
175
  context = NATIVE_WIN,
170
176
  srcEventName = 'output',
171
177
  entryTransformer,
172
178
  } = properties;
173
- const listener = (/** @type {import('../utils').LogEntry} */ logEntry) => {
179
+ const listener: LogListener = (logEntry: LogEntry) => {
174
180
  const finalEntry = entryTransformer ? entryTransformer(logEntry) : logEntry;
175
181
  this.eventEmitter.emit(BIDI_EVENT_NAME, makeLogEntryAddedEvent(finalEntry, context, type));
176
182
  };
@@ -179,42 +185,31 @@ export function assignBiDiLogListener (logEmitter, properties) {
179
185
  }
180
186
 
181
187
  /**
182
- * @this {import('../driver').AndroidDriver}
183
- * @param {string} logType
184
- * @returns {Promise<any>}
188
+ * Gets logs of a specific type.
189
+ *
190
+ * @param logType The type of logs to retrieve.
191
+ * @returns Promise that resolves to the logs for the specified type.
185
192
  */
186
- export async function getLog(logType) {
193
+ export async function getLog(
194
+ this: AndroidDriver,
195
+ logType: string,
196
+ ): Promise<any> {
187
197
  if (this.isWebContext() && !_.keys(this.supportedLogTypes).includes(logType)) {
188
- return await /** @type {import('appium-chromedriver').Chromedriver} */ (
189
- this.chromedriver
190
- ).jwproxy.command('/log', 'POST', {type: logType});
198
+ return await (this.chromedriver as Chromedriver).jwproxy.command('/log', 'POST', {type: logType});
191
199
  }
192
- // XXX why doesn't `super` work here?
193
200
  return await BaseDriver.prototype.getLog.call(this, logType);
194
201
  }
195
202
 
196
203
  // #region Internal helpers
197
204
 
198
205
  /**
199
- * @param {string} sessionId
200
- * @returns {string}
206
+ * Generates the websocket endpoint path for logcat broadcasting.
207
+ *
208
+ * @param sessionId The session ID.
209
+ * @returns The websocket endpoint path.
201
210
  */
202
- const WEBSOCKET_ENDPOINT = (sessionId) =>
211
+ const WEBSOCKET_ENDPOINT = (sessionId: string): string =>
203
212
  `${DEFAULT_WS_PATHNAME_PREFIX}/session/${sessionId}/appium/device/logcat`;
204
213
 
205
-
206
214
  // #endregion
207
215
 
208
- /**
209
- * @typedef {import('appium-adb').ADB} ADB
210
- */
211
-
212
- /**
213
- * @typedef {Object} BiDiListenerProperties
214
- * @property {string} type
215
- * @property {string} [srcEventName='output']
216
- * @property {string} [context=NATIVE_WIN]
217
- * @property {(x: Object) => import('../utils').LogEntry} [entryTransformer]
218
- */
219
-
220
- /** @typedef {(logEntry: import('../utils').LogEntry) => any} LogListener */