UNPKG

appium-android-driver

Version:

Android UiAutomator and Chrome support for Appium

260 lines (215 loc) 8.22 kB
import {resolveExecutablePath} from './utils'; import {system, fs, doctor} from '@appium/support'; import path from 'path'; import '@colors/colors'; import {getAndroidBinaryPath, getSdkRootFromEnv} from 'appium-adb'; import type {IDoctorCheck, AppiumLogger, DoctorCheckResult} from '@appium/types'; const JAVA_HOME_VAR_NAME = system.isWindows() ? '%JAVA_HOME%' : '$JAVA_HOME'; const ENVIRONMENT_VARS_TUTORIAL_URL = 'https://github.com/appium/java-client/blob/master/docs/environment.md'; const JAVA_HOME_TUTORIAL = 'https://docs.oracle.com/cd/E21454_01/html/821-2531/inst_jdk_javahome_t.html'; const ANDROID_SDK_LINK1 = 'https://developer.android.com/studio#cmdline-tools'; const ANDROID_SDK_LINK2 = 'https://developer.android.com/studio/intro/update#sdk-manager'; const BUNDLETOOL_RELEASES_LINK = 'https://github.com/google/bundletool/releases/'; const GSTREAMER_INSTALL_LINK = 'https://gstreamer.freedesktop.org/documentation/installing/index.html?gi-language=c'; const FFMPEG_INSTALL_LINK = 'https://www.ffmpeg.org/download.html'; export interface EnvVarCheckOptions { expectDir?: boolean; expectFile?: boolean; } class EnvVarAndPathCheck implements IDoctorCheck { log!: AppiumLogger; varName: string; opts: EnvVarCheckOptions; constructor(varName: string, opts: EnvVarCheckOptions = {}) { this.varName = varName; this.opts = opts; } async diagnose(): Promise<DoctorCheckResult> { const varValue = process.env[this.varName]; if (!varValue) { return doctor.nok(`${this.varName} environment variable is NOT set!`); } if (!(await fs.exists(varValue))) { let errMsg = `${this.varName} is set to '${varValue}' but this path does not exist!`; if (system.isWindows() && varValue.includes('%')) { errMsg += ` Consider replacing all references to other environment variables with absolute paths.`; } return doctor.nok(errMsg); } const stat = await fs.stat(varValue); if (this.opts.expectDir && !stat.isDirectory()) { return doctor.nok( `${this.varName} is expected to be a valid folder, got a file path instead`, ); } if (this.opts.expectFile && stat.isDirectory()) { return doctor.nok( `${this.varName} is expected to be a valid file, got a folder path instead`, ); } return doctor.ok(`${this.varName} is set to: ${varValue}`); } async fix(): Promise<string> { return ( `Make sure the environment variable ${this.varName.bold} is properly configured for the Appium process. ` + `Refer ${ENVIRONMENT_VARS_TUTORIAL_URL} for more details.` ); } hasAutofix(): boolean { return false; } isOptional(): boolean { return false; } } export const androidHomeCheck = new EnvVarAndPathCheck('ANDROID_HOME', {expectDir: true}); export const javaHomeCheck = new EnvVarAndPathCheck('JAVA_HOME', {expectDir: true}); export class JavaHomeValueCheck implements IDoctorCheck { log!: AppiumLogger; async diagnose(): Promise<DoctorCheckResult> { const envVar = process.env.JAVA_HOME; if (!envVar) { return doctor.nok(`${JAVA_HOME_VAR_NAME} environment variable must be set`); } const javaBinaryRelativePath = path.join('bin', `java${system.isWindows() ? '.exe' : ''}`); const javaBinary = path.join(envVar, javaBinaryRelativePath); if (!(await fs.exists(javaBinary))) { return doctor.nok( `${JAVA_HOME_VAR_NAME} is set to an invalid value. ` + `It must be pointing to a folder containing ${javaBinaryRelativePath}`, ); } return doctor.ok(`'${javaBinaryRelativePath}' exists under '${envVar}'`); } async fix(): Promise<string> { return ( `Set ${JAVA_HOME_VAR_NAME} environment variable to the root folder path of your local JDK installation. ` + `Read ${JAVA_HOME_TUTORIAL}` ); } hasAutofix(): boolean { return false; } isOptional(): boolean { return false; } } export const javaHomeValueCheck = new JavaHomeValueCheck(); export class AndroidSdkCheck implements IDoctorCheck { log!: AppiumLogger; TOOL_NAMES: readonly string[] = ['adb', 'emulator']; async diagnose(): Promise<DoctorCheckResult> { const listOfTools = this.TOOL_NAMES.join(', '); const sdkRoot = getSdkRootFromEnv(); if (!sdkRoot) { return doctor.nok(`${listOfTools} could not be found because ANDROID_HOME is NOT set!`); } this.log.info(` Checking ${listOfTools}`); const missingBinaries: string[] = []; for (const binary of this.TOOL_NAMES) { try { this.log.info(` '${binary}' exists in ${await getAndroidBinaryPath(binary)}`); } catch { missingBinaries.push(binary); } } if (missingBinaries.length > 0) { return doctor.nok(`${missingBinaries.join(', ')} could NOT be found in '${sdkRoot}'!`); } return doctor.ok(`${listOfTools} exist in '${sdkRoot}'`); } async fix(): Promise<string> { return ( `Manually install ${'Android SDK'.bold} and set ${'ANDROID_HOME'.bold}. ` + `Read ${[ANDROID_SDK_LINK1, ANDROID_SDK_LINK2].join(' and ')}.` ); } hasAutofix(): boolean { return false; } isOptional(): boolean { return false; } } export const androidSdkCheck = new AndroidSdkCheck(); export class OptionalBundletoolCheck implements IDoctorCheck { log!: AppiumLogger; async diagnose(): Promise<DoctorCheckResult> { const bundletoolPath = await resolveExecutablePath('bundletool.jar'); return bundletoolPath ? doctor.okOptional(`bundletool.jar is installed at: ${bundletoolPath}`) : doctor.nokOptional('bundletool.jar cannot be found'); } async fix(): Promise<string> { return ( `${'bundletool.jar'.bold} is used to handle Android App bundles. ` + `Please download the binary from ${BUNDLETOOL_RELEASES_LINK} and store it ` + `to any folder listed in the PATH environment variable. Folders that ` + `are currently present in PATH: ${process.env.PATH}` ); } hasAutofix(): boolean { return false; } isOptional(): boolean { return true; } } export const optionalBundletoolCheck = new OptionalBundletoolCheck(); export class OptionalGstreamerCheck implements IDoctorCheck { log!: AppiumLogger; GSTREAMER_BINARY = `gst-launch-1.0${system.isWindows() ? '.exe' : ''}`; GST_INSPECT_BINARY = `gst-inspect-1.0${system.isWindows() ? '.exe' : ''}`; async diagnose(): Promise<DoctorCheckResult> { const gstreamerPath = await resolveExecutablePath(this.GSTREAMER_BINARY); const gstInspectPath = await resolveExecutablePath(this.GST_INSPECT_BINARY); return gstreamerPath && gstInspectPath ? doctor.okOptional( `${this.GSTREAMER_BINARY} and ${this.GST_INSPECT_BINARY} are installed at: ${gstreamerPath} and ${gstInspectPath}`, ) : doctor.nokOptional( `${this.GSTREAMER_BINARY} and/or ${this.GST_INSPECT_BINARY} cannot be found`, ); } async fix(): Promise<string> { return ( `${ `${this.GSTREAMER_BINARY} and ${this.GST_INSPECT_BINARY}`.bold } are used to stream the screen of the device under test. ` + `Please read ${GSTREAMER_INSTALL_LINK}.` ); } hasAutofix(): boolean { return false; } isOptional(): boolean { return true; } } export const optionalGstreamerCheck = new OptionalGstreamerCheck(); export class OptionalFfmpegCheck implements IDoctorCheck { log!: AppiumLogger; FFMPEG_BINARY = `ffmpeg${system.isWindows() ? '.exe' : ''}`; async diagnose(): Promise<DoctorCheckResult> { const ffmpegPath = await resolveExecutablePath(this.FFMPEG_BINARY); return ffmpegPath ? doctor.okOptional(`${this.FFMPEG_BINARY} exists at '${ffmpegPath}'`) : doctor.nokOptional(`${this.FFMPEG_BINARY} cannot be found`); } async fix(): Promise<string> { return ( `${`${this.FFMPEG_BINARY}`.bold} is used to capture screen recordings from the device under test. ` + `Please read ${FFMPEG_INSTALL_LINK}.` ); } hasAutofix(): boolean { return false; } isOptional(): boolean { return true; } } export const optionalFfmpegCheck = new OptionalFfmpegCheck();