Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: add lib/link.js tests #1786

Closed
Closed
Show file tree
Hide file tree
Changes from 1 commit
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
45 changes: 29 additions & 16 deletions lib/link.js
Original file line number Diff line number Diff line change
@@ -1,66 +1,79 @@
// link with no args: symlink the folder to the global location
// link with package arg: symlink the global to the local
'use strict'

const { readdir } = require('fs')
const { resolve } = require('path')

const Arborist = require('@npmcli/arborist')

const npm = require('./npm.js')
const usageUtil = require('./utils/usage.js')
const reifyOutput = require('./utils/reify-output.js')
const { resolve } = require('path')
const Arborist = require('@npmcli/arborist')

const completion = (opts, cb) => {
const { readdir } = require('fs')
const dir = npm.globalDir
readdir(dir, (er, files) => cb(er, files.filter(f => !/^[._-]/.test(f))))
}

const usage = usageUtil(
'link',
'npm link (in package dir)' +
'\nnpm link [<@scope>/]<pkg>[@<version>]'
'\nnpm link [<@scope>/]<pkg>'
)

const cmd = (args, cb) => link(args).then(() => cb()).catch(cb)

const link = async args => {
if (npm.config.get('global')) {
throw new Error(
'link should never be --global.\n' +
'Please re-run this command with --local'
throw Object.assign(
new Error(
'link should never be --global.\n' +
'Please re-run this command with --local'
),
{ code: 'ELINKGLOBAL' }
)
}

// link with no args: symlink the folder to the global location
// link with package arg: symlink the global to the local
args = args.filter(a => resolve(a) !== npm.prefix)
return args.length ? linkInstall(args) : linkPkg()
return args.length
? linkInstall(args)
: linkPkg()
}

const linkInstall = async args => {
// add all the args as global installs, and then add symlink installs locally
// to the packages in the global space.
// load current packages from the global space,
// and then add symlinks installs locally
const globalTop = resolve(npm.globalDir, '..')
const globalArb = new Arborist({
...npm.flatOptions,
path: globalTop,
global: true
})

const globals = await globalArb.reify({ add: args })
const globals = await globalArb.loadActual()
ruyadorno marked this conversation as resolved.
Show resolved Hide resolved

const links = [
...globals.children.values()
]
.filter(i => args.some(j => j === i.name))
ruyadorno marked this conversation as resolved.
Show resolved Hide resolved

const links = globals.edgesOut.keys()
const localArb = new Arborist({
...npm.flatOptions,
path: npm.prefix
})
await localArb.reify({
add: links.map(l => `file:${resolve(globalTop, 'node_modules', l)}`)
add: links.map(l => `file:${resolve(globalTop, 'node_modules', l.path)}`)
})

reifyOutput(localArb)
}

const linkPkg = async () => {
const globalTop = resolve(npm.globalDir, '..')
const arb = new Arborist({
...npm.flatOptions,
path: resolve(npm.globalDir, '..'),
path: globalTop,
global: true
})
await arb.reify({ add: [`file:${npm.prefix}`] })
Expand Down
19 changes: 19 additions & 0 deletions tap-snapshots/test-lib-link.js-TAP.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/* IMPORTANT
* This snapshot file is auto-generated, but designed for humans.
* It should be checked into source control and tracked carefully.
* Re-generate by setting TAP_SNAPSHOT=1 and running tests.
* Make sure to inspect the output below. Do not ignore changes!
*/
'use strict'
exports[`test/lib/link.js TAP link global linked pkg to local nm when using args > should create a local symlink to global pkg 1`] = `
{CWD}/test/lib/link-link-global-linked-pkg-to-local-nm-when-using-args/my-project/node_modules/a -> {CWD}/test/lib/link-link-global-linked-pkg-to-local-nm-when-using-args/global-prefix/lib/node_modules/a
{CWD}/test/lib/link-link-global-linked-pkg-to-local-nm-when-using-args/my-project/node_modules/@myscope/bar -> {CWD}/test/lib/link-link-global-linked-pkg-to-local-nm-when-using-args/global-prefix/lib/node_modules/@myscope/bar
{CWD}/test/lib/link-link-global-linked-pkg-to-local-nm-when-using-args/my-project/node_modules/test-pkg-link -> {CWD}/test/lib/link-link-global-linked-pkg-to-local-nm-when-using-args/test-pkg-link
{CWD}/test/lib/link-link-global-linked-pkg-to-local-nm-when-using-args/my-project/node_modules/@myscope/linked -> {CWD}/test/lib/link-link-global-linked-pkg-to-local-nm-when-using-args/scoped-linked

`

exports[`test/lib/link.js TAP link to globalDir when in current working dir of pkg and no args > should create a global link to current pkg 1`] = `
{CWD}/test/lib/link-link-to-globalDir-when-in-current-working-dir-of-pkg-and-no-args/global-prefix/lib/node_modules/test-pkg-link -> {CWD}/test/lib/link-link-to-globalDir-when-in-current-working-dir-of-pkg-and-no-args/test-pkg-link

`
218 changes: 218 additions & 0 deletions test/lib/link.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
const { resolve } = require('path')

const Arborist = require('@npmcli/arborist')
const t = require('tap')
const requireInject = require('require-inject')

const redactCwd = (path) => {
const normalizePath = p => p
.replace(/\\+/g, '/')
.replace(/\r\n/g, '\n')
return normalizePath(path)
.replace(new RegExp(normalizePath(process.cwd()), 'g'), '{CWD}')
}

t.cleanSnapshot = (str) => redactCwd(str)

let reifyOutput
const npm = {
globalDir: null,
prefix: null,
flatOptions: {},
config: {
get () { return false }
}
}
const printLinks = async (opts) => {
let res = ''
const arb = new Arborist(opts)
const tree = await arb.loadActual()
const linkedItems = [...tree.inventory.values()]
for (const item of linkedItems) {
if (item.target)
res += `${item.path} -> ${item.target.path}\n`
}
return res
}

const mocks = {
'../../lib/npm.js': npm,
'../../lib/utils/reify-output.js': () => reifyOutput()
}

const link = requireInject('../../lib/link.js', mocks)

t.test('link to globalDir when in current working dir of pkg and no args', (t) => {
t.plan(2)

const testdir = t.testdir({
'global-prefix': {
lib: {
node_modules: {
a: {
'package.json': JSON.stringify({
name: 'a',
version: '1.0.0'
})
}
}
}
},
'test-pkg-link': {
'package.json': JSON.stringify({
name: 'test-pkg-link',
version: '1.0.0'
})
}
})
npm.globalDir = resolve(testdir, 'global-prefix', 'lib', 'node_modules')
npm.prefix = resolve(testdir, 'test-pkg-link')

reifyOutput = async () => {
reifyOutput = undefined

const links = await printLinks({
path: resolve(npm.globalDir, '..'),
global: true
})

t.matchSnapshot(links, 'should create a global link to current pkg')
}

link([], (err) => {
t.ifError(err, 'should not error out')
})
})

t.test('link global linked pkg to local nm when using args', (t) => {
t.plan(2)

const testdir = t.testdir({
'global-prefix': {
lib: {
node_modules: {
'@myscope': {
foo: {
'package.json': JSON.stringify({
name: '@myscope/foo',
version: '1.0.0'
})
},
bar: {
'package.json': JSON.stringify({
name: '@myscope/bar',
version: '1.0.0'
})
},
linked: t.fixture('symlink', '../../../../scoped-linked')
},
a: {
'package.json': JSON.stringify({
name: 'a',
version: '1.0.0'
})
},
b: {
'package.json': JSON.stringify({
name: 'b',
version: '1.0.0'
})
},
'test-pkg-link': t.fixture('symlink', '../../../test-pkg-link')
}
}
},
'test-pkg-link': {
'package.json': JSON.stringify({
name: 'test-pkg-link',
version: '1.0.0'
})
},
'scoped-linked': {
'package.json': JSON.stringify({
name: '@myscope/linked',
version: '1.0.0'
})
},
'my-project': {
'package.json': JSON.stringify({
name: 'my-project',
version: '1.0.0',
dependencies: {
foo: '^1.0.0'
}
}),
node_modules: {
foo: {
'package.json': JSON.stringify({
name: 'foo',
version: '1.0.0'
})
}
}
}
})
npm.globalDir = resolve(testdir, 'global-prefix', 'lib', 'node_modules')
npm.prefix = resolve(testdir, 'my-project')

reifyOutput = async () => {
reifyOutput = undefined

const links = await printLinks({
path: npm.prefix
})

t.matchSnapshot(links, 'should create a local symlink to global pkg')
}

// installs examples for:
// - test-pkg-link: pkg linked to globalDir from local fs
// - @myscope/linked: scoped pkg linked to globalDir from local fs
// - @myscope/bar: prev installed scoped package available in globalDir
// - a: prev installed package available in globalDir
link(['test-pkg-link', '@myscope/linked', '@myscope/bar', 'a'], (err) => {
t.ifError(err, 'should not error out')
})
})

t.test('completion', (t) => {
const testdir = t.testdir({
'global-prefix': {
lib: {
node_modules: {
foo: {},
bar: {},
lorem: {},
ipsum: {}
}
}
}
})
npm.globalDir = resolve(testdir, 'global-prefix', 'lib', 'node_modules')

link.completion({}, (err, words) => {
t.ifError(err, 'should not error out')
t.deepEqual(
words,
['bar', 'foo', 'ipsum', 'lorem'],
'should list all package names available in globalDir'
)
t.end()
})
})

t.test('--global option', (t) => {
const _config = npm.config
npm.config = { get () { return true } }
link([], (err) => {
npm.config = _config

t.match(
err.message,
/link should never be --global/,
'should throw an useful error'
)

t.end()
})
})