appium-xcuitest-driver 10.14.0 → 10.14.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/build/lib/commands/condition.d.ts +9 -72
  3. package/build/lib/commands/condition.d.ts.map +1 -1
  4. package/build/lib/commands/condition.js +5 -66
  5. package/build/lib/commands/condition.js.map +1 -1
  6. package/build/lib/commands/execute.d.ts +10 -22
  7. package/build/lib/commands/execute.d.ts.map +1 -1
  8. package/build/lib/commands/execute.js +12 -28
  9. package/build/lib/commands/execute.js.map +1 -1
  10. package/build/lib/commands/location.d.ts +8 -11
  11. package/build/lib/commands/location.d.ts.map +1 -1
  12. package/build/lib/commands/location.js +7 -15
  13. package/build/lib/commands/location.js.map +1 -1
  14. package/build/lib/commands/navigation.d.ts +14 -26
  15. package/build/lib/commands/navigation.d.ts.map +1 -1
  16. package/build/lib/commands/navigation.js +10 -18
  17. package/build/lib/commands/navigation.js.map +1 -1
  18. package/build/lib/commands/pcap.d.ts +18 -38
  19. package/build/lib/commands/pcap.d.ts.map +1 -1
  20. package/build/lib/commands/pcap.js +9 -14
  21. package/build/lib/commands/pcap.js.map +1 -1
  22. package/build/lib/commands/record-audio.d.ts +25 -53
  23. package/build/lib/commands/record-audio.d.ts.map +1 -1
  24. package/build/lib/commands/record-audio.js +17 -19
  25. package/build/lib/commands/record-audio.js.map +1 -1
  26. package/build/lib/commands/screenshots.d.ts +15 -9
  27. package/build/lib/commands/screenshots.d.ts.map +1 -1
  28. package/build/lib/commands/screenshots.js +13 -11
  29. package/build/lib/commands/screenshots.js.map +1 -1
  30. package/build/lib/commands/source.d.ts +10 -8
  31. package/build/lib/commands/source.d.ts.map +1 -1
  32. package/build/lib/commands/source.js +11 -14
  33. package/build/lib/commands/source.js.map +1 -1
  34. package/build/lib/commands/types.d.ts +58 -0
  35. package/build/lib/commands/types.d.ts.map +1 -1
  36. package/build/lib/commands/xctest-record-screen.d.ts +17 -47
  37. package/build/lib/commands/xctest-record-screen.d.ts.map +1 -1
  38. package/build/lib/commands/xctest-record-screen.js +28 -59
  39. package/build/lib/commands/xctest-record-screen.js.map +1 -1
  40. package/build/lib/driver.d.ts +1 -1
  41. package/build/lib/driver.d.ts.map +1 -1
  42. package/build/lib/driver.js +1 -1
  43. package/build/lib/driver.js.map +1 -1
  44. package/build/lib/execute-method-map.d.ts.map +1 -1
  45. package/build/lib/execute-method-map.js +0 -6
  46. package/build/lib/execute-method-map.js.map +1 -1
  47. package/lib/commands/{condition.js → condition.ts} +21 -77
  48. package/lib/commands/{execute.js → execute.ts} +41 -37
  49. package/lib/commands/{location.js → location.ts} +19 -22
  50. package/lib/commands/{navigation.js → navigation.ts} +23 -26
  51. package/lib/commands/{pcap.js → pcap.ts} +28 -28
  52. package/lib/commands/{record-audio.js → record-audio.ts} +35 -33
  53. package/lib/commands/{screenshots.js → screenshots.ts} +24 -16
  54. package/lib/commands/{source.js → source.ts} +23 -20
  55. package/lib/commands/types.ts +63 -0
  56. package/lib/commands/{xctest-record-screen.js → xctest-record-screen.ts} +54 -71
  57. package/lib/driver.ts +2 -2
  58. package/lib/execute-method-map.ts +0 -6
  59. package/npm-shrinkwrap.json +2 -2
  60. package/package.json +1 -1
@@ -2,6 +2,8 @@ import {fs, tempDir, logger, util} from 'appium/support';
2
2
  import {SubProcess} from 'teen_process';
3
3
  import {encodeBase64OrUpload} from '../utils';
4
4
  import {waitForCondition} from 'asyncbox';
5
+ import type {XCUITestDriver} from '../driver';
6
+ import type {AudioRecorderOptions} from './types';
5
7
 
6
8
  const MAX_RECORDING_TIME_SEC = 43200;
7
9
  const AUDIO_RECORD_FEAT_NAME = 'audio_record';
@@ -13,7 +15,13 @@ const FFMPEG_BINARY = 'ffmpeg';
13
15
  const ffmpegLogger = logger.getLogger(FFMPEG_BINARY);
14
16
 
15
17
  export class AudioRecorder {
16
- constructor(input, log, audioPath, opts = {}) {
18
+ private readonly input: string | number;
19
+ private readonly log: any;
20
+ private readonly audioPath: string;
21
+ private readonly opts: AudioRecorderOptions;
22
+ private mainProcess: SubProcess | null;
23
+
24
+ constructor(input: string | number, log: any, audioPath: string, opts: AudioRecorderOptions = {} as AudioRecorderOptions) {
17
25
  this.input = input;
18
26
  this.log = log;
19
27
  this.audioPath = audioPath;
@@ -21,7 +29,7 @@ export class AudioRecorder {
21
29
  this.mainProcess = null;
22
30
  }
23
31
 
24
- async start(timeoutSeconds) {
32
+ async start(timeoutSeconds: number): Promise<void> {
25
33
  try {
26
34
  await fs.which(FFMPEG_BINARY);
27
35
  } catch {
@@ -39,7 +47,7 @@ export class AudioRecorder {
39
47
  '-f',
40
48
  audioSource,
41
49
  '-i',
42
- this.input,
50
+ String(this.input),
43
51
  '-c:a',
44
52
  audioCodec,
45
53
  '-b:a',
@@ -90,7 +98,7 @@ export class AudioRecorder {
90
98
  );
91
99
  this.mainProcess.once('exit', (code, signal) => {
92
100
  // ffmpeg returns code 255 if SIGINT arrives
93
- if ([0, 255].includes(code)) {
101
+ if ([0, 255].includes(code ?? 0)) {
94
102
  this.log.info(`The recording session on audio input '${this.input}' has been finished`);
95
103
  } else {
96
104
  this.log.debug(
@@ -101,17 +109,17 @@ export class AudioRecorder {
101
109
  });
102
110
  }
103
111
 
104
- isRecording() {
112
+ isRecording(): boolean {
105
113
  return !!this.mainProcess?.isRunning;
106
114
  }
107
115
 
108
- async interrupt(force = false) {
116
+ async interrupt(force = false): Promise<boolean> {
109
117
  if (this.isRecording()) {
110
118
  const interruptPromise = this.mainProcess?.stop(force ? 'SIGTERM' : 'SIGINT');
111
119
  this.mainProcess = null;
112
120
  try {
113
121
  await interruptPromise;
114
- } catch (e) {
122
+ } catch (e: any) {
115
123
  this.log.warn(
116
124
  `Cannot ${force ? 'terminate' : 'interrupt'} ${FFMPEG_BINARY}. ` +
117
125
  `Original error: ${e.message}`,
@@ -123,12 +131,12 @@ export class AudioRecorder {
123
131
  return true;
124
132
  }
125
133
 
126
- async finish() {
134
+ async finish(): Promise<string> {
127
135
  await this.interrupt();
128
136
  return this.audioPath;
129
137
  }
130
138
 
131
- async cleanup() {
139
+ async cleanup(): Promise<void> {
132
140
  if (await fs.exists(this.audioPath)) {
133
141
  await fs.rimraf(this.audioPath);
134
142
  }
@@ -140,27 +148,25 @@ export class AudioRecorder {
140
148
  *
141
149
  * **To use this command, the `audio_record` security feature must be enabled _and_ [FFMpeg](https://ffmpeg.org/) must be installed on the Appium server.**
142
150
  *
143
- * @param {string|number} audioInput - The name of the corresponding audio input device to use for the capture. The full list of capture devices could be shown by executing `ffmpeg -f avfoundation -list_devices true -i ""`
144
- * @param {string|number} timeLimit - The maximum recording time, in seconds.
145
- * @param {string} audioCodec - The name of the audio codec.
146
- * @param {string} audioBitrate - The bitrate of the resulting audio stream.
147
- * @param {string|number} audioChannels - The count of audio channels in the resulting stream. Setting it to `1` will create a single channel (mono) audio stream.
148
- * @param {string|number} audioRate - The sampling rate of the resulting audio stream (in Hz).
149
- * @param {boolean} forceRestart - Whether to restart audio capture process forcefully when `mobile: startRecordingAudio` is called (`true`) or ignore the call until the current audio recording is completed (`false`).
151
+ * @param audioInput - The name of the corresponding audio input device to use for the capture. The full list of capture devices could be shown by executing `ffmpeg -f avfoundation -list_devices true -i ""`
152
+ * @param timeLimit - The maximum recording time, in seconds.
153
+ * @param audioCodec - The name of the audio codec.
154
+ * @param audioBitrate - The bitrate of the resulting audio stream.
155
+ * @param audioChannels - The count of audio channels in the resulting stream. Setting it to `1` will create a single channel (mono) audio stream.
156
+ * @param audioRate - The sampling rate of the resulting audio stream (in Hz).
157
+ * @param forceRestart - Whether to restart audio capture process forcefully when `mobile: startRecordingAudio` is called (`true`) or ignore the call until the current audio recording is completed (`false`).
150
158
  * @group Real Device Only
151
- * @this {XCUITestDriver}
152
- * @returns {Promise<void>}
153
- * @privateRemarks Using string literals for the default parameters makes better documentation.
154
159
  */
155
160
  export async function startAudioRecording(
156
- audioInput,
157
- timeLimit = 180,
161
+ this: XCUITestDriver,
162
+ audioInput: string | number,
163
+ timeLimit: string | number = 180,
158
164
  audioCodec = 'aac',
159
165
  audioBitrate = '128k',
160
- audioChannels = 2,
161
- audioRate = 44100,
166
+ audioChannels: string | number = 2,
167
+ audioRate: string | number = 44100,
162
168
  forceRestart = false,
163
- ) {
169
+ ): Promise<void> {
164
170
  if (!this.isFeatureEnabled(AUDIO_RECORD_FEAT_NAME)) {
165
171
  throw this.log.errorWithException(
166
172
  `Audio capture feature must be enabled on the server side. ` +
@@ -203,8 +209,8 @@ export async function startAudioRecording(
203
209
  audioSource: DEFAULT_SOURCE,
204
210
  audioCodec,
205
211
  audioBitrate,
206
- audioChannels,
207
- audioRate,
212
+ audioChannels: Number(audioChannels),
213
+ audioRate: Number(audioRate),
208
214
  });
209
215
 
210
216
  const timeoutSeconds = parseInt(String(timeLimit), 10);
@@ -231,18 +237,17 @@ export async function startAudioRecording(
231
237
  * If no previously recorded file is found and no active audio recording
232
238
  * processes are running then the method returns an empty string.
233
239
  *
234
- * @returns {Promise<string>} Base64-encoded content of the recorded media file or an
240
+ * @returns Base64-encoded content of the recorded media file or an
235
241
  * empty string if no audio recording has been started before.
236
242
  * @throws {Error} If there was an error while getting the recorded file.
237
- * @this {XCUITestDriver}
238
243
  */
239
- export async function stopAudioRecording() {
244
+ export async function stopAudioRecording(this: XCUITestDriver): Promise<string> {
240
245
  if (!this._audioRecorder) {
241
246
  this.log.info('Audio recording has not been started. There is nothing to stop');
242
247
  return '';
243
248
  }
244
249
 
245
- let resultPath;
250
+ let resultPath: string;
246
251
  try {
247
252
  resultPath = await this._audioRecorder.finish();
248
253
  if (!(await fs.exists(resultPath))) {
@@ -259,6 +264,3 @@ export async function stopAudioRecording() {
259
264
  return await encodeBase64OrUpload(resultPath);
260
265
  }
261
266
 
262
- /**
263
- * @typedef {import('../driver').XCUITestDriver} XCUITestDriver
264
- */
@@ -2,19 +2,23 @@ import {retryInterval} from 'asyncbox';
2
2
  import _ from 'lodash';
3
3
  import {errors} from 'appium/driver';
4
4
  import {util, imageUtil} from 'appium/support';
5
+ import type {XCUITestDriver} from '../driver';
6
+ import type {Simulator} from 'appium-ios-simulator';
7
+ import type {Element} from '@appium/types';
5
8
 
6
9
  /**
7
- * @this {XCUITestDriver}
8
- * @returns {Promise<string>}
10
+ * Takes a screenshot of the current screen.
11
+ *
12
+ * @returns Base64-encoded screenshot data
9
13
  */
10
- export async function getScreenshot() {
14
+ export async function getScreenshot(this: XCUITestDriver): Promise<string> {
11
15
  if (this.isWebContext()) {
12
16
  const webScreenshotMode = (await this.settings.getSettings()).webScreenshotMode;
13
17
  switch (_.toLower(webScreenshotMode)) {
14
18
  case 'page':
15
19
  case 'viewport':
16
20
  return await this.remote.captureScreenshot({
17
- coordinateSystem: /** @type {'Viewport'|'Page'} */ (_.capitalize(webScreenshotMode)),
21
+ coordinateSystem: _.capitalize(webScreenshotMode) as 'Viewport' | 'Page',
18
22
  });
19
23
  case 'native':
20
24
  case undefined:
@@ -29,7 +33,7 @@ export async function getScreenshot() {
29
33
  }
30
34
  }
31
35
 
32
- const getScreenshotFromWDA = async () => {
36
+ const getScreenshotFromWDA = async (): Promise<string> => {
33
37
  this.log.debug(`Taking screenshot with WDA`);
34
38
  const data = await this.proxyCommand('/screenshot', 'GET');
35
39
  if (!_.isString(data)) {
@@ -53,14 +57,14 @@ export async function getScreenshot() {
53
57
 
54
58
  try {
55
59
  return await getScreenshotFromWDA();
56
- } catch (err) {
60
+ } catch (err: any) {
57
61
  this.log.warn(`Error getting screenshot: ${err.message}`);
58
62
  }
59
63
 
60
64
  // simulator attempt
61
65
  if (this.isSimulator()) {
62
66
  this.log.info(`Falling back to 'simctl io screenshot' API`);
63
- const payload = await /** @type {import('appium-ios-simulator').Simulator} */ (this.device).simctl.getScreenshot();
67
+ const payload = await (this.device as Simulator).simctl.getScreenshot();
64
68
  if (!payload) {
65
69
  throw new errors.UnableToCaptureScreen();
66
70
  }
@@ -68,13 +72,19 @@ export async function getScreenshot() {
68
72
  }
69
73
 
70
74
  // Retry for real devices only. Fail fast on Simulator if simctl does not work as expected
71
- return /** @type {string} */ (await retryInterval(2, 1000, getScreenshotFromWDA));
75
+ return await retryInterval(2, 1000, getScreenshotFromWDA) as string;
72
76
  }
73
77
 
74
78
  /**
75
- * @this {XCUITestDriver}
79
+ * Takes a screenshot of a specific element.
80
+ *
81
+ * @param el - Element to capture
82
+ * @returns Base64-encoded screenshot data
76
83
  */
77
- export async function getElementScreenshot(el) {
84
+ export async function getElementScreenshot(
85
+ this: XCUITestDriver,
86
+ el: Element<string> | string,
87
+ ): Promise<string> {
78
88
  el = util.unwrapElement(el);
79
89
  if (this.isWebContext()) {
80
90
  const atomsElement = this.getAtomsElement(el);
@@ -96,10 +106,11 @@ export async function getElementScreenshot(el) {
96
106
  }
97
107
 
98
108
  /**
99
- * @this {XCUITestDriver}
100
- * @returns {Promise<string>}
109
+ * Takes a screenshot of the current viewport.
110
+ *
111
+ * @returns Base64-encoded screenshot data
101
112
  */
102
- export async function getViewportScreenshot() {
113
+ export async function getViewportScreenshot(this: XCUITestDriver): Promise<string> {
103
114
  if (this.isWebContext()) {
104
115
  return await this.remote.captureScreenshot();
105
116
  }
@@ -130,6 +141,3 @@ export async function getViewportScreenshot() {
130
141
  return await imageUtil.cropBase64Image(screenshot, region);
131
142
  }
132
143
 
133
- /**
134
- * @typedef {import('../driver').XCUITestDriver} XCUITestDriver
135
- */
@@ -1,12 +1,16 @@
1
1
  import _ from 'lodash';
2
2
  import js2xml from 'js2xmlparser2';
3
+ import type {XCUITestDriver} from '../driver';
4
+ import type {SourceFormat} from './types';
3
5
 
4
6
  const APPIUM_AUT_TAG = 'AppiumAUT';
5
7
 
6
8
  /**
7
- * @this {XCUITestDriver}
9
+ * Retrieves the page source of the current application.
10
+ *
11
+ * @returns The page source as XML or HTML string
8
12
  */
9
- export async function getPageSource() {
13
+ export async function getPageSource(this: XCUITestDriver): Promise<string> {
10
14
  if (this.isWebContext()) {
11
15
  const script = 'return document.documentElement.outerHTML';
12
16
  return await this.executeAtom('execute_script', [script, []]);
@@ -28,14 +32,17 @@ export async function getPageSource() {
28
32
  /**
29
33
  * Retrieve the source tree of the current page in XML or JSON format.
30
34
  *
31
- * @param {import('./types').SourceFormat} format - Page tree source representation format.
32
- * @param {string} [excludedAttributes] A comma-separated string of attribute names to exclude from the output. Only works if `format` is `xml`.
35
+ * @param format - Page tree source representation format.
36
+ * @param excludedAttributes - A comma-separated string of attribute names to exclude from the output. Only works if `format` is `xml`.
33
37
  * @privateRemarks Why isn't `excludedAttributes` an array?
34
- * @returns {Promise<string>} The source tree of the current page in the given format.
35
- * @this {XCUITestDriver}
38
+ * @returns The source tree of the current page in the given format.
36
39
  */
37
- export async function mobileGetSource(format = 'xml', excludedAttributes) {
38
- const paramsMap = {
40
+ export async function mobileGetSource(
41
+ this: XCUITestDriver,
42
+ format: SourceFormat = 'xml',
43
+ excludedAttributes?: string,
44
+ ): Promise<string> {
45
+ const paramsMap: Record<string, string> = {
39
46
  format,
40
47
  scope: APPIUM_AUT_TAG,
41
48
  };
@@ -45,7 +52,7 @@ export async function mobileGetSource(format = 'xml', excludedAttributes) {
45
52
  const query = Object.entries(paramsMap)
46
53
  .map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`)
47
54
  .join('&');
48
- return /** @type {string} */ (await this.proxyCommand(`/source?${query}`, 'GET'));
55
+ return await this.proxyCommand(`/source?${query}`, 'GET') as string;
49
56
  }
50
57
 
51
58
  /**
@@ -76,15 +83,14 @@ export async function mobileGetSource(format = 'xml', excludedAttributes) {
76
83
  * rawIdentifier: null }
77
84
  * ```
78
85
  */
79
- function getTreeForXML(srcTree) {
80
- function getTree(element, elementIndex, parentPath) {
81
- let curPath = `${parentPath}/${elementIndex}`;
82
- let rect = element.rect || {};
86
+ function getTreeForXML(srcTree: any): any {
87
+ function getTree(element: any, elementIndex: number, parentPath: string): any {
88
+ const curPath = `${parentPath}/${elementIndex}`;
89
+ const rect = element.rect || {};
83
90
  /**
84
91
  * @privateRemarks I don't even want to try to type this right now
85
- * @type {any}
86
92
  */
87
- let subtree = {
93
+ const subtree: any = {
88
94
  '@': {
89
95
  type: `XCUIElementType${element.type}`,
90
96
  enabled: parseInt(element.isEnabled, 10) === 1,
@@ -114,11 +120,11 @@ function getTreeForXML(srcTree) {
114
120
  [`XCUIElementType${element.type}`]: subtree,
115
121
  };
116
122
  }
117
- let tree = getTree(srcTree, 0, '');
123
+ const tree = getTree(srcTree, 0, '');
118
124
  return tree;
119
125
  }
120
126
 
121
- function getSourceXml(jsonSource) {
127
+ function getSourceXml(jsonSource: any): string {
122
128
  return js2xml('AppiumAUT', jsonSource, {
123
129
  wrapArray: {enabled: false, elementName: 'element'},
124
130
  declaration: {include: true},
@@ -126,6 +132,3 @@ function getSourceXml(jsonSource) {
126
132
  });
127
133
  }
128
134
 
129
- /**
130
- * @typedef {import('../driver').XCUITestDriver} XCUITestDriver
131
- */
@@ -646,3 +646,66 @@ export interface LogEntry {
646
646
  }
647
647
 
648
648
  export type LogListener = (logEntry: LogEntry) => any;
649
+
650
+ /**
651
+ * Condition inducer profile configuration
652
+ */
653
+ export interface Profile {
654
+ name: string;
655
+ /** The property is profileID used in {@linkcode XCUITestDriver.enableConditionInducer} */
656
+ identifier: string;
657
+ /** Configuration details */
658
+ description: string;
659
+ }
660
+
661
+ /**
662
+ * We can use the returned data to determine whether the Condition is enabled and the currently enabled configuration information
663
+ */
664
+ export interface Condition {
665
+ profiles: Profile[];
666
+ /** The property is conditionID used in {@linkcode XCUITestDriver.enableConditionInducer} */
667
+ identifier: string;
668
+ profilesSorted: boolean;
669
+ isDestructive: boolean;
670
+ isInternal: boolean;
671
+ /** `true` if this condition identifier is enabled */
672
+ isActive: boolean;
673
+ /** Enabled profiles identifier */
674
+ activeProfile: string;
675
+ }
676
+
677
+ /**
678
+ * Information about an XCTest screen recording session
679
+ */
680
+ export interface XcTestScreenRecordingInfo {
681
+ /** Unique identifier of the video being recorded */
682
+ uuid: string;
683
+ /** FPS value */
684
+ fps: number;
685
+ /** Video codec, where 0 is h264 */
686
+ codec: number;
687
+ /** The timestamp when the screen recording has started in float Unix seconds */
688
+ startedAt: number;
689
+ }
690
+
691
+ /**
692
+ * XCTest screen recording result with payload
693
+ */
694
+ export interface XcTestScreenRecording extends XcTestScreenRecordingInfo {
695
+ /**
696
+ * Base64-encoded content of the recorded media file if `remotePath` parameter is empty or null or an empty string otherwise.
697
+ * The media is expected to a be a valid QuickTime movie (.mov).
698
+ */
699
+ payload: string;
700
+ }
701
+
702
+ /**
703
+ * Options for AudioRecorder
704
+ */
705
+ export interface AudioRecorderOptions {
706
+ audioSource: string;
707
+ audioCodec: string;
708
+ audioBitrate: string;
709
+ audioChannels: number;
710
+ audioRate: number;
711
+ }
@@ -2,6 +2,11 @@ import _ from 'lodash';
2
2
  import {fs, util} from 'appium/support';
3
3
  import {encodeBase64OrUpload} from '../utils';
4
4
  import path from 'node:path';
5
+ import type {XCUITestDriver} from '../driver';
6
+ import type {Simulator} from 'appium-ios-simulator';
7
+ import type {RealDevice} from '../device/real-device-management';
8
+ import type {HTTPHeaders} from '@appium/types';
9
+ import type {XcTestScreenRecordingInfo, XcTestScreenRecording} from './types';
5
10
 
6
11
  const MOV_EXT = '.mov';
7
12
  const FEATURE_NAME = 'xctest_screen_record';
@@ -10,30 +15,9 @@ const DOMAIN_TYPE = 'appDataContainer';
10
15
  const USERNAME = 'mobile';
11
16
  const SUBDIRECTORY = 'Attachments';
12
17
 
13
- /**
14
- * @typedef {Object} XcTestScreenRecordingInfo
15
- * @property {string} uuid Unique identifier of the video being recorded
16
- * @property {number} fps FPS value
17
- * @property {number} codec Video codec, where 0 is h264
18
- * @property {number} startedAt The timestamp when the screen recording has started in float Unix seconds
19
- */
20
-
21
- /**
22
- * @typedef {Object} XcTestScreenRecordingType
23
- * @property {string} payload Base64-encoded content of the recorded media
24
- * file if `remotePath` parameter is empty or null or an empty string otherwise.
25
- * The media is expected to a be a valid QuickTime movie (.mov).
26
- * @typedef {XcTestScreenRecordingInfo & XcTestScreenRecordingType} XcTestScreenRecording
27
- */
28
-
29
- /**
30
- * @this {XCUITestDriver}
31
- * @param {string} uuid Unique identifier of the video being recorded
32
- * @returns {Promise<string>} The full path to the screen recording movie
33
- */
34
- async function retrieveRecodingFromSimulator(uuid) {
35
- const device = /** @type {import('appium-ios-simulator').Simulator} */ (this.device);
36
- const dataRoot = /** @type {string} */ (device.getDir());
18
+ async function retrieveRecodingFromSimulator(this: XCUITestDriver, uuid: string): Promise<string> {
19
+ const device = this.device as Simulator;
20
+ const dataRoot = device.getDir();
37
21
  // On Simulators the path looks like
38
22
  // $HOME/Library/Developer/CoreSimulator/Devices/F8E1968A-8443-4A9A-AB86-27C54C36A2F6/data/Containers/Data/InternalDaemon/4E3FE8DF-AD0A-41DA-B6EC-C35E5798C219/Attachments/A044DAF7-4A58-4CD5-95C3-29B4FE80C377
39
23
  const internalDaemonRoot = path.resolve(dataRoot, 'Containers', 'Data', 'InternalDaemon');
@@ -52,13 +36,8 @@ async function retrieveRecodingFromSimulator(uuid) {
52
36
  return videoPath;
53
37
  }
54
38
 
55
- /**
56
- * @this {XCUITestDriver}
57
- * @param {string} uuid Unique identifier of the video being recorded
58
- * @returns {Promise<string>} The full path to the screen recording movie
59
- */
60
- async function retrieveRecodingFromRealDevice(uuid) {
61
- const device = /** @type {import('../device/real-device-management').RealDevice} */ (this.device);
39
+ async function retrieveRecodingFromRealDevice(this: XCUITestDriver, uuid: string): Promise<string> {
40
+ const device = this.device as RealDevice;
62
41
 
63
42
  const fileNames = await device.devicectl.listFiles(DOMAIN_TYPE, DOMAIN_IDENTIFIER, {
64
43
  username: USERNAME,
@@ -69,7 +48,10 @@ async function retrieveRecodingFromRealDevice(uuid) {
69
48
  `Unable to locate XCTest screen recording identified by '${uuid}' for the device ${this.opts.udid}`
70
49
  );
71
50
  }
72
- const videoPath = path.join(/** @type {string} */ (this.opts.tmpDir), `${uuid}${MOV_EXT}`);
51
+ if (!this.opts.tmpDir) {
52
+ throw new Error('tmpDir is not set in driver options');
53
+ }
54
+ const videoPath = path.join(this.opts.tmpDir, `${uuid}${MOV_EXT}`);
73
55
  await device.devicectl.pullFile(`${SUBDIRECTORY}/${uuid}`, videoPath, {
74
56
  username: USERNAME,
75
57
  domainIdentifier: DOMAIN_IDENTIFIER,
@@ -80,15 +62,10 @@ async function retrieveRecodingFromRealDevice(uuid) {
80
62
  return videoPath;
81
63
  }
82
64
 
83
- /**
84
- * @this {XCUITestDriver}
85
- * @param {string} uuid Unique identifier of the video being recorded
86
- * @returns {Promise<string>} The full path to the screen recording movie
87
- */
88
- async function retrieveXcTestScreenRecording(uuid) {
65
+ async function retrieveXcTestScreenRecording(this: XCUITestDriver, uuid: string): Promise<string> {
89
66
  return this.isRealDevice()
90
- ? await retrieveRecodingFromRealDevice.bind(this)(uuid)
91
- : await retrieveRecodingFromSimulator.bind(this)(uuid);
67
+ ? await retrieveRecodingFromRealDevice.call(this, uuid)
68
+ : await retrieveRecodingFromSimulator.call(this, uuid);
92
69
  }
93
70
 
94
71
  /**
@@ -102,14 +79,16 @@ async function retrieveXcTestScreenRecording(uuid) {
102
79
  * If the recording is already running this API is a noop.
103
80
  *
104
81
  * @since Xcode 15/iOS 17
105
- * @param {number} [fps] FPS value
106
- * @param {number} [codec] Video codec, where 0 is h264, 1 is HEVC
107
- * @returns {Promise<XcTestScreenRecordingInfo>} The information
108
- * about a newly created or a running the screen recording.
82
+ * @param fps - FPS value
83
+ * @param codec - Video codec, where 0 is h264, 1 is HEVC
84
+ * @returns The information about a newly created or a running the screen recording.
109
85
  * @throws {Error} If screen recording has failed to start.
110
- * @this {XCUITestDriver}
111
86
  */
112
- export async function mobileStartXctestScreenRecording(fps, codec) {
87
+ export async function mobileStartXctestScreenRecording(
88
+ this: XCUITestDriver,
89
+ fps?: number,
90
+ codec?: number,
91
+ ): Promise<XcTestScreenRecordingInfo> {
113
92
  if (this.isRealDevice()) {
114
93
  // This feature might be used to abuse real devices as there is no
115
94
  // reliable way (yet) to cleanup video recordings stored there
@@ -117,16 +96,14 @@ export async function mobileStartXctestScreenRecording(fps, codec) {
117
96
  this.assertFeatureEnabled(FEATURE_NAME);
118
97
  }
119
98
 
120
- const opts = {};
99
+ const opts: {codec?: number; fps?: number} = {};
121
100
  if (_.isInteger(codec)) {
122
101
  opts.codec = codec;
123
102
  }
124
103
  if (_.isInteger(fps)) {
125
104
  opts.fps = fps;
126
105
  }
127
- const response = /** @type {XcTestScreenRecordingInfo} */ (
128
- await this.proxyCommand('/wda/video/start', 'POST', opts)
129
- );
106
+ const response = await this.proxyCommand('/wda/video/start', 'POST', opts) as XcTestScreenRecordingInfo;
130
107
  this.log.info(`Started a new screen recording: ${JSON.stringify(response)}`);
131
108
  return response;
132
109
  }
@@ -134,13 +111,11 @@ export async function mobileStartXctestScreenRecording(fps, codec) {
134
111
  /**
135
112
  * Retrieves information about the current running screen recording.
136
113
  * If no screen recording is running then `null` is returned.
137
- *
138
- * @returns {Promise<XcTestScreenRecordingInfo?>}
139
114
  */
140
- export async function mobileGetXctestScreenRecordingInfo() {
141
- return /** @type {XcTestScreenRecordingInfo?} */ (
142
- await this.proxyCommand('/wda/video', 'GET')
143
- );
115
+ export async function mobileGetXctestScreenRecordingInfo(
116
+ this: XCUITestDriver,
117
+ ): Promise<XcTestScreenRecordingInfo | null> {
118
+ return (await this.proxyCommand('/wda/video', 'GET')) as XcTestScreenRecordingInfo | null;
144
119
  }
145
120
 
146
121
  /**
@@ -157,29 +132,37 @@ export async function mobileGetXctestScreenRecordingInfo() {
157
132
  * on the device directly or by doing factory reset.
158
133
  *
159
134
  * @since Xcode 15/iOS 17
160
- * @param {string} [remotePath] The path to the remote location, where the resulting video should be
135
+ * @param remotePath - The path to the remote location, where the resulting video should be
161
136
  * uploaded.
162
137
  * The following protocols are supported: `http`, `https`, `ftp`. Null or empty
163
138
  * string value (the default setting) means the content of resulting file
164
139
  * should be encoded as Base64 and passed as the endpoint response value. An
165
140
  * exception will be thrown if the generated media file is too big to fit into
166
141
  * the available process memory.
167
- * @param {string} [user] The name of the user for the remote authentication.
142
+ * @param user - The name of the user for the remote authentication.
168
143
  * Only works if `remotePath` is provided.
169
- * @param {string} [pass] The password for the remote authentication.
144
+ * @param pass - The password for the remote authentication.
170
145
  * Only works if `remotePath` is provided.
171
- * @param {import('@appium/types').HTTPHeaders} [headers] Additional headers mapping for multipart http(s) uploads
172
- * @param {string} [fileFieldName] The name of the form field where the file content BLOB should be stored for
146
+ * @param headers - Additional headers mapping for multipart http(s) uploads
147
+ * @param fileFieldName - The name of the form field where the file content BLOB should be stored for
173
148
  * http(s) uploads
174
- * @param {Record<string, any> | [string, any][]} [formFields] Additional form fields for multipart http(s) uploads
175
- * @param {'PUT' | 'POST' | 'PATCH'} [method='PUT'] The http multipart upload method name.
149
+ * @param formFields - Additional form fields for multipart http(s) uploads
150
+ * @param method - The http multipart upload method name.
176
151
  * Only works if `remotePath` is provided.
177
- * @returns {Promise<XcTestScreenRecording>}
152
+ * @returns The resulting movie with base64-encoded content or empty string if uploaded remotely.
178
153
  * @throws {Error} If there was an error while retrieving the video
179
154
  * file or the file content cannot be uploaded to the remote location.
180
- * @this {XCUITestDriver}
181
155
  */
182
- export async function mobileStopXctestScreenRecording(remotePath, user, pass, headers, fileFieldName, formFields, method) {
156
+ export async function mobileStopXctestScreenRecording(
157
+ this: XCUITestDriver,
158
+ remotePath?: string,
159
+ user?: string,
160
+ pass?: string,
161
+ headers?: HTTPHeaders,
162
+ fileFieldName?: string,
163
+ formFields?: Record<string, any> | [string, any][],
164
+ method: 'PUT' | 'POST' | 'PATCH' = 'PUT',
165
+ ): Promise<XcTestScreenRecording> {
183
166
  const screenRecordingInfo = await this.mobileGetXctestScreenRecordingInfo();
184
167
  if (!screenRecordingInfo) {
185
168
  throw new Error('There is no active screen recording. Did you start one beforehand?');
@@ -187,8 +170,11 @@ export async function mobileStopXctestScreenRecording(remotePath, user, pass, he
187
170
 
188
171
  this.log.debug(`Stopping the active screen recording: ${JSON.stringify(screenRecordingInfo)}`);
189
172
  await this.proxyCommand('/wda/video/stop', 'POST', {});
190
- const videoPath = await retrieveXcTestScreenRecording.bind(this)(screenRecordingInfo.uuid);
191
- const result = /** @type {XcTestScreenRecording} */ (screenRecordingInfo);
173
+ const videoPath = await retrieveXcTestScreenRecording.call(this, screenRecordingInfo.uuid);
174
+ const result: XcTestScreenRecording = {
175
+ ...screenRecordingInfo,
176
+ payload: '', // Will be set below
177
+ };
192
178
  try {
193
179
  result.payload = await encodeBase64OrUpload(videoPath, remotePath, {
194
180
  user, pass, headers, fileFieldName, formFields, method
@@ -199,6 +185,3 @@ export async function mobileStopXctestScreenRecording(remotePath, user, pass, he
199
185
  return result;
200
186
  }
201
187
 
202
- /**
203
- * @typedef {import('../driver').XCUITestDriver} XCUITestDriver
204
- */
package/lib/driver.ts CHANGED
@@ -121,7 +121,7 @@ import type { PerfRecorder } from './commands/performance';
121
121
  import type { AudioRecorder } from './commands/record-audio';
122
122
  import type { TrafficCapture } from './commands/pcap';
123
123
  import type { ScreenRecorder } from './commands/recordscreen';
124
- import type { DVTServiceWithConnection } from './commands/condition.js';
124
+ import type { DVTServiceWithConnection } from 'appium-ios-remotexpc';
125
125
  import type { IOSDeviceLog } from './device/log/ios-device-log';
126
126
  import type { IOSSimulatorLog } from './device/log/ios-simulator-log';
127
127
  import type { IOSCrashLog } from './device/log/ios-crash-log';
@@ -500,7 +500,7 @@ export class XCUITestDriver
500
500
  this.log.debug(`Executing command '${cmd}'`);
501
501
 
502
502
  if (cmd === 'receiveAsyncResponse') {
503
- return await this.receiveAsyncResponse(...args);
503
+ return await this.receiveAsyncResponse(args[0], args[1]);
504
504
  }
505
505
  // TODO: once this fix gets into base driver remove from here
506
506
  if (cmd === 'getStatus') {