Files
bun.sh/test/cli/install/bun-pm-version.test.ts
2025-07-07 11:21:36 -07:00

878 lines
28 KiB
TypeScript

import { spawn, spawnSync } from "bun";
import { describe, expect, it } from "bun:test";
import { bunEnv, bunExe, tempDirWithFiles } from "harness";
import { join } from "node:path";
describe("bun pm version", () => {
let i = 0;
function setupTest() {
const testDir = tempDirWithFiles(`version-${i++}`, {
"package.json": JSON.stringify(
{
name: "test-package",
version: "1.0.0",
},
null,
2,
),
});
return testDir;
}
function setupGitTest() {
const testDir = setupTest();
spawnSync({
cmd: ["git", "init"],
cwd: testDir,
env: bunEnv,
});
spawnSync({
cmd: ["git", "config", "user.name", "Test User"],
cwd: testDir,
env: bunEnv,
});
spawnSync({
cmd: ["git", "config", "user.email", "test@example.com"],
cwd: testDir,
env: bunEnv,
});
spawnSync({
cmd: ["git", "add", "package.json"],
cwd: testDir,
env: bunEnv,
});
spawnSync({
cmd: ["git", "commit", "-m", "Initial commit"],
cwd: testDir,
env: bunEnv,
});
return testDir;
}
function setupMonorepoTest() {
const testDir = tempDirWithFiles(`version-${i++}`, {
"package.json": JSON.stringify(
{
name: "monorepo-root",
version: "1.0.0",
workspaces: ["packages/*"],
},
null,
2,
),
"packages/pkg-a/package.json": JSON.stringify(
{
name: "@test/pkg-a",
version: "2.0.0",
},
null,
2,
),
"packages/pkg-b/package.json": JSON.stringify(
{
name: "@test/pkg-b",
version: "3.0.0",
dependencies: {
"@test/pkg-a": "workspace:*",
},
},
null,
2,
),
});
return testDir;
}
async function runCommand(args: string[], cwd: string, expectSuccess = true) {
const result = spawn({
cmd: args,
cwd,
env: bunEnv,
stdout: "pipe",
stderr: "pipe",
});
const [output, error] = await Promise.all([new Response(result.stdout).text(), new Response(result.stderr).text()]);
const code = await result.exited;
return { output, error, code };
}
describe("help and version previews", () => {
it("should show help when no arguments provided", async () => {
const testDir = setupTest();
const { output, code } = await runCommand([bunExe(), "pm", "version"], testDir);
expect(code).toBe(0);
expect(output).toContain("bun pm version");
expect(output).toContain("Current package version: v1.0.0");
expect(output).toContain("patch");
expect(output).toContain("minor");
expect(output).toContain("major");
});
it("shows help with version previews", async () => {
const testDir1 = tempDirWithFiles(`version-${i++}`, {
"package.json": JSON.stringify({ name: "test", version: "2.5.3" }, null, 2),
});
const { output: output1, code: code1 } = await runCommand([bunExe(), "pm", "version"], testDir1);
expect(code1).toBe(0);
expect(output1).toContain("Current package version: v2.5.3");
expect(output1).toContain("patch 2.5.3 → 2.5.4");
expect(output1).toContain("minor 2.5.3 → 2.6.0");
expect(output1).toContain("major 2.5.3 → 3.0.0");
const testDir2 = tempDirWithFiles(`version-${i++}`, {
"package.json": JSON.stringify({ name: "test", version: "1.0.0-alpha.0" }, null, 2),
});
const { output: output2, code: code2 } = await runCommand([bunExe(), "pm", "version"], testDir2);
expect(code2).toBe(0);
expect(output2).toContain("prepatch");
expect(output2).toContain("preminor");
expect(output2).toContain("premajor");
expect(output2).toContain("1.0.1-alpha.0");
expect(output2).toContain("1.1.0-alpha.0");
expect(output2).toContain("2.0.0-alpha.0");
const testDir3 = tempDirWithFiles(`version-${i++}`, {
"package.json": JSON.stringify({ name: "test", version: "1.0.0" }, null, 2),
});
const { output: output3, code: code3 } = await runCommand(
[bunExe(), "pm", "version", "--preid", "beta"],
testDir3,
);
expect(code3).toBe(0);
expect(output3).toContain("prepatch");
expect(output3).toContain("preminor");
expect(output3).toContain("premajor");
expect(output3).toContain("1.0.1-beta.0");
expect(output3).toContain("1.1.0-beta.0");
expect(output3).toContain("2.0.0-beta.0");
const testDir4 = tempDirWithFiles(`version-${i++}`, {
"package.json": JSON.stringify({ name: "test" }, null, 2),
});
const { output: output4 } = await runCommand([bunExe(), "pm", "version"], testDir4);
expect(output4).not.toContain("Current package version:");
expect(output4).toContain("patch 1.0.0 → 1.0.1");
});
});
describe("basic version incrementing", () => {
it("should increment versions correctly", async () => {
const testDir = setupTest();
const { output: patchOutput, code: patchCode } = await runCommand(
[bunExe(), "pm", "version", "patch", "--no-git-tag-version"],
testDir,
);
expect(patchCode).toBe(0);
expect(patchOutput.trim()).toBe("v1.0.1");
const { output: minorOutput, code: minorCode } = await runCommand(
[bunExe(), "pm", "version", "minor", "--no-git-tag-version"],
testDir,
);
expect(minorCode).toBe(0);
expect(minorOutput.trim()).toBe("v1.1.0");
const { output: majorOutput, code: majorCode } = await runCommand(
[bunExe(), "pm", "version", "major", "--no-git-tag-version"],
testDir,
);
expect(majorCode).toBe(0);
expect(majorOutput.trim()).toBe("v2.0.0");
const packageJson = await Bun.file(`${testDir}/package.json`).json();
expect(packageJson.version).toBe("2.0.0");
});
it("should set specific version", async () => {
const testDir = setupTest();
const { output, code } = await runCommand([bunExe(), "pm", "version", "3.2.1", "--no-git-tag-version"], testDir);
expect(code).toBe(0);
expect(output.trim()).toBe("v3.2.1");
const packageJson = await Bun.file(`${testDir}/package.json`).json();
expect(packageJson.version).toBe("3.2.1");
});
it("handles empty package.json", async () => {
const testDir = tempDirWithFiles(`version-${i++}`, {
"package.json": "{}",
});
const { output, code } = await runCommand([bunExe(), "pm", "version", "patch", "--no-git-tag-version"], testDir);
expect(code).toBe(0);
expect(output.trim()).toBe("v0.0.1");
const packageJson = await Bun.file(`${testDir}/package.json`).json();
expect(packageJson.version).toBe("0.0.1");
});
});
describe("error handling", () => {
it("handles various error conditions", async () => {
const testDir2 = tempDirWithFiles(`version-${i++}`, {
"package.json": JSON.stringify({ name: "test", version: "invalid-version" }, null, 2),
});
const { error: error2, code: code2 } = await runCommand(
[bunExe(), "pm", "version", "patch", "--no-git-tag-version"],
testDir2,
false,
);
expect(error2).toContain("is not a valid semver");
expect(code2).toBe(1);
const testDir3 = setupTest();
const { error: error3, code: code3 } = await runCommand(
[bunExe(), "pm", "version", "invalid-arg", "--no-git-tag-version"],
testDir3,
false,
);
expect(error3).toContain("Invalid version argument");
expect(code3).toBe(1);
const testDir4 = setupTest();
const { error: error4, code: code4 } = await runCommand(
[bunExe(), "pm", "version", "1.0.0", "--no-git-tag-version"],
testDir4,
false,
);
expect(error4).toContain("Version not changed");
expect(code4).toBe(1);
const { output: output5, code: code5 } = await runCommand(
[bunExe(), "pm", "version", "1.0.0", "--no-git-tag-version", "--allow-same-version"],
testDir4,
);
expect(output5.trim()).toBe("v1.0.0");
expect(code5).toBe(0);
});
it("handles missing package.json like npm", async () => {
const testDir = tempDirWithFiles(`version-${i++}`, {
"README.md": "# Test project",
});
const { error, code } = await runCommand(
[bunExe(), "pm", "version", "patch", "--no-git-tag-version"],
testDir,
false,
);
expect(error).toContain("package.json");
expect(code).toBe(1);
// its an ealier check that "bun pm *" commands do so not "bun pm version" specific
// expect(error.includes("ENOENT") || error.includes("no such file")).toBe(true);
});
it("handles empty string package.json like npm", async () => {
const testDir = tempDirWithFiles(`version-${i++}`, {
"package.json": '""',
});
const { error, code } = await runCommand(
[bunExe(), "pm", "version", "patch", "--no-git-tag-version"],
testDir,
false,
);
expect(error).toContain("Failed to parse package.json");
expect(code).toBe(1);
});
it("handles malformed JSON like npm", async () => {
const testDir = tempDirWithFiles(`version-${i++}`, {
"package.json": '{ "name": "test", invalid json }',
});
const { error, code } = await runCommand(
[bunExe(), "pm", "version", "patch", "--no-git-tag-version"],
testDir,
false,
);
expect(error).toContain("Failed to parse package.json");
expect(code).toBe(1);
});
});
describe("git integration", () => {
it("creates git commits and tags by default", async () => {
const testDir1 = setupGitTest();
const {
output: output1,
code: code1,
error: stderr1,
} = await runCommand([bunExe(), "pm", "version", "patch"], testDir1);
expect(stderr1.trim()).toBe("");
expect(output1.trim()).toBe("v1.0.1");
expect(code1).toBe(0);
const { output: tagOutput } = await runCommand(["git", "tag", "-l"], testDir1);
expect(tagOutput).toContain("v1.0.1");
const { output: logOutput } = await runCommand(["git", "log", "--oneline"], testDir1);
expect(logOutput).toContain("v1.0.1");
});
it("supports custom commit messages", async () => {
const testDir2 = setupGitTest();
const {
output: output2,
error: error2,
code: code2,
} = await runCommand([bunExe(), "pm", "version", "patch", "--message", "Custom release message"], testDir2);
expect(error2).toBe("");
const { output: gitLogOutput } = await runCommand(["git", "log", "--oneline"], testDir2);
expect(gitLogOutput).toContain("Custom release message");
expect(code2).toBe(0);
expect(output2.trim()).toBe("v1.0.1");
});
it("fails when git working directory is not clean", async () => {
const testDir3 = setupGitTest();
await Bun.write(join(testDir3, "untracked.txt"), "untracked content");
const { error: error3, code: code3 } = await runCommand([bunExe(), "pm", "version", "patch"], testDir3, false);
expect(error3).toContain("Git working directory not clean");
expect(code3).toBe(1);
});
it("allows dirty working directory with --force flag", async () => {
const testDir = setupGitTest();
await Bun.write(join(testDir, "untracked.txt"), "untracked content");
const { output, code, error } = await runCommand([bunExe(), "pm", "version", "patch", "--force"], testDir);
expect(code).toBe(0);
expect(error.trim()).toBe("");
expect(output.trim()).toBe("v1.0.1");
const { output: tagOutput } = await runCommand(["git", "tag", "-l"], testDir);
expect(tagOutput).toContain("v1.0.1");
const { output: logOutput } = await runCommand(["git", "log", "--oneline"], testDir);
expect(logOutput).toContain("v1.0.1");
});
it("works without git when no repo is present", async () => {
const testDir4 = setupTest();
const { output: output4, code: code4 } = await runCommand([bunExe(), "pm", "version", "patch"], testDir4);
expect(code4).toBe(0);
expect(output4.trim()).toBe("v1.0.1");
const packageJson = await Bun.file(`${testDir4}/package.json`).json();
expect(packageJson.version).toBe("1.0.1");
});
it("respects --no-git-tag-version flag", async () => {
const testDir5 = setupGitTest();
const { output: output5, code: code5 } = await runCommand(
[bunExe(), "pm", "version", "patch", "--no-git-tag-version"],
testDir5,
);
expect(code5).toBe(0);
expect(output5.trim()).toBe("v1.0.1");
const packageJson5 = await Bun.file(`${testDir5}/package.json`).json();
expect(packageJson5.version).toBe("1.0.1");
const { output: tagOutput5 } = await runCommand(["git", "tag", "-l"], testDir5);
expect(tagOutput5.trim()).toBe("");
const { output: logOutput5 } = await runCommand(["git", "log", "--oneline"], testDir5);
expect(logOutput5).toContain("Initial commit");
expect(logOutput5).not.toContain("v1.0.1");
});
it("respects --git-tag-version=false flag", async () => {
const testDir6 = setupGitTest();
const { output: output6, code: code6 } = await runCommand(
[bunExe(), "pm", "version", "patch", "--git-tag-version=false"],
testDir6,
);
expect(code6).toBe(0);
expect(output6.trim()).toBe("v1.0.1");
const packageJson6 = await Bun.file(`${testDir6}/package.json`).json();
expect(packageJson6.version).toBe("1.0.1");
const { output: tagOutput6 } = await runCommand(["git", "tag", "-l"], testDir6);
expect(tagOutput6.trim()).toBe("");
const { output: logOutput6 } = await runCommand(["git", "log", "--oneline"], testDir6);
expect(logOutput6).toContain("Initial commit");
expect(logOutput6).not.toContain("v1.0.1");
});
it("respects --git-tag-version=true flag", async () => {
const testDir7 = setupGitTest();
const { output: output7, code: code7 } = await runCommand(
[bunExe(), "pm", "version", "patch", "--git-tag-version=true"],
testDir7,
);
expect(code7).toBe(0);
expect(output7.trim()).toBe("v1.0.1");
const packageJson7 = await Bun.file(`${testDir7}/package.json`).json();
expect(packageJson7.version).toBe("1.0.1");
const { output: tagOutput7 } = await runCommand(["git", "tag", "-l"], testDir7);
expect(tagOutput7).toContain("v1.0.1");
const { output: logOutput7 } = await runCommand(["git", "log", "--oneline"], testDir7);
expect(logOutput7).toContain("v1.0.1");
});
it("supports %s substitution in commit messages", async () => {
const testDir8 = setupGitTest();
const { output: output8, code: code8 } = await runCommand(
[bunExe(), "pm", "version", "patch", "--message", "Bump version to %s"],
testDir8,
);
expect(code8).toBe(0);
expect(output8.trim()).toBe("v1.0.1");
const { output: logOutput8 } = await runCommand(["git", "log", "--oneline", "-1"], testDir8);
expect(logOutput8).toContain("Bump version to 1.0.1");
const testDir9 = setupGitTest();
const { output: output9, code: code9 } = await runCommand(
[bunExe(), "pm", "version", "2.5.0", "-m", "Release %s with fixes"],
testDir9,
);
expect(code9).toBe(0);
expect(output9.trim()).toBe("v2.5.0");
const { output: logOutput9 } = await runCommand(["git", "log", "--oneline", "-1"], testDir9);
expect(logOutput9).toContain("Release 2.5.0 with fixes");
});
});
describe("JSON formatting preservation", () => {
it("preserves JSON formatting correctly", async () => {
const originalJson1 = `{
"name": "test",
"version": "1.0.0",
"scripts": {
"test": "echo test"
},
"dependencies": {
"lodash": "^4.17.21"
}
}`;
const testDir1 = tempDirWithFiles(`version-${i++}`, {
"package.json": originalJson1,
});
const { output: output1, code: code1 } = await runCommand(
[bunExe(), "pm", "version", "patch", "--no-git-tag-version"],
testDir1,
);
expect(code1).toBe(0);
expect(output1.trim()).toBe("v1.0.1");
const updatedJson1 = await Bun.file(`${testDir1}/package.json`).text();
expect(updatedJson1).toContain(' "version": "1.0.1"');
expect(updatedJson1).toContain('"name": "test"');
expect(updatedJson1).toContain(' "test": "echo test"');
expect(JSON.parse(updatedJson1)).toMatchObject({
name: "test",
version: "1.0.1",
scripts: {
test: "echo test",
},
});
});
});
describe("prerelease handling", () => {
it("handles custom preid and prerelease scenarios", async () => {
const testDir1 = tempDirWithFiles(`version-${i++}`, {
"package.json": JSON.stringify({ name: "test", version: "1.0.0" }, null, 2),
});
const { output: output1, code: code1 } = await runCommand(
[bunExe(), "pm", "version", "prerelease", "--preid", "beta", "--no-git-tag-version"],
testDir1,
);
expect(code1).toBe(0);
expect(output1.trim()).toBe("v1.0.1-beta.0");
const testDir3 = tempDirWithFiles(`version-${i++}`, {
"package.json": JSON.stringify({ name: "test", version: "1.0.0" }, null, 2),
});
const { output: output3, code: code3 } = await runCommand(
[bunExe(), "pm", "version", "prerelease", "--no-git-tag-version"],
testDir3,
);
expect(code3).toBe(0);
expect(output3.trim()).toBe("v1.0.1-0");
const testDir5 = tempDirWithFiles(`version-${i++}`, {
"package.json": JSON.stringify({ name: "test", version: "1.0.0-alpha" }, null, 2),
});
const { output: output5, code: code5 } = await runCommand(
[bunExe(), "pm", "version", "prerelease", "--no-git-tag-version"],
testDir5,
);
expect(code5).toBe(0);
expect(output5.trim()).toBe("v1.0.0-alpha.1");
const testDir6 = tempDirWithFiles(`version-${i++}`, {
"package.json": JSON.stringify({ name: "test", version: "1.0.0-3" }, null, 2),
});
const { output: output6, code: code6 } = await runCommand(
[bunExe(), "pm", "version", "prerelease", "--no-git-tag-version"],
testDir6,
);
expect(code6).toBe(0);
expect(output6.trim()).toBe("v1.0.0-4");
});
it("should preserve prerelease identifiers correctly", async () => {
const scenarios = [
{
version: "1.0.3-alpha.1",
preid: "beta",
expected: {
patch: "1.0.3-alpha.1 → 1.0.4",
minor: "1.0.3-alpha.1 → 1.1.0",
major: "1.0.3-alpha.1 → 2.0.0",
prerelease: "1.0.3-alpha.1 → 1.0.3-beta.2",
prepatch: "1.0.3-alpha.1 → 1.0.4-beta.0",
preminor: "1.0.3-alpha.1 → 1.1.0-beta.0",
premajor: "1.0.3-alpha.1 → 2.0.0-beta.0",
},
},
{
version: "1.0.3-1",
preid: "abcd",
expected: {
patch: "1.0.3-1 → 1.0.4",
minor: "1.0.3-1 → 1.1.0",
major: "1.0.3-1 → 2.0.0",
prerelease: "1.0.3-1 → 1.0.3-abcd.2",
prepatch: "1.0.3-1 → 1.0.4-abcd.0",
preminor: "1.0.3-1 → 1.1.0-abcd.0",
premajor: "1.0.3-1 → 2.0.0-abcd.0",
},
},
{
version: "2.5.0-rc.3",
preid: "next",
expected: {
patch: "2.5.0-rc.3 → 2.5.1",
minor: "2.5.0-rc.3 → 2.6.0",
major: "2.5.0-rc.3 → 3.0.0",
prerelease: "2.5.0-rc.3 → 2.5.0-next.4",
prepatch: "2.5.0-rc.3 → 2.5.1-next.0",
preminor: "2.5.0-rc.3 → 2.6.0-next.0",
premajor: "2.5.0-rc.3 → 3.0.0-next.0",
},
},
{
version: "1.0.0-a",
preid: "b",
expected: {
patch: "1.0.0-a → 1.0.1",
minor: "1.0.0-a → 1.1.0",
major: "1.0.0-a → 2.0.0",
prerelease: "1.0.0-a → 1.0.0-b.1",
prepatch: "1.0.0-a → 1.0.1-b.0",
preminor: "1.0.0-a → 1.1.0-b.0",
premajor: "1.0.0-a → 2.0.0-b.0",
},
},
];
for (const scenario of scenarios) {
const testDir = tempDirWithFiles(`version-${i++}`, {
"package.json": JSON.stringify({ name: "test", version: scenario.version }, null, 2),
});
const { output, code } = await runCommand(
[bunExe(), "pm", "version", "--no-git-tag-version", `--preid=${scenario.preid}`],
testDir,
);
expect(code).toBe(0);
expect(output).toContain(`Current package version: v${scenario.version}`);
for (const [incrementType, expectedTransformation] of Object.entries(scenario.expected)) {
expect(output).toContain(`${incrementType.padEnd(10)} ${expectedTransformation}`);
}
}
const testDir2 = tempDirWithFiles(`version-${i++}`, {
"package.json": JSON.stringify({ name: "test", version: "1.0.3-alpha.1" }, null, 2),
});
const { output: output2, code: code2 } = await runCommand(
[bunExe(), "pm", "version", "--no-git-tag-version"],
testDir2,
);
expect(code2).toBe(0);
expect(output2).toContain("prerelease 1.0.3-alpha.1 → 1.0.3-alpha.2");
});
});
describe("lifecycle scripts", () => {
it("runs lifecycle scripts in correct order and handles failures", async () => {
const testDir1 = tempDirWithFiles(`version-${i++}`, {
"package.json": JSON.stringify(
{
name: "test",
version: "1.0.0",
scripts: {
preversion: "echo 'step1' >> lifecycle.log",
version: "echo 'step2' >> lifecycle.log",
postversion: "echo 'step3' >> lifecycle.log",
},
},
null,
2,
),
});
await Bun.spawn([bunExe(), "pm", "version", "patch", "--no-git-tag-version"], {
cwd: testDir1,
env: bunEnv,
stderr: "ignore",
stdout: "ignore",
}).exited;
expect(await Bun.file(join(testDir1, "lifecycle.log")).exists()).toBe(true);
const logContent = await Bun.file(join(testDir1, "lifecycle.log")).text();
expect(logContent.trim()).toBe("step1\nstep2\nstep3");
const testDir2 = tempDirWithFiles(`version-${i++}`, {
"package.json": JSON.stringify(
{
name: "test",
version: "1.0.0",
scripts: {
preversion: "echo $npm_lifecycle_event > event.log && echo $npm_lifecycle_script > script.log",
},
},
null,
2,
),
});
await Bun.spawn([bunExe(), "pm", "version", "patch", "--no-git-tag-version"], {
cwd: testDir2,
env: bunEnv,
stderr: "ignore",
stdout: "ignore",
}).exited;
expect(Bun.file(join(testDir2, "event.log")).exists()).resolves.toBe(true);
expect(Bun.file(join(testDir2, "script.log")).exists()).resolves.toBe(true);
const eventContent = await Bun.file(join(testDir2, "event.log")).text();
const scriptContent = await Bun.file(join(testDir2, "script.log")).text();
expect(eventContent.trim()).toBe("preversion");
expect(scriptContent.trim()).toContain("echo $npm_lifecycle_event");
const testDir3 = tempDirWithFiles(`version-${i++}`, {
"package.json": JSON.stringify(
{
name: "test",
version: "1.0.0",
scripts: {
preversion: "exit 1",
},
},
null,
2,
),
});
const proc = Bun.spawn([bunExe(), "pm", "version", "minor", "--no-git-tag-version"], {
cwd: testDir3,
env: bunEnv,
stderr: "pipe",
stdout: "ignore",
});
await proc.exited;
expect(proc.exitCode).toBe(1);
expect(await proc.stderr.text()).toContain('script "preversion" exited with code 1');
const packageJson = await Bun.file(join(testDir3, "package.json")).json();
expect(packageJson.version).toBe("1.0.0");
const testDir4 = tempDirWithFiles(`version-${i++}`, {
"package.json": JSON.stringify(
{
name: "test",
version: "1.0.0",
scripts: {
preversion: "mkdir -p build && echo 'built' > build/output.txt",
version: "cp build/output.txt version-output.txt",
postversion: "rm -rf build",
},
},
null,
2,
),
});
await Bun.spawn([bunExe(), "pm", "version", "patch", "--no-git-tag-version"], {
cwd: testDir4,
env: bunEnv,
stderr: "ignore",
stdout: "ignore",
}).exited;
expect(Bun.file(join(testDir4, "version-output.txt")).exists()).resolves.toBe(true);
expect(Bun.file(join(testDir4, "build")).exists()).resolves.toBe(false);
const content = await Bun.file(join(testDir4, "version-output.txt")).text();
expect(content.trim()).toBe("built");
const testDir5 = tempDirWithFiles(`version-${i++}`, {
"package.json": JSON.stringify(
{
name: "test",
version: "1.0.0",
scripts: {
preversion: "echo 'should not run' >> ignored.log",
version: "echo 'should not run' >> ignored.log",
postversion: "echo 'should not run' >> ignored.log",
},
},
null,
2,
),
});
const { output: output5, code: code5 } = await runCommand(
[bunExe(), "pm", "version", "patch", "--no-git-tag-version", "--ignore-scripts"],
testDir5,
);
expect(code5).toBe(0);
expect(output5.trim()).toBe("v1.0.1");
const packageJson5 = await Bun.file(join(testDir5, "package.json")).json();
expect(packageJson5.version).toBe("1.0.1");
expect(await Bun.file(join(testDir5, "ignored.log")).exists()).toBe(false);
});
});
describe("workspace and directory handling", () => {
it("should version workspace packages individually", async () => {
const testDir = setupMonorepoTest();
const { output: outputA, code: codeA } = await runCommand(
[bunExe(), "pm", "version", "patch", "--no-git-tag-version"],
join(testDir, "packages", "pkg-a"),
);
expect(codeA).toBe(0);
expect(outputA.trim()).toBe("v2.0.1");
const rootPackageJson = await Bun.file(`${testDir}/package.json`).json();
expect(rootPackageJson.version).toBe("1.0.0");
const pkgAJson = await Bun.file(`${testDir}/packages/pkg-a/package.json`).json();
const pkgBJson = await Bun.file(`${testDir}/packages/pkg-b/package.json`).json();
expect(pkgAJson.version).toBe("2.0.1");
expect(pkgBJson.version).toBe("3.0.0");
});
it("should work from subdirectories", async () => {
const testDir = tempDirWithFiles(`version-${i++}`, {
"package.json": JSON.stringify({ name: "test", version: "1.0.0" }, null, 2),
"src/index.js": "console.log('hello');",
});
const { output, code } = await runCommand(
[bunExe(), "pm", "version", "patch", "--no-git-tag-version"],
join(testDir, "src"),
);
expect(code).toBe(0);
expect(output.trim()).toBe("v1.0.1");
const packageJson = await Bun.file(`${testDir}/package.json`).json();
expect(packageJson.version).toBe("1.0.1");
const monorepoDir = setupMonorepoTest();
await Bun.write(join(monorepoDir, "packages", "pkg-a", "lib", "index.js"), "");
const { output: output2, code: code2 } = await runCommand(
[bunExe(), "pm", "version", "minor", "--no-git-tag-version"],
join(monorepoDir, "packages", "pkg-a", "lib"),
);
expect(code2).toBe(0);
expect(output2.trim()).toBe("v2.1.0");
const rootJson = await Bun.file(`${monorepoDir}/package.json`).json();
const pkgAJson = await Bun.file(`${monorepoDir}/packages/pkg-a/package.json`).json();
const pkgBJson = await Bun.file(`${monorepoDir}/packages/pkg-b/package.json`).json();
expect(rootJson.version).toBe("1.0.0");
expect(pkgAJson.version).toBe("2.1.0");
expect(pkgBJson.version).toBe("3.0.0");
});
});
});