From 5c92ba222a6aa9a0032459d0b1a271c041643e84 Mon Sep 17 00:00:00 2001 From: Andrey Lushnikov Date: Fri, 20 Oct 2017 10:45:49 -0700 Subject: [PATCH] fix(page.evaluate): jsonValue should ignore toJSON property (#1098) Currently, JSHandle.jsonValue() is implemented as in-page JSON.stringify call and consequent JSON.parse in node. This approach proved to be unfortunate for automation purposes: if page author overrode the Object.prototype.toJSON method, then it's harder for puppeteer to interact with the page. This patch switches JSHandle.jsonValue to use protocol serialization that ignores toJSON property. THis also changes the `page.evaluate` behavior since it is based on JSHandle.jsonValue(). Fixes #1003. BREAKING CHANGE: `page.evaluate` no longer calls toJSON when generating return value. For the old behavior, do JSON.parse/JSON.stringify manually: ```js const json = JSON.parse(await page.evaluate(() => JSON.stringify(obj))); ``` --- docs/api.md | 4 +++- lib/ExecutionContext.js | 9 +++++++-- test/test.js | 6 +++--- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/docs/api.md b/docs/api.md index cefdf22853d..f733f7f23a0 100644 --- a/docs/api.md +++ b/docs/api.md @@ -1677,7 +1677,9 @@ Fetches a single property from the referenced object. #### jsHandle.jsonValue() - returns: <[Promise]<[Object]>> -Returns a JSON representation of the object. The JSON is generated by running [`JSON.stringify`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify) on the object in page and consequent [`JSON.parse`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse) in puppeteer. +Returns a JSON representation of the object. If the object has a +[`toJSON`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#toJSON()_behavior) +function, it **will not be called**. > **NOTE** The method will throw if the referenced object is not stringifiable. diff --git a/lib/ExecutionContext.js b/lib/ExecutionContext.js index 325cd801826..def4d7d0eeb 100644 --- a/lib/ExecutionContext.js +++ b/lib/ExecutionContext.js @@ -168,8 +168,13 @@ class JSHandle { */ async jsonValue() { if (this._remoteObject.objectId) { - const jsonString = await this._context.evaluate(object => JSON.stringify(object), this); - return JSON.parse(jsonString); + const response = await this._client.send('Runtime.callFunctionOn', { + functionDeclaration: 'function() { return this; }', + objectId: this._remoteObject.objectId, + returnByValue: true, + awaitPromise: true, + }); + return helper.valueFromRemoteObject(response.result); } return helper.valueFromRemoteObject(this._remoteObject); } diff --git a/test/test.js b/test/test.js index 32e03c0f059..26443dcda8a 100644 --- a/test/test.js +++ b/test/test.js @@ -429,16 +429,16 @@ describe('Page', function() { const json = await aHandle.jsonValue(); expect(json).toEqual({foo: 'bar'}); })); - it('should work with dates', SX(async function() { + it('should not work with dates', SX(async function() { const dateHandle = await page.evaluateHandle(() => new Date('2017-09-26T00:00:00.000Z')); const json = await dateHandle.jsonValue(); - expect(json).toBe('2017-09-26T00:00:00.000Z'); + expect(json).toEqual({}); })); it('should throw for circular objects', SX(async function() { const windowHandle = await page.evaluateHandle('window'); let error = null; await windowHandle.jsonValue().catch(e => error = e); - expect(error.message).toContain('Converting circular structure to JSON'); + expect(error.message).toContain('Object reference chain is too long'); })); });