initial update
This commit is contained in:
218
node_modules/@wry/equality/src/index.ts
generated
vendored
Normal file
218
node_modules/@wry/equality/src/index.ts
generated
vendored
Normal file
@@ -0,0 +1,218 @@
|
||||
const { toString, hasOwnProperty } = Object.prototype;
|
||||
const fnToStr = Function.prototype.toString;
|
||||
const previousComparisons = new Map<object, Set<object>>();
|
||||
|
||||
/**
|
||||
* Performs a deep equality check on two JavaScript values, tolerating cycles.
|
||||
*/
|
||||
export function equal(a: any, b: any): boolean {
|
||||
try {
|
||||
return check(a, b);
|
||||
} finally {
|
||||
previousComparisons.clear();
|
||||
}
|
||||
}
|
||||
|
||||
// Allow default imports as well.
|
||||
export default equal;
|
||||
|
||||
function check(a: any, b: any): boolean {
|
||||
// If the two values are strictly equal, our job is easy.
|
||||
if (a === b) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Object.prototype.toString returns a representation of the runtime type of
|
||||
// the given value that is considerably more precise than typeof.
|
||||
const aTag = toString.call(a);
|
||||
const bTag = toString.call(b);
|
||||
|
||||
// If the runtime types of a and b are different, they could maybe be equal
|
||||
// under some interpretation of equality, but for simplicity and performance
|
||||
// we just return false instead.
|
||||
if (aTag !== bTag) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (aTag) {
|
||||
case '[object Array]':
|
||||
// Arrays are a lot like other objects, but we can cheaply compare their
|
||||
// lengths as a short-cut before comparing their elements.
|
||||
if (a.length !== b.length) return false;
|
||||
// Fall through to object case...
|
||||
case '[object Object]': {
|
||||
if (previouslyCompared(a, b)) return true;
|
||||
|
||||
const aKeys = definedKeys(a);
|
||||
const bKeys = definedKeys(b);
|
||||
|
||||
// If `a` and `b` have a different number of enumerable keys, they
|
||||
// must be different.
|
||||
const keyCount = aKeys.length;
|
||||
if (keyCount !== bKeys.length) return false;
|
||||
|
||||
// Now make sure they have the same keys.
|
||||
for (let k = 0; k < keyCount; ++k) {
|
||||
if (!hasOwnProperty.call(b, aKeys[k])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Finally, check deep equality of all child properties.
|
||||
for (let k = 0; k < keyCount; ++k) {
|
||||
const key = aKeys[k];
|
||||
if (!check(a[key], b[key])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
case '[object Error]':
|
||||
return a.name === b.name && a.message === b.message;
|
||||
|
||||
case '[object Number]':
|
||||
// Handle NaN, which is !== itself.
|
||||
if (a !== a) return b !== b;
|
||||
// Fall through to shared +a === +b case...
|
||||
case '[object Boolean]':
|
||||
case '[object Date]':
|
||||
return +a === +b;
|
||||
|
||||
case '[object RegExp]':
|
||||
case '[object String]':
|
||||
return a == `${b}`;
|
||||
|
||||
case '[object Map]':
|
||||
case '[object Set]': {
|
||||
if (a.size !== b.size) return false;
|
||||
if (previouslyCompared(a, b)) return true;
|
||||
|
||||
const aIterator = a.entries();
|
||||
const isMap = aTag === '[object Map]';
|
||||
|
||||
while (true) {
|
||||
const info = aIterator.next();
|
||||
if (info.done) break;
|
||||
|
||||
// If a instanceof Set, aValue === aKey.
|
||||
const [aKey, aValue] = info.value;
|
||||
|
||||
// So this works the same way for both Set and Map.
|
||||
if (!b.has(aKey)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// However, we care about deep equality of values only when dealing
|
||||
// with Map structures.
|
||||
if (isMap && !check(aValue, b.get(aKey))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
case '[object Uint16Array]':
|
||||
case '[object Uint8Array]': // Buffer, in Node.js.
|
||||
case '[object Uint32Array]':
|
||||
case '[object Int32Array]':
|
||||
case '[object Int8Array]':
|
||||
case '[object Int16Array]':
|
||||
case '[object ArrayBuffer]':
|
||||
// DataView doesn't need these conversions, but the equality check is
|
||||
// otherwise the same.
|
||||
a = new Uint8Array(a);
|
||||
b = new Uint8Array(b);
|
||||
// Fall through...
|
||||
case '[object DataView]': {
|
||||
let len = a.byteLength;
|
||||
if (len === b.byteLength) {
|
||||
while (len-- && a[len] === b[len]) {
|
||||
// Keep looping as long as the bytes are equal.
|
||||
}
|
||||
}
|
||||
return len === -1;
|
||||
}
|
||||
|
||||
case '[object AsyncFunction]':
|
||||
case '[object GeneratorFunction]':
|
||||
case '[object AsyncGeneratorFunction]':
|
||||
case '[object Function]': {
|
||||
const aCode = fnToStr.call(a);
|
||||
if (aCode !== fnToStr.call(b)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// We consider non-native functions equal if they have the same code
|
||||
// (native functions require === because their code is censored).
|
||||
// Note that this behavior is not entirely sound, since !== function
|
||||
// objects with the same code can behave differently depending on
|
||||
// their closure scope. However, any function can behave differently
|
||||
// depending on the values of its input arguments (including this)
|
||||
// and its calling context (including its closure scope), even
|
||||
// though the function object is === to itself; and it is entirely
|
||||
// possible for functions that are not === to behave exactly the
|
||||
// same under all conceivable circumstances. Because none of these
|
||||
// factors are statically decidable in JavaScript, JS function
|
||||
// equality is not well-defined. This ambiguity allows us to
|
||||
// consider the best possible heuristic among various imperfect
|
||||
// options, and equating non-native functions that have the same
|
||||
// code has enormous practical benefits, such as when comparing
|
||||
// functions that are repeatedly passed as fresh function
|
||||
// expressions within objects that are otherwise deeply equal. Since
|
||||
// any function created from the same syntactic expression (in the
|
||||
// same code location) will always stringify to the same code
|
||||
// according to fnToStr.call, we can reasonably expect these
|
||||
// repeatedly passed function expressions to have the same code, and
|
||||
// thus behave "the same" (with all the caveats mentioned above),
|
||||
// even though the runtime function objects are !== to one another.
|
||||
return !endsWith(aCode, nativeCodeSuffix);
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise the values are not equal.
|
||||
return false;
|
||||
}
|
||||
|
||||
function definedKeys<TObject extends object>(obj: TObject) {
|
||||
// Remember that the second argument to Array.prototype.filter will be
|
||||
// used as `this` within the callback function.
|
||||
return Object.keys(obj).filter(isDefinedKey, obj);
|
||||
}
|
||||
function isDefinedKey<TObject extends object>(
|
||||
this: TObject,
|
||||
key: keyof TObject,
|
||||
) {
|
||||
return this[key] !== void 0;
|
||||
}
|
||||
|
||||
const nativeCodeSuffix = "{ [native code] }";
|
||||
|
||||
function endsWith(full: string, suffix: string) {
|
||||
const fromIndex = full.length - suffix.length;
|
||||
return fromIndex >= 0 &&
|
||||
full.indexOf(suffix, fromIndex) === fromIndex;
|
||||
}
|
||||
|
||||
function previouslyCompared(a: object, b: object): boolean {
|
||||
// Though cyclic references can make an object graph appear infinite from the
|
||||
// perspective of a depth-first traversal, the graph still contains a finite
|
||||
// number of distinct object references. We use the previousComparisons cache
|
||||
// to avoid comparing the same pair of object references more than once, which
|
||||
// guarantees termination (even if we end up comparing every object in one
|
||||
// graph to every object in the other graph, which is extremely unlikely),
|
||||
// while still allowing weird isomorphic structures (like rings with different
|
||||
// lengths) a chance to pass the equality test.
|
||||
let bSet = previousComparisons.get(a);
|
||||
if (bSet) {
|
||||
// Return true here because we can be sure false will be returned somewhere
|
||||
// else if the objects are not equivalent.
|
||||
if (bSet.has(b)) return true;
|
||||
} else {
|
||||
previousComparisons.set(a, bSet = new Set);
|
||||
}
|
||||
bSet.add(b);
|
||||
return false;
|
||||
}
|
||||
411
node_modules/@wry/equality/src/tests/main.ts
generated
vendored
Normal file
411
node_modules/@wry/equality/src/tests/main.ts
generated
vendored
Normal file
@@ -0,0 +1,411 @@
|
||||
import * as assert from "assert";
|
||||
import defaultEqual, { equal } from "../index.js";
|
||||
|
||||
function toStr(value: any) {
|
||||
try {
|
||||
return JSON.stringify(value);
|
||||
} catch {
|
||||
return String(value);
|
||||
}
|
||||
}
|
||||
|
||||
function assertEqual(a: any, b: any) {
|
||||
assert.strictEqual(equal(a, b), true, `unexpectedly not equal(${toStr(a)}}, ${toStr(b)})`);
|
||||
assert.strictEqual(equal(b, a), true, `unexpectedly not equal(${toStr(b)}, ${toStr(a)})`);
|
||||
}
|
||||
|
||||
function assertNotEqual(a: any, b: any) {
|
||||
assert.strictEqual(equal(a, b), false, `unexpectedly equal(${toStr(a)}, ${toStr(b)})`);
|
||||
assert.strictEqual(equal(b, a), false, `unexpectedly equal(${toStr(b)}, ${toStr(a)})`);
|
||||
}
|
||||
|
||||
describe("equality", function () {
|
||||
it("should work with named and default imports", function () {
|
||||
assert.strictEqual(defaultEqual, equal);
|
||||
});
|
||||
|
||||
it("should work for primitive types", function () {
|
||||
assertEqual(2 + 2, 4);
|
||||
assertNotEqual(2 + 2, 5);
|
||||
|
||||
assertEqual("oyez", "oyez");
|
||||
assertNotEqual("oyez", "onoz");
|
||||
|
||||
assertEqual(null, null);
|
||||
assertEqual(void 0, void 1);
|
||||
assertEqual(NaN, NaN);
|
||||
|
||||
assertNotEqual(void 0, null);
|
||||
assertNotEqual(void 0, false);
|
||||
assertNotEqual(false, null);
|
||||
assertNotEqual(0, null);
|
||||
assertNotEqual(0, false);
|
||||
assertNotEqual(0, void 0);
|
||||
|
||||
assertEqual(123, new Number(123));
|
||||
assertEqual(true, new Boolean(true));
|
||||
assertEqual(false, new Boolean(false));
|
||||
assertEqual("oyez", new String("oyez"));
|
||||
});
|
||||
|
||||
it("should work for arrays", function () {
|
||||
assertEqual([1, 2, 3], [1, 2, 3]);
|
||||
assertEqual([1, [2], 3], [1, [2], 3]);
|
||||
|
||||
const a: any[] = [1];
|
||||
a.push(a, 2);
|
||||
const b: any[] = [1];
|
||||
b.push(b, 2);
|
||||
assertEqual(a, b);
|
||||
|
||||
assertEqual(
|
||||
[1, /*hole*/, 3],
|
||||
[1, /*hole*/, 3],
|
||||
);
|
||||
|
||||
assertEqual(
|
||||
[1, /*hole*/, 3],
|
||||
[1, void 0, 3],
|
||||
);
|
||||
|
||||
// Not equal because the arrays are a different length.
|
||||
assertNotEqual(
|
||||
[1, 2, /*hole*/,],
|
||||
[1, 2],
|
||||
);
|
||||
});
|
||||
|
||||
it("should work for objects", function () {
|
||||
assertEqual({
|
||||
a: 1,
|
||||
b: 2,
|
||||
}, {
|
||||
b: 2,
|
||||
a: 1,
|
||||
});
|
||||
|
||||
assertNotEqual({
|
||||
a: 1,
|
||||
b: 2,
|
||||
c: 3,
|
||||
}, {
|
||||
b: 2,
|
||||
a: 1,
|
||||
});
|
||||
|
||||
const a: any = {};
|
||||
a.self = a;
|
||||
const b: any = {};
|
||||
b.self = b;
|
||||
assertEqual(a, b);
|
||||
|
||||
b.foo = 42;
|
||||
assertNotEqual(a, b);
|
||||
});
|
||||
|
||||
it("should consider undefined and missing object properties equivalent", function () {
|
||||
assertEqual({
|
||||
a: 1,
|
||||
b: void 0,
|
||||
c: 3,
|
||||
}, {
|
||||
a: 1,
|
||||
c: 3,
|
||||
});
|
||||
|
||||
assertEqual({
|
||||
a: void 0,
|
||||
b: void 0,
|
||||
c: void 0,
|
||||
}, {});
|
||||
});
|
||||
|
||||
it("should work for Error objects", function () {
|
||||
assertEqual(new Error("oyez"), new Error("oyez"));
|
||||
assertNotEqual(new Error("oyez"), new Error("onoz"));
|
||||
});
|
||||
|
||||
it("should work for Date objects", function () {
|
||||
const now = new Date;
|
||||
const alsoNow = new Date(+now);
|
||||
assert.notStrictEqual(now, alsoNow);
|
||||
assertEqual(now, alsoNow);
|
||||
const later = new Date(+now + 10);
|
||||
assertNotEqual(now, later);
|
||||
});
|
||||
|
||||
it("should work for RegExp objects", function () {
|
||||
assert.notStrictEqual(/xy/, /xy/);
|
||||
assertEqual(/xy/img, /xy/mgi);
|
||||
assertNotEqual(/xy/img, /x.y/img);
|
||||
});
|
||||
|
||||
it("should work for Set objects", function () {
|
||||
assertEqual(
|
||||
new Set().add(1).add(2).add(3).add(2),
|
||||
new Set().add(3).add(1).add(2).add(1),
|
||||
);
|
||||
|
||||
const obj = {};
|
||||
assertEqual(
|
||||
new Set().add(1).add(obj).add(3).add(2),
|
||||
new Set().add(3).add(obj).add(2).add(1),
|
||||
);
|
||||
|
||||
assertNotEqual(
|
||||
new Set(),
|
||||
new Set().add(void 0),
|
||||
);
|
||||
});
|
||||
|
||||
it("should work for Map objects", function () {
|
||||
assertEqual(
|
||||
new Map().set(1, 2).set(2, 3),
|
||||
new Map().set(2, 3).set(1, 2),
|
||||
);
|
||||
|
||||
assertEqual(
|
||||
new Map().set(1, 2).set(2, 3).set(1, 0),
|
||||
new Map().set(2, 3).set(1, 2).set(1, 0),
|
||||
);
|
||||
|
||||
assertNotEqual(
|
||||
new Map().set(1, 2).set(2, 3).set(1, 0),
|
||||
new Map().set(2, 3).set(1, 2).set(3, 4),
|
||||
);
|
||||
|
||||
assertEqual(
|
||||
new Map().set(1, new Set().add(2)),
|
||||
new Map().set(1, new Set().add(2)),
|
||||
);
|
||||
|
||||
assertNotEqual(
|
||||
new Map().set(1, new Set().add(2)),
|
||||
new Map().set(1, new Set().add(2).add(3)),
|
||||
);
|
||||
|
||||
const a = new Map;
|
||||
a.set(a, a);
|
||||
const b = new Map;
|
||||
b.set(a, b);
|
||||
assertEqual(a, b);
|
||||
|
||||
a.set(1, 2);
|
||||
b.set(1, 2);
|
||||
assertEqual(a, b);
|
||||
|
||||
a.set(3, 4);
|
||||
assertNotEqual(a, b);
|
||||
});
|
||||
|
||||
it("should tolerate cycles", function () {
|
||||
const a: any[] = [];
|
||||
a.push(a);
|
||||
const b: any[] = [];
|
||||
b.push(b);
|
||||
assertEqual(a, b);
|
||||
assertEqual([a], b);
|
||||
assertEqual(a, [b]);
|
||||
assertEqual([a], [b]);
|
||||
|
||||
a.push(1);
|
||||
b.push(1);
|
||||
assertEqual(a, b);
|
||||
assertEqual([a, 1], b);
|
||||
assertEqual(a, [b, 1]);
|
||||
|
||||
const ring1 = { self: { self: { self: {} as any }}};
|
||||
ring1.self.self.self.self = ring1;
|
||||
const ring2 = { self: { self: {} as any }};
|
||||
ring2.self.self.self = ring2;
|
||||
assertEqual(ring1, ring2);
|
||||
|
||||
ring1.self.self.self.self = ring1.self;
|
||||
assertEqual(ring1, ring2);
|
||||
});
|
||||
|
||||
it("should not care about repeated references", function () {
|
||||
const r = { foo: 42 };
|
||||
assertEqual(
|
||||
[r, r, r],
|
||||
JSON.parse(JSON.stringify([r, r, r])),
|
||||
);
|
||||
});
|
||||
|
||||
it("should equate non-native functions with the same code", function () {
|
||||
const fn = () => 1234;
|
||||
assertEqual(fn, fn);
|
||||
assertEqual(fn, () => 1234);
|
||||
|
||||
// These functions are behaviorally the same, but there's no way to
|
||||
// decide that question statically.
|
||||
assertNotEqual(
|
||||
(a: number) => a + 1,
|
||||
(b: number) => b + 1,
|
||||
);
|
||||
|
||||
assertEqual(
|
||||
{ before: 123, fn() { return 4 }, after: 321 },
|
||||
{ after: 321, before: 123, fn() { return 4 } },
|
||||
);
|
||||
|
||||
assertEqual(Object.assign, Object.assign);
|
||||
|
||||
// Since these slice methods are native functions, they happen to have
|
||||
// exactly the same (censored) code, but we can test their equality by
|
||||
// reference, since we can generally assume native functions are pure.
|
||||
assertNotEqual(String.prototype.slice, Array.prototype.slice);
|
||||
assertEqual(
|
||||
Function.prototype.toString.call(String.prototype.slice),
|
||||
Function.prototype.toString.call(Array.prototype.slice),
|
||||
);
|
||||
});
|
||||
|
||||
it("should equate async functions with the same code", function () {
|
||||
const fn = async () => 1234;
|
||||
assertEqual(fn, fn);
|
||||
assertEqual(fn, async () => 1234);
|
||||
|
||||
// These functions are behaviorally the same, but there's no way to
|
||||
// decide that question statically.
|
||||
assertNotEqual(
|
||||
async (a: number) => a + 1,
|
||||
async (b: number) => b + 1,
|
||||
);
|
||||
|
||||
assertEqual(
|
||||
{ before: 123, async fn() { return 4 }, after: 321 },
|
||||
{ after: 321, before: 123, async fn() { return 4 } },
|
||||
);
|
||||
});
|
||||
|
||||
it("should equate generator functions with the same code", function () {
|
||||
const fn = function *(): Generator<number> { return yield 1234 };
|
||||
assertEqual(fn, fn);
|
||||
assertEqual(fn, function *(): Generator<number> { return yield 1234 });
|
||||
|
||||
// These functions are behaviorally the same, but there's no way to
|
||||
// decide that question statically.
|
||||
assertNotEqual(
|
||||
function *(a: number): Generator<number> { return yield a + 1 },
|
||||
function *(b: number): Generator<number> { return yield b + 1 },
|
||||
);
|
||||
|
||||
assertEqual(
|
||||
{ before: 123, *fn() { return 4 }, after: 321 },
|
||||
{ after: 321, before: 123, *fn() { return 4 } },
|
||||
);
|
||||
});
|
||||
|
||||
it("should equate async generator functions with the same code", function () {
|
||||
const fn = async function *(): AsyncGenerator<number> { return await (yield 1234) };
|
||||
assertEqual(fn, fn);
|
||||
assertEqual(fn, async function *(): AsyncGenerator<number> { return await (yield 1234) });
|
||||
|
||||
// These functions are behaviorally the same, but there's no way to
|
||||
// decide that question statically.
|
||||
assertNotEqual(
|
||||
async function *(a: number): AsyncGenerator<number> { return yield a + 1 },
|
||||
async function *(b: number): AsyncGenerator<number> { return yield b + 1 },
|
||||
);
|
||||
|
||||
assertEqual(
|
||||
{ before: 123, async *fn() { return 4 }, after: 321 },
|
||||
{ after: 321, before: 123, async *fn() { return 4 } },
|
||||
);
|
||||
});
|
||||
|
||||
it('should work for Array Buffers And Typed Arrays', function () {
|
||||
const hello = new Int8Array([1, 2, 3, 4, 5]);
|
||||
const world = new Int8Array([1, 2, 3, 4, 5]);
|
||||
const small = new Int8Array([1, 2, 3, 4])
|
||||
assertEqual(new DataView(new ArrayBuffer(4)), new DataView(new ArrayBuffer(4)))
|
||||
assertEqual(new Int16Array([42]), new Int16Array([42]));
|
||||
assertEqual(new Int32Array(new ArrayBuffer(4)), new Int32Array(new ArrayBuffer(4)))
|
||||
assertEqual(new ArrayBuffer(2), new ArrayBuffer(2))
|
||||
assertNotEqual(new Int16Array([1, 2, 3]), new Int16Array([1, 2]));
|
||||
assertNotEqual(new Int16Array([1, 2, 3]), new Uint16Array([1, 2, 3]))
|
||||
assertNotEqual(new Int16Array([1, 2, 3]), new Int8Array([1, 2, 3]))
|
||||
assertNotEqual(new Int32Array(8), new Uint32Array(8));
|
||||
assertNotEqual(new Int32Array(new ArrayBuffer(8)), new Int32Array(Array.from({ length: 8 })));
|
||||
assertNotEqual(new ArrayBuffer(1), new ArrayBuffer(2));
|
||||
assertEqual(hello, world);
|
||||
assertEqual(hello.buffer, world.buffer);
|
||||
assertEqual(new DataView(hello.buffer), new DataView(world.buffer));
|
||||
assertNotEqual(small, world)
|
||||
assertNotEqual(small.buffer, world.buffer);
|
||||
assertNotEqual(new DataView(small.buffer), new DataView(world.buffer));
|
||||
});
|
||||
|
||||
it('should work with a kitchen sink', function () {
|
||||
const foo = {
|
||||
foo: 'value1',
|
||||
bar: new Set([1, 2, 3]),
|
||||
baz: /foo/i,
|
||||
bat: {
|
||||
hello: new Map([ ['hello', 'world'] ]),
|
||||
world: {
|
||||
aaa: new Map([
|
||||
[{ foo: /bar/ }, 'sub sub value1'],
|
||||
]),
|
||||
bbb: [1, 2, { prop2:1, prop:2 }, 4, 5]
|
||||
}
|
||||
},
|
||||
quz: new Set([{ a:1 , b:2 }]),
|
||||
qut: new Date(2016, 2, 10),
|
||||
qar: new Uint8Array([1, 2, 3, 4, 5]),
|
||||
}
|
||||
|
||||
const bar = {
|
||||
quz: new Set([{ a:1 , b:2 }]),
|
||||
baz: /foo/i,
|
||||
foo: 'value1',
|
||||
bar: new Set([1, 2, 3]),
|
||||
qar: new Uint8Array([1, 2, 3, 4, 5]),
|
||||
qut: new Date('2016/03/10'),
|
||||
bat: {
|
||||
world: {
|
||||
aaa: new Map([
|
||||
[{ foo: /bar/ }, 'sub sub value1'],
|
||||
]),
|
||||
bbb: [1, 2, { prop2:1, prop:2 }, 4, 5]
|
||||
},
|
||||
hello: new Map([ ['hello', 'world'] ])
|
||||
}
|
||||
};
|
||||
|
||||
assertNotEqual(foo, bar)
|
||||
});
|
||||
|
||||
describe("performance", function () {
|
||||
const limit = 1e6;
|
||||
this.timeout(20000);
|
||||
|
||||
function check(a: any, bEqual: any, bNotEqual: any) {
|
||||
for (let i = 0; i < limit; ++i) {
|
||||
assert.strictEqual(equal(a, bEqual), true);
|
||||
assert.strictEqual(equal(bEqual, a), true);
|
||||
assert.strictEqual(equal(a, bNotEqual), false);
|
||||
assert.strictEqual(equal(bNotEqual, a), false);
|
||||
}
|
||||
}
|
||||
|
||||
it("should be fast for arrays", function () {
|
||||
const a = [1, 2, 3];
|
||||
check(a, a.slice(0), [1, 2, 4]);
|
||||
});
|
||||
|
||||
it("should be fast for objects", function () {
|
||||
const a = { a: 1, b: 2, c: 3 };
|
||||
check(a, { ...a }, { a: 1, b: 3 });
|
||||
});
|
||||
|
||||
it("should be fast for strings", function () {
|
||||
check('foo', new String('foo'), 'bar');
|
||||
});
|
||||
|
||||
it("should be fast for functions", function () {
|
||||
check(() => 123, () => 123, () => 321);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user