of an

Appium

LIFE CYCLE

​Command

Agenda

Session Creation

new AndroidDriver() and new IOSDriver()

FindElementBy.X

Problem Statement

 

Timed out waiting for Espresso Server to start due to Socket exception.

Android Architecture

 

W3C

Server Request

Server

Appium Server

Appium Espresso Driver

Client

Appium Android Driver

Appium ADB

Appium Espresso Driver

Appium Base Driver

Appium UIA2 Driver

Appium Support

Intialisation

Creation

Instrumentation

Execution

Android Life Cycle

DesiredCapabilities capabilities = new DesiredCapabilities();
capabilities.setCapability("deviceName", "Android Emulator");
capabilities.setCapability("app", app.getAbsolutePath());
capabilities.setCapability("appPackage", "io.appium.android.apis");
capabilities.setCapability("appActivity", ".ApiDemos");
capabilities.setCapability("automationName", "Espresso");
driver = new AndroidDriver<WebElement>(getServiceUrl(), capabilities);

Intialization

Driver Creation

App Launch State

automationNameCap = automationNameCap.toLowerCase();

try {
  const {driverPackage, driverClassName} = DRIVER_MAP[automationNameCap];
  const driver = require('appium-espresso-driver')[driverClassName];
  return {
    driver,
    version: this.getDriverVersion(driver.name, driverPackage),
  };
} catch () {

Appium Base Driver

Protocol Proxy

UIA2 or

Espresso Driver

Espresso Driver

  • EspressoDriver extends BaseDriver
  • Merges the default values with desired capability

async createSession (...args) {
  try {
    let [sessionId, caps] = await super.createSession(...args);

    let 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);

Espresso Driver

async startEspressoSession () {
  logger.info(`EspressoDriver version: ${version}`);
  await helpers.getDeviceInfoFromCaps(this.opts);

   ....



  await androidHelpers.createADB(this.opts);

   ....

 

   this.initEspressoServer();

 

 

appium-android-driver

appium-adb

initEspressoServer () {
  this.espresso = new EspressoRunner({
    host: this.opts.remoteAdbHost || this.opts.host || 'localhost',
    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,
    serverLaunchTimeout: this.opts.espressoServerLaunchTimeout,
    androidInstallTimeout: this.opts.androidInstallTimeout,
  });
  this.proxyReqRes = this.espresso.proxyReqRes.bind(this.espresso);
}

espresso-driver/lib/driver.js

Espresso Session Creation

adb forward tcp:8300 tcp:6791

AUT

await this.adb.forwardPort(this.opts.systemPort, DEVICE_PORT);
await this.adb.install(this.modServerPath, { replace: false,timeout: this.androidInstallTimeout });

Instrumentation

async startSession (caps) {
  await this.cleanupSessionLeftovers();

  const cmd = [
    'shell',
    'am', 'instrument',
    '-w',
    '-e', 'debug', process.env.ESPRESSO_JAVA_DEBUG === 'true' ? 'true' : 'false',
    `${TEST_APK_PKG}/androidx.test.runner.AndroidJUnitRunner`,
  ];

  logger.info(`Starting Espresso Server v${version} with cmd: adb ${cmd.join(' ')}`);

Execution

try {
  await retryInterval(20, 1000, async () => {
    await this.jwproxy.command('/status', 'GET');
  });
} catch (e) {
  if (hasSocketError) {
    logger.errorAndThrow('....');
  } else {
    logger.errorAndThrow(`Timed out waiting for Espresso Server to start. Original error: ${e.message}`);
  }
}

await this.jwproxy.command('/session', 'POST', {desiredCapabilities: caps});

Execution

{
  "desiredCapabilities": {
  "desired": {
    "platformName": "android",
      "app": "/Users/saikrisv/git/VodQaAdvancedAppium/VodQA.apk",
      "automationName": "Espresso",
      "deviceName": "Android Emulator",
      "launchTimeout": 900000
  },
  "platformName": "android",
    "app": "/Users/saikrisv/git/VodQaAdvancedAppium/VodQA.apk",
    "automationName": "Espresso",
    "deviceName": "emulator-5554",
    "launchTimeout": 900000,
    "deviceUDID": "emulator-5554",
    "appPackage": "com.vodqareactnative",
    "appWaitPackage": "com.vodqareactnative",
    "appActivity": "com.vodqareactnative.MainActivity",
    "appWaitActivity": "com.vodqareactnative.MainActivity"
}

Execution

 POST /wd/hub/session

Demo

IOS Session Creation

Problem Statement

 

Unable to launch WebDriverAgent because of xcodebuild failure: "xcodebuild failed with code 65".

W3C

Server Request

WDA

Appium XCUI driver

IOS Architecture

 

Appium Server

Client

appium-ios-simulator

node-simctl

appium-xcode

appium-remote-debugger

Appium XCUI Driver

Appium IOS Driver

Appium Base Driver

Intialization

APP & WDA

Execution

iOS Life Cycle

Creation

DesiredCapabilities caps = new DesiredCapabilities();
caps.setCapability("platformName", "iOS");
caps.setCapability("platformVersion", "12.2");
caps.setCapability("deviceName", "iPhone Xs");
caps.setCapability(IOSMobileCapabilityType.BUNDLE_ID, BUNDLE_ID);

caps.setCapability("udid", "666B367A-62E7-45F1-B430-36C2A1971BB8");

driver = new IOSDriver<MobileElement>(new URL("http://localhost:4723/wd/hub"), caps);

Intialization

Driver Creation

App Launch State

Protocol Proxy

XCUI Driver

Appium Base Driver

let automationNameCap = caps.automationName;
automationNameCap = automationNameCap.toLowerCase();

try {
  const {driverPackage, driverClassName} = DRIVER_MAP[automationNameCap];

  return {
    driver,
    version: this.getDriverVersion(driver.name, driverPackage),
  };
 
const driver = require['appium-xcuitest-driver'];
  •  XCUIDriver extends BaseDriver
  • Merges the default values to the user given capability

 
 

../appium-xcuitest-driver/lib/driver.js

XCUI Driver



    // merge server capabilities + desired capabilities
    caps = Object.assign({}, defaultServerCaps, caps);
async createSession (...args) {
  this.lifecycleData = {}; 
  try {
    let [sessionId, caps] = await super.createSession(...args);
    this.opts.sessionId = sessionId;

    await this.start();

../appium-xcuitest-driver/lib/simulator-management.js

const {device, udid, realDevice} = await this.determineDevice();
   return await getSimulator(appiumTestDevice.udid);

Device Name Platform Version

node-simctl

appium-ios-simulator

async function getExistingSim (opts) {
   const devices = await getDevices(opts.platformVersion);

Simulators

Simulators/Real Device

   
 log.debug(`Available devices: ${devices.join(', ')}`);
    if (!devices.includes(this.opts.udid)) {
      if (await simExists(this.opts.udid)) {
        
        return {device, realDevice: false, udid: this.opts.udid};
      }
      throw new Error(`Unknown device or simulator UDID: '${this.opts.udid}'`);
    }
  }



  if (_.isEmpty(this.opts.platformVersion)) {
    log.info('Trying to determine platformVersion from ideviceinfo output');
    try {
      const {stdout} = await exec('ideviceinfo', [
        '-u', this.opts.udid,
        '-s',
        '-k', 'ProductVersion',
      ]);
      this.opts.platformVersion = util.coerceVersion(stdout.trim(), false);
    } catch (e) {
      log.warn(`Cannot determine real device platform version. Original error: ${e.message}`);
    }
  }
  return {device, realDevice: true, udid: this.opts.udid};
}

node-simctl

idevice_id

const device = await getSimulator(this.opts.udid);
  const device = await getRealDeviceObj(this.opts.udid);
 const devices = await getConnectedDevices();
 

App Installation

async function installToSimulator (device, app, bundleId, noReset = true) {
  if (bundleId) {
    if (await device.isAppInstalled(bundleId)) {
      if (noReset) {
        log.debug(`App '${bundleId}' is already installed. No need to reinstall.`);
        return;
      }
      log.debug(`Reset requested. Removing app with id '${bundleId}' from the device`);
      await device.removeApp(bundleId);
    }
  }
     ...... 
   try {

    } catch (e) {
      }
      log.info('Retrying application install');
      ....
    }
    log.debug('The app has been installed successfully.');
  } finally {
    if (tmpRoot && await fs.exists(tmpRoot)) {
      await fs.rimraf(tmpRoot);
    }
  }
}

node-simctl

      await device.installApp(app);

App Installation

ios-deploy

async function installToRealDevice (device, app, bundleId, noReset = true) {
  if (!device.udid || !app) {
    log.debug('No device id or app, not installing to real device.');
    return;
  }

  if (await device.isAppInstalled(bundleId)) {
    if (noReset) {
      log.debug(`App '${bundleId}' is already installed. No need to reinstall.`);
      return;
    }
    log.debug(`Reset requested. Removing app with id '${bundleId}' from the device`);
    await device.remove(bundleId);
  }

 }
  await device.install(app);

 

WDA Start

wdaLocalPort

async start (buildOnly = false) {
 

Execution

 this.xcodebuild.processExited = true;
      if (this.xcodebuild._wda_error_occurred || (!signal && code !== 0)) {
        return reject(new Error(`xcodebuild failed with code ${code}${EOL}` +
          `xcodebuild error message:${EOL}${this.xcodebuild._wda_error_message}`));
      }
this.xcodebuild = await this.createSubProcess(buildOnly);
this.xcodebuild._wda_error_message = '';

 

try {
  await retryInterval(15, 1000, async () => {
    try {
        this.proxyCommand('/status', 'GET');
      await this.startWdaSession(this.opts.bundleId, this.opts.processArguments);
    } catch (err) {
      originalStacktrace = err.stack;
      log.debug(`Failed to create WDA session (${err.message}). Retrying...`);
      throw err;
    }
  });
}

 

Execution

FindElement

driver.findElement(By.accessibility)

[HTTP] --> POST /wd/hub/session/ca5d6df3-c557-40ae-973a-6620d4a9fc42/elements

[HTTP] {"using":"accessibility id","value":"login"}

 

 

@saikrisv

 

saikrisv@thoughtworks.com

 

github.com/saikrishna321

 

 

@srinivasanskr

 

sekars@thoughtworks.com

 

github.com/srinivasantarget

 

Made with Slides.com