From 51c3e20ab20aedae6b198df91438b8f560467fc6 Mon Sep 17 00:00:00 2001 From: Priyansh Prajapati <88396544+itsspriyansh@users.noreply.github.com> Date: Wed, 4 Sep 2024 02:17:55 +0530 Subject: [PATCH] Show help for configs allowed for subcommands and flags. (#55) Co-authored-by: Priyansh Garg --- src/commands/android/androidSetup.ts | 7 +- src/commands/android/constants.ts | 4 +- src/commands/android/index.ts | 2 +- src/commands/android/subcommands/help.ts | 121 ++++++++++++++++++++-- src/commands/android/subcommands/index.ts | 3 +- src/commands/android/utils/common.ts | 24 +---- 6 files changed, 121 insertions(+), 40 deletions(-) diff --git a/src/commands/android/androidSetup.ts b/src/commands/android/androidSetup.ts index 835332b..706dc46 100644 --- a/src/commands/android/androidSetup.ts +++ b/src/commands/android/androidSetup.ts @@ -1,4 +1,5 @@ import colors from 'ansi-colors'; +import boxen from 'boxen'; import {execSync} from 'child_process'; import * as dotenv from 'dotenv'; import fs from 'fs'; @@ -6,7 +7,6 @@ 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'; @@ -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 { diff --git a/src/commands/android/constants.ts b/src/commands/android/constants.ts index 7f3d3d4..a4b1aa1 100644 --- a/src/commands/android/constants.ts +++ b/src/commands/android/constants.ts @@ -54,7 +54,7 @@ export const AVAILABLE_SUBCOMMANDS: AvailableSubcommands = { }] }, install: { - description: 'Install APK or AVD on a device', + description: 'Install system images, AVDs, or APKs on a device', flags: [ { name: 'avd', @@ -73,7 +73,7 @@ export const AVAILABLE_SUBCOMMANDS: AvailableSubcommands = { { name: 'deviceId', alias: ['s'], - description: 'Id of the device to install the APK', + description: 'Id of the device to install the APK to if multiple devices are connected', usageHelp: 'device_id' } ] diff --git a/src/commands/android/index.ts b/src/commands/android/index.ts index f224c3b..d6db39b 100644 --- a/src/commands/android/index.ts +++ b/src/commands/android/index.ts @@ -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'; import {AndroidDotCommand} from './dotcommands'; export function handleAndroidCommand(args: string[], options: Options, argv: string[]): void { diff --git a/src/commands/android/subcommands/help.ts b/src/commands/android/subcommands/help.ts index 40048a9..0b74d6c 100644 --- a/src/commands/android/subcommands/help.ts +++ b/src/commands/android/subcommands/help.ts @@ -2,12 +2,12 @@ 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]; - const subcmdFlagUsage = subcmd.flags?.length ? ' [flag]' : ''; + const subcmdFlagUsage = subcmd.flags.length ? ' [flag]' : ''; Logger.log(`Usage: ${colors.cyan(`npx @nightwatch/mobile-helper android ${subcommand}${subcmdFlagUsage} [configs]`)}\n`); const subcmdFlagsHelp = getSubcommandFlagsHelp(subcmd); @@ -17,18 +17,119 @@ 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 SUBCOMMAND [flag] [configs]')}\n`; + output += ' Perform common Android SDK operations using subcommands.\n\n'; + output += `${colors.yellow('Subcommands (with available flags and configs):')}\n`; + + // A subcommand can allow multiple flags to facilitate different workflows, and each flag can + // allow multiple configs to customize the workflow. + // But, if a subcommand has a single workflow, then it doesn't require any flag and can have + // multiple configs directly associated with the subcommand itself. + Object.keys(AVAILABLE_SUBCOMMANDS).forEach(subcommand => { + const subcmd = AVAILABLE_SUBCOMMANDS[subcommand]; + + const subcmdFlagsUsage = subcmd.flags?.map(flag => `[--${flag.name}]`).join(' ') || ''; + const subcmdConfigsUsage = generateConfigsUsageString(subcmd.cliConfigs || []); + + // Display the subcommand name along with flags in the format: + // 'subcommand [--flag1] [--flag2]' + // OR + // Display the subcommand name along with direct configs in the format: + // 'subcommand [--config1 ] [--config2 ]' + output += ` ${colors.cyan(subcommand)} ${subcmdFlagsUsage}${subcmdConfigsUsage}\n`; + output += ` ${colors.gray(subcmd.description)}\n`; + + // Append a list of configs allowed for the subcommand, along with their aliases and description. + if (subcmd.cliConfigs) { + 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`; + }); + } + + output += getSubcommandFlagsHelp(subcmd); + output += '\n'; + }); + + return output; +}; + +/** + * Display a list of flags followed by their allowed configs for a subcommand. + */ +export const getSubcommandFlagsHelp = (subcmd: Subcommand): string => { + let output = ''; + + if (subcmd.flags.length) { + // Generate a list of flags usage with configs in the format: + // '--flag1 [--config11 ] [--config12 ]', + // '--flag2 [--config21 ] [--config22 ]' + const flagsUsageWithConfigs = subcmd.flags.map((flag) => { + let flagHelp = `--${flag.name}`; + + if (flag.cliConfigs) { + flagHelp += ' ' + generateConfigsUsageString(flag.cliConfigs); + } + + return flagHelp; + }); + + // Generate final help output for each flag. + subcmd.flags.forEach((flag, idx) => { + const padding = generatePadding(flagsUsageWithConfigs, flagsUsageWithConfigs[idx].length); + output += ` ${flagsUsageWithConfigs[idx]} ${colors.grey(padding)} ${colors.gray(flag.description)}\n`; + + // Append a list of configs allowed for the flag, along with their aliases and description. + if (flag.cliConfigs) { + 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; }; + +/* + * Generate a string of configs in the format: + * [--config1 ] [--config2 ] ... + */ +const generateConfigsUsageString = (configs: CliConfig[]): string => { + let configsStr = ''; + configs.forEach(config => { + configsStr += `[--${config.name} <${config.usageHelp}>]`; + }); + + return configsStr; +}; + +/** +* Generate a list of configs with their aliases in the format: +* '--config1 | -c11 | -c12', +* '--config2 | -c21 | -c22' +*/ +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; +}; + +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; +}; diff --git a/src/commands/android/subcommands/index.ts b/src/commands/android/subcommands/index.ts index b16e20f..fd01d14 100644 --- a/src/commands/android/subcommands/index.ts +++ b/src/commands/android/subcommands/index.ts @@ -6,7 +6,8 @@ import Logger from '../../../logger'; import {getPlatformName} from '../../../utils'; import {AVAILABLE_SUBCOMMANDS} from '../constants'; import {Options, Platform} from '../interfaces'; -import {checkJavaInstallation, getSdkRootFromEnv, getSubcommandHelp} from '../utils/common'; +import {checkJavaInstallation, getSdkRootFromEnv} from '../utils/common'; +import {getSubcommandHelp} from './help'; import {connect} from './connect'; import {showHelp} from './help'; import {install} from './install'; diff --git a/src/commands/android/utils/common.ts b/src/commands/android/utils/common.ts index 0ae2b28..8fa6485 100644 --- a/src/commands/android/utils/common.ts +++ b/src/commands/android/utils/common.ts @@ -12,11 +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} from '../interfaces'; -import {getSubcommandFlagsHelp} from '../subcommands/help'; export const getAllAvailableOptions = () => { const mainOptions = Object.keys(AVAILABLE_OPTIONS); @@ -212,23 +210,3 @@ export const checkJavaInstallation = (cwd: string): boolean => { return false; } }; - -export const getSubcommandHelp = (): string => { - let output = ''; - - output += `Usage: ${colors.cyan('npx @nightwatch/mobile-helper android SUBCOMMAND [flag] [configs]')}\n`; - output += ' Perform common Android SDK operations using subcommands.\n\n'; - output += `${colors.yellow('Subcommands (with available flags and configs):')}\n`; - - Object.keys(AVAILABLE_SUBCOMMANDS).forEach(subcommand => { - const subcmd = AVAILABLE_SUBCOMMANDS[subcommand]; - const subcmdOptions = subcmd.flags?.map(flag => `[--${flag.name}]`).join(' ') || ''; - - output += ` ${colors.cyan(subcommand)} ${subcmdOptions}\n`; - output += ` ${colors.gray(subcmd.description)}\n`; - - output += getSubcommandFlagsHelp(subcmd); - }); - - return output; -};