Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions test/js/bun/shell/bunshell-default.test.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { expect, test } from "bun:test";
import { bunEnv } from "harness";
import { bunExe, createTestBuilder } from "./test_builder";
const TestBuilder = createTestBuilder(import.meta.path);
Expand All @@ -24,7 +25,7 @@ test("default throw on command failure", async () => {
await TestBuilder.command`echo ${code} > index.test.ts; ${bunExe()} test index.test.ts`
.ensureTempDir()
.stdout(`bun test ${Bun.version_with_sha}\n`)
.stderr(s => s.includes("1 pass"))
.stderr(s => expect(s).toContain("1 pass"))
.env(bunEnv)
.run();
});
Expand All @@ -49,7 +50,7 @@ test("ShellError has .text()", async () => {
await TestBuilder.command`echo ${code} > index.test.ts; ${bunExe()} test index.test.ts`
.ensureTempDir()
.stdout(`bun test ${Bun.version_with_sha}\n`)
.stderr(s => s.includes("1 pass"))
.stderr(s => expect(s).toContain("1 pass"))
.env(bunEnv)
.run();
});
20 changes: 11 additions & 9 deletions test/js/bun/shell/bunshell.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@ describe("bunshell", () => {
process.platform === "win32" ? "cat ldkfjsldf" : null,
"touch -alskdjfakjfhasjfh",
"mkdir",
"export",
"cd lskfjlsdkjf",
process.platform !== "win32" ? "echo hi > /dev/full" : null,
"pwd sldfkj sfks jdflks flksd f",
Expand All @@ -83,7 +82,7 @@ describe("bunshell", () => {
failing_cmds.forEach(cmdstr =>
!!cmdstr
? TestBuilder.command`${{ raw: cmdstr }}`
.exitCode(c => c !== 0)
.exitCode(c => expect(c).not.toBe(0))
.stdout(() => {})
.stderr(() => {})
.runAsTest(cmdstr)
Expand Down Expand Up @@ -1328,6 +1327,12 @@ describe("deno_task", () => {
TestBuilder.command`export VAR=1 VAR2=testing VAR3="test this out" && echo $VAR $VAR2 $VAR3`
.stdout("1 testing test this out\n")
.runAsTest("exported vars 2");

TestBuilder.command`export FOO=bar; export`
.env({ ZED: "2", ALPHA: "1" })
.ensureTempDir()
.stdout("ALPHA=1\nFOO=bar\nPWD=$TEMP_DIR\nZED=2\n")
.runAsTest("export with no arguments prints the exported vars sorted and succeeds");
});

describe("pipeline", async () => {
Expand Down Expand Up @@ -1467,7 +1472,7 @@ describe("deno_task", () => {
// Test nested pipeline with mixed success/failure
TestBuilder.command`(echo success | echo works) | (nonexistent | echo backup) || echo final_fallback`
.stdout("backup\n")
.stderr(s => s.includes("command not found"))
.stderr("bun: command not found: nonexistent\n")
.runAsTest("nested pipeline mixed success failure");

TestBuilder.command`echo 0 | echo 1 | echo 2 | echo 3 | echo 4 | echo 5 | echo 6 | echo 7 | echo 8 | echo 9 | echo 10 | echo 11 | echo 12 | echo 13 | echo 14 | echo 15 | echo 16 | echo 17 | echo 18 | echo 19 | echo 20 | echo 21 | echo 22 | echo 23 | echo 24 | echo 25 | echo 26 | echo 27 | echo 28 | echo 29 | echo 30 | echo 31 | echo 32 | echo 33 | echo 34 | echo 35 | echo 36 | echo 37 | echo 38 | echo 39 | echo 40 | echo 41 | echo 42 | echo 43 | echo 44 | echo 45 | echo 46 | echo 47 | echo 48 | echo 49 | BUN_TEST_VAR=1 ${BUN} -e 'process.stdin.pipe(process.stdout)'`
Expand Down Expand Up @@ -2394,16 +2399,14 @@ describe("condexprs", () => {
[[ ! 1 -eq 1 ]]; echo $?
[[ ! ! 1 -eq 1 ]]; echo $?
`
.stdout("1")
.stdout("0")
.stdout("1\n0\n")
.runAsTest("! toggles on and off rather than just setting an 'invert result' flag");

TestBuilder.command`
[[ ! ! ! 1 -eq 1 ]]; echo $?
[[ ! ! ! ! 1 -eq 1 ]]; echo $?
`
.stdout("1")
.stdout("0")
.stdout("1\n0\n")
.runAsTest("! toggles on and off rather than just setting an 'invert result' flag");

TestBuilder.command`[[ a ]]`.exitCode(0).runAsTest("parenthesized terms didn't work right until post-2.04");
Expand Down Expand Up @@ -2624,8 +2627,7 @@ fi
if [[ "123abc" == *?(a)bc ]]; then echo ok 42; else echo bad 42; fi
if [[ "123abc" == *?(a)bc ]]; then echo ok 43; else echo bad 43; fi
`
.stdout("ok 42")
.stdout("ok 43")
.stdout("ok 42\nok 43\n")
.runAsTest("bug in all versions up to and including bash-2.05b");

// TestBuilder.command`
Expand Down
18 changes: 6 additions & 12 deletions test/js/bun/shell/commands/ls.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,10 +110,7 @@ describe.concurrent("bunshell ls", () => {
await $`touch .hidden regular`.quiet().throws(true).cwd(tempdir);
await TestBuilder.command`ls -A`
.setTempdir(tempdir)
.stdout(s => expect(sortedLsOutput(s)).not.toContain("."))
.stdout(s => expect(sortedLsOutput(s)).not.toContain(".."))
.stdout(s => expect(sortedLsOutput(s)).toContain(".hidden"))
.stdout(s => expect(sortedLsOutput(s)).toContain("regular"))
.stdout(s => expect(sortedLsOutput(s)).toEqual([".hidden", "regular"]))
.run();
});

Expand Down Expand Up @@ -205,8 +202,9 @@ describe.concurrent("bunshell ls", () => {
await $`mkdir sub; touch .hidden sub/.hidden-sub`.quiet().throws(true).cwd(tempdir);
await TestBuilder.command`ls -Ra`
.setTempdir(tempdir)
.stdout(s => expect(sortedLsOutput(s)).toContain(".hidden"))
.stdout(s => expect(sortedLsOutput(s)).toContain(".hidden-sub"))
.stdout(s =>
expect(sortedLsOutput(s)).toEqual([".", "..", ".hidden", "sub", "./sub:", ".", "..", ".hidden-sub"].sort()),
)
.run();
});

Expand All @@ -215,9 +213,7 @@ describe.concurrent("bunshell ls", () => {
await $`mkdir sub; touch .hidden sub/.hidden-sub`.quiet().throws(true).cwd(tempdir);
await TestBuilder.command`ls -RA`
.setTempdir(tempdir)
.stdout(s => expect(sortedLsOutput(s)).toContain(".hidden"))
.stdout(s => expect(sortedLsOutput(s)).toContain(".hidden-sub"))
.stdout(s => expect(sortedLsOutput(s)).not.toContain("."))
.stdout(s => expect(sortedLsOutput(s)).toEqual([".hidden", "sub", "./sub:", ".hidden-sub"].sort()))
.run();
});

Expand Down Expand Up @@ -327,9 +323,7 @@ describe.concurrent("bunshell ls", () => {
await TestBuilder.command`ls -R level1`
.setTempdir(tempdir)
.exitCode(1)
.stdout(s => expect(sortedLsOutput(s)).toContain("file1"))
.stdout(s => expect(sortedLsOutput(s)).toContain("file2"))
.stdout(s => expect(sortedLsOutput(s)).toContain("file3"))
.stdout(s => expect(sortedLsOutput(s)).toEqual(["file1", "file2", "file3", "level2"]))
.stderr(s => expect(s).toContain("Permission denied"))
.run();

Expand Down
15 changes: 9 additions & 6 deletions test/js/bun/shell/pipeline_stack.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,22 +75,25 @@ describe("pipeline stack edge cases", () => {

describe("cd builtin in pipelines", () => {
TestBuilder.command`cd / | pwd`
.stdout(s => s.includes("$TEMP_DIR"))
.stdout("$TEMP_DIR\n")
.ensureTempDir()
.runAsTest("cd | pwd - cd doesn't affect next command in pipeline");

TestBuilder.command`mkdir foo; mkdir foo/bar; cd foo | cd foo/bar | pwd`
.stdout(s => s.includes("$TEMP_DIR"))
.stdout("$TEMP_DIR\n")
.ensureTempDir()
.runAsTest("cd | cd | pwd - multiple cd's don't affect");

// The first pwd writes into the pipe, so only the last one reaches stdout.
TestBuilder.command`pwd | cd / | pwd`
.stdout(s => {
const lines = s.trim().split("\n");
return lines.length === 2 && lines[0].includes("$TEMP_DIR") && lines[1].includes("$TEMP_DIR");
})
.stdout("$TEMP_DIR\n")
.ensureTempDir()
.runAsTest("pwd | cd | pwd - cd in middle doesn't affect");

TestBuilder.command`mkdir foo; cd foo | pwd; pwd`
.stdout("$TEMP_DIR\n$TEMP_DIR\n")
.ensureTempDir()
.runAsTest("cd in a pipeline doesn't affect the commands after the pipeline either");
});

describe("mixed builtin and subprocess pipelines", () => {
Expand Down
106 changes: 106 additions & 0 deletions test/js/bun/shell/test_builder.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import { $ } from "bun";
import { describe, expect, test } from "bun:test";
import { createTestBuilder } from "./test_builder";

const TestBuilder = createTestBuilder(import.meta.path);

// Other shell test files switch the shared `$` to nothrow mode; the ShellError tests below need the default.
$.throws(true);

describe.concurrent("TestBuilder", () => {
describe("an expectation can only be set once per builder", () => {
test("stdout", () => {
expect(() => TestBuilder.command`echo hi`.stdout("hi\n").stdout("hi\n")).toThrow(
"the stdout expectation was set more than once",
);
});

test("stderr", () => {
expect(() => TestBuilder.command`echo hi`.stderr("").stderr_contains("")).toThrow(
"the stderr expectation was set more than once",
);
});

test("exitCode", () => {
expect(() => TestBuilder.command`echo hi`.exitCode(0).exitCode(0)).toThrow(
"the exitCode expectation was set more than once",
);
});

test("error", () => {
expect(() => TestBuilder.command`echo hi`.error("a").error("b")).toThrow(
"the error expectation was set more than once",
);
});

test("different expectations on one builder are fine", async () => {
await TestBuilder.command`echo hi`.stdout("hi\n").stderr("").exitCode(0).run();
});
});

describe("expectation callbacks", () => {
test("assert by calling expect()", async () => {
await TestBuilder.command`echo hi`
.stdout(s => expect(s).toBe("hi\n"))
.stderr(s => {
expect(s).toBe("");
})
.exitCode(c => expect(c).toBe(0))
.run();
});

test("a failing expect() in a callback fails the run", async () => {
await expect(TestBuilder.command`echo hi`.stdout(s => expect(s).toBe("bye\n")).run()).rejects.toThrow();
});

test("stdout callback must not return a value", async () => {
await expect(TestBuilder.command`echo hi`.stdout(s => s.includes("hi")).run()).rejects.toThrow(
"the .stdout() callback returned a boolean",
);
});

test("stderr callback must not return a value", async () => {
await expect(TestBuilder.command`true`.stderr(s => s.length).run()).rejects.toThrow(
"the .stderr() callback returned a number",
);
});

test("exitCode callback must not return a value", async () => {
await expect(TestBuilder.command`true`.exitCode(c => c === 0).run()).rejects.toThrow(
"the .exitCode() callback returned a boolean",
);
});
});

describe("error()", () => {
test("passes when the command throws the expected error", async () => {
await TestBuilder.command`echo hi |`.error("Unexpected EOF").run();
});

test("fails when the command does not throw", async () => {
await expect(TestBuilder.command`echo hi`.error("Unexpected EOF").run()).rejects.toThrow(
"expected the command to throw, but it completed with exit code 0",
);
});
});

describe("when the command rejects with a ShellError", () => {
test("the expectations are checked against its output", async () => {
await TestBuilder.command`echo out; ls does-not-exist`
.stdout("out\n")
.stderr("ls: does-not-exist: No such file or directory\n")
.exitCode(1)
.run();
});

test("a failing expectation fails the run", async () => {
await expect(
TestBuilder.command`echo out; ls does-not-exist`
.stdout("something else\n")
.stderr_contains("No such file or directory")
.exitCode(1)
.run(),
).rejects.toThrow();
});
});
});
Loading
Loading