appium-xcuitest-driver 10.13.1 → 10.13.3

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 (49) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/build/lib/commands/app-management.d.ts +99 -76
  3. package/build/lib/commands/app-management.d.ts.map +1 -1
  4. package/build/lib/commands/app-management.js +83 -73
  5. package/build/lib/commands/app-management.js.map +1 -1
  6. package/build/lib/commands/certificate.d.ts +14 -19
  7. package/build/lib/commands/certificate.d.ts.map +1 -1
  8. package/build/lib/commands/certificate.js +24 -31
  9. package/build/lib/commands/certificate.js.map +1 -1
  10. package/build/lib/commands/element.d.ts +83 -67
  11. package/build/lib/commands/element.d.ts.map +1 -1
  12. package/build/lib/commands/element.js +111 -134
  13. package/build/lib/commands/element.js.map +1 -1
  14. package/build/lib/commands/file-movement.d.ts +31 -42
  15. package/build/lib/commands/file-movement.d.ts.map +1 -1
  16. package/build/lib/commands/file-movement.js +146 -205
  17. package/build/lib/commands/file-movement.js.map +1 -1
  18. package/build/lib/commands/find.d.ts +20 -12
  19. package/build/lib/commands/find.d.ts.map +1 -1
  20. package/build/lib/commands/find.js +27 -65
  21. package/build/lib/commands/find.js.map +1 -1
  22. package/build/lib/commands/navigation.d.ts.map +1 -1
  23. package/build/lib/commands/navigation.js +12 -14
  24. package/build/lib/commands/navigation.js.map +1 -1
  25. package/build/lib/commands/performance.d.ts +36 -55
  26. package/build/lib/commands/performance.d.ts.map +1 -1
  27. package/build/lib/commands/performance.js +93 -86
  28. package/build/lib/commands/performance.js.map +1 -1
  29. package/build/lib/commands/recordscreen.d.ts +31 -63
  30. package/build/lib/commands/recordscreen.d.ts.map +1 -1
  31. package/build/lib/commands/recordscreen.js +29 -28
  32. package/build/lib/commands/recordscreen.js.map +1 -1
  33. package/build/lib/execute-method-map.d.ts.map +1 -1
  34. package/build/lib/execute-method-map.js +0 -1
  35. package/build/lib/execute-method-map.js.map +1 -1
  36. package/lib/commands/app-management.ts +414 -0
  37. package/lib/commands/{certificate.js → certificate.ts} +55 -50
  38. package/lib/commands/element.ts +419 -0
  39. package/lib/commands/{file-movement.js → file-movement.ts} +212 -235
  40. package/lib/commands/find.ts +277 -0
  41. package/lib/commands/navigation.js +20 -14
  42. package/lib/commands/{performance.js → performance.ts} +133 -114
  43. package/lib/commands/{recordscreen.js → recordscreen.ts} +78 -50
  44. package/lib/execute-method-map.ts +0 -1
  45. package/npm-shrinkwrap.json +5 -5
  46. package/package.json +1 -1
  47. package/lib/commands/app-management.js +0 -346
  48. package/lib/commands/element.js +0 -423
  49. package/lib/commands/find.js +0 -205
@@ -5,6 +5,9 @@ import {encodeBase64OrUpload} from '../utils';
5
5
  import {WDA_BASE_URL} from 'appium-webdriveragent';
6
6
  import {waitForCondition} from 'asyncbox';
7
7
  import url from 'url';
8
+ import type {XCUITestDriver} from '../driver';
9
+ import type {StartRecordingScreenOptions, StopRecordingScreenOptions} from './types';
10
+ import type {WDASettings} from 'appium-webdriveragent';
8
11
 
9
12
  /**
10
13
  * Set max timeout for 'reconnect_delay_max' ffmpeg argument usage.
@@ -20,14 +23,22 @@ const DEFAULT_VCODEC = 'mjpeg';
20
23
  const MP4_EXT = '.mp4';
21
24
  const FFMPEG_BINARY = 'ffmpeg';
22
25
  const ffmpegLogger = logger.getLogger(FFMPEG_BINARY);
23
- const QUALITY_MAPPING = {
26
+ const QUALITY_MAPPING: Record<string, number> = {
24
27
  low: 10,
25
28
  medium: 25,
26
29
  high: 75,
27
30
  photo: 100,
28
31
  };
29
32
 
30
- const HARDWARE_ACCELERATION_PARAMETERS = {
33
+ const HARDWARE_ACCELERATION_PARAMETERS: Record<
34
+ string,
35
+ {
36
+ hwaccel: string;
37
+ hwaccelOutputFormat: string;
38
+ scaleFilterHWAccel: string;
39
+ videoTypeHWAccel: string;
40
+ }
41
+ > = {
31
42
  /* https://trac.ffmpeg.org/wiki/HWAccelIntro#VideoToolbox */
32
43
  videoToolbox: {
33
44
  hwaccel: 'videotoolbox',
@@ -54,21 +65,28 @@ const HARDWARE_ACCELERATION_PARAMETERS = {
54
65
  hwaccel: 'qsv',
55
66
  hwaccelOutputFormat: '',
56
67
  scaleFilterHWAccel: 'scale_qsv',
57
- videoTypeHWAccel: 'h264_qsv'
68
+ videoTypeHWAccel: 'h264_qsv',
58
69
  },
59
70
  /* https://trac.ffmpeg.org/wiki/Hardware/VAAPI */
60
71
  vaapi: {
61
72
  hwaccel: 'vaapi',
62
73
  hwaccelOutputFormat: 'vaapi',
63
74
  scaleFilterHWAccel: 'scale_vaapi',
64
- videoTypeHWAccel: 'h264_vaapi'
65
- }
75
+ videoTypeHWAccel: 'h264_vaapi',
76
+ },
66
77
  };
67
78
 
68
79
  const CAPTURE_START_MARKER = /^\s*frame=/;
69
80
 
70
81
  export class ScreenRecorder {
71
- constructor(udid, log, videoPath, opts = {}) {
82
+ private readonly videoPath: string;
83
+ private readonly log: any;
84
+ private readonly opts: ScreenRecorderOptions;
85
+ private readonly udid: string;
86
+ private mainProcess: SubProcess | null;
87
+ private timeoutHandler: NodeJS.Timeout | null;
88
+
89
+ constructor(udid: string, log: any, videoPath: string, opts: ScreenRecorderOptions = {}) {
72
90
  this.videoPath = videoPath;
73
91
  this.log = log;
74
92
  this.opts = opts;
@@ -77,7 +95,7 @@ export class ScreenRecorder {
77
95
  this.timeoutHandler = null;
78
96
  }
79
97
 
80
- async start(timeoutMs) {
98
+ async start(timeoutMs: number): Promise<void> {
81
99
  try {
82
100
  await fs.which(FFMPEG_BINARY);
83
101
  } catch {
@@ -98,7 +116,7 @@ export class ScreenRecorder {
98
116
  pixelFormat,
99
117
  } = this.opts;
100
118
 
101
- const args = [
119
+ const args: string[] = [
102
120
  '-f',
103
121
  'mjpeg',
104
122
  // https://github.com/appium/appium/issues/16294
@@ -115,8 +133,8 @@ export class ScreenRecorder {
115
133
  hwaccel,
116
134
  hwaccelOutputFormat,
117
135
  scaleFilterHWAccel,
118
- videoTypeHWAccel
119
- } = HARDWARE_ACCELERATION_PARAMETERS[hardwareAcceleration] ?? {};
136
+ videoTypeHWAccel,
137
+ } = HARDWARE_ACCELERATION_PARAMETERS[hardwareAcceleration || ''] ?? {};
120
138
 
121
139
  if (hwaccel) {
122
140
  args.push('-hwaccel', hwaccel);
@@ -127,10 +145,10 @@ export class ScreenRecorder {
127
145
  }
128
146
 
129
147
  //Parameter `-r` is optional. See details: https://github.com/appium/appium/issues/12067
130
- if (videoFps && videoType === 'libx264' || videoTypeHWAccel) {
131
- args.push('-r', videoFps);
148
+ if ((videoFps && videoType === 'libx264') || videoTypeHWAccel) {
149
+ args.push('-r', String(videoFps));
132
150
  }
133
- const {protocol, hostname} = url.parse(remoteUrl);
151
+ const {protocol, hostname} = url.parse(remoteUrl || '');
134
152
  args.push('-i', `${protocol}//${hostname}:${remotePort}`);
135
153
 
136
154
  if (videoFilters || videoScale) {
@@ -141,13 +159,13 @@ export class ScreenRecorder {
141
159
  if (pixelFormat) {
142
160
  args.push('-pix_fmt', pixelFormat);
143
161
  }
144
- args.push('-vcodec', videoTypeHWAccel || videoType);
162
+ args.push('-vcodec', videoTypeHWAccel || videoType || DEFAULT_VCODEC);
145
163
  args.push('-y');
146
164
  args.push(this.videoPath);
147
165
 
148
166
  this.mainProcess = new SubProcess(FFMPEG_BINARY, args);
149
167
  let isCaptureStarted = false;
150
- this.mainProcess.on('line-stderr', (line) => {
168
+ this.mainProcess.on('line-stderr', (line: string) => {
151
169
  if (CAPTURE_START_MARKER.test(line)) {
152
170
  if (!isCaptureStarted) {
153
171
  isCaptureStarted = true;
@@ -175,9 +193,8 @@ export class ScreenRecorder {
175
193
  );
176
194
  }
177
195
  this.log.info(
178
- `Starting screen capture on the device '${
179
- this.udid
180
- }' with command: '${FFMPEG_BINARY} ${args.join(' ')}'. ` + `Will timeout in ${timeoutMs}ms`,
196
+ `Starting screen capture on the device '${this.udid}' with command: '${FFMPEG_BINARY} ${args.join(' ')}'. ` +
197
+ `Will timeout in ${timeoutMs}ms`,
181
198
  );
182
199
 
183
200
  this.timeoutHandler = setTimeout(async () => {
@@ -189,7 +206,7 @@ export class ScreenRecorder {
189
206
  }, timeoutMs);
190
207
  }
191
208
 
192
- async interrupt(force = false) {
209
+ async interrupt(force = false): Promise<boolean> {
193
210
  let result = true;
194
211
 
195
212
  if (this.timeoutHandler) {
@@ -202,7 +219,7 @@ export class ScreenRecorder {
202
219
  this.mainProcess = null;
203
220
  try {
204
221
  await interruptPromise;
205
- } catch (e) {
222
+ } catch (e: any) {
206
223
  this.log.warn(
207
224
  `Cannot ${force ? 'terminate' : 'interrupt'} ${FFMPEG_BINARY}. ` +
208
225
  `Original error: ${e.message}`,
@@ -214,12 +231,12 @@ export class ScreenRecorder {
214
231
  return result;
215
232
  }
216
233
 
217
- async finish() {
234
+ async finish(): Promise<string> {
218
235
  await this.interrupt();
219
236
  return this.videoPath;
220
237
  }
221
238
 
222
- async cleanup() {
239
+ async cleanup(): Promise<void> {
223
240
  if (await fs.exists(this.videoPath)) {
224
241
  await fs.rimraf(this.videoPath);
225
242
  }
@@ -235,13 +252,15 @@ export class ScreenRecorder {
235
252
  * If screen recording has been already started then the command will stop it forcefully and start a new one.
236
253
  * The previously recorded video file will be deleted.
237
254
  *
238
- * @param {import('./types').StartRecordingScreenOptions} [options] - The available options.
239
- * @returns {Promise<string>} Base64-encoded content of the recorded media file if
255
+ * @param options - The available options.
256
+ * @returns Base64-encoded content of the recorded media file if
240
257
  * any screen recording is currently running or an empty string.
241
258
  * @throws {Error} If screen recording has failed to start.
242
- * @this {XCUITestDriver}
243
259
  */
244
- export async function startRecordingScreen(options = {}) {
260
+ export async function startRecordingScreen(
261
+ this: XCUITestDriver,
262
+ options: StartRecordingScreenOptions = {},
263
+ ): Promise<string> {
245
264
  const {
246
265
  videoType = DEFAULT_VCODEC,
247
266
  timeLimit = DEFAULT_RECORDING_TIME_SEC,
@@ -251,21 +270,21 @@ export async function startRecordingScreen(options = {}) {
251
270
  videoScale,
252
271
  forceRestart,
253
272
  pixelFormat,
254
- hardwareAcceleration
273
+ hardwareAcceleration,
255
274
  } = options;
256
275
 
257
276
  let result = '';
258
277
  if (!forceRestart) {
259
278
  this.log.info(
260
279
  `Checking if there is/was a previous screen recording. ` +
261
- `Set 'forceRestart' option to 'true' if you'd like to skip this step.`
280
+ `Set 'forceRestart' option to 'true' if you'd like to skip this step.`,
262
281
  );
263
282
  result = (await this.stopRecordingScreen(options)) ?? result;
264
283
  }
265
284
 
266
285
  const videoPath = await tempDir.path({
267
286
  prefix: `appium_${Math.random().toString(16).substring(2, 8)}`,
268
- suffix: MP4_EXT
287
+ suffix: MP4_EXT,
269
288
  });
270
289
 
271
290
  const wdaBaseUrl = this.opts.wdaBaseUrl || WDA_BASE_URL;
@@ -275,9 +294,9 @@ export async function startRecordingScreen(options = {}) {
275
294
  videoType,
276
295
  videoFilters,
277
296
  videoScale,
278
- videoFps,
297
+ videoFps: typeof videoFps === 'string' ? parseInt(videoFps, 10) : videoFps,
279
298
  pixelFormat,
280
- hardwareAcceleration
299
+ hardwareAcceleration,
281
300
  });
282
301
  if (!(await screenRecorder.interrupt(true))) {
283
302
  throw this.log.errorWithException('Unable to stop screen recording process');
@@ -295,10 +314,10 @@ export async function startRecordingScreen(options = {}) {
295
314
  );
296
315
  }
297
316
 
298
- let {mjpegServerScreenshotQuality, mjpegServerFramerate} =
299
- /** @type {import('appium-webdriveragent').WDASettings} */ (
300
- await this.proxyCommand('/appium/settings', 'GET')
301
- );
317
+ let {mjpegServerScreenshotQuality, mjpegServerFramerate} = (await this.proxyCommand(
318
+ '/appium/settings',
319
+ 'GET',
320
+ )) as WDASettings;
302
321
  if (videoQuality) {
303
322
  const quality = _.isInteger(videoQuality)
304
323
  ? videoQuality
@@ -306,11 +325,12 @@ export async function startRecordingScreen(options = {}) {
306
325
  if (!quality) {
307
326
  throw new Error(
308
327
  `videoQuality value should be one of ${JSON.stringify(
309
- _.keys(QUALITY_MAPPING)
310
- )} or a number in range 1..100. ` + `'${videoQuality}' is given instead`
328
+ _.keys(QUALITY_MAPPING),
329
+ )} or a number in range 1..100. ` + `'${videoQuality}' is given instead`,
311
330
  );
312
331
  }
313
- mjpegServerScreenshotQuality = mjpegServerScreenshotQuality !== quality ? quality : undefined;
332
+ mjpegServerScreenshotQuality =
333
+ mjpegServerScreenshotQuality !== quality ? (quality as number) : undefined;
314
334
  } else {
315
335
  mjpegServerScreenshotQuality = undefined;
316
336
  }
@@ -319,7 +339,7 @@ export async function startRecordingScreen(options = {}) {
319
339
  if (isNaN(fps)) {
320
340
  throw new Error(
321
341
  `videoFps value should be a valid number in range 1..60. ` +
322
- `'${videoFps}' is given instead`
342
+ `'${videoFps}' is given instead`,
323
343
  );
324
344
  }
325
345
  mjpegServerFramerate = mjpegServerFramerate !== fps ? fps : undefined;
@@ -330,8 +350,8 @@ export async function startRecordingScreen(options = {}) {
330
350
  await this.proxyCommand('/appium/settings', 'POST', {
331
351
  settings: {
332
352
  mjpegServerScreenshotQuality,
333
- mjpegServerFramerate
334
- }
353
+ mjpegServerFramerate,
354
+ },
335
355
  });
336
356
  }
337
357
 
@@ -355,16 +375,17 @@ export async function startRecordingScreen(options = {}) {
355
375
  * active screen recording processes are running then the method returns an
356
376
  * empty string.
357
377
  *
358
- * @param {import('./types').StopRecordingScreenOptions} options - The available
359
- * options.
360
- * @returns {Promise<string?>} Base64-encoded content of the recorded media
378
+ * @param options - The available options.
379
+ * @returns Base64-encoded content of the recorded media
361
380
  * file if `remotePath` parameter is empty or null or an empty string.
362
381
  * @throws {Error} If there was an error while getting the name of a media
363
382
  * file or the file content cannot be uploaded to the remote
364
383
  * location.
365
- * @this {XCUITestDriver}
366
384
  */
367
- export async function stopRecordingScreen(options = {}) {
385
+ export async function stopRecordingScreen(
386
+ this: XCUITestDriver,
387
+ options: StopRecordingScreenOptions = {},
388
+ ): Promise<string | null> {
368
389
  if (!this._recentScreenRecorder) {
369
390
  this.log.info('Screen recording is not running. There is nothing to stop.');
370
391
  return '';
@@ -375,7 +396,7 @@ export async function stopRecordingScreen(options = {}) {
375
396
  if (!(await fs.exists(videoPath))) {
376
397
  throw this.log.errorWithException(
377
398
  `The screen recorder utility has failed ` +
378
- `to store the actual screen recording at '${videoPath}'`
399
+ `to store the actual screen recording at '${videoPath}'`,
379
400
  );
380
401
  }
381
402
  return await encodeBase64OrUpload(videoPath, options.remotePath, options);
@@ -386,6 +407,13 @@ export async function stopRecordingScreen(options = {}) {
386
407
  }
387
408
  }
388
409
 
389
- /**
390
- * @typedef {import('../driver').XCUITestDriver} XCUITestDriver
391
- */
410
+ interface ScreenRecorderOptions {
411
+ hardwareAcceleration?: string;
412
+ remotePort?: number;
413
+ remoteUrl?: string;
414
+ videoFps?: number;
415
+ videoType?: string;
416
+ videoScale?: string;
417
+ videoFilters?: string;
418
+ pixelFormat?: string;
419
+ }
@@ -215,7 +215,6 @@ export const executeMethodMap = {
215
215
  command: 'getViewportRect',
216
216
  },
217
217
  'mobile: startPerfRecord': {
218
- // @ts-expect-error Class field assignment - method exists on XCUITestDriver
219
218
  command: 'mobileStartPerfRecord',
220
219
  params: {
221
220
  optional: ['timeout', 'profileName', 'pid'],
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "appium-xcuitest-driver",
3
- "version": "10.13.1",
3
+ "version": "10.13.3",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "appium-xcuitest-driver",
9
- "version": "10.13.1",
9
+ "version": "10.13.3",
10
10
  "license": "Apache-2.0",
11
11
  "dependencies": {
12
12
  "@appium/strongbox": "^1.0.0-rc.1",
@@ -773,9 +773,9 @@
773
773
  }
774
774
  },
775
775
  "node_modules/appium-webdriveragent": {
776
- "version": "11.0.1",
777
- "resolved": "https://registry.npmjs.org/appium-webdriveragent/-/appium-webdriveragent-11.0.1.tgz",
778
- "integrity": "sha512-pVrwRadjw4kP8RDlIwDsEWqWn0G1IRBok2+HgOFdXsoEIDL0O7DQrlVMSLmD5eiHTCWUjSqCCLpDSE6c7QaYzw==",
776
+ "version": "11.0.2",
777
+ "resolved": "https://registry.npmjs.org/appium-webdriveragent/-/appium-webdriveragent-11.0.2.tgz",
778
+ "integrity": "sha512-Wu/kMQG9DS6w0abw1AOoz43C6uwA2KbhzEhVoHgvlLQ7Jhbpz/rc4D2/MMylax9J2LMNsNaOkkZdQLiPQyhx6w==",
779
779
  "license": "Apache-2.0",
780
780
  "dependencies": {
781
781
  "@appium/base-driver": "^10.0.0-rc.1",
package/package.json CHANGED
@@ -8,7 +8,7 @@
8
8
  "xcuitest",
9
9
  "xctest"
10
10
  ],
11
- "version": "10.13.1",
11
+ "version": "10.13.3",
12
12
  "author": "Appium Contributors",
13
13
  "license": "Apache-2.0",
14
14
  "repository": {