Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/commands/deprecate.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class Deprecate extends BaseCommand {
const packages = await libaccess.lsPackages(username, this.npm.flatOptions)
return Object.keys(packages)
.filter((name) =>
packages[name] === 'write' &&
packages[name] === 'read-write' &&
(opts.conf.argv.remain.length === 0 ||
name.startsWith(opts.conf.argv.remain[0])))
}
Expand Down
21 changes: 15 additions & 6 deletions test/fixtures/mock-registry.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,12 @@ class MockRegistry {
this.#nock = nock
}

whoami ({ username }) {
this.nock = this.nock.get('/-/whoami').reply(200, { username })
whoami ({ username, body, responseCode = 200, times = 1 }) {
if (username) {
this.nock = this.nock.get('/-/whoami').times(times).reply(responseCode, { username })
} else {
this.nock = this.nock.get('/-/whoami').times(times).reply(responseCode, body)
}
}

access ({ spec, access, publishRequires2fa }) {
Expand Down Expand Up @@ -108,7 +112,7 @@ class MockRegistry {
}

// team can be a team or a username
lsPackages ({ team, packages = {} }) {
lsPackages ({ team, packages = {}, times = 1 }) {
if (team.startsWith('@')) {
team = team.slice(1)
}
Expand All @@ -119,7 +123,7 @@ class MockRegistry {
} else {
uri = `/-/org/${encodeURIComponent(scope)}/package`
}
this.nock = this.nock.get(uri).query({ format: 'cli' }).reply(200, packages)
this.nock = this.nock.get(uri).query({ format: 'cli' }).times(times).reply(200, packages)
}

lsCollaborators ({ spec, user, collaborators = {} }) {
Expand Down Expand Up @@ -169,8 +173,10 @@ class MockRegistry {
this.nock = nock
}

// the last packument in the packuments array will be tagged as latest
manifest ({ name = 'test-package', packuments } = {}) {
// either pass in packuments if you need to set specific attributes besides version,
// or an array of versions
// the last packument in the packuments or versions array will be tagged latest
manifest ({ name = 'test-package', packuments, versions } = {}) {
packuments = this.packuments(packuments, name)
const latest = packuments.slice(-1)[0]
const manifest = {
Expand All @@ -184,6 +190,9 @@ class MockRegistry {
'dist-tags': { latest: latest.version },
...latest,
}
if (versions) {
packuments = versions.map(version => ({ version }))
}

for (const packument of packuments) {
manifest.versions[packument.version] = {
Expand Down
177 changes: 96 additions & 81 deletions test/lib/commands/deprecate.js
Original file line number Diff line number Diff line change
@@ -1,137 +1,152 @@
const t = require('tap')
const { load: loadMockNpm } = require('../../fixtures/mock-npm')

let getIdentityImpl = () => 'someperson'
let npmFetchBody = null
const MockRegistry = require('../../fixtures/mock-registry.js')

const npmFetch = async (uri, opts) => {
npmFetchBody = opts.body
}
const user = 'test-user'
const token = 'test-auth-token'
const auth = { '//registry.npmjs.org/:_authToken': token }
const versions = ['1.0.0', '1.0.1', '1.0.1-pre']

npmFetch.json = async (uri, opts) => {
return {
versions: {
'1.0.0': {},
'1.0.1': {},
'1.0.1-pre': {},
},
}
}

const Deprecate = t.mock('../../../lib/commands/deprecate.js', {
'../../../lib/utils/get-identity.js': async () => getIdentityImpl(),
libnpmaccess: {
lsPackages: async () => ({ foo: 'write', bar: 'write', baz: 'write', buzz: 'read' }),
},
'npm-registry-fetch': npmFetch,
})

const deprecate = new Deprecate({
flatOptions: { registry: 'https://registry.npmjs.org' },
})
// libnpmaccess maps these to read-write and read-only
const packages = { foo: 'write', bar: 'write', baz: 'write', buzz: 'read' }

t.test('completion', async t => {
const defaultIdentityImpl = getIdentityImpl
t.teardown(() => {
getIdentityImpl = defaultIdentityImpl
const { npm } = await loadMockNpm(t, {
config: {
...auth,
},
})

const deprecate = await npm.cmd('deprecate')
const testComp = async (argv, expect) => {
const res =
await deprecate.completion({ conf: { argv: { remain: argv } } })
t.strictSame(res, expect, `completion: ${argv}`)
}

const registry = new MockRegistry({
tap: t,
registry: npm.config.get('registry'),
authorization: token,
})

registry.whoami({ username: user, times: 4 })
registry.lsPackages({ team: user, packages, times: 4 })
await Promise.all([
testComp([], ['foo', 'bar', 'baz']),
testComp(['b'], ['bar', 'baz']),
testComp(['fo'], ['foo']),
testComp(['g'], []),
testComp(['foo', 'something'], []),
])

getIdentityImpl = () => {
throw new Error('deprecate test failure')
}
await testComp(['foo', 'something'], [])

registry.whoami({ statusCode: 404, body: {} })

t.rejects(testComp([], []), { message: 'deprecate test failure' })
t.rejects(testComp([], []), { code: 'ENEEDAUTH' })
})

t.test('no args', async t => {
const { npm } = await loadMockNpm(t)
await t.rejects(
deprecate.exec([]),
/Usage:/,
npm.exec('deprecate', []),
{ code: 'EUSAGE' },
'logs usage'
)
})

t.test('only one arg', async t => {
const { npm } = await loadMockNpm(t)
await t.rejects(
deprecate.exec(['foo']),
/Usage:/,
npm.exec('deprecate', ['foo']),
{ code: 'EUSAGE' },
'logs usage'
)
})

t.test('invalid semver range', async t => {
const { npm } = await loadMockNpm(t)
await t.rejects(
deprecate.exec(['foo@notaversion', 'this will fail']),
npm.exec('deprecate', ['foo@notaversion', 'this will fail']),
/invalid version range/,
'logs semver error'
)
})

t.test('undeprecate', async t => {
t.teardown(() => {
npmFetchBody = null
const { npm, joinedOutput } = await loadMockNpm(t, { config: { ...auth } })
const registry = new MockRegistry({
tap: t,
registry: npm.config.get('registry'),
authorization: token,
})
await deprecate.exec(['foo', ''])
t.match(npmFetchBody, {
versions: {
'1.0.0': { deprecated: '' },
'1.0.1': { deprecated: '' },
'1.0.1-pre': { deprecated: '' },
},
}, 'undeprecates everything')
const manifest = registry.manifest({
name: 'foo',
versions,
})
registry.package({ manifest, query: { write: true } })
registry.nock.put('/foo', body => {
for (const version of versions) {
if (body.versions[version].deprecated !== '') {
return false
}
}
return true
}).reply(200, {})

await npm.exec('deprecate', ['foo', ''])
t.match(joinedOutput(), '')
})

t.test('deprecates given range', async t => {
t.teardown(() => {
npmFetchBody = null
const { npm, joinedOutput } = await loadMockNpm(t, { config: { ...auth } })
const registry = new MockRegistry({
tap: t,
registry: npm.config.get('registry'),
authorization: token,
})

await deprecate.exec(['[email protected]', 'this version is deprecated'])
t.match(npmFetchBody, {
versions: {
'1.0.0': {
deprecated: 'this version is deprecated',
},
'1.0.1': {
// the undefined here is necessary to ensure that we absolutely
// did not assign this property
deprecated: undefined,
},
},
const manifest = registry.manifest({
name: 'foo',
versions,
})
registry.package({ manifest, query: { write: true } })
const message = 'test deprecation message'
registry.nock.put('/foo', body => {
if (body.versions['1.0.1'].deprecated) {
return false
}
if (body.versions['1.0.1-pre'].deprecated) {
return false
}
return body.versions['1.0.0'].deprecated === message
}).reply(200, {})
await npm.exec('deprecate', ['[email protected]', message])
t.match(joinedOutput(), '')
})

t.test('deprecates all versions when no range is specified', async t => {
t.teardown(() => {
npmFetchBody = null
const { npm, joinedOutput } = await loadMockNpm(t, { config: { ...auth } })
const registry = new MockRegistry({
tap: t,
registry: npm.config.get('registry'),
authorization: token,
})

await deprecate.exec(['foo', 'this version is deprecated'])

t.match(npmFetchBody, {
versions: {
'1.0.0': {
deprecated: 'this version is deprecated',
},
'1.0.1': {
deprecated: 'this version is deprecated',
},
'1.0.1-pre': {
deprecated: 'this version is deprecated',
},
},
const manifest = registry.manifest({
name: 'foo',
versions,
})
registry.package({ manifest, query: { write: true } })
const message = 'test deprecation message'
registry.nock.put('/foo', body => {
for (const version of versions) {
if (body.versions[version].deprecated !== message) {
return false
}
}
return true
}).reply(200, {})

await npm.exec('deprecate', ['foo', message])
t.match(joinedOutput(), '')
})
38 changes: 19 additions & 19 deletions test/lib/commands/unpublish.js
Original file line number Diff line number Diff line change
Expand Up @@ -423,8 +423,8 @@ t.test('completion', async t => {
packuments: ['1.0.0', '1.0.1'],
})
await registry.package({ manifest, query: { write: true } })
registry.nock.get('/-/whoami').reply(200, { username: user })
.get('/-/org/test-user/package?format=cli').reply(200, { [pkg]: 'write' })
registry.whoami({ username: user })
registry.nock.get('/-/org/test-user/package?format=cli').reply(200, { [pkg]: 'write' })

await testComp(t, {
argv: ['npm', 'unpublish'],
Expand All @@ -445,8 +445,8 @@ t.test('completion', async t => {
const manifest = registry.manifest({ name: pkg })
manifest.versions = {}
await registry.package({ manifest, query: { write: true } })
registry.nock.get('/-/whoami').reply(200, { username: user })
.get('/-/org/test-user/package?format=cli').reply(200, { [pkg]: 'write' })
registry.whoami({ username: user })
registry.nock.get('/-/org/test-user/package?format=cli').reply(200, { [pkg]: 'write' })

await testComp(t, {
argv: ['npm', 'unpublish'],
Expand All @@ -464,12 +464,12 @@ t.test('completion', async t => {
registry: npm.config.get('registry'),
authorization: 'test-auth-token',
})
registry.nock.get('/-/whoami').reply(200, { username: user })
.get('/-/org/test-user/package?format=cli').reply(200, {
[pkg]: 'write',
[`${pkg}a`]: 'write',
[`${pkg}b`]: 'write',
})
registry.whoami({ username: user })
registry.nock.get('/-/org/test-user/package?format=cli').reply(200, {
[pkg]: 'write',
[`${pkg}a`]: 'write',
[`${pkg}b`]: 'write',
})

await testComp(t, {
argv: ['npm', 'unpublish'],
Expand All @@ -488,7 +488,7 @@ t.test('completion', async t => {
registry: npm.config.get('registry'),
authorization: 'test-auth-token',
})
registry.nock.get('/-/whoami').reply(200, { username: user })
registry.whoami({ username: user })
registry.nock.get('/-/org/test-user/package?format=cli').reply(200, {})

await testComp(t, {
Expand All @@ -505,11 +505,11 @@ t.test('completion', async t => {
registry: npm.config.get('registry'),
authorization: 'test-auth-token',
})
registry.nock.get('/-/whoami').reply(200, { username: user })
.get('/-/org/test-user/package?format=cli').reply(200, {
[pkg]: 'write',
[`${pkg}a`]: 'write',
})
registry.whoami({ username: user })
registry.nock.get('/-/org/test-user/package?format=cli').reply(200, {
[pkg]: 'write',
[`${pkg}a`]: 'write',
})

await testComp(t, {
argv: ['npm', 'unpublish'],
Expand All @@ -525,8 +525,8 @@ t.test('completion', async t => {
registry: npm.config.get('registry'),
authorization: 'test-auth-token',
})
registry.nock.get('/-/whoami').reply(200, { username: user })
.get('/-/org/test-user/package?format=cli').reply(200, null)
registry.whoami({ username: user })
registry.nock.get('/-/org/test-user/package?format=cli').reply(200, null)

await testComp(t, {
argv: ['npm', 'unpublish'],
Expand All @@ -542,7 +542,7 @@ t.test('completion', async t => {
registry: npm.config.get('registry'),
authorization: 'test-auth-token',
})
registry.nock.get('/-/whoami').reply(404)
registry.whoami({ responseCode: 404 })

await testComp(t, {
argv: ['npm', 'unpublish'],
Expand Down