Compare commits

...

1 Commits

Author SHA1 Message Date
Claude Bot
1910a50bd6 Fix console output suppression when happy-dom overrides console
Fixes #6044

When DOM libraries like happy-dom override globalThis.console with their
VirtualConsole implementation, console output was silently suppressed
during tests because the VirtualConsole doesn't print to stdout/stderr
by default.

This fix implements console preservation by:
1. Storing the original Bun console when tests start
2. Setting up a property interceptor for globalThis.console
3. When a non-Bun console is assigned, replacing it with a bridged
   console that forwards all calls to Bun's original console

The solution maintains compatibility with DOM libraries while ensuring
console output remains visible during test execution.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-04 22:32:55 +00:00
2 changed files with 110 additions and 0 deletions

View File

@@ -355,6 +355,12 @@ pub const Jest = struct {
else
.{ TestScope, DescribeScope };
// Preserve console functionality for test environment
// This ensures that DOM libraries like happy-dom don't break console output
if (!outside_of_test) {
preserveConsoleForTests(globalObject);
}
const module = JSValue.createEmptyObject(globalObject, 14);
const test_fn = jsc.host_fn.NewFunction(globalObject, ZigString.static("test"), 2, ThisTestScope.call, false);
@@ -451,6 +457,69 @@ pub const Jest = struct {
return module;
}
/// Preserve console functionality for test environments when DOM libraries override it
fn preserveConsoleForTests(globalObject: *JSGlobalObject) void {
// Execute JavaScript code to set up console preservation
const console_preserve_code =
\\(() => {
\\ // Store the original Bun console
\\ const originalConsole = globalThis.console;
\\ const bunConsoleSymbol = Symbol.for('Bun.originalConsole');
\\ originalConsole[bunConsoleSymbol] = true;
\\
\\ // Create a bridged console that always forwards to Bun's console
\\ const bridgedMethods = ['log', 'error', 'warn', 'info', 'debug', 'trace', 'assert', 'clear', 'count', 'countReset', 'dir', 'dirxml', 'group', 'groupCollapsed', 'groupEnd', 'table', 'time', 'timeEnd', 'timeLog'];
\\ const bridgedConsole = {};
\\
\\ for (const method of bridgedMethods) {
\\ bridgedConsole[method] = function(...args) {
\\ return originalConsole[method](...args);
\\ };
\\ }
\\
\\ // Copy Console constructor and other properties
\\ bridgedConsole.Console = originalConsole.Console;
\\
\\ // Set up property descriptor to intercept console assignment
\\ const descriptor = Object.getOwnPropertyDescriptor(globalThis, 'console');
\\ let currentConsole = originalConsole;
\\
\\ Object.defineProperty(globalThis, 'console', {
\\ get() {
\\ return currentConsole;
\\ },
\\ set(newConsole) {
\\ // Check if this is not Bun's original console (e.g., happy-dom's VirtualConsole)
\\ const isBunConsole = newConsole && newConsole[bunConsoleSymbol];
\\
\\ if (!isBunConsole && newConsole && typeof newConsole.log === 'function') {
\\ // Use bridged console that forwards to Bun's original console
\\ currentConsole = bridgedConsole;
\\ } else {
\\ currentConsole = newConsole || originalConsole;
\\ }
\\ },
\\ configurable: true,
\\ enumerable: true
\\ });
\\})();
;
// Execute the console preservation code
var exception: JSValue = .zero;
_ = jsc.JSModuleLoader.evaluate(
globalObject,
console_preserve_code.ptr,
console_preserve_code.len,
"bun:test:console-preserve".ptr,
"bun:test:console-preserve".len,
"".ptr,
0,
globalObject.toJSValue(),
@ptrCast(&exception),
);
}
fn createMockObjects(globalObject: *JSGlobalObject, module: JSValue) void {
const setSystemTime = jsc.host_fn.NewFunction(globalObject, ZigString.static("setSystemTime"), 0, JSMock__jsSetSystemTime, false);
module.put(

View File

@@ -0,0 +1,41 @@
// Regression test for issue #6044: happy-dom causes console.log() to not print during tests
// https://github.com/oven-sh/bun/issues/6044
import { test, expect, describe } from "bun:test";
import { Window } from "happy-dom";
describe("Console preservation with happy-dom", () => {
test("console.log should work when happy-dom overrides console", () => {
const window = new Window();
globalThis.window = window;
globalThis.document = window.document;
globalThis.console = window.console; // This previously broke console output
// These console calls should now appear in the output thanks to the fix
console.log("console.log works with happy-dom override");
console.error("console.error works with happy-dom override");
expect(true).toBe(true);
});
test("console works with multiple happy-dom instances", () => {
// Test with multiple DOM setups that override console
for (let i = 0; i < 2; i++) {
const window = new Window();
globalThis.console = window.console;
console.log(`Console works with happy-dom instance ${i}`);
}
expect(true).toBe(true);
});
test("console methods with complex arguments", () => {
const window = new Window();
globalThis.console = window.console;
console.log("Complex object:", { key: "value", nested: { prop: 123 } });
console.log("Multiple arguments:", "string", 42, true, null);
expect(true).toBe(true);
});
});