Skip to content

Commit

Permalink
Add doctor test for bun.
Browse files Browse the repository at this point in the history
  • Loading branch information
raineorshine committed Apr 26, 2024
1 parent bc18c92 commit 5d47fe3
Show file tree
Hide file tree
Showing 3 changed files with 182 additions and 151 deletions.
20 changes: 20 additions & 0 deletions test/bun/index.test.ts
@@ -1,8 +1,17 @@
import * as bun from '../../src/package-managers/bun'
import chaiSetup from '../helpers/chaiSetup'
import { testFail, testPass } from '../helpers/doctorHelpers'
import stubVersions from '../helpers/stubVersions'

chaiSetup()

const mockNpmVersions = {
emitter20: '2.0.0',
'ncu-test-return-version': '2.0.0',
'ncu-test-tag': '1.1.0',
'ncu-test-v2': '2.0.0',
}

describe('bun', function () {
it('list', async () => {
const result = await bun.list({ cwd: __dirname })
Expand All @@ -13,4 +22,15 @@ describe('bun', function () {
const { version } = await bun.latest('ncu-test-v2', '1.0.0', { cwd: __dirname })
version!.should.equal('2.0.0')
})

describe('doctor', function () {
this.timeout(3 * 60 * 1000)

let stub: { restore: () => void }
before(() => (stub = stubVersions(mockNpmVersions, { spawn: true })))
after(() => stub.restore())

testPass({ packageManager: 'bun' })
testFail({ packageManager: 'bun' })
})
})
152 changes: 1 addition & 151 deletions test/doctor.test.ts
Expand Up @@ -4,8 +4,8 @@ import path from 'path'
import spawn from 'spawn-please'
import { cliOptionsMap } from '../src/cli-options'
import { chalkInit } from '../src/lib/chalk'
import { PackageManagerName } from '../src/types/PackageManagerName'
import chaiSetup from './helpers/chaiSetup'
import { testFail, testPass } from './helpers/doctorHelpers'
import stubVersions from './helpers/stubVersions'

chaiSetup()
Expand All @@ -30,150 +30,6 @@ const ncu = async (
return stdout
}

/** Assertions for npm or yarn when tests pass. */
const testPass = ({ packageManager }: { packageManager: PackageManagerName }) => {
it('upgrade dependencies when tests pass', async function () {
// use dynamic import for ESM module
const { default: stripAnsi } = await import('strip-ansi')
const cwd = path.join(doctorTests, 'pass')
const pkgPath = path.join(cwd, 'package.json')
const nodeModulesPath = path.join(cwd, 'node_modules')
const lockfilePath = path.join(
cwd,
packageManager === 'yarn'
? 'yarn.lock'
: packageManager === 'pnpm'
? 'pnpm-lock.yaml'
: packageManager === 'bun'
? 'bun.lockb'
: 'package-lock.json',
)
const pkgOriginal = await fs.readFile(path.join(cwd, 'package.json'), 'utf-8')
let stdout = ''
let stderr = ''

// touch yarn.lock
// yarn.lock is necessary otherwise yarn sees the package.json in the npm-check-updates directory and throws an error.
if (packageManager === 'yarn' || packageManager === 'bun') {
await fs.writeFile(lockfilePath, '')
}

try {
// explicitly set packageManager to avoid auto yarn detection
await ncu(
['--doctor', '-u', '-p', packageManager],
{
stdout: function (data: string) {
stdout += data
},
stderr: function (data: string) {
stderr += data
},
},
{ cwd },
)
} catch (e) {}

const pkgUpgraded = await fs.readFile(pkgPath, 'utf-8')

// cleanup before assertions in case they fail
await fs.writeFile(pkgPath, pkgOriginal)
await fs.rm(nodeModulesPath, { recursive: true, force: true })
await fs.rm(lockfilePath, { recursive: true, force: true })

// delete yarn cache
if (packageManager === 'yarn') {
await fs.rm(path.join(cwd, '.yarn'), { recursive: true, force: true })
await fs.rm(path.join(cwd, '.pnp.js'), { recursive: true, force: true })
}

// bun prints the run header to stderr instead of stdout
if (packageManager === 'bun') {
stripAnsi(stderr).should.equal('$ echo Success\n\n$ echo Success\n\n')
} else {
stderr.should.equal('')
}

// stdout should include normal output
stripAnsi(stdout).should.containIgnoreCase('Tests pass')
stripAnsi(stdout).should.containIgnoreCase('ncu-test-v2 ~1.0.0 → ~2.0.0')

// package file should include upgrades
pkgUpgraded.should.containIgnoreCase('"ncu-test-v2": "~2.0.0"')
})
}

/** Assertions for npm or yarn when tests fail. */
const testFail = ({ packageManager }: { packageManager: PackageManagerName }) => {
it('identify broken upgrade', async function () {
const cwd = path.join(doctorTests, 'fail')
const pkgPath = path.join(cwd, 'package.json')
const nodeModulesPath = path.join(cwd, 'node_modules')
const lockfilePath = path.join(
cwd,
packageManager === 'yarn'
? 'yarn.lock'
: packageManager === 'pnpm'
? 'pnpm-lock.yaml'
: packageManager === 'bun'
? 'bun.lockb'
: 'package-lock.json',
)
const pkgOriginal = await fs.readFile(path.join(cwd, 'package.json'), 'utf-8')
let stdout = ''
let stderr = ''
let pkgUpgraded

// touch yarn.lock (see fail/README)
if (packageManager === 'yarn') {
await fs.writeFile(lockfilePath, '')
}

try {
// explicitly set packageManager to avoid auto yarn detection
await ncu(
['--doctor', '-u', '-p', packageManager],
{
stdout: function (data: string) {
stdout += data
},
stderr: function (data: string) {
stderr += data
},
},
{ cwd },
)
} finally {
pkgUpgraded = await fs.readFile(pkgPath, 'utf-8')
await fs.writeFile(pkgPath, pkgOriginal)
await fs.rm(nodeModulesPath, { recursive: true, force: true })
await fs.rm(lockfilePath, { recursive: true, force: true })

// delete yarn cache
if (packageManager === 'yarn') {
await fs.rm(path.join(cwd, '.yarn'), { recursive: true, force: true })
await fs.rm(path.join(cwd, '.pnp.js'), { recursive: true, force: true })
}
}

// stdout should include successful upgrades
stdout.should.containIgnoreCase('ncu-test-v2 ~1.0.0 →')
stdout.should.not.include('ncu-test-return-version ~1.0.0 →')
stdout.should.containIgnoreCase('emitter20 1.0.0 →')

// stderr should include first failing upgrade
stderr.should.containIgnoreCase('Breaks with v2.x')
stderr.should.not.include('ncu-test-v2 ~1.0.0 →')
stderr.should.containIgnoreCase('ncu-test-return-version ~1.0.0 →')
stderr.should.not.include('emitter20 1.0.0 →')

// package file should only include successful upgrades
pkgUpgraded.should.containIgnoreCase('"ncu-test-v2": "~2.0.0"')
pkgUpgraded.should.containIgnoreCase('"ncu-test-return-version": "~1.0.0"')
pkgUpgraded.should.not.include('"emitter20": "1.0.0"')
})
}

describe('doctor', function () {
// 3 min timeout
this.timeout(3 * 60 * 1000)
Expand Down Expand Up @@ -482,10 +338,4 @@ else {
testPass({ packageManager: 'yarn' })
testFail({ packageManager: 'yarn' })
})

// TODO: Works locally, but not in GitHub action.
describe.skip('bun', () => {
testPass({ packageManager: 'bun' })
testFail({ packageManager: 'bun' })
})
})
161 changes: 161 additions & 0 deletions test/helpers/doctorHelpers.ts
@@ -0,0 +1,161 @@
import fs from 'fs/promises'
import path from 'path'
import spawn from 'spawn-please'
import { PackageManagerName } from '../../src/types/PackageManagerName'

const bin = path.join(__dirname, '../../build/cli.js')
const doctorTests = path.join(__dirname, '../test-data/doctor')

/** Run the ncu CLI. */
const ncu = async (
args: string[],
spawnPleaseOptions?: Parameters<typeof spawn>[2],
spawnOptions?: Parameters<typeof spawn>[3],
) => {
const { stdout } = await spawn('node', [bin, ...args], spawnPleaseOptions, spawnOptions)
return stdout
}

/** Assertions for npm or yarn when tests pass. */
export const testPass = ({ packageManager }: { packageManager: PackageManagerName }) => {
it('upgrade dependencies when tests pass', async function () {
// use dynamic import for ESM module
const { default: stripAnsi } = await import('strip-ansi')
const cwd = path.join(doctorTests, 'pass')
const pkgPath = path.join(cwd, 'package.json')
const nodeModulesPath = path.join(cwd, 'node_modules')
const lockfilePath = path.join(
cwd,
packageManager === 'yarn'
? 'yarn.lock'
: packageManager === 'pnpm'
? 'pnpm-lock.yaml'
: packageManager === 'bun'
? 'bun.lockb'
: 'package-lock.json',
)
const pkgOriginal = await fs.readFile(path.join(cwd, 'package.json'), 'utf-8')
let stdout = ''
let stderr = ''

// touch yarn.lock
// yarn.lock is necessary otherwise yarn sees the package.json in the npm-check-updates directory and throws an error.
if (packageManager === 'yarn' || packageManager === 'bun') {
await fs.writeFile(lockfilePath, '')
}

try {
// explicitly set packageManager to avoid auto yarn detection
await ncu(
['--doctor', '-u', '-p', packageManager],
{
stdout: function (data: string) {
stdout += data
},
stderr: function (data: string) {
stderr += data
},
},
{ cwd },
)
} catch (e) {}

const pkgUpgraded = await fs.readFile(pkgPath, 'utf-8')

// cleanup before assertions in case they fail
await fs.writeFile(pkgPath, pkgOriginal)
await fs.rm(nodeModulesPath, { recursive: true, force: true })
await fs.rm(lockfilePath, { recursive: true, force: true })

// delete yarn cache
if (packageManager === 'yarn') {
await fs.rm(path.join(cwd, '.yarn'), { recursive: true, force: true })
await fs.rm(path.join(cwd, '.pnp.js'), { recursive: true, force: true })
}

// bun prints the run header to stderr instead of stdout
if (packageManager === 'bun') {
stripAnsi(stderr).should.equal('$ echo Success\n\n$ echo Success\n\n')
} else {
stderr.should.equal('')
}

// stdout should include normal output
stripAnsi(stdout).should.containIgnoreCase('Tests pass')
stripAnsi(stdout).should.containIgnoreCase('ncu-test-v2 ~1.0.0 → ~2.0.0')

// package file should include upgrades
pkgUpgraded.should.containIgnoreCase('"ncu-test-v2": "~2.0.0"')
})
}

/** Assertions for npm or yarn when tests fail. */
export const testFail = ({ packageManager }: { packageManager: PackageManagerName }) => {
it('identify broken upgrade', async function () {
const cwd = path.join(doctorTests, 'fail')
const pkgPath = path.join(cwd, 'package.json')
const nodeModulesPath = path.join(cwd, 'node_modules')
const lockfilePath = path.join(
cwd,
packageManager === 'yarn'
? 'yarn.lock'
: packageManager === 'pnpm'
? 'pnpm-lock.yaml'
: packageManager === 'bun'
? 'bun.lockb'
: 'package-lock.json',
)
const pkgOriginal = await fs.readFile(path.join(cwd, 'package.json'), 'utf-8')
let stdout = ''
let stderr = ''
let pkgUpgraded

// touch yarn.lock (see fail/README)
if (packageManager === 'yarn') {
await fs.writeFile(lockfilePath, '')
}

try {
// explicitly set packageManager to avoid auto yarn detection
await ncu(
['--doctor', '-u', '-p', packageManager],
{
stdout: function (data: string) {
stdout += data
},
stderr: function (data: string) {
stderr += data
},
},
{ cwd },
)
} finally {
pkgUpgraded = await fs.readFile(pkgPath, 'utf-8')
await fs.writeFile(pkgPath, pkgOriginal)
await fs.rm(nodeModulesPath, { recursive: true, force: true })
await fs.rm(lockfilePath, { recursive: true, force: true })

// delete yarn cache
if (packageManager === 'yarn') {
await fs.rm(path.join(cwd, '.yarn'), { recursive: true, force: true })
await fs.rm(path.join(cwd, '.pnp.js'), { recursive: true, force: true })
}
}

// stdout should include successful upgrades
stdout.should.containIgnoreCase('ncu-test-v2 ~1.0.0 →')
stdout.should.not.include('ncu-test-return-version ~1.0.0 →')
stdout.should.containIgnoreCase('emitter20 1.0.0 →')

// stderr should include first failing upgrade
stderr.should.containIgnoreCase('Breaks with v2.x')
stderr.should.not.include('ncu-test-v2 ~1.0.0 →')
stderr.should.containIgnoreCase('ncu-test-return-version ~1.0.0 →')
stderr.should.not.include('emitter20 1.0.0 →')

// package file should only include successful upgrades
pkgUpgraded.should.containIgnoreCase('"ncu-test-v2": "~2.0.0"')
pkgUpgraded.should.containIgnoreCase('"ncu-test-return-version": "~1.0.0"')
pkgUpgraded.should.not.include('"emitter20": "1.0.0"')
})
}

0 comments on commit 5d47fe3

Please sign in to comment.