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