@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,424 @@
1
+ import { expectErrorToBeNull } from "../expectHelpers";
2
+ import { isReactive, nextTick } from "vue";
3
+ import { keyBy } from "lodash";
4
+ import flushPromises from "flush-promises";
5
+ import { inspect } from "util";
6
+
7
+ afterAll(() => {
8
+ jest.restoreAllMocks();
9
+ });
10
+
11
+ const fields = ["id", "__str__", "name"];
12
+ describe("use/listInstance.spec.js", function () {
13
+ let useListInstance, ListError, useListInstances, globalList;
14
+ beforeEach(async () => {
15
+ const imported = await import("../../../use/listInstance");
16
+ globalList = jest.fn();
17
+ imported.setListInstanceCrud({
18
+ list: globalList,
19
+ args: { stream: "test_stream" },
20
+ });
21
+ useListInstance = imported.useListInstance;
22
+ ListError = imported.ListError;
23
+ useListInstances = imported.useListInstances;
24
+ });
25
+ afterEach(function () {
26
+ jest.resetAllMocks();
27
+ });
28
+ const crudListResolvedPage1 = [
29
+ {
30
+ id: 1,
31
+ __str__: "qwer",
32
+ name: "qwer",
33
+ },
34
+ {
35
+ id: 2,
36
+ __str__: "asfd",
37
+ name: "asdf",
38
+ },
39
+ {
40
+ id: 3,
41
+ __str__: "zxcv",
42
+ name: "zxcv",
43
+ },
44
+ ];
45
+ const crudListResolvedPage2 = [
46
+ {
47
+ id: 4,
48
+ __str__: "yuio",
49
+ name: "yiuo",
50
+ },
51
+ {
52
+ id: 5,
53
+ __str__: "hjkl",
54
+ name: "hjkl",
55
+ },
56
+ {
57
+ id: 6,
58
+ __str__: "nm,.",
59
+ name: "nm,.",
60
+ },
61
+ ];
62
+ const listObject = {
63
+ id: null,
64
+ __str__: "nvm",
65
+ name: "nvm",
66
+ };
67
+ const crudListResolvedObjectsMid = keyBy(crudListResolvedPage1, "id");
68
+ const crudListResolvedObjects = keyBy([...crudListResolvedPage1, ...crudListResolvedPage2], "id");
69
+ describe("list", function () {
70
+ it("success", async function () {
71
+ const listInstance = useListInstance({});
72
+ let crudListResolve;
73
+ const crudListPromise = new Promise((resolve) => {
74
+ crudListResolve = resolve;
75
+ });
76
+ let passedPageCallback;
77
+ globalList.mockImplementation(({ pageCallback }) => {
78
+ passedPageCallback = pageCallback;
79
+ return crudListPromise;
80
+ });
81
+
82
+ expectErrorToBeNull(listInstance.state.error);
83
+ expect(listInstance.state.errored).toBe(false);
84
+ expect(listInstance.state.loading).toBeUndefined();
85
+ expect({ ...listInstance.state.objects }).toEqual({});
86
+
87
+ const liListResolve = listInstance.list({
88
+ listArgs: { user: 1 },
89
+ retrieveArgs: { fields: fields },
90
+ });
91
+
92
+ expectErrorToBeNull(listInstance.state.error);
93
+ expect(listInstance.state.errored).toBe(false);
94
+ expect(listInstance.state.loading).toBe(true);
95
+ expect({ ...listInstance.state.object }).toEqual({});
96
+
97
+ await nextTick();
98
+
99
+ passedPageCallback(crudListResolvedPage1);
100
+
101
+ expectErrorToBeNull(listInstance.state.error);
102
+ expect(listInstance.state.errored).toBe(false);
103
+ expect(listInstance.state.loading).toBe(true);
104
+ expect({ ...listInstance.state.objects }).toEqual(crudListResolvedObjectsMid);
105
+
106
+ passedPageCallback(crudListResolvedPage2);
107
+
108
+ expectErrorToBeNull(listInstance.state.error);
109
+ expect(listInstance.state.errored).toBe(false);
110
+ expect(listInstance.state.loading).toBe(true);
111
+ expect({ ...listInstance.state.objects }).toEqual(crudListResolvedObjects);
112
+
113
+ crudListResolve();
114
+ await flushPromises();
115
+
116
+ await expect(liListResolve).resolves.toBe(true);
117
+
118
+ expectErrorToBeNull(listInstance.state.error);
119
+ expect(listInstance.state.errored).toBe(false);
120
+ expect(listInstance.state.loading).toBe(false);
121
+ expect({ ...listInstance.state.objects }).toEqual(crudListResolvedObjects);
122
+ expect(globalList).toHaveBeenCalledWith({
123
+ crudArgs: { stream: "test_stream" },
124
+ listArgs: { user: 1 },
125
+ retrieveArgs: { fields: fields },
126
+ pageCallback: passedPageCallback,
127
+ });
128
+ expect(globalList).toHaveBeenCalledTimes(1);
129
+ });
130
+ it("already loading", async function () {
131
+ const listInstance = useListInstance({});
132
+ expectErrorToBeNull(listInstance.state.error);
133
+ expect(listInstance.state.errored).toBe(false);
134
+ expect(listInstance.state.loading).toBeUndefined();
135
+ expect({ ...listInstance.state.objects }).toEqual({});
136
+ globalList.mockImplementation(() => new Promise(() => {}));
137
+
138
+ listInstance.list({
139
+ listArgs: { user: 1 },
140
+ retrieveArgs: { fields: fields },
141
+ });
142
+ await expect(
143
+ listInstance.list({
144
+ listArgs: { user: 1 },
145
+ retrieveArgs: { fields: fields },
146
+ })
147
+ ).rejects.toThrow(ListError);
148
+
149
+ expect(globalList).toHaveBeenCalledWith({
150
+ crudArgs: { stream: "test_stream" },
151
+ listArgs: { user: 1 },
152
+ retrieveArgs: { fields: fields },
153
+ pageCallback: expect.any(Function),
154
+ });
155
+ expect(globalList).toHaveBeenCalledTimes(1);
156
+ expectErrorToBeNull(listInstance.state.error);
157
+ expect(listInstance.state.errored).toBe(false);
158
+ expect(listInstance.state.loading).toBe(true);
159
+ expect({ ...listInstance.state.objects }).toEqual({});
160
+ });
161
+ it("errored", async function () {
162
+ const listInstance = useListInstance({});
163
+ let crudListReject;
164
+ const crudListPromise = new Promise((resolve, reject) => {
165
+ crudListReject = reject;
166
+ });
167
+ let passedPageCallback;
168
+ globalList.mockImplementation(({ pageCallback }) => {
169
+ passedPageCallback = pageCallback;
170
+ return crudListPromise;
171
+ });
172
+
173
+ expectErrorToBeNull(listInstance.state.error);
174
+ expect(listInstance.state.errored).toBe(false);
175
+ expect(listInstance.state.loading).toBeUndefined();
176
+ expect({ ...listInstance.state.objects }).toEqual({});
177
+
178
+ const liListResolve = listInstance.list({
179
+ listArgs: { user: 1 },
180
+ retrieveArgs: { fields: fields },
181
+ });
182
+
183
+ expectErrorToBeNull(listInstance.state.error);
184
+ expect(listInstance.state.errored).toBe(false);
185
+ expect(listInstance.state.loading).toBe(true);
186
+ expect({ ...listInstance.state.object }).toEqual({});
187
+
188
+ await nextTick();
189
+
190
+ const rejected = new Error("Test Error");
191
+ crudListReject(rejected);
192
+ await flushPromises();
193
+
194
+ await expect(liListResolve).resolves.toBe(false);
195
+
196
+ expect(listInstance.state.error).toBe(rejected);
197
+ expect(listInstance.state.errored).toBe(true);
198
+ expect(listInstance.state.loading).toBe(false);
199
+ expect({ ...listInstance.state.objects }).toEqual({});
200
+ expect(globalList).toHaveBeenCalledWith({
201
+ crudArgs: { stream: "test_stream" },
202
+ listArgs: { user: 1 },
203
+ retrieveArgs: { fields: fields },
204
+ pageCallback: passedPageCallback,
205
+ });
206
+ expect(globalList).toHaveBeenCalledTimes(1);
207
+ });
208
+ it("success (default args)", async function () {
209
+ const listInstance = useListInstance({
210
+ defaultListArgs: { user: 1 },
211
+ defaultRetrieveArgs: { fields: fields },
212
+ });
213
+ let crudListResolve;
214
+ const crudListPromise = new Promise((resolve) => {
215
+ crudListResolve = resolve;
216
+ });
217
+ let passedPageCallback;
218
+ globalList.mockImplementation(({ pageCallback }) => {
219
+ passedPageCallback = pageCallback;
220
+ return crudListPromise;
221
+ });
222
+
223
+ expectErrorToBeNull(listInstance.state.error);
224
+ expect(listInstance.state.errored).toBe(false);
225
+ expect(listInstance.state.loading).toBeUndefined();
226
+ expect({ ...listInstance.state.objects }).toEqual({});
227
+
228
+ const liListResolve = listInstance.list();
229
+
230
+ expectErrorToBeNull(listInstance.state.error);
231
+ expect(listInstance.state.errored).toBe(false);
232
+ expect(listInstance.state.loading).toBe(true);
233
+ expect({ ...listInstance.state.object }).toEqual({});
234
+
235
+ await nextTick();
236
+
237
+ passedPageCallback(crudListResolvedPage1);
238
+
239
+ expectErrorToBeNull(listInstance.state.error);
240
+ expect(listInstance.state.errored).toBe(false);
241
+ expect(listInstance.state.loading).toBe(true);
242
+ expect({ ...listInstance.state.objects }).toEqual(crudListResolvedObjectsMid);
243
+
244
+ passedPageCallback(crudListResolvedPage2);
245
+
246
+ expectErrorToBeNull(listInstance.state.error);
247
+ expect(listInstance.state.errored).toBe(false);
248
+ expect(listInstance.state.loading).toBe(true);
249
+ expect({ ...listInstance.state.objects }).toEqual(crudListResolvedObjects);
250
+
251
+ crudListResolve();
252
+ await flushPromises();
253
+
254
+ await expect(liListResolve).resolves.toBe(true);
255
+
256
+ expectErrorToBeNull(listInstance.state.error);
257
+ expect(listInstance.state.errored).toBe(false);
258
+ expect(listInstance.state.loading).toBe(false);
259
+ expect({ ...listInstance.state.objects }).toEqual(crudListResolvedObjects);
260
+ expect(globalList).toHaveBeenCalledWith({
261
+ crudArgs: { stream: "test_stream" },
262
+ listArgs: { user: 1 },
263
+ retrieveArgs: { fields: fields },
264
+ pageCallback: passedPageCallback,
265
+ });
266
+ expect(globalList).toHaveBeenCalledTimes(1);
267
+ });
268
+ it("success (custom stream)", async function () {
269
+ const listInstance = useListInstance({
270
+ crudArgs: { stream: "custom_stream" },
271
+ });
272
+ let crudListResolve;
273
+ const crudListPromise = new Promise((resolve) => {
274
+ crudListResolve = resolve;
275
+ });
276
+ let passedPageCallback;
277
+ globalList.mockImplementation(({ pageCallback }) => {
278
+ passedPageCallback = pageCallback;
279
+ return crudListPromise;
280
+ });
281
+
282
+ expectErrorToBeNull(listInstance.state.error);
283
+ expect(listInstance.state.errored).toBe(false);
284
+ expect(listInstance.state.loading).toBeUndefined();
285
+ expect({ ...listInstance.state.objects }).toEqual({});
286
+
287
+ const liListResolve = listInstance.list({
288
+ listArgs: { user: 1 },
289
+ retrieveArgs: { fields: fields },
290
+ });
291
+
292
+ expectErrorToBeNull(listInstance.state.error);
293
+ expect(listInstance.state.errored).toBe(false);
294
+ expect(listInstance.state.loading).toBe(true);
295
+ expect({ ...listInstance.state.object }).toEqual({});
296
+
297
+ await nextTick();
298
+
299
+ passedPageCallback(crudListResolvedPage1);
300
+
301
+ expectErrorToBeNull(listInstance.state.error);
302
+ expect(listInstance.state.errored).toBe(false);
303
+ expect(listInstance.state.loading).toBe(true);
304
+ expect({ ...listInstance.state.objects }).toEqual(crudListResolvedObjectsMid);
305
+
306
+ passedPageCallback(crudListResolvedPage2);
307
+
308
+ expectErrorToBeNull(listInstance.state.error);
309
+ expect(listInstance.state.errored).toBe(false);
310
+ expect(listInstance.state.loading).toBe(true);
311
+ expect({ ...listInstance.state.objects }).toEqual(crudListResolvedObjects);
312
+
313
+ crudListResolve();
314
+ await flushPromises();
315
+
316
+ await expect(liListResolve).resolves.toBe(true);
317
+
318
+ expectErrorToBeNull(listInstance.state.error);
319
+ expect(listInstance.state.errored).toBe(false);
320
+ expect(listInstance.state.loading).toBe(false);
321
+ expect({ ...listInstance.state.objects }).toEqual(crudListResolvedObjects);
322
+ expect(globalList).toHaveBeenCalledWith({
323
+ crudArgs: { stream: "custom_stream" },
324
+ listArgs: { user: 1 },
325
+ retrieveArgs: { fields: fields },
326
+ pageCallback: passedPageCallback,
327
+ });
328
+ expect(globalList).toHaveBeenCalledTimes(1);
329
+ });
330
+ });
331
+ it("useListInstances", async function () {
332
+ const listInstanceA = useListInstance({
333
+ crudArgs: { stream: "test_streamA" },
334
+ id: 1,
335
+ retrieveArgs: {
336
+ fields,
337
+ },
338
+ });
339
+ const listInstanceB = useListInstance({
340
+ crudArgs: { stream: "test_streamB" },
341
+ id: 2,
342
+ retrieveArgs: {
343
+ fields,
344
+ },
345
+ });
346
+ const listInstances = useListInstances({
347
+ A: {
348
+ crudArgs: { stream: "test_streamA" },
349
+ id: 1,
350
+ retrieveArgs: {
351
+ fields,
352
+ },
353
+ },
354
+ B: {
355
+ crudArgs: { stream: "test_streamB" },
356
+ id: 2,
357
+ retrieveArgs: {
358
+ fields,
359
+ },
360
+ },
361
+ });
362
+ expect(inspect(listInstances.A)).toEqual(inspect(listInstanceA));
363
+ expect(inspect(listInstances.B)).toEqual(inspect(listInstanceB));
364
+ });
365
+ describe("addListObject", function () {
366
+ it("errored", function () {
367
+ const listInstance = useListInstance({});
368
+ expect(() => listInstance.addListObject({ listObject })).toThrowError(ListError);
369
+ listObject.id = listInstance.getFakeId();
370
+ listInstance.addListObject(listObject);
371
+ expect(() => listInstance.addListObject({ listObject })).toThrowError(ListError);
372
+ });
373
+ it("succeeded", function () {
374
+ const listInstance = useListInstance({});
375
+ const newId = listInstance.getFakeId();
376
+ listObject.id = newId;
377
+ listInstance.addListObject(listObject);
378
+ expect(listInstance.state.objects[newId]).toEqual(listObject);
379
+ const reactiveProxy = listInstance.state.objects[newId];
380
+ expect(isReactive(reactiveProxy)).toBe(true);
381
+ });
382
+ });
383
+ describe("updateListObject", function () {
384
+ it("errors", function () {
385
+ const listInstance = useListInstance({});
386
+ expect(() => listInstance.updateListObject({ listObject })).toThrowError(ListError);
387
+ listObject.id = -50002000;
388
+ listInstance.addListObject(listObject);
389
+ expect(() => listInstance.updateListObject({ listObject })).toThrowError(ListError);
390
+ });
391
+ it("succeeds", async function () {
392
+ const listInstance = useListInstance({
393
+ defaultListArgs: { user: 1 },
394
+ defaultRetrieveArgs: { fields: fields },
395
+ });
396
+ let crudListResolve;
397
+ const crudListPromise = new Promise((resolve) => {
398
+ crudListResolve = resolve;
399
+ });
400
+ let passedPageCallback;
401
+ globalList.mockImplementation(({ pageCallback }) => {
402
+ passedPageCallback = pageCallback;
403
+ return crudListPromise;
404
+ });
405
+
406
+ listInstance.list();
407
+
408
+ passedPageCallback(crudListResolvedPage1);
409
+ crudListResolve();
410
+
411
+ let updateObject = listInstance.state.objects["1"];
412
+ updateObject.name = "updated";
413
+ listInstance.updateListObject(updateObject);
414
+ expect(listInstance.state.objects["1"]).toEqual(updateObject);
415
+ });
416
+ });
417
+ describe("getFakeId", function () {
418
+ it("returns fakeId", function () {
419
+ const listInstance = useListInstance({});
420
+ const fakeId = listInstance.getFakeId();
421
+ expect(fakeId).toBeTruthy();
422
+ });
423
+ });
424
+ });
@@ -0,0 +1,73 @@
1
+ import { nextTick } from "vue";
2
+ import { unrefAndToRawDeep } from "../../../utils/unrefAndToRawDeep";
3
+
4
+ describe("use/listRelated", () => {
5
+ let useListInstance, useListRelated;
6
+ beforeEach(async () => {
7
+ const listInstanceModule = await import("../../../use/listInstance");
8
+ useListInstance = listInstanceModule.useListInstance;
9
+ const listRelatedModule = await import("../../../use/listRelated");
10
+ useListRelated = listRelatedModule.useListRelated;
11
+ });
12
+ it("should return a list of related items", async () => {
13
+ const mainListInstance = useListInstance({});
14
+ const relatedListInstance = useListInstance({});
15
+ mainListInstance.addListObject({
16
+ id: "1",
17
+ name: "main",
18
+ related_items: ["2", "3"],
19
+ related_id: "4",
20
+ });
21
+ relatedListInstance.addListObject({
22
+ id: "2",
23
+ name: "related1",
24
+ });
25
+ relatedListInstance.addListObject({
26
+ id: "3",
27
+ name: "related2",
28
+ });
29
+ relatedListInstance.addListObject({
30
+ id: "4",
31
+ name: "related3",
32
+ });
33
+ const listRelated = useListRelated({
34
+ parentState: mainListInstance.state,
35
+ relatedObjectsRules: {
36
+ relatedItems: {
37
+ objects: relatedListInstance.state.objects,
38
+ pkKey: "related_items",
39
+ },
40
+ relatedItem: {
41
+ objects: relatedListInstance.state.objects,
42
+ pkKey: "related_id",
43
+ },
44
+ },
45
+ relatedObjectsPropertyName: "myRelatedObjects",
46
+ });
47
+ await nextTick();
48
+ expect(unrefAndToRawDeep(listRelated.state.objects)).toEqual({
49
+ 1: {
50
+ id: "1",
51
+ name: "main",
52
+ related_id: "4",
53
+ related_items: ["2", "3"],
54
+ myRelatedObjects: {
55
+ relatedItems: [
56
+ {
57
+ id: "2",
58
+ name: "related1",
59
+ },
60
+ {
61
+ id: "3",
62
+ name: "related2",
63
+ },
64
+ ],
65
+ relatedItem: {
66
+ id: "4",
67
+ name: "related3",
68
+ },
69
+ },
70
+ },
71
+ });
72
+ });
73
+ });