diff --git a/__tests__/fixtures/index/run-version/package.json b/__tests__/fixtures/index/run-version/package.json new file mode 100644 index 0000000000..501b0f2ddf --- /dev/null +++ b/__tests__/fixtures/index/run-version/package.json @@ -0,0 +1,5 @@ +{ + "name": "test_add", + "version": "1.0.0", + "license": "UNLICENSED" +} diff --git a/__tests__/index.js b/__tests__/index.js index e2e96907d8..b2dbe2d78d 100644 --- a/__tests__/index.js +++ b/__tests__/index.js @@ -3,6 +3,7 @@ import NoopReporter from '../src/reporters/base-reporter.js'; import makeTemp from './_temp'; import * as fs from '../src/util/fs.js'; +const pkg = require('../package.json'); const path = require('path'); const exec = require('child_process').exec; @@ -37,40 +38,68 @@ Promise> { }); } -test.concurrent('should add package', async () => { - const stdout = await execCommand('add', ['left-pad'], 'run-add', true); +function expectAddSuccessfullOutput(stdout, pkg) { const lastLines = stdout.slice(stdout.length - 4); expect(lastLines[0]).toEqual('success Saved lockfile.'); expect(lastLines[1]).toEqual('success Saved 1 new dependency.'); - expect(lastLines[2]).toMatch(/left-pad/); - expect(lastLines[3]).toMatch(/^Done/); + expect(lastLines[2]).toContain(pkg); + expect(lastLines[3]).toContain('Done'); +} + +function expectAddSuccessfullOutputWithNoLockFile(stdout, pkg) { + const lastLines = stdout.slice(stdout.length - 4); + expect(lastLines[1]).toEqual('success Saved 1 new dependency.'); + expect(lastLines[2]).toContain(pkg); + expect(lastLines[3]).toContain('Done'); +} + +function expectRunOutput(stdout) { + const lastLines = stdout.slice(stdout.length - 2); + expect(lastLines[0]).toMatch(/A message from custom script/); + expect(lastLines[1]).toMatch(/^Done/); +} + +function expectHelpOutput(stdout) { + expect(stdout[0]).toEqual('Usage: yarn [command] [flags]'); + const lastLines = stdout.slice(stdout.length - 2); + expect(lastLines[0]).toEqual('Run `yarn help COMMAND` for more information on specific commands.'); + expect(lastLines[1]).toEqual('Visit https://yarnpkg.com/en/docs/cli/ to learn more about Yarn.'); +} + +function expectHelpOutputAsSubcommand(stdout) { + expect(stdout[0]).toEqual('Usage: yarn add [packages ...] [flags]'); + expect(stdout[stdout.length - 1]) + .toEqual('Visit https://yarnpkg.com/en/docs/cli/add for documentation about this command.'); +} + +function expectAnErrorMessage(command: Promise>, error: string) : Promise { + return command.catch((reason) => + expect(reason.message).toContain(error), + ); +} + +function expectInstallOutput(stdout) { + expect(stdout[0]).toEqual(`yarn install v${pkg.version}`); +} + +test.concurrent('should add package', async () => { + const stdout = await execCommand('add', ['left-pad'], 'run-add', true); + expectAddSuccessfullOutput(stdout, 'left-pad'); }); test.concurrent('should add package with no-lockfile option', async () => { const stdout = await execCommand('add', ['repeating', '--no-lockfile'], 'run-add-option', true); - const lastLines = stdout.slice(stdout.length - 4); - expect(lastLines[0]).not.toMatch(/Saved lockfile/); - expect(lastLines[1]).toEqual('success Saved 1 new dependency.'); - expect(lastLines[2]).toMatch(/repeating/); - expect(lastLines[3]).toMatch(/^Done/); + expectAddSuccessfullOutputWithNoLockFile(stdout, 'repeating'); }); test.concurrent('should add package with no-lockfile option in front', async () => { const stdout = await execCommand('add', ['--no-lockfile', 'split-lines'], 'run-add-option-in-front', true); - const lastLines = stdout.slice(stdout.length - 4); - expect(lastLines[0]).not.toMatch(/Saved lockfile/); - expect(lastLines[1]).toEqual('success Saved 1 new dependency.'); - expect(lastLines[2]).toMatch(/split-lines/); - expect(lastLines[3]).toMatch(/^Done/); + expectAddSuccessfullOutputWithNoLockFile(stdout, 'split-lines'); }); test.concurrent('should add lockfile package', async () => { const stdout = await execCommand('add', ['lockfile'], 'run-add-lockfile', true); - const lastLines = stdout.slice(stdout.length - 4); - expect(lastLines[0]).toEqual('success Saved lockfile.'); - expect(lastLines[1]).toEqual('success Saved 1 new dependency.'); - expect(lastLines[2]).toMatch(/lockfile/); - expect(lastLines[3]).toMatch(/^Done/); + expectAddSuccessfullOutput(stdout, 'lockfile'); }); test.concurrent('should add progress package globally', async () => { @@ -85,24 +114,79 @@ test.concurrent('should add progress package globally', async () => { test.concurrent('should run custom script', async () => { const stdout = await execCommand('run', ['custom-script'], 'run-custom-script'); - const lastLines = stdout.slice(stdout.length - 2); - expect(lastLines[0]).toMatch(/A message from custom script/); - expect(lastLines[1]).toMatch(/^Done/); + expectRunOutput(stdout); }); test.concurrent('should run custom script without run command', async () => { const stdout = await execCommand('custom-script', [], 'run-custom-script'); - const lastLines = stdout.slice(stdout.length - 2); - expect(lastLines[0]).toMatch(/A message from custom script/); - expect(lastLines[1]).toMatch(/^Done/); + expectRunOutput(stdout); }); test.concurrent('should run help command', async () => { const stdout = await execCommand('help', [], 'run-help'); - expect(stdout[0]).toEqual('Usage: yarn [command] [flags]'); + expectHelpOutput(stdout); +}); + +test.concurrent('should run help command with --help', async () => { + const stdout = await execCommand('--help', [], 'run-help'); + expectHelpOutput(stdout); +}); + +test.concurrent('should run help command with -h', async () => { + const stdout = await execCommand('-h', [], 'run-help'); + expectHelpOutput(stdout); }); test.concurrent('should run add command with help option', async () => { const stdout = await execCommand('add', ['--help'], 'run-help'); - expect(stdout[0]).toEqual('Usage: yarn add [packages ...] [flags]'); + expectHelpOutputAsSubcommand(stdout); +}); + +test.concurrent('should run help command with add option', async () => { + const stdout = await execCommand('help', ['add'], 'run-help'); + expectHelpOutputAsSubcommand(stdout); +}); + +test.concurrent('should run --help command with add option', async () => { + const stdout = await execCommand('--help', ['add'], 'run-help'); + expectHelpOutputAsSubcommand(stdout); +}); + +test.concurrent('should run -h command with add option', async () => { + const stdout = await execCommand('-h', ['add'], 'run-help'); + expectHelpOutputAsSubcommand(stdout); +}); + +test.concurrent('should ignore all arguments after --', async () => { + const stdout = await execCommand('help', ['--', 'add'], 'run-help'); + expectHelpOutput(stdout); +}); + +test.concurrent('should run version command', async () => { + await expectAnErrorMessage( + execCommand('version', [], 'run-version'), + 'Can\'t answer a question unless a user TTY', + ); +}); + +test.concurrent('should run --version command with --version', async () => { + const stdout = await execCommand('--version', [], 'run-version'); + expect(stdout[0]).toEqual(pkg.version); +}); + +test.concurrent('should install if no args', async () => { + const stdout = await execCommand('', [], 'run-add', true); + expectInstallOutput(stdout); +}); + +test.concurrent('should install if first arg looks like a flag', async () => { + const stdout = await execCommand('--offline', [], 'run-add', true); + expectInstallOutput(stdout); +}); + +test.concurrent('should interpolate aliases', async () => { + await expectAnErrorMessage( + execCommand('i', [], 'run-add', true), + 'Did you mean `yarn install`?', + ); }); diff --git a/src/cli/index.js b/src/cli/index.js index da28db6325..103f304438 100644 --- a/src/cli/index.js +++ b/src/cli/index.js @@ -171,6 +171,9 @@ if (commandName === 'help' || args.indexOf('--help') >= 0 || args.indexOf('-h') console.log(); }); } + if (commandName !== 'help') { + commander.on('--help', () => console.log(' ' + getDocsInfo(commandName) + '\n')); + } commander.parse(startArgs.concat(args)); commander.help();