import 'sepia' ; import tape = require ( 'tape' ) ; import promisifyTape from 'tape-promise' ; const test = promisifyTape ( tape ) ; import caw = require ( 'caw' ) ; import crossSpawn = require ( 'cross-spawn' ) ; import fs = require ( 'mz/fs' ) ; import path = require ( 'path' ) ; import semver = require ( 'semver' ) ; const spawnSync = crossSpawn . sync ; import isCI = require ( 'is-ci' ) ; import loadJsonFile = require ( 'load-json-file' ) ; import readPkg = require ( 'read-pkg' ) ; import rimraf = require ( 'rimraf-then' ) ; import { addDistTag , prepare , testDefaults , } from '../utils' ; const basicPackageJson = loadJsonFile . sync ( path . join ( __dirname , '../utils/simple-package.json' ) ) ; import deepRequireCwd = require ( 'deep-require-cwd' ) ; import isWindows = require ( 'is-windows' ) ; import exists = require ( 'path-exists' ) ; import sinon = require ( 'sinon' ) ; import { install , installPkgs , uninstall } from 'supi' ; import { PackageJsonLog , ProgressLog , RootLog , StageLog , StatsLog , } from 'supi' ; import writeJsonFile = require ( 'write-json-file' ) ; const IS_WINDOWS = isWindows ( ) ; if ( ! caw ( ) && ! IS_WINDOWS ) { process . env . VCR_MODE = 'cache' ; } test ( 'small with dependencies (rimraf)' , async ( t ) => { const project = prepare ( t ) ; await installPkgs ( [ 'rimraf@2.5.1' ] , await testDefaults ( ) ) ; const m = project . requireModule ( 'rimraf' ) ; t . ok ( typeof m === 'function' , 'rimraf() is available' ) ; await project . isExecutable ( '.bin/rimraf' ) ; } ) ; test ( 'spec not specified in package.json.dependencies' , async ( t ) => { const project = prepare ( t , { dependencies : { 'is-positive' : '' , } , } ) ; await install ( await testDefaults ( ) ) ; const shr = await project . loadShrinkwrap ( ) ; t . ok ( shr . specifiers [ 'is-positive' ] === '' , 'spec saved properly in shrinkwrap.yaml' ) ; } ) ; test ( 'ignoring some files in the dependency' , async ( t ) => { const project = prepare ( t ) ; const ignoreFile = ( filename ) => filename === 'readme.md' ; await installPkgs ( [ 'is-positive@1.0.0' ] , await testDefaults ( { } , { } , { ignoreFile } ) ) ; t . ok ( await exists ( path . resolve ( 'node_modules' , 'is-positive' , 'package.json' ) ) , 'package.json was not ignored' ) ; t . notOk ( await exists ( path . resolve ( 'node_modules' , 'is-positive' , 'readme.md' ) ) , 'readme.md was ignored' ) ; } ) ; test ( 'no dependencies (lodash)' , async ( t ) => { const project = prepare ( t ) ; const reporter = sinon . spy ( ) ; await addDistTag ( 'lodash' , '4.1.0' , 'latest' ) ; await installPkgs ( [ 'lodash@4.0.0' ] , await testDefaults ( { reporter } ) ) ; t . ok ( reporter . calledWithMatch ( { initial : { name : 'project' , version : '0.0.0' } , level : 'debug' , name : 'pnpm:package-json' , } as PackageJsonLog ) , 'initial package.json logged' ) ; t . ok ( reporter . calledWithMatch ( { level : 'debug' , message : 'resolution_started' , name : 'pnpm:stage' , } as StageLog ) , 'resolution stage start logged' ) ; t . ok ( reporter . calledWithMatch ( { level : 'debug' , message : 'resolution_done' , name : 'pnpm:stage' , } as StageLog ) , 'resolution stage done logged' ) ; t . ok ( reporter . calledWithMatch ( { level : 'debug' , message : 'importing_started' , name : 'pnpm:stage' , } as StageLog ) , 'importing stage start logged' ) ; t . ok ( reporter . calledWithMatch ( { level : 'debug' , message : 'importing_done' , name : 'pnpm:stage' , } as StageLog ) , 'importing stage done logged' ) ; t . ok ( reporter . calledWithMatch ( { added : 1 , level : 'debug' , name : 'pnpm:stats' , } as StatsLog ) , 'added stat' ) ; t . ok ( reporter . calledWithMatch ( { level : 'debug' , name : 'pnpm:stats' , removed : 0 , } as StatsLog ) , 'removed stat' ) ; t . ok ( reporter . calledWithMatch ( { added : { dependencyType : 'prod' , latest : '4.1.0' , name : 'lodash' , realName : 'lodash' , version : '4.0.0' , } , level : 'info' , name : 'pnpm:root' , } as RootLog ) , 'added to root' ) ; t . ok ( reporter . calledWithMatch ( { level : 'debug' , name : 'pnpm:package-json' , updated : { dependencies : { lodash : '^4.0.0' , } , name : 'project' , version : '0.0.0' , } , } as PackageJsonLog ) , 'updated package.json logged' ) ; const m = project . requireModule ( 'lodash' ) ; t . ok ( typeof m === 'function' , '_ is available' ) ; t . ok ( typeof m . clone === 'function' , '_.clone is available' ) ; } ) ; test ( 'scoped modules without version spec (@rstacruz/tap-spec)' , async ( t ) => { const project = prepare ( t ) ; await installPkgs ( [ '@rstacruz/tap-spec' ] , await testDefaults ( ) ) ; const m = project . requireModule ( '@rstacruz/tap-spec' ) ; t . ok ( typeof m === 'function' , 'tap-spec is available' ) ; } ) ; test ( 'scoped package with custom registry' , async ( t ) => { const project = prepare ( t ) ; await installPkgs ( [ '@scoped/peer' ] , await testDefaults ( { rawNpmConfig : { '@scoped:registry' : 'http://localhost:4873/' , } , registry : 'http://localhost:9999/' , } ) ) ; const m = project . requireModule ( '@scoped/peer/package.json' ) ; t . ok ( m , 'is available' ) ; } ) ; test ( 'modules without version spec, with custom tag config' , async ( t ) => { const project = prepare ( t ) ; const tag = 'beta' ; await addDistTag ( 'dep-of-pkg-with-1-dep' , '100.1.0' , 'latest' ) ; await addDistTag ( 'dep-of-pkg-with-1-dep' , '100.0.0' , tag ) ; await installPkgs ( [ 'dep-of-pkg-with-1-dep' ] , await testDefaults ( { tag } ) ) ; await project . storeHas ( 'dep-of-pkg-with-1-dep' , '100.0.0' ) ; } ) ; test ( 'installing a package by specifying a specific dist-tag' , async ( t ) => { const project = prepare ( t ) ; await addDistTag ( 'dep-of-pkg-with-1-dep' , '100.1.0' , 'latest' ) ; await addDistTag ( 'dep-of-pkg-with-1-dep' , '100.0.0' , 'beta' ) ; await installPkgs ( [ 'dep-of-pkg-with-1-dep@beta' ] , await testDefaults ( ) ) ; await project . storeHas ( 'dep-of-pkg-with-1-dep' , '100.0.0' ) ; } ) ; test ( 'update a package when installing with a dist-tag' , async ( t ) => { const project = prepare ( t ) ; await addDistTag ( 'dep-of-pkg-with-1-dep' , '100.0.0' , 'latest' ) ; await addDistTag ( 'dep-of-pkg-with-1-dep' , '100.1.0' , 'beta' ) ; await installPkgs ( [ 'dep-of-pkg-with-1-dep' ] , await testDefaults ( { saveDev : true } ) ) ; const reporter = sinon . spy ( ) ; await installPkgs ( [ 'dep-of-pkg-with-1-dep@beta' ] , await testDefaults ( { saveDev : true , reporter } ) ) ; t . ok ( reporter . calledWithMatch ( { level : 'info' , name : 'pnpm:root' , removed : { dependencyType : 'dev' , name : 'dep-of-pkg-with-1-dep' , version : '100.0.0' , } , } as RootLog ) , 'reported old version removed from the root' ) ; t . ok ( reporter . calledWithMatch ( { added : { dependencyType : 'dev' , name : 'dep-of-pkg-with-1-dep' , version : '100.1.0' , } , level : 'info' , name : 'pnpm:root' , } as RootLog ) , 'reported new version added to the root' ) ; await project . has ( 'dep-of-pkg-with-1-dep' ) ; await project . storeHas ( 'dep-of-pkg-with-1-dep' , '100.1.0' ) ; const pkg = await readPkg ( ) ; t . equal ( pkg . devDependencies [ 'dep-of-pkg-with-1-dep' ] , '^100.1.0' ) ; } ) ; test ( 'scoped modules with versions (@rstacruz/tap-spec@4.1.1)' , async ( t ) => { const project = prepare ( t ) ; await installPkgs ( [ '@rstacruz/tap-spec@4.1.1' ] , await testDefaults ( ) ) ; const m = project . requireModule ( '@rstacruz/tap-spec' ) ; t . ok ( typeof m === 'function' , 'tap-spec is available' ) ; } ) ; test ( 'scoped modules (@rstacruz/tap-spec@*)' , async ( t ) => { const project = prepare ( t ) ; await installPkgs ( [ '@rstacruz/tap-spec@*' ] , await testDefaults ( ) ) ; const m = project . requireModule ( '@rstacruz/tap-spec' ) ; t . ok ( typeof m === 'function' , 'tap-spec is available' ) ; } ) ; test ( 'multiple scoped modules (@rstacruz/...)' , async ( t ) => { const project = prepare ( t ) ; await installPkgs ( [ '@rstacruz/tap-spec@*' , '@rstacruz/travis-encrypt@*' ] , await testDefaults ( ) ) ; t . equal ( typeof project . requireModule ( '@rstacruz/tap-spec' ) , 'function' , 'tap-spec is available' ) ; t . equal ( typeof project . requireModule ( '@rstacruz/travis-encrypt' ) , 'function' , 'travis-encrypt is available' ) ; } ) ; test ( 'nested scoped modules (test-pnpm-issue219 -> @zkochan/test-pnpm-issue219)' , async ( t ) => { const project = prepare ( t ) ; await installPkgs ( [ 'test-pnpm-issue219@1.0.2' ] , await testDefaults ( ) ) ; const m = project . requireModule ( 'test-pnpm-issue219' ) ; t . ok ( m === 'test-pnpm-issue219,@zkochan/test-pnpm-issue219' , 'nested scoped package is available' ) ; } ) ; test ( 'idempotency (rimraf)' , async ( t ) => { const project = prepare ( t ) ; const reporter = sinon . spy ( ) ; const opts = await testDefaults ( { reporter } ) ; await installPkgs ( [ 'rimraf@2.5.1' ] , opts ) ; t . ok ( reporter . calledWithMatch ( { added : { dependencyType : 'prod' , name : 'rimraf' , version : '2.5.1' , } , level : 'info' , name : 'pnpm:root' , } as RootLog ) , 'reported that rimraf added to the root' ) ; reporter . resetHistory ( ) ; await installPkgs ( [ 'rimraf@2.5.1' ] , opts ) ; t . notOk ( reporter . calledWithMatch ( { added : { dependencyType : 'prod' , name : 'rimraf' , version : '2.5.1' , } , level : 'info' , name : 'pnpm:root' , } as RootLog ) , 'did not reported that rimraf was added because it was already there' ) ; const m = project . requireModule ( 'rimraf' ) ; t . ok ( typeof m === 'function' , 'rimraf is available' ) ; } ) ; test ( 'reporting adding root package' , async ( t ) => { const project = prepare ( t ) ; await installPkgs ( [ 'magic-hook@2.0.0' ] , await testDefaults ( ) ) ; await project . storeHas ( 'flatten' , '1.0.2' ) ; const reporter = sinon . spy ( ) ; await installPkgs ( [ 'flatten@1.0.2' ] , await testDefaults ( { reporter } ) ) ; t . ok ( reporter . calledWithMatch ( { added : { dependencyType : 'prod' , name : 'flatten' , version : '1.0.2' , } , level : 'info' , name : 'pnpm:root' , } as RootLog ) , 'reported that flatten added to the root' ) ; } ) ; test ( 'overwriting (magic-hook@2.0.0 and @0.1.0)' , async ( t ) => { const project = prepare ( t ) ; await installPkgs ( [ 'magic-hook@2.0.0' ] , await testDefaults ( ) ) ; await project . storeHas ( 'flatten' , '1.0.2' ) ; await installPkgs ( [ 'magic-hook@0.1.0' ] , await testDefaults ( ) ) ; await project . storeHas ( 'flatten' , '1.0.2' ) ; const m = project . requireModule ( 'magic-hook/package.json' ) ; t . ok ( m . version === '0.1.0' , 'magic-hook is 0.1.0' ) ; } ) ; test ( 'overwriting (is-positive@3.0.0 with is-positive@latest)' , async ( t ) => { const project = prepare ( t ) ; await installPkgs ( [ 'is-positive@3.0.0' ] , await testDefaults ( { save : true } ) ) ; await project . storeHas ( 'is-positive' , '3.0.0' ) ; await installPkgs ( [ 'is-positive@latest' ] , await testDefaults ( { save : true } ) ) ; await project . storeHas ( 'is-positive' , '3.1.0' ) ; } ) ; test ( 'forcing' , async ( t ) => { const project = prepare ( t ) ; await installPkgs ( [ 'magic-hook@2.0.0' ] , await testDefaults ( ) ) ; const distPath = path . resolve ( 'node_modules' , 'magic-hook' , 'dist' ) ; await rimraf ( distPath ) ; await installPkgs ( [ 'magic-hook@2.0.0' ] , await testDefaults ( { force : true } ) ) ; const distPathExists = await exists ( distPath ) ; t . ok ( distPathExists , 'magic-hook@2.0.0 dist folder reinstalled' ) ; } ) ; test ( 'argumentless forcing' , async ( t ) => { const project = prepare ( t ) ; await installPkgs ( [ 'magic-hook@2.0.0' ] , await testDefaults ( ) ) ; const distPath = path . resolve ( 'node_modules' , 'magic-hook' , 'dist' ) ; await rimraf ( distPath ) ; await install ( await testDefaults ( { force : true } ) ) ; const distPathExists = await exists ( distPath ) ; t . ok ( distPathExists , 'magic-hook@2.0.0 dist folder reinstalled' ) ; } ) ; test ( 'no forcing' , async ( t ) => { const project = prepare ( t ) ; await installPkgs ( [ 'magic-hook@2.0.0' ] , await testDefaults ( ) ) ; const distPath = path . resolve ( 'node_modules' , 'magic-hook' , 'dist' ) ; await rimraf ( distPath ) ; await installPkgs ( [ 'magic-hook@2.0.0' ] , await testDefaults ( ) ) ; const distPathExists = await exists ( distPath ) ; t . notOk ( distPathExists , 'magic-hook@2.0.0 dist folder not reinstalled' ) ; } ) ; test ( 'refetch package to store if it has been modified' , async ( t ) => { const project = prepare ( t ) ; await installPkgs ( [ 'magic-hook@2.0.0' ] , await testDefaults ( ) ) ; const distPathInStore = await project . resolve ( 'magic-hook' , '2.0.0' , 'dist' ) ; await rimraf ( distPathInStore ) ; await rimraf ( 'node_modules' ) ; const distPath = path . resolve ( 'node_modules' , 'magic-hook' , 'dist' ) ; await installPkgs ( [ 'magic-hook@2.0.0' ] , await testDefaults ( ) ) ; const distPathExists = await exists ( distPath ) ; t . ok ( distPathExists , 'magic-hook@2.0.0 dist folder reinstalled' ) ; } ) ; test ( "don't refetch package to store if it has been modified and verify-store-integrity = false" , async ( t ) => { const project = prepare ( t ) ; const opts = await testDefaults ( { verifyStoreIntegrity : false } ) ; await installPkgs ( [ 'magic-hook@2.0.0' ] , opts ) ; await writeJsonFile ( path . join ( await project . getStorePath ( ) , 'localhost+4873' , 'magic-hook' , '2.0.0' , 'node_modules' , 'magic-hook' , 'package.json' ) , { } ) ; await rimraf ( 'node_modules' ) ; await installPkgs ( [ 'magic-hook@2.0.0' ] , opts ) ; t . deepEqual ( project . requireModule ( 'magic-hook/package.json' ) , { } , 'package.json not refetched even though it was mutated' ) ; } ) ; test [ 'skip' ] ( 'relink package to project if the dependency is not linked from store' , async ( t ) => { const project = prepare ( t ) ; await installPkgs ( [ 'magic-hook@2.0.0' ] , await testDefaults ( { save : true , saveExact : true } ) ) ; const pkgJsonPath = path . resolve ( 'node_modules' , 'magic-hook' , 'package.json' ) ; async function getInode ( ) { return ( await fs . stat ( pkgJsonPath ) ) . ino ; } const storeInode = await getInode ( ) ; const pkgJson = await fs . readFile ( pkgJsonPath , 'utf8' ) ; await rimraf ( pkgJsonPath ) ; await fs . writeFile ( pkgJsonPath , pkgJson , 'utf8' ) ; t . ok ( storeInode !== await getInode ( ) , 'package.json inode changed' ) ; await install ( await testDefaults ( { repeatInstallDepth : 0 } ) ) ; t . ok ( storeInode === await getInode ( ) , 'package.json inode matches the one that is in store' ) ; } ) ; test ( 'circular deps' , async ( t ) => { const project = prepare ( t ) ; await installPkgs ( [ 'circular-deps-1-of-2' ] , await testDefaults ( ) ) ; const m = project . requireModule ( 'circular-deps-1-of-2/mirror' ) ; t . equal ( m ( ) , 'circular-deps-1-of-2' , 'circular dependencies can access each other' ) ; t . notOk ( await exists ( path . join ( 'node_modules' , 'circular-deps-1-of-2' , 'node_modules' , 'circular-deps-2-of-2' , 'node_modules' , 'circular-deps-1-of-2' ) ) , 'circular dependency is avoided' ) ; } ) ; test ( 'concurrent circular deps' , async ( t ) => { await addDistTag ( 'es5-ext' , '0.10.31' , 'latest' ) ; await addDistTag ( 'es6-iterator' , '2.0.1' , 'latest' ) ; const project = prepare ( t ) ; await installPkgs ( [ 'es6-iterator@2.0.0' ] , await testDefaults ( ) ) ; const m = project . requireModule ( 'es6-iterator' ) ; t . ok ( m , 'es6-iterator is installed' ) ; t . ok ( await exists ( path . join ( 'node_modules' , '.localhost+4873' , 'es6-iterator' , '2.0.0' , 'node_modules' , 'es5-ext' ) ) ) ; t . ok ( await exists ( path . join ( 'node_modules' , '.localhost+4873' , 'es6-iterator' , '2.0.1' , 'node_modules' , 'es5-ext' ) ) ) ; t . ok ( await exists ( path . join ( 'node_modules' , '.localhost+4873' , 'es5-ext' , '0.10.31' , 'node_modules' , 'es6-iterator' ) ) ) ; t . ok ( await exists ( path . join ( 'node_modules' , '.localhost+4873' , 'es5-ext' , '0.10.31' , 'node_modules' , 'es6-symbol' ) ) ) ; } ) ; test ( 'concurrent installation of the same packages' , async ( t ) => { const project = prepare ( t ) ; await installPkgs ( [ 'babel-core@6.21.0' ] , await testDefaults ( ) ) ; const m = project . requireModule ( 'babel-core' ) ; t . ok ( m , 'babel-core is installed' ) ; } ) ; test ( 'big with dependencies and circular deps (babel-preset-2015)' , async ( t ) => { const project = prepare ( t ) ; await installPkgs ( [ 'babel-preset-es2015@6.3.13' ] , await testDefaults ( ) ) ; const m = project . requireModule ( 'babel-preset-es2015' ) ; t . ok ( typeof m === 'object' , 'babel-preset-es2015 is available' ) ; } ) ; test ( 'bundledDependencies (pkg-with-bundled-dependencies@1.0.0)' , async ( t ) => { const project = prepare ( t ) ; await installPkgs ( [ 'pkg-with-bundled-dependencies@1.0.0' ] , await testDefaults ( ) ) ; await project . isExecutable ( 'pkg-with-bundled-dependencies/node_modules/.bin/hello-world-js-bin' ) ; const shr = await project . loadShrinkwrap ( ) ; t . deepEqual ( shr . packages [ '/pkg-with-bundled-dependencies/1.0.0' ] . bundledDependencies , [ 'hello-world-js-bin' ] , 'bundledDependencies added to shrinkwrap.yaml' ) ; } ) ; test ( 'bundleDependencies (pkg-with-bundle-dependencies@1.0.0)' , async ( t ) => { const project = prepare ( t ) ; await installPkgs ( [ 'pkg-with-bundle-dependencies@1.0.0' ] , await testDefaults ( ) ) ; await project . isExecutable ( 'pkg-with-bundle-dependencies/node_modules/.bin/hello-world-js-bin' ) ; const shr = await project . loadShrinkwrap ( ) ; t . deepEqual ( shr . packages [ '/pkg-with-bundle-dependencies/1.0.0' ] . bundledDependencies , [ 'hello-world-js-bin' ] , 'bundledDependencies added to shrinkwrap.yaml' ) ; } ) ; test ( 'compiled modules (ursa@0.9.1)' , async ( t ) => { if ( ! isCI || IS_WINDOWS || semver . satisfies ( process . version , '>=7.0.0' ) ) { t . skip ( 'runs only on CI' ) ; return ; } const project = prepare ( t ) ; await installPkgs ( [ 'ursa@0.9.1' ] , await testDefaults ( ) ) ; const m = project . requireModule ( 'ursa' ) ; t . ok ( typeof m === 'object' , 'ursa() is available' ) ; } ) ; test ( 'shrinkwrap compatibility' , async ( t ) => { if ( semver . satisfies ( process . version , '4' ) ) { t . skip ( "don't run on Node.js 4" ) ; return ; } const project = prepare ( t , { dependencies : { rimraf : '*' } } ) ; await installPkgs ( [ 'rimraf@2.5.1' ] , await testDefaults ( ) ) ; 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' ) ; resolve ( ) ; } ) ; } ) ; } ) ; const wait = ( ms ) => new Promise ( ( resolve ) => setTimeout ( resolve , ms ) ) ; test ( 'support installing into the same store simultaneously' , async ( t ) => { const project = prepare ( t ) ; await Promise . all ( [ installPkgs ( [ 'pkg-that-installs-slowly' ] , await testDefaults ( ) ) , wait ( 500 ) . then ( async ( ) => { await project . storeHasNot ( 'pkg-that-installs-slowly' ) ; await installPkgs ( [ 'rimraf@2.5.1' ] , await testDefaults ( ) ) ; } ) . then ( async ( ) => { await project . has ( 'pkg-that-installs-slowly' ) ; await project . has ( 'rimraf' ) ; } ) . catch ( ( err ) => t . notOk ( err ) ) , ] ) ; } ) ; test ( 'support installing and uninstalling from the same store simultaneously' , async ( t ) => { const project = prepare ( t ) ; await Promise . all ( [ installPkgs ( [ 'pkg-that-installs-slowly' ] , await testDefaults ( ) ) , wait ( 500 ) . then ( async ( ) => { await project . storeHasNot ( 'pkg-that-installs-slowly' ) ; await uninstall ( [ 'rimraf@2.5.1' ] , await testDefaults ( ) ) ; } ) . then ( async ( ) => { await project . has ( 'pkg-that-installs-slowly' ) ; await project . hasNot ( 'rimraf' ) ; } ) . catch ( ( err ) => t . notOk ( err ) ) , ] ) ; } ) ; test ( 'top-level packages should find the plugins they use' , async ( t ) => { const project = prepare ( t , { scripts : { test : 'pkg-that-uses-plugins' , } , } ) ; await installPkgs ( [ 'pkg-that-uses-plugins' , 'plugin-example' ] , await testDefaults ( { save : true } ) ) ; const result = spawnSync ( 'npm' , [ 'test' ] ) ; t . ok ( result . stdout . toString ( ) . indexOf ( 'My plugin is plugin-example' ) !== - 1 , 'package executable have found its plugin' ) ; t . equal ( result . status , 0 , 'executable exited with success' ) ; } ) ; test ( 'not top-level packages should find the plugins they use' , async ( t ) => { const project = prepare ( t , { scripts : { test : 'standard' , } , } ) ; await installPkgs ( [ 'standard@8.6.0' ] , await testDefaults ( { save : true } ) ) ; const result = spawnSync ( 'npm' , [ 'test' ] ) ; t . equal ( result . status , 0 , 'standard exited with success' ) ; } ) ; test ( 'bin specified in the directories property linked to .bin folder' , async ( t ) => { const project = prepare ( t ) ; await installPkgs ( [ 'pkg-with-directories-bin' ] , await testDefaults ( ) ) ; await project . isExecutable ( '.bin/pkg-with-directories-bin' ) ; } ) ; test ( 'run js bin file' , async ( t ) => { const project = prepare ( t , { scripts : { test : 'hello-world-js-bin' , } , } ) ; await installPkgs ( [ 'hello-world-js-bin' ] , await testDefaults ( { save : true } ) ) ; const result = spawnSync ( 'npm' , [ 'test' ] ) ; t . ok ( result . stdout . toString ( ) . indexOf ( 'Hello world!' ) !== - 1 , 'package executable printed its message' ) ; t . equal ( result . status , 0 , 'executable exited with success' ) ; } ) ; test ( 'building native addons' , async ( t ) => { const project = prepare ( t ) ; await installPkgs ( [ 'runas@3.1.1' ] , await testDefaults ( ) ) ; t . ok ( await exists ( path . join ( 'node_modules' , 'runas' , 'build' ) ) , 'build folder created' ) ; } ) ; test ( 'should update subdep on second install' , async ( t ) => { const project = prepare ( t ) ; await addDistTag ( 'dep-of-pkg-with-1-dep' , '100.0.0' , 'latest' ) ; await installPkgs ( [ 'pkg-with-1-dep' ] , await testDefaults ( { save : true } ) ) ; await project . storeHas ( 'dep-of-pkg-with-1-dep' , '100.0.0' ) ; let shr = await project . loadShrinkwrap ( ) ; t . ok ( shr . packages [ '/dep-of-pkg-with-1-dep/100.0.0' ] , 'shrinkwrap has resolution for package' ) ; await addDistTag ( 'dep-of-pkg-with-1-dep' , '100.1.0' , 'latest' ) ; const reporter = sinon . spy ( ) ; await install ( await testDefaults ( { depth : 1 , update : true , reporter } ) ) ; t . ok ( reporter . calledWithMatch ( { added : 1 , level : 'debug' , name : 'pnpm:stats' , } as StatsLog ) , 'added stat' ) ; await project . storeHas ( 'dep-of-pkg-with-1-dep' , '100.1.0' ) ; shr = await project . loadShrinkwrap ( ) ; t . notOk ( shr . packages [ '/dep-of-pkg-with-1-dep/100.0.0' ] , "shrinkwrap doesn't have old dependency" ) ; t . ok ( shr . packages [ '/dep-of-pkg-with-1-dep/100.1.0' ] , 'shrinkwrap has new dependency' ) ; t . equal ( deepRequireCwd ( [ 'pkg-with-1-dep' , 'dep-of-pkg-with-1-dep' , './package.json' ] ) . version , '100.1.0' , 'updated in node_modules' ) ; } ) ; test ( 'should not update subdep when depth is smaller than depth of package' , async ( t ) => { const project = prepare ( t ) ; await addDistTag ( 'dep-of-pkg-with-1-dep' , '100.0.0' , 'latest' ) ; await installPkgs ( [ 'pkg-with-1-dep' ] , await testDefaults ( { save : true } ) ) ; await project . storeHas ( 'dep-of-pkg-with-1-dep' , '100.0.0' ) ; let shr = await project . loadShrinkwrap ( ) ; t . ok ( shr . packages [ '/dep-of-pkg-with-1-dep/100.0.0' ] , 'shrinkwrap has resolution for package' ) ; await addDistTag ( 'dep-of-pkg-with-1-dep' , '100.1.0' , 'latest' ) ; await install ( await testDefaults ( { depth : 0 , update : true } ) ) ; await project . storeHas ( 'dep-of-pkg-with-1-dep' , '100.0.0' ) ; shr = await project . loadShrinkwrap ( ) ; t . ok ( shr . packages [ '/dep-of-pkg-with-1-dep/100.0.0' ] , 'shrinkwrap has old dependency' ) ; t . notOk ( shr . packages [ '/dep-of-pkg-with-1-dep/100.1.0' ] , 'shrinkwrap has not the new dependency' ) ; t . equal ( deepRequireCwd ( [ 'pkg-with-1-dep' , 'dep-of-pkg-with-1-dep' , './package.json' ] ) . version , '100.0.0' , 'not updated in node_modules' ) ; } ) ; test ( 'should install dependency in second project' , async ( t ) => { const project1 = prepare ( t ) ; await installPkgs ( [ 'pkg-with-1-dep' ] , await testDefaults ( { save : true , store : '../store' } ) ) ; t . equal ( project1 . requireModule ( 'pkg-with-1-dep' ) ( ) . name , 'dep-of-pkg-with-1-dep' , 'can require in 1st pkg' ) ; const project2 = prepare ( t ) ; await installPkgs ( [ 'pkg-with-1-dep' ] , await testDefaults ( { save : true , store : '../store' } ) ) ; t . equal ( project2 . requireModule ( 'pkg-with-1-dep' ) ( ) . name , 'dep-of-pkg-with-1-dep' , 'can require in 2nd pkg' ) ; } ) ; test ( 'should throw error when trying to install using a different store then the previous one' , async ( t ) => { const project = prepare ( t ) ; await installPkgs ( [ 'rimraf@2.5.1' ] , await testDefaults ( { store : 'node_modules/.store1' } ) ) ; try { await installPkgs ( [ 'is-negative' ] , await testDefaults ( { store : 'node_modules/.store2' } ) ) ; t . fail ( 'installation should have failed' ) ; } catch ( err ) { t . equal ( err . code , 'UNEXPECTED_STORE' , 'failed with correct error code' ) ; } } ) ; test ( 'ignores drive case in store path' , async ( t ) => { if ( ! isWindows ( ) ) return ; const project = prepare ( t ) ; const storePathUpper = path . resolve ( 'node_modules/.store1' ) . toUpperCase ( ) ; const storePathLower = storePathUpper . toLowerCase ( ) ; await installPkgs ( [ 'rimraf@2.5.1' ] , await testDefaults ( { store : storePathUpper } ) ) ; await installPkgs ( [ 'is-negative' ] , await testDefaults ( { store : storePathLower } ) ) ; t . pass ( 'Install did not fail' ) ; } ) ; test ( 'should not throw error if using a different store after all the packages were uninstalled' , async ( t ) => { } ) ; test ( 'shrinkwrap locks npm dependencies' , async ( t ) => { const project = prepare ( t ) ; const reporter = sinon . spy ( ) ; await addDistTag ( 'dep-of-pkg-with-1-dep' , '100.0.0' , 'latest' ) ; await installPkgs ( [ 'pkg-with-1-dep' ] , await testDefaults ( { save : true , reporter } ) ) ; t . ok ( reporter . calledWithMatch ( { level : 'debug' , name : 'pnpm:progress' , pkgId : 'localhost+4873/pkg-with-1-dep/100.0.0' , status : 'resolving_content' , } as ProgressLog ) , 'logs that package is being resolved' ) ; t . ok ( reporter . calledWithMatch ( { level : 'debug' , pkgId : 'localhost+4873/pkg-with-1-dep/100.0.0' , status : 'fetched' , } as ProgressLog ) , 'logged that package was fetched from registry' ) ; await project . storeHas ( 'dep-of-pkg-with-1-dep' , '100.0.0' ) ; await addDistTag ( 'dep-of-pkg-with-1-dep' , '100.1.0' , 'latest' ) ; await rimraf ( 'node_modules' ) ; reporter . resetHistory ( ) ; await install ( await testDefaults ( { reporter } ) ) ; t . ok ( reporter . calledWithMatch ( { level : 'debug' , pkgId : 'localhost+4873/pkg-with-1-dep/100.0.0' , status : 'resolving_content' , } as ProgressLog ) , 'logs that package is being resolved' ) ; t . ok ( reporter . calledWithMatch ( { level : 'debug' , pkgId : 'localhost+4873/pkg-with-1-dep/100.0.0' , status : 'found_in_store' , } as ProgressLog ) , 'logged that package was found in store' ) ; const m = project . requireModule ( '.localhost+4873/pkg-with-1-dep/100.0.0/node_modules/dep-of-pkg-with-1-dep/package.json' ) ; t . equal ( m . version , '100.0.0' , 'dependency specified in shrinkwrap.yaml is installed' ) ; } ) ; test ( 'self-require should work' , async ( t ) => { const project = prepare ( t ) ; await installPkgs ( [ 'uses-pkg-with-self-usage' ] , await testDefaults ( ) ) ; t . ok ( project . requireModule ( 'uses-pkg-with-self-usage' ) ) ; } ) ; test ( 'install on project with lockfile and no node_modules' , async ( t ) => { const project = prepare ( t ) ; await installPkgs ( [ 'is-negative' ] , await testDefaults ( ) ) ; await rimraf ( 'node_modules' ) ; await installPkgs ( [ 'is-positive' ] , await testDefaults ( ) ) ; t . ok ( project . requireModule ( 'is-positive' ) , 'installed new dependency' ) ; await project . hasNot ( 'is-negative' ) ; } ) ; test ( 'install a dependency with * range' , async ( t ) => { const project = prepare ( t , { dependencies : { 'has-beta-only' : '*' , } , } ) ; const reporter = sinon . spy ( ) ; await install ( await testDefaults ( { reporter } ) ) ; await project . has ( 'has-beta-only' ) ; t . ok ( reporter . calledWithMatch ( { level : 'debug' , name : 'pnpm:package-json' , updated : { dependencies : { 'has-beta-only' : '*' , } , name : 'project' , version : '0.0.0' , } , } as PackageJsonLog ) , 'should log package-json updated even when package.json was not changed' ) ; } ) ; test ( 'create a package.json if there is none' , async ( t ) => { const project = prepare ( t ) ; await rimraf ( 'package.json' ) ; await installPkgs ( [ 'dep-of-pkg-with-1-dep@100.1.0' ] , await testDefaults ( ) ) ; t . deepEqual ( await readPkg ( { normalize : false } ) , { dependencies : { 'dep-of-pkg-with-1-dep' : '^100.1.0' , } , } , 'package.json created' ) ; } ) ;