diff --git a/package.json b/package.json index 0f336ab..d810db8 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ "scripts": { "build": "bundt", "pretest": "npm run build", - "test": "uvu -r esm test" + "test": "uvu -r esm test -i suites" }, "keywords": [ "clone", diff --git a/test/index.js b/test/index.js index 59b9b7b..46470d4 100644 --- a/test/index.js +++ b/test/index.js @@ -1,608 +1,24 @@ -import { suite } from 'uvu'; -import * as assert from 'assert'; -import { klona } from '../src'; +import * as suites from './suites'; +import { klona } from '../src/index'; -const API = suite('exports'); +suites.API(klona); -API('should export a function', () => { - assert.equal(typeof klona, 'function'); -}); +suites.Strings(klona); +suites.Booleans(klona); +suites.Numbers(klona); +suites.Nully(klona); -API.run(); +suites.Dates(klona); +suites.RegExps(klona); -// --- +suites.Objects(klona); +suites.Arrays(klona); -const Arrays = suite('Arrays'); +suites.Functions(klona); +suites.Pollutions(klona); +suites.Classes(klona); -Arrays('flat', () => { - const input = ['foo', 'bar', 'baz']; - const output = klona(input); +suites.Maps(klona); +suites.Sets(klona); - assert.deepEqual(input, output); - - output[1] = 'hello'; - assert.notEqual(input[1], 'hello'); -}); - -Arrays('nested', () => { - const input = ['foo', [1, 2, ['hello', 'world'], 3], 'bar', 'baz']; - const output = klona(input); - - assert.deepEqual(input, output); - - output[1][2][0] = 'howdy'; - assert.equal(input[1][2][0], 'hello'); - - output[1] = 'hello'; - assert.notEqual(input[1], 'hello'); -}); - -Arrays.run(); - -// --- - -const Booleans = suite('Boolean'); - -Booleans('boolean', () => { - const input = true; - let output = klona(input); - - assert.deepEqual(input, output); - - output = false; - assert.equal(input, true); -}); - -Booleans.run(); - -// --- - -const Classes = suite('class'); - -Classes('class', () => { - class Foobar {} - const input = new Foobar(); - const output = klona(input); - - assert.deepEqual(input, output); - assert.equal(input.constructor, output.constructor); - assert.equal(output.constructor.name, 'Foobar'); - - output.foobar = 123; - // @ts-ignore - assert.notEqual(input.foobar, 123); -}); - -// @see https://github.com/lukeed/klona/issues/14 -Classes('prototype', () => { - function Test () {} - Test.prototype.val = 42; - - const input = new Test(); - const output = klona(input); - - assert.deepEqual(input, output); - - assert.deepEqual(output.constructor, Test); - assert.deepEqual(output.__proto__, { val: 42 }); - assert.deepEqual(output, {}); - assert.equal(output.val, 42); -}); - -Classes('constructor properties', () => { - function Test (num) { - this.value = num; - } - - Test.prototype.val = 42; - - const input = new Test(123); - const output = klona(input); - - assert.deepEqual(input, output); - assert.deepEqual(output.constructor, Test); - assert.deepEqual(output.__proto__, { val: 42 }); - assert.equal(output.value, 123); - assert.equal(output.val, 42); -}); - -Classes('constructor properties :: class', () => { - class Test { - constructor(num) { - this.value = num; - } - } - - Test.prototype.val = 42; - - const input = new Test(123); - const output = klona(input); - - assert.deepEqual(input, output); - assert.deepEqual(output.constructor, Test); - assert.deepEqual(output.__proto__, { val: 42 }); - - assert.equal(output.value, 123); - assert.equal(output.val, 42); -}); - -Classes('constructor properties :: defaults', () => { - class Test { - constructor(num = 123) { - this.value = num; - } - } - - const input = new Test(456); - const output = klona(input); - - assert.deepEqual(input, output); - assert.equal(output.value, 456); -}); - -Classes('accessors', () => { - class Test { - get val() { - return 42; - } - } - - const input = new Test(); - const output = klona(input); - - assert.deepEqual(input, output); - assert.deepEqual(output.constructor, Test); - assert.deepEqual(output.__proto__, {}); - - assert.deepEqual( - // @ts-ignore - Object.getOwnPropertyDescriptor(input.__proto__, 'val'), - Object.getOwnPropertyDescriptor(output.__proto__, 'val'), - ); - - assert.equal(output.val, 42); -}) - -Classes.run(); - -// --- - -const Pollution = suite('pollution'); - -// @see https://snyk.io/vuln/SNYK-JS-LODASH-450202 -Pollution('constructor', () => { - const payload = '{"constructor":{"prototype":{"a0":true}}}'; - - const input = JSON.parse(payload); - const output = klona(input); - - assert.equal( - JSON.stringify(output), - payload - ); - - assert.notEqual(({})['a0'], true, 'Safe POJO'); - assert.notEqual(new Object()['a0'], true, 'Safe Object'); - - assert.notEqual(input['a0'], true, 'Safe input'); - assert.notEqual(output['a0'], true, 'Safe output'); -}); - -// @see https://snyk.io/vuln/SNYK-JS-LODASH-450202 -Pollution('__proto__', () => { - const payload = '{"__proto__":{"a0":true}}'; - const input = JSON.parse(payload); - const output = klona(input); - - assert.equal( - JSON.stringify(output), - payload - ); - - assert.notEqual(({})['a0'], true, 'Safe POJO'); - assert.notEqual(new Object()['a0'], true, 'Safe Object'); - - assert.notEqual(input['a0'], true, 'Safe input'); - assert.notEqual(output['a0'], true, 'Safe output'); -}); - -Pollution('prototype', () => { - const payload = '{"prototype":{"hello":"world"}}'; - const input = JSON.parse(payload); - const output = klona(input); - - assert.equal( - JSON.stringify(output), - payload - ); - - assert.notEqual(({})['hello'], 'world', 'Safe POJO'); - assert.notEqual(new Object()['hello'], 'world', 'Safe Object'); - - assert.notEqual(input['hello'], 'world', 'Safe input'); - assert.notEqual(output['hello'], 'world', 'Safe output'); -}); - -Pollution.run(); - -// --- - -const Dates = suite('Date'); - -Dates('date', () => { - const input = new Date; - const output = klona(input); - const original = input.toString(); - - assert.deepEqual(input, output); - - output.setDate('foobar123'); - assert.equal(output.toString(), 'Invalid Date'); - assert.equal(input.toString(), original); -}); - -Dates.run(); - -// --- - -const Functions = suite('Function'); - -Functions('function', () => { - let input = () => {}; - let output = klona(input); - let original = input.toString(); - - assert.deepEqual(input, output); - - input = () => 123; - assert.equal(output.toString(), original); - - output = () => 456; - assert.equal(input.toString(), '() => 123'); -}); - -Functions.run(); - -// --- - -const Maps = suite('Map'); - -Maps('flat', () => { - const input = new Map(); - const output = klona(input); - - assert.deepEqual(input, output); - - output.set('hello', 'world'); - assert.equal(input.get('hello'), undefined); - - input.set('foo', 'bar'); - assert.equal(output.get('foo'), undefined); -}); - -Maps('nested', () => { - const input = new Map([ - ['foo', { a: 1 }], - ['bar', [1, 2, 3]], - ]); - const output = klona(input); - - const foo = output.get('foo'); - foo.b = 2; - foo.a++; - - assert.deepEqual(input.get('foo'), { a: 1 }); - assert.deepEqual(output.get('foo'), { a: 2, b: 2 }); - - output.get('bar').push(4, 5, 6); - assert.deepEqual(input.get('bar'), [1, 2, 3]); - assert.deepEqual(output.get('bar'), [1, 2, 3, 4, 5, 6]); -}); - -Maps('nested :: keys', () => { - const input = new Map([ - [{ foo:1 }, { a: 1 }] - ]); - - const output = klona(input); - assert.deepEqual(input, output); - - [...output.keys()][0].bar = 2; - - assert.deepEqual([...input.keys()][0], { foo:1 }); - assert.deepEqual([...output.keys()][0], { foo:1, bar:2 }); -}); - -Maps.run(); - -// --- - -const Null = suite('null'); - -Null('null', () => { - let input = null; - let output = klona(input); - - assert.deepEqual(input, output); - - output = 1; - assert.equal(input, null); - - input = 123; - assert.equal(output, 1); -}); - -Null.run(); - -// --- - -const Numbers = suite('Number'); - -Numbers('number', () => { - let input = 123; - let output = klona(input); - - assert.deepEqual(input, output); - - output++; - assert.equal(input, 123); - - input += 100; - assert.equal(output, 124); -}); - -Numbers.run(); - -// --- - -const Objects = suite('Object'); - -Objects('flat', () => { - const input = { foo:1, bar:2, baz:3 }; - const output = klona(input); - - assert.deepEqual(input, output); - - output.foo++; - assert.equal(input.foo, 1); - - output.bar = 'hello'; - assert.equal(input.bar, 2); -}); - -Objects('nested', () => { - const input = { - foo: 1, - bar: { - a: 2, - b: ['hello', 'world'], - c: [{ hello: 1, world: 2 }] - }, - baz: 3 - }; - - const output = klona(input); - - assert.equal( - JSON.stringify(input), - JSON.stringify(output) - ); - - output.bar.a = 11; - assert.equal(input.bar.a, 2); - - output.bar.b[1] = 'mundo'; - assert.equal(input.bar.b[1], 'world'); - - output.bar.c[0].hello = 99; - assert.equal(input.bar.c[0].hello, 1); -}); - -Objects.run(); - -// --- - -const RegExps = suite('RegExp'); - -RegExps('basic', () => { - const input = /foo/gi; - const output = klona(input); - - assert.deepEqual(input, output); - - output.exec('foofoofoo'); - assert.equal(output.lastIndex, 3); - assert.equal(input.lastIndex, 0); -}); - -RegExps('state', () => { - const input = /foo/gi; - input.exec('foofoofoo'); - const index = input.lastIndex; - - const output = klona(input); - - assert.deepEqual(input, output); - - assert.equal(index, 3); - assert.equal(input.lastIndex, index); - assert.equal(output.lastIndex, index); -}); - -RegExps.run(); - -// --- - -const Sets = suite('Set'); - -Sets('flat', () => { - const input = new Set('hello'); - const output = klona(input); - - assert.deepEqual(input, output); - - output.add('world'); - assert.equal(input.has('world'), false); - - input.add('foobar'); - assert.equal(output.has('foobar'), false); -}); - -Sets('nested', () => { - const input = new Set([{ foo: 123 }]); - const output = klona(input); - - assert.deepEqual(input, output); - - const [obj] = [...output.keys()]; - obj.bar = 456; - obj.foo++; - - const [item] = [...input.keys()]; - assert.deepEqual(item, { foo: 123 }); -}); - -Sets.run(); - -// --- - - -const Strings = suite('String'); - -Strings('string', () => { - let input = 'hello'; - let output = klona(input); - - assert.equal(input, output); - - output += ' world'; - assert.equal(input, 'hello'); - - input += '123'; - assert.equal(output, 'hello world'); -}); - -Strings.run(); - -// --- - -const TypedArrays = suite('TypedArray'); - -TypedArrays('Buffer', () => { - const input = Buffer.from('asd'); - const output = klona(input); - - assert.deepEqual(input, output); - - output.write('foobar'); - assert.equal(input.toString(), 'asd'); - - output[1] = 11; - assert.notEqual(input[1], output[1]); - - const current = output.toString(); - input.write('hello'); - assert.equal(output.toString(), current); -}); - -TypedArrays('Int16Array', () => { - const input = new Int16Array([42]); - const output = klona(input); - - assert.deepEqual(input, output); - - output[1] = 42; - assert.equal(input[1], undefined); - - input[0] = 0; - assert.equal(output[0], 42); -}); - -TypedArrays('Int32Array', () => { - const buf = new ArrayBuffer(8); - const input = new Int32Array(buf); - const output = klona(input); - - assert.deepEqual(input, new Int32Array([0, 0])); - assert.deepEqual(output, new Int32Array([0, 0])); - assert.deepEqual(input, output); - - output[1] = 42; - assert.equal(input[1], 0); - - input[0] = 22; - assert.equal(output[0], 0); -}); - -TypedArrays('Int32Array', () => { - const buf = new ArrayBuffer(8); - const input = new Int32Array(buf); - const output = klona(input); - - assert.deepEqual(input, new Int32Array([0, 0])); - assert.deepEqual(output, new Int32Array([0, 0])); - assert.deepEqual(input, output); - - output[1] = 42; - assert.equal(input[1], 0); - - input[0] = 22; - assert.equal(output[0], 0); -}); - -TypedArrays('ArrayBuffer :: empty', () => { - const input = new ArrayBuffer(6); - const output = klona(input); - - assert.deepEqual(input, output); - - const view1 = new DataView(input); - const view2 = new DataView(output); - - view1.setInt8(0, 4); - assert.equal(view1.getInt8(0), 4); - assert.equal(view2.getInt8(0), 0); - - view2.setInt8(1, 8); - assert.equal(view1.getInt8(1), 0); - assert.equal(view2.getInt8(1), 8); -}); - -TypedArrays('ArrayBuffer :: values', () => { - const input = new ArrayBuffer(3); - const view1 = new DataView(input); - - view1.setInt8(0, 4); - view1.setInt8(1, 5); - view1.setInt8(2, 6); - - const output = klona(input); - const view2 = new DataView(output); - - assert.deepEqual(input, output); - - assert.equal(view2.getInt8(0), 4); - assert.equal(view2.getInt8(1), 5); - assert.equal(view2.getInt8(2), 6); -}); - -TypedArrays('DataView', () => { - const ints = new Int8Array([1, 2, 3]); - const input = new DataView(ints.buffer); - const output = klona(input); - - assert.deepEqual(input, output); - assert.deepEqual(input.buffer, output.buffer); - - input.setInt8(1, 6); - assert.equal(ints[1], 6); - assert.equal(input.getInt8(1), 6); - assert.equal(output.getInt8(1), 2); - - output.setInt8(0, 4); - assert.equal(ints[0], 1); - assert.equal(input.getInt8(0), 1); - assert.equal(output.getInt8(0), 4); -}); - -TypedArrays.run(); +suites.TypedArrays(klona); diff --git a/test/suites/api.js b/test/suites/api.js new file mode 100644 index 0000000..11b3d54 --- /dev/null +++ b/test/suites/api.js @@ -0,0 +1,12 @@ +import { suite } from 'uvu'; +import * as assert from 'uvu/assert'; + +export default function (klona) { + const API = suite('exports'); + + API('should export a function', () => { + assert.type(klona, 'function'); + }); + + API.run(); +} diff --git a/test/suites/array.js b/test/suites/array.js new file mode 100644 index 0000000..e50a4f5 --- /dev/null +++ b/test/suites/array.js @@ -0,0 +1,31 @@ +import { suite } from 'uvu'; +import * as assert from 'assert'; + +export default function (klona) { + const Arrays = suite('Arrays'); + + Arrays('flat', () => { + const input = ['foo', 'bar', 'baz']; + const output = klona(input); + + assert.deepEqual(input, output); + + output[1] = 'hello'; + assert.notEqual(input[1], 'hello'); + }); + + Arrays('nested', () => { + const input = ['foo', [1, 2, ['hello', 'world'], 3], 'bar', 'baz']; + const output = klona(input); + + assert.deepEqual(input, output); + + output[1][2][0] = 'howdy'; + assert.equal(input[1][2][0], 'hello'); + + output[1] = 'hello'; + assert.notEqual(input[1], 'hello'); + }); + + Arrays.run(); +} diff --git a/test/suites/boolean.js b/test/suites/boolean.js new file mode 100644 index 0000000..c55371c --- /dev/null +++ b/test/suites/boolean.js @@ -0,0 +1,18 @@ +import { suite } from 'uvu'; +import * as assert from 'assert'; + +export default function (klona) { + const Booleans = suite('Boolean'); + + Booleans('boolean', () => { + const input = true; + let output = klona(input); + + assert.deepEqual(input, output); + + output = false; + assert.equal(input, true); + }); + + Booleans.run(); +} diff --git a/test/suites/class.js b/test/suites/class.js new file mode 100644 index 0000000..752ebf4 --- /dev/null +++ b/test/suites/class.js @@ -0,0 +1,112 @@ +import { suite } from 'uvu'; +import * as assert from 'assert'; + +export default function (klona) { + const Classes = suite('class'); + + Classes('class', () => { + class Foobar {} + const input = new Foobar(); + const output = klona(input); + + assert.deepEqual(input, output); + assert.equal(input.constructor, output.constructor); + assert.equal(output.constructor.name, 'Foobar'); + + output.foobar = 123; + // @ts-ignore + assert.notEqual(input.foobar, 123); + }); + + // @see https://github.com/lukeed/klona/issues/14 + Classes('prototype', () => { + function Test () {} + Test.prototype.val = 42; + + const input = new Test(); + const output = klona(input); + + assert.deepEqual(input, output); + + assert.deepEqual(output.constructor, Test); + assert.deepEqual(output.__proto__, { val: 42 }); + assert.deepEqual(output, {}); + assert.equal(output.val, 42); + }); + + Classes('constructor properties', () => { + function Test (num) { + this.value = num; + } + + Test.prototype.val = 42; + + const input = new Test(123); + const output = klona(input); + + assert.deepEqual(input, output); + assert.deepEqual(output.constructor, Test); + assert.deepEqual(output.__proto__, { val: 42 }); + assert.equal(output.value, 123); + assert.equal(output.val, 42); + }); + + Classes('constructor properties :: class', () => { + class Test { + constructor(num) { + this.value = num; + } + } + + Test.prototype.val = 42; + + const input = new Test(123); + const output = klona(input); + + assert.deepEqual(input, output); + assert.deepEqual(output.constructor, Test); + assert.deepEqual(output.__proto__, { val: 42 }); + + assert.equal(output.value, 123); + assert.equal(output.val, 42); + }); + + Classes('constructor properties :: defaults', () => { + class Test { + constructor(num = 123) { + this.value = num; + } + } + + const input = new Test(456); + const output = klona(input); + + assert.deepEqual(input, output); + assert.equal(output.value, 456); + }); + + Classes('accessors', () => { + class Test { + get val() { + return 42; + } + } + + const input = new Test(); + const output = klona(input); + + assert.deepEqual(input, output); + assert.deepEqual(output.constructor, Test); + assert.deepEqual(output.__proto__, {}); + + assert.deepEqual( + // @ts-ignore + Object.getOwnPropertyDescriptor(input.__proto__, 'val'), + Object.getOwnPropertyDescriptor(output.__proto__, 'val'), + ); + + assert.equal(output.val, 42); + }); + + Classes.run(); +} diff --git a/test/suites/date.js b/test/suites/date.js new file mode 100644 index 0000000..727affa --- /dev/null +++ b/test/suites/date.js @@ -0,0 +1,20 @@ +import { suite } from 'uvu'; +import * as assert from 'assert'; + +export default function (klona) { + const Dates = suite('Date'); + + Dates('date', () => { + const input = new Date; + const output = klona(input); + const original = input.toString(); + + assert.deepEqual(input, output); + + output.setDate('foobar123'); + assert.equal(output.toString(), 'Invalid Date'); + assert.equal(input.toString(), original); + }); + + Dates.run(); +} diff --git a/test/suites/function.js b/test/suites/function.js new file mode 100644 index 0000000..380d1c3 --- /dev/null +++ b/test/suites/function.js @@ -0,0 +1,22 @@ +import { suite } from 'uvu'; +import * as assert from 'assert'; + +export default function (klona) { + const Functions = suite('Function'); + + Functions('function', () => { + let input = () => {}; + let output = klona(input); + let original = input.toString(); + + assert.deepEqual(input, output); + + input = () => 123; + assert.equal(output.toString(), original); + + output = () => 456; + assert.equal(input.toString(), '() => 123'); + }); + + Functions.run(); +} diff --git a/test/suites/index.js b/test/suites/index.js new file mode 100644 index 0000000..a806fdd --- /dev/null +++ b/test/suites/index.js @@ -0,0 +1,21 @@ +export { default as API } from './api'; + +export { default as Numbers } from './number'; +export { default as Booleans } from './boolean'; +export { default as Strings } from './string'; +export { default as Nully } from './nullish'; + +export { default as Maps } from './map'; +export { default as Sets } from './set'; + +export { default as TypedArrays } from './typedarray'; + +export { default as Dates } from './date'; +export { default as RegExps } from './regexp'; + +export { default as Objects } from './object'; +export { default as Arrays } from './array'; + +export { default as Functions } from './function'; +export { default as Pollutions } from './pollution'; +export { default as Classes } from './class'; diff --git a/test/suites/map.js b/test/suites/map.js new file mode 100644 index 0000000..d231f35 --- /dev/null +++ b/test/suites/map.js @@ -0,0 +1,54 @@ +import { suite } from 'uvu'; +import * as assert from 'assert'; + +export default function (klona) { + const Maps = suite('Map'); + + Maps('flat', () => { + const input = new Map(); + const output = klona(input); + + assert.deepEqual(input, output); + + output.set('hello', 'world'); + assert.equal(input.get('hello'), undefined); + + input.set('foo', 'bar'); + assert.equal(output.get('foo'), undefined); + }); + + Maps('nested', () => { + const input = new Map([ + ['foo', { a: 1 }], + ['bar', [1, 2, 3]], + ]); + const output = klona(input); + + const foo = output.get('foo'); + foo.b = 2; + foo.a++; + + assert.deepEqual(input.get('foo'), { a: 1 }); + assert.deepEqual(output.get('foo'), { a: 2, b: 2 }); + + output.get('bar').push(4, 5, 6); + assert.deepEqual(input.get('bar'), [1, 2, 3]); + assert.deepEqual(output.get('bar'), [1, 2, 3, 4, 5, 6]); + }); + + Maps('nested :: keys', () => { + const input = new Map([ + [{ foo:1 }, { a: 1 }] + ]); + + const output = klona(input); + assert.deepEqual(input, output); + + [...output.keys()][0].bar = 2; + + assert.deepEqual([...input.keys()][0], { foo:1 }); + assert.deepEqual([...output.keys()][0], { foo:1, bar:2 }); + }); + + Maps.run(); +} diff --git a/test/suites/nullish.js b/test/suites/nullish.js new file mode 100644 index 0000000..a17f6dd --- /dev/null +++ b/test/suites/nullish.js @@ -0,0 +1,60 @@ +import { suite } from 'uvu'; +import * as assert from 'assert'; + +export default function (klona) { + const Nully = suite('nullish'); + + Nully('null', () => { + let input = null; + let output = klona(input); + + assert.deepEqual(input, output); + + output = 1; + assert.equal(input, null); + + input = 123; + assert.equal(output, 1); + }); + + Nully('undefined', () => { + let input = undefined; + let output = klona(input); + + assert.deepEqual(input, output); + + output = 1; + assert.equal(input, undefined); + + input = 123; + assert.equal(output, 1); + }); + + Nully('0', () => { + let input = 0; + let output = klona(input); + + assert.deepEqual(input, output); + + output = 1; + assert.equal(input, 0); + + input = 123; + assert.equal(output, 1); + }); + + Nully('NaN', () => { + let input = NaN; + let output = klona(input); + + assert.equal(Number.isNaN(output), true); + + output = 1; + assert.equal(Number.isNaN(input), true); + + input = 123; + assert.equal(output, 1); + }); + + Nully.run(); +} diff --git a/test/suites/number.js b/test/suites/number.js new file mode 100644 index 0000000..4ca68f4 --- /dev/null +++ b/test/suites/number.js @@ -0,0 +1,21 @@ +import { suite } from 'uvu'; +import * as assert from 'assert'; + +export default function (klona) { + const Numbers = suite('Number'); + + Numbers('number', () => { + let input = 123; + let output = klona(input); + + assert.deepEqual(input, output); + + output++; + assert.equal(input, 123); + + input += 100; + assert.equal(output, 124); + }); + + Numbers.run(); +} diff --git a/test/suites/object.js b/test/suites/object.js new file mode 100644 index 0000000..43ddede --- /dev/null +++ b/test/suites/object.js @@ -0,0 +1,49 @@ +import { suite } from 'uvu'; +import * as assert from 'assert'; + +export default function (klona) { + const Objects = suite('Object'); + + Objects('flat', () => { + const input = { foo:1, bar:2, baz:3 }; + const output = klona(input); + + assert.deepEqual(input, output); + + output.foo++; + assert.equal(input.foo, 1); + + output.bar = 'hello'; + assert.equal(input.bar, 2); + }); + + Objects('nested', () => { + const input = { + foo: 1, + bar: { + a: 2, + b: ['hello', 'world'], + c: [{ hello: 1, world: 2 }] + }, + baz: 3 + }; + + const output = klona(input); + + assert.equal( + JSON.stringify(input), + JSON.stringify(output) + ); + + output.bar.a = 11; + assert.equal(input.bar.a, 2); + + output.bar.b[1] = 'mundo'; + assert.equal(input.bar.b[1], 'world'); + + output.bar.c[0].hello = 99; + assert.equal(input.bar.c[0].hello, 1); + }); + + Objects.run(); +} diff --git a/test/suites/pollution.js b/test/suites/pollution.js new file mode 100644 index 0000000..b1ab918 --- /dev/null +++ b/test/suites/pollution.js @@ -0,0 +1,62 @@ +import { suite } from 'uvu'; +import * as assert from 'assert'; + +export default function (klona) { + const Pollution = suite('pollution'); + + // @see https://snyk.io/vuln/SNYK-JS-LODASH-450202 + Pollution('constructor', () => { + const payload = '{"constructor":{"prototype":{"a0":true}}}'; + + const input = JSON.parse(payload); + const output = klona(input); + + assert.equal( + JSON.stringify(output), + payload + ); + + assert.notEqual(({})['a0'], true, 'Safe POJO'); + assert.notEqual(new Object()['a0'], true, 'Safe Object'); + + assert.notEqual(input['a0'], true, 'Safe input'); + assert.notEqual(output['a0'], true, 'Safe output'); + }); + + // @see https://snyk.io/vuln/SNYK-JS-LODASH-450202 + Pollution('__proto__', () => { + const payload = '{"__proto__":{"a0":true}}'; + const input = JSON.parse(payload); + const output = klona(input); + + assert.equal( + JSON.stringify(output), + payload + ); + + assert.notEqual(({})['a0'], true, 'Safe POJO'); + assert.notEqual(new Object()['a0'], true, 'Safe Object'); + + assert.notEqual(input['a0'], true, 'Safe input'); + assert.notEqual(output['a0'], true, 'Safe output'); + }); + + Pollution('prototype', () => { + const payload = '{"prototype":{"hello":"world"}}'; + const input = JSON.parse(payload); + const output = klona(input); + + assert.equal( + JSON.stringify(output), + payload + ); + + assert.notEqual(({})['hello'], 'world', 'Safe POJO'); + assert.notEqual(new Object()['hello'], 'world', 'Safe Object'); + + assert.notEqual(input['hello'], 'world', 'Safe input'); + assert.notEqual(output['hello'], 'world', 'Safe output'); + }); + + Pollution.run(); +} diff --git a/test/suites/regexp.js b/test/suites/regexp.js new file mode 100644 index 0000000..068aef3 --- /dev/null +++ b/test/suites/regexp.js @@ -0,0 +1,33 @@ +import { suite } from 'uvu'; +import * as assert from 'assert'; + +export default function (klona) { + const RegExps = suite('RegExp'); + + RegExps('basic', () => { + const input = /foo/gi; + const output = klona(input); + + assert.deepEqual(input, output); + + output.exec('foofoofoo'); + assert.equal(output.lastIndex, 3); + assert.equal(input.lastIndex, 0); + }); + + RegExps('state', () => { + const input = /foo/gi; + input.exec('foofoofoo'); + const index = input.lastIndex; + + const output = klona(input); + + assert.deepEqual(input, output); + + assert.equal(index, 3); + assert.equal(input.lastIndex, index); + assert.equal(output.lastIndex, index); + }); + + RegExps.run(); +} diff --git a/test/suites/set.js b/test/suites/set.js new file mode 100644 index 0000000..f2019e1 --- /dev/null +++ b/test/suites/set.js @@ -0,0 +1,35 @@ +import { suite } from 'uvu'; +import * as assert from 'assert'; + +export default function (klona) { + const Sets = suite('Set'); + + Sets('flat', () => { + const input = new Set('hello'); + const output = klona(input); + + assert.deepEqual(input, output); + + output.add('world'); + assert.equal(input.has('world'), false); + + input.add('foobar'); + assert.equal(output.has('foobar'), false); + }); + + Sets('nested', () => { + const input = new Set([{ foo: 123 }]); + const output = klona(input); + + assert.deepEqual(input, output); + + const [obj] = [...output.keys()]; + obj.bar = 456; + obj.foo++; + + const [item] = [...input.keys()]; + assert.deepEqual(item, { foo: 123 }); + }); + + Sets.run(); +} diff --git a/test/suites/string.js b/test/suites/string.js new file mode 100644 index 0000000..80fe2d4 --- /dev/null +++ b/test/suites/string.js @@ -0,0 +1,21 @@ +import { suite } from 'uvu'; +import * as assert from 'assert'; + +export default function (klona) { + const Strings = suite('String'); + + Strings('string', () => { + let input = 'hello'; + let output = klona(input); + + assert.equal(input, output); + + output += ' world'; + assert.equal(input, 'hello'); + + input += '123'; + assert.equal(output, 'hello world'); + }); + + Strings.run(); +} diff --git a/test/suites/typedarray.js b/test/suites/typedarray.js new file mode 100644 index 0000000..0cfa561 --- /dev/null +++ b/test/suites/typedarray.js @@ -0,0 +1,126 @@ +import { suite } from 'uvu'; +// import * as assert from 'uvu/assert'; +import * as assert from 'assert'; + +export default function(klona) { + const test = suite('TypedArray'); + + test('Buffer', () => { + const input = Buffer.from('asd'); + const output = klona(input); + + assert.deepEqual(input, output); + + output.write('foobar'); + assert.equal(input.toString(), 'asd'); + + output[1] = 11; + assert.notEqual(input[1], output[1]); + + const current = output.toString(); + input.write('hello'); + assert.equal(output.toString(), current); + }); + + test('Int16Array', () => { + const input = new Int16Array([42]); + const output = klona(input); + + assert.deepEqual(input, output); + + output[1] = 42; + assert.equal(input[1], undefined); + + input[0] = 0; + assert.equal(output[0], 42); + }); + + test('Int32Array', () => { + const buf = new ArrayBuffer(8); + const input = new Int32Array(buf); + const output = klona(input); + + assert.deepEqual(input, new Int32Array([0, 0])); + assert.deepEqual(output, new Int32Array([0, 0])); + assert.deepEqual(input, output); + + output[1] = 42; + assert.equal(input[1], 0); + + input[0] = 22; + assert.equal(output[0], 0); + }); + + test('Int32Array', () => { + const buf = new ArrayBuffer(8); + const input = new Int32Array(buf); + const output = klona(input); + + assert.deepEqual(input, new Int32Array([0, 0])); + assert.deepEqual(output, new Int32Array([0, 0])); + assert.deepEqual(input, output); + + output[1] = 42; + assert.equal(input[1], 0); + + input[0] = 22; + assert.equal(output[0], 0); + }); + + test('ArrayBuffer :: empty', () => { + const input = new ArrayBuffer(6); + const output = klona(input); + + assert.deepEqual(input, output); + + const view1 = new DataView(input); + const view2 = new DataView(output); + + view1.setInt8(0, 4); + assert.equal(view1.getInt8(0), 4); + assert.equal(view2.getInt8(0), 0); + + view2.setInt8(1, 8); + assert.equal(view1.getInt8(1), 0); + assert.equal(view2.getInt8(1), 8); + }); + + test('ArrayBuffer :: values', () => { + const input = new ArrayBuffer(3); + const view1 = new DataView(input); + + view1.setInt8(0, 4); + view1.setInt8(1, 5); + view1.setInt8(2, 6); + + const output = klona(input); + const view2 = new DataView(output); + + assert.deepEqual(input, output); + + assert.equal(view2.getInt8(0), 4); + assert.equal(view2.getInt8(1), 5); + assert.equal(view2.getInt8(2), 6); + }); + + test('DataView', () => { + const ints = new Int8Array([1, 2, 3]); + const input = new DataView(ints.buffer); + const output = klona(input); + + assert.deepEqual(input, output); + assert.deepEqual(input.buffer, output.buffer); + + input.setInt8(1, 6); + assert.equal(ints[1], 6); + assert.equal(input.getInt8(1), 6); + assert.equal(output.getInt8(1), 2); + + output.setInt8(0, 4); + assert.equal(ints[0], 1); + assert.equal(input.getInt8(0), 1); + assert.equal(output.getInt8(0), 4); + }); + + test.run(); +}