appium-uiautomator2-driver
Version:
UiAutomator2 integration for Appium
133 lines (125 loc) • 5.35 kB
text/typescript
import {utils} from 'appium-android-driver';
import {fs} from 'appium/support';
import {SETTINGS_HELPER_ID} from 'io.appium.settings';
import type {AndroidUiautomator2Driver} from '../driver';
import {APKS_EXTENSION, APK_EXTENSION} from '../extensions';
import {SERVER_PACKAGE_ID, SERVER_TEST_PACKAGE_ID} from '../uiautomator2-server';
import {signApp} from '../utils';
/** Configures the app capability and validates the APK path before session start. */
export async function prepareSessionApp(this: AndroidUiautomator2Driver): Promise<void> {
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, [APK_EXTENSION, APKS_EXTENSION]);
await this.checkAppPresent();
} else if (this.opts.appPackage) {
// 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(`Starting '${this.opts.appPackage}' directly on the device`);
} else {
this.log.info(
`Neither 'app' nor 'appPackage' was set. Starting UiAutomator2 ` +
'without the target application',
);
}
}
/** Verifies that the app APK path from capabilities exists on the host. */
export async function checkAppPresent(this: AndroidUiautomator2Driver): Promise<void> {
this.log.debug('Checking whether app is actually present');
if (!this.opts.app || !(await fs.exists(this.opts.app))) {
throw this.log.errorWithException(`Could not find app apk at '${this.opts.app}'`);
}
}
/** Installs and prepares the application under test on the device. */
export async function initAUT(this: AndroidUiautomator2Driver): Promise<void> {
// Uninstall any uninstallOtherPackages which were specified in caps
if (this.opts.uninstallOtherPackages) {
await this.uninstallOtherPackages(utils.parseArray(this.opts.uninstallOtherPackages), [
SETTINGS_HELPER_ID,
SERVER_PACKAGE_ID,
SERVER_TEST_PACKAGE_ID,
]);
}
// Install any "otherApps" that were specified in caps
if (this.opts.otherApps) {
let otherApps;
try {
otherApps = utils.parseArray(this.opts.otherApps);
} catch (e) {
throw this.log.errorWithException(
`Could not parse "otherApps" capability: ${(e as Error).message}`,
);
}
otherApps = await Promise.all(
otherApps.map((app) => this.helpers.configureApp(app, [APK_EXTENSION, APKS_EXTENSION])),
);
await this.installOtherApks(otherApps);
}
if (this.opts.app && this.opts.appPackage) {
const adb = this.requireAdb();
const appPackage = this.opts.appPackage;
if ((this.opts.noReset && !(await adb.isAppInstalled(appPackage))) || !this.opts.noReset) {
if (
!this.opts.noSign &&
!(await adb.checkApkCert(this.opts.app, {
requireDefaultCert: false,
}))
) {
await signApp(adb, this.opts.app);
}
if (!this.opts.skipUninstall) {
await adb.uninstallApk(appPackage);
}
await this.installAUT();
} else {
this.log.debug('noReset has been requested and the app is already installed. Doing nothing');
}
} else {
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 && this.opts.appPackage) {
await this.resetAUT();
}
}
}
/** Launches the application under test according to session capabilities. */
export async function ensureAppStarts(this: AndroidUiautomator2Driver): Promise<void> {
const adb = this.requireAdb();
const appPackage = this.opts.appPackage;
const appActivity = this.opts.appActivity;
if (!appPackage) {
throw this.log.errorWithException('appPackage capability is required to start the application');
}
// make sure we have an activity and package to wait for
const appWaitPackage = this.opts.appWaitPackage || appPackage;
const appWaitActivity = this.opts.appWaitActivity || appActivity;
this.log.info(
`Starting '${appPackage}${appActivity ? '/' + appActivity : ''}` +
`and waiting for '${appWaitPackage}${appWaitActivity ? '/' + appWaitActivity : ''}'`,
);
if (this.opts.noReset && !this.opts.forceAppLaunch && (await adb.processExists(appPackage))) {
this.log.info(
`'${appPackage}' is already running and noReset is enabled. ` +
`Set forceAppLaunch capability to true if the app must be forcefully restarted on session startup.`,
);
return;
}
await adb.startApp({
pkg: appPackage,
activity: appActivity,
action: this.opts.intentAction || 'android.intent.action.MAIN',
category: this.opts.intentCategory || 'android.intent.category.LAUNCHER',
flags: this.opts.intentFlags || '0x10200000', // FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
waitPkg: this.opts.appWaitPackage,
waitActivity: this.opts.appWaitActivity,
waitForLaunch: this.opts.appWaitForLaunch,
waitDuration: this.opts.appWaitDuration,
optionalIntentArguments: this.opts.optionalIntentArguments,
stopApp: this.opts.forceAppLaunch || !this.opts.dontStopAppOnReset,
retry: true,
user: this.opts.userProfile,
});
}