How to use helpers.createBaseADB method in Appium Android Driver

Best JavaScript code snippet using appium-android-driver

Run Appium Android Driver automation tests on LambdaTest cloud grid

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

ah1.js

Source: ah1.js Github

copy
1import _ from 'lodash';
2import path from 'path';
3import { exec } from 'teen_process';
4import { retry, retryInterval } from 'asyncbox';
5import logger from './logger';
6import { fs } from 'appium-support';
7import { path as unicodeIMEPath } from 'appium-android-ime';
8import { path as settingsApkPath } from 'io.appium.settings';
9import { path as unlockApkPath } from 'appium-unlock';
10import Bootstrap from 'appium-android-bootstrap';
11import B from 'bluebird';
12
13import ADB from 'appium-adb';
14import { default as unlocker, PIN_UNLOCK, PASSWORD_UNLOCK,
15  PATTERN_UNLOCK, FINGERPRINT_UNLOCK } from './unlock-helpers';
16
17const PACKAGE_INSTALL_TIMEOUT = 90000; // milliseconds
18const CHROME_BROWSER_PACKAGE_ACTIVITY = {
19  chrome: {
20    pkg: 'com.android.chrome',
21    activity: 'com.google.android.apps.chrome.Main',
22  },
23  chromium: {
24    pkg: 'org.chromium.chrome.shell',
25    activity: '.ChromeShellActivity',
26  },
27  chromebeta: {
28    pkg: 'com.chrome.beta',
29    activity: 'com.google.android.apps.chrome.Main',
30  },
31  browser: {
32    pkg: 'com.android.browser',
33    activity: 'com.android.browser.BrowserActivity',
34  },
35  'chromium-browser': {
36    pkg: 'org.chromium.chrome',
37    activity: 'com.google.android.apps.chrome.Main',
38  },
39  'chromium-webview': {
40    pkg: 'org.chromium.webview_shell',
41    activity: 'org.chromium.webview_shell.WebViewBrowserActivity',
42  },
43  default: {
44    pkg: 'com.android.chrome',
45    activity: 'com.google.android.apps.chrome.Main',
46  },
47};
48const SETTINGS_HELPER_PKG_ID = 'io.appium.settings';
49const SETTINGS_HELPER_PKG_ACTIVITY = ".Settings";
50const UNLOCK_HELPER_PKG_ID = 'io.appium.unlock';
51const UNLOCK_HELPER_PKG_ACTIVITY = ".Unlock";
52const UNICODE_IME_PKG_ID = 'io.appium.android.ime';
53
54let helpers = {};
55
56helpers.createBaseADB = async function (opts = {}) {
57  // filter out any unwanted options sent in
58  // this list should be updated as ADB takes more arguments
59  const {
60    javaVersion,
61    adbPort,
62    suppressKillServer,
63    remoteAdbHost,
64    clearDeviceLogsOnStart,
65    adbExecTimeout,
66  } = opts;
67  return await ADB.createADB({
68    javaVersion,
69    adbPort,
70    suppressKillServer,
71    remoteAdbHost,
72    clearDeviceLogsOnStart,
73    adbExecTimeout,
74  });
75};
76
77helpers.parseJavaVersion = function (stderr) {
78  let lines = stderr.split("\n");
79  for (let line of lines) {
80    if (new RegExp(/(java|openjdk) version/).test(line)) {
81      return line.split(" ")[2].replace(/"/g, '');
82    }
83  }
84  return null;
85};
86
87helpers.getJavaVersion = async function (logVersion = true) {
88  let {stderr} = await exec('java', ['-version']);
89  let javaVer = helpers.parseJavaVersion(stderr);
90  if (javaVer === null) {
91    throw new Error("Could not get the Java version. Is Java installed?");
92  }
93  if (logVersion) {
94    logger.info(`Java version is: ${javaVer}`);
95  }
96  return javaVer;
97};
98
99helpers.prepareEmulator = async function (adb, opts) {
100  let {avd, avdArgs, language, locale, avdLaunchTimeout,
101       avdReadyTimeout} = opts;
102  if (!avd) {
103    throw new Error("Cannot launch AVD without AVD name");
104  }
105  let avdName = avd.replace('@', '');
106  let runningAVD = await adb.getRunningAVD(avdName);
107  if (runningAVD !== null) {
108    if (avdArgs && avdArgs.toLowerCase().indexOf("-wipe-data") > -1) {
109      logger.debug(`Killing '${avdName}' because it needs to be wiped at start.`);
110      await adb.killEmulator(avdName);
111    } else {
112      logger.debug("Not launching AVD because it is already running.");
113      return;
114    }
115  }
116  avdArgs = this.prepareAVDArgs(opts, adb, avdArgs);
117  await adb.launchAVD(avd, avdArgs, language, locale, avdLaunchTimeout,
118                      avdReadyTimeout);
119};
120
121helpers.prepareAVDArgs = function (opts, adb, avdArgs) {
122  let args = avdArgs ? [avdArgs] : [];
123  if (!_.isUndefined(opts.networkSpeed)) {
124    let networkSpeed = this.ensureNetworkSpeed(adb, opts.networkSpeed);
125    args.push('-netspeed', networkSpeed);
126  }
127  if (opts.isHeadless) {
128    args.push('-no-window');
129  }
130  return args.join(' ');
131};
132
133helpers.ensureNetworkSpeed = function (adb, networkSpeed) {
134  if (_.values(adb.NETWORK_SPEED).indexOf(networkSpeed) !== -1) {
135    return networkSpeed;
136  }
137  logger.warn(`Wrong network speed param ${networkSpeed}, using default: full. Supported values: ${_.values(adb.NETWORK_SPEED)}`);
138  return adb.NETWORK_SPEED.FULL;
139};
140
141helpers.ensureDeviceLocale = async function (adb, language, country) {
142  if (!_.isString(language) && !_.isString(country)) {
143    logger.warn(`setDeviceLanguageCountry requires language or country.`);
144    logger.warn(`Got language: '${language}' and country: '${country}'`);
145    return;
146  }
147
148  await adb.setDeviceLanguageCountry(language, country);
149
150  if (!await adb.ensureCurrentLocale(language, country)) {
151    throw new Error(`Failed to set language: ${language} and country: ${country}`);
152  }
153};
154
155helpers.getDeviceInfoFromCaps = async function (opts = {}) {
156  // we can create a throwaway ADB instance here, so there is no dependency
157  // on instantiating on earlier (at this point, we have no udid)
158  // we can only use this ADB object for commands that would not be confused
159  // if multiple devices are connected
160  const adb = await helpers.createBaseADB(opts);
161  let udid = opts.udid;
162  let emPort = null;
163
164  // a specific avd name was given. try to initialize with that
165  if (opts.avd) {
166    await helpers.prepareEmulator(adb, opts);
167    udid = adb.curDeviceId;
168    emPort = adb.emulatorPort;
169  } else {
170    // no avd given. lets try whatever's plugged in devices/emulators
171    logger.info("Retrieving device list");
172    let devices = await adb.getDevicesWithRetry();
173
174    // udid was given, lets try to init with that device
175    if (udid) {
176      if (!_.includes(_.map(devices, 'udid'), udid)) {
177        logger.errorAndThrow(`Device ${udid} was not in the list ` +
178                             `of connected devices`);
179      }
180      emPort = adb.getPortFromEmulatorString(udid);
181    } else if (opts.platformVersion) {
182      opts.platformVersion = `${opts.platformVersion}`.trim();
183
184      // a platform version was given. lets try to find a device with the same os
185      logger.info(`Looking for a device with Android '${opts.platformVersion}'`);
186
187      // in case we fail to find something, give the user a useful log that has
188      // the device udids and os versions so they know what's available
189      let availDevicesStr = [];
190
191      // first try started devices/emulators
192      for (let device of devices) {
193        // direct adb calls to the specific device
194        await adb.setDeviceId(device.udid);
195        let deviceOS = await adb.getPlatformVersion();
196
197        // build up our info string of available devices as we iterate
198        availDevicesStr.push(`${device.udid} (${deviceOS})`);
199
200        // we do a begins with check for implied wildcard matching
201        // eg: 4 matches 4.1, 4.0, 4.1.3-samsung, etc
202        if (deviceOS.indexOf(opts.platformVersion) === 0) {
203          udid = device.udid;
204          break;
205        }
206      }
207
208      // we couldn't find anything! quit
209      if (!udid) {
210        logger.errorAndThrow(`Unable to find an active device or emulator ` +
211                             `with OS ${opts.platformVersion}. The following ` +
212                             `are available: ` + availDevicesStr.join(', '));
213      }
214
215      emPort = adb.getPortFromEmulatorString(udid);
216    } else {
217      // a udid was not given, grab the first device we see
218      udid = devices[0].udid;
219      emPort = adb.getPortFromEmulatorString(udid);
220    }
221  }
222
223  logger.info(`Using device: ${udid}`);
224  return {udid, emPort};
225};
226
227// returns a new adb instance with deviceId set
228helpers.createADB = async function (opts = {}) {
229  const {udid, emPort} = opts;
230  const adb = await helpers.createBaseADB(opts);
231  adb.setDeviceId(udid);
232  if (emPort) {
233    adb.setEmulatorPort(emPort);
234  }
235
236  return adb;
237};
238
239helpers.validatePackageActivityNames = function (opts) {
240  for (const key of ['appPackage', 'appActivity', 'appWaitPackage', 'appWaitActivity']) {
241    const name = opts[key];
242    if (!name) {
243      continue;
244    }
245
246    const match = /([^\w.*,])+/.exec(name);
247    if (!match) {
248      continue;
249    }
250
251    logger.warn(`Capability '${key}' is expected to only include latin letters, digits, underscore, dot, comma and asterisk characters.`);
252    logger.warn(`Current value '${name}' has non-matching character at index ${match.index}: '${name.substring(0, match.index + 1)}'`);
253  }
254};
255
256helpers.getLaunchInfo = async function (adb, opts) {
257  let {app, appPackage, appActivity, appWaitPackage, appWaitActivity} = opts;
258  if (!app) {
259    logger.warn("No app sent in, not parsing package/activity");
260    return;
261  }
262
263  this.validatePackageActivityNames(opts);
264
265  if (appPackage && appActivity) {
266    return;
267  }
268
269  logger.debug("Parsing package and activity from app manifest");
270  let {apkPackage, apkActivity} =
271    await adb.packageAndLaunchActivityFromManifest(app);
272  if (apkPackage && !appPackage) {
273    appPackage = apkPackage;
274  }
275  if (!appWaitPackage) {
276    appWaitPackage = appPackage;
277  }
278  if (apkActivity && !appActivity) {
279    appActivity = apkActivity;
280  }
281  if (!appWaitActivity) {
282    appWaitActivity = appActivity;
283  }
284  logger.debug(`Parsed package and activity are: ${apkPackage}/${apkActivity}`);
285  return {appPackage, appWaitPackage, appActivity, appWaitActivity};
286};
287
288helpers.resetApp = async function (adb, opts = {}) {
289  const {
290    app,
291    appPackage,
292    fastReset,
293    fullReset,
294    androidInstallTimeout = PACKAGE_INSTALL_TIMEOUT,
295    autoGrantPermissions,
296    allowTestPackages
297  } = opts;
298
299  if (!appPackage) {
300    throw new Error("'appPackage' option is required");
301  }
302
303  const isInstalled = await adb.isAppInstalled(appPackage);
304
305  if (isInstalled) {
306    try {
307      await adb.forceStop(appPackage);
308    } catch (ign) {}
309    // fullReset has priority over fastReset
310    if (!fullReset && fastReset) {
311      const output = await adb.clear(appPackage);
312      if (_.isString(output) && output.toLowerCase().includes('failed')) {
313        throw new Error(`Cannot clear the application data of '${appPackage}'. Original error: ${output}`);
314      }
315      // executing `shell pm clear` resets previously assigned application permissions as well
316      if (autoGrantPermissions) {
317        try {
318          await adb.grantAllPermissions(appPackage);
319        } catch (error) {
320          logger.error(`Unable to grant permissions requested. Original error: ${error.message}`);
321        }
322      }
323      logger.debug(`Performed fast reset on the installed '${appPackage}' application (stop and clear)`);
324      return;
325    }
326  }
327
328  if (!app) {
329    throw new Error("'app' option is required for reinstall");
330  }
331
332  logger.debug(`Running full reset on '${appPackage}' (reinstall)`);
333  if (isInstalled) {
334    await adb.uninstallApk(appPackage);
335  }
336  await adb.install(app, {
337    grantPermissions: autoGrantPermissions,
338    timeout: androidInstallTimeout,
339    allowTestPackages,
340  });
341};
342
343helpers.installApk = async function (adb, opts = {}) {
344  const {
345    app,
346    appPackage,
347    fastReset,
348    fullReset,
349    androidInstallTimeout = PACKAGE_INSTALL_TIMEOUT,
350    autoGrantPermissions,
351    allowTestPackages
352  } = opts;
353
354  if (!app || !appPackage) {
355    throw new Error("'app' and 'appPackage' options are required");
356  }
357
358  if (fullReset) {
359    await this.resetApp(adb, opts);
360    return;
361  }
362
363  // There is no need to reset the newly installed app
364  const shouldPerformFastReset = fastReset && await adb.isAppInstalled(appPackage);
365
366  await adb.installOrUpgrade(app, appPackage, {
367    grantPermissions: autoGrantPermissions,
368    timeout: androidInstallTimeout,
369    allowTestPackages,
370  });
371
372  if (shouldPerformFastReset) {
373    logger.info(`Performing fast reset on '${appPackage}'`);
374    await this.resetApp(adb, opts);
375  }
376};
377
378/**
379 * Installs an array of apks
380 * @param {ADB} adb Instance of Appium ADB object
381 * @param {Object} opts Opts defined in driver.js
382 */
383helpers.installOtherApks = async function (otherApps, adb, opts) {
384  let {
385    androidInstallTimeout = PACKAGE_INSTALL_TIMEOUT,
386    autoGrantPermissions,
387    allowTestPackages
388  } = opts;
389
390  // Install all of the APK's asynchronously
391  await B.all(otherApps.map((otherApp) => {
392    logger.debug(`Installing app: ${otherApp}`);
393    return adb.installOrUpgrade(otherApp, null, {
394      grantPermissions: autoGrantPermissions,
395      timeout: androidInstallTimeout,
396      allowTestPackages,
397    });
398  }));
399};
400
401helpers.initUnicodeKeyboard = async function (adb) {
402  logger.debug('Enabling Unicode keyboard support');
403  logger.debug("Pushing unicode ime to device...");
404  try {
405    await adb.install(unicodeIMEPath, {replace: false});
406  } catch (err) {
407    logger.info(`Performing full reinstall of ${UNICODE_IME_PKG_ID} as a possible fix for: ${err.message}`);
408    await adb.uninstallApk(UNICODE_IME_PKG_ID);
409    await adb.install(unicodeIMEPath, {replace: false});
410  }
411
412  // get the default IME so we can return back to it later if we want
413  let defaultIME = await adb.defaultIME();
414
415  logger.debug(`Unsetting previous IME ${defaultIME}`);
416  const appiumIME = `${UNICODE_IME_PKG_ID}/.UnicodeIME`;
417  logger.debug(`Setting IME to '${appiumIME}'`);
418  await adb.enableIME(appiumIME);
419  await adb.setIME(appiumIME);
420  return defaultIME;
421};
422
423helpers.setMockLocationApp = async function (adb, app) {
424  try {
425    if (await adb.getApiLevel() < 23) {
426      await adb.shell(['settings', 'put', 'secure', 'mock_location', '1']);
427    } else {
428      await adb.shell(['appops', 'set', app, 'android:mock_location', 'allow']);
429    }
430  } catch (err) {
431    logger.warn(`Unable to set mock location for app '${app}': ${err.message}`);
432  }
433};
434
435helpers.installHelperApp = async function (adb, apkPath, packageId, appName) {
436  try {
437    await adb.installOrUpgrade(apkPath, packageId, {grantPermissions: true});
438  } catch (err) {
439    logger.warn(`Ignored error while installing Appium ${appName} helper: ` +
440                `'${err.message}'. Manually uninstalling the application ` +
441                `with package id '${packageId}' may help. Expect some Appium ` +
442                `features may not work as expected unless this problem is ` +
443                `fixed.`);
444  }
445};
446
447helpers.pushSettingsApp = async function (adb, throwError = false) {
448  logger.debug("Pushing settings apk to device...");
449
450  await helpers.installHelperApp(adb, settingsApkPath, SETTINGS_HELPER_PKG_ID, 'Settings');
451
452  // Reinstall will stop the settings helper process anyway, so
453  // there is no need to continue if the application is still running
454  if (await adb.processExists(SETTINGS_HELPER_PKG_ID)) {
455    logger.debug(`${SETTINGS_HELPER_PKG_ID} is already running. ` +
456                 `There is no need to reset its permissions.`);
457    return;
458  }
459
460  // lauch io.appium.settings app due to settings failing to be set
461  // if the app is not launched prior to start the session on android 7+
462  // see https://github.com/appium/appium/issues/8957
463  try {
464    await adb.startApp({
465      pkg: SETTINGS_HELPER_PKG_ID,
466      activity: SETTINGS_HELPER_PKG_ACTIVITY,
467      action: "android.intent.action.MAIN",
468      category: "android.intent.category.LAUNCHER",
469      flags: "0x10200000",
470      stopApp: false,
471    });
472  } catch (err) {
473    logger.warn(`Failed to launch settings app: ${err.message}`);
474    if (throwError) {
475      throw err;
476    }
477  }
478};
479
480helpers.pushUnlock = async function (adb) {
481  logger.debug("Pushing unlock helper app to device...");
482
483  await helpers.installHelperApp(adb, unlockApkPath, UNLOCK_HELPER_PKG_ID, 'Unlock');
484};
485
486/**
487 * Extracts string.xml and converts it to string.json and pushes
488 * it to /data/local/tmp/string.json on for use of bootstrap
489 * If app is not present to extract string.xml it deletes remote strings.json
490 * If app does not have strings.xml we push an empty json object to remote
491 *
492 * @param {?string} language - Language abbreviation, for example 'fr'. The default language
493 * is used if this argument is not defined.
494 * @param {Object} adb - The adb mofdule instance.
495 * @param {Object} opts - Driver options dictionary.
496 * @returns {Object} The dictionary, where string resourtces identifiers are keys
497 * along with their corresponding values for the given language or an empty object
498 * if no matching resources were extracted.
499 */
500helpers.pushStrings = async function (language, adb, opts) {
501  const remoteDir = '/data/local/tmp';
502  const stringsJson = 'strings.json';
503  const remoteFile = `${remoteDir}/${stringsJson}`;
504
505  // clean up remote string.json if present
506  await adb.rimraf(remoteFile);
507
508  if (_.isEmpty(opts.appPackage) || !(await fs.exists(opts.app))) {
509    return {};
510  }
511
512  const stringsTmpDir = path.resolve(opts.tmpDir, opts.appPackage);
513  try {
514    logger.debug('Extracting strings from apk', opts.app, language, stringsTmpDir);
515    const {apkStrings, localPath} = await adb.extractStringsFromApk(opts.app, language, stringsTmpDir);
516    await adb.push(localPath, remoteDir);
517    return apkStrings;
518  } catch (err) {
519    logger.warn(`Could not get strings, continuing anyway. Original error: ${err.message}`);
520    await adb.shell('echo', [`'{}' > ${remoteFile}`]);
521  } finally {
522    await fs.rimraf(stringsTmpDir);
523  }
524  return {};
525};
526
527helpers.unlockWithUIAutomation = async function (driver, adb, unlockCapabilities) {
528  let unlockType = unlockCapabilities.unlockType;
529  if (!unlocker.isValidUnlockType(unlockType)) {
530    throw new Error(`Invalid unlock type ${unlockType}`);
531  }
532  let unlockKey = unlockCapabilities.unlockKey;
533  if (!unlocker.isValidKey(unlockType, unlockKey)) {
534    throw new Error(`Missing unlockKey ${unlockKey} capability for unlockType ${unlockType}`);
535  }
536  const unlockMethod = {
537    [PIN_UNLOCK]: unlocker.pinUnlock,
538    [PASSWORD_UNLOCK]: unlocker.passwordUnlock,
539    [PATTERN_UNLOCK]: unlocker.patternUnlock,
540    [FINGERPRINT_UNLOCK]: unlocker.fingerprintUnlock
541  }[unlockType];
542  await unlockMethod(adb, driver, unlockCapabilities);
543};
544
545helpers.unlockWithHelperApp = async function (adb) {
546  logger.info("Unlocking screen");
547
548  try {
549    await adb.forceStop(UNLOCK_HELPER_PKG_ID);
550  } catch (e) {
551    // Sometimes we can see the below error, but we can ignore it.
552    // [W3C] Encountered internal error running command: Error: Error executing adbExec. Original error: 'Command 'adb -P 5037 -s emulator-5554 shell am force-stop io.appium.unlock' timed out after 20000ms'; Stderr: ''; Code: 'null'
553    logger.warn(`An error in unlockWithHelperApp: ${e.message}`);
554  }
555
556  let startOpts = {
557    pkg: UNLOCK_HELPER_PKG_ID,
558    activity: UNLOCK_HELPER_PKG_ACTIVITY,
559    action: "android.intent.action.MAIN",
560    category: "android.intent.category.LAUNCHER",
561    flags: "0x10200000",
562    stopApp: false,
563    retry: false,
564    waitDuration: 1000
565  };
566
567  // Unlock succeed with a couple of retries.
568  let firstRun = true;
569  await retry(3, async function () {
570    // To reduce a time to call adb.isScreenLocked() since `adb shell dumpsys window` is easy to hang adb commands
571    if (firstRun) {
572      firstRun = false;
573    } else {
574      try {
575        if (!(await adb.isScreenLocked())) {
576          return;
577        }
578      } catch (e) {
579        logger.warn(`Error in isScreenLocked: ${e.message}`);
580        logger.warn("\"adb shell dumpsys window\" command has timed out.");
581        logger.warn("The reason of this timeout is the delayed adb response. Resetting adb server can improve it.");
582      }
583    }
584
585    logger.info(`Launching ${UNLOCK_HELPER_PKG_ID}`);
586
587    // The command takes too much time so we should not call the command over twice continuously.
588    await adb.startApp(startOpts);
589  });
590};
591
592helpers.unlock = async function (driver, adb, capabilities) {
593  if (!(await adb.isScreenLocked())) {
594    logger.info("Screen already unlocked, doing nothing");
595    return;
596  }
597
598  logger.debug("Screen is locked, trying to unlock");
599  if (_.isUndefined(capabilities.unlockType)) {
600    logger.warn("Using app unlock, this is going to be deprecated!");
601    await helpers.unlockWithHelperApp(adb);
602  } else {
603    await helpers.unlockWithUIAutomation(driver, adb, {unlockType: capabilities.unlockType, unlockKey: capabilities.unlockKey});
604    await helpers.verifyUnlock(adb);
605  }
606};
607
608helpers.verifyUnlock = async function (adb) {
609  await retryInterval(2, 1000, async () => {
610    if (await adb.isScreenLocked()) {
611      throw new Error("Screen did not unlock successfully, retrying");
612    }
613    logger.debug("Screen unlocked successfully");
614  });
615};
616
617helpers.initDevice = async function (adb, opts) {
618  await adb.waitForDevice();
619  // pushSettingsApp required before calling ensureDeviceLocale for API Level 24+
620  await helpers.pushSettingsApp(adb);
621  if (!opts.avd) {
622    await helpers.setMockLocationApp(adb, SETTINGS_HELPER_PKG_ID);
623  }
624
625  await helpers.ensureDeviceLocale(adb, opts.language, opts.locale);
626  await adb.startLogcat();
627  let defaultIME;
628  if (opts.unicodeKeyboard) {
629    defaultIME = await helpers.initUnicodeKeyboard(adb);
630  }
631  if (_.isUndefined(opts.unlockType)) {
632    await helpers.pushUnlock(adb);
633  }
634  return defaultIME;
635};
636
637helpers.removeNullProperties = function (obj) {
638  for (let key of _.keys(obj)) {
639    if (_.isNull(obj[key]) || _.isUndefined(obj[key])) {
640      delete obj[key];
641    }
642  }
643};
644
645helpers.truncateDecimals = function (number, digits) {
646  let multiplier = Math.pow(10, digits),
647      adjustedNum = number * multiplier,
648      truncatedNum = Math[adjustedNum < 0 ? 'ceil' : 'floor'](adjustedNum);
649
650  return truncatedNum / multiplier;
651};
652
653helpers.isChromeBrowser = function (browser) {
654  return _.includes(Object.keys(CHROME_BROWSER_PACKAGE_ACTIVITY), (browser || '').toLowerCase());
655};
656
657helpers.getChromePkg = function (browser) {
658  return CHROME_BROWSER_PACKAGE_ACTIVITY[browser.toLowerCase()] ||
659         CHROME_BROWSER_PACKAGE_ACTIVITY.default;
660};
661
662helpers.removeAllSessionWebSocketHandlers = async function (server, sessionId) {
663  if (!server || !_.isFunction(server.getWebSocketHandlers)) {
664    return;
665  }
666
667  const activeHandlers = await server.getWebSocketHandlers(sessionId);
668  for (const pathname of _.keys(activeHandlers)) {
669    await server.removeWebSocketHandler(pathname);
670  }
671};
672
673/**
674 * Takes a desired capability and tries to JSON.parse it as an array,
675 * and either returns the parsed array or a singleton array.
676 *
677 * @param {any} cap A desired capability
678 */
679helpers.parseArray = function (cap) {
680  let parsedCaps;
681  try {
682    parsedCaps = JSON.parse(cap);
683  } catch (ign) { }
684
685  if (_.isArray(parsedCaps)) {
686    return parsedCaps;
687  } else if (_.isString(cap)) {
688    return [cap];
689  }
690
691  throw new Error(`must provide a string or JSON Array; received ${cap}`);
692};
693
694helpers.validateDesiredCaps = function (caps) {
695  // make sure that the capabilities have one of `app`, `appPackage` or `browser`
696  if ((!caps.browserName || !this.isChromeBrowser(caps.browserName)) && !caps.app && !caps.appPackage) {
697    logger.errorAndThrow('The desired capabilities must include either an app, appPackage or browserName');
698  }
699  if (caps.browserName) {
700    if (caps.app) {
701      // warn if the capabilities have both `app` and `browser, although this is common with selenium grid
702      logger.warn('The desired capabilities should generally not include both an app and a browserName');
703    }
704    if (caps.appPackage) {
705      logger.errorAndThrow(`The desired capabilities must include either 'appPackage' or 'browserName'`);
706    }
707  }
708
709  return true;
710};
711
712helpers.bootstrap = Bootstrap;
713helpers.unlocker = unlocker;
714
715export default helpers;
716
Full Screen

android-helpers.js

Source: android-helpers.js Github

copy
1import _ from 'lodash';
2import path from 'path';
3import { exec } from 'teen_process';
4import { retry, retryInterval } from 'asyncbox';
5import logger from './logger';
6import { fs } from 'appium-support';
7import { path as unicodeIMEPath } from 'appium-android-ime';
8import { path as settingsApkPath } from 'io.appium.settings';
9import { path as unlockApkPath } from 'appium-unlock';
10import Bootstrap from 'appium-android-bootstrap';
11import B from 'bluebird';
12
13import ADB from 'appium-adb';
14import { default as unlocker, PIN_UNLOCK, PASSWORD_UNLOCK,
15  PATTERN_UNLOCK, FINGERPRINT_UNLOCK } from './unlock-helpers';
16
17const PACKAGE_INSTALL_TIMEOUT = 90000; // milliseconds
18const CHROME_BROWSER_PACKAGE_ACTIVITY = {
19  chrome: {
20    pkg: 'com.android.chrome',
21    activity: 'com.google.android.apps.chrome.Main',
22  },
23  chromium: {
24    pkg: 'org.chromium.chrome.shell',
25    activity: '.ChromeShellActivity',
26  },
27  chromebeta: {
28    pkg: 'com.chrome.beta',
29    activity: 'com.google.android.apps.chrome.Main',
30  },
31  browser: {
32    pkg: 'com.android.browser',
33    activity: 'com.android.browser.BrowserActivity',
34  },
35  'chromium-browser': {
36    pkg: 'org.chromium.chrome',
37    activity: 'com.google.android.apps.chrome.Main',
38  },
39  'chromium-webview': {
40    pkg: 'org.chromium.webview_shell',
41    activity: 'org.chromium.webview_shell.WebViewBrowserActivity',
42  },
43  default: {
44    pkg: 'com.android.chrome',
45    activity: 'com.google.android.apps.chrome.Main',
46  },
47};
48const SETTINGS_HELPER_PKG_ID = 'io.appium.settings';
49const SETTINGS_HELPER_PKG_ACTIVITY = ".Settings";
50const UNLOCK_HELPER_PKG_ID = 'io.appium.unlock';
51const UNLOCK_HELPER_PKG_ACTIVITY = ".Unlock";
52const UNICODE_IME_PKG_ID = 'io.appium.android.ime';
53
54let helpers = {};
55
56helpers.createBaseADB = async function (opts = {}) {
57  // filter out any unwanted options sent in
58  // this list should be updated as ADB takes more arguments
59  const {
60    javaVersion,
61    adbPort,
62    suppressKillServer,
63    remoteAdbHost,
64    clearDeviceLogsOnStart,
65    adbExecTimeout,
66  } = opts;
67  return await ADB.createADB({
68    javaVersion,
69    adbPort,
70    suppressKillServer,
71    remoteAdbHost,
72    clearDeviceLogsOnStart,
73    adbExecTimeout,
74  });
75};
76
77helpers.parseJavaVersion = function (stderr) {
78  let lines = stderr.split("\n");
79  for (let line of lines) {
80    if (new RegExp(/(java|openjdk) version/).test(line)) {
81      return line.split(" ")[2].replace(/"/g, '');
82    }
83  }
84  return null;
85};
86
87helpers.getJavaVersion = async function (logVersion = true) {
88  let {stderr} = await exec('java', ['-version']);
89  let javaVer = helpers.parseJavaVersion(stderr);
90  if (javaVer === null) {
91    throw new Error("Could not get the Java version. Is Java installed?");
92  }
93  if (logVersion) {
94    logger.info(`Java version is: ${javaVer}`);
95  }
96  return javaVer;
97};
98
99helpers.prepareEmulator = async function (adb, opts) {
100  let {avd, avdArgs, language, locale, avdLaunchTimeout,
101       avdReadyTimeout} = opts;
102  if (!avd) {
103    throw new Error("Cannot launch AVD without AVD name");
104  }
105  let avdName = avd.replace('@', '');
106  let runningAVD = await adb.getRunningAVD(avdName);
107  if (runningAVD !== null) {
108    if (avdArgs && avdArgs.toLowerCase().indexOf("-wipe-data") > -1) {
109      logger.debug(`Killing '${avdName}' because it needs to be wiped at start.`);
110      await adb.killEmulator(avdName);
111    } else {
112      logger.debug("Not launching AVD because it is already running.");
113      return;
114    }
115  }
116  avdArgs = this.prepareAVDArgs(opts, adb, avdArgs);
117  await adb.launchAVD(avd, avdArgs, language, locale, avdLaunchTimeout,
118                      avdReadyTimeout);
119};
120
121helpers.prepareAVDArgs = function (opts, adb, avdArgs) {
122  let args = avdArgs ? [avdArgs] : [];
123  if (!_.isUndefined(opts.networkSpeed)) {
124    let networkSpeed = this.ensureNetworkSpeed(adb, opts.networkSpeed);
125    args.push('-netspeed', networkSpeed);
126  }
127  if (opts.isHeadless) {
128    args.push('-no-window');
129  }
130  return args.join(' ');
131};
132
133helpers.ensureNetworkSpeed = function (adb, networkSpeed) {
134  if (_.values(adb.NETWORK_SPEED).indexOf(networkSpeed) !== -1) {
135    return networkSpeed;
136  }
137  logger.warn(`Wrong network speed param ${networkSpeed}, using default: full. Supported values: ${_.values(adb.NETWORK_SPEED)}`);
138  return adb.NETWORK_SPEED.FULL;
139};
140
141helpers.ensureDeviceLocale = async function (adb, language, country) {
142  if (!_.isString(language) && !_.isString(country)) {
143    logger.warn(`setDeviceLanguageCountry requires language or country.`);
144    logger.warn(`Got language: '${language}' and country: '${country}'`);
145    return;
146  }
147
148  await adb.setDeviceLanguageCountry(language, country);
149
150  if (!await adb.ensureCurrentLocale(language, country)) {
151    throw new Error(`Failed to set language: ${language} and country: ${country}`);
152  }
153};
154
155helpers.getDeviceInfoFromCaps = async function (opts = {}) {
156  // we can create a throwaway ADB instance here, so there is no dependency
157  // on instantiating on earlier (at this point, we have no udid)
158  // we can only use this ADB object for commands that would not be confused
159  // if multiple devices are connected
160  const adb = await helpers.createBaseADB(opts);
161  let udid = opts.udid;
162  let emPort = null;
163
164  // a specific avd name was given. try to initialize with that
165  if (opts.avd) {
166    await helpers.prepareEmulator(adb, opts);
167    udid = adb.curDeviceId;
168    emPort = adb.emulatorPort;
169  } else {
170    // no avd given. lets try whatever's plugged in devices/emulators
171    logger.info("Retrieving device list");
172    let devices = await adb.getDevicesWithRetry();
173
174    // udid was given, lets try to init with that device
175    if (udid) {
176      if (!_.includes(_.map(devices, 'udid'), udid)) {
177        logger.errorAndThrow(`Device ${udid} was not in the list ` +
178                             `of connected devices`);
179      }
180      emPort = adb.getPortFromEmulatorString(udid);
181    } else if (opts.platformVersion) {
182      opts.platformVersion = `${opts.platformVersion}`.trim();
183
184      // a platform version was given. lets try to find a device with the same os
185      logger.info(`Looking for a device with Android '${opts.platformVersion}'`);
186
187      // in case we fail to find something, give the user a useful log that has
188      // the device udids and os versions so they know what's available
189      let availDevicesStr = [];
190
191      // first try started devices/emulators
192      for (let device of devices) {
193        // direct adb calls to the specific device
194        await adb.setDeviceId(device.udid);
195        let deviceOS = await adb.getPlatformVersion();
196
197        // build up our info string of available devices as we iterate
198        availDevicesStr.push(`${device.udid} (${deviceOS})`);
199
200        // we do a begins with check for implied wildcard matching
201        // eg: 4 matches 4.1, 4.0, 4.1.3-samsung, etc
202        if (deviceOS.indexOf(opts.platformVersion) === 0) {
203          udid = device.udid;
204          break;
205        }
206      }
207
208      // we couldn't find anything! quit
209      if (!udid) {
210        logger.errorAndThrow(`Unable to find an active device or emulator ` +
211                             `with OS ${opts.platformVersion}. The following ` +
212                             `are available: ` + availDevicesStr.join(', '));
213      }
214
215      emPort = adb.getPortFromEmulatorString(udid);
216    } else {
217      // a udid was not given, grab the first device we see
218      udid = devices[0].udid;
219      emPort = adb.getPortFromEmulatorString(udid);
220    }
221  }
222
223  logger.info(`Using device: ${udid}`);
224  return {udid, emPort};
225};
226
227// returns a new adb instance with deviceId set
228helpers.createADB = async function (opts = {}) {
229  const {udid, emPort} = opts;
230  const adb = await helpers.createBaseADB(opts);
231  adb.setDeviceId(udid);
232  if (emPort) {
233    adb.setEmulatorPort(emPort);
234  }
235
236  return adb;
237};
238
239helpers.validatePackageActivityNames = function (opts) {
240  for (const key of ['appPackage', 'appActivity', 'appWaitPackage', 'appWaitActivity']) {
241    const name = opts[key];
242    if (!name) {
243      continue;
244    }
245
246    const match = /([^\w.*,])+/.exec(name);
247    if (!match) {
248      continue;
249    }
250
251    logger.warn(`Capability '${key}' is expected to only include latin letters, digits, underscore, dot, comma and asterisk characters.`);
252    logger.warn(`Current value '${name}' has non-matching character at index ${match.index}: '${name.substring(0, match.index + 1)}'`);
253  }
254};
255
256helpers.getLaunchInfo = async function (adb, opts) {
257  let {app, appPackage, appActivity, appWaitPackage, appWaitActivity} = opts;
258  if (!app) {
259    logger.warn("No app sent in, not parsing package/activity");
260    return;
261  }
262
263  this.validatePackageActivityNames(opts);
264
265  if (appPackage && appActivity) {
266    return;
267  }
268
269  logger.debug("Parsing package and activity from app manifest");
270  let {apkPackage, apkActivity} =
271    await adb.packageAndLaunchActivityFromManifest(app);
272  if (apkPackage && !appPackage) {
273    appPackage = apkPackage;
274  }
275  if (!appWaitPackage) {
276    appWaitPackage = appPackage;
277  }
278  if (apkActivity && !appActivity) {
279    appActivity = apkActivity;
280  }
281  if (!appWaitActivity) {
282    appWaitActivity = appActivity;
283  }
284  logger.debug(`Parsed package and activity are: ${apkPackage}/${apkActivity}`);
285  return {appPackage, appWaitPackage, appActivity, appWaitActivity};
286};
287
288helpers.resetApp = async function (adb, opts = {}) {
289  const {
290    app,
291    appPackage,
292    fastReset,
293    fullReset,
294    androidInstallTimeout = PACKAGE_INSTALL_TIMEOUT,
295    autoGrantPermissions,
296    allowTestPackages
297  } = opts;
298
299  if (!appPackage) {
300    throw new Error("'appPackage' option is required");
301  }
302
303  const isInstalled = await adb.isAppInstalled(appPackage);
304
305  if (isInstalled) {
306    try {
307      await adb.forceStop(appPackage);
308    } catch (ign) {}
309    // fullReset has priority over fastReset
310    if (!fullReset && fastReset) {
311      const output = await adb.clear(appPackage);
312      if (_.isString(output) && output.toLowerCase().includes('failed')) {
313        throw new Error(`Cannot clear the application data of '${appPackage}'. Original error: ${output}`);
314      }
315      // executing `shell pm clear` resets previously assigned application permissions as well
316      if (autoGrantPermissions) {
317        try {
318          await adb.grantAllPermissions(appPackage);
319        } catch (error) {
320          logger.error(`Unable to grant permissions requested. Original error: ${error.message}`);
321        }
322      }
323      logger.debug(`Performed fast reset on the installed '${appPackage}' application (stop and clear)`);
324      return;
325    }
326  }
327
328  if (!app) {
329    throw new Error("'app' option is required for reinstall");
330  }
331
332  logger.debug(`Running full reset on '${appPackage}' (reinstall)`);
333  if (isInstalled) {
334    await adb.uninstallApk(appPackage);
335  }
336  await adb.install(app, {
337    grantPermissions: autoGrantPermissions,
338    timeout: androidInstallTimeout,
339    allowTestPackages,
340  });
341};
342
343helpers.installApk = async function (adb, opts = {}) {
344  const {
345    app,
346    appPackage,
347    fastReset,
348    fullReset,
349    androidInstallTimeout = PACKAGE_INSTALL_TIMEOUT,
350    autoGrantPermissions,
351    allowTestPackages
352  } = opts;
353
354  if (!app || !appPackage) {
355    throw new Error("'app' and 'appPackage' options are required");
356  }
357
358  if (fullReset) {
359    await this.resetApp(adb, opts);
360    return;
361  }
362
363  // There is no need to reset the newly installed app
364  const shouldPerformFastReset = fastReset && await adb.isAppInstalled(appPackage);
365
366  await adb.installOrUpgrade(app, appPackage, {
367    grantPermissions: autoGrantPermissions,
368    timeout: androidInstallTimeout,
369    allowTestPackages,
370  });
371
372  if (shouldPerformFastReset) {
373    logger.info(`Performing fast reset on '${appPackage}'`);
374    await this.resetApp(adb, opts);
375  }
376};
377
378/**
379 * Installs an array of apks
380 * @param {ADB} adb Instance of Appium ADB object
381 * @param {Object} opts Opts defined in driver.js
382 */
383helpers.installOtherApks = async function (otherApps, adb, opts) {
384  let {
385    androidInstallTimeout = PACKAGE_INSTALL_TIMEOUT,
386    autoGrantPermissions,
387    allowTestPackages
388  } = opts;
389
390  // Install all of the APK's asynchronously
391  await B.all(otherApps.map((otherApp) => {
392    logger.debug(`Installing app: ${otherApp}`);
393    return adb.installOrUpgrade(otherApp, null, {
394      grantPermissions: autoGrantPermissions,
395      timeout: androidInstallTimeout,
396      allowTestPackages,
397    });
398  }));
399};
400
401helpers.initUnicodeKeyboard = async function (adb) {
402  logger.debug('Enabling Unicode keyboard support');
403  logger.debug("Pushing unicode ime to device...");
404  try {
405//    await adb.install(unicodeIMEPath, {replace: false});//leung
406  } catch (err) {
407    logger.info(`Performing full reinstall of ${UNICODE_IME_PKG_ID} as a possible fix for: ${err.message}`);
408//    await adb.uninstallApk(UNICODE_IME_PKG_ID);
409//    await adb.install(unicodeIMEPath, {replace: false});
410  }
411
412  // get the default IME so we can return back to it later if we want
413  let defaultIME = await adb.defaultIME();
414
415  logger.debug(`Unsetting previous IME ${defaultIME}`);
416  const appiumIME = `${UNICODE_IME_PKG_ID}/.UnicodeIME`;
417  logger.debug(`Setting IME to '${appiumIME}'`);
418  await adb.enableIME(appiumIME);
419  await adb.setIME(appiumIME);
420  return defaultIME;
421};
422
423helpers.setMockLocationApp = async function (adb, app) {
424  try {
425    if (await adb.getApiLevel() < 23) {
426      await adb.shell(['settings', 'put', 'secure', 'mock_location', '1']);
427    } else {
428      await adb.shell(['appops', 'set', app, 'android:mock_location', 'allow']);
429    }
430  } catch (err) {
431    logger.warn(`Unable to set mock location for app '${app}': ${err.message}`);
432  }
433};
434
435helpers.installHelperApp = async function (adb, apkPath, packageId, appName) {
436  try {
437    await adb.installOrUpgrade(apkPath, packageId, {grantPermissions: true});
438  } catch (err) {
439    logger.warn(`Ignored error while installing Appium ${appName} helper: ` +
440                `'${err.message}'. Manually uninstalling the application ` +
441                `with package id '${packageId}' may help. Expect some Appium ` +
442                `features may not work as expected unless this problem is ` +
443                `fixed.`);
444  }
445};
446
447helpers.pushSettingsApp = async function (adb, throwError = false) {
448  logger.debug("Pushing settings apk to device...");
449
450  await helpers.installHelperApp(adb, settingsApkPath, SETTINGS_HELPER_PKG_ID, 'Settings');
451
452  // Reinstall will stop the settings helper process anyway, so
453  // there is no need to continue if the application is still running
454  if (await adb.processExists(SETTINGS_HELPER_PKG_ID)) {
455    logger.debug(`${SETTINGS_HELPER_PKG_ID} is already running. ` +
456                 `There is no need to reset its permissions.`);
457    return;
458  }
459
460  // lauch io.appium.settings app due to settings failing to be set
461  // if the app is not launched prior to start the session on android 7+
462  // see https://github.com/appium/appium/issues/8957
463  try {
464    await adb.startApp({
465      pkg: SETTINGS_HELPER_PKG_ID,
466      activity: SETTINGS_HELPER_PKG_ACTIVITY,
467      action: "android.intent.action.MAIN",
468      category: "android.intent.category.LAUNCHER",
469      flags: "0x10200000",
470      stopApp: false,
471    });
472  } catch (err) {
473    logger.warn(`Failed to launch settings app: ${err.message}`);
474    if (throwError) {
475      throw err;
476    }
477  }
478};
479
480helpers.pushUnlock = async function (adb) {
481  logger.debug("Pushing unlock helper app to device...");
482
483  await helpers.installHelperApp(adb, unlockApkPath, UNLOCK_HELPER_PKG_ID, 'Unlock');
484};
485
486/**
487 * Extracts string.xml and converts it to string.json and pushes
488 * it to /data/local/tmp/string.json on for use of bootstrap
489 * If app is not present to extract string.xml it deletes remote strings.json
490 * If app does not have strings.xml we push an empty json object to remote
491 *
492 * @param {?string} language - Language abbreviation, for example 'fr'. The default language
493 * is used if this argument is not defined.
494 * @param {Object} adb - The adb mofdule instance.
495 * @param {Object} opts - Driver options dictionary.
496 * @returns {Object} The dictionary, where string resourtces identifiers are keys
497 * along with their corresponding values for the given language or an empty object
498 * if no matching resources were extracted.
499 */
500helpers.pushStrings = async function (language, adb, opts) {
501  const remoteDir = '/data/local/tmp';
502  const stringsJson = 'strings.json';
503  const remoteFile = `${remoteDir}/${stringsJson}`;
504
505  // clean up remote string.json if present
506  await adb.rimraf(remoteFile);
507
508  if (_.isEmpty(opts.appPackage) || !(await fs.exists(opts.app))) {
509    return {};
510  }
511
512  const stringsTmpDir = path.resolve(opts.tmpDir, opts.appPackage);
513  try {
514    logger.debug('Extracting strings from apk', opts.app, language, stringsTmpDir);
515    const {apkStrings, localPath} = await adb.extractStringsFromApk(opts.app, language, stringsTmpDir);
516    await adb.push(localPath, remoteDir);
517    return apkStrings;
518  } catch (err) {
519    logger.warn(`Could not get strings, continuing anyway. Original error: ${err.message}`);
520    await adb.shell('echo', [`'{}' > ${remoteFile}`]);
521  } finally {
522    await fs.rimraf(stringsTmpDir);
523  }
524  return {};
525};
526
527helpers.unlockWithUIAutomation = async function (driver, adb, unlockCapabilities) {
528  let unlockType = unlockCapabilities.unlockType;
529  if (!unlocker.isValidUnlockType(unlockType)) {
530    throw new Error(`Invalid unlock type ${unlockType}`);
531  }
532  let unlockKey = unlockCapabilities.unlockKey;
533  if (!unlocker.isValidKey(unlockType, unlockKey)) {
534    throw new Error(`Missing unlockKey ${unlockKey} capability for unlockType ${unlockType}`);
535  }
536  const unlockMethod = {
537    [PIN_UNLOCK]: unlocker.pinUnlock,
538    [PASSWORD_UNLOCK]: unlocker.passwordUnlock,
539    [PATTERN_UNLOCK]: unlocker.patternUnlock,
540    [FINGERPRINT_UNLOCK]: unlocker.fingerprintUnlock
541  }[unlockType];
542  await unlockMethod(adb, driver, unlockCapabilities);
543};
544
545helpers.unlockWithHelperApp = async function (adb) {
546  logger.info("Unlocking screen");
547
548  try {
549    await adb.forceStop(UNLOCK_HELPER_PKG_ID);
550  } catch (e) {
551    // Sometimes we can see the below error, but we can ignore it.
552    // [W3C] Encountered internal error running command: Error: Error executing adbExec. Original error: 'Command 'adb -P 5037 -s emulator-5554 shell am force-stop io.appium.unlock' timed out after 20000ms'; Stderr: ''; Code: 'null'
553    logger.warn(`An error in unlockWithHelperApp: ${e.message}`);
554  }
555
556  let startOpts = {
557    pkg: UNLOCK_HELPER_PKG_ID,
558    activity: UNLOCK_HELPER_PKG_ACTIVITY,
559    action: "android.intent.action.MAIN",
560    category: "android.intent.category.LAUNCHER",
561    flags: "0x10200000",
562    stopApp: false,
563    retry: false,
564    waitDuration: 1000
565  };
566
567  // Unlock succeed with a couple of retries.
568  let firstRun = true;
569  await retry(3, async function () {
570    // To reduce a time to call adb.isScreenLocked() since `adb shell dumpsys window` is easy to hang adb commands
571    if (firstRun) {
572      firstRun = false;
573    } else {
574      try {
575        if (!(await adb.isScreenLocked())) {
576          return;
577        }
578      } catch (e) {
579        logger.warn(`Error in isScreenLocked: ${e.message}`);
580        logger.warn("\"adb shell dumpsys window\" command has timed out.");
581        logger.warn("The reason of this timeout is the delayed adb response. Resetting adb server can improve it.");
582      }
583    }
584
585    logger.info(`Launching ${UNLOCK_HELPER_PKG_ID}`);
586
587    // The command takes too much time so we should not call the command over twice continuously.
588    await adb.startApp(startOpts);
589  });
590};
591
592helpers.unlock = async function (driver, adb, capabilities) {
593  if (!(await adb.isScreenLocked())) {
594    logger.info("Screen already unlocked, doing nothing");
595    return;
596  }
597
598  logger.debug("Screen is locked, trying to unlock");
599  if (_.isUndefined(capabilities.unlockType)) {
600    logger.warn("Using app unlock, this is going to be deprecated!");
601    await helpers.unlockWithHelperApp(adb);
602  } else {
603    await helpers.unlockWithUIAutomation(driver, adb, {unlockType: capabilities.unlockType, unlockKey: capabilities.unlockKey});
604    await helpers.verifyUnlock(adb);
605  }
606};
607
608helpers.verifyUnlock = async function (adb) {
609  await retryInterval(2, 1000, async () => {
610    if (await adb.isScreenLocked()) {
611      throw new Error("Screen did not unlock successfully, retrying");
612    }
613    logger.debug("Screen unlocked successfully");
614  });
615};
616
617helpers.initDevice = async function (adb, opts) {
618  await adb.waitForDevice();
619  // pushSettingsApp required before calling ensureDeviceLocale for API Level 24+
620
621// await helpers.pushSettingsApp(adb);//leung
622  if (!opts.avd) {
623    await helpers.setMockLocationApp(adb, SETTINGS_HELPER_PKG_ID);
624  }
625
626  await helpers.ensureDeviceLocale(adb, opts.language, opts.locale);
627  await adb.startLogcat();
628  let defaultIME;
629  if (opts.unicodeKeyboard) {
630    defaultIME = await helpers.initUnicodeKeyboard(adb);
631  }
632  if (_.isUndefined(opts.unlockType)) {
633//    await helpers.pushUnlock(adb);//leung
634  }
635  return defaultIME;
636};
637
638helpers.removeNullProperties = function (obj) {
639  for (let key of _.keys(obj)) {
640    if (_.isNull(obj[key]) || _.isUndefined(obj[key])) {
641      delete obj[key];
642    }
643  }
644};
645
646helpers.truncateDecimals = function (number, digits) {
647  let multiplier = Math.pow(10, digits),
648      adjustedNum = number * multiplier,
649      truncatedNum = Math[adjustedNum < 0 ? 'ceil' : 'floor'](adjustedNum);
650
651  return truncatedNum / multiplier;
652};
653
654helpers.isChromeBrowser = function (browser) {
655  return _.includes(Object.keys(CHROME_BROWSER_PACKAGE_ACTIVITY), (browser || '').toLowerCase());
656};
657
658helpers.getChromePkg = function (browser) {
659  return CHROME_BROWSER_PACKAGE_ACTIVITY[browser.toLowerCase()] ||
660         CHROME_BROWSER_PACKAGE_ACTIVITY.default;
661};
662
663helpers.removeAllSessionWebSocketHandlers = async function (server, sessionId) {
664  if (!server || !_.isFunction(server.getWebSocketHandlers)) {
665    return;
666  }
667
668  const activeHandlers = await server.getWebSocketHandlers(sessionId);
669  for (const pathname of _.keys(activeHandlers)) {
670    await server.removeWebSocketHandler(pathname);
671  }
672};
673
674/**
675 * Takes a desired capability and tries to JSON.parse it as an array,
676 * and either returns the parsed array or a singleton array.
677 *
678 * @param {any} cap A desired capability
679 */
680helpers.parseArray = function (cap) {
681  let parsedCaps;
682  try {
683    parsedCaps = JSON.parse(cap);
684  } catch (ign) { }
685
686  if (_.isArray(parsedCaps)) {
687    return parsedCaps;
688  } else if (_.isString(cap)) {
689    return [cap];
690  }
691
692  throw new Error(`must provide a string or JSON Array; received ${cap}`);
693};
694
695helpers.validateDesiredCaps = function (caps) {
696  // make sure that the capabilities have one of `app`, `appPackage` or `browser`
697  if ((!caps.browserName || !this.isChromeBrowser(caps.browserName)) && !caps.app && !caps.appPackage) {
698    logger.errorAndThrow('The desired capabilities must include either an app, appPackage or browserName');
699  }
700  if (caps.browserName) {
701    if (caps.app) {
702      // warn if the capabilities have both `app` and `browser, although this is common with selenium grid
703      logger.warn('The desired capabilities should generally not include both an app and a browserName');
704    }
705    if (caps.appPackage) {
706      logger.errorAndThrow(`The desired capabilities must include either 'appPackage' or 'browserName'`);
707    }
708  }
709
710  return true;
711};
712
713helpers.bootstrap = Bootstrap;
714helpers.unlocker = unlocker;
715
716export default helpers;
717
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 Android 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)