/
githubmirror
/
yarn
Обзор
Документация
Войти
/
githubmirror
/
yarn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
__tests__/commands/run.js
232 строки
9 KB
Jeff Valore
fix(run): add cwd/node_modules/.bin to run command search path (#7151)
31 окт 2019, 01:43
31 окт 2019, 01:43
79a96e3
Код
Авторство
О чём код?
/* @flow */ jest.mock('../../src/util/execute-lifecycle-script', () => { return { // $FlowFixMe ...require.requireActual('../../src/util/execute-lifecycle-script'), execCommand: jest.fn(), }; }); import path from 'path'; import {run as buildRun} from './_helpers.js'; import {BufferReporter} from '../../src/reporters/index.js'; import {run} from '../../src/cli/commands/run.js'; import * as fs from '../../src/util/fs.js'; import * as reporters from '../../src/reporters/index.js'; jasmine.DEFAULT_TIMEOUT_INTERVAL = 90000; const {execCommand}: $FlowFixMe = require('../../src/util/execute-lifecycle-script'); beforeEach(() => execCommand.mockClear()); const fixturesLoc = path.join(__dirname, '..', 'fixtures', 'run'); const runRun = buildRun.bind(null, BufferReporter, fixturesLoc, (args, flags, config, reporter): Promise<void> => { return run(config, reporter, flags, args); }); const runRunInWorkspacePackage = function(cwd, ...args): Promise<void> { return buildRun.bind(null, BufferReporter, fixturesLoc, (args, flags, config, reporter): Promise<void> => { const originalCwd = config.cwd; config.cwd = path.join(originalCwd, cwd); const retVal = run(config, reporter, flags, args); retVal.then(() => { config.cwd = originalCwd; }); return retVal; })(...args); }; const runRunWithCustomShell = function(customShell, ...args): Promise<void> { return buildRun.bind(null, BufferReporter, fixturesLoc, (args, flags, config, reporter): Promise<void> => { const yarnRegistry = config.registries.yarn; const originalCustomShell = yarnRegistry.config['script-shell']; yarnRegistry.config['script-shell'] = customShell; const retVal = run(config, reporter, flags, args); retVal.then(() => { yarnRegistry.config['script-shell'] = originalCustomShell; }); return retVal; })(...args); }; test('lists all available commands with no arguments', (): Promise<void> => runRun([], {}, 'no-args', (config, reporter): ?Promise<void> => { const rprtr = new reporters.BufferReporter({stdout: null, stdin: null}); const scripts = ['build', 'prestart', 'start']; const hints = { build: "echo 'building'", prestart: "echo 'prestart'", start: 'node index.js', }; const bins = ['cat-names']; // Emulate run output rprtr.info(`${rprtr.lang('binCommands')}${bins.join(', ')}`); rprtr.info(rprtr.lang('possibleCommands')); rprtr.list('possibleCommands', scripts, hints); rprtr.error(rprtr.lang('commandNotSpecified')); expect(reporter.getBuffer()).toEqual(rprtr.getBuffer()); })); test('lists all available commands with no arguments and --non-interactive', (): Promise<void> => runRun([], {nonInteractive: true}, 'no-args', (config, reporter): ?Promise<void> => { const rprtr = new reporters.BufferReporter({stdout: null, stdin: null}); const scripts = ['build', 'prestart', 'start']; const hints = { build: "echo 'building'", prestart: "echo 'prestart'", start: 'node index.js', }; const bins = ['cat-names']; // Emulate run output rprtr.info(`${rprtr.lang('binCommands')}${bins.join(', ')}`); rprtr.info(rprtr.lang('possibleCommands')); rprtr.list('possibleCommands', scripts, hints); expect(reporter.getBuffer()).toEqual(rprtr.getBuffer()); })); test('runs script containing spaces', (): Promise<void> => runRun(['build'], {}, 'spaces', async (config): ?Promise<void> => { const pkg = await fs.readJson(path.join(config.cwd, 'package.json')); // The command gets called with a space appended expect(execCommand).toBeCalledWith({ stage: 'build', config, cmd: pkg.scripts.build, cwd: config.cwd, isInteractive: true, }); })); test('properly handles extra arguments and pre/post scripts', (): Promise<void> => runRun(['start', '--hello'], {}, 'extra-args', async (config): ?Promise<void> => { const pkg = await fs.readJson(path.join(config.cwd, 'package.json')); const poststart = {stage: 'poststart', config, cmd: pkg.scripts.poststart, cwd: config.cwd, isInteractive: true}; const prestart = {stage: 'prestart', config, cmd: pkg.scripts.prestart, cwd: config.cwd, isInteractive: true}; const start = {stage: 'start', config, cmd: pkg.scripts.start + ' --hello', cwd: config.cwd, isInteractive: true}; expect(execCommand.mock.calls[0]).toEqual([prestart]); expect(execCommand.mock.calls[1]).toEqual([start]); expect(execCommand.mock.calls[2]).toEqual([poststart]); })); test('properly handle bin scripts', (): Promise<void> => runRun(['cat-names'], {}, 'bin', config => { const script = path.join(config.cwd, 'node_modules', '.bin', 'cat-names'); expect(execCommand).toBeCalledWith({ stage: 'cat-names', config, cmd: script, cwd: config.cwd, isInteractive: true, }); })); test('properly handle env command', (): Promise<void> => runRun(['env'], {}, 'no-args', (config, reporter): ?Promise<void> => { // $FlowFixMe const result = JSON.parse(reporter.getBuffer()[0].data); const env = {}; let pathVarName = 'PATH'; for (const key of Object.keys(process.env)) { // Filter out yarn-added `npm_` variables since we run tests through yarn already if (key.startsWith('npm_')) { continue; } // We need this below for Windows which has case-insensitive env vars // If we used `process.env` directly, node takes care of this for us, // but since we use a subset of it, we need to get the "real" path key // name for Jest's case-sensitive object comparison below. if (key.toUpperCase() === 'PATH') { pathVarName = key; } env[key] = process.env[key]; } result[pathVarName] = result[pathVarName] ? result[pathVarName].split(path.delimiter) : []; // $FlowFixMe env[pathVarName] = env[pathVarName] ? expect.arrayContaining(env[pathVarName].split(path.delimiter)) : []; expect(result).toMatchObject(env); expect(result).toHaveProperty('npm_lifecycle_event'); expect(result).toHaveProperty('npm_execpath'); expect(result).toHaveProperty('npm_node_execpath'); })); test('adds string delimiters if args have spaces', (): Promise<void> => runRun(['cat-names', '--filter', 'cat names'], {}, 'bin', config => { const script = path.join(config.cwd, 'node_modules', '.bin', 'cat-names'); const q = process.platform === 'win32' ? '"' : "'"; expect(execCommand).toBeCalledWith({ stage: 'cat-names', config, cmd: `${script} --filter ${q}cat names${q}`, cwd: config.cwd, isInteractive: true, }); })); test('adds quotes if args have spaces and quotes', (): Promise<void> => runRun(['cat-names', '--filter', '"cat names"'], {}, 'bin', config => { const script = path.join(config.cwd, 'node_modules', '.bin', 'cat-names'); const quotedCatNames = process.platform === 'win32' ? '^"\\^"cat^ names\\^"^"' : `'"cat names"'`; expect(execCommand).toBeCalledWith({ stage: 'cat-names', config, cmd: `${script} --filter ${quotedCatNames}`, cwd: config.cwd, isInteractive: true, }); })); test('returns noScriptsAvailable with no scripts', (): Promise<void> => runRun([], {}, 'no-scripts', (config, reporter) => { expect(reporter.getBuffer()).toMatchSnapshot(); })); test('returns noBinAvailable with no bins', (): Promise<void> => runRun([], {}, 'no-bin', (config, reporter) => { expect(reporter.getBuffer()).toMatchSnapshot(); })); test('adds workspace root node_modules/.bin to path when in a workspace', (): Promise<void> => runRunInWorkspacePackage('packages/pkg1', ['env'], {}, 'workspace', (config, reporter): ?Promise<void> => { const logEntry = reporter.getBuffer().find(entry => entry.type === 'log'); const parsedLogData = JSON.parse(logEntry ? logEntry.data.toString() : '{}'); const envPaths = (parsedLogData.PATH || parsedLogData.Path).split(path.delimiter); expect(envPaths).toContain(path.join(config.cwd, 'node_modules', '.bin')); expect(envPaths).toContain(path.join(config.cwd, 'packages', 'pkg1', 'node_modules', '.bin')); })); test('adds cwd node_modules/.bin to path when in a workspace usig nohoist', (): Promise<void> => runRunInWorkspacePackage('packages/pkg1', ['env'], {}, 'nohoist-workspace', (config, reporter): ?Promise<void> => { const logEntry = reporter.getBuffer().find(entry => entry.type === 'log'); const parsedLogData = JSON.parse(logEntry ? logEntry.data.toString() : '{}'); const envPaths = (parsedLogData.PATH || parsedLogData.Path).split(path.delimiter); expect(envPaths).toContain(path.join(config.cwd, 'node_modules', '.bin')); expect(envPaths).toContain(path.join(config.cwd, 'packages', 'pkg1', 'node_modules', '.bin')); })); test('runs script with custom script-shell', (): Promise<void> => runRunWithCustomShell('/usr/bin/dummy', ['start'], {}, 'script-shell', async (config): ?Promise<void> => { const pkg = await fs.readJson(path.join(config.cwd, 'package.json')); // The command gets called with the provided customShell expect(execCommand).toBeCalledWith({ stage: 'start', config, cmd: pkg.scripts.start, cwd: config.cwd, isInteractive: true, customShell: '/usr/bin/dummy', }); }));