How to use driver.clickCoords 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
_

web.js

Source: web.js Github

copy
1import { iosCommands } from 'appium-ios-driver';
2import { retryInterval } from 'asyncbox';
3import { util } from 'appium-support';
4import log from '../logger';
5import _ from 'lodash';
6import B from 'bluebird';
7
8const IPHONE_TOP_BAR_HEIGHT = 71;
9const IPHONE_SCROLLED_TOP_BAR_HEIGHT = 41;
10const IPHONE_X_NOTCH_OFFSET_IOS_12 = 24;
11const IPHONE_X_NOTCH_OFFSET_IOS_13 = 20;
12const IPHONE_LANDSCAPE_TOP_BAR_HEIGHT = 51;
13const IPHONE_BOTTOM_BAR_OFFSET = 49;
14const TAB_BAR_OFFSET = 33;
15const IPHONE_WEB_COORD_SMART_APP_BANNER_OFFSET = 84;
16const IPAD_WEB_COORD_SMART_APP_BANNER_OFFSET = 95;
17
18const IPHONE_X_WIDTH = 375;
19const IPHONE_X_HEIGHT = 812;
20const IPHONE_XR_WIDTH = 414;
21const IPHONE_XR_HEIGHT = 896;
22
23const ATOM_WAIT_TIMEOUT = 5 * 60000;
24
25let extensions = {};
26
27Object.assign(extensions, iosCommands.web);
28
29
30
31extensions.getSafariIsIphone = _.memoize(async function getSafariIsIphone () {
32  try {
33    const userAgent = await this.execute('return navigator.userAgent');
34    return userAgent.toLowerCase().includes('iphone');
35  } catch (err) {
36    log.warn(`Unable to find device type from useragent. Assuming iPhone`);
37    log.debug(`Error: ${err.message}`);
38  }
39  return true;
40});
41
42extensions.getSafariIsIphoneX = _.memoize(async function getSafariIsIphone () {
43  try {
44    const script = 'return {height: window.screen.availHeight, width: window.screen.availWidth};';
45    const {height, width} = await this.execute(script);
46    // check for the correct height and width
47    const [portraitHeight, portraitWidth] = height > width ? [height, width] : [width, height];
48    return (portraitHeight === IPHONE_X_HEIGHT && portraitWidth === IPHONE_X_WIDTH) ||
49           (portraitHeight === IPHONE_XR_HEIGHT && portraitWidth === IPHONE_XR_WIDTH);
50
51  } catch (err) {
52    log.warn(`Unable to find device type from dimensions. Assuming not iPhone X`);
53    log.debug(`Error: ${err.message}`);
54  }
55  return false;
56});
57
58extensions.getExtraTranslateWebCoordsOffset = async function getExtraTranslateWebCoordsOffset (wvPos, realDims) {
59  let topOffset = 0;
60  let bottomOffset = 0;
61
62  // keep track of implicit wait, and set locally to 0
63  const implicitWaitMs = this.implicitWaitMs;
64
65  const isIphone = await this.getSafariIsIphone();
66  const isIphoneX = isIphone && await this.getSafariIsIphoneX();
67
68  const orientation = realDims.h > realDims.w ? 'PORTRAIT' : 'LANDSCAPE';
69
70  const notchOffset = isIphoneX
71    ? util.compareVersions(this.opts.platformVersion, '<', '13.0')
72      ? IPHONE_X_NOTCH_OFFSET_IOS_12
73      : IPHONE_X_NOTCH_OFFSET_IOS_13
74    : 0;
75
76  try {
77    this.setImplicitWait(0);
78
79    // check if the full url bar is up
80    await this.findNativeElementOrElements('accessibility id', 'ReloadButton', false);
81
82    // reload button found, which means scrolling has not happened
83    topOffset = IPHONE_TOP_BAR_HEIGHT + notchOffset;
84    if (isIphone) {
85      if (orientation === 'PORTRAIT') {
86        // The bottom bar is only visible when portrait
87        bottomOffset = IPHONE_BOTTOM_BAR_OFFSET;
88      } else {
89        topOffset = IPHONE_LANDSCAPE_TOP_BAR_HEIGHT;
90      }
91    }
92    if (orientation === 'LANDSCAPE' || !isIphone) {
93      // Tabs only appear if the device is landscape or if it's an iPad so we only check visibility in this case
94      try {
95        await this.findNativeElementOrElements('-ios predicate string', `name LIKE '*, Tab' AND visible = 1`, false);
96        topOffset += TAB_BAR_OFFSET;
97      } catch (ign) {
98        // no element found, so no tabs and no need to deal with offset
99      }
100    }
101
102  } catch (err) {
103    // no reload button, which indicates scrolling has happened
104    topOffset = IPHONE_SCROLLED_TOP_BAR_HEIGHT + notchOffset;
105
106    // If the iPhone is landscape then there is not top bar
107    if (orientation === 'LANDSCAPE' && isIphone) {
108      topOffset = 0;
109    }
110
111  } finally {
112    // return implicit wait to what it was
113    this.setImplicitWait(implicitWaitMs);
114  }
115
116  topOffset += await this.getExtraNativeWebTapOffset();
117
118  wvPos.y += topOffset;
119  realDims.h -= (topOffset + bottomOffset);
120};
121
122extensions.getExtraNativeWebTapOffset = async function getExtraNativeWebTapOffset () {
123  let offset = 0;
124
125  // keep track of implicit wait, and set locally to 0
126  const implicitWaitMs = this.implicitWaitMs;
127  try {
128    this.setImplicitWait(0);
129
130    // try to see if there is an Smart App Banner
131    try {
132      await this.findNativeElementOrElements('accessibility id', 'Close app download offer', false);
133      offset += await this.getSafariIsIphone() ?
134        IPHONE_WEB_COORD_SMART_APP_BANNER_OFFSET :
135        IPAD_WEB_COORD_SMART_APP_BANNER_OFFSET;
136    } catch (ign) {
137      // no smart app banner found, so continue
138    }
139  } finally {
140    // return implicit wait to what it was
141    this.setImplicitWait(implicitWaitMs);
142  }
143
144  log.debug(`Additional native web tap offset computed: ${offset}`);
145  return offset;
146};
147
148async function tapWebElementNatively (driver, atomsElement) {
149  // try to get the text of the element, which will be accessible in the
150  // native context
151  try {
152    let text = await driver.executeAtom('get_text', [atomsElement]);
153    if (!text) {
154      text = await driver.executeAtom('get_attribute_value', [atomsElement, 'value']);
155    }
156
157    if (text) {
158      const els = await driver.findNativeElementOrElements('accessibility id', text, true);
159      if (els.length === 1 || els.length === 2) {
160        const el = els[0];
161        // use tap because on iOS 11.2 and below `nativeClick` crashes WDA
162        const rect = await driver.proxyCommand(`/element/${util.unwrapElement(el)}/rect`, 'GET');
163        if (els.length === 2) {
164          const el2 = els[1];
165          const rect2 = await driver.proxyCommand(`/element/${util.unwrapElement(el2)}/rect`, 'GET');
166
167          if ((rect.x !== rect2.x || rect.y !== rect2.y) ||
168          (rect.width !== rect2.width || rect.height !== rect2.height)) {
169            // These 2 native elements are not referring to the same web element
170            return false;
171          }
172        }
173        const coords = {
174          x: Math.round(rect.x + rect.width / 2),
175          y: Math.round(rect.y + rect.height / 2),
176        };
177        await driver.clickCoords(coords);
178        return true;
179      }
180    }
181  } catch (err) {
182    // any failure should fall through and trigger the more elaborate
183    // method of clicking
184    log.warn(`Error attempting to click: ${err.message}`);
185  }
186  return false;
187}
188
189extensions.nativeWebTap = async function nativeWebTap (el) {
190  const atomsElement = this.useAtomsElement(el);
191
192  if (await tapWebElementNatively(this, atomsElement)) {
193    return;
194  }
195  log.warn('Unable to do simple native web tap. Attempting to convert coordinates');
196
197  // `get_top_left_coordinates` returns the wrong value sometimes,
198  // unless we pre-call both of these functions before the actual calls
199  await this.executeAtom('get_size', [atomsElement]);
200  await this.executeAtom('get_top_left_coordinates', [atomsElement]);
201
202  const {width, height} = await this.executeAtom('get_size', [atomsElement]);
203  let {x, y} = await this.executeAtom('get_top_left_coordinates', [atomsElement]);
204  x += width / 2;
205  y += height / 2;
206
207  this.curWebCoords = {x, y};
208  await this.clickWebCoords();
209};
210
211extensions.clickCoords = async function clickCoords (coords) {
212  await this.performTouch([
213    {
214      action: 'tap',
215      options: coords,
216    },
217  ]);
218};
219
220extensions.translateWebCoords = async function translateWebCoords (coords) {
221  log.debug(`Translating coordinates (${JSON.stringify(coords)}) to web coordinates`);
222
223  // absolutize web coords
224  const implicitWaitMs = this.implicitWaitMs;
225  let webview;
226  try {
227    this.setImplicitWait(0);
228    webview = await retryInterval(5, 100, async () => {
229      return await this.findNativeElementOrElements('class name', 'XCUIElementTypeWebView', false);
230    });
231  } finally {
232    this.setImplicitWait(implicitWaitMs);
233  }
234
235  webview = util.unwrapElement(webview);
236  const rect = await this.proxyCommand(`/element/${webview}/rect`, 'GET');
237  const wvPos = {x: rect.x, y: rect.y};
238  const realDims = {w: rect.width, h: rect.height};
239
240  const cmd = '(function () { return {w: window.innerWidth, h: window.innerHeight}; })()';
241  const wvDims = await this.remote.execute(cmd);
242
243  await this.getExtraTranslateWebCoordsOffset(wvPos, realDims);
244
245  if (wvDims && realDims && wvPos) {
246    let xRatio = realDims.w / wvDims.w;
247    let yRatio = realDims.h / wvDims.h;
248    let newCoords = {
249      x: wvPos.x + Math.round(xRatio * coords.x),
250      y: wvPos.y + Math.round(yRatio * coords.y),
251    };
252
253    // additional logging for coordinates, since it is sometimes broken
254    //   see https://github.com/appium/appium/issues/9159
255    log.debug(`Converted coordinates: ${JSON.stringify(newCoords)}`);
256    log.debug(`    rect: ${JSON.stringify(rect)}`);
257    log.debug(`    wvPos: ${JSON.stringify(wvPos)}`);
258    log.debug(`    realDims: ${JSON.stringify(realDims)}`);
259    log.debug(`    wvDims: ${JSON.stringify(wvDims)}`);
260    log.debug(`    xRatio: ${JSON.stringify(xRatio)}`);
261    log.debug(`    yRatio: ${JSON.stringify(yRatio)}`);
262
263    log.debug(`Converted web coords ${JSON.stringify(coords)} ` +
264              `into real coords ${JSON.stringify(newCoords)}`);
265    return newCoords;
266  }
267};
268
269extensions.checkForAlert = async function checkForAlert () { // eslint-disable-line require-await
270  return false;
271};
272
273extensions.waitForAtom = async function waitForAtom (promise) {
274  const started = process.hrtime();
275  try {
276    return this.parseExecuteResponse(await B.resolve(promise)
277      .timeout(ATOM_WAIT_TIMEOUT));
278  } catch (err) {
279    if (err instanceof B.TimeoutError) {
280      throw new Error(`Did not get any response after ${process.hrtime(started)[0]}s`);
281    }
282    throw err;
283  }
284};
285
286export default extensions;
287
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)