From e34fa4feeb2a8d3710efb7aeae0b7c446471518a Mon Sep 17 00:00:00 2001 From: Max Schmitt Date: Wed, 29 Jun 2022 08:07:32 +0200 Subject: [PATCH] fix(evaluate): fallback to toJSON if it exists when serializing (#15188) * fix(evaluate): fallback to toJSON if it exists when serializing * fix test in ff * window.performance test --- .../isomorphic/utilityScriptSerializers.ts | 11 ++++- tests/page/page-evaluate.spec.ts | 47 +++++++++++++++++++ 2 files changed, 57 insertions(+), 1 deletion(-) diff --git a/packages/playwright-core/src/server/isomorphic/utilityScriptSerializers.ts b/packages/playwright-core/src/server/isomorphic/utilityScriptSerializers.ts index b10a6dc879..e23844aa58 100644 --- a/packages/playwright-core/src/server/isomorphic/utilityScriptSerializers.ts +++ b/packages/playwright-core/src/server/isomorphic/utilityScriptSerializers.ts @@ -42,7 +42,11 @@ export function source() { } function isError(obj: any): obj is Error { - return obj instanceof Error || (obj && obj.__proto__ && obj.__proto__.name === 'Error'); + try { + return obj instanceof Error || (obj && obj.__proto__ && obj.__proto__.name === 'Error'); + } catch (error) { + return false; + } } function parseEvaluationResultValue(value: SerializedValue, handles: any[] = [], refs: Map = new Map()): any { @@ -177,6 +181,11 @@ export function source() { else o.push({ k: name, v: serialize(item, handleSerializer, visitorInfo) }); } + + // If Object.keys().length === 0 we fall back to toJSON if it exists + if (o.length === 0 && value.toJSON && typeof value.toJSON === 'function') + return innerSerialize(value.toJSON(), handleSerializer, visitorInfo); + return { o, id }; } } diff --git a/tests/page/page-evaluate.spec.ts b/tests/page/page-evaluate.spec.ts index 5e56fe9fec..6ee02367e9 100644 --- a/tests/page/page-evaluate.spec.ts +++ b/tests/page/page-evaluate.spec.ts @@ -330,6 +330,53 @@ it('should properly serialize null fields', async ({ page }) => { expect(await page.evaluate(() => ({ a: null }))).toEqual({ a: null }); }); +it('should properly serialize PerformanceMeasure object', async ({ page }) => { + expect(await page.evaluate(() => { + window.performance.mark('start'); + window.performance.mark('end'); + window.performance.measure('my-measure', 'start', 'end'); + return performance.getEntriesByType('measure'); + })).toEqual([{ + duration: expect.any(Number), + entryType: 'measure', + name: 'my-measure', + startTime: expect.any(Number), + }]); +}); + +it('shuld properly serialize window.performance object', async ({ page }) => { + expect(await page.evaluate(() => performance)).toEqual({ + 'navigation': { + 'redirectCount': 0, + 'type': 0 + }, + 'timeOrigin': expect.any(Number), + 'timing': { + 'connectEnd': expect.any(Number), + 'connectStart': expect.any(Number), + 'domComplete': expect.any(Number), + 'domContentLoadedEventEnd': expect.any(Number), + 'domContentLoadedEventStart': expect.any(Number), + 'domInteractive': expect.any(Number), + 'domLoading': expect.any(Number), + 'domainLookupEnd': expect.any(Number), + 'domainLookupStart': expect.any(Number), + 'fetchStart': expect.any(Number), + 'loadEventEnd': expect.any(Number), + 'loadEventStart': expect.any(Number), + 'navigationStart': expect.any(Number), + 'redirectEnd': expect.any(Number), + 'redirectStart': expect.any(Number), + 'requestStart': expect.any(Number), + 'responseEnd': expect.any(Number), + 'responseStart': expect.any(Number), + 'secureConnectionStart': expect.any(Number), + 'unloadEventEnd': expect.any(Number), + 'unloadEventStart': expect.any(Number), + } + }); +}); + it('should return undefined for non-serializable objects', async ({ page }) => { expect(await page.evaluate(() => function() {})).toBe(undefined); });