diff --git a/lib/FrameManager.js b/lib/FrameManager.js index 73fc8a64..c7a2abc3 100644 --- a/lib/FrameManager.js +++ b/lib/FrameManager.js @@ -169,29 +169,7 @@ class FrameManager extends EventEmitter { let message = await helper.getExceptionMessage(this._client, exceptionDetails); throw new Error('Evaluation failed: ' + message); } - if (remoteObject.unserializableValue) { - switch (remoteObject.unserializableValue) { - case '-0': - return -0; - case 'NaN': - return NaN; - case 'Infinity': - return Infinity; - case '-Infinity': - return -Infinity; - default: - throw new Error('Unsupported unserializable value: ' + remoteObject.unserializableValue); - } - } - if (!remoteObject.objectId) - return remoteObject.value; - let response = await this._client.send('Runtime.callFunctionOn', { - objectId: remoteObject.objectId, - functionDeclaration: 'function() { return this; }', - returnByValue: true, - }); - this._client.send('Runtime.releaseObject', {objectId: remoteObject.objectId}); - return response.result.value; + return await helper.serializeRemoteObject(this._client, remoteObject); } /** diff --git a/lib/Page.js b/lib/Page.js index 700c413b..b42953dc 100644 --- a/lib/Page.js +++ b/lib/Page.js @@ -223,8 +223,8 @@ class Page extends EventEmitter { } return; } - let values = event.args.map(arg => arg.value || arg.description || ''); - this.emit(Page.Events.ConsoleMessage, values.join(' ')); + let values = await Promise.all(event.args.map(arg => helper.serializeRemoteObject(this._client, arg))); + this.emit(Page.Events.ConsoleMessage, ...values); } _onDialog(event) { diff --git a/lib/helper.js b/lib/helper.js index 794090f9..01eebd60 100644 --- a/lib/helper.js +++ b/lib/helper.js @@ -52,6 +52,37 @@ class Helper { } return message; } + + /** + * @param {!Connection} client + * @param {!Object} remoteObject + * @return {!Object} + */ + static async serializeRemoteObject(client, remoteObject) { + if (remoteObject.unserializableValue) { + switch (remoteObject.unserializableValue) { + case '-0': + return -0; + case 'NaN': + return NaN; + case 'Infinity': + return Infinity; + case '-Infinity': + return -Infinity; + default: + throw new Error('Unsupported unserializable value: ' + remoteObject.unserializableValue); + } + } + if (!remoteObject.objectId) + return remoteObject.value; + let response = await client.send('Runtime.callFunctionOn', { + objectId: remoteObject.objectId, + functionDeclaration: 'function() { return this; }', + returnByValue: true, + }); + client.send('Runtime.releaseObject', {objectId: remoteObject.objectId}); + return response.result.value; + } } module.exports = Helper; diff --git a/phantom_shim/WebPage.js b/phantom_shim/WebPage.js index 4c453aaf..def5ba16 100644 --- a/phantom_shim/WebPage.js +++ b/phantom_shim/WebPage.js @@ -62,7 +62,7 @@ class WebPage { this._pageEvents.on(PageEvents.Response, response => this._onResponseReceived(response)); this._pageEvents.on(PageEvents.RequestFinished, request => this._onRequestFinished(request)); this._pageEvents.on(PageEvents.RequestFailed, event => (this.onResourceError || noop).call(null, event)); - this._pageEvents.on(PageEvents.ConsoleMessage, msg => (this.onConsoleMessage || noop).call(null, msg)); + this._pageEvents.on(PageEvents.ConsoleMessage, (...args) => this._onConsoleMessage(...args)); this._pageEvents.on(PageEvents.Confirm, message => this._onConfirm(message)); this._pageEvents.on(PageEvents.Alert, message => this._onAlert(message)); this._pageEvents.on(PageEvents.Dialog, dialog => this._onDialog(dialog)); @@ -81,6 +81,16 @@ class WebPage { }; } + /** + * @param {!Array} args + */ + _onConsoleMessage(...args) { + if (!this.onConsoleMessage) + return; + const text = args.join(' '); + this.onConsoleMessage(text); + } + /** * @return {string} */ diff --git a/test/test.js b/test/test.js index 8697a7b2..b4b3dc3e 100644 --- a/test/test.js +++ b/test/test.js @@ -226,12 +226,37 @@ describe('Puppeteer', function() { })); }); - it('Page Events: ConsoleMessage', SX(async function() { - let msgs = []; - page.on('consolemessage', msg => msgs.push(msg)); - await page.evaluate(() => console.log('Message!')); - expect(msgs).toEqual(['Message!']); - })); + describe('Page.Events.ConsoleMessage', function() { + it('should work', SX(async function() { + let commandArgs = []; + page.once('consolemessage', (...args) => commandArgs = args); + page.evaluate(() => console.log(5, 'hello', {foo: 'bar'})); + await waitForEvents(page, 'consolemessage'); + expect(commandArgs).toEqual([5, 'hello', {foo: 'bar'}]); + })); + it('should work for different console API calls', SX(async function() { + let messages = []; + page.on('consolemessage', msg => messages.push(msg)); + page.evaluate(() => { + // A pair of time/timeEnd generates only one Console API call. + console.time('calling console.time'); + console.timeEnd('calling console.time'); + console.trace('calling console.trace'); + console.dir('calling console.dir'); + console.warn('calling console.warn'); + console.error('calling console.error'); + }); + // Wait for 5 events to hit. + await waitForEvents(page, 'consolemessage', 5); + expect(messages[0]).toContain('calling console.time'); + expect(messages.slice(1)).toEqual([ + 'calling console.trace', + 'calling console.dir', + 'calling console.warn', + 'calling console.error', + ]); + })); + }); describe('Page.navigate', function() { it('should fail when navigating to bad url', SX(async function() { @@ -990,6 +1015,27 @@ describe('Puppeteer', function() { }); }); +/** + * @param {!EventEmitter} emitter + * @param {string} eventName + * @param {number=} eventCount + * @return {!Promise} + */ +function waitForEvents(emitter, eventName, eventCount = 1) { + let fulfill; + let promise = new Promise(x => fulfill = x); + emitter.on(eventName, onEvent); + return promise; + + function onEvent() { + --eventCount; + if (eventCount) + return; + emitter.removeListener(eventName, onEvent); + fulfill(); + } +} + // Since Jasmine doesn't like async functions, they should be wrapped // in a SX function. function SX(fun) {