Add object utility tests
This commit is contained in:
parent
a3e846685d
commit
772ff4e257
1 changed files with 262 additions and 0 deletions
262
test/objects-test.ts
Normal file
262
test/objects-test.ts
Normal file
|
@ -0,0 +1,262 @@
|
|||
/*
|
||||
Copyright 2021 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
objectClone,
|
||||
objectDiff,
|
||||
objectExcluding,
|
||||
objectFromEntries,
|
||||
objectHasDiff,
|
||||
objectKeyChanges,
|
||||
objectShallowClone,
|
||||
objectWithOnly,
|
||||
} from "../src/utils/objects";
|
||||
|
||||
describe('objects', () => {
|
||||
describe('objectExcluding', () => {
|
||||
it('should exclude the given properties', () => {
|
||||
const input = {hello: "world", test: true};
|
||||
const output = {hello: "world"};
|
||||
const props = ["test", "doesnotexist"]; // we also make sure it doesn't explode on missing props
|
||||
const result = objectExcluding(input, <any>props); // any is to test the missing prop
|
||||
expect(result).toBeDefined();
|
||||
expect(result).toMatchObject(output);
|
||||
});
|
||||
});
|
||||
|
||||
describe('objectWithOnly', () => {
|
||||
it('should exclusively use the given properties', () => {
|
||||
const input = {hello: "world", test: true};
|
||||
const output = {hello: "world"};
|
||||
const props = ["hello", "doesnotexist"]; // we also make sure it doesn't explode on missing props
|
||||
const result = objectWithOnly(input, <any>props); // any is to test the missing prop
|
||||
expect(result).toBeDefined();
|
||||
expect(result).toMatchObject(output);
|
||||
});
|
||||
});
|
||||
|
||||
describe('objectShallowClone', () => {
|
||||
it('should create a new object', () => {
|
||||
const input = {test: 1};
|
||||
const result = objectShallowClone(input);
|
||||
expect(result).toBeDefined();
|
||||
expect(result).not.toBe(input);
|
||||
expect(result).toMatchObject(input);
|
||||
});
|
||||
|
||||
it('should only clone the top level properties', () => {
|
||||
const input = {a: 1, b: {c: 2}};
|
||||
const result = objectShallowClone(input);
|
||||
expect(result).toBeDefined();
|
||||
expect(result).toMatchObject(input);
|
||||
expect(result.b).toBe(input.b);
|
||||
});
|
||||
|
||||
it('should support custom clone functions', () => {
|
||||
const input = {a: 1, b: 2};
|
||||
const output = {a: 4, b: 8};
|
||||
const result = objectShallowClone(input, (k, v) => {
|
||||
// XXX: inverted expectation for ease of assertion
|
||||
expect(Object.keys(input)).toContain(k);
|
||||
|
||||
return v * 4;
|
||||
});
|
||||
expect(result).toBeDefined();
|
||||
expect(result).toMatchObject(output);
|
||||
});
|
||||
});
|
||||
|
||||
describe('objectHasDiff', () => {
|
||||
it('should return false for the same pointer', () => {
|
||||
const a = {};
|
||||
const result = objectHasDiff(a, a);
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
it('should return true if keys for A > keys for B', () => {
|
||||
const a = {a: 1, b: 2};
|
||||
const b = {a: 1};
|
||||
const result = objectHasDiff(a, b);
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
it('should return true if keys for A < keys for B', () => {
|
||||
const a = {a: 1};
|
||||
const b = {a: 1, b: 2};
|
||||
const result = objectHasDiff(a, b);
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false if the objects are the same but different pointers', () => {
|
||||
const a = {a: 1, b: 2};
|
||||
const b = {a: 1, b: 2};
|
||||
const result = objectHasDiff(a, b);
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
it('should consider pointers when testing values', () => {
|
||||
const a = {a: {}, b: 2}; // `{}` is shorthand for `new Object()`
|
||||
const b = {a: {}, b: 2};
|
||||
const result = objectHasDiff(a, b);
|
||||
expect(result).toBe(true); // even though the keys are the same, the value pointers vary
|
||||
});
|
||||
});
|
||||
|
||||
describe('objectDiff', () => {
|
||||
it('should return empty sets for the same object', () => {
|
||||
const a = {a: 1, b: 2};
|
||||
const b = {a: 1, b: 2};
|
||||
const result = objectDiff(a, b);
|
||||
expect(result).toBeDefined();
|
||||
expect(result.changed).toBeDefined();
|
||||
expect(result.added).toBeDefined();
|
||||
expect(result.removed).toBeDefined();
|
||||
expect(result.changed).toHaveLength(0);
|
||||
expect(result.added).toHaveLength(0);
|
||||
expect(result.removed).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('should return empty sets for the same object pointer', () => {
|
||||
const a = {a: 1, b: 2};
|
||||
const result = objectDiff(a, a);
|
||||
expect(result).toBeDefined();
|
||||
expect(result.changed).toBeDefined();
|
||||
expect(result.added).toBeDefined();
|
||||
expect(result.removed).toBeDefined();
|
||||
expect(result.changed).toHaveLength(0);
|
||||
expect(result.added).toHaveLength(0);
|
||||
expect(result.removed).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('should indicate when property changes are made', () => {
|
||||
const a = {a: 1, b: 2};
|
||||
const b = {a: 11, b: 2};
|
||||
const result = objectDiff(a, b);
|
||||
expect(result.changed).toBeDefined();
|
||||
expect(result.added).toBeDefined();
|
||||
expect(result.removed).toBeDefined();
|
||||
expect(result.changed).toHaveLength(1);
|
||||
expect(result.added).toHaveLength(0);
|
||||
expect(result.removed).toHaveLength(0);
|
||||
expect(result.changed).toEqual(['a']);
|
||||
});
|
||||
|
||||
it('should indicate when properties are added', () => {
|
||||
const a = {a: 1, b: 2};
|
||||
const b = {a: 1, b: 2, c: 3};
|
||||
const result = objectDiff(a, b);
|
||||
expect(result.changed).toBeDefined();
|
||||
expect(result.added).toBeDefined();
|
||||
expect(result.removed).toBeDefined();
|
||||
expect(result.changed).toHaveLength(0);
|
||||
expect(result.added).toHaveLength(1);
|
||||
expect(result.removed).toHaveLength(0);
|
||||
expect(result.added).toEqual(['c']);
|
||||
});
|
||||
|
||||
it('should indicate when properties are removed', () => {
|
||||
const a = {a: 1, b: 2};
|
||||
const b = {a: 1};
|
||||
const result = objectDiff(a, b);
|
||||
expect(result.changed).toBeDefined();
|
||||
expect(result.added).toBeDefined();
|
||||
expect(result.removed).toBeDefined();
|
||||
expect(result.changed).toHaveLength(0);
|
||||
expect(result.added).toHaveLength(0);
|
||||
expect(result.removed).toHaveLength(1);
|
||||
expect(result.removed).toEqual(['b']);
|
||||
});
|
||||
|
||||
it('should indicate when multiple aspects change', () => {
|
||||
const a = {a: 1, b: 2, c: 3};
|
||||
const b: (typeof a | {d: number}) = {a: 1, b: 22, d: 4};
|
||||
const result = objectDiff(a, b);
|
||||
expect(result.changed).toBeDefined();
|
||||
expect(result.added).toBeDefined();
|
||||
expect(result.removed).toBeDefined();
|
||||
expect(result.changed).toHaveLength(1);
|
||||
expect(result.added).toHaveLength(1);
|
||||
expect(result.removed).toHaveLength(1);
|
||||
expect(result.changed).toEqual(['b']);
|
||||
expect(result.removed).toEqual(['c']);
|
||||
expect(result.added).toEqual(['d']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('objectKeyChanges', () => {
|
||||
it('should return an empty set if no properties changed', () => {
|
||||
const a = {a: 1, b: 2};
|
||||
const b = {a: 1, b: 2};
|
||||
const result = objectKeyChanges(a, b);
|
||||
expect(result).toBeDefined();
|
||||
expect(result).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('should return an empty set if no properties changed for the same pointer', () => {
|
||||
const a = {a: 1, b: 2};
|
||||
const result = objectKeyChanges(a, a);
|
||||
expect(result).toBeDefined();
|
||||
expect(result).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('should return properties which were changed, added, or removed', () => {
|
||||
const a = {a: 1, b: 2, c: 3};
|
||||
const b: (typeof a | {d: number}) = {a: 1, b: 22, d: 4};
|
||||
const result = objectKeyChanges(a, b);
|
||||
expect(result).toBeDefined();
|
||||
expect(result).toHaveLength(3);
|
||||
expect(result).toEqual(['c', 'd', 'b']); // order isn't important, but the test cares
|
||||
});
|
||||
});
|
||||
|
||||
describe('objectClone', () => {
|
||||
it('should deep clone an object', () => {
|
||||
const a = {
|
||||
hello: "world",
|
||||
test: {
|
||||
another: "property",
|
||||
test: 42,
|
||||
third: {
|
||||
prop: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
const result = objectClone(a);
|
||||
expect(result).toBeDefined();
|
||||
expect(result).not.toBe(a);
|
||||
expect(result).toMatchObject(a);
|
||||
expect(result.test).not.toBe(a.test);
|
||||
expect(result.test.third).not.toBe(a.test.third);
|
||||
});
|
||||
});
|
||||
|
||||
describe('objectFromEntries', () => {
|
||||
it('should create an object from an array of entries', () => {
|
||||
const output = {a: 1, b: 2, c: 3};
|
||||
const result = objectFromEntries(Object.entries(output));
|
||||
expect(result).toBeDefined();
|
||||
expect(result).toMatchObject(output);
|
||||
});
|
||||
|
||||
it('should maintain pointers in values', () => {
|
||||
const output = {a: {}, b: 2, c: 3};
|
||||
const result = objectFromEntries(Object.entries(output));
|
||||
expect(result).toBeDefined();
|
||||
expect(result).toMatchObject(output);
|
||||
expect(result['a']).toBe(output.a);
|
||||
});
|
||||
});
|
||||
});
|
Loading…
Reference in a new issue