appium-xcuitest-driver 10.13.1 → 10.13.3
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/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/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/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/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/navigation.d.ts.map +1 -1
- package/build/lib/commands/navigation.js +12 -14
- package/build/lib/commands/navigation.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/recordscreen.d.ts +31 -63
- 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/execute-method-map.d.ts.map +1 -1
- package/build/lib/execute-method-map.js +0 -1
- package/build/lib/execute-method-map.js.map +1 -1
- package/lib/commands/app-management.ts +414 -0
- package/lib/commands/{certificate.js → certificate.ts} +55 -50
- package/lib/commands/element.ts +419 -0
- package/lib/commands/{file-movement.js → file-movement.ts} +212 -235
- package/lib/commands/find.ts +277 -0
- package/lib/commands/navigation.js +20 -14
- package/lib/commands/{performance.js → performance.ts} +133 -114
- package/lib/commands/{recordscreen.js → recordscreen.ts} +78 -50
- package/lib/execute-method-map.ts +0 -1
- package/npm-shrinkwrap.json +5 -5
- package/package.json +1 -1
- package/lib/commands/app-management.js +0 -346
- package/lib/commands/element.js +0 -423
- package/lib/commands/find.js +0 -205
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
import _ from 'lodash';
|
|
2
|
+
import {CssConverter} from '../css-converter';
|
|
3
|
+
import {errors} from 'appium/driver';
|
|
4
|
+
import {util} from 'appium/support';
|
|
5
|
+
import type {Element, AppiumLogger} from '@appium/types';
|
|
6
|
+
import type {XCUITestDriver} from '../driver';
|
|
7
|
+
import type {AllowedHttpMethod} from './proxy-helper';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Finds elements, delegating to web or native based on context.
|
|
11
|
+
*/
|
|
12
|
+
export async function findElOrEls(
|
|
13
|
+
this: XCUITestDriver,
|
|
14
|
+
strategy: string,
|
|
15
|
+
selector: string,
|
|
16
|
+
mult: true,
|
|
17
|
+
context?: any,
|
|
18
|
+
): Promise<Element[]>;
|
|
19
|
+
export async function findElOrEls(
|
|
20
|
+
this: XCUITestDriver,
|
|
21
|
+
strategy: string,
|
|
22
|
+
selector: string,
|
|
23
|
+
mult: false,
|
|
24
|
+
context?: any,
|
|
25
|
+
): Promise<Element>;
|
|
26
|
+
export async function findElOrEls(
|
|
27
|
+
this: XCUITestDriver,
|
|
28
|
+
strategy: string,
|
|
29
|
+
selector: string,
|
|
30
|
+
mult: boolean,
|
|
31
|
+
context?: any,
|
|
32
|
+
): Promise<Element | Element[]>;
|
|
33
|
+
export async function findElOrEls(
|
|
34
|
+
this: XCUITestDriver,
|
|
35
|
+
strategy: string,
|
|
36
|
+
selector: string,
|
|
37
|
+
mult: boolean,
|
|
38
|
+
context?: any,
|
|
39
|
+
): Promise<Element | Element[]>;
|
|
40
|
+
export async function findElOrEls(
|
|
41
|
+
this: XCUITestDriver,
|
|
42
|
+
strategy: string,
|
|
43
|
+
selector: string,
|
|
44
|
+
mult: boolean,
|
|
45
|
+
context?: any,
|
|
46
|
+
): Promise<Element | Element[]> {
|
|
47
|
+
if (this.isWebview()) {
|
|
48
|
+
return mult
|
|
49
|
+
? await this.findWebElementOrElements(strategy, selector, true, context)
|
|
50
|
+
: await this.findWebElementOrElements(strategy, selector, false, context);
|
|
51
|
+
}
|
|
52
|
+
return mult
|
|
53
|
+
? await this.findNativeElementOrElements(strategy, selector, true, context)
|
|
54
|
+
: await this.findNativeElementOrElements(strategy, selector, false, context);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Finds elements natively with strategy/selector rewriting for WDA.
|
|
59
|
+
*/
|
|
60
|
+
export async function findNativeElementOrElements(
|
|
61
|
+
this: XCUITestDriver,
|
|
62
|
+
strategy: string,
|
|
63
|
+
selector: string,
|
|
64
|
+
mult: true,
|
|
65
|
+
context?: any,
|
|
66
|
+
): Promise<Element[]>;
|
|
67
|
+
export async function findNativeElementOrElements(
|
|
68
|
+
this: XCUITestDriver,
|
|
69
|
+
strategy: string,
|
|
70
|
+
selector: string,
|
|
71
|
+
mult: false,
|
|
72
|
+
context?: any,
|
|
73
|
+
): Promise<Element>;
|
|
74
|
+
export async function findNativeElementOrElements(
|
|
75
|
+
this: XCUITestDriver,
|
|
76
|
+
strategy: string,
|
|
77
|
+
selector: string,
|
|
78
|
+
mult: boolean,
|
|
79
|
+
context?: any,
|
|
80
|
+
): Promise<Element | Element[]>;
|
|
81
|
+
export async function findNativeElementOrElements(
|
|
82
|
+
this: XCUITestDriver,
|
|
83
|
+
strategy: string,
|
|
84
|
+
selector: string,
|
|
85
|
+
mult: boolean,
|
|
86
|
+
context?: any,
|
|
87
|
+
): Promise<Element | Element[]> {
|
|
88
|
+
const initSelector = selector;
|
|
89
|
+
let rewroteSelector = false;
|
|
90
|
+
if (strategy === '-ios predicate string') {
|
|
91
|
+
strategy = 'predicate string';
|
|
92
|
+
} else if (strategy === '-ios class chain') {
|
|
93
|
+
strategy = WDA_CLASS_CHAIN_STRATEGY;
|
|
94
|
+
} else if (strategy === 'css selector') {
|
|
95
|
+
strategy = WDA_CLASS_CHAIN_STRATEGY;
|
|
96
|
+
selector = CssConverter.toIosClassChainSelector(selector);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (strategy === 'class name') {
|
|
100
|
+
if (selector.startsWith('UIA')) {
|
|
101
|
+
selector = selector.substring(3);
|
|
102
|
+
}
|
|
103
|
+
if (!selector.startsWith('XCUIElementType')) {
|
|
104
|
+
selector = stripViewFromSelector(`XCUIElementType${selector}`);
|
|
105
|
+
rewroteSelector = true;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (strategy === 'xpath' && MAGIC_FIRST_VIS_CHILD_SEL.test(selector)) {
|
|
110
|
+
return await this.getFirstVisibleChild(mult, context);
|
|
111
|
+
} else if (strategy === 'xpath' && MAGIC_SCROLLABLE_SEL.test(selector)) {
|
|
112
|
+
[strategy, selector] = rewriteMagicScrollable(mult, this.log);
|
|
113
|
+
} else if (strategy === 'xpath') {
|
|
114
|
+
selector = selector.replace(/(^|\/)(UIA)([^[/]+)/g, (str, g1, _g2, g3) => {
|
|
115
|
+
rewroteSelector = true;
|
|
116
|
+
return g1 + stripViewFromSelector(`XCUIElementType${g3}`);
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (rewroteSelector) {
|
|
121
|
+
this.log.info(
|
|
122
|
+
`Rewrote incoming selector from '${initSelector}' to ` +
|
|
123
|
+
`'${selector}' to match XCUI type. You should consider ` +
|
|
124
|
+
`updating your tests to use the new selectors directly`,
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return mult
|
|
129
|
+
? await this.doNativeFind(strategy, selector, true, context)
|
|
130
|
+
: await this.doNativeFind(strategy, selector, false, context);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Finds elements natively and returns either a single element or an array depending on `mult`.
|
|
135
|
+
*
|
|
136
|
+
* Returns an array when `mult` is true; otherwise returns a single element.
|
|
137
|
+
*/
|
|
138
|
+
export async function doNativeFind(
|
|
139
|
+
this: XCUITestDriver,
|
|
140
|
+
strategy: string,
|
|
141
|
+
selector: string,
|
|
142
|
+
mult: true,
|
|
143
|
+
context?: any,
|
|
144
|
+
): Promise<Element[]>;
|
|
145
|
+
export async function doNativeFind(
|
|
146
|
+
this: XCUITestDriver,
|
|
147
|
+
strategy: string,
|
|
148
|
+
selector: string,
|
|
149
|
+
mult: false,
|
|
150
|
+
context?: any,
|
|
151
|
+
): Promise<Element>;
|
|
152
|
+
export async function doNativeFind(
|
|
153
|
+
this: XCUITestDriver,
|
|
154
|
+
strategy: string,
|
|
155
|
+
selector: string,
|
|
156
|
+
mult: boolean,
|
|
157
|
+
context?: any,
|
|
158
|
+
): Promise<Element | Element[]>;
|
|
159
|
+
export async function doNativeFind(
|
|
160
|
+
this: XCUITestDriver,
|
|
161
|
+
strategy: string,
|
|
162
|
+
selector: string,
|
|
163
|
+
mult: boolean,
|
|
164
|
+
context?: any,
|
|
165
|
+
): Promise<Element | Element[]>;
|
|
166
|
+
export async function doNativeFind(
|
|
167
|
+
this: XCUITestDriver,
|
|
168
|
+
strategy: string,
|
|
169
|
+
selector: string,
|
|
170
|
+
mult: boolean,
|
|
171
|
+
context?: any,
|
|
172
|
+
): Promise<Element | Element[]> {
|
|
173
|
+
const ctx = util.unwrapElement(context ?? null);
|
|
174
|
+
const endpoint = `/element${ctx ? `/${ctx}/element` : ''}${mult ? 's' : ''}`;
|
|
175
|
+
|
|
176
|
+
const body = {
|
|
177
|
+
using: strategy,
|
|
178
|
+
value: selector,
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
const method: AllowedHttpMethod = 'POST';
|
|
182
|
+
|
|
183
|
+
let els: Element[] | Element = [];
|
|
184
|
+
try {
|
|
185
|
+
await this.implicitWaitForCondition(async () => {
|
|
186
|
+
try {
|
|
187
|
+
els = (await this.proxyCommand(endpoint, method, body)) as Element[] | Element;
|
|
188
|
+
} catch {
|
|
189
|
+
els = [] as Element[];
|
|
190
|
+
}
|
|
191
|
+
return !_.isEmpty(els as any[]);
|
|
192
|
+
});
|
|
193
|
+
} catch (err: any) {
|
|
194
|
+
if (err.message?.match(/Condition unmet/)) {
|
|
195
|
+
els = [] as Element[];
|
|
196
|
+
} else {
|
|
197
|
+
throw err;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
if (mult) {
|
|
201
|
+
return Array.isArray(els) ? els : [els];
|
|
202
|
+
}
|
|
203
|
+
if (Array.isArray(els)) {
|
|
204
|
+
if (_.isEmpty(els)) {
|
|
205
|
+
throw new errors.NoSuchElementError();
|
|
206
|
+
}
|
|
207
|
+
return els[0];
|
|
208
|
+
}
|
|
209
|
+
if (!els) {
|
|
210
|
+
throw new errors.NoSuchElementError();
|
|
211
|
+
}
|
|
212
|
+
return els;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Finds the first visible child element inside a context.
|
|
217
|
+
*/
|
|
218
|
+
export async function getFirstVisibleChild(
|
|
219
|
+
this: XCUITestDriver,
|
|
220
|
+
mult: boolean,
|
|
221
|
+
context: Element | string | null,
|
|
222
|
+
): Promise<Element> {
|
|
223
|
+
this.log.info(`Getting first visible child`);
|
|
224
|
+
if (mult) {
|
|
225
|
+
throw new Error('Cannot get multiple first visible children!');
|
|
226
|
+
}
|
|
227
|
+
if (!context) {
|
|
228
|
+
throw new Error('Cannot get first visible child without a context element');
|
|
229
|
+
}
|
|
230
|
+
let index = 1;
|
|
231
|
+
while (true) {
|
|
232
|
+
const strategy = WDA_CLASS_CHAIN_STRATEGY;
|
|
233
|
+
const selector = `*[${index}]`;
|
|
234
|
+
const nthChild = (await this.doNativeFind(strategy, selector, false, context)) as Element;
|
|
235
|
+
const visible = await this.getAttribute('visible', nthChild);
|
|
236
|
+
if (visible === 'true') {
|
|
237
|
+
this.log.info(`Found first visible child at position ${index}`);
|
|
238
|
+
return nthChild;
|
|
239
|
+
}
|
|
240
|
+
index++;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
const MAGIC_FIRST_VIS_CHILD_SEL = /\/\*\[@firstVisible\s*=\s*('|")true\1\]/;
|
|
245
|
+
const MAGIC_SCROLLABLE_SEL = /\/\/\*\[@scrollable\s*=\s*('|")true\1\]/;
|
|
246
|
+
const WDA_CLASS_CHAIN_STRATEGY = 'class chain';
|
|
247
|
+
|
|
248
|
+
function stripViewFromSelector(selector: string): string {
|
|
249
|
+
const keepView = [
|
|
250
|
+
'XCUIElementTypeScrollView',
|
|
251
|
+
'XCUIElementTypeCollectionView',
|
|
252
|
+
'XCUIElementTypeTextView',
|
|
253
|
+
'XCUIElementTypeWebView',
|
|
254
|
+
].includes(selector);
|
|
255
|
+
|
|
256
|
+
if (!keepView && selector.indexOf('View') === selector.length - 4) {
|
|
257
|
+
return selector.substring(0, selector.length - 4);
|
|
258
|
+
}
|
|
259
|
+
return selector;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
function rewriteMagicScrollable(mult: boolean, log: AppiumLogger | null = null): [string, string] {
|
|
263
|
+
const pred = ['ScrollView', 'Table', 'CollectionView', 'WebView']
|
|
264
|
+
.map((t) => `type == "XCUIElementType${t}"`)
|
|
265
|
+
.join(' OR ');
|
|
266
|
+
const strategy = WDA_CLASS_CHAIN_STRATEGY;
|
|
267
|
+
let selector = '**/*[`' + pred + '`]';
|
|
268
|
+
if (!mult) {
|
|
269
|
+
selector += '[1]';
|
|
270
|
+
}
|
|
271
|
+
log?.info(
|
|
272
|
+
'Rewrote request for scrollable descendants to class chain ' +
|
|
273
|
+
`format with selector '${selector}'`,
|
|
274
|
+
);
|
|
275
|
+
return [strategy, selector];
|
|
276
|
+
}
|
|
277
|
+
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import {errors} from 'appium/driver';
|
|
2
|
-
import _ from 'lodash';
|
|
3
2
|
import {waitForCondition} from 'asyncbox';
|
|
4
3
|
import { isTvOs } from '../utils';
|
|
5
4
|
|
|
@@ -84,29 +83,36 @@ export async function nativeBack() {
|
|
|
84
83
|
}
|
|
85
84
|
|
|
86
85
|
try {
|
|
87
|
-
|
|
86
|
+
const navBar = await this.findNativeElementOrElements(
|
|
88
87
|
'class name',
|
|
89
88
|
'XCUIElementTypeNavigationBar',
|
|
90
89
|
false,
|
|
91
90
|
);
|
|
92
|
-
let
|
|
93
|
-
|
|
94
|
-
'
|
|
91
|
+
let dstButton;
|
|
92
|
+
const backButtons = await this.findNativeElementOrElements(
|
|
93
|
+
'-ios predicate string',
|
|
94
|
+
'type == "XCUIElementTypeButton" AND label == "Back"',
|
|
95
95
|
true,
|
|
96
96
|
navBar,
|
|
97
97
|
);
|
|
98
|
-
if (
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
98
|
+
if (backButtons.length === 0) {
|
|
99
|
+
const buttons = await this.findNativeElementOrElements(
|
|
100
|
+
'-ios predicate string',
|
|
101
|
+
'type == "XCUIElementTypeButton"',
|
|
102
|
+
true,
|
|
103
|
+
navBar,
|
|
104
|
+
);
|
|
105
|
+
if (buttons.length === 0) {
|
|
106
|
+
throw new Error('No buttons found in navigation bar');
|
|
107
|
+
}
|
|
104
108
|
this.log.debug(`Found navigation bar 'back' button. Clicking.`);
|
|
109
|
+
dstButton = buttons[0];
|
|
105
110
|
} else {
|
|
106
|
-
this.log.debug(`
|
|
107
|
-
|
|
111
|
+
this.log.debug(`Did not find any navigation bar 'back' button. Clicking the first one.`);
|
|
112
|
+
dstButton = backButtons[0];
|
|
108
113
|
}
|
|
109
|
-
|
|
114
|
+
|
|
115
|
+
await this.nativeClick(dstButton);
|
|
110
116
|
} catch (err) {
|
|
111
117
|
this.log.error(`Unable to find navigation bar and back button: ${err.message}`);
|
|
112
118
|
}
|