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
55 changes: 10 additions & 45 deletions src/js/node/assert.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,6 @@ function loadAssertionError() {
}
}

let warned = false;

// The assert module provides functions that throw
// AssertionError's when particular conditions are not met. The
// assert module must conform to the following interface.
Expand All @@ -98,57 +96,24 @@ const NO_EXCEPTION_SENTINEL = {};

function innerFail(obj) {
const objMessage = obj.message;
if (objMessage instanceof Error) throw objMessage;
if (Error.isError(objMessage)) throw objMessage;

throw new AssertionError(obj);
}

function fail(message?: string | Error): never;
/** @deprecated since v10.0.0 - use fail([message]) or other assert functions instead. */
function fail(
actual: unknown,
expected: unknown,
message?: string | Error,
operator?: string,
// eslint-disable-next-line @typescript-eslint/ban-types
stackStartFn?: Function,
): never;
function fail(
actual: unknown,
expected: unknown,
message?: string | Error,
operator?: string,
stackStartFn?: Function,
) {
const argsLen = arguments.length;

let internalMessage = false;
if (actual == null && argsLen <= 1) {
internalMessage = true;
function fail(message?: string | Error): never {
if (Error.isError(message)) throw message;

const internalMessage = message === undefined;
if (internalMessage) {
message = "Failed";
} else if (argsLen === 1) {
message = actual;
actual = undefined;
} else {
if (warned === false) {
warned = true;
process.emitWarning(
"assert.fail() with more than one argument is deprecated. " +
"Please use assert.strictEqual() instead or only pass a message.",
"DeprecationWarning",
"DEP0094",
);
}
if (argsLen === 2) operator = "!=";
}
Comment thread
robobun marked this conversation as resolved.

if (message instanceof Error) throw message;

const errArgs = {
actual,
expected,
operator: operator === undefined ? "fail" : operator,
stackStartFn: stackStartFn || fail,
actual: undefined,
expected: undefined,
operator: "fail",
stackStartFn: fail,
Comment thread
robobun marked this conversation as resolved.
message,
};
if (AssertionError === undefined) loadAssertionError();
Expand Down
131 changes: 131 additions & 0 deletions test/js/node/assert/assert-fail.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
import { describe, expect, it } from "bun:test";
import { bunEnv, bunExe } from "harness";
import assert from "node:assert";
import vm from "node:vm";

// node v26 removed the end-of-life DEP0094 multi-argument behaviour of
// assert.fail: only the first argument is used (as the message, or thrown if
// it is an Error), operator is always "fail", actual/expected are undefined.
describe("assert.fail", () => {
const capture = (fn: () => void) => {
try {
fn();
} catch (e: any) {
return {
message: e.message,
actual: e.actual,
expected: e.expected,
operator: e.operator,
generatedMessage: e.generatedMessage,
};
}
throw new Error("assert.fail did not throw");
};

it("with no arguments uses the default generated message", () => {
expect(capture(() => assert.fail())).toEqual({
message: "Failed",
actual: undefined,
expected: undefined,
operator: "fail",
generatedMessage: true,
});
});

it("with an explicit undefined message uses the default generated message", () => {
expect(capture(() => assert.fail(undefined))).toEqual({
message: "Failed",
actual: undefined,
expected: undefined,
operator: "fail",
generatedMessage: true,
});
});

it("treats a falsy non-Error message as generated (matches AssertionError)", () => {
expect(capture(() => assert.fail(""))).toEqual({
message: "",
actual: undefined,
expected: undefined,
operator: "fail",
generatedMessage: true,
});
});

it("uses the first argument as the message", () => {
expect(capture(() => assert.fail("boom"))).toEqual({
message: "boom",
actual: undefined,
expected: undefined,
operator: "fail",
generatedMessage: false,
});
});

it("ignores extra arguments (no legacy actual/expected synthesis)", () => {
expect(capture(() => (assert.fail as any)(1, 2))).toEqual({
message: "1",
actual: undefined,
expected: undefined,
operator: "fail",
generatedMessage: false,
});
expect(capture(() => (assert.fail as any)(1, 2, undefined, "=="))).toEqual({
message: "1",
actual: undefined,
expected: undefined,
operator: "fail",
generatedMessage: false,
});
expect(capture(() => (assert.fail as any)("a", "b", "m"))).toEqual({
message: "a",
actual: undefined,
expected: undefined,
operator: "fail",
generatedMessage: false,
});
});

it("throws the first argument when it is an Error", () => {
const err = new Error("custom");
try {
assert.fail(err);
} catch (e) {
expect(e).toBe(err);
return;
}
throw new Error("assert.fail did not throw");
});

it("throws a cross-realm Error instance directly", () => {
const err = vm.runInNewContext("new Error('cross-realm')");
try {
assert.fail(err);
} catch (e) {
expect(e).toBe(err);
return;
}
throw new Error("assert.fail did not throw");
});

it("does not emit a DEP0094 deprecation warning for multi-argument calls", async () => {
await using proc = Bun.spawn({
cmd: [
bunExe(),
"-e",
`const assert = require("node:assert");
process.on("warning", w => { console.error("ROBOBUN_PROCESS_WARNING", w.name, w.code); process.exit(2); });
try { assert.fail(1, 2); } catch {}
setImmediate(() => process.exit(0));`,
],
env: bunEnv,
stdout: "pipe",
stderr: "pipe",
});
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
expect(stderr).not.toContain("DEP0094");
expect(stderr).not.toContain("ROBOBUN_PROCESS_WARNING");
expect(stdout).toBe("");
expect(exitCode).toBe(0);
});
});
Comment thread
coderabbitai[bot] marked this conversation as resolved.
70 changes: 0 additions & 70 deletions test/js/node/test/parallel/test-assert-fail-deprecation.js

This file was deleted.

Loading