@arrai-innovations/reactive-helpers 9.0.2 → 10.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.
@@ -0,0 +1,37 @@
1
+ import { addOrUpdateReactiveObject } from "../utils/assignReactiveObject.js";
2
+ import cloneDeep from "lodash-es/cloneDeep.js";
3
+ import isFunction from "lodash-es/isFunction.js";
4
+ import { isReactive, toRef } from "vue";
5
+
6
+ const defaultCrud = {
7
+ args: {},
8
+ list: undefined,
9
+ subscribe: undefined,
10
+ };
11
+
12
+ export const setListCrud = ({ list, subscribe, args = {}, ...rest }) => {
13
+ defaultCrud.list = list;
14
+ defaultCrud.subscribe = subscribe;
15
+ // defensive cloning
16
+ Object.assign(defaultCrud.args, cloneDeep(args));
17
+ if (Object.keys(rest).length) {
18
+ throw new Error(`Unknown key(s) passed to setListCrud: ${Object.keys(rest).join(", ")}`);
19
+ }
20
+ };
21
+
22
+ export const getListCrud = (reactiveCrud, { props, functions } = {}) => {
23
+ // don't mutate the default crud
24
+ Object.assign(reactiveCrud, cloneDeep(defaultCrud));
25
+ if (props?.crudArgs) {
26
+ addOrUpdateReactiveObject(reactiveCrud.args, props.crudArgs);
27
+ }
28
+ if (functions) {
29
+ for (const [key, value] of Object.entries(functions)) {
30
+ if (isFunction(value) && key in reactiveCrud) {
31
+ reactiveCrud[key] = isReactive(functions) ? toRef(functions, key) : value;
32
+ } else {
33
+ throw Error(`Invalid function "${key}" for getListCrud: invalid key or not a function.`);
34
+ }
35
+ }
36
+ }
37
+ };
@@ -0,0 +1,45 @@
1
+ import { addOrUpdateReactiveObject } from "../utils/assignReactiveObject.js";
2
+ import cloneDeep from "lodash-es/cloneDeep.js";
3
+ import isFunction from "lodash-es/isFunction.js";
4
+ import { isReactive, toRef } from "vue";
5
+
6
+ const defaultCrud = {
7
+ args: {},
8
+ retrieve: undefined,
9
+ create: undefined,
10
+ update: undefined,
11
+ patch: undefined,
12
+ delete: undefined,
13
+ subscribe: undefined,
14
+ };
15
+
16
+ export const setObjectCrud = ({ retrieve, create, update, patch, delete: deleteFn, subscribe, args = {}, ...rest }) => {
17
+ defaultCrud.retrieve = retrieve;
18
+ defaultCrud.create = create;
19
+ defaultCrud.update = update;
20
+ defaultCrud.patch = patch;
21
+ defaultCrud.delete = deleteFn;
22
+ defaultCrud.subscribe = subscribe;
23
+ // defensive cloning
24
+ Object.assign(defaultCrud.args, cloneDeep(args));
25
+ if (Object.keys(rest).length) {
26
+ throw new Error(`Unknown key(s) passed to setObjectCrud: ${Object.keys(rest).join(", ")}`);
27
+ }
28
+ };
29
+
30
+ export const getObjectCrud = (reactiveCrud, { props, functions } = {}) => {
31
+ // don't mutate the default crud
32
+ Object.assign(reactiveCrud, cloneDeep(defaultCrud));
33
+ if (props?.crudArgs) {
34
+ addOrUpdateReactiveObject(reactiveCrud.args, props.crudArgs);
35
+ }
36
+ if (functions) {
37
+ for (const [key, value] of Object.entries(functions)) {
38
+ if (isFunction(value) && key in reactiveCrud) {
39
+ reactiveCrud[key] = isReactive(functions) ? toRef(functions, key) : value;
40
+ } else {
41
+ throw Error(`Invalid function "${key}" for getObjectCrud: invalid key or not a function.`);
42
+ }
43
+ }
44
+ }
45
+ };
package/docs.md CHANGED
@@ -23,23 +23,31 @@
23
23
 
24
24
  ## Functions
25
25
 
26
- | Name | Description |
27
- | ---------------------------- | ----------- |
28
- | [useCombineClasses(classes)] |
26
+ | Name | Description |
27
+ | ------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
28
+ | [useCombineClasses(classes)] |
29
+ | [useListInstance(options)] | `useListInstance` is a Vue composition function that manages a list of objects. It has the ability to retrieve the list from an implementation, or subscribe to updates from an implementation. It tracks the objects in the list, and their added order. |
30
+ | [useListSubscription(options)] | `useListSubscription` creates a reactive object that manages a list of objects, as returned by `useListInstance`, causing the list to be re-fetched as needed and listening for updates to the list. |
29
31
 
30
32
  ## Typedefs
31
33
 
32
- | Name | Description |
33
- | -------------------- | ------------------------------------------------------------------------------------------------------ |
34
- | [CSSValue] | A string representing a CSS class or a space-separated list of CSS classes. |
35
- | [CSSObject] | A CSS object where keys are CSS classes and values are booleans indicating whether to apply the class. |
36
- | [CSSClasses] | A mixed array containing multiple ways of specifying CSS classes. |
37
- | [CSSStringOrObject] | The amalgamated classes as returned by `objectifyClasses` & `combineClasses`. |
38
- | [CSSValue] | A string representing a CSS class or a space-separated list of CSS classes. |
39
- | [CSSObject] | A CSS object where keys are CSS classes and values are booleans indicating whether to apply the class. |
40
- | [CSSClasses] | A mixed array containing multiple ways of specifying CSS classes. |
41
- | [CSSClassesWithRefs] | A mixed array containing multiple ways of specifying CSS classes. |
42
- | [CSSStringOrObject] | A CSS object or a space-separated list of CSS classes. |
34
+ | Name | Description |
35
+ | ------------------------- | ------------------------------------------------------------------------------------------------------ |
36
+ | [CSSValue] | A string representing a CSS class or a space-separated list of CSS classes. |
37
+ | [CSSObject] | A CSS object where keys are CSS classes and values are booleans indicating whether to apply the class. |
38
+ | [CSSClasses] | A mixed array containing multiple ways of specifying CSS classes. |
39
+ | [CSSStringOrObject] | The amalgamated classes as returned by `objectifyClasses` & `combineClasses`. |
40
+ | [ListInstanceOptions] | The configuration options used to create a list instance. |
41
+ | [ListInstanceState] | A reactive object that manages a list of objects, as returned by `useListInstance`. |
42
+ | [ListInstance] |
43
+ | [ListSubscriptionOptions] | The configuration options used to create a list subscription. |
44
+ | [ListSubscriptionState] | A reactive object that manages a list of objects, as returned by `useListInstance`. |
45
+ | [ListSubscription] |
46
+ | [CSSValue] | A string representing a CSS class or a space-separated list of CSS classes. |
47
+ | [CSSObject] | A CSS object where keys are CSS classes and values are booleans indicating whether to apply the class. |
48
+ | [CSSClasses] | A mixed array containing multiple ways of specifying CSS classes. |
49
+ | [CSSClassesWithRefs] | A mixed array containing multiple ways of specifying CSS classes. |
50
+ | [CSSStringOrObject] | A CSS object or a space-separated list of CSS classes. |
43
51
 
44
52
  ## utils/assignReactiveObject
45
53
 
@@ -427,6 +435,31 @@ Remove empty objects and undefined values from arrays in a mixed object array tr
427
435
  | ------- | -------------- | -------------------------------------------------------------------------------------------- |
428
436
  | classes | [`CSSClasses`] | A mixed array containing multiple ways of specifying CSS classes. Non-ref values are cloned. |
429
437
 
438
+ ## useListInstance(options)
439
+
440
+ `useListInstance` is a Vue composition function that manages a list of objects.
441
+ It has the ability to retrieve the list from an implementation, or subscribe to updates from an implementation.
442
+ It tracks the objects in the list, and their added order.
443
+
444
+ **Kind**: global function
445
+ **Returns**: [`ListInstance`] - - the list instance
446
+
447
+ | Param | Type | Description |
448
+ | ------- | ----------------------- | -------------------------------------------- |
449
+ | options | [`ListInstanceOptions`] | the options used to create the list instance |
450
+
451
+ ## useListSubscription(options)
452
+
453
+ `useListSubscription` creates a reactive object that manages a list of objects, as returned by `useListInstance`,
454
+ causing the list to be re-fetched as needed and listening for updates to the list.
455
+
456
+ **Kind**: global function
457
+ **Returns**: ListSubscription
458
+
459
+ | Param | Type |
460
+ | ------- | --------------------------- |
461
+ | options | [`ListSubscriptionOptions`] |
462
+
430
463
  ## CSSValue
431
464
 
432
465
  A string representing a CSS class or a space-separated list of CSS classes.
@@ -451,6 +484,115 @@ The amalgamated classes as returned by `objectifyClasses` & `combineClasses`.
451
484
 
452
485
  **Kind**: global typedef
453
486
 
487
+ ## ListInstanceOptions
488
+
489
+ The configuration options used to create a list instance.
490
+
491
+ **Kind**: global typedef
492
+ **Properties**
493
+
494
+ | Name | Type | Description |
495
+ | ------------------- | ---------- | ---------------------------------------------------------------------------------------- |
496
+ | props | `object` | props passed to the component |
497
+ | props.retrieveArgs | `object` | the arguments passed to the server |
498
+ | props.listArgs | `object` | the arguments passed to the server |
499
+ | props.crudArgs | `object` | implementation specific arguments |
500
+ | functions | `object` | optional. default implementation are used as set by `setListCrud`. |
501
+ | functions.list | `function` | provide the implementation for the list function |
502
+ | functions.subscribe | `function` | provide the implementation for the subscribe function |
503
+ | keepOldPages | `boolean` | if true, pages will not be cleared when defaultPageCallback is called. default is false. |
504
+
505
+ ## ListInstanceState
506
+
507
+ A reactive object that manages a list of objects, as returned by `useListInstance`.
508
+
509
+ **Kind**: global typedef
510
+ **Properties**
511
+
512
+ | Name | Type | Description |
513
+ | -------------- | ---------- | ------------------------------------------ |
514
+ | crud | `object` | the crud functions |
515
+ | crud.args | `object` | the arguments passed to the crud functions |
516
+ | crud.list | `function` | the list function |
517
+ | crud.subscribe | `function` | the subscribe function |
518
+ | retrieveArgs | `object` | the arguments passed to the server |
519
+ | listArgs | `object` | the arguments passed to the server |
520
+ | objects | `Map` | the objects in the list |
521
+ | loading | `boolean` | true if the list is loading |
522
+ | errored | `boolean` | true if the list has errored |
523
+ | error | `object` | the error object |
524
+ | objectsInOrder | `Array` | the objects in the list in order |
525
+ | order | `Array` | the order of the objects in the list |
526
+ | totalRecords | `number` | the total number of records |
527
+ | totalPages | `number` | the total number of pages |
528
+ | perPage | `number` | the number of records per page |
529
+
530
+ ## ListInstance
531
+
532
+ **Kind**: global typedef
533
+ **Properties**
534
+
535
+ | Name | Type | Description |
536
+ | ------------------- | --------------------- | -------------------------------------------- |
537
+ | list | `function` | subscribe to updates from the implementation |
538
+ | addListObject | `function` | add an object to the list |
539
+ | updateListObject | `function` | update an object in the list |
540
+ | deleteListObject | `function` | delete an object from the list |
541
+ | clearList | `function` | clear the list |
542
+ | clearError | `function` | clear the error |
543
+ | getFakeId | `function` | get a fake id |
544
+ | defaultPageCallback | `function` | the default page callback |
545
+ | pageCallback | `function` | the page callback |
546
+ | state | [`ListInstanceState`] | the list instance state |
547
+ | effectScope | `object` | a Vue effect scope |
548
+
549
+ ## ListSubscriptionOptions
550
+
551
+ The configuration options used to create a list subscription.
552
+
553
+ **Kind**: global typedef
554
+ **Properties**
555
+
556
+ | Name | Type | Description |
557
+ | ------------ | ---------------- | ---------------------------------------------------------------------------------------- |
558
+ | props | `object` | passed on to a created list instance if one is not provided |
559
+ | functions | `object` | passed on to a created list instance if one is not provided |
560
+ | listInstance | [`ListInstance`] | a list instance to use instead of creating one |
561
+ | keepOldPages | `boolean` | if true, pages will not be cleared when defaultPageCallback is called. default is false. |
562
+
563
+ ## ListSubscriptionState
564
+
565
+ A reactive object that manages a list of objects, as returned by `useListInstance`.
566
+
567
+ **Kind**: global typedef
568
+ **Extends**: [`ListInstanceState`]
569
+ **Properties**
570
+
571
+ | Name | Type | Description |
572
+ | ------------------- | --------- | ----------------------------------------------------------------- |
573
+ | subscriptionLoading | `boolean` | true if the subscription is loading |
574
+ | subscriptionErrored | `boolean` | true if the subscription errored |
575
+ | subscriptionError | `Error` | the error that caused the subscription to error |
576
+ | intendToList | `boolean` | true if the list should be fetched, or refetched when args change |
577
+ | intendToSubscribe | `boolean` | true if the list should subscribe for updates |
578
+ | subscribed | `boolean` | true if the subscription is active |
579
+
580
+ ## ListSubscription
581
+
582
+ **Kind**: global typedef
583
+ **Properties**
584
+
585
+ | Name | Type | Description |
586
+ | ------------------ | ------------------------- | -------------------------------------------------------------------------------------- |
587
+ | state | [`ListSubscriptionState`] | the reactive state of the list subscription |
588
+ | listInstance | [`ListInstance`] | the list instance used by the subscription |
589
+ | listIntent | `object` | the useCancelleableIntent object managing if the list should be (re)fetched |
590
+ | subscriptionIntent | `object` | the useCancelleableIntent object managing if the subscription should be (un)subscribed |
591
+ | subscribe | `function` | subscribe to the list |
592
+ | unsubscribe | `function` | unsubscribe from the list |
593
+ | clearError | `function` | clear the subscription error |
594
+ | effectScope | `object` | a Vue effect scope |
595
+
454
596
  ## CSSValue
455
597
 
456
598
  A string representing a CSS class or a space-separated list of CSS classes.
@@ -500,6 +642,12 @@ A CSS object or a space-separated list of CSS classes.
500
642
  [cssobject]: #cssobject
501
643
  [cssclasses]: #cssclasses
502
644
  [cssstringorobject]: #cssstringorobject
645
+ [listinstanceoptions]: #listinstanceoptions
646
+ [listinstancestate]: #listinstancestate
647
+ [listinstance]: #listinstance
648
+ [listsubscriptionoptions]: #listsubscriptionoptions
649
+ [listsubscriptionstate]: #listsubscriptionstate
650
+ [listsubscription]: #listsubscription
503
651
  [cssclasseswithrefs]: #cssclasseswithrefs
504
652
  [~validtargetorsource]: #utilsassignreactiveobjectvalidtargetorsource
505
653
  [`utils/assignreactiveobject`]: #utilsassignreactiveobject
@@ -515,7 +663,14 @@ A CSS object or a space-separated list of CSS classes.
515
663
  [`cssclasses`]: #cssclasses
516
664
  [`cssstringorobject`]: #cssstringorobject
517
665
  [`cssclasseswithrefs`]: #cssclasseswithrefs
666
+ [`listinstance`]: #listinstance
667
+ [`listinstanceoptions`]: #listinstanceoptions
668
+ [`listsubscriptionoptions`]: #listsubscriptionoptions
669
+ [`listinstancestate`]: #listinstancestate
670
+ [`listsubscriptionstate`]: #listsubscriptionstate
518
671
  [usecombineclasses(classes)]: #usecombineclassesclasses
672
+ [uselistinstance(options)]: #uselistinstanceoptions
673
+ [uselistsubscription(options)]: #uselistsubscriptionoptions
519
674
  [~addreactiveobject(target, source, \[exclude\], \[addedkeys\])]: #utilsassignreactiveobjectaddreactiveobjecttarget-source-exclude-addedkeys
520
675
  [~updatereactiveobject(target, source, \[exclude\], \[samekeys\])]: #utilsassignreactiveobjectupdatereactiveobjecttarget-source-exclude-samekeys
521
676
  [~addorupdatereactiveobject(target, source, \[exclude\], \[addedkeys\], \[samekeys\])]: #utilsassignreactiveobjectaddorupdatereactiveobjecttarget-source-exclude-addedkeys-samekeys
package/index.js CHANGED
@@ -1,2 +1,3 @@
1
+ export * from "./config/index.js";
1
2
  export * from "./use/index.js";
2
3
  export * from "./utils/index.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arrai-innovations/reactive-helpers",
3
- "version": "9.0.2",
3
+ "version": "10.0.0",
4
4
  "description": "VueJS 3 utility composition functions to help manipulate objects and lists.",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -0,0 +1,18 @@
1
+ import * as config from "../../../config/index.js";
2
+ import fs from "fs/promises";
3
+
4
+ describe("config/index.js", function () {
5
+ it("should export everything exported by individual files", async function () {
6
+ const files = await fs.readdir("./config");
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(`../../../config/${module}`);
11
+ exportedKeys.push(...Object.keys(moduleExports).filter((key) => key !== "default"));
12
+ }
13
+ const useKeys = Object.keys(config);
14
+ useKeys.sort();
15
+ exportedKeys.sort();
16
+ expect(useKeys).toEqual(exportedKeys);
17
+ });
18
+ });
@@ -0,0 +1,107 @@
1
+ import cloneDeep from "lodash-es/cloneDeep.js";
2
+ import { reactive } from "vue";
3
+
4
+ describe("config/listCrud.js", () => {
5
+ describe("get & set", () => {
6
+ let setListCrud, getListCrud;
7
+ beforeAll(async () => {
8
+ const listCrudModule = await import("../../../config/listCrud.js");
9
+ setListCrud = listCrudModule.setListCrud;
10
+ getListCrud = listCrudModule.getListCrud;
11
+ });
12
+ it("should not die getting crud before setting", () => {
13
+ const reactiveCrud = reactive({});
14
+ expect(() => getListCrud(reactiveCrud)).not.toThrow();
15
+ });
16
+ it("should set and get the default crud, avoiding mutation", () => {
17
+ const crud = {
18
+ list: () => 1,
19
+ subscribe: () => 2,
20
+ args: {
21
+ test: "test",
22
+ },
23
+ };
24
+ expect(() => setListCrud(crud)).not.toThrow();
25
+
26
+ const retrievedCrud = reactive({});
27
+ expect(() => getListCrud(retrievedCrud)).not.toThrow();
28
+ expect(new Set(Object.keys(retrievedCrud))).toEqual(new Set(["list", "subscribe", "args"]));
29
+ expect(retrievedCrud.args).not.toBe(crud.args);
30
+ expect(retrievedCrud.args).toEqual(crud.args);
31
+ expect(retrievedCrud.list).toBe(crud.list);
32
+ expect(retrievedCrud.subscribe).toBe(crud.subscribe);
33
+
34
+ const originalCrud = cloneDeep(crud);
35
+ crud.args.test = "test2";
36
+ crud.list = () => 3;
37
+ crud.subscribe = () => 4;
38
+ expect(retrievedCrud.args.test).toBe(originalCrud.args.test);
39
+ expect(retrievedCrud.list).toBe(originalCrud.list);
40
+ expect(retrievedCrud.subscribe).toBe(originalCrud.subscribe);
41
+ });
42
+ it("should die if an unknown function is passed", () => {
43
+ expect(() =>
44
+ setListCrud({ list: () => 1, subscribe: () => 2, args: { test: "test" }, unknown: () => 3 })
45
+ ).toThrow("Unknown key(s) passed to setListCrud: unknown");
46
+ });
47
+ it("should customize via getListCrud", () => {
48
+ const defaultCrud = {
49
+ list: () => 1,
50
+ subscribe: () => 2,
51
+ args: {
52
+ test: "test",
53
+ },
54
+ };
55
+ const customCrudArgs = {
56
+ props: reactive({
57
+ crudArgs: {
58
+ test: "test2",
59
+ },
60
+ }),
61
+ functions: {
62
+ list: () => 3,
63
+ subscribe: () => 4,
64
+ },
65
+ };
66
+ const expectedCustomCrud = {
67
+ list: customCrudArgs.functions.list,
68
+ subscribe: customCrudArgs.functions.subscribe,
69
+ args: customCrudArgs.props.crudArgs,
70
+ };
71
+ expect(() => setListCrud(defaultCrud)).not.toThrow();
72
+ const defaultRetrievedCrud = reactive({});
73
+ getListCrud(defaultRetrievedCrud);
74
+ expect(defaultRetrievedCrud).toEqual(defaultCrud);
75
+ expect(defaultRetrievedCrud).not.toEqual(expectedCustomCrud);
76
+ const customRetrievedCrud = reactive({});
77
+ getListCrud(customRetrievedCrud, customCrudArgs);
78
+ expect(customRetrievedCrud).toEqual(expectedCustomCrud);
79
+ expect(customRetrievedCrud).not.toEqual(defaultCrud);
80
+ expect(defaultRetrievedCrud).toEqual(defaultCrud);
81
+ expect(defaultRetrievedCrud).not.toEqual(expectedCustomCrud);
82
+ });
83
+ it("should throw if passed functions object that has values that are not functions", () => {
84
+ const retrievedCrud = reactive({});
85
+ expect(() =>
86
+ getListCrud(retrievedCrud, {
87
+ functions: {
88
+ list: () => 1,
89
+ subscribe: true,
90
+ },
91
+ })
92
+ ).toThrow('Invalid function "subscribe" for getListCrud: invalid key or not a function.');
93
+ });
94
+ it("should throw if passed unexpected functions", () => {
95
+ const retrievedCrud = reactive({});
96
+ expect(() =>
97
+ getListCrud(retrievedCrud, {
98
+ functions: {
99
+ list: () => 1,
100
+ subscribe: () => 2,
101
+ unknown: () => 3,
102
+ },
103
+ })
104
+ ).toThrow('Invalid function "unknown" for getListCrud: invalid key or not a function.');
105
+ });
106
+ });
107
+ });
@@ -0,0 +1,155 @@
1
+ import cloneDeep from "lodash-es/cloneDeep.js";
2
+ import { reactive } from "vue";
3
+
4
+ describe("config/objectCrud.js", () => {
5
+ describe("get & set", () => {
6
+ let setObjectCrud, getObjectCrud;
7
+ beforeAll(async () => {
8
+ const objectCrudModule = await import("../../../config/objectCrud.js");
9
+ setObjectCrud = objectCrudModule.setObjectCrud;
10
+ getObjectCrud = objectCrudModule.getObjectCrud;
11
+ });
12
+ it("should not die getting crud before setting", () => {
13
+ const reactiveCrud = reactive({});
14
+ expect(() => getObjectCrud(reactiveCrud)).not.toThrow();
15
+ });
16
+ it("should set and get the default crud, avoiding mutation", () => {
17
+ const crud = {
18
+ retrieve: () => 1,
19
+ create: () => 2,
20
+ update: () => 3,
21
+ patch: () => 4,
22
+ delete: () => 5,
23
+ subscribe: () => 6,
24
+ args: {
25
+ test: "test",
26
+ },
27
+ };
28
+ expect(() => setObjectCrud(crud)).not.toThrow();
29
+
30
+ const retrievedCrud = reactive({});
31
+ expect(() => getObjectCrud(retrievedCrud)).not.toThrow();
32
+ expect(new Set(Object.keys(retrievedCrud))).toEqual(
33
+ new Set(["retrieve", "create", "update", "patch", "delete", "subscribe", "args"])
34
+ );
35
+ expect(retrievedCrud.args).not.toBe(crud.args);
36
+ expect(retrievedCrud.args).toEqual(crud.args);
37
+ expect(retrievedCrud.retrieve).toBe(crud.retrieve);
38
+ expect(retrievedCrud.create).toBe(crud.create);
39
+ expect(retrievedCrud.update).toBe(crud.update);
40
+ expect(retrievedCrud.patch).toBe(crud.patch);
41
+ expect(retrievedCrud.delete).toBe(crud.delete);
42
+ expect(retrievedCrud.subscribe).toBe(crud.subscribe);
43
+
44
+ const originalCrud = cloneDeep(crud);
45
+ crud.args.test = "test2";
46
+ crud.retrieve = () => 7;
47
+ crud.create = () => 8;
48
+ crud.update = () => 9;
49
+ crud.patch = () => 10;
50
+ crud.delete = () => 11;
51
+ crud.subscribe = () => 12;
52
+ expect(retrievedCrud.args.test).toBe(originalCrud.args.test);
53
+ expect(retrievedCrud.retrieve).toBe(originalCrud.retrieve);
54
+ expect(retrievedCrud.create).toBe(originalCrud.create);
55
+ expect(retrievedCrud.update).toBe(originalCrud.update);
56
+ expect(retrievedCrud.patch).toBe(originalCrud.patch);
57
+ expect(retrievedCrud.delete).toBe(originalCrud.delete);
58
+ expect(retrievedCrud.subscribe).toBe(originalCrud.subscribe);
59
+ });
60
+ it("should customize via getObjectCrud", () => {
61
+ const defaultCrud = {
62
+ retrieve: () => 1,
63
+ create: () => 2,
64
+ update: () => 3,
65
+ patch: () => 4,
66
+ delete: () => 5,
67
+ subscribe: () => 6,
68
+ args: {
69
+ test: "test",
70
+ },
71
+ };
72
+ const customCrudArgs = {
73
+ props: reactive({
74
+ crudArgs: {
75
+ test: "test2",
76
+ },
77
+ }),
78
+ functions: {
79
+ retrieve: () => 7,
80
+ create: () => 8,
81
+ update: () => 9,
82
+ patch: () => 10,
83
+ delete: () => 11,
84
+ subscribe: () => 12,
85
+ },
86
+ };
87
+ const expectedCustomCrud = {
88
+ retrieve: customCrudArgs.functions.retrieve,
89
+ create: customCrudArgs.functions.create,
90
+ update: customCrudArgs.functions.update,
91
+ patch: customCrudArgs.functions.patch,
92
+ delete: customCrudArgs.functions.delete,
93
+ subscribe: customCrudArgs.functions.subscribe,
94
+ args: customCrudArgs.props.crudArgs,
95
+ };
96
+ expect(() => setObjectCrud(defaultCrud)).not.toThrow();
97
+ const defaultRetrievedCrud = reactive({});
98
+ getObjectCrud(defaultRetrievedCrud);
99
+ expect(defaultRetrievedCrud).toEqual(defaultCrud);
100
+ expect(defaultRetrievedCrud).not.toEqual(expectedCustomCrud);
101
+ const customRetrievedCrud = reactive({});
102
+ getObjectCrud(customRetrievedCrud, customCrudArgs);
103
+ expect(customRetrievedCrud).toEqual(expectedCustomCrud);
104
+ expect(customRetrievedCrud).not.toEqual(defaultCrud);
105
+ expect(defaultRetrievedCrud).toEqual(defaultCrud);
106
+ expect(defaultRetrievedCrud).not.toEqual(expectedCustomCrud);
107
+ });
108
+ it("should die if an unknown function is passed", () => {
109
+ expect(() =>
110
+ setObjectCrud({
111
+ retrieve: () => 1,
112
+ create: () => 2,
113
+ update: () => 3,
114
+ patch: () => 4,
115
+ delete: () => 5,
116
+ subscribe: () => 6,
117
+ args: { test: "test" },
118
+ unknown: () => 7,
119
+ })
120
+ ).toThrow("Unknown key(s) passed to setObjectCrud: unknown");
121
+ });
122
+
123
+ it("should throw if passed functions object that has values that are not functions", () => {
124
+ const retrievedCrud = reactive({});
125
+ expect(() =>
126
+ getObjectCrud(retrievedCrud, {
127
+ functions: {
128
+ retrieve: () => 1,
129
+ create: () => 2,
130
+ update: 3,
131
+ patch: () => 4,
132
+ delete: () => 5,
133
+ subscribe: () => 6,
134
+ },
135
+ })
136
+ ).toThrow('Invalid function "update" for getObjectCrud: invalid key or not a function.');
137
+ });
138
+ it("should throw if passed unexpected functions", () => {
139
+ const retrievedCrud = reactive({});
140
+ expect(() =>
141
+ getObjectCrud(retrievedCrud, {
142
+ functions: {
143
+ retrieve: () => 1,
144
+ create: () => 2,
145
+ update: () => 3,
146
+ patch: () => 4,
147
+ delete: () => 5,
148
+ subscribe: () => 6,
149
+ unknown: () => 7,
150
+ },
151
+ })
152
+ ).toThrow('Invalid function "unknown" for getObjectCrud: invalid key or not a function.');
153
+ });
154
+ });
155
+ });
@@ -1,4 +1,4 @@
1
- import { useCancellableIntent } from "../../../use/index.js";
1
+ import { useCancellableIntent } from "../../../use/cancellableIntent.js";
2
2
  import { CancellableResolvable } from "../crudPromise.js";
3
3
  import flushPromises from "flush-promises";
4
4
  import { nextTick, reactive, ref } from "vue";
@@ -9,12 +9,13 @@ afterAll(() => {
9
9
  });
10
10
 
11
11
  const fields = ["id", "__str__", "name"];
12
- describe.skip("use/listInstance.spec.js", function () {
12
+ describe("use/listInstance.spec.js", function () {
13
13
  let useListInstance, ListError, useListInstances, globalList;
14
14
  beforeEach(async () => {
15
+ const listCrud = await import("../../../config/listCrud.js");
15
16
  const imported = await import("../../../use/listInstance");
16
17
  globalList = vi.fn();
17
- imported.setListInstanceCrud({
18
+ listCrud.setListCrud({
18
19
  list: globalList,
19
20
  args: { stream: "test_stream" },
20
21
  });
@@ -143,8 +144,8 @@ describe.skip("use/listInstance.spec.js", function () {
143
144
  expect({ ...listInstance.state.objects }).toEqual({});
144
145
  globalList.mockImplementation(() => new Promise(() => {}));
145
146
 
146
- listInstance.list();
147
- await expect(listInstance.list()).rejects.toThrow(ListError);
147
+ const firstPromise = listInstance.list();
148
+ expect(listInstance.list()).toBe(firstPromise);
148
149
 
149
150
  expect(globalList).toHaveBeenCalledWith({
150
151
  crudArgs: { stream: "test_stream" },