@arrai-innovations/reactive-helpers 1.2.2 → 2.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.
@@ -1,5 +1,9 @@
1
- import { reactive } from "vue";
1
+ import { nextTick, reactive } from "vue";
2
2
  import { inspect } from "util";
3
+ import { doAwaitTimeout } from "../../../utils";
4
+ import flushPromises from "flush-promises";
5
+ import { CancellableResolvable } from "../crudPromise";
6
+ import { poll } from "../poll";
3
7
 
4
8
  describe("use/listSubscription.spec.js", function () {
5
9
  let useListSubscription,
@@ -7,32 +11,57 @@ describe("use/listSubscription.spec.js", function () {
7
11
  useListSubscriptions,
8
12
  useListInstance,
9
13
  useListInstances,
10
- globalList,
11
- globalSubscribe,
12
- globalUnsubscribe,
14
+ crudList,
15
+ crudListResolvable = [],
16
+ crudSubscribe,
17
+ crudSubscribeResolvable = [],
13
18
  passedSubscriptionEventCallback;
14
19
  beforeEach(async () => {
20
+ crudListResolvable = [];
21
+ crudSubscribeResolvable = [];
22
+ crudListResolvable.push(new CancellableResolvable());
23
+ crudSubscribeResolvable.push(new CancellableResolvable());
15
24
  const listInstanceModule = await import("../../../use/listInstance");
16
- globalList = jest.fn();
25
+ crudList = jest
26
+ .fn()
27
+ .mockImplementationOnce(() => {
28
+ return crudListResolvable[0].promise;
29
+ })
30
+ .mockImplementation(() => {
31
+ const newResolvable = new CancellableResolvable();
32
+ crudListResolvable.push(newResolvable);
33
+ return newResolvable.promise;
34
+ });
17
35
  listInstanceModule.setListInstanceCrud({
18
- list: globalList,
36
+ list: crudList,
37
+ args: { stream: "test_stream" },
38
+ });
39
+ const listSubscriptionModule = await import("../../../use/listSubscription");
40
+ crudSubscribe = jest
41
+ .fn()
42
+ .mockImplementationOnce(({ subscriptionEventCallback }) => {
43
+ // this function cannot be async, or the resulting promise will lose its .cancel() method
44
+ passedSubscriptionEventCallback = subscriptionEventCallback;
45
+ return crudSubscribeResolvable[0].promise;
46
+ })
47
+ .mockImplementation(({ subscriptionEventCallback }) => {
48
+ // this function cannot be async, or the resulting promise will lose its .cancel() method
49
+ const newResolvable = new CancellableResolvable();
50
+ crudSubscribeResolvable.push(newResolvable);
51
+ passedSubscriptionEventCallback = subscriptionEventCallback;
52
+ return newResolvable.promise;
53
+ });
54
+ listSubscriptionModule.setListSubscriptionCrud({
55
+ subscribe: crudSubscribe,
19
56
  args: { stream: "test_stream" },
20
57
  });
58
+
21
59
  useListInstance = listInstanceModule.useListInstance;
22
60
  useListInstances = listInstanceModule.useListInstances;
23
- const imported = await import("../../../use/listSubscription");
24
- globalUnsubscribe = jest.fn();
25
- globalSubscribe = jest.fn();
26
- globalSubscribe.mockImplementation(({ subscriptionEventCallback } = {}) => {
27
- passedSubscriptionEventCallback = subscriptionEventCallback;
28
- return Promise.resolve(globalUnsubscribe);
29
- });
30
- imported.setListSubscriptionCrud({
31
- subscribe: globalSubscribe,
32
- });
33
- useListSubscription = imported.useListSubscription;
34
- ListSubscriptionError = imported.ListSubscriptionError;
35
- useListSubscriptions = imported.useListSubscriptions;
61
+
62
+ useListSubscription = listSubscriptionModule.useListSubscription;
63
+ ListSubscriptionError = listSubscriptionModule.ListSubscriptionError;
64
+ useListSubscriptions = listSubscriptionModule.useListSubscriptions;
36
65
  });
37
66
  afterEach(function () {
38
67
  jest.resetAllMocks();
@@ -40,25 +69,31 @@ describe("use/listSubscription.spec.js", function () {
40
69
  const fields = ["id", "__str__", "name"];
41
70
  describe("lifecycle", function () {
42
71
  it("success", async function () {
43
- globalUnsubscribe.mockImplementation(() => Promise.resolve(true));
44
- const defaultListArgs = reactive({
72
+ const listArgs = reactive({
45
73
  user: 1,
46
74
  });
47
- const defaultRetrieveArgs = reactive({
75
+ const retrieveArgs = reactive({
48
76
  fields: fields,
49
77
  });
50
78
  const listSubscription = useListSubscription({
51
- defaultListArgs,
52
- defaultRetrieveArgs,
79
+ listArgs,
80
+ retrieveArgs,
53
81
  });
54
- await listSubscription.subscribe();
55
- expect(globalSubscribe).toHaveBeenCalledWith({
82
+ listSubscription.subscribe();
83
+ await nextTick();
84
+ await flushPromises();
85
+ expect(crudSubscribe).toHaveBeenCalledWith({
56
86
  crudArgs: { stream: "test_stream" },
57
87
  listArgs: { user: 1 },
58
88
  retrieveArgs: { fields: fields },
59
89
  subscriptionEventCallback: expect.any(Function),
60
90
  });
61
- expect(globalSubscribe).toHaveBeenCalledTimes(1);
91
+ expect(crudSubscribe).toHaveBeenCalledTimes(1);
92
+ expect(listSubscription.state.subscribed).toBe(true);
93
+
94
+ crudListResolvable[0].resolve();
95
+ crudSubscribeResolvable[0].resolve();
96
+ await poll(() => listSubscription.state.subscribed);
62
97
 
63
98
  passedSubscriptionEventCallback(
64
99
  {
@@ -97,33 +132,46 @@ describe("use/listSubscription.spec.js", function () {
97
132
  passedSubscriptionEventCallback(1, "delete");
98
133
 
99
134
  expect(listSubscription.listInstance.state.objects).toEqual({});
135
+ expect(listSubscription.state.subscribed).toBe(true);
100
136
 
101
137
  const returnValue = await listSubscription.unsubscribe();
102
- expect(globalUnsubscribe).toHaveBeenCalledWith();
103
- expect(globalUnsubscribe).toHaveBeenCalledTimes(1);
138
+ expect(listSubscription.state.intendToSubscribe).toBe(false);
139
+ expect(listSubscription.state.intendToList).toBe(false);
140
+ await poll(() => !listSubscription.listIntent.state.active);
141
+ expect(crudListResolvable[0].promise.cancel).toHaveBeenCalledTimes(0);
142
+
143
+ crudSubscribeResolvable[0].cancel.resolve(true);
144
+ await nextTick();
145
+ await flushPromises();
146
+ await poll(() => !listSubscription.subscribeIntent.state.active);
147
+ expect(listSubscription.subscribeIntent.state.active).toBe(false);
148
+ expect(crudSubscribeResolvable[0].promise.cancel).toHaveBeenCalledWith();
149
+ expect(crudSubscribeResolvable[0].promise.cancel).toHaveBeenCalledTimes(1);
104
150
  expect(returnValue).toBe(true);
151
+
105
152
  expect(listSubscription.state.subscribed).toBe(false);
106
153
  });
107
154
  it("missing data", async function () {
108
- globalUnsubscribe.mockImplementation(() => Promise.resolve(true));
109
- const defaultListArgs = reactive({
155
+ const listArgs = reactive({
110
156
  user: 1,
111
157
  });
112
- const defaultRetrieveArgs = reactive({
158
+ const retrieveArgs = reactive({
113
159
  fields: fields,
114
160
  });
115
161
  const listSubscription = useListSubscription({
116
- defaultListArgs,
117
- defaultRetrieveArgs,
162
+ listArgs,
163
+ retrieveArgs,
118
164
  });
119
- await listSubscription.subscribe({});
120
- expect(globalSubscribe).toHaveBeenCalledWith({
165
+ listSubscription.subscribe();
166
+ await nextTick();
167
+ await flushPromises();
168
+ expect(crudSubscribe).toHaveBeenCalledWith({
121
169
  crudArgs: { stream: "test_stream" },
122
170
  listArgs: { user: 1 },
123
171
  retrieveArgs: { fields: fields },
124
172
  subscriptionEventCallback: expect.any(Function),
125
173
  });
126
- expect(globalSubscribe).toHaveBeenCalledTimes(1);
174
+ expect(crudSubscribe).toHaveBeenCalledTimes(1);
127
175
 
128
176
  expect(() => passedSubscriptionEventCallback({}, "create")).toThrow(ListSubscriptionError);
129
177
  expect(() => passedSubscriptionEventCallback({}, "create")).toThrow(
@@ -273,141 +321,120 @@ describe("use/listSubscription.spec.js", function () {
273
321
  );
274
322
 
275
323
  const returnValue = await listSubscription.unsubscribe();
276
- expect(globalUnsubscribe).toHaveBeenCalledWith();
277
- expect(globalUnsubscribe).toHaveBeenCalledTimes(1);
324
+ expect(crudSubscribeResolvable[0].promise.cancel).toHaveBeenCalledWith();
325
+ expect(crudSubscribeResolvable[0].promise.cancel).toHaveBeenCalledTimes(1);
278
326
  expect(returnValue).toBe(true);
279
327
  expect(listSubscription.state.subscribed).toBe(false);
280
328
  });
281
329
  it("unsubscribe false", async function () {
282
- globalUnsubscribe.mockImplementation(() => Promise.resolve(false));
283
- const defaultListArgs = reactive({
330
+ const listArgs = reactive({
284
331
  user: 1,
285
332
  });
286
- const defaultRetrieveArgs = reactive({
333
+ const retrieveArgs = reactive({
287
334
  fields: fields,
288
335
  });
289
336
  const listSubscription = useListSubscription({
290
- defaultListArgs,
291
- defaultRetrieveArgs,
337
+ listArgs,
338
+ retrieveArgs,
292
339
  });
293
- await listSubscription.subscribe({});
294
- expect(globalSubscribe).toHaveBeenCalledWith({
340
+ expect(listSubscription.unsubscribe()).toBe(false);
341
+ listSubscription.subscribe();
342
+ await nextTick();
343
+ await flushPromises();
344
+ expect(crudSubscribe).toHaveBeenCalledWith({
295
345
  crudArgs: { stream: "test_stream" },
296
346
  listArgs: { user: 1 },
297
347
  retrieveArgs: { fields: fields },
298
348
  subscriptionEventCallback: expect.any(Function),
299
349
  });
300
- expect(globalSubscribe).toHaveBeenCalledTimes(1);
350
+ expect(crudSubscribe).toHaveBeenCalledTimes(1);
301
351
 
302
- const returnValue = await listSubscription.unsubscribe();
303
- expect(globalUnsubscribe).toHaveBeenCalledWith();
304
- expect(globalUnsubscribe).toHaveBeenCalledTimes(1);
305
- expect(returnValue).toBe(false);
352
+ const returnValue = listSubscription.unsubscribe();
353
+ await nextTick();
354
+ await flushPromises();
355
+ expect(crudSubscribeResolvable[0].promise.cancel).toHaveBeenCalledWith();
356
+ expect(crudSubscribeResolvable[0].promise.cancel).toHaveBeenCalledTimes(1);
357
+ expect(returnValue).toBe(true);
306
358
  expect(listSubscription.state.subscribed).toBe(false);
359
+ expect(listSubscription.unsubscribe()).toBe(false);
307
360
  });
308
361
  it("just unsubscribe", async function () {
309
- globalUnsubscribe.mockImplementation(() => Promise.resolve(false));
310
- const defaultListArgs = reactive({
311
- user: 1,
312
- });
313
- const defaultRetrieveArgs = reactive({
314
- fields: fields,
315
- });
316
- const listSubscription = useListSubscription({
317
- defaultListArgs,
318
- defaultRetrieveArgs,
319
- });
320
-
321
- const returnValue = await listSubscription.unsubscribe();
322
- expect(globalUnsubscribe).toHaveBeenCalledTimes(0);
323
- expect(returnValue).toBe(false);
324
- expect(listSubscription.state.subscribed).toBe(undefined);
325
- });
326
- it("passed subscribe args", async function () {
327
- globalUnsubscribe.mockImplementation(() => Promise.resolve(true));
362
+ crudSubscribeResolvable[0].promise.cancel.mockImplementation(() => Promise.resolve(false));
328
363
  const listArgs = reactive({
329
364
  user: 1,
330
365
  });
331
366
  const retrieveArgs = reactive({
332
367
  fields: fields,
333
368
  });
334
- const listSubscription = useListSubscription({});
335
- await listSubscription.subscribe({
369
+ const listSubscription = useListSubscription({
336
370
  listArgs,
337
371
  retrieveArgs,
338
372
  });
339
- expect(globalSubscribe).toHaveBeenCalledWith({
340
- crudArgs: { stream: "test_stream" },
341
- listArgs: { user: 1 },
342
- retrieveArgs: { fields: fields },
343
- subscriptionEventCallback: expect.any(Function),
344
- });
345
- expect(globalSubscribe).toHaveBeenCalledTimes(1);
346
373
 
347
374
  const returnValue = await listSubscription.unsubscribe();
348
- expect(globalUnsubscribe).toHaveBeenCalledWith();
349
- expect(globalUnsubscribe).toHaveBeenCalledTimes(1);
350
- expect(returnValue).toBe(true);
351
- expect(listSubscription.state.subscribed).toBe(false);
375
+ expect(crudSubscribeResolvable[0].promise.cancel).toHaveBeenCalledTimes(0);
376
+ expect(returnValue).toBe(false);
377
+ expect(listSubscription.state.subscribed).toBe(undefined);
352
378
  });
353
379
  it("double subscribe", async function () {
354
- globalUnsubscribe.mockImplementation(() => Promise.resolve(true));
380
+ crudSubscribeResolvable[0].promise.cancel.mockImplementation(() => Promise.resolve(true));
355
381
  const listArgs = reactive({
356
382
  user: 1,
357
383
  });
358
384
  const retrieveArgs = reactive({
359
385
  fields: fields,
360
386
  });
361
- const listSubscription = useListSubscription({});
362
- const firstReturnValue = await listSubscription.subscribe({
363
- listArgs,
364
- retrieveArgs,
365
- });
366
- const secondReturnValue = await listSubscription.subscribe({
387
+ const listSubscription = useListSubscription({
367
388
  listArgs,
368
389
  retrieveArgs,
369
390
  });
370
- expect(globalSubscribe).toHaveBeenCalledWith({
391
+ const firstReturnValue = listSubscription.subscribe();
392
+ const secondReturnValue = listSubscription.subscribe();
393
+ await nextTick();
394
+ await flushPromises();
395
+ expect(crudSubscribe).toHaveBeenCalledWith({
371
396
  crudArgs: { stream: "test_stream" },
372
397
  listArgs: { user: 1 },
373
398
  retrieveArgs: { fields: fields },
374
399
  subscriptionEventCallback: expect.any(Function),
375
400
  });
376
- expect(globalSubscribe).toHaveBeenCalledTimes(1);
401
+ expect(crudSubscribe).toHaveBeenCalledTimes(1);
377
402
 
378
403
  expect(firstReturnValue).toBe(true);
379
404
  expect(secondReturnValue).toBe(false);
380
405
 
381
406
  const returnValue = await listSubscription.unsubscribe();
382
- expect(globalUnsubscribe).toHaveBeenCalledWith();
383
- expect(globalUnsubscribe).toHaveBeenCalledTimes(1);
407
+ expect(crudSubscribeResolvable[0].promise.cancel).toHaveBeenCalledWith();
408
+ expect(crudSubscribeResolvable[0].promise.cancel).toHaveBeenCalledTimes(1);
384
409
  expect(returnValue).toBe(true);
385
410
  expect(listSubscription.state.subscribed).toBe(false);
386
411
  });
387
412
  it("manual list instance", async function () {
388
- globalUnsubscribe.mockImplementation(() => Promise.resolve(true));
389
- const defaultListArgs = reactive({
413
+ crudSubscribeResolvable[0].promise.cancel.mockImplementation(() => Promise.resolve(true));
414
+ const listArgs = reactive({
390
415
  user: 1,
391
416
  });
392
- const defaultRetrieveArgs = reactive({
417
+ const retrieveArgs = reactive({
393
418
  fields: fields,
394
419
  });
395
420
  const listInstance = useListInstance({
396
- defaultListArgs,
397
- defaultRetrieveArgs,
421
+ listArgs,
422
+ retrieveArgs,
398
423
  });
399
424
  const listSubscription = useListSubscription({
400
425
  listInstance,
401
426
  });
402
427
  expect(listSubscription.listInstance).toBe(listInstance);
403
- await listSubscription.subscribe();
404
- expect(globalSubscribe).toHaveBeenCalledWith({
428
+ listSubscription.subscribe();
429
+ await nextTick();
430
+ await flushPromises();
431
+ expect(crudSubscribe).toHaveBeenCalledWith({
405
432
  crudArgs: { stream: "test_stream" },
406
433
  listArgs: { user: 1 },
407
434
  retrieveArgs: { fields: fields },
408
435
  subscriptionEventCallback: expect.any(Function),
409
436
  });
410
- expect(globalSubscribe).toHaveBeenCalledTimes(1);
437
+ expect(crudSubscribe).toHaveBeenCalledTimes(1);
411
438
 
412
439
  passedSubscriptionEventCallback(
413
440
  {
@@ -448,9 +475,62 @@ describe("use/listSubscription.spec.js", function () {
448
475
  expect(listInstance.state.objects).toEqual({});
449
476
 
450
477
  const returnValue = await listSubscription.unsubscribe();
451
- expect(globalUnsubscribe).toHaveBeenCalledWith();
452
- expect(globalUnsubscribe).toHaveBeenCalledTimes(1);
478
+ expect(crudSubscribeResolvable[0].promise.cancel).toHaveBeenCalledWith();
479
+ expect(crudSubscribeResolvable[0].promise.cancel).toHaveBeenCalledTimes(1);
480
+ expect(returnValue).toBe(true);
481
+ expect(listSubscription.state.subscribed).toBe(false);
482
+ });
483
+ it("subscribe resubscribes when listArgs or retrieveArgs change", async function () {
484
+ crudSubscribeResolvable[0].promise.cancel.mockImplementation(() => Promise.resolve(true));
485
+ const listArgs = reactive({
486
+ user: 1,
487
+ });
488
+ const retrieveArgs = reactive({
489
+ fields: fields,
490
+ });
491
+ const listSubscription = useListSubscription({
492
+ listArgs,
493
+ retrieveArgs,
494
+ });
495
+ listSubscription.subscribe();
496
+ await nextTick();
497
+ await flushPromises();
498
+ expect(crudSubscribe).toHaveBeenCalledWith({
499
+ crudArgs: { stream: "test_stream" },
500
+ listArgs: { user: 1 },
501
+ retrieveArgs: { fields: fields },
502
+ subscriptionEventCallback: expect.any(Function),
503
+ });
504
+ expect(crudSubscribe).toHaveBeenCalledTimes(1);
505
+ expect(listSubscription.state.subscribed).toBe(true);
506
+ await nextTick();
507
+ await flushPromises();
508
+ await crudSubscribeResolvable[0].resolve();
509
+ await crudListResolvable[0].resolve();
510
+ await nextTick();
511
+ await flushPromises();
512
+ listArgs.user = 2;
513
+ retrieveArgs.fields = ["name"];
514
+ await nextTick();
515
+ await flushPromises();
516
+ await doAwaitTimeout(1500);
517
+ expect(crudSubscribeResolvable[0].promise.cancel).toHaveBeenCalledWith();
518
+ expect(crudSubscribeResolvable[0].promise.cancel).toHaveBeenCalledTimes(1);
519
+ expect(crudSubscribe).toHaveBeenCalledWith({
520
+ crudArgs: { stream: "test_stream" },
521
+ listArgs: { user: 2 },
522
+ retrieveArgs: { fields: ["name"] },
523
+ subscriptionEventCallback: expect.any(Function),
524
+ });
525
+ expect(crudSubscribe).toHaveBeenCalledTimes(2);
526
+
527
+ const returnValue = listSubscription.unsubscribe();
453
528
  expect(returnValue).toBe(true);
529
+ await nextTick();
530
+ await flushPromises();
531
+ await doAwaitTimeout(1500);
532
+ expect(crudSubscribeResolvable[1].promise.cancel).toHaveBeenCalledWith();
533
+ expect(crudSubscribeResolvable[1].promise.cancel).toHaveBeenCalledTimes(1);
454
534
  expect(listSubscription.state.subscribed).toBe(false);
455
535
  });
456
536
  });