UNPKG

appium-espresso-driver

Version:
689 lines 35.2 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.EspressoDriver = void 0; const lodash_1 = __importDefault(require("lodash")); const path_1 = __importDefault(require("path")); const bluebird_1 = __importDefault(require("bluebird")); const driver_1 = require("appium/driver"); const espresso_runner_1 = require("./espresso-runner"); const support_1 = require("appium/support"); const appManagementCmds = __importStar(require("./commands/app-management")); const contextCmds = __importStar(require("./commands/context")); const elementCmds = __importStar(require("./commands/element")); const miscCmds = __importStar(require("./commands/misc")); const servicesCmds = __importStar(require("./commands/services")); const screenshotCmds = __importStar(require("./commands/screenshot")); const idlingResourcesCmds = __importStar(require("./commands/idling-resources")); const actionsCmds = __importStar(require("./commands/actions")); const clipboardCmds = __importStar(require("./commands/clipboard")); const appium_adb_1 = require("appium-adb"); const appium_android_driver_1 = require("appium-android-driver"); const io_appium_settings_1 = require("io.appium.settings"); const constraints_1 = require("./constraints"); const portscanner_1 = require("portscanner"); const asyncbox_1 = require("asyncbox"); const utils_1 = require("./utils"); const method_map_1 = require("./method-map"); const execute_method_map_1 = require("./execute-method-map"); // The range of ports we can use on the system for communicating to the // Espresso HTTP server on the device const SYSTEM_PORT_RANGE = [8300, 8399]; // This is the port that the espresso server listens to on the device. We will // forward one of the ports above on the system to this port on the device. const DEVICE_PORT = 6791; // NO_PROXY contains the paths that we never want to proxy to espresso server. // TODO: Add the list of paths that we never want to proxy to espresso server. // TODO: Need to segregate the paths better way using regular expressions wherever applicable. // (Not segregating right away because more paths to be added in the NO_PROXY list) const NO_PROXY = [ ['GET', new RegExp('^/session/(?!.*/)')], ['GET', new RegExp('^/session/[^/]+/appium/capabilities')], ['GET', new RegExp('^/session/[^/]+/appium/commands')], ['GET', new RegExp('^/session/[^/]+/appium/device/current_activity')], ['GET', new RegExp('^/session/[^/]+/appium/device/current_package')], ['GET', new RegExp('^/session/[^/]+/appium/device/display_density')], ['GET', new RegExp('^/session/[^/]+/appium/device/is_keyboard_shown')], ['GET', new RegExp('^/session/[^/]+/appium/device/system_bars')], ['GET', new RegExp('^/session/[^/]+/appium/device/system_time')], ['GET', new RegExp('^/session/[^/]+/appium/extensions')], ['GET', new RegExp('^/session/[^/]+/appium/settings')], ['GET', new RegExp('^/session/[^/]+/context')], ['GET', new RegExp('^/session/[^/]+/contexts')], ['GET', new RegExp('^/session/[^/]+/ime/[^/]+')], ['GET', new RegExp('^/session/[^/]+/network_connection')], ['GET', new RegExp('^/session/[^/]+/timeouts')], ['GET', new RegExp('^/session/[^/]+/url')], ['POST', new RegExp('^/session/[^/]+/appium/app/background')], ['POST', new RegExp('^/session/[^/]+/appium/app/close')], ['POST', new RegExp('^/session/[^/]+/appium/app/launch')], ['POST', new RegExp('^/session/[^/]+/appium/app/reset')], ['POST', new RegExp('^/session/[^/]+/appium/app/strings')], ['POST', new RegExp('^/session/[^/]+/appium/compare_images')], ['POST', new RegExp('^/session/[^/]+/appium/device/activate_app')], ['POST', new RegExp('^/session/[^/]+/appium/device/app_installed')], ['POST', new RegExp('^/session/[^/]+/appium/device/app_state')], ['POST', new RegExp('^/session/[^/]+/appium/device/finger_print')], ['POST', new RegExp('^/session/[^/]+/appium/device/get_clipboard')], ['POST', new RegExp('^/session/[^/]+/appium/device/install_app')], ['POST', new RegExp('^/session/[^/]+/appium/device/is_locked')], ['POST', new RegExp('^/session/[^/]+/appium/device/lock')], ['POST', new RegExp('^/session/[^/]+/appium/device/pull_file')], ['POST', new RegExp('^/session/[^/]+/appium/device/pull_folder')], ['POST', new RegExp('^/session/[^/]+/appium/device/push_file')], ['POST', new RegExp('^/session/[^/]+/appium/device/remove_app')], ['POST', new RegExp('^/session/[^/]+/appium/device/start_activity')], ['POST', new RegExp('^/session/[^/]+/appium/device/terminate_app')], ['POST', new RegExp('^/session/[^/]+/appium/device/unlock')], ['POST', new RegExp('^/session/[^/]+/appium/getPerformanceData')], ['POST', new RegExp('^/session/[^/]+/appium/performanceData/types')], ['POST', new RegExp('^/session/[^/]+/appium/settings')], ['POST', new RegExp('^/session/[^/]+/appium/execute_driver')], ['POST', new RegExp('^/session/[^/]+/appium/start_recording_screen')], ['POST', new RegExp('^/session/[^/]+/appium/stop_recording_screen')], ['POST', new RegExp('^/session/[^/]+/context')], ['POST', new RegExp('^/session/[^/]+/execute')], ['POST', new RegExp('^/session/[^/]+/execute/async')], ['POST', new RegExp('^/session/[^/]+/execute/sync')], ['POST', new RegExp('^/session/[^/]+/execute_async')], ['POST', new RegExp('^/session/[^/]+/ime/[^/]+')], ['POST', new RegExp('^/session/[^/]+/location')], ['POST', new RegExp('^/session/[^/]+/network_connection')], ['POST', new RegExp('^/session/[^/]+/timeouts')], ['POST', new RegExp('^/session/[^/]+/url')], // MJSONWP commands ['GET', new RegExp('^/session/[^/]+/log/types')], ['POST', new RegExp('^/session/[^/]+/log')], // W3C commands // For Selenium v4 (W3C does not have this route) ['GET', new RegExp('^/session/[^/]+/se/log/types')], // For Selenium v4 (W3C does not have this route) ['POST', new RegExp('^/session/[^/]+/se/log')], ]; // This is a set of methods and paths that we never want to proxy to Chromedriver. const CHROME_NO_PROXY = [ ['GET', new RegExp('^/session/[^/]+/appium')], ['GET', new RegExp('^/session/[^/]+/context')], ['GET', new RegExp('^/session/[^/]+/element/[^/]+/rect')], ['GET', new RegExp('^/session/[^/]+/orientation')], ['POST', new RegExp('^/session/[^/]+/appium')], ['POST', new RegExp('^/session/[^/]+/context')], ['POST', new RegExp('^/session/[^/]+/orientation')], ['POST', new RegExp('^/session/[^/]+/touch/multi/perform')], ['POST', new RegExp('^/session/[^/]+/touch/perform')], // this is needed to make the mobile: commands working in web context ['POST', new RegExp('^/session/[^/]+/execute$')], ['POST', new RegExp('^/session/[^/]+/execute/sync')], // MJSONWP commands ['GET', new RegExp('^/session/[^/]+/log/types')], ['POST', new RegExp('^/session/[^/]+/log')], // W3C commands // For Selenium v4 (W3C does not have this route) ['GET', new RegExp('^/session/[^/]+/se/log/types')], // For Selenium v4 (W3C does not have this route) ['POST', new RegExp('^/session/[^/]+/se/log')], ]; const APK_EXT = '.apk'; const AAB_EXT = '.aab'; const SUPPORTED_EXTENSIONS = [APK_EXT, AAB_EXT]; class EspressoDriver extends appium_android_driver_1.AndroidDriver { constructor(opts = {}, shouldValidateCaps = true) { // `shell` overwrites adb.shell, so remove // @ts-expect-error FIXME: what is this? delete opts.shell; super(opts, shouldValidateCaps); // @ts-ignore It's expected this.performActions = actionsCmds.performActions; this.startActivity = appManagementCmds.startActivity; // @ts-ignore It's expected this.mobileStartActivity = appManagementCmds.mobileStartActivity; this.mobileWebAtoms = contextCmds.mobileWebAtoms; // @ts-ignore It's expected this.suspendChromedriverProxy = contextCmds.suspendChromedriverProxy; // @ts-ignore It's expected this.mobilePerformEditorAction = elementCmds.mobilePerformEditorAction; this.mobileSwipe = elementCmds.mobileSwipe; this.mobileOpenDrawer = elementCmds.mobileOpenDrawer; this.mobileCloseDrawer = elementCmds.mobileCloseDrawer; this.mobileSetDate = elementCmds.mobileSetDate; this.mobileSetTime = elementCmds.mobileSetTime; this.mobileNavigateTo = elementCmds.mobileNavigateTo; this.mobileScrollToPage = elementCmds.mobileScrollToPage; this.mobileFlashElement = elementCmds.mobileFlashElement; this.mobileClickAction = elementCmds.mobileClickAction; this.mobileDismissAutofill = elementCmds.mobileDismissAutofill; this.mobilePressKey = miscCmds.mobilePressKey; this.mobileGetDeviceInfo = miscCmds.mobileGetDeviceInfo; this.mobileIsToastVisible = miscCmds.mobileIsToastVisible; // @ts-ignore It's expected this.getDisplayDensity = miscCmds.getDisplayDensity; this.mobileBackdoor = miscCmds.mobileBackdoor; this.mobileUiautomator = miscCmds.mobileUiautomator; this.mobileUiautomatorPageSource = miscCmds.mobileUiautomatorPageSource; this.updateSettings = miscCmds.updateSettings; this.getSettings = miscCmds.getSettings; this.getClipboard = clipboardCmds.getClipboard; this.mobileGetClipboard = clipboardCmds.getClipboard; this.mobileSetClipboard = clipboardCmds.mobileSetClipboard; // @ts-ignore It's expected this.mobileStartService = servicesCmds.mobileStartService; // @ts-ignore It's expected this.mobileStopService = servicesCmds.mobileStopService; this.getScreenshot = screenshotCmds.getScreenshot; this.mobileScreenshots = screenshotCmds.mobileScreenshots; this.mobileRegisterIdlingResources = idlingResourcesCmds.mobileRegisterIdlingResources; this.mobileUnregisterIdlingResources = idlingResourcesCmds.mobileUnregisterIdlingResources; this.mobileListIdlingResources = idlingResourcesCmds.mobileListIdlingResources; this.mobileWaitForUIThread = idlingResourcesCmds.mobileWaitForUIThread; this.locatorStrategies = [ 'id', 'class name', 'accessibility id', ]; this.desiredCapConstraints = constraints_1.ESPRESSO_CONSTRAINTS; this.jwpProxyAvoid = NO_PROXY; this._originalIme = null; this.caps = {}; this.opts = opts; this.settings = new driver_1.DeviceSettings({}, this.onSettingsUpdate.bind(this)); this.chromedriver = undefined; } async getSession() { return await driver_1.BaseDriver.prototype.getSession.call(this); } async createSession(w3cCaps1, w3cCaps2, w3cCaps3, driverData) { try { const [sessionId, caps] = (await driver_1.BaseDriver.prototype.createSession.call(this, w3cCaps1, w3cCaps2, w3cCaps3, driverData)); const serverDetails = { platform: 'LINUX', webStorageEnabled: false, takesScreenshot: true, javascriptEnabled: true, databaseEnabled: false, networkConnectionEnabled: true, locationContextEnabled: false, warnings: {}, desired: Object.assign({}, this.caps) }; this.caps = Object.assign(serverDetails, this.caps); this.curContext = this.defaultContextName(); const defaultOpts = { fullReset: false, autoLaunch: true, adbPort: appium_adb_1.DEFAULT_ADB_PORT, androidInstallTimeout: 90000 }; lodash_1.default.defaults(this.opts, defaultOpts); if (this.isChromeSession) { if (this.opts.app) { this.log.warn(`'browserName' capability will be ignored`); this.log.warn(`Chrome browser cannot be run in Espresso sessions because Espresso automation doesn't ` + `have permission to access Chrome`); } else { throw this.log.errorWithException(`Chrome browser sessions cannot be run in Espresso because Espresso ` + `automation doesn't have permission to access Chrome`); } } this.opts.systemPort = this.opts.systemPort || await (0, portscanner_1.findAPortNotInUse)(SYSTEM_PORT_RANGE[0], SYSTEM_PORT_RANGE[1]); this.opts.adbPort = this.opts.adbPort || appium_adb_1.DEFAULT_ADB_PORT; // get device udid for this session const { udid, emPort } = await this.getDeviceInfoFromCaps(); this.opts.udid = udid; // @ts-expect-error do not put random stuff on opts this.opts.emPort = emPort; // now that we know our java version and device info, we can create our // ADB instance this.adb = await this.createADB(); if (this.opts.app) { // find and copy, or download and unzip an app url or path this.opts.app = await this.helpers.configureApp(this.opts.app, { onPostProcess: this.onPostConfigureApp.bind(this), supportedExtensions: SUPPORTED_EXTENSIONS }); } else if (this.appOnDevice) { // the app isn't an actual app file but rather something we want to // assume is on the device and just launch via the appPackage this.log.info(`App file was not listed, instead we're going to run ` + `${this.opts.appPackage} directly on the device`); if (!await this.adb.isAppInstalled(this.opts.appPackage)) { throw this.log.errorWithException(`Could not find the package '${this.opts.appPackage}' installed on the device`); } } await this.startEspressoSession(); return [sessionId, caps]; } catch (e) { await this.deleteSession(); e.message += `${lodash_1.default.endsWith(e.message, '.') ? '' : '.'} Check ` + 'https://github.com/appium/appium-espresso-driver#troubleshooting ' + 'regarding advanced session startup troubleshooting.'; if ((0, driver_1.isErrorType)(e, driver_1.errors.SessionNotCreatedError)) { throw e; } const err = new driver_1.errors.SessionNotCreatedError(e.message); err.stack = e.stack; throw err; } } /** * Unzip the given app path and return the first package that has SUPPORTED_EXTENSIONS * in the archived file. * * @param {string} appPath The path to app file. * @returns {Promise<string>} Returns the path to an unzipped app file path. * @throws Raise an exception if the zip did not have any SUPPORTED_EXTENSIONS packages. */ async unzipApp(appPath) { const useSystemUnzipEnv = process.env.APPIUM_PREFER_SYSTEM_UNZIP; const useSystemUnzip = lodash_1.default.isEmpty(useSystemUnzipEnv) || !['0', 'false'].includes(lodash_1.default.toLower(useSystemUnzipEnv)); const tmpRoot = await support_1.tempDir.openDir(); await support_1.zip.extractAllTo(appPath, tmpRoot, { useSystemUnzip }); const globPattern = `**/*.+(${SUPPORTED_EXTENSIONS.map((ext) => ext.replace(/^\./, '')).join('|')})`; const sortedBundleItems = (await support_1.fs.glob(globPattern, { cwd: tmpRoot, })).sort((a, b) => a.split(path_1.default.sep).length - b.split(path_1.default.sep).length); if (sortedBundleItems.length === 0) { // no expected packages in the zip throw this.log.errorWithException(`${this.opts.app} did not have any of '${SUPPORTED_EXTENSIONS.join(', ')}' ` + `extension packages. Please make sure the provided .zip archive contains at ` + `least one valid application package.`); } const unzippedAppPath = path_1.default.join(tmpRoot, lodash_1.default.first(sortedBundleItems)); this.log.debug(`'${unzippedAppPath}' is the unzipped file from '${appPath}'`); return unzippedAppPath; } async onPostConfigureApp({ cachedAppInfo, isUrl, appPath }) { const presignApp = async (appLocation) => { if (this.opts.noSign) { this.log.info('Skipping application signing because noSign capability is set to true. ' + 'Having the application under test with improper signature/non-signed will cause ' + 'Espresso automation startup failure.'); } else if (!await this.adb.checkApkCert(appLocation, this.opts.appPackage)) { await this.adb.sign(appLocation); } }; const hasApkExt = (appPath) => lodash_1.default.endsWith(lodash_1.default.toLower(appPath), APK_EXT); const hasAabExt = (appPath) => lodash_1.default.endsWith(lodash_1.default.toLower(appPath), AAB_EXT); const extractUniversalApk = async (shouldExtract, appPath) => shouldExtract ? appPath : await this.adb.extractUniversalApk(appPath); let pathInCache = null; let isResultAppPathAlreadyCached = false; if (lodash_1.default.isPlainObject(cachedAppInfo)) { const packageHash = await support_1.fs.hash(appPath); if (packageHash === cachedAppInfo.packageHash && await support_1.fs.exists(cachedAppInfo.fullPath)) { this.log.info(`Using '${cachedAppInfo.fullPath}' which is cached from '${appPath}'`); isResultAppPathAlreadyCached = true; pathInCache = cachedAppInfo.fullPath; } } // appPath can be .zip, .apk or .aab const isApk = hasApkExt(appPath); // Only local .apk files that are available in-place should not be cached const shouldResultAppPathBeCached = !isApk || (isApk && isUrl); if (!isResultAppPathAlreadyCached) { if (shouldResultAppPathBeCached) { // .zip, .aab or downloaded .apk let unzippedAppPath; let isUnzippedApk = false; if (!(hasApkExt(appPath) || hasAabExt(appPath))) { unzippedAppPath = await this.unzipApp(appPath); isUnzippedApk = hasApkExt(unzippedAppPath); } // unzippedAppPath or appPath has SUPPORTED_EXTENSIONS. pathInCache = unzippedAppPath ? await extractUniversalApk(isUnzippedApk, unzippedAppPath) : await extractUniversalApk(isApk, appPath); if (!isApk && isUrl) { // Clean up the temporarily downloaded .aab or .zip package await support_1.fs.rimraf(appPath); } if (hasAabExt(unzippedAppPath)) { // Cleanup the local unzipped .aab file await support_1.fs.rimraf(/** @type {string} */ (unzippedAppPath)); } await presignApp(pathInCache); } else if (isApk) { // It is probably not the best idea to modify the provided app in-place, // but this is how it was always working await presignApp(appPath); } } return shouldResultAppPathBeCached ? { appPath: pathInCache } : false; } get driverData() { // TODO fille out resource info here return {}; } // TODO much of this logic is duplicated from uiautomator2 async startEspressoSession() { const { manifestPayload } = await (0, utils_1.getPackageInfo)(); this.log.info(`EspressoDriver version: ${manifestPayload.version}`); // Read https://github.com/appium/appium-android-driver/pull/461 what happens if ther is no setHiddenApiPolicy for Android P+ if (await this.adb.getApiLevel() >= 28) { // Android P this.log.warn('Relaxing hidden api policy'); await this.adb.setHiddenApiPolicy('1', !!this.opts.ignoreHiddenApiPolicyError); } // get appPackage et al from manifest if necessary let appInfo = await this.getLaunchInfo(); if (appInfo) { // and get it onto our 'opts' object so we use it from now on Object.assign(this.opts, appInfo); } else { appInfo = this.opts; } // start an avd, set the language/locale, pick an emulator, etc... if (this.opts.hideKeyboard) { this._originalIme = await this.adb.defaultIME(); } await this.initDevice(); // Default state is window animation disabled. await this.setWindowAnimationState(this.caps.disableWindowAnimation === false); // set actual device name, udid this.caps.deviceName = this.adb.curDeviceId; this.caps.deviceUDID = this.opts.udid; // set up the modified espresso server etc this.initEspressoServer(); // Further prepare the device by forwarding the espresso port this.log.debug(`Forwarding Espresso Server port ${DEVICE_PORT} to ${this.opts.systemPort}`); await this.adb.forwardPort(this.opts.systemPort, DEVICE_PORT); if (!this.opts.skipUnlock) { // unlock the device to prepare it for testing // @ts-ignore This is ok await this.unlock(); } else { this.log.debug(`'skipUnlock' capability set, so skipping device unlock`); } // set up app under test // prepare our actual AUT, get it on the device, etc... await this.initAUT(); //Adding AUT package name in the capabilities if package name not exist in caps if (!this.caps.appPackage) { this.caps.appPackage = appInfo.appPackage; } if (!this.caps.appWaitPackage) { this.caps.appWaitPackage = appInfo.appWaitPackage || appInfo.appPackage || this.caps.appPackage; } if (this.caps.appActivity) { this.caps.appActivity = (0, utils_1.qualifyActivityName)(this.caps.appActivity, this.caps.appPackage); } else { this.caps.appActivity = (0, utils_1.qualifyActivityName)(appInfo.appActivity, this.caps.appPackage); } if (this.caps.appWaitActivity) { this.caps.appWaitActivity = (0, utils_1.qualifyActivityName)(this.caps.appWaitActivity, this.caps.appWaitPackage); } else { this.caps.appWaitActivity = (0, utils_1.qualifyActivityName)(appInfo.appWaitActivity || appInfo.appActivity || this.caps.appActivity, this.caps.appWaitPackage); } // launch espresso and wait till its online and we have a session await this.espresso.startSession(this.caps); if (this.caps.autoLaunch === false) { this.log.info(`Not waiting for the application activity to start because 'autoLaunch' is disabled`); } else { await this.adb.waitForActivity(this.caps.appWaitPackage, this.caps.appWaitActivity, this.opts.appWaitDuration); } // if we want to immediately get into a webview, set our context // appropriately if (this.opts.autoWebview) { await this.initWebview(); } // now that everything has started successfully, turn on proxying so all // subsequent session requests go straight to/from espresso this.jwpProxyActive = true; await this.addDeviceInfoToCaps(); } /** * Turn on or off animation scale. * '--no-window-animation' instrument argument for Espresso disables window animations, * but it does not bring the animation scale back to the pre-instrument process start state in Espresso * unlike Appium UIA2 driver case. We want to disable/enable the animation scale only in an appium espresso session as possible. * @param isEnabled */ async setWindowAnimationState(isEnabled) { const isAnimationOn = await this.adb.isAnimationOn(); const shouldDisableAnimation = !isEnabled && isAnimationOn; const shouldEnableAnimation = isEnabled && !isAnimationOn; if (shouldDisableAnimation) { this.log.debug('Disabling window animation as "disableWindowAnimation" capability is set to true/fallback to default value "true"'); await this.settingsApp.setAnimationState(false); this.wasAnimationEnabled = true; } else if (shouldEnableAnimation) { this.log.debug('Enabling window animation as "disableWindowAnimation" capability is set to false'); await this.settingsApp.setAnimationState(true); this.wasAnimationEnabled = false; } else { this.log.debug(`Window animation is already ${isEnabled ? 'enabled' : 'disabled'}`); } } async initWebview() { const viewName = this.defaultWebviewName(); const timeout = this.opts.autoWebviewTimeout || 2000; this.log.info(`Setting webview to context '${viewName}' with timeout ${timeout}ms`); await (0, asyncbox_1.retryInterval)(timeout / 500, 500, this.setContext.bind(this), viewName); } async addDeviceInfoToCaps() { const { apiVersion, platformVersion, manufacturer, model, realDisplaySize, displayDensity, } = await this.mobileGetDeviceInfo(); this.caps.deviceApiLevel = parseInt(apiVersion, 10); this.caps.platformVersion = platformVersion; this.caps.deviceScreenSize = realDisplaySize; this.caps.deviceScreenDensity = displayDensity; this.caps.deviceModel = model; this.caps.deviceManufacturer = manufacturer; } initEspressoServer() { // now that we have package and activity, we can create an instance of // espresso with the appropriate data this.espresso = new espresso_runner_1.EspressoRunner(this.log, { // @ts-ignore TODO: Is .host a legacy property? host: this.opts.remoteAdbHost || this.opts.host || '127.0.0.1', systemPort: this.opts.systemPort, devicePort: DEVICE_PORT, adb: this.adb, apk: this.opts.app, tmpDir: this.opts.tmpDir, appPackage: this.opts.appPackage, appActivity: this.opts.appActivity, forceEspressoRebuild: !!this.opts.forceEspressoRebuild, espressoBuildConfig: this.opts.espressoBuildConfig, showGradleLog: !!this.opts.showGradleLog, serverLaunchTimeout: this.opts.espressoServerLaunchTimeout, androidInstallTimeout: this.opts.androidInstallTimeout, skipServerInstallation: this.opts.skipServerInstallation, useKeystore: this.opts.useKeystore, keystorePath: this.opts.keystorePath, keystorePassword: this.opts.keystorePassword, keyAlias: this.opts.keyAlias, keyPassword: this.opts.keyPassword, disableSuppressAccessibilityService: this.opts.disableSuppressAccessibilityService, reqBasePath: this.basePath, }); this.proxyReqRes = this.espresso.proxyReqRes.bind(this.espresso); this.proxyCommand = this.espresso.proxyCommand.bind(this.espresso); } async initAUT() { // Uninstall any uninstallOtherPackages which were specified in caps if (this.opts.uninstallOtherPackages) { await this.uninstallOtherPackages(appium_android_driver_1.utils.parseArray(this.opts.uninstallOtherPackages), [io_appium_settings_1.SETTINGS_HELPER_ID, espresso_runner_1.TEST_APK_PKG]); } if (!this.opts.app) { if (this.opts.fullReset) { throw this.log.errorWithException('Full reset requires an app capability, use fastReset if app is not provided'); } this.log.debug('No app capability. Assuming it is already on the device'); if (this.opts.fastReset) { await this.resetAUT(); } } if (!this.opts.skipUninstall) { await this.adb.uninstallApk(this.opts.appPackage); } if (this.opts.app) { await this.installAUT(); } if (this.opts.skipServerInstallation) { this.log.debug('skipServerInstallation capability is set. Not installig espresso-server '); } else { await this.espresso.installTestApk(); try { await this.adb.addToDeviceIdleWhitelist(io_appium_settings_1.SETTINGS_HELPER_ID, espresso_runner_1.TEST_APK_PKG); } catch (e) { this.log.warn(`Cannot add server packages to the Doze whitelist. Original error: ` + (e.stderr || e.message)); } } } async deleteSession() { this.log.debug('Deleting espresso session'); const screenRecordingStopTasks = [async () => { if (!lodash_1.default.isEmpty(this._screenRecordingProperties)) { await this.stopRecordingScreen(); } }, async () => { if (await this.mobileIsMediaProjectionRecordingRunning()) { await this.mobileStopMediaProjectionRecording(); } }, async () => { if (!lodash_1.default.isEmpty(this._screenStreamingProps)) { await this.mobileStopScreenStreaming(); } }]; if (this.espresso) { if (this.jwpProxyActive) { await this.espresso.deleteSession(); } // @ts-ignore This is ok this.espresso = null; } this.jwpProxyActive = false; if (this.adb) { await bluebird_1.default.all(screenRecordingStopTasks.map((task) => { (async () => { try { await task(); } catch { } })(); })); if (this.wasAnimationEnabled) { try { await this.settingsApp.setAnimationState(true); } catch (err) { this.log.warn(`Unable to reset animation: ${err.message}`); } } if (this._originalIme) { try { await this.adb.setIME(this._originalIme); } catch (e) { this.log.warn(`Cannot restore the original IME: ${e.message}`); } } if (!this.isChromeSession && this.opts.appPackage && !this.opts.dontStopAppOnReset) { await this.adb.forceStop(this.opts.appPackage); } if (this.opts.fullReset && !this.opts.skipUninstall && !this.appOnDevice) { this.log.debug(`FULL_RESET set to 'true', Uninstalling '${this.opts.appPackage}'`); await this.adb.uninstallApk(this.opts.appPackage); } if (await this.adb.getApiLevel() >= 28) { // Android P this.log.info('Restoring hidden api policy to the device default configuration'); await this.adb.setDefaultHiddenApiPolicy(!!this.opts.ignoreHiddenApiPolicyError); } } await super.deleteSession(); if (this.opts.systemPort) { try { await this.adb.removePortForward(this.opts.systemPort); } catch (error) { this.log.warn(`Unable to remove port forward '${error.message}'`); //Ignore, this block will also be called when we fall in catch block // and before even port forward. } } } async onSettingsUpdate() { // intentionally do nothing here, since commands.updateSettings proxies // settings to the espresso server already } // eslint-disable-next-line @typescript-eslint/no-unused-vars proxyActive(sessionId) { // we always have an active proxy to the espresso server return true; } // eslint-disable-next-line @typescript-eslint/no-unused-vars canProxy(sessionId) { // we can always proxy to the espresso server return true; } // eslint-disable-next-line @typescript-eslint/no-unused-vars getProxyAvoidList(sessionId) { // we are maintaining two sets of NO_PROXY lists, one for chromedriver(CHROME_NO_PROXY) // and one for Espresso(NO_PROXY), based on current context will return related NO_PROXY list this.jwpProxyAvoid = lodash_1.default.isNil(this.chromedriver) ? NO_PROXY : CHROME_NO_PROXY; if (this.opts.nativeWebScreenshot) { this.jwpProxyAvoid = ([ ...this.jwpProxyAvoid, ['GET', new RegExp('^/session/[^/]+/screenshot')] ]); } return this.jwpProxyAvoid; } get appOnDevice() { return !this.opts.app && this.helpers.isPackageOrBundle(this.opts.appPackage); } } exports.EspressoDriver = EspressoDriver; EspressoDriver.newMethodMap = method_map_1.newMethodMap; EspressoDriver.executeMethodMap = execute_method_map_1.executeMethodMap; exports.default = EspressoDriver; //# sourceMappingURL=driver.js.map