From 77e29e40e100527a8a3afeab6bd09ce7a8a1a073 Mon Sep 17 00:00:00 2001 From: Doug Parker Date: Tue, 3 May 2022 17:54:21 -0700 Subject: [PATCH] fix(@angular/cli): improve error message for Windows autocompletion use cases Windows Cmd and Powershell don't support autocompletion, but it can be done with utilities like Windows Subsystem for Linux and Git Bash, which should "just work" due to emulating a Linux environment. This clarifies the error message most users will see to call out the state of the world with regard to autocompletion on Windows platforms. --- .../angular/cli/src/utilities/completion.ts | 6 ++-- .../e2e/tests/misc/completion-prompt.ts | 30 +++++++++++++++++-- tests/legacy-cli/e2e/tests/misc/completion.ts | 26 ++++++++++++++-- 3 files changed, 54 insertions(+), 8 deletions(-) diff --git a/packages/angular/cli/src/utilities/completion.ts b/packages/angular/cli/src/utilities/completion.ts index 42c006db08ad..546f962543d4 100644 --- a/packages/angular/cli/src/utilities/completion.ts +++ b/packages/angular/cli/src/utilities/completion.ts @@ -193,7 +193,9 @@ export async function initializeAutocomplete(): Promise { if (!shell) { throw new Error( '`$SHELL` environment variable not set. Angular CLI autocompletion only supports Bash or' + - ' Zsh.', + " Zsh. If you're on Windows, Cmd and Powershell don't support command autocompletion," + + ' but Git Bash or Windows Subsystem for Linux should work, so please try again in one of' + + ' those environments.', ); } const home = env['HOME']; @@ -234,7 +236,7 @@ export async function initializeAutocomplete(): Promise { return rcFile; } -/** Returns an ordered list of possibile candidates of RC files used by the given shell. */ +/** Returns an ordered list of possible candidates of RC files used by the given shell. */ function getShellRunCommandCandidates(shell: string, home: string): string[] | undefined { if (shell.toLowerCase().includes('bash')) { return ['.bashrc', '.bash_profile', '.profile'].map((file) => path.join(home, file)); diff --git a/tests/legacy-cli/e2e/tests/misc/completion-prompt.ts b/tests/legacy-cli/e2e/tests/misc/completion-prompt.ts index d6ce1c00a56a..2fd74de20bcd 100644 --- a/tests/legacy-cli/e2e/tests/misc/completion-prompt.ts +++ b/tests/legacy-cli/e2e/tests/misc/completion-prompt.ts @@ -19,6 +19,13 @@ const DEFAULT_ENV = Object.freeze({ }); export default async function () { + // Windows Cmd and Powershell do not support autocompletion. Run a different set of tests to + // confirm autocompletion skips the prompt appropriately. + if (process.platform === 'win32') { + await windowsTests(); + return; + } + // Sets up autocompletion after user accepts a prompt from any command. await mockHome(async (home) => { const bashrc = path.join(home, '.bashrc'); @@ -42,7 +49,7 @@ export default async function () { const bashrcContents = await fs.readFile(bashrc, 'utf-8'); if (!bashrcContents.includes('source <(ng completion script)')) { throw new Error( - 'Autocompletion was *not* added to `~/.bashrc` after accepting the setup' + ' prompt.', + 'Autocompletion was *not* added to `~/.bashrc` after accepting the setup prompt.', ); } @@ -74,13 +81,13 @@ export default async function () { const bashrcContents = await fs.readFile(bashrc, 'utf-8'); if (bashrcContents.includes('ng completion')) { throw new Error( - 'Autocompletion was incorrectly added to `~/.bashrc` after refusing the setup' + ' prompt.', + 'Autocompletion was incorrectly added to `~/.bashrc` after refusing the setup prompt.', ); } if (stdout.includes('Appended `source <(ng completion script)`')) { throw new Error( - 'CLI printed that it successfully set up autocompletion when it actually' + " didn't.", + "CLI printed that it successfully set up autocompletion when it actually didn't.", ); } @@ -363,6 +370,23 @@ source <(ng completion script) }); } +async function windowsTests(): Promise { + // Should *not* prompt on Windows, autocompletion isn't supported. + await mockHome(async (home) => { + const bashrc = path.join(home, '.bashrc'); + await fs.writeFile(bashrc, `# Other content...`); + + const { stdout } = await execWithEnv('ng', ['version'], { ...env }); + + if (AUTOCOMPLETION_PROMPT.test(stdout)) { + throw new Error( + 'Execution prompted to set up autocompletion on Windows despite not actually being' + + ' supported.', + ); + } + }); +} + async function mockHome(cb: (home: string) => Promise): Promise { const tempHome = await fs.mkdtemp(path.join(os.tmpdir(), 'angular-cli-e2e-home-')); diff --git a/tests/legacy-cli/e2e/tests/misc/completion.ts b/tests/legacy-cli/e2e/tests/misc/completion.ts index 52e6bf18ad90..f7ce6243fa8e 100644 --- a/tests/legacy-cli/e2e/tests/misc/completion.ts +++ b/tests/legacy-cli/e2e/tests/misc/completion.ts @@ -4,6 +4,14 @@ import * as path from 'path'; import { execAndCaptureError, execAndWaitForOutputToMatch } from '../../utils/process'; export default async function () { + // Windows Cmd and Powershell do not support autocompletion. Run a different set of tests to + // confirm autocompletion fails gracefully. + if (process.platform === 'win32') { + await windowsTests(); + + return; + } + // Generates new `.bashrc` file. await mockHome(async (home) => { await execAndWaitForOutputToMatch( @@ -302,25 +310,37 @@ source <(ng completion script) } // Fails for no `$SHELL`. - { + await mockHome(async (home) => { const err = await execAndCaptureError('ng', ['completion'], { ...process.env, SHELL: undefined, + HOME: home, }); if (!err.message.includes('`$SHELL` environment variable not set.')) { throw new Error(`Expected unset \`$SHELL\` error message, but got:\n\n${err.message}`); } - } + }); // Fails for unknown `$SHELL`. - { + await mockHome(async (home) => { const err = await execAndCaptureError('ng', ['completion'], { ...process.env, SHELL: '/usr/bin/unknown', + HOME: home, }); if (!err.message.includes('Unknown `$SHELL` environment variable')) { throw new Error(`Expected unknown \`$SHELL\` error message, but got:\n\n${err.message}`); } + }); +} + +async function windowsTests(): Promise { + // Should fail with a clear error message. + const err = await execAndCaptureError('ng', ['completion']); + if (!err.message.includes("Cmd and Powershell don't support command autocompletion")) { + throw new Error( + `Expected Windows autocompletion to fail with custom error, but got:\n\n${err.message}`, + ); } }