diff --git a/packages/playwright-core/src/protocol/channels.ts b/packages/playwright-core/src/protocol/channels.ts index ba439e1309..1f32cff4ba 100644 --- a/packages/playwright-core/src/protocol/channels.ts +++ b/packages/playwright-core/src/protocol/channels.ts @@ -163,6 +163,7 @@ export type SerializedValue = { s?: string, v?: 'null' | 'undefined' | 'NaN' | 'Infinity' | '-Infinity' | '-0', d?: string, + u?: string, r?: { p: string, f: string, diff --git a/packages/playwright-core/src/protocol/protocol.yml b/packages/playwright-core/src/protocol/protocol.yml index ea9ac91ff4..018d4c3460 100644 --- a/packages/playwright-core/src/protocol/protocol.yml +++ b/packages/playwright-core/src/protocol/protocol.yml @@ -66,6 +66,8 @@ SerializedValue: - "-0" # String representation of the Date. d: string? + # String representation of the URL. + u: string? # Regular expression pattern and flags. r: type: object? diff --git a/packages/playwright-core/src/protocol/serializers.ts b/packages/playwright-core/src/protocol/serializers.ts index aa28d24f8b..aa3eb07b51 100644 --- a/packages/playwright-core/src/protocol/serializers.ts +++ b/packages/playwright-core/src/protocol/serializers.ts @@ -69,6 +69,8 @@ function innerParseSerializedValue(value: SerializedValue, handles: any[] | unde } if (value.d !== undefined) return new Date(value.d); + if (value.u !== undefined) + return new URL(value.u); if (value.r !== undefined) return new RegExp(value.r.p, value.r.f); @@ -141,6 +143,8 @@ function innerSerializeValue(value: any, handleSerializer: (value: any) => Handl } if (isDate(value)) return { d: value.toJSON() }; + if (isURL(value)) + return { u: value.toJSON() }; if (isRegExp(value)) return { r: { p: value.source, f: value.flags } }; @@ -175,6 +179,10 @@ function isDate(obj: any): obj is Date { return obj instanceof Date || Object.prototype.toString.call(obj) === '[object Date]'; } +function isURL(obj: any): obj is URL { + return obj instanceof URL || Object.prototype.toString.call(obj) === '[object URL]'; +} + function isError(obj: any): obj is Error { return obj instanceof Error || obj?.__proto__?.name === 'Error' || (obj?.__proto__ && isError(obj.__proto__)); } diff --git a/packages/playwright-core/src/protocol/validator.ts b/packages/playwright-core/src/protocol/validator.ts index 780b3ce812..520092d3e7 100644 --- a/packages/playwright-core/src/protocol/validator.ts +++ b/packages/playwright-core/src/protocol/validator.ts @@ -47,6 +47,7 @@ scheme.SerializedValue = tObject({ s: tOptional(tString), v: tOptional(tEnum(['null', 'undefined', 'NaN', 'Infinity', '-Infinity', '-0'])), d: tOptional(tString), + u: tOptional(tString), r: tOptional(tObject({ p: tString, f: tString, diff --git a/packages/playwright-core/src/server/isomorphic/utilityScriptSerializers.ts b/packages/playwright-core/src/server/isomorphic/utilityScriptSerializers.ts index e23844aa58..1f8799d269 100644 --- a/packages/playwright-core/src/server/isomorphic/utilityScriptSerializers.ts +++ b/packages/playwright-core/src/server/isomorphic/utilityScriptSerializers.ts @@ -18,6 +18,7 @@ export type SerializedValue = undefined | boolean | number | string | { v: 'null' | 'undefined' | 'NaN' | 'Infinity' | '-Infinity' | '-0' } | { d: string } | + { u: string } | { r: { p: string, f: string} } | { a: SerializedValue[], id: number } | { o: { k: string, v: SerializedValue }[], id: number } | @@ -41,6 +42,10 @@ export function source() { return obj instanceof Date || Object.prototype.toString.call(obj) === '[object Date]'; } + function isURL(obj: any): obj is URL { + return obj instanceof URL || Object.prototype.toString.call(obj) === '[object URL]'; + } + function isError(obj: any): obj is Error { try { return obj instanceof Error || (obj && obj.__proto__ && obj.__proto__.name === 'Error'); @@ -72,6 +77,8 @@ export function source() { } if ('d' in value) return new Date(value.d); + if ('u' in value) + return new URL(value.u); if ('r' in value) return new RegExp(value.r.p, value.r.f); if ('a' in value) { @@ -149,6 +156,8 @@ export function source() { } if (isDate(value)) return { d: value.toJSON() }; + if (isURL(value)) + return { u: value.toJSON() }; if (isRegExp(value)) return { r: { p: value.source, f: value.flags } }; diff --git a/tests/page/page-evaluate.spec.ts b/tests/page/page-evaluate.spec.ts index 6ee02367e9..07e8f6b084 100644 --- a/tests/page/page-evaluate.spec.ts +++ b/tests/page/page-evaluate.spec.ts @@ -597,6 +597,22 @@ it('should jsonValue() date', async ({ page }) => { expect(await resultHandle.jsonValue()).toEqual({ date: new Date('2020-05-27T01:31:38.506Z') }); }); +it('should evaluate url', async ({ page }) => { + const result = await page.evaluate(() => ({ url: new URL('https://example.com') })); + expect(result).toEqual({ url: new URL('https://example.com') }); +}); + +it('should roundtrip url', async ({ page }) => { + const url = new URL('https://example.com'); + const result = await page.evaluate(url => url, url); + expect(result.toString()).toEqual(url.toString()); +}); + +it('should jsonValue() url', async ({ page }) => { + const resultHandle = await page.evaluateHandle(() => ({ url: new URL('https://example.com') })); + expect(await resultHandle.jsonValue()).toEqual({ url: new URL('https://example.com') }); +}); + it('should not use toJSON when evaluating', async ({ page }) => { const result = await page.evaluate(() => ({ toJSON: () => 'string', data: 'data' })); expect(result).toEqual({ data: 'data', toJSON: {} });