Testing Advanced Serialization Implementation in Parcel Bundler
This test suite validates the serializer functionality in Parcel, focusing on object serialization and deserialization with complex data structures and custom class handling. It ensures reliable data transformation and state preservation across different scenarios.
Test Coverage Overview
Implementation Analysis
Technical Details
Best Practices Demonstrated
parcel-bundler/parcel
packages/core/core/test/serializer.test.js
// @flow
import {
serialize,
deserialize,
registerSerializableClass,
unregisterSerializableClass,
} from '../src/serializer';
import assert from 'assert';
import sinon from 'sinon';
describe('serializer', () => {
it('should serialize a basic object', () => {
let serialized = serialize({foo: 2, bar: 3});
assert(Buffer.isBuffer(serialized));
let deserialized = deserialize(serialized);
assert.equal(typeof deserialized, 'object');
assert.deepEqual(deserialized, {foo: 2, bar: 3});
});
it('should serialize an object with multiple references', () => {
let a = {foo: 2};
let b = {bar: a, baz: a};
let res = deserialize(serialize(b));
assert.deepEqual(res, b);
assert.equal(res.bar, res.baz);
});
it('should serialize a cyclic object', () => {
let a = {foo: 2, bar: {}};
a.bar = a;
let res = deserialize(serialize(a));
assert.deepEqual(res, a);
assert.equal(res.bar, res);
assert.equal(a.bar, a);
});
it('should serialize a Map', () => {
let a = new Map([[2, 3]]);
let res = deserialize(serialize(a));
assert(res instanceof Map);
assert.equal(res.get(2), 3);
});
it('should serialize a Set', () => {
let a = new Set([2, 3]);
let res = deserialize(serialize(a));
assert(res instanceof Set);
assert(res.has(2));
assert(res.has(3));
});
it('should serialize a class', () => {
class Test {
x: number;
constructor(x: number) {
this.x = x;
}
}
registerSerializableClass('Test', Test);
let x = new Test(2);
let res = deserialize(serialize(x));
assert(res instanceof Test);
assert.equal(res.x, x.x);
unregisterSerializableClass('Test', Test);
});
it('should serialize a class with a custom serialize method', () => {
class Test {
x: number;
constructor(x: number) {
this.x = x;
}
serialize() {
return {
x: this.x,
serialized: true,
};
}
}
registerSerializableClass('Test', Test);
let x = new Test(2);
let res = deserialize(serialize(x));
assert(res instanceof Test);
assert.equal(res.x, x.x);
assert.equal(res.serialized, true);
unregisterSerializableClass('Test', Test);
});
it('should serialize a class with a custom deserialize method', () => {
class Test {
x: number;
constructor(x: number) {
this.x = x;
}
static deserialize(x: any) {
return {
deserialized: true,
value: x,
};
}
}
registerSerializableClass('Test', Test);
let x = new Test(2);
let res = deserialize(serialize(x));
assert(!(res instanceof Test));
assert.equal(res.value.x, x.x);
assert.equal(res.deserialized, true);
unregisterSerializableClass('Test', Test);
});
it('should serialize a class recursively', () => {
class Foo {
x: number;
constructor(x: number) {
this.x = x;
}
}
class Bar {
foo: Foo;
constructor(foo: Foo) {
this.foo = foo;
}
}
registerSerializableClass('Foo', Foo);
registerSerializableClass('Bar', Bar);
let x = new Bar(new Foo(2));
let res = deserialize(serialize(x));
assert(res instanceof Bar);
assert(res.foo instanceof Foo);
assert.equal(res.foo.x, 2);
unregisterSerializableClass('Foo', Foo);
unregisterSerializableClass('Bar', Bar);
});
it('should serialize a cyclic class', () => {
class Foo {
x: ?Foo;
constructor(x: ?Foo) {
this.x = x;
}
}
registerSerializableClass('Foo', Foo);
let x = new Foo();
x.x = x;
let res = deserialize(serialize(x));
assert(res instanceof Foo);
assert(res.x instanceof Foo);
assert.equal(res.x, res);
assert.equal(x.x, x);
unregisterSerializableClass('Foo', Foo);
});
it('should copy on write', () => {
class Foo {
x: number;
constructor(x: number) {
this.x = x;
}
}
registerSerializableClass('Foo', Foo);
let x = {y: {foo: new Foo(2)}};
let res = deserialize(serialize(x));
assert(res.y.foo instanceof Foo);
assert(x.y.foo instanceof Foo);
unregisterSerializableClass('Foo', Foo);
});
it('should serialize a cyclic class and copy on write', () => {
class Foo {
x: ?Foo;
constructor(x: ?Foo) {
this.x = x;
}
}
registerSerializableClass('Foo', Foo);
let x = new Foo();
x.x = x;
let y = {x: {y: x}};
let res = deserialize(serialize(y));
assert(res.x.y instanceof Foo);
assert(res.x.y.x instanceof Foo);
assert(y.x.y instanceof Foo);
assert(y.x.y.x instanceof Foo);
assert.equal(res.x.y.x, res.x.y);
assert.equal(x.x, x);
unregisterSerializableClass('Foo', Foo);
});
it('should serialize a class inside a Map', () => {
class Test {
x: number;
constructor(x: number) {
this.x = x;
}
}
registerSerializableClass('Test', Test);
let x = new Map([[2, new Test(2)]]);
let res = deserialize(serialize(x));
assert(res instanceof Map);
assert(res.get(2) instanceof Test);
unregisterSerializableClass('Test', Test);
});
it('should serialize a class inside a Set', () => {
class Test {
x: number;
constructor(x: number) {
this.x = x;
}
}
registerSerializableClass('Test', Test);
let x = new Set([new Test(2)]);
let res = deserialize(serialize(x));
assert(res instanceof Set);
assert(res.values().next().value instanceof Test);
unregisterSerializableClass('Test', Test);
});
describe('raw values', () => {
class Outer {
inner: Inner;
constructor(inner) {
this.inner = inner;
}
serialize() {
return {
$$raw: true,
inner: this.inner,
};
}
}
class Inner {
x: number;
constructor(x) {
this.x = x;
}
static deserialize = sinon.spy();
}
beforeEach(() => {
registerSerializableClass('Outer', Outer);
registerSerializableClass('Inner', Inner);
});
afterEach(() => {
unregisterSerializableClass('Outer', Outer);
unregisterSerializableClass('Inner', Inner);
});
it('should not recursively serialize raw values', () => {
let res = deserialize(serialize(new Outer(new Inner(42))));
assert(res instanceof Outer);
assert(!(res.inner instanceof Inner));
assert.equal(res.inner.x, 42);
});
it('should not recursively deserialize raw values', () => {
deserialize(serialize(new Outer(new Inner(42))));
assert(Inner.deserialize.notCalled);
});
});
});