Files
pnpm/packages/supi/test/install/lifecycleScripts.ts
2018-05-11 22:09:36 +03:00

278 lines
9.7 KiB
TypeScript

import loadJsonFile = require('load-json-file')
import path = require('path')
import exists = require('path-exists')
import PATH = require('path-name')
import rimraf = require('rimraf-then')
import sinon = require('sinon')
import {
install,
installPkgs,
LifecycleLog,
} from 'supi'
import tape = require('tape')
import promisifyTape from 'tape-promise'
import {
prepare,
testDefaults,
} from '../utils'
const pkgRoot = path.join(__dirname, '..', '..')
const pnpmPkg = loadJsonFile.sync(path.join(pkgRoot, 'package.json'))
const test = promisifyTape(tape)
test('run pre/postinstall scripts', async (t: tape.Test) => {
const project = prepare(t)
await installPkgs(['pre-and-postinstall-scripts-example'], await testDefaults({saveDev: true}))
{
t.notOk(await exists('node_modules/pre-and-postinstall-scripts-example/generated-by-prepare.js'))
t.ok(await exists('node_modules/pre-and-postinstall-scripts-example/generated-by-preinstall.js'))
const generatedByPreinstall = project.requireModule('pre-and-postinstall-scripts-example/generated-by-preinstall')
t.ok(typeof generatedByPreinstall === 'function', 'generatedByPreinstall() is available')
const generatedByPostinstall = project.requireModule('pre-and-postinstall-scripts-example/generated-by-postinstall')
t.ok(typeof generatedByPostinstall === 'function', 'generatedByPostinstall() is available')
}
await rimraf('node_modules')
// testing that the packages are not installed even though they are in shrinkwrap
// and that their scripts are not tried to be executed
await install(await testDefaults({production: true}))
{
const generatedByPreinstall = project.requireModule('pre-and-postinstall-scripts-example/generated-by-preinstall')
t.ok(typeof generatedByPreinstall === 'function', 'generatedByPreinstall() is not available')
const generatedByPostinstall = project.requireModule('pre-and-postinstall-scripts-example/generated-by-postinstall')
t.ok(typeof generatedByPostinstall === 'function', 'generatedByPostinstall() is not available')
}
const shr = await project.loadShrinkwrap()
t.ok(shr.packages['/pre-and-postinstall-scripts-example/1.0.0'].requiresBuild, 'requiresBuild: true added to shrinkwrap')
})
test('testing that the bins are linked when the package with the bins was already in node_modules', async (t: tape.Test) => {
const project = prepare(t)
await installPkgs(['hello-world-js-bin'], await testDefaults())
await installPkgs(['pre-and-postinstall-scripts-example'], await testDefaults({saveDev: true}))
const generatedByPreinstall = project.requireModule('pre-and-postinstall-scripts-example/generated-by-preinstall')
t.ok(typeof generatedByPreinstall === 'function', 'generatedByPreinstall() is available')
const generatedByPostinstall = project.requireModule('pre-and-postinstall-scripts-example/generated-by-postinstall')
t.ok(typeof generatedByPostinstall === 'function', 'generatedByPostinstall() is available')
})
test('run install scripts', async (t: tape.Test) => {
const project = prepare(t)
await installPkgs(['install-script-example'], await testDefaults())
const generatedByInstall = project.requireModule('install-script-example/generated-by-install')
t.ok(typeof generatedByInstall === 'function', 'generatedByInstall() is available')
})
test('run install scripts in the current project', async (t: tape.Test) => {
const project = prepare(t, {
scripts: {
install: `node -e "process.stdout.write('install')" | json-append output.json`,
postinstall: `node -e "process.stdout.write('postinstall')" | json-append output.json`,
preinstall: `node -e "process.stdout.write('preinstall')" | json-append output.json`,
},
})
await installPkgs(['json-append@1.1.1'], await testDefaults())
await install(await testDefaults())
const output = await loadJsonFile('output.json')
t.deepEqual(output, ['preinstall', 'install', 'postinstall'])
})
test('run install scripts in the current project when its name is different than its directory', async (t: tape.Test) => {
const project = prepare(t, {
name: 'different-name',
scripts: {
install: `node -e "process.stdout.write('install')" | json-append output.json`,
postinstall: `node -e "process.stdout.write('postinstall')" | json-append output.json`,
preinstall: `node -e "process.stdout.write('preinstall')" | json-append output.json`,
},
})
await installPkgs(['json-append@1.1.1'], await testDefaults())
await install(await testDefaults())
const output = await loadJsonFile('output.json')
t.deepEqual(output, ['preinstall', 'install', 'postinstall'])
})
test('do not run install scripts if unsafePerm is false', async (t: tape.Test) => {
const project = prepare(t, {
name: 'different-name',
scripts: {
install: `node -e "process.stdout.write('install')" | json-append output.json`,
postinstall: `node -e "process.stdout.write('postinstall')" | json-append output.json`,
preinstall: `node -e "process.stdout.write('preinstall')" | json-append output.json`,
},
})
const opts = await testDefaults({ unsafePerm: false })
await installPkgs(['json-append@1.1.1'], opts)
await install(opts)
const outputExists = await exists('output.json')
t.false(outputExists, 'no output expected as install scripts should not run')
})
test('installation fails if lifecycle script fails', async (t: tape.Test) => {
const project = prepare(t, {
scripts: {
preinstall: 'exit 1',
},
})
try {
await install(await testDefaults())
t.fail('should have failed')
} catch (err) {
t.equal(err.code, 'ELIFECYCLE', 'failed with correct error code')
}
})
// TODO: unskip
// For some reason this fails on CI environments
// tslint:disable-next-line:no-string-literal
test['skip']('creates env for scripts', async (t: tape.Test) => {
const project = prepare(t, {
scripts: {
install: `node -e "process.stdout.write(process.env.INIT_CWD)" | json-append output.json`,
},
})
await installPkgs(['json-append@1.1.1'], await testDefaults())
await install(await testDefaults())
const output = await loadJsonFile('output.json')
t.deepEqual(output, [process.cwd()])
})
test('INIT_CWD is set correctly', async (t: tape.Test) => {
const project = prepare(t)
await installPkgs(['write-lifecycle-env'], await testDefaults())
const childEnv = await loadJsonFile(path.resolve('node_modules', 'write-lifecycle-env', 'env.json'))
t.equal(childEnv.INIT_CWD, process.cwd())
})
// TODO: duplicate this test to @pnpm/lifecycle
test("reports child's output", async (t: tape.Test) => {
const project = prepare(t)
const reporter = sinon.spy()
await installPkgs(['count-to-10'], await testDefaults({reporter}))
t.ok(reporter.calledWithMatch({
depPath: 'localhost+4873/count-to-10/1.0.0',
level: 'debug',
name: 'pnpm:lifecycle',
script: 'node postinstall',
stage: 'postinstall',
} as LifecycleLog))
t.ok(reporter.calledWithMatch({
depPath: 'localhost+4873/count-to-10/1.0.0',
level: 'debug',
line: '1',
name: 'pnpm:lifecycle',
stage: 'postinstall',
} as LifecycleLog))
t.ok(reporter.calledWithMatch({
depPath: 'localhost+4873/count-to-10/1.0.0',
level: 'debug',
line: '2',
name: 'pnpm:lifecycle',
stage: 'postinstall',
} as LifecycleLog))
t.ok(reporter.calledWithMatch({
depPath: 'localhost+4873/count-to-10/1.0.0',
level: 'error',
line: '6',
name: 'pnpm:lifecycle',
stage: 'postinstall',
} as LifecycleLog))
t.ok(reporter.calledWithMatch({
depPath: 'localhost+4873/count-to-10/1.0.0',
exitCode: 0,
level: 'debug',
name: 'pnpm:lifecycle',
stage: 'postinstall',
} as LifecycleLog))
})
test("reports child's close event", async (t: tape.Test) => {
const project = prepare(t)
const reporter = sinon.spy()
try {
await installPkgs(['failing-postinstall'], await testDefaults({reporter}))
t.fail()
} catch (err) {
t.ok(reporter.calledWithMatch({
depPath: 'localhost+4873/failing-postinstall/1.0.0',
exitCode: 1,
level: 'error',
name: 'pnpm:lifecycle',
stage: 'postinstall',
} as LifecycleLog))
}
})
test('lifecycle scripts have access to node-gyp', async (t: tape.Test) => {
const project = prepare(t)
// `npm test` adds node-gyp to the PATH
// it is removed here to test that pnpm adds it
const initialPath = process.env.PATH
if (typeof initialPath !== 'string') throw new Error('PATH is not defined')
process.env[PATH] = initialPath
.split(path.delimiter)
.filter((p: string) => !p.includes('node-gyp-bin') && !p.includes('npm'))
.join(path.delimiter)
await installPkgs(['drivelist@5.1.8'], await testDefaults())
process.env[PATH] = initialPath
t.pass("drivelist's install script has found node-gyp in PATH")
})
test('run lifecycle scripts of dependent packages after running scripts of their deps', async (t: tape.Test) => {
const project = prepare(t)
await installPkgs(['with-postinstall-a'], await testDefaults())
t.ok(+project.requireModule('.localhost+4873/with-postinstall-b/1.0.0/node_modules/with-postinstall-b/output.json')[0] < +project.requireModule('with-postinstall-a/output.json')[0])
})
test('run prepare script for git-hosted dependencies', async (t: tape.Test) => {
const project = prepare(t)
await installPkgs(['zkochan/install-scripts-example#prepare'], await testDefaults())
const scripts = project.requireModule('install-scripts-example-for-pnpm/output.json')
t.equal(scripts[0], 'preinstall')
t.equal(scripts[1], 'install')
t.equal(scripts[2], 'postinstall')
t.equal(scripts[3], 'prepare')
const shr = await project.loadShrinkwrap()
t.ok(shr.packages['github.com/zkochan/install-scripts-example/2de638b8b572cd1e87b74f4540754145fb2c0ebb'].prepare === true, 'prepare field added to shrinkwrap.yaml')
})