@arrai-innovations/reactive-helpers 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/.circleci/config.yml +91 -0
  2. package/.commitlintrc.json +3 -0
  3. package/.editorconfig +5 -0
  4. package/.eslintignore +2 -0
  5. package/.eslintrc.js +26 -0
  6. package/.husky/commit-msg +2 -0
  7. package/.husky/pre-commit +2 -0
  8. package/.idea/encodings.xml +6 -0
  9. package/.idea/inspectionProfiles/Project_Default.xml +6 -0
  10. package/.idea/modules.xml +8 -0
  11. package/.idea/prettier.xml +8 -0
  12. package/.idea/reactive-helpers.iml +15 -0
  13. package/.lintstagedrc +11 -0
  14. package/.prettierignore +4 -0
  15. package/.prettierrc.js +5 -0
  16. package/README.md +598 -0
  17. package/babel.config.js +15 -0
  18. package/index.js +2 -0
  19. package/jest.config.mjs +27 -0
  20. package/package.json +59 -0
  21. package/tests/unit/.eslintrc.js +10 -0
  22. package/tests/unit/expectHelpers.js +6 -0
  23. package/tests/unit/mockOnUnmounted.js +9 -0
  24. package/tests/unit/use/index.spec.js +18 -0
  25. package/tests/unit/use/listFilter.spec.js +332 -0
  26. package/tests/unit/use/listInstance.spec.js +424 -0
  27. package/tests/unit/use/listRelated.spec.js +73 -0
  28. package/tests/unit/use/listSort.spec.js +220 -0
  29. package/tests/unit/use/listSubscription.spec.js +540 -0
  30. package/tests/unit/use/objectInstance.spec.js +897 -0
  31. package/tests/unit/use/objectSubscription.spec.js +671 -0
  32. package/tests/unit/use/search.spec.js +67 -0
  33. package/tests/unit/use/watches.spec.js +252 -0
  34. package/tests/unit/utils/assignReactiveObject.spec.js +127 -0
  35. package/tests/unit/utils/index.spec.js +18 -0
  36. package/tests/unit/utils/keyDiff.spec.js +67 -0
  37. package/tests/unit/utils/set.spec.js +68 -0
  38. package/tests/unit/utils/unrefAndToRawDeep.spec.js +170 -0
  39. package/use/index.js +8 -0
  40. package/use/listFilter.js +157 -0
  41. package/use/listInstance.js +144 -0
  42. package/use/listRelated.js +135 -0
  43. package/use/listSort.js +190 -0
  44. package/use/listSubscription.js +143 -0
  45. package/use/objectInstance.js +222 -0
  46. package/use/objectSubscription.js +188 -0
  47. package/use/search.js +104 -0
  48. package/utils/assignReactiveObject.js +62 -0
  49. package/utils/index.js +5 -0
  50. package/utils/keyDiff.js +10 -0
  51. package/utils/set.js +48 -0
  52. package/utils/unrefAndToRawDeep.js +49 -0
  53. package/utils/watches.js +170 -0
@@ -0,0 +1,67 @@
1
+ import { doAwaitTimeout } from "../../../utils";
2
+
3
+ describe("use/search", () => {
4
+ let useSearch, setDefaultSearchOptions;
5
+ beforeEach(async () => {
6
+ const searchModule = await import("../../../use/search");
7
+ useSearch = searchModule.useSearch;
8
+ setDefaultSearchOptions = searchModule.setDefaultSearchOptions;
9
+ setDefaultSearchOptions({
10
+ throttle: 150,
11
+ });
12
+ });
13
+ it("should allow adding items to the index", async () => {
14
+ const search = useSearch();
15
+ search.addIndex(1, "test index value one");
16
+ search.addIndex(2, "test index value two");
17
+ search.addIndex(3, "test index value three");
18
+ await doAwaitTimeout(200);
19
+ expect(Object.keys(search.state.results)).toEqual([]);
20
+ search.state.search = "test";
21
+ await doAwaitTimeout(200);
22
+ // keys are strings
23
+ expect(Object.keys(search.state.results)).toEqual(["1", "2", "3"]);
24
+ });
25
+ it("should allow removing items from the index", async () => {
26
+ const search = useSearch();
27
+ search.addIndex(1, "test index value one");
28
+ search.addIndex(2, "test index value two");
29
+ search.addIndex(3, "test index value three");
30
+ search.state.search = "test";
31
+ await doAwaitTimeout(200);
32
+ // keys are strings
33
+ expect(Object.keys(search.state.results)).toEqual(["1", "2", "3"]);
34
+ search.removeIndex(2);
35
+ await doAwaitTimeout(200);
36
+ expect(Object.keys(search.state.results)).toEqual(["1", "3"]);
37
+ });
38
+ it("should allow updating items in the index", async () => {
39
+ const search = useSearch();
40
+ search.addIndex(1, "test index value one");
41
+ search.addIndex(2, "test index value two");
42
+ search.addIndex(3, "test index value three");
43
+ search.state.search = "test";
44
+ await doAwaitTimeout(200);
45
+ // keys are strings
46
+ expect(Object.keys(search.state.results)).toEqual(["1", "2", "3"]);
47
+ search.updateIndex(2, "twest index value two updated");
48
+ await doAwaitTimeout(200);
49
+ expect(Object.keys(search.state.results)).toEqual(["1", "3"]);
50
+ search.state.search = "two";
51
+ await doAwaitTimeout(200);
52
+ expect(Object.keys(search.state.results)).toEqual(["2"]);
53
+ });
54
+ it("should allow clearing all items from the index", async () => {
55
+ const search = useSearch();
56
+ search.addIndex(1, "test index value one");
57
+ search.addIndex(2, "test index value two");
58
+ search.addIndex(3, "test index value three");
59
+ search.state.search = "test";
60
+ await doAwaitTimeout(200);
61
+ // keys are strings
62
+ expect(Object.keys(search.state.results)).toEqual(["1", "2", "3"]);
63
+ search.clearIndex();
64
+ await doAwaitTimeout(200);
65
+ expect(Object.keys(search.state.results)).toEqual([]);
66
+ });
67
+ });
@@ -0,0 +1,252 @@
1
+ import {
2
+ AwaitNot,
3
+ AwaitNotError,
4
+ AwaitTimeout,
5
+ AwaitTimeoutError,
6
+ doAwaitTimeout,
7
+ ImmediateStopWatch,
8
+ } from "../../../utils";
9
+ import { performance } from "perf_hooks";
10
+ import { nextTick, reactive } from "vue";
11
+
12
+ describe("use/watches", () => {
13
+ let timeout, reactiveObject, awaitNot;
14
+ beforeEach(async () => {
15
+ reactiveObject = reactive({});
16
+ awaitNot = new AwaitNot({
17
+ obj: reactiveObject,
18
+ prop: "prop",
19
+ couldAlreadyBeFalse: false,
20
+ timeout: 500,
21
+ });
22
+ });
23
+ afterEach(() => {
24
+ jest.resetAllMocks();
25
+ });
26
+ describe("doAwaitTimeout", () => {
27
+ it("should resolve after the passed timeout", async () => {
28
+ timeout = 200;
29
+ const start = performance.now();
30
+ await doAwaitTimeout(timeout);
31
+ const end = performance.now();
32
+ expect(end - start).toBeLessThan(timeout * 1.1);
33
+ });
34
+ it("rejects the promise when stopped manually", async () => {
35
+ timeout = 200;
36
+ const awaitTimeout = new AwaitTimeout(timeout);
37
+ awaitTimeout.start();
38
+ setTimeout(() => awaitTimeout.stop(), timeout / 2);
39
+ await expect(awaitTimeout.promise).rejects.toThrow(AwaitTimeoutError);
40
+ });
41
+ });
42
+ describe("doAwaitNot instance's obj.prop loading pattern begins as undefined...", () => {
43
+ it("resolves on cycling through true and false && couldAlreadyBeFalse = false", async () => {
44
+ awaitNot.start();
45
+ reactiveObject.prop = true;
46
+ await nextTick();
47
+ reactiveObject.prop = false;
48
+ await expect(awaitNot.promise).resolves.toBe(undefined);
49
+ });
50
+ it("resolves on cycling through true, true and false && couldAlreadyBeFalse = false", async () => {
51
+ awaitNot.start();
52
+ reactiveObject.prop = true;
53
+ await nextTick();
54
+ reactiveObject.prop = true;
55
+ await nextTick();
56
+ reactiveObject.prop = false;
57
+ await expect(awaitNot.promise).resolves.toBe(undefined);
58
+ });
59
+ it("rejects on cycling through true to undefined && couldAlreadyBeFalse = false", async () => {
60
+ awaitNot.start();
61
+ reactiveObject.prop = true;
62
+ await nextTick();
63
+ reactiveObject.prop = undefined;
64
+ await expect(awaitNot.promise).rejects.toThrow(AwaitNotError);
65
+ });
66
+ it("rejects as static undefined && couldAlreadyBeFalse: false", async () => {
67
+ awaitNot.start();
68
+ await nextTick();
69
+ await expect(awaitNot.promise).rejects.toThrow(AwaitNotError);
70
+ });
71
+ it("resolves on cycling through false && couldAlreadyBeFalse: true", async () => {
72
+ awaitNot.couldAlreadyBeFalse = true;
73
+ awaitNot.start();
74
+ reactiveObject.prop = false;
75
+ await nextTick();
76
+ await expect(awaitNot.promise).resolves.toBe(undefined);
77
+ });
78
+ it("resolves on cycling through true and false && couldAlreadyBeFalse: true", async () => {
79
+ awaitNot.couldAlreadyBeFalse = true;
80
+ awaitNot.start();
81
+ reactiveObject.prop = true;
82
+ await nextTick();
83
+ reactiveObject.prop = false;
84
+ await nextTick();
85
+ await expect(awaitNot.promise).resolves.toBe(undefined);
86
+ });
87
+ it("rejects on cycling through false and undefined && couldAlreadyBeFalse: true", async () => {
88
+ awaitNot.couldAlreadyBeFalse = true;
89
+ awaitNot.start();
90
+ reactiveObject.prop = false;
91
+ await nextTick();
92
+ reactiveObject.prop = undefined;
93
+ await nextTick();
94
+ await expect(awaitNot.promise).resolves.toBe(undefined);
95
+ });
96
+ it("rejects as static undefined && couldAlreadyBeFalse: true", async () => {
97
+ awaitNot.couldAlreadyBeFalse = true;
98
+ awaitNot.start();
99
+ await nextTick();
100
+ await expect(awaitNot.promise).rejects.toThrow(AwaitNotError);
101
+ });
102
+ });
103
+ describe("doAwaitNot instance's obj.prop loading pattern begins as false...", () => {
104
+ it("resolves on cycling through true and false && couldAlreadyBeFalse: false", async () => {
105
+ reactiveObject.prop = false;
106
+ awaitNot.start();
107
+ reactiveObject.prop = true;
108
+ await nextTick();
109
+ reactiveObject.prop = false;
110
+ await expect(awaitNot.promise).resolves.toBe(undefined);
111
+ });
112
+ it("resolves on cycling through true, true and false && couldAlreadyBeFalse: false", async () => {
113
+ reactiveObject.prop = false;
114
+ awaitNot.start();
115
+ reactiveObject.prop = true;
116
+ await nextTick();
117
+ reactiveObject.prop = true;
118
+ await nextTick();
119
+ reactiveObject.prop = false;
120
+ await expect(awaitNot.promise).resolves.toBe(undefined);
121
+ });
122
+ it("resolves as static false && couldAlreadyBeFalse: false", async () => {
123
+ reactiveObject.prop = false;
124
+ awaitNot.start();
125
+ await expect(awaitNot.promise).resolves.toBe(undefined);
126
+ });
127
+ it("resolves on cycling through true and false && couldAlreadyBeFalse: true", async () => {
128
+ awaitNot.couldAlreadyBeFalse = true;
129
+ reactiveObject.prop = false;
130
+ awaitNot.start();
131
+ reactiveObject.prop = true;
132
+ await nextTick();
133
+ reactiveObject.prop = false;
134
+ await expect(awaitNot.promise).resolves.toBe(undefined);
135
+ });
136
+ it("resolves on cycling through true, true and false && couldAlreadyBeFalse: true", async () => {
137
+ awaitNot.couldAlreadyBeFalse = true;
138
+ reactiveObject.prop = false;
139
+ awaitNot.start();
140
+ reactiveObject.prop = true;
141
+ await nextTick();
142
+ reactiveObject.prop = true;
143
+ await nextTick();
144
+ reactiveObject.prop = false;
145
+ await expect(awaitNot.promise).resolves.toBe(undefined);
146
+ });
147
+ it("rejects as static false && couldAlreadyBeFalse: true", async () => {
148
+ awaitNot.couldAlreadyBeFalse = true;
149
+ reactiveObject.prop = false;
150
+ awaitNot.start();
151
+ await expect(awaitNot.promise).rejects.toThrow(AwaitNotError);
152
+ });
153
+ });
154
+ describe("doAwaitNot instance's obj.prop loading pattern begins as true...", () => {
155
+ it("resolves on cycling through true and false && couldAlreadyBeFalse: false", async () => {
156
+ reactiveObject.prop = true;
157
+ awaitNot.start();
158
+ reactiveObject.prop = true;
159
+ await nextTick();
160
+ reactiveObject.prop = false;
161
+ await nextTick();
162
+ await expect(awaitNot.promise).resolves.toBe(undefined);
163
+ });
164
+ it("resolves on cycling through false && couldAlreadyBeFalse: false", async () => {
165
+ reactiveObject.prop = true;
166
+ awaitNot.start();
167
+ reactiveObject.prop = false;
168
+ await nextTick();
169
+ await expect(awaitNot.promise).resolves.toBe(undefined);
170
+ });
171
+ it("rejects as static true && couldAlreadyBeFalse: false", async () => {
172
+ reactiveObject.prop = true;
173
+ awaitNot.start();
174
+ await expect(awaitNot.promise).rejects.toThrow(AwaitNotError);
175
+ });
176
+ it("resolves on cycling through false && couldAlreadyBeFalse: true", async () => {
177
+ awaitNot.couldAlreadyBeFalse = true;
178
+ reactiveObject.prop = true;
179
+ awaitNot.start();
180
+ reactiveObject.prop = false;
181
+ await nextTick();
182
+ await expect(awaitNot.promise).resolves.toBe(undefined);
183
+ });
184
+ it("resolves on cycling through true and false && couldAlreadyBeFalse: true", async () => {
185
+ awaitNot.couldAlreadyBeFalse = true;
186
+ reactiveObject.prop = true;
187
+ awaitNot.start();
188
+ reactiveObject.prop = true;
189
+ await nextTick();
190
+ reactiveObject.prop = false;
191
+ await nextTick();
192
+ await expect(awaitNot.promise).resolves.toBe(undefined);
193
+ });
194
+ it("rejects as static true && couldAlreadyBeFalse: true", async () => {
195
+ awaitNot.couldAlreadyBeFalse = true;
196
+ reactiveObject.prop = true;
197
+ awaitNot.start();
198
+ await nextTick();
199
+ await expect(awaitNot.promise).rejects.toThrow(AwaitNotError);
200
+ });
201
+ });
202
+ describe("ImmediateStopWatch", () => {
203
+ it("responds to multiple calls", async () => {
204
+ const immediateStopWatch = new ImmediateStopWatch({});
205
+ reactiveObject.prop = true;
206
+ const watchSources = () => reactiveObject.prop;
207
+ const watchFunc = jest.fn();
208
+ const watchFuncArgs = [reactiveObject.prop];
209
+
210
+ const propsList = [true, false, true, false];
211
+ const cycleProp = async (propsList) => {
212
+ for (let prop of propsList) {
213
+ reactiveObject.prop = prop;
214
+ await nextTick();
215
+ }
216
+ };
217
+
218
+ immediateStopWatch.start(watchSources, watchFunc, watchFuncArgs);
219
+ reactiveObject.prop = false;
220
+ await nextTick();
221
+ expect(watchFunc).toBeCalled();
222
+ await cycleProp(propsList);
223
+ expect(watchFunc).toHaveBeenCalledTimes(6);
224
+ });
225
+ it("stops when expected", async () => {
226
+ const immediateStopWatch = new ImmediateStopWatch({});
227
+ reactiveObject.prop = true;
228
+ const watchSources = () => reactiveObject.prop;
229
+ const watchFunc = jest.fn((newValue) => {
230
+ if (newValue === undefined) {
231
+ immediateStopWatch.stop();
232
+ }
233
+ });
234
+ const watchFuncArgs = [reactiveObject.prop];
235
+
236
+ const propsList = [true, false, undefined, true, false];
237
+ const cycleProp = async (propsList) => {
238
+ for (let prop of propsList) {
239
+ reactiveObject.prop = prop;
240
+ await nextTick();
241
+ }
242
+ };
243
+
244
+ immediateStopWatch.start(watchSources, watchFunc, watchFuncArgs);
245
+ reactiveObject.prop = false;
246
+ await nextTick();
247
+ expect(watchFunc).toBeCalled();
248
+ await cycleProp(propsList);
249
+ expect(watchFunc).toHaveBeenCalledTimes(5);
250
+ });
251
+ });
252
+ });
@@ -0,0 +1,127 @@
1
+ import {
2
+ addOrUpdateReactiveObject,
3
+ assignReactiveObject,
4
+ AssignReactiveObjectError,
5
+ } from "../../../utils/assignReactiveObject";
6
+ import { computed, EffectScope, effectScope, reactive, toRef } from "vue";
7
+
8
+ describe("utils/assignReactiveObject", function () {
9
+ describe("addOrUpdateReactiveObject", function () {});
10
+ describe("assignReactiveObject", function () {
11
+ describe("should update the target", function () {
12
+ it("when both target and source are not reactive", function () {
13
+ const target = {
14
+ a: 1,
15
+ b: 2,
16
+ c: 3,
17
+ };
18
+ const source = {
19
+ a: 4,
20
+ b: 5,
21
+ c: 6,
22
+ };
23
+ assignReactiveObject(target, source);
24
+ expect(target).toEqual({
25
+ a: 4,
26
+ b: 5,
27
+ c: 6,
28
+ });
29
+ });
30
+ it("when both target and source are reactive, to not break reactivity", function () {
31
+ const target = reactive({
32
+ a: 1,
33
+ b: 2,
34
+ c: 3,
35
+ });
36
+ const source = reactive({
37
+ a: 4,
38
+ b: 5,
39
+ c: 6,
40
+ });
41
+ const tes = effectScope();
42
+ let computedSum;
43
+ tes.run(() => {
44
+ computedSum = computed(() => {
45
+ return target.a + target.b + target.c;
46
+ });
47
+ });
48
+ try {
49
+ expect(computedSum.value).toBe(6);
50
+ assignReactiveObject(target, source);
51
+ expect(target).toEqual({
52
+ a: 4,
53
+ b: 5,
54
+ c: 6,
55
+ });
56
+ expect(computedSum.value).toBe(15);
57
+ } finally {
58
+ computedSum = null;
59
+ tes.stop();
60
+ }
61
+ });
62
+ });
63
+ describe("should throw an error", function () {
64
+ it("when target is not an array or object", function () {
65
+ expect(() => assignReactiveObject(null, {})).toThrowError(
66
+ new AssignReactiveObjectError("target must be an object or an array, not null")
67
+ );
68
+ expect(() => assignReactiveObject(undefined, {})).toThrowError(
69
+ new AssignReactiveObjectError("target must be an object or an array, not undefined")
70
+ );
71
+ expect(() => assignReactiveObject(1, {})).toThrowError(
72
+ new AssignReactiveObjectError("target must be an object or an array, not 1")
73
+ );
74
+ expect(() => assignReactiveObject(NaN, {})).toThrowError(
75
+ new AssignReactiveObjectError("target must be an object or an array, not NaN")
76
+ );
77
+ expect(() => assignReactiveObject(Infinity, {})).toThrowError(
78
+ new AssignReactiveObjectError("target must be an object or an array, not Infinity")
79
+ );
80
+ });
81
+ it("when source is not an array or object", function () {
82
+ expect(() => assignReactiveObject({}, null)).toThrowError(
83
+ new AssignReactiveObjectError("source must be an object or an array, not null")
84
+ );
85
+ expect(() => assignReactiveObject({}, undefined)).toThrowError(
86
+ new AssignReactiveObjectError("source must be an object or an array, not undefined")
87
+ );
88
+ expect(() => assignReactiveObject({}, 1)).toThrowError(
89
+ new AssignReactiveObjectError("source must be an object or an array, not 1")
90
+ );
91
+ expect(() => assignReactiveObject({}, NaN)).toThrowError(
92
+ new AssignReactiveObjectError("source must be an object or an array, not NaN")
93
+ );
94
+ expect(() => assignReactiveObject({}, Infinity)).toThrowError(
95
+ new AssignReactiveObjectError("source must be an object or an array, not Infinity")
96
+ );
97
+ });
98
+ });
99
+ });
100
+ it("should work as in the example for the readme", function () {
101
+ const es = new EffectScope();
102
+ es.run(() => {
103
+ const target = reactive({ a: 1 });
104
+ const source = { a: 3, b: 4 };
105
+ const source2 = reactive({ b: 5 });
106
+
107
+ const a = toRef(target, "a");
108
+ const b = toRef(target, "b");
109
+ const mySum = computed(() => (a.value || 0) + (b.value || 0));
110
+
111
+ expect(mySum.value).toBe(1);
112
+ assignReactiveObject(target, source);
113
+ expect(mySum.value).toBe(7);
114
+ addOrUpdateReactiveObject(target, source2);
115
+ expect({ ...target }).toEqual({ a: 3, b: 5 });
116
+ expect(mySum.value).toBe(8);
117
+ source2.b = 6;
118
+ expect(mySum.value).toBe(9);
119
+ assignReactiveObject(target, source2);
120
+ expect({ ...target }).toEqual({ b: 6 });
121
+ expect(mySum.value).toBe(6);
122
+ source2.b = 10;
123
+ expect(mySum.value).toBe(10);
124
+ });
125
+ es.stop();
126
+ });
127
+ });
@@ -0,0 +1,18 @@
1
+ import fs from "fs/promises";
2
+ import * as utils from "../../../utils";
3
+
4
+ describe("utils/index.js", function () {
5
+ it("should export everything exported by individual files", async function () {
6
+ const files = await fs.readdir("./utils");
7
+ const modules = files.filter((file) => file.endsWith(".js") && file !== "index.js");
8
+ const exportedKeys = [];
9
+ for (const module of modules) {
10
+ const moduleExports = await import(`../../../utils/${module}`);
11
+ exportedKeys.push(...Object.keys(moduleExports).filter((key) => key !== "default"));
12
+ }
13
+ const utilsKeys = Object.keys(utils);
14
+ utilsKeys.sort();
15
+ exportedKeys.sort();
16
+ expect(utilsKeys).toEqual(exportedKeys);
17
+ });
18
+ });
@@ -0,0 +1,67 @@
1
+ import { keyDiff } from "../../../utils/keyDiff";
2
+
3
+ describe("keyDiff", function () {
4
+ describe("should return the difference between two arrays of object keys", function () {
5
+ it("when the objects are the same", function () {
6
+ const newObj = {
7
+ a: 1,
8
+ b: 2,
9
+ c: 3,
10
+ };
11
+ const oldObj = {
12
+ a: 1,
13
+ b: 2,
14
+ c: 3,
15
+ };
16
+ expect(keyDiff(Object.keys(newObj), Object.keys(oldObj))).toEqual({
17
+ addedKeys: new Set(),
18
+ removedKeys: new Set(),
19
+ sameKeys: new Set(["a", "b", "c"]),
20
+ });
21
+ });
22
+ it("when keys are added", function () {
23
+ const newObj = {
24
+ a: 1,
25
+ b: 2,
26
+ c: 3,
27
+ d: 4,
28
+ };
29
+ const oldObj = {
30
+ a: 1,
31
+ b: 2,
32
+ c: 3,
33
+ };
34
+ expect(keyDiff(Object.keys(newObj), Object.keys(oldObj))).toEqual({
35
+ addedKeys: new Set(["d"]),
36
+ removedKeys: new Set(),
37
+ sameKeys: new Set(["a", "b", "c"]),
38
+ });
39
+ });
40
+ it("when keys are removed", function () {
41
+ const newObj = {
42
+ a: 1,
43
+ b: 2,
44
+ c: 3,
45
+ };
46
+ const oldObj = {
47
+ a: 1,
48
+ b: 2,
49
+ c: 3,
50
+ d: 4,
51
+ };
52
+ expect(keyDiff(Object.keys(newObj), Object.keys(oldObj))).toEqual({
53
+ addedKeys: new Set(),
54
+ removedKeys: new Set(["d"]),
55
+ sameKeys: new Set(["a", "b", "c"]),
56
+ });
57
+ });
58
+ });
59
+ it("should work as in the example for the readme", function () {
60
+ const newKeys = Object.keys({ a: 1, b: 2 });
61
+ const oldKeys = Object.keys({ a: 1, c: 3 });
62
+ const { addedKeys, removedKeys, sameKeys } = keyDiff(newKeys, oldKeys);
63
+ expect(addedKeys).toEqual(new Set(["b"]));
64
+ expect(removedKeys).toEqual(new Set(["c"]));
65
+ expect(sameKeys).toEqual(new Set(["a"]));
66
+ });
67
+ });
@@ -0,0 +1,68 @@
1
+ import { difference, intersection, isSuperset, symmetricDifference, union } from "../../../utils/set";
2
+
3
+ describe("utils/set", function () {
4
+ describe("isSuperset", function () {
5
+ it("should return true if the first set is a super set of the subset", function () {
6
+ const set = new Set([1, 2, 3, 4]);
7
+ const subset = new Set([1, 2, 3]);
8
+ expect(isSuperset(set, subset)).toBe(true);
9
+ });
10
+ it("should return false if the first set is not a super set of the subset", function () {
11
+ const set = new Set([1, 2, 3, 4]);
12
+ const subset = new Set([1, 2, 5]);
13
+ expect(isSuperset(set, subset)).toBe(false);
14
+ });
15
+ it("should return true if the subset is equal to the first set", function () {
16
+ const set = new Set([1, 2, 3]);
17
+ const subset = new Set([1, 2, 3]);
18
+ expect(isSuperset(set, subset)).toBe(true);
19
+ });
20
+ it("should return true if the subset is empty", function () {
21
+ const set = new Set([1, 2, 3]);
22
+ const subset = new Set([]);
23
+ expect(isSuperset(set, subset)).toBe(true);
24
+ });
25
+ it("should return false if the set is empty", function () {
26
+ const set = new Set([]);
27
+ const subset = new Set([1, 2, 3]);
28
+ expect(isSuperset(set, subset)).toBe(false);
29
+ });
30
+ });
31
+ describe("union", function () {
32
+ it("should return a set with the union of the two sets", function () {
33
+ const set1 = new Set([1, 2, 3, 5]);
34
+ const set2 = new Set([1, 2, 3, 4]);
35
+ expect(union(set1, set2)).toEqual(new Set([1, 2, 3, 4, 5]));
36
+ });
37
+ });
38
+ describe("intersection", function () {
39
+ it("should return a set with the intersection of the two sets", function () {
40
+ const set1 = new Set([1, 2, 3, 5]);
41
+ const set2 = new Set([1, 2, 3, 4]);
42
+ expect(intersection(set1, set2)).toEqual(new Set([1, 2, 3]));
43
+ });
44
+ });
45
+ describe("symmetricDifference", function () {
46
+ it("should return a set with the symmetric difference of the two sets", function () {
47
+ const set1 = new Set([1, 2, 3, 5]);
48
+ const set2 = new Set([1, 2, 3, 4]);
49
+ expect(symmetricDifference(set1, set2)).toEqual(new Set([4, 5]));
50
+ });
51
+ });
52
+ describe("difference", function () {
53
+ it("should return a set with the difference of the two sets", function () {
54
+ const set1 = new Set([1, 2, 3, 4]);
55
+ const set2 = new Set([3, 4, 5, 6]);
56
+ expect(difference(set1, set2)).toEqual(new Set([1, 2]));
57
+ expect(difference(set2, set1)).toEqual(new Set([5, 6]));
58
+ });
59
+ });
60
+ it("should work as in the example for the readme", function () {
61
+ expect(isSuperset(new Set([1, 2, 3, 4]), new Set([1, 2, 3]))).toBe(true);
62
+ expect(union(new Set([1, 2, 3, 4]), new Set([1, 2, 3]))).toEqual(new Set([1, 2, 3, 4]));
63
+ expect(intersection(new Set([1, 2, 3, 4]), new Set([1, 2, 3]))).toEqual(new Set([1, 2, 3]));
64
+ expect(symmetricDifference(new Set([1, 2, 3, 4]), new Set([1, 2, 3, 5]))).toEqual(new Set([4, 5]));
65
+ expect(difference(new Set([1, 2, 3, 4]), new Set([1, 2, 3, 5]))).toEqual(new Set([4]));
66
+ expect(difference(new Set([1, 2, 3, 5]), new Set([1, 2, 3, 4]))).toEqual(new Set([5]));
67
+ });
68
+ });