appium-xcuitest-driver 10.2.2 → 10.4.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.
- package/CHANGELOG.md +12 -0
- package/build/lib/commands/active-app-info.d.ts +9 -0
- package/build/lib/commands/active-app-info.d.ts.map +1 -0
- package/build/lib/commands/active-app-info.js +14 -0
- package/build/lib/commands/active-app-info.js.map +1 -0
- package/build/lib/commands/alert.d.ts +42 -45
- package/build/lib/commands/alert.d.ts.map +1 -1
- package/build/lib/commands/alert.js +66 -62
- package/build/lib/commands/alert.js.map +1 -1
- package/build/lib/commands/app-management.d.ts +150 -153
- package/build/lib/commands/app-management.d.ts.map +1 -1
- package/build/lib/commands/app-management.js +300 -286
- package/build/lib/commands/app-management.js.map +1 -1
- package/build/lib/commands/app-strings.d.ts +14 -17
- package/build/lib/commands/app-strings.d.ts.map +1 -1
- package/build/lib/commands/app-strings.js +23 -24
- package/build/lib/commands/app-strings.js.map +1 -1
- package/build/lib/commands/appearance.d.ts +19 -22
- package/build/lib/commands/appearance.d.ts.map +1 -1
- package/build/lib/commands/appearance.js +56 -56
- package/build/lib/commands/appearance.js.map +1 -1
- package/build/lib/commands/audit.d.ts +22 -17
- package/build/lib/commands/audit.d.ts.map +1 -1
- package/build/lib/commands/audit.js +17 -18
- package/build/lib/commands/audit.js.map +1 -1
- package/build/lib/commands/battery.d.ts +11 -14
- package/build/lib/commands/battery.d.ts.map +1 -1
- package/build/lib/commands/battery.js +36 -37
- package/build/lib/commands/battery.js.map +1 -1
- package/build/lib/commands/biometric.d.ts +30 -33
- package/build/lib/commands/biometric.d.ts.map +1 -1
- package/build/lib/commands/biometric.js +42 -41
- package/build/lib/commands/biometric.js.map +1 -1
- package/build/lib/commands/certificate.d.ts +48 -45
- package/build/lib/commands/certificate.d.ts.map +1 -1
- package/build/lib/commands/certificate.js +218 -205
- package/build/lib/commands/certificate.js.map +1 -1
- package/build/lib/commands/clipboard.d.ts +19 -22
- package/build/lib/commands/clipboard.d.ts.map +1 -1
- package/build/lib/commands/clipboard.js +30 -30
- package/build/lib/commands/clipboard.js.map +1 -1
- package/build/lib/commands/condition.d.ts +49 -26
- package/build/lib/commands/condition.d.ts.map +1 -1
- package/build/lib/commands/condition.js +87 -86
- package/build/lib/commands/condition.js.map +1 -1
- package/build/lib/commands/content-size.d.ts +26 -29
- package/build/lib/commands/content-size.d.ts.map +1 -1
- package/build/lib/commands/content-size.js +36 -36
- package/build/lib/commands/content-size.js.map +1 -1
- package/build/lib/commands/context.d.ts +161 -108
- package/build/lib/commands/context.d.ts.map +1 -1
- package/build/lib/commands/context.js +530 -517
- package/build/lib/commands/context.js.map +1 -1
- package/build/lib/commands/deviceInfo.d.ts +9 -12
- package/build/lib/commands/deviceInfo.d.ts.map +1 -1
- package/build/lib/commands/deviceInfo.js +17 -18
- package/build/lib/commands/deviceInfo.js.map +1 -1
- package/build/lib/commands/element.d.ts +102 -105
- package/build/lib/commands/element.d.ts.map +1 -1
- package/build/lib/commands/element.js +337 -323
- package/build/lib/commands/element.js.map +1 -1
- package/build/lib/commands/execute.d.ts +24 -19
- package/build/lib/commands/execute.d.ts.map +1 -1
- package/build/lib/commands/execute.js +63 -62
- package/build/lib/commands/execute.js.map +1 -1
- package/build/lib/commands/file-movement.d.ts +77 -80
- package/build/lib/commands/file-movement.d.ts.map +1 -1
- package/build/lib/commands/file-movement.js +130 -124
- package/build/lib/commands/file-movement.js.map +1 -1
- package/build/lib/commands/find.d.ts +18 -21
- package/build/lib/commands/find.d.ts.map +1 -1
- package/build/lib/commands/find.js +158 -156
- package/build/lib/commands/find.js.map +1 -1
- package/build/lib/commands/general.d.ts +124 -116
- package/build/lib/commands/general.d.ts.map +1 -1
- package/build/lib/commands/general.js +248 -232
- package/build/lib/commands/general.js.map +1 -1
- package/build/lib/commands/geolocation.d.ts +43 -46
- package/build/lib/commands/geolocation.d.ts.map +1 -1
- package/build/lib/commands/geolocation.js +10 -11
- package/build/lib/commands/geolocation.js.map +1 -1
- package/build/lib/commands/gesture.d.ts +273 -276
- package/build/lib/commands/gesture.d.ts.map +1 -1
- package/build/lib/commands/gesture.js +506 -492
- package/build/lib/commands/gesture.js.map +1 -1
- package/build/lib/commands/increase-contrast.d.ts +20 -23
- package/build/lib/commands/increase-contrast.d.ts.map +1 -1
- package/build/lib/commands/increase-contrast.js +30 -30
- package/build/lib/commands/increase-contrast.js.map +1 -1
- package/build/lib/commands/iohid.d.ts +1370 -1373
- package/build/lib/commands/iohid.d.ts.map +1 -1
- package/build/lib/commands/iohid.js +30 -31
- package/build/lib/commands/iohid.js.map +1 -1
- package/build/lib/commands/keyboard.d.ts +29 -32
- package/build/lib/commands/keyboard.d.ts.map +1 -1
- package/build/lib/commands/keyboard.js +53 -51
- package/build/lib/commands/keyboard.js.map +1 -1
- package/build/lib/commands/keychains.d.ts +9 -12
- package/build/lib/commands/keychains.d.ts.map +1 -1
- package/build/lib/commands/keychains.js +13 -14
- package/build/lib/commands/keychains.js.map +1 -1
- package/build/lib/commands/localization.d.ts +16 -19
- package/build/lib/commands/localization.d.ts.map +1 -1
- package/build/lib/commands/localization.js +25 -26
- package/build/lib/commands/localization.js.map +1 -1
- package/build/lib/commands/location.d.ts +36 -39
- package/build/lib/commands/location.d.ts.map +1 -1
- package/build/lib/commands/location.js +99 -98
- package/build/lib/commands/location.js.map +1 -1
- package/build/lib/commands/lock.d.ts +21 -24
- package/build/lib/commands/lock.d.ts.map +1 -1
- package/build/lib/commands/lock.js +39 -38
- package/build/lib/commands/lock.js.map +1 -1
- package/build/lib/commands/log.d.ts +43 -37
- package/build/lib/commands/log.d.ts.map +1 -1
- package/build/lib/commands/log.js +174 -171
- package/build/lib/commands/log.js.map +1 -1
- package/build/lib/commands/memory.d.ts +9 -12
- package/build/lib/commands/memory.d.ts.map +1 -1
- package/build/lib/commands/memory.js +37 -39
- package/build/lib/commands/memory.js.map +1 -1
- package/build/lib/commands/navigation.d.ts +30 -33
- package/build/lib/commands/navigation.d.ts.map +1 -1
- package/build/lib/commands/navigation.js +92 -92
- package/build/lib/commands/navigation.js.map +1 -1
- package/build/lib/commands/notifications.d.ts +26 -29
- package/build/lib/commands/notifications.d.ts.map +1 -1
- package/build/lib/commands/notifications.js +53 -53
- package/build/lib/commands/notifications.js.map +1 -1
- package/build/lib/commands/pasteboard.d.ts +21 -24
- package/build/lib/commands/pasteboard.d.ts.map +1 -1
- package/build/lib/commands/pasteboard.js +37 -37
- package/build/lib/commands/pasteboard.js.map +1 -1
- package/build/lib/commands/pcap.d.ts +39 -26
- package/build/lib/commands/pcap.d.ts.map +1 -1
- package/build/lib/commands/pcap.js +81 -81
- package/build/lib/commands/pcap.js.map +1 -1
- package/build/lib/commands/performance.d.ts +63 -44
- package/build/lib/commands/performance.d.ts.map +1 -1
- package/build/lib/commands/performance.js +105 -105
- package/build/lib/commands/performance.js.map +1 -1
- package/build/lib/commands/permissions.d.ts +33 -36
- package/build/lib/commands/permissions.d.ts.map +1 -1
- package/build/lib/commands/permissions.js +66 -65
- package/build/lib/commands/permissions.js.map +1 -1
- package/build/lib/commands/proxy-helper.d.ts +12 -15
- package/build/lib/commands/proxy-helper.d.ts.map +1 -1
- package/build/lib/commands/proxy-helper.js +53 -54
- package/build/lib/commands/proxy-helper.js.map +1 -1
- package/build/lib/commands/record-audio.d.ts +49 -29
- package/build/lib/commands/record-audio.d.ts.map +1 -1
- package/build/lib/commands/record-audio.js +100 -104
- package/build/lib/commands/record-audio.js.map +1 -1
- package/build/lib/commands/recordscreen.d.ts +54 -18
- package/build/lib/commands/recordscreen.d.ts.map +1 -1
- package/build/lib/commands/recordscreen.js +127 -129
- package/build/lib/commands/recordscreen.js.map +1 -1
- package/build/lib/commands/screenshots.d.ts +14 -17
- package/build/lib/commands/screenshots.d.ts.map +1 -1
- package/build/lib/commands/screenshots.js +108 -107
- package/build/lib/commands/screenshots.js.map +1 -1
- package/build/lib/commands/simctl.d.ts +11 -14
- package/build/lib/commands/simctl.d.ts.map +1 -1
- package/build/lib/commands/simctl.js +23 -26
- package/build/lib/commands/simctl.js.map +1 -1
- package/build/lib/commands/source.d.ts +14 -17
- package/build/lib/commands/source.d.ts.map +1 -1
- package/build/lib/commands/source.js +40 -43
- package/build/lib/commands/source.js.map +1 -1
- package/build/lib/commands/timeouts.d.ts +44 -33
- package/build/lib/commands/timeouts.d.ts.map +1 -1
- package/build/lib/commands/timeouts.js +65 -63
- package/build/lib/commands/timeouts.js.map +1 -1
- package/build/lib/commands/web.d.ts +247 -197
- package/build/lib/commands/web.d.ts.map +1 -1
- package/build/lib/commands/web.js +815 -786
- package/build/lib/commands/web.js.map +1 -1
- package/build/lib/commands/xctest-record-screen.d.ts +63 -66
- package/build/lib/commands/xctest-record-screen.d.ts.map +1 -1
- package/build/lib/commands/xctest-record-screen.js +103 -102
- package/build/lib/commands/xctest-record-screen.js.map +1 -1
- package/build/lib/commands/xctest.d.ts +55 -51
- package/build/lib/commands/xctest.d.ts.map +1 -1
- package/build/lib/commands/xctest.js +116 -117
- package/build/lib/commands/xctest.js.map +1 -1
- package/build/lib/driver.d.ts +278 -1597
- package/build/lib/driver.d.ts.map +1 -1
- package/build/lib/driver.js +320 -236
- package/build/lib/driver.js.map +1 -1
- package/build/lib/execute-method-map.d.ts.map +1 -1
- package/build/lib/execute-method-map.js +9 -0
- package/build/lib/execute-method-map.js.map +1 -1
- package/build/lib/real-device.d.ts +1 -1
- package/build/lib/real-device.d.ts.map +1 -1
- package/build/lib/real-device.js +2 -2
- package/build/lib/real-device.js.map +1 -1
- package/lib/commands/active-app-info.js +12 -0
- package/lib/commands/alert.js +68 -65
- package/lib/commands/app-management.js +308 -301
- package/lib/commands/app-strings.js +24 -26
- package/lib/commands/appearance.js +54 -56
- package/lib/commands/audit.js +18 -20
- package/lib/commands/battery.js +35 -37
- package/lib/commands/biometric.js +44 -46
- package/lib/commands/certificate.js +226 -215
- package/lib/commands/clipboard.js +30 -32
- package/lib/commands/condition.js +98 -100
- package/lib/commands/content-size.js +36 -38
- package/lib/commands/context.js +495 -490
- package/lib/commands/deviceInfo.js +19 -20
- package/lib/commands/element.js +367 -357
- package/lib/commands/execute.js +72 -72
- package/lib/commands/file-movement.js +132 -134
- package/lib/commands/find.js +160 -159
- package/lib/commands/general.js +238 -231
- package/lib/commands/geolocation.js +6 -14
- package/lib/commands/gesture.js +525 -515
- package/lib/commands/increase-contrast.js +30 -32
- package/lib/commands/iohid.js +32 -34
- package/lib/commands/keyboard.js +49 -51
- package/lib/commands/keychains.js +12 -14
- package/lib/commands/localization.js +24 -26
- package/lib/commands/location.js +102 -104
- package/lib/commands/lock.js +38 -38
- package/lib/commands/log.js +197 -198
- package/lib/commands/memory.js +40 -43
- package/lib/commands/navigation.js +96 -100
- package/lib/commands/notifications.js +57 -59
- package/lib/commands/pasteboard.js +37 -39
- package/lib/commands/pcap.js +84 -86
- package/lib/commands/performance.js +132 -133
- package/lib/commands/permissions.js +67 -69
- package/lib/commands/proxy-helper.js +60 -61
- package/lib/commands/record-audio.js +115 -120
- package/lib/commands/recordscreen.js +145 -149
- package/lib/commands/screenshots.js +116 -116
- package/lib/commands/simctl.js +25 -29
- package/lib/commands/source.js +42 -46
- package/lib/commands/timeouts.js +59 -63
- package/lib/commands/web.js +878 -858
- package/lib/commands/xctest-record-screen.js +103 -105
- package/lib/commands/xctest.js +134 -139
- package/lib/driver.js +288 -236
- package/lib/execute-method-map.ts +9 -0
- package/lib/real-device.js +2 -2
- package/npm-shrinkwrap.json +440 -76
- package/package.json +2 -1
- package/build/lib/commands/activeAppInfo.d.ts +0 -12
- package/build/lib/commands/activeAppInfo.d.ts.map +0 -1
- package/build/lib/commands/activeAppInfo.js +0 -15
- package/build/lib/commands/activeAppInfo.js.map +0 -1
- package/build/lib/commands/index.d.ts +0 -96
- package/build/lib/commands/index.d.ts.map +0 -1
- package/build/lib/commands/index.js +0 -100
- package/build/lib/commands/index.js.map +0 -1
- package/build/lib/real-device-clients/devicectl.d.ts +0 -204
- package/build/lib/real-device-clients/devicectl.d.ts.map +0 -1
- package/build/lib/real-device-clients/devicectl.js +0 -264
- package/build/lib/real-device-clients/devicectl.js.map +0 -1
- package/lib/commands/activeAppInfo.js +0 -14
- package/lib/commands/index.js +0 -95
- package/lib/real-device-clients/devicectl.js +0 -291
|
@@ -21,6 +21,138 @@ const DEFAULT_PID = 'current';
|
|
|
21
21
|
const INSTRUMENTS = 'instruments';
|
|
22
22
|
const XCTRACE = 'xctrace';
|
|
23
23
|
|
|
24
|
+
/**
|
|
25
|
+
* Starts performance profiling for the device under test.
|
|
26
|
+
*
|
|
27
|
+
* Relaxing security is mandatory for simulators. It can always work for real devices.
|
|
28
|
+
*
|
|
29
|
+
* Since XCode 12 the method tries to use `xctrace` tool to record performance stats.
|
|
30
|
+
*
|
|
31
|
+
* The `instruments` developer utility is used as a fallback for this purpose if `xctrace` is not available.
|
|
32
|
+
*
|
|
33
|
+
* It is possible to record multiple profiles at the same time.
|
|
34
|
+
*
|
|
35
|
+
* Read [Recording, Pausing, and Stopping Traces](https://developer.apple.com/library/content/documentation/DeveloperTools/Conceptual/InstrumentsUserGuide/Recording,Pausing,andStoppingTraces.html) for more details.
|
|
36
|
+
*
|
|
37
|
+
* @param {number} timeout - The maximum count of milliseconds to record the profiling information.
|
|
38
|
+
* @param {string} profileName - The name of existing performance profile to apply. Can also contain the full path to the chosen template on the server file system. Note: not all profiles are supported on mobile devices.
|
|
39
|
+
* @param {number|'current'} [pid] - The ID of the process to measure the performance for. Set it to `current` in order to measure the performance of the process, which belongs to the currently active application. All processes running on the device are measured if `pid` is unset (the default setting).
|
|
40
|
+
* @this {XCUITestDriver}
|
|
41
|
+
*/
|
|
42
|
+
export async function mobileStartPerfRecord(
|
|
43
|
+
timeout = DEFAULT_TIMEOUT_MS,
|
|
44
|
+
profileName = DEFAULT_PROFILE_NAME,
|
|
45
|
+
pid,
|
|
46
|
+
) {
|
|
47
|
+
if (!this.isFeatureEnabled(PERF_RECORD_FEAT_NAME) && !this.isRealDevice()) {
|
|
48
|
+
throw this.log.errorWithException(PERF_RECORD_SECURITY_MESSAGE);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (!_.isEmpty(this._perfRecorders)) {
|
|
52
|
+
for (const recorder of this._perfRecorders.filter((x) => x.profileName === profileName)) {
|
|
53
|
+
if (recorder.isRunning()) {
|
|
54
|
+
this.log.debug(
|
|
55
|
+
`Performance recorder for '${profileName}' on device '${this.device.udid}' ` +
|
|
56
|
+
` is already running. Doing nothing`,
|
|
57
|
+
);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
_.pull(this._perfRecorders, recorder);
|
|
61
|
+
await recorder.stop(true);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
let realPid;
|
|
66
|
+
if (pid) {
|
|
67
|
+
if (_.toLower(String(pid)) === DEFAULT_PID) {
|
|
68
|
+
const appInfo = /** @type {import('./types').ActiveAppInfo} */ (
|
|
69
|
+
await this.proxyCommand('/wda/activeAppInfo', 'GET')
|
|
70
|
+
);
|
|
71
|
+
realPid = appInfo.pid;
|
|
72
|
+
} else {
|
|
73
|
+
realPid = pid;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
const recorder = new PerfRecorder(await tempDir.openDir(), this.device.udid, {
|
|
77
|
+
timeout: parseInt(String(timeout), 10),
|
|
78
|
+
profileName,
|
|
79
|
+
pid: parseInt(String(realPid), 10),
|
|
80
|
+
});
|
|
81
|
+
await recorder.start();
|
|
82
|
+
this._perfRecorders = [...(this._perfRecorders || []), recorder];
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Stops performance recording operation previously started by {@linkcode XCUITestDriver.mobileStartPerfRecord mobile: startPerfRecord}.
|
|
87
|
+
*
|
|
88
|
+
* If the previous call has already been completed due to the timeout, then its result is returned immediately. An error is thrown if the performance recording failed to start.
|
|
89
|
+
*
|
|
90
|
+
* The resulting file in `.trace` format can be either returned directly as base64-encoded zip archive or uploaded to a remote location (such files can be pretty large). Afterwards it is possible to unarchive and open such files with Xcode Dev Tools.
|
|
91
|
+
*
|
|
92
|
+
* @param {string} [remotePath] - The path to the remote location, where the resulting zipped `.trace` file should be uploaded. The following protocols are supported: `http`, `https`, `ftp`. Null or empty string value (the default setting) means the content of resulting file should be zipped, encoded as Base64 and passed as the endpoint response value. An exception will be thrown if the generated file is too big to fit into the available process memory.
|
|
93
|
+
* @param {string} [user] - The name of the user for the remote authentication. Only works if `remotePath` is provided.
|
|
94
|
+
* @param {string} [pass] - The password for the remote authentication. Only works if `remotePath` is provided.
|
|
95
|
+
* @param {import('axios').Method} [method] - The http multipart upload method name. Only works if `remotePath` is provided. Defaults to `PUT`
|
|
96
|
+
* @param {string} profileName - The name of existing performance profile to stop the recording for. Multiple recorders for different profile names could be executed at the same time.
|
|
97
|
+
* @param {Record<string,any>} [headers] - Additional headers mapping for multipart http(s) uploads
|
|
98
|
+
* @param {string} [fileFieldName] - The name of the form field, where the file content BLOB should be stored for http(s) uploads. Defaults to `file`
|
|
99
|
+
* @param {Record<string,any>|([string, any])[]} [formFields] - Additional form fields for multipart http(s) uploads
|
|
100
|
+
*
|
|
101
|
+
* @returns {Promise<string>} The resulting file in `.trace` format. This file can either be returned directly as base64-encoded `.zip` archive or uploaded to a remote location (note that such files may be large), _depending on the `remotePath` argument value._ Thereafter, the file may be unarchived and opened with Xcode Developer Tools.
|
|
102
|
+
* @throws {Error} If no performance recording with given profile name/device udid combination
|
|
103
|
+
* has been started before or the resulting .trace file has not been generated properly.
|
|
104
|
+
* @this {XCUITestDriver}
|
|
105
|
+
*/
|
|
106
|
+
export async function mobileStopPerfRecord(
|
|
107
|
+
remotePath,
|
|
108
|
+
user,
|
|
109
|
+
pass,
|
|
110
|
+
method,
|
|
111
|
+
profileName = DEFAULT_PROFILE_NAME,
|
|
112
|
+
headers,
|
|
113
|
+
fileFieldName,
|
|
114
|
+
formFields,
|
|
115
|
+
) {
|
|
116
|
+
if (!this.isFeatureEnabled(PERF_RECORD_FEAT_NAME) && !this.isRealDevice()) {
|
|
117
|
+
throw this.log.errorWithException(PERF_RECORD_SECURITY_MESSAGE);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (_.isEmpty(this._perfRecorders)) {
|
|
121
|
+
this.log.info('No performance recorders have been started. Doing nothing');
|
|
122
|
+
return '';
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const recorders = this._perfRecorders.filter((x) => x.profileName === profileName);
|
|
126
|
+
if (_.isEmpty(recorders)) {
|
|
127
|
+
throw this.log.errorWithException(
|
|
128
|
+
`There are no records for performance profile '${profileName}' ` +
|
|
129
|
+
`and device ${this.device.udid}. Have you started the profiling before?`,
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const recorder = _.first(recorders);
|
|
134
|
+
const resultPath = await /** @type {PerfRecorder} */ (recorder).stop();
|
|
135
|
+
if (!(await fs.exists(resultPath))) {
|
|
136
|
+
throw this.log.errorWithException(
|
|
137
|
+
`There is no ${DEFAULT_EXT} file found for performance profile '${profileName}' ` +
|
|
138
|
+
`and device ${this.device.udid}. Make sure the selected profile is supported on this device`,
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const result = await encodeBase64OrUpload(resultPath, remotePath, {
|
|
143
|
+
user,
|
|
144
|
+
pass,
|
|
145
|
+
method,
|
|
146
|
+
headers,
|
|
147
|
+
fileFieldName,
|
|
148
|
+
formFields,
|
|
149
|
+
});
|
|
150
|
+
_.pull(this._perfRecorders, recorder);
|
|
151
|
+
await fs.rimraf(resultPath);
|
|
152
|
+
return result;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
|
|
24
156
|
async function requireXctrace() {
|
|
25
157
|
const xcrunPath = await requireXcrun();
|
|
26
158
|
try {
|
|
@@ -255,139 +387,6 @@ export class PerfRecorder {
|
|
|
255
387
|
}
|
|
256
388
|
}
|
|
257
389
|
|
|
258
|
-
export default {
|
|
259
|
-
/**
|
|
260
|
-
* Starts performance profiling for the device under test.
|
|
261
|
-
*
|
|
262
|
-
* Relaxing security is mandatory for simulators. It can always work for real devices.
|
|
263
|
-
*
|
|
264
|
-
* Since XCode 12 the method tries to use `xctrace` tool to record performance stats.
|
|
265
|
-
*
|
|
266
|
-
* The `instruments` developer utility is used as a fallback for this purpose if `xctrace` is not available.
|
|
267
|
-
*
|
|
268
|
-
* It is possible to record multiple profiles at the same time.
|
|
269
|
-
*
|
|
270
|
-
* Read [Recording, Pausing, and Stopping Traces](https://developer.apple.com/library/content/documentation/DeveloperTools/Conceptual/InstrumentsUserGuide/Recording,Pausing,andStoppingTraces.html) for more details.
|
|
271
|
-
*
|
|
272
|
-
* @param {number} timeout - The maximum count of milliseconds to record the profiling information.
|
|
273
|
-
* @param {string} profileName - The name of existing performance profile to apply. Can also contain the full path to the chosen template on the server file system. Note: not all profiles are supported on mobile devices.
|
|
274
|
-
* @param {number|'current'} [pid] - The ID of the process to measure the performance for. Set it to `current` in order to measure the performance of the process, which belongs to the currently active application. All processes running on the device are measured if `pid` is unset (the default setting).
|
|
275
|
-
* @this {XCUITestDriver}
|
|
276
|
-
*/
|
|
277
|
-
async mobileStartPerfRecord(
|
|
278
|
-
timeout = DEFAULT_TIMEOUT_MS,
|
|
279
|
-
profileName = DEFAULT_PROFILE_NAME,
|
|
280
|
-
pid,
|
|
281
|
-
) {
|
|
282
|
-
if (!this.isFeatureEnabled(PERF_RECORD_FEAT_NAME) && !this.isRealDevice()) {
|
|
283
|
-
throw this.log.errorWithException(PERF_RECORD_SECURITY_MESSAGE);
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
if (!_.isEmpty(this._perfRecorders)) {
|
|
287
|
-
for (const recorder of this._perfRecorders.filter((x) => x.profileName === profileName)) {
|
|
288
|
-
if (recorder.isRunning()) {
|
|
289
|
-
this.log.debug(
|
|
290
|
-
`Performance recorder for '${profileName}' on device '${this.device.udid}' ` +
|
|
291
|
-
` is already running. Doing nothing`,
|
|
292
|
-
);
|
|
293
|
-
return;
|
|
294
|
-
}
|
|
295
|
-
_.pull(this._perfRecorders, recorder);
|
|
296
|
-
await recorder.stop(true);
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
let realPid;
|
|
301
|
-
if (pid) {
|
|
302
|
-
if (_.toLower(String(pid)) === DEFAULT_PID) {
|
|
303
|
-
const appInfo = /** @type {import('./types').ActiveAppInfo} */ (
|
|
304
|
-
await this.proxyCommand('/wda/activeAppInfo', 'GET')
|
|
305
|
-
);
|
|
306
|
-
realPid = appInfo.pid;
|
|
307
|
-
} else {
|
|
308
|
-
realPid = pid;
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
const recorder = new PerfRecorder(await tempDir.openDir(), this.device.udid, {
|
|
312
|
-
timeout: parseInt(String(timeout), 10),
|
|
313
|
-
profileName,
|
|
314
|
-
pid: parseInt(String(realPid), 10),
|
|
315
|
-
});
|
|
316
|
-
await recorder.start();
|
|
317
|
-
this._perfRecorders = [...(this._perfRecorders || []), recorder];
|
|
318
|
-
},
|
|
319
|
-
|
|
320
|
-
/**
|
|
321
|
-
* Stops performance recording operation previously started by {@linkcode XCUITestDriver.mobileStartPerfRecord mobile: startPerfRecord}.
|
|
322
|
-
*
|
|
323
|
-
* If the previous call has already been completed due to the timeout, then its result is returned immediately. An error is thrown if the performance recording failed to start.
|
|
324
|
-
*
|
|
325
|
-
* The resulting file in `.trace` format can be either returned directly as base64-encoded zip archive or uploaded to a remote location (such files can be pretty large). Afterwards it is possible to unarchive and open such files with Xcode Dev Tools.
|
|
326
|
-
*
|
|
327
|
-
* @param {string} [remotePath] - The path to the remote location, where the resulting zipped `.trace` file should be uploaded. The following protocols are supported: `http`, `https`, `ftp`. Null or empty string value (the default setting) means the content of resulting file should be zipped, encoded as Base64 and passed as the endpoint response value. An exception will be thrown if the generated file is too big to fit into the available process memory.
|
|
328
|
-
* @param {string} [user] - The name of the user for the remote authentication. Only works if `remotePath` is provided.
|
|
329
|
-
* @param {string} [pass] - The password for the remote authentication. Only works if `remotePath` is provided.
|
|
330
|
-
* @param {import('axios').Method} [method] - The http multipart upload method name. Only works if `remotePath` is provided. Defaults to `PUT`
|
|
331
|
-
* @param {string} profileName - The name of existing performance profile to stop the recording for. Multiple recorders for different profile names could be executed at the same time.
|
|
332
|
-
* @param {Record<string,any>} [headers] - Additional headers mapping for multipart http(s) uploads
|
|
333
|
-
* @param {string} [fileFieldName] - The name of the form field, where the file content BLOB should be stored for http(s) uploads. Defaults to `file`
|
|
334
|
-
* @param {Record<string,any>|([string, any])[]} [formFields] - Additional form fields for multipart http(s) uploads
|
|
335
|
-
*
|
|
336
|
-
* @returns {Promise<string>} The resulting file in `.trace` format. This file can either be returned directly as base64-encoded `.zip` archive or uploaded to a remote location (note that such files may be large), _depending on the `remotePath` argument value._ Thereafter, the file may be unarchived and opened with Xcode Developer Tools.
|
|
337
|
-
* @throws {Error} If no performance recording with given profile name/device udid combination
|
|
338
|
-
* has been started before or the resulting .trace file has not been generated properly.
|
|
339
|
-
* @this {XCUITestDriver}
|
|
340
|
-
*/
|
|
341
|
-
async mobileStopPerfRecord(
|
|
342
|
-
remotePath,
|
|
343
|
-
user,
|
|
344
|
-
pass,
|
|
345
|
-
method,
|
|
346
|
-
profileName = DEFAULT_PROFILE_NAME,
|
|
347
|
-
headers,
|
|
348
|
-
fileFieldName,
|
|
349
|
-
formFields,
|
|
350
|
-
) {
|
|
351
|
-
if (!this.isFeatureEnabled(PERF_RECORD_FEAT_NAME) && !this.isRealDevice()) {
|
|
352
|
-
throw this.log.errorWithException(PERF_RECORD_SECURITY_MESSAGE);
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
if (_.isEmpty(this._perfRecorders)) {
|
|
356
|
-
this.log.info('No performance recorders have been started. Doing nothing');
|
|
357
|
-
return '';
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
const recorders = this._perfRecorders.filter((x) => x.profileName === profileName);
|
|
361
|
-
if (_.isEmpty(recorders)) {
|
|
362
|
-
throw this.log.errorWithException(
|
|
363
|
-
`There are no records for performance profile '${profileName}' ` +
|
|
364
|
-
`and device ${this.device.udid}. Have you started the profiling before?`,
|
|
365
|
-
);
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
const recorder = _.first(recorders);
|
|
369
|
-
const resultPath = await /** @type {PerfRecorder} */ (recorder).stop();
|
|
370
|
-
if (!(await fs.exists(resultPath))) {
|
|
371
|
-
throw this.log.errorWithException(
|
|
372
|
-
`There is no ${DEFAULT_EXT} file found for performance profile '${profileName}' ` +
|
|
373
|
-
`and device ${this.device.udid}. Make sure the selected profile is supported on this device`,
|
|
374
|
-
);
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
const result = await encodeBase64OrUpload(resultPath, remotePath, {
|
|
378
|
-
user,
|
|
379
|
-
pass,
|
|
380
|
-
method,
|
|
381
|
-
headers,
|
|
382
|
-
fileFieldName,
|
|
383
|
-
formFields,
|
|
384
|
-
});
|
|
385
|
-
_.pull(this._perfRecorders, recorder);
|
|
386
|
-
await fs.rimraf(resultPath);
|
|
387
|
-
return result;
|
|
388
|
-
},
|
|
389
|
-
};
|
|
390
|
-
|
|
391
390
|
/**
|
|
392
391
|
* @typedef {import('../driver').XCUITestDriver} XCUITestDriver
|
|
393
392
|
*/
|
|
@@ -4,83 +4,81 @@ import {assertSimulator as _assertSimulator} from '../utils';
|
|
|
4
4
|
|
|
5
5
|
const assertSimulator = _.partial(_assertSimulator, 'Permission-related operations');
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
if (!resource) {
|
|
24
|
-
throw new Error(
|
|
25
|
-
`The 'service' value must be one of ` + `${JSON.stringify(_.keys(PermissionService))}`,
|
|
26
|
-
);
|
|
27
|
-
}
|
|
28
|
-
} else if (_.isInteger(service)) {
|
|
29
|
-
resource = service;
|
|
30
|
-
} else {
|
|
7
|
+
/**
|
|
8
|
+
* Resets the given permission for the active application under test.
|
|
9
|
+
* Works for both Simulator and real devices using Xcode SDK 11.4+
|
|
10
|
+
*
|
|
11
|
+
* @param {PermissionService|number} service - One of the available service names. This could also be an integer protected resource identifier; see [this list](https://developer.apple.com/documentation/xctest/xcuiprotectedresource?language=objc)
|
|
12
|
+
* @throws {Error} If permission reset fails on the device.
|
|
13
|
+
* @this {XCUITestDriver}
|
|
14
|
+
*/
|
|
15
|
+
export async function mobileResetPermission(service) {
|
|
16
|
+
if (!service) {
|
|
17
|
+
throw new Error(`The 'service' option is expected to be present`);
|
|
18
|
+
}
|
|
19
|
+
let resource;
|
|
20
|
+
if (_.isString(service)) {
|
|
21
|
+
resource = PermissionService[_.toLower(service)];
|
|
22
|
+
if (!resource) {
|
|
31
23
|
throw new Error(
|
|
32
|
-
`The 'service' value must be
|
|
33
|
-
`'${service}' is passed instead`,
|
|
24
|
+
`The 'service' value must be one of ` + `${JSON.stringify(_.keys(PermissionService))}`,
|
|
34
25
|
);
|
|
35
26
|
}
|
|
27
|
+
} else if (_.isInteger(service)) {
|
|
28
|
+
resource = service;
|
|
29
|
+
} else {
|
|
30
|
+
throw new Error(
|
|
31
|
+
`The 'service' value must be either a string or an integer. ` +
|
|
32
|
+
`'${service}' is passed instead`,
|
|
33
|
+
);
|
|
34
|
+
}
|
|
36
35
|
|
|
37
|
-
|
|
38
|
-
|
|
36
|
+
await this.proxyCommand('/wda/resetAppAuth', 'POST', {resource});
|
|
37
|
+
}
|
|
39
38
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
39
|
+
/**
|
|
40
|
+
* Gets application permission state on a simulated device.
|
|
41
|
+
*
|
|
42
|
+
* **This method requires [WIX applesimutils](https://github.com/wix/AppleSimulatorUtils) to be installed on the Appium server host.**
|
|
43
|
+
*
|
|
44
|
+
* @param {string} bundleId - Bundle identifier of the target application
|
|
45
|
+
* @param {import('./enum').PermissionService} service - Service name
|
|
46
|
+
* @returns {Promise<import('./types').PermissionState>} Either 'yes', 'no', 'unset' or 'limited'
|
|
47
|
+
* @throws {Error} If permission getting fails or the device is not a Simulator.
|
|
48
|
+
* @this {XCUITestDriver}
|
|
49
|
+
* @group Simulator Only
|
|
50
|
+
*/
|
|
51
|
+
export async function mobileGetPermission(bundleId, service) {
|
|
52
|
+
if (!service) {
|
|
53
|
+
throw new Error(`The 'service' option is expected to be present`);
|
|
54
|
+
}
|
|
55
|
+
assertSimulator(this);
|
|
57
56
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
57
|
+
return /** @type {import('./types').PermissionState} */ (
|
|
58
|
+
await /** @type {import('../driver').Simulator} */ (this.device).getPermission(
|
|
59
|
+
bundleId, String(service)
|
|
60
|
+
)
|
|
61
|
+
);
|
|
62
|
+
}
|
|
64
63
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
64
|
+
/**
|
|
65
|
+
* Set application permission state on Simulator.
|
|
66
|
+
*
|
|
67
|
+
* @param {Record<Partial<import('./types').AccessRule>, import('./types').PermissionState>} access - One or more access rules to set.
|
|
68
|
+
* @param {string} bundleId - Bundle identifier of the target application
|
|
69
|
+
* @since Xcode SDK 11.4
|
|
70
|
+
* @throws {Error} If permission setting fails or the device is not a Simulator.
|
|
71
|
+
* @group Simulator Only
|
|
72
|
+
* @this {XCUITestDriver}
|
|
73
|
+
*/
|
|
74
|
+
export async function mobileSetPermissions(access, bundleId) {
|
|
75
|
+
if (!_.isPlainObject(access)) {
|
|
76
|
+
throw new Error(`The 'access' option is expected to be a map`);
|
|
77
|
+
}
|
|
78
|
+
assertSimulator(this);
|
|
80
79
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
};
|
|
80
|
+
await /** @type {import('../driver').Simulator} */ (this.device).setPermissions(bundleId, access);
|
|
81
|
+
}
|
|
84
82
|
|
|
85
83
|
/**
|
|
86
84
|
* @typedef {import('../driver').XCUITestDriver} XCUITestDriver
|
|
@@ -46,76 +46,75 @@ const WDA_ROUTES = /** @type {const} */ ({
|
|
|
46
46
|
});
|
|
47
47
|
|
|
48
48
|
/**
|
|
49
|
-
*
|
|
49
|
+
* Proxies a command to WebDriverAgent
|
|
50
|
+
* @template [TReq=any]
|
|
51
|
+
* @template [TRes=unknown]
|
|
52
|
+
* @param {string} url
|
|
50
53
|
* @param {AllowedHttpMethod} method
|
|
51
|
-
* @
|
|
54
|
+
* @param {TReq} [body]
|
|
55
|
+
* @param {boolean} isSessionCommand
|
|
56
|
+
* @this {import('../driver').XCUITestDriver}
|
|
57
|
+
* @returns {Promise<TRes>}
|
|
52
58
|
*/
|
|
53
|
-
function
|
|
54
|
-
if (
|
|
55
|
-
return
|
|
59
|
+
export async function proxyCommand(url, method, body, isSessionCommand = true) {
|
|
60
|
+
if (this.shutdownUnexpectedly) {
|
|
61
|
+
return /** @type {TRes} */ (undefined);
|
|
56
62
|
}
|
|
57
|
-
}
|
|
58
|
-
export default {
|
|
59
|
-
/**
|
|
60
|
-
* Proxies a command to WebDriverAgent
|
|
61
|
-
* @template [TReq=any]
|
|
62
|
-
* @template [TRes=unknown]
|
|
63
|
-
* @param {string} url
|
|
64
|
-
* @param {AllowedHttpMethod} method
|
|
65
|
-
* @param {TReq} [body]
|
|
66
|
-
* @param {boolean} isSessionCommand
|
|
67
|
-
* @this {import('../driver').XCUITestDriver}
|
|
68
|
-
* @returns {Promise<TRes>}
|
|
69
|
-
*/
|
|
70
|
-
async proxyCommand(url, method, body, isSessionCommand = true) {
|
|
71
|
-
if (this.shutdownUnexpectedly) {
|
|
72
|
-
return /** @type {TRes} */ (undefined);
|
|
73
|
-
}
|
|
74
63
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
64
|
+
if (!url) {
|
|
65
|
+
throw this.log.errorWithException('Proxying requires an endpoint');
|
|
66
|
+
} else if (!SUPPORTED_METHODS.has(method)) {
|
|
67
|
+
throw this.log.errorWithException(
|
|
68
|
+
`Proxying only works for the following HTTP methods: ${[...SUPPORTED_METHODS].join(', ')}`,
|
|
69
|
+
);
|
|
70
|
+
}
|
|
82
71
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
72
|
+
if (!this.wda) {
|
|
73
|
+
throw new Error('Cannot call proxyCommand without WDA driver active');
|
|
74
|
+
}
|
|
75
|
+
const proxy = isSessionCommand ? this.wda.jwproxy : this.wda.noSessionProxy;
|
|
76
|
+
if (!proxy) {
|
|
77
|
+
throw new Error('Cannot call proxyCommand without WDA proxy active');
|
|
78
|
+
}
|
|
90
79
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
80
|
+
let cmdName = wdaRouteToCommandName(url, method) || routeToCommandName(url, method);
|
|
81
|
+
const timeout = this._getCommandTimeout(cmdName);
|
|
82
|
+
if (!cmdName) {
|
|
83
|
+
// this should never happen except when adding new routes
|
|
84
|
+
cmdName = 'Unknown'; // just for logging purposes below
|
|
85
|
+
this.log.info(`Proxying to WDA with an unknown route: ${method} ${url}`);
|
|
86
|
+
}
|
|
98
87
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
88
|
+
if (!timeout) {
|
|
89
|
+
return /** @type {TRes} */ (await proxy.command(url, method, body));
|
|
90
|
+
}
|
|
102
91
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
}
|
|
110
|
-
proxy.cancelActiveRequests();
|
|
111
|
-
const error = new errors.TimeoutError(
|
|
112
|
-
`Appium did not get any response from '${cmdName}' command in ${timeout} ms`
|
|
113
|
-
);
|
|
114
|
-
await this.startUnexpectedShutdown(error);
|
|
115
|
-
throw error;
|
|
92
|
+
this.log.debug(`Setting custom timeout to ${timeout} ms for '${cmdName}' command`);
|
|
93
|
+
try {
|
|
94
|
+
return /** @type {TRes} */ (await B.resolve(proxy.command(url, method, body)).timeout(timeout));
|
|
95
|
+
} catch (e) {
|
|
96
|
+
if (!(e instanceof B.Promise.TimeoutError)) {
|
|
97
|
+
throw e;
|
|
116
98
|
}
|
|
117
|
-
|
|
118
|
-
|
|
99
|
+
proxy.cancelActiveRequests();
|
|
100
|
+
const error = new errors.TimeoutError(
|
|
101
|
+
`Appium did not get any response from '${cmdName}' command in ${timeout} ms`
|
|
102
|
+
);
|
|
103
|
+
await this.startUnexpectedShutdown(error);
|
|
104
|
+
throw error;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* @param {string} endpoint
|
|
110
|
+
* @param {AllowedHttpMethod} method
|
|
111
|
+
* @returns {string|undefined}
|
|
112
|
+
*/
|
|
113
|
+
function wdaRouteToCommandName(endpoint, method) {
|
|
114
|
+
if (endpoint in WDA_ROUTES) {
|
|
115
|
+
return WDA_ROUTES[endpoint][method];
|
|
116
|
+
}
|
|
117
|
+
}
|
|
119
118
|
|
|
120
119
|
/**
|
|
121
120
|
* @typedef {import('../driver').XCUITestDriver} XCUITestDriver
|