From d6d468e1a6f3e610aa984ddf9e57ec7b79abb8db Mon Sep 17 00:00:00 2001 From: robobun <117481402+robobun@users.noreply.github.com> Date: Mon, 6 Jul 2026 23:03:39 +0000 Subject: [PATCH 1/7] assert.fail: match Node 26 single-argument behavior --- src/js/node/assert.ts | 49 +++------------- test/js/node/assert/assert.spec.ts | 91 ++++++++++++++++++++++++++++++ 2 files changed, 98 insertions(+), 42 deletions(-) diff --git a/src/js/node/assert.ts b/src/js/node/assert.ts index 7e842a348ebe..305516b7af4d 100644 --- a/src/js/node/assert.ts +++ b/src/js/node/assert.ts @@ -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. @@ -103,52 +101,19 @@ function innerFail(obj) { 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 { + const internalMessage = arguments.length === 0; + 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 = "!="; } 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, message, }; if (AssertionError === undefined) loadAssertionError(); diff --git a/test/js/node/assert/assert.spec.ts b/test/js/node/assert/assert.spec.ts index c2c10d2db079..ad23adbe9bdc 100644 --- a/test/js/node/assert/assert.spec.ts +++ b/test/js/node/assert/assert.spec.ts @@ -1,5 +1,6 @@ import assert, { AssertionError } from "assert"; import { beforeEach, describe, expect, it } from "bun:test"; +import { bunEnv, bunExe } from "harness"; describe("assert(expr)", () => { // https://github.com/oven-sh/bun/issues/941 @@ -94,3 +95,93 @@ describe("assert.deepEqual(actual, expected)", () => { }); }); }); + +// 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("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(1 as any, 2 as any))).toEqual({ + message: "1", + actual: undefined, + expected: undefined, + operator: "fail", + generatedMessage: false, + }); + expect(capture(() => assert.fail(1 as any, 2 as any, undefined, "==" as any))).toEqual({ + message: "1", + actual: undefined, + expected: undefined, + operator: "fail", + generatedMessage: false, + }); + expect(capture(() => assert.fail("a" as any, "b" as any, "m" as any))).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"); + expect(() => assert.fail(err)).toThrow(err); + }); + + 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("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("WARNING"); + expect(stdout).toBe(""); + expect(exitCode).toBe(0); + }); +}); From 736f669346b82a038e03d21f10208b75d05d7e23 Mon Sep 17 00:00:00 2001 From: robobun <117481402+robobun@users.noreply.github.com> Date: Mon, 6 Jul 2026 23:06:30 +0000 Subject: [PATCH 2/7] test: move assert.fail tests into assert.test.cjs --- test/js/node/assert/assert.spec.ts | 91 ----------------------------- test/js/node/assert/assert.test.cjs | 91 +++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+), 91 deletions(-) diff --git a/test/js/node/assert/assert.spec.ts b/test/js/node/assert/assert.spec.ts index ad23adbe9bdc..c2c10d2db079 100644 --- a/test/js/node/assert/assert.spec.ts +++ b/test/js/node/assert/assert.spec.ts @@ -1,6 +1,5 @@ import assert, { AssertionError } from "assert"; import { beforeEach, describe, expect, it } from "bun:test"; -import { bunEnv, bunExe } from "harness"; describe("assert(expr)", () => { // https://github.com/oven-sh/bun/issues/941 @@ -95,93 +94,3 @@ describe("assert.deepEqual(actual, expected)", () => { }); }); }); - -// 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("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(1 as any, 2 as any))).toEqual({ - message: "1", - actual: undefined, - expected: undefined, - operator: "fail", - generatedMessage: false, - }); - expect(capture(() => assert.fail(1 as any, 2 as any, undefined, "==" as any))).toEqual({ - message: "1", - actual: undefined, - expected: undefined, - operator: "fail", - generatedMessage: false, - }); - expect(capture(() => assert.fail("a" as any, "b" as any, "m" as any))).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"); - expect(() => assert.fail(err)).toThrow(err); - }); - - 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("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("WARNING"); - expect(stdout).toBe(""); - expect(exitCode).toBe(0); - }); -}); diff --git a/test/js/node/assert/assert.test.cjs b/test/js/node/assert/assert.test.cjs index e9d4724125cc..f1acffe6046b 100644 --- a/test/js/node/assert/assert.test.cjs +++ b/test/js/node/assert/assert.test.cjs @@ -1,4 +1,5 @@ const assert = require("assert"); +const { bunEnv, bunExe } = require("harness"); test("assert from require as a function does not throw", () => assert(true)); test("assert from require as a function does throw", () => { @@ -7,3 +8,93 @@ test("assert from require as a function does throw", () => { expect(false).toBe(true); } catch (e) {} }); + +// 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 => { + try { + fn(); + } catch (e) { + return { + message: e.message, + actual: e.actual, + expected: e.expected, + operator: e.operator, + generatedMessage: e.generatedMessage, + }; + } + throw new Error("assert.fail did not throw"); + }; + + test("with no arguments uses the default generated message", () => { + expect(capture(() => assert.fail())).toEqual({ + message: "Failed", + actual: undefined, + expected: undefined, + operator: "fail", + generatedMessage: true, + }); + }); + + test("uses the first argument as the message", () => { + expect(capture(() => assert.fail("boom"))).toEqual({ + message: "boom", + actual: undefined, + expected: undefined, + operator: "fail", + generatedMessage: false, + }); + }); + + test("ignores extra arguments (no legacy actual/expected synthesis)", () => { + expect(capture(() => assert.fail(1, 2))).toEqual({ + message: "1", + actual: undefined, + expected: undefined, + operator: "fail", + generatedMessage: false, + }); + expect(capture(() => assert.fail(1, 2, undefined, "=="))).toEqual({ + message: "1", + actual: undefined, + expected: undefined, + operator: "fail", + generatedMessage: false, + }); + expect(capture(() => assert.fail("a", "b", "m"))).toEqual({ + message: "a", + actual: undefined, + expected: undefined, + operator: "fail", + generatedMessage: false, + }); + }); + + test("throws the first argument when it is an Error", () => { + const err = new Error("custom"); + expect(() => assert.fail(err)).toThrow(err); + }); + + test("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("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("WARNING"); + expect(stdout).toBe(""); + expect(exitCode).toBe(0); + }); +}); From 36668c98b9bfab69684cc5c23c35500ab451c3da Mon Sep 17 00:00:00 2001 From: robobun <117481402+robobun@users.noreply.github.com> Date: Mon, 6 Jul 2026 23:08:35 +0000 Subject: [PATCH 3/7] test: add assert.fail node 26 regression tests --- test/js/node/assert/assert-fail.test.ts | 93 +++++++++++++++++++++++++ test/js/node/assert/assert.test.cjs | 91 ------------------------ 2 files changed, 93 insertions(+), 91 deletions(-) create mode 100644 test/js/node/assert/assert-fail.test.ts diff --git a/test/js/node/assert/assert-fail.test.ts b/test/js/node/assert/assert-fail.test.ts new file mode 100644 index 000000000000..f03cea4af608 --- /dev/null +++ b/test/js/node/assert/assert-fail.test.ts @@ -0,0 +1,93 @@ +import assert from "node:assert"; +import { describe, expect, it } from "bun:test"; +import { bunEnv, bunExe } from "harness"; + +// 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("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"); + expect(() => assert.fail(err)).toThrow(err); + }); + + 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("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("WARNING"); + expect(stdout).toBe(""); + expect(exitCode).toBe(0); + }); +}); diff --git a/test/js/node/assert/assert.test.cjs b/test/js/node/assert/assert.test.cjs index f1acffe6046b..e9d4724125cc 100644 --- a/test/js/node/assert/assert.test.cjs +++ b/test/js/node/assert/assert.test.cjs @@ -1,5 +1,4 @@ const assert = require("assert"); -const { bunEnv, bunExe } = require("harness"); test("assert from require as a function does not throw", () => assert(true)); test("assert from require as a function does throw", () => { @@ -8,93 +7,3 @@ test("assert from require as a function does throw", () => { expect(false).toBe(true); } catch (e) {} }); - -// 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 => { - try { - fn(); - } catch (e) { - return { - message: e.message, - actual: e.actual, - expected: e.expected, - operator: e.operator, - generatedMessage: e.generatedMessage, - }; - } - throw new Error("assert.fail did not throw"); - }; - - test("with no arguments uses the default generated message", () => { - expect(capture(() => assert.fail())).toEqual({ - message: "Failed", - actual: undefined, - expected: undefined, - operator: "fail", - generatedMessage: true, - }); - }); - - test("uses the first argument as the message", () => { - expect(capture(() => assert.fail("boom"))).toEqual({ - message: "boom", - actual: undefined, - expected: undefined, - operator: "fail", - generatedMessage: false, - }); - }); - - test("ignores extra arguments (no legacy actual/expected synthesis)", () => { - expect(capture(() => assert.fail(1, 2))).toEqual({ - message: "1", - actual: undefined, - expected: undefined, - operator: "fail", - generatedMessage: false, - }); - expect(capture(() => assert.fail(1, 2, undefined, "=="))).toEqual({ - message: "1", - actual: undefined, - expected: undefined, - operator: "fail", - generatedMessage: false, - }); - expect(capture(() => assert.fail("a", "b", "m"))).toEqual({ - message: "a", - actual: undefined, - expected: undefined, - operator: "fail", - generatedMessage: false, - }); - }); - - test("throws the first argument when it is an Error", () => { - const err = new Error("custom"); - expect(() => assert.fail(err)).toThrow(err); - }); - - test("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("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("WARNING"); - expect(stdout).toBe(""); - expect(exitCode).toBe(0); - }); -}); From 246348d7d151872f9ee57b69908199e9c026f4d4 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Mon, 6 Jul 2026 23:10:33 +0000 Subject: [PATCH 4/7] [autofix.ci] apply automated fixes --- test/js/node/assert/assert-fail.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/js/node/assert/assert-fail.test.ts b/test/js/node/assert/assert-fail.test.ts index f03cea4af608..1e514f759034 100644 --- a/test/js/node/assert/assert-fail.test.ts +++ b/test/js/node/assert/assert-fail.test.ts @@ -1,6 +1,6 @@ -import assert from "node:assert"; import { describe, expect, it } from "bun:test"; import { bunEnv, bunExe } from "harness"; +import assert from "node:assert"; // 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 From f997fa4ba98e81893e1882c528a1282e69cd391f Mon Sep 17 00:00:00 2001 From: robobun <117481402+robobun@users.noreply.github.com> Date: Mon, 6 Jul 2026 23:20:39 +0000 Subject: [PATCH 5/7] assert.fail: handle undefined message, cross-realm errors; drop legacy deprecation test --- src/js/node/assert.ts | 12 ++-- test/js/node/assert/assert-fail.test.ts | 44 +++++++++++- .../parallel/test-assert-fail-deprecation.js | 70 ------------------- 3 files changed, 46 insertions(+), 80 deletions(-) delete mode 100644 test/js/node/test/parallel/test-assert-fail-deprecation.js diff --git a/src/js/node/assert.ts b/src/js/node/assert.ts index 305516b7af4d..8b2a52ec35ed 100644 --- a/src/js/node/assert.ts +++ b/src/js/node/assert.ts @@ -96,19 +96,19 @@ 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 { - const internalMessage = arguments.length === 0; + if (Error.isError(message)) throw message; + + const internalMessage = message === undefined; if (internalMessage) { message = "Failed"; } - if (message instanceof Error) throw message; - const errArgs = { actual: undefined, expected: undefined, @@ -118,9 +118,7 @@ function fail(message?: string | Error): never { }; if (AssertionError === undefined) loadAssertionError(); const err = new AssertionError(errArgs); - if (internalMessage) { - err.generatedMessage = true; - } + err.generatedMessage = internalMessage; throw err; } diff --git a/test/js/node/assert/assert-fail.test.ts b/test/js/node/assert/assert-fail.test.ts index 1e514f759034..037054046031 100644 --- a/test/js/node/assert/assert-fail.test.ts +++ b/test/js/node/assert/assert-fail.test.ts @@ -1,6 +1,7 @@ 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 @@ -31,6 +32,26 @@ describe("assert.fail", () => { }); }); + 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("keeps generatedMessage false for an explicit empty-string message", () => { + expect(capture(() => assert.fail(""))).toEqual({ + message: "", + actual: undefined, + expected: undefined, + operator: "fail", + generatedMessage: false, + }); + }); + it("uses the first argument as the message", () => { expect(capture(() => assert.fail("boom"))).toEqual({ message: "boom", @@ -67,7 +88,24 @@ describe("assert.fail", () => { it("throws the first argument when it is an Error", () => { const err = new Error("custom"); - expect(() => assert.fail(err)).toThrow(err); + 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 () => { @@ -76,7 +114,7 @@ describe("assert.fail", () => { bunExe(), "-e", `const assert = require("node:assert"); - process.on("warning", w => { console.error("WARNING", w.name, w.code); process.exit(2); }); + 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));`, ], @@ -86,7 +124,7 @@ describe("assert.fail", () => { }); const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); expect(stderr).not.toContain("DEP0094"); - expect(stderr).not.toContain("WARNING"); + expect(stderr).not.toContain("ROBOBUN_PROCESS_WARNING"); expect(stdout).toBe(""); expect(exitCode).toBe(0); }); diff --git a/test/js/node/test/parallel/test-assert-fail-deprecation.js b/test/js/node/test/parallel/test-assert-fail-deprecation.js deleted file mode 100644 index ab31b08f953f..000000000000 --- a/test/js/node/test/parallel/test-assert-fail-deprecation.js +++ /dev/null @@ -1,70 +0,0 @@ -// Flags: --no-warnings -'use strict'; - -const { expectWarning } = require('../common'); -const assert = require('assert'); -const { test } = require('node:test'); - -expectWarning( - 'DeprecationWarning', - 'assert.fail() with more than one argument is deprecated. ' + - 'Please use assert.strictEqual() instead or only pass a message.', - 'DEP0094' -); - -test('Two args only, operator defaults to "!="', () => { - assert.throws(() => { - assert.fail('first', 'second'); - }, { - code: 'ERR_ASSERTION', - name: 'AssertionError', - message: '\'first\' != \'second\'', - operator: '!=', - actual: 'first', - expected: 'second', - generatedMessage: true - }); -}); - -test('Three args', () => { - assert.throws(() => { - assert.fail('ignored', 'ignored', 'another custom message'); - }, { - code: 'ERR_ASSERTION', - name: 'AssertionError', - message: 'another custom message', - operator: 'fail', - actual: 'ignored', - expected: 'ignored', - generatedMessage: false - }); -}); - -test('Three args with custom Error', () => { - assert.throws(() => { - assert.fail(typeof 1, 'object', new TypeError('another custom message')); - }, { - name: 'TypeError', - message: 'another custom message' - }); -}); - -test('No third arg (but a fourth arg)', () => { - assert.throws(() => { - assert.fail('first', 'second', undefined, 'operator'); - }, { - code: 'ERR_ASSERTION', - name: 'AssertionError', - message: '\'first\' operator \'second\'', - operator: 'operator', - actual: 'first', - expected: 'second' - }); -}); - -test('The stackFrameFunction should exclude the foo frame', () => { - assert.throws( - function foo() { assert.fail('first', 'second', 'message', '!==', foo); }, - (err) => !/^\s*at\sfoo\b/m.test(err.stack) - ); -}); From 27d1b2c7929ae067ffc90978f81ce3ae636cf2f3 Mon Sep 17 00:00:00 2001 From: robobun <117481402+robobun@users.noreply.github.com> Date: Mon, 6 Jul 2026 23:51:10 +0000 Subject: [PATCH 6/7] assert.fail: keep generatedMessage for falsy non-Error messages --- src/js/node/assert.ts | 4 +++- test/js/node/assert/assert-fail.test.ts | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/js/node/assert.ts b/src/js/node/assert.ts index 8b2a52ec35ed..14ee14294eb0 100644 --- a/src/js/node/assert.ts +++ b/src/js/node/assert.ts @@ -118,7 +118,9 @@ function fail(message?: string | Error): never { }; if (AssertionError === undefined) loadAssertionError(); const err = new AssertionError(errArgs); - err.generatedMessage = internalMessage; + if (internalMessage) { + err.generatedMessage = true; + } throw err; } diff --git a/test/js/node/assert/assert-fail.test.ts b/test/js/node/assert/assert-fail.test.ts index 037054046031..80a41849f43a 100644 --- a/test/js/node/assert/assert-fail.test.ts +++ b/test/js/node/assert/assert-fail.test.ts @@ -42,13 +42,13 @@ describe("assert.fail", () => { }); }); - it("keeps generatedMessage false for an explicit empty-string message", () => { + it("treats a falsy non-Error message as generated (matches AssertionError)", () => { expect(capture(() => assert.fail(""))).toEqual({ message: "", actual: undefined, expected: undefined, operator: "fail", - generatedMessage: false, + generatedMessage: true, }); }); From b0e29b3f6013e6dd5f272854be3689a3ba238890 Mon Sep 17 00:00:00 2001 From: robobun <117481402+robobun@users.noreply.github.com> Date: Tue, 7 Jul 2026 00:02:54 +0000 Subject: [PATCH 7/7] ci: retrigger gate build