Source code

Revision control

Copy as Markdown

Other Tools

/*
* Any copyright is dedicated to the Public Domain.
*/
load(libdir + "asserts.js");
function roundtrip(error) {
return deserialize(serialize(error, []));
}
// Basic
{
let error = new Error("hello world");
let cloned = roundtrip(error);
assertDeepEq(cloned, error);
assertEq(cloned.name, "Error");
assertEq(cloned.message, "hello world");
assertEq(cloned.stack, error.stack);
}
let constructors = [Error, EvalError, RangeError, ReferenceError,
SyntaxError, TypeError, URIError];
for (let constructor of constructors) {
// With message
let error = new constructor("hello");
let cloned = roundtrip(error);
assertDeepEq(cloned, error);
assertEq(cloned.hasOwnProperty('message'), true);
assertEq(cloned instanceof constructor, true);
// Without message
error = new constructor();
cloned = roundtrip(error);
assertDeepEq(cloned, error);
assertEq(cloned.hasOwnProperty('message'), false);
assertEq(cloned instanceof constructor, true);
// Custom name
error = new constructor("hello");
error.name = "MyError";
cloned = roundtrip(error);
assertEq(cloned.name, "Error");
assertEq(cloned.message, "hello");
assertEq(cloned.stack, error.stack);
if (constructor !== Error) {
assertEq(cloned instanceof constructor, false);
}
// |cause| property
error = new constructor("hello", { cause: new Error("foobar") });
cloned = roundtrip(error);
assertDeepEq(cloned, error);
assertEq(cloned.hasOwnProperty('message'), true);
assertEq(cloned instanceof constructor, true);
assertEq(cloned.stack, error.stack);
assertEq(cloned.stack === undefined, false);
// |cause| property, manually added after construction.
error = new constructor("hello");
error.cause = new Error("foobar");
assertDeepEq(Object.getOwnPropertyDescriptor(error, "cause"), {
value: error.cause,
writable: true,
enumerable: true,
configurable: true,
});
cloned = roundtrip(error);
assertDeepEq(Object.getOwnPropertyDescriptor(cloned, "cause"), {
value: cloned.cause,
writable: true,
enumerable: false, // Non-enumerable in the cloned object!
configurable: true,
});
assertEq(cloned.hasOwnProperty('message'), true);
assertEq(cloned instanceof constructor, true);
assertEq(cloned.stack, error.stack);
assertEq(cloned.stack === undefined, false);
// Subclassing
error = new (class MyError extends constructor {});
cloned = roundtrip(error);
assertEq(cloned.name, constructor.name);
assertEq(cloned.hasOwnProperty('message'), false);
assertEq(cloned.stack, error.stack);
assertEq(cloned instanceof Error, true);
// Cross-compartment
error = evalcx(`new ${constructor.name}("hello")`);
cloned = roundtrip(error);
assertEq(cloned.name, constructor.name);
assertEq(cloned.message, "hello");
assertEq(cloned.stack, error.stack);
assertEq(cloned instanceof constructor, true);
}
// Non-string message
{
let error = new Error("hello world");
error.message = 123;
let cloned = roundtrip(error);
assertEq(cloned.message, "123");
assertEq(cloned.hasOwnProperty('message'), true);
error = new Error();
Object.defineProperty(error, 'message', { get: () => {} });
cloned = roundtrip(error);
assertEq(cloned.message, "");
assertEq(cloned.hasOwnProperty('message'), false);
}
// AggregateError
{
// With message
let error = new AggregateError([{a: 1}, {b: 2}], "hello");
let cloned = roundtrip(error);
assertDeepEq(cloned, error);
assertEq(cloned.hasOwnProperty('message'), true);
assertEq(cloned instanceof AggregateError, true);
// Without message
error = new AggregateError([{a: 1}, {b: 2}]);
cloned = roundtrip(error);
assertDeepEq(cloned, error);
assertEq(cloned.hasOwnProperty('message'), false);
assertEq(cloned instanceof AggregateError, true);
// Custom name breaks this!
error = new AggregateError([{a: 1}, {b: 2}]);
error.name = "MyError";
cloned = roundtrip(error);
assertEq(cloned.name, "Error");
assertEq(cloned.message, "");
assertEq(cloned.stack, error.stack);
assertEq(cloned instanceof AggregateError, false);
assertEq(cloned.errors, undefined);
assertEq(cloned.hasOwnProperty('errors'), false);
}