diff --git a/test/arrays.test.js b/test/arrays.test.js index 1071cc48..d5fc73a9 100644 --- a/test/arrays.test.js +++ b/test/arrays.test.js @@ -170,74 +170,225 @@ describe('Arrays', () => { describe('extra properties', () => { describe('non-circular', () => { - itSerializesEqual('without descriptors', { - in() { - const arr = [1, 2, 3]; - arr.x = 4; - arr.y = 5; - return arr; - }, - out: 'Object.assign([1,2,3],{x:4,y:5})' + describe('without descriptors', () => { + itSerializesEqual('string keys', { + in() { + const arr = [1, 2, 3]; + arr.x = 4; + arr.y = 5; + return arr; + }, + out: 'Object.assign([1,2,3],{x:4,y:5})' + }); + + itSerializesEqual("including prop called '__proto__'", { + in() { + const arr = [1, 2, 3]; + arr.x = 4; + Object.defineProperty( + arr, '__proto__', + {value: 5, writable: true, enumerable: true, configurable: true} + ); + arr.y = 6; + return arr; + }, + out: `(()=>{ + const a=Object, + b=a.defineProperties; + return b( + a.defineProperty( + b( + [1,2,3], + {x:{value:4,writable:true,enumerable:true,configurable:true}} + ), + "__proto__", + {value:5,writable:true,enumerable:true,configurable:true} + ), + {y:{value:6,writable:true,enumerable:true,configurable:true}} + ) + })()`, + validate(arr) { + expect(arr).toHaveOwnPropertyNames(['0', '1', '2', 'length', 'x', '__proto__', 'y']); + expect(arr.x).toBe(4); + expect(arr.__proto__).toBe(5); // eslint-disable-line no-proto + expect(arr.y).toBe(6); + expect(arr).toHaveDescriptorModifiersFor('x', true, true, true); + expect(arr).toHaveDescriptorModifiersFor('__proto__', true, true, true); + expect(arr).toHaveDescriptorModifiersFor('y', true, true, true); + } + }); }); - itSerializesEqual('with descriptors', { - in() { - const arr = [1, 2, 3]; - Object.defineProperty(arr, 'x', {value: 4, enumerable: true}); - Object.defineProperty(arr, 'y', {value: 5, writable: true, configurable: true}); - return arr; - }, - out: `Object.defineProperties( - [1,2,3], - {x:{value:4,enumerable:true},y:{value:5,writable:true,configurable:true}} - )`, - validate(arr) { - expect(arr).toHaveOwnPropertyNames(['0', '1', '2', 'length', 'x', 'y']); - expect(arr.x).toBe(4); - expect(arr.y).toBe(5); - expect(arr).toHaveDescriptorModifiersFor('x', false, true, false); - expect(arr).toHaveDescriptorModifiersFor('y', true, false, true); - } + describe('with descriptors', () => { + itSerializesEqual('string keys', { + in() { + const arr = [1, 2, 3]; + Object.defineProperty(arr, 'x', {value: 4, enumerable: true}); + Object.defineProperty(arr, 'y', {value: 5, writable: true, configurable: true}); + return arr; + }, + out: `Object.defineProperties( + [1,2,3], + {x:{value:4,enumerable:true},y:{value:5,writable:true,configurable:true}} + )`, + validate(arr) { + expect(arr).toHaveOwnPropertyNames(['0', '1', '2', 'length', 'x', 'y']); + expect(arr.x).toBe(4); + expect(arr.y).toBe(5); + expect(arr).toHaveDescriptorModifiersFor('x', false, true, false); + expect(arr).toHaveDescriptorModifiersFor('y', true, false, true); + } + }); + + itSerializesEqual("including prop called '__proto__'", { + in() { + const arr = [1, 2, 3]; + Object.defineProperty(arr, 'x', {value: 4, enumerable: true}); + Object.defineProperty(arr, '__proto__', {value: 5, configurable: true}); + Object.defineProperty(arr, 'y', {value: 6, writable: true, configurable: true}); + return arr; + }, + out: `(()=>{ + const a=Object, + b=a.defineProperties; + return b( + a.defineProperty( + b([1,2,3],{x:{value:4,enumerable:true}}), + "__proto__", + {value:5,configurable:true} + ), + {y:{value:6,writable:true,configurable:true}} + ) + })()`, + validate(arr) { + expect(arr).toHaveOwnPropertyNames(['0', '1', '2', 'length', 'x', '__proto__', 'y']); + expect(arr.x).toBe(4); + expect(arr.__proto__).toBe(5); // eslint-disable-line no-proto + expect(arr.y).toBe(6); + expect(arr).toHaveDescriptorModifiersFor('x', false, true, false); + expect(arr).toHaveDescriptorModifiersFor('__proto__', false, false, true); + expect(arr).toHaveDescriptorModifiersFor('y', true, false, true); + } + }); }); }); describe('circular references', () => { - itSerializesEqual('without descriptors', { - in() { - const arr = [1, 2, 3]; - arr.x = arr; - arr.y = arr; - return arr; - }, - out: '(()=>{const a=[1,2,3];a.x=a;a.y=a;return a})()', - validate(arr) { - expect(arr.x).toBe(arr); - expect(arr.y).toBe(arr); - } + describe('without descriptors', () => { + itSerializesEqual('string keys', { + in() { + const arr = [1, 2, 3]; + arr.x = arr; + arr.y = arr; + return arr; + }, + out: '(()=>{const a=[1,2,3];a.x=a;a.y=a;return a})()', + validate(arr) { + expect(arr.x).toBe(arr); + expect(arr.y).toBe(arr); + } + }); + + itSerializesEqual("including prop called '__proto__'", { + in() { + const arr = [1, 2, 3]; + arr.x = arr; + Object.defineProperty( + arr, '__proto__', + {value: arr, writable: true, enumerable: true, configurable: true} + ); + arr.y = 4; + return arr; + }, + out: `(()=>{ + const a=Object, + b=a.defineProperties, + c=b( + a.defineProperty( + b([1,2,3],{x:{writable:true,enumerable:true,configurable:true}}), + "__proto__", + {writable:true,enumerable:true,configurable:true} + ), + {y:{value:4,writable:true,enumerable:true,configurable:true}} + ); + c.x=c; + c.__proto__=c; + return c + })()`, + validate(arr) { + expect(arr).toHaveOwnPropertyNames(['0', '1', '2', 'length', 'x', '__proto__', 'y']); + expect(arr.x).toBe(arr); + expect(arr.__proto__).toBe(arr); // eslint-disable-line no-proto + expect(arr.y).toBe(4); + expect(arr).toHaveDescriptorModifiersFor('x', true, true, true); + expect(arr).toHaveDescriptorModifiersFor('__proto__', true, true, true); + expect(arr).toHaveDescriptorModifiersFor('y', true, true, true); + } + }); }); - itSerializesEqual('with descriptors', { - in() { - const arr = [1, 2, 3]; - Object.defineProperty(arr, 'x', {value: arr, enumerable: true}); - Object.defineProperty(arr, 'y', {value: arr, writable: true, configurable: true}); - return arr; - }, - out: `(()=>{ - const a=[1,2,3]; - Object.defineProperties(a,{ - x:{value:a,enumerable:true}, - y:{value:a,writable:true,configurable:true} - }); - return a - })()`, - validate(arr) { - expect(arr).toHaveOwnPropertyNames(['0', '1', '2', 'length', 'x', 'y']); - expect(arr.x).toBe(arr); - expect(arr.y).toBe(arr); - expect(arr).toHaveDescriptorModifiersFor('x', false, true, false); - expect(arr).toHaveDescriptorModifiersFor('y', true, false, true); - } + describe('with descriptors', () => { + itSerializesEqual('string keys', { + in() { + const arr = [1, 2, 3]; + Object.defineProperty(arr, 'x', {value: arr, enumerable: true}); + Object.defineProperty(arr, 'y', {value: arr, writable: true, configurable: true}); + return arr; + }, + out: `(()=>{ + const a=[1,2,3]; + Object.defineProperties(a,{ + x:{value:a,enumerable:true}, + y:{value:a,writable:true,configurable:true} + }); + return a + })()`, + validate(arr) { + expect(arr).toHaveOwnPropertyNames(['0', '1', '2', 'length', 'x', 'y']); + expect(arr.x).toBe(arr); + expect(arr.y).toBe(arr); + expect(arr).toHaveDescriptorModifiersFor('x', false, true, false); + expect(arr).toHaveDescriptorModifiersFor('y', true, false, true); + } + }); + + itSerializesEqual("including prop called '__proto__'", { + in() { + const arr = [1, 2, 3]; + Object.defineProperty(arr, 'x', {value: arr, enumerable: true}); + Object.defineProperty(arr, '__proto__', {value: arr, configurable: true}); + Object.defineProperty(arr, 'y', {value: 4, writable: true, configurable: true}); + return arr; + }, + out: `(()=>{ + const a=Object, + b=a.defineProperties, + c=a.defineProperty, + d=b( + c( + b([1,2,3],{x:{writable:true,enumerable:true,configurable:true}}), + "__proto__", + {writable:true,enumerable:true,configurable:true} + ), + {y:{value:4,writable:true,configurable:true}} + ); + c( + b(d,{x:{value:d,writable:false,configurable:false}}), + "__proto__", + {value:d,writable:false,enumerable:false} + ); + return d + })()`, + validate(arr) { + expect(arr).toHaveOwnPropertyNames(['0', '1', '2', 'length', 'x', '__proto__', 'y']); + expect(arr.x).toBe(arr); + expect(arr.__proto__).toBe(arr); // eslint-disable-line no-proto + expect(arr.y).toBe(4); + expect(arr).toHaveDescriptorModifiersFor('x', false, true, false); + expect(arr).toHaveDescriptorModifiersFor('__proto__', false, false, true); + expect(arr).toHaveDescriptorModifiersFor('y', true, false, true); + } + }); }); }); }); @@ -331,6 +482,184 @@ describe('Arrays', () => { }); }); + describe('prototype altered', () => { + describe('to null', () => { + itSerializesEqual('with no extra properties', { + in: () => Object.setPrototypeOf([1, 2, 3], null), + out: 'Object.setPrototypeOf([1,2,3],null)', + validate(arr) { + expect(arr).toHavePrototype(null); + } + }); + + itSerializesEqual("with extra properties including one named '__proto__'", { + in() { + const arr = [1, 2, 3]; + Object.setPrototypeOf(arr, null); + arr.x = 4; + arr.__proto__ = 5; // eslint-disable-line no-proto + arr.y = 6; + return arr; + }, + out: `(()=>{ + const a=Object, + b=a.defineProperties; + return b( + a.defineProperty( + b( + a.setPrototypeOf([1,2,3],null), + {x:{value:4,writable:true,enumerable:true,configurable:true}} + ), + "__proto__", + {value:5,writable:true,enumerable:true,configurable:true} + ), + {y:{value:6,writable:true,enumerable:true,configurable:true}} + ) + })()`, + validate(arr) { + expect(arr).toHavePrototype(null); + expect(arr).toHaveOwnPropertyNames(['0', '1', '2', 'length', 'x', '__proto__', 'y']); + expect(arr.x).toBe(4); + expect(arr.__proto__).toBe(5); // eslint-disable-line no-proto + expect(arr.y).toBe(6); + expect(arr).toHaveDescriptorModifiersFor('x', true, true, true); + expect(arr).toHaveDescriptorModifiersFor('__proto__', true, true, true); + expect(arr).toHaveDescriptorModifiersFor('y', true, true, true); + } + }); + + itSerializesEqual("with extra circular properties including one named '__proto__'", { + in() { + const arr = [1, 2, 3]; + Object.setPrototypeOf(arr, null); + arr.x = arr; + arr.__proto__ = arr; // eslint-disable-line no-proto + arr.y = 4; + return arr; + }, + out: `(()=>{ + const a=Object, + b=a.defineProperties, + c=b( + a.defineProperty( + b( + a.setPrototypeOf([1,2,3],null), + {x:{writable:true,enumerable:true,configurable:true}} + ), + "__proto__", + {writable:true,enumerable:true,configurable:true} + ), + {y:{value:4,writable:true,enumerable:true,configurable:true}}); + c.x=c; + c.__proto__=c; + return c + })()`, + validate(arr) { + expect(arr).toHavePrototype(null); + expect(arr).toHaveOwnPropertyNames(['0', '1', '2', 'length', 'x', '__proto__', 'y']); + expect(arr.x).toBe(arr); + expect(arr.__proto__).toBe(arr); // eslint-disable-line no-proto + expect(arr.y).toBe(4); + expect(arr).toHaveDescriptorModifiersFor('x', true, true, true); + expect(arr).toHaveDescriptorModifiersFor('__proto__', true, true, true); + expect(arr).toHaveDescriptorModifiersFor('y', true, true, true); + } + }); + }); + + describe('to Object.prototype', () => { + itSerializesEqual('with no extra properties', { + in: () => Object.setPrototypeOf([1, 2, 3], Object.prototype), + out: '(()=>{const a=Object;return a.setPrototypeOf([1,2,3],a.prototype)})()', + validate(arr) { + expect(arr).toHavePrototype(Object.prototype); + } + }); + + itSerializesEqual("with extra properties including one named '__proto__'", { + in() { + const arr = [1, 2, 3]; + Object.setPrototypeOf(arr, Object.prototype); + arr.x = 4; + Object.defineProperty( + arr, '__proto__', + {value: arr, writable: true, enumerable: true, configurable: true} + ); + arr.__proto__ = 5; // eslint-disable-line no-proto + arr.y = 6; + return arr; + }, + out: `(()=>{ + const a=Object, + b=a.defineProperties; + return b( + a.defineProperty( + b( + a.setPrototypeOf([1,2,3],a.prototype), + {x:{value:4,writable:true,enumerable:true,configurable:true}} + ), + "__proto__", + {value:5,writable:true,enumerable:true,configurable:true} + ), + {y:{value:6,writable:true,enumerable:true,configurable:true}} + ) + })()`, + validate(arr) { + expect(arr).toHavePrototype(Object.prototype); + expect(arr).toHaveOwnPropertyNames(['0', '1', '2', 'length', 'x', '__proto__', 'y']); + expect(arr.x).toBe(4); + expect(arr.__proto__).toBe(5); // eslint-disable-line no-proto + expect(arr.y).toBe(6); + expect(arr).toHaveDescriptorModifiersFor('x', true, true, true); + expect(arr).toHaveDescriptorModifiersFor('__proto__', true, true, true); + expect(arr).toHaveDescriptorModifiersFor('y', true, true, true); + } + }); + + itSerializesEqual("with extra circular properties including one named '__proto__'", { + in() { + const arr = [1, 2, 3]; + Object.setPrototypeOf(arr, Object.prototype); + arr.x = arr; + Object.defineProperty( + arr, '__proto__', + {value: arr, writable: true, enumerable: true, configurable: true} + ); + arr.y = 4; + return arr; + }, + out: `(()=>{ + const a=Object, + b=a.defineProperties, + c=b( + a.defineProperty( + b( + a.setPrototypeOf([1,2,3],a.prototype), + {x:{writable:true,enumerable:true,configurable:true}} + ), + "__proto__", + {writable:true,enumerable:true,configurable:true} + ), + {y:{value:4,writable:true,enumerable:true,configurable:true}} + ); + c.x=c; + c.__proto__=c; + return c + })()`, + validate(arr) { + expect(arr).toHavePrototype(Object.prototype); + expect(arr).toHaveOwnPropertyNames(['0', '1', '2', 'length', 'x', '__proto__', 'y']); + expect(arr.x).toBe(arr); + expect(arr.__proto__).toBe(arr); // eslint-disable-line no-proto + expect(arr.y).toBe(4); + expect(arr).toHaveDescriptorModifiersFor('x', true, true, true); + expect(arr).toHaveDescriptorModifiersFor('__proto__', true, true, true); + expect(arr).toHaveDescriptorModifiersFor('y', true, true, true); + } + }); + }); + }); + describe.skip('array subclass', () => { itSerializesEqual('empty array', { in() { diff --git a/test/boxed.test.js b/test/boxed.test.js index b7e4f024..85827427 100644 --- a/test/boxed.test.js +++ b/test/boxed.test.js @@ -357,6 +357,25 @@ describe('Boxed BigInts', () => { } }); + itSerializes.skip('with `valueOf` property', { + in() { + const bigInt = Object(1n); + bigInt.valueOf = () => 2n; + return bigInt; + }, + out: 'Object.assign(Object(1n),{valueOf:(0,()=>2n)})', + validate(bigInt) { + expect(typeof bigInt).toBe('object'); + expect(typeof bigInt.valueOf()).toBe('bigint'); + expect(bigInt).toHavePrototype(BigInt.prototype); + expect(BigInt.prototype.valueOf.call(bigInt)).toBe(1n); + expect(BigInt(bigInt)).toBe(2n); + expect(bigInt.valueOf).toBeFunction(); + expect(bigInt).toHaveDescriptorModifiersFor('valueOf', true, true, true); + expect(bigInt.valueOf()).toBe(2n); + } + }); + itSerializesEqual.skip('BigInt subclass', { in() { class B extends BigInt {} diff --git a/test/maps.test.js b/test/maps.test.js index 44f7e08b..8159a4d4 100644 --- a/test/maps.test.js +++ b/test/maps.test.js @@ -266,4 +266,32 @@ describe('WeakMaps', () => { expect(weakMap.get(weakMap)).toEqual(obj2); } }); + + itSerializes('with circular contents followed by non-circular', { + in() { + const weak = new WeakMap(); + const x = {a: 1}, + y = {b: 2}; + weak.set(x, weak); + weak.set(weak, y); + weak.set(y, x); + return {obj1: x, obj2: y, weakMap: weak}; + }, + out: `(()=>{ + const a={a:1}, + b={b:2}, + c=new WeakMap([[b,a]]); + c.set(a,c); + c.set(c,b); + return{obj1:a,obj2:b,weakMap:c} + })()`, + validate({obj1, obj2, weakMap}) { + expect(weakMap).toBeInstanceOf(WeakMap); + expect(obj1).toEqual({a: 1}); + expect(obj2).toEqual({b: 2}); + expect(weakMap.get(obj1)).toBe(weakMap); + expect(weakMap.get(weakMap)).toEqual(obj2); + expect(weakMap.get(obj2)).toEqual(obj1); + } + }); }); diff --git a/test/objects.test.js b/test/objects.test.js index 61943832..ecd2aae7 100644 --- a/test/objects.test.js +++ b/test/objects.test.js @@ -344,7 +344,7 @@ describe('Objects', () => { obj[Symbol('symbol2')] = obj; return obj; }, - out: '(()=>{const a={x:1,y:2},b=Symbol;a[b("symbol1")]=a;a[b("symbol2")]=a;return a})()', + out: '(()=>{const a=Symbol,b={x:1,y:2};b[a("symbol1")]=b;b[a("symbol2")]=b;return b})()', validate(obj) { const symbolKeys = Object.getOwnPropertySymbols(obj); expect(symbolKeys).toBeArrayOfSize(2); @@ -406,11 +406,11 @@ describe('Objects', () => { return obj; }, out: `(()=>{ - const a=Symbol("symbol1"), - b=Object.defineProperties; + const a=Object.defineProperties, + b=Symbol("symbol1"); return{ - obj1:b({},{[a]:{value:1,writable:true,configurable:true}}), - obj2:b({},{[a]:{value:2,writable:true,configurable:true}}) + obj1:a({},{[b]:{value:1,writable:true,configurable:true}}), + obj2:a({},{[b]:{value:2,writable:true,configurable:true}}) } })()`, validate(obj) { @@ -469,13 +469,13 @@ describe('Objects', () => { return obj; }, out: `(()=>{ - const a={x:1,y:2}, - b=Symbol; - Object.defineProperties(a,{ - [b("symbol1")]:{value:a,writable:true,configurable:true}, - [b("symbol2")]:{value:a,writable:true,configurable:true} + const a=Symbol, + b={x:1,y:2}; + Object.defineProperties(b,{ + [a("symbol1")]:{value:b,writable:true,configurable:true}, + [a("symbol2")]:{value:b,writable:true,configurable:true} }); - return a + return b })()`, validate(obj) { const symbolKeys = Object.getOwnPropertySymbols(obj); @@ -1299,13 +1299,14 @@ describe('Objects', () => { return obj; }, out: `(()=>{ - const a={}, - b=Object; - b.defineProperties( - b.defineProperty(a,"__proto__",{value:a}), - {y:{value:2,writable:true,enumerable:true,configurable:true}} - ); - return a + const a=Object, + b=a.defineProperty, + c=a.defineProperties( + b({},"__proto__",{writable:true,enumerable:true,configurable:true}), + {y:{value:2,writable:true,enumerable:true,configurable:true}} + ); + b(c,"__proto__",{value:c,writable:false,enumerable:false,configurable:false}); + return c })()`, validate(obj) { expect(obj).toBeObject(); @@ -1363,13 +1364,19 @@ describe('Objects', () => { return obj; }, out: `(()=>{ - const a={y:2}, - b=Object; - b.defineProperties( - b.defineProperty(a,"__proto__",{value:a}), - {z:{value:3,writable:true,enumerable:true,configurable:true}} - ); - return a + const a=Object, + b=a.defineProperties, + c=a.defineProperty, + d=b( + c( + b({},{y:{value:2,writable:true,enumerable:true,configurable:true}}), + "__proto__", + {writable:true,enumerable:true,configurable:true} + ), + {z:{value:3,writable:true,enumerable:true,configurable:true}} + ); + c(d,"__proto__",{value:d,writable:false,enumerable:false,configurable:false}); + return d })()`, validate(obj) { expect(obj).toBeObject(); @@ -1387,41 +1394,89 @@ describe('Objects', () => { }); describe('null prototype object', () => { - itSerializesEqual('non-circular', { - in: () => Object.defineProperty(Object.create(null), '__proto__', {value: {x: 1}}), - out: `(()=>{ - const a=Object; - return a.defineProperty(a.create(null),"__proto__",{value:{x:1}}) - })()`, - validate(obj) { - expect(obj).toBeObject(); - expect(obj).toHaveOwnPropertyNames(['__proto__']); - expect(obj.__proto__).toEqual({x: 1}); // eslint-disable-line no-proto - expect(obj).toHaveDescriptorModifiersFor('__proto__', false, false, false); - expect(obj.x).toBeUndefined(); - expect(obj).toHavePrototype(null); - } + describe('non-circular', () => { + itSerializesEqual('with no descriptor', { + in() { + const obj = Object.create(null); + obj.__proto__ = {x: 1}; // eslint-disable-line no-proto + return obj; + }, + out: `(()=>{ + const a=Object; + return a.defineProperty( + a.create(null), + "__proto__", + {value:{x:1},writable:true,enumerable:true,configurable:true} + ) + })()`, + validate(obj) { + expect(obj).toBeObject(); + expect(obj).toHaveOwnPropertyNames(['__proto__']); + expect(obj.__proto__).toEqual({x: 1}); // eslint-disable-line no-proto + expect(obj).toHaveDescriptorModifiersFor('__proto__', true, true, true); + expect(obj.x).toBeUndefined(); + expect(obj).toHavePrototype(null); + } + }); + + itSerializesEqual('with descriptor', { + in: () => Object.defineProperty(Object.create(null), '__proto__', {value: {x: 1}}), + out: `(()=>{ + const a=Object; + return a.defineProperty(a.create(null),"__proto__",{value:{x:1}}) + })()`, + validate(obj) { + expect(obj).toBeObject(); + expect(obj).toHaveOwnPropertyNames(['__proto__']); + expect(obj.__proto__).toEqual({x: 1}); // eslint-disable-line no-proto + expect(obj).toHaveDescriptorModifiersFor('__proto__', false, false, false); + expect(obj.x).toBeUndefined(); + expect(obj).toHavePrototype(null); + } + }); }); - itSerializesEqual('circular', { - in() { - const obj = Object.create(null); - Object.defineProperty(obj, '__proto__', {value: obj}); - return obj; - }, - out: `(()=>{ - const a=Object, - b=a.create(null); - a.defineProperty(b,"__proto__",{value:b}); - return b - })()`, - validate(obj) { - expect(obj).toBeObject(); - expect(obj).toHaveOwnPropertyNames(['__proto__']); - expect(obj.__proto__).toBe(obj); // eslint-disable-line no-proto - expect(obj).toHaveDescriptorModifiersFor('__proto__', false, false, false); - expect(obj).toHavePrototype(null); - } + describe('circular', () => { + itSerializesEqual('with no descriptor', { + in() { + const obj = Object.create(null); + obj.__proto__ = obj; // eslint-disable-line no-proto + return obj; + }, + out: `(()=>{ + const a=Object.create(null); + a.__proto__=a; + return a + })()`, + validate(obj) { + expect(obj).toBeObject(); + expect(obj).toHaveOwnPropertyNames(['__proto__']); + expect(obj.__proto__).toBe(obj); // eslint-disable-line no-proto + expect(obj).toHaveDescriptorModifiersFor('__proto__', true, true, true); + expect(obj).toHavePrototype(null); + } + }); + + itSerializesEqual('with descriptor', { + in() { + const obj = Object.create(null); + Object.defineProperty(obj, '__proto__', {value: obj}); + return obj; + }, + out: `(()=>{ + const a=Object, + b=a.create(null); + a.defineProperty(b,"__proto__",{value:b}); + return b + })()`, + validate(obj) { + expect(obj).toBeObject(); + expect(obj).toHaveOwnPropertyNames(['__proto__']); + expect(obj.__proto__).toBe(obj); // eslint-disable-line no-proto + expect(obj).toHaveDescriptorModifiersFor('__proto__', false, false, false); + expect(obj).toHavePrototype(null); + } + }); }); }); }); diff --git a/test/other.test.js b/test/other.test.js index 742280e2..fad8fc86 100644 --- a/test/other.test.js +++ b/test/other.test.js @@ -5,18 +5,11 @@ 'use strict'; -// Modules -const parseNodeVersion = require('parse-node-version'); - // Imports const {itSerializes, itSerializesEqual} = require('./support/index.js'); // Tests -// `url[Symbol('context')]` property was removed in NodeJS v20.0.0. -const urlsHaveContext = parseNodeVersion(process.version).major < 20, - itSerializesEqualIfUrlsHaveContext = urlsHaveContext ? itSerializesEqual : itSerializesEqual.skip; - describe('RegExps', () => { itSerializesEqual('with no flags', { in: () => /^foo$/, @@ -101,6 +94,16 @@ describe('Dates', () => { } }); + itSerializes('invalid date', { + in: () => new Date(Number.MAX_SAFE_INTEGER), + out: 'new Date(NaN)', + validate(date) { + expect(date).toHavePrototype(Date.prototype); + expect(date).not.toBeValidDate(); + expect(date.getTime()).toBeNaN(); + } + }); + itSerializesEqual.skip('Date subclass', { in() { class D extends Date {} @@ -128,7 +131,7 @@ describe('Dates', () => { }); describe('URLs', () => { - itSerializesEqual('URL', { + itSerializes('URL', { in: () => new URL('http://foo.com/path/to/file.html?a=1&b=2'), out: 'new URL("http://foo.com/path/to/file.html?a=1&b=2")', validate(url) { @@ -165,7 +168,7 @@ describe('URLs', () => { }); describe('URLSearchParams', () => { - itSerializesEqual('without context', { + itSerializesEqual('without accompanying URL', { in: () => new URLSearchParams('a=1&b=2'), out: 'new URLSearchParams("a=1&b=2")', validate(params) { @@ -174,23 +177,42 @@ describe('URLSearchParams', () => { } }); - // This test only makes sense in NodeJS v18. - // `url[Symbol('context')]` was removed in NodeJS v20.0.0. - itSerializesEqualIfUrlsHaveContext('with context', { - in: () => new URL('http://foo.com/path/to/file.html?a=1&b=2').searchParams, - out: 'new URL("http://foo.com/path/to/file.html?a=1&b=2").searchParams', - /* eslint-disable jest/no-standalone-expect */ - validate(params) { - expect(params).toBeInstanceOf(URLSearchParams); - expect(params.toString()).toBe('a=1&b=2'); + describe('with URL', () => { + itSerializesEqual('URL traced first', { + in() { + const url = new URL('http://foo.com/path/to/file.html?a=1&b=2'); + return {url, params: url.searchParams}; + }, + out: `(()=>{ + const a=new URL("http://foo.com/path/to/file.html?a=1&b=2"); + return{url:a,params:a.searchParams} + })()`, + validate({url, params}) { + expect(url).toBeInstanceOf(URL); + expect(url.toString()).toBe('http://foo.com/path/to/file.html?a=1&b=2'); + expect(params).toBeInstanceOf(URLSearchParams); + expect(params.toString()).toBe('a=1&b=2'); + expect(params).toBe(url.searchParams); + } + }); - const contextSymbol = Object.getOwnPropertySymbols(params)[1]; - expect(contextSymbol.toString()).toBe('Symbol(context)'); - const url = params[contextSymbol]; - expect(url).toBeInstanceOf(URL); - expect(url.toString()).toBe('http://foo.com/path/to/file.html?a=1&b=2'); - } - /* eslint-enable jest/no-standalone-expect */ + itSerializesEqual('URLSearchParams traced first', { + in() { + const url = new URL('http://foo.com/path/to/file.html?a=1&b=2'); + return {params: url.searchParams, url}; + }, + out: `(()=>{ + const a=new URL("http://foo.com/path/to/file.html?a=1&b=2"); + return{params:a.searchParams,url:a} + })()`, + validate({url, params}) { + expect(url).toBeInstanceOf(URL); + expect(url.toString()).toBe('http://foo.com/path/to/file.html?a=1&b=2'); + expect(params).toBeInstanceOf(URLSearchParams); + expect(params.toString()).toBe('a=1&b=2'); + expect(params).toBe(url.searchParams); + } + }); }); itSerializes.skip('URLSearchParams subclass', { diff --git a/test/sets.test.js b/test/sets.test.js index 6e095624..53435f3b 100644 --- a/test/sets.test.js +++ b/test/sets.test.js @@ -234,4 +234,25 @@ describe('WeakSets', () => { expect(weakSet.has(weakSet)).toBeTrue(); } }); + + itSerializes('with circular contents followed by non-circular', { + in() { + const weakSet = new WeakSet(); + weakSet.add(weakSet); + const obj = {x: 1}; + weakSet.add(obj); + return {weakSet, obj}; + }, + out: `(()=>{ + const a={x:1}, + b=new WeakSet([a]); + b.add(b); + return{weakSet:b,obj:a} + })()`, + validate({weakSet, obj}) { + expect(weakSet).toBeInstanceOf(WeakSet); + expect(weakSet.has(weakSet)).toBeTrue(); + expect(weakSet.has(obj)).toBeTrue(); + } + }); });