Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/npm/cli.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authorRuy Adorno <ruyadorno@hotmail.com>2021-03-07 20:13:58 +0300
committerRuy Adorno <ruyadorno@hotmail.com>2021-03-19 01:14:46 +0300
commit33c4189f939aebdfaf85ea419e6ea01d0977b79d (patch)
treed819ff7f8738a216911fe7a2d8ffdc9e18b5353b /test
parent8a38afe779ce71a10178ed62b13709d06adf7a66 (diff)
feat: add run-script workspaces
- Add workspaces-related configs: - workspace: list of workspaces names/dir to filter for - workspaces: boolean value to enable/disable workspaces awareness - adds the proposed note in the docs of each of the commands that are not affected by these configs. - Add workspaces support to `npm run-script` - add ability to serially run lifecycle scripts in workspaces - add ability to list scripts for all workspaces - add colors to `npm run` (no args) output Relates to: https://github.com/npm/rfcs/pull/117 Fixes: https://github.com/npm/statusboard/issues/276 Fixes: https://github.com/npm/statusboard/issues/283 Fixes: https://github.com/npm/statusboard/issues/284 Fixes: https://github.com/npm/statusboard/issues/285 Fixes: https://github.com/npm/statusboard/issues/286 PR-URL: https://github.com/npm/cli/pull/2864 Credit: @ruyadorno Close: #2864 Reviewed-by: @wraithgar
Diffstat (limited to 'test')
-rw-r--r--test/lib/npm.js78
-rw-r--r--test/lib/run-script.js466
-rw-r--r--test/lib/utils/did-you-mean.js20
-rw-r--r--test/lib/utils/lifecycle-cmd.js6
4 files changed, 560 insertions, 10 deletions
diff --git a/test/lib/npm.js b/test/lib/npm.js
index eb0f8ab27..de0dcaa1c 100644
--- a/test/lib/npm.js
+++ b/test/lib/npm.js
@@ -348,6 +348,84 @@ t.test('npm.load', t => {
await new Promise((res) => setTimeout(res))
})
+ t.test('workpaces-aware configs and commands', async t => {
+ const dir = t.testdir({
+ packages: {
+ a: {
+ 'package.json': JSON.stringify({
+ name: 'a',
+ version: '1.0.0',
+ scripts: { test: 'echo test a' },
+ }),
+ },
+ b: {
+ 'package.json': JSON.stringify({
+ name: 'b',
+ version: '1.0.0',
+ scripts: { test: 'echo test b' },
+ }),
+ },
+ },
+ 'package.json': JSON.stringify({
+ name: 'root',
+ version: '1.0.0',
+ workspaces: ['./packages/*'],
+ }),
+ '.npmrc': '',
+ })
+
+ const { log } = console
+ const consoleLogs = []
+ console.log = (...msg) => consoleLogs.push(msg)
+
+ const { execPath } = process
+ t.teardown(() => {
+ console.log = log
+ })
+
+ freshConfig({
+ argv: [
+ execPath,
+ process.argv[1],
+ '--userconfig',
+ resolve(dir, '.npmrc'),
+ '--color',
+ 'false',
+ '--workspaces',
+ 'true',
+ ],
+ })
+
+ await npm.load(er => {
+ if (er)
+ throw er
+ })
+
+ npm.localPrefix = dir
+
+ await new Promise((res, rej) => {
+ npm.commands['run-script']([], er => {
+ if (er)
+ rej(er)
+
+ t.match(
+ consoleLogs,
+ [
+ ['Lifecycle scripts included in a@1.0.0:'],
+ [' test\n echo test a'],
+ [''],
+ ['Lifecycle scripts included in b@1.0.0:'],
+ [' test\n echo test b'],
+ [''],
+ ],
+ 'should exec workspaces version of commands'
+ )
+
+ res()
+ })
+ })
+ })
+
t.end()
})
diff --git a/test/lib/run-script.js b/test/lib/run-script.js
index d2cac2f42..db1fc4b5c 100644
--- a/test/lib/run-script.js
+++ b/test/lib/run-script.js
@@ -1,7 +1,15 @@
+const { resolve } = require('path')
const t = require('tap')
const requireInject = require('require-inject')
const mockNpm = require('../fixtures/mock-npm')
+const normalizePath = p => p
+ .replace(/\\+/g, '/')
+ .replace(/\r\n/g, '\n')
+
+const cleanOutput = (str) => normalizePath(str)
+ .replace(normalizePath(process.cwd()), '{CWD}')
+
const RUN_SCRIPTS = []
const flatOptions = {
scriptShell: undefined,
@@ -20,21 +28,33 @@ const npm = mockNpm({
help: {
description: 'test help description',
},
+ test: {
+ description: 'test test description',
+ },
},
output: (...msg) => output.push(msg),
})
const output = []
+const npmlog = {
+ disableProgress: () => null,
+ level: 'warn',
+ error: () => null,
+}
+
t.afterEach(cb => {
+ npm.color = false
+ npmlog.level = 'warn'
+ npmlog.error = () => null
output.length = 0
RUN_SCRIPTS.length = 0
+ config['if-present'] = false
config.json = false
config.parseable = false
cb()
})
-const npmlog = { level: 'warn' }
const getRS = windows => {
const RunScript = requireInject('../../lib/run-script.js', {
'@npmcli/run-script': Object.assign(async opts => {
@@ -298,7 +318,7 @@ t.test('try to run missing script', t => {
})
})
t.test('with --if-present', t => {
- npm.config.set('if-present', true)
+ config['if-present'] = true
runScript.exec(['goodbye'], er => {
if (er)
throw er
@@ -461,13 +481,14 @@ t.test('list scripts', t => {
if (er)
throw er
t.strictSame(output, [
- ['Lifecycle scripts included in x:'],
+ ['Lifecycle scripts included in x@1.2.3:'],
[' test\n exit 2'],
[' start\n node server.js'],
[' stop\n node kill-server.js'],
['\navailable via `npm run-script`:'],
[' preenv\n echo before the env'],
[' postenv\n echo after the env'],
+ [''],
], 'basic report')
t.end()
})
@@ -540,8 +561,9 @@ t.test('list scripts, only commands', t => {
if (er)
throw er
t.strictSame(output, [
- ['Lifecycle scripts included in x:'],
+ ['Lifecycle scripts included in x@1.2.3:'],
[' preversion\n echo doing the version dance'],
+ [''],
])
t.end()
})
@@ -560,9 +582,443 @@ t.test('list scripts, only non-commands', t => {
if (er)
throw er
t.strictSame(output, [
- ['Scripts available in x via `npm run-script`:'],
+ ['Scripts available in x@1.2.3 via `npm run-script`:'],
[' glorp\n echo doing the glerp glop'],
+ [''],
])
t.end()
})
})
+
+t.test('workspaces', t => {
+ npm.localPrefix = t.testdir({
+ packages: {
+ a: {
+ 'package.json': JSON.stringify({
+ name: 'a',
+ version: '1.0.0',
+ scripts: { glorp: 'echo a doing the glerp glop' },
+ }),
+ },
+ b: {
+ 'package.json': JSON.stringify({
+ name: 'b',
+ version: '2.0.0',
+ scripts: { glorp: 'echo b doing the glerp glop' },
+ }),
+ },
+ c: {
+ 'package.json': JSON.stringify({
+ name: 'c',
+ version: '1.0.0',
+ scripts: {
+ test: 'exit 0',
+ posttest: 'echo posttest',
+ lorem: 'echo c lorem',
+ },
+ }),
+ },
+ d: {
+ 'package.json': JSON.stringify({
+ name: 'd',
+ version: '1.0.0',
+ scripts: {
+ test: 'exit 0',
+ posttest: 'echo posttest',
+ },
+ }),
+ },
+ e: {
+ 'package.json': JSON.stringify({
+ name: 'e',
+ scripts: { test: 'exit 0', start: 'echo start something' },
+ }),
+ },
+ noscripts: {
+ 'package.json': JSON.stringify({
+ name: 'noscripts',
+ version: '1.0.0',
+ }),
+ },
+ },
+ 'package.json': JSON.stringify({
+ name: 'x',
+ version: '1.2.3',
+ workspaces: ['packages/*'],
+ }),
+ })
+
+ t.test('list all scripts', t => {
+ runScript.execWorkspaces([], [], er => {
+ if (er)
+ throw er
+ t.strictSame(output, [
+ ['Scripts available in a@1.0.0 via `npm run-script`:'],
+ [' glorp\n echo a doing the glerp glop'],
+ [''],
+ ['Scripts available in b@2.0.0 via `npm run-script`:'],
+ [' glorp\n echo b doing the glerp glop'],
+ [''],
+ ['Lifecycle scripts included in c@1.0.0:'],
+ [' test\n exit 0'],
+ [' posttest\n echo posttest'],
+ ['\navailable via `npm run-script`:'],
+ [' lorem\n echo c lorem'],
+ [''],
+ ['Lifecycle scripts included in d@1.0.0:'],
+ [' test\n exit 0'],
+ [' posttest\n echo posttest'],
+ [''],
+ ['Lifecycle scripts included in e:'],
+ [' test\n exit 0'],
+ [' start\n echo start something'],
+ [''],
+ ])
+ t.end()
+ })
+ })
+
+ t.test('list regular scripts, filtered by name', t => {
+ runScript.execWorkspaces([], ['a', 'b'], er => {
+ if (er)
+ throw er
+ t.strictSame(output, [
+ ['Scripts available in a@1.0.0 via `npm run-script`:'],
+ [' glorp\n echo a doing the glerp glop'],
+ [''],
+ ['Scripts available in b@2.0.0 via `npm run-script`:'],
+ [' glorp\n echo b doing the glerp glop'],
+ [''],
+ ])
+ t.end()
+ })
+ })
+
+ t.test('list regular scripts, filtered by path', t => {
+ runScript.execWorkspaces([], ['./packages/a'], er => {
+ if (er)
+ throw er
+ t.strictSame(output, [
+ ['Scripts available in a@1.0.0 via `npm run-script`:'],
+ [' glorp\n echo a doing the glerp glop'],
+ [''],
+ ])
+ t.end()
+ })
+ })
+
+ t.test('list regular scripts, filtered by parent folder', t => {
+ runScript.execWorkspaces([], ['./packages'], er => {
+ if (er)
+ throw er
+ t.strictSame(output, [
+ ['Scripts available in a@1.0.0 via `npm run-script`:'],
+ [' glorp\n echo a doing the glerp glop'],
+ [''],
+ ['Scripts available in b@2.0.0 via `npm run-script`:'],
+ [' glorp\n echo b doing the glerp glop'],
+ [''],
+ ['Lifecycle scripts included in c@1.0.0:'],
+ [' test\n exit 0'],
+ [' posttest\n echo posttest'],
+ ['\navailable via `npm run-script`:'],
+ [' lorem\n echo c lorem'],
+ [''],
+ ['Lifecycle scripts included in d@1.0.0:'],
+ [' test\n exit 0'],
+ [' posttest\n echo posttest'],
+ [''],
+ ['Lifecycle scripts included in e:'],
+ [' test\n exit 0'],
+ [' start\n echo start something'],
+ [''],
+ ])
+ t.end()
+ })
+ })
+
+ t.test('list all scripts with colors', t => {
+ npm.color = true
+ runScript.execWorkspaces([], [], er => {
+ if (er)
+ throw er
+ t.strictSame(output, [
+ [
+ '\u001b[1mScripts\u001b[22m available in \x1B[32ma@1.0.0\x1B[39m via `\x1B[34mnpm run-script\x1B[39m`:',
+ ],
+ [' glorp\n \x1B[2mecho a doing the glerp glop\x1B[22m'],
+ [''],
+ [
+ '\u001b[1mScripts\u001b[22m available in \x1B[32mb@2.0.0\x1B[39m via `\x1B[34mnpm run-script\x1B[39m`:',
+ ],
+ [' glorp\n \x1B[2mecho b doing the glerp glop\x1B[22m'],
+ [''],
+ [
+ '\x1B[0m\x1B[1mLifecycle scripts\x1B[22m\x1B[0m included in \x1B[32mc@1.0.0\x1B[39m:',
+ ],
+ [' test\n \x1B[2mexit 0\x1B[22m'],
+ [' posttest\n \x1B[2mecho posttest\x1B[22m'],
+ ['\navailable via `\x1B[34mnpm run-script\x1B[39m`:'],
+ [' lorem\n \x1B[2mecho c lorem\x1B[22m'],
+ [''],
+ [
+ '\x1B[0m\x1B[1mLifecycle scripts\x1B[22m\x1B[0m included in \x1B[32md@1.0.0\x1B[39m:',
+ ],
+ [' test\n \x1B[2mexit 0\x1B[22m'],
+ [' posttest\n \x1B[2mecho posttest\x1B[22m'],
+ [''],
+ [
+ '\x1B[0m\x1B[1mLifecycle scripts\x1B[22m\x1B[0m included in \x1B[32me\x1B[39m:',
+ ],
+ [' test\n \x1B[2mexit 0\x1B[22m'],
+ [' start\n \x1B[2mecho start something\x1B[22m'],
+ [''],
+ ])
+ t.end()
+ })
+ })
+
+ t.test('list all scripts --json', t => {
+ config.json = true
+ runScript.execWorkspaces([], [], er => {
+ if (er)
+ throw er
+ t.strictSame(output, [
+ [
+ '{\n' +
+ ' "a": {\n' +
+ ' "glorp": "echo a doing the glerp glop"\n' +
+ ' },\n' +
+ ' "b": {\n' +
+ ' "glorp": "echo b doing the glerp glop"\n' +
+ ' },\n' +
+ ' "c": {\n' +
+ ' "test": "exit 0",\n' +
+ ' "posttest": "echo posttest",\n' +
+ ' "lorem": "echo c lorem"\n' +
+ ' },\n' +
+ ' "d": {\n' +
+ ' "test": "exit 0",\n' +
+ ' "posttest": "echo posttest"\n' +
+ ' },\n' +
+ ' "e": {\n' +
+ ' "test": "exit 0",\n' +
+ ' "start": "echo start something"\n' +
+ ' },\n' +
+ ' "noscripts": {}\n' +
+ '}',
+ ],
+ ])
+ t.end()
+ })
+ })
+
+ t.test('list all scripts --parseable', t => {
+ config.parseable = true
+ runScript.execWorkspaces([], [], er => {
+ if (er)
+ throw er
+ t.strictSame(output, [
+ ['a:glorp:echo a doing the glerp glop'],
+ ['b:glorp:echo b doing the glerp glop'],
+ ['c:test:exit 0'],
+ ['c:posttest:echo posttest'],
+ ['c:lorem:echo c lorem'],
+ ['d:test:exit 0'],
+ ['d:posttest:echo posttest'],
+ ['e:test:exit 0'],
+ ['e:start:echo start something'],
+ ])
+ t.end()
+ })
+ })
+
+ t.test('list no scripts --loglevel=silent', t => {
+ npmlog.level = 'silent'
+ runScript.execWorkspaces([], [], er => {
+ if (er)
+ throw er
+ t.strictSame(output, [])
+ t.end()
+ })
+ })
+
+ t.test('run scripts across all workspaces', t => {
+ runScript.execWorkspaces(['test'], [], er => {
+ if (er)
+ throw er
+
+ t.match(RUN_SCRIPTS, [
+ {
+ path: resolve(npm.localPrefix, 'packages/c'),
+ pkg: { name: 'c', version: '1.0.0' },
+ event: 'test',
+ },
+ {
+ path: resolve(npm.localPrefix, 'packages/c'),
+ pkg: { name: 'c', version: '1.0.0' },
+ event: 'posttest',
+ },
+ {
+ path: resolve(npm.localPrefix, 'packages/d'),
+ pkg: { name: 'd', version: '1.0.0' },
+ event: 'test',
+ },
+ {
+ path: resolve(npm.localPrefix, 'packages/d'),
+ pkg: { name: 'd', version: '1.0.0' },
+ event: 'posttest',
+ },
+ {
+ path: resolve(npm.localPrefix, 'packages/e'),
+ pkg: { name: 'e' },
+ event: 'test',
+ },
+ ])
+ t.end()
+ })
+ })
+
+ t.test('missing scripts in all workspaces', t => {
+ const LOG = []
+ npmlog.error = (err) => {
+ LOG.push(String(err))
+ }
+ runScript.execWorkspaces(['missing-script'], [], er => {
+ t.match(
+ er,
+ /Missing script: missing-script/,
+ 'should throw missing script error'
+ )
+
+ process.exitCode = 0 // clean exit code
+
+ t.match(RUN_SCRIPTS, [])
+ t.strictSame(LOG.map(cleanOutput), [
+ 'Lifecycle script `missing-script` failed with error:',
+ 'Error: Unknown command: "missing-script"',
+ ' in workspace: a@1.0.0',
+ ' at location: {CWD}/test/lib/run-script-workspaces/packages/a',
+ 'Lifecycle script `missing-script` failed with error:',
+ 'Error: Unknown command: "missing-script"',
+ ' in workspace: b@2.0.0',
+ ' at location: {CWD}/test/lib/run-script-workspaces/packages/b',
+ 'Lifecycle script `missing-script` failed with error:',
+ 'Error: Unknown command: "missing-script"',
+ ' in workspace: c@1.0.0',
+ ' at location: {CWD}/test/lib/run-script-workspaces/packages/c',
+ 'Lifecycle script `missing-script` failed with error:',
+ 'Error: Unknown command: "missing-script"',
+ ' in workspace: d@1.0.0',
+ ' at location: {CWD}/test/lib/run-script-workspaces/packages/d',
+ 'Lifecycle script `missing-script` failed with error:',
+ 'Error: Unknown command: "missing-script"',
+ ' in workspace: e',
+ ' at location: {CWD}/test/lib/run-script-workspaces/packages/e',
+ 'Lifecycle script `missing-script` failed with error:',
+ 'Error: Unknown command: "missing-script"',
+ ' in workspace: noscripts@1.0.0',
+ ' at location: {CWD}/test/lib/run-script-workspaces/packages/noscripts',
+ ], 'should log error msgs for each workspace script')
+
+ t.end()
+ })
+ })
+
+ t.test('missing scripts in some workspaces', t => {
+ const LOG = []
+ npmlog.error = (err) => {
+ LOG.push(String(err))
+ }
+ runScript.execWorkspaces(['test'], ['a', 'b', 'c', 'd'], er => {
+ if (er)
+ throw er
+
+ t.match(RUN_SCRIPTS, [])
+ t.strictSame(LOG.map(cleanOutput), [
+ 'Lifecycle script `test` failed with error:',
+ 'Error: Unknown command: "test"',
+ ' in workspace: a@1.0.0',
+ ' at location: {CWD}/test/lib/run-script-workspaces/packages/a',
+ 'Lifecycle script `test` failed with error:',
+ 'Error: Unknown command: "test"',
+ ' in workspace: b@2.0.0',
+ ' at location: {CWD}/test/lib/run-script-workspaces/packages/b',
+ ], 'should log error msgs for each workspace script')
+ t.end()
+ })
+ })
+
+ t.test('no workspaces when filtering by user args', t => {
+ runScript.execWorkspaces([], ['foo', 'bar'], er => {
+ t.equal(
+ er.message,
+ 'No workspaces found:\n --workspace=foo --workspace=bar',
+ 'should throw error msg'
+ )
+ t.end()
+ })
+ })
+
+ t.test('no workspaces', t => {
+ const _prevPrefix = npm.localPrefix
+ npm.localPrefix = t.testdir({
+ 'package.json': JSON.stringify({
+ name: 'foo',
+ version: '1.0.0',
+ }),
+ })
+
+ runScript.execWorkspaces([], [], er => {
+ t.match(er, /No workspaces found!/, 'should throw error msg')
+ npm.localPrefix = _prevPrefix
+ t.end()
+ })
+ })
+
+ t.test('single failed workspace run', t => {
+ const RunScript = requireInject('../../lib/run-script.js', {
+ '@npmcli/run-script': () => {
+ throw new Error('err')
+ },
+ npmlog,
+ '../../lib/utils/is-windows-shell.js': false,
+ })
+ const runScript = new RunScript(npm)
+
+ runScript.execWorkspaces(['test'], ['c'], er => {
+ t.ok('should complete running all targets')
+ process.exitCode = 0 // clean up exit code
+ t.end()
+ })
+ })
+
+ t.test('failed workspace run with succeeded runs', t => {
+ const RunScript = requireInject('../../lib/run-script.js', {
+ '@npmcli/run-script': async opts => {
+ if (opts.pkg.name === 'a')
+ throw new Error('ERR')
+
+ RUN_SCRIPTS.push(opts)
+ },
+ npmlog,
+ '../../lib/utils/is-windows-shell.js': false,
+ })
+ const runScript = new RunScript(npm)
+
+ runScript.execWorkspaces(['glorp'], ['a', 'b'], er => {
+ t.match(RUN_SCRIPTS, [
+ {
+ path: resolve(npm.localPrefix, 'packages/b'),
+ pkg: { name: 'b', version: '2.0.0' },
+ event: 'glorp',
+ },
+ ])
+
+ process.exitCode = 0 // clean up exit code
+ t.end()
+ })
+ })
+
+ t.end()
+})
diff --git a/test/lib/utils/did-you-mean.js b/test/lib/utils/did-you-mean.js
index 48b6d4027..898806aa1 100644
--- a/test/lib/utils/did-you-mean.js
+++ b/test/lib/utils/did-you-mean.js
@@ -7,25 +7,37 @@ t.test('did-you-mean', t => {
npm.load(err => {
t.notOk(err)
t.test('nistall', async t => {
- const result = await dym(npm, 'nistall')
+ const result = await dym(npm, npm.localPrefix, 'nistall')
t.match(result, 'Unknown command')
t.match(result, 'npm install')
})
t.test('sttest', async t => {
- const result = await dym(npm, 'sttest')
+ const result = await dym(npm, npm.localPrefix, 'sttest')
t.match(result, 'Unknown command')
t.match(result, 'npm test')
t.match(result, 'npm run posttest')
})
t.test('npz', async t => {
- const result = await dym(npm, 'npxx')
+ const result = await dym(npm, npm.localPrefix, 'npxx')
t.match(result, 'Unknown command')
t.match(result, 'npm exec npx')
})
t.test('qwuijbo', async t => {
- const result = await dym(npm, 'qwuijbo')
+ const result = await dym(npm, npm.localPrefix, 'qwuijbo')
t.match(result, 'Unknown command')
})
t.end()
})
})
+
+t.test('missing bin and script properties', async t => {
+ const path = t.testdir({
+ 'package.json': JSON.stringify({
+ name: 'missing-bin',
+ }),
+ })
+
+ const result = await dym(npm, path, 'nistall')
+ t.match(result, 'Unknown command')
+ t.match(result, 'npm install')
+})
diff --git a/test/lib/utils/lifecycle-cmd.js b/test/lib/utils/lifecycle-cmd.js
index 3e3a7da43..862c87a8e 100644
--- a/test/lib/utils/lifecycle-cmd.js
+++ b/test/lib/utils/lifecycle-cmd.js
@@ -10,6 +10,7 @@ const npm = {
},
}
t.test('create a lifecycle command', t => {
+ t.plan(5)
class TestStage extends LifecycleCmd {
static get name () {
return 'test-stage'
@@ -20,6 +21,9 @@ t.test('create a lifecycle command', t => {
cmd.exec(['some', 'args'], (er, result) => {
t.same(runArgs, ['test-stage', 'some', 'args'])
t.strictSame(result, 'called npm.commands.run')
- t.end()
+ })
+ cmd.execWorkspaces(['some', 'args'], [], (er, result) => {
+ t.same(runArgs, ['test-stage', 'some', 'args'])
+ t.strictSame(result, 'called npm.commands.run')
})
})