豆豆友情提示:这是一个非官方 GitHub 代理镜像,主要用于网络测试或访问加速。请勿在此进行登录、注册或处理任何敏感信息。进行这些操作请务必访问官方网站 github.com。 Raw 内容也通过此代理提供。
Skip to content
Merged
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
13 changes: 11 additions & 2 deletions src/DevtoolsUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -255,10 +255,15 @@ export class SymbolizedError {
targetId: string;
includeStackAndCause?: boolean;
resolvedStackTraceForTesting?: DevTools.StackTrace.StackTrace.StackTrace;
resolvedCauseForTesting?: SymbolizedError;
}): Promise<SymbolizedError> {
const message = SymbolizedError.#getMessage(opts.details);
if (!opts.includeStackAndCause || !opts.devTools) {
return new SymbolizedError(message, opts.resolvedStackTraceForTesting);
return new SymbolizedError(
message,
opts.resolvedStackTraceForTesting,
opts.resolvedCauseForTesting,
);
}

let stackTrace: DevTools.StackTrace.StackTrace.StackTrace | undefined;
Expand All @@ -278,7 +283,11 @@ export class SymbolizedError {

// TODO: Turn opts.details.exception into a JSHandle and retrieve the 'cause' property.
// If its an Error, recursively create a SymbolizedError.
return new SymbolizedError(message, stackTrace);
let cause: SymbolizedError | undefined;
if (opts.resolvedCauseForTesting) {
cause = opts.resolvedCauseForTesting;
}
return new SymbolizedError(message, stackTrace, cause);
}

static async fromError(opts: {
Expand Down
46 changes: 38 additions & 8 deletions src/formatters/ConsoleFormatter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export interface ConsoleFormatterOptions {
devTools?: TargetUniverse;
resolvedArgsForTesting?: unknown[];
resolvedStackTraceForTesting?: DevTools.DevTools.StackTrace.StackTrace.StackTrace;
resolvedCauseForTesting?: SymbolizedError;
}

export class ConsoleFormatter {
Expand All @@ -30,7 +31,7 @@ export class ConsoleFormatter {
readonly #resolvedArgs: unknown[];

readonly #stack?: DevTools.DevTools.StackTrace.StackTrace.StackTrace;
readonly #cause?: SymbolizedError; // eslint-disable-line no-unused-private-class-members
readonly #cause?: SymbolizedError;

private constructor(params: {
id: number;
Expand Down Expand Up @@ -61,6 +62,7 @@ export class ConsoleFormatter {
targetId: msg.targetId,
includeStackAndCause: options?.fetchDetailedData,
resolvedStackTraceForTesting: options?.resolvedStackTraceForTesting,
resolvedCauseForTesting: options?.resolvedCauseForTesting,
});
return new ConsoleFormatter({
id: options.id,
Expand Down Expand Up @@ -130,7 +132,10 @@ export class ConsoleFormatter {
`ID: ${this.#id}`,
`Message: ${this.#type}> ${this.#text}`,
this.#formatArgs(),
this.#formatStackTrace(this.#stack),
this.#formatStackTrace(this.#stack, this.#cause, {
includeHeading: true,
includeNote: true,
}),
].filter(line => !!line);
return result.join('\n');
}
Expand All @@ -151,7 +156,10 @@ export class ConsoleFormatter {
if (arg instanceof SymbolizedError) {
return [
arg.message,
this.#formatStackTrace(arg.stackTrace, /* includeHeading */ false),
this.#formatStackTrace(arg.stackTrace, arg.cause, {
includeHeading: false,
includeNote: true,
}),
]
.filter(line => !!line)
.join('\n');
Expand All @@ -177,19 +185,24 @@ export class ConsoleFormatter {

#formatStackTrace(
stackTrace: DevTools.DevTools.StackTrace.StackTrace.StackTrace | undefined,
includeHeading = true,
cause: SymbolizedError | undefined,
opts: {includeHeading: boolean; includeNote: boolean},
): string {
if (!stackTrace) {
return '';
}

const heading = includeHeading ? ['### Stack trace'] : [];
return [
...heading,
opts.includeHeading ? '### Stack trace' : '',
this.#formatFragment(stackTrace.syncFragment),
...stackTrace.asyncFragments.map(this.#formatAsyncFragment.bind(this)),
'Note: line and column numbers use 1-based indexing',
].join('\n');
this.#formatCause(cause),
opts.includeNote
? 'Note: line and column numbers use 1-based indexing'
: '',
]
.filter(line => !!line)
.join('\n');
}

#formatFragment(
Expand Down Expand Up @@ -217,6 +230,23 @@ export class ConsoleFormatter {
}
return result;
}

#formatCause(cause: SymbolizedError | undefined): string {
if (!cause) {
return '';
}

return [
`Caused by: ${cause.message}`,
this.#formatStackTrace(cause.stackTrace, cause.cause, {
includeHeading: false,
includeNote: false,
}),
]
.filter(line => !!line)
.join('\n');
}

toJSON(): object {
return {
type: this.#type,
Expand Down
25 changes: 25 additions & 0 deletions tests/formatters/ConsoleFormatter.test.js.snapshot
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,18 @@ at bar (foo.ts:20:2)
Note: line and column numbers use 1-based indexing
`;

exports[`ConsoleFormatter > toStringDetailed > formats a console message with an Error object with cause 1`] = `
ID: 9
Message: log> JSHandle@error
### Arguments
Arg #0: AppError: Compute failed
at foo (foo.ts:10:2)
at bar (foo.ts:20:2)
Caused by: TypeError: Cannot read properties of undefined
at compute (library.js:5:10)
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
Expand Down Expand Up @@ -71,6 +83,19 @@ at schedule (util.ts:5:2)
Note: line and column numbers use 1-based indexing
`;

exports[`ConsoleFormatter > toStringDetailed > formats an UncaughtError with a stack trace and a cause 1`] = `
ID: 10
Message: error> Uncaught TypeError: Cannot read properties of undefined
### Stack trace
at foo (foo.ts:10:2)
at bar (foo.ts:20:2)
--- setTimeout -------------------------
at schedule (util.ts:5:2)
Caused by: TypeError: Cannot read properties of undefined
at compute (library.js:5:8)
Note: line and column numbers use 1-based indexing
`;

exports[`ConsoleFormatter > toStringDetailed > handles \"Execution context is not available\" error in args 1`] = `
ID: 6
Message: log> Processing file:
Expand Down
126 changes: 126 additions & 0 deletions tests/formatters/ConsoleFormatter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,132 @@ describe('ConsoleFormatter', () => {
).toStringDetailed();
t.assert.snapshot?.(result);
});

it('formats a console message with an Error object with cause', 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(
'AppError: Compute failed',
stackTrace,
SymbolizedError.createForTesting(
'TypeError: Cannot read properties of undefined',
{
syncFragment: {
frames: [
{
line: 5,
column: 10,
url: 'library.js',
name: 'compute',
},
],
},
asyncFragments: [],
} as unknown as DevTools.StackTrace.StackTrace.StackTrace,
),
);

const result = (
await ConsoleFormatter.from(message, {
id: 9,
resolvedArgsForTesting: [error],
})
).toStringDetailed();
t.assert.snapshot?.(result);
});

it('formats an UncaughtError with a stack trace and a cause', async t => {
const stackTrace = {
syncFragment: {
frames: [
{
line: 10,
column: 2,
url: 'foo.ts',
name: 'foo',
},
{
line: 20,
column: 2,
url: 'foo.ts',
name: 'bar',
},
],
},
asyncFragments: [
{
description: 'setTimeout',
frames: [
{
line: 5,
column: 2,
url: 'util.ts',
name: 'schedule',
},
],
},
],
} as unknown as DevTools.StackTrace.StackTrace.StackTrace;
const error = new UncaughtError(
{
exceptionId: 1,
lineNumber: 0,
columnNumber: 5,
exception: {
type: 'object',
description: 'TypeError: Cannot read properties of undefined',
},
text: 'Uncaught',
},
'<mock target ID>',
);
const cause = SymbolizedError.createForTesting(
'TypeError: Cannot read properties of undefined',
{
syncFragment: {
frames: [
{
line: 5,
column: 8,
url: 'library.js',
name: 'compute',
},
],
},
asyncFragments: [],
} as unknown as DevTools.StackTrace.StackTrace.StackTrace,
);

const result = (
await ConsoleFormatter.from(error, {
id: 10,
resolvedStackTraceForTesting: stackTrace,
resolvedCauseForTesting: cause,
})
).toStringDetailed();
t.assert.snapshot?.(result);
});
});
describe('toJSON', () => {
it('formats a console.log message', async () => {
Expand Down