How to use this.initAUT 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.

driver.js

Source: driver.js Github

copy
1import _ from 'lodash';
2import { BaseDriver } from 'appium-base-driver';
3import SelendroidServer from './selendroid';
4import { fs } from 'appium-support';
5import { serverExists } from './installer';
6import { retryInterval } from 'asyncbox';
7import logger from './logger';
8import commands from './commands';
9import { DEFAULT_ADB_PORT } from 'appium-adb';
10import selendroidHelpers from './helpers';
11import { androidHelpers, androidCommands, WEBVIEW_BASE } from 'appium-android-driver';
12import desiredCapConstraints from './desired-caps';
13
14
15let helpers = {};
16Object.assign(helpers, selendroidHelpers, androidHelpers);
17
18// The range of ports we can use on the system for communicating to the
19// Selendroid HTTP server on the device
20const SYSTEM_PORT_RANGE = [8200, 8299];
21
22// This is the port that Selendroid listens to on the device. We will forward
23// one of the ports above on the system to this port on the device.
24const DEVICE_PORT = 8080;
25
26// This is a set of methods and paths that we never want to proxy to Selendroid
27const NO_PROXY = [
28  ['GET', new RegExp('^/session/[^/]+/log/types$')],
29  ['POST', new RegExp('^/session/[^/]+/log')],
30  ['POST', new RegExp('^/session/[^/]+/location')],
31  ['POST', new RegExp('^/session/[^/]+/appium')],
32  ['GET', new RegExp('^/session/[^/]+/appium')],
33  ['POST', new RegExp('^/session/[^/]+/context')],
34  ['GET', new RegExp('^/session/[^/]+/context')],
35  ['GET', new RegExp('^/session/[^/]+/contexts')],
36  ['POST', new RegExp('^/session/[^/]+/element/[^/]+/value')],
37  ['GET', new RegExp('^/session/[^/]+/element/[^/]+/rect')],
38  ['GET', new RegExp('^/session/[^/]+/network_connection')],
39  ['POST', new RegExp('^/session/[^/]+/network_connection')],
40  ['POST', new RegExp('^/session/[^/]+/ime')],
41  ['GET', new RegExp('^/session/[^/]+/ime')],
42  ['POST', new RegExp('^/session/[^/]+/keys')],
43  ['POST', new RegExp('^/session/[^/]+/touch/multi/perform')],
44];
45
46const APP_EXTENSION = '.apk';
47
48
49class SelendroidDriver extends BaseDriver {
50  constructor (opts = {}, shouldValidateCaps = true) {
51    // `shell` overwrites adb.shell, so remove
52    delete opts.shell;
53
54    super(opts, shouldValidateCaps);
55
56    this.desiredCapConstraints = desiredCapConstraints;
57    this.selendroid = null;
58    this.jwpProxyActive = false;
59    this.defaultIME = null;
60    this.jwpProxyAvoid = NO_PROXY;
61    this.apkStrings = {}; // map of language -> strings obj
62
63    // handle webview mechanics from AndroidDriver
64    this.chromedriver = null;
65    this.sessionChromedrivers = {};
66
67    this.opts.systemPort = opts.selendroidPort || SYSTEM_PORT_RANGE[0];
68    this.opts.adbPort = opts.adbPort || DEFAULT_ADB_PORT;
69  }
70
71  async createSession (caps) {
72    try {
73      if (!(await serverExists())) {
74        throw new Error('Cannot start a selendroid session because the server ' +
75                        'apk does not exist. Please run `npm run-script ' +
76                        'selendroid` in the appium-selendroid-driver package');
77      }
78
79      // TODO handle otherSessionData for multiple sessions
80      let sessionId;
81      [sessionId] = await super.createSession(caps);
82      this.curContext = this.defaultContextName();
83      // fail very early if the app doesn't actually exist, since selendroid
84      // (unlike the android driver) can't run a pre-installed app based
85      // only on package name. It has to be an actual apk
86      this.opts.app = await this.helpers.configureApp(this.opts.app, APP_EXTENSION);
87      await this.checkAppPresent();
88      this.opts.systemPort = this.opts.selendroidPort || SYSTEM_PORT_RANGE[0];
89      this.opts.adbPort = this.opts.adbPort || DEFAULT_ADB_PORT;
90      await this.startSelendroidSession();
91      return [sessionId, caps];
92    } catch (e) {
93      await this.deleteSession();
94      throw e;
95    }
96  }
97
98  validateDesiredCaps (caps) {
99    // check with the base class, and return if it fails
100    let res = super.validateDesiredCaps(caps);
101    if (!res) return res; // eslint-disable-line curly
102
103    if (this.opts.reboot) {
104      this.setAvdFromCapabilities(caps);
105    }
106  }
107
108  setAvdFromCapabilities (caps) {
109    if (this.opts.avd) {
110      logger.info('avd name defined, ignoring device name and platform version');
111    } else {
112      if (!caps.deviceName) {
113        logger.errorAndThrow('avd or deviceName should be specified when reboot option is enabled');
114      }
115      if (!caps.platformVersion) {
116        logger.errorAndThrow('avd or platformVersion should be specified when reboot option is enabled');
117      }
118      let avdDevice = caps.deviceName.replace(/[^a-zA-Z0-9_.]/g, '-');
119      this.opts.avd = `${avdDevice}__${caps.platformVersion}`;
120    }
121  }
122
123  get driverData () {
124    // TODO fille out resource info here
125    return {};
126  }
127
128  isEmulator () {
129    return !!this.opts.avd;
130  }
131
132  async startSelendroidSession () {
133    if (!this.opts.javaVersion) {
134      this.opts.javaVersion = await helpers.getJavaVersion();
135    }
136
137    // get device udid for this session
138    let {udid, emPort} = await helpers.getDeviceInfoFromCaps(this.opts);
139    this.opts.udid = udid;
140    this.opts.emPort = emPort;
141
142    // now that we know our java version and device info, we can create our
143    // ADB instance
144    this.adb = await androidHelpers.createADB(this.opts);
145    // fail very early if the user's app doesn't have the appropriate perms
146    // for selendroid automation
147    await helpers.ensureInternetPermissionForApp(this.adb, this.opts.app);
148    // get appPackage et al from manifest if necessary
149    let appInfo = await helpers.getLaunchInfo(this.adb, this.opts);
150    // and get it onto our 'opts' object so we use it from now on
151    Object.assign(this.opts, appInfo);
152    // set up the modified selendroid server etc
153    await this.initSelendroidServer();
154    // start an avd, set the language/locale, pick an emulator, etc...
155    // TODO with multiple devices we'll need to parameterize this
156    await helpers.initDevice(this.adb, this.opts);
157    // Further prepare the device by forwarding the Selendroid port
158    await this.adb.forwardPort(this.opts.systemPort, DEVICE_PORT);
159    // prepare our actual AUT, get it on the device, etc...
160    await this.initAUT();
161    // unlock the device to prepare it for testing
162    await helpers.unlock(this, this.adb, this.caps);
163    // launch selendroid and wait till its online and we have a session
164    await this.selendroid.startSession(this.caps);
165    // rescue selendroid if it fails to start our AUT
166    await this.ensureAppStarts();
167    // if we want to immediately get into a webview, set our context
168    // appropriately
169    if (this.opts.autoWebview) {
170      await retryInterval(20, this.opts.autoWebviewTimeout || 2000, async () => {
171        await this.setContext(this.defaultWebviewName());
172      });
173    }
174    // now that everything has started successfully, turn on proxying so all
175    // subsequent session requests go straight to/from selendroid
176    this.jwpProxyActive = true;
177  }
178
179  async initSelendroidServer () {
180    // now that we have package and activity, we can create an instance of
181    // selendroid with the appropriate data
182    this.selendroid = new SelendroidServer({
183      host: this.opts.host || 'localhost',
184      systemPort: this.opts.systemPort,
185      devicePort: DEVICE_PORT,
186      adb: this.adb,
187      apk: this.opts.app,
188      tmpDir: this.opts.tmpDir,
189      appPackage: this.opts.appPackage,
190      appActivity: this.opts.appActivity,
191    });
192    this.proxyReqRes = this.selendroid.proxyReqRes.bind(this.selendroid);
193    // let selendroid repackage itself for our AUT
194    await this.selendroid.prepareModifiedServer();
195  }
196
197  async initAUT () {
198    logger.debug('Initializing application under test');
199    // set the localized strings for the current language from the apk
200    // TODO: incorporate changes from appium#5308 which fix a race cond-
201    // ition bug in old appium and need to be replicated here
202    this.apkStrings[this.opts.language] = await helpers.pushStrings(
203        this.opts.language, this.adb, this.opts);
204    if (!this.opts.skipUninstall) {
205      await this.adb.uninstallApk(this.opts.appPackage);
206    }
207    if (!this.opts.noSign) {
208      let signed = await this.adb.checkApkCert(this.opts.app, this.opts.appPackage);
209      if (!signed) {
210        logger.debug('Application not signed. Signing.');
211        await this.adb.sign(this.opts.app, this.opts.appPackage);
212      }
213    }
214    await helpers.installApk(this.adb, this.opts);
215    // get Selendroid on the device too
216    await this.selendroid.installModifiedServer();
217  }
218
219  async ensureAppStarts () {
220    // make sure we have an activity and package to wait for
221    let appWaitPackage = this.opts.appWaitPackage || this.opts.appPackage;
222    let appWaitActivity = this.opts.appWaitActivity || this.opts.appActivity;
223    try {
224      // wait for up to 5s for selendroid to have started the app after it is
225      // online
226      await this.adb.waitForActivity(appWaitPackage, appWaitActivity, 5000);
227    } catch (e) {
228      logger.info(`Selendroid did not start the activity we were waiting for, ` +
229                  `'${appWaitPackage}/${appWaitActivity}'. ` +
230                  `Starting it ourselves`);
231      await this.adb.startApp({
232        pkg: this.opts.appPackage,
233        activity: this.opts.appActivity,
234        action: this.opts.intentAction,
235        category: this.opts.intentCategory,
236        flags: this.opts.intentFlags,
237        waitPkg: this.opts.appWaitPackage,
238        waitActivity: this.opts.appWaitActivity,
239        optionalIntentArguments: this.opts.optionalIntentArguments,
240        stopApp: !this.opts.dontStopAppOnReset,
241        retry: false
242      });
243    }
244  }
245
246  async deleteSession () {
247    logger.debug('Deleting Selendroid session');
248    if (this.selendroid) {
249      if (this.jwpProxyActive) {
250        await this.selendroid.deleteSession();
251      }
252      this.selendroid = null;
253    }
254    this.jwpProxyActive = false;
255
256    if (this.adb) {
257      if (this.opts.unicodeKeyboard && this.opts.resetKeyboard &&
258          this.defaultIME) {
259        logger.debug(`Resetting IME to '${this.defaultIME}'`);
260        await this.adb.setIME(this.defaultIME);
261      }
262      await this.adb.forceStop(this.opts.appPackage);
263      await this.adb.stopLogcat();
264      if (this.opts.reboot) {
265        let avdName = this.opts.avd.replace('@', '');
266        logger.debug(`closing emulator '${avdName}'`);
267        await this.adb.killEmulator(avdName);
268      }
269    }
270    await super.deleteSession();
271  }
272
273  async checkAppPresent () {
274    logger.debug('Checking whether app is actually present');
275    if (!(await fs.exists(this.opts.app))) {
276      logger.errorAndThrow(`Could not find app apk at '${this.opts.app}'`);
277    }
278  }
279
280  defaultWebviewName () {
281    return `${WEBVIEW_BASE}0`;
282  }
283
284  proxyActive (sessionId) {
285    super.proxyActive(sessionId);
286
287    // we always have an active proxy to the selendroid server
288    return true;
289  }
290
291  getProxyAvoidList (sessionId) {
292    super.getProxyAvoidList(sessionId);
293
294    return this.jwpProxyAvoid;
295  }
296
297  canProxy (sessionId) {
298    super.canProxy(sessionId);
299
300    // we can always proxy to the selendroid server
301    return true;
302  }
303}
304
305// first add the android-driver commands which we will fall back to
306for (let [cmd, fn] of _.toPairs(androidCommands)) {
307  // we do some different/special things with these methods
308  if (!_.includes(['defaultWebviewName'], cmd)) {
309    SelendroidDriver.prototype[cmd] = fn;
310  }
311}
312
313// then overwrite with any selendroid-specific commands
314for (let [cmd, fn] of _.toPairs(commands)) {
315  SelendroidDriver.prototype[cmd] = fn;
316}
317
318export { SelendroidDriver, DEVICE_PORT };
319export default SelendroidDriver;
320
Full Screen

general.js

Source: general.js Github

copy
1import _ from 'lodash';
2import androidHelpers from '../android-helpers';
3import { fs, util } from 'appium-support';
4import B from 'bluebird';
5import log from '../logger';
6
7
8const APP_EXTENSION = '.apk';
9
10let commands = {}, helpers = {}, extensions = {};
11
12const logTypesSupported = {
13  'logcat' : 'Logs for Android applications on real device and emulators via ADB'
14};
15
16commands.keys = async function (keys) {
17  // Protocol sends an array; rethink approach
18  keys = _.isArray(keys) ? keys.join('') : keys;
19  let params = {
20    text: keys,
21    replace: false
22  };
23  if (this.opts.unicodeKeyboard) {
24    params.unicodeKeyboard = true;
25  }
26  await this.doSendKeys(params);
27};
28
29commands.doSendKeys = async function (params) {
30  return await this.bootstrap.sendAction('setText', params);
31};
32
33commands.getDeviceTime = async function () {
34  log.info('Attempting to capture android device date and time');
35  try {
36    let out = await this.adb.shell(['date']);
37    return out.trim();
38  } catch (err) {
39    log.errorAndThrow(`Could not capture device date and time: ${err}`);
40  }
41};
42
43commands.getPageSource = function () {
44  return this.bootstrap.sendAction('source');
45};
46
47commands.back = function () {
48  return this.bootstrap.sendAction('pressBack');
49};
50
51commands.isKeyboardShown = async function () {
52  let keyboardInfo = await this.adb.isSoftKeyboardPresent();
53  return keyboardInfo.isKeyboardShown;
54};
55
56commands.hideKeyboard = async function () {
57  let {isKeyboardShown, canCloseKeyboard} = await this.adb.isSoftKeyboardPresent();
58  if (!isKeyboardShown) {
59    throw new Error("Soft keyboard not present, cannot hide keyboard");
60  }
61
62  if (canCloseKeyboard) {
63    return this.back();
64  } else {
65    log.info("Keyboard has no UI; no closing necessary");
66  }
67};
68
69commands.openSettingsActivity = async function (setting) {
70  let {appPackage, appActivity} = await this.adb.getFocusedPackageAndActivity();
71  await this.adb.shell(['am', 'start', '-a', `android.settings.${setting}`]);
72  await this.adb.waitForNotActivity(appPackage, appActivity, 5000);
73};
74
75commands.getWindowSize = function () {
76  return this.bootstrap.sendAction('getDeviceSize');
77};
78
79commands.getCurrentActivity = async function () {
80  return (await this.adb.getFocusedPackageAndActivity()).appActivity;
81};
82
83commands.getCurrentPackage = async function () {
84  return (await this.adb.getFocusedPackageAndActivity()).appPackage;
85};
86
87commands.getLogTypes = function () {
88  return _.keys(logTypesSupported);
89};
90
91commands.getLog = function (logType) {
92  if (!_.has(logTypesSupported, logType)) {
93    throw new Error(`Unsupported log type ${logType}. ` +
94                    `Supported types are ${JSON.stringify(logTypesSupported)}`);
95  }
96
97  if (logType === 'logcat') {
98    return this.adb.getLogcatLogs();
99  }
100};
101
102commands.isAppInstalled = function (appPackage) {
103  return this.adb.isAppInstalled(appPackage);
104};
105
106commands.removeApp = function (appPackage) {
107  return this.adb.uninstallApk(appPackage);
108};
109
110commands.installApp = async function (appPath) {
111  appPath = await this.helpers.configureApp(appPath, APP_EXTENSION);
112  if (!(await fs.exists(appPath))) {
113    log.errorAndThrow(`Could not find app apk at ${appPath}`);
114  }
115
116  let {apkPackage} = await this.adb.packageAndLaunchActivityFromManifest(appPath);
117  let opts = {
118    app: appPath,
119    appPackage: apkPackage,
120    fastReset: this.opts.fastReset
121  };
122  return androidHelpers.installApkRemotely(this.adb, opts);
123};
124
125commands.background = async function (seconds) {
126  if (seconds < 0) {
127    // if user passes in a negative seconds value, interpret that as the instruction
128    // to not bring the app back at all
129    await this.adb.goToHome();
130    return true;
131  }
132  let {appPackage, appActivity} = await this.adb.getFocusedPackageAndActivity();
133  await this.adb.goToHome();
134  await B.delay(seconds * 1000);
135
136  let args;
137  if (this.opts.startActivityArgs && this.opts.startActivityArgs[`${appPackage}/${appActivity}`]) {
138    // the activity was started with `startActivity`, so use those args to restart
139    args = this.opts.startActivityArgs[`${appPackage}/${appActivity}`];
140  } else if (appPackage === this.opts.appPackage && appActivity === this.opts.appActivity) {
141    // the activity is the original session activity, so use the original args
142    args = {
143      pkg: appPackage,
144      activity: appActivity,
145      action: this.opts.intentAction,
146      category: this.opts.intentCategory,
147      flags: this.opts.intentFlags,
148      waitPkg: this.opts.appWaitPackage,
149      waitActivity: this.opts.appWaitActivity,
150      optionalIntentArguments: this.opts.optionalIntentArguments,
151      stopApp: false,
152    };
153  } else {
154    // the activity was started some other way, so use defaults
155    args = {
156      pkg: appPackage,
157      activity: appActivity,
158      waitPkg: appPackage,
159      waitActivity: appActivity,
160      stopApp: false
161    };
162  }
163  args = await util.filterObject(args);
164  log.debug(`Bringing application back to foreground with arguments: ${JSON.stringify(args)}`);
165  return await this.adb.startApp(args);
166};
167
168commands.getStrings = async function (language) {
169  if (!language) {
170    language = await this.adb.getDeviceLanguage();
171    log.info(`No language specified, returning strings for: ${language}`);
172  }
173
174  if (this.apkStrings[language]) {
175    // Return cached strings
176    return this.apkStrings[language];
177  }
178
179  // TODO: This is mutating the current language, but it's how appium currently works
180  this.apkStrings[language] = await androidHelpers.pushStrings(language, this.adb, this.opts);
181  await this.bootstrap.sendAction('updateStrings');
182
183  return this.apkStrings[language];
184};
185
186commands.launchApp = async function () {
187  await this.initAUT();
188  await this.startAUT();
189};
190
191commands.startActivity = async function (appPackage, appActivity,
192                                         appWaitPackage, appWaitActivity,
193                                         intentAction, intentCategory,
194                                         intentFlags, optionalIntentArguments,
195                                         dontStopAppOnReset) {
196  log.debug(`Starting package '${appPackage}' and activity '${appActivity}'`);
197
198  // dontStopAppOnReset is both an argument here, and a desired capability
199  // if the argument is set, use it, otherwise use the cap
200  if (!util.hasValue(dontStopAppOnReset)) {
201    dontStopAppOnReset = !!this.opts.dontStopAppOnReset;
202  }
203
204  let args = {
205    pkg: appPackage,
206    activity: appActivity,
207    waitPkg: appWaitPackage || appPackage,
208    waitActivity: appWaitActivity || appActivity,
209    action: intentAction,
210    category: intentCategory,
211    flags: intentFlags,
212    optionalIntentArguments,
213    stopApp: !dontStopAppOnReset
214  };
215  this.opts.startActivityArgs = this.opts.startActivityArgs || {};
216  this.opts.startActivityArgs[`${appPackage}/${appActivity}`] = args;
217  await this.adb.startApp(args);
218};
219
220commands.reset = async function () {
221  if (this.opts.fullReset) {
222    log.info("Running old fashion reset (reinstall)");
223    await this.adb.stopAndClear(this.opts.appPackage);
224    await this.adb.uninstallApk(this.opts.appPackage);
225    await androidHelpers.installApkRemotely(this.adb, this.opts);
226  } else {
227    log.info("Running fast reset (stop and clear)");
228    await this.adb.stopAndClear(this.opts.appPackage);
229  }
230
231  await this.grantPermissions();
232
233  return await this.startAUT();
234};
235
236commands.startAUT = async function () {
237  await this.adb.startApp({
238    pkg: this.opts.appPackage,
239    activity: this.opts.appActivity,
240    action: this.opts.intentAction,
241    category: this.opts.intentCategory,
242    flags: this.opts.intentFlags,
243    waitPkg: this.opts.appWaitPackage,
244    waitActivity: this.opts.appWaitActivity,
245    waitDuration: this.opts.appWaitDuration,
246    optionalIntentArguments: this.opts.optionalIntentArguments,
247    stopApp: !this.opts.dontStopAppOnReset
248  });
249};
250
251// we override setUrl to take an android URI which can be used for deep-linking
252// inside an app, similar to starting an intent
253commands.setUrl = async function (uri) {
254  await this.adb.startUri(uri, this.opts.appPackage);
255};
256
257// closing app using force stop
258commands.closeApp = async function () {
259  await this.adb.forceStop(this.opts.appPackage);
260  // reset context since we don't know what kind on context we will end up after app launch.
261  this.curContext = null;
262};
263
264commands.getDisplayDensity = async function () {
265  // first try the property for devices
266  let out = await this.adb.shell(['getprop', 'ro.sf.lcd_density']);
267  if (out) {
268    let val = parseInt(out, 10);
269    // if the value is NaN, try getting the emulator property
270    if (!isNaN(val)) {
271      return val;
272    }
273    log.debug(`Parsed density value was NaN: "${out}"`);
274  }
275  // fallback to trying property for emulators
276  out = await this.adb.shell(['getprop', 'qemu.sf.lcd_density']);
277  if (out) {
278    let val = parseInt(out, 10);
279    if (!isNaN(val)) {
280      return val;
281    }
282    log.debug(`Parsed density value was NaN: "${out}"`);
283  }
284  // couldn't get anything, so error out
285  log.errorAndThrow('Failed to get display density property.');
286};
287
288/**
289 * Parses the given window manager Surface string to get info.
290 * @param line: To parse. This is assumed to be valid.
291 * @return: Visibility and bounds of the Surface.
292 */
293function parseSurfaceLine (line) {
294  // the surface bounds are in the format:
295  // "rect=(0.0,1184.0) 768.0 x 96.0"
296  //       ^ location   ^ size
297  // cut out the stuff before the 'rect' and then split the numbers apart
298  let bounds = line.split('rect=')[1]
299  .replace(/[\(\), x]+/g, ' ')
300  .trim()
301  .split(' ');
302
303  return {
304    visible: (line.indexOf('shown=true') !== -1),
305    x: parseFloat(bounds[0]),
306    y: parseFloat(bounds[1]),
307    width: parseFloat(bounds[2]),
308    height: parseFloat(bounds[3])
309  };
310}
311
312/**
313 * Extracts status and navigation bar information from the window manager output.
314 * @param lines: Output from dumpsys command
315 * @return: Visibility and bounds info of status and navigation bar
316 */
317function parseWindows (lines) {
318  let atStatusBar = false;
319  let atNavBar = false;
320  let statusBar;
321  let navigationBar;
322  // the window manager output looks like:
323  // Window #1 ... WindowID
324  //   A bunch of properties
325  // Window #2 ... WindowID
326  //   A bunch of properties
327  lines.split('\n').forEach((line) => {
328    // the start of a new window section
329    if (line.indexOf('  Window #') !== -1) {
330      // determine which section we're in
331      // only one will be true
332      atStatusBar = (line.indexOf('StatusBar') !== -1);
333      atNavBar = (line.indexOf('NavigationBar') !== -1);
334      // don't need anything else. move to next line
335      return;
336    }
337    // once we're in a window section, look for the surface data line
338    if (line.indexOf('      Surface:') === -1) {
339      return;
340    }
341    if (atStatusBar) {
342      statusBar = parseSurfaceLine(line);
343      atStatusBar = false;
344    } else if (atNavBar) {
345      navigationBar = parseSurfaceLine(line);
346      atNavBar = false;
347    }
348  });
349
350  if (!statusBar) {
351    log.errorAndThrow('Failed to parse status bar information.');
352  }
353  if (!navigationBar) {
354    log.errorAndThrow('Failed to parse navigation bar information.');
355  }
356
357  return {statusBar, navigationBar};
358}
359
360commands.getSystemBars = async function () {
361  let out = await this.adb.shell(['dumpsys', 'window', 'windows']);
362  if (!out) {
363    log.errorAndThrow('Did not get window manager output.');
364  }
365  return parseWindows(out);
366};
367
368Object.assign(extensions, commands, helpers);
369export { commands, helpers };
370export default extensions;
371// for unit tests
372export { parseWindows, parseSurfaceLine };
373
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)