mirror of
https://github.com/pnpm/pnpm.git
synced 2026-03-28 12:01:37 -04:00
250 lines
7.5 KiB
TypeScript
250 lines
7.5 KiB
TypeScript
import fs from 'fs'
|
|
import { createRequire } from 'module'
|
|
import path from 'path'
|
|
import { prependDirsToPath } from '@pnpm/env.path'
|
|
import { tempDir, prepare as prepareWithPkg } from '@pnpm/prepare'
|
|
import { jest } from '@jest/globals'
|
|
import spawn from 'cross-spawn'
|
|
import nock from 'nock'
|
|
|
|
const require = createRequire(import.meta.dirname)
|
|
const pnpmTarballPath = require.resolve('@pnpm/tgz-fixtures/tgz/pnpm-9.1.0.tgz')
|
|
|
|
const actualModule = await import('@pnpm/cli-meta')
|
|
jest.unstable_mockModule('@pnpm/cli-meta', () => {
|
|
return {
|
|
...actualModule,
|
|
packageManager: {
|
|
name: 'pnpm',
|
|
version: '9.0.0',
|
|
},
|
|
}
|
|
})
|
|
const { selfUpdate } = await import('@pnpm/tools.plugin-commands-self-updater')
|
|
|
|
afterEach(() => {
|
|
nock.cleanAll()
|
|
nock.disableNetConnect()
|
|
})
|
|
|
|
beforeEach(() => {
|
|
nock.enableNetConnect()
|
|
})
|
|
|
|
function prepare () {
|
|
const dir = tempDir(false)
|
|
return prepareOptions(dir)
|
|
}
|
|
|
|
function prepareOptions (dir: string) {
|
|
return {
|
|
argv: {
|
|
original: [],
|
|
},
|
|
cliOptions: {},
|
|
excludeLinksFromLockfile: false,
|
|
linkWorkspacePackages: true,
|
|
bail: true,
|
|
pnpmHomeDir: dir,
|
|
preferWorkspacePackages: true,
|
|
registries: {
|
|
default: 'https://registry.npmjs.org/',
|
|
},
|
|
rawLocalConfig: {},
|
|
sort: false,
|
|
rootProjectManifestDir: dir,
|
|
bin: dir,
|
|
workspaceConcurrency: 1,
|
|
extraEnv: {},
|
|
pnpmfile: '',
|
|
rawConfig: {},
|
|
cacheDir: path.join(dir, '.cache'),
|
|
virtualStoreDirMaxLength: process.platform === 'win32' ? 60 : 120,
|
|
dir,
|
|
managePackageManagerVersions: false,
|
|
}
|
|
}
|
|
|
|
function createMetadata (latest: string, registry: string, otherVersions: string[] = []) {
|
|
const versions = [...otherVersions, latest]
|
|
return {
|
|
name: 'pnpm',
|
|
'dist-tags': { latest },
|
|
versions: Object.fromEntries(versions.map((version) => [
|
|
version,
|
|
{
|
|
name: 'pnpm',
|
|
version,
|
|
dist: {
|
|
shasum: '217063ce3fcbf44f3051666f38b810f1ddefee4a',
|
|
tarball: `${registry}pnpm/-/pnpm-${version}.tgz`,
|
|
fileCount: 880,
|
|
integrity: 'sha512-Z/WHmRapKT5c8FnCOFPVcb6vT3U8cH9AyyK+1fsVeMaq07bEEHzLO6CzW+AD62IaFkcayDbIe+tT+dVLtGEnJA==',
|
|
},
|
|
},
|
|
])),
|
|
}
|
|
}
|
|
|
|
test('self-update', async () => {
|
|
const opts = prepare()
|
|
nock(opts.registries.default)
|
|
.get('/pnpm')
|
|
.reply(200, createMetadata('9.1.0', opts.registries.default))
|
|
nock(opts.registries.default)
|
|
.get('/pnpm/-/pnpm-9.1.0.tgz')
|
|
.replyWithFile(200, pnpmTarballPath)
|
|
|
|
await selfUpdate.handler(opts, [])
|
|
|
|
const pnpmPkgJson = JSON.parse(fs.readFileSync(path.join(opts.pnpmHomeDir, '.tools/pnpm/9.1.0/node_modules/pnpm/package.json'), 'utf8'))
|
|
expect(pnpmPkgJson.version).toBe('9.1.0')
|
|
|
|
const pnpmEnv = prependDirsToPath([opts.pnpmHomeDir])
|
|
const { status, stdout } = spawn.sync('pnpm', ['-v'], {
|
|
env: {
|
|
...process.env,
|
|
[pnpmEnv.name]: pnpmEnv.value,
|
|
},
|
|
})
|
|
expect(status).toBe(0)
|
|
expect(stdout.toString().trim()).toBe('9.1.0')
|
|
})
|
|
|
|
test('self-update by exact version', async () => {
|
|
const opts = prepare()
|
|
nock(opts.registries.default)
|
|
.get('/pnpm')
|
|
.reply(200, createMetadata('9.2.0', opts.registries.default, ['9.1.0']))
|
|
nock(opts.registries.default)
|
|
.get('/pnpm/-/pnpm-9.1.0.tgz')
|
|
.replyWithFile(200, pnpmTarballPath)
|
|
|
|
await selfUpdate.handler(opts, ['9.1.0'])
|
|
|
|
const pnpmPkgJson = JSON.parse(fs.readFileSync(path.join(opts.pnpmHomeDir, '.tools/pnpm/9.1.0/node_modules/pnpm/package.json'), 'utf8'))
|
|
expect(pnpmPkgJson.version).toBe('9.1.0')
|
|
|
|
const pnpmEnv = prependDirsToPath([opts.pnpmHomeDir])
|
|
const { status, stdout } = spawn.sync('pnpm', ['-v'], {
|
|
env: {
|
|
...process.env,
|
|
[pnpmEnv.name]: pnpmEnv.value,
|
|
},
|
|
})
|
|
expect(status).toBe(0)
|
|
expect(stdout.toString().trim()).toBe('9.1.0')
|
|
})
|
|
|
|
test('self-update does nothing when pnpm is up to date', async () => {
|
|
const opts = prepare()
|
|
nock(opts.registries.default)
|
|
.get('/pnpm')
|
|
.reply(200, createMetadata('9.0.0', opts.registries.default))
|
|
|
|
const output = await selfUpdate.handler(opts, [])
|
|
|
|
expect(output).toBe('The currently active pnpm v9.0.0 is already "latest" and doesn\'t need an update')
|
|
})
|
|
|
|
test('should update packageManager field when a newer pnpm version is available', async () => {
|
|
const opts = prepare()
|
|
const pkgJsonPath = path.join(opts.dir, 'package.json')
|
|
fs.writeFileSync(pkgJsonPath, JSON.stringify({
|
|
packageManager: 'pnpm@8.0.0',
|
|
}), 'utf8')
|
|
nock(opts.registries.default)
|
|
.get('/pnpm')
|
|
.reply(200, createMetadata('9.0.0', opts.registries.default))
|
|
|
|
const output = await selfUpdate.handler({
|
|
...opts,
|
|
managePackageManagerVersions: true,
|
|
wantedPackageManager: {
|
|
name: 'pnpm',
|
|
version: '8.0.0',
|
|
},
|
|
}, [])
|
|
|
|
expect(output).toBe('The current project has been updated to use pnpm v9.0.0')
|
|
expect(JSON.parse(fs.readFileSync(pkgJsonPath, 'utf8')).packageManager).toBe('pnpm@9.0.0')
|
|
})
|
|
|
|
test('should not update packageManager field when current version matches latest', async () => {
|
|
const opts = prepare()
|
|
const pkgJsonPath = path.join(opts.dir, 'package.json')
|
|
fs.writeFileSync(pkgJsonPath, JSON.stringify({
|
|
packageManager: 'pnpm@9.0.0',
|
|
}), 'utf8')
|
|
nock(opts.registries.default)
|
|
.get('/pnpm')
|
|
.reply(200, createMetadata('9.0.0', opts.registries.default))
|
|
|
|
const output = await selfUpdate.handler({
|
|
...opts,
|
|
managePackageManagerVersions: true,
|
|
wantedPackageManager: {
|
|
name: 'pnpm',
|
|
version: '9.0.0',
|
|
},
|
|
}, [])
|
|
|
|
expect(output).toBe('The current project is already set to use pnpm v9.0.0')
|
|
expect(JSON.parse(fs.readFileSync(pkgJsonPath, 'utf8')).packageManager).toBe('pnpm@9.0.0')
|
|
})
|
|
|
|
test('self-update links pnpm that is already present on the disk', async () => {
|
|
const opts = prepare()
|
|
nock(opts.registries.default)
|
|
.get('/pnpm')
|
|
.reply(200, createMetadata('9.2.0', opts.registries.default))
|
|
|
|
const baseDir = path.join(opts.pnpmHomeDir, '.tools/pnpm/9.2.0')
|
|
fs.mkdirSync(path.join(baseDir, 'bin'), { recursive: true })
|
|
const latestPnpmDir = path.join(baseDir, 'node_modules/pnpm')
|
|
fs.mkdirSync(latestPnpmDir, { recursive: true })
|
|
fs.writeFileSync(path.join(latestPnpmDir, 'package.json'), JSON.stringify({ name: 'pnpm', bin: 'bin.js' }), 'utf8')
|
|
fs.writeFileSync(path.join(latestPnpmDir, 'bin.js'), `#!/usr/bin/env node
|
|
console.log('9.2.0')`, 'utf8')
|
|
const output = await selfUpdate.handler(opts, [])
|
|
|
|
expect(output).toBe(`The latest version, v9.2.0, is already present on the system. It was activated by linking it from ${path.join(latestPnpmDir, '../..')}.`)
|
|
|
|
const pnpmEnv = prependDirsToPath([opts.pnpmHomeDir])
|
|
const { status, stdout } = spawn.sync('pnpm', ['-v'], {
|
|
env: {
|
|
...process.env,
|
|
[pnpmEnv.name]: pnpmEnv.value,
|
|
},
|
|
})
|
|
expect(status).toBe(0)
|
|
expect(stdout.toString().trim()).toBe('9.2.0')
|
|
})
|
|
|
|
test('self-update updates the packageManager field in package.json', async () => {
|
|
prepareWithPkg({
|
|
packageManager: 'pnpm@9.0.0',
|
|
})
|
|
const opts = {
|
|
...prepareOptions(process.cwd()),
|
|
managePackageManagerVersions: true,
|
|
wantedPackageManager: {
|
|
name: 'pnpm',
|
|
version: '9.0.0',
|
|
},
|
|
}
|
|
nock(opts.registries.default)
|
|
.get('/pnpm')
|
|
.reply(200, createMetadata('9.1.0', opts.registries.default))
|
|
nock(opts.registries.default)
|
|
.get('/pnpm/-/pnpm-9.1.0.tgz')
|
|
.replyWithFile(200, pnpmTarballPath)
|
|
|
|
const output = await selfUpdate.handler(opts, [])
|
|
|
|
expect(output).toBe('The current project has been updated to use pnpm v9.1.0')
|
|
|
|
const pkgJson = JSON.parse(fs.readFileSync(path.resolve('package.json'), 'utf8'))
|
|
expect(pkgJson.packageManager).toBe('pnpm@9.1.0')
|
|
})
|