Skip to content

Commit

Permalink
Merge branch 'main' into help2
Browse files Browse the repository at this point in the history
  • Loading branch information
garg3133 authored Sep 3, 2024
2 parents cbe328e + f4304f1 commit a53ce89
Show file tree
Hide file tree
Showing 22 changed files with 1,299 additions and 15 deletions.
15 changes: 15 additions & 0 deletions docs/subcommands/connect.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
## Workflows of the `connect` subcommand

**Syntax**
```sh
npx @nightwatch/mobile-helper android connect FLAG [configs]
```

### 1. Connect a real device wirelessly

> Note: Only devices with Android version 11 or higher are supported.
Run the below command to connect to a real device wirelessly:
```sh
npx @nightwatch/mobile-helper android connect --wireless
```
32 changes: 32 additions & 0 deletions docs/subcommands/install.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
## Workflows of the `install` subcommand

**Syntax**
```sh
npx @nightwatch/mobile-helper android install FLAG [configs]
```

### 1. Install an APK

Run the below command to install an APK on a real device or an AVD:

```sh
npx @nightwatch/mobile-helper android install --app

# with configs
npx @nightwatch/mobile-helper android install --app [--deviceId <device_id>] [--path <path_to_apk>]
```

**Configs**

| Config | Description |
| ------------------------------ | -------------------------------------------------------------- |
| --deviceId \| -s <device_id> | Id of the device to install the APK to |
| --path \| -p <path_to_apk> | Path to the APK file relative to the current working directory |

### 2. Create a new Android Virtual Device

Run the below command to create a new AVD:

```sh
npx @nightwatch/mobile-helper android install --avd
```
23 changes: 23 additions & 0 deletions docs/subcommands/list.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
## Workflows of the `list` subcommand

**Syntax**

```sh
npx @nightwatch/mobile-helper android list [flags]
```

### 1. Show a list of connected devices

Run the below command to show a list of all the connected real devices and running AVDs:

```sh
npx @nightwatch/mobile-helper android list --device
```

### 2. Show a list of installed AVDs

Run the below command to show a list of all the currently installed AVDs

```sh
npx @nightwatch/mobile-helper android list --avd
```
15 changes: 15 additions & 0 deletions docs/subcommands/uninstall.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
## Workflows of the `uninstall` subcommand

**Syntax**

```sh
npx @nightwatch/mobile-helper android uninstall FLAG [configs]
```

### 1. Delete an Android Virtual Device (AVD)

Run the below command to delete an AVD:

```sh
npx @nightwatch/mobile-helper android uninstall --avd
```
37 changes: 35 additions & 2 deletions src/commands/android/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,24 @@ export const AVAILABLE_SUBCOMMANDS: AvailableSubcommands = {
}
]
},
list: {
description: 'List connected devices or installed AVDs',
flags: [{
name: 'device',
description: 'List connected devices (real devices and AVDs)'
},
{
name: 'avd',
description: 'List installed AVDs'
}]
},
install: {
description: 'Install system images for Android Virtual Device',
description: 'Install APK or AVD on a device',
flags: [
{
name: 'avd',
description: 'Create an Android Virtual Device'
},
{
name: 'app',
description: 'Install an APK on the device',
Expand All @@ -58,7 +73,25 @@ export const AVAILABLE_SUBCOMMANDS: AvailableSubcommands = {
{
name: 'deviceId',
alias: ['s'],
description: 'Id of the device to install the APK if multiple devices are connected',
description: 'Id of the device to install the APK to if multiple devices are connected',
usageHelp: 'device_id'
}
]
}
]
},
uninstall: {
description: 'todo item',
flags: [
{name: 'avd', description: 'todo item'},
{
name: 'app',
description: 'Uninstall an APK from a device',
cliConfigs: [
{
name: 'deviceId',
alias: ['s'],
description: 'Id of the device to uninstall the APK from if multiple devices are connected',
usageHelp: 'device_id'
}
]
Expand Down
96 changes: 96 additions & 0 deletions src/commands/android/dotcommands.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import colors from 'ansi-colors';
import {spawnSync} from 'child_process';
import * as dotenv from 'dotenv';
import path from 'path';

import {ANDROID_DOTCOMMANDS} from '../../constants';
import Logger from '../../logger';
import {getPlatformName} from '../../utils';
import {Platform, SdkBinary} from './interfaces';
import {checkJavaInstallation, getBinaryLocation, getBinaryNameForOS, getSdkRootFromEnv} from './utils/common';

export class AndroidDotCommand {
dotcmd: string;
args: string[];
sdkRoot: string;
rootDir: string;
platform: Platform;
androidHomeInGlobalEnv: boolean;

constructor(dotcmd: string, argv: string[], rootDir = process.cwd()) {
this.dotcmd = dotcmd;
this.args = argv.slice(1);
this.sdkRoot = '';
this.rootDir = rootDir;
this.platform = getPlatformName();
this.androidHomeInGlobalEnv = false;
}

async run(): Promise<boolean> {
if (!ANDROID_DOTCOMMANDS.includes(this.dotcmd)) {
Logger.log(colors.red(`Unknown dot command passed: ${this.dotcmd}\n`));

Logger.log('Run Android SDK command line tools using the following command:');
Logger.log(colors.cyan('npx @nightwatch/mobile-helper <DOTCMD> [options|args]\n'));

Logger.log(`Available Dot Commands: ${colors.magenta(ANDROID_DOTCOMMANDS.join(', '))}`);
Logger.log(`(Example command: ${colors.gray('npx @nightwatch/mobile-helper android.emulator @nightwatch-android-11')})\n`);

return false;
}

const javaInstalled = checkJavaInstallation(this.rootDir);
if (!javaInstalled) {
return false;
}

this.loadEnvFromDotEnv();

const sdkRootEnv = getSdkRootFromEnv(this.rootDir, this.androidHomeInGlobalEnv);
if (!sdkRootEnv) {
Logger.log(`Run: ${colors.cyan('npx @nightwatch/mobile-helper android --standalone')} to fix this issue.`);
Logger.log(`(Remove the ${colors.gray('--standalone')} flag from the above command if using the tool for testing.)\n`);

return false;
}
this.sdkRoot = sdkRootEnv;

return this.executeDotCommand();
}

loadEnvFromDotEnv(): void {
this.androidHomeInGlobalEnv = 'ANDROID_HOME' in process.env;
dotenv.config({path: path.join(this.rootDir, '.env')});
}

buildCommand(): string {
const binaryName = this.dotcmd.split('.')[1] as SdkBinary;
const binaryLocation = getBinaryLocation(this.sdkRoot, this.platform, binaryName, true);

let cmd: string;
if (binaryLocation === 'PATH') {
const binaryFullName = getBinaryNameForOS(this.platform, binaryName);
cmd = `${binaryFullName}`;
} else {
const binaryFullName = path.basename(binaryLocation);
const binaryDirPath = path.dirname(binaryLocation);
cmd = path.join(binaryDirPath, binaryFullName);
}

return cmd;
}

executeDotCommand(): boolean {
const cmd = this.buildCommand();
const result = spawnSync(cmd, this.args, {stdio: 'inherit'});

if (result.error) {
console.error(result.error);

return false;
}

return result.status === 0;
}
}

9 changes: 7 additions & 2 deletions src/commands/android/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,14 @@ import {AndroidSetup} from './androidSetup';
import {Options} from './interfaces';
import {AndroidSubcommand} from './subcommands';
import {getSubcommandHelp} from './subcommands/help';
import {AndroidDotCommand} from './dotcommands';

export function handleAndroidCommand(args: string[], options: Options): void {
if (args.length === 1) {
export function handleAndroidCommand(args: string[], options: Options, argv: string[]): void {
if (args[0].includes('.')) {
// Here args[0] represents the android dot command
const androidDotCommand = new AndroidDotCommand(args[0], argv, process.cwd());
androidDotCommand.run();
} else if (args.length === 1) {
const androidSetup = new AndroidSetup(options);
androidSetup.run();
} else if (args.length === 2) {
Expand Down
34 changes: 33 additions & 1 deletion src/commands/android/subcommands/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ import colors from 'ansi-colors';
import Logger from '../../../logger';
import {symbols} from '../../../utils';
import {AVAILABLE_SUBCOMMANDS} from '../constants';
import {Options, SdkBinary} from '../interfaces';
import {Platform, Options, SdkBinary} from '../interfaces';
import ADB from '../utils/appium-adb';
import {execBinarySync} from '../utils/sdk';
import {CliConfig, SubcommandOptionsVerificationResult} from './interfaces';
import {showHelp} from './help';

Expand Down Expand Up @@ -72,6 +73,37 @@ export async function showConnectedEmulators() {
}
}

export async function getInstalledSystemImages(sdkmanagerLocation: string, platform: Platform): Promise<{
result: boolean,
systemImages: string[]
}> {
const stdout = execBinarySync(sdkmanagerLocation, 'sdkmanager', platform, '--list');
if (!stdout) {
Logger.log(`\n${colors.red('Failed to fetch system images!')} Please try again.`);

return {
result: false,
systemImages: []
};
}
const lines = stdout.split('\n');
const installedImages: string[] = [];

for (const line of lines) {
if (line.includes('Available Packages:')) {
break;
}
if (line.includes('system-images')) {
installedImages.push(line.split('|')[0].trim());
}
}

return {
result: true,
systemImages: installedImages
};
}

export function showMissingBinaryHelp(binaryName: SdkBinary) {
Logger.log(` ${colors.red(symbols().fail)} ${colors.cyan(binaryName)} binary not found.\n`);
Logger.log(`Run: ${colors.cyan('npx @nightwatch/mobile-helper android --standalone')} to setup missing requirements.`);
Expand Down
25 changes: 20 additions & 5 deletions src/commands/android/subcommands/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,14 @@ import path from 'path';

import Logger from '../../../logger';
import {getPlatformName} from '../../../utils';
import {AVAILABLE_SUBCOMMANDS} from '../constants';
import {Options, Platform} from '../interfaces';
import {checkJavaInstallation, getSdkRootFromEnv} from '../utils/common';
import {showHelp} from './help';
import {checkJavaInstallation, getSdkRootFromEnv, getSubcommandHelp} from '../utils/common';
import {connect} from './connect';
import {showHelp} from './help';
import {install} from './install';
import {list} from './list';
import {uninstall} from './uninstall';

export class AndroidSubcommand {
sdkRoot: string;
Expand All @@ -28,11 +31,21 @@ export class AndroidSubcommand {
}

async run(): Promise<boolean> {
if (!Object.keys(AVAILABLE_SUBCOMMANDS).includes(this.subcommand)) {
Logger.log(`${colors.red(`unknown subcommand passed: ${this.subcommand}`)}\n`);
Logger.log(getSubcommandHelp());
Logger.log(`For individual subcommand help, run: ${colors.cyan('npx @nightwatch/mobile-helper android SUBCOMMAND --help')}`);
Logger.log(`For complete Android help, run: ${colors.cyan('npx @nightwatch/mobile-helper android --help')}\n`);

return false;
}

if (this.options.help) {
showHelp(this.subcommand);

return true;
}

this.loadEnvFromDotEnv();

const javaInstalled = checkJavaInstallation(this.rootDir);
Expand All @@ -49,9 +62,7 @@ export class AndroidSubcommand {
}
this.sdkRoot = sdkRootEnv;

this.executeSubcommand();

return false;
return await this.executeSubcommand();
}

loadEnvFromDotEnv(): void {
Expand All @@ -65,6 +76,10 @@ export class AndroidSubcommand {
return await connect(this.options, this.sdkRoot, this.platform);
} else if (this.subcommand === 'install') {
return await install(this.options, this.sdkRoot, this.platform);
} else if (this.subcommand === 'list') {
return await list(this.options, this.sdkRoot, this.platform);
} else if (this.subcommand === 'uninstall') {
return await uninstall(this.options, this.sdkRoot, this.platform);
}

return false;
Expand Down
2 changes: 1 addition & 1 deletion src/commands/android/subcommands/install/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ const handleError = (consoleOutput: any) => {
let errorMessage = consoleOutput;
if (consoleOutput.includes('INSTALL_FAILED_ALREADY_EXISTS')) {
errorMessage = 'APK with the same package name already exists on the device.\n';
errorMessage += colors.reset(`\nPlease uninstall the app first from the device and then install again.\n`);
errorMessage += colors.reset('\nPlease uninstall the app first from the device and then install again.\n');
errorMessage += colors.reset(`To uninstall, use: ${colors.cyan('npx @nightwatch/mobile-helper android uninstall --app')}\n`);
} else if (consoleOutput.includes('INSTALL_FAILED_OLDER_SDK')) {
errorMessage = 'Target installation location (AVD/Real device) has older SDK version than the minimum requirement of the APK.\n';
Expand Down
Loading

0 comments on commit a53ce89

Please sign in to comment.