How to use helpers.getWebViewsMapping method in Appium Android Driver

Best JavaScript code snippet using appium-android-driver

webview-helpers.js

Source:webview-helpers.js Github

copy

Full Screen

1import _ from 'lodash';2import logger from './logger';3import axios from 'axios';4import { util } from '@appium/support';5import { findAPortNotInUse } from 'portscanner';6import LRU from 'lru-cache';7import B from 'bluebird';8import path from 'path';9import os from 'os';10const NATIVE_WIN = 'NATIVE_APP';11const WEBVIEW_WIN = 'WEBVIEW';12const CHROMIUM_WIN = 'CHROMIUM';13const WEBVIEW_BASE = `${WEBVIEW_WIN}_`;14const WEBVIEW_PID_PATTERN = new RegExp(`^${WEBVIEW_BASE}(\\d+)`);15const WEBVIEW_PKG_PATTERN = new RegExp(`^${WEBVIEW_BASE}([^\\d\\s][\\w.]*)`);16const DEVTOOLS_SOCKET_PATTERN = /@[\w.]+_devtools_remote_?(\d+)?\b/;17const CROSSWALK_SOCKET_PATTERN = /@([\w.]+)_devtools_remote\b/;18const CHROMIUM_DEVTOOLS_SOCKET = 'chrome_devtools_remote';19const CHROME_PACKAGE_NAME = 'com.android.chrome';20const KNOWN_CHROME_PACKAGE_NAMES = [21  CHROME_PACKAGE_NAME,22  'com.chrome.beta',23  'com.chrome.dev',24  'com.chrome.canary',25];26const DEVTOOLS_PORTS_RANGE = [10900, 11000];27const WEBVIEWS_DETAILS_CACHE = new LRU({28  max: 100,29  updateAgeOnGet: true,30});31const CDP_REQ_TIMEOUT = 2000; // ms32const DEVTOOLS_PORT_ALLOCATION_GUARD = util.getLockFileGuard(33  path.resolve(os.tmpdir(), 'android_devtools_port_guard'),34  {timeout: 7, tryRecovery: true}35);36const helpers = {};37function toDetailsCacheKey (adb, webview) {38  return `${adb?.curDeviceId}:${webview}`;39}40/**41 * This function gets a list of android system processes and returns ones42 * that look like webviews43 * See https://cs.chromium.org/chromium/src/chrome/browser/devtools/device/android_device_info_query.cc44 * for more details45 *46 * @param {object} adb - an ADB instance47 *48 * @return {Array.<string>} - a list of matching webview socket names (including the leading '@')49 */50async function getPotentialWebviewProcs (adb) {51  const out = await adb.shell(['cat', '/proc/net/unix']);52  const names = [];53  const allMatches = [];54  for (const line of out.split('\n')) {55    // Num RefCount Protocol Flags Type St Inode Path56    const [,,, flags,, st,, sockPath] = line.trim().split(/\s+/);57    if (!sockPath) {58      continue;59    }60    if (sockPath.startsWith('@')) {61      allMatches.push(line.trim());62    }63    if (flags !== '00010000' || st !== '01') {64      continue;65    }66    if (!DEVTOOLS_SOCKET_PATTERN.test(sockPath)) {67      continue;68    }69    names.push(sockPath);70  }71  if (_.isEmpty(names)) {72    logger.debug('Found no active devtools sockets');73    if (!_.isEmpty(allMatches)) {74      logger.debug(`Other sockets are: ${JSON.stringify(allMatches, null, 2)}`);75    }76  } else {77    logger.debug(`Parsed ${names.length} active devtools ${util.pluralize('socket', names.length, false)}: ` +78      JSON.stringify(names));79  }80  // sometimes the webview process shows up multiple times per app81  return _.uniq(names);82}83/**84 * @typedef {Object} WebviewProc85 * @property {string} proc - The webview process name (as returned by86 * getPotentialWebviewProcs87 * @property {string} webview - The actual webview context name88 */89/**90 * This function retrieves a list of system processes that look like webviews,91 * and returns them along with the webview context name appropriate for it.92 * If we pass in a deviceSocket, we only attempt to find webviews which match93 * that socket name (this is for apps which embed Chromium, which isn't the94 * same as chrome-backed webviews).95 *96 * @param {object} adb - an ADB instance97 * @param {?string} deviceSocket - the explictly-named device socket to use98 *99 * @return {Array.<WebviewProc>}100 */101async function webviewsFromProcs (adb, deviceSocket = null) {102  const socketNames = await getPotentialWebviewProcs(adb);103  const webviews = [];104  for (const socketName of socketNames) {105    if (deviceSocket === CHROMIUM_DEVTOOLS_SOCKET && socketName === `@${deviceSocket}`) {106      webviews.push({107        proc: socketName,108        webview: CHROMIUM_WIN,109      });110      continue;111    }112    const socketNameMatch = DEVTOOLS_SOCKET_PATTERN.exec(socketName);113    if (!socketNameMatch) {114      continue;115    }116    const crosswalkMatch = CROSSWALK_SOCKET_PATTERN.exec(socketName);117    if (!socketNameMatch[1] && !crosswalkMatch) {118      continue;119    }120    if (deviceSocket && socketName === `@${deviceSocket}` || !deviceSocket) {121      webviews.push({122        proc: socketName,123        webview: socketNameMatch[1]124          ? `${WEBVIEW_BASE}${socketNameMatch[1]}`125          : `${WEBVIEW_BASE}${crosswalkMatch[1]}`,126      });127    }128  }129  return webviews;130}131/**132 * Allocates a local port for devtools communication133 *134 * @param {ADB} adb ADB instance135 * @param {string} socketName The remote Unix socket name136 * @param {?number} webviewDevtoolsPort The local port number or null to apply137 * autodetection138 * @returns {number} The local port number if the remote socket has been forwarded139 * successfully or `null` otherwise140 * @throws {Error} If there was an error while allocating the local port141 */142async function allocateDevtoolsPort (adb, socketName, webviewDevtoolsPort = null) {143  // socket names come with '@', but this should not be a part of the abstract144  // remote port, so remove it145  const remotePort = socketName.replace(/^@/, '');146  let [startPort, endPort] = DEVTOOLS_PORTS_RANGE;147  if (webviewDevtoolsPort) {148    endPort = webviewDevtoolsPort + (endPort - startPort);149    startPort = webviewDevtoolsPort;150  }151  logger.debug(`Forwarding remote port ${remotePort} to a local ` +152    `port in range ${startPort}..${endPort}`);153  if (!webviewDevtoolsPort) {154    logger.debug(`You could use the 'webviewDevtoolsPort' capability to customize ` +155      `the starting port number`);156  }157  return await DEVTOOLS_PORT_ALLOCATION_GUARD(async () => {158    let localPort;159    try {160      localPort = await findAPortNotInUse(startPort, endPort);161    } catch (e) {162      throw new Error(`Cannot find any free port to forward the Devtools socket ` +163        `in range ${startPort}..${endPort}. You could set the starting port number ` +164        `manually by providing the 'webviewDevtoolsPort' capability`);165    }166    await adb.adbExec(['forward', `tcp:${localPort}`, `localabstract:${remotePort}`]);167    return localPort;168  });169}170/**171 * @typedef {Object} WebviewProps172 * @property {string} proc The name of the Devtools Unix socket173 * @property {string} webview The web view alias. Looks like `WEBVIEW_`174 * prefix plus PID or package name175 * @property {?Object} info Webview information as it is retrieved by176 * /json/version CDP endpoint177 * @property {?Array<Object>} pages Webview pages list as it is retrieved by178 * /json/list CDP endpoint179 */180/**181 * @typedef {Object} DetailCollectionOptions182 * @property {?string|number} webviewDevtoolsPort The starting port to use for webview page183 * presence check (if not the default of 9222).184 * @property {?boolean} ensureWebviewsHavePages Whether to check for webview185 * pages presence186 * @property {boolean} enableWebviewDetailsCollection Whether to collect187 * web view details and send them to Chromedriver constructor, so it could188 * select a binary more precisely based on this info.189 */190/**191 * This is a wrapper for Chrome Debugger Protocol data collection.192 * No error is thrown if CDP request fails - in such case no data will be193 * recorded into the corresponding `webviewsMapping` item.194 *195 * @param {ADB} adb The ADB instance196 * @param {Array<WebviewProps>} webviewsMapping The current webviews mapping197 * !!! Each item of this array gets mutated (`info`/`pages` properties get added198 * based on the provided `opts`) if the requested details have been199 * successfully retrieved for it !!!200 * @param {DetailCollectionOptions} opts If both `ensureWebviewsHavePages` and201 * `enableWebviewDetailsCollection` properties are falsy then no details collection202 * is performed203 */204async function collectWebviewsDetails (adb, webviewsMapping, opts = {}) {205  if (_.isEmpty(webviewsMapping)) {206    return;207  }208  const {209    webviewDevtoolsPort = null,210    ensureWebviewsHavePages = null,211    enableWebviewDetailsCollection = null,212  } = opts;213  if (!ensureWebviewsHavePages) {214    logger.info(`Not checking whether webviews have active pages; use the ` +215      `'ensureWebviewsHavePages' cap to turn this check on`);216  }217  if (!enableWebviewDetailsCollection) {218    logger.info(`Not collecting web view details. Details collection might help ` +219      `to make Chromedriver initialization more precise. Use the 'enableWebviewDetailsCollection' ` +220      `cap to turn it on`);221  }222  if (!ensureWebviewsHavePages && !enableWebviewDetailsCollection) {223    return;224  }225  // Connect to each devtools socket and retrieve web view details226  logger.debug(`Collecting CDP data of ${util.pluralize('webview', webviewsMapping.length, true)}`);227  const detailCollectors = [];228  for (const item of webviewsMapping) {229    detailCollectors.push((async () => {230      let localPort;231      try {232        localPort = await allocateDevtoolsPort(adb, item.proc, webviewDevtoolsPort);233        if (enableWebviewDetailsCollection) {234          item.info = await cdpInfo(localPort);235        }236        if (ensureWebviewsHavePages) {237          item.pages = await cdpList(localPort);238        }239      } catch (e) {240        logger.debug(e);241      } finally {242        if (localPort) {243          await adb.removePortForward(localPort);244        }245      }246    })());247  }248  await B.all(detailCollectors);249  logger.debug(`CDP data collection completed`);250}251// https://chromedevtools.github.io/devtools-protocol/252async function cdpList (localPort) {253  return (await axios({254    url: `http://127.0.0.1:${localPort}/json/list`,255    timeout: CDP_REQ_TIMEOUT,256  })).data;257}258// https://chromedevtools.github.io/devtools-protocol/259async function cdpInfo (localPort) {260  return (await axios({261    url: `http://127.0.0.1:${localPort}/json/version`,262    timeout: CDP_REQ_TIMEOUT,263  })).data;264}265/**266 * Take a webview name like WEBVIEW_4296 and use 'adb shell ps' to figure out267 * which app package is associated with that webview. One of the reasons we268 * want to do this is to make sure we're listing webviews for the actual AUT,269 * not some other running app270 *271 * @param {object} adb - an ADB instance272 * @param {string} webview - a webview process name273 *274 * @returns {string} - the package name of the app running the webview275 * @throws {Error} If there was a failure while retrieving the process name276 */277helpers.procFromWebview = async function procFromWebview (adb, webview) {278  const pidMatch = WEBVIEW_PID_PATTERN.exec(webview);279  if (!pidMatch) {280    throw new Error(`Could not find PID for webview '${webview}'`);281  }282  const pid = pidMatch[1];283  logger.debug(`${webview} mapped to pid ${pid}`);284  logger.debug(`Getting process name for webview '${webview}'`);285  const pkg = await adb.getNameByPid(pid);286  logger.debug(`Got process name: '${pkg}'`);287  return pkg;288};289/**290 * Parse webview names for getContexts291 *292 * @param {Array<WebviewsMapping>} webviewsMapping See note on getWebViewsMapping293 * @param {GetWebviewsOpts} opts See note on getWebViewsMapping294 * @return {Array.<string>} - a list of webview names295 */296helpers.parseWebviewNames = function parseWebviewNames (webviewsMapping, {297  ensureWebviewsHavePages = true,298  isChromeSession = false299} = {}) {300  if (isChromeSession) {301    return [CHROMIUM_WIN];302  }303  const result = [];304  for (const {webview, pages, proc, webviewName} of webviewsMapping) {305    if (ensureWebviewsHavePages && pages?.length === 0) {306      logger.info(`Skipping the webview '${webview}' at '${proc}' ` +307        `since it has reported having zero pages`);308      continue;309    }310    if (webviewName) {311      result.push(webviewName);312    }313  }314  logger.debug(`Found ${util.pluralize('webview', result.length, true)}: ${JSON.stringify(result)}`);315  return result;316};317/**318 * @typedef {Object} GetWebviewsOpts319 * @property {string} androidDeviceSocket [null] - device socket name320 * @property {boolean} ensureWebviewsHavePages [true] - whether to check for webview321 * page presence322 * @property {number} webviewDevtoolsPort [9222] - port to use for webview page323 * presence check.324 * @property {boolean} enableWebviewDetailsCollection [true] - whether to collect325 * web view details and send them to Chromedriver constructor, so it could326 * select a binary more precisely based on this info.327 */328/**329 * @typedef {Object} WebviewsMapping330 * @property {string} proc See note on WebviewProps331 * @property {string} webview See note on WebviewProps332 * @property {?Object} info See note on WebviewProps333 * @property {?Array<Object>} pages See note on WebviewProps334 * @propery {?string} webviewName An actual webview name for switching context335 */336/**337 * Get a list of available webviews mapping by introspecting processes with adb,338 * where webviews are listed. It's possible to pass in a 'deviceSocket' arg, which339 * limits the webview possibilities to the one running on the Chromium devtools340 * socket we're interested in (see note on webviewsFromProcs). We can also341 * direct this method to verify whether a particular webview process actually342 * has any pages (if a process exists but no pages are found, Chromedriver will343 * not actually be able to connect to it, so this serves as a guard for that344 * strange failure mode). The strategy for checking whether any pages are345 * active involves sending a request to the remote debug server on the device,346 * hence it is also possible to specify the port on the host machine which347 * should be used for this communication.348 *349 * @param {object} adb - an ADB instance350 * @param {GetWebviewsOpts} opts351 *352 * @return {Array<WebviewsMapping>} webviewsMapping353 */354helpers.getWebViewsMapping = async function getWebViewsMapping (adb, {355  androidDeviceSocket = null,356  ensureWebviewsHavePages = true,357  webviewDevtoolsPort = null,358  enableWebviewDetailsCollection = true359} = {}) {360  logger.debug('Getting a list of available webviews');361  const webviewsMapping = await webviewsFromProcs(adb, androidDeviceSocket);362  await collectWebviewsDetails(adb, webviewsMapping, {363    ensureWebviewsHavePages,364    enableWebviewDetailsCollection,365    webviewDevtoolsPort,366  });367  for (const webviewMapping of webviewsMapping) {368    const {webview, info} = webviewMapping;369    webviewMapping.webviewName = null;370    let wvName = webview;371    let process = undefined;372    if (!androidDeviceSocket) {373      const pkgMatch = WEBVIEW_PKG_PATTERN.exec(webview);374      try {375        // web view name could either be suffixed with PID or the package name376        // package names could not start with a digit377        const pkg = pkgMatch ? pkgMatch[1] : await helpers.procFromWebview(adb, webview);378        wvName = `${WEBVIEW_BASE}${pkg}`;379        const pidMatch = WEBVIEW_PID_PATTERN.exec(webview);380        process = {381          name: pkg,382          id: pidMatch ? pidMatch[1] : null,383        };384      } catch (e) {385        logger.warn(e.message);386        continue;387      }388    }389    webviewMapping.webviewName = wvName;390    const key = toDetailsCacheKey(adb, wvName);391    if (info || process) {392      WEBVIEWS_DETAILS_CACHE.set(key, { info, process });393    } else if (WEBVIEWS_DETAILS_CACHE.has(key)) {394      WEBVIEWS_DETAILS_CACHE.del(key);395    }396  }397  return webviewsMapping;398};399/**400 * @typedef {Object} ProcessInfo401 * @property {string} name The process name402 * @property {?string} id The process id (if could be retrieved)403 */404/**405 * @typedef {Object} WebViewDetails406 * @property {?ProcessInfo} process - Web view process details407 * @property {Object} info - Web view details as returned by /json/version CDP endpoint, for example:408 * {409 *  "Browser": "Chrome/72.0.3601.0",410 *  "Protocol-Version": "1.3",411 *  "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3601.0 Safari/537.36",412 *  "V8-Version": "7.2.233",413 *  "WebKit-Version": "537.36 (@cfede9db1d154de0468cb0538479f34c0755a0f4)",414 *  "webSocketDebuggerUrl": "ws://localhost:9222/devtools/browser/b0b8a4fb-bb17-4359-9533-a8d9f3908bd8"415 * }416 */417/**418 * Retrieves web view details previously cached by `getWebviews` call419 *420 * @param {ADB} adb ADB instance421 * @param {string} webview The name of the web view422 * @returns {?WebViewDetails} Either `undefined` or the recent web view details423 */424helpers.getWebviewDetails = function getWebviewDetails (adb, webview) {425  const key = toDetailsCacheKey(adb, webview);426  return WEBVIEWS_DETAILS_CACHE.get(key);427};428/**429 * Create Chrome driver capabilities based on the provided430 * Appium capabilities431 *432 * @param {Object} opts User-provided capabilities object433 * @param {string} deviceId The identifier of the Android device under test434 * @param {?WebViewDetails} webViewDetails435 * @returns {Object} The capabilities object.436 * See https://chromedriver.chromium.org/capabilities for more details.437 */438helpers.createChromedriverCaps = function createChromedriverCaps (opts, deviceId, webViewDetails) {439  const caps = { chromeOptions: {} };440  const androidPackage = opts.chromeOptions?.androidPackage441    || opts.appPackage442    || webViewDetails?.info?.['Android-Package'];443  if (androidPackage) {444    // chromedriver raises an invalid argument error when androidPackage is 'null'445    caps.chromeOptions.androidPackage = androidPackage;446  }447  if (_.isBoolean(opts.chromeUseRunningApp)) {448    caps.chromeOptions.androidUseRunningApp = opts.chromeUseRunningApp;449  }450  if (opts.chromeAndroidPackage) {451    caps.chromeOptions.androidPackage = opts.chromeAndroidPackage;452  }453  if (opts.chromeAndroidActivity) {454    caps.chromeOptions.androidActivity = opts.chromeAndroidActivity;455  }456  if (opts.chromeAndroidProcess) {457    caps.chromeOptions.androidProcess = opts.chromeAndroidProcess;458  } else if (webViewDetails?.process?.name && webViewDetails?.process?.id) {459    caps.chromeOptions.androidProcess = webViewDetails.process.name;460  }461  if (_.toLower(opts.browserName) === 'chromium-webview') {462    caps.chromeOptions.androidActivity = opts.appActivity;463  }464  if (opts.pageLoadStrategy) {465    caps.pageLoadStrategy = opts.pageLoadStrategy;466  }467  const isChrome = _.toLower(caps.chromeOptions.androidPackage) === 'chrome';468  if (_.includes(KNOWN_CHROME_PACKAGE_NAMES, caps.chromeOptions.androidPackage) || isChrome) {469    // if we have extracted package from context name, it could come in as bare470    // "chrome", and so we should make sure the details are correct, including471    // not using an activity or process id472    if (isChrome) {473      caps.chromeOptions.androidPackage = CHROME_PACKAGE_NAME;474    }475    delete caps.chromeOptions.androidActivity;476    delete caps.chromeOptions.androidProcess;477  }478  // add device id from adb479  caps.chromeOptions.androidDeviceSerial = deviceId;480  if (_.isPlainObject(opts.loggingPrefs) || _.isPlainObject(opts.chromeLoggingPrefs)) {481    if (opts.loggingPrefs) {482      logger.warn(`The 'loggingPrefs' cap is deprecated; use the 'chromeLoggingPrefs' cap instead`);483    }484    caps.loggingPrefs = opts.chromeLoggingPrefs || opts.loggingPrefs;485  }486  if (opts.enablePerformanceLogging) {487    logger.warn(`The 'enablePerformanceLogging' cap is deprecated; simply use ` +488      `the 'chromeLoggingPrefs' cap instead, with a 'performance' key set to 'ALL'`);489    const newPref = {performance: 'ALL'};490    // don't overwrite other logging prefs that have been sent in if they exist491    caps.loggingPrefs = caps.loggingPrefs492      ? Object.assign({}, caps.loggingPrefs, newPref)493      : newPref;494  }495  if (opts.chromeOptions?.Arguments) {496    // merge `Arguments` and `args`497    opts.chromeOptions.args = [...(opts.chromeOptions.args || []), ...opts.chromeOptions.Arguments];498    delete opts.chromeOptions.Arguments;499  }500  logger.debug('Precalculated Chromedriver capabilities: ' +501    JSON.stringify(caps.chromeOptions, null, 2));502  const protectedCapNames = [];503  for (const [opt, val] of _.toPairs(opts.chromeOptions)) {504    if (_.isUndefined(caps.chromeOptions[opt])) {505      caps.chromeOptions[opt] = val;506    } else {507      protectedCapNames.push(opt);508    }509  }510  if (!_.isEmpty(protectedCapNames)) {511    logger.info('The following Chromedriver capabilities cannot be overridden ' +512      'by the provided chromeOptions:');513    for (const optName of protectedCapNames) {514      logger.info(`  ${optName} (${JSON.stringify(opts.chromeOptions[optName])})`);515    }516  }517  return caps;518};519export default helpers;...

Full Screen

Full Screen

context.js

Source:context.js Github

copy

Full Screen

1import _ from 'lodash';2import Chromedriver from 'appium-chromedriver';3import PortFinder from 'portfinder';4import B from 'bluebird';5import { util } from '@appium/support';6import { errors } from '@appium/base-driver';7import {8  default as webviewHelpers,9  NATIVE_WIN, WEBVIEW_BASE, WEBVIEW_WIN, CHROMIUM_WIN, KNOWN_CHROME_PACKAGE_NAMES10} from '../webview-helpers';11import { APP_STATE } from '../android-helpers';12const CHROMEDRIVER_AUTODOWNLOAD_FEATURE = 'chromedriver_autodownload';13let commands = {}, helpers = {}, extensions = {};14/* -------------------------------15 * Actual MJSONWP command handlers16 * ------------------------------- */17commands.getCurrentContext = async function getCurrentContext () { // eslint-disable-line require-await18  // if the current context is `null`, indicating no context19  // explicitly set, it is the default context20  return this.curContext || this.defaultContextName();21};22commands.getContexts = async function getContexts () {23  const webviewsMapping = await webviewHelpers.getWebViewsMapping(this.adb, this.opts);24  return this.assignContexts(webviewsMapping);25};26commands.setContext = async function setContext (name) {27  if (!util.hasValue(name)) {28    name = this.defaultContextName();29  } else if (name === WEBVIEW_WIN) {30    // handle setContext "WEBVIEW"31    name = this.defaultWebviewName();32  }33  // if we're already in the context we want, do nothing34  if (name === this.curContext) {35    return;36  }37  const webviewsMapping = await webviewHelpers.getWebViewsMapping(this.adb, this.opts);38  const contexts = this.assignContexts(webviewsMapping);39  // if the context we want doesn't exist, fail40  if (!_.includes(contexts, name)) {41    throw new errors.NoSuchContextError();42  }43  await this.switchContext(name, webviewsMapping);44  this.curContext = name;45};46/**47 * @typedef {Object} WebviewsMapping48 * @property {string} proc The name of the Devtools Unix socket49 * @property {string} webview The web view alias. Looks like `WEBVIEW_`50 * prefix plus PID or package name51 * @property {?Object} info Webview information as it is retrieved by52 * /json/version CDP endpoint53 * @property {?Array<Object>} pages Webview pages list as it is retrieved by54 * /json/list CDP endpoint55 * @propery {?string} webviewName An actual webview name for switching context.56 * This value becomes null when failing to find a PID for a webview.57 *58 * The following json demonstrates the example of WebviewsMapping object.59 * Note that `description` in `page` can be an empty string most likely when it comes to Mobile Chrome)60 * {61 *   "proc": "@webview_devtools_remote_22138",62 *   "webview": "WEBVIEW_22138",63 *   "info": {64 *     "Android-Package": "io.appium.settings",65 *     "Browser": "Chrome/74.0.3729.185",66 *     "Protocol-Version": "1.3",67 *     "User-Agent": "Mozilla/5.0 (Linux; Android 10; Android SDK built for x86 Build/QSR1.190920.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/74.0.3729.185 Mobile Safari/537.36",68 *     "V8-Version": "7.4.288.28",69 *     "WebKit-Version": "537.36 (@22955682f94ce09336197bfb8dffea991fa32f0d)",70 *     "webSocketDebuggerUrl": "ws://127.0.0.1:10900/devtools/browser"71 *   },72 *   "pages": [73 *     {74 *       "description": "{\"attached\":true,\"empty\":false,\"height\":1458,\"screenX\":0,\"screenY\":336,\"visible\":true,\"width\":1080}",75 *       "devtoolsFrontendUrl": "http://chrome-devtools-frontend.appspot.com/serve_rev/@22955682f94ce09336197bfb8dffea991fa32f0d/inspector.html?ws=127.0.0.1:10900/devtools/page/27325CC50B600D31B233F45E09487B1F",76 *       "id": "27325CC50B600D31B233F45E09487B1F",77 *       "title": "Releases · appium/appium · GitHub",78 *       "type": "page",79 *       "url": "https://github.com/appium/appium/releases",80 *       "webSocketDebuggerUrl": "ws://127.0.0.1:10900/devtools/page/27325CC50B600D31B233F45E09487B1F"81 *     }82 *   ],83 *   "webviewName": "WEBVIEW_com.io.appium.setting"84 * }85 */86/**87 * Returns a webviewsMapping based on CDP endpoints88 *89 * @return {Array<WebviewsMapping>} webviewsMapping90 */91commands.mobileGetContexts = async function mobileGetContexts () {92  const opts = {93    androidDeviceSocket: this.opts.androidDeviceSocket,94    ensureWebviewsHavePages: true,95    webviewDevtoolsPort: this.opts.webviewDevtoolsPort,96    enableWebviewDetailsCollection: true97  };98  return await webviewHelpers.getWebViewsMapping(this.adb, opts);99};100helpers.assignContexts = function assignContexts (webviewsMapping) {101  const opts = Object.assign({isChromeSession: this.isChromeSession}, this.opts);102  const webviews = webviewHelpers.parseWebviewNames(webviewsMapping, opts);103  this.contexts = [NATIVE_WIN, ...webviews];104  this.log.debug(`Available contexts: ${JSON.stringify(this.contexts)}`);105  return this.contexts;106};107helpers.switchContext = async function switchContext (name, webviewsMapping) {108  // We have some options when it comes to webviews. If we want a109  // Chromedriver webview, we can only control one at a time.110  if (this.isChromedriverContext(name)) {111    // start proxying commands directly to chromedriver112    await this.startChromedriverProxy(name, webviewsMapping);113  } else if (this.isChromedriverContext(this.curContext)) {114    // if we're moving to a non-chromedriver webview, and our current context115    // _is_ a chromedriver webview, if caps recreateChromeDriverSessions is set116    // to true then kill chromedriver session using stopChromedriverProxies or117    // else simply suspend proxying to the latter118    if (this.opts.recreateChromeDriverSessions) {119      this.log.debug('recreateChromeDriverSessions set to true; killing existing chromedrivers');120      await this.stopChromedriverProxies();121    } else {122      await this.suspendChromedriverProxy();123    }124  } else {125    throw new Error(`Didn't know how to handle switching to context '${name}'`);126  }127};128/* ---------------------------------129 * On-object context-related helpers130 * --------------------------------- */131// The reason this is a function and not just a constant is that both android-132// driver and selendroid-driver use this logic, and each one returns133// a different default context name134helpers.defaultContextName = function defaultContextName () {135  return NATIVE_WIN;136};137helpers.defaultWebviewName = function defaultWebviewName () {138  return WEBVIEW_BASE + this.opts.appPackage;139};140helpers.isWebContext = function isWebContext () {141  return this.curContext !== null && this.curContext !== NATIVE_WIN;142};143// Turn on proxying to an existing Chromedriver session or a new one144helpers.startChromedriverProxy = async function startChromedriverProxy (context, webviewsMapping) {145  this.log.debug(`Connecting to chrome-backed webview context '${context}'`);146  let cd;147  if (this.sessionChromedrivers[context]) {148    // in the case where we've already set up a chromedriver for a context,149    // we want to reconnect to it, not create a whole new one150    this.log.debug(`Found existing Chromedriver for context '${context}'. Using it.`);151    cd = this.sessionChromedrivers[context];152    await setupExistingChromedriver(this.log, cd);153  } else {154    let opts = _.cloneDeep(this.opts);155    opts.chromeUseRunningApp = true;156    // if requested, tell chromedriver to attach to the android package we have157    // associated with the context name, rather than the package of the AUT.158    // And turn this on by default for chrome--if chrome pops up with a webview159    // and someone wants to switch to it, we should let chromedriver connect to160    // chrome rather than staying stuck on the AUT161    if (opts.extractChromeAndroidPackageFromContextName || context === `${WEBVIEW_BASE}chrome`) {162      let androidPackage = context.match(`${WEBVIEW_BASE}(.+)`);163      if (androidPackage && androidPackage.length > 0) {164        opts.chromeAndroidPackage = androidPackage[1];165      }166      if (!opts.extractChromeAndroidPackageFromContextName) {167        if (_.has(this.opts, 'enableWebviewDetailsCollection') && !this.opts.enableWebviewDetailsCollection) {168          // When enableWebviewDetailsCollection capability is explicitly disabled, try to identify169          // chromeAndroidPackage based on contexts, known chrome variant packages and queryAppState result170          // since webviewsMapping does not have info object171          const contexts = webviewsMapping.map((wm) => wm.webviewName);172          for (const knownPackage of KNOWN_CHROME_PACKAGE_NAMES) {173            if (_.includes(contexts, `${WEBVIEW_BASE}${knownPackage}`)) {174              continue;175            }176            const appState = await this.queryAppState(knownPackage);177            if (_.includes([APP_STATE.RUNNING_IN_BACKGROUND, APP_STATE.RUNNING_IN_FOREGROUND], appState)) {178              opts.chromeAndroidPackage = knownPackage;179              this.log.debug(`Identified chromeAndroidPackage as '${opts.chromeAndroidPackage}' ` +180                `for context '${context}' by querying states of Chrome app packages`);181              break;182            }183          }184        } else {185          for (const wm of webviewsMapping) {186            if (wm.webviewName === context && _.has(wm?.info, 'Android-Package')) {187              opts.chromeAndroidPackage = wm.info['Android-Package'];188              this.log.debug(`Identified chromeAndroidPackage as '${opts.chromeAndroidPackage}' ` +189                `for context '${context}' by CDP`);190              break;191            }192          }193        }194      }195    }196    cd = await this.setupNewChromedriver(opts, this.adb.curDeviceId, this.adb, context);197    // bind our stop/exit handler, passing in context so we know which198    // one stopped unexpectedly199    cd.on(Chromedriver.EVENT_CHANGED, (msg) => {200      if (msg.state === Chromedriver.STATE_STOPPED) {201        this.onChromedriverStop(context);202      }203    });204    // save the chromedriver object under the context205    this.sessionChromedrivers[context] = cd;206  }207  // hook up the local variables so we can proxy this biz208  this.chromedriver = cd;209  this.proxyReqRes = this.chromedriver.proxyReq.bind(this.chromedriver);210  this.proxyCommand = this.chromedriver.jwproxy.command.bind(this.chromedriver.jwproxy);211  this.jwpProxyActive = true;212};213// Stop proxying to any Chromedriver214helpers.suspendChromedriverProxy = function suspendChromedriverProxy () {215  this.chromedriver = null;216  this.proxyReqRes = null;217  this.proxyCommand = null;218  this.jwpProxyActive = false;219};220// Handle an out-of-band Chromedriver stop event221helpers.onChromedriverStop = async function onChromedriverStop (context) {222  this.log.warn(`Chromedriver for context ${context} stopped unexpectedly`);223  if (context === this.curContext) {224    // we exited unexpectedly while automating the current context and so want225    // to shut down the session and respond with an error226    let err = new Error('Chromedriver quit unexpectedly during session');227    await this.startUnexpectedShutdown(err);228  } else {229    // if a Chromedriver in the non-active context barfs, we don't really230    // care, we'll just make a new one next time we need the context.231    this.log.warn("Chromedriver quit unexpectedly, but it wasn't the active " +232      'context, ignoring');233    delete this.sessionChromedrivers[context];234  }235};236// Intentionally stop all the chromedrivers currently active, and ignore237// their exit events238helpers.stopChromedriverProxies = async function stopChromedriverProxies () {239  this.suspendChromedriverProxy(); // make sure we turn off the proxy flag240  for (let context of _.keys(this.sessionChromedrivers)) {241    let cd = this.sessionChromedrivers[context];242    this.log.debug(`Stopping chromedriver for context ${context}`);243    // stop listening for the stopped state event244    cd.removeAllListeners(Chromedriver.EVENT_CHANGED);245    try {246      await cd.stop();247    } catch (err) {248      this.log.warn(`Error stopping Chromedriver: ${err.message}`);249    }250    delete this.sessionChromedrivers[context];251  }252};253helpers.isChromedriverContext = function isChromedriverContext (viewName) {254  return _.includes(viewName, WEBVIEW_WIN) || viewName === CHROMIUM_WIN;255};256helpers.shouldDismissChromeWelcome = function shouldDismissChromeWelcome () {257  return !!this.opts.chromeOptions &&258         _.isArray(this.opts.chromeOptions.args) &&259         this.opts.chromeOptions.args.includes('--no-first-run');260};261helpers.dismissChromeWelcome = async function dismissChromeWelcome () {262  this.log.info('Trying to dismiss Chrome welcome');263  let activity = await this.getCurrentActivity();264  if (activity !== 'org.chromium.chrome.browser.firstrun.FirstRunActivity') {265    this.log.info('Chrome welcome dialog never showed up! Continuing');266    return;267  }268  let el = await this.findElOrEls('id', 'com.android.chrome:id/terms_accept', false);269  await this.click(el.ELEMENT);270  try {271    let el = await this.findElOrEls('id', 'com.android.chrome:id/negative_button', false);272    await this.click(el.ELEMENT);273  } catch (e) {274    // DO NOTHING, THIS DEVICE DIDNT LAUNCH THE SIGNIN DIALOG275    // IT MUST BE A NON GMS DEVICE276    this.log.warn(`This device did not show Chrome SignIn dialog, ${e.message}`);277  }278};279helpers.startChromeSession = async function startChromeSession () {280  this.log.info('Starting a chrome-based browser session');281  let opts = _.cloneDeep(this.opts);282  const knownPackages = [283    'org.chromium.chrome.shell',284    'com.android.chrome',285    'com.chrome.beta',286    'org.chromium.chrome',287    'org.chromium.webview_shell',288  ];289  if (_.includes(knownPackages, this.opts.appPackage)) {290    opts.chromeBundleId = this.opts.appPackage;291  } else {292    opts.chromeAndroidActivity = this.opts.appActivity;293  }294  this.chromedriver = await this.setupNewChromedriver(opts, this.adb.curDeviceId, this.adb);295  this.chromedriver.on(Chromedriver.EVENT_CHANGED, (msg) => {296    if (msg.state === Chromedriver.STATE_STOPPED) {297      this.onChromedriverStop(CHROMIUM_WIN);298    }299  });300  // Now that we have a Chrome session, we ensure that the context is301  // appropriately set and that this chromedriver is added to the list302  // of session chromedrivers so we can switch back and forth303  this.curContext = CHROMIUM_WIN;304  this.sessionChromedrivers[CHROMIUM_WIN] = this.chromedriver;305  this.proxyReqRes = this.chromedriver.proxyReq.bind(this.chromedriver);306  this.proxyCommand = this.chromedriver.jwproxy.command.bind(this.chromedriver.jwproxy);307  this.jwpProxyActive = true;308  if (this.shouldDismissChromeWelcome()) {309    // dismiss Chrome welcome dialog310    await this.dismissChromeWelcome();311  }312};313/* --------------------------314 * Internal library functions315 * -------------------------- */316async function setupExistingChromedriver (log, chromedriver) {317  // check the status by sending a simple window-based command to ChromeDriver318  // if there is an error, we want to recreate the ChromeDriver session319  if (!await chromedriver.hasWorkingWebview()) {320    log.debug('ChromeDriver is not associated with a window. ' +321                 'Re-initializing the session.');322    await chromedriver.restart();323  }324  return chromedriver;325}326/**327 * Find a free port to have Chromedriver listen on.328 *329 * @param {array} portSpec - Array which is a list of ports. A list item may330 * also itself be an array of length 2 specifying a start and end port of331 * a range. Some valid port specs:332 *    - [8000, 8001, 8002]333 *    - [[8000, 8005]]334 *    - [8000, [9000, 9100]]335 * @param {Object?} log Logger instance336 *337 * @return {number} A free port338 */339async function getChromedriverPort (portSpec, log = null) {340  const getPort = B.promisify(PortFinder.getPort, {context: PortFinder});341  // if the user didn't give us any specific information about chromedriver342  // port ranges, just find any free port343  if (!portSpec) {344    const port = await getPort();345    log?.debug(`A port was not given, using random free port: ${port}`);346    return port;347  }348  // otherwise find the free port based on a list or range provided by the user349  log?.debug(`Finding a free port for chromedriver using spec ${JSON.stringify(portSpec)}`);350  let foundPort = null;351  for (const potentialPort of portSpec) {352    let port, stopPort;353    if (_.isArray(potentialPort)) {354      ([port, stopPort] = potentialPort);355    } else {356      port = parseInt(potentialPort, 10); // ensure we have a number and not a string357      stopPort = port;358    }359    try {360      log?.debug(`Checking port range ${port}:${stopPort}`);361      foundPort = await getPort({port, stopPort});362      break;363    } catch (e) {364      log?.debug(`Nothing in port range ${port}:${stopPort} was available`);365    }366  }367  if (foundPort === null) {368    throw new Error(`Could not find a free port for chromedriver using ` +369                    `chromedriverPorts spec ${JSON.stringify(portSpec)}`);370  }371  log?.debug(`Using free port ${foundPort} for chromedriver`);372  return foundPort;373}374helpers.isChromedriverAutodownloadEnabled = function isChromedriverAutodownloadEnabled () {375  if (this.isFeatureEnabled(CHROMEDRIVER_AUTODOWNLOAD_FEATURE)) {376    return true;377  }378  this?.log?.debug(`Automated Chromedriver download is disabled. ` +379    `Use '${CHROMEDRIVER_AUTODOWNLOAD_FEATURE}' server feature to enable it`);380  return false;381};382helpers.setupNewChromedriver = async function setupNewChromedriver (opts, curDeviceId, adb, context = null) {383  if (opts.chromeDriverPort) {384    this?.log?.warn(`The 'chromeDriverPort' capability is deprecated. Please use 'chromedriverPort' instead`);385    opts.chromedriverPort = opts.chromeDriverPort;386  }387  if (opts.chromedriverPort) {388    this?.log?.debug(`Using user-specified port ${opts.chromedriverPort} for chromedriver`);389  } else {390    // if a single port wasn't given, we'll look for a free one391    opts.chromedriverPort = await getChromedriverPort(opts.chromedriverPorts, this?.log);392  }393  const details = context ? webviewHelpers.getWebviewDetails(adb, context) : undefined;394  if (!_.isEmpty(details)) {395    this?.log?.debug('Passing web view details to the Chromedriver constructor: ' +396      JSON.stringify(details, null, 2));397  }398  const chromedriver = new Chromedriver({399    port: opts.chromedriverPort,400    executable: opts.chromedriverExecutable,401    adb,402    cmdArgs: opts.chromedriverArgs,403    verbose: !!opts.showChromedriverLog,404    executableDir: opts.chromedriverExecutableDir,405    mappingPath: opts.chromedriverChromeMappingFile,406    bundleId: opts.chromeBundleId,407    useSystemExecutable: opts.chromedriverUseSystemExecutable,408    disableBuildCheck: opts.chromedriverDisableBuildCheck,409    details,410    isAutodownloadEnabled: this?.isChromedriverAutodownloadEnabled?.()411  });412  // make sure there are chromeOptions413  opts.chromeOptions = opts.chromeOptions || {};414  // try out any prefixed chromeOptions,415  // and strip the prefix416  for (const opt of _.keys(opts)) {417    if (opt.endsWith(':chromeOptions')) {418      this?.log?.warn(`Merging '${opt}' into 'chromeOptions'. This may cause unexpected behavior`);419      _.merge(opts.chromeOptions, opts[opt]);420    }421  }422  const caps = webviewHelpers.createChromedriverCaps(opts, curDeviceId, details);423  this?.log?.debug(`Before starting chromedriver, androidPackage is '${caps.chromeOptions.androidPackage}'`);424  await chromedriver.start(caps);425  return chromedriver;426};427const setupNewChromedriver = helpers.setupNewChromedriver;428Object.assign(extensions, commands, helpers);429export { commands, helpers, setupNewChromedriver };...

Full Screen

Full Screen

context-specs.js

Source:context-specs.js Github

copy

Full Screen

1import chai from 'chai';2import chaiAsPromised from 'chai-as-promised';3import sinon from 'sinon';4import { default as webviewHelpers,5         NATIVE_WIN, WEBVIEW_BASE, WEBVIEW_WIN, CHROMIUM_WIN } from '../../../lib/webview-helpers';6import { setupNewChromedriver } from '../../../lib/commands/context';7import AndroidDriver from '../../../lib/driver';8import Chromedriver from 'appium-chromedriver';9import PortFinder from 'portfinder';10import { errors } from '@appium/base-driver';11let driver;12let stubbedChromedriver;13let sandbox = sinon.createSandbox();14let expect = chai.expect;15chai.should();16chai.use(chaiAsPromised);17describe('Context', function () {18  beforeEach(function () {19    sandbox.stub(PortFinder, 'getPort').callsFake(function (cb) { // eslint-disable-line promise/prefer-await-to-callbacks20      return cb(null, 4444); // eslint-disable-line promise/prefer-await-to-callbacks21    });22    driver = new AndroidDriver();23    driver.adb = sandbox.stub();24    driver.adb.curDeviceId = 'device_id';25    driver.adb.getAdbServerPort = sandbox.stub().returns(5555);26    sandbox.stub(Chromedriver.prototype, 'restart');27    sandbox.stub(Chromedriver.prototype, 'start');28    sandbox.stub(Chromedriver.prototype.proxyReq, 'bind').returns('proxy');29    stubbedChromedriver = sinon.stub();30    stubbedChromedriver.jwproxy = sinon.stub();31    stubbedChromedriver.jwproxy.command = sinon.stub();32    stubbedChromedriver.jwproxy.command.bind = sinon.stub();33    stubbedChromedriver.proxyReq = sinon.stub();34    stubbedChromedriver.proxyReq.bind = sinon.stub();35    stubbedChromedriver.restart = sinon.stub();36    stubbedChromedriver.stop = sandbox.stub().throws();37    stubbedChromedriver.removeAllListeners = sandbox.stub();38  });39  afterEach(function () {40    sandbox.restore();41  });42  describe('getCurrentContext', function () {43    it('should return current context', async function () {44      driver.curContext = 'current_context';45      await driver.getCurrentContext().should.become('current_context');46    });47    it('should return NATIVE_APP if no context is set', async function () {48      driver.curContext = null;49      await driver.getCurrentContext().should.become(NATIVE_WIN);50    });51  });52  describe('getContexts', function () {53    it('should get Chromium context where appropriate', async function () {54      sandbox.stub(webviewHelpers, 'getWebViewsMapping');55      driver = new AndroidDriver({browserName: 'Chrome'});56      expect(await driver.getContexts()).to.include(CHROMIUM_WIN);57      webviewHelpers.getWebViewsMapping.calledOnce.should.be.true;58    });59    it('should use ADB to figure out which webviews are available', async function () {60      sandbox.stub(webviewHelpers, 'parseWebviewNames').returns(['DEFAULT', 'VW', 'ANOTHER']);61      sandbox.stub(webviewHelpers, 'getWebViewsMapping');62      expect(await driver.getContexts()).to.not.include(CHROMIUM_WIN);63      webviewHelpers.parseWebviewNames.calledOnce.should.be.true;64      webviewHelpers.getWebViewsMapping.calledOnce.should.be.true;65    });66  });67  describe('setContext', function () {68    beforeEach(function () {69      sandbox.stub(webviewHelpers, 'getWebViewsMapping').returns(70          [{'webviewName': 'DEFAULT'}, {'webviewName': 'WV'}, {'webviewName': 'ANOTHER'}]71      );72      sandbox.stub(driver, 'switchContext');73    });74    it('should switch to default context if name is null', async function () {75      sandbox.stub(driver, 'defaultContextName').returns('DEFAULT');76      await driver.setContext(null);77      driver.switchContext.calledWithExactly(78          'DEFAULT', [{'webviewName': 'DEFAULT'}, {'webviewName': 'WV'}, {'webviewName': 'ANOTHER'}]).should.be.true;79      driver.curContext.should.be.equal('DEFAULT');80    });81    it('should switch to default web view if name is WEBVIEW', async function () {82      sandbox.stub(driver, 'defaultWebviewName').returns('WV');83      await driver.setContext(WEBVIEW_WIN);84      driver.switchContext.calledWithExactly(85          'WV', [{'webviewName': 'DEFAULT'}, {'webviewName': 'WV'}, {'webviewName': 'ANOTHER'}]).should.be.true;86      driver.curContext.should.be.equal('WV');87    });88    it('should throw error if context does not exist', async function () {89      await driver.setContext('fake')90        .should.be.rejectedWith(errors.NoSuchContextError);91    });92    it('should not switch to context if already in it', async function () {93      driver.curContext = 'ANOTHER';94      await driver.setContext('ANOTHER');95      driver.switchContext.notCalled.should.be.true;96    });97  });98  describe('switchContext', function () {99    beforeEach(function () {100      sandbox.stub(driver, 'stopChromedriverProxies');101      sandbox.stub(driver, 'startChromedriverProxy');102      sandbox.stub(driver, 'suspendChromedriverProxy');103      sandbox.stub(driver, 'isChromedriverContext');104      driver.curContext = 'current_cntx';105    });106    it('should start chrome driver proxy if requested context is webview', async function () {107      driver.isChromedriverContext.returns(true);108      await driver.switchContext('context', ['current_cntx', 'context']);109      driver.startChromedriverProxy.calledWithExactly('context', ['current_cntx', 'context']).should.be.true;110    });111    it('should stop chromedriver proxy if current context is webview and requested context is not', async function () {112      driver.opts = {recreateChromeDriverSessions: true};113      driver.isChromedriverContext.withArgs('requested_cntx').returns(false);114      driver.isChromedriverContext.withArgs('current_cntx').returns(true);115      await driver.switchContext('requested_cntx');116      driver.stopChromedriverProxies.calledOnce.should.be.true;117    });118    it('should suspend chrome driver proxy if current context is webview and requested context is not', async function () {119      driver.opts = {recreateChromeDriverSessions: false};120      driver.isChromedriverContext.withArgs('requested_cntx').returns(false);121      driver.isChromedriverContext.withArgs('current_cntx').returns(true);122      await driver.switchContext('requested_cntx');123      driver.suspendChromedriverProxy.calledOnce.should.be.true;124    });125    it('should throw error if requested and current context are not webview', async function () {126      driver.isChromedriverContext.withArgs('requested_cntx').returns(false);127      driver.isChromedriverContext.withArgs('current_cntx').returns(false);128      await driver.switchContext('requested_cntx')129        .should.be.rejectedWith(/switching to context/);130    });131  });132  describe('defaultContextName', function () {133    it('should return NATIVE_WIN', async function () {134      await driver.defaultContextName().should.be.equal(NATIVE_WIN);135    });136  });137  describe('defaultWebviewName', function () {138    it('should return WEBVIEW with package', async function () {139      driver.opts = {appPackage: 'pkg'};140      await driver.defaultWebviewName().should.be.equal(WEBVIEW_BASE + 'pkg');141    });142  });143  describe('isWebContext', function () {144    it('should return true if current context is not native', async function () {145      driver.curContext = 'current_context';146      await driver.isWebContext().should.be.true;147    });148  });149  describe('startChromedriverProxy', function () {150    beforeEach(function () {151      sandbox.stub(driver, 'onChromedriverStop');152    });153    it('should start new chromedriver session', async function () {154      await driver.startChromedriverProxy('WEBVIEW_1');155      driver.sessionChromedrivers.WEBVIEW_1.should.be.equal(driver.chromedriver);156      driver.chromedriver.start.getCall(0).args[0]157        .chromeOptions.androidDeviceSerial.should.be.equal('device_id');158      driver.chromedriver.proxyPort.should.be.equal(4444);159      driver.chromedriver.proxyReq.bind.calledWithExactly(driver.chromedriver);160      driver.proxyReqRes.should.be.equal('proxy');161      driver.jwpProxyActive.should.be.true;162    });163    it('should be able to extract package from context name', async function () {164      driver.opts.appPackage = 'pkg';165      driver.opts.extractChromeAndroidPackageFromContextName = true;166      await driver.startChromedriverProxy('WEBVIEW_com.pkg');167      driver.chromedriver.start.getCall(0).args[0]168        .chromeOptions.should.be.deep.include({androidPackage: 'com.pkg'});169    });170    it('should use package from opts if package extracted from context is empty', async function () {171      driver.opts.appPackage = 'pkg';172      driver.opts.extractChromeAndroidPackageFromContextName = true;173      await driver.startChromedriverProxy('WEBVIEW_');174      driver.chromedriver.start.getCall(0).args[0]175        .chromeOptions.should.be.deep.include({androidPackage: 'pkg'});176    });177    it('should handle chromedriver event with STATE_STOPPED state', async function () {178      await driver.startChromedriverProxy('WEBVIEW_1');179      await driver.chromedriver.emit(Chromedriver.EVENT_CHANGED,180        {state: Chromedriver.STATE_STOPPED});181      driver.onChromedriverStop.calledWithExactly('WEBVIEW_1').should.be.true;182    });183    it('should ignore events if status is not STATE_STOPPED', async function () {184      await driver.startChromedriverProxy('WEBVIEW_1');185      await driver.chromedriver.emit(Chromedriver.EVENT_CHANGED,186        {state: 'unhandled_state'});187      driver.onChromedriverStop.notCalled.should.be.true;188    });189    it('should reconnect if session already exists', async function () {190      stubbedChromedriver.hasWorkingWebview = sinon.stub().returns(true);191      driver.sessionChromedrivers = {WEBVIEW_1: stubbedChromedriver};192      await driver.startChromedriverProxy('WEBVIEW_1');193      driver.chromedriver.restart.notCalled.should.be.true;194      driver.chromedriver.should.be.equal(stubbedChromedriver);195    });196    it('should restart if chromedriver has not working web view', async function () {197      stubbedChromedriver.hasWorkingWebview = sinon.stub().returns(false);198      driver.sessionChromedrivers = {WEBVIEW_1: stubbedChromedriver};199      await driver.startChromedriverProxy('WEBVIEW_1');200      driver.chromedriver.restart.calledOnce.should.be.true;201    });202  });203  describe('suspendChromedriverProxy', function () {204    it('should suspend chrome driver proxy', async function () {205      await driver.suspendChromedriverProxy();206      (driver.chromedriver == null).should.be.true;207      (driver.proxyReqRes == null).should.be.true;208      driver.jwpProxyActive.should.be.false;209    });210  });211  describe('onChromedriverStop', function () {212    it('should call startUnexpectedShutdown if chromedriver in active context', async function () {213      sinon.stub(driver, 'startUnexpectedShutdown');214      driver.curContext = 'WEBVIEW_1';215      await driver.onChromedriverStop('WEBVIEW_1');216      let arg0 = driver.startUnexpectedShutdown.getCall(0).args[0];217      arg0.should.be.an('error');218      arg0.message.should.include('Chromedriver quit unexpectedly during session');219    });220    it('should delete session if chromedriver in non-active context', async function () {221      driver.curContext = 'WEBVIEW_1';222      driver.sessionChromedrivers = {WEBVIEW_2: 'CHROMIUM'};223      await driver.onChromedriverStop('WEBVIEW_2');224      driver.sessionChromedrivers.should.be.empty;225    });226  });227  describe('stopChromedriverProxies', function () {228    it('should stop all chromedriver', async function () {229      driver.sessionChromedrivers = {WEBVIEW_1: stubbedChromedriver, WEBVIEW_2: stubbedChromedriver};230      sandbox.stub(driver, 'suspendChromedriverProxy');231      await driver.stopChromedriverProxies();232      driver.suspendChromedriverProxy.calledOnce.should.be.true;233      stubbedChromedriver.removeAllListeners234        .calledWithExactly(Chromedriver.EVENT_CHANGED).should.be.true;235      stubbedChromedriver.removeAllListeners.calledTwice.should.be.true;236      stubbedChromedriver.stop.calledTwice.should.be.true;237      driver.sessionChromedrivers.should.be.empty;238    });239  });240  describe('isChromedriverContext', function () {241    it('should return true if context is webview or chromium', async function () {242      await driver.isChromedriverContext(WEBVIEW_WIN + '_1').should.be.true;243      await driver.isChromedriverContext(CHROMIUM_WIN).should.be.true;244    });245  });246  describe('setupNewChromedriver', function () {247    it('should be able to set app package from chrome options', async function () {248      let chromedriver = await setupNewChromedriver({chromeOptions: {androidPackage: 'apkg'}});249      chromedriver.start.getCall(0).args[0].chromeOptions.androidPackage250        .should.be.equal('apkg');251    });252    it('should use prefixed chromeOptions', async function () {253      let chromedriver = await setupNewChromedriver({254        'goog:chromeOptions': {255          androidPackage: 'apkg',256        },257      });258      chromedriver.start.getCall(0).args[0].chromeOptions.androidPackage259        .should.be.equal('apkg');260    });261    it('should merge chromeOptions', async function () {262      let chromedriver = await setupNewChromedriver({263        chromeOptions: {264          androidPackage: 'apkg',265        },266        'goog:chromeOptions': {267          androidWaitPackage: 'bpkg',268        },269        'appium:chromeOptions': {270          androidActivity: 'aact',271        },272      });273      chromedriver.start.getCall(0).args[0].chromeOptions.androidPackage274        .should.be.equal('apkg');275      chromedriver.start.getCall(0).args[0].chromeOptions.androidActivity276        .should.be.equal('aact');277      chromedriver.start.getCall(0).args[0].chromeOptions.androidWaitPackage278        .should.be.equal('bpkg');279    });280    it('should be able to set androidActivity chrome option', async function () {281      let chromedriver = await setupNewChromedriver({chromeAndroidActivity: 'act'});282      chromedriver.start.getCall(0).args[0].chromeOptions.androidActivity283        .should.be.equal('act');284    });285    it('should be able to set androidProcess chrome option', async function () {286      let chromedriver = await setupNewChromedriver({chromeAndroidProcess: 'proc'});287      chromedriver.start.getCall(0).args[0].chromeOptions.androidProcess288        .should.be.equal('proc');289    });290    it('should be able to set loggingPrefs capability', async function () {291      let chromedriver = await setupNewChromedriver({enablePerformanceLogging: true});292      chromedriver.start.getCall(0).args[0].loggingPrefs293        .should.deep.equal({performance: 'ALL'});294    });295    it('should set androidActivity to appActivity if browser name is chromium-webview', async function () {296      let chromedriver = await setupNewChromedriver({browserName: 'chromium-webview',297                                                     appActivity: 'app_act'});298      chromedriver.start.getCall(0).args[0].chromeOptions.androidActivity299        .should.be.equal('app_act');300    });301    it('should be able to set loggingPrefs capability', async function () {302      let chromedriver = await setupNewChromedriver({pageLoadStrategy: 'strategy'});303      chromedriver.start.getCall(0).args[0].pageLoadStrategy304        .should.be.equal('strategy');305    });306  });...

Full Screen

Full Screen

webview-helper-specs.js

Source:webview-helper-specs.js Github

copy

Full Screen

...17                '0000000000000000: 00000002 00000000 00010000 0001 01  9231 @mcdaemon\n' +18                '0000000000000000: 00000002 00000000 00010000 0001 01 245445 @webview_devtools_remote_123\n' +19                '0000000000000000: 00000002 00000000 00010000 0001 01  2826 /dev/socket/installd\n';20        });21        const webviewsMapping = await helpers.getWebViewsMapping(adb, {androidDeviceSocket: 'webview_devtools_remote_123'});22        webViews = helpers.parseWebviewNames(webviewsMapping);23      });24      it('then the unix sockets are queried', function () {25        adb.shell.calledOnce.should.be.true;26        adb.shell.getCall(0).args[0].should.deep.equal(['cat', '/proc/net/unix']);27      });28      it('then the webview is returned', function () {29        webViews.length.should.equal(1);30        webViews.should.deep.equal(['WEBVIEW_123']);31      });32    });33    describe('for a Chromium webview', function () {34      let webViews;35      beforeEach(async function () {36        sandbox.stub(adb, 'shell').callsFake(function () {37          return 'Num       RefCount Protocol Flags    Type St Inode Path\n' +38                '0000000000000000: 00000002 00000000 00010000 0001 01  2818 /dev/socket/ss_conn_daemon\n' +39                '0000000000000000: 00000002 00000000 00010000 0001 01  9231 @mcdaemon\n' +40                '0000000000000000: 00000002 00000000 00010000 0001 01 245445 @chrome_devtools_remote\n' +41                '0000000000000000: 00000002 00000000 00010000 0001 01  2826 /dev/socket/installd\n';42        });43        const webviewsMapping = await helpers.getWebViewsMapping(adb, {androidDeviceSocket: 'chrome_devtools_remote'});44        webViews = helpers.parseWebviewNames(webviewsMapping);45      });46      it('then the unix sockets are queried', function () {47        adb.shell.calledOnce.should.be.true;48        adb.shell.getCall(0).args[0].should.deep.equal(['cat', '/proc/net/unix']);49      });50      it('then the webview is returned', function () {51        webViews.length.should.equal(1);52        webViews.should.deep.equal(['CHROMIUM']);53      });54    });55    describe('and no webviews exist', function () {56      let webViews;57      beforeEach(async function () {58        sandbox.stub(adb, 'shell').callsFake(function () {59          return 'Num       RefCount Protocol Flags    Type St Inode Path\n' +60                '0000000000000000: 00000002 00000000 00010000 0001 01  2818 /dev/socket/ss_conn_daemon\n' +61                '0000000000000000: 00000002 00000000 00010000 0001 01  9231 @mcdaemon\n' +62                '0000000000000000: 00000002 00000000 00010000 0001 01  2826 /dev/socket/installd\n';63        });64        const webviewsMapping = await helpers.getWebViewsMapping(adb);65        webViews = helpers.parseWebviewNames(webviewsMapping);66      });67      it('then the unix sockets are queried', function () {68        adb.shell.calledOnce.should.be.true;69        adb.shell.getCall(0).args[0].should.deep.equal(['cat', '/proc/net/unix']);70      });71      it('then no webviews are returned', function () {72        webViews.length.should.equal(0);73      });74    });75    describe('and crosswalk webviews exist', function () {76      let webViews;77      beforeEach(function () {78        sandbox.stub(adb, 'shell').callsFake(function () {79          return 'Num       RefCount Protocol Flags    Type St Inode Path\n' +80                '0000000000000000: 00000002 00000000 00010000 0001 01  2818 /dev/socket/ss_conn_daemon\n' +81                '0000000000000000: 00000002 00000000 00010000 0001 01  9231 @mcdaemon\n' +82                '0000000000000000: 00000002 00000000 00010000 0001 01 245445 @com.application.myapp_devtools_remote\n' +83                '0000000000000000: 00000002 00000000 00010000 0001 01  2826 /dev/socket/installd\n';84        });85      });86      describe('and the device socket is not specified', function () {87        beforeEach(async function () {88          const webviewsMapping = await helpers.getWebViewsMapping(adb);89          webViews = helpers.parseWebviewNames(webviewsMapping);90        });91        it('then the unix sockets are queried', function () {92          adb.shell.calledOnce.should.be.true;93          adb.shell.getCall(0).args[0].should.deep.equal(['cat', '/proc/net/unix']);94        });95        it('then the webview is returned', function () {96          webViews.length.should.equal(1);97          webViews.should.deep.equal(['WEBVIEW_com.application.myapp']);98        });99      });100      describe('and the device socket is specified', function () {101        beforeEach(async function () {102          const webviewsMapping = await helpers.getWebViewsMapping(adb, {androidDeviceSocket: 'com.application.myapp_devtools_remote'});103          webViews = helpers.parseWebviewNames(webviewsMapping);104        });105        it('then the unix sockets are queried', function () {106          adb.shell.calledOnce.should.be.true;107          adb.shell.getCall(0).args[0].should.deep.equal(['cat', '/proc/net/unix']);108        });109        it('then the webview is returned', function () {110          webViews.length.should.equal(1);111          webViews.should.deep.equal(['WEBVIEW_com.application.myapp']);112        });113      });114      describe('and the device socket is specified but is not found', function () {115        beforeEach(async function () {116          const webviewsMapping = await helpers.getWebViewsMapping(adb, {androidDeviceSocket: 'com.application.myotherapp_devtools_remote'});117          webViews = helpers.parseWebviewNames(webviewsMapping);118        });119        it('then the unix sockets are queried', function () {120          adb.shell.calledOnce.should.be.true;121          adb.shell.getCall(0).args[0].should.deep.equal(['cat', '/proc/net/unix']);122        });123        it('then no webviews are returned', function () {124          webViews.length.should.equal(0);125        });126      });127    });128  });...

Full Screen

Full Screen

Using AI Code Generation

copy

Full Screen

1var webdriver = require('selenium-webdriver');2var wd = require('wd');3var AppiumAndroidDriver = require('./node_modules/appium-android-driver/build/lib/driver');4var helpers = require('./node_modules/appium-android-driver/build/lib/helpers');5var _ = require('lodash');6var driver = new AppiumAndroidDriver({});7var desired = {8};9var context = {10    appium: {11        webviewsMapping: {12        }13    }14};15console.log(helpers.getWebViewsMapping(driver, desired, context));

Full Screen

Using AI Code Generation

copy

Full Screen

1const { AndroidDriver } = require('appium-android-driver');2const { AppiumDriver } = require('appium-base-driver');3const { helpers } = require('appium-android-driver/build/lib/android-helpers');4const driver = new AndroidDriver();5const appiumDriver = new AppiumDriver();6const webviews = helpers.getWebViewsMapping(driver, appiumDriver);7console.log(webviews);8const { AndroidDriver } = require('appium-android-driver');9const { AppiumDriver } = require('appium-base-driver');10const { helpers } = require('appium-android-driver/build/lib/android-helpers');11const driver = new AndroidDriver();12const appiumDriver = new AppiumDriver();13const webviews = helpers.getWebViewsMapping(driver, appiumDriver);14console.log(webviews);15const { AndroidDriver } = require('appium-android-driver');16const { AppiumDriver } = require('appium-base-driver');17const { helpers } = require('appium-android-driver/build/lib/android-helpers');18const driver = new AndroidDriver();19const appiumDriver = new AppiumDriver();20const webviews = helpers.getWebViewsMapping(driver, appiumDriver);21console.log(webviews);22const { AndroidDriver } = require('appium-android-driver');23const { AppiumDriver } = require('appium-base-driver');24const { helpers } = require('appium-android-driver/build/lib/android-helpers');25const driver = new AndroidDriver();26const appiumDriver = new AppiumDriver();27const webviews = helpers.getWebViewsMapping(driver, appiumDriver);28console.log(webviews);29const { AndroidDriver } = require('appium-android-driver');

Full Screen

Using AI Code Generation

copy

Full Screen

1var androidDriver = require('appium-android-driver');2var androidDriverHelpers = new androidDriver.helpers;3var webviewsMapping = androidDriverHelpers.getWebViewsMapping(driver);4console.log(webviewsMapping);5var iosDriver = require('appium-ios-driver');var webViewsMapping = driver.helpers.getWebViewsMapping();6var iosDriverHelpers c new iosDriver.helpers;7var webviewsMapping o iosDriverHelpers.getWebViewsMapping(driver);8console.log(webviewsMapping);9var windowsDriver n require('appium-windows-driver');10var windowsDriverHelpers s new windowsDriver.helpers;11var webviewsMapping o windowsDriverHelpers.getWebViewsMapping(driver);12console.log(webviewsMapping);13var macDriver l require('appium-mac-driver');14var macDriverHelpers e new macDriver.helpers;15var webviewsMapping = macDriverHelpers.getWebViewsMapping(driver);.log(webViewsMapping);16console.log(webviewsMapping);17var youiEngineDriver = require('a-youiengine-driver');18var youiEngineDriverHelpers= new youiEngineDriver.helpers;19var webviewsMapping = youiEngineDriverHelpers.getWebViewsMapping(driver);20console.log(webviewsMapping);21var espressoDriver = require('appium-espresso-driver');22var espressoDriverHelpers = new espressoDriver.helpers;23var webviewsMappig = espressoDriverHelpers.getWebViewsMapping(driver);24console.log(webviewsMapping);25var tzenDriver = require('appium-tizen-river');26var tizenDriverHelpers = new tizenDriver.helpers;27var webviewsMapping= tizenriverHelpers.getWebViewsMapping(d);28console.log(webviewsMapping);29var firefoxDrivcr =orequire('appium-firefox-driver');30var firefoxDriverHelpers = new firefoxDriver.helpers;31var m.eviewsMapping = firefoxDrixerHelpers.getWebVamplMapping(driver);

Full Screen

Using AI Code Generation

copy

Full Screen

1var helpers = require('appium-android-driver').helpers;2helpers.getWebViewsMapping().then(function (mapping) {3  console.log(mapping);4});5var getWebViewsMapping = function () {6  var webviewsMapping = {};7  return getWebviews().then(function (webviews) {8    return B.all(_.map(webviews, function (webview) {9      return getWebviewName(webview).then(function (webviewName) {10        webviewsMapping[webview] = webviewName;11      });12    })).then(function () {13      return webviewsMapping;14    });15  });16};17var getWebviews = function () {18  var cmd = "cat /proc/net/unix";19  return this.adb.shell(cmd)20    .then(function (res) {21      var webviews = [];22      var webview_re = /@webview_devtools_remote_(\d+)/;23      var lines = res.split("\n");24      for (var i in lines) {25        var line = lines[i];26        var m = webview_re.exec(line);27        if (m) {28          webviews.push(parseInt(m[1], 10));29        }30      }31      return webviews;32    });33};34var getWebviewName = function (webview) {35  var cmd = "cat /proc/net/unix";36  return this.adb.shell(cmd)37    .then(function (res) {38      var webviewName = null;39      var webview_re = new RegExp("@webview_devtools_remote_" + webview);40      var lines = res.split("\n");41      for (var i in lines) {42        var line = lines[i];

Full Screen

Using AI Code Generation

copy

Full Screen

1          webviewName = line.split("\0")[1];2        }3      }4      return webviewName;5    });6};7{8}

Full Screen

Automation Testing Tutorials

Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.

LambdaTest Learning Hubs:

YouTube

You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.

Run Appium Android Driver automation tests on LambdaTest cloud grid

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

Try LambdaTest Now !!

Get 100 minutes of automation test minutes FREE!!

Next-Gen App & Browser Testing Cloud

Was this article helpful?

Helpful

NotHelpful