Files
pnpm/test/index.js
2016-08-30 21:36:13 +03:00

777 lines
24 KiB
JavaScript

'use strict'
const test = require('tape')
const path = require('path')
const join = require('path').join
const delimiter = require('path').delimiter
const fs = require('fs')
const caw = require('caw')
const isexe = require('isexe')
const semver = require('semver')
const crossSpawn = require('cross-spawn')
const spawnSync = crossSpawn.sync
const thenify = require('thenify')
const ncp = thenify(require('ncp').ncp)
const mkdirp = require('mkdirp')
const prepare = require('./support/prepare')
const basicPackageJson = require('./support/simple-package.json')
const install = require('../lib/cmd/install')
const uninstall = require('../lib/cmd/uninstall')
const link = require('../lib/cmd/link')
const isWindows = process.platform === 'win32'
const preserveSymlinks = semver.satisfies(process.version, '>=6.3.0')
const globalPath = join(process.cwd(), '.tmp', 'global')
if (!caw() && !isWindows) {
require('./support/sepia')
}
let stat, _
function isExecutable (t, filePath) {
if (!isWindows && !preserveSymlinks) {
stat = fs.lstatSync(filePath)
t.ok(stat.isSymbolicLink(), filePath + ' symlink is available')
stat = fs.statSync(filePath)
t.equal(stat.mode, parseInt('100755', 8), filePath + ' is executable')
t.ok(stat.isFile(), filePath + ' refers to a file')
return
}
t.ok(isexe(filePath), filePath + ' is executable')
}
test('API', t => {
const pnpm = require('..')
t.equal(typeof pnpm.install, 'function', 'exports install()')
t.equal(typeof pnpm.install, 'function', 'exports installPkgDeps()')
t.equal(typeof pnpm.uninstall, 'function', 'exports uninstall()')
t.equal(typeof pnpm.linkFromGlobal, 'function', 'exports linkFromGlobal()')
t.equal(typeof pnpm.linkFromRelative, 'function', 'exports linkFromRelative()')
t.equal(typeof pnpm.linkToGlobal, 'function', 'exports linkToGlobal()')
t.end()
})
test('small with dependencies (rimraf)', t => {
prepare()
install(['rimraf@2.5.1'], { quiet: true })
.then(() => {
const rimraf = require(join(process.cwd(), 'node_modules', 'rimraf'))
t.ok(typeof rimraf === 'function', 'rimraf() is available')
isExecutable(t, join(process.cwd(), 'node_modules', '.bin', 'rimraf'))
t.end()
})
.catch(t.end)
})
test('no dependencies (lodash)', t => {
prepare()
install(['lodash@4.0.0'], { quiet: true })
.then(() => {
_ = require(join(process.cwd(), 'node_modules', 'lodash'))
t.ok(typeof _ === 'function', '_ is available')
t.ok(typeof _.clone === 'function', '_.clone is available')
t.end()
})
.catch(t.end)
})
test('scoped modules without version spec (@rstacruz/tap-spec)', t => {
prepare()
install(['@rstacruz/tap-spec'], { quiet: true })
.then(() => {
_ = require(join(process.cwd(), 'node_modules', '@rstacruz/tap-spec'))
t.ok(typeof _ === 'function', 'tap-spec is available')
t.end()
})
.catch(t.end)
})
test('scoped modules with versions (@rstacruz/tap-spec@4.1.1)', t => {
prepare()
install(['@rstacruz/tap-spec@4.1.1'], { quiet: true })
.then(() => {
_ = require(join(process.cwd(), 'node_modules', '@rstacruz/tap-spec'))
t.ok(typeof _ === 'function', 'tap-spec is available')
t.end()
})
.catch(t.end)
})
test('scoped modules (@rstacruz/tap-spec@*)', t => {
prepare()
install(['@rstacruz/tap-spec@*'], { quiet: true })
.then(() => {
_ = require(join(process.cwd(), 'node_modules', '@rstacruz/tap-spec'))
t.ok(typeof _ === 'function', 'tap-spec is available')
t.end()
})
.catch(t.end)
})
test('multiple scoped modules (@rstacruz/...)', t => {
prepare()
install(['@rstacruz/tap-spec@*', '@rstacruz/travis-encrypt@*'], { quiet: true })
.then(() => {
_ = require(join(process.cwd(), 'node_modules', '@rstacruz/tap-spec'))
t.ok(typeof _ === 'function', 'tap-spec is available')
_ = require(join(process.cwd(), 'node_modules', '@rstacruz/travis-encrypt'))
t.ok(typeof _ === 'function', 'travis-encrypt is available')
t.end()
})
.catch(t.end)
})
test('nested scoped modules (test-pnpm-issue219 -> @zkochan/test-pnpm-issue219)', t => {
prepare()
install(['test-pnpm-issue219@1.0.2'], { quiet: true })
.then(() => {
_ = require(join(process.cwd(), 'node_modules', 'test-pnpm-issue219'))
t.ok(_ === 'test-pnpm-issue219,@zkochan/test-pnpm-issue219', 'nested scoped package is available')
t.end()
})
.catch(t.end)
})
test('scoped modules from a directory', t => {
prepare()
install([local('local-scoped-pkg')], { quiet: true })
.then(() => {
const localPkg = require(
join(process.cwd(), 'node_modules', '@scope', 'local-scoped-pkg'))
t.equal(localPkg(), '@scope/local-scoped-pkg', 'localScopedPkg() is available')
t.end()
})
.catch(t.end)
})
test('skip failing optional dependencies', t => {
prepare()
install(['pkg-with-failing-optional-dependency@1.0.1'], { quiet: true })
.then(() => {
const isNegative = require(join(process.cwd(), 'node_modules', 'pkg-with-failing-optional-dependency'))
t.ok(isNegative(-1), 'package with failed optional dependency has the dependencies installed correctly')
t.end()
})
.catch(t.end)
})
test('idempotency (rimraf)', t => {
prepare()
install(['rimraf@2.5.1'], { quiet: true })
.then(() => install([ 'rimraf@2.5.1' ], { quiet: true }))
.then(() => {
const rimraf = require(join(process.cwd(), 'node_modules', 'rimraf'))
t.ok(typeof rimraf === 'function', 'rimraf is available')
t.end()
})
.catch(t.end)
})
test('overwriting (lodash@3.10.1 and @4.0.0)', t => {
prepare()
install(['lodash@3.10.1'], { quiet: true })
.then(() => install([ 'lodash@4.0.0' ], { quiet: true }))
.then(() => {
_ = require(join(process.cwd(), 'node_modules', 'lodash', 'package.json'))
t.ok(_.version === '4.0.0', 'lodash is 4.0.0')
t.end()
})
.catch(t.end)
})
test('big with dependencies and circular deps (babel-preset-2015)', t => {
prepare()
install(['babel-preset-es2015@6.3.13'], { quiet: true })
.then(() => {
const b = require(join(process.cwd(), 'node_modules', 'babel-preset-es2015'))
t.ok(typeof b === 'object', 'babel-preset-es2015 is available')
t.end()
})
.catch(t.end)
})
// NOTE: fsevents can't be installed on Windows
if (!isWindows) {
test('bundleDependencies (fsevents@1.0.6)', t => {
prepare()
install(['fsevents@1.0.6'], { quiet: true })
.then(() => {
isExecutable(t, join(process.cwd(), 'node_modules', 'fsevents', 'node_modules', '.bin', 'mkdirp'))
t.end()
})
.catch(t.end)
})
}
test('compiled modules (ursa@0.9.1)', t => {
if (!process.env.CI || isWindows) {
t.skip('only ran on CI')
return t.end()
}
prepare()
install(['ursa@0.9.1'], { quiet: false })
.then(() => {
const ursa = require(join(process.cwd(), 'node_modules', 'ursa'))
t.ok(typeof ursa === 'object', 'ursa() is available')
t.end()
})
.catch(t.end)
})
test('tarballs (is-array-1.0.1.tgz)', t => {
prepare()
install(['http://registry.npmjs.org/is-array/-/is-array-1.0.1.tgz'], { quiet: true })
.then(() => {
const isArray = require(
join(process.cwd(), 'node_modules', 'is-array'))
t.ok(isArray, 'isArray() is available')
stat = fs.statSync(
join(process.cwd(), 'node_modules', '.store',
'is-array-1.0.1#a83102a9c117983e6ff4d85311fb322231abe3d6'))
t.ok(stat.isDirectory(), 'stored in the proper location')
t.end()
})
.catch(t.end)
})
test('local file', t => {
prepare()
install([local('local-pkg')], { quiet: true })
.then(() => {
const localPkg = require(
join(process.cwd(), 'node_modules', 'local-pkg'))
t.ok(localPkg, 'localPkg() is available')
t.end()
})
.catch(t.end)
})
test('nested local dependency of a local dependency', t => {
prepare()
install([local('pkg-with-local-dep')], { quiet: true })
.then(() => {
const pkgWithLocalDep = require(
join(process.cwd(), 'node_modules', 'pkg-with-local-dep'))
t.ok(pkgWithLocalDep, 'pkgWithLocalDep() is available')
t.equal(pkgWithLocalDep(), 'local-pkg', 'pkgWithLocalDep() returns data from local-pkg')
t.end()
})
.catch(t.end)
})
// Skipping on CI as failing frequently there, due to environment issues
if (!process.env.CI) {
test('from a github repo', t => {
prepare()
install(['kevva/is-negative'], { quiet: true })
.then(() => {
const localPkg = require(
join(process.cwd(), 'node_modules', 'is-negative'))
t.ok(localPkg, 'isNegative() is available')
t.end()
})
.catch(t.end)
})
}
test('shrinkwrap compatibility', t => {
prepare({ dependencies: { rimraf: '*' } })
install(['rimraf@2.5.1'], { quiet: true })
.then(() => {
return new Promise((resolve, reject) => {
const proc = crossSpawn.spawn('npm', ['shrinkwrap'])
proc.on('error', reject)
proc.on('close', code => {
if (code > 0) return reject(new Error('Exit code ' + code))
const wrap = JSON.parse(fs.readFileSync('npm-shrinkwrap.json', 'utf-8'))
t.ok(wrap.dependencies.rimraf.version === '2.5.1',
'npm shrinkwrap is successful')
t.end()
})
})
})
.catch(t.end)
})
test('run pre/postinstall scripts', t => {
prepare()
install([local('pre-and-postinstall-scripts-example')], { quiet: true })
.then(() => {
const generatedByPreinstall = require(join(process.cwd(), 'node_modules', 'pre-and-postinstall-scripts-example/generated-by-preinstall'))
t.ok(typeof generatedByPreinstall === 'function', 'generatedByPreinstall() is available')
const generatedByPostinstall = require(join(process.cwd(), 'node_modules', 'pre-and-postinstall-scripts-example/generated-by-postinstall'))
t.ok(typeof generatedByPostinstall === 'function', 'generatedByPostinstall() is available')
t.end()
})
.catch(t.end)
})
test('run install scripts', t => {
prepare()
install([local('install-script-example')], { quiet: true })
.then(() => {
const generatedByInstall = require(join(process.cwd(), 'node_modules', 'install-script-example/generated-by-install'))
t.ok(typeof generatedByInstall === 'function', 'generatedByInstall() is available')
t.end()
})
.catch(t.end)
})
test('save to package.json (rimraf@2.5.1)', t => {
prepare()
install(['rimraf@2.5.1'], { quiet: true, save: true })
.then(() => {
const rimraf = require(join(process.cwd(), 'node_modules', 'rimraf'))
t.ok(typeof rimraf === 'function', 'rimraf() is available')
const pkgJson = fs.readFileSync(join(process.cwd(), 'package.json'), 'utf8')
const dependencies = JSON.parse(pkgJson).dependencies
t.deepEqual(dependencies, {rimraf: '^2.5.1'}, 'rimraf has been added to dependencies')
t.end()
})
.catch(t.end)
})
test('saveDev scoped module to package.json (@rstacruz/tap-spec)', t => {
prepare()
install(['@rstacruz/tap-spec'], { quiet: true, saveDev: true })
.then(() => {
const tapSpec = require(join(process.cwd(), 'node_modules', '@rstacruz/tap-spec'))
t.ok(typeof tapSpec === 'function', 'tapSpec() is available')
const pkgJson = fs.readFileSync(join(process.cwd(), 'package.json'), 'utf8')
const devDependencies = JSON.parse(pkgJson).devDependencies
t.deepEqual(devDependencies, { '@rstacruz/tap-spec': '^4.1.1' }, 'tap-spec has been added to devDependencies')
t.end()
})
.catch(t.end)
})
test('multiple save to package.json with `exact` versions (@rstacruz/tap-spec & rimraf@2.5.1) (in sorted order)', t => {
prepare()
install(['rimraf@2.5.1', '@rstacruz/tap-spec@latest'], { quiet: true, save: true, saveExact: true })
.then(() => {
const tapSpec = require(join(process.cwd(), 'node_modules', '@rstacruz/tap-spec'))
t.ok(typeof tapSpec === 'function', 'tapSpec() is available')
const rimraf = require(join(process.cwd(), 'node_modules', 'rimraf'))
t.ok(typeof rimraf === 'function', 'rimraf() is available')
const pkgJson = fs.readFileSync(join(process.cwd(), 'package.json'), 'utf8')
const dependencies = JSON.parse(pkgJson).dependencies
const expectedDeps = {
'@rstacruz/tap-spec': '4.1.1',
rimraf: '2.5.1'
}
t.deepEqual(dependencies, expectedDeps, 'tap-spec and rimraf have been added to dependencies')
t.deepEqual(Object.keys(dependencies), Object.keys(expectedDeps), 'tap-spec and rimraf have been added to dependencies in sorted order')
t.end()
})
.catch(t.end)
})
test('flattening symlinks (minimatch@3.0.0)', t => {
prepare()
install(['minimatch@3.0.0'], { quiet: true })
.then(() => {
stat = fs.lstatSync(join(process.cwd(), 'node_modules', '.store', 'node_modules', 'balanced-match'))
t.ok(stat.isSymbolicLink(), 'balanced-match is linked into store node_modules')
_ = exists(join(process.cwd(), 'node_modules', 'balanced-match'))
t.ok(!_, 'balanced-match is not linked into main node_modules')
t.end()
})
.catch(t.end)
})
test('flattening symlinks (minimatch + balanced-match)', t => {
prepare()
install(['minimatch@3.0.0'], { quiet: true })
.then(() => install(['balanced-match@^0.3.0'], { quiet: true }))
.then(() => {
_ = exists(join(process.cwd(), 'node_modules', '.store', 'node_modules', 'balanced-match'))
t.ok(!_, 'balanced-match is removed from store node_modules')
_ = exists(join(process.cwd(), 'node_modules', 'balanced-match'))
t.ok(_, 'balanced-match now in main node_modules')
t.end()
})
.catch(t.end)
})
test('production install (with --production flag)', t => {
prepare(basicPackageJson)
return install([], { quiet: true, production: true })
.then(() => {
const rimrafDir = fs.statSync(join(process.cwd(), 'node_modules', 'rimraf'))
let tapStatErrCode
try {
fs.statSync(join(process.cwd(), 'node_modules', '@rstacruz'))
} catch (err) { tapStatErrCode = err.code }
t.ok(rimrafDir.isSymbolicLink, 'rimraf exists')
t.is(tapStatErrCode, 'ENOENT', 'tap-spec does not exist')
t.end()
})
.catch(t.end)
})
test('production install (with production NODE_ENV)', t => {
const originalNodeEnv = process.env.NODE_ENV
process.env.NODE_ENV = 'production'
prepare(basicPackageJson)
return install([], { quiet: true })
.then(() => {
// reset NODE_ENV
process.env.NODE_ENV = originalNodeEnv
const rimrafDir = fs.statSync(join(process.cwd(), 'node_modules', 'rimraf'))
let tapStatErrCode
try {
fs.statSync(join(process.cwd(), 'node_modules', '@rstacruz'))
} catch (err) { tapStatErrCode = err.code }
t.ok(rimrafDir.isSymbolicLink, 'rimraf exists')
t.is(tapStatErrCode, 'ENOENT', 'tap-spec does not exist')
t.end()
})
.catch(t.end)
})
test('uninstall package with no dependencies', t => {
prepare()
install(['is-negative@2.1.0'], { quiet: true, save: true })
.then(_ => uninstall(['is-negative'], { save: true }))
.then(() => {
stat = exists(join(process.cwd(), 'node_modules', '.store', 'is-negative@2.1.0'))
t.ok(!stat, 'is-negative is removed from store')
stat = existsSymlink(join(process.cwd(), 'node_modules', 'is-negative'))
t.ok(!stat, 'is-negative is removed from node_modules')
const pkgJson = fs.readFileSync(join(process.cwd(), 'package.json'), 'utf8')
const dependencies = JSON.parse(pkgJson).dependencies
const expectedDeps = {}
t.deepEqual(dependencies, expectedDeps, 'is-negative has been removed from dependencies')
t.end()
})
.catch(t.end)
})
test('uninstall package with dependencies and do not touch other deps', t => {
prepare()
install(['is-negative@2.1.0', 'camelcase-keys@3.0.0'], { quiet: true, save: true })
.then(_ => uninstall(['camelcase-keys'], { save: true }))
.then(() => {
stat = exists(join(process.cwd(), 'node_modules', '.store', 'camelcase-keys@2.1.0'))
t.ok(!stat, 'camelcase-keys is removed from store')
stat = existsSymlink(join(process.cwd(), 'node_modules', 'camelcase-keys'))
t.ok(!stat, 'camelcase-keys is removed from node_modules')
stat = exists(join(process.cwd(), 'node_modules', '.store', 'camelcase@3.0.0'))
t.ok(!stat, 'camelcase is removed from store')
stat = existsSymlink(join(process.cwd(), 'node_modules', 'camelcase'))
t.ok(!stat, 'camelcase is removed from node_modules')
stat = exists(join(process.cwd(), 'node_modules', '.store', 'map-obj@1.0.1'))
t.ok(!stat, 'map-obj is removed from store')
stat = existsSymlink(join(process.cwd(), 'node_modules', 'map-obj'))
t.ok(!stat, 'map-obj is removed from node_modules')
stat = exists(join(process.cwd(), 'node_modules', '.store', 'is-negative@2.1.0'))
t.ok(stat, 'is-negative is not removed from store')
stat = existsSymlink(join(process.cwd(), 'node_modules', 'is-negative'))
t.ok(stat, 'is-negative is not removed from node_modules')
const pkgJson = fs.readFileSync(join(process.cwd(), 'package.json'), 'utf8')
const dependencies = JSON.parse(pkgJson).dependencies
const expectedDeps = {
'is-negative': '^2.1.0'
}
t.deepEqual(dependencies, expectedDeps, 'camelcase-keys has been removed from dependencies')
t.end()
})
.catch(t.end)
})
test('uninstall package with its bin files', t => {
prepare()
install(['sh-hello-world@1.0.0'], { quiet: true, save: true })
.then(_ => uninstall(['sh-hello-world'], { save: true }))
.then(() => {
// check for both a symlink and a file because in some cases the file will be a proxied not symlinked
stat = existsSymlink(join(process.cwd(), 'node_modules', '.bin', 'sh-hello-world'))
t.ok(!stat, 'sh-hello-world is removed from .bin')
stat = exists(join(process.cwd(), 'node_modules', '.bin', 'sh-hello-world'))
t.ok(!stat, 'sh-hello-world is removed from .bin')
t.end()
})
.catch(t.end)
})
test('keep dependencies used by others', t => {
prepare()
install(['hastscript@3.0.0', 'camelcase-keys@3.0.0'], { quiet: true, save: true })
.then(_ => uninstall(['camelcase-keys'], { save: true }))
.then(() => {
stat = exists(join(process.cwd(), 'node_modules', '.store', 'camelcase-keys@2.1.0'))
t.ok(!stat, 'camelcase-keys is removed from store')
stat = existsSymlink(join(process.cwd(), 'node_modules', 'camelcase-keys'))
t.ok(!stat, 'camelcase-keys is removed from node_modules')
stat = exists(join(process.cwd(), 'node_modules', '.store', 'camelcase@3.0.0'))
t.ok(stat, 'camelcase is not removed from store')
stat = exists(join(process.cwd(), 'node_modules', '.store', 'map-obj@1.0.1'))
t.ok(!stat, 'map-obj is removed from store')
stat = existsSymlink(join(process.cwd(), 'node_modules', 'map-obj'))
t.ok(!stat, 'map-obj is removed from node_modules')
const pkgJson = fs.readFileSync(join(process.cwd(), 'package.json'), 'utf8')
const dependencies = JSON.parse(pkgJson).dependencies
const expectedDeps = {
'hastscript': '^3.0.0'
}
t.deepEqual(dependencies, expectedDeps, 'camelcase-keys has been removed from dependencies')
t.end()
})
.catch(t.end)
})
const wait = ms => new Promise(resolve => setTimeout(resolve, ms))
test('fail when trying to install into the same store simultaneously', t => {
prepare()
Promise.all([
install([local('pkg-that-installs-slowly')], { quiet: true }),
wait(500) // to be sure that lock was created
.then(_ => install(['rimraf@2.5.1'], { quiet: true }))
.then(_ => t.fail('the store should have been locked'))
.catch(err => t.ok(err, 'store is locked'))
])
.then(_ => t.end())
.catch(t.end)
})
test('fail when trying to install and uninstall from the same store simultaneously', t => {
prepare()
Promise.all([
install([local('pkg-that-installs-slowly')], { quiet: true }),
wait(500) // to be sure that lock was created
.then(_ => uninstall(['rimraf@2.5.1'], { quiet: true }))
.then(_ => t.fail('the store should have been locked'))
.catch(err => t.ok(err, 'store is locked'))
])
.then(_ => t.end())
.catch(t.end)
})
if (preserveSymlinks) {
test('packages should find the plugins they use when symlinks are preserved', t => {
prepare()
install([local('pkg-that-uses-plugins'), local('plugin-example')], { quiet: true, save: true })
.then(_ => {
const result = spawnSync('pkg-that-uses-plugins', [], {
env: extendPathWithLocalBin()
})
t.equal(result.stdout.toString(), 'plugin-example\n', 'package executable have found its plugin')
t.equal(result.status, 0, 'executable exited with success')
t.end()
})
.catch(t.end)
})
}
test('run js bin file', t => {
prepare()
install([local('hello-world-js-bin')], { quiet: true, save: true })
.then(_ => {
const result = spawnSync('hello-world-js-bin', [], {
env: extendPathWithLocalBin()
})
t.equal(result.stdout.toString(), 'Hello world!\n', 'package executable printed its message')
t.equal(result.status, 0, 'executable exited with success')
t.end()
})
.catch(t.end)
})
const pnpmBin = join(__dirname, '../bin/pnpm.js')
test('installation via the CLI', t => {
prepare()
const result = spawnSync('node', [pnpmBin, 'install', 'rimraf@2.5.1'])
t.equal(result.status, 0, 'install successful')
const rimraf = require(join(process.cwd(), 'node_modules', 'rimraf'))
t.ok(typeof rimraf === 'function', 'rimraf() is available')
isExecutable(t, join(process.cwd(), 'node_modules', '.bin', 'rimraf'))
t.end()
})
test('pass through to npm CLI for commands that are not supported by npm', t => {
const result = spawnSync('node', [pnpmBin, 'config', 'get', 'user-agent'])
t.equal(result.status, 0, 'command was successfull')
t.ok(result.stdout.toString().indexOf('npm/') !== -1, 'command returned correct result')
t.end()
})
test('postinstall is executed after installation', t => {
prepare({
scripts: {
postinstall: 'echo "Hello world!"'
}
})
const result = spawnSync('node', [pnpmBin, 'install', 'is-negative'])
t.equal(result.status, 0, 'installation was successfull')
t.ok(result.stdout.toString().indexOf('Hello world!') !== -1, 'postinstall script was executed')
t.end()
})
test('prepublish is executed after installation', t => {
prepare({
scripts: {
prepublish: 'echo "Hello world!"'
}
})
const result = spawnSync('node', [pnpmBin, 'install', 'is-negative'])
t.equal(result.status, 0, 'installation was successfull')
t.ok(result.stdout.toString().indexOf('Hello world!') !== -1, 'prepublish script was executed')
t.end()
})
test('global installation', t => {
install(['is-positive'], {quiet: true, globalPath, global: true})
.then(_ => {
const isPositive = require(join(globalPath, 'node_modules', 'is-positive'))
t.ok(typeof isPositive === 'function', 'isPositive() is available')
t.end()
})
.catch(t.end)
})
test('relative link', t => {
prepare()
const tmpDir = path.resolve(__dirname, '..', '.tmp')
const linkedPkgName = 'hello-world-js-bin'
const linkedPkgDirName = linkedPkgName + Math.random().toString()
const linkedPkgPath = path.resolve(tmpDir, linkedPkgDirName)
ncp(pathToLocalPkg(linkedPkgName), linkedPkgPath)
.then(_ => link([`../${linkedPkgDirName}`], { quiet: true }))
.then(_ => {
isExecutable(t, join(process.cwd(), 'node_modules', '.bin', 'hello-world-js-bin'))
t.end()
})
.catch(t.end)
})
test('global link', t => {
const tmpDir = path.resolve(__dirname, '..', '.tmp')
mkdirp.sync(tmpDir)
const linkedPkgName = 'hello-world-js-bin'
const linkedPkgPath = path.resolve(tmpDir, linkedPkgName + Math.random().toString())
ncp(pathToLocalPkg(linkedPkgName), linkedPkgPath)
.then(_ => {
process.chdir(linkedPkgPath)
return link([], { globalPath, quiet: true })
})
.then(_ => {
prepare()
return link([linkedPkgName], { globalPath, quiet: true })
})
.then(_ => {
isExecutable(t, join(process.cwd(), 'node_modules', '.bin', 'hello-world-js-bin'))
t.end()
})
.catch(t.end)
})
function extendPathWithLocalBin () {
return {
PATH: [
join(process.cwd(), 'node_modules', '.bin'),
process.env.PATH
].join(delimiter)
}
}
function pathToLocalPkg (pkgName) {
return join(__dirname, 'packages', pkgName)
}
function local (pkgName) {
return `file:${pathToLocalPkg(pkgName)}`
}
function exists (path) {
try {
return fs.statSync(path)
} catch (err) {
if (err.code !== 'ENOENT') throw err
}
}
function existsSymlink (path) {
try {
return fs.lstatSync(path)
} catch (err) {
if (err.code !== 'ENOENT') throw err
}
}