2021-04-23 04:28:56 +03:00
|
|
|
/*
|
|
|
|
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,
|
|
|
|
objectHasDiff,
|
|
|
|
objectKeyChanges,
|
|
|
|
objectShallowClone,
|
|
|
|
objectWithOnly,
|
2021-04-23 05:30:14 +03:00
|
|
|
} from "../../src/utils/objects";
|
2021-04-23 04:28:56 +03:00
|
|
|
|
2022-12-12 14:24:14 +03:00
|
|
|
describe("objects", () => {
|
|
|
|
describe("objectExcluding", () => {
|
|
|
|
it("should exclude the given properties", () => {
|
2021-06-29 15:11:58 +03:00
|
|
|
const input = { hello: "world", test: true };
|
|
|
|
const output = { hello: "world" };
|
2021-04-23 04:28:56 +03:00
|
|
|
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);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2022-12-12 14:24:14 +03:00
|
|
|
describe("objectWithOnly", () => {
|
|
|
|
it("should exclusively use the given properties", () => {
|
2021-06-29 15:11:58 +03:00
|
|
|
const input = { hello: "world", test: true };
|
|
|
|
const output = { hello: "world" };
|
2021-04-23 04:28:56 +03:00
|
|
|
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);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2022-12-12 14:24:14 +03:00
|
|
|
describe("objectShallowClone", () => {
|
|
|
|
it("should create a new object", () => {
|
2021-06-29 15:11:58 +03:00
|
|
|
const input = { test: 1 };
|
2021-04-23 04:28:56 +03:00
|
|
|
const result = objectShallowClone(input);
|
|
|
|
expect(result).toBeDefined();
|
|
|
|
expect(result).not.toBe(input);
|
|
|
|
expect(result).toMatchObject(input);
|
|
|
|
});
|
|
|
|
|
2022-12-12 14:24:14 +03:00
|
|
|
it("should only clone the top level properties", () => {
|
2021-06-29 15:11:58 +03:00
|
|
|
const input = { a: 1, b: { c: 2 } };
|
2021-04-23 04:28:56 +03:00
|
|
|
const result = objectShallowClone(input);
|
|
|
|
expect(result).toBeDefined();
|
|
|
|
expect(result).toMatchObject(input);
|
|
|
|
expect(result.b).toBe(input.b);
|
|
|
|
});
|
|
|
|
|
2022-12-12 14:24:14 +03:00
|
|
|
it("should support custom clone functions", () => {
|
2021-06-29 15:11:58 +03:00
|
|
|
const input = { a: 1, b: 2 };
|
|
|
|
const output = { a: 4, b: 8 };
|
2021-04-23 04:28:56 +03:00
|
|
|
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);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2022-12-12 14:24:14 +03:00
|
|
|
describe("objectHasDiff", () => {
|
|
|
|
it("should return false for the same pointer", () => {
|
2021-04-23 04:28:56 +03:00
|
|
|
const a = {};
|
|
|
|
const result = objectHasDiff(a, a);
|
|
|
|
expect(result).toBe(false);
|
|
|
|
});
|
|
|
|
|
2022-12-12 14:24:14 +03:00
|
|
|
it("should return true if keys for A > keys for B", () => {
|
2021-06-29 15:11:58 +03:00
|
|
|
const a = { a: 1, b: 2 };
|
|
|
|
const b = { a: 1 };
|
2021-04-23 04:28:56 +03:00
|
|
|
const result = objectHasDiff(a, b);
|
|
|
|
expect(result).toBe(true);
|
|
|
|
});
|
|
|
|
|
2022-12-12 14:24:14 +03:00
|
|
|
it("should return true if keys for A < keys for B", () => {
|
2021-06-29 15:11:58 +03:00
|
|
|
const a = { a: 1 };
|
|
|
|
const b = { a: 1, b: 2 };
|
2021-04-23 04:28:56 +03:00
|
|
|
const result = objectHasDiff(a, b);
|
|
|
|
expect(result).toBe(true);
|
|
|
|
});
|
|
|
|
|
2022-12-12 14:24:14 +03:00
|
|
|
it("should return false if the objects are the same but different pointers", () => {
|
2021-06-29 15:11:58 +03:00
|
|
|
const a = { a: 1, b: 2 };
|
|
|
|
const b = { a: 1, b: 2 };
|
2021-04-23 04:28:56 +03:00
|
|
|
const result = objectHasDiff(a, b);
|
|
|
|
expect(result).toBe(false);
|
|
|
|
});
|
|
|
|
|
2022-12-12 14:24:14 +03:00
|
|
|
it("should consider pointers when testing values", () => {
|
2021-06-29 15:11:58 +03:00
|
|
|
const a = { a: {}, b: 2 }; // `{}` is shorthand for `new Object()`
|
|
|
|
const b = { a: {}, b: 2 };
|
2021-04-23 04:28:56 +03:00
|
|
|
const result = objectHasDiff(a, b);
|
|
|
|
expect(result).toBe(true); // even though the keys are the same, the value pointers vary
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2022-12-12 14:24:14 +03:00
|
|
|
describe("objectDiff", () => {
|
|
|
|
it("should return empty sets for the same object", () => {
|
2021-06-29 15:11:58 +03:00
|
|
|
const a = { a: 1, b: 2 };
|
|
|
|
const b = { a: 1, b: 2 };
|
2021-04-23 04:28:56 +03:00
|
|
|
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);
|
|
|
|
});
|
|
|
|
|
2022-12-12 14:24:14 +03:00
|
|
|
it("should return empty sets for the same object pointer", () => {
|
2021-06-29 15:11:58 +03:00
|
|
|
const a = { a: 1, b: 2 };
|
2021-04-23 04:28:56 +03:00
|
|
|
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);
|
|
|
|
});
|
|
|
|
|
2022-12-12 14:24:14 +03:00
|
|
|
it("should indicate when property changes are made", () => {
|
2021-06-29 15:11:58 +03:00
|
|
|
const a = { a: 1, b: 2 };
|
|
|
|
const b = { a: 11, b: 2 };
|
2021-04-23 04:28:56 +03:00
|
|
|
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);
|
2022-12-12 14:24:14 +03:00
|
|
|
expect(result.changed).toEqual(["a"]);
|
2021-04-23 04:28:56 +03:00
|
|
|
});
|
|
|
|
|
2022-12-12 14:24:14 +03:00
|
|
|
it("should indicate when properties are added", () => {
|
2021-06-29 15:11:58 +03:00
|
|
|
const a = { a: 1, b: 2 };
|
|
|
|
const b = { a: 1, b: 2, c: 3 };
|
2021-04-23 04:28:56 +03:00
|
|
|
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);
|
2022-12-12 14:24:14 +03:00
|
|
|
expect(result.added).toEqual(["c"]);
|
2021-04-23 04:28:56 +03:00
|
|
|
});
|
|
|
|
|
2022-12-12 14:24:14 +03:00
|
|
|
it("should indicate when properties are removed", () => {
|
2021-06-29 15:11:58 +03:00
|
|
|
const a = { a: 1, b: 2 };
|
|
|
|
const b = { a: 1 };
|
2021-04-23 04:28:56 +03:00
|
|
|
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);
|
2022-12-12 14:24:14 +03:00
|
|
|
expect(result.removed).toEqual(["b"]);
|
2021-04-23 04:28:56 +03:00
|
|
|
});
|
|
|
|
|
2022-12-12 14:24:14 +03:00
|
|
|
it("should indicate when multiple aspects change", () => {
|
2021-06-29 15:11:58 +03:00
|
|
|
const a = { a: 1, b: 2, c: 3 };
|
2022-12-12 14:24:14 +03:00
|
|
|
const b: typeof a | { d: number } = { a: 1, b: 22, d: 4 };
|
2021-04-23 04:28:56 +03:00
|
|
|
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);
|
2022-12-12 14:24:14 +03:00
|
|
|
expect(result.changed).toEqual(["b"]);
|
|
|
|
expect(result.removed).toEqual(["c"]);
|
|
|
|
expect(result.added).toEqual(["d"]);
|
2021-04-23 04:28:56 +03:00
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2022-12-12 14:24:14 +03:00
|
|
|
describe("objectKeyChanges", () => {
|
|
|
|
it("should return an empty set if no properties changed", () => {
|
2021-06-29 15:11:58 +03:00
|
|
|
const a = { a: 1, b: 2 };
|
|
|
|
const b = { a: 1, b: 2 };
|
2021-04-23 04:28:56 +03:00
|
|
|
const result = objectKeyChanges(a, b);
|
|
|
|
expect(result).toBeDefined();
|
|
|
|
expect(result).toHaveLength(0);
|
|
|
|
});
|
|
|
|
|
2022-12-12 14:24:14 +03:00
|
|
|
it("should return an empty set if no properties changed for the same pointer", () => {
|
2021-06-29 15:11:58 +03:00
|
|
|
const a = { a: 1, b: 2 };
|
2021-04-23 04:28:56 +03:00
|
|
|
const result = objectKeyChanges(a, a);
|
|
|
|
expect(result).toBeDefined();
|
|
|
|
expect(result).toHaveLength(0);
|
|
|
|
});
|
|
|
|
|
2022-12-12 14:24:14 +03:00
|
|
|
it("should return properties which were changed, added, or removed", () => {
|
2021-06-29 15:11:58 +03:00
|
|
|
const a = { a: 1, b: 2, c: 3 };
|
2022-12-12 14:24:14 +03:00
|
|
|
const b: typeof a | { d: number } = { a: 1, b: 22, d: 4 };
|
2021-04-23 04:28:56 +03:00
|
|
|
const result = objectKeyChanges(a, b);
|
|
|
|
expect(result).toBeDefined();
|
|
|
|
expect(result).toHaveLength(3);
|
2022-12-12 14:24:14 +03:00
|
|
|
expect(result).toEqual(["c", "d", "b"]); // order isn't important, but the test cares
|
2021-04-23 04:28:56 +03:00
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2022-12-12 14:24:14 +03:00
|
|
|
describe("objectClone", () => {
|
|
|
|
it("should deep clone an object", () => {
|
2021-04-23 04:28:56 +03:00
|
|
|
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);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|