appium-xcuitest-driver 10.3.0 → 10.4.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 +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 -38
- 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 +275 -197
- package/build/lib/commands/web.d.ts.map +1 -1
- package/build/lib/commands/web.js +866 -785
- 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 +277 -1597
- package/build/lib/driver.d.ts.map +1 -1
- package/build/lib/driver.js +318 -235
- 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/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 -42
- 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 +932 -859
- package/lib/commands/xctest-record-screen.js +103 -105
- package/lib/commands/xctest.js +134 -139
- package/lib/driver.js +286 -235
- package/lib/execute-method-map.ts +9 -0
- package/npm-shrinkwrap.json +8 -8
- package/package.json +1 -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/cookies.d.ts +0 -15
- package/build/lib/cookies.d.ts.map +0 -1
- package/build/lib/cookies.js +0 -84
- package/build/lib/cookies.js.map +0 -1
- package/lib/commands/activeAppInfo.js +0 -14
- package/lib/commands/index.js +0 -95
- package/lib/cookies.js +0 -92
package/lib/commands/context.js
CHANGED
|
@@ -18,457 +18,461 @@ const DEFAULT_LIST_WEB_FRAMES_RETRIES = 20;
|
|
|
18
18
|
const DEFAULT_NATIVE_WINDOW_HANDLE = '1';
|
|
19
19
|
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
/**
|
|
22
|
+
* @this {XCUITestDriver}
|
|
23
|
+
* @param {boolean} [useUrl=false]
|
|
24
|
+
* @returns {Promise<import('./types').ViewContext[]>}
|
|
25
|
+
*/
|
|
26
|
+
export async function getContextsAndViews(useUrl = true) {
|
|
27
|
+
this.log.debug('Retrieving contexts and views');
|
|
28
|
+
const webviews = await this.listWebFrames(useUrl);
|
|
22
29
|
/**
|
|
23
|
-
* @
|
|
24
|
-
* @param {boolean} [useUrl=false]
|
|
25
|
-
* @returns {Promise<import('./types').ViewContext[]>}
|
|
30
|
+
* @type {import('./types').ViewContext[]}
|
|
26
31
|
*/
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
for (const view of webviews) {
|
|
36
|
-
ctxs.push({id: `${WEBVIEW_BASE}${view.id}`, view});
|
|
37
|
-
this.contexts.push(view.id.toString());
|
|
38
|
-
}
|
|
39
|
-
return ctxs;
|
|
40
|
-
},
|
|
32
|
+
const ctxs = [{id: NATIVE_WIN, view: {}}];
|
|
33
|
+
this.contexts = [NATIVE_WIN];
|
|
34
|
+
for (const view of webviews) {
|
|
35
|
+
ctxs.push({id: `${WEBVIEW_BASE}${view.id}`, view});
|
|
36
|
+
this.contexts.push(view.id.toString());
|
|
37
|
+
}
|
|
38
|
+
return ctxs;
|
|
39
|
+
}
|
|
41
40
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
41
|
+
/**
|
|
42
|
+
* @deprecated this method is not used anywhere and will be removed in the future
|
|
43
|
+
* @this {XCUITestDriver}
|
|
44
|
+
* @returns {boolean}
|
|
45
|
+
*/
|
|
46
|
+
export function useNewSafari() {
|
|
47
|
+
return this.isSimulator() && this.isSafari();
|
|
48
|
+
}
|
|
50
49
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
50
|
+
/**
|
|
51
|
+
* @this {XCUITestDriver}
|
|
52
|
+
* @returns {Promise<void>}
|
|
53
|
+
*/
|
|
54
|
+
export async function activateRecentWebview() {
|
|
55
|
+
this.log.debug('Activating a recent webview');
|
|
56
|
+
const timer = new timing.Timer().start();
|
|
57
|
+
const contextId = await this.getRecentWebviewContextId(/.*/, /.*/);
|
|
58
|
+
if (contextId) {
|
|
59
|
+
this.log.info(`Picking webview '${contextId}' after ${timer.getDuration().asMilliSeconds.toFixed(0)}ms`);
|
|
60
|
+
await this.setContext(contextId);
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
const appDict = (/** @type {RemoteDebugger} */ (this.remote)).appDict;
|
|
64
|
+
const errSuffix = `Make sure your web application is debuggable ` +
|
|
65
|
+
`and could be inspected in Safari Web Inspector.`;
|
|
66
|
+
if (_.isEmpty(appDict)) {
|
|
67
|
+
throw new Error(
|
|
68
|
+
`The remote debugger did not return any connected web applications after ` +
|
|
69
|
+
`${timer.getDuration().asMilliSeconds.toFixed(0)}ms. ` +
|
|
70
|
+
`${errSuffix} ` +
|
|
71
|
+
`You may try to change the 'webviewConnectTimeout' capability value to ` +
|
|
72
|
+
`customize the retrieval timeout.`
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
const errSuffix2 = `${errSuffix} You may try to change the 'webviewConnectRetries' ` +
|
|
76
|
+
`capability value to customize the amount of pages retrieval retries.`;
|
|
77
|
+
const appsWithPages = _.values(appDict).filter(({pageArray}) => !_.isEmpty(pageArray));
|
|
78
|
+
if (appsWithPages.length > 0) {
|
|
79
|
+
throw new Error(
|
|
80
|
+
`The remote debugger returned ${util.pluralize('web application', appsWithPages.length, true)} ` +
|
|
81
|
+
`with pages after ${timer.getDuration().asMilliSeconds.toFixed(0)}ms, ` +
|
|
82
|
+
`although none of them matched our page search criteria. ${errSuffix2}`
|
|
83
|
+
);
|
|
84
|
+
} else {
|
|
85
|
+
throw new Error(
|
|
86
|
+
`The remote debugger returned ${util.pluralize('web application', _.size(appDict), true)}, ` +
|
|
87
|
+
`but none of them had pages after ${timer.getDuration().asMilliSeconds.toFixed(0)}ms. ` +
|
|
88
|
+
`${errSuffix2} Also, in rare cases the device restart or device OS upgrade may fix this ` +
|
|
89
|
+
`issue if none of the above advices helps.`
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* @this {XCUITestDriver}
|
|
96
|
+
* @returns {Promise<import('../types').Page[]>}
|
|
97
|
+
*/
|
|
98
|
+
export async function listWebFrames(useUrl = true) {
|
|
99
|
+
const shouldFilterByUrl = useUrl && !this.isRealDevice() && !!this.getCurrentUrl();
|
|
100
|
+
this.log.debug(
|
|
101
|
+
`Selecting by url: ${shouldFilterByUrl}` +
|
|
102
|
+
(shouldFilterByUrl ? ` (expected url: '${this.getCurrentUrl()}')` : '')
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
if (!this.remote) {
|
|
106
|
+
await this.connectToRemoteDebugger();
|
|
107
|
+
}
|
|
108
|
+
const doListPages = async (/** @type {number} */ retries) => {
|
|
109
|
+
try {
|
|
110
|
+
const pageArray = await (/** @type {RemoteDebugger} */ (this.remote)).selectApp(
|
|
111
|
+
shouldFilterByUrl ? this.getCurrentUrl() : undefined,
|
|
112
|
+
retries,
|
|
113
|
+
this.opts.ignoreAboutBlankUrl,
|
|
74
114
|
);
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
`
|
|
83
|
-
`although none of them matched our page search criteria. ${errSuffix2}`
|
|
115
|
+
if (_.isEmpty(pageArray)) {
|
|
116
|
+
// we have no web frames, but continue anyway
|
|
117
|
+
this.log.debug(`No web frames found after ${util.pluralize('retry', retries, true)}`);
|
|
118
|
+
}
|
|
119
|
+
return pageArray;
|
|
120
|
+
} catch (err) {
|
|
121
|
+
this.log.debug(
|
|
122
|
+
`No available web pages after ${util.pluralize('retry', retries, true)}: ${err.message}`
|
|
84
123
|
);
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
124
|
+
return [];
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
/** @type {number} */
|
|
129
|
+
const maxRetriesCount = _.isInteger(this.opts.webviewConnectRetries)
|
|
130
|
+
? Math.max(/** @type {number} */ (this.opts.webviewConnectRetries), 1)
|
|
131
|
+
: DEFAULT_LIST_WEB_FRAMES_RETRIES;
|
|
132
|
+
this.log.debug(
|
|
133
|
+
`About to select a web application with ${util.pluralize('retry', maxRetriesCount, true)} ` +
|
|
134
|
+
`and 500ms interval between each retry. Consider customizing the value of 'webviewConnectRetries' ` +
|
|
135
|
+
`capability to change the amount of retries.`
|
|
136
|
+
);
|
|
137
|
+
return await doListPages(maxRetriesCount);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* @this {XCUITestDriver}
|
|
142
|
+
* @returns {Promise<void>}
|
|
143
|
+
*/
|
|
144
|
+
export async function connectToRemoteDebugger() {
|
|
145
|
+
this.remote = await this.getNewRemoteDebugger();
|
|
146
|
+
|
|
147
|
+
// @ts-ignore static is fine
|
|
148
|
+
this.remote.on(RemoteDebugger.EVENT_PAGE_CHANGE, this.onPageChange.bind(this));
|
|
149
|
+
// @ts-ignore static is fine
|
|
150
|
+
this.remote.on(RemoteDebugger.EVENT_FRAMES_DETACHED, () => {
|
|
151
|
+
if (!_.isEmpty(this.curWebFrames)) {
|
|
152
|
+
const curWebFrames = this.curWebFrames;
|
|
153
|
+
this.log.debug(
|
|
154
|
+
`Clearing ${util.pluralize('frame', curWebFrames.length, true)}: ${curWebFrames.join(
|
|
155
|
+
', ',
|
|
156
|
+
)}`,
|
|
91
157
|
);
|
|
92
158
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
`
|
|
102
|
-
|
|
159
|
+
this.curWebFrames = [];
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
const timeoutMs = this.opts.webviewConnectTimeout ?? DEFAULT_REMOTE_DEBUGGER_CONNECT_TIMEOUT_MS;
|
|
163
|
+
const apps = await this.remote.connect(timeoutMs);
|
|
164
|
+
if (_.isEmpty(apps)) {
|
|
165
|
+
this.log.info(
|
|
166
|
+
`The remote debugger did not report any active web applications within ${timeoutMs}ms timeout. ` +
|
|
167
|
+
`Consider increasing the value of 'webviewConnectTimeout' capability to wait longer ` +
|
|
168
|
+
`on slower devices.`
|
|
103
169
|
);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
104
172
|
|
|
105
|
-
|
|
106
|
-
|
|
173
|
+
/**
|
|
174
|
+
* Retrieves the list of available contexts.
|
|
175
|
+
*
|
|
176
|
+
* The list includes extended context information, like URLs and page names.
|
|
177
|
+
* This is different from the standard `getContexts` API, because the latter
|
|
178
|
+
* only has web view names without any additional information.
|
|
179
|
+
*
|
|
180
|
+
* @remarks In situations where multiple web views are available at once, the
|
|
181
|
+
* client code would have to connect to each of them in order to detect the
|
|
182
|
+
* one which needs to be interacted with. This extra effort is not needed with
|
|
183
|
+
* the information provided by this extension.
|
|
184
|
+
* @param {number} [waitForWebviewMs=0] - The period to poll for available webview(s) (in ms)
|
|
185
|
+
* @returns {Promise<Context[]>} The list of available context objects along with their properties.
|
|
186
|
+
* @this {XCUITestDriver}
|
|
187
|
+
*/
|
|
188
|
+
export async function mobileGetContexts(waitForWebviewMs = 0) {
|
|
189
|
+
// make sure it is a number, so the duration check works properly
|
|
190
|
+
if (!_.isNumber(waitForWebviewMs)) {
|
|
191
|
+
waitForWebviewMs = parseInt(waitForWebviewMs, 10);
|
|
192
|
+
if (isNaN(waitForWebviewMs)) {
|
|
193
|
+
waitForWebviewMs = 0;
|
|
107
194
|
}
|
|
108
|
-
|
|
109
|
-
try {
|
|
110
|
-
const pageArray = await (/** @type {RemoteDebugger} */ (this.remote)).selectApp(
|
|
111
|
-
shouldFilterByUrl ? this.getCurrentUrl() : undefined,
|
|
112
|
-
retries,
|
|
113
|
-
this.opts.ignoreAboutBlankUrl,
|
|
114
|
-
);
|
|
115
|
-
if (_.isEmpty(pageArray)) {
|
|
116
|
-
// we have no web frames, but continue anyway
|
|
117
|
-
this.log.debug(`No web frames found after ${util.pluralize('retry', retries, true)}`);
|
|
118
|
-
}
|
|
119
|
-
return pageArray;
|
|
120
|
-
} catch (err) {
|
|
121
|
-
this.log.debug(
|
|
122
|
-
`No available web pages after ${util.pluralize('retry', retries, true)}: ${err.message}`
|
|
123
|
-
);
|
|
124
|
-
return [];
|
|
125
|
-
}
|
|
126
|
-
};
|
|
195
|
+
}
|
|
127
196
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
/**
|
|
140
|
-
* @this {XCUITestDriver}
|
|
141
|
-
* @returns {Promise<void>}
|
|
142
|
-
*/
|
|
143
|
-
async connectToRemoteDebugger() {
|
|
144
|
-
this.remote = await this.getNewRemoteDebugger();
|
|
145
|
-
|
|
146
|
-
// @ts-ignore static is fine
|
|
147
|
-
this.remote.on(RemoteDebugger.EVENT_PAGE_CHANGE, this.onPageChange.bind(this));
|
|
148
|
-
// @ts-ignore static is fine
|
|
149
|
-
this.remote.on(RemoteDebugger.EVENT_FRAMES_DETACHED, () => {
|
|
150
|
-
if (!_.isEmpty(this.curWebFrames)) {
|
|
151
|
-
const curWebFrames = this.curWebFrames;
|
|
197
|
+
const curOpt = this.opts.fullContextList;
|
|
198
|
+
this.opts.fullContextList = true;
|
|
199
|
+
|
|
200
|
+
const timer = new timing.Timer().start();
|
|
201
|
+
try {
|
|
202
|
+
/** @type {FullContext[]} */
|
|
203
|
+
let contexts;
|
|
204
|
+
do {
|
|
205
|
+
contexts = /** @type {FullContext[]} */ (await this.getContexts());
|
|
206
|
+
|
|
207
|
+
if (contexts.length >= 2) {
|
|
152
208
|
this.log.debug(
|
|
153
|
-
`
|
|
154
|
-
', ',
|
|
155
|
-
)}`,
|
|
209
|
+
`Found webview context after ${timer.getDuration().asMilliSeconds.toFixed(0)}ms`,
|
|
156
210
|
);
|
|
211
|
+
return contexts;
|
|
157
212
|
}
|
|
158
|
-
this.
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
213
|
+
this.log.debug(`No webviews found in ${timer.getDuration().asMilliSeconds.toFixed(0)}ms`);
|
|
214
|
+
} while (timer.getDuration().asMilliSeconds < waitForWebviewMs);
|
|
215
|
+
return contexts;
|
|
216
|
+
} finally {
|
|
217
|
+
// reset the option so there are no side effects
|
|
218
|
+
this.opts.fullContextList = curOpt;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* @this {XCUITestDriver}
|
|
224
|
+
* @param {import('./types').PageChangeNotification} pageChangeNotification
|
|
225
|
+
* @returns {Promise<void>}
|
|
226
|
+
*/
|
|
227
|
+
export async function onPageChange(pageChangeNotification) {
|
|
228
|
+
this.log.debug(
|
|
229
|
+
`Remote debugger notified us of a new page listing: ${JSON.stringify(
|
|
230
|
+
pageChangeNotification,
|
|
231
|
+
)}`,
|
|
232
|
+
);
|
|
233
|
+
if (this.selectingNewPage) {
|
|
234
|
+
this.log.debug('We are in the middle of selecting a page, ignoring');
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
if (!this.remote?.isConnected) {
|
|
238
|
+
this.log.debug('We have not yet connected, ignoring');
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
const {appIdKey, pageArray} = pageChangeNotification;
|
|
243
|
+
|
|
244
|
+
/** @type {string[]} */
|
|
245
|
+
const newIds = [];
|
|
246
|
+
/** @type {string[]} */
|
|
247
|
+
const newPages = [];
|
|
248
|
+
/** @type {string|null} */
|
|
249
|
+
let keyId = null;
|
|
250
|
+
for (const page of pageArray) {
|
|
251
|
+
const id = page.id.toString();
|
|
252
|
+
newIds.push(id);
|
|
253
|
+
if (page.isKey) {
|
|
254
|
+
keyId = id;
|
|
169
255
|
}
|
|
170
|
-
|
|
256
|
+
const contextId = `${appIdKey}.${id}`;
|
|
171
257
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
* the information provided by this extension.
|
|
183
|
-
* @param {number} [waitForWebviewMs=0] - The period to poll for available webview(s) (in ms)
|
|
184
|
-
* @returns {Promise<Context[]>} The list of available context objects along with their properties.
|
|
185
|
-
* @this {XCUITestDriver}
|
|
186
|
-
*/
|
|
187
|
-
async mobileGetContexts(waitForWebviewMs = 0) {
|
|
188
|
-
// make sure it is a number, so the duration check works properly
|
|
189
|
-
if (!_.isNumber(waitForWebviewMs)) {
|
|
190
|
-
waitForWebviewMs = parseInt(waitForWebviewMs, 10);
|
|
191
|
-
if (isNaN(waitForWebviewMs)) {
|
|
192
|
-
waitForWebviewMs = 0;
|
|
258
|
+
// add if this is a new page
|
|
259
|
+
if (!_.includes(this.contexts, contextId)) {
|
|
260
|
+
if (isUrlIgnored(page.url, this.opts.safariIgnoreWebHostnames)) {
|
|
261
|
+
this.log.info(
|
|
262
|
+
`Not tracking '${page.url}' page because it is blacklisted. ` +
|
|
263
|
+
`'safariIgnoreWebHostnames'=${this.opts.safariIgnoreWebHostnames}`,
|
|
264
|
+
);
|
|
265
|
+
} else {
|
|
266
|
+
newPages.push(id);
|
|
267
|
+
this.contexts.push(contextId);
|
|
193
268
|
}
|
|
194
269
|
}
|
|
270
|
+
}
|
|
195
271
|
|
|
196
|
-
|
|
197
|
-
|
|
272
|
+
if (!keyId) {
|
|
273
|
+
// if there is no key id, pull the first id from the page array and use that
|
|
274
|
+
// as a stand in
|
|
275
|
+
this.log.debug('No key id found. Choosing first id from page array');
|
|
276
|
+
keyId = newIds[0] || null;
|
|
277
|
+
}
|
|
198
278
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
} finally {
|
|
216
|
-
// reset the option so there are no side effects
|
|
217
|
-
this.opts.fullContextList = curOpt;
|
|
218
|
-
}
|
|
219
|
-
},
|
|
220
|
-
/**
|
|
221
|
-
* @this {XCUITestDriver}
|
|
222
|
-
* @param {import('./types').PageChangeNotification} pageChangeNotification
|
|
223
|
-
* @returns {Promise<void>}
|
|
224
|
-
*/
|
|
225
|
-
async onPageChange(pageChangeNotification) {
|
|
279
|
+
if (!util.hasValue(this.curContext)) {
|
|
280
|
+
this.log.debug('We do not appear to have window set yet, ignoring');
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
const [curAppIdKey, curPageIdKey] = this.curContext.split('.');
|
|
284
|
+
if (curAppIdKey !== appIdKey) {
|
|
285
|
+
this.log.debug('Page change not referring to currently selected app, ignoring.');
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/** @type {string|null} */
|
|
290
|
+
let newPage = null;
|
|
291
|
+
if (newPages.length) {
|
|
292
|
+
newPage = /** @type {string} */ (_.last(newPages));
|
|
293
|
+
this.log.debug(`We have new pages, selecting page '${newPage}'`);
|
|
294
|
+
} else if (!_.includes(newIds, curPageIdKey)) {
|
|
226
295
|
this.log.debug(
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
)}`,
|
|
296
|
+
'New page listing from remote debugger does not contain ' +
|
|
297
|
+
'current window; assuming it is closed',
|
|
230
298
|
);
|
|
231
|
-
if (
|
|
232
|
-
this.log.
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
this.
|
|
299
|
+
if (!util.hasValue(keyId)) {
|
|
300
|
+
this.log.error(
|
|
301
|
+
'Do not have our current window anymore, and there ' +
|
|
302
|
+
'are not any more to load! Doing nothing...',
|
|
303
|
+
);
|
|
304
|
+
this.setCurrentUrl(undefined);
|
|
237
305
|
return;
|
|
238
306
|
}
|
|
239
307
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
let keyId = null;
|
|
248
|
-
for (const page of pageArray) {
|
|
249
|
-
const id = page.id.toString();
|
|
250
|
-
newIds.push(id);
|
|
251
|
-
if (page.isKey) {
|
|
252
|
-
keyId = id;
|
|
253
|
-
}
|
|
254
|
-
const contextId = `${appIdKey}.${id}`;
|
|
255
|
-
|
|
256
|
-
// add if this is a new page
|
|
257
|
-
if (!_.includes(this.contexts, contextId)) {
|
|
258
|
-
if (isUrlIgnored(page.url, this.opts.safariIgnoreWebHostnames)) {
|
|
259
|
-
this.log.info(
|
|
260
|
-
`Not tracking '${page.url}' page because it is blacklisted. ` +
|
|
261
|
-
`'safariIgnoreWebHostnames'=${this.opts.safariIgnoreWebHostnames}`,
|
|
262
|
-
);
|
|
263
|
-
} else {
|
|
264
|
-
newPages.push(id);
|
|
265
|
-
this.contexts.push(contextId);
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
}
|
|
308
|
+
this.log.debug(`Debugger already selected page '${keyId}', ` + `confirming that choice.`);
|
|
309
|
+
this.curContext = `${appIdKey}.${keyId}`;
|
|
310
|
+
newPage = keyId;
|
|
311
|
+
} else {
|
|
312
|
+
// at this point, there are no new pages, and the current page still exists
|
|
313
|
+
this.log.debug('New page listing is same as old, doing nothing');
|
|
314
|
+
}
|
|
269
315
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
316
|
+
// make sure that the page listing isn't indicating a redirect
|
|
317
|
+
if (util.hasValue(this.curContext)) {
|
|
318
|
+
const currentPageId = parseInt(String(_.last(this.curContext.split('.'))), 10);
|
|
319
|
+
const page = _.find(pageArray, (p) => parseInt(String(p.id), 10) === currentPageId);
|
|
320
|
+
if (page && page.url !== this.getCurrentUrl()) {
|
|
321
|
+
this.log.debug(`Redirected from '${this.getCurrentUrl()}' to '${page.url}'`);
|
|
322
|
+
this.setCurrentUrl(page.url);
|
|
275
323
|
}
|
|
324
|
+
}
|
|
276
325
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
326
|
+
if (util.hasValue(newPage)) {
|
|
327
|
+
this.selectingNewPage = true;
|
|
328
|
+
const oldContext = this.curContext;
|
|
329
|
+
this.curContext = `${appIdKey}.${newPage}`;
|
|
330
|
+
try {
|
|
331
|
+
await this.remote.selectPage(appIdKey, parseInt(newPage, 10));
|
|
332
|
+
await notifyBiDiContextChange.bind(this)();
|
|
333
|
+
} catch (e) {
|
|
334
|
+
this.log.warn(`Failed to select page: ${e.message}`);
|
|
335
|
+
this.curContext = oldContext;
|
|
336
|
+
} finally {
|
|
337
|
+
this.selectingNewPage = false;
|
|
285
338
|
}
|
|
339
|
+
}
|
|
340
|
+
}
|
|
286
341
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
'current window; assuming it is closed',
|
|
296
|
-
);
|
|
297
|
-
if (!util.hasValue(keyId)) {
|
|
298
|
-
this.log.error(
|
|
299
|
-
'Do not have our current window anymore, and there ' +
|
|
300
|
-
'are not any more to load! Doing nothing...',
|
|
301
|
-
);
|
|
302
|
-
this.setCurrentUrl(undefined);
|
|
303
|
-
return;
|
|
304
|
-
}
|
|
342
|
+
/**
|
|
343
|
+
* @this {XCUITestDriver}
|
|
344
|
+
* @returns {Promise<void>}
|
|
345
|
+
*/
|
|
346
|
+
export async function stopRemote(closeWindowBeforeDisconnecting = false) {
|
|
347
|
+
if (!this.remote) {
|
|
348
|
+
throw this.log.errorWithException('Tried to leave a web frame but were not in one');
|
|
349
|
+
}
|
|
305
350
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
351
|
+
if (closeWindowBeforeDisconnecting) {
|
|
352
|
+
await this.closeWindow();
|
|
353
|
+
}
|
|
354
|
+
await this.remote.disconnect();
|
|
355
|
+
this.curContext = null;
|
|
356
|
+
await notifyBiDiContextChange.bind(this)();
|
|
357
|
+
this.curWebFrames = [];
|
|
358
|
+
this.remote = null;
|
|
359
|
+
}
|
|
313
360
|
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
}
|
|
322
|
-
}
|
|
361
|
+
/**
|
|
362
|
+
* @this {XCUITestDriver}
|
|
363
|
+
* @param {string|undefined|null} url
|
|
364
|
+
*/
|
|
365
|
+
export function setCurrentUrl(url) {
|
|
366
|
+
this._currentUrl = url;
|
|
367
|
+
}
|
|
323
368
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
} catch (e) {
|
|
332
|
-
this.log.warn(`Failed to select page: ${e.message}`);
|
|
333
|
-
this.curContext = oldContext;
|
|
334
|
-
} finally {
|
|
335
|
-
this.selectingNewPage = false;
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
},
|
|
339
|
-
};
|
|
369
|
+
/**
|
|
370
|
+
* @this {XCUITestDriver}
|
|
371
|
+
* @returns {string|undefined|null}
|
|
372
|
+
*/
|
|
373
|
+
export function getCurrentUrl() {
|
|
374
|
+
return this._currentUrl;
|
|
375
|
+
}
|
|
340
376
|
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
377
|
+
/**
|
|
378
|
+
* @param {RegExp} titleRegExp
|
|
379
|
+
* @param {RegExp} urlRegExp
|
|
380
|
+
* @this {XCUITestDriver}
|
|
381
|
+
* @returns {Promise<string|undefined>}
|
|
382
|
+
*/
|
|
383
|
+
export async function getRecentWebviewContextId(titleRegExp, urlRegExp) {
|
|
384
|
+
if (!_.isRegExp(titleRegExp) && !_.isRegExp(urlRegExp)) {
|
|
385
|
+
throw new errors.InvalidArgumentError(
|
|
386
|
+
'A regular expression for either web view title or url must be provided',
|
|
387
|
+
);
|
|
388
|
+
}
|
|
350
389
|
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
this.remote = null;
|
|
359
|
-
},
|
|
360
|
-
/**
|
|
361
|
-
* @this {XCUITestDriver}
|
|
362
|
-
* @param {string|undefined|null} url
|
|
363
|
-
*/
|
|
364
|
-
setCurrentUrl(url) {
|
|
365
|
-
this._currentUrl = url;
|
|
366
|
-
},
|
|
367
|
-
/**
|
|
368
|
-
* @this {XCUITestDriver}
|
|
369
|
-
* @returns {string|undefined|null}
|
|
370
|
-
*/
|
|
371
|
-
getCurrentUrl() {
|
|
372
|
-
return this._currentUrl;
|
|
373
|
-
},
|
|
374
|
-
/**
|
|
375
|
-
* @param {RegExp} titleRegExp
|
|
376
|
-
* @param {RegExp} urlRegExp
|
|
377
|
-
* @this {XCUITestDriver}
|
|
378
|
-
* @returns {Promise<string|undefined>}
|
|
379
|
-
*/
|
|
380
|
-
async getRecentWebviewContextId(titleRegExp, urlRegExp) {
|
|
381
|
-
if (!_.isRegExp(titleRegExp) && !_.isRegExp(urlRegExp)) {
|
|
382
|
-
throw new errors.InvalidArgumentError(
|
|
383
|
-
'A regular expression for either web view title or url must be provided',
|
|
384
|
-
);
|
|
390
|
+
const currentUrl = this.getCurrentUrl();
|
|
391
|
+
const contexts = _.filter(await this.getContextsAndViews(false), 'view');
|
|
392
|
+
// first try to match by current url
|
|
393
|
+
if (currentUrl) {
|
|
394
|
+
const ctx = contexts.find(({view}) => (view?.url || '') === currentUrl);
|
|
395
|
+
if (ctx) {
|
|
396
|
+
return ctx.id;
|
|
385
397
|
}
|
|
398
|
+
}
|
|
399
|
+
// if not, try to match by regular expression
|
|
400
|
+
return contexts.find(
|
|
401
|
+
({view}) =>
|
|
402
|
+
(view?.title && titleRegExp?.test(view.title)) || (view?.url && urlRegExp?.test(view.url)),
|
|
403
|
+
)?.id;
|
|
404
|
+
}
|
|
386
405
|
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
// if not, try to match by regular expression
|
|
397
|
-
return contexts.find(
|
|
398
|
-
({view}) =>
|
|
399
|
-
(view?.title && titleRegExp?.test(view.title)) || (view?.url && urlRegExp?.test(view.url)),
|
|
400
|
-
)?.id;
|
|
401
|
-
},
|
|
402
|
-
/**
|
|
403
|
-
* @this {XCUITestDriver}
|
|
404
|
-
* @returns {boolean}
|
|
405
|
-
*/
|
|
406
|
-
isWebContext() {
|
|
407
|
-
return !!this.curContext && this.curContext !== NATIVE_WIN;
|
|
408
|
-
},
|
|
409
|
-
/**
|
|
410
|
-
* @this {XCUITestDriver}
|
|
411
|
-
* @returns {boolean}
|
|
412
|
-
*/
|
|
413
|
-
isWebview() {
|
|
414
|
-
return this.isWebContext();
|
|
415
|
-
},
|
|
416
|
-
/**
|
|
417
|
-
* @this {XCUITestDriver}
|
|
418
|
-
* @returns {Promise<RemoteDebugger>}
|
|
419
|
-
*/
|
|
420
|
-
async getNewRemoteDebugger() {
|
|
421
|
-
const socketPath = this.isRealDevice()
|
|
422
|
-
? undefined
|
|
423
|
-
: (await /** @type {import('../driver').Simulator} */ (this.device).getWebInspectorSocket() ?? undefined);
|
|
424
|
-
return createRemoteDebugger(
|
|
425
|
-
{
|
|
426
|
-
bundleId: this.opts.bundleId,
|
|
427
|
-
additionalBundleIds: this.opts.additionalWebviewBundleIds,
|
|
428
|
-
isSafari: this.isSafari(),
|
|
429
|
-
includeSafari: this.opts.includeSafariInWebviews,
|
|
430
|
-
pageLoadMs: this.pageLoadMs,
|
|
431
|
-
platformVersion: this.opts.platformVersion,
|
|
432
|
-
socketPath,
|
|
433
|
-
remoteDebugProxy: this.opts.remoteDebugProxy,
|
|
434
|
-
garbageCollectOnExecute: util.hasValue(this.opts.safariGarbageCollect)
|
|
435
|
-
? !!this.opts.safariGarbageCollect
|
|
436
|
-
: false,
|
|
437
|
-
udid: this.opts.udid,
|
|
438
|
-
logAllCommunication: this.opts.safariLogAllCommunication,
|
|
439
|
-
logAllCommunicationHexDump: this.opts.safariLogAllCommunicationHexDump,
|
|
440
|
-
socketChunkSize: this.opts.safariSocketChunkSize,
|
|
441
|
-
webInspectorMaxFrameLength: this.opts.safariWebInspectorMaxFrameLength,
|
|
442
|
-
pageLoadStrategy: this.caps.pageLoadStrategy,
|
|
443
|
-
},
|
|
444
|
-
this.isRealDevice(),
|
|
445
|
-
);
|
|
446
|
-
},
|
|
447
|
-
};
|
|
406
|
+
/**
|
|
407
|
+
* @this {XCUITestDriver}
|
|
408
|
+
* @returns {boolean}
|
|
409
|
+
*/
|
|
410
|
+
export function isWebContext() {
|
|
411
|
+
return !!this.curContext && this.curContext !== NATIVE_WIN;
|
|
412
|
+
}
|
|
448
413
|
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
return `${WEBVIEW_BASE}${this.curContext}`;
|
|
457
|
-
}
|
|
458
|
-
return NATIVE_WIN;
|
|
459
|
-
},
|
|
414
|
+
/**
|
|
415
|
+
* @this {XCUITestDriver}
|
|
416
|
+
* @returns {boolean}
|
|
417
|
+
*/
|
|
418
|
+
export function isWebview() {
|
|
419
|
+
return this.isWebContext();
|
|
420
|
+
}
|
|
460
421
|
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
422
|
+
/**
|
|
423
|
+
* @this {XCUITestDriver}
|
|
424
|
+
* @returns {Promise<RemoteDebugger>}
|
|
425
|
+
*/
|
|
426
|
+
export async function getNewRemoteDebugger() {
|
|
427
|
+
const socketPath = this.isRealDevice()
|
|
428
|
+
? undefined
|
|
429
|
+
: (await /** @type {import('../driver').Simulator} */ (this.device).getWebInspectorSocket() ?? undefined);
|
|
430
|
+
return createRemoteDebugger(
|
|
431
|
+
{
|
|
432
|
+
bundleId: this.opts.bundleId,
|
|
433
|
+
additionalBundleIds: this.opts.additionalWebviewBundleIds,
|
|
434
|
+
isSafari: this.isSafari(),
|
|
435
|
+
includeSafari: this.opts.includeSafariInWebviews,
|
|
436
|
+
pageLoadMs: this.pageLoadMs,
|
|
437
|
+
platformVersion: this.opts.platformVersion,
|
|
438
|
+
socketPath,
|
|
439
|
+
remoteDebugProxy: this.opts.remoteDebugProxy,
|
|
440
|
+
garbageCollectOnExecute: util.hasValue(this.opts.safariGarbageCollect)
|
|
441
|
+
? !!this.opts.safariGarbageCollect
|
|
442
|
+
: false,
|
|
443
|
+
udid: this.opts.udid,
|
|
444
|
+
logAllCommunication: this.opts.safariLogAllCommunication,
|
|
445
|
+
logAllCommunicationHexDump: this.opts.safariLogAllCommunicationHexDump,
|
|
446
|
+
socketChunkSize: this.opts.safariSocketChunkSize,
|
|
447
|
+
webInspectorMaxFrameLength: this.opts.safariWebInspectorMaxFrameLength,
|
|
448
|
+
pageLoadStrategy: this.caps.pageLoadStrategy,
|
|
449
|
+
},
|
|
450
|
+
this.isRealDevice(),
|
|
451
|
+
);
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
/**
|
|
455
|
+
* @this {XCUITestDriver}
|
|
456
|
+
* @returns {Promise<string>}
|
|
457
|
+
*/
|
|
458
|
+
export async function getCurrentContext() {
|
|
459
|
+
if (this.curContext && this.curContext !== NATIVE_WIN) {
|
|
460
|
+
return `${WEBVIEW_BASE}${this.curContext}`;
|
|
461
|
+
}
|
|
462
|
+
return NATIVE_WIN;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
/**
|
|
466
|
+
* Set context
|
|
467
|
+
*
|
|
468
|
+
* @param {string|Context} name - The name of context to set. It could be 'null' as NATIVE_WIN.
|
|
469
|
+
* @param {any} [callback] The callback. (It is not called in this method)
|
|
470
|
+
* @param {boolean} [skipReadyCheck=false] - Whether it waits for the new context is ready
|
|
471
|
+
* @this {XCUITestDriver}
|
|
472
|
+
* @returns {Promise<void>}
|
|
473
|
+
*/
|
|
474
|
+
export async function setContext(name, callback, skipReadyCheck = false) {
|
|
475
|
+
function alreadyInContext(desired, current) {
|
|
472
476
|
return (
|
|
473
477
|
desired === current ||
|
|
474
478
|
(desired === null && current === NATIVE_WIN) ||
|
|
@@ -563,88 +567,90 @@ const commands = {
|
|
|
563
567
|
if (this.logs.safariNetwork) {
|
|
564
568
|
(/** @type {RemoteDebugger} */ (this.remote)).startNetwork(
|
|
565
569
|
this.logs.safariNetwork.onNetworkEvent.bind(this.logs.safariNetwork),
|
|
566
|
-
);
|
|
567
|
-
}
|
|
568
|
-
}
|
|
569
|
-
},
|
|
570
|
-
/**
|
|
571
|
-
* @this {XCUITestDriver}
|
|
572
|
-
* @returns {Promise<string[]|FullContext[]>}
|
|
573
|
-
*/
|
|
574
|
-
async getContexts() {
|
|
575
|
-
this.log.debug('Getting list of available contexts');
|
|
576
|
-
const contexts = await this.getContextsAndViews(false);
|
|
577
|
-
|
|
578
|
-
if (this.opts.fullContextList) {
|
|
579
|
-
return /** @type {import('./types').FullContext[]} */ (
|
|
580
|
-
contexts.map((context) => ({
|
|
581
|
-
id: context.id.toString(),
|
|
582
|
-
title: context.view?.title,
|
|
583
|
-
url: context.view?.url,
|
|
584
|
-
bundleId: context.view?.bundleId,
|
|
585
|
-
}))
|
|
586
570
|
);
|
|
587
571
|
}
|
|
588
|
-
|
|
589
|
-
|
|
572
|
+
}
|
|
573
|
+
}
|
|
590
574
|
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
}
|
|
608
|
-
},
|
|
609
|
-
/**
|
|
610
|
-
* @this {XCUITestDriver}
|
|
611
|
-
* @returns {Promise<string>}
|
|
612
|
-
*/
|
|
613
|
-
async getWindowHandle() {
|
|
614
|
-
if (!this.isWebContext()) {
|
|
615
|
-
// https://github.com/appium/appium/issues/20710
|
|
616
|
-
return DEFAULT_NATIVE_WINDOW_HANDLE;
|
|
617
|
-
}
|
|
618
|
-
if (!this.curContext) {
|
|
619
|
-
throw new errors.InvalidContextError();
|
|
620
|
-
}
|
|
621
|
-
this.log.debug(`Getting current window handle`);
|
|
622
|
-
return this.curContext;
|
|
623
|
-
},
|
|
624
|
-
/**
|
|
625
|
-
* @this {XCUITestDriver}
|
|
626
|
-
* @returns {Promise<string[]>}
|
|
627
|
-
*/
|
|
628
|
-
async getWindowHandles() {
|
|
629
|
-
if (!this.isWebContext()) {
|
|
630
|
-
// https://github.com/appium/appium/issues/20710
|
|
631
|
-
return [DEFAULT_NATIVE_WINDOW_HANDLE];
|
|
632
|
-
}
|
|
633
|
-
this.log.debug('Getting list of available window handles');
|
|
634
|
-
const contexts = await this.getContextsAndViews(false);
|
|
635
|
-
return (
|
|
636
|
-
contexts
|
|
637
|
-
// get rid of the native app context
|
|
638
|
-
.filter((context) => context.id !== NATIVE_WIN)
|
|
639
|
-
// get the `app.id` format expected
|
|
640
|
-
.map((context) =>
|
|
641
|
-
// This is non-nullable because the `FullContext` having `id` `NATIVE_WIN`
|
|
642
|
-
// _looks like_ the only with an empty view.
|
|
643
|
-
context.view?.id?.toString() ?? ''
|
|
644
|
-
)
|
|
575
|
+
/**
|
|
576
|
+
* @this {XCUITestDriver}
|
|
577
|
+
* @returns {Promise<string[]|FullContext[]>}
|
|
578
|
+
*/
|
|
579
|
+
export async function getContexts() {
|
|
580
|
+
this.log.debug('Getting list of available contexts');
|
|
581
|
+
const contexts = await this.getContextsAndViews(false);
|
|
582
|
+
|
|
583
|
+
if (this.opts.fullContextList) {
|
|
584
|
+
return /** @type {import('./types').FullContext[]} */ (
|
|
585
|
+
contexts.map((context) => ({
|
|
586
|
+
id: context.id.toString(),
|
|
587
|
+
title: context.view?.title,
|
|
588
|
+
url: context.view?.url,
|
|
589
|
+
bundleId: context.view?.bundleId,
|
|
590
|
+
}))
|
|
645
591
|
);
|
|
646
|
-
}
|
|
647
|
-
};
|
|
592
|
+
}
|
|
593
|
+
return /** @type {string[]} */ (contexts.map((context) => context.id.toString()));
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
/**
|
|
597
|
+
* @this {XCUITestDriver}
|
|
598
|
+
* @param {string} name
|
|
599
|
+
* @param {boolean} [skipReadyCheck]
|
|
600
|
+
* @returns {Promise<void>}
|
|
601
|
+
*/
|
|
602
|
+
export async function setWindow(name, skipReadyCheck) {
|
|
603
|
+
if (!this.isWebContext()) {
|
|
604
|
+
// https://github.com/appium/appium/issues/20710
|
|
605
|
+
return;
|
|
606
|
+
}
|
|
607
|
+
try {
|
|
608
|
+
await this.setContext(name, _.noop, skipReadyCheck);
|
|
609
|
+
} catch (err) {
|
|
610
|
+
// translate the error in terms of windows
|
|
611
|
+
throw isErrorType(err, errors.NoSuchContextError) ? new errors.NoSuchWindowError() : err;
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
/**
|
|
616
|
+
* @this {XCUITestDriver}
|
|
617
|
+
* @returns {Promise<string>}
|
|
618
|
+
*/
|
|
619
|
+
export async function getWindowHandle() {
|
|
620
|
+
if (!this.isWebContext()) {
|
|
621
|
+
// https://github.com/appium/appium/issues/20710
|
|
622
|
+
return DEFAULT_NATIVE_WINDOW_HANDLE;
|
|
623
|
+
}
|
|
624
|
+
if (!this.curContext) {
|
|
625
|
+
throw new errors.InvalidContextError();
|
|
626
|
+
}
|
|
627
|
+
this.log.debug(`Getting current window handle`);
|
|
628
|
+
return this.curContext;
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
/**
|
|
632
|
+
* @this {XCUITestDriver}
|
|
633
|
+
* @returns {Promise<string[]>}
|
|
634
|
+
*/
|
|
635
|
+
export async function getWindowHandles() {
|
|
636
|
+
if (!this.isWebContext()) {
|
|
637
|
+
// https://github.com/appium/appium/issues/20710
|
|
638
|
+
return [DEFAULT_NATIVE_WINDOW_HANDLE];
|
|
639
|
+
}
|
|
640
|
+
this.log.debug('Getting list of available window handles');
|
|
641
|
+
const contexts = await this.getContextsAndViews(false);
|
|
642
|
+
return (
|
|
643
|
+
contexts
|
|
644
|
+
// get rid of the native app context
|
|
645
|
+
.filter((context) => context.id !== NATIVE_WIN)
|
|
646
|
+
// get the `app.id` format expected
|
|
647
|
+
.map((context) =>
|
|
648
|
+
// This is non-nullable because the `FullContext` having `id` `NATIVE_WIN`
|
|
649
|
+
// _looks like_ the only with an empty view.
|
|
650
|
+
context.view?.id?.toString() ?? ''
|
|
651
|
+
)
|
|
652
|
+
);
|
|
653
|
+
}
|
|
648
654
|
|
|
649
655
|
/**
|
|
650
656
|
* Checks if a URL is blacklisted in the 'safariIgnoreWebHostnames' capability
|
|
@@ -691,7 +697,6 @@ export async function notifyBiDiContextChange() {
|
|
|
691
697
|
this.eventEmitter.emit(BIDI_EVENT_NAME, makeObsoleteContextUpdatedEvent(name));
|
|
692
698
|
}
|
|
693
699
|
|
|
694
|
-
export default {...helpers, ...extensions, ...commands};
|
|
695
700
|
|
|
696
701
|
/**
|
|
697
702
|
* @typedef {import('../driver').XCUITestDriver} XCUITestDriver
|