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
diff options
context:
space:
mode:
authorisaacs <i@izs.me>2020-09-04 04:22:42 +0300
committerisaacs <i@izs.me>2020-09-04 21:51:33 +0300
commit2a4e2e9efecb7f86147e5071c59cfc2461a5a7f5 (patch)
tree3c51f6083502b161c62069505d2ab2730f1f30c7 /test/lib/utils
parent371f0f06215ad8caf598c20e3d0d38ff597531e9 (diff)
Explain ERESOLVE errors
When peerDependencies conflict, Arborist is now providing details of the nodes and their reasons for inclusion on the Error object, including whether or not this resolution error could be overridden using the --force flag. Print this data out in a minimal way as a warning if we override an ERESOLVE forcefully. When the ERESOLVE causes the install to fail, print a somewhat longer message, and save a MUCH longer full report to the cache folder. PR-URL: https://github.com/npm/cli/pull/1761 Credit: @isaacs Close: #1761 Reviewed-by: @darcyclarke, @ruyadorno
Diffstat (limited to 'test/lib/utils')
-rw-r--r--test/lib/utils/error-message.js20
-rw-r--r--test/lib/utils/explain-eresolve.js50
-rw-r--r--test/lib/utils/setup-log.js77
3 files changed, 123 insertions, 24 deletions
diff --git a/test/lib/utils/error-message.js b/test/lib/utils/error-message.js
index b69b5302f..14d75e31e 100644
--- a/test/lib/utils/error-message.js
+++ b/test/lib/utils/error-message.js
@@ -57,7 +57,16 @@ npmlog.verbose = (...message) => {
verboseLogs.push(message)
}
-const errorMessage = require('../../../lib/utils/error-message.js')
+const requireInject = require('require-inject')
+const EXPLAIN_CALLED = []
+const errorMessage = requireInject('../../../lib/utils/error-message.js', {
+ '../../../lib/utils/explain-eresolve.js': {
+ report: (...args) => {
+ EXPLAIN_CALLED.push(args)
+ return 'explanation'
+ }
+ }
+})
t.test('just simple messages', t => {
npm.command = 'audit'
@@ -416,3 +425,12 @@ t.test('bad platform', t => {
t.end()
})
+
+t.test('explain ERESOLVE errors', t => {
+ const er = Object.assign(new Error('could not resolve'), {
+ code: 'ERESOLVE'
+ })
+ t.matchSnapshot(errorMessage(er))
+ t.strictSame(EXPLAIN_CALLED, [[er]])
+ t.end()
+})
diff --git a/test/lib/utils/explain-eresolve.js b/test/lib/utils/explain-eresolve.js
new file mode 100644
index 000000000..def13153d
--- /dev/null
+++ b/test/lib/utils/explain-eresolve.js
@@ -0,0 +1,50 @@
+const t = require('tap')
+const requireInject = require('require-inject')
+const npm = {}
+const { explain, report } = requireInject('../../../lib/utils/explain-eresolve.js', {
+ '../../../lib/npm.js': npm
+})
+const { statSync, readFileSync, unlinkSync } = require('fs')
+// strip out timestamps from reports
+const read = f => readFileSync(f, 'utf8')
+ .replace(/\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z/g, '${TIME}')
+
+const { resolve } = require('path')
+
+const cases = require('../../fixtures/eresolve-explanations.js')
+
+for (const [name, expl] of Object.entries(cases)) {
+ // no sense storing the whole contents of each object in the snapshot
+ // we can trust that JSON.stringify still works just fine.
+ expl.toJSON = () => {
+ return { name, json: true }
+ }
+
+ t.test(name, t => {
+ npm.cache = t.testdir()
+ const reportFile = resolve(npm.cache, 'eresolve-report.txt')
+ t.cleanSnapshot = str => str.split(reportFile).join('${REPORT}')
+
+ npm.color = true
+ t.matchSnapshot(report(expl), 'report with color')
+ const reportData = read(reportFile)
+ t.matchSnapshot(reportData, 'report')
+ unlinkSync(reportFile)
+ t.matchSnapshot(report(expl, 2), 'report with color, depth only 2')
+ t.equal(read(reportFile), reportData, 'same report written for object')
+ unlinkSync(reportFile)
+ npm.color = false
+ t.matchSnapshot(report(expl, 6), 'report with no color, depth of 6')
+ t.equal(read(reportFile), reportData, 'same report written for object')
+
+ unlinkSync(reportFile)
+ npm.color = true
+ t.matchSnapshot(explain(expl), 'explain with color')
+ t.throws(() => statSync(reportFile), { code: 'ENOENT' }, 'no report')
+ npm.color = false
+ t.matchSnapshot(explain(expl, 6), 'explain with no color, depth of 6')
+ t.throws(() => statSync(reportFile), { code: 'ENOENT' }, 'no report')
+
+ t.end()
+ })
+}
diff --git a/test/lib/utils/setup-log.js b/test/lib/utils/setup-log.js
index 107edfb0f..2d5d794f1 100644
--- a/test/lib/utils/setup-log.js
+++ b/test/lib/utils/setup-log.js
@@ -7,30 +7,43 @@ t.afterEach(cb => {
cb()
})
+const WARN_CALLED = []
+const npmlog = {
+ level: 'warn',
+ warn: (...args) => {
+ WARN_CALLED.push(args)
+ },
+ levels: {
+ silly: -Infinity,
+ verbose: 1000,
+ info: 2000,
+ timing: 2500,
+ http: 3000,
+ notice: 3500,
+ warn: 4000,
+ error: 5000,
+ silent: Infinity
+ },
+ settings,
+ enableColor: () => { settings.color = true },
+ disableColor: () => { settings.color = false },
+ enableUnicode: () => { settings.unicode = true },
+ disableUnicode: () => { settings.unicode = false },
+ enableProgress: () => { settings.progress = true },
+ disableProgress: () => { settings.progress = false },
+ set heading (h) { settings.heading = h },
+ set level (l) { settings.level = l }
+}
+
+const EXPLAIN_CALLED = []
const setupLog = requireInject('../../../lib/utils/setup-log.js', {
- npmlog: {
- level: 'warn',
- levels: {
- silly: -Infinity,
- verbose: 1000,
- info: 2000,
- timing: 2500,
- http: 3000,
- notice: 3500,
- warn: 4000,
- error: 5000,
- silent: Infinity
- },
- settings,
- enableColor: () => { settings.color = true },
- disableColor: () => { settings.color = false },
- enableUnicode: () => { settings.unicode = true },
- disableUnicode: () => { settings.unicode = false },
- enableProgress: () => { settings.progress = true },
- disableProgress: () => { settings.progress = false },
- set heading (h) { settings.heading = h },
- set level (l) { settings.level = l }
- }
+ '../../../lib/utils/explain-eresolve.js': {
+ explain: (...args) => {
+ EXPLAIN_CALLED.push(args)
+ return 'explanation'
+ }
+ },
+ npmlog
})
const config = obj => ({
@@ -43,6 +56,11 @@ const config = obj => ({
})
t.test('setup with color=always and unicode', t => {
+ npmlog.warn('ERESOLVE', 'hello', { some: 'object' })
+ t.strictSame(EXPLAIN_CALLED, [], 'log.warn() not patched yet')
+ t.strictSame(WARN_CALLED, [['ERESOLVE', 'hello', { some: 'object' }]])
+ WARN_CALLED.length = 0
+
t.equal(setupLog(config({
loglevel: 'warn',
color: 'always',
@@ -50,6 +68,19 @@ t.test('setup with color=always and unicode', t => {
progress: false
})), true)
+ npmlog.warn('ERESOLVE', 'hello', { some: { other: 'object' } })
+ t.strictSame(EXPLAIN_CALLED, [[{ some: { other: 'object' } }]],
+ 'log.warn(ERESOLVE) patched to call explainEresolve()')
+ t.strictSame(WARN_CALLED, [
+ ['ERESOLVE', 'hello'],
+ ['', 'explanation']
+ ], 'warn the explanation')
+ EXPLAIN_CALLED.length = 0
+ WARN_CALLED.length = 0
+ npmlog.warn('some', 'other', 'thing')
+ t.strictSame(EXPLAIN_CALLED, [], 'do not try to explain other things')
+ t.strictSame(WARN_CALLED, [['some', 'other', 'thing']], 'warnings passed through')
+
t.strictSame(settings, {
level: 'warn',
color: true,