aboutsummaryrefslogtreecommitdiff
path: root/test/bun.js
diff options
context:
space:
mode:
Diffstat (limited to 'test/bun.js')
-rw-r--r--test/bun.js/bun-spawn-test.js21
-rw-r--r--test/bun.js/child_process-node.test.js367
-rw-r--r--test/bun.js/child_process.test.ts24
-rw-r--r--test/bun.js/node-test-helpers.js145
-rw-r--r--test/bun.js/node-test-helpers.test.js157
-rw-r--r--test/bun.js/spawned-child.js1
6 files changed, 482 insertions, 233 deletions
diff --git a/test/bun.js/bun-spawn-test.js b/test/bun.js/bun-spawn-test.js
new file mode 100644
index 000000000..1617a8588
--- /dev/null
+++ b/test/bun.js/bun-spawn-test.js
@@ -0,0 +1,21 @@
+const EventEmitter = import.meta.require("events");
+class TestClass extends EventEmitter {
+ #handle = null;
+ spawn() {
+ this.#handle = Bun.spawn(["pwd"], {
+ cwd: "/tmp",
+ onExit: this.#handleOnExit.bind(this),
+ });
+ }
+ #handleOnExit(code) {
+ console.log(code);
+ this.emit("exit");
+ }
+}
+
+const testClass = new TestClass();
+testClass.spawn();
+testClass.on("exit", () => {
+ console.log("exiting");
+ process.exit(0);
+});
diff --git a/test/bun.js/child_process-node.test.js b/test/bun.js/child_process-node.test.js
index 10135affa..3ac9b2fd1 100644
--- a/test/bun.js/child_process-node.test.js
+++ b/test/bun.js/child_process-node.test.js
@@ -1,10 +1,19 @@
-import { describe, expect, it } from "bun:test";
+import { beforeAll, describe, it } from "bun:test";
import { ChildProcess, spawn, exec } from "node:child_process";
-import { EOL } from "node:os";
-import assertNode from "node:assert";
-import { inspect } from "node:util";
+import {
+ strictEqual,
+ throws,
+ assert,
+ assertOk,
+ createCallCheckCtx,
+ createDoneDotAll,
+} from "node-test-helpers";
-const debug = console.log;
+const debug = process.env.DEBUG ? console.log : () => {};
+
+const platformTmpDir = `${process.platform === "darwin" ? "/private" : ""}${
+ process.env.TMPDIR
+}`.slice(0, -1); // remove trailing slash
// Copyright Joyent, Inc. and other Node contributors.
//
@@ -28,154 +37,9 @@ const debug = console.log;
// USE OR OTHER DEALINGS IN THE SOFTWARE.
const common = {
- // // TODO: Fix the implementations of these functions, they may be ruining everything...
- // mustCallAtLeast: function mustCallAtLeast(callback) {
- // return (...args) => {
- // callback(...args);
- // expect(true).toBe(true);
- // };
- // },
- // mustCall: function mustCall(callback) {
- // return (...args) => {
- // callback(...args);
- // expect(true).toBe(true);
- // };
- // },
pwdCommand: ["pwd", []],
};
-const mustCallChecks = [];
-
-function runCallChecks(exitCode) {
- if (exitCode !== 0) return;
-
- const failed = mustCallChecks.filter(function (context) {
- if ("minimum" in context) {
- context.messageSegment = `at least ${context.minimum}`;
- return context.actual < context.minimum;
- }
- context.messageSegment = `exactly ${context.exact}`;
- return context.actual !== context.exact;
- });
-
- failed.forEach(function (context) {
- console.log(
- "Mismatched %s function calls. Expected %s, actual %d.",
- context.name,
- context.messageSegment,
- context.actual
- );
- console.log(context.stack.split("\n").slice(2).join("\n"));
- });
-
- if (failed.length) process.exit(1);
-}
-
-function mustCall(fn, exact) {
- return _mustCallInner(fn, exact, "exact");
-}
-
-function mustSucceed(fn, exact) {
- return mustCall(function (err, ...args) {
- assert.ifError(err);
- if (typeof fn === "function") return fn.apply(this, args);
- }, exact);
-}
-
-function mustCallAtLeast(fn, minimum) {
- return _mustCallInner(fn, minimum, "minimum");
-}
-
-function _mustCallInner(fn, criteria = 1, field) {
- if (process._exiting)
- throw new Error("Cannot use common.mustCall*() in process exit handler");
- if (typeof fn === "number") {
- criteria = fn;
- fn = noop;
- } else if (fn === undefined) {
- fn = noop;
- }
-
- if (typeof criteria !== "number")
- throw new TypeError(`Invalid ${field} value: ${criteria}`);
-
- const context = {
- [field]: criteria,
- actual: 0,
- stack: inspect(new Error()),
- name: fn.name || "<anonymous>",
- };
-
- // Add the exit listener only once to avoid listener leak warnings
- if (mustCallChecks.length === 0) process.on("exit", runCallChecks);
-
- mustCallChecks.push(context);
-
- const _return = function () {
- // eslint-disable-line func-style
- context.actual++;
- return fn.apply(this, arguments);
- };
- // Function instances have own properties that may be relevant.
- // Let's replicate those properties to the returned function.
- // Refs: https://tc39.es/ecma262/#sec-function-instances
- Object.defineProperties(_return, {
- name: {
- value: fn.name,
- writable: false,
- enumerable: false,
- configurable: true,
- },
- length: {
- value: fn.length,
- writable: false,
- enumerable: false,
- configurable: true,
- },
- });
- return _return;
-}
-
-const strictEqual = (...args) => {
- let error = null;
- try {
- assertNode.strictEqual(...args);
- } catch (err) {
- error = err;
- }
- expect(error).toBe(null);
-};
-
-const throws = (...args) => {
- let error = null;
- try {
- assertNode.throws(...args);
- } catch (err) {
- error = err;
- }
- expect(error).toBe(null);
-};
-
-const assert = (...args) => {
- let error = null;
- try {
- assertNode(...args);
- } catch (err) {
- error = err;
- }
- expect(error).toBe(null);
-};
-
-const assertOk = (...args) => {
- let error = null;
- try {
- assertNode.ok(...args);
- } catch (err) {
- error = err;
- }
- expect(error).toBe(null);
-};
-
describe("ChildProcess.constructor", () => {
it("should be a function", () => {
strictEqual(typeof ChildProcess, "function");
@@ -302,62 +166,80 @@ describe("ChildProcess spawn bad stdio", () => {
// Monkey patch spawn() to create a child process normally, but destroy the
// stdout and stderr streams. This replicates the conditions where the streams
// cannot be properly created.
- const original = ChildProcess.prototype.spawn;
-
- ChildProcess.prototype.spawn = function () {
- const err = original.apply(this, arguments);
-
- this.stdout.destroy();
- this.stderr.destroy();
- this.stdout = null;
- this.stderr = null;
-
- return err;
- };
-
- function createChild(options, callback) {
- const cmd = `"${process.execPath}" "${import.meta.path}" child`;
- return exec(cmd, options, mustCall(callback));
+ function createChild(options, callback, done) {
+ var __originalSpawn = ChildProcess.prototype.spawn;
+ ChildProcess.prototype.spawn = function () {
+ const err = __originalSpawn.apply(this, arguments);
+
+ this.stdout.destroy();
+ this.stderr.destroy();
+ this.stdout = null;
+ this.stderr = null;
+
+ return err;
+ };
+
+ const { mustCall } = createCallCheckCtx(done);
+ const cmd = `bun ${__dirname}/spawned-child.js`;
+ const child = exec(cmd, options, mustCall(callback));
+ ChildProcess.prototype.spawn = __originalSpawn;
+ return child;
}
- it("should handle normal execution of child process", () => {
- createChild({}, (err, stdout, stderr) => {
- strictEqual(err, null);
- strictEqual(stdout, "");
- strictEqual(stderr, "");
- });
+ it("should handle normal execution of child process", (done) => {
+ createChild(
+ {},
+ (err, stdout, stderr) => {
+ strictEqual(err, null);
+ strictEqual(stdout, "");
+ strictEqual(stderr, "");
+ },
+ done
+ );
});
- it("should handle error event of child process", () => {
+ it("should handle error event of child process", (done) => {
const error = new Error("foo");
- const child = createChild({}, (err, stdout, stderr) => {
- strictEqual(err, error);
- strictEqual(stdout, "");
- strictEqual(stderr, "");
- });
+ const child = createChild(
+ {},
+ (err, stdout, stderr) => {
+ strictEqual(err, error);
+ strictEqual(stdout, "");
+ strictEqual(stderr, "");
+ },
+ done
+ );
child.emit("error", error);
});
- it("should handle killed process", () => {
- createChild({ timeout: 1 }, (err, stdout, stderr) => {
- strictEqual(err.killed, true);
- strictEqual(stdout, "");
- strictEqual(stderr, "");
- });
+ it("should handle killed process", (done) => {
+ createChild(
+ { timeout: 1 },
+ (err, stdout, stderr) => {
+ strictEqual(err.killed, true);
+ strictEqual(stdout, "");
+ strictEqual(stderr, "");
+ },
+ done
+ );
});
-
- ChildProcess.prototype.spawn = original;
});
describe("child_process cwd", () => {
- const tmpdir = { path: Bun.env.TMPDIR };
-
// Spawns 'pwd' with given options, then test
// - whether the child pid is undefined or number,
// - whether the exit code equals expectCode,
// - optionally whether the trimmed stdout result matches expectData
- function testCwd(options, expectPidType, expectCode = 0, expectData) {
+ function testCwd(
+ options,
+ { expectPidType, expectCode = 0, expectData },
+ done = () => {}
+ ) {
+ const createDone = createDoneDotAll(done);
+ const { mustCall } = createCallCheckCtx(createDone(1500));
+ const exitDone = createDone(5000);
+
const child = spawn(...common.pwdCommand, options);
strictEqual(typeof child.pid, expectPidType);
@@ -366,19 +248,25 @@ describe("child_process cwd", () => {
// No need to assert callback since `data` is asserted.
let data = "";
- child.stdout.on("data", function (chunk) {
+ child.stdout.on("data", (chunk) => {
data += chunk;
});
- // Can't assert callback, as stayed in to API:
- // _The 'exit' event may or may not fire after an error has occurred._
- child.on("exit", function (code, signal) {
- strictEqual(code, expectCode).bind(this);
+ // TODO: Test exit events
+ // // Can't assert callback, as stayed in to API:
+ // // _The 'exit' event may or may not fire after an error has occurred._
+ child.on("exit", (code, signal) => {
+ try {
+ strictEqual(code, expectCode);
+ exitDone();
+ } catch (err) {
+ exitDone(err);
+ }
});
child.on(
"close",
- mustCall(function () {
+ mustCall(() => {
expectData && strictEqual(data.trim(), expectData);
})
);
@@ -426,55 +314,82 @@ describe("child_process cwd", () => {
// // }
// });
- it("should work for valid given cwd", () => {
+ it("should work for valid given cwd", (done) => {
+ const tmpdir = { path: Bun.env.TMPDIR };
+ const createDone = createDoneDotAll(done);
+
// Assume these exist, and 'pwd' gives us the right directory back
- testCwd({ cwd: tmpdir.path }, "number", 0, tmpdir.path);
+ testCwd(
+ { cwd: tmpdir.path },
+ {
+ expectPidType: "number",
+ expectCode: 0,
+ expectData: platformTmpDir,
+ },
+ createDone(1500)
+ );
const shouldExistDir = "/dev";
- testCwd({ cwd: shouldExistDir }, "number", 0, shouldExistDir);
- testCwd({ cwd: Bun.pathToFileURL(tmpdir.path) }, "number", 0, tmpdir.path);
+ testCwd(
+ { cwd: shouldExistDir },
+ {
+ expectPidType: "number",
+ expectCode: 0,
+ expectData: shouldExistDir,
+ },
+ createDone(1500)
+ );
+ testCwd(
+ { cwd: Bun.pathToFileURL(tmpdir.path) },
+ {
+ expectPidType: "number",
+ expectCode: 0,
+ expectData: platformTmpDir,
+ },
+ createDone(1500)
+ );
});
- it("shouldn't try to chdir to an invalid cwd", () => {
+ it("shouldn't try to chdir to an invalid cwd", (done) => {
+ const createDone = createDoneDotAll(done);
// Spawn() shouldn't try to chdir() to invalid arg, so this should just work
- testCwd({ cwd: "" }, "number");
- testCwd({ cwd: undefined }, "number");
- testCwd({ cwd: null }, "number");
+ testCwd({ cwd: "" }, { expectPidType: "number" }, createDone(1500));
+ testCwd({ cwd: undefined }, { expectPidType: "number" }, createDone(1500));
+ testCwd({ cwd: null }, { expectPidType: "number" }, createDone(1500));
});
});
describe("child_process default options", () => {
- process.env.HELLO = "WORLD";
-
- let child = spawn("/usr/bin/env", [], {});
- let response = "";
+ it("should use process.env as default env", (done) => {
+ let child = spawn("printenv", [], {});
+ let response = "";
- child.stdout.setEncoding("utf8");
-
- it("should use process.env as default env", () => {
- child.stdout.on("data", function (chunk) {
+ child.stdout.setEncoding("utf8");
+ child.stdout.on("data", (chunk) => {
debug(`stdout: ${chunk}`);
response += chunk;
});
- process.on("exit", function () {
+ // NOTE: Original test used child.on("exit"), but this is unreliable
+ // because the process can exit before the stream is closed and the data is read
+ child.stdout.on("close", () => {
assertOk(
- response.includes("HELLO=WORLD"),
+ response.includes(`TMPDIR=${process.env.TMPDIR}`),
"spawn did not use process.env as default " +
- `(process.env.HELLO = ${process.env.HELLO})`
+ `(process.env.TMPDIR = ${process.env.TMPDIR})`
);
+ done();
});
});
-
- delete process.env.HELLO;
});
describe("child_process double pipe", () => {
- let grep, sed, echo;
- grep = spawn("grep", ["o"]);
- sed = spawn("sed", ["s/o/O/"]);
- echo = spawn("echo", ["hello\nnode\nand\nworld\n"]);
+ it("should allow two pipes to be used at once", (done) => {
+ const { mustCallAtLeast, mustCall } = createCallCheckCtx(done);
+ let grep, sed, echo;
+ grep = spawn("grep", ["o"]);
+ sed = spawn("sed", ["s/o/O/"]);
+ echo = spawn("echo", ["hello\nnode\nand\nworld\n"]);
- it("should allow two pipes to be used at once", () => {
// pipe echo | grep
echo.stdout.on(
"data",
@@ -497,7 +412,8 @@ describe("child_process double pipe", () => {
// Propagate end from echo to grep
echo.stdout.on(
"end",
- mustCall((code) => {
+ mustCall(() => {
+ debug("echo stdout end");
grep.stdin.end();
})
);
@@ -555,15 +471,16 @@ describe("child_process double pipe", () => {
sed.stdout.on(
"data",
mustCallAtLeast((data) => {
- result += data.toString("utf8", 0, data.length);
+ result += data.toString("utf8");
debug(data);
})
);
sed.stdout.on(
"end",
- mustCall((code) => {
- strictEqual(result, `hellO${EOL}nOde${EOL}wOrld${EOL}`);
+ mustCall(() => {
+ debug("result: " + result);
+ strictEqual(result, `hellO\nnOde\nwOrld\n`);
})
);
});
diff --git a/test/bun.js/child_process.test.ts b/test/bun.js/child_process.test.ts
index 6387589b0..536f4947b 100644
--- a/test/bun.js/child_process.test.ts
+++ b/test/bun.js/child_process.test.ts
@@ -10,6 +10,12 @@ import {
execSync,
} from "node:child_process";
+const debug = process.env.DEBUG ? console.log : () => {};
+
+const platformTmpDir = `${process.platform === "darwin" ? "/private" : ""}${
+ process.env.TMPDIR
+}`.slice(0, -1); // remove trailing slash
+
// Semver regex: https://gist.github.com/jhorsman/62eeea161a13b80e39f5249281e17c39?permalink_comment_id=2896416#gistcomment-2896416
// Not 100% accurate, but good enough for this test
const SEMVER_REGEX =
@@ -65,11 +71,11 @@ describe("spawn()", () => {
console.error(e);
});
child.stdout.on("data", (data) => {
- console.log(`stdout: ${data}`);
+ debug(`stdout: ${data}`);
resolve(data.toString());
});
child.stderr.on("data", (data) => {
- console.log(`stderr: ${data}`);
+ debug(`stderr: ${data}`);
});
});
expect(SEMVER_REGEX.test(result.trim())).toBe(true);
@@ -120,10 +126,7 @@ describe("spawn()", () => {
resolve(data.toString());
});
});
- const platformTmpDir = `${process.platform === "darwin" ? "/private" : ""}${
- process.env.TMPDIR
- }`;
- expect(`${result.trim()}/`).toBe(platformTmpDir);
+ expect(result.trim()).toBe(platformTmpDir);
});
it("should allow us to write to stdin", async () => {
@@ -261,11 +264,16 @@ describe("execSync()", () => {
describe("Bun.spawn()", () => {
it("should return exit code 0 on successful execution", async () => {
const result = await new Promise((resolve) => {
- Bun.spawn({
+ const proc = Bun.spawn({
cmd: ["echo", "hello"],
- onExit: (code) => resolve(code),
stdout: "inherit",
});
+ const maybeExited = Bun.peek(proc.exited);
+ if (maybeExited === proc.exited) {
+ proc.exited.then((code) => resolve(code));
+ } else {
+ resolve(maybeExited);
+ }
});
expect(result).toBe(0);
});
diff --git a/test/bun.js/node-test-helpers.js b/test/bun.js/node-test-helpers.js
new file mode 100644
index 000000000..e7f6c74f6
--- /dev/null
+++ b/test/bun.js/node-test-helpers.js
@@ -0,0 +1,145 @@
+import { expect } from "bun:test";
+import assertNode from "node:assert";
+
+export const strictEqual = (...args) => {
+ let error = null;
+ try {
+ assertNode.strictEqual(...args);
+ } catch (err) {
+ error = err;
+ }
+ expect(error).toBe(null);
+};
+
+export const throws = (...args) => {
+ let error = null;
+ try {
+ assertNode.throws(...args);
+ } catch (err) {
+ error = err;
+ }
+ expect(error).toBe(null);
+};
+
+export const assert = (...args) => {
+ let error = null;
+ try {
+ assertNode(...args);
+ } catch (err) {
+ error = err;
+ }
+ expect(error).toBe(null);
+};
+
+export const assertOk = (...args) => {
+ let error = null;
+ try {
+ assertNode.ok(...args);
+ } catch (err) {
+ error = err;
+ }
+ expect(error).toBe(null);
+};
+
+export const createCallCheckCtx = (done, timeout = 1500) => {
+ const createDone = createDoneDotAll(done);
+ // const mustCallChecks = [];
+
+ // failed.forEach(function (context) {
+ // console.log(
+ // "Mismatched %s function calls. Expected %s, actual %d.",
+ // context.name,
+ // context.messageSegment,
+ // context.actual
+ // );
+ // console.log(context.stack.split("\n").slice(2).join("\n"));
+ // });
+
+ // TODO: Implement this to be exact only
+ function mustCall(fn, exact) {
+ return mustCallAtLeast(fn, exact);
+ }
+
+ function mustSucceed(fn, exact) {
+ return mustCall(function (err, ...args) {
+ assert.ifError(err);
+ if (typeof fn === "function") return fn.apply(this, args);
+ }, exact);
+ }
+
+ function mustCallAtLeast(fn, minimum) {
+ return _mustCallInner(fn, minimum, "minimum");
+ }
+
+ function _mustCallInner(fn, criteria = 1, field) {
+ if (process._exiting)
+ throw new Error("Cannot use common.mustCall*() in process exit handler");
+ if (typeof fn === "number") {
+ criteria = fn;
+ fn = noop;
+ } else if (fn === undefined) {
+ fn = noop;
+ }
+
+ if (typeof criteria !== "number")
+ throw new TypeError(`Invalid ${field} value: ${criteria}`);
+
+ let actual = 0;
+ let expected = criteria;
+
+ // mustCallChecks.push(context);
+ const done = createDone(timeout);
+ const _return = (...args) => {
+ const result = fn.apply(this, args);
+ actual++;
+ if (actual >= expected) {
+ done();
+ }
+ return result;
+ };
+ // Function instances have own properties that may be relevant.
+ // Let's replicate those properties to the returned function.
+ // Refs: https://tc39.es/ecma262/#sec-function-instances
+ Object.defineProperties(_return, {
+ name: {
+ value: fn.name,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+ },
+ length: {
+ value: fn.length,
+ writable: false,
+ enumerable: false,
+ configurable: true,
+ },
+ });
+ return _return;
+ }
+ return {
+ mustSucceed,
+ mustCall,
+ mustCallAtLeast,
+ };
+};
+
+export function createDoneDotAll(done) {
+ let toComplete = 0;
+ let completed = 0;
+ function createDoneCb(timeout) {
+ toComplete += 1;
+ const timer = setTimeout(() => done(new Error("Timed out!")), timeout);
+ return (result) => {
+ clearTimeout(timer);
+ if (result instanceof Error) {
+ done(result);
+ return;
+ }
+ completed += 1;
+ if (completed === toComplete) {
+ done();
+ }
+ };
+ }
+ return createDoneCb;
+}
diff --git a/test/bun.js/node-test-helpers.test.js b/test/bun.js/node-test-helpers.test.js
new file mode 100644
index 000000000..7fb2de320
--- /dev/null
+++ b/test/bun.js/node-test-helpers.test.js
@@ -0,0 +1,157 @@
+import { describe, it, expect } from "bun:test";
+import {
+ throws,
+ assert,
+ strictEqual,
+ createCallCheckCtx,
+ createDoneDotAll,
+} from "node-test-helpers";
+
+describe("OurAssert.throws()", () => {
+ it("should pass when the function throws", () => {
+ throws(() => {
+ throw new Error("THROWN!");
+ });
+ });
+
+ it("should fail when the function doesn't throw", () => {
+ let err;
+ try {
+ throws(() => {}, Error);
+ } catch (e) {
+ err = e;
+ }
+
+ console.log(err.code);
+ expect(err instanceof Error).toBe(true);
+ });
+});
+
+describe("OurAssert.assert()", () => {
+ it("should pass when the provided value is true", () => {
+ assert(true);
+ });
+
+ it("should fail when the provided value is false", () => {
+ let err;
+ try {
+ assert(false);
+ } catch (e) {
+ err = e;
+ }
+ expect(err instanceof Error).toBe(true);
+ });
+});
+
+describe("OurAssert.strictEqual()", () => {
+ it("should pass when the provided values are deeply equal", () => {
+ strictEqual(1, 1);
+ strictEqual("hello", "hello");
+ const testing = { hello: "world" };
+ const testing2 = testing;
+ testing2.hello = "bla";
+ strictEqual(testing, testing2);
+ strictEqual(NaN, NaN);
+ strictEqual(Infinity, Infinity);
+ strictEqual(-Infinity, -Infinity);
+ strictEqual(null, null);
+ strictEqual(undefined, undefined);
+ });
+
+ it("should fail when the provided values are not deeply equal", () => {
+ let err = null;
+ try {
+ strictEqual(1, 5);
+ } catch (e) {
+ err = e;
+ }
+ expect(err instanceof Error).toBe(true);
+ err = null;
+ try {
+ strictEqual({ foo: "bar" }, { foo: "bar" });
+ } catch (e) {
+ err = e;
+ }
+ expect(err instanceof Error).toBe(true);
+ err = null;
+ try {
+ strictEqual("1", 1);
+ } catch (e) {
+ err = e;
+ }
+ expect(err instanceof Error).toBe(true);
+ err = null;
+ const obj1 = { foo: "bar" };
+ const obj2 = JSON.parse(JSON.stringify(obj1));
+ try {
+ strictEqual(obj1, obj2);
+ } catch (e) {
+ err = e;
+ }
+ expect(err instanceof Error).toBe(true);
+ });
+});
+
+describe("OurAssert.createCallCheckCtx", () => {
+ it("should pass when all mustCall marked callbacks have been called", (done) => {
+ const { mustCall } = createCallCheckCtx(done);
+ const fn1 = mustCall(() => {});
+ const fn2 = mustCall(() => {});
+ fn1();
+ fn2();
+ });
+
+ it("should fail when all mustCall marked callbacks have NOT been called", (done) => {
+ const mockDone = (result) => {
+ expect(result instanceof Error).toBe(true);
+ done();
+ };
+ const { mustCall } = createCallCheckCtx(mockDone, 600);
+ const fn1 = mustCall(() => {});
+ mustCall(() => {});
+ fn1();
+ });
+
+ it("should allow us to get the args of the wrapped callback from mustCall", (done) => {
+ const { mustCall } = createCallCheckCtx(done);
+ const fn1 = mustCall((arg1, arg2) => {
+ expect(arg1).toBe("hello");
+ expect(arg2).toBe("world");
+ });
+ fn1("hello", "world");
+ });
+});
+
+describe("OurAssert.createDoneDotAll()", () => {
+ it("should pass when all dones have been called", (done) => {
+ const createDone = createDoneDotAll(done);
+ const done1 = createDone(600);
+ const done2 = createDone(600);
+ setTimeout(() => done1(), 300);
+ setTimeout(() => done2(), 450);
+ });
+
+ it("should fail when all dones have NOT been called before timeout", (done) => {
+ const mockDone = (result) => {
+ expect(result instanceof Error).toBe(true);
+ done();
+ };
+ const createDone = createDoneDotAll(mockDone);
+ const done1 = createDone(400);
+ createDone(400);
+ setTimeout(() => done1(), 200);
+ });
+
+ it("should allow us to combine mustCall and multiple dones", (done) => {
+ const createDone = createDoneDotAll(done);
+ const { mustCall } = createCallCheckCtx(createDone(600));
+ const done1 = createDone(600);
+ const done2 = createDone(600);
+ const fn1 = mustCall(() => {});
+ const fn2 = mustCall(() => {});
+ setTimeout(() => done1(), 300);
+ setTimeout(() => done2(), 450);
+ setTimeout(() => fn1(), 200);
+ setTimeout(() => fn2(), 200);
+ });
+});
diff --git a/test/bun.js/spawned-child.js b/test/bun.js/spawned-child.js
new file mode 100644
index 000000000..757aacd5c
--- /dev/null
+++ b/test/bun.js/spawned-child.js
@@ -0,0 +1 @@
+setTimeout(() => console.log("hello"), 150);