@limrun/appium-xcuitest-driver 10.11.0-lim.3 → 10.14.6-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 +94 -0
- package/build/lib/commands/active-app-info.d.ts +4 -3
- package/build/lib/commands/active-app-info.d.ts.map +1 -1
- package/build/lib/commands/active-app-info.js +2 -3
- package/build/lib/commands/active-app-info.js.map +1 -1
- package/build/lib/commands/alert.d.ts +26 -31
- package/build/lib/commands/alert.d.ts.map +1 -1
- package/build/lib/commands/alert.js +20 -29
- package/build/lib/commands/alert.js.map +1 -1
- package/build/lib/commands/app-management.d.ts +99 -76
- package/build/lib/commands/app-management.d.ts.map +1 -1
- package/build/lib/commands/app-management.js +83 -73
- package/build/lib/commands/app-management.js.map +1 -1
- package/build/lib/commands/app-strings.d.ts +6 -7
- package/build/lib/commands/app-strings.d.ts.map +1 -1
- package/build/lib/commands/app-strings.js +3 -8
- package/build/lib/commands/app-strings.js.map +1 -1
- package/build/lib/commands/appearance.d.ts +7 -9
- package/build/lib/commands/appearance.d.ts.map +1 -1
- package/build/lib/commands/appearance.js +13 -19
- package/build/lib/commands/appearance.js.map +1 -1
- package/build/lib/commands/audit.d.ts +5 -33
- package/build/lib/commands/audit.d.ts.map +1 -1
- package/build/lib/commands/audit.js +3 -16
- package/build/lib/commands/audit.js.map +1 -1
- package/build/lib/commands/battery.d.ts +4 -4
- package/build/lib/commands/battery.d.ts.map +1 -1
- package/build/lib/commands/battery.js +3 -7
- package/build/lib/commands/battery.js.map +1 -1
- package/build/lib/commands/biometric.d.ts +12 -14
- package/build/lib/commands/biometric.d.ts.map +1 -1
- package/build/lib/commands/biometric.js +10 -19
- package/build/lib/commands/biometric.js.map +1 -1
- package/build/lib/commands/certificate.d.ts +14 -19
- package/build/lib/commands/certificate.d.ts.map +1 -1
- package/build/lib/commands/certificate.js +24 -31
- package/build/lib/commands/certificate.js.map +1 -1
- package/build/lib/commands/clipboard.d.ts +9 -11
- package/build/lib/commands/clipboard.d.ts.map +1 -1
- package/build/lib/commands/clipboard.js +8 -13
- package/build/lib/commands/clipboard.js.map +1 -1
- package/build/lib/commands/condition.d.ts +9 -72
- package/build/lib/commands/condition.d.ts.map +1 -1
- package/build/lib/commands/condition.js +5 -66
- package/build/lib/commands/condition.js.map +1 -1
- package/build/lib/commands/content-size.d.ts +16 -19
- package/build/lib/commands/content-size.d.ts.map +1 -1
- package/build/lib/commands/content-size.js +14 -22
- package/build/lib/commands/content-size.js.map +1 -1
- package/build/lib/commands/context.d.ts +130 -161
- package/build/lib/commands/context.d.ts.map +1 -1
- package/build/lib/commands/context.js +123 -108
- package/build/lib/commands/context.js.map +1 -1
- package/build/lib/commands/device-info.d.ts +13 -0
- package/build/lib/commands/device-info.d.ts.map +1 -0
- package/build/lib/commands/device-info.js +20 -0
- package/build/lib/commands/device-info.js.map +1 -0
- package/build/lib/commands/element.d.ts +83 -67
- package/build/lib/commands/element.d.ts.map +1 -1
- package/build/lib/commands/element.js +111 -134
- package/build/lib/commands/element.js.map +1 -1
- package/build/lib/commands/execute.d.ts +10 -22
- package/build/lib/commands/execute.d.ts.map +1 -1
- package/build/lib/commands/execute.js +13 -29
- package/build/lib/commands/execute.js.map +1 -1
- package/build/lib/commands/file-movement.d.ts +31 -42
- package/build/lib/commands/file-movement.d.ts.map +1 -1
- package/build/lib/commands/file-movement.js +146 -205
- package/build/lib/commands/file-movement.js.map +1 -1
- package/build/lib/commands/find.d.ts +20 -12
- package/build/lib/commands/find.d.ts.map +1 -1
- package/build/lib/commands/find.js +27 -65
- package/build/lib/commands/find.js.map +1 -1
- package/build/lib/commands/general.d.ts +84 -80
- package/build/lib/commands/general.d.ts.map +1 -1
- package/build/lib/commands/general.js +67 -54
- package/build/lib/commands/general.js.map +1 -1
- package/build/lib/commands/geolocation.d.ts +16 -36
- package/build/lib/commands/geolocation.d.ts.map +1 -1
- package/build/lib/commands/geolocation.js +8 -25
- package/build/lib/commands/geolocation.js.map +1 -1
- package/build/lib/commands/gesture.d.ts +103 -119
- package/build/lib/commands/gesture.d.ts.map +1 -1
- package/build/lib/commands/gesture.js +98 -138
- package/build/lib/commands/gesture.js.map +1 -1
- package/build/lib/commands/increase-contrast.d.ts +10 -13
- package/build/lib/commands/increase-contrast.d.ts.map +1 -1
- package/build/lib/commands/increase-contrast.js +8 -16
- package/build/lib/commands/increase-contrast.js.map +1 -1
- package/build/lib/commands/iohid.d.ts +6 -1359
- package/build/lib/commands/iohid.d.ts.map +1 -1
- package/build/lib/commands/iohid.js +5 -10
- package/build/lib/commands/iohid.js.map +1 -1
- package/build/lib/commands/keyboard.d.ts +16 -13
- package/build/lib/commands/keyboard.d.ts.map +1 -1
- package/build/lib/commands/keyboard.js +14 -18
- package/build/lib/commands/keyboard.js.map +1 -1
- package/build/lib/commands/keychains.d.ts +2 -2
- package/build/lib/commands/keychains.d.ts.map +1 -1
- package/build/lib/commands/keychains.js +1 -4
- package/build/lib/commands/keychains.js.map +1 -1
- package/build/lib/commands/localization.d.ts +7 -6
- package/build/lib/commands/localization.d.ts.map +1 -1
- package/build/lib/commands/localization.js +7 -8
- package/build/lib/commands/localization.js.map +1 -1
- package/build/lib/commands/location.d.ts +8 -11
- package/build/lib/commands/location.d.ts.map +1 -1
- package/build/lib/commands/location.js +7 -15
- package/build/lib/commands/location.js.map +1 -1
- package/build/lib/commands/lock.d.ts +6 -10
- package/build/lib/commands/lock.d.ts.map +1 -1
- package/build/lib/commands/lock.js +3 -10
- package/build/lib/commands/lock.js.map +1 -1
- package/build/lib/commands/log.d.ts +42 -44
- package/build/lib/commands/log.d.ts.map +1 -1
- package/build/lib/commands/log.js +32 -53
- package/build/lib/commands/log.js.map +1 -1
- package/build/lib/commands/memory.d.ts +4 -5
- package/build/lib/commands/memory.d.ts.map +1 -1
- package/build/lib/commands/memory.js +3 -8
- package/build/lib/commands/memory.js.map +1 -1
- package/build/lib/commands/navigation.d.ts +14 -26
- package/build/lib/commands/navigation.d.ts.map +1 -1
- package/build/lib/commands/navigation.js +22 -32
- package/build/lib/commands/navigation.js.map +1 -1
- package/build/lib/commands/notifications.d.ts +10 -10
- package/build/lib/commands/notifications.d.ts.map +1 -1
- package/build/lib/commands/notifications.js +8 -12
- package/build/lib/commands/notifications.js.map +1 -1
- package/build/lib/commands/pasteboard.d.ts +9 -10
- package/build/lib/commands/pasteboard.d.ts.map +1 -1
- package/build/lib/commands/pasteboard.js +8 -13
- package/build/lib/commands/pasteboard.js.map +1 -1
- package/build/lib/commands/pcap.d.ts +18 -38
- package/build/lib/commands/pcap.d.ts.map +1 -1
- package/build/lib/commands/pcap.js +9 -14
- package/build/lib/commands/pcap.js.map +1 -1
- package/build/lib/commands/performance.d.ts +36 -55
- package/build/lib/commands/performance.d.ts.map +1 -1
- package/build/lib/commands/performance.js +93 -86
- package/build/lib/commands/performance.js.map +1 -1
- package/build/lib/commands/permissions.d.ts +15 -17
- package/build/lib/commands/permissions.d.ts.map +1 -1
- package/build/lib/commands/permissions.js +12 -18
- package/build/lib/commands/permissions.js.map +1 -1
- package/build/lib/commands/proxy-helper.d.ts +11 -11
- package/build/lib/commands/proxy-helper.d.ts.map +1 -1
- package/build/lib/commands/proxy-helper.js +15 -24
- package/build/lib/commands/proxy-helper.js.map +1 -1
- package/build/lib/commands/record-audio.d.ts +25 -52
- package/build/lib/commands/record-audio.d.ts.map +1 -1
- package/build/lib/commands/record-audio.js +17 -19
- package/build/lib/commands/record-audio.js.map +1 -1
- package/build/lib/commands/recordscreen.d.ts +31 -62
- package/build/lib/commands/recordscreen.d.ts.map +1 -1
- package/build/lib/commands/recordscreen.js +29 -28
- package/build/lib/commands/recordscreen.js.map +1 -1
- package/build/lib/commands/screenshots.d.ts +15 -9
- package/build/lib/commands/screenshots.d.ts.map +1 -1
- package/build/lib/commands/screenshots.js +16 -16
- package/build/lib/commands/screenshots.js.map +1 -1
- package/build/lib/commands/simctl.d.ts +16 -22
- package/build/lib/commands/simctl.d.ts.map +1 -1
- package/build/lib/commands/simctl.js +13 -17
- package/build/lib/commands/simctl.js.map +1 -1
- package/build/lib/commands/source.d.ts +10 -8
- package/build/lib/commands/source.d.ts.map +1 -1
- package/build/lib/commands/source.js +11 -14
- package/build/lib/commands/source.js.map +1 -1
- package/build/lib/commands/timeouts.d.ts +25 -32
- package/build/lib/commands/timeouts.d.ts.map +1 -1
- package/build/lib/commands/timeouts.js +19 -15
- package/build/lib/commands/timeouts.js.map +1 -1
- package/build/lib/commands/types.d.ts +80 -0
- package/build/lib/commands/types.d.ts.map +1 -1
- package/build/lib/commands/web.d.ts +199 -202
- package/build/lib/commands/web.d.ts.map +1 -1
- package/build/lib/commands/web.js +216 -175
- package/build/lib/commands/web.js.map +1 -1
- package/build/lib/commands/xctest-record-screen.d.ts +17 -47
- package/build/lib/commands/xctest-record-screen.d.ts.map +1 -1
- package/build/lib/commands/xctest-record-screen.js +28 -59
- package/build/lib/commands/xctest-record-screen.js.map +1 -1
- package/build/lib/commands/xctest.d.ts +37 -37
- package/build/lib/commands/xctest.d.ts.map +1 -1
- package/build/lib/commands/xctest.js +38 -50
- package/build/lib/commands/xctest.js.map +1 -1
- package/build/lib/desired-caps.js +1 -1
- package/build/lib/device/log/ios-simulator-log.d.ts.map +1 -1
- package/build/lib/device/log/ios-simulator-log.js +2 -0
- package/build/lib/device/log/ios-simulator-log.js.map +1 -1
- package/build/lib/device/simulator-management.d.ts.map +1 -1
- package/build/lib/device/simulator-management.js +4 -5
- package/build/lib/device/simulator-management.js.map +1 -1
- 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/optional-checks.d.ts +0 -9
- package/build/lib/doctor/optional-checks.d.ts.map +1 -1
- package/build/lib/doctor/optional-checks.js +1 -30
- package/build/lib/doctor/optional-checks.js.map +1 -1
- package/build/lib/driver.d.ts +11 -3
- package/build/lib/driver.d.ts.map +1 -1
- package/build/lib/driver.js +36 -12
- 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 +0 -9
- package/build/lib/execute-method-map.js.map +1 -1
- package/build/lib/simulator-management.js +3 -3
- package/build/lib/simulator-management.js.map +1 -1
- package/lib/commands/active-app-info.ts +15 -0
- package/lib/commands/alert.ts +98 -0
- package/lib/commands/app-management.ts +414 -0
- package/lib/commands/{app-strings.js → app-strings.ts} +10 -9
- package/lib/commands/appearance.ts +70 -0
- package/lib/commands/audit.ts +25 -0
- package/lib/commands/{battery.js → battery.ts} +10 -12
- package/lib/commands/biometric.ts +52 -0
- package/lib/commands/{certificate.js → certificate.ts} +55 -50
- package/lib/commands/clipboard.ts +37 -0
- package/lib/commands/{condition.js → condition.ts} +21 -77
- package/lib/commands/content-size.ts +67 -0
- package/lib/commands/{context.js → context.ts} +174 -146
- package/lib/commands/device-info.ts +24 -0
- package/lib/commands/element.ts +419 -0
- package/lib/commands/{execute.js → execute.ts} +42 -38
- package/lib/commands/{file-movement.js → file-movement.ts} +212 -235
- package/lib/commands/find.ts +277 -0
- package/lib/commands/{general.js → general.ts} +102 -77
- package/lib/commands/geolocation.ts +55 -0
- package/lib/commands/{gesture.js → gesture.ts} +225 -183
- package/lib/commands/increase-contrast.ts +49 -0
- package/lib/commands/{iohid.js → iohid.ts} +15 -13
- package/lib/commands/keyboard.ts +70 -0
- package/lib/commands/keychains.ts +16 -0
- package/lib/commands/{localization.js → localization.ts} +22 -12
- package/lib/commands/{location.js → location.ts} +19 -22
- package/lib/commands/lock.ts +43 -0
- package/lib/commands/{log.js → log.ts} +68 -68
- package/lib/commands/{memory.js → memory.ts} +9 -9
- package/lib/commands/{navigation.js → navigation.ts} +42 -39
- package/lib/commands/{notifications.js → notifications.ts} +22 -14
- package/lib/commands/pasteboard.ts +44 -0
- package/lib/commands/{pcap.js → pcap.ts} +28 -28
- package/lib/commands/{performance.js → performance.ts} +133 -114
- package/lib/commands/permissions.ts +90 -0
- package/lib/commands/{proxy-helper.js → proxy-helper.ts} +26 -26
- package/lib/commands/{record-audio.js → record-audio.ts} +35 -33
- package/lib/commands/{recordscreen.js → recordscreen.ts} +78 -50
- package/lib/commands/{screenshots.js → screenshots.ts} +27 -21
- package/lib/commands/simctl.ts +82 -0
- package/lib/commands/{source.js → source.ts} +23 -20
- package/lib/commands/timeouts.ts +95 -0
- package/lib/commands/types.ts +86 -0
- package/lib/commands/{web.js → web.ts} +314 -264
- package/lib/commands/{xctest-record-screen.js → xctest-record-screen.ts} +54 -71
- package/lib/commands/{xctest.js → xctest.ts} +78 -71
- package/lib/desired-caps.ts +1 -1
- package/lib/device/log/ios-simulator-log.ts +2 -0
- package/lib/device/simulator-management.ts +3 -6
- package/lib/doctor/optional-checks.ts +0 -33
- package/lib/driver.ts +38 -13
- package/lib/execute-method-map.ts +0 -9
- package/package.json +6 -5
- package/lib/commands/active-app-info.js +0 -12
- package/lib/commands/alert.js +0 -88
- package/lib/commands/app-management.js +0 -346
- package/lib/commands/appearance.js +0 -71
- package/lib/commands/audit.js +0 -31
- package/lib/commands/biometric.js +0 -52
- package/lib/commands/clipboard.js +0 -35
- package/lib/commands/content-size.js +0 -68
- package/lib/commands/deviceInfo.js +0 -27
- package/lib/commands/element.js +0 -423
- package/lib/commands/find.js +0 -205
- package/lib/commands/geolocation.js +0 -56
- package/lib/commands/increase-contrast.js +0 -50
- package/lib/commands/keyboard.js +0 -62
- package/lib/commands/keychains.js +0 -17
- package/lib/commands/lock.js +0 -46
- package/lib/commands/pasteboard.js +0 -43
- package/lib/commands/permissions.js +0 -85
- package/lib/commands/simctl.js +0 -71
- package/lib/commands/timeouts.js +0 -68
|
@@ -2,27 +2,29 @@ import { Pyidevice } from '../device/clients/py-ios-device-client';
|
|
|
2
2
|
import {fs, tempDir, util} from 'appium/support';
|
|
3
3
|
import {encodeBase64OrUpload} from '../utils';
|
|
4
4
|
import {errors} from 'appium/driver';
|
|
5
|
+
import type {XCUITestDriver} from '../driver';
|
|
6
|
+
import type {SubProcess} from 'teen_process';
|
|
5
7
|
|
|
6
8
|
const MAX_CAPTURE_TIME_SEC = 60 * 60 * 12;
|
|
7
9
|
const DEFAULT_EXT = '.pcap';
|
|
8
10
|
|
|
9
11
|
export class TrafficCapture {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
12
|
+
private mainProcess: SubProcess | null = null;
|
|
13
|
+
private readonly udid: string;
|
|
14
|
+
private readonly log: any;
|
|
15
|
+
private readonly resultPath: string;
|
|
16
|
+
|
|
17
|
+
constructor(udid: string, log: any, resultPath: string) {
|
|
13
18
|
this.udid = udid;
|
|
14
19
|
this.log = log;
|
|
15
20
|
this.resultPath = resultPath;
|
|
16
|
-
this.mainProcess = null;
|
|
17
21
|
}
|
|
18
22
|
|
|
19
|
-
async start(timeoutSeconds) {
|
|
20
|
-
this.mainProcess =
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
}).collectPcap(this.resultPath)
|
|
25
|
-
);
|
|
23
|
+
async start(timeoutSeconds: number): Promise<void> {
|
|
24
|
+
this.mainProcess = await new Pyidevice({
|
|
25
|
+
udid: this.udid,
|
|
26
|
+
log: this.log,
|
|
27
|
+
}).collectPcap(this.resultPath);
|
|
26
28
|
this.mainProcess.on('line-stderr', (line) => this.log.info(`[Pcap] ${line}`));
|
|
27
29
|
this.log.info(
|
|
28
30
|
`Starting network traffic capture session on the device '${this.udid}'. ` +
|
|
@@ -37,17 +39,17 @@ export class TrafficCapture {
|
|
|
37
39
|
});
|
|
38
40
|
}
|
|
39
41
|
|
|
40
|
-
isCapturing() {
|
|
42
|
+
isCapturing(): boolean {
|
|
41
43
|
return !!this.mainProcess?.isRunning;
|
|
42
44
|
}
|
|
43
45
|
|
|
44
|
-
async interrupt(force = false) {
|
|
46
|
+
async interrupt(force = false): Promise<boolean> {
|
|
45
47
|
if (this.isCapturing()) {
|
|
46
48
|
const interruptPromise = this.mainProcess?.stop(force ? 'SIGTERM' : 'SIGINT');
|
|
47
49
|
this.mainProcess = null;
|
|
48
50
|
try {
|
|
49
51
|
await interruptPromise;
|
|
50
|
-
} catch (e) {
|
|
52
|
+
} catch (e: any) {
|
|
51
53
|
this.log.warn(
|
|
52
54
|
`Cannot ${force ? 'terminate' : 'interrupt'} the traffic capture session. ` +
|
|
53
55
|
`Original error: ${e.message}`,
|
|
@@ -59,12 +61,12 @@ export class TrafficCapture {
|
|
|
59
61
|
return true;
|
|
60
62
|
}
|
|
61
63
|
|
|
62
|
-
async finish() {
|
|
64
|
+
async finish(): Promise<string> {
|
|
63
65
|
await this.interrupt();
|
|
64
66
|
return this.resultPath;
|
|
65
67
|
}
|
|
66
68
|
|
|
67
|
-
async cleanup() {
|
|
69
|
+
async cleanup(): Promise<void> {
|
|
68
70
|
if (await fs.exists(this.resultPath)) {
|
|
69
71
|
await fs.rimraf(this.resultPath);
|
|
70
72
|
}
|
|
@@ -74,13 +76,15 @@ export class TrafficCapture {
|
|
|
74
76
|
/**
|
|
75
77
|
* Records the given network traffic capture into a .pcap file.
|
|
76
78
|
*
|
|
77
|
-
* @param
|
|
78
|
-
* @param
|
|
79
|
+
* @param timeLimitSec - The maximum recording time, in seconds. The maximum value is `43200` (12 hours).
|
|
80
|
+
* @param forceRestart - Whether to restart traffic capture process forcefully when startPcap is called (`true`) or ignore the call until the current traffic capture is completed (`false`, the default value).
|
|
79
81
|
* @throws {Error} If network traffic capture has failed to start.
|
|
80
|
-
* @returns {Promise<void>}
|
|
81
|
-
* @this {XCUITestDriver}
|
|
82
82
|
*/
|
|
83
|
-
export async function mobileStartPcap(
|
|
83
|
+
export async function mobileStartPcap(
|
|
84
|
+
this: XCUITestDriver,
|
|
85
|
+
timeLimitSec = 180,
|
|
86
|
+
forceRestart = false,
|
|
87
|
+
): Promise<void> {
|
|
84
88
|
if (this.isSimulator()) {
|
|
85
89
|
throw this.log.errorWithException('Network traffic capture only works on real devices');
|
|
86
90
|
}
|
|
@@ -135,17 +139,16 @@ export async function mobileStartPcap(timeLimitSec = 180, forceRestart = false)
|
|
|
135
139
|
* If no previously recorded file is found and no active traffic capture processes are running, then the method returns an empty string.
|
|
136
140
|
*
|
|
137
141
|
* @remarks Network capture files can be viewed in [Wireshark](https://www.wireshark.org/) and other similar applications.
|
|
138
|
-
* @returns
|
|
142
|
+
* @returns Base64-encoded content of the recorded pcap file or an empty string if no traffic capture has been started before.
|
|
139
143
|
* @throws {Error} If there was an error while getting the capture file.
|
|
140
|
-
* @this {XCUITestDriver}
|
|
141
144
|
*/
|
|
142
|
-
export async function mobileStopPcap() {
|
|
145
|
+
export async function mobileStopPcap(this: XCUITestDriver): Promise<string> {
|
|
143
146
|
if (!this._trafficCapture) {
|
|
144
147
|
this.log.info('Network traffic collector has not been started. There is nothing to stop');
|
|
145
148
|
return '';
|
|
146
149
|
}
|
|
147
150
|
|
|
148
|
-
let resultPath;
|
|
151
|
+
let resultPath: string;
|
|
149
152
|
try {
|
|
150
153
|
resultPath = await this._trafficCapture.finish();
|
|
151
154
|
if (!(await fs.exists(resultPath))) {
|
|
@@ -163,6 +166,3 @@ export async function mobileStopPcap() {
|
|
|
163
166
|
return await encodeBase64OrUpload(resultPath);
|
|
164
167
|
}
|
|
165
168
|
|
|
166
|
-
/**
|
|
167
|
-
* @typedef {import('../driver').XCUITestDriver} XCUITestDriver
|
|
168
|
-
*/
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import _ from 'lodash';
|
|
2
|
-
import path from 'path';
|
|
2
|
+
import path from 'node:path';
|
|
3
3
|
import {fs, zip, logger, util, tempDir} from 'appium/support';
|
|
4
4
|
import {SubProcess, exec} from 'teen_process';
|
|
5
5
|
import {encodeBase64OrUpload} from '../utils';
|
|
6
6
|
import {waitForCondition} from 'asyncbox';
|
|
7
7
|
import B from 'bluebird';
|
|
8
|
+
import type {XCUITestDriver} from '../driver';
|
|
9
|
+
import type {ActiveAppInfo} from './types';
|
|
10
|
+
import type {Method} from 'axios';
|
|
8
11
|
|
|
9
12
|
const PERF_RECORD_FEAT_NAME = 'perf_record';
|
|
10
13
|
const PERF_RECORD_SECURITY_MESSAGE =
|
|
@@ -34,16 +37,16 @@ const XCRUN = 'xcrun';
|
|
|
34
37
|
*
|
|
35
38
|
* Read [Recording, Pausing, and Stopping Traces](https://developer.apple.com/library/content/documentation/DeveloperTools/Conceptual/InstrumentsUserGuide/Recording,Pausing,andStoppingTraces.html) for more details.
|
|
36
39
|
*
|
|
37
|
-
* @param
|
|
38
|
-
* @param
|
|
39
|
-
* @param
|
|
40
|
-
* @this {XCUITestDriver}
|
|
40
|
+
* @param timeout - The maximum count of milliseconds to record the profiling information.
|
|
41
|
+
* @param 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.
|
|
42
|
+
* @param 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).
|
|
41
43
|
*/
|
|
42
44
|
export async function mobileStartPerfRecord(
|
|
45
|
+
this: XCUITestDriver,
|
|
43
46
|
timeout = DEFAULT_TIMEOUT_MS,
|
|
44
47
|
profileName = DEFAULT_PROFILE_NAME,
|
|
45
|
-
pid,
|
|
46
|
-
) {
|
|
48
|
+
pid?: number | 'current',
|
|
49
|
+
): Promise<void> {
|
|
47
50
|
if (!this.isFeatureEnabled(PERF_RECORD_FEAT_NAME) && !this.isRealDevice()) {
|
|
48
51
|
throw this.log.errorWithException(PERF_RECORD_SECURITY_MESSAGE);
|
|
49
52
|
}
|
|
@@ -62,21 +65,19 @@ export async function mobileStartPerfRecord(
|
|
|
62
65
|
}
|
|
63
66
|
}
|
|
64
67
|
|
|
65
|
-
let realPid;
|
|
68
|
+
let realPid: number | undefined;
|
|
66
69
|
if (pid) {
|
|
67
70
|
if (_.toLower(String(pid)) === DEFAULT_PID) {
|
|
68
|
-
const appInfo =
|
|
69
|
-
await this.proxyCommand('/wda/activeAppInfo', 'GET')
|
|
70
|
-
);
|
|
71
|
+
const appInfo = (await this.proxyCommand('/wda/activeAppInfo', 'GET')) as ActiveAppInfo;
|
|
71
72
|
realPid = appInfo.pid;
|
|
72
73
|
} else {
|
|
73
|
-
realPid = pid;
|
|
74
|
+
realPid = pid as number;
|
|
74
75
|
}
|
|
75
76
|
}
|
|
76
77
|
const recorder = new PerfRecorder(await tempDir.openDir(), this.device.udid, {
|
|
77
78
|
timeout: parseInt(String(timeout), 10),
|
|
78
79
|
profileName,
|
|
79
|
-
pid:
|
|
80
|
+
pid: realPid,
|
|
80
81
|
});
|
|
81
82
|
await recorder.start();
|
|
82
83
|
this._perfRecorders = [...(this._perfRecorders || []), recorder];
|
|
@@ -89,30 +90,29 @@ export async function mobileStartPerfRecord(
|
|
|
89
90
|
*
|
|
90
91
|
* 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
|
*
|
|
92
|
-
* @param
|
|
93
|
-
* @param
|
|
94
|
-
* @param
|
|
95
|
-
* @param
|
|
96
|
-
* @param
|
|
97
|
-
* @param
|
|
98
|
-
* @param
|
|
99
|
-
* @param
|
|
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.
|
|
93
|
+
* @param 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.
|
|
94
|
+
* @param user - The name of the user for the remote authentication. Only works if `remotePath` is provided.
|
|
95
|
+
* @param pass - The password for the remote authentication. Only works if `remotePath` is provided.
|
|
96
|
+
* @param method - The http multipart upload method name. Only works if `remotePath` is provided. Defaults to `PUT`
|
|
97
|
+
* @param 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.
|
|
98
|
+
* @param headers - Additional headers mapping for multipart http(s) uploads
|
|
99
|
+
* @param fileFieldName - The name of the form field, where the file content BLOB should be stored for http(s) uploads. Defaults to `file`
|
|
100
|
+
* @param formFields - Additional form fields for multipart http(s) uploads
|
|
101
|
+
* @returns 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
102
|
* @throws {Error} If no performance recording with given profile name/device udid combination
|
|
103
103
|
* has been started before or the resulting .trace file has not been generated properly.
|
|
104
|
-
* @this {XCUITestDriver}
|
|
105
104
|
*/
|
|
106
105
|
export async function mobileStopPerfRecord(
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
106
|
+
this: XCUITestDriver,
|
|
107
|
+
remotePath?: string,
|
|
108
|
+
user?: string,
|
|
109
|
+
pass?: string,
|
|
110
|
+
method?: Method,
|
|
111
111
|
profileName = DEFAULT_PROFILE_NAME,
|
|
112
|
-
headers,
|
|
113
|
-
fileFieldName,
|
|
114
|
-
formFields,
|
|
115
|
-
) {
|
|
112
|
+
headers?: Record<string, any>,
|
|
113
|
+
fileFieldName?: string,
|
|
114
|
+
formFields?: Record<string, any> | [string, any][],
|
|
115
|
+
): Promise<string> {
|
|
116
116
|
if (!this.isFeatureEnabled(PERF_RECORD_FEAT_NAME) && !this.isRealDevice()) {
|
|
117
117
|
throw this.log.errorWithException(PERF_RECORD_SECURITY_MESSAGE);
|
|
118
118
|
}
|
|
@@ -131,7 +131,12 @@ export async function mobileStopPerfRecord(
|
|
|
131
131
|
}
|
|
132
132
|
|
|
133
133
|
const recorder = _.first(recorders);
|
|
134
|
-
|
|
134
|
+
if (!recorder) {
|
|
135
|
+
throw this.log.errorWithException(
|
|
136
|
+
`No recorder found for performance profile '${profileName}' and device ${this.device.udid}`,
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
const resultPath = await recorder.stop();
|
|
135
140
|
if (!(await fs.exists(resultPath))) {
|
|
136
141
|
throw this.log.errorWithException(
|
|
137
142
|
`There is no ${DEFAULT_EXT} file found for performance profile '${profileName}' ` +
|
|
@@ -152,35 +157,18 @@ export async function mobileStopPerfRecord(
|
|
|
152
157
|
return result;
|
|
153
158
|
}
|
|
154
159
|
|
|
155
|
-
|
|
156
|
-
async function requireXctrace() {
|
|
157
|
-
const xcrunPath = await requireXcrun();
|
|
158
|
-
try {
|
|
159
|
-
await exec(xcrunPath, [XCTRACE, 'help']);
|
|
160
|
-
} catch (e) {
|
|
161
|
-
throw new Error(
|
|
162
|
-
`${XCTRACE} is not available for the active Xcode version. ` +
|
|
163
|
-
`Please make sure XCode is up to date. Original error: ${e.stderr || e.message}`,
|
|
164
|
-
);
|
|
165
|
-
}
|
|
166
|
-
return xcrunPath;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
async function requireInstruments() {
|
|
170
|
-
try {
|
|
171
|
-
return await fs.which(INSTRUMENTS);
|
|
172
|
-
} catch {
|
|
173
|
-
throw new Error(
|
|
174
|
-
`${INSTRUMENTS} has not been found in PATH. ` +
|
|
175
|
-
`Please make sure XCode development tools are installed`,
|
|
176
|
-
);
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
|
|
180
160
|
export class PerfRecorder {
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
161
|
+
private _process: SubProcess | null;
|
|
162
|
+
private _zippedReportPath: string;
|
|
163
|
+
private readonly _timeout: number;
|
|
164
|
+
private readonly _profileName: string;
|
|
165
|
+
private readonly _reportPath: string;
|
|
166
|
+
private readonly _pid: number | undefined;
|
|
167
|
+
private readonly _udid: string;
|
|
168
|
+
private readonly _logger: any;
|
|
169
|
+
private _archivePromise: Promise<string> | null;
|
|
170
|
+
|
|
171
|
+
constructor(reportRoot: string, udid: string, opts: PerfRecorderOptions = {}) {
|
|
184
172
|
this._process = null;
|
|
185
173
|
this._zippedReportPath = '';
|
|
186
174
|
this._timeout = opts.timeout && opts.timeout > 0 ? opts.timeout : DEFAULT_TIMEOUT_MS;
|
|
@@ -197,15 +185,11 @@ export class PerfRecorder {
|
|
|
197
185
|
this._archivePromise = null;
|
|
198
186
|
}
|
|
199
187
|
|
|
200
|
-
get profileName() {
|
|
188
|
+
get profileName(): string {
|
|
201
189
|
return this._profileName;
|
|
202
190
|
}
|
|
203
191
|
|
|
204
|
-
async
|
|
205
|
-
return (await fs.exists(this._reportPath)) ? this._reportPath : '';
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
async getZippedReportPath() {
|
|
192
|
+
async getZippedReportPath(): Promise<string> {
|
|
209
193
|
// This is to prevent possible race conditions, because the archive operation
|
|
210
194
|
// could be pretty time-intensive
|
|
211
195
|
if (!this._archivePromise) {
|
|
@@ -229,55 +213,21 @@ export class PerfRecorder {
|
|
|
229
213
|
return await this._archivePromise;
|
|
230
214
|
}
|
|
231
215
|
|
|
232
|
-
isRunning() {
|
|
216
|
+
isRunning(): boolean {
|
|
233
217
|
return !!this._process?.isRunning;
|
|
234
218
|
}
|
|
235
219
|
|
|
236
|
-
async
|
|
237
|
-
|
|
238
|
-
this._logger.debug('Force-stopping the currently running perf recording');
|
|
239
|
-
try {
|
|
240
|
-
await this._process.stop('SIGKILL');
|
|
241
|
-
} catch {}
|
|
242
|
-
}
|
|
243
|
-
this._process = null;
|
|
244
|
-
const performCleanup = async () => {
|
|
245
|
-
try {
|
|
246
|
-
await B.all(
|
|
247
|
-
[this._zippedReportPath, path.dirname(this._reportPath)]
|
|
248
|
-
.filter(Boolean)
|
|
249
|
-
.map((x) => fs.rimraf(x)),
|
|
250
|
-
);
|
|
251
|
-
} catch (e) {
|
|
252
|
-
this._logger.warn(e.message);
|
|
253
|
-
}
|
|
254
|
-
};
|
|
255
|
-
if (this._archivePromise) {
|
|
256
|
-
(async () => {
|
|
257
|
-
try {
|
|
258
|
-
await this._archivePromise;
|
|
259
|
-
} catch {
|
|
260
|
-
} finally {
|
|
261
|
-
await performCleanup();
|
|
262
|
-
this._archivePromise = null;
|
|
263
|
-
}
|
|
264
|
-
})();
|
|
265
|
-
}
|
|
266
|
-
await performCleanup();
|
|
267
|
-
return '';
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
async start() {
|
|
271
|
-
let binaryPath;
|
|
220
|
+
async start(): Promise<void> {
|
|
221
|
+
let binaryPath: string;
|
|
272
222
|
try {
|
|
273
223
|
binaryPath = await requireXctrace();
|
|
274
|
-
} catch (e) {
|
|
224
|
+
} catch (e: any) {
|
|
275
225
|
this._logger.debug(e.message);
|
|
276
226
|
this._logger.warn(`Defaulting to ${INSTRUMENTS} usage`);
|
|
277
227
|
binaryPath = await requireInstruments();
|
|
278
228
|
}
|
|
279
229
|
|
|
280
|
-
const args = [];
|
|
230
|
+
const args: string[] = [];
|
|
281
231
|
const toolName = path.basename(binaryPath) === XCRUN ? XCTRACE : INSTRUMENTS;
|
|
282
232
|
if (toolName === XCTRACE) {
|
|
283
233
|
args.push(
|
|
@@ -318,16 +268,18 @@ export class PerfRecorder {
|
|
|
318
268
|
this._archivePromise = null;
|
|
319
269
|
this._logger.debug(`Starting performance recording: ${util.quote(fullCmd)}`);
|
|
320
270
|
for (const streamName of ['stdout', 'stderr']) {
|
|
321
|
-
this._process.on(`line-${streamName}`, (line) =>
|
|
271
|
+
this._process.on(`line-${streamName}`, (line: string) =>
|
|
272
|
+
this._logger.debug(`[${toolName}] ${line}`),
|
|
273
|
+
);
|
|
322
274
|
}
|
|
323
|
-
this._process.once('exit', async (code, signal) => {
|
|
275
|
+
this._process.once('exit', async (code: number | null, signal: string | null) => {
|
|
324
276
|
this._process = null;
|
|
325
277
|
if (code === 0) {
|
|
326
278
|
this._logger.debug('Performance recording exited without errors');
|
|
327
279
|
try {
|
|
328
280
|
// cache zipped report
|
|
329
281
|
await this.getZippedReportPath();
|
|
330
|
-
} catch (e) {
|
|
282
|
+
} catch (e: any) {
|
|
331
283
|
this._logger.warn(e);
|
|
332
284
|
}
|
|
333
285
|
} else {
|
|
@@ -366,7 +318,7 @@ export class PerfRecorder {
|
|
|
366
318
|
this._logger.info(`The performance recording has started. Will timeout in ${this._timeout}ms`);
|
|
367
319
|
}
|
|
368
320
|
|
|
369
|
-
async stop(force = false) {
|
|
321
|
+
async stop(force = false): Promise<string> {
|
|
370
322
|
if (force) {
|
|
371
323
|
return await this._enforceTermination();
|
|
372
324
|
}
|
|
@@ -385,9 +337,74 @@ export class PerfRecorder {
|
|
|
385
337
|
}
|
|
386
338
|
return await this.getZippedReportPath();
|
|
387
339
|
}
|
|
340
|
+
|
|
341
|
+
private async getOriginalReportPath(): Promise<string> {
|
|
342
|
+
return (await fs.exists(this._reportPath)) ? this._reportPath : '';
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
private async _enforceTermination(): Promise<string> {
|
|
346
|
+
if (this._process && this.isRunning()) {
|
|
347
|
+
this._logger.debug('Force-stopping the currently running perf recording');
|
|
348
|
+
try {
|
|
349
|
+
await this._process.stop('SIGKILL');
|
|
350
|
+
} catch {
|
|
351
|
+
// Ignore errors
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
this._process = null;
|
|
355
|
+
const performCleanup = async () => {
|
|
356
|
+
try {
|
|
357
|
+
await B.all(
|
|
358
|
+
[this._zippedReportPath, path.dirname(this._reportPath)]
|
|
359
|
+
.filter(Boolean)
|
|
360
|
+
.map((x) => fs.rimraf(x)),
|
|
361
|
+
);
|
|
362
|
+
} catch (e: any) {
|
|
363
|
+
this._logger.warn(e.message);
|
|
364
|
+
}
|
|
365
|
+
};
|
|
366
|
+
if (this._archivePromise) {
|
|
367
|
+
(async () => {
|
|
368
|
+
try {
|
|
369
|
+
await this._archivePromise;
|
|
370
|
+
} catch {
|
|
371
|
+
// Ignore errors
|
|
372
|
+
} finally {
|
|
373
|
+
await performCleanup();
|
|
374
|
+
this._archivePromise = null;
|
|
375
|
+
}
|
|
376
|
+
})();
|
|
377
|
+
}
|
|
378
|
+
await performCleanup();
|
|
379
|
+
return '';
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
async function requireXctrace(): Promise<string> {
|
|
384
|
+
const xcrunPath = await requireXcrun();
|
|
385
|
+
try {
|
|
386
|
+
await exec(xcrunPath, [XCTRACE, 'help']);
|
|
387
|
+
} catch (e: any) {
|
|
388
|
+
throw new Error(
|
|
389
|
+
`${XCTRACE} is not available for the active Xcode version. ` +
|
|
390
|
+
`Please make sure XCode is up to date. Original error: ${e.stderr || e.message}`,
|
|
391
|
+
);
|
|
392
|
+
}
|
|
393
|
+
return xcrunPath;
|
|
388
394
|
}
|
|
389
395
|
|
|
390
|
-
async function
|
|
396
|
+
async function requireInstruments(): Promise<string> {
|
|
397
|
+
try {
|
|
398
|
+
return await fs.which(INSTRUMENTS);
|
|
399
|
+
} catch {
|
|
400
|
+
throw new Error(
|
|
401
|
+
`${INSTRUMENTS} has not been found in PATH. ` +
|
|
402
|
+
`Please make sure XCode development tools are installed`,
|
|
403
|
+
);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
async function requireXcrun(): Promise<string> {
|
|
391
408
|
try {
|
|
392
409
|
return await fs.which(XCRUN);
|
|
393
410
|
} catch {
|
|
@@ -398,6 +415,8 @@ async function requireXcrun() {
|
|
|
398
415
|
}
|
|
399
416
|
}
|
|
400
417
|
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
418
|
+
interface PerfRecorderOptions {
|
|
419
|
+
timeout?: number;
|
|
420
|
+
profileName?: string;
|
|
421
|
+
pid?: number;
|
|
422
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import _ from 'lodash';
|
|
2
|
+
import {PermissionService} from './enum';
|
|
3
|
+
import {assertSimulator as _assertSimulator} from '../utils';
|
|
4
|
+
import type {XCUITestDriver} from '../driver';
|
|
5
|
+
import type {PermissionState} from './types';
|
|
6
|
+
import type {Simulator} from 'appium-ios-simulator';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Resets the given permission for the active application under test.
|
|
10
|
+
* Works for both Simulator and real devices using Xcode SDK 11.4+
|
|
11
|
+
*
|
|
12
|
+
* @param 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)
|
|
13
|
+
* @throws If permission reset fails on the device.
|
|
14
|
+
*/
|
|
15
|
+
export async function mobileResetPermission(
|
|
16
|
+
this: XCUITestDriver,
|
|
17
|
+
service: PermissionService | number,
|
|
18
|
+
): Promise<void> {
|
|
19
|
+
if (!service) {
|
|
20
|
+
throw new Error(`The 'service' option is expected to be present`);
|
|
21
|
+
}
|
|
22
|
+
let resource: number;
|
|
23
|
+
if (_.isString(service)) {
|
|
24
|
+
resource = PermissionService[_.toLower(service) as keyof typeof PermissionService];
|
|
25
|
+
if (!resource) {
|
|
26
|
+
throw new Error(
|
|
27
|
+
`The 'service' value must be one of ` + `${JSON.stringify(_.keys(PermissionService))}`,
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
} else if (_.isInteger(service)) {
|
|
31
|
+
resource = service;
|
|
32
|
+
} else {
|
|
33
|
+
throw new Error(
|
|
34
|
+
`The 'service' value must be either a string or an integer. ` +
|
|
35
|
+
`'${service}' is passed instead`,
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
await this.proxyCommand('/wda/resetAppAuth', 'POST', {resource});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Gets application permission state on a simulated device.
|
|
44
|
+
*
|
|
45
|
+
* **This method requires [WIX applesimutils](https://github.com/wix/AppleSimulatorUtils) to be installed on the Appium server host.**
|
|
46
|
+
*
|
|
47
|
+
* @param bundleId - Bundle identifier of the target application
|
|
48
|
+
* @param service - Service name
|
|
49
|
+
* @returns Either 'yes', 'no', 'unset' or 'limited'
|
|
50
|
+
* @throws If permission getting fails or the device is not a Simulator.
|
|
51
|
+
* @group Simulator Only
|
|
52
|
+
*/
|
|
53
|
+
export async function mobileGetPermission(
|
|
54
|
+
this: XCUITestDriver,
|
|
55
|
+
bundleId: string,
|
|
56
|
+
service: PermissionService,
|
|
57
|
+
): Promise<PermissionState> {
|
|
58
|
+
if (!service) {
|
|
59
|
+
throw new Error(`The 'service' option is expected to be present`);
|
|
60
|
+
}
|
|
61
|
+
assertSimulator(this);
|
|
62
|
+
|
|
63
|
+
return await (this.device as Simulator).getPermission(
|
|
64
|
+
bundleId, String(service)
|
|
65
|
+
) as PermissionState;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Set application permission state on Simulator.
|
|
70
|
+
*
|
|
71
|
+
* @param access - One or more access rules to set.
|
|
72
|
+
* @param bundleId - Bundle identifier of the target application
|
|
73
|
+
* @since Xcode SDK 11.4
|
|
74
|
+
* @throws If permission setting fails or the device is not a Simulator.
|
|
75
|
+
* @group Simulator Only
|
|
76
|
+
*/
|
|
77
|
+
export async function mobileSetPermissions(
|
|
78
|
+
this: XCUITestDriver,
|
|
79
|
+
access: Record<string, PermissionState>,
|
|
80
|
+
bundleId: string,
|
|
81
|
+
): Promise<void> {
|
|
82
|
+
if (!_.isPlainObject(access)) {
|
|
83
|
+
throw new Error(`The 'access' option is expected to be a map`);
|
|
84
|
+
}
|
|
85
|
+
assertSimulator(this);
|
|
86
|
+
|
|
87
|
+
await (this.device as Simulator).setPermissions(bundleId, access);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const assertSimulator = (driver: XCUITestDriver) => _assertSimulator.call(driver, 'Permission-related operations');
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import {errors, routeToCommandName} from 'appium/driver';
|
|
2
2
|
import B from 'bluebird';
|
|
3
|
+
import type {XCUITestDriver} from '../driver';
|
|
3
4
|
|
|
4
5
|
const GET = 'GET';
|
|
5
6
|
const POST = 'POST';
|
|
6
7
|
const DELETE = 'DELETE';
|
|
7
|
-
const SUPPORTED_METHODS = Object.freeze(new Set(
|
|
8
|
+
const SUPPORTED_METHODS = Object.freeze(new Set([GET, POST, DELETE] as const));
|
|
8
9
|
|
|
9
|
-
const WDA_ROUTES =
|
|
10
|
+
const WDA_ROUTES = {
|
|
10
11
|
'/wda/screen': {
|
|
11
12
|
GET: 'getScreenInfo',
|
|
12
13
|
},
|
|
@@ -43,22 +44,30 @@ const WDA_ROUTES = /** @type {const} */ ({
|
|
|
43
44
|
'/wda/locked': {
|
|
44
45
|
GET: 'isLocked',
|
|
45
46
|
},
|
|
46
|
-
}
|
|
47
|
+
} as const;
|
|
48
|
+
|
|
49
|
+
export type AllowedHttpMethod = 'GET' | 'POST' | 'DELETE';
|
|
47
50
|
|
|
48
51
|
/**
|
|
49
52
|
* Proxies a command to WebDriverAgent
|
|
50
|
-
*
|
|
51
|
-
* @template
|
|
52
|
-
* @
|
|
53
|
-
* @param
|
|
54
|
-
* @param
|
|
55
|
-
* @param
|
|
56
|
-
* @this
|
|
57
|
-
* @returns
|
|
53
|
+
*
|
|
54
|
+
* @template TReq - Request body type
|
|
55
|
+
* @template TRes - Response type
|
|
56
|
+
* @param url - The endpoint URL
|
|
57
|
+
* @param method - HTTP method to use
|
|
58
|
+
* @param body - Optional request body
|
|
59
|
+
* @param isSessionCommand - Whether this is a session command (default: true)
|
|
60
|
+
* @returns Promise resolving to the response
|
|
58
61
|
*/
|
|
59
|
-
export async function proxyCommand
|
|
62
|
+
export async function proxyCommand<TReq = any, TRes = unknown>(
|
|
63
|
+
this: XCUITestDriver,
|
|
64
|
+
url: string,
|
|
65
|
+
method: AllowedHttpMethod,
|
|
66
|
+
body?: TReq,
|
|
67
|
+
isSessionCommand: boolean = true,
|
|
68
|
+
): Promise<TRes> {
|
|
60
69
|
if (this.shutdownUnexpectedly) {
|
|
61
|
-
return
|
|
70
|
+
return undefined as TRes;
|
|
62
71
|
}
|
|
63
72
|
|
|
64
73
|
if (!url) {
|
|
@@ -83,12 +92,12 @@ export async function proxyCommand(url, method, body, isSessionCommand = true) {
|
|
|
83
92
|
}
|
|
84
93
|
|
|
85
94
|
if (!timeout) {
|
|
86
|
-
return
|
|
95
|
+
return await proxy.command(url, method, body) as TRes;
|
|
87
96
|
}
|
|
88
97
|
|
|
89
98
|
this.log.debug(`Setting custom timeout to ${timeout} ms for '${cmdName}' command`);
|
|
90
99
|
try {
|
|
91
|
-
return
|
|
100
|
+
return await B.resolve(proxy.command(url, method, body)).timeout(timeout) as TRes;
|
|
92
101
|
} catch (e) {
|
|
93
102
|
if (!(e instanceof B.Promise.TimeoutError)) {
|
|
94
103
|
throw e;
|
|
@@ -102,18 +111,9 @@ export async function proxyCommand(url, method, body, isSessionCommand = true) {
|
|
|
102
111
|
}
|
|
103
112
|
}
|
|
104
113
|
|
|
105
|
-
|
|
106
|
-
* @param {string} endpoint
|
|
107
|
-
* @param {AllowedHttpMethod} method
|
|
108
|
-
* @returns {string|undefined}
|
|
109
|
-
*/
|
|
110
|
-
function wdaRouteToCommandName(endpoint, method) {
|
|
114
|
+
function wdaRouteToCommandName(endpoint: string, method: AllowedHttpMethod): string | undefined {
|
|
111
115
|
if (endpoint in WDA_ROUTES) {
|
|
112
|
-
return WDA_ROUTES[endpoint][method];
|
|
116
|
+
return WDA_ROUTES[endpoint as keyof typeof WDA_ROUTES]?.[method];
|
|
113
117
|
}
|
|
114
118
|
}
|
|
115
119
|
|
|
116
|
-
/**
|
|
117
|
-
* @typedef {import('../driver').XCUITestDriver} XCUITestDriver
|
|
118
|
-
* @typedef {'GET'|'POST'|'DELETE'} AllowedHttpMethod
|
|
119
|
-
*/
|