mirror of
https://github.com/oven-sh/bun
synced 2026-02-10 10:58:56 +00:00
- Add $`command` syntax for running shell commands in REPL - Transform $`...` to await Bun.$`...` for shell execution - Fix event loop handling for async operations by calling autoTick() - Now properly awaits Bun.sleep(), setTimeout(), and Bun.$ operations - Add tests for shell commands and Bun.sleep Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
363 lines
9.4 KiB
TypeScript
363 lines
9.4 KiB
TypeScript
import { describe, expect, test } from "bun:test";
|
|
import { bunEnv, bunExe } from "harness";
|
|
|
|
describe("bun repl", () => {
|
|
test("evaluates simple expressions", async () => {
|
|
await using proc = Bun.spawn({
|
|
cmd: [bunExe(), "repl"],
|
|
env: bunEnv,
|
|
stdin: "pipe",
|
|
stdout: "pipe",
|
|
stderr: "pipe",
|
|
});
|
|
|
|
proc.stdin.write("1 + 2\n");
|
|
proc.stdin.end();
|
|
|
|
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
|
|
|
|
expect(stdout).toContain("3");
|
|
expect(exitCode).toBe(0);
|
|
});
|
|
|
|
test("supports Bun globals", async () => {
|
|
await using proc = Bun.spawn({
|
|
cmd: [bunExe(), "repl"],
|
|
env: bunEnv,
|
|
stdin: "pipe",
|
|
stdout: "pipe",
|
|
stderr: "pipe",
|
|
});
|
|
|
|
proc.stdin.write("typeof Bun\n");
|
|
proc.stdin.end();
|
|
|
|
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
|
|
|
|
expect(stdout).toContain("object");
|
|
expect(exitCode).toBe(0);
|
|
});
|
|
|
|
test("Bun.version is available", async () => {
|
|
await using proc = Bun.spawn({
|
|
cmd: [bunExe(), "repl"],
|
|
env: bunEnv,
|
|
stdin: "pipe",
|
|
stdout: "pipe",
|
|
stderr: "pipe",
|
|
});
|
|
|
|
proc.stdin.write("Bun.version\n");
|
|
proc.stdin.end();
|
|
|
|
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
|
|
|
|
// Should contain a version string
|
|
expect(stdout).toMatch(/\d+\.\d+\.\d+/);
|
|
expect(exitCode).toBe(0);
|
|
});
|
|
|
|
test("let declarations persist across lines", async () => {
|
|
await using proc = Bun.spawn({
|
|
cmd: [bunExe(), "repl"],
|
|
env: bunEnv,
|
|
stdin: "pipe",
|
|
stdout: "pipe",
|
|
stderr: "pipe",
|
|
});
|
|
|
|
proc.stdin.write("let x = 5\n");
|
|
proc.stdin.write("x * 2\n");
|
|
proc.stdin.end();
|
|
|
|
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
|
|
|
|
expect(stdout).toContain("10");
|
|
expect(exitCode).toBe(0);
|
|
});
|
|
|
|
test("const declarations persist across lines", async () => {
|
|
await using proc = Bun.spawn({
|
|
cmd: [bunExe(), "repl"],
|
|
env: bunEnv,
|
|
stdin: "pipe",
|
|
stdout: "pipe",
|
|
stderr: "pipe",
|
|
});
|
|
|
|
proc.stdin.write("const y = 7\n");
|
|
proc.stdin.write("y + 3\n");
|
|
proc.stdin.end();
|
|
|
|
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
|
|
|
|
expect(stdout).toContain("10");
|
|
expect(exitCode).toBe(0);
|
|
});
|
|
|
|
test("function declarations persist", async () => {
|
|
await using proc = Bun.spawn({
|
|
cmd: [bunExe(), "repl"],
|
|
env: bunEnv,
|
|
stdin: "pipe",
|
|
stdout: "pipe",
|
|
stderr: "pipe",
|
|
});
|
|
|
|
proc.stdin.write("function add(a, b) { return a + b }\n");
|
|
proc.stdin.write("add(2, 3)\n");
|
|
proc.stdin.end();
|
|
|
|
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
|
|
|
|
expect(stdout).toContain("5");
|
|
expect(exitCode).toBe(0);
|
|
});
|
|
|
|
test(".help command works", async () => {
|
|
await using proc = Bun.spawn({
|
|
cmd: [bunExe(), "repl"],
|
|
env: bunEnv,
|
|
stdin: "pipe",
|
|
stdout: "pipe",
|
|
stderr: "pipe",
|
|
});
|
|
|
|
proc.stdin.write(".help\n");
|
|
proc.stdin.end();
|
|
|
|
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
|
|
|
|
expect(stdout).toContain("REPL Commands");
|
|
expect(stdout).toContain(".exit");
|
|
expect(stdout).toContain(".clear");
|
|
expect(exitCode).toBe(0);
|
|
});
|
|
|
|
test(".timing command toggles timing display", async () => {
|
|
await using proc = Bun.spawn({
|
|
cmd: [bunExe(), "repl"],
|
|
env: bunEnv,
|
|
stdin: "pipe",
|
|
stdout: "pipe",
|
|
stderr: "pipe",
|
|
});
|
|
|
|
proc.stdin.write(".timing\n");
|
|
proc.stdin.end();
|
|
|
|
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
|
|
|
|
expect(stdout).toContain("Timing");
|
|
expect(exitCode).toBe(0);
|
|
});
|
|
|
|
test("error handling shows error message", async () => {
|
|
await using proc = Bun.spawn({
|
|
cmd: [bunExe(), "repl"],
|
|
env: bunEnv,
|
|
stdin: "pipe",
|
|
stdout: "pipe",
|
|
stderr: "pipe",
|
|
});
|
|
|
|
proc.stdin.write("throw new Error('test error')\n");
|
|
proc.stdin.end();
|
|
|
|
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
|
|
|
|
// Error should be displayed in stderr
|
|
const output = stdout + stderr;
|
|
expect(output).toContain("test error");
|
|
expect(exitCode).toBe(0);
|
|
});
|
|
|
|
test("object literals are displayed", async () => {
|
|
await using proc = Bun.spawn({
|
|
cmd: [bunExe(), "repl"],
|
|
env: bunEnv,
|
|
stdin: "pipe",
|
|
stdout: "pipe",
|
|
stderr: "pipe",
|
|
});
|
|
|
|
proc.stdin.write("({ foo: 'bar' })\n");
|
|
proc.stdin.end();
|
|
|
|
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
|
|
|
|
expect(stdout).toContain("foo");
|
|
expect(stdout).toContain("bar");
|
|
expect(exitCode).toBe(0);
|
|
});
|
|
|
|
test("arrays are displayed", async () => {
|
|
await using proc = Bun.spawn({
|
|
cmd: [bunExe(), "repl"],
|
|
env: bunEnv,
|
|
stdin: "pipe",
|
|
stdout: "pipe",
|
|
stderr: "pipe",
|
|
});
|
|
|
|
proc.stdin.write("[1, 2, 3]\n");
|
|
proc.stdin.end();
|
|
|
|
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
|
|
|
|
expect(stdout).toContain("1");
|
|
expect(stdout).toContain("2");
|
|
expect(stdout).toContain("3");
|
|
expect(exitCode).toBe(0);
|
|
});
|
|
|
|
test("undefined is not printed for statements", async () => {
|
|
await using proc = Bun.spawn({
|
|
cmd: [bunExe(), "repl"],
|
|
env: bunEnv,
|
|
stdin: "pipe",
|
|
stdout: "pipe",
|
|
stderr: "pipe",
|
|
});
|
|
|
|
proc.stdin.write("let z = 10\n");
|
|
proc.stdin.write("z\n");
|
|
proc.stdin.end();
|
|
|
|
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
|
|
|
|
// The second line should show 10
|
|
expect(stdout).toContain("10");
|
|
expect(exitCode).toBe(0);
|
|
});
|
|
|
|
test("multiline input with semicolons", async () => {
|
|
await using proc = Bun.spawn({
|
|
cmd: [bunExe(), "repl"],
|
|
env: bunEnv,
|
|
stdin: "pipe",
|
|
stdout: "pipe",
|
|
stderr: "pipe",
|
|
});
|
|
|
|
proc.stdin.write("var a = 1; var b = 2; a + b\n");
|
|
proc.stdin.end();
|
|
|
|
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
|
|
|
|
expect(stdout).toContain("3");
|
|
expect(exitCode).toBe(0);
|
|
});
|
|
|
|
test("async/await works", async () => {
|
|
await using proc = Bun.spawn({
|
|
cmd: [bunExe(), "repl"],
|
|
env: bunEnv,
|
|
stdin: "pipe",
|
|
stdout: "pipe",
|
|
stderr: "pipe",
|
|
});
|
|
|
|
proc.stdin.write("await Promise.resolve(42)\n");
|
|
proc.stdin.end();
|
|
|
|
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
|
|
|
|
expect(stdout).toContain("42");
|
|
expect(exitCode).toBe(0);
|
|
});
|
|
|
|
test("class declarations persist", async () => {
|
|
await using proc = Bun.spawn({
|
|
cmd: [bunExe(), "repl"],
|
|
env: bunEnv,
|
|
stdin: "pipe",
|
|
stdout: "pipe",
|
|
stderr: "pipe",
|
|
});
|
|
|
|
proc.stdin.write("class Calculator { add(a, b) { return a + b } }\n");
|
|
proc.stdin.write("new Calculator().add(3, 7)\n");
|
|
proc.stdin.end();
|
|
|
|
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
|
|
|
|
expect(stdout).toContain("10");
|
|
expect(exitCode).toBe(0);
|
|
});
|
|
|
|
test("destructuring works", async () => {
|
|
await using proc = Bun.spawn({
|
|
cmd: [bunExe(), "repl"],
|
|
env: bunEnv,
|
|
stdin: "pipe",
|
|
stdout: "pipe",
|
|
stderr: "pipe",
|
|
});
|
|
|
|
proc.stdin.write("const { a, b } = { a: 1, b: 2 }\n");
|
|
proc.stdin.write("a + b\n");
|
|
proc.stdin.end();
|
|
|
|
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
|
|
|
|
expect(stdout).toContain("3");
|
|
expect(exitCode).toBe(0);
|
|
});
|
|
|
|
test("shell command syntax works", async () => {
|
|
await using proc = Bun.spawn({
|
|
cmd: [bunExe(), "repl"],
|
|
env: bunEnv,
|
|
stdin: "pipe",
|
|
stdout: "pipe",
|
|
stderr: "pipe",
|
|
});
|
|
|
|
proc.stdin.write("$`echo hello from shell`\n");
|
|
proc.stdin.end();
|
|
|
|
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
|
|
|
|
expect(stdout).toContain("hello from shell");
|
|
expect(exitCode).toBe(0);
|
|
});
|
|
|
|
test("Bun.$ template literal works", async () => {
|
|
await using proc = Bun.spawn({
|
|
cmd: [bunExe(), "repl"],
|
|
env: bunEnv,
|
|
stdin: "pipe",
|
|
stdout: "pipe",
|
|
stderr: "pipe",
|
|
});
|
|
|
|
proc.stdin.write("await Bun.$`echo test output`\n");
|
|
proc.stdin.end();
|
|
|
|
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
|
|
|
|
expect(stdout).toContain("test output");
|
|
expect(exitCode).toBe(0);
|
|
});
|
|
|
|
test("Bun.sleep works", async () => {
|
|
await using proc = Bun.spawn({
|
|
cmd: [bunExe(), "repl"],
|
|
env: bunEnv,
|
|
stdin: "pipe",
|
|
stdout: "pipe",
|
|
stderr: "pipe",
|
|
});
|
|
|
|
proc.stdin.write("await Bun.sleep(10)\n");
|
|
proc.stdin.write("'slept'\n");
|
|
proc.stdin.end();
|
|
|
|
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
|
|
|
|
expect(stdout).toContain("slept");
|
|
expect(exitCode).toBe(0);
|
|
});
|
|
});
|