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
3 changes: 3 additions & 0 deletions src/bun.js/ModuleLoader.zig
Original file line number Diff line number Diff line change
Expand Up @@ -2678,6 +2678,7 @@ pub const HardcodedModule = enum {
@"node:stream/web",
@"node:string_decoder",
@"node:test",
@"node:test/reporters",
@"node:timers",
@"node:timers/promises",
@"node:tls",
Expand Down Expand Up @@ -2760,6 +2761,7 @@ pub const HardcodedModule = enum {
.{ "node:net", .@"node:net" },
.{ "node:readline", .@"node:readline" },
.{ "node:test", .@"node:test" },
.{ "node:test/reporters", .@"node:test/reporters" },
.{ "node:os", .@"node:os" },
.{ "node:path", .@"node:path" },
.{ "node:path/posix", .@"node:path/posix" },
Expand Down Expand Up @@ -2899,6 +2901,7 @@ pub const HardcodedModule = enum {
nodeEntry("node:zlib"),
// New Node.js builtins only resolve from the prefixed one.
nodeEntryOnlyPrefix("node:test"),
nodeEntryOnlyPrefix("node:test/reporters"),

nodeEntry("assert"),
nodeEntry("assert/strict"),
Expand Down
6 changes: 6 additions & 0 deletions src/bun.js/bindings/ProcessBindingNatives.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,12 @@ static JSValue processBindingNativesReturnUndefined(VM& vm, JSObject* bindingObj
internal/streams/transform processBindingNativesGetter PropertyCallback
internal/streams/utils processBindingNativesGetter PropertyCallback
internal/streams/writable processBindingNativesGetter PropertyCallback
internal/test/reporter/dot processBindingNativesGetter PropertyCallback
internal/test/reporter/junit processBindingNativesGetter PropertyCallback
internal/test/reporter/lcov processBindingNativesGetter PropertyCallback
internal/test/reporter/spec processBindingNativesGetter PropertyCallback
internal/test/reporter/tap processBindingNativesGetter PropertyCallback
internal/test/reporter/utils processBindingNativesGetter PropertyCallback
internal/timers processBindingNativesGetter PropertyCallback
internal/tls processBindingNativesGetter PropertyCallback
internal/tty processBindingNativesGetter PropertyCallback
Expand Down
1 change: 1 addition & 0 deletions src/bun.js/bindings/isBuiltinModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ static constexpr ASCIILiteral builtinModuleNamesSortedLength[] = {
"inspector/promises"_s,
"_stream_passthrough"_s,
"diagnostics_channel"_s,
"node:test/reporters"_s,
};

namespace Bun {
Expand Down
129 changes: 129 additions & 0 deletions src/js/internal/primordials.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,27 @@ const SafeArrayIterator = createSafeIterator(ArrayPrototypeSymbolIterator, Array

const ArrayPrototypeMap = Array.prototype.map;
const PromisePrototypeThen = $Promise.prototype.$then;
const RegExpPrototype = RegExp.prototype;
const SymbolMatch = Symbol.match;
const SymbolMatchAll = Symbol.matchAll;
const SymbolReplace = Symbol.replace;
const SymbolSearch = Symbol.search;
const SymbolSplit = Symbol.split;
const RegExpPrototypeSymbolMatch = RegExpPrototype[SymbolMatch];
const RegExpPrototypeSymbolMatchAll = RegExpPrototype[SymbolMatchAll];
const RegExpPrototypeSymbolReplace = RegExpPrototype[SymbolReplace];
const RegExpPrototypeSymbolSearch = RegExpPrototype[SymbolSearch];
const RegExpPrototypeSymbolSplit = RegExpPrototype[SymbolSplit];
const RegExpPrototypeExec = RegExpPrototype.exec;
const RegExpPrototypeGetDotAll = getGetter(RegExp, "dotAll");
const RegExpPrototypeGetGlobal = getGetter(RegExp, "global");
const RegExpPrototypeGetHasIndices = getGetter(RegExp, "hasIndices");
const RegExpPrototypeGetIgnoreCase = getGetter(RegExp, "ignoreCase");
const RegExpPrototypeGetMultiline = getGetter(RegExp, "multiline");
const RegExpPrototypeGetSource = getGetter(RegExp, "source");
const RegExpPrototypeGetSticky = getGetter(RegExp, "sticky");
const RegExpPrototypeGetUnicode = getGetter(RegExp, "unicode");
const RegExpPrototypeGetFlags = getGetter(RegExp, "flags");

const arrayToSafePromiseIterable = (promises, mapFn) =>
new SafeArrayIterator(
Expand Down Expand Up @@ -118,6 +139,113 @@ const SafePromiseAllReturnArrayLike = (promises, mapFn) =>
}
});

class RegExpLikeForStringSplitting {
#regex;
constructor() {
this.#regex = ReflectConstruct(RegExp, arguments);
}

get lastIndex() {
return ReflectGet(this.#regex, "lastIndex");
}
set lastIndex(value) {
ReflectSet(this.#regex, "lastIndex", value);
}

exec() {
return ReflectApply(RegExpPrototypeExec, this.#regex, arguments);
}
}
ObjectSetPrototypeOf(RegExpLikeForStringSplitting.prototype, null);

function hardenRegExp(pattern) {
ObjectDefineProperties(pattern, {
[SymbolMatch]: {
__proto__: null,
configurable: true,
value: RegExpPrototypeSymbolMatch,
},
[SymbolMatchAll]: {
__proto__: null,
configurable: true,
value: RegExpPrototypeSymbolMatchAll,
},
[SymbolReplace]: {
__proto__: null,
configurable: true,
value: RegExpPrototypeSymbolReplace,
},
[SymbolSearch]: {
__proto__: null,
configurable: true,
value: RegExpPrototypeSymbolSearch,
},
[SymbolSplit]: {
__proto__: null,
configurable: true,
value: RegExpPrototypeSymbolSplit,
},
constructor: {
__proto__: null,
configurable: true,
value: {
[SymbolSpecies]: RegExpLikeForStringSplitting,
},
},
dotAll: {
__proto__: null,
configurable: true,
value: RegExpPrototypeGetDotAll(pattern),
},
exec: {
__proto__: null,
configurable: true,
value: OriginalRegExpPrototypeExec,
},
Comment on lines +200 to +204

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Fix undefined reference to OriginalRegExpPrototypeExec.

Line 203 references OriginalRegExpPrototypeExec, which is not defined anywhere in this file or the visible context. This will cause a ReferenceError at runtime.

Apply this diff to use the correct variable:

     exec: {
       __proto__: null,
       configurable: true,
-      value: OriginalRegExpPrototypeExec,
+      value: RegExpPrototypeExec,
     },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
exec: {
__proto__: null,
configurable: true,
value: OriginalRegExpPrototypeExec,
},
exec: {
__proto__: null,
configurable: true,
value: RegExpPrototypeExec,
},
🤖 Prompt for AI Agents
In src/js/internal/primordials.js around lines 200 to 204, the property value
references an undefined identifier `OriginalRegExpPrototypeExec`; replace that
reference with the correct, existing variable used elsewhere in this module
(e.g. `OriginalRegExpExec`) or, alternatively, declare/assign
`OriginalRegExpPrototypeExec` earlier so it points to the same defined function;
ensure the property uses the exact identifier already defined in this file to
avoid the ReferenceError.

global: {
__proto__: null,
configurable: true,
value: RegExpPrototypeGetGlobal(pattern),
},
hasIndices: {
__proto__: null,
configurable: true,
value: RegExpPrototypeGetHasIndices(pattern),
},
ignoreCase: {
__proto__: null,
configurable: true,
value: RegExpPrototypeGetIgnoreCase(pattern),
},
multiline: {
__proto__: null,
configurable: true,
value: RegExpPrototypeGetMultiline(pattern),
},
source: {
__proto__: null,
configurable: true,
value: RegExpPrototypeGetSource(pattern),
},
sticky: {
__proto__: null,
configurable: true,
value: RegExpPrototypeGetSticky(pattern),
},
unicode: {
__proto__: null,
configurable: true,
value: RegExpPrototypeGetUnicode(pattern),
},
});
ObjectDefineProperty(pattern, "flags", {
__proto__: null,
configurable: true,
value: RegExpPrototypeGetFlags(pattern),
});
return pattern;
}

export default {
Array,
SafeArrayIterator,
Expand Down Expand Up @@ -177,4 +305,5 @@ export default {
BigUint64Array,
BigInt64Array,
uncurryThis,
hardenRegExp,
};
40 changes: 40 additions & 0 deletions src/js/internal/test/reporter/dot.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
const colors = require("internal/util/colors");
const { formatTestReport } = require("internal/test/reporter/utils");

const ArrayPrototypePush = Array.prototype.push;
const MathMax = Math.max;

async function* dot(source) {
let count = 0;
let columns = getLineLength();
const failedTests = [];
for await (const { type, data } of source) {
if (type === "test:pass") {
yield `${colors.green}.${colors.reset}`;
}
if (type === "test:fail") {
yield `${colors.red}X${colors.reset}`;
ArrayPrototypePush.$apply(failedTests, data);
}
if ((type === "test:fail" || type === "test:pass") && ++count === columns) {
yield "\n";

// Getting again in case the terminal was resized.
columns = getLineLength();
count = 0;
}
}
yield "\n";
if (failedTests.length > 0) {
yield `\n${colors.red}Failed tests:${colors.white}\n\n`;
for (const test of failedTests) {
yield formatTestReport("test:fail", test);
}
}
Comment on lines +28 to +33

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Color reset after “Failed tests:” header

Header leaves terminal color as white; reset to avoid bleed.

-    yield `\n${colors.red}Failed tests:${colors.white}\n\n`;
+    yield `\n${colors.red}Failed tests:${colors.reset}\n\n`;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (failedTests.length > 0) {
yield `\n${colors.red}Failed tests:${colors.white}\n\n`;
for (const test of failedTests) {
yield formatTestReport("test:fail", test);
}
}
if (failedTests.length > 0) {
yield `\n${colors.red}Failed tests:${colors.reset}\n\n`;
for (const test of failedTests) {
yield formatTestReport("test:fail", test);
}
}
🤖 Prompt for AI Agents
In src/js/internal/test/reporter/dot.ts around lines 28 to 33, the header writes
colors.red then colors.white leaving the terminal in white; replace the trailing
colors.white with the colors reset sequence (e.g., colors.reset or the library's
reset token) so the color is properly reset after the "Failed tests:" header to
avoid color bleed.

}

function getLineLength() {
return MathMax(process.stdout.columns ?? 20, 20);
}

export default dot;
Loading