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:
authorGar <gar+gh@danger.computer>2022-04-19 21:29:05 +0300
committerLuke Karrys <luke@lukekarrys.com>2022-04-20 02:25:15 +0300
commitced0acfe5998a5be9313815f76f5c1439a09db78 (patch)
tree805489670e3cb0b0ac01619b7ad33e02771982e3 /test
parent4a46a27f2b968e2f8c1f4821508f93013738c482 (diff)
fix: consolidate registryConfig application logic
It should happen whenever we read a manifest anyways. Tests were also rewritten to be real.
Diffstat (limited to 'test')
-rw-r--r--test/fixtures/mock-npm.js2
-rw-r--r--test/lib/commands/publish.js1244
2 files changed, 489 insertions, 757 deletions
diff --git a/test/fixtures/mock-npm.js b/test/fixtures/mock-npm.js
index b6742a425..4263dc8fb 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 64eb7c60c..b17424b08 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])
})