From 995560fad01c7b57d78af7833f165a8fd5238a57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20Z=C3=BCnd?= Date: Fri, 6 Feb 2026 08:14:35 +0100 Subject: [PATCH] chore: implement formatting of Error console.log arguments --- src/DevtoolsUtils.ts | 8 ++++ src/formatters/ConsoleFormatter.ts | 12 +++++- .../ConsoleFormatter.test.js.snapshot | 10 +++++ tests/formatters/ConsoleFormatter.test.ts | 39 +++++++++++++++++++ 4 files changed, 68 insertions(+), 1 deletion(-) diff --git a/src/DevtoolsUtils.ts b/src/DevtoolsUtils.ts index d6058169d..38a66bce4 100644 --- a/src/DevtoolsUtils.ts +++ b/src/DevtoolsUtils.ts @@ -292,6 +292,14 @@ export class SymbolizedError { } return details.text; } + + static createForTesting( + message: string, + stackTrace?: DevTools.StackTrace.StackTrace.StackTrace, + cause?: SymbolizedError, + ) { + return new SymbolizedError(message, stackTrace, cause); + } } export async function createStackTraceForConsoleMessage( diff --git a/src/formatters/ConsoleFormatter.ts b/src/formatters/ConsoleFormatter.ts index 40e265f4d..71968393e 100644 --- a/src/formatters/ConsoleFormatter.ts +++ b/src/formatters/ConsoleFormatter.ts @@ -136,6 +136,14 @@ export class ConsoleFormatter { } #formatArg(arg: unknown) { + if (arg instanceof SymbolizedError) { + return [ + arg.message, + this.#formatStackTrace(arg.stackTrace, /* includeHeading */ false), + ] + .filter(line => !!line) + .join('\n'); + } return typeof arg === 'object' ? JSON.stringify(arg) : String(arg); } @@ -157,13 +165,15 @@ export class ConsoleFormatter { #formatStackTrace( stackTrace: DevTools.DevTools.StackTrace.StackTrace.StackTrace | undefined, + includeHeading = true, ): string { if (!stackTrace) { return ''; } + const heading = includeHeading ? ['### Stack trace'] : []; return [ - '### Stack trace', + ...heading, this.#formatFragment(stackTrace.syncFragment), ...stackTrace.asyncFragments.map(this.#formatAsyncFragment.bind(this)), 'Note: line and column numbers use 1-based indexing', diff --git a/tests/formatters/ConsoleFormatter.test.js.snapshot b/tests/formatters/ConsoleFormatter.test.js.snapshot index 67a6e59e9..87c16e94b 100644 --- a/tests/formatters/ConsoleFormatter.test.js.snapshot +++ b/tests/formatters/ConsoleFormatter.test.js.snapshot @@ -25,6 +25,16 @@ at schedule (util.ts:5:2) Note: line and column numbers use 1-based indexing `; +exports[`ConsoleFormatter > toStringDetailed > formats a console message with an Error object argument 1`] = ` +ID: 8 +Message: log> JSHandle@error +### Arguments +Arg #0: TypeError: Cannot read properties of undefined +at foo (foo.ts:10:2) +at bar (foo.ts:20:2) +Note: line and column numbers use 1-based indexing +`; + exports[`ConsoleFormatter > toStringDetailed > formats a console.error message 1`] = ` ID: 4 Message: error> Something went wrong diff --git a/tests/formatters/ConsoleFormatter.test.ts b/tests/formatters/ConsoleFormatter.test.ts index 58e727f3f..efc1a3b72 100644 --- a/tests/formatters/ConsoleFormatter.test.ts +++ b/tests/formatters/ConsoleFormatter.test.ts @@ -7,6 +7,7 @@ import assert from 'node:assert'; import {describe, it} from 'node:test'; +import {SymbolizedError} from '../../src/DevtoolsUtils.js'; import {ConsoleFormatter} from '../../src/formatters/ConsoleFormatter.js'; import {UncaughtError} from '../../src/PageCollector.js'; import type {ConsoleMessage} from '../../src/third_party/index.js'; @@ -260,6 +261,44 @@ describe('ConsoleFormatter', () => { ).toStringDetailed(); t.assert.snapshot?.(result); }); + + it('formats a console message with an Error object argument', async t => { + const message = createMockMessage({ + type: () => 'log', + text: () => 'JSHandle@error', + }); + const stackTrace = { + syncFragment: { + frames: [ + { + line: 10, + column: 2, + url: 'foo.ts', + name: 'foo', + }, + { + line: 20, + column: 2, + url: 'foo.ts', + name: 'bar', + }, + ], + }, + asyncFragments: [], + } as unknown as DevTools.StackTrace.StackTrace.StackTrace; + const error = SymbolizedError.createForTesting( + 'TypeError: Cannot read properties of undefined', + stackTrace, + ); + + const result = ( + await ConsoleFormatter.from(message, { + id: 8, + resolvedArgsForTesting: [error], + }) + ).toStringDetailed(); + t.assert.snapshot?.(result); + }); }); describe('toJSON', () => { it('formats a console.log message', async () => {