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