napi-rs/cli/e2e/cli.spec.ts
copilot-swe-agent[bot] 5e3bfe3d6f Complete AVA to node:test migration
- Migrated all remaining test files in examples/napi
- Migrated CLI e2e test
- Updated CLI codegen and source code references from ava to node:test
- Fixed test context handling in all migrated files

Co-authored-by: Brooooooklyn <3468483+Brooooooklyn@users.noreply.github.com>
2025-10-10 14:28:31 +00:00

195 lines
4.9 KiB
TypeScript

import { exec, type ExecOptions } from 'node:child_process'
import { join } from 'node:path'
// use posix path to prevent `\` on Windows
import { join as posixJoin } from 'node:path/posix'
import { tmpdir } from 'node:os'
import { existsSync } from 'node:fs'
import { mkdir, rm, writeFile } from 'node:fs/promises'
import { test, before, beforeEach, afterEach } from 'node:test'
import assert from 'node:assert'
import packageJson from '../package.json' with { type: 'json' }
import { fileURLToPath } from 'node:url'
interface TestContext {
context: string
}
let context: TestContext
const rootDir = join(fileURLToPath(import.meta.url), '..', '..', '..')
const rootDirPosix = posixJoin(
fileURLToPath(import.meta.url, {
windows: false,
}),
'..',
'..',
'..',
)
before(async () => {
await execAsync(`yarn workspace @napi-rs/cli build`, {
cwd: rootDir,
})
await execAsync(`npm pack`, {
cwd: join(rootDir, 'cli'),
})
})
beforeEach(async () => {
const random = Math.random().toString(36).slice(2)
const contextDir = join(tmpdir(), 'napi-rs-cli-e2e', random)
context = { context: contextDir }
await mkdir(context.context, { recursive: true })
await writePackageJson(context.context, {})
await execAsync(`npm install`, {
cwd: context.context,
})
})
afterEach(async () => {
await rm(context.context, { recursive: true, force: true })
})
test('should print help', async () => {
const bin = join(context.context, 'node_modules', '.bin')
await execAsync(`${bin}/napi --help`)
await execAsync(`${bin}/napi build --help`)
await execAsync(`${bin}/napi version --help`)
await execAsync(`${bin}/napi pre-publish --help`)
await execAsync(`${bin}/napi create-npm-dirs --help`)
await execAsync(`${bin}/napi new --help`)
await execAsync(`${bin}/napi rename --help`)
await execAsync(`${bin}/napi version --help`)
assert.ok(true)
})
test('should be able to build a project', async () => {
const { context } = context
await writeCargoToml(context)
await writePackageJson(context, {})
const bin = join(context, 'node_modules', '.bin')
await execAsync(`${bin}/napi build`, {
cwd: context,
env: {
...process.env,
DEBUG: 'napi:*',
},
})
assert.ok(existsSync(join(context, 'index.node')))
})
test('should throw error when duplicate targets are provided', async () => {
const { context } = context
await writeCargoToml(context)
await writePackageJson(context, {
napi: {
targets: ['aarch64-apple-darwin', 'aarch64-apple-darwin'],
},
})
const bin = join(context, 'node_modules', '.bin')
let errMsg = ''
const cp = exec(
`${bin}/napi build`,
{
encoding: 'utf8',
cwd: context,
env: {
...process.env,
FORCE_COLOR: '0',
},
},
(_, stdout) => {
errMsg += stdout
},
)
await new Promise<void>((resolve) => {
cp.on('close', () => {
resolve()
})
})
assert.ok(
errMsg
.trim()
.startsWith(
'Internal Error: Duplicate targets are not allowed: aarch64-apple-darwin',
),
)
})
async function execAsync(command: string, options: ExecOptions = {}) {
return new Promise<void>((resolve, reject) => {
const cp = exec(command, options, (_, stdout, stderr) => {
process.stdout.write(stdout)
process.stderr.write(stderr)
})
cp.on('close', (code) => {
if (code !== 0) {
reject(new Error(`Command ${command} failed with code ${code}`))
}
resolve()
})
})
}
async function writeCargoToml(projectDir: string, cargoToml: string = '') {
await writeFile(
join(projectDir, 'Cargo.toml'),
`[package]
name = "napi-rs-cli-e2e"
version = "1.0.0"
authors = ["napi-rs <dev@napi.rs>"]
edition = "2021"
[lib]
crate-type = ["cdylib"]
[dependencies]
napi = { path = "${posixJoin(rootDirPosix, 'crates', 'napi').substring(process.platform === 'win32' ? 1 : 0)}" }
napi-derive = { path = "${posixJoin(rootDirPosix, 'crates', 'macro').substring(process.platform === 'win32' ? 1 : 0)}" }
[build-dependencies]
napi-build = { path = "${posixJoin(rootDirPosix, 'crates', 'build').substring(process.platform === 'win32' ? 1 : 0)}" }
${cargoToml}
`,
)
await mkdir(join(projectDir, 'src'), { recursive: true })
await writeFile(
join(projectDir, 'src', 'lib.rs'),
`use napi_derive::napi;
#[napi]
pub fn hello() -> String {
"Hello, world!".to_string()
}
`,
)
await writeFile(
join(projectDir, 'build.rs'),
`fn main() {
napi_build::setup();
}`,
)
}
async function writePackageJson(
projectDir: string,
extraPackageJson: Record<string, any>,
) {
await writeFile(
join(projectDir, 'package.json'),
JSON.stringify(
{
name: 'napi-rs-cli-e2e',
version: '1.0.0',
private: true,
devDependencies: {
'@napi-rs/cli': `file://${posixJoin(rootDirPosix, 'cli', `napi-rs-cli-${packageJson.version}.tgz`)}`,
},
...extraPackageJson,
},
null,
2,
),
)
}