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

certificate.js

Source: certificate.js Github

copy
1import _ from 'lodash';
2import { fs, plist, tempDir, util } from 'appium-support';
3import { iosCommands } from 'appium-ios-driver';
4import { openUrl } from 'node-simctl';
5import { retryInterval, retry, waitForCondition } from 'asyncbox';
6import B from 'bluebird';
7import log from '../logger';
8import os from 'os';
9import path from 'path';
10import http from 'http';
11import UUID from 'uuid-js';
12import { exec } from 'teen_process';
13import { findAPortNotInUse, checkPortStatus } from 'portscanner';
14
15let extensions = {}, commands = {};
16
17const CONFIG_EXTENSION = 'mobileconfig';
18const HOST_PORT_RANGE = [38200, 38299];
19const TMPSERVER_STARTUP_TIMEOUT = 5000;
20const Settings = {
21  General: {
22    type: 'accessibility id',
23    value: 'General',
24  },
25  Profile: {
26    type: '-ios predicate string',
27    value: `name BEGINSWITH 'Profile'`,
28  },
29  About: {
30    type: 'accessibility id',
31    value: 'About',
32  },
33  Certificate_Trust_Settings: {
34    type: 'accessibility id',
35    value: 'Certificate Trust Settings',
36  },
37};
38const Button = {
39  Install: {
40    type: 'accessibility id',
41    value: 'Install',
42  },
43  Allow: {
44    type: 'accessibility id',
45    value: 'Allow',
46  },
47  Done: {
48    type: 'accessibility id',
49    value: 'Done',
50  },
51  Return_to_Settings: {
52    type: 'accessibility id',
53    value: 'Return to Settings',
54  },
55};
56const Alert = {
57  Install: {
58    type: '-ios class chain',
59    value: '**/XCUIElementTypeAny[`type == \'XCUIElementTypeAlert\' OR type == \'XCUIElementTypeSheet\'`]/**/XCUIElementTypeButton[`label == \'Install\'`]',
60  },
61};
62
63
64async function extractCommonName (certBuffer) {
65  const tempCert = await tempDir.open({
66    prefix: 'cert',
67    suffix: '.cer'
68  });
69  try {
70    await fs.writeFile(tempCert.path, certBuffer);
71    const {stdout} = await exec('openssl', ['x509', '-noout', '-subject', '-in', tempCert.path]);
72    const cnMatch = /\/CN=([^\/]+)/.exec(stdout); // eslint-disable-line no-useless-escape
73    if (cnMatch) {
74      return cnMatch[1].trim();
75    }
76    throw new Error(`There is no common name value in '${stdout}' output`);
77  } catch (err) {
78    throw new Error(`Cannot parse common name value from the certificate. Is it valid and base64-encoded? ` +
79                    `Original error: ${err.message}`);
80  } finally {
81    await fs.rimraf(tempCert.path);
82  }
83}
84
85/**
86 * Generates Apple's over-the-air configuration profile
87 * for certificate deployment based on the given PEM certificate content.
88 * Read https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/iPhoneOTAConfiguration/Introduction/Introduction.html
89 * for more details on such profiles.
90 *
91 * @param {Buffer} certBuffer - The actual content of PEM certificate encoded into NodeJS buffer
92 * @param {string} commonName - Certificate's common name
93 * @returns {Object} The encoded structure of the given certificate, which is ready to be passed
94 * as an argument to plist builder
95 * @throws {Error} If the given certificate cannot be parsed
96 */
97function toMobileConfig (certBuffer, commonName) {
98  const getUUID = () => UUID.create().hex.toUpperCase();
99  const contentUuid = getUUID();
100  return {
101    PayloadContent: [{
102      PayloadCertificateFileName: `${commonName}.cer`,
103      PayloadContent: certBuffer,
104      PayloadDescription: 'Adds a CA root certificate',
105      PayloadDisplayName: commonName,
106      PayloadIdentifier: `com.apple.security.root.${contentUuid}`,
107      PayloadType: 'com.apple.security.root',
108      PayloadUUID: contentUuid,
109      PayloadVersion: 1
110    }],
111    PayloadDisplayName: commonName,
112    PayloadIdentifier: `${os.hostname().split('.')[0]}.${getUUID()}`,
113    PayloadRemovalDisallowed: false,
114    PayloadType: 'Configuration',
115    PayloadUUID: getUUID(),
116    PayloadVersion: 1
117  };
118}
119
120async function clickElement (driver, locator, options = {}) {
121  let element = null;
122  const {
123    timeout = 5000,
124    skipIfInvisible = false
125  } = options;
126  const lookupDelay = 500;
127  try {
128    element = await retryInterval(timeout < lookupDelay ? 1 : timeout / lookupDelay, lookupDelay,
129      () => driver.findNativeElementOrElements(locator.type, locator.value, false)
130    );
131  } catch (err) {
132    if (skipIfInvisible) {
133      return false;
134    }
135    throw new Error(`Cannot find ${JSON.stringify(locator)} within ${timeout}ms timeout`);
136  }
137  await driver.nativeClick(element);
138  return true;
139}
140
141async function installPre122Certificate (driver) {
142  // Accept Safari alert
143  await clickElement(driver, Button.Allow, {
144    // certificate load might take some time on slow machines
145    timeout: 15000,
146  });
147  // Wait until Preferences are opened
148  await B.delay(2000);
149
150  // Go through Preferences wizard
151  if (!await clickElement(driver, Button.Install, {
152    skipIfInvisible: true,
153  })) {
154    return false;
155  }
156  // We need to click Install button on two different tabs
157  // The second one confirms the previous
158  await B.delay(1500);
159  await clickElement(driver, Button.Install);
160  // Accept alert
161  await clickElement(driver, Alert.Install);
162  // Finish adding certificate
163  await clickElement(driver, Button.Done);
164  return true;
165}
166
167async function trustCertificateInPreferences (driver, name) {
168  await clickElement(driver, Settings.General);
169  await clickElement(driver, Settings.About);
170  const switchLocator = {
171    type: '-ios class chain',
172    value: `**/XCUIElementTypeCell[\`label == '${name}'\`]/**/XCUIElementTypeSwitch`,
173  };
174  await retry(5, async () => {
175    await driver.mobileSwipe({
176      element: await driver.findNativeElementOrElements('class name', 'XCUIElementTypeTable', false),
177      direction: 'up'
178    });
179    await clickElement(driver, Settings.Certificate_Trust_Settings, {
180      timeout: 500,
181    });
182
183    await driver.findNativeElementOrElements(switchLocator.type, switchLocator.value, false);
184  });
185  // Only click the switch if it is set to Off
186  if (await clickElement(driver, {
187    type: switchLocator.type,
188    value: `${switchLocator.value}[\`value == '0'\`]`
189  }, {
190    timeout: 1000,
191    skipIfInvisible: true,
192  })) {
193    await driver.postAcceptAlert();
194  }
195}
196
197async function installPost122Certificate (driver, name) {
198  // Accept Safari alert
199  await clickElement(driver, Button.Allow, {
200    // certificate load might take some time on slow machines
201    timeout: 15000,
202  });
203  // Wait for the second alert
204  await B.delay(2000);
205
206  await driver.postAcceptAlert();
207  await driver.activateApp('com.apple.Preferences');
208  await clickElement(driver, Settings.General);
209  await clickElement(driver, Settings.Profile);
210  // Select the target cert
211  let isCertFound = false;
212  for (let swipeNum = 0; swipeNum < 5; ++swipeNum) {
213    if (await clickElement(driver, {
214      type: '-ios class chain',
215      value: `**/XCUIElementTypeCell[\`label == '${name}'\`]`,
216    }, {
217      timeout: 500,
218      skipIfInvisible: true,
219    })) {
220      isCertFound = true;
221      break;
222    }
223
224    await driver.mobileSwipe({
225      element: await driver.findNativeElementOrElements('class name', 'XCUIElementTypeTable', false),
226      direction: 'up'
227    });
228  }
229  if (!isCertFound) {
230    throw new Error(`'${name}' cannot be found in the certificates list`);
231  }
232
233  // Install option is only visible if the cert is not installed yet
234  if (!await clickElement(driver, Button.Install, {
235    skipIfInvisible: true,
236  })) {
237    return false;
238  }
239  await B.delay(1500);
240  // Confirm untrusted cert install
241  await clickElement(driver, Button.Install);
242  // Accept alert
243  await clickElement(driver, Alert.Install);
244  // Finish adding certificate
245  await clickElement(driver, Button.Done);
246
247  return true;
248}
249
250/**
251 * @typedef {Object} CertificateInstallationOptions
252 *
253 * @property {!string} content - Base64-encoded content of the public certificate
254 * @property {?string} commonName - Common name of the certificate. If this is not set
255 *                                  then the script will try to parse it from the given
256 *                                  certificate content.
257 */
258
259/**
260 * Installs a custom certificate onto the device.
261 * Since Apple provides no official way to do it via command line,
262 * this method tries to wrap the certificate into .mobileconfig format
263 * and then deploys the wrapped file to the internal HTTP server,
264 * so one can open it with mobile Safari.
265 * Then the algorithm goes through the profile installation procedure by
266 * clicking the necessary buttons using WebDriverAgent.
267 *
268 * @param {CertificateInstallationOptions} opts
269 * @returns {string} The content of the generated .mobileconfig file as
270 * base64-encoded string. This config might be useful for debugging purposes.
271 */
272commands.mobileInstallCertificate = async function mobileInstallCertificate (opts = {}) {
273  const {content, commonName} = opts;
274  if (_.isEmpty(content)) {
275    throw new Error('Certificate content should not be empty');
276  }
277
278  const tmpRoot = await tempDir.openDir();
279  const tmpPort = await findAPortNotInUse(HOST_PORT_RANGE[0], HOST_PORT_RANGE[1]);
280  const configName = `appium.${CONFIG_EXTENSION}`;
281  const configPath = path.resolve(tmpRoot, configName);
282  const tmpServer = http.createServer(async function (_, res) {
283    const configFile = await fs.readFile(configPath);
284    res.end(configFile);
285  });
286  try {
287    const certBuffer = Buffer.from(content, 'base64');
288    const cn = commonName || await extractCommonName(certBuffer);
289    const mobileConfig = toMobileConfig(certBuffer, cn);
290    try {
291      await plist.updatePlistFile(configPath, mobileConfig, false, false);
292    } catch (err) {
293      throw new Error(`Cannot store the generated config as '${configPath}'. ` +
294                      `Original error: ${err.message}`);
295    }
296
297    try {
298      const host = os.hostname();
299      const certUrl = `http://${host}:${tmpPort}/${configName}`;
300      await tmpServer.listen(tmpPort);
301      try {
302        await waitForCondition(async () => {
303          try {
304            return (await checkPortStatus(tmpPort, host)) === 'open';
305          } catch (ign) {
306            return false;
307          }
308        }, {
309          waitMs: TMPSERVER_STARTUP_TIMEOUT,
310          intervalMs: 300,
311        });
312        log.debug(`The temporary web server is running at http://${host}:${tmpPort}`);
313      } catch (e) {
314        throw new Error(`The temporary web server cannot be started at http://${host}:${tmpPort}.`);
315      }
316      if (this.isRealDevice()) {
317        try {
318          await this.proxyCommand('/url', 'POST', {url: certUrl});
319        } catch (err) {
320          if (this.isWebContext()) {
321            // The command above does not always work on real devices
322            await iosCommands.general.setUrl.call(this, certUrl);
323          } else {
324            throw err;
325          }
326        }
327      } else {
328        await openUrl(this.opts.udid || this.sim.udid, certUrl);
329      }
330
331      let isCertAlreadyInstalled = false;
332      if (util.compareVersions(this.opts.platformVersion, '>=', '12.2')) {
333        if (await installPost122Certificate(this, cn)) {
334          await clickElement(this, Settings.Profile);
335          await trustCertificateInPreferences(this, cn);
336        } else {
337          isCertAlreadyInstalled = true;
338        }
339      } else {
340        if (await installPre122Certificate(this)) {
341          await clickElement(this, Button.Return_to_Settings);
342          await trustCertificateInPreferences(this, cn);
343        } else {
344          isCertAlreadyInstalled = true;
345        }
346      }
347      if (isCertAlreadyInstalled) {
348        log.info(`It looks like the '${cn}' certificate has been already added to the CA root`);
349      }
350    } finally {
351      try {
352        await this.activateApp(this.opts.bundleId);
353      } catch (e) {
354        log.warn(`Cannot restore the application '${this.opts.bundleId}'. Original error: ${e.message}`);
355      }
356    }
357
358    return (await fs.readFile(configPath)).toString('base64');
359  } finally {
360    await tmpServer.close();
361    await fs.rimraf(tmpRoot);
362  }
363};
364
365Object.assign(extensions, commands);
366export { commands };
367export default extensions;
368
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)