chore: allow evaluating Error objects (#31691)
Previously, Error objects were replaced with strings. Now, Error objects are reconstructed back from the serialized value.
This commit is contained in:
parent
074cc7d467
commit
1686e5174d
|
|
@ -49,6 +49,12 @@ function innerParseSerializedValue(value: SerializedValue, handles: any[] | unde
|
||||||
return new URL(value.u);
|
return new URL(value.u);
|
||||||
if (value.bi !== undefined)
|
if (value.bi !== undefined)
|
||||||
return BigInt(value.bi);
|
return BigInt(value.bi);
|
||||||
|
if (value.e !== undefined) {
|
||||||
|
const error = new Error(value.e.m);
|
||||||
|
error.name = value.e.n;
|
||||||
|
error.stack = value.e.s;
|
||||||
|
return error;
|
||||||
|
}
|
||||||
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);
|
||||||
|
|
||||||
|
|
@ -113,14 +119,8 @@ function innerSerializeValue(value: any, handleSerializer: (value: any) => Handl
|
||||||
return { s: value };
|
return { s: value };
|
||||||
if (typeof value === 'bigint')
|
if (typeof value === 'bigint')
|
||||||
return { bi: value.toString() };
|
return { bi: value.toString() };
|
||||||
if (isError(value)) {
|
if (isError(value))
|
||||||
const error = value;
|
return { e: { n: value.name, m: value.message, s: value.stack || '' } };
|
||||||
if ('captureStackTrace' in globalThis.Error) {
|
|
||||||
// v8
|
|
||||||
return { s: error.stack || '' };
|
|
||||||
}
|
|
||||||
return { s: `${error.name}: ${error.message}\n${error.stack}` };
|
|
||||||
}
|
|
||||||
if (isDate(value))
|
if (isDate(value))
|
||||||
return { d: value.toJSON() };
|
return { d: value.toJSON() };
|
||||||
if (isURL(value))
|
if (isURL(value))
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,11 @@ scheme.SerializedValue = tObject({
|
||||||
d: tOptional(tString),
|
d: tOptional(tString),
|
||||||
u: tOptional(tString),
|
u: tOptional(tString),
|
||||||
bi: tOptional(tString),
|
bi: tOptional(tString),
|
||||||
|
e: tOptional(tObject({
|
||||||
|
m: tString,
|
||||||
|
n: tString,
|
||||||
|
s: tString,
|
||||||
|
})),
|
||||||
r: tOptional(tObject({
|
r: tOptional(tObject({
|
||||||
p: tString,
|
p: tString,
|
||||||
f: tString,
|
f: tString,
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,8 @@ export type SerializedValue =
|
||||||
{ d: string } |
|
{ d: string } |
|
||||||
{ u: string } |
|
{ u: string } |
|
||||||
{ bi: string } |
|
{ bi: string } |
|
||||||
{ r: { p: string, f: string} } |
|
{ e: { n: string, m: string, s: 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 } |
|
||||||
{ ref: number } |
|
{ ref: number } |
|
||||||
|
|
@ -94,6 +95,12 @@ export function source() {
|
||||||
return new URL(value.u);
|
return new URL(value.u);
|
||||||
if ('bi' in value)
|
if ('bi' in value)
|
||||||
return BigInt(value.bi);
|
return BigInt(value.bi);
|
||||||
|
if ('e' in value) {
|
||||||
|
const error = new Error(value.e.m);
|
||||||
|
error.name = value.e.n;
|
||||||
|
error.stack = value.e.s;
|
||||||
|
return error;
|
||||||
|
}
|
||||||
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) {
|
||||||
|
|
@ -163,14 +170,8 @@ export function source() {
|
||||||
if (typeof value === 'bigint')
|
if (typeof value === 'bigint')
|
||||||
return { bi: value.toString() };
|
return { bi: value.toString() };
|
||||||
|
|
||||||
if (isError(value)) {
|
if (isError(value))
|
||||||
const error = value;
|
return { e: { n: value.name, m: value.message, s: value.stack || '' } };
|
||||||
if (error.stack?.startsWith(error.name + ': ' + error.message)) {
|
|
||||||
// v8
|
|
||||||
return error.stack;
|
|
||||||
}
|
|
||||||
return `${error.name}: ${error.message}\n${error.stack}`;
|
|
||||||
}
|
|
||||||
if (isDate(value))
|
if (isDate(value))
|
||||||
return { d: value.toJSON() };
|
return { d: value.toJSON() };
|
||||||
if (isURL(value))
|
if (isURL(value))
|
||||||
|
|
|
||||||
|
|
@ -177,6 +177,11 @@ export type SerializedValue = {
|
||||||
d?: string,
|
d?: string,
|
||||||
u?: string,
|
u?: string,
|
||||||
bi?: string,
|
bi?: string,
|
||||||
|
e?: {
|
||||||
|
m: string,
|
||||||
|
n: string,
|
||||||
|
s: string,
|
||||||
|
},
|
||||||
r?: {
|
r?: {
|
||||||
p: string,
|
p: string,
|
||||||
f: string,
|
f: string,
|
||||||
|
|
|
||||||
|
|
@ -82,6 +82,13 @@ SerializedValue:
|
||||||
u: string?
|
u: string?
|
||||||
# String representation of BigInt.
|
# String representation of BigInt.
|
||||||
bi: string?
|
bi: string?
|
||||||
|
# Serialized Error object.
|
||||||
|
e:
|
||||||
|
type: object?
|
||||||
|
properties:
|
||||||
|
m: string
|
||||||
|
n: string
|
||||||
|
s: string
|
||||||
# Regular expression pattern and flags.
|
# Regular expression pattern and flags.
|
||||||
r:
|
r:
|
||||||
type: object?
|
type: object?
|
||||||
|
|
|
||||||
|
|
@ -585,13 +585,37 @@ it('should evaluate exception with a function on the stack', async ({ page }) =>
|
||||||
return new Error('error message');
|
return new Error('error message');
|
||||||
})();
|
})();
|
||||||
});
|
});
|
||||||
expect(error).toContain('Error: error message');
|
expect(error.message).toBe('error message');
|
||||||
expect(error).toContain('functionOnStack');
|
expect(error.stack).toContain('functionOnStack');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should evaluate exception', async ({ page }) => {
|
it('should evaluate exception', async ({ page }) => {
|
||||||
const error = await page.evaluate(`new Error('error message')`);
|
const error = await page.evaluate(() => {
|
||||||
expect(error).toContain('Error: error message');
|
function innerFunction() {
|
||||||
|
const e = new Error('error message');
|
||||||
|
e.name = 'foobar';
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
return innerFunction();
|
||||||
|
});
|
||||||
|
expect(error).toBeInstanceOf(Error);
|
||||||
|
expect((error as Error).message).toBe('error message');
|
||||||
|
expect((error as Error).name).toBe('foobar');
|
||||||
|
expect((error as Error).stack).toContain('innerFunction');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should pass exception argument', async ({ page }) => {
|
||||||
|
function innerFunction() {
|
||||||
|
const e = new Error('error message');
|
||||||
|
e.name = 'foobar';
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
const received = await page.evaluate(e => {
|
||||||
|
return { message: e.message, name: e.name, stack: e.stack };
|
||||||
|
}, innerFunction());
|
||||||
|
expect(received.message).toBe('error message');
|
||||||
|
expect(received.name).toBe('foobar');
|
||||||
|
expect(received.stack).toContain('innerFunction');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should evaluate date', async ({ page }) => {
|
it('should evaluate date', async ({ page }) => {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue