feat(evaluate): serialized URL instances (#15023)
Add support for returning [URL][] instances from `page.evaluate` calls. Follow the patterns established by `Date` and `RegExp` serialization. [URL]: https://developer.mozilla.org/en-US/docs/Web/API/URL
This commit is contained in:
parent
35a2792bb4
commit
ef5a56ce18
|
|
@ -163,6 +163,7 @@ export type SerializedValue = {
|
||||||
s?: string,
|
s?: string,
|
||||||
v?: 'null' | 'undefined' | 'NaN' | 'Infinity' | '-Infinity' | '-0',
|
v?: 'null' | 'undefined' | 'NaN' | 'Infinity' | '-Infinity' | '-0',
|
||||||
d?: string,
|
d?: string,
|
||||||
|
u?: string,
|
||||||
r?: {
|
r?: {
|
||||||
p: string,
|
p: string,
|
||||||
f: string,
|
f: string,
|
||||||
|
|
|
||||||
|
|
@ -66,6 +66,8 @@ SerializedValue:
|
||||||
- "-0"
|
- "-0"
|
||||||
# String representation of the Date.
|
# String representation of the Date.
|
||||||
d: string?
|
d: string?
|
||||||
|
# String representation of the URL.
|
||||||
|
u: string?
|
||||||
# Regular expression pattern and flags.
|
# Regular expression pattern and flags.
|
||||||
r:
|
r:
|
||||||
type: object?
|
type: object?
|
||||||
|
|
|
||||||
|
|
@ -69,6 +69,8 @@ function innerParseSerializedValue(value: SerializedValue, handles: any[] | unde
|
||||||
}
|
}
|
||||||
if (value.d !== undefined)
|
if (value.d !== undefined)
|
||||||
return new Date(value.d);
|
return new Date(value.d);
|
||||||
|
if (value.u !== undefined)
|
||||||
|
return new URL(value.u);
|
||||||
if (value.r !== undefined)
|
if (value.r !== undefined)
|
||||||
return new RegExp(value.r.p, value.r.f);
|
return new RegExp(value.r.p, value.r.f);
|
||||||
|
|
||||||
|
|
@ -141,6 +143,8 @@ function innerSerializeValue(value: any, handleSerializer: (value: any) => Handl
|
||||||
}
|
}
|
||||||
if (isDate(value))
|
if (isDate(value))
|
||||||
return { d: value.toJSON() };
|
return { d: value.toJSON() };
|
||||||
|
if (isURL(value))
|
||||||
|
return { u: value.toJSON() };
|
||||||
if (isRegExp(value))
|
if (isRegExp(value))
|
||||||
return { r: { p: value.source, f: value.flags } };
|
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]';
|
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 {
|
function isError(obj: any): obj is Error {
|
||||||
return obj instanceof Error || obj?.__proto__?.name === 'Error' || (obj?.__proto__ && isError(obj.__proto__));
|
return obj instanceof Error || obj?.__proto__?.name === 'Error' || (obj?.__proto__ && isError(obj.__proto__));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,7 @@ scheme.SerializedValue = tObject({
|
||||||
s: tOptional(tString),
|
s: tOptional(tString),
|
||||||
v: tOptional(tEnum(['null', 'undefined', 'NaN', 'Infinity', '-Infinity', '-0'])),
|
v: tOptional(tEnum(['null', 'undefined', 'NaN', 'Infinity', '-Infinity', '-0'])),
|
||||||
d: tOptional(tString),
|
d: tOptional(tString),
|
||||||
|
u: tOptional(tString),
|
||||||
r: tOptional(tObject({
|
r: tOptional(tObject({
|
||||||
p: tString,
|
p: tString,
|
||||||
f: tString,
|
f: tString,
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ export type SerializedValue =
|
||||||
undefined | boolean | number | string |
|
undefined | boolean | number | string |
|
||||||
{ v: 'null' | 'undefined' | 'NaN' | 'Infinity' | '-Infinity' | '-0' } |
|
{ v: 'null' | 'undefined' | 'NaN' | 'Infinity' | '-Infinity' | '-0' } |
|
||||||
{ d: string } |
|
{ d: string } |
|
||||||
|
{ u: string } |
|
||||||
{ r: { p: string, f: string} } |
|
{ r: { p: string, f: string} } |
|
||||||
{ a: SerializedValue[], id: number } |
|
{ a: SerializedValue[], id: number } |
|
||||||
{ o: { k: string, v: 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]';
|
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 {
|
function isError(obj: any): obj is Error {
|
||||||
try {
|
try {
|
||||||
return obj instanceof Error || (obj && obj.__proto__ && obj.__proto__.name === 'Error');
|
return obj instanceof Error || (obj && obj.__proto__ && obj.__proto__.name === 'Error');
|
||||||
|
|
@ -72,6 +77,8 @@ export function source() {
|
||||||
}
|
}
|
||||||
if ('d' in value)
|
if ('d' in value)
|
||||||
return new Date(value.d);
|
return new Date(value.d);
|
||||||
|
if ('u' in value)
|
||||||
|
return new URL(value.u);
|
||||||
if ('r' in value)
|
if ('r' in value)
|
||||||
return new RegExp(value.r.p, value.r.f);
|
return new RegExp(value.r.p, value.r.f);
|
||||||
if ('a' in value) {
|
if ('a' in value) {
|
||||||
|
|
@ -149,6 +156,8 @@ export function source() {
|
||||||
}
|
}
|
||||||
if (isDate(value))
|
if (isDate(value))
|
||||||
return { d: value.toJSON() };
|
return { d: value.toJSON() };
|
||||||
|
if (isURL(value))
|
||||||
|
return { u: value.toJSON() };
|
||||||
if (isRegExp(value))
|
if (isRegExp(value))
|
||||||
return { r: { p: value.source, f: value.flags } };
|
return { r: { p: value.source, f: value.flags } };
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -597,6 +597,22 @@ it('should jsonValue() date', async ({ page }) => {
|
||||||
expect(await resultHandle.jsonValue()).toEqual({ date: new Date('2020-05-27T01:31:38.506Z') });
|
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 }) => {
|
it('should not use toJSON when evaluating', async ({ page }) => {
|
||||||
const result = await page.evaluate(() => ({ toJSON: () => 'string', data: 'data' }));
|
const result = await page.evaluate(() => ({ toJSON: () => 'string', data: 'data' }));
|
||||||
expect(result).toEqual({ data: 'data', toJSON: {} });
|
expect(result).toEqual({ data: 'data', toJSON: {} });
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue