diff --git a/src/protocol/serializers.ts b/src/protocol/serializers.ts index 7e404c65b1..a324bb30c9 100644 --- a/src/protocol/serializers.ts +++ b/src/protocol/serializers.ts @@ -36,6 +36,7 @@ export function parseError(error: SerializedError): Error { } const e = new Error(error.error.message); e.stack = error.error.stack || ''; + e.name = error.error.name; return e; } diff --git a/src/server/chromium/crProtocolHelper.ts b/src/server/chromium/crProtocolHelper.ts index 2f544b5910..8794f51c4f 100644 --- a/src/server/chromium/crProtocolHelper.ts +++ b/src/server/chromium/crProtocolHelper.ts @@ -21,6 +21,7 @@ import fs from 'fs'; import * as util from 'util'; import * as types from '../types'; import { mkdirIfNeeded } from '../../utils/utils'; +import { splitErrorMessage } from '../../utils/stackTrace'; export function getExceptionMessage(exceptionDetails: Protocol.Runtime.ExceptionDetails): string { if (exceptionDetails.exception) @@ -74,18 +75,18 @@ export function exceptionToError(exceptionDetails: Protocol.Runtime.ExceptionDet const messageWithStack = getExceptionMessage(exceptionDetails); const lines = messageWithStack.split('\n'); const firstStackTraceLine = lines.findIndex(line => line.startsWith(' at')); - let message = ''; + let messageWithName = ''; let stack = ''; if (firstStackTraceLine === -1) { - message = messageWithStack; + messageWithName = messageWithStack; } else { - message = lines.slice(0, firstStackTraceLine).join('\n'); + messageWithName = lines.slice(0, firstStackTraceLine).join('\n'); stack = messageWithStack; } - const match = message.match(/^[a-zA-Z0-0_]*Error: (.*)$/); - if (match) - message = match[1]; + const {name, message} = splitErrorMessage(messageWithName); + const err = new Error(message); err.stack = stack; + err.name = name; return err; } diff --git a/src/server/firefox/ffPage.ts b/src/server/firefox/ffPage.ts index 394ecd5cca..523f910355 100644 --- a/src/server/firefox/ffPage.ts +++ b/src/server/firefox/ffPage.ts @@ -30,6 +30,7 @@ import { RawKeyboardImpl, RawMouseImpl, RawTouchscreenImpl } from './ffInput'; import { FFNetworkManager } from './ffNetworkManager'; import { Protocol } from './protocol'; import { Progress } from '../progress'; +import { splitErrorMessage } from '../../utils/stackTrace'; const UTILITY_WORLD_NAME = '__playwright_utility_world__'; @@ -221,9 +222,11 @@ export class FFPage implements PageDelegate { } _onUncaughtError(params: Protocol.Page.uncaughtErrorPayload) { - const message = params.message.startsWith('Error: ') ? params.message.substring(7) : params.message; + const {name, message} = splitErrorMessage(params.message); + const error = new Error(message); error.stack = params.stack; + error.name = name; this._page.emit(Page.Events.PageError, error); } diff --git a/src/server/webkit/wkPage.ts b/src/server/webkit/wkPage.ts index 4664127e10..8c5b615f82 100644 --- a/src/server/webkit/wkPage.ts +++ b/src/server/webkit/wkPage.ts @@ -18,6 +18,7 @@ import * as jpeg from 'jpeg-js'; import path from 'path'; import * as png from 'pngjs'; +import { splitErrorMessage } from '../../utils/stackTrace'; import { assert, createGuid, debugAssert, headersArrayToObject, headersObjectToArray } from '../../utils/utils'; import * as accessibility from '../accessibility'; import * as dialog from '../dialog'; @@ -490,7 +491,7 @@ export class WKPage implements PageDelegate { return; } if (level === 'error' && source === 'javascript') { - const message = text.startsWith('Error: ') ? text.substring(7) : text; + const {name, message} = splitErrorMessage(text); const error = new Error(message); if (event.message.stackTrace) { error.stack = event.message.stackTrace.map(callFrame => { @@ -499,6 +500,7 @@ export class WKPage implements PageDelegate { } else { error.stack = ''; } + error.name = name; this._page.emit(Page.Events.PageError, error); return; } diff --git a/src/utils/stackTrace.ts b/src/utils/stackTrace.ts index 385812dd61..6dc008f1a7 100644 --- a/src/utils/stackTrace.ts +++ b/src/utils/stackTrace.ts @@ -69,3 +69,11 @@ export function captureStackTrace(): { stack: string, frames: StackFrame[] } { } return { stack, frames }; } + +export function splitErrorMessage(message: string): { name: string, message: string } { + const separationIdx = message.indexOf(':'); + return { + name: separationIdx !== -1 ? message.slice(0, separationIdx) : '', + message: separationIdx !== -1 && separationIdx + 2 <= message.length ? message.substring(separationIdx + 2) : message, + }; +} diff --git a/tests/page-event-pageerror.spec.ts b/tests/page-event-pageerror.spec.ts index ee92207cf5..80ca2805b7 100644 --- a/tests/page-event-pageerror.spec.ts +++ b/tests/page-event-pageerror.spec.ts @@ -41,7 +41,37 @@ it('should contain sourceURL', async ({page, server, isWebKit}) => { expect(error.stack).toContain('myscript.js'); }); -it('should handle odd values', async ({page, isFirefox}) => { +it('should contain the Error.name property', async ({ page }) => { + const [error] = await Promise.all([ + page.waitForEvent('pageerror'), + page.evaluate(() => { + setTimeout(() => { + const error = new Error('my-message'); + error.name = 'my-name'; + throw error; + }, 0); + }) + ]); + expect(error.name).toBe('my-name'); + expect(error.message).toBe('my-message'); +}); + +it('should support an empty Error.name property', async ({ page }) => { + const [error] = await Promise.all([ + page.waitForEvent('pageerror'), + page.evaluate(() => { + setTimeout(() => { + const error = new Error('my-message'); + error.name = ''; + throw error; + }, 0); + }) + ]); + expect(error.name).toBe(''); + expect(error.message).toBe('my-message'); +}); + +it('should handle odd values', async ({page}) => { const cases = [ [null, 'null'], [undefined, 'undefined'], @@ -53,14 +83,11 @@ it('should handle odd values', async ({page, isFirefox}) => { page.waitForEvent('pageerror'), page.evaluate(value => setTimeout(() => { throw value; }, 0), value), ]); - expect(error.message).toBe(isFirefox ? 'uncaught exception: ' + message : message); + expect(error.message).toBe(message); } }); -it('should handle object', async ({page, isChromium, isFirefox}) => { - it.fixme(isFirefox); - - // Firefox just does not report this error. +it('should handle object', async ({page, isChromium}) => { const [error] = await Promise.all([ page.waitForEvent('pageerror'), page.evaluate(() => setTimeout(() => { throw {}; }, 0)), @@ -69,10 +96,7 @@ it('should handle object', async ({page, isChromium, isFirefox}) => { }); it('should handle window', async ({page, isChromium, isFirefox, isElectron}) => { - it.fixme(isFirefox); it.skip(isElectron); - - // Firefox just does not report this error. const [error] = await Promise.all([ page.waitForEvent('pageerror'), page.evaluate(() => setTimeout(() => { throw window; }, 0)),