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);
|
||||
if (value.bi !== undefined)
|
||||
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)
|
||||
return new RegExp(value.r.p, value.r.f);
|
||||
|
||||
|
|
@ -113,14 +119,8 @@ function innerSerializeValue(value: any, handleSerializer: (value: any) => Handl
|
|||
return { s: value };
|
||||
if (typeof value === 'bigint')
|
||||
return { bi: value.toString() };
|
||||
if (isError(value)) {
|
||||
const error = value;
|
||||
if ('captureStackTrace' in globalThis.Error) {
|
||||
// v8
|
||||
return { s: error.stack || '' };
|
||||
}
|
||||
return { s: `${error.name}: ${error.message}\n${error.stack}` };
|
||||
}
|
||||
if (isError(value))
|
||||
return { e: { n: value.name, m: value.message, s: value.stack || '' } };
|
||||
if (isDate(value))
|
||||
return { d: value.toJSON() };
|
||||
if (isURL(value))
|
||||
|
|
|
|||
|
|
@ -58,6 +58,11 @@ scheme.SerializedValue = tObject({
|
|||
d: tOptional(tString),
|
||||
u: tOptional(tString),
|
||||
bi: tOptional(tString),
|
||||
e: tOptional(tObject({
|
||||
m: tString,
|
||||
n: tString,
|
||||
s: tString,
|
||||
})),
|
||||
r: tOptional(tObject({
|
||||
p: tString,
|
||||
f: tString,
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ export type SerializedValue =
|
|||
{ d: string } |
|
||||
{ u: string } |
|
||||
{ bi: string } |
|
||||
{ e: { n: string, m: string, s: string } } |
|
||||
{ r: { p: string, f: string } } |
|
||||
{ a: SerializedValue[], id: number } |
|
||||
{ o: { k: string, v: SerializedValue }[], id: number } |
|
||||
|
|
@ -94,6 +95,12 @@ export function source() {
|
|||
return new URL(value.u);
|
||||
if ('bi' in value)
|
||||
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)
|
||||
return new RegExp(value.r.p, value.r.f);
|
||||
if ('a' in value) {
|
||||
|
|
@ -163,14 +170,8 @@ export function source() {
|
|||
if (typeof value === 'bigint')
|
||||
return { bi: value.toString() };
|
||||
|
||||
if (isError(value)) {
|
||||
const error = value;
|
||||
if (error.stack?.startsWith(error.name + ': ' + error.message)) {
|
||||
// v8
|
||||
return error.stack;
|
||||
}
|
||||
return `${error.name}: ${error.message}\n${error.stack}`;
|
||||
}
|
||||
if (isError(value))
|
||||
return { e: { n: value.name, m: value.message, s: value.stack || '' } };
|
||||
if (isDate(value))
|
||||
return { d: value.toJSON() };
|
||||
if (isURL(value))
|
||||
|
|
|
|||
|
|
@ -177,6 +177,11 @@ export type SerializedValue = {
|
|||
d?: string,
|
||||
u?: string,
|
||||
bi?: string,
|
||||
e?: {
|
||||
m: string,
|
||||
n: string,
|
||||
s: string,
|
||||
},
|
||||
r?: {
|
||||
p: string,
|
||||
f: string,
|
||||
|
|
|
|||
|
|
@ -82,6 +82,13 @@ SerializedValue:
|
|||
u: string?
|
||||
# String representation of BigInt.
|
||||
bi: string?
|
||||
# Serialized Error object.
|
||||
e:
|
||||
type: object?
|
||||
properties:
|
||||
m: string
|
||||
n: string
|
||||
s: string
|
||||
# Regular expression pattern and flags.
|
||||
r:
|
||||
type: object?
|
||||
|
|
|
|||
|
|
@ -585,13 +585,37 @@ it('should evaluate exception with a function on the stack', async ({ page }) =>
|
|||
return new Error('error message');
|
||||
})();
|
||||
});
|
||||
expect(error).toContain('Error: error message');
|
||||
expect(error).toContain('functionOnStack');
|
||||
expect(error.message).toBe('error message');
|
||||
expect(error.stack).toContain('functionOnStack');
|
||||
});
|
||||
|
||||
it('should evaluate exception', async ({ page }) => {
|
||||
const error = await page.evaluate(`new Error('error message')`);
|
||||
expect(error).toContain('Error: error message');
|
||||
const error = await page.evaluate(() => {
|
||||
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 }) => {
|
||||
|
|
|
|||
Loading…
Reference in a new issue