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
Appium LifeCycle
By Sai Krishna
Appium LifeCycle
- 2,857