How to use rewriteMagicScrollable method in Appium Xcuitest Driver

Best JavaScript code snippet using appium-xcuitest-driver

Run Appium Xcuitest Driver automation tests on LambdaTest cloud grid

Perform automation testing on 3000+ real desktop and mobile devices online.

Sign up Free
_

find.js

Source: find.js Github

copy
1import _ from 'lodash';
2import { errors } from 'appium-base-driver';
3import { util } from 'appium-support';
4import log from '../logger';
5
6// we override the xpath search for this first-visible-child selector, which
7// looks like /*[@firstVisible="true"]
8const MAGIC_FIRST_VIS_CHILD_SEL = /\/\*\[@firstVisible\s*=\s*('|")true\1\]/;
9
10// we likewise override xpath search to provide a shortcut for finding all
11// scrollable elements
12const MAGIC_SCROLLABLE_SEL = /\/\/\*\[@scrollable\s*=\s*('|")true\1\]/;
13
14const WDA_CLASS_CHAIN_STRATEGY = 'class chain';
15
16let helpers = {}, commands = {}, extensions = {};
17
18helpers.findElOrEls = async function findElOrEls (strategy, selector, mult, context) {
19  if (this.isWebview()) {
20    return await this.findWebElementOrElements(strategy, selector, mult, context);
21  } else {
22    return await this.findNativeElementOrElements(strategy, selector, mult, context);
23  }
24};
25
26helpers.findNativeElementOrElements = async function findNativeElementOrElements (strategy, selector, mult, context) {
27  const initSelector = selector;
28  let rewroteSelector = false;
29  if (strategy === '-ios predicate string') {
30    // WebDriverAgent uses 'predicate string'
31    strategy = 'predicate string';
32  } else if (strategy === '-ios class chain') {
33    // WebDriverAgent uses 'class chain'
34    strategy = WDA_CLASS_CHAIN_STRATEGY;
35  }
36
37  // Check if the word 'View' is appended to selector and if it is, strip it out
38  function stripViewFromSelector (selector) {
39    // Don't strip it out if it's one of these 4 element types
40    // (see https://github.com/facebook/WebDriverAgent/blob/master/WebDriverAgentLib/Utilities/FBElementTypeTransformer.m for reference)
41    const keepView = [
42      'XCUIElementTypeScrollView',
43      'XCUIElementTypeCollectionView',
44      'XCUIElementTypeTextView',
45      'XCUIElementTypeWebView',
46    ].includes(selector);
47
48    if (!keepView && selector.indexOf('View') === selector.length - 4) {
49      return selector.substr(0, selector.length - 4);
50    } else {
51      return selector;
52    }
53  }
54
55  if (strategy === 'class name') {
56    // XCUITest classes have `XCUIElementType` prepended
57    // first check if there is the old `UIA` prefix
58    if (selector.startsWith('UIA')) {
59      selector = selector.substring(3);
60    }
61    // now check if we need to add `XCUIElementType`
62    if (!selector.startsWith('XCUIElementType')) {
63      selector = stripViewFromSelector(`XCUIElementType${selector}`);
64      rewroteSelector = true;
65    }
66  }
67
68  if (strategy === 'xpath' && MAGIC_FIRST_VIS_CHILD_SEL.test(selector)) {
69    return await this.getFirstVisibleChild(mult, context);
70  } else if (strategy === 'xpath' && MAGIC_SCROLLABLE_SEL.test(selector)) {
71    [strategy, selector] = rewriteMagicScrollable(mult);
72  } else if (strategy === 'xpath') {
73    // Replace UIA if it comes after a forward slash or is at the beginning of the string
74    selector = selector.replace(/(^|\/)(UIA)([^[/]+)/g, (str, g1, g2, g3) => {
75      rewroteSelector = true;
76      return g1 + stripViewFromSelector(`XCUIElementType${g3}`);
77    });
78  }
79
80  if (rewroteSelector) {
81    log.info(`Rewrote incoming selector from '${initSelector}' to ` +
82             `'${selector}' to match XCUI type. You should consider ` +
83             `updating your tests to use the new selectors directly`);
84  }
85
86  return await this.doNativeFind(strategy, selector, mult, context);
87};
88
89helpers.doNativeFind = async function doNativeFind (strategy, selector, mult, context) {
90  context = util.unwrapElement(context);
91
92  let endpoint = `/element${context ? `/${context}/element` : ''}${mult ? 's' : ''}`;
93
94  let body = {
95    using: strategy,
96    value: selector
97  };
98
99  let method = 'POST';
100
101  // This is either an array is mult === true
102  // or an object if mult === false
103  let els;
104  try {
105    await this.implicitWaitForCondition(async () => {
106      try {
107        els = await this.proxyCommand(endpoint, method, body);
108      } catch (err) {
109        els = [];
110      }
111      // we succeed if we get some elements
112      return !_.isEmpty(els);
113    });
114  } catch (err) {
115    if (err.message && err.message.match(/Condition unmet/)) {
116      // condition was not met setting res to empty array
117      els = [];
118    } else {
119      throw err;
120    }
121  }
122  if (mult) {
123    return els;
124  }
125  if (_.isEmpty(els)) {
126    throw new errors.NoSuchElementError();
127  }
128  return els;
129};
130
131helpers.getFirstVisibleChild = async function getFirstVisibleChild (mult, context) {
132  log.info(`Getting first visible child`);
133  if (mult) {
134    throw new Error('Cannot get multiple first visible children!');
135  }
136  if (!context) {
137    throw new Error('Cannot get first visible child without a context element');
138  }
139  let index = 1;
140  // loop through children via class-chain finds, until we run out of children
141  // or we find a visible one. This loop looks infinite but its not, because at
142  // some point the call to doNativeFind will throw with an Element Not Found
143  // error, when the index gets higher than the number of child elements. This
144  // is what we want because that error will halt the loop and make it all the
145  // way to the client.
146  while (true) { // eslint-disable-line no-constant-condition
147    const strategy = WDA_CLASS_CHAIN_STRATEGY;
148    const selector = `*[${index}]`;
149    const nthChild = await this.doNativeFind(strategy, selector, false, context);
150    const visible = await this.getAttribute('visible', nthChild);
151    if (visible === 'true') {
152      log.info(`Found first visible child at position ${index}`);
153      return nthChild;
154    }
155    index++;
156  }
157};
158
159function rewriteMagicScrollable (mult) {
160  const types = [
161    'ScrollView',
162    'Table',
163    'CollectionView',
164    'WebView'
165  ].map(t => `XCUIElementType${t}`);
166  const pred = types.map(t => `type == "${t}"`).join(' OR ');
167  const strategy = WDA_CLASS_CHAIN_STRATEGY;
168  let selector = '**/*[`' + pred + '`]';
169  if (!mult) {
170    selector += '[1]';
171  }
172  log.info('Rewrote request for scrollable descendants to class chain ' +
173           `format with selector '${selector}'`);
174  return [strategy, selector];
175}
176
177
178Object.assign(extensions, commands, helpers);
179export { commands, helpers};
180export default extensions;
181
Full Screen

Accelerate Your Automation Test Cycles With LambdaTest

Leverage LambdaTest’s cloud-based platform to execute your automation tests in parallel and trim down your test execution time significantly. Your first 100 automation testing minutes are on us.

Try LambdaTest

Run JavaScript Tests on LambdaTest Cloud Grid

Execute automation tests with Appium Xcuitest Driver on a cloud-based Grid of 3000+ real browsers and operating systems for both web and mobile applications.

Test now for Free
LambdaTestX

We use cookies to give you the best experience. Cookies help to provide a more personalized experience and relevant advertising for you, and web analytics for us. Learn More in our Cookies policy, Privacy & Terms of service

Allow Cookie
Sarah

I hope you find the best code examples for your project.

If you want to accelerate automated browser testing, try LambdaTest. Your first 100 automation testing minutes are FREE.

Sarah Elson (Product & Growth Lead)