appium-xcuitest-driver 10.8.4 → 10.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (176) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/build/lib/app-utils.d.ts +2 -2
  3. package/build/lib/app-utils.d.ts.map +1 -1
  4. package/build/lib/app-utils.js +4 -1
  5. package/build/lib/app-utils.js.map +1 -1
  6. package/build/lib/commands/app-management.js +1 -1
  7. package/build/lib/commands/app-management.js.map +1 -1
  8. package/build/lib/commands/appearance.js +2 -2
  9. package/build/lib/commands/appearance.js.map +1 -1
  10. package/build/lib/commands/biometric.js +3 -3
  11. package/build/lib/commands/biometric.js.map +1 -1
  12. package/build/lib/commands/certificate.d.ts.map +1 -1
  13. package/build/lib/commands/certificate.js +9 -3
  14. package/build/lib/commands/certificate.js.map +1 -1
  15. package/build/lib/commands/context.d.ts +5 -5
  16. package/build/lib/commands/context.d.ts.map +1 -1
  17. package/build/lib/commands/context.js +6 -6
  18. package/build/lib/commands/context.js.map +1 -1
  19. package/build/lib/commands/file-movement.js +3 -3
  20. package/build/lib/commands/file-movement.js.map +1 -1
  21. package/build/lib/commands/general.js +1 -1
  22. package/build/lib/commands/general.js.map +1 -1
  23. package/build/lib/commands/gesture.js +1 -1
  24. package/build/lib/commands/gesture.js.map +1 -1
  25. package/build/lib/commands/keychains.js +1 -1
  26. package/build/lib/commands/keychains.js.map +1 -1
  27. package/build/lib/commands/localization.js +1 -1
  28. package/build/lib/commands/localization.js.map +1 -1
  29. package/build/lib/commands/location.js +1 -1
  30. package/build/lib/commands/location.js.map +1 -1
  31. package/build/lib/commands/log.js +7 -7
  32. package/build/lib/commands/log.js.map +1 -1
  33. package/build/lib/commands/notifications.js +1 -1
  34. package/build/lib/commands/notifications.js.map +1 -1
  35. package/build/lib/commands/pasteboard.js +2 -2
  36. package/build/lib/commands/pasteboard.js.map +1 -1
  37. package/build/lib/commands/pcap.js +1 -1
  38. package/build/lib/commands/pcap.js.map +1 -1
  39. package/build/lib/commands/permissions.js +2 -2
  40. package/build/lib/commands/permissions.js.map +1 -1
  41. package/build/lib/commands/proxy-helper.d.ts.map +1 -1
  42. package/build/lib/commands/proxy-helper.js +0 -3
  43. package/build/lib/commands/proxy-helper.js.map +1 -1
  44. package/build/lib/commands/screenshots.js +1 -1
  45. package/build/lib/commands/screenshots.js.map +1 -1
  46. package/build/lib/commands/simctl.d.ts +1 -1
  47. package/build/lib/commands/simctl.d.ts.map +1 -1
  48. package/build/lib/commands/simctl.js +1 -1
  49. package/build/lib/commands/simctl.js.map +1 -1
  50. package/build/lib/commands/web.js +1 -1
  51. package/build/lib/commands/web.js.map +1 -1
  52. package/build/lib/commands/xctest-record-screen.js +1 -1
  53. package/build/lib/commands/xctest-record-screen.js.map +1 -1
  54. package/build/lib/desired-caps.d.ts +383 -507
  55. package/build/lib/desired-caps.d.ts.map +1 -1
  56. package/build/lib/desired-caps.js +6 -10
  57. package/build/lib/desired-caps.js.map +1 -1
  58. package/build/lib/device/clients/base-device-client.d.ts.map +1 -0
  59. package/build/lib/device/clients/base-device-client.js.map +1 -0
  60. package/build/lib/{real-device-clients → device/clients}/py-ios-device-client.d.ts +1 -1
  61. package/build/lib/device/clients/py-ios-device-client.d.ts.map +1 -0
  62. package/build/lib/device/clients/py-ios-device-client.js.map +1 -0
  63. package/build/lib/{device-log → device/log}/helpers.d.ts +1 -1
  64. package/build/lib/device/log/helpers.d.ts.map +1 -0
  65. package/build/lib/device/log/helpers.js.map +1 -0
  66. package/build/lib/{device-log → device/log}/ios-crash-log.d.ts +1 -1
  67. package/build/lib/device/log/ios-crash-log.d.ts.map +1 -0
  68. package/build/lib/{device-log → device/log}/ios-crash-log.js +1 -1
  69. package/build/lib/device/log/ios-crash-log.js.map +1 -0
  70. package/build/lib/device/log/ios-device-log.d.ts.map +1 -0
  71. package/build/lib/device/log/ios-device-log.js.map +1 -0
  72. package/build/lib/{device-log → device/log}/ios-log.d.ts +1 -1
  73. package/build/lib/device/log/ios-log.d.ts.map +1 -0
  74. package/build/lib/device/log/ios-log.js.map +1 -0
  75. package/build/lib/device/log/ios-performance-log.d.ts.map +1 -0
  76. package/build/lib/device/log/ios-performance-log.js.map +1 -0
  77. package/build/lib/device/log/ios-simulator-log.d.ts.map +1 -0
  78. package/build/lib/device/log/ios-simulator-log.js.map +1 -0
  79. package/build/lib/{device-log → device/log}/line-consuming-log.d.ts +1 -1
  80. package/build/lib/device/log/line-consuming-log.d.ts.map +1 -0
  81. package/build/lib/device/log/line-consuming-log.js.map +1 -0
  82. package/build/lib/{device-log → device/log}/safari-console-log.d.ts +1 -1
  83. package/build/lib/device/log/safari-console-log.d.ts.map +1 -0
  84. package/build/lib/device/log/safari-console-log.js.map +1 -0
  85. package/build/lib/device/log/safari-network-log.d.ts.map +1 -0
  86. package/build/lib/device/log/safari-network-log.js.map +1 -0
  87. package/build/lib/device/real-device-management.d.ts.map +1 -1
  88. package/build/lib/device/real-device-management.js +3 -2
  89. package/build/lib/device/real-device-management.js.map +1 -1
  90. package/build/lib/device/simulator-management.js +1 -1
  91. package/build/lib/device/simulator-management.js.map +1 -1
  92. package/build/lib/driver.d.ts +129 -1385
  93. package/build/lib/driver.d.ts.map +1 -1
  94. package/build/lib/driver.js +471 -594
  95. package/build/lib/driver.js.map +1 -1
  96. package/build/lib/method-map.d.ts +1 -1
  97. package/build/lib/method-map.d.ts.map +1 -1
  98. package/build/lib/method-map.js +2 -2
  99. package/build/lib/method-map.js.map +1 -1
  100. package/lib/app-utils.js +5 -1
  101. package/lib/commands/app-management.js +1 -1
  102. package/lib/commands/appearance.js +2 -2
  103. package/lib/commands/biometric.js +3 -3
  104. package/lib/commands/certificate.js +9 -3
  105. package/lib/commands/context.js +6 -6
  106. package/lib/commands/file-movement.js +3 -3
  107. package/lib/commands/general.js +1 -1
  108. package/lib/commands/gesture.js +1 -1
  109. package/lib/commands/keychains.js +1 -1
  110. package/lib/commands/localization.js +1 -1
  111. package/lib/commands/location.js +1 -1
  112. package/lib/commands/log.js +7 -7
  113. package/lib/commands/notifications.js +1 -1
  114. package/lib/commands/pasteboard.js +2 -2
  115. package/lib/commands/pcap.js +1 -1
  116. package/lib/commands/permissions.js +2 -2
  117. package/lib/commands/proxy-helper.js +0 -3
  118. package/lib/commands/screenshots.js +1 -1
  119. package/lib/commands/simctl.js +1 -1
  120. package/lib/commands/web.js +1 -1
  121. package/lib/commands/xctest-record-screen.js +1 -1
  122. package/lib/{desired-caps.js → desired-caps.ts} +7 -6
  123. package/lib/{real-device-clients → device/clients}/py-ios-device-client.ts +1 -1
  124. package/lib/{device-log → device/log}/helpers.ts +1 -1
  125. package/lib/{device-log → device/log}/ios-crash-log.ts +3 -3
  126. package/lib/{device-log → device/log}/ios-log.ts +1 -1
  127. package/lib/{device-log → device/log}/line-consuming-log.ts +1 -1
  128. package/lib/{device-log → device/log}/safari-console-log.ts +1 -1
  129. package/lib/device/real-device-management.ts +3 -2
  130. package/lib/device/simulator-management.ts +1 -1
  131. package/lib/{driver.js → driver.ts} +616 -708
  132. package/lib/{method-map.js → method-map.ts} +5 -2
  133. package/npm-shrinkwrap.json +5 -5
  134. package/package.json +1 -1
  135. package/build/lib/device-log/helpers.d.ts.map +0 -1
  136. package/build/lib/device-log/helpers.js.map +0 -1
  137. package/build/lib/device-log/ios-crash-log.d.ts.map +0 -1
  138. package/build/lib/device-log/ios-crash-log.js.map +0 -1
  139. package/build/lib/device-log/ios-device-log.d.ts.map +0 -1
  140. package/build/lib/device-log/ios-device-log.js.map +0 -1
  141. package/build/lib/device-log/ios-log.d.ts.map +0 -1
  142. package/build/lib/device-log/ios-log.js.map +0 -1
  143. package/build/lib/device-log/ios-performance-log.d.ts.map +0 -1
  144. package/build/lib/device-log/ios-performance-log.js.map +0 -1
  145. package/build/lib/device-log/ios-simulator-log.d.ts.map +0 -1
  146. package/build/lib/device-log/ios-simulator-log.js.map +0 -1
  147. package/build/lib/device-log/line-consuming-log.d.ts.map +0 -1
  148. package/build/lib/device-log/line-consuming-log.js.map +0 -1
  149. package/build/lib/device-log/safari-console-log.d.ts.map +0 -1
  150. package/build/lib/device-log/safari-console-log.js.map +0 -1
  151. package/build/lib/device-log/safari-network-log.d.ts.map +0 -1
  152. package/build/lib/device-log/safari-network-log.js.map +0 -1
  153. package/build/lib/real-device-clients/base-device-client.d.ts.map +0 -1
  154. package/build/lib/real-device-clients/base-device-client.js.map +0 -1
  155. package/build/lib/real-device-clients/py-ios-device-client.d.ts.map +0 -1
  156. package/build/lib/real-device-clients/py-ios-device-client.js.map +0 -1
  157. /package/build/lib/{real-device-clients → device/clients}/base-device-client.d.ts +0 -0
  158. /package/build/lib/{real-device-clients → device/clients}/base-device-client.js +0 -0
  159. /package/build/lib/{real-device-clients → device/clients}/py-ios-device-client.js +0 -0
  160. /package/build/lib/{device-log → device/log}/helpers.js +0 -0
  161. /package/build/lib/{device-log → device/log}/ios-device-log.d.ts +0 -0
  162. /package/build/lib/{device-log → device/log}/ios-device-log.js +0 -0
  163. /package/build/lib/{device-log → device/log}/ios-log.js +0 -0
  164. /package/build/lib/{device-log → device/log}/ios-performance-log.d.ts +0 -0
  165. /package/build/lib/{device-log → device/log}/ios-performance-log.js +0 -0
  166. /package/build/lib/{device-log → device/log}/ios-simulator-log.d.ts +0 -0
  167. /package/build/lib/{device-log → device/log}/ios-simulator-log.js +0 -0
  168. /package/build/lib/{device-log → device/log}/line-consuming-log.js +0 -0
  169. /package/build/lib/{device-log → device/log}/safari-console-log.js +0 -0
  170. /package/build/lib/{device-log → device/log}/safari-network-log.d.ts +0 -0
  171. /package/build/lib/{device-log → device/log}/safari-network-log.js +0 -0
  172. /package/lib/{real-device-clients → device/clients}/base-device-client.ts +0 -0
  173. /package/lib/{device-log → device/log}/ios-device-log.ts +0 -0
  174. /package/lib/{device-log → device/log}/ios-performance-log.ts +0 -0
  175. /package/lib/{device-log → device/log}/ios-simulator-log.ts +0 -0
  176. /package/lib/{device-log → device/log}/safari-network-log.ts +0 -0
@@ -1,8 +1,18 @@
1
1
  import IDB from 'appium-idb';
2
2
  import {getSimulator} from 'appium-ios-simulator';
3
- import {WebDriverAgent} from 'appium-webdriveragent';
3
+ import {WebDriverAgent, type WebDriverAgentArgs} from 'appium-webdriveragent';
4
4
  import {BaseDriver, DeviceSettings, errors} from 'appium/driver';
5
5
  import {fs, mjpeg, util, timing} from 'appium/support';
6
+ import type {
7
+ RouteMatcher,
8
+ DefaultCreateSessionResult,
9
+ DriverData,
10
+ StringRecord,
11
+ ExternalDriver,
12
+ W3CDriverCaps,
13
+ DriverCaps,
14
+ DriverOpts,
15
+ } from '@appium/types';
6
16
  import AsyncLock from 'async-lock';
7
17
  import {retryInterval} from 'asyncbox';
8
18
  import B from 'bluebird';
@@ -64,11 +74,11 @@ import * as webCommands from './commands/web';
64
74
  import * as xctestCommands from './commands/xctest';
65
75
  import * as xctestRecordScreenCommands from './commands/xctest-record-screen';
66
76
  import * as increaseContrastCommands from './commands/increase-contrast';
67
- import {desiredCapConstraints} from './desired-caps';
77
+ import {desiredCapConstraints, type XCUITestDriverConstraints} from './desired-caps';
68
78
  import {DEVICE_CONNECTIONS_FACTORY} from './device/device-connections-factory';
69
79
  import {executeMethodMap} from './execute-method-map';
70
80
  import {newMethodMap} from './method-map';
71
- import { Pyidevice } from './real-device-clients/py-ios-device-client';
81
+ import { Pyidevice } from './device/clients/py-ios-device-client';
72
82
  import {
73
83
  installToRealDevice,
74
84
  runRealDeviceReset,
@@ -105,6 +115,22 @@ import {
105
115
  } from './utils';
106
116
  import { AppInfosCache } from './app-infos-cache';
107
117
  import { notifyBiDiContextChange } from './commands/context';
118
+ import type { CalibrationData, AsyncPromise, LifecycleData } from './types';
119
+ import type { WaitingAtoms, LogListener, FullContext } from './commands/types';
120
+ import type { PerfRecorder } from './commands/performance';
121
+ import type { AudioRecorder } from './commands/record-audio';
122
+ import type { TrafficCapture } from './commands/pcap';
123
+ import type { ScreenRecorder } from './commands/recordscreen';
124
+ import type { DVTServiceWithConnection } from './commands/condition.js';
125
+ import type { IOSDeviceLog } from './device/log/ios-device-log';
126
+ import type { IOSSimulatorLog } from './device/log/ios-simulator-log';
127
+ import type { IOSCrashLog } from './device/log/ios-crash-log';
128
+ import type { SafariConsoleLog } from './device/log/safari-console-log';
129
+ import type { SafariNetworkLog } from './device/log/safari-network-log';
130
+ import type { IOSPerformanceLog } from './device/log/ios-performance-log';
131
+ import type { RemoteDebugger } from 'appium-remote-debugger';
132
+ import type { XcodeVersion } from 'appium-xcode';
133
+ import type { Simulator } from 'appium-ios-simulator';
108
134
 
109
135
  const SHUTDOWN_OTHER_FEAT_NAME = 'shutdown_other_sims';
110
136
  const CUSTOMIZE_RESULT_BUNDLE_PATH = 'customize_result_bundle_path';
@@ -148,8 +174,7 @@ const SUPPORTED_ORIENATIONS = ['LANDSCAPE', 'PORTRAIT'];
148
174
  const DEFAULT_MJPEG_SERVER_PORT = 9100;
149
175
 
150
176
  /* eslint-disable no-useless-escape */
151
- /** @type {import('@appium/types').RouteMatcher[]} */
152
- const NO_PROXY_NATIVE_LIST = [
177
+ const NO_PROXY_NATIVE_LIST: RouteMatcher[] = [
153
178
  ['DELETE', /window/],
154
179
  ['GET', /^\/session\/[^\/]+$/],
155
180
  ['GET', /alert_text/],
@@ -194,9 +219,9 @@ const NO_PROXY_NATIVE_LIST = [
194
219
  ['DELETE', /cookie/],
195
220
  ['GET', /cookie/],
196
221
  ['POST', /cookie/],
197
- ];
222
+ ] as RouteMatcher[];
198
223
 
199
- const NO_PROXY_WEB_LIST = /** @type {import('@appium/types').RouteMatcher[]} */ ([
224
+ const NO_PROXY_WEB_LIST: RouteMatcher[] = [
200
225
  ['GET', /attribute/],
201
226
  ['GET', /element/],
202
227
  ['GET', /text/],
@@ -208,7 +233,8 @@ const NO_PROXY_WEB_LIST = /** @type {import('@appium/types').RouteMatcher[]} */
208
233
  ['POST', /frame/],
209
234
  ['POST', /keys/],
210
235
  ['POST', /refresh/],
211
- ]).concat(NO_PROXY_NATIVE_LIST);
236
+ ...NO_PROXY_NATIVE_LIST,
237
+ ] as RouteMatcher[];
212
238
  /* eslint-enable no-useless-escape */
213
239
 
214
240
  const MEMOIZED_FUNCTIONS = ['getStatusBarHeight', 'getDevicePixelRatio', 'getScreenInfo'];
@@ -218,112 +244,57 @@ const CAP_NAMES_NO_XCODEBUILD_REQUIRED = ['webDriverAgentUrl', 'usePreinstalledW
218
244
 
219
245
  const BUNDLE_VERSION_PATTERN = /CFBundleVersion\s+=\s+"?([^(;|")]+)/;
220
246
 
221
- /**
222
- * @implements {ExternalDriver<XCUITestDriverConstraints, FullContext|string>}
223
- * @extends {BaseDriver<XCUITestDriverConstraints>}
224
- * @privateRemarks **This class should be considered "final"**. It cannot be extended
225
- * due to use of public class field assignments. If extending this class becomes a hard requirement, refer to the implementation of `BaseDriver` on how to do so.
226
- */
227
- export class XCUITestDriver extends BaseDriver {
247
+ export class XCUITestDriver
248
+ extends BaseDriver<XCUITestDriverConstraints, StringRecord>
249
+ implements ExternalDriver<XCUITestDriverConstraints, FullContext|string, StringRecord> {
228
250
  static newMethodMap = newMethodMap;
229
251
 
230
252
  static executeMethodMap = executeMethodMap;
231
253
 
232
- /** @type {string|null|undefined} */
233
- curWindowHandle;
234
-
235
- /**
236
- * @type {boolean|undefined}
237
- */
238
- selectingNewPage;
239
-
240
- /** @type {string[]} */
241
- contexts;
242
-
243
- /** @type {string|null} */
244
- curContext;
245
-
246
- /** @type {string[]} */
247
- curWebFrames;
248
-
249
- /** @type {import('./types').CalibrationData|null} */
250
- webviewCalibrationResult;
251
-
252
- /** @type {import('./types').AsyncPromise|undefined} */
253
- asyncPromise;
254
-
255
- /** @type {number|undefined} */
256
- asyncWaitMs;
257
-
258
- /** @type {((logRecord: {message: string}) => void)|null} */
259
- _syslogWebsocketListener;
260
-
261
- /** @type {import('./commands/performance').PerfRecorder[]} */
262
- _perfRecorders;
263
-
264
- /** @type {LRUCache} */
265
- webElementsCache;
266
-
267
- /**
268
- * @type {any|null}
269
- * @privateRemarks needs types
270
- **/
271
- _conditionInducerService;
272
-
273
- /**
274
- * @type {import('./commands/condition.js').DVTServiceWithConnection|null}
275
- * @privateRemarks RemoteXPC DVT connection for iOS>=18 condition inducer
276
- **/
277
- _remoteXPCConditionInducerConnection;
278
-
279
- /** @type {boolean|undefined} */
280
- _isSafariIphone;
281
-
282
- /** @type {boolean|undefined} */
283
- _isSafariNotched;
284
-
285
- /** @type {import('./commands/types').WaitingAtoms} */
286
- _waitingAtoms;
287
-
288
- /** @type {import('./types').LifecycleData} */
289
- lifecycleData;
290
-
291
- /** @type {import('./commands/record-audio').AudioRecorder|null} */
292
- _audioRecorder;
293
-
294
- /** @type {XcodeVersion|undefined} */
295
- xcodeVersion;
296
-
297
- /** @type {import('./commands/pcap').TrafficCapture|null} */
298
- _trafficCapture;
299
-
300
- /** @type {import('./commands/recordscreen').ScreenRecorder|null} */
301
- _recentScreenRecorder;
302
-
303
- /** @type {Simulator|RealDevice} */
304
- _device;
305
-
306
- /** @type {string|null} */
307
- _iosSdkVersion;
308
-
309
- /** @type {WebDriverAgent} */
310
- wda;
311
-
312
- /** @type {import('appium-remote-debugger').RemoteDebugger|null} */
313
- remote;
314
-
315
- /** @type {DriverLogs} */
316
- logs;
317
-
318
- /** @type {import('./commands/types').LogListener|undefined} */
319
- _bidiServerLogListener;
320
-
321
- /**
322
- *
323
- * @param {XCUITestDriverOpts} opts
324
- * @param {boolean} shouldValidateCaps
325
- */
326
- constructor(opts = /** @type {XCUITestDriverOpts} */ ({}), shouldValidateCaps = true) {
254
+ curWindowHandle: string | null | undefined;
255
+ selectingNewPage: boolean | undefined;
256
+ contexts: string[];
257
+ curContext: string | null;
258
+ curWebFrames: string[];
259
+
260
+ webviewCalibrationResult: CalibrationData | null;
261
+ asyncPromise: AsyncPromise | undefined;
262
+ asyncWaitMs: number | undefined;
263
+ _syslogWebsocketListener: ((logRecord: {message: string}) => void) | null;
264
+ _perfRecorders: PerfRecorder[];
265
+ webElementsCache: LRUCache<any, any>;
266
+
267
+ _conditionInducerService: any | null; // needs types
268
+ _remoteXPCConditionInducerConnection: DVTServiceWithConnection | null; // RemoteXPC DVT connection for iOS>=18 condition inducer
269
+ _isSafariIphone: boolean | undefined;
270
+ _isSafariNotched: boolean | undefined;
271
+ _waitingAtoms: WaitingAtoms;
272
+ lifecycleData: LifecycleData;
273
+
274
+ _audioRecorder: AudioRecorder | null;
275
+ xcodeVersion: XcodeVersion | undefined;
276
+ _trafficCapture: TrafficCapture | null;
277
+ _recentScreenRecorder: ScreenRecorder | null;
278
+ _device: Simulator | RealDevice;
279
+ _iosSdkVersion: string | null;
280
+ _wda: WebDriverAgent | null;
281
+ remote: RemoteDebugger | null;
282
+ logs: DriverLogs;
283
+ _bidiServerLogListener: LogListener | undefined;
284
+
285
+ // Additional properties that were missing
286
+ appInfosCache: AppInfosCache;
287
+ doesSupportBidi: boolean;
288
+ jwpProxyActive: boolean;
289
+ proxyReqRes: ((...args: any[]) => any) | null;
290
+ safari: boolean;
291
+ cachedWdaStatus: any;
292
+ _currentUrl: string | null;
293
+ pageLoadMs: number;
294
+ landscapeWebCoordsOffset: number;
295
+ mjpegStream?: mjpeg.MJpegStream;
296
+
297
+ constructor(opts: XCUITestDriverOpts, shouldValidateCaps = true) {
327
298
  super(opts, shouldValidateCaps);
328
299
 
329
300
  this.locatorStrategies = [
@@ -368,9 +339,368 @@ export class XCUITestDriver extends BaseDriver {
368
339
  this.appInfosCache = new AppInfosCache(this.log);
369
340
  this.remote = null;
370
341
  this.doesSupportBidi = true;
342
+ this._wda = null;
343
+ }
344
+
345
+ // Override methods from BaseDriver
346
+ override async createSession(
347
+ w3cCaps1: W3CXCUITestDriverCaps,
348
+ w3cCaps2?: W3CXCUITestDriverCaps,
349
+ w3cCaps3?: W3CXCUITestDriverCaps,
350
+ driverData?: DriverData[]
351
+ ): Promise<DefaultCreateSessionResult<XCUITestDriverConstraints>> {
352
+ try {
353
+ const [sessionId, initialCaps] = await super.createSession(w3cCaps1, w3cCaps2, w3cCaps3, driverData);
354
+ let caps = initialCaps;
355
+
356
+ // merge cli args to opts, and if we did merge any, revalidate opts to ensure the final set
357
+ // is also consistent
358
+ if (this.mergeCliArgsToOpts()) {
359
+ this.validateDesiredCaps({...caps, ...this.cliArgs});
360
+ }
361
+
362
+ await this.start();
363
+
364
+ // merge server capabilities + desired capabilities
365
+ caps = { ...defaultServerCaps, ...caps };
366
+ // update the udid with what is actually used
367
+ caps.udid = this.opts.udid;
368
+ // ensure we track nativeWebTap capability as a setting as well
369
+ if (_.has(this.opts, 'nativeWebTap')) {
370
+ await this.updateSettings({nativeWebTap: this.opts.nativeWebTap});
371
+ }
372
+ // ensure we track nativeWebTapStrict capability as a setting as well
373
+ if (_.has(this.opts, 'nativeWebTapStrict')) {
374
+ await this.updateSettings({nativeWebTapStrict: this.opts.nativeWebTapStrict});
375
+ }
376
+ // ensure we track useJSONSource capability as a setting as well
377
+ if (_.has(this.opts, 'useJSONSource')) {
378
+ await this.updateSettings({useJSONSource: this.opts.useJSONSource});
379
+ }
380
+
381
+ const wdaSettings: StringRecord = {
382
+ elementResponseAttributes: DEFAULT_SETTINGS.elementResponseAttributes,
383
+ shouldUseCompactResponses: DEFAULT_SETTINGS.shouldUseCompactResponses,
384
+ };
385
+ if ('elementResponseAttributes' in this.opts && _.isString(this.opts.elementResponseAttributes)) {
386
+ wdaSettings.elementResponseAttributes = this.opts.elementResponseAttributes;
387
+ }
388
+ if ('shouldUseCompactResponses' in this.opts && _.isBoolean(this.opts.shouldUseCompactResponses)) {
389
+ wdaSettings.shouldUseCompactResponses = this.opts.shouldUseCompactResponses;
390
+ }
391
+ if ('mjpegServerScreenshotQuality' in this.opts && _.isNumber(this.opts.mjpegServerScreenshotQuality)) {
392
+ wdaSettings.mjpegServerScreenshotQuality = this.opts.mjpegServerScreenshotQuality;
393
+ }
394
+ if ('mjpegServerFramerate' in this.opts && _.isNumber(this.opts.mjpegServerFramerate)) {
395
+ wdaSettings.mjpegServerFramerate = this.opts.mjpegServerFramerate;
396
+ }
397
+ if (_.has(this.opts, 'screenshotQuality')) {
398
+ this.log.info(`Setting the quality of phone screenshot: '${this.opts.screenshotQuality}'`);
399
+ wdaSettings.screenshotQuality = this.opts.screenshotQuality;
400
+ }
401
+ // ensure WDA gets our defaults instead of whatever its own might be
402
+ await this.updateSettings(wdaSettings);
403
+
404
+ await this.handleMjpegOptions();
405
+
406
+ return [
407
+ sessionId,
408
+ caps,
409
+ ];
410
+ } catch (e) {
411
+ this.log.error(JSON.stringify(e));
412
+ await this.deleteSession();
413
+ throw e;
414
+ }
415
+ }
416
+
417
+ override async deleteSession(sessionId?: string): Promise<void> {
418
+ await removeAllSessionWebSocketHandlers.bind(this)();
419
+
420
+ for (const recorder of _.compact([
421
+ this._recentScreenRecorder,
422
+ this._audioRecorder,
423
+ this._trafficCapture,
424
+ ])) {
425
+ await recorder.interrupt(true);
426
+ await recorder.cleanup();
427
+ }
428
+
429
+ if (!_.isEmpty(this._perfRecorders)) {
430
+ await B.all(this._perfRecorders.map((x) => x.stop(true)));
431
+ this._perfRecorders = [];
432
+ }
433
+
434
+ if (this._conditionInducerService || this._remoteXPCConditionInducerConnection) {
435
+ try {
436
+ await this.disableConditionInducer();
437
+ } catch (err) {
438
+ this.log.warn(`Cannot disable condition inducer: ${err.message}`);
439
+ }
440
+ }
441
+
442
+ await this.stop();
443
+
444
+ if (this._wda && this.isXcodebuildNeeded()) {
445
+ if (this.opts.clearSystemFiles) {
446
+ let synchronizationKey = XCUITestDriver.name;
447
+ const derivedDataPath = await this.wda.retrieveDerivedDataPath();
448
+ if (derivedDataPath) {
449
+ synchronizationKey = path.normalize(derivedDataPath);
450
+ }
451
+ await SHARED_RESOURCES_GUARD.acquire(synchronizationKey, async () => {
452
+ await clearSystemFiles(this.wda);
453
+ });
454
+ } else {
455
+ this.log.debug('Not clearing log files. Use `clearSystemFiles` capability to turn on.');
456
+ }
457
+ }
458
+
459
+ if (this.remote) {
460
+ this.log.debug('Found a remote debugger session. Removing...');
461
+ await this.stopRemote();
462
+ }
463
+
464
+ if (this.opts.resetOnSessionStartOnly === false) {
465
+ await this.runReset(true);
466
+ }
467
+
468
+ const simulatorDevice = this.isSimulator() ? this.device as Simulator : null;
469
+ if (simulatorDevice && this.lifecycleData.createSim) {
470
+ this.log.debug(`Deleting simulator created for this run (udid: '${simulatorDevice.udid}')`);
471
+ await shutdownSimulator.bind(this)();
472
+ await simulatorDevice.delete();
473
+ }
474
+
475
+ const shouldResetLocationService = this.isRealDevice() && !!this.opts.resetLocationService;
476
+ if (shouldResetLocationService) {
477
+ try {
478
+ await this.mobileResetLocationService();
479
+ } catch {
480
+ /* Ignore this error since mobileResetLocationService already logged the error */
481
+ }
482
+ }
483
+
484
+ await this.logs.syslog?.stopCapture();
485
+ _.values(this.logs).forEach((x: any) => x?.removeAllListeners?.());
486
+ if (this._bidiServerLogListener) {
487
+ this.log.unwrap().off('log', this._bidiServerLogListener);
488
+ }
489
+ this.logs = {};
490
+
491
+ if (this.mjpegStream) {
492
+ this.log.info('Closing MJPEG stream');
493
+ this.mjpegStream.stop();
494
+ }
495
+
496
+ this.resetIos();
497
+
498
+ await super.deleteSession(sessionId);
499
+ }
500
+
501
+ override async executeCommand(cmd: string, ...args: any[]): Promise<any> {
502
+ this.log.debug(`Executing command '${cmd}'`);
503
+
504
+ if (cmd === 'receiveAsyncResponse') {
505
+ return await this.receiveAsyncResponse(...args);
506
+ }
507
+ // TODO: once this fix gets into base driver remove from here
508
+ if (cmd === 'getStatus') {
509
+ return await this.getStatus();
510
+ }
511
+ return await super.executeCommand(cmd, ...args);
512
+ }
513
+
514
+ override proxyActive(): boolean {
515
+ return Boolean(this.jwpProxyActive);
516
+ }
517
+
518
+ override getProxyAvoidList(): RouteMatcher[] {
519
+ if (this.isWebview()) {
520
+ return NO_PROXY_WEB_LIST;
521
+ }
522
+ return NO_PROXY_NATIVE_LIST;
523
+ }
524
+
525
+ override canProxy(): boolean {
526
+ return true;
527
+ }
528
+
529
+ override validateLocatorStrategy(strategy: string): void {
530
+ super.validateLocatorStrategy(strategy, this.isWebContext());
531
+ }
532
+
533
+ override validateDesiredCaps(caps: any): caps is DriverCaps<XCUITestDriverConstraints> {
534
+ if (!super.validateDesiredCaps(caps)) {
535
+ return false;
536
+ }
537
+
538
+ // make sure that the capabilities have one of `app` or `bundleId`
539
+ if (_.toLower(caps.browserName) !== 'safari' && !caps.app && !caps.bundleId) {
540
+ this.log.info(
541
+ 'The desired capabilities include neither an app nor a bundleId. ' +
542
+ 'WebDriverAgent will be started without the default app',
543
+ );
544
+ }
545
+
546
+ if (!util.coerceVersion(String(caps.platformVersion), false)) {
547
+ this.log.warn(
548
+ `'platformVersion' capability ('${caps.platformVersion}') is not a valid version number. ` +
549
+ `Consider fixing it or be ready to experience an inconsistent driver behavior.`,
550
+ );
551
+ }
552
+
553
+ const verifyProcessArgument = (processArguments) => {
554
+ const {args, env} = processArguments;
555
+ if (!_.isNil(args) && !_.isArray(args)) {
556
+ throw this.log.errorWithException('processArguments.args must be an array of strings');
557
+ }
558
+ if (!_.isNil(env) && !_.isPlainObject(env)) {
559
+ throw this.log.errorWithException(
560
+ 'processArguments.env must be an object <key,value> pair {a:b, c:d}',
561
+ );
562
+ }
563
+ };
564
+
565
+ // `processArguments` should be JSON string or an object with arguments and/ environment details
566
+ if (caps.processArguments) {
567
+ if (_.isString(caps.processArguments)) {
568
+ try {
569
+ // try to parse the string as JSON
570
+ caps.processArguments = JSON.parse(caps.processArguments as string);
571
+ verifyProcessArgument(caps.processArguments);
572
+ } catch (err) {
573
+ throw this.log.errorWithException(
574
+ `processArguments must be a JSON format or an object with format {args : [], env : {a:b, c:d}}. ` +
575
+ `Both environment and argument can be null. Error: ${err}`,
576
+ );
577
+ }
578
+ } else if (_.isPlainObject(caps.processArguments)) {
579
+ verifyProcessArgument(caps.processArguments);
580
+ } else {
581
+ throw this.log.errorWithException(
582
+ `'processArguments must be an object, or a string JSON object with format {args : [], env : {a:b, c:d}}. ` +
583
+ `Both environment and argument can be null.`,
584
+ );
585
+ }
586
+ }
587
+
588
+ // there is no point in having `keychainPath` without `keychainPassword`
589
+ if (
590
+ (caps.keychainPath && !caps.keychainPassword) ||
591
+ (!caps.keychainPath && caps.keychainPassword)
592
+ ) {
593
+ throw this.log.errorWithException(
594
+ `If 'keychainPath' is set, 'keychainPassword' must also be set (and vice versa).`,
595
+ );
596
+ }
597
+
598
+ // `resetOnSessionStartOnly` should be set to true by default
599
+ this.opts.resetOnSessionStartOnly =
600
+ !util.hasValue(this.opts.resetOnSessionStartOnly) || this.opts.resetOnSessionStartOnly;
601
+ this.opts.useNewWDA = util.hasValue(this.opts.useNewWDA) ? this.opts.useNewWDA : false;
602
+
603
+ if (caps.commandTimeouts) {
604
+ caps.commandTimeouts = normalizeCommandTimeouts(caps.commandTimeouts);
605
+ }
606
+
607
+ if (_.isString(caps.webDriverAgentUrl)) {
608
+ const {protocol, host} = url.parse(caps.webDriverAgentUrl);
609
+ if (_.isEmpty(protocol) || _.isEmpty(host)) {
610
+ throw this.log.errorWithException(
611
+ `'webDriverAgentUrl' capability is expected to contain a valid WebDriverAgent server URL. ` +
612
+ `'${caps.webDriverAgentUrl}' is given instead`,
613
+ );
614
+ }
615
+ }
616
+
617
+ if (caps.browserName) {
618
+ if (caps.bundleId) {
619
+ throw this.log.errorWithException(
620
+ `'browserName' cannot be set together with 'bundleId' capability`
621
+ );
622
+ }
623
+ // warn if the capabilities have both `app` and `browser, although this
624
+ // is common with selenium grid
625
+ if (caps.app) {
626
+ this.log.warn(
627
+ `The capabilities should generally not include both an 'app' and a 'browserName'`,
628
+ );
629
+ }
630
+ }
631
+
632
+ if (caps.permissions) {
633
+ try {
634
+ for (const [bundleId, perms] of _.toPairs(JSON.parse(caps.permissions))) {
635
+ if (!_.isString(bundleId)) {
636
+ throw new Error(`'${JSON.stringify(bundleId)}' must be a string`);
637
+ }
638
+ if (!_.isPlainObject(perms)) {
639
+ throw new Error(`'${JSON.stringify(perms)}' must be a JSON object`);
640
+ }
641
+ }
642
+ } catch (e) {
643
+ throw this.log.errorWithException(
644
+ `'${caps.permissions}' is expected to be a valid object with format ` +
645
+ `{"<bundleId1>": {"<serviceName1>": "<serviceStatus1>", ...}, ...}. Original error: ${e.message}`,
646
+ );
647
+ }
648
+ }
649
+
650
+ if (caps.platformVersion && !util.coerceVersion(caps.platformVersion, false)) {
651
+ throw this.log.errorWithException(
652
+ `'platformVersion' must be a valid version number. ` +
653
+ `'${caps.platformVersion}' is given instead.`,
654
+ );
655
+ }
656
+
657
+ // additionalWebviewBundleIds is an array, JSON array, or string
658
+ if (caps.additionalWebviewBundleIds) {
659
+ caps.additionalWebviewBundleIds = this.helpers.parseCapsArray(
660
+ caps.additionalWebviewBundleIds as string | string[],
661
+ );
662
+ }
663
+
664
+ // finally, return true since the superclass check passed, as did this
665
+ return true;
666
+ }
667
+
668
+ // Getter methods
669
+ get wda(): WebDriverAgent {
670
+ if (!this._wda) {
671
+ throw new Error('WebDriverAgent is not initialized');
672
+ }
673
+ return this._wda;
674
+ }
675
+
676
+ get driverData(): Record<string, any> {
677
+ // TODO fill out resource info here
678
+ return {};
679
+ }
680
+
681
+ get device(): Simulator | RealDevice {
682
+ return this._device;
371
683
  }
372
684
 
373
- async onSettingsUpdate(key, value) {
685
+ // Utility methods
686
+ isSafari(): boolean {
687
+ return !!this.safari;
688
+ }
689
+
690
+ isRealDevice(): boolean {
691
+ return 'devicectl' in (this.device ?? {});
692
+ }
693
+
694
+ isSimulator(): boolean {
695
+ return 'simctl' in (this.device ?? {});
696
+ }
697
+
698
+ isXcodebuildNeeded(): boolean {
699
+ return !(CAP_NAMES_NO_XCODEBUILD_REQUIRED.some((x) => Boolean(this.opts[x])));
700
+ }
701
+
702
+ // Core driver methods
703
+ async onSettingsUpdate(key: string, value: any): Promise<any> {
374
704
  // skip sending the update request to the WDA nor saving it in opts
375
705
  // to not spend unnecessary time.
376
706
  if (['pageSourceExcludedAttributes'].includes(key)) {
@@ -385,56 +715,19 @@ export class XCUITestDriver extends BaseDriver {
385
715
  this.opts[key] = !!value;
386
716
  }
387
717
 
388
- resetIos() {
389
- this.opts = this.opts || {};
390
- // @ts-ignore this is ok
391
- this.wda = null;
392
- this.jwpProxyActive = false;
393
- this.proxyReqRes = null;
394
- this.safari = false;
395
- this.cachedWdaStatus = null;
396
-
397
- this.curWebFrames = [];
398
- this._currentUrl = null;
399
- this.curContext = null;
400
- this.xcodeVersion = undefined;
401
- this.contexts = [];
402
- this.implicitWaitMs = 0;
403
- this.pageLoadMs = 6000;
404
- this.landscapeWebCoordsOffset = 0;
405
- this.remote = null;
406
- this._conditionInducerService = null;
407
- this._remoteXPCConditionInducerConnection = null;
408
-
409
- this.webElementsCache = new LRUCache({
410
- max: WEB_ELEMENTS_CACHE_SIZE,
411
- });
412
-
413
- this._waitingAtoms = {
414
- count: 0,
415
- alertNotifier: new EventEmitter(),
416
- alertMonitor: B.resolve(),
417
- };
418
- }
419
-
420
- get driverData() {
421
- // TODO fill out resource info here
422
- return {};
423
- }
424
-
425
- async getStatus() {
718
+ async getStatus(): Promise<Record<string, any>> {
426
719
  const status = {
427
720
  ready: true,
428
721
  message: 'The driver is ready to accept new connections',
429
722
  build: await getDriverInfo(),
430
723
  };
431
724
  if (this.cachedWdaStatus) {
432
- status.wda = this.cachedWdaStatus;
725
+ (status as any).wda = this.cachedWdaStatus;
433
726
  }
434
727
  return status;
435
728
  }
436
729
 
437
- mergeCliArgsToOpts() {
730
+ mergeCliArgsToOpts(): boolean {
438
731
  let didMerge = false;
439
732
  // this.cliArgs should never include anything we do not expect.
440
733
  for (const [key, value] of Object.entries(this.cliArgs ?? {})) {
@@ -449,88 +742,7 @@ export class XCUITestDriver extends BaseDriver {
449
742
  return didMerge;
450
743
  }
451
744
 
452
- /**
453
- * @returns {Simulator|RealDevice}
454
- */
455
- get device() {
456
- return this._device;
457
- }
458
-
459
- isXcodebuildNeeded() {
460
- return !(CAP_NAMES_NO_XCODEBUILD_REQUIRED.some((x) => Boolean(this.opts[x])));
461
- }
462
-
463
- async createSession(w3cCaps1, w3cCaps2, w3cCaps3, driverData) {
464
- try {
465
- let [sessionId, caps] = await super.createSession(w3cCaps1, w3cCaps2, w3cCaps3, driverData);
466
-
467
- // merge cli args to opts, and if we did merge any, revalidate opts to ensure the final set
468
- // is also consistent
469
- if (this.mergeCliArgsToOpts()) {
470
- this.validateDesiredCaps({...caps, ...this.cliArgs});
471
- }
472
-
473
- await this.start();
474
-
475
- // merge server capabilities + desired capabilities
476
- caps = { ...defaultServerCaps, ...caps };
477
- // update the udid with what is actually used
478
- caps.udid = this.opts.udid;
479
- // ensure we track nativeWebTap capability as a setting as well
480
- if (_.has(this.opts, 'nativeWebTap')) {
481
- await this.updateSettings({nativeWebTap: this.opts.nativeWebTap});
482
- }
483
- // ensure we track nativeWebTapStrict capability as a setting as well
484
- if (_.has(this.opts, 'nativeWebTapStrict')) {
485
- await this.updateSettings({nativeWebTapStrict: this.opts.nativeWebTapStrict});
486
- }
487
- // ensure we track useJSONSource capability as a setting as well
488
- if (_.has(this.opts, 'useJSONSource')) {
489
- await this.updateSettings({useJSONSource: this.opts.useJSONSource});
490
- }
491
-
492
- /** @type {import('appium-webdriveragent').WDASettings} */
493
- let wdaSettings = {
494
- elementResponseAttributes: DEFAULT_SETTINGS.elementResponseAttributes,
495
- shouldUseCompactResponses: DEFAULT_SETTINGS.shouldUseCompactResponses,
496
- };
497
- if ('elementResponseAttributes' in this.opts && _.isString(this.opts.elementResponseAttributes)) {
498
- wdaSettings.elementResponseAttributes = this.opts.elementResponseAttributes;
499
- }
500
- if ('shouldUseCompactResponses' in this.opts && _.isBoolean(this.opts.shouldUseCompactResponses)) {
501
- wdaSettings.shouldUseCompactResponses = this.opts.shouldUseCompactResponses;
502
- }
503
- if ('mjpegServerScreenshotQuality' in this.opts && _.isNumber(this.opts.mjpegServerScreenshotQuality)) {
504
- wdaSettings.mjpegServerScreenshotQuality = this.opts.mjpegServerScreenshotQuality;
505
- }
506
- if ('mjpegServerFramerate' in this.opts && _.isNumber(this.opts.mjpegServerFramerate)) {
507
- wdaSettings.mjpegServerFramerate = this.opts.mjpegServerFramerate;
508
- }
509
- if (_.has(this.opts, 'screenshotQuality')) {
510
- this.log.info(`Setting the quality of phone screenshot: '${this.opts.screenshotQuality}'`);
511
- wdaSettings.screenshotQuality = this.opts.screenshotQuality;
512
- }
513
- // ensure WDA gets our defaults instead of whatever its own might be
514
- await this.updateSettings(wdaSettings);
515
-
516
- await this.handleMjpegOptions();
517
-
518
- return /** @type {[string, import('@appium/types').DriverCaps<XCUITestDriverConstraints>]} */ ([
519
- sessionId,
520
- caps,
521
- ]);
522
- } catch (e) {
523
- this.log.error(JSON.stringify(e));
524
- await this.deleteSession();
525
- throw e;
526
- }
527
- }
528
-
529
- /**
530
- * Handles MJPEG server-related capabilities
531
- * @returns {Promise<void>}
532
- */
533
- async handleMjpegOptions() {
745
+ async handleMjpegOptions(): Promise<void> {
534
746
  await this.allocateMjpegServerPort();
535
747
  // turn on mjpeg stream reading if requested
536
748
  if (this.opts.mjpegScreenshotUrl) {
@@ -540,12 +752,7 @@ export class XCUITestDriver extends BaseDriver {
540
752
  }
541
753
  }
542
754
 
543
- /**
544
- * Allocates and configures port forwarding for the MJPEG server
545
- * @returns {Promise<void>}
546
- * @throws {Error} If port forwarding fails and mjpegServerPort capability value is provided explicitly
547
- */
548
- async allocateMjpegServerPort() {
755
+ async allocateMjpegServerPort(): Promise<void> {
549
756
  const mjpegServerPort = this.opts.mjpegServerPort || DEFAULT_MJPEG_SERVER_PORT;
550
757
  this.log.debug(
551
758
  `Forwarding MJPEG server port ${mjpegServerPort} to local port ${mjpegServerPort}`,
@@ -573,16 +780,12 @@ export class XCUITestDriver extends BaseDriver {
573
780
  }
574
781
  }
575
782
 
576
- /**
577
- * Returns the default URL for Safari browser
578
- * @returns {string} The default URL
579
- */
580
- getDefaultUrl() {
783
+ getDefaultUrl(): string {
581
784
  // Setting this to some external URL slows down the session init
582
785
  return `${this.getWdaLocalhostRoot()}/health`;
583
786
  }
584
787
 
585
- async start() {
788
+ async start(): Promise<void> {
586
789
  this.opts.noReset = !!this.opts.noReset;
587
790
  this.opts.fullReset = !!this.opts.fullReset;
588
791
 
@@ -604,7 +807,7 @@ export class XCUITestDriver extends BaseDriver {
604
807
  this.log.info(
605
808
  `Setting simulator devices set path to '${this.opts.simulatorDevicesSetPath}'`,
606
809
  );
607
- (/** @type {Simulator} */ (this.device)).devicesSetPath = this.opts.simulatorDevicesSetPath;
810
+ (this.device as Simulator).devicesSetPath = this.opts.simulatorDevicesSetPath;
608
811
  }
609
812
  }
610
813
 
@@ -656,23 +859,30 @@ export class XCUITestDriver extends BaseDriver {
656
859
 
657
860
  await this.runReset();
658
861
 
659
- this.wda = new WebDriverAgent(
660
- /** @type {import('appium-xcode').XcodeVersion} */ (this.xcodeVersion),
862
+ this._wda = new WebDriverAgent(
863
+ // @ts-ignore This property is not used by WDA, and will be removed in the future
864
+ this.xcodeVersion,
661
865
  {
662
866
  ...this.opts,
663
867
  device: this.device,
664
868
  realDevice: this.isRealDevice(),
665
869
  iosSdkVersion: this._iosSdkVersion ?? undefined,
666
870
  reqBasePath: this.basePath,
667
- },
668
- // @ts-ignore this is ok
871
+ } as WebDriverAgentArgs,
669
872
  this.log,
670
873
  );
671
874
  // Derived data path retrieval is an expensive operation
672
875
  // We could start that now in background and get the cached result
673
876
  // whenever it is needed
674
- // eslint-disable-next-line promise/prefer-await-to-then
675
- this.wda.retrieveDerivedDataPath().catch((e) => this.log.debug(e));
877
+ (
878
+ async () => {
879
+ try {
880
+ await this.wda.retrieveDerivedDataPath();
881
+ } catch (e) {
882
+ this.log.debug(e);
883
+ }
884
+ }
885
+ )();
676
886
 
677
887
  const memoizedLogInfo = _.memoize(() => {
678
888
  this.log.info(
@@ -724,8 +934,8 @@ export class XCUITestDriver extends BaseDriver {
724
934
  if (this.isSimulator()) {
725
935
  if (this.opts.permissions) {
726
936
  this.log.debug('Setting the requested permissions before WDA is started');
727
- for (const [bundleId, permissionsMapping] of _.toPairs(JSON.parse(this.opts.permissions))) {
728
- await /** @type {Simulator} */ (this.device).setPermissions(bundleId, permissionsMapping);
937
+ for (const [bundleId, permissionsMapping] of _.toPairs(JSON.parse(this.opts.permissions as string))) {
938
+ await (this.device as Simulator).setPermissions(bundleId, permissionsMapping as StringRecord);
729
939
  }
730
940
  }
731
941
 
@@ -770,11 +980,41 @@ export class XCUITestDriver extends BaseDriver {
770
980
  }
771
981
  }
772
982
 
773
- /**
774
- * Start the simulator and initialize based on capabilities
775
- */
776
- async initSimulator() {
777
- const device = /** @type {Simulator} */ (this.device);
983
+ async runReset(enforceSimulatorShutdown = false): Promise<void> {
984
+ this.logEvent('resetStarted');
985
+ if (this.isRealDevice()) {
986
+ await runRealDeviceReset.bind(this)();
987
+ } else {
988
+ await runSimulatorReset.bind(this)(enforceSimulatorShutdown);
989
+ }
990
+ this.logEvent('resetComplete');
991
+ }
992
+
993
+ async stop(): Promise<void> {
994
+ this.jwpProxyActive = false;
995
+ this.proxyReqRes = null;
996
+
997
+ if (this._wda?.fullyStarted) {
998
+ if (this.wda.jwproxy) {
999
+ try {
1000
+ await this.proxyCommand(`/session/${this.sessionId}`, 'DELETE');
1001
+ } catch (err) {
1002
+ // an error here should not short-circuit the rest of clean up
1003
+ this.log.debug(`Unable to DELETE session on WDA: '${err.message}'. Continuing shutdown.`);
1004
+ }
1005
+ }
1006
+ // The former could cache the xcodebuild, so should not quit the process.
1007
+ // If the session skipped the xcodebuild (this.wda.canSkipXcodebuild), the this.wda instance
1008
+ // should quit properly.
1009
+ if ((!this.wda.webDriverAgentUrl && this.opts.useNewWDA) || this.wda.canSkipXcodebuild) {
1010
+ await this.wda.quit();
1011
+ }
1012
+ }
1013
+ DEVICE_CONNECTIONS_FACTORY.releaseConnection(this.opts.udid);
1014
+ }
1015
+
1016
+ async initSimulator(): Promise<void> {
1017
+ const device = this.device as Simulator;
778
1018
 
779
1019
  if (this.opts.shutdownOtherSimulators) {
780
1020
  this.assertFeatureEnabled(SHUTDOWN_OTHER_FEAT_NAME);
@@ -785,7 +1025,7 @@ export class XCUITestDriver extends BaseDriver {
785
1025
 
786
1026
  if (this.opts.customSSLCert) {
787
1027
  // Simulator must be booted in order to call this helper
788
- await device.addCertificate(this.opts.customSSLCert);
1028
+ await (device as Simulator).addCertificate(this.opts.customSSLCert);
789
1029
  this.logEvent('customCertInstalled');
790
1030
  }
791
1031
 
@@ -797,8 +1037,7 @@ export class XCUITestDriver extends BaseDriver {
797
1037
  this.log.debug('Localization preferences have been updated');
798
1038
  }
799
1039
 
800
- /** @type {Promise[]} */
801
- const promises = ['reduceMotion', 'reduceTransparency', 'autoFillPasswords']
1040
+ const promises: Promise<any>[] = ['reduceMotion', 'reduceTransparency', 'autoFillPasswords']
802
1041
  .filter((optName) => _.isBoolean(this.opts[optName]))
803
1042
  .map((optName) => {
804
1043
  this.log.info(`Setting ${optName} to ${this.opts[optName]}`);
@@ -823,10 +1062,7 @@ export class XCUITestDriver extends BaseDriver {
823
1062
  this.logEvent('simStarted');
824
1063
  }
825
1064
 
826
- /**
827
- * Start WebDriverAgentRunner
828
- */
829
- async startWda() {
1065
+ async startWda(): Promise<void> {
830
1066
  // Don't cleanup the processes if webDriverAgentUrl is set
831
1067
  if (!util.hasValue(this.wda.webDriverAgentUrl)) {
832
1068
  await this.wda.cleanupObsoleteProcesses();
@@ -920,8 +1156,7 @@ export class XCUITestDriver extends BaseDriver {
920
1156
  startupRetries = 1;
921
1157
  }
922
1158
 
923
- /** @type {Error|null} */
924
- let shortCircuitError = null;
1159
+ let shortCircuitError: Error | null = null;
925
1160
  let retryCount = 0;
926
1161
  await retryInterval(startupRetries, startupRetryInterval, async () => {
927
1162
  this.logEvent('wdaStartAttempted');
@@ -933,7 +1168,10 @@ export class XCUITestDriver extends BaseDriver {
933
1168
  await this.preparePreinstalledWda();
934
1169
  }
935
1170
 
936
- this.cachedWdaStatus = await this.wda.launch(/** @type {string} */ (this.sessionId));
1171
+ if (!this.sessionId) {
1172
+ throw new Error('Session ID is required but was not set');
1173
+ }
1174
+ this.cachedWdaStatus = await this.wda.launch(this.sessionId);
937
1175
  } catch (err) {
938
1176
  this.logEvent('wdaStartFailed');
939
1177
  this.log.debug(err.stack);
@@ -986,173 +1224,35 @@ export class XCUITestDriver extends BaseDriver {
986
1224
  throw new Error(errorMsg);
987
1225
  }
988
1226
 
989
- if (this.opts.clearSystemFiles && this.isXcodebuildNeeded()) {
990
- await markSystemFilesForCleanup(this.wda);
991
- }
992
-
993
- // We don't restrict the version, but show what version of WDA is running on the device for debugging purposes.
994
- if (this.cachedWdaStatus?.build) {
995
- this.log.info(`WebDriverAgent version: '${this.cachedWdaStatus.build.version}'`);
996
- } else {
997
- this.log.warn(
998
- `WebDriverAgent does not provide any version information. ` +
999
- `This might indicate either a custom or an outdated build.`
1000
- );
1001
- }
1002
-
1003
- // we expect certain socket errors until this point, but now
1004
- // mark things as fully working
1005
- this.wda.fullyStarted = true;
1006
- this.logEvent('wdaStarted');
1007
- });
1008
-
1009
- if (shortCircuitError) {
1010
- throw shortCircuitError;
1011
- }
1012
- });
1013
- }
1014
-
1015
- /**
1016
- *
1017
- * @param {boolean} [enforceSimulatorShutdown=false]
1018
- */
1019
- async runReset(enforceSimulatorShutdown = false) {
1020
- this.logEvent('resetStarted');
1021
- if (this.isRealDevice()) {
1022
- await runRealDeviceReset.bind(this)();
1023
- } else {
1024
- await runSimulatorReset.bind(this)(enforceSimulatorShutdown);
1025
- }
1026
- this.logEvent('resetComplete');
1027
- }
1028
-
1029
- async deleteSession(sessionId) {
1030
- await removeAllSessionWebSocketHandlers.bind(this)();
1031
-
1032
- for (const recorder of _.compact([
1033
- this._recentScreenRecorder,
1034
- this._audioRecorder,
1035
- this._trafficCapture,
1036
- ])) {
1037
- await recorder.interrupt(true);
1038
- await recorder.cleanup();
1039
- }
1040
-
1041
- if (!_.isEmpty(this._perfRecorders)) {
1042
- await B.all(this._perfRecorders.map((x) => x.stop(true)));
1043
- this._perfRecorders = [];
1044
- }
1045
-
1046
- if (this._conditionInducerService || this._remoteXPCConditionInducerConnection) {
1047
- try {
1048
- await this.disableConditionInducer();
1049
- } catch (err) {
1050
- this.log.warn(`Cannot disable condition inducer: ${err.message}`);
1051
- }
1052
- }
1053
-
1054
- await this.stop();
1055
-
1056
- if (this.wda && this.isXcodebuildNeeded()) {
1057
- if (this.opts.clearSystemFiles) {
1058
- let synchronizationKey = XCUITestDriver.name;
1059
- const derivedDataPath = await this.wda.retrieveDerivedDataPath();
1060
- if (derivedDataPath) {
1061
- synchronizationKey = path.normalize(derivedDataPath);
1062
- }
1063
- await SHARED_RESOURCES_GUARD.acquire(synchronizationKey, async () => {
1064
- await clearSystemFiles(this.wda);
1065
- });
1066
- } else {
1067
- this.log.debug('Not clearing log files. Use `clearSystemFiles` capability to turn on.');
1068
- }
1069
- }
1070
-
1071
- if (this.remote) {
1072
- this.log.debug('Found a remote debugger session. Removing...');
1073
- await this.stopRemote();
1074
- }
1075
-
1076
- if (this.opts.resetOnSessionStartOnly === false) {
1077
- await this.runReset(true);
1078
- }
1079
-
1080
- const simulatorDevice = this.isSimulator() ? /** @type {Simulator} */ (this.device) : null;
1081
- if (simulatorDevice && this.lifecycleData.createSim) {
1082
- this.log.debug(`Deleting simulator created for this run (udid: '${simulatorDevice.udid}')`);
1083
- await shutdownSimulator.bind(this)();
1084
- await simulatorDevice.delete();
1085
- }
1086
-
1087
- const shouldResetLocationService = this.isRealDevice() && !!this.opts.resetLocationService;
1088
- if (shouldResetLocationService) {
1089
- try {
1090
- await this.mobileResetLocationService();
1091
- } catch {
1092
- /* Ignore this error since mobileResetLocationService already logged the error */
1093
- }
1094
- }
1095
-
1096
- await this.logs.syslog?.stopCapture();
1097
- _.values(this.logs).forEach((x) => x.removeAllListeners());
1098
- if (this._bidiServerLogListener) {
1099
- this.log.unwrap().off('log', this._bidiServerLogListener);
1100
- }
1101
- this.logs = {};
1102
-
1103
- if (this.mjpegStream) {
1104
- this.log.info('Closing MJPEG stream');
1105
- this.mjpegStream.stop();
1106
- }
1107
-
1108
- this.resetIos();
1227
+ if (this.opts.clearSystemFiles && this.isXcodebuildNeeded()) {
1228
+ await markSystemFilesForCleanup(this.wda);
1229
+ }
1109
1230
 
1110
- await super.deleteSession(sessionId);
1111
- }
1231
+ // We don't restrict the version, but show what version of WDA is running on the device for debugging purposes.
1232
+ if (this.cachedWdaStatus?.build) {
1233
+ this.log.info(`WebDriverAgent version: '${this.cachedWdaStatus.build.version}'`);
1234
+ } else {
1235
+ this.log.warn(
1236
+ `WebDriverAgent does not provide any version information. ` +
1237
+ `This might indicate either a custom or an outdated build.`
1238
+ );
1239
+ }
1112
1240
 
1113
- async stop() {
1114
- this.jwpProxyActive = false;
1115
- this.proxyReqRes = null;
1241
+ // we expect certain socket errors until this point, but now
1242
+ // mark things as fully working
1243
+ this.wda.fullyStarted = true;
1244
+ this.logEvent('wdaStarted');
1245
+ });
1116
1246
 
1117
- if (this.wda?.fullyStarted) {
1118
- if (this.wda.jwproxy) {
1119
- try {
1120
- await this.proxyCommand(`/session/${this.sessionId}`, 'DELETE');
1121
- } catch (err) {
1122
- // an error here should not short-circuit the rest of clean up
1123
- this.log.debug(`Unable to DELETE session on WDA: '${err.message}'. Continuing shutdown.`);
1124
- }
1125
- }
1126
- // The former could cache the xcodebuild, so should not quit the process.
1127
- // If the session skipped the xcodebuild (this.wda.canSkipXcodebuild), the this.wda instance
1128
- // should quit properly.
1129
- if ((!this.wda.webDriverAgentUrl && this.opts.useNewWDA) || this.wda.canSkipXcodebuild) {
1130
- await this.wda.quit();
1247
+ if (shortCircuitError) {
1248
+ throw shortCircuitError;
1131
1249
  }
1132
- }
1133
- DEVICE_CONNECTIONS_FACTORY.releaseConnection(this.opts.udid);
1250
+ });
1134
1251
  }
1135
1252
 
1136
- /**
1137
- *
1138
- * @param {string} cmd
1139
- * @param {...any} args
1140
- * @returns {Promise<any>}
1141
- */
1142
- async executeCommand(cmd, ...args) {
1143
- this.log.debug(`Executing command '${cmd}'`);
1144
1253
 
1145
- if (cmd === 'receiveAsyncResponse') {
1146
- return await this.receiveAsyncResponse(...args);
1147
- }
1148
- // TODO: once this fix gets into base driver remove from here
1149
- if (cmd === 'getStatus') {
1150
- return await this.getStatus();
1151
- }
1152
- return await super.executeCommand(cmd, ...args);
1153
- }
1154
1254
 
1155
- async configureApp() {
1255
+ async configureApp(): Promise<void> {
1156
1256
  function appIsPackageOrBundle(app) {
1157
1257
  return /^([a-zA-Z0-9\-_]+\.[a-zA-Z0-9\-_]+)+$/.test(app);
1158
1258
  }
@@ -1176,22 +1276,24 @@ export class XCUITestDriver extends BaseDriver {
1176
1276
  switch (_.toLower(this.opts.app)) {
1177
1277
  case 'settings':
1178
1278
  this.opts.bundleId = 'com.apple.Preferences';
1179
- this.opts.app = null;
1279
+ this.opts.app = undefined;
1180
1280
  return;
1181
1281
  case 'calendar':
1182
1282
  this.opts.bundleId = 'com.apple.mobilecal';
1183
- this.opts.app = null;
1283
+ this.opts.app = undefined;
1184
1284
  return;
1185
1285
  }
1186
1286
 
1187
- this.opts.app = await this.helpers.configureApp(this.opts.app, {
1287
+ this.opts.app = await this.helpers.configureApp(this.opts.app as string, {
1188
1288
  onPostProcess: onPostConfigureApp.bind(this),
1189
1289
  onDownload: onDownloadApp.bind(this),
1190
1290
  supportedExtensions: SUPPORTED_EXTENSIONS,
1191
1291
  });
1192
1292
  }
1193
1293
 
1194
- async determineDevice() {
1294
+
1295
+
1296
+ async determineDevice(): Promise<{device: Simulator | RealDevice, realDevice: boolean, udid: string}> {
1195
1297
  // in the one case where we create a sim, we will set this state
1196
1298
  this.lifecycleData.createSim = false;
1197
1299
 
@@ -1258,8 +1360,8 @@ export class XCUITestDriver extends BaseDriver {
1258
1360
  }
1259
1361
 
1260
1362
  this.log.debug(`Creating iDevice object with udid '${this.opts.udid}'`);
1261
- const device = new RealDevice(this.opts.udid, this.log);
1262
- return {device, realDevice: true, udid: this.opts.udid};
1363
+ const device = new RealDevice(this.opts.udid as string, this.log);
1364
+ return {device, realDevice: true, udid: this.opts.udid as string};
1263
1365
  }
1264
1366
 
1265
1367
  this.log.info(
@@ -1286,11 +1388,9 @@ export class XCUITestDriver extends BaseDriver {
1286
1388
  return {device, realDevice: false, udid: device.udid};
1287
1389
  }
1288
1390
 
1289
- async startSim() {
1290
- /** @type {import('appium-ios-simulator').DevicePreferences} */
1291
- const devicePreferences = {};
1292
- /** @type {import('appium-ios-simulator').RunOptions} */
1293
- const runOpts = {
1391
+ async startSim(): Promise<void> {
1392
+ const devicePreferences: any = {};
1393
+ const runOpts: any = {
1294
1394
  scaleFactor: this.opts.scaleFactor,
1295
1395
  connectHardwareKeyboard: !!this.opts.connectHardwareKeyboard,
1296
1396
  pasteboardAutomaticSync: this.opts.simulatorPasteboardAutomaticSync ?? 'off',
@@ -1310,7 +1410,7 @@ export class XCUITestDriver extends BaseDriver {
1310
1410
 
1311
1411
  // This is to workaround XCTest bug about changing Simulator
1312
1412
  // orientation is not synchronized to the actual window orientation
1313
- const orientation = _.isString(this.opts.orientation) && this.opts.orientation.toUpperCase();
1413
+ const orientation = _.isString(this.opts.orientation) && (this.opts.orientation as string).toUpperCase();
1314
1414
  switch (orientation) {
1315
1415
  case 'LANDSCAPE':
1316
1416
  devicePreferences.SimulatorWindowOrientation = 'LandscapeLeft';
@@ -1322,10 +1422,10 @@ export class XCUITestDriver extends BaseDriver {
1322
1422
  break;
1323
1423
  }
1324
1424
 
1325
- await /** @type {Simulator} */ (this.device).run(runOpts);
1425
+ await (this.device as Simulator).run(runOpts);
1326
1426
  }
1327
1427
 
1328
- async createSim() {
1428
+ async createSim(): Promise<Simulator> {
1329
1429
  this.lifecycleData.createSim = true;
1330
1430
  // create sim for caps
1331
1431
  const sim = await createSim.bind(this)();
@@ -1333,7 +1433,7 @@ export class XCUITestDriver extends BaseDriver {
1333
1433
  return sim;
1334
1434
  }
1335
1435
 
1336
- async startWdaSession(bundleId, processArguments) {
1436
+ async startWdaSession(bundleId?: string, processArguments?: any): Promise<void> {
1337
1437
  const args = processArguments ? _.cloneDeep(processArguments.args) || [] : [];
1338
1438
  if (!_.isArray(args)) {
1339
1439
  throw new Error(
@@ -1371,8 +1471,7 @@ export class XCUITestDriver extends BaseDriver {
1371
1471
  env.TZ = this.opts.appTimeZone;
1372
1472
  }
1373
1473
 
1374
- /** @type {import('appium-webdriveragent').WDACapabilities} */
1375
- const wdaCaps = {
1474
+ const wdaCaps: StringRecord = {
1376
1475
  bundleId: this.opts.autoLaunch === false ? undefined : bundleId,
1377
1476
  arguments: args,
1378
1477
  environment: env,
@@ -1415,199 +1514,10 @@ export class XCUITestDriver extends BaseDriver {
1415
1514
  this.log.info(`WDA session startup took ${timer.getDuration().asMilliSeconds.toFixed(0)}ms`);
1416
1515
  }
1417
1516
 
1418
- // Override Proxy methods from BaseDriver
1419
- proxyActive() {
1420
- return Boolean(this.jwpProxyActive);
1421
- }
1422
-
1423
- getProxyAvoidList() {
1424
- if (this.isWebview()) {
1425
- return NO_PROXY_WEB_LIST;
1426
- }
1427
- return NO_PROXY_NATIVE_LIST;
1428
- }
1429
-
1430
- canProxy() {
1431
- return true;
1432
- }
1433
-
1434
- /**
1435
- * @returns {boolean}
1436
- */
1437
- isSafari() {
1438
- return !!this.safari;
1439
- }
1440
-
1441
- /**
1442
- * @returns {boolean}
1443
- */
1444
- isRealDevice() {
1445
- return 'devicectl' in (this.device ?? {});
1446
- }
1447
-
1448
- /**
1449
- * @returns {boolean}
1450
- */
1451
- isSimulator() {
1452
- return 'simctl' in (this.device ?? {});
1453
- }
1454
-
1455
- /**
1456
- * @param {string} strategy
1457
- */
1458
- validateLocatorStrategy(strategy) {
1459
- super.validateLocatorStrategy(strategy, this.isWebContext());
1460
- }
1461
-
1462
- /**
1463
- * @param {any} caps
1464
- * @returns {caps is import('@appium/types').DriverCaps<XCUITestDriverConstraints>}
1465
- */
1466
- validateDesiredCaps(caps) {
1467
- if (!super.validateDesiredCaps(caps)) {
1468
- return false;
1469
- }
1470
-
1471
- // make sure that the capabilities have one of `app` or `bundleId`
1472
- if (_.toLower(caps.browserName) !== 'safari' && !caps.app && !caps.bundleId) {
1473
- this.log.info(
1474
- 'The desired capabilities include neither an app nor a bundleId. ' +
1475
- 'WebDriverAgent will be started without the default app',
1476
- );
1477
- }
1478
-
1479
- if (!util.coerceVersion(String(caps.platformVersion), false)) {
1480
- this.log.warn(
1481
- `'platformVersion' capability ('${caps.platformVersion}') is not a valid version number. ` +
1482
- `Consider fixing it or be ready to experience an inconsistent driver behavior.`,
1483
- );
1484
- }
1485
-
1486
- let verifyProcessArgument = (processArguments) => {
1487
- const {args, env} = processArguments;
1488
- if (!_.isNil(args) && !_.isArray(args)) {
1489
- throw this.log.errorWithException('processArguments.args must be an array of strings');
1490
- }
1491
- if (!_.isNil(env) && !_.isPlainObject(env)) {
1492
- throw this.log.errorWithException(
1493
- 'processArguments.env must be an object <key,value> pair {a:b, c:d}',
1494
- );
1495
- }
1496
- };
1497
-
1498
- // `processArguments` should be JSON string or an object with arguments and/ environment details
1499
- if (caps.processArguments) {
1500
- if (_.isString(caps.processArguments)) {
1501
- try {
1502
- // try to parse the string as JSON
1503
- caps.processArguments = JSON.parse(caps.processArguments);
1504
- verifyProcessArgument(caps.processArguments);
1505
- } catch (err) {
1506
- throw this.log.errorWithException(
1507
- `processArguments must be a JSON format or an object with format {args : [], env : {a:b, c:d}}. ` +
1508
- `Both environment and argument can be null. Error: ${err}`,
1509
- );
1510
- }
1511
- } else if (_.isPlainObject(caps.processArguments)) {
1512
- verifyProcessArgument(caps.processArguments);
1513
- } else {
1514
- throw this.log.errorWithException(
1515
- `'processArguments must be an object, or a string JSON object with format {args : [], env : {a:b, c:d}}. ` +
1516
- `Both environment and argument can be null.`,
1517
- );
1518
- }
1519
- }
1520
-
1521
- // there is no point in having `keychainPath` without `keychainPassword`
1522
- if (
1523
- (caps.keychainPath && !caps.keychainPassword) ||
1524
- (!caps.keychainPath && caps.keychainPassword)
1525
- ) {
1526
- throw this.log.errorWithException(
1527
- `If 'keychainPath' is set, 'keychainPassword' must also be set (and vice versa).`,
1528
- );
1529
- }
1530
-
1531
- // `resetOnSessionStartOnly` should be set to true by default
1532
- this.opts.resetOnSessionStartOnly =
1533
- !util.hasValue(this.opts.resetOnSessionStartOnly) || this.opts.resetOnSessionStartOnly;
1534
- this.opts.useNewWDA = util.hasValue(this.opts.useNewWDA) ? this.opts.useNewWDA : false;
1535
-
1536
- if (caps.commandTimeouts) {
1537
- caps.commandTimeouts = normalizeCommandTimeouts(caps.commandTimeouts);
1538
- }
1539
-
1540
- if (_.isString(caps.webDriverAgentUrl)) {
1541
- const {protocol, host} = url.parse(caps.webDriverAgentUrl);
1542
- if (_.isEmpty(protocol) || _.isEmpty(host)) {
1543
- throw this.log.errorWithException(
1544
- `'webDriverAgentUrl' capability is expected to contain a valid WebDriverAgent server URL. ` +
1545
- `'${caps.webDriverAgentUrl}' is given instead`,
1546
- );
1547
- }
1548
- }
1549
-
1550
- if (caps.browserName) {
1551
- if (caps.bundleId) {
1552
- throw this.log.errorWithException(
1553
- `'browserName' cannot be set together with 'bundleId' capability`
1554
- );
1555
- }
1556
- // warn if the capabilities have both `app` and `browser, although this
1557
- // is common with selenium grid
1558
- if (caps.app) {
1559
- this.log.warn(
1560
- `The capabilities should generally not include both an 'app' and a 'browserName'`,
1561
- );
1562
- }
1563
- }
1564
-
1565
- if (caps.permissions) {
1566
- try {
1567
- for (const [bundleId, perms] of _.toPairs(JSON.parse(caps.permissions))) {
1568
- if (!_.isString(bundleId)) {
1569
- throw new Error(`'${JSON.stringify(bundleId)}' must be a string`);
1570
- }
1571
- if (!_.isPlainObject(perms)) {
1572
- throw new Error(`'${JSON.stringify(perms)}' must be a JSON object`);
1573
- }
1574
- }
1575
- } catch (e) {
1576
- throw this.log.errorWithException(
1577
- `'${caps.permissions}' is expected to be a valid object with format ` +
1578
- `{"<bundleId1>": {"<serviceName1>": "<serviceStatus1>", ...}, ...}. Original error: ${e.message}`,
1579
- );
1580
- }
1581
- }
1582
-
1583
- if (caps.platformVersion && !util.coerceVersion(caps.platformVersion, false)) {
1584
- throw this.log.errorWithException(
1585
- `'platformVersion' must be a valid version number. ` +
1586
- `'${caps.platformVersion}' is given instead.`,
1587
- );
1588
- }
1589
-
1590
- // additionalWebviewBundleIds is an array, JSON array, or string
1591
- if (caps.additionalWebviewBundleIds) {
1592
- caps.additionalWebviewBundleIds = this.helpers.parseCapsArray(
1593
- caps.additionalWebviewBundleIds,
1594
- );
1595
- }
1596
-
1597
- // finally, return true since the superclass check passed, as did this
1598
- return true;
1599
- }
1600
-
1601
- /**
1602
- * Check if the given app can be installed, or should uninstall before installing it.
1603
- *
1604
- * @param {AutInstallationStateOptions} [opts]
1605
- * @returns {Promise<AutInstallationState>}
1606
- */
1607
- async checkAutInstallationState(opts) {
1517
+ async checkAutInstallationState(opts?: AutInstallationStateOptions): Promise<AutInstallationState> {
1608
1518
  const {enforceAppInstall, fullReset, noReset, bundleId, app} = opts ?? this.opts;
1609
1519
 
1610
- const wasAppInstalled = await this.device.isAppInstalled(bundleId);
1520
+ const wasAppInstalled = !!bundleId && await this.device.isAppInstalled(bundleId);
1611
1521
  if (wasAppInstalled) {
1612
1522
  this.log.info(`App '${bundleId}' is already installed`);
1613
1523
  if (noReset) {
@@ -1628,7 +1538,7 @@ export class XCUITestDriver extends BaseDriver {
1628
1538
  };
1629
1539
  }
1630
1540
 
1631
- const candidateBundleVersion = await this.appInfosCache.extractBundleVersion(app);
1541
+ const candidateBundleVersion = app ? await this.appInfosCache.extractBundleVersion(app) : undefined;
1632
1542
  this.log.debug(`CFBundleVersion from Info.plist: ${candidateBundleVersion}`);
1633
1543
  if (!candidateBundleVersion) {
1634
1544
  return {
@@ -1638,8 +1548,8 @@ export class XCUITestDriver extends BaseDriver {
1638
1548
  }
1639
1549
 
1640
1550
  const appBundleVersion = this.isRealDevice()
1641
- ? (await /** @type {RealDevice} */ (this.device).fetchAppInfo(bundleId))?.CFBundleVersion
1642
- : BUNDLE_VERSION_PATTERN.exec(await /** @type {Simulator} */ (this.device).simctl.appInfo(bundleId))?.[1];
1551
+ ? (await (this.device as RealDevice).fetchAppInfo(bundleId))?.CFBundleVersion
1552
+ : BUNDLE_VERSION_PATTERN.exec(await (this.device as Simulator).simctl.appInfo(bundleId))?.[1];
1643
1553
  this.log.debug(`CFBundleVersion from installed app info: ${appBundleVersion}`);
1644
1554
  if (!appBundleVersion) {
1645
1555
  return {
@@ -1648,7 +1558,7 @@ export class XCUITestDriver extends BaseDriver {
1648
1558
  };
1649
1559
  }
1650
1560
 
1651
- let shouldUpgrade;
1561
+ let shouldUpgrade: boolean;
1652
1562
  try {
1653
1563
  shouldUpgrade = util.compareVersions(candidateBundleVersion, '>', appBundleVersion);
1654
1564
  } catch (err) {
@@ -1675,7 +1585,7 @@ export class XCUITestDriver extends BaseDriver {
1675
1585
  };
1676
1586
  }
1677
1587
 
1678
- async installAUT() {
1588
+ async installAUT(): Promise<void> {
1679
1589
  // install any other apps
1680
1590
  if (this.opts.otherApps) {
1681
1591
  await this.installOtherApps(this.opts.otherApps);
@@ -1702,7 +1612,7 @@ export class XCUITestDriver extends BaseDriver {
1702
1612
  }
1703
1613
  if (util.hasValue(this.opts.iosInstallPause)) {
1704
1614
  // https://github.com/appium/appium/issues/6889
1705
- const pauseMs = parseInt(this.opts.iosInstallPause, 10);
1615
+ const pauseMs = this.opts.iosInstallPause;
1706
1616
  this.log.debug(`iosInstallPause set. Pausing ${pauseMs} ms before continuing`);
1707
1617
  await B.delay(pauseMs);
1708
1618
  }
@@ -1710,13 +1620,8 @@ export class XCUITestDriver extends BaseDriver {
1710
1620
  }
1711
1621
  }
1712
1622
 
1713
- /**
1714
- * @param {string|string[]} otherApps
1715
- * @returns {Promise<void>}
1716
- */
1717
- async installOtherApps(otherApps) {
1718
- /** @type {string[]|undefined} */
1719
- let appsList;
1623
+ async installOtherApps(otherApps: string | string[]): Promise<void> {
1624
+ let appsList: string[] | undefined;
1720
1625
  try {
1721
1626
  appsList = this.helpers.parseCapsArray(otherApps);
1722
1627
  } catch (e) {
@@ -1727,14 +1632,12 @@ export class XCUITestDriver extends BaseDriver {
1727
1632
  return;
1728
1633
  }
1729
1634
 
1730
- /** @type {string[]} */
1731
- const appPaths = await B.all(appsList.map((app) => this.helpers.configureApp(app, {
1635
+ const appPaths: string[] = await B.all(appsList.map((app) => this.helpers.configureApp(app, {
1732
1636
  onPostProcess: onPostConfigureApp.bind(this),
1733
1637
  onDownload: onDownloadApp.bind(this),
1734
1638
  supportedExtensions: SUPPORTED_EXTENSIONS,
1735
1639
  })));
1736
- /** @type {string[]} */
1737
- const appIds = await B.all(appPaths.map((appPath) => this.appInfosCache.extractBundleId(appPath)));
1640
+ const appIds: string[] = await B.all(appPaths.map((appPath) => this.appInfosCache.extractBundleId(appPath)));
1738
1641
  for (const [appId, appPath] of _.zip(appIds, appPaths)) {
1739
1642
  if (this.isRealDevice()) {
1740
1643
  await installToRealDevice.bind(this)(
@@ -1757,11 +1660,7 @@ export class XCUITestDriver extends BaseDriver {
1757
1660
  }
1758
1661
  }
1759
1662
 
1760
- /**
1761
- * @param {string} orientation
1762
- * @returns {Promise<void>}
1763
- */
1764
- async setInitialOrientation(orientation) {
1663
+ async setInitialOrientation(orientation: string): Promise<void> {
1765
1664
  const dstOrientation = _.toUpper(orientation);
1766
1665
  if (!SUPPORTED_ORIENATIONS.includes(dstOrientation)) {
1767
1666
  this.log.debug(
@@ -1779,30 +1678,14 @@ export class XCUITestDriver extends BaseDriver {
1779
1678
  }
1780
1679
  }
1781
1680
 
1782
- /**
1783
- * @param {string} [cmdName]
1784
- * @returns {number|undefined}
1785
- */
1786
- _getCommandTimeout(cmdName) {
1787
- if (this.opts.commandTimeouts) {
1788
- if (cmdName && _.has(this.opts.commandTimeouts, cmdName)) {
1789
- return this.opts.commandTimeouts[cmdName];
1790
- }
1791
- return this.opts.commandTimeouts[DEFAULT_TIMEOUT_KEY];
1792
- }
1793
- }
1794
-
1795
- /**
1796
- * Reset the current session (run the delete session and create session subroutines)
1797
- */
1798
- async reset() {
1681
+ async reset(): Promise<never> {
1799
1682
  throw new Error(
1800
1683
  `The reset API has been deprecated and is not supported anymore. ` +
1801
1684
  `Consider using corresponding 'mobile:' extensions to manage the state of the app under test.`,
1802
1685
  );
1803
1686
  }
1804
1687
 
1805
- async preparePreinstalledWda() {
1688
+ async preparePreinstalledWda(): Promise<void> {
1806
1689
  if (this.isRealDevice()) {
1807
1690
  // Stop the existing process before starting a new one to start a fresh WDA process every session.
1808
1691
  await this.mobileKillApp(this.wda.bundleIdForXctest);
@@ -1838,6 +1721,46 @@ export class XCUITestDriver extends BaseDriver {
1838
1721
  }
1839
1722
  }
1840
1723
 
1724
+ resetIos(): void {
1725
+ this.opts = this.opts || {};
1726
+ this._wda = null;
1727
+ this.jwpProxyActive = false;
1728
+ this.proxyReqRes = null;
1729
+ this.safari = false;
1730
+ this.cachedWdaStatus = null;
1731
+
1732
+ this.curWebFrames = [];
1733
+ this._currentUrl = null;
1734
+ this.curContext = null;
1735
+ this.xcodeVersion = undefined;
1736
+ this.contexts = [];
1737
+ this.implicitWaitMs = 0;
1738
+ this.pageLoadMs = 6000;
1739
+ this.landscapeWebCoordsOffset = 0;
1740
+ this.remote = null;
1741
+ this._conditionInducerService = null;
1742
+ this._remoteXPCConditionInducerConnection = null;
1743
+
1744
+ this.webElementsCache = new LRUCache({
1745
+ max: WEB_ELEMENTS_CACHE_SIZE,
1746
+ });
1747
+
1748
+ this._waitingAtoms = {
1749
+ count: 0,
1750
+ alertNotifier: new EventEmitter(),
1751
+ alertMonitor: B.resolve(),
1752
+ };
1753
+ }
1754
+
1755
+ _getCommandTimeout(cmdName?: string): number | undefined {
1756
+ if (this.opts.commandTimeouts) {
1757
+ if (cmdName && _.has(this.opts.commandTimeouts, cmdName)) {
1758
+ return this.opts.commandTimeouts[cmdName];
1759
+ }
1760
+ return this.opts.commandTimeouts[DEFAULT_TIMEOUT_KEY];
1761
+ }
1762
+ }
1763
+
1841
1764
  /*---------------+
1842
1765
  | ACTIVEAPPINFO |
1843
1766
  +---------------+*/
@@ -2288,35 +2211,20 @@ export class XCUITestDriver extends BaseDriver {
2288
2211
 
2289
2212
  export default XCUITestDriver;
2290
2213
 
2291
- /**
2292
- * @template {import('@appium/types').Constraints} C
2293
- * @template [Ctx=string]
2294
- * @typedef {import('@appium/types').ExternalDriver<C, Ctx>} ExternalDriver
2295
- */
2296
-
2297
- /**
2298
- * @typedef {Pick<XCUITestDriverOpts, 'enforceAppInstall' | 'fullReset' | 'noReset' | 'bundleId' | 'app'>} AutInstallationStateOptions
2299
- */
2300
-
2301
- /**
2302
- * @typedef {Object} AutInstallationState
2303
- * @property {boolean} install - If the given app should install, or not need to install.
2304
- * @property {boolean} skipUninstall - If the installed app should be uninstalled, or not.
2305
- */
2306
-
2307
- /**
2308
- * @typedef {typeof desiredCapConstraints} XCUITestDriverConstraints
2309
- * @typedef {import('@appium/types').DriverOpts<XCUITestDriverConstraints>} XCUITestDriverOpts
2310
- * @typedef {import('./commands/types').FullContext} FullContext
2311
- * @typedef {import('appium-xcode').XcodeVersion} XcodeVersion
2312
- * @typedef {import('appium-ios-simulator').Simulator} Simulator
2313
- */
2314
-
2315
- /**
2316
- * @typedef {Object} DriverLogs
2317
- * @property {import('./device-log/ios-device-log').IOSDeviceLog|import('./device-log/ios-simulator-log').IOSSimulatorLog} [syslog]
2318
- * @property {import('./device-log/ios-crash-log').IOSCrashLog} [crashlog]
2319
- * @property {import('./device-log/safari-console-log').SafariConsoleLog} [safariConsole]
2320
- * @property {import('./device-log/safari-network-log').SafariNetworkLog} [safariNetwork]
2321
- * @property {import('./device-log/ios-performance-log').IOSPerformanceLog} [performance]
2322
- */
2214
+ export type AutInstallationStateOptions = Pick<XCUITestDriverOpts, 'enforceAppInstall' | 'fullReset' | 'noReset' | 'bundleId' | 'app'>;
2215
+
2216
+ export interface AutInstallationState {
2217
+ install: boolean; // If the given app should install, or not need to install.
2218
+ skipUninstall: boolean; // If the installed app should be uninstalled, or not.
2219
+ }
2220
+
2221
+ export type XCUITestDriverOpts = DriverOpts<XCUITestDriverConstraints>;
2222
+ export type W3CXCUITestDriverCaps = W3CDriverCaps<XCUITestDriverConstraints>;
2223
+
2224
+ export interface DriverLogs {
2225
+ syslog?: IOSDeviceLog | IOSSimulatorLog;
2226
+ crashlog?: IOSCrashLog;
2227
+ safariConsole?: SafariConsoleLog;
2228
+ safariNetwork?: SafariNetworkLog;
2229
+ performance?: IOSPerformanceLog;
2230
+ }