Skip to content

Commit

Permalink
refactor show help for subcommand
Browse files Browse the repository at this point in the history
  • Loading branch information
itsspriyansh committed Aug 19, 2024
1 parent 1099e77 commit cbe328e
Show file tree
Hide file tree
Showing 5 changed files with 148 additions and 126 deletions.
7 changes: 4 additions & 3 deletions src/commands/android/androidSetup.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import colors from 'ansi-colors';
import boxen from 'boxen';
import {execSync} from 'child_process';
import * as dotenv from 'dotenv';
import fs from 'fs';
import os from 'os';
import path from 'path';
import untildify from 'untildify';
import {prompt} from 'inquirer';
import boxen from 'boxen';

import Logger from '../../logger';
import {getPlatformName, symbols} from '../../utils';
Expand All @@ -15,18 +15,19 @@ import {
ABI, AVAILABLE_OPTIONS, BINARY_TO_PACKAGE_NAME, DEFAULT_CHROME_VERSIONS,
DEFAULT_FIREFOX_VERSION, NIGHTWATCH_AVD, SETUP_CONFIG_QUES
} from './constants';
import DOWNLOADS from './downloads.json';
import {AndroidSetupResult, Options, OtherInfo, Platform, SdkBinary, SetupConfigs} from './interfaces';
import {getSubcommandHelp} from './subcommands/help';
import {
checkJavaInstallation, downloadFirefoxAndroid, downloadWithProgressBar,
getAllAvailableOptions, getBinaryLocation, getBinaryNameForOS,
getFirefoxApkName, getLatestVersion, getSdkRootFromEnv, getSubcommandHelp
getFirefoxApkName, getLatestVersion, getSdkRootFromEnv
} from './utils/common';
import {
downloadAndSetupAndroidSdk, downloadSdkBuildTools, execBinarySync,
getBuildToolsAvailableVersions, getDefaultAndroidSdkRoot, installPackagesUsingSdkManager
} from './utils/sdk';

import DOWNLOADS from './downloads.json';


export class AndroidSetup {
Expand Down
23 changes: 23 additions & 0 deletions src/commands/android/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,29 @@ export const AVAILABLE_SUBCOMMANDS: AvailableSubcommands = {
description: 'Connect a real device wirelessly'
}
]
},
install: {
description: 'Install system images for Android Virtual Device',
flags: [
{
name: 'app',
description: 'Install an APK on the device',
cliConfigs: [
{
name: 'path',
alias: ['p'],
description: 'Path to the APK file',
usageHelp: 'path_to_apk'
},
{
name: 'deviceId',
alias: ['s'],
description: 'Id of the device to install the APK if multiple devices are connected',
usageHelp: 'device_id'
}
]
}
]
}
};

Expand Down
2 changes: 1 addition & 1 deletion src/commands/android/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import colors from 'ansi-colors';
import {AndroidSetup} from './androidSetup';
import {Options} from './interfaces';
import {AndroidSubcommand} from './subcommands';
import {getSubcommandHelp} from './utils/common';
import {getSubcommandHelp} from './subcommands/help';

export function handleAndroidCommand(args: string[], options: Options): void {
if (args.length === 1) {
Expand Down
128 changes: 119 additions & 9 deletions src/commands/android/subcommands/help.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import colors from 'ansi-colors';

import Logger from '../../../logger';
import {AVAILABLE_SUBCOMMANDS} from '../constants';
import {Subcommand} from './interfaces';
import {CliConfig, Subcommand} from './interfaces';

export function showHelp(subcommand: string) {
const subcmd = AVAILABLE_SUBCOMMANDS[subcommand];
Expand All @@ -17,18 +17,128 @@ export function showHelp(subcommand: string) {
}
}

export const getSubcommandFlagsHelp = (subcmd: Subcommand) => {
export const getSubcommandHelp = (): string => {
let output = '';
const longest = (xs: string[]) => Math.max.apply(null, xs.map(x => x.length));

if (subcmd.flags && subcmd.flags.length > 0) {
const optionLongest = longest(subcmd.flags.map(flag => `--${flag.name}`));
subcmd.flags.forEach(flag => {
const flagStr = `--${flag.name}`;
const optionPadding = new Array(Math.max(optionLongest - flagStr.length + 3, 0)).join('.');
output += ` ${flagStr} ${colors.grey(optionPadding)} ${colors.gray(flag.description)}\n`;
output += `Usage: ${colors.cyan('npx @nightwatch/mobile-helper android subcmd [subcmd-options]')}\n`;
output += ' The following subcommands are used for different operations on Android SDK:\n\n';
output += `${colors.yellow('Subcommands and Subcommand-Options:')}\n`;

Object.keys(AVAILABLE_SUBCOMMANDS).forEach(subcommand => {
const subcmd = AVAILABLE_SUBCOMMANDS[subcommand];

// A subcommand will have flags to facilitate multiple workflows. If a subcommand has single
// workflow, then it won't have flags but might have configs with string or boolean values.

// Display the subcommand name along with flags in the format:
// subcommand [--flag1] [--flag2] ...
// OR
// with the configs in the format:
// subcommand [--config1 <usageHelp>] [--config2 <usageHelp>] ...

const subcmdFlags = subcmd.flags?.map(flag => `[--${flag.name}]`).join(' ') || '';
const subcmdConfigs = generateConfigsString(subcmd.cliConfigs);

output += ` ${colors.cyan(subcommand)} ${subcmdFlags} ${subcmdConfigs}\n`;
output += ` ${colors.gray(subcmd.description)}\n`;

// Display list of configs for the subcommand along with description

if (subcmd.cliConfigs) {

// Generate strings of configs with their aliases in the format:
// --config1 | -c11 | -c12 ...
// --config2 | -c21 | -c22 ...

const configsWithAlias = getConfigsWithAlias(subcmd.cliConfigs);

subcmd.cliConfigs.forEach((config, idx) => {
const padding = generatePadding(configsWithAlias, configsWithAlias[idx].length);
output += ` ${configsWithAlias[idx]} ${colors.grey(padding)} ${colors.gray(config.description)}\n`;
});
}

// Display the list of flags for the subcommand along with description.

output += getSubcommandFlagsHelp(subcmd);
output += '\n';
});

return output;
};

export const getSubcommandFlagsHelp = (subcmd: Subcommand): string => {
let output = '';

if (subcmd.flags.length) {

// Generate a list of 'flagsWithConfigs' strings in the format:
// --flag1 [--config11 <usageHelp>] [--config12 <usageHelp>] ...
// --flag2 [--config21 <usageHelp>] [--config22 <usageHelp>] ...

const flagsWithConfigs = subcmd.flags.map((flag) => {
const configs = generateConfigsString(flag.cliConfigs);

return '--' + flag.name + ' ' + configs;
});

subcmd.flags.forEach((flag, idx) => {
const padding = generatePadding(flagsWithConfigs, flagsWithConfigs[idx].length);

output += ` ${flagsWithConfigs[idx]} ${colors.grey(padding)} ${colors.gray(flag.description)}\n`;

// Show the list of configs for the flag along with description.

if (flag.cliConfigs) {

// Generate strings of configs with their aliases in the format:
// --config1 | -c11 | -c12 ...
// --config2 | -c21 | -c22 ...

const configsWithAlias = getConfigsWithAlias(flag.cliConfigs);

flag.cliConfigs.forEach((config, idx) => {
const padding = generatePadding(configsWithAlias, configsWithAlias[idx].length);
output += ` ${configsWithAlias[idx]} ${colors.grey(padding)} ${colors.gray(config.description)}\n`;
});
}
});
}

return output;
};

const generateConfigsString = (configs: CliConfig[] | undefined) => {

// Generate a string of configs in the format:
// [--config1 <usageHelp>] [--config2 <usageHelp>] ...

if (!configs) {
return '';
}

let configsStr = '';
configs.forEach(config => {
configsStr += `[--${config.name} <${config.usageHelp}>] `;
});

return configsStr;
};

const generatePadding = (array: string[], length: number): string => {
const longest = (xs: string[]) => Math.max.apply(null, xs.map(x => x.length));
const padding = new Array(Math.max(longest(array) - length + 3, 0)).join('.');

return padding;
};

const getConfigsWithAlias = (configs: CliConfig[]): string[] => {
const configsWithAlias = configs.map(config => {
const configAlias = config.alias.map(alias => `-${alias}`).join(' |');

return `--${config.name}` + (configAlias ? ` | ${configAlias}` : '');
});

return configsWithAlias;
};

114 changes: 1 addition & 113 deletions src/commands/android/utils/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,9 @@ import which from 'which';
import Logger from '../../../logger';
import {symbols} from '../../../utils';
import {
ABI, AVAILABLE_OPTIONS, AVAILABLE_SUBCOMMANDS,
DEFAULT_CHROME_VERSIONS, DEFAULT_FIREFOX_VERSION, SDK_BINARY_LOCATIONS
ABI, AVAILABLE_OPTIONS, DEFAULT_CHROME_VERSIONS, DEFAULT_FIREFOX_VERSION, SDK_BINARY_LOCATIONS
} from '../constants';
import {Platform, SdkBinary, Subcommand, Flag} from '../interfaces';
import {Platform, SdkBinary} from '../interfaces';
import {getSubcommandFlagsHelp} from '../subcommands/help';

export const getAllAvailableOptions = () => {
const mainOptions = Object.keys(AVAILABLE_OPTIONS);
Expand Down Expand Up @@ -214,112 +211,3 @@ export const checkJavaInstallation = (cwd: string): boolean => {
}
};

export const getSubcommandHelp = (): string => {
let output = '';

output += `Usage: ${colors.cyan('npx @nightwatch/mobile-helper android subcmd [subcmd-options]')}\n`;
output += ' The following subcommands are used for different operations on Android SDK:\n\n';
output += `${colors.yellow('Subcommands and Subcommand-Options:')}\n`;

Object.keys(AVAILABLE_SUBCOMMANDS).forEach(subcommand => {
const subcmd = AVAILABLE_SUBCOMMANDS[subcommand];
const subcmdFlags = generateFlagsString(subcmd.flags);
const subcmdOptions = subcmd.flags?.map(flag => `[--${flag.name}]`).join(' ') || '';

// A subcommand will have main options to facilitate multiple workflows.
// If a subcommand has single workflow, then it won't have main options but might
// have flags with string values.

// Display the subcommand name along with main options or flags in the format:
// subcommand [--option1] [--option2] ...
// OR
// subcommand [--flag1 <flag1_value>] [--flag2 <flag2_value>] ...
output += ` ${colors.cyan(subcommand)} ${subcmdOptions} ${subcmdFlags}\n`;
output += ` ${colors.gray(subcmd.description)}\n`;

// Display list of flags for the subcommand along with description
if (subcmd.flags) {
// Generate a list of flags with their aliases in the format:
// --flag | -f1 | -f2 ...
const subcmdFlagsWithAlias = getFlagsWithAlias(subcmd.flags);

subcmd.flags.forEach((valOption, idx) => {
const optionPadding = generatePadding(subcmdFlagsWithAlias, subcmdFlagsWithAlias[idx].length);
output += ` ${subcmdFlagsWithAlias[idx]} ${colors.grey(optionPadding)} ${colors.gray(valOption.description)}\n`;
});
}

// Display the list of main options for the subcommand along with description
output += getSubcommandOptionsHelp(subcmd);
output += '\n';
});

return output;
};

export const getSubcommandOptionsHelp = (subcmd: Subcommand): string => {
let output = '';

if (subcmd.options && subcmd.options.length > 0) {
// Generate a list of options along with their flags in the format:
// --option [--flag1 <flag1_value>] [--flag2 <flag2_value>] ...
const optionsWithFlags = subcmd.options.map((option) => {
const flags = generateFlagsString(option.flags);

return option.name + ' ' + flags;
});

subcmd.options.forEach((option, idx) => {
const optionStr = `--${optionsWithFlags[idx]}`;
const optionPadding = generatePadding(optionsWithFlags, optionStr.length);

output += ` ${optionStr} ${colors.grey(optionPadding)} ${colors.gray(option.description)}\n`;

if (option.flags) {
// Generate a list of flags with their aliases in the format:
// --flag | -f1 | -f2 ...
const flagsWithAlias = getFlagsWithAlias(option.flags);

option.flags.forEach((valOption, idx) => {
const optionPadding = generatePadding(flagsWithAlias, flagsWithAlias[idx].length);
output += ` ${flagsWithAlias[idx]} ${colors.grey(optionPadding)} ${colors.gray(valOption.description)}\n`;
});
}
});
}

return output;
};

const generateFlagsString = (flags: Flag[] | undefined) => {
// Generate a string of flags in the format:
// [--flag1 <flag1_value>] [--flag2 <flag2_value>] ...
if (!flags) {
return '';
}

let flagsStr = '';
flags.forEach(valOption => {
flagsStr += `[--${valOption.name} <${valOption.name}>] `;
});

return flagsStr;
};

const generatePadding = (array: string[], length: number): string => {
const longest = (xs: string[]) => Math.max.apply(null, xs.map(x => x.length));
const padding = new Array(Math.max(longest(array) - length + 3, 0)).join('.');

return padding;
};

const getFlagsWithAlias = (flags: Flag[]): string[] => {
const flagsWithAlias = flags.map(valOption => {
const optionAlias = valOption.alias.map(alias => `-${alias}`).join(' |');

return `--${valOption.name}` + (optionAlias ? ` | ${optionAlias}` : '');
});

return flagsWithAlias;
};

0 comments on commit cbe328e

Please sign in to comment.