From 195fe31cb837448e166eebc2a1c007e89424444c Mon Sep 17 00:00:00 2001 From: Anshuman Verma Date: Tue, 22 Sep 2020 20:49:02 +0530 Subject: [PATCH] refactor: cleanup package-utils --- packages/utils/__tests__/index.test.ts | 80 +++++++++++++++++++++++ packages/utils/src/index.ts | 1 + packages/utils/src/prompt-installation.ts | 6 +- yarn.lock | 18 ++++- 4 files changed, 101 insertions(+), 4 deletions(-) create mode 100644 packages/utils/__tests__/index.test.ts diff --git a/packages/utils/__tests__/index.test.ts b/packages/utils/__tests__/index.test.ts new file mode 100644 index 00000000000..2cfcaeab359 --- /dev/null +++ b/packages/utils/__tests__/index.test.ts @@ -0,0 +1,80 @@ +'use strict'; + +import { packageExists, promptInstallation, runCommand } from '../'; +import ExternalCommand from '../../webpack-cli/lib/commands/ExternalCommand'; + +describe('@webpack-cli/utils', () => { + it('should check existence of package', () => { + // use an actual path relative to the packageUtils file + expect(packageExists('../lib/package-exists')).toBeTruthy(); + expect(packageExists('./nonexistent-package')).toBeFalsy(); + }); + + it('should not throw if the user interrupts', async () => { + (promptInstallation as jest.Mock).mockImplementation(() => { + throw new Error(); + }); + await expect(ExternalCommand.run('info')).resolves.not.toThrow(); + }); +}); + +describe('promptInstallation', () => { + let packageUtils; + beforeAll(() => { + packageUtils = require('../lib/'); + packageUtils.getPackageManager = jest.fn(); + packageUtils.packageExists = jest.fn(() => true); + }); + + beforeEach(() => { + (runCommand as jest.Mock).mockClear(); + (prompt as jest.Mock).mockClear(); + }); + + it('should prompt to install using npm if npm is package manager', async () => { + packageUtils.getPackageManager.mockReturnValue('npm'); + (prompt as jest.Mock).mockReturnValue({ + installConfirm: true, + }); + + const preMessage = jest.fn(); + const promptResult = await packageUtils.promptInstallation('test-package', preMessage); + expect(promptResult).toBeTruthy(); + expect(preMessage.mock.calls.length).toEqual(1); + expect((prompt as jest.Mock).mock.calls.length).toEqual(1); + expect((runCommand as jest.Mock).mock.calls.length).toEqual(1); + expect((prompt as jest.Mock).mock.calls[0][0][0].message).toMatch(/Would you like to install test-package\?/); + // install the package using npm + expect((runCommand as jest.Mock).mock.calls[0][0]).toEqual('npm install -D test-package'); + }); + + it('should prompt to install using yarn if yarn is package manager', async () => { + packageUtils.getPackageManager.mockReturnValue('yarn'); + (prompt as jest.Mock).mockReturnValue({ + installConfirm: true, + }); + + const promptResult = await packageUtils.promptInstallation('test-package'); + expect(promptResult).toBeTruthy(); + expect((prompt as jest.Mock).mock.calls.length).toEqual(1); + expect((runCommand as jest.Mock).mock.calls.length).toEqual(1); + expect((prompt as jest.Mock).mock.calls[0][0][0].message).toMatch(/Would you like to install test-package\?/); + // install the package using yarn + expect((runCommand as jest.Mock).mock.calls[0][0]).toEqual('yarn add -D test-package'); + }); + + it('should not install if install is not confirmed', async () => { + packageUtils.getPackageManager.mockReturnValue('npm'); + (prompt as jest.Mock).mockReturnValue({ + installConfirm: false, + }); + + const promptResult = await packageUtils.promptInstallation('test-package'); + expect(promptResult).toBeUndefined(); + expect((prompt as jest.Mock).mock.calls.length).toEqual(1); + // runCommand should not be called, because the installation is not confirmed + expect((runCommand as jest.Mock).mock.calls.length).toEqual(0); + expect((prompt as jest.Mock).mock.calls[0][0][0].message).toMatch(/Would you like to install test-package\?/); + expect(process.exitCode).toEqual(2); + }); +}); diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index 46145db13f5..5a29dc28ac6 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -13,3 +13,4 @@ export * from './global-packages-path'; export * from './get-package-manager'; export * from './package-exists'; export * from './run-command'; +export * from './prompt-installation'; diff --git a/packages/utils/src/prompt-installation.ts b/packages/utils/src/prompt-installation.ts index 601f3f74e66..c8ee4075b97 100644 --- a/packages/utils/src/prompt-installation.ts +++ b/packages/utils/src/prompt-installation.ts @@ -1,6 +1,8 @@ import { prompt } from 'enquirer'; import { green } from 'colorette'; import { runCommand } from './run-command'; +import { getPackageManager } from './get-package-manager'; +import { packageExists } from './package-exists'; /** * @@ -9,7 +11,7 @@ import { runCommand } from './run-command'; */ // eslint-disable-next-line @typescript-eslint/explicit-function-return-type export async function promptInstallation(packageName: string, preMessage?: Function) { - const packageManager = exports.getPackageManager(); + const packageManager = getPackageManager(); const options = [packageManager === 'yarn' ? 'add' : 'install', '-D', packageName]; const commandToBeRun = `${packageManager} ${options.join(' ')}`; @@ -27,7 +29,7 @@ export async function promptInstallation(packageName: string, preMessage?: Funct ]); if (installConfirm) { await runCommand(commandToBeRun); - return exports.packageExists(packageName); + return packageExists(packageName); } // eslint-disable-next-line require-atomic-updates process.exitCode = 2; diff --git a/yarn.lock b/yarn.lock index 69fdb2e9b0d..c47831037ee 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2235,7 +2235,12 @@ "@types/node" "*" "@types/responselike" "*" -"@types/cross-spawn@^6.0.1": +"@types/color-name@^1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0" + integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ== + +"@types/cross-spawn@^6.0.2": version "6.0.2" resolved "https://registry.yarnpkg.com/@types/cross-spawn/-/cross-spawn-6.0.2.tgz#168309de311cd30a2b8ae720de6475c2fbf33ac7" integrity sha512-KuwNhp3eza+Rhu8IFI5HUXRP0LIhqH5cAjubUvGXXthh4YYBuP2ntwEX+Cz8GJoZUHlKo247wPWOfA9LYEq4cw== @@ -4348,7 +4353,16 @@ cross-spawn@^6.0.0, cross-spawn@^6.0.5: shebang-command "^1.2.0" which "^1.2.9" -cross-spawn@^7.0.0, cross-spawn@^7.0.1: +cross-spawn@^7.0.0: + version "7.0.1" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.1.tgz#0ab56286e0f7c24e153d04cc2aa027e43a9a5d14" + integrity sha512-u7v4o84SwFpD32Z8IIcPZ6z1/ie24O6RU3RbtL5Y316l3KuHVPx9ItBgWQ6VlfAFnRnTtMUrsQ9MUUTuEZjogg== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==