[6a3a178] | 1 | 'use strict'
|
---|
| 2 |
|
---|
| 3 | const { test } = require('tap')
|
---|
| 4 | const rfdc = require('..')
|
---|
| 5 | const cloneDefault = require('../default')
|
---|
| 6 | const clone = rfdc()
|
---|
| 7 | const cloneProto = rfdc({ proto: true })
|
---|
| 8 | const cloneCircles = rfdc({ circles: true })
|
---|
| 9 | const cloneCirclesProto = rfdc({ circles: true, proto: true })
|
---|
| 10 |
|
---|
| 11 | const rnd = (max) => Math.round(Math.random() * max)
|
---|
| 12 |
|
---|
| 13 | types(clone, 'default')
|
---|
| 14 | types(cloneProto, 'proto option')
|
---|
| 15 | types(cloneCircles, 'circles option')
|
---|
| 16 | types(cloneCirclesProto, 'circles and proto option')
|
---|
| 17 |
|
---|
| 18 | test('default – does not copy proto properties', async ({ is }) => {
|
---|
| 19 | is(clone(Object.create({ a: 1 })).a, undefined, 'value not copied')
|
---|
| 20 | })
|
---|
| 21 | test('default – shorthand import', async ({ same }) => {
|
---|
| 22 | same(
|
---|
| 23 | clone(Object.create({ a: 1 })),
|
---|
| 24 | cloneDefault(Object.create({ a: 1 })),
|
---|
| 25 | 'import equals clone with default options'
|
---|
| 26 | )
|
---|
| 27 | })
|
---|
| 28 | test('proto option – copies enumerable proto properties', async ({ is }) => {
|
---|
| 29 | is(cloneProto(Object.create({ a: 1 })).a, 1, 'value copied')
|
---|
| 30 | })
|
---|
| 31 | test('circles option - circular object', async ({ same, is, isNot }) => {
|
---|
| 32 | const o = { nest: { a: 1, b: 2 } }
|
---|
| 33 | o.circular = o
|
---|
| 34 | same(cloneCircles(o), o, 'same values')
|
---|
| 35 | isNot(cloneCircles(o), o, 'different objects')
|
---|
| 36 | isNot(cloneCircles(o).nest, o.nest, 'different nested objects')
|
---|
| 37 | const c = cloneCircles(o)
|
---|
| 38 | is(c.circular, c, 'circular references point to copied parent')
|
---|
| 39 | isNot(c.circular, o, 'circular references do not point to original parent')
|
---|
| 40 | })
|
---|
| 41 | test('circles option – deep circular object', async ({ same, is, isNot }) => {
|
---|
| 42 | const o = { nest: { a: 1, b: 2 } }
|
---|
| 43 | o.nest.circular = o
|
---|
| 44 | same(cloneCircles(o), o, 'same values')
|
---|
| 45 | isNot(cloneCircles(o), o, 'different objects')
|
---|
| 46 | isNot(cloneCircles(o).nest, o.nest, 'different nested objects')
|
---|
| 47 | const c = cloneCircles(o)
|
---|
| 48 | is(c.nest.circular, c, 'circular references point to copied parent')
|
---|
| 49 | isNot(
|
---|
| 50 | c.nest.circular,
|
---|
| 51 | o,
|
---|
| 52 | 'circular references do not point to original parent'
|
---|
| 53 | )
|
---|
| 54 | })
|
---|
| 55 | test('circles option alone – does not copy proto properties', async ({
|
---|
| 56 | is
|
---|
| 57 | }) => {
|
---|
| 58 | is(cloneCircles(Object.create({ a: 1 })).a, undefined, 'value not copied')
|
---|
| 59 | })
|
---|
| 60 | test('circles and proto option – copies enumerable proto properties', async ({
|
---|
| 61 | is
|
---|
| 62 | }) => {
|
---|
| 63 | is(cloneCirclesProto(Object.create({ a: 1 })).a, 1, 'value copied')
|
---|
| 64 | })
|
---|
| 65 | test('circles and proto option - circular object', async ({
|
---|
| 66 | same,
|
---|
| 67 | is,
|
---|
| 68 | isNot
|
---|
| 69 | }) => {
|
---|
| 70 | const o = { nest: { a: 1, b: 2 } }
|
---|
| 71 | o.circular = o
|
---|
| 72 | same(cloneCirclesProto(o), o, 'same values')
|
---|
| 73 | isNot(cloneCirclesProto(o), o, 'different objects')
|
---|
| 74 | isNot(cloneCirclesProto(o).nest, o.nest, 'different nested objects')
|
---|
| 75 | const c = cloneCirclesProto(o)
|
---|
| 76 | is(c.circular, c, 'circular references point to copied parent')
|
---|
| 77 | isNot(c.circular, o, 'circular references do not point to original parent')
|
---|
| 78 | })
|
---|
| 79 | test('circles and proto option – deep circular object', async ({
|
---|
| 80 | same,
|
---|
| 81 | is,
|
---|
| 82 | isNot
|
---|
| 83 | }) => {
|
---|
| 84 | const o = { nest: { a: 1, b: 2 } }
|
---|
| 85 | o.nest.circular = o
|
---|
| 86 | same(cloneCirclesProto(o), o, 'same values')
|
---|
| 87 | isNot(cloneCirclesProto(o), o, 'different objects')
|
---|
| 88 | isNot(cloneCirclesProto(o).nest, o.nest, 'different nested objects')
|
---|
| 89 | const c = cloneCirclesProto(o)
|
---|
| 90 | is(c.nest.circular, c, 'circular references point to copied parent')
|
---|
| 91 | isNot(
|
---|
| 92 | c.nest.circular,
|
---|
| 93 | o,
|
---|
| 94 | 'circular references do not point to original parent'
|
---|
| 95 | )
|
---|
| 96 | })
|
---|
| 97 | test('circles and proto option – deep circular array', async ({
|
---|
| 98 | same,
|
---|
| 99 | is,
|
---|
| 100 | isNot
|
---|
| 101 | }) => {
|
---|
| 102 | const o = { nest: [1, 2] }
|
---|
| 103 | o.nest.push(o)
|
---|
| 104 | same(cloneCirclesProto(o), o, 'same values')
|
---|
| 105 | isNot(cloneCirclesProto(o), o, 'different objects')
|
---|
| 106 | isNot(cloneCirclesProto(o).nest, o.nest, 'different nested objects')
|
---|
| 107 | const c = cloneCirclesProto(o)
|
---|
| 108 | is(c.nest[2], c, 'circular references point to copied parent')
|
---|
| 109 | isNot(c.nest[2], o, 'circular references do not point to original parent')
|
---|
| 110 | })
|
---|
| 111 |
|
---|
| 112 | function types (clone, label) {
|
---|
| 113 | test(label + ' – number', async ({ is }) => {
|
---|
| 114 | is(clone(42), 42, 'same value')
|
---|
| 115 | })
|
---|
| 116 | test(label + ' – string', async ({ is }) => {
|
---|
| 117 | is(clone('str'), 'str', 'same value')
|
---|
| 118 | })
|
---|
| 119 | test(label + ' – boolean', async ({ is }) => {
|
---|
| 120 | is(clone(true), true, 'same value')
|
---|
| 121 | })
|
---|
| 122 | test(label + ' – function', async ({ is }) => {
|
---|
| 123 | const fn = () => {}
|
---|
| 124 | is(clone(fn), fn, 'same function')
|
---|
| 125 | })
|
---|
| 126 | test(label + ' – async function', async ({ is }) => {
|
---|
| 127 | const fn = async () => {}
|
---|
| 128 | is(clone(fn), fn, 'same function')
|
---|
| 129 | })
|
---|
| 130 | test(label + ' – generator function', async ({ is }) => {
|
---|
| 131 | const fn = function * () {}
|
---|
| 132 | is(clone(fn), fn, 'same function')
|
---|
| 133 | })
|
---|
| 134 | test(label + ' – date', async ({ is, isNot }) => {
|
---|
| 135 | const date = new Date()
|
---|
| 136 | is(+clone(date), +date, 'same value')
|
---|
| 137 | isNot(clone(date), date, 'different object')
|
---|
| 138 | })
|
---|
| 139 | test(label + ' – null', async ({ is }) => {
|
---|
| 140 | is(clone(null), null, 'same value')
|
---|
| 141 | })
|
---|
| 142 | test(label + ' – shallow object', async ({ same, isNot }) => {
|
---|
| 143 | const o = { a: 1, b: 2 }
|
---|
| 144 | same(clone(o), o, 'same values')
|
---|
| 145 | isNot(clone(o), o, 'different object')
|
---|
| 146 | })
|
---|
| 147 | test(label + ' – shallow array', async ({ same, isNot }) => {
|
---|
| 148 | const o = [1, 2]
|
---|
| 149 | same(clone(o), o, 'same values')
|
---|
| 150 | isNot(clone(o), o, 'different arrays')
|
---|
| 151 | })
|
---|
| 152 | test(label + ' – deep object', async ({ same, isNot }) => {
|
---|
| 153 | const o = { nest: { a: 1, b: 2 } }
|
---|
| 154 | same(clone(o), o, 'same values')
|
---|
| 155 | isNot(clone(o), o, 'different objects')
|
---|
| 156 | isNot(clone(o).nest, o.nest, 'different nested objects')
|
---|
| 157 | })
|
---|
| 158 | test(label + ' – deep array', async ({ same, isNot }) => {
|
---|
| 159 | const o = [{ a: 1, b: 2 }, [3]]
|
---|
| 160 | same(clone(o), o, 'same values')
|
---|
| 161 | isNot(clone(o), o, 'different arrays')
|
---|
| 162 | isNot(clone(o)[0], o[0], 'different array elements')
|
---|
| 163 | isNot(clone(o)[1], o[1], 'different array elements')
|
---|
| 164 | })
|
---|
| 165 | test(label + ' – nested number', async ({ is }) => {
|
---|
| 166 | is(clone({ a: 1 }).a, 1, 'same value')
|
---|
| 167 | })
|
---|
| 168 | test(label + ' – nested string', async ({ is }) => {
|
---|
| 169 | is(clone({ s: 'str' }).s, 'str', 'same value')
|
---|
| 170 | })
|
---|
| 171 | test(label + ' – nested boolean', async ({ is }) => {
|
---|
| 172 | is(clone({ b: true }).b, true, 'same value')
|
---|
| 173 | })
|
---|
| 174 | test(label + ' – nested function', async ({ is }) => {
|
---|
| 175 | const fn = () => {}
|
---|
| 176 | is(clone({ fn }).fn, fn, 'same function')
|
---|
| 177 | })
|
---|
| 178 | test(label + ' – nested async function', async ({ is }) => {
|
---|
| 179 | const fn = async () => {}
|
---|
| 180 | is(clone({ fn }).fn, fn, 'same function')
|
---|
| 181 | })
|
---|
| 182 | test(label + ' – nested generator function', async ({ is }) => {
|
---|
| 183 | const fn = function * () {}
|
---|
| 184 | is(clone({ fn }).fn, fn, 'same function')
|
---|
| 185 | })
|
---|
| 186 | test(label + ' – nested date', async ({ is, isNot }) => {
|
---|
| 187 | const date = new Date()
|
---|
| 188 | is(+clone({ d: date }).d, +date, 'same value')
|
---|
| 189 | isNot(clone({ d: date }).d, date, 'different object')
|
---|
| 190 | })
|
---|
| 191 | test(label + ' – nested date in array', async ({ is, isNot }) => {
|
---|
| 192 | const date = new Date()
|
---|
| 193 | is(+clone({ d: [date] }).d[0], +date, 'same value')
|
---|
| 194 | isNot(clone({ d: [date] }).d[0], date, 'different object')
|
---|
| 195 | is(+cloneCircles({ d: [date] }).d[0], +date, 'same value')
|
---|
| 196 | isNot(cloneCircles({ d: [date] }).d, date, 'different object')
|
---|
| 197 | })
|
---|
| 198 | test(label + ' – nested null', async ({ is }) => {
|
---|
| 199 | is(clone({ n: null }).n, null, 'same value')
|
---|
| 200 | })
|
---|
| 201 | test(label + ' – arguments', async ({ isNot, same }) => {
|
---|
| 202 | function fn (...args) {
|
---|
| 203 | same(clone(arguments), args, 'same values')
|
---|
| 204 | isNot(clone(arguments), arguments, 'different object')
|
---|
| 205 | }
|
---|
| 206 | fn(1, 2, 3)
|
---|
| 207 | })
|
---|
| 208 | test(`${label} copies buffers from object correctly`, async ({ ok, is, isNot }) => {
|
---|
| 209 | const input = Date.now().toString(36)
|
---|
| 210 | const inputBuffer = Buffer.from(input)
|
---|
| 211 | const clonedBuffer = clone({ a: inputBuffer }).a
|
---|
| 212 | ok(Buffer.isBuffer(clonedBuffer), 'cloned value is buffer')
|
---|
| 213 | isNot(clonedBuffer, inputBuffer, 'cloned buffer is not same as input buffer')
|
---|
| 214 | is(clonedBuffer.toString(), input, 'cloned buffer content is correct')
|
---|
| 215 | })
|
---|
| 216 | test(`${label} copies buffers from arrays correctly`, async ({ ok, is, isNot }) => {
|
---|
| 217 | const input = Date.now().toString(36)
|
---|
| 218 | const inputBuffer = Buffer.from(input)
|
---|
| 219 | const [clonedBuffer] = clone([inputBuffer])
|
---|
| 220 | ok(Buffer.isBuffer(clonedBuffer), 'cloned value is buffer')
|
---|
| 221 | isNot(clonedBuffer, inputBuffer, 'cloned buffer is not same as input buffer')
|
---|
| 222 | is(clonedBuffer.toString(), input, 'cloned buffer content is correct')
|
---|
| 223 | })
|
---|
| 224 | test(`${label} copies TypedArrays from object correctly`, async ({ ok, is, isNot }) => {
|
---|
| 225 | const [input1, input2] = [rnd(10), rnd(10)]
|
---|
| 226 | var buffer = new ArrayBuffer(8)
|
---|
| 227 | const int32View = new Int32Array(buffer)
|
---|
| 228 | int32View[0] = input1
|
---|
| 229 | int32View[1] = input2
|
---|
| 230 | const cloned = clone({ a: int32View }).a
|
---|
| 231 | ok(cloned instanceof Int32Array, 'cloned value is instance of class')
|
---|
| 232 | isNot(cloned, int32View, 'cloned value is not same as input value')
|
---|
| 233 | is(cloned[0], input1, 'cloned value content is correct')
|
---|
| 234 | is(cloned[1], input2, 'cloned value content is correct')
|
---|
| 235 | })
|
---|
| 236 | test(`${label} copies TypedArrays from array correctly`, async ({ ok, is, isNot }) => {
|
---|
| 237 | const [input1, input2] = [rnd(10), rnd(10)]
|
---|
| 238 | var buffer = new ArrayBuffer(16)
|
---|
| 239 | const int32View = new Int32Array(buffer)
|
---|
| 240 | int32View[0] = input1
|
---|
| 241 | int32View[1] = input2
|
---|
| 242 | const [cloned] = clone([int32View])
|
---|
| 243 | ok(cloned instanceof Int32Array, 'cloned value is instance of class')
|
---|
| 244 | isNot(cloned, int32View, 'cloned value is not same as input value')
|
---|
| 245 | is(cloned[0], input1, 'cloned value content is correct')
|
---|
| 246 | is(cloned[1], input2, 'cloned value content is correct')
|
---|
| 247 | })
|
---|
| 248 | test(`${label} copies complex TypedArrays`, async ({ ok, deepEqual, is, isNot }) => {
|
---|
| 249 | const [input1, input2, input3] = [rnd(10), rnd(10), rnd(10)]
|
---|
| 250 | var buffer = new ArrayBuffer(4)
|
---|
| 251 | const view1 = new Int8Array(buffer, 0, 2)
|
---|
| 252 | const view2 = new Int8Array(buffer, 2, 2)
|
---|
| 253 | const view3 = new Int8Array(buffer)
|
---|
| 254 | view1[0] = input1
|
---|
| 255 | view2[0] = input2
|
---|
| 256 | view3[3] = input3
|
---|
| 257 | const cloned = clone({ view1, view2, view3 })
|
---|
| 258 | ok(cloned.view1 instanceof Int8Array, 'cloned value is instance of class')
|
---|
| 259 | ok(cloned.view2 instanceof Int8Array, 'cloned value is instance of class')
|
---|
| 260 | ok(cloned.view3 instanceof Int8Array, 'cloned value is instance of class')
|
---|
| 261 | isNot(cloned.view1, view1, 'cloned value is not same as input value')
|
---|
| 262 | isNot(cloned.view2, view2, 'cloned value is not same as input value')
|
---|
| 263 | isNot(cloned.view3, view3, 'cloned value is not same as input value')
|
---|
| 264 | deepEqual(Array.from(cloned.view1), [input1, 0], 'cloned value content is correct')
|
---|
| 265 | deepEqual(Array.from(cloned.view2), [input2, input3], 'cloned value content is correct')
|
---|
| 266 | deepEqual(Array.from(cloned.view3), [input1, 0, input2, input3], 'cloned value content is correct')
|
---|
| 267 | })
|
---|
| 268 | test(`${label} - maps`, async ({ same, isNot }) => {
|
---|
| 269 | const map = new Map([['a', 1]])
|
---|
| 270 | same(Array.from(clone(map)), [['a', 1]], 'same value')
|
---|
| 271 | isNot(clone(map), map, 'different object')
|
---|
| 272 | })
|
---|
| 273 | test(`${label} - sets`, async ({ same, isNot }) => {
|
---|
| 274 | const set = new Set([1])
|
---|
| 275 | same(Array.from(clone(set)), [1])
|
---|
| 276 | isNot(clone(set), set, 'different object')
|
---|
| 277 | })
|
---|
| 278 | test(`${label} - nested maps`, async ({ same, isNot }) => {
|
---|
| 279 | const data = { m: new Map([['a', 1]]) }
|
---|
| 280 | same(Array.from(clone(data).m), [['a', 1]], 'same value')
|
---|
| 281 | isNot(clone(data).m, data.m, 'different object')
|
---|
| 282 | })
|
---|
| 283 | test(`${label} - nested sets`, async ({ same, isNot }) => {
|
---|
| 284 | const data = { s: new Set([1]) }
|
---|
| 285 | same(Array.from(clone(data).s), [1], 'same value')
|
---|
| 286 | isNot(clone(data).s, data.s, 'different object')
|
---|
| 287 | })
|
---|
| 288 | }
|
---|