@limrun/appium-xcuitest-driver 10.4.3-lim.1 → 10.10.1-lim.1
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.
- package/CHANGELOG.md +84 -0
- package/build/lib/app-utils.d.ts +2 -2
- package/build/lib/app-utils.d.ts.map +1 -1
- package/build/lib/app-utils.js +4 -1
- package/build/lib/app-utils.js.map +1 -1
- package/build/lib/commands/app-management.js +2 -2
- package/build/lib/commands/app-management.js.map +1 -1
- package/build/lib/commands/appearance.js +2 -2
- package/build/lib/commands/appearance.js.map +1 -1
- package/build/lib/commands/bidi/models.d.ts.map +1 -1
- package/build/lib/commands/bidi/models.js +1 -0
- package/build/lib/commands/bidi/models.js.map +1 -1
- package/build/lib/commands/bidi/types.d.ts +1 -0
- package/build/lib/commands/bidi/types.d.ts.map +1 -1
- package/build/lib/commands/biometric.js +3 -3
- package/build/lib/commands/biometric.js.map +1 -1
- package/build/lib/commands/certificate.d.ts.map +1 -1
- package/build/lib/commands/certificate.js +9 -3
- package/build/lib/commands/certificate.js.map +1 -1
- package/build/lib/commands/condition.d.ts +2 -0
- package/build/lib/commands/condition.d.ts.map +1 -1
- package/build/lib/commands/condition.js +75 -2
- package/build/lib/commands/condition.js.map +1 -1
- package/build/lib/commands/context.d.ts +5 -5
- package/build/lib/commands/context.d.ts.map +1 -1
- package/build/lib/commands/context.js +6 -6
- package/build/lib/commands/context.js.map +1 -1
- package/build/lib/commands/file-movement.d.ts.map +1 -1
- package/build/lib/commands/file-movement.js +7 -7
- package/build/lib/commands/file-movement.js.map +1 -1
- package/build/lib/commands/general.js +1 -1
- package/build/lib/commands/general.js.map +1 -1
- package/build/lib/commands/gesture.js +1 -1
- package/build/lib/commands/gesture.js.map +1 -1
- package/build/lib/commands/keychains.js +1 -1
- package/build/lib/commands/keychains.js.map +1 -1
- package/build/lib/commands/localization.js +1 -1
- package/build/lib/commands/localization.js.map +1 -1
- package/build/lib/commands/location.d.ts +3 -2
- package/build/lib/commands/location.d.ts.map +1 -1
- package/build/lib/commands/location.js +10 -4
- package/build/lib/commands/location.js.map +1 -1
- package/build/lib/commands/log.js +9 -9
- package/build/lib/commands/log.js.map +1 -1
- package/build/lib/commands/memory.js +1 -1
- package/build/lib/commands/memory.js.map +1 -1
- package/build/lib/commands/notifications.js +1 -1
- package/build/lib/commands/notifications.js.map +1 -1
- package/build/lib/commands/pasteboard.js +2 -2
- package/build/lib/commands/pasteboard.js.map +1 -1
- package/build/lib/commands/pcap.js +1 -1
- package/build/lib/commands/pcap.js.map +1 -1
- package/build/lib/commands/performance.d.ts.map +1 -1
- package/build/lib/commands/performance.js +13 -4
- package/build/lib/commands/performance.js.map +1 -1
- package/build/lib/commands/permissions.js +2 -2
- package/build/lib/commands/permissions.js.map +1 -1
- package/build/lib/commands/proxy-helper.d.ts.map +1 -1
- package/build/lib/commands/proxy-helper.js +0 -3
- package/build/lib/commands/proxy-helper.js.map +1 -1
- package/build/lib/commands/screenshots.js +1 -1
- package/build/lib/commands/screenshots.js.map +1 -1
- package/build/lib/commands/simctl.d.ts +1 -1
- package/build/lib/commands/simctl.d.ts.map +1 -1
- package/build/lib/commands/simctl.js +1 -1
- package/build/lib/commands/simctl.js.map +1 -1
- package/build/lib/commands/web.js +1 -1
- package/build/lib/commands/web.js.map +1 -1
- package/build/lib/commands/xctest-record-screen.js +2 -2
- package/build/lib/commands/xctest-record-screen.js.map +1 -1
- package/build/lib/desired-caps.d.ts +392 -505
- package/build/lib/desired-caps.d.ts.map +1 -1
- package/build/lib/desired-caps.js +19 -10
- package/build/lib/desired-caps.js.map +1 -1
- package/build/lib/device/clients/base-device-client.d.ts +22 -0
- package/build/lib/device/clients/base-device-client.d.ts.map +1 -0
- package/build/lib/device/clients/base-device-client.js +14 -0
- package/build/lib/device/clients/base-device-client.js.map +1 -0
- package/build/lib/device/clients/py-ios-device-client.d.ts +21 -0
- package/build/lib/device/clients/py-ios-device-client.d.ts.map +1 -0
- package/build/lib/device/clients/py-ios-device-client.js +125 -0
- package/build/lib/device/clients/py-ios-device-client.js.map +1 -0
- package/build/lib/device/device-connections-factory.d.ts +18 -0
- package/build/lib/device/device-connections-factory.d.ts.map +1 -0
- package/build/lib/device/device-connections-factory.js +260 -0
- package/build/lib/device/device-connections-factory.js.map +1 -0
- package/build/lib/device/log/helpers.d.ts +10 -0
- package/build/lib/device/log/helpers.d.ts.map +1 -0
- package/build/lib/device/log/helpers.js +37 -0
- package/build/lib/device/log/helpers.js.map +1 -0
- package/build/lib/device/log/ios-crash-log.d.ts +34 -0
- package/build/lib/device/log/ios-crash-log.d.ts.map +1 -0
- package/build/lib/device/log/ios-crash-log.js +141 -0
- package/build/lib/device/log/ios-crash-log.js.map +1 -0
- package/build/lib/device/log/ios-device-log.d.ts +19 -0
- package/build/lib/device/log/ios-device-log.d.ts.map +1 -0
- package/build/lib/device/log/ios-device-log.js +42 -0
- package/build/lib/device/log/ios-device-log.js.map +1 -0
- package/build/lib/device/log/ios-log.d.ts +24 -0
- package/build/lib/device/log/ios-log.d.ts.map +1 -0
- package/build/lib/device/log/ios-log.js +50 -0
- package/build/lib/device/log/ios-log.js.map +1 -0
- package/build/lib/device/log/ios-performance-log.d.ts +18 -0
- package/build/lib/device/log/ios-performance-log.d.ts.map +1 -0
- package/build/lib/device/log/ios-performance-log.js +43 -0
- package/build/lib/device/log/ios-performance-log.js.map +1 -0
- package/build/lib/device/log/ios-simulator-log.d.ts +38 -0
- package/build/lib/device/log/ios-simulator-log.d.ts.map +1 -0
- package/build/lib/device/log/ios-simulator-log.js +184 -0
- package/build/lib/device/log/ios-simulator-log.js.map +1 -0
- package/build/lib/device/log/line-consuming-log.d.ts +9 -0
- package/build/lib/device/log/line-consuming-log.d.ts.map +1 -0
- package/build/lib/device/log/line-consuming-log.js +16 -0
- package/build/lib/device/log/line-consuming-log.js.map +1 -0
- package/build/lib/device/log/safari-console-log.d.ts +67 -0
- package/build/lib/device/log/safari-console-log.d.ts.map +1 -0
- package/build/lib/device/log/safari-console-log.js +81 -0
- package/build/lib/device/log/safari-console-log.js.map +1 -0
- package/build/lib/device/log/safari-network-log.d.ts +75 -0
- package/build/lib/device/log/safari-network-log.d.ts.map +1 -0
- package/build/lib/device/log/safari-network-log.js +47 -0
- package/build/lib/device/log/safari-network-log.js.map +1 -0
- package/build/lib/device/real-device-management.d.ts +146 -0
- package/build/lib/device/real-device-management.d.ts.map +1 -0
- package/build/lib/device/real-device-management.js +740 -0
- package/build/lib/device/real-device-management.js.map +1 -0
- package/build/lib/device/simulator-management.d.ts +65 -0
- package/build/lib/device/simulator-management.d.ts.map +1 -0
- package/build/lib/device/simulator-management.js +261 -0
- package/build/lib/device/simulator-management.js.map +1 -0
- package/build/lib/device-log/ios-crash-log.d.ts +1 -1
- package/build/lib/device-log/ios-crash-log.d.ts.map +1 -1
- package/build/lib/device-log/ios-simulator-log.d.ts +1 -1
- package/build/lib/device-log/ios-simulator-log.d.ts.map +1 -1
- package/build/lib/doctor/required-checks.js +1 -1
- package/build/lib/doctor/required-checks.js.map +1 -1
- package/build/lib/driver.d.ts +129 -1377
- package/build/lib/driver.d.ts.map +1 -1
- package/build/lib/driver.js +461 -573
- package/build/lib/driver.js.map +1 -1
- package/build/lib/method-map.d.ts +1 -1
- package/build/lib/method-map.d.ts.map +1 -1
- package/build/lib/method-map.js +2 -2
- package/build/lib/method-map.js.map +1 -1
- package/build/lib/simulator-management.d.ts +10 -0
- package/build/lib/simulator-management.d.ts.map +1 -1
- package/build/lib/simulator-management.js +9 -5
- package/build/lib/simulator-management.js.map +1 -1
- package/build/lib/utils.d.ts +2 -9
- package/build/lib/utils.d.ts.map +1 -1
- package/build/lib/utils.js +3 -47
- package/build/lib/utils.js.map +1 -1
- package/lib/app-utils.js +5 -1
- package/lib/commands/app-management.js +2 -2
- package/lib/commands/appearance.js +2 -2
- package/lib/commands/bidi/models.ts +1 -0
- package/lib/commands/bidi/types.ts +1 -0
- package/lib/commands/biometric.js +3 -3
- package/lib/commands/certificate.js +9 -3
- package/lib/commands/condition.js +85 -2
- package/lib/commands/context.js +6 -6
- package/lib/commands/file-movement.js +11 -7
- package/lib/commands/general.js +1 -1
- package/lib/commands/gesture.js +1 -1
- package/lib/commands/keychains.js +1 -1
- package/lib/commands/localization.js +1 -1
- package/lib/commands/location.js +11 -4
- package/lib/commands/log.js +9 -9
- package/lib/commands/memory.js +1 -1
- package/lib/commands/notifications.js +1 -1
- package/lib/commands/pasteboard.js +2 -2
- package/lib/commands/pcap.js +1 -1
- package/lib/commands/performance.js +12 -1
- package/lib/commands/permissions.js +2 -2
- package/lib/commands/proxy-helper.js +0 -3
- package/lib/commands/screenshots.js +1 -1
- package/lib/commands/simctl.js +1 -1
- package/lib/commands/web.js +1 -1
- package/lib/commands/xctest-record-screen.js +2 -2
- package/lib/{desired-caps.js → desired-caps.ts} +20 -6
- package/lib/{real-device-clients → device/clients}/py-ios-device-client.ts +1 -1
- package/lib/{device-connections-factory.js → device/device-connections-factory.ts} +96 -60
- package/lib/{device-log → device/log}/helpers.ts +1 -1
- package/lib/{device-log → device/log}/ios-crash-log.ts +4 -4
- package/lib/{device-log → device/log}/ios-log.ts +1 -1
- package/lib/{device-log → device/log}/ios-simulator-log.ts +1 -1
- package/lib/{device-log → device/log}/line-consuming-log.ts +1 -1
- package/lib/{device-log → device/log}/safari-console-log.ts +1 -1
- package/lib/device/real-device-management.ts +831 -0
- package/lib/{simulator-management.js → device/simulator-management.ts} +75 -64
- package/lib/doctor/required-checks.ts +1 -1
- package/lib/{driver.js → driver.ts} +623 -707
- package/lib/{method-map.js → method-map.ts} +5 -2
- package/lib/utils.js +3 -54
- package/package.json +17 -19
- package/scripts/build-wda.js +3 -3
- package/lib/ios-fs-helpers.js +0 -355
- package/lib/ios-generic-simulators.js +0 -11
- package/lib/real-device-management.js +0 -133
- package/lib/real-device.js +0 -347
- package/lib/xcrun.js +0 -16
- /package/lib/{real-device-clients → device/clients}/base-device-client.ts +0 -0
- /package/lib/{device-log → device/log}/ios-device-log.ts +0 -0
- /package/lib/{device-log → device/log}/ios-performance-log.ts +0 -0
- /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 '
|
|
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 './
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
226
|
-
|
|
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
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
(
|
|
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.
|
|
656
|
-
|
|
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
|
-
|
|
671
|
-
|
|
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
|
|
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
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
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
|
-
|
|
1109
|
-
|
|
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 =
|
|
1279
|
+
this.opts.app = undefined;
|
|
1171
1280
|
return;
|
|
1172
1281
|
case 'calendar':
|
|
1173
1282
|
this.opts.bundleId = 'com.apple.mobilecal';
|
|
1174
|
-
this.opts.app =
|
|
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
|
-
|
|
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
|
-
|
|
1285
|
-
const
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
1636
|
-
: BUNDLE_VERSION_PATTERN.exec(await
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
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
|
+
}
|