diff --git a/lib/ExecutionContext.js b/lib/ExecutionContext.js index 25ed67e1..fd2c7d03 100644 --- a/lib/ExecutionContext.js +++ b/lib/ExecutionContext.js @@ -94,8 +94,26 @@ class ExecutionContext { if (typeof pageFunction !== 'function') throw new Error('The following is not a function: ' + pageFunction); + let functionText = pageFunction.toString(); + try { + new Function('(' + functionText + ')'); + } catch (e1) { + // This means we might have a function shorthand. Try another + // time prefixing 'function '. + if (functionText.startsWith('async ')) + functionText = 'async function ' + functionText.substring('async '.length); + else + functionText = 'function ' + functionText; + try { + new Function('(' + functionText + ')'); + } catch (e2) { + // We tried hard to serialize, but there's a weird beast here. + throw new Error('Passed function is not well-serializable!'); + } + } + const { exceptionDetails, result: remoteObject } = await this._client.send('Runtime.callFunctionOn', { - functionDeclaration: pageFunction.toString() + '\n' + suffix + '\n', + functionDeclaration: functionText + '\n' + suffix + '\n', executionContextId: this._contextId, arguments: args.map(convertArgument.bind(this)), returnByValue: false, diff --git a/test/page.spec.js b/test/page.spec.js index d9685587..15f95e08 100644 --- a/test/page.spec.js +++ b/test/page.spec.js @@ -23,6 +23,13 @@ const DeviceDescriptors = utils.requireRoot('DeviceDescriptors'); const iPhone = DeviceDescriptors['iPhone 6']; const iPhoneLandscape = DeviceDescriptors['iPhone 6 landscape']; +let asyncawait = true; +try { + new Function('async function foo() {await 1}'); +} catch (e) { + asyncawait = false; +} + module.exports.addTests = function({testRunner, expect, headless}) { const {describe, xdescribe, fdescribe} = testRunner; const {it, fit, xit} = testRunner; @@ -65,12 +72,6 @@ module.exports.addTests = function({testRunner, expect, headless}) { }); }); - let asyncawait = true; - try { - new Function('async function foo() {await 1}'); - } catch (e) { - asyncawait = false; - } (asyncawait ? describe : xdescribe)('Async stacks', () => { it('should work', async({page, server}) => { server.setRoute('/empty.html', (req, res) => { @@ -176,6 +177,17 @@ module.exports.addTests = function({testRunner, expect, headless}) { const result = await page.evaluate(() => 7 * 3); expect(result).toBe(21); }); + (asyncawait ? it : xit)('should work with function shorthands', async({page, server}) => { + // trick node6 transpiler to not touch our object. + // TODO(lushnikov): remove eval once Node6 is dropped. + const a = eval(`({ + sum(a, b) { return a + b; }, + + async mult(a, b) { return a * b; } + })`); + expect(await page.evaluate(a.sum, 1, 2)).toBe(3); + expect(await page.evaluate(a.mult, 2, 4)).toBe(8); + }); it('should throw when evaluation triggers reload', async({page, server}) => { let error = null; await page.evaluate(() => {