@limrun/appium-xcuitest-driver 10.4.3-lim.1 → 10.10.1-lim.10

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