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-08-07 00:13:30 +0300
committerisaacs <i@izs.me>2020-08-07 00:28:50 +0300
commitd062b2c02a4d6d5f1a274aa8eb9c5969ca6253db (patch)
treed9e1d7ff5fe77d21f93d37c50bb72bb03f4a7940
parentcf2819210327952696346486002239f9fc184a3e (diff)
new npm-specific update-notifier implementation
This drops our usage of the update-notifier module, in favor of checking ourselves, using the modules and UX patterns that npm already has in place. - While on a prerelease version, updates are checked for every day, instead of every week, and always checks for a new beta in the current release family. Ie, ^7.0.0-beta.2 instead of latest. - Latest version is suggested if newer than current. - If current version is newer than latest, then we check again for an update in the current version family. Ie, ^7.0.0 instead of latest, if current is 7.0.0 and latest is 6.x. - Output is printed using log.notice, at the end of all other log output, so that it's both less visually disruptive, and less likely to be missed among other warnings and notices. This has the side effect of requiring that we set npm.flatOptions as soon as config is loaded, rather than waiting for a command to be run. Since the cli runs a command immediately after loading anyway, this is not a relevant change for our purposes, but worth mentioning here.
-rw-r--r--lib/cli.js4
-rw-r--r--lib/npm.js15
-rw-r--r--lib/utils/error-handler.js7
-rw-r--r--lib/utils/update-notifier.js151
-rw-r--r--tap-snapshots/test-lib-utils-update-notifier.js-TAP.test.js120
-rw-r--r--test/lib/npm.js3
-rw-r--r--test/lib/utils/update-notifier.js287
7 files changed, 388 insertions, 199 deletions
diff --git a/lib/cli.js b/lib/cli.js
index f76d88991..b8ed90902 100644
--- a/lib/cli.js
+++ b/lib/cli.js
@@ -52,10 +52,10 @@ module.exports = (process) => {
// this is how to use npm programmatically:
conf._exit = true
const updateNotifier = require('../lib/utils/update-notifier.js')
- npm.load(conf, er => {
+ npm.load(conf, async er => {
if (er) return errorHandler(er)
- updateNotifier(npm)
+ npm.updateNotification = await updateNotifier(npm)
const cmd = npm.argv.shift()
const impl = npm.commands[cmd]
diff --git a/lib/npm.js b/lib/npm.js
index ae4537d95..d8a325a79 100644
--- a/lib/npm.js
+++ b/lib/npm.js
@@ -66,6 +66,11 @@ const npm = module.exports = new class extends EventEmitter {
constructor () {
super()
require('./utils/perf.js')
+ this.modes = {
+ exec: 0o755,
+ file: 0o644,
+ umask: 0o22
+ }
this.started = Date.now()
this.command = null
this.commands = proxyCmds(this)
@@ -75,6 +80,7 @@ const npm = module.exports = new class extends EventEmitter {
this.config = notYetLoadedConfig
this.loading = false
this.loaded = false
+ this.updateNotification = null
}
deref (c) {
@@ -93,11 +99,6 @@ const npm = module.exports = new class extends EventEmitter {
process.emit('time', `command:${cmd}`)
this.command = cmd
- if (!this[_flatOptions]) {
- this[_flatOptions] = require('./config/flat-options.js')(this)
- require('./config/set-envs.js')(this)
- }
-
// Options are prefixed by a hyphen-minus (-, \u2d).
// Other dash-type chars look similar but are invalid.
if (!warnedNonDashArg) {
@@ -144,6 +145,10 @@ const npm = module.exports = new class extends EventEmitter {
if (!er && this.config.get('force')) {
this.log.warn('using --force', 'Recommended protections disabled.')
}
+ if (!er && !this[_flatOptions]) {
+ this[_flatOptions] = require('./config/flat-options.js')(this)
+ require('./config/set-envs.js')(this)
+ }
process.emit('timeEnd', 'npm:load')
this.emit('load', er)
})
diff --git a/lib/utils/error-handler.js b/lib/utils/error-handler.js
index 8f5ad6fab..94ce38ad0 100644
--- a/lib/utils/error-handler.js
+++ b/lib/utils/error-handler.js
@@ -130,6 +130,13 @@ const errorHandler = (er) => {
er = er || new Error('Callback called more than once.')
}
+ if (npm.updateNotification) {
+ const { level } = log
+ log.level = log.levels.notice
+ log.notice('', npm.updateNotification)
+ log.level = level
+ }
+
cbCalled = true
if (!er) return exit(0)
if (typeof er === 'string') {
diff --git a/lib/utils/update-notifier.js b/lib/utils/update-notifier.js
index 5a88e54f1..dda6f3dca 100644
--- a/lib/utils/update-notifier.js
+++ b/lib/utils/update-notifier.js
@@ -1,57 +1,122 @@
// print a banner telling the user to upgrade npm to latest
// but not in CI, and not if we're doing that already.
+// Check daily for betas, and weekly otherwise.
+
+const pacote = require('pacote')
+const ciDetect = require('@npmcli/ci-detect')
+const semver = require('semver')
+const chalk = require('chalk')
+const { promisify } = require('util')
+const stat = promisify(require('fs').stat)
+const writeFile = promisify(require('fs').writeFile)
+const { resolve } = require('path')
const isGlobalNpmUpdate = npm => {
- return npm.config.get('global') &&
+ return npm.flatOptions.global &&
['install', 'update'].includes(npm.command) &&
npm.argv.includes('npm')
}
-const { checkVersion } = require('./unsupported.js')
+// update check frequency
+const DAILY = 1000 * 60 * 60 * 24
+const WEEKLY = DAILY * 7
+
+const updateTimeout = async (npm, duration) => {
+ const t = new Date(Date.now() - duration)
+ // don't put it in the _cacache folder, just in npm's cache
+ const f = resolve(npm.flatOptions.cache, '../_update-notifier-last-checked')
+ // if we don't have a file, then definitely check it.
+ const st = await stat(f).catch(() => ({ mtime: t - 1 }))
+
+ if (t > st.mtime) {
+ // best effort, if this fails, it's ok.
+ // might be using /dev/null as the cache or something weird like that.
+ await writeFile(f, '').catch(() => {})
+ return true
+ } else {
+ return false
+ }
+}
-module.exports = (npm) => {
+const updateNotifier = module.exports = async (npm, spec = 'latest') => {
+ // never check for updates in CI, when updating npm already, or opted out
if (!npm.config.get('update-notifier') ||
- isGlobalNpmUpdate(npm) ||
- checkVersion(process.version).unsupported) {
- return
+ isGlobalNpmUpdate(npm) ||
+ ciDetect()) {
+ return null
+ }
+
+ // if we're on a prerelease train, then updates are coming fast
+ // check for a new one daily. otherwise, weekly.
+ const { version } = npm
+ const current = semver.parse(version)
+
+ // if we're on a beta train, always get the next beta
+ if (current.prerelease.length) {
+ spec = `^${version}`
+ }
+
+ // while on a beta train, get updates daily
+ const duration = spec !== 'latest' ? DAILY : WEEKLY
+
+ // if we've already checked within the specified duration, don't check again
+ if (!(await updateTimeout(npm, duration))) {
+ return null
+ }
+
+ // if they're currently using a prerelease, nudge to the next prerelease
+ // otherwise, nudge to latest.
+ const useColor = npm.log.useColor()
+
+ const mani = await pacote.manifest(`npm@${spec}`, {
+ // always prefer latest, even if doing --tag=whatever on the cmd
+ defaultTag: 'latest',
+ ...npm.flatOptions
+ }).catch(() => null)
+
+ // if pacote failed, give up
+ if (!mani) {
+ return null
}
- const pkg = require('../../package.json')
- const notifier = require('update-notifier')({ pkg })
- const ciDetect = require('@npmcli/ci-detect')
- if (
- notifier.update &&
- notifier.update.latest !== pkg.version &&
- !ciDetect()
- ) {
- const chalk = require('chalk')
- const useColor = npm.color
- const useUnicode = npm.config.get('unicode')
- const old = notifier.update.current
- const latest = notifier.update.latest
- const type = notifier.update.type
- const typec = !useColor ? type
- : type === 'major' ? chalk.red(type)
- : type === 'minor' ? chalk.yellow(type)
- : chalk.green(type)
-
- const changelog = `https://github.com/npm/cli/releases/tag/v${latest}`
- notifier.notify({
- message: `New ${typec} version of ${pkg.name} available! ${
- useColor ? chalk.red(old) : old
- } ${useUnicode ? '→' : '->'} ${
- useColor ? chalk.green(latest) : latest
- }\n` +
- `${
- useColor ? chalk.yellow('Changelog:') : 'Changelog:'
- } ${
- useColor ? chalk.cyan(changelog) : changelog
- }\n` +
- `Run ${
- useColor
- ? chalk.green(`npm install -g ${pkg.name}`)
- : `npm i -g ${pkg.name}`
- } to update!`
- })
+ const latest = mani.version
+
+ // if the current version is *greater* than latest, we're on a 'next'
+ // and should get the updates from that release train.
+ // Note that this isn't another http request over the network, because
+ // the packument will be cached by pacote from previous request.
+ if (semver.gt(version, latest) && spec === 'latest') {
+ return updateNotifier(npm, `^${version}`)
+ }
+
+ // if we already have something >= the desired spec, then we're done
+ if (semver.gte(version, latest)) {
+ return null
}
+
+ // ok! notify the user about this update they should get.
+ // The message is saved for printing at process exit so it will not get
+ // lost in any other messages being printed as part of the command.
+ const update = semver.parse(mani.version)
+ const type = update.major !== current.major ? 'major'
+ : update.minor !== current.minor ? 'minor'
+ : update.patch !== current.patch ? 'patch'
+ : 'prerelease'
+ const typec = !useColor ? type
+ : type === 'major' ? chalk.red(type)
+ : type === 'minor' ? chalk.yellow(type)
+ : chalk.green(type)
+ const oldc = !useColor ? current : chalk.red(current)
+ const latestc = !useColor ? latest : chalk.green(latest)
+ const changelog = `https://github.com/npm/cli/releases/tag/v${latest}`
+ const changelogc = !useColor ? `<${changelog}>` : chalk.cyan(changelog)
+ const cmd = `npm install -g npm@${latest}`
+ const cmdc = !useColor ? `\`${cmd}\`` : chalk.green(cmd)
+ const message = `\nNew ${typec} version of npm available! ` +
+ `${oldc} -> ${latestc}\n` +
+ `Changelog: ${changelogc}\n` +
+ `Run ${cmdc} to update!\n`
+ const messagec = !useColor ? message : chalk.bgBlack.white(message)
+
+ return messagec
}
diff --git a/tap-snapshots/test-lib-utils-update-notifier.js-TAP.test.js b/tap-snapshots/test-lib-utils-update-notifier.js-TAP.test.js
index cab4cdf18..91228650d 100644
--- a/tap-snapshots/test-lib-utils-update-notifier.js-TAP.test.js
+++ b/tap-snapshots/test-lib-utils-update-notifier.js-TAP.test.js
@@ -5,74 +5,98 @@
* Make sure to inspect the output below. Do not ignore changes!
*/
'use strict'
-exports[`test/lib/utils/update-notifier.js TAP notification situations color and unicode major > must match snapshot 1`] = `
-New major version of npm available! <<major>>-beta.1 → 7.0.0
-Changelog: https://github.com/npm/cli/releases/tag/v7.0.0
-Run npm install -g npm to update!
+exports[`test/lib/utils/update-notifier.js TAP notification situations major to current > color 1`] = `
+
+New major version of npm available! 122.420.69 -> 123.420.69
+Changelog: https://github.com/npm/cli/releases/tag/v123.420.69
+Run npm install -g npm@123.420.69 to update!
+
`
-exports[`test/lib/utils/update-notifier.js TAP notification situations color and unicode minor > must match snapshot 1`] = `
-New minor version of npm available! <<minor>>-beta.1 → 7.0.0
-Changelog: https://github.com/npm/cli/releases/tag/v7.0.0
-Run npm install -g npm to update!
+exports[`test/lib/utils/update-notifier.js TAP notification situations major to current > no color 1`] = `
+
+New major version of npm available! 122.420.69 -> 123.420.69
+Changelog: <https://github.com/npm/cli/releases/tag/v123.420.69>
+Run \`npm install -g npm@123.420.69\` to update!
+
`
-exports[`test/lib/utils/update-notifier.js TAP notification situations color and unicode minor > must match snapshot 2`] = `
-New patch version of npm available! <<patch>>-beta.1 → 7.0.0
-Changelog: https://github.com/npm/cli/releases/tag/v7.0.0
-Run npm install -g npm to update!
+exports[`test/lib/utils/update-notifier.js TAP notification situations minor to current > color 1`] = `
+
+New minor version of npm available! 123.419.69 -> 123.420.69
+Changelog: https://github.com/npm/cli/releases/tag/v123.420.69
+Run npm install -g npm@123.420.69 to update!
+
`
-exports[`test/lib/utils/update-notifier.js TAP notification situations color, no unicode major > must match snapshot 1`] = `
-New major version of npm available! <<major>>-beta.1 -> 7.0.0
-Changelog: https://github.com/npm/cli/releases/tag/v7.0.0
-Run npm install -g npm to update!
+exports[`test/lib/utils/update-notifier.js TAP notification situations minor to current > no color 1`] = `
+
+New minor version of npm available! 123.419.69 -> 123.420.69
+Changelog: <https://github.com/npm/cli/releases/tag/v123.420.69>
+Run \`npm install -g npm@123.420.69\` to update!
+
`
-exports[`test/lib/utils/update-notifier.js TAP notification situations color, no unicode minor > must match snapshot 1`] = `
-New minor version of npm available! <<minor>>-beta.1 -> 7.0.0
-Changelog: https://github.com/npm/cli/releases/tag/v7.0.0
-Run npm install -g npm to update!
+exports[`test/lib/utils/update-notifier.js TAP notification situations minor to next version > color 1`] = `
+
+New minor version of npm available! 123.420.70 -> 123.421.70
+Changelog: https://github.com/npm/cli/releases/tag/v123.421.70
+Run npm install -g npm@123.421.70 to update!
+
`
-exports[`test/lib/utils/update-notifier.js TAP notification situations color, no unicode minor > must match snapshot 2`] = `
-New patch version of npm available! <<patch>>-beta.1 -> 7.0.0
-Changelog: https://github.com/npm/cli/releases/tag/v7.0.0
-Run npm install -g npm to update!
+exports[`test/lib/utils/update-notifier.js TAP notification situations minor to next version > no color 1`] = `
+
+New minor version of npm available! 123.420.70 -> 123.421.70
+Changelog: <https://github.com/npm/cli/releases/tag/v123.421.70>
+Run \`npm install -g npm@123.421.70\` to update!
+
`
-exports[`test/lib/utils/update-notifier.js TAP notification situations no color, no unicode major > must match snapshot 1`] = `
-New major version of npm available! <<major>>-beta.1 -> 7.0.0
-Changelog: https://github.com/npm/cli/releases/tag/v7.0.0
-Run npm i -g npm to update!
+exports[`test/lib/utils/update-notifier.js TAP notification situations new beta available > color 1`] = `
+
+New prerelease version of npm available! 124.0.0-beta.0 -> 124.0.0-beta.99999
+Changelog: https://github.com/npm/cli/releases/tag/v124.0.0-beta.99999
+Run npm install -g npm@124.0.0-beta.99999 to update!
+
`
-exports[`test/lib/utils/update-notifier.js TAP notification situations no color, no unicode minor > must match snapshot 1`] = `
-New minor version of npm available! <<minor>>-beta.1 -> 7.0.0
-Changelog: https://github.com/npm/cli/releases/tag/v7.0.0
-Run npm i -g npm to update!
+exports[`test/lib/utils/update-notifier.js TAP notification situations new beta available > no color 1`] = `
+
+New prerelease version of npm available! 124.0.0-beta.0 -> 124.0.0-beta.99999
+Changelog: <https://github.com/npm/cli/releases/tag/v124.0.0-beta.99999>
+Run \`npm install -g npm@124.0.0-beta.99999\` to update!
+
`
-exports[`test/lib/utils/update-notifier.js TAP notification situations no color, no unicode minor > must match snapshot 2`] = `
-New patch version of npm available! <<patch>>-beta.1 -> 7.0.0
-Changelog: https://github.com/npm/cli/releases/tag/v7.0.0
-Run npm i -g npm to update!
+exports[`test/lib/utils/update-notifier.js TAP notification situations patch to current > color 1`] = `
+
+New patch version of npm available! 123.420.68 -> 123.420.69
+Changelog: https://github.com/npm/cli/releases/tag/v123.420.69
+Run npm install -g npm@123.420.69 to update!
+
`
-exports[`test/lib/utils/update-notifier.js TAP notification situations unicode, no color major > must match snapshot 1`] = `
-New major version of npm available! <<major>>-beta.1 → 7.0.0
-Changelog: https://github.com/npm/cli/releases/tag/v7.0.0
-Run npm i -g npm to update!
+exports[`test/lib/utils/update-notifier.js TAP notification situations patch to current > no color 1`] = `
+
+New patch version of npm available! 123.420.68 -> 123.420.69
+Changelog: <https://github.com/npm/cli/releases/tag/v123.420.69>
+Run \`npm install -g npm@123.420.69\` to update!
+
`
-exports[`test/lib/utils/update-notifier.js TAP notification situations unicode, no color minor > must match snapshot 1`] = `
-New minor version of npm available! <<minor>>-beta.1 → 7.0.0
-Changelog: https://github.com/npm/cli/releases/tag/v7.0.0
-Run npm i -g npm to update!
+exports[`test/lib/utils/update-notifier.js TAP notification situations patch to next version > color 1`] = `
+
+New patch version of npm available! 123.421.69 -> 123.421.70
+Changelog: https://github.com/npm/cli/releases/tag/v123.421.70
+Run npm install -g npm@123.421.70 to update!
+
`
-exports[`test/lib/utils/update-notifier.js TAP notification situations unicode, no color minor > must match snapshot 2`] = `
-New patch version of npm available! <<patch>>-beta.1 → 7.0.0
-Changelog: https://github.com/npm/cli/releases/tag/v7.0.0
-Run npm i -g npm to update!
+exports[`test/lib/utils/update-notifier.js TAP notification situations patch to next version > no color 1`] = `
+
+New patch version of npm available! 123.421.69 -> 123.421.70
+Changelog: <https://github.com/npm/cli/releases/tag/v123.421.70>
+Run \`npm install -g npm@123.421.70\` to update!
+
`
diff --git a/test/lib/npm.js b/test/lib/npm.js
index e4e31e5d3..3c62643fc 100644
--- a/test/lib/npm.js
+++ b/test/lib/npm.js
@@ -115,8 +115,7 @@ t.test('npm.load', t => {
t.match(npm, {
loaded: true,
loading: false,
- // flatOptions only loaded when we run an actual command
- flatOptions: null
+ flatOptions: {}
})
t.equal(firstCalled, true, 'first callback got called')
t.equal(secondCalled, true, 'second callback got called')
diff --git a/test/lib/utils/update-notifier.js b/test/lib/utils/update-notifier.js
index c55a2c1d4..903e888a5 100644
--- a/test/lib/utils/update-notifier.js
+++ b/test/lib/utils/update-notifier.js
@@ -1,121 +1,210 @@
const t = require('tap')
const requireInject = require('require-inject')
-const notifierMock = {}
-const unsupportedMock = {}
-const unsupported = () => unsupportedMock
let ciMock = null
-const updateNotifier = requireInject.installGlobally('../../../lib/utils/update-notifier.js', {
- 'update-notifier': () => notifierMock,
- '../../../lib/utils/unsupported.js': { checkVersion: unsupported },
- '@npmcli/ci-detect': () => ciMock
+const flatOptions = { global: false, cache: t.testdir() + '/_cacache' }
+
+const MANIFEST_REQUEST = []
+const CURRENT_VERSION = '123.420.69'
+const CURRENT_MAJOR = '122.420.69'
+const CURRENT_MINOR = '123.419.69'
+const CURRENT_PATCH = '123.420.68'
+const NEXT_VERSION = '123.421.70'
+const NEXT_MINOR = '123.420.70'
+const NEXT_PATCH = '123.421.69'
+const CURRENT_BETA = '124.0.0-beta.99999'
+const HAVE_BETA = '124.0.0-beta.0'
+
+let PACOTE_ERROR = null
+const pacote = {
+ manifest: async (spec, opts) => {
+ if (!spec.match(/^npm@/)) {
+ console.error(new Error('should only fetch manifest for npm'))
+ process.exit(1)
+ }
+ MANIFEST_REQUEST.push(spec)
+ if (PACOTE_ERROR) {
+ throw PACOTE_ERROR
+ }
+ return {
+ version: spec === 'npm@latest' ? CURRENT_VERSION
+ : /-/.test(spec) ? CURRENT_BETA
+ : NEXT_VERSION
+ }
+ }
+}
+
+const npm = {
+ flatOptions,
+ log: { useColor: () => true },
+ version: CURRENT_VERSION,
+ config: { get: (k) => k !== 'global' },
+ flatOptions,
+ command: 'view',
+ argv: ['npm']
+}
+const npmNoColor = {
+ ...npm,
+ log: { useColor: () => false }
+}
+
+const { basename } = require('path')
+
+let STAT_ERROR = null
+let STAT_MTIME = null
+let WRITE_ERROR = null
+const fs = {
+ stat: (path, cb) => {
+ if (basename(path) !== '_update-notifier-last-checked') {
+ console.error(new Error('should only write to notifier last checked file'))
+ process.exit(1)
+ }
+ process.nextTick(() => cb(STAT_ERROR, { mtime: new Date(STAT_MTIME) }))
+ },
+ writeFile: (path, content, cb) => {
+ if (content !== '') {
+ console.error(new Error('should not be writing content'))
+ process.exit(1)
+ }
+ if (basename(path) !== '_update-notifier-last-checked') {
+ console.error(new Error('should only write to notifier last checked file'))
+ process.exit(1)
+ }
+ process.nextTick(() => cb(WRITE_ERROR))
+ }
+}
+
+const updateNotifier = requireInject('../../../lib/utils/update-notifier.js', {
+ '@npmcli/ci-detect': () => ciMock,
+ pacote,
+ fs
})
-const { version } = require('../../../package.json')
+
const semver = require('semver')
+t.afterEach(cb => {
+ MANIFEST_REQUEST.length = 0
+ STAT_ERROR = null
+ PACOTE_ERROR = null
+ STAT_MTIME = null
+ WRITE_ERROR = null
+ cb()
+})
+
t.test('situations in which we do not notify', t => {
- t.test('nothing to do if notifier disabled', t => {
- Object.assign(notifierMock, {
- notify: () => { throw new Error('should not notify!') },
- update: { latest: '99.99.99' },
- })
- updateNotifier({
- config: { get: (k) => k === 'update-notifier' ? false : true },
- })
- t.end()
- })
-
- t.test('do not suggest update if already updating', t => {
- Object.assign(notifierMock, {
- notify: () => { throw new Error('should not notify!') },
- update: { latest: '99.99.99' },
- })
- updateNotifier({
- config: { get: (k) => true },
+ t.test('nothing to do if notifier disabled', async t => {
+ t.equal(await updateNotifier({
+ ...npm,
+ config: { get: (k) => k === 'update-notifier' ? false : true }
+ }), null)
+ t.strictSame(MANIFEST_REQUEST, [], 'no requests for manifests')
+ })
+
+ t.test('do not suggest update if already updating', async t => {
+ t.equal(await updateNotifier({
+ ...npm,
+ flatOptions: { ...flatOptions, global: true },
command: 'install',
argv: ['npm']
- })
- t.end()
- })
-
- t.test('do not suggest update if version if unsupported', t => {
- t.teardown(() => { delete unsupportedMock.unsupported })
- unsupportedMock.unsupported = true
- Object.assign(notifierMock, {
- notify: () => { throw new Error('should not notify!') },
- update: { latest: '99.99.99' },
- })
- updateNotifier({
- config: { get: (k) => k !== 'global' },
- command: 'view',
- argv: ['npm']
- })
- t.end()
- })
-
- t.test('do not update if nothing to update', t => {
- Object.assign(notifierMock, {
- notify: () => { throw new Error('should not notify!') },
- update: { latest: version },
- })
- updateNotifier({
- config: { get: (k) => k !== 'global' },
- command: 'view',
- argv: ['npm']
- })
- t.end()
+ }), null)
+ t.strictSame(MANIFEST_REQUEST, [], 'no requests for manifests')
})
- t.test('do not update in CI', t => {
+ t.test('do not update if same as latest', async t => {
+ t.equal(await updateNotifier(npm), null)
+ t.strictSame(MANIFEST_REQUEST, ['npm@latest'], 'requested latest version')
+ })
+ t.test('check if stat errors (here for coverage)', async t => {
+ STAT_ERROR = new Error('blorg')
+ t.equal(await updateNotifier(npm), null)
+ t.strictSame(MANIFEST_REQUEST, ['npm@latest'], 'requested latest version')
+ })
+ t.test('ok if write errors (here for coverage)', async t => {
+ WRITE_ERROR = new Error('grolb')
+ t.equal(await updateNotifier(npm), null)
+ t.strictSame(MANIFEST_REQUEST, ['npm@latest'], 'requested latest version')
+ })
+ t.test('ignore pacote failures (here for coverage)', async t => {
+ PACOTE_ERROR = new Error('pah-KO-tchay')
+ t.equal(await updateNotifier(npm), null)
+ t.strictSame(MANIFEST_REQUEST, ['npm@latest'], 'requested latest version')
+ })
+ t.test('do not update if newer than latest, but same as next', async t => {
+ t.equal(await updateNotifier({ ...npm, version: NEXT_VERSION }), null)
+ const reqs = ['npm@latest', `npm@^${NEXT_VERSION}`]
+ t.strictSame(MANIFEST_REQUEST, reqs, 'requested latest and next versions')
+ })
+ t.test('do not update if on the latest beta', async t => {
+ t.equal(await updateNotifier({ ...npm, version: CURRENT_BETA }), null)
+ const reqs = [`npm@^${CURRENT_BETA}`]
+ t.strictSame(MANIFEST_REQUEST, reqs, 'requested latest and next versions')
+ })
+
+ t.test('do not update in CI', async t => {
t.teardown(() => { ciMock = null })
ciMock = 'something'
- Object.assign(notifierMock, {
- notify: () => { throw new Error('should not notify!') },
- update: { latest: '99.99.99' },
- })
- updateNotifier({
- config: { get: (k) => k !== 'global' },
- command: 'view',
- argv: ['npm']
- })
- t.end()
+ t.equal(await updateNotifier(npm), null)
+ t.strictSame(MANIFEST_REQUEST, [], 'no requests for manifests')
+ })
+
+ t.test('only check weekly for GA releases', async t => {
+ // the 10 is fuzz factor for test environment
+ STAT_MTIME = Date.now() - (1000*60*60*24*7) + 10
+ t.equal(await updateNotifier(npm), null)
+ t.strictSame(MANIFEST_REQUEST, [], 'no requests for manifests')
+ })
+
+ t.test('only check daily for betas', async t => {
+ // the 10 is fuzz factor for test environment
+ STAT_MTIME = Date.now() - (1000*60*60*24) + 10
+ t.equal(await updateNotifier({ ...npm, version: HAVE_BETA }), null)
+ t.strictSame(MANIFEST_REQUEST, [], 'no requests for manifests')
})
t.end()
})
t.test('notification situations', t => {
- const runTests = (config, t) => {
- t.plan(3)
- t.test('major', t => runTest(config, 'major', t))
- t.test('minor', t => runTest(config, 'minor', t))
- t.test('minor', t => runTest(config, 'patch', t))
- }
+ t.test('new beta available', async t => {
+ const version = HAVE_BETA
+ t.matchSnapshot(await updateNotifier({ ...npm, version }), 'color')
+ t.matchSnapshot(await updateNotifier({ ...npmNoColor, version }), 'no color')
+ t.strictSame(MANIFEST_REQUEST, [`npm@^${version}`, `npm@^${version}`])
+ })
- const runTest = (config, type, t) => {
- t.plan(1)
- const latest = type === 'major' ? semver.inc(version, 'major')
- : type === 'minor' ? semver.inc(version, 'minor')
- : type === 'patch' ? semver.inc(version, 'patch')
- : null
-
- Object.assign(notifierMock, {
- notify: ({ message }) => {
- // swap this out so we don't have to regen snapshots each version bump
- t.matchSnapshot(message.replace(latest, `<<${type}>>`))
- },
- update: { latest, type, current: version }
- })
- updateNotifier({
- color: config.color,
- config: { get: (k) => k === 'update-notifier' ? true : !!config[k] },
- command: 'view',
- argv: ['npm']
- })
- }
+ t.test('patch to next version', async t => {
+ const version = NEXT_PATCH
+ t.matchSnapshot(await updateNotifier({ ...npm, version }), 'color')
+ t.matchSnapshot(await updateNotifier({ ...npmNoColor, version }), 'no color')
+ t.strictSame(MANIFEST_REQUEST, ['npm@latest', `npm@^${version}`, 'npm@latest', `npm@^${version}`])
+ })
+
+ t.test('minor to next version', async t => {
+ const version = NEXT_MINOR
+ t.matchSnapshot(await updateNotifier({ ...npm, version }), 'color')
+ t.matchSnapshot(await updateNotifier({ ...npmNoColor, version }), 'no color')
+ t.strictSame(MANIFEST_REQUEST, ['npm@latest', `npm@^${version}`, 'npm@latest', `npm@^${version}`])
+ })
+
+ t.test('patch to current', async t => {
+ const version = CURRENT_PATCH
+ t.matchSnapshot(await updateNotifier({ ...npm, version }), 'color')
+ t.matchSnapshot(await updateNotifier({ ...npmNoColor, version }), 'no color')
+ t.strictSame(MANIFEST_REQUEST, ['npm@latest', 'npm@latest'])
+ })
- t.plan(4)
- t.test('color and unicode', t => runTests({ unicode: true, color: true }, t))
- t.test('color, no unicode', t => runTests({ color: true }, t))
- t.test('unicode, no color', t => runTests({ unicode: true }, t))
- t.test('no color, no unicode', t => runTests({}, t))
+ t.test('minor to current', async t => {
+ const version = CURRENT_MINOR
+ t.matchSnapshot(await updateNotifier({ ...npm, version }), 'color')
+ t.matchSnapshot(await updateNotifier({ ...npmNoColor, version }), 'no color')
+ t.strictSame(MANIFEST_REQUEST, ['npm@latest', 'npm@latest'])
+ })
+
+ t.test('major to current', async t => {
+ const version = CURRENT_MAJOR
+ t.matchSnapshot(await updateNotifier({ ...npm, version }), 'color')
+ t.matchSnapshot(await updateNotifier({ ...npmNoColor, version }), 'no color')
+ t.strictSame(MANIFEST_REQUEST, ['npm@latest', 'npm@latest'])
+ })
+
+ t.end()
})