diff options
author | isaacs <i@izs.me> | 2020-09-04 04:22:42 +0300 |
---|---|---|
committer | isaacs <i@izs.me> | 2020-09-04 21:51:33 +0300 |
commit | 2a4e2e9efecb7f86147e5071c59cfc2461a5a7f5 (patch) | |
tree | 3c51f6083502b161c62069505d2ab2730f1f30c7 /test/lib/utils | |
parent | 371f0f06215ad8caf598c20e3d0d38ff597531e9 (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.js | 20 | ||||
-rw-r--r-- | test/lib/utils/explain-eresolve.js | 50 | ||||
-rw-r--r-- | test/lib/utils/setup-log.js | 77 |
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, |