From 2a7d8d5b36f1d40743b8d3130ed501612a5123a1 Mon Sep 17 00:00:00 2001 From: Gar Date: Tue, 19 Apr 2022 11:29:05 -0700 Subject: [PATCH] fix: consolidate registryConfig application logic It should happen whenever we read a manifest anyways. Tests were also rewritten to be real. --- lib/commands/publish.js | 31 +- .../test/lib/commands/ls.js.test.cjs | 32 - .../test/lib/commands/publish.js.test.cjs | 324 +++-- test/fixtures/mock-npm.js | 2 +- test/lib/commands/publish.js | 1244 +++++++---------- 5 files changed, 715 insertions(+), 918 deletions(-) diff --git a/lib/commands/publish.js b/lib/commands/publish.js index 51861c5aa3554..ff30366938786 100644 --- a/lib/commands/publish.js +++ b/lib/commands/publish.js @@ -69,10 +69,6 @@ class Publish extends BaseCommand { const spec = npa(args[0]) let manifest = await this.getManifest(spec, opts) - if (manifest.publishConfig) { - flatten(manifest.publishConfig, opts) - } - // only run scripts for directory type publishes if (spec.type === 'directory' && !ignoreScripts) { await runScript({ @@ -92,12 +88,8 @@ class Publish extends BaseCommand { // so that we send the latest and greatest thing to the registry // note that publishConfig might have changed as well! manifest = await this.getManifest(spec, opts) - if (manifest.publishConfig) { - flatten(manifest.publishConfig, opts) - } - // note that logTar calls log.notice(), so if we ARE in silent mode, - // this will do nothing, but we still want it in the debuglog if it fails. + // JSON already has the package contents if (!json) { logTar(pkgContents, { unicode }) } @@ -197,15 +189,22 @@ class Publish extends BaseCommand { // if it's a directory, read it from the file system // otherwise, get the full metadata from whatever it is - getManifest (spec, opts) { + // XXX can't pacote read the manifest from a directory? + async getManifest (spec, opts) { + let manifest if (spec.type === 'directory') { - return readJson(`${spec.fetchSpec}/package.json`) + manifest = await readJson(`${spec.fetchSpec}/package.json`) + } else { + manifest = await pacote.manifest(spec, { + ...opts, + fullmetadata: true, + fullReadJson: true, + }) + } + if (manifest.publishConfig) { + flatten(manifest.publishConfig, opts) } - return pacote.manifest(spec, { - ...opts, - fullMetadata: true, - fullReadJson: true, - }) + return manifest } } module.exports = Publish diff --git a/tap-snapshots/test/lib/commands/ls.js.test.cjs b/tap-snapshots/test/lib/commands/ls.js.test.cjs index e98898aeff417..a10d37ab92881 100644 --- a/tap-snapshots/test/lib/commands/ls.js.test.cjs +++ b/tap-snapshots/test/lib/commands/ls.js.test.cjs @@ -114,23 +114,6 @@ test-npm-ls@1.0.0 ` -exports[`test/lib/commands/ls.js TAP ls --only=development > should output tree containing only development deps 1`] = ` -test-npm-ls@1.0.0 {CWD}/tap-testdir-ls-ls---only-development -\`-- dev-dep@1.0.0 - \`-- foo@1.0.0 - \`-- dog@1.0.0 - -` - -exports[`test/lib/commands/ls.js TAP ls --only=prod > should output tree containing only prod deps 1`] = ` -test-npm-ls@1.0.0 {CWD}/tap-testdir-ls-ls---only-prod -+-- chai@1.0.0 -+-- optional-dep@1.0.0 -\`-- prod-dep@1.0.0 - \`-- dog@2.0.0 - -` - exports[`test/lib/commands/ls.js TAP ls --parseable --depth=0 > should output tree containing only top-level dependencies 1`] = ` {CWD}/tap-testdir-ls-ls---parseable---depth-0 {CWD}/tap-testdir-ls-ls---parseable---depth-0/node_modules/chai @@ -204,21 +187,6 @@ exports[`test/lib/commands/ls.js TAP ls --parseable --long with extraneous deps {CWD}/tap-testdir-ls-ls---parseable---long-with-extraneous-deps/node_modules/dog:dog@1.0.0 ` -exports[`test/lib/commands/ls.js TAP ls --parseable --only=development > should output tree containing only development deps 1`] = ` -{CWD}/tap-testdir-ls-ls---parseable---only-development -{CWD}/tap-testdir-ls-ls---parseable---only-development/node_modules/dev-dep -{CWD}/tap-testdir-ls-ls---parseable---only-development/node_modules/foo -{CWD}/tap-testdir-ls-ls---parseable---only-development/node_modules/dog -` - -exports[`test/lib/commands/ls.js TAP ls --parseable --only=prod > should output tree containing only prod deps 1`] = ` -{CWD}/tap-testdir-ls-ls---parseable---only-prod -{CWD}/tap-testdir-ls-ls---parseable---only-prod/node_modules/chai -{CWD}/tap-testdir-ls-ls---parseable---only-prod/node_modules/optional-dep -{CWD}/tap-testdir-ls-ls---parseable---only-prod/node_modules/prod-dep -{CWD}/tap-testdir-ls-ls---parseable---only-prod/node_modules/prod-dep/node_modules/dog -` - exports[`test/lib/commands/ls.js TAP ls --parseable --production > should output tree containing production deps 1`] = ` {CWD}/tap-testdir-ls-ls---parseable---production {CWD}/tap-testdir-ls-ls---parseable---production/node_modules/chai diff --git a/tap-snapshots/test/lib/commands/publish.js.test.cjs b/tap-snapshots/test/lib/commands/publish.js.test.cjs index dec7727834fa6..c6f757e8a9b2e 100644 --- a/tap-snapshots/test/lib/commands/publish.js.test.cjs +++ b/tap-snapshots/test/lib/commands/publish.js.test.cjs @@ -5,153 +5,251 @@ * Make sure to inspect the output below. Do not ignore changes! */ 'use strict' -exports[`test/lib/commands/publish.js TAP private workspaces colorless > should output all publishes 1`] = ` +exports[`test/lib/commands/publish.js TAP dry-run > must match snapshot 1`] = ` Array [ - "+ @npmcli/b@1.0.0", + Array [ + "", + ], + Array [ + "", + "package: test-package@1.0.0", + ], + Array [ + "=== Tarball Contents ===", + ], + Array [ + "", + "87B package.json", + ], + Array [ + "=== Tarball Details ===", + ], + Array [ + "", + String( + name: test-package + version: 1.0.0 + filename: test-package-1.0.0.tgz + package size: 160 B + unpacked size: 87 B + shasum:{sha} + integrity:{sha} + total files: 1 + ), + ], + Array [ + "", + "", + ], + Array [ + "", + "Publishing to https://registry.npmjs.org/ (dry-run)", + ], ] ` -exports[`test/lib/commands/publish.js TAP private workspaces colorless > should publish all non-private workspaces 1`] = ` -Array [ - Object { - "_id": "@npmcli/b@1.0.0", - "name": "@npmcli/b", - "readme": "ERROR: No README data found!", - "version": "1.0.0", - }, -] +exports[`test/lib/commands/publish.js TAP has auth for scope configured registry > new package version 1`] = ` ++ @npm/test-package@1.0.0 ` -exports[`test/lib/commands/publish.js TAP private workspaces with color > should output all publishes 1`] = ` +exports[`test/lib/commands/publish.js TAP ignore-scripts > new package version 1`] = ` ++ test-package@1.0.0 +` + +exports[`test/lib/commands/publish.js TAP json > must match snapshot 1`] = ` Array [ - "+ @npmcli/b@1.0.0", + Array [ + "", + "Publishing to https://registry.npmjs.org/", + ], ] ` -exports[`test/lib/commands/publish.js TAP private workspaces with color > should publish all non-private workspaces 1`] = ` +exports[`test/lib/commands/publish.js TAP json > new package json 1`] = ` +{ + "id": "test-package@1.0.0", + "name": "test-package", + "version": "1.0.0", + "size": 160, + "unpackedSize": 87, + "shasum": "{sha}", + "integrity": "{sha}", + "filename": "test-package-1.0.0.tgz", + "files": [ + { + "path": "package.json", + "size": 87, + "mode": 420 + } + ], + "entryCount": 1, + "bundled": [] +} +` + +exports[`test/lib/commands/publish.js TAP no auth dry-run > must match snapshot 1`] = ` ++ test-package@1.0.0 +` + +exports[`test/lib/commands/publish.js TAP no auth dry-run > warns about auth being needed 1`] = ` Array [ - Object { - "_id": "@npmcli/b@1.0.0", - "name": "@npmcli/b", - "readme": "ERROR: No README data found!", - "version": "1.0.0", - }, + Array [ + "", + "This command requires you to be logged in to https://registry.npmjs.org/ (dry-run)", + ], ] ` -exports[`test/lib/commands/publish.js TAP workspaces all workspaces > should output all publishes 1`] = ` +exports[`test/lib/commands/publish.js TAP re-loads publishConfig.registry if added during script process > new package version 1`] = ` ++ test-package@1.0.0 +` + +exports[`test/lib/commands/publish.js TAP respects publishConfig.registry, runs appropriate scripts > new package version 1`] = ` + +` + +exports[`test/lib/commands/publish.js TAP tarball > must match snapshot 1`] = ` Array [ - "+ workspace-a@1.2.3-a", - "+ workspace-b@1.2.3-n", - "+ workspace-n@1.2.3-n", + Array [ + "", + ], + Array [ + "", + "package: test-tar-package@1.0.0", + ], + Array [ + "=== Tarball Contents ===", + ], + Array [ + "", + String( + 26B index.js + 98B package.json + ), + ], + Array [ + "=== Tarball Details ===", + ], + Array [ + "", + String( + name: test-tar-package + version: 1.0.0 + filename: test-tar-package-1.0.0.tgz + package size: 218 B + unpacked size: 124 B + shasum:{sha} + integrity:{sha} + total files: 2 + ), + ], + Array [ + "", + "", + ], + Array [ + "", + "Publishing to https://registry.npmjs.org/", + ], ] ` -exports[`test/lib/commands/publish.js TAP workspaces all workspaces > should publish all workspaces 1`] = ` +exports[`test/lib/commands/publish.js TAP tarball > new package json 1`] = ` ++ test-tar-package@1.0.0 +` + +exports[`test/lib/commands/publish.js TAP workspaces all workspaces - color > all public workspaces 1`] = ` ++ workspace-a@1.2.3-a ++ workspace-b@1.2.3-n ++ workspace-n@1.2.3-n +` + +exports[`test/lib/commands/publish.js TAP workspaces all workspaces - color > warns about skipped private workspace in color 1`] = ` Array [ - Object { - "_id": "workspace-a@1.2.3-a", - "name": "workspace-a", - "readme": "ERROR: No README data found!", - "repository": Object { - "type": "git", - "url": "http://repo.workspace-a/", - }, - "version": "1.2.3-a", - }, - Object { - "_id": "workspace-b@1.2.3-n", - "bugs": Object { - "url": "https://github.com/npm/workspace-b/issues", - }, - "homepage": "https://github.com/npm/workspace-b#readme", - "name": "workspace-b", - "readme": "ERROR: No README data found!", - "repository": Object { - "type": "git", - "url": "git+https://github.com/npm/workspace-b.git", - }, - "version": "1.2.3-n", - }, - Object { - "_id": "workspace-n@1.2.3-n", - "name": "workspace-n", - "readme": "ERROR: No README data found!", - "version": "1.2.3-n", - }, + Array [ + "publish", + "Skipping workspace \\u001b[32mworkspace-p\\u001b[39m, marked as \\u001b[1mprivate\\u001b[22m", + ], ] ` -exports[`test/lib/commands/publish.js TAP workspaces json > should output all publishes as json 1`] = ` +exports[`test/lib/commands/publish.js TAP workspaces all workspaces - no color > all public workspaces 1`] = ` ++ workspace-a@1.2.3-a ++ workspace-b@1.2.3-n ++ workspace-n@1.2.3-n +` + +exports[`test/lib/commands/publish.js TAP workspaces all workspaces - no color > warns about skipped private workspace 1`] = ` Array [ - String( - { - "workspace-a": { - "id": "workspace-a@1.2.3-a" - }, - "workspace-b": { - "id": "workspace-b@1.2.3-n" - }, - "workspace-n": { - "id": "workspace-n@1.2.3-n" - } - } - ), + Array [ + "publish", + "Skipping workspace workspace-p, marked as private", + ], ] ` -exports[`test/lib/commands/publish.js TAP workspaces json > should publish all workspaces 1`] = ` -Array [ - Object { - "_id": "workspace-a@1.2.3-a", +exports[`test/lib/commands/publish.js TAP workspaces json > all workspaces in json 1`] = ` +{ + "workspace-a": { + "id": "workspace-a@1.2.3-a", "name": "workspace-a", - "readme": "ERROR: No README data found!", - "repository": Object { - "type": "git", - "url": "http://repo.workspace-a/", - }, "version": "1.2.3-a", + "size": 162, + "unpackedSize": 82, + "shasum": "{sha}", + "integrity": "{sha}", + "filename": "workspace-a-1.2.3-a.tgz", + "files": [ + { + "path": "package.json", + "size": 82, + "mode": 420 + } + ], + "entryCount": 1, + "bundled": [] }, - Object { - "_id": "workspace-b@1.2.3-n", - "bugs": Object { - "url": "https://github.com/npm/workspace-b/issues", - }, - "homepage": "https://github.com/npm/workspace-b#readme", + "workspace-b": { + "id": "workspace-b@1.2.3-n", "name": "workspace-b", - "readme": "ERROR: No README data found!", - "repository": Object { - "type": "git", - "url": "git+https://github.com/npm/workspace-b.git", - }, "version": "1.2.3-n", + "size": 171, + "unpackedSize": 92, + "shasum": "{sha}", + "integrity": "{sha}", + "filename": "workspace-b-1.2.3-n.tgz", + "files": [ + { + "path": "package.json", + "size": 92, + "mode": 420 + } + ], + "entryCount": 1, + "bundled": [] }, - Object { - "_id": "workspace-n@1.2.3-n", + "workspace-n": { + "id": "workspace-n@1.2.3-n", "name": "workspace-n", - "readme": "ERROR: No README data found!", "version": "1.2.3-n", - }, -] -` - -exports[`test/lib/commands/publish.js TAP workspaces one workspace > should output one publish 1`] = ` -Array [ - "+ workspace-a@1.2.3-a", -] + "size": 140, + "unpackedSize": 42, + "shasum": "{sha}", + "integrity": "{sha}", + "filename": "workspace-n-1.2.3-n.tgz", + "files": [ + { + "path": "package.json", + "size": 42, + "mode": 420 + } + ], + "entryCount": 1, + "bundled": [] + } +} ` -exports[`test/lib/commands/publish.js TAP workspaces one workspace > should publish given workspace 1`] = ` -Array [ - Object { - "_id": "workspace-a@1.2.3-a", - "name": "workspace-a", - "readme": "ERROR: No README data found!", - "repository": Object { - "type": "git", - "url": "http://repo.workspace-a/", - }, - "version": "1.2.3-a", - }, -] +exports[`test/lib/commands/publish.js TAP workspaces one workspace - success > single workspace 1`] = ` ++ workspace-a@1.2.3-a ` diff --git a/test/fixtures/mock-npm.js b/test/fixtures/mock-npm.js index b6742a425aa9a..4263dc8fbedc3 100644 --- a/test/fixtures/mock-npm.js +++ b/test/fixtures/mock-npm.js @@ -118,7 +118,7 @@ const LoadMockNpm = async (t, { mockGlobals(t, { 'process.env.HOME': home, 'process.env.npm_config_cache': cache, - ...(globals ? result(globals, { prefix, cache }) : {}), + ...(globals ? result(globals, { prefix, cache, home }) : {}), // Some configs don't work because they can't be set via npm.config.set until // config is loaded. But some config items are needed before that. So this is // an explicit set of configs that must be loaded as env vars. diff --git a/test/lib/commands/publish.js b/test/lib/commands/publish.js index 64eb7c60cb062..b17424b084461 100644 --- a/test/lib/commands/publish.js +++ b/test/lib/commands/publish.js @@ -1,271 +1,189 @@ const t = require('tap') -const { fake: mockNpm } = require('../../fixtures/mock-npm') -const fs = require('fs') +const { load: loadMockNpm } = require('../../fixtures/mock-npm') +const MockRegistry = require('../../fixtures/mock-registry.js') +const pacote = require('pacote') +const path = require('path') +const fs = require('@npmcli/fs') +const npa = require('npm-package-arg') + +const pkg = 'test-package' +const token = 'test-auth-token' +const auth = { '//registry.npmjs.org/:_authToken': token } +const alternateRegistry = 'https://other.registry.npmjs.org' + +const pkgJson = { + name: pkg, + description: 'npm test package', + version: '1.0.0', +} t.cleanSnapshot = data => { - return data.replace(/^ *"gitHead": .*$\n/gm, '') + return data.replace(/shasum:.*/g, 'shasum:{sha}') + .replace(/integrity:.*/g, 'integrity:{sha}') + .replace(/"shasum": ".*",/g, '"shasum": "{sha}",') + .replace(/"integrity": ".*",/g, '"integrity": "{sha}",') } -const { definitions } = require('../../../lib/utils/config') -const defaults = Object.entries(definitions).reduce((defaults, [key, def]) => { - defaults[key] = def.default - return defaults -}, {}) - -t.test( - /* eslint-disable-next-line max-len */ - 'should publish with libnpmpublish, passing through flatOptions and respecting publishConfig.registry', - async t => { - t.plan(6) - - const registry = 'https://some.registry' - const publishConfig = { registry } - const testDir = t.testdir({ - 'package.json': JSON.stringify( - { - name: 'my-cool-pkg', - version: '1.0.0', - publishConfig, +t.test('respects publishConfig.registry, runs appropriate scripts', async t => { + const { npm, joinedOutput, prefix } = await loadMockNpm(t, { + config: { + loglevel: 'silent', // prevent scripts from leaking to stdout during the test + [`${alternateRegistry.slice(6)}/:_authToken`]: 'test-other-token', + }, + prefixDir: { + 'package.json': JSON.stringify({ + ...pkgJson, + scripts: { + prepublishOnly: 'touch scripts-prepublishonly', + prepublish: 'touch scripts-prepublish', // should NOT run this one + publish: 'touch scripts-publish', + postpublish: 'touch scripts-postpublish', }, - null, - 2 - ), - }) - - const Publish = t.mock('../../../lib/commands/publish.js', { - // verify that we do NOT remove publishConfig if it was there originally - // and then removed during the script/pack process - libnpmpack: async () => { - fs.writeFileSync( - `${testDir}/package.json`, - JSON.stringify({ - name: 'my-cool-pkg', - version: '1.0.0', - }) - ) - return Buffer.from('') - }, - libnpmpublish: { - publish: (manifest, tarData, opts) => { - t.match(manifest, { name: 'my-cool-pkg', version: '1.0.0' }, 'gets manifest') - t.type(tarData, Buffer, 'tarData is a buffer') - t.ok(opts, 'gets opts object') - t.same(opts.customValue, true, 'flatOptions values are passed through') - t.same(opts.registry, registry, 'publishConfig.registry is passed through') + publishConfig: { registry: alternateRegistry }, + }, null, 2), + }, + globals: ({ prefix }) => ({ + 'process.cwd': () => prefix, + }), + }) + const registry = new MockRegistry({ + tap: t, + registry: alternateRegistry, + authorization: 'test-other-token', + }) + registry.nock.put(`/${pkg}`, body => { + return t.match(body, { + _id: pkg, + name: pkg, + 'dist-tags': { latest: '1.0.0' }, + access: null, + versions: { + '1.0.0': { + name: pkg, + version: '1.0.0', + _id: `${pkg}@1.0.0`, + dist: { + shasum: /\.*/, + tarball: `http:${alternateRegistry.slice(6)}/test-package/-/test-package-1.0.0.tgz`, + }, + publishConfig: { + registry: alternateRegistry, + }, }, }, - }) - const npm = mockNpm({ - flatOptions: { - customValue: true, - workspacesEnabled: true, + _attachments: { + [`${pkg}-1.0.0.tgz`]: {}, }, }) - npm.config.getCredentialsByURI = uri => { - t.same(uri, registry, 'gets credentials for expected registry') - return { token: 'some.registry.token' } - } - const publish = new Publish(npm) - - await publish.exec([testDir]) - } -) + }).reply(200, {}) + await npm.exec('publish', []) + t.matchSnapshot(joinedOutput(), 'new package version') + t.resolveMatch(fs.exists(path.join(prefix, 'scripts-prepublishonly')), true, 'ran prepublishOnly') + t.resolveMatch( + fs.exists(path.join(prefix, 'scripts-prepublish')), + false, + 'did not run prepublish' + ) + t.resolveMatch(fs.exists(path.join(prefix, 'scripts-publish')), true, 'ran publish') + t.resolveMatch(fs.exists(path.join(prefix, 'scripts-postpublish')), true, 'ran postpublish') +}) t.test('re-loads publishConfig.registry if added during script process', async t => { - t.plan(5) - const registry = 'https://some.registry' - const publishConfig = { registry } - const testDir = t.testdir({ - 'package.json': JSON.stringify( - { - name: 'my-cool-pkg', - version: '1.0.0', - }, - null, - 2 - ), - }) - - const Publish = t.mock('../../../lib/commands/publish.js', { - libnpmpack: async () => { - fs.writeFileSync( - `${testDir}/package.json`, - JSON.stringify({ - name: 'my-cool-pkg', - version: '1.0.0', - publishConfig, - }) - ) - return Buffer.from('') - }, - libnpmpublish: { - publish: (manifest, tarData, opts) => { - t.match(manifest, { name: 'my-cool-pkg', version: '1.0.0' }, 'gets manifest') - t.type(tarData, Buffer, 'tarData is a buffer') - t.ok(opts, 'gets opts object') - t.same(opts.registry, registry, 'publishConfig.registry is passed through') - }, + const { joinedOutput, npm } = await loadMockNpm(t, { + config: { + [`${alternateRegistry.slice(6)}/:_authToken`]: 'test-other-token', }, - }) - const npm = mockNpm() - npm.config.getCredentialsByURI = uri => { - t.same(uri, registry, 'gets credentials for expected registry') - return { token: 'some.registry.token' } - } - const publish = new Publish(npm) - - await publish.exec([testDir]) -}) - -t.test('if loglevel=info and json, should not output package contents', async t => { - t.plan(3) - - const testDir = t.testdir({ - 'package.json': JSON.stringify( - { - name: 'my-cool-pkg', - version: '1.0.0', - }, - null, - 2 - ), - }) - - const Publish = t.mock('../../../lib/commands/publish.js', { - '../../../lib/utils/tar.js': { - getContents: () => ({ - id: 'someid', + prefixDir: { + 'package.json': JSON.stringify({ + ...pkgJson, + scripts: { + prepare: 'cp new.json package.json', + }, + }, null, 2), + 'new.json': JSON.stringify({ + ...pkgJson, + publishConfig: { registry: alternateRegistry }, }), - logTar: () => { - t.fail('logTar is not called in json mode') - }, - }, - libnpmpublish: { - publish: () => { - t.pass('publish called') - }, }, + globals: ({ prefix }) => ({ + 'process.cwd': () => prefix, + }), }) - const npm = mockNpm({ - config: { json: true, loglevel: 'info' }, - output: () => { - t.pass('output is called') - }, - }, t) - npm.config.getCredentialsByURI = uri => { - t.same(uri, npm.config.get('registry'), 'gets credentials for expected registry') - return { token: 'some.registry.token' } - } - const publish = new Publish(npm) - - await publish.exec([testDir]) -}) - -t.test( - /* eslint-disable-next-line max-len */ - 'if loglevel=silent and dry-run, should not output package contents or publish, should log tarball contents', - async t => { - t.plan(2) - - const testDir = t.testdir({ - 'package.json': JSON.stringify( - { - name: 'my-cool-pkg', + const registry = new MockRegistry({ + tap: t, + registry: alternateRegistry, + authorization: 'test-other-token', + }) + registry.nock.put(`/${pkg}`, body => { + return t.match(body, { + _id: pkg, + name: pkg, + 'dist-tags': { latest: '1.0.0' }, + access: null, + versions: { + '1.0.0': { + name: pkg, version: '1.0.0', - }, - null, - 2 - ), - }) - - const Publish = t.mock('../../../lib/commands/publish.js', { - '../../../lib/utils/tar.js': { - getContents: () => ({ - id: 'someid', - }), - logTar: () => { - t.pass('logTar is called') - }, - }, - libnpmpublish: { - publish: () => { - throw new Error('should not call libnpmpublish in dry run') - }, - }, - }) - const npm = mockNpm({ - config: { 'dry-run': true, loglevel: 'silent' }, - output: () => { - throw new Error('should not output in dry run mode') - }, - }, t) - npm.config.getCredentialsByURI = uri => { - t.same(uri, npm.config.get('registry'), 'gets credentials for expected registry') - return { token: 'some.registry.token' } - } - - const publish = new Publish(npm) - - await publish.exec([testDir]) - } -) - -t.test( - /* eslint-disable-next-line max-len */ - 'if loglevel=info and dry-run, should not publish, should log package contents and log tarball contents', - async t => { - t.plan(3) - - const testDir = t.testdir({ - 'package.json': JSON.stringify( - { - name: 'my-cool-pkg', - version: '1.0.0', - }, - null, - 2 - ), - }) - - const npm = mockNpm({ - config: { 'dry-run': true, loglevel: 'info' }, - output: () => { - t.pass('output fn is called') - }, - }, t) - const registry = npm.config.get('registry') - npm.config.getCredentialsByURI = uri => { - t.same(uri, registry, 'gets credentials for expected registry') - return { /* no token will call log.warn */ } - } - - const Publish = t.mock('../../../lib/commands/publish.js', { - '../../../lib/utils/tar.js': { - getContents: () => ({ - id: 'someid', - }), - logTar: () => { - t.pass('logTar is called') - }, - 'proc-log': { - warn (_, msg) { - t.match(msg, - `This command requires you to be logged in to ${registry} (dry-run)`) + _id: `${pkg}@1.0.0`, + dist: { + shasum: /\.*/, + tarball: `http:${alternateRegistry.slice(6)}/test-package/-/test-package-1.0.0.tgz`, + }, + publishConfig: { + registry: alternateRegistry, }, }, }, - libnpmpublish: { - publish: () => { - throw new Error('should not call libnpmpublish in dry run') - }, + _attachments: { + [`${pkg}-1.0.0.tgz`]: {}, }, }) + }).reply(200, {}) + await npm.exec('publish', []) + t.matchSnapshot(joinedOutput(), 'new package version') +}) - const publish = new Publish(npm) +t.test('json', async t => { + const { joinedOutput, npm, logs } = await loadMockNpm(t, { + config: { + json: true, + ...auth, + }, + prefixDir: { + 'package.json': JSON.stringify(pkgJson, null, 2), + }, + globals: ({ prefix }) => ({ + 'process.cwd': () => prefix, + }), + }) + const registry = new MockRegistry({ + tap: t, + registry: npm.config.get('registry'), + authorization: token, + }) + registry.nock.put(`/${pkg}`).reply(200, {}) + await npm.exec('publish', []) + t.matchSnapshot(logs.notice) + t.matchSnapshot(joinedOutput(), 'new package json') +}) - await publish.exec([testDir]) - } -) +t.test('dry-run', async t => { + const { joinedOutput, npm, logs } = await loadMockNpm(t, { + config: { + 'dry-run': true, + ...auth, + }, + prefixDir: { + 'package.json': JSON.stringify(pkgJson, null, 2), + }, + globals: ({ prefix }) => ({ + 'process.cwd': () => prefix, + }), + }) + await npm.exec('publish', []) + t.equal(joinedOutput(), `+ ${pkg}@1.0.0`) + t.matchSnapshot(logs.notice) +}) t.test('shows usage with wrong set of arguments', async t => { t.plan(1) @@ -276,279 +194,174 @@ t.test('shows usage with wrong set of arguments', async t => { }) t.test('throws when invalid tag', async t => { - t.plan(1) - - const Publish = t.mock('../../../lib/commands/publish.js') - const npm = mockNpm({ - config: { tag: '0.0.13' }, + const { npm } = await loadMockNpm(t, { + config: { + tag: '0.0.13', + }, + prefixDir: { + 'package.json': JSON.stringify(pkgJson, null, 2), + }, + globals: ({ prefix }) => ({ + 'process.cwd': () => prefix, + }), }) - const publish = new Publish(npm) - await t.rejects( - publish.exec([]), - /Tag name must not be a valid SemVer range: /, - 'throws when tag name is a valid SemVer range' + npm.exec('publish', []), + { message: 'Tag name must not be a valid SemVer range: 0.0.13' } ) }) -t.test('can publish a tarball', async t => { - t.plan(3) - - const testDir = t.testdir({ - tarball: {}, - package: { +t.test('tarball', async t => { + const { npm, joinedOutput, logs, home } = await loadMockNpm(t, { + config: { + 'fetch-retries': 0, + ...auth, + }, + homeDir: { 'package.json': JSON.stringify({ - name: 'my-cool-tarball', - version: '1.2.3', - }), - 'README.md': 'This is my readme', + name: 'test-tar-package', + description: 'this was from a tarball', + version: '1.0.0', + }, null, 2), + 'index.js': 'console.log("hello world"}', }, }) - const tar = require('tar') - tar.c( - { - cwd: testDir, - file: `${testDir}/tarball/package.tgz`, - sync: true, - }, - ['package'] - ) + const tarball = await pacote.tarball(home) + const tarFilename = path.join(home, 'tarball.tgz') + await fs.writeFile(tarFilename, tarball) + const registry = new MockRegistry({ + tap: t, + registry: npm.config.get('registry'), + authorization: token, + }) + registry.nock.put('/test-tar-package', body => { + return t.match(body, { + name: 'test-tar-package', + }) + }).reply(200, {}) + await npm.exec('publish', [tarFilename]) + t.matchSnapshot(logs.notice) + t.matchSnapshot(joinedOutput(), 'new package json') +}) - const tarFile = fs.readFileSync(`${testDir}/tarball/package.tgz`) - const Publish = t.mock('../../../lib/commands/publish.js', { - libnpmpublish: { - publish: (manifest, tarData, opts) => { - t.match( - manifest, - { - name: 'my-cool-tarball', - version: '1.2.3', - readme: 'This is my readme', - description: 'This is my readme', - readmeFilename: 'README.md', - }, - 'sent manifest to lib pub' - ) - t.strictSame(tarData, tarFile, 'sent the tarball data to lib pub') - }, +t.test('no auth default registry', async t => { + const { npm } = await loadMockNpm(t, { + prefixDir: { + 'package.json': JSON.stringify(pkgJson, null, 2), }, + globals: ({ prefix }) => ({ + 'process.cwd': () => prefix, + }), }) - const npm = mockNpm() - npm.config.getCredentialsByURI = uri => { - t.same(uri, npm.config.get('registry'), 'gets credentials for expected registry') - return { token: 'some.registry.token' } - } - const publish = new Publish(npm) - - await publish.exec([`${testDir}/tarball/package.tgz`]) -}) - -t.test('should check auth for default registry', async t => { - t.plan(2) - const npm = mockNpm() - const registry = npm.config.get('registry') - const errorMessage = `This command requires you to be logged in to ${registry}` - const Publish = t.mock('../../../lib/commands/publish.js') - npm.config.getCredentialsByURI = uri => { - t.same(uri, npm.config.get('registry'), 'gets credentials for expected registry') - return {} - } - const publish = new Publish(npm) - await t.rejects( - publish.exec([]), - { message: errorMessage, code: 'ENEEDAUTH' }, - 'throws when not logged in' + npm.exec('publish', []), + { + message: 'This command requires you to be logged in to https://registry.npmjs.org/', + code: 'ENEEDAUTH', + } ) }) -t.test('should check auth for configured registry', async t => { - t.plan(2) - const registry = 'https://some.registry' - const errorMessage = 'This command requires you to be logged in to https://some.registry' - const Publish = t.mock('../../../lib/commands/publish.js') - const npm = mockNpm({ - flatOptions: { registry }, +t.test('no auth dry-run', async t => { + const { npm, joinedOutput, logs } = await loadMockNpm(t, { + config: { + 'dry-run': true, + }, + prefixDir: { + 'package.json': JSON.stringify(pkgJson, null, 2), + }, + globals: ({ prefix }) => ({ + 'process.cwd': () => prefix, + }), }) - npm.config.getCredentialsByURI = uri => { - t.same(uri, registry, 'gets credentials for expected registry') - return {} - } - const publish = new Publish(npm) - - await t.rejects( - publish.exec([]), - { message: errorMessage, code: 'ENEEDAUTH' }, - 'throws when not logged in' - ) + await npm.exec('publish', []) + t.matchSnapshot(joinedOutput()) + t.matchSnapshot(logs.warn, 'warns about auth being needed') }) -t.test('should check auth for scope specific registry', async t => { - t.plan(2) - const registry = 'https://some.registry' - const errorMessage = 'This command requires you to be logged in to https://some.registry' - const testDir = t.testdir({ - 'package.json': JSON.stringify( - { - name: '@npm/my-cool-pkg', - version: '1.0.0', - }, - null, - 2 - ), - }) - - const Publish = t.mock('../../../lib/commands/publish.js') - const npm = mockNpm({ - flatOptions: { '@npm:registry': registry }, +t.test('no auth for configured registry', async t => { + const { npm } = await loadMockNpm(t, { + config: { + registry: alternateRegistry, + ...auth, + }, + prefixDir: { + 'package.json': JSON.stringify(pkgJson, null, 2), + }, + globals: ({ prefix }) => ({ + 'process.cwd': () => prefix, + }), }) - npm.config.getCredentialsByURI = uri => { - t.same(uri, registry, 'gets credentials for expected registry') - return {} - } - const publish = new Publish(npm) - await t.rejects( - publish.exec([testDir]), - { message: errorMessage, code: 'ENEEDAUTH' }, - 'throws when not logged in' + npm.exec('publish', []), + { + message: `This command requires you to be logged in to ${alternateRegistry}`, + code: 'ENEEDAUTH', + } ) }) -t.test('should use auth for scope specific registry', async t => { - t.plan(3) - const registry = 'https://some.registry' - const testDir = t.testdir({ - 'package.json': JSON.stringify( - { - name: '@npm/my-cool-pkg', - version: '1.0.0', - }, - null, - 2 - ), - }) - - const Publish = t.mock('../../../lib/commands/publish.js', { - libnpmpublish: { - publish: (manifest, tarData, opts) => { - t.ok(opts, 'gets opts object') - t.same(opts['@npm:registry'], registry, 'scope specific registry is passed through') - }, +t.test('no auth for scope configured registry', async t => { + const { npm } = await loadMockNpm(t, { + config: { + '@npm:registry': alternateRegistry, + ...auth, }, - }) - const npm = mockNpm({ - flatOptions: { '@npm:registry': registry }, - }) - npm.config.getCredentialsByURI = uri => { - t.same(uri, registry, 'gets credentials for expected registry') - return { token: 'some.registry.token' } - } - const publish = new Publish(npm) - - await publish.exec([testDir]) -}) - -t.test('read registry only from publishConfig', async t => { - t.plan(3) - - const registry = 'https://some.registry' - const publishConfig = { registry } - const testDir = t.testdir({ - 'package.json': JSON.stringify( - { - name: 'my-cool-pkg', + prefixDir: { + 'package.json': JSON.stringify({ + name: '@npm/test-package', version: '1.0.0', - publishConfig, - }, - null, - 2 - ), - }) - - const Publish = t.mock('../../../lib/commands/publish.js', { - libnpmpublish: { - publish: (manifest, tarData, opts) => { - t.match(manifest, { name: 'my-cool-pkg', version: '1.0.0' }, 'gets manifest') - t.same(opts.registry, registry, 'publishConfig is passed through') - }, + }, null, 2), }, + globals: ({ prefix }) => ({ + 'process.cwd': () => prefix, + }), }) - const npm = mockNpm() - npm.config.getCredentialsByURI = uri => { - t.same(uri, registry, 'gets credentials for expected registry') - return { token: 'some.registry.token' } - } - const publish = new Publish(npm) - - await publish.exec([testDir]) + await t.rejects( + npm.exec('publish', []), + { + message: `This command requires you to be logged in to ${alternateRegistry}`, + code: 'ENEEDAUTH', + } + ) }) -t.test('able to publish after if encountered multiple configs', async t => { - t.plan(2) - - const registry = 'https://some.registry' - const tag = 'better-tag' - const publishConfig = { registry } - const testDir = t.testdir({ - 'package.json': JSON.stringify( - { - name: 'my-cool-pkg', +t.test('has auth for scope configured registry', async t => { + const spec = npa('@npm/test-package') + const { npm, joinedOutput } = await loadMockNpm(t, { + config: { + '@npm:registry': alternateRegistry, + [`${alternateRegistry.slice(6)}/:_authToken`]: 'test-scope-token', + }, + prefixDir: { + 'package.json': JSON.stringify({ + name: '@npm/test-package', version: '1.0.0', - publishConfig, - }, - null, - 2 - ), - }) - - const configList = [defaults] - configList.unshift( - Object.assign(Object.create(configList[0]), { - registry: `https://other.registry`, - tag: 'some-tag', - }) - ) - configList.unshift(Object.assign(Object.create(configList[0]), { tag })) - - const Publish = t.mock('../../../lib/commands/publish.js', { - libnpmpublish: { - publish: (manifest, tarData, opts) => { - t.same(opts.defaultTag, tag, 'gets option for expected tag') - }, + }, null, 2), }, + globals: ({ prefix }) => ({ + 'process.cwd': () => prefix, + }), }) - const publish = new Publish({ - // what would be flattened by the configList created above - flatOptions: { - defaultTag: 'better-tag', - registry: 'https://other.registry', - }, - output () {}, - config: { - get: key => configList[0][key], - list: configList, - getCredentialsByURI: uri => { - t.same(uri, registry, 'gets credentials for expected registry') - return { token: 'some.registry.token' } - }, - }, + const registry = new MockRegistry({ + tap: t, + registry: alternateRegistry, + authorization: 'test-scope-token', }) - - await publish.exec([testDir]) + registry.nock.put(`/${spec.escapedName}`, body => { + return t.match(body, { name: '@npm/test-package' }) + }).reply(200, {}) + await npm.exec('publish', []) + t.matchSnapshot(joinedOutput(), 'new package version') }) t.test('workspaces', t => { - const testDir = t.testdir({ + const dir = { 'package.json': JSON.stringify( { - name: 'my-cool-pkg', - version: '1.0.0', - workspaces: ['workspace-a', 'workspace-b', 'workspace-c'], - }, - null, - 2 - ), + ...pkgJson, + workspaces: ['workspace-a', 'workspace-b', 'workspace-c', 'workspace-p'], + }, null, 2), 'workspace-a': { 'package.json': JSON.stringify({ name: 'workspace-a', @@ -569,304 +382,223 @@ t.test('workspaces', t => { version: '1.2.3-n', }), }, - }) - - const publishes = [] - const outputs = [] - t.beforeEach(() => { - npm.config.set('json', false) - outputs.length = 0 - publishes.length = 0 - }) - const Publish = t.mock('../../../lib/commands/publish.js', { - '../../../lib/utils/tar.js': { - getContents: manifest => ({ - id: manifest._id, + 'workspace-p': { + 'package.json': JSON.stringify({ + name: 'workspace-p', + version: '1.2.3-p', + private: true, }), - logTar: () => {}, }, - libnpmpublish: { - publish: (manifest, tarballData, opts) => { - publishes.push(manifest) - }, - }, - }) - const npm = mockNpm({ - output: o => { - outputs.push(o) - }, - }) - npm.localPrefix = testDir - npm.config.getCredentialsByURI = uri => { - return { token: 'some.registry.token' } } - const publish = new Publish(npm) - t.test('all workspaces', async t => { - await publish.execWorkspaces([], []) - t.matchSnapshot(publishes, 'should publish all workspaces') - t.matchSnapshot(outputs, 'should output all publishes') - }) - - t.test('one workspace', async t => { - await publish.execWorkspaces([], ['workspace-a']) - t.matchSnapshot(publishes, 'should publish given workspace') - t.matchSnapshot(outputs, 'should output one publish') - }) - - t.test('invalid workspace', async t => { - await t.rejects(publish.execWorkspaces([], ['workspace-x']), /No workspaces found/) - await t.rejects(publish.execWorkspaces([], ['workspace-x']), /workspace-x/) - }) - - t.test('json', async t => { - npm.config.set('json', true) - await publish.execWorkspaces([], []) - t.matchSnapshot(publishes, 'should publish all workspaces') - t.matchSnapshot(outputs, 'should output all publishes as json') - }) - t.end() -}) - -t.test('private workspaces', async t => { - const testDir = t.testdir({ - 'package.json': JSON.stringify({ - name: 'workspaces-project', - version: '1.0.0', - workspaces: ['packages/*'], - }), - packages: { - a: { - 'package.json': JSON.stringify({ - name: '@npmcli/a', - version: '1.0.0', - private: true, - }), + t.test('all workspaces - no color', async t => { + const { npm, joinedOutput, logs } = await loadMockNpm(t, { + config: { + color: false, + ...auth, + workspaces: true, }, - b: { - 'package.json': JSON.stringify({ - name: '@npmcli/b', - version: '1.0.0', - }), - }, - }, - }) - - const publishes = [] - const outputs = [] - t.beforeEach(() => { - npm.config.set('json', false) - outputs.length = 0 - publishes.length = 0 - }) - const mocks = { - '../../../lib/utils/tar.js': { - getContents: manifest => ({ - id: manifest._id, + globals: ({ prefix }) => ({ + 'process.cwd': () => prefix, }), - logTar: () => {}, - }, - libnpmpublish: { - publish: (manifest, tarballData, opts) => { - if (manifest.private) { - throw Object.assign(new Error('private pkg'), { code: 'EPRIVATE' }) - } - publishes.push(manifest) - }, - }, - } - const npm = mockNpm({ - config: { loglevel: 'info' }, - output: o => { - outputs.push(o) - }, - }, t) - npm.localPrefix = testDir - npm.config.getCredentialsByURI = uri => { - return { token: 'some.registry.token' } - } - - t.test('with color', async t => { - t.plan(4) - - const Publish = t.mock('../../../lib/commands/publish.js', { - ...mocks, - 'proc-log': { - notice () {}, - verbose () {}, - warn (title, msg) { - t.equal(title, 'publish', 'should use publish warn title') - t.match( - msg, - /* eslint-disable-next-line max-len */ - 'Skipping workspace \u001b[32m@npmcli/a\u001b[39m, marked as \u001b[1mprivate\u001b[22m', - 'should display skip private workspace warn msg' - ) - }, - }, + prefixDir: dir, }) - const publish = new Publish(npm) - - npm.color = true - await publish.execWorkspaces([], []) - t.matchSnapshot(publishes, 'should publish all non-private workspaces') - t.matchSnapshot(outputs, 'should output all publishes') - npm.color = false + const registry = new MockRegistry({ + tap: t, + registry: npm.config.get('registry'), + authorization: token, + }) + registry.nock + .put('/workspace-a', body => { + return t.match(body, { name: 'workspace-a' }) + }).reply(200, {}) + .put('/workspace-b', body => { + return t.match(body, { name: 'workspace-b' }) + }).reply(200, {}) + .put('/workspace-n', body => { + return t.match(body, { name: 'workspace-n' }) + }).reply(200, {}) + await npm.exec('publish', []) + t.matchSnapshot(joinedOutput(), 'all public workspaces') + t.matchSnapshot(logs.warn, 'warns about skipped private workspace') + }) + + t.test('all workspaces - color', async t => { + const { npm, joinedOutput, logs } = await loadMockNpm(t, { + config: { + ...auth, + color: 'always', + workspaces: true, + }, + globals: ({ prefix }) => ({ + 'process.cwd': () => prefix, + }), + prefixDir: dir, + }) + const registry = new MockRegistry({ + tap: t, + registry: npm.config.get('registry'), + authorization: token, + }) + registry.nock + .put('/workspace-a', body => { + return t.match(body, { name: 'workspace-a' }) + }).reply(200, {}) + .put('/workspace-b', body => { + return t.match(body, { name: 'workspace-b' }) + }).reply(200, {}) + .put('/workspace-n', body => { + return t.match(body, { name: 'workspace-n' }) + }).reply(200, {}) + await npm.exec('publish', []) + t.matchSnapshot(joinedOutput(), 'all public workspaces') + t.matchSnapshot(logs.warn, 'warns about skipped private workspace in color') + }) + + t.test('one workspace - success', async t => { + const { npm, joinedOutput } = await loadMockNpm(t, { + config: { + ...auth, + workspace: ['workspace-a'], + }, + globals: ({ prefix }) => ({ + 'process.cwd': () => prefix, + }), + prefixDir: dir, + }) + const registry = new MockRegistry({ + tap: t, + registry: npm.config.get('registry'), + authorization: token, + }) + registry.nock + .put('/workspace-a', body => { + return t.match(body, { name: 'workspace-a' }) + }).reply(200, {}) + await npm.exec('publish', []) + t.matchSnapshot(joinedOutput(), 'single workspace') + }) + + t.test('one workspace - failure', async t => { + const { npm } = await loadMockNpm(t, { + config: { + ...auth, + workspace: ['workspace-a'], + }, + globals: ({ prefix }) => ({ + 'process.cwd': () => prefix, + }), + prefixDir: dir, + }) + const registry = new MockRegistry({ + tap: t, + registry: npm.config.get('registry'), + authorization: token, + }) + registry.nock + .put('/workspace-a', body => { + return t.match(body, { name: 'workspace-a' }) + }).reply(404, {}) + await t.rejects(npm.exec('publish', []), { code: 'E404' }) }) - t.test('colorless', async t => { - t.plan(4) - - const Publish = t.mock('../../../lib/commands/publish.js', { - ...mocks, - 'proc-log': { - notice () {}, - verbose () {}, - warn (title, msg) { - t.equal(title, 'publish', 'should use publish warn title') - t.equal( - msg, - 'Skipping workspace @npmcli/a, marked as private', - 'should display skip private workspace warn msg' - ) - }, + t.test('invalid workspace', async t => { + const { npm } = await loadMockNpm(t, { + config: { + ...auth, + workspace: ['workspace-x'], }, + globals: ({ prefix }) => ({ + 'process.cwd': () => prefix, + }), + prefixDir: dir, }) - const publish = new Publish(npm) - - await publish.execWorkspaces([], []) - t.matchSnapshot(publishes, 'should publish all non-private workspaces') - t.matchSnapshot(outputs, 'should output all publishes') + await t.rejects( + npm.exec('publish', []), + { message: 'No workspaces found:\n --workspace=workspace-x' } + ) }) - t.test('unexpected error', async t => { - t.plan(2) - - const Publish = t.mock('../../../lib/commands/publish.js', { - ...mocks, - libnpmpublish: { - publish: (manifest, tarballData, opts) => { - if (manifest.private) { - throw new Error('ERR') - } - publishes.push(manifest) - }, - }, - 'proc-log': { - notice (__, msg) { - t.match(msg, 'Publishing to https://registry.npmjs.org/') - }, - verbose () {}, - }, + t.test('json', async t => { + const { npm, joinedOutput } = await loadMockNpm(t, { + config: { + ...auth, + workspaces: true, + json: true, + }, + globals: ({ prefix }) => ({ + 'process.cwd': () => prefix, + }), + prefixDir: dir, }) - const publish = new Publish(npm) - - await t.rejects(publish.execWorkspaces([], []), /ERR/, 'should throw unexpected error') + const registry = new MockRegistry({ + tap: t, + registry: npm.config.get('registry'), + authorization: token, + }) + registry.nock + .put('/workspace-a', body => { + return t.match(body, { name: 'workspace-a' }) + }).reply(200, {}) + .put('/workspace-b', body => { + return t.match(body, { name: 'workspace-b' }) + }).reply(200, {}) + .put('/workspace-n', body => { + return t.match(body, { name: 'workspace-n' }) + }).reply(200, {}) + await npm.exec('publish', []) + t.matchSnapshot(joinedOutput(), 'all workspaces in json') }) - t.end() }) -t.test('runs correct lifecycle scripts', async t => { - t.plan(5) - - const testDir = t.testdir({ - 'package.json': JSON.stringify( - { - name: 'my-cool-pkg', - version: '1.0.0', +t.test('ignore-scripts', async t => { + const { npm, joinedOutput, prefix } = await loadMockNpm(t, { + config: { + ...auth, + 'ignore-scripts': true, + }, + prefixDir: { + 'package.json': JSON.stringify({ + ...pkgJson, scripts: { - prepublishOnly: 'echo test prepublishOnly', - prepublish: 'echo test prepublish', // should NOT run this one - publish: 'echo test publish', - postpublish: 'echo test postpublish', + prepublishOnly: 'touch scripts-prepublishonly', + prepublish: 'touch scripts-prepublish', // should NOT run this one + publish: 'touch scripts-publish', + postpublish: 'touch scripts-postpublish', }, - }, - null, - 2 - ), - }) - - const scripts = [] - const Publish = t.mock('../../../lib/commands/publish.js', { - '@npmcli/run-script': args => { - scripts.push(args) - }, - '../../../lib/utils/tar.js': { - getContents: () => ({ - id: 'someid', - }), - logTar: () => { - t.pass('logTar is called') - }, - }, - libnpmpublish: { - publish: () => { - t.pass('publish called') - }, + }, null, 2), }, + globals: ({ prefix }) => ({ + 'process.cwd': () => prefix, + }), }) - const npm = mockNpm({ - config: { loglevel: 'info' }, - output: () => { - t.pass('output is called') - }, - }, t) - npm.config.getCredentialsByURI = uri => { - t.same(uri, npm.config.get('registry'), 'gets credentials for expected registry') - return { token: 'some.registry.token' } - } - const publish = new Publish(npm) - await publish.exec([testDir]) - t.same( - scripts.map(s => s.event), - ['prepublishOnly', 'publish', 'postpublish'], - 'runs only expected scripts, in order' + const registry = new MockRegistry({ + tap: t, + registry: npm.config.get('registry'), + authorization: token, + }) + registry.nock.put(`/${pkg}`).reply(200, {}) + await npm.exec('publish', []) + t.matchSnapshot(joinedOutput(), 'new package version') + t.resolveMatch( + fs.exists(path.join(prefix, 'scripts-prepublishonly')), + false, + 'did not run prepublishOnly' + ) + t.resolveMatch( + fs.exists(path.join(prefix, 'scripts-prepublish')), + false, + 'did not run prepublish' + ) + t.resolveMatch( + fs.exists(path.join(prefix, 'scripts-publish')), + false, + 'did not run publish' + ) + t.resolveMatch( + fs.exists(path.join(prefix, 'scripts-postpublish')), + false, + 'did not run postpublish' ) -}) - -t.test('does not run scripts on --ignore-scripts', async t => { - t.plan(4) - - const testDir = t.testdir({ - 'package.json': JSON.stringify( - { - name: 'my-cool-pkg', - version: '1.0.0', - }, - null, - 2 - ), - }) - - const Publish = t.mock('../../../lib/commands/publish.js', { - '@npmcli/run-script': () => { - t.fail('should not call run-script') - }, - '../../../lib/utils/tar.js': { - getContents: () => ({ - id: 'someid', - }), - logTar: () => { - t.pass('logTar is called') - }, - }, - libnpmpublish: { - publish: () => { - t.pass('publish called') - }, - }, - }) - const npm = mockNpm({ - config: { 'ignore-scripts': true, loglevel: 'info' }, - output: () => { - t.pass('output is called') - }, - }, t) - npm.config.getCredentialsByURI = uri => { - t.same(uri, npm.config.get('registry'), 'gets credentials for expected registry') - return { token: 'some.registry.token' } - } - const publish = new Publish(npm) - await publish.exec([testDir]) })