diff --git a/docs/api.md b/docs/api.md index 329f683097750..ea67d0b954106 100644 --- a/docs/api.md +++ b/docs/api.md @@ -185,6 +185,7 @@ * [dialog.type()](#dialogtype) - [class: ConsoleMessage](#class-consolemessage) * [consoleMessage.args()](#consolemessageargs) + * [consoleMessage.location()](#consolemessagelocation) * [consoleMessage.text()](#consolemessagetext) * [consoleMessage.type()](#consolemessagetype) - [class: Frame](#class-frame) @@ -2315,6 +2316,12 @@ puppeteer.launch().then(async browser => { #### consoleMessage.args() - returns: <[Array]<[JSHandle]>> +#### consoleMessage.location() +- returns: <[Object]> + - `url` <[string]> URL of the resource if known + - `lineNumber` <[number]> line number in the resource + - `columnNumber` <[number]> line number in the resource + #### consoleMessage.text() - returns: <[string]> diff --git a/lib/Page.js b/lib/Page.js index 592cb3dfb0144..1e7555a9140e4 100644 --- a/lib/Page.js +++ b/lib/Page.js @@ -187,11 +187,11 @@ class Page extends EventEmitter { * @param {!Protocol.Log.entryAddedPayload} event */ _onLogEntryAdded(event) { - const {level, text, args, source} = event.entry; + const {level, text, args, source, url, lineNumber} = event.entry; if (args) args.map(arg => helper.releaseObject(this._client, arg)); if (source !== 'worker') - this.emit(Page.Events.Console, new ConsoleMessage(level, text)); + this.emit(Page.Events.Console, new ConsoleMessage(level, text, undefined, { url, lineNumber })); } /** @@ -510,7 +510,14 @@ class Page extends EventEmitter { async _onConsoleAPI(event) { const context = this._frameManager.executionContextById(event.executionContextId); const values = event.args.map(arg => createJSHandle(context, arg)); - this._addConsoleMessage(event.type, values); + const location = {}; + if (event.stackTrace && event.stackTrace.callFrames) { + const {url, lineNumber, columnNumber} = event.stackTrace.callFrames[0]; + if (url) location.url = url; + if (lineNumber) location.lineNumber = lineNumber; + if (columnNumber) location.columnNumber = columnNumber; + } + this._addConsoleMessage(event.type, values, location); } /** @@ -567,8 +574,9 @@ class Page extends EventEmitter { /** * @param {string} type * @param {!Array} args + * @param {ConsoleMessage.Location} location */ - _addConsoleMessage(type, args) { + _addConsoleMessage(type, args, location) { if (!this.listenerCount(Page.Events.Console)) { args.forEach(arg => arg.dispose()); return; @@ -581,7 +589,7 @@ class Page extends EventEmitter { else textTokens.push(helper.valueFromRemoteObject(remoteObject)); } - const message = new ConsoleMessage(type, textTokens.join(' '), args); + const message = new ConsoleMessage(type, textTokens.join(' '), args, location); this.emit(Page.Events.Console, message); } @@ -1226,16 +1234,25 @@ Page.Events = { * @property {("Strict"|"Lax")=} sameSite */ +/** + * @typedef {Object} ConsoleMessage.Location + * @property {string=} url + * @property {number=} lineNumber + * @property {number=} columnNumber + */ + class ConsoleMessage { /** * @param {string} type * @param {string} text * @param {!Array} args + * @param {ConsoleMessage.Location} location */ - constructor(type, text, args = []) { + constructor(type, text, args = [], location = {}) { this._type = type; this._text = text; this._args = args; + this._location = location; } /** @@ -1258,6 +1275,13 @@ class ConsoleMessage { args() { return this._args; } + + /** + * @return {Object} + */ + location() { + return this._location; + } } diff --git a/test/assets/console-message-1.html b/test/assets/console-message-1.html new file mode 100644 index 0000000000000..3336525736e94 --- /dev/null +++ b/test/assets/console-message-1.html @@ -0,0 +1,11 @@ + + + + Log Entry Test + + + + + diff --git a/test/assets/console-message-2.html b/test/assets/console-message-2.html new file mode 100644 index 0000000000000..ae576efc99249 --- /dev/null +++ b/test/assets/console-message-2.html @@ -0,0 +1,11 @@ + + + + Log Entry Test + + + + + diff --git a/test/page.spec.js b/test/page.spec.js index 8cc9557c796e3..588a30054f1d4 100644 --- a/test/page.spec.js +++ b/test/page.spec.js @@ -322,6 +322,41 @@ module.exports.addTests = function({testRunner, expect, headless}) { expect(message.text()).toContain('No \'Access-Control-Allow-Origin\''); expect(message.type()).toEqual('error'); }); + it('should show correct additional info when console event emitted for logEntry', async({page, server}) => { + let message = null; + page.on('console', msg => { + message = msg; + }); + await Promise.all([ + page.goto(server.PREFIX + '/console-message-1.html'), + waitEvent(page, 'console'), + ]); + await new Promise(resolve => setTimeout(resolve, 5000)); + expect(message.text()).toContain(`ERR_NAME_NOT_RESOLVED`); + expect(message.type()).toEqual('error'); + expect(message.location()).toEqual({ + url: 'http://wat/', + lineNumber: undefined + }); + }); + it('should show correct additional info when console event emitted for consoleAPI', async({page, server}) => { + let message = null; + page.on('console', msg => { + message = msg; + }); + await Promise.all([ + page.goto(server.PREFIX + '/console-message-2.html'), + waitEvent(page, 'console'), + ]); + await new Promise(resolve => setTimeout(resolve, 5000)); + expect(message.text()).toContain(`wat`); + expect(message.type()).toEqual('warning'); + expect(message.location()).toEqual({ + url: `http://localhost:${server.PORT}/console-message-2.html`, + lineNumber: 7, + columnNumber: 16 + }); + }); }); describe('Page.Events.DOMContentLoaded', function() {