@wordpress/abilities 0.2.1-next.8b30e05b0.0 → 0.3.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 (72) hide show
  1. package/CHANGELOG.md +2 -0
  2. package/README.md +32 -40
  3. package/build/api.js +19 -66
  4. package/build/api.js.map +3 -3
  5. package/build/index.js.map +2 -2
  6. package/build/store/actions.js +41 -31
  7. package/build/store/actions.js.map +2 -2
  8. package/build/store/constants.js +6 -15
  9. package/build/store/constants.js.map +2 -2
  10. package/build/store/index.js +1 -27
  11. package/build/store/index.js.map +3 -3
  12. package/build/store/reducer.js +2 -22
  13. package/build/store/reducer.js.map +2 -2
  14. package/build/store/selectors.js +1 -1
  15. package/build/store/selectors.js.map +2 -2
  16. package/build/store/types.js +19 -0
  17. package/build/store/types.js.map +7 -0
  18. package/build/types.js.map +1 -1
  19. package/build-module/api.js +20 -57
  20. package/build-module/api.js.map +2 -2
  21. package/build-module/index.js.map +2 -2
  22. package/build-module/store/actions.js +43 -32
  23. package/build-module/store/actions.js.map +2 -2
  24. package/build-module/store/constants.js +4 -10
  25. package/build-module/store/constants.js.map +2 -2
  26. package/build-module/store/index.js +3 -34
  27. package/build-module/store/index.js.map +2 -2
  28. package/build-module/store/reducer.js +2 -24
  29. package/build-module/store/reducer.js.map +2 -2
  30. package/build-module/store/selectors.js +1 -1
  31. package/build-module/store/selectors.js.map +2 -2
  32. package/build-module/store/types.js +1 -0
  33. package/build-module/store/types.js.map +7 -0
  34. package/build-types/api.d.ts +16 -23
  35. package/build-types/api.d.ts.map +1 -1
  36. package/build-types/index.d.ts +1 -1
  37. package/build-types/index.d.ts.map +1 -1
  38. package/build-types/store/actions.d.ts +5 -27
  39. package/build-types/store/actions.d.ts.map +1 -1
  40. package/build-types/store/constants.d.ts +2 -5
  41. package/build-types/store/constants.d.ts.map +1 -1
  42. package/build-types/store/index.d.ts.map +1 -1
  43. package/build-types/store/reducer.d.ts +21 -11
  44. package/build-types/store/reducer.d.ts.map +1 -1
  45. package/build-types/store/selectors.d.ts +2 -1
  46. package/build-types/store/selectors.d.ts.map +1 -1
  47. package/build-types/store/types.d.ts +18 -0
  48. package/build-types/store/types.d.ts.map +1 -0
  49. package/build-types/types.d.ts +14 -24
  50. package/build-types/types.d.ts.map +1 -1
  51. package/package.json +4 -7
  52. package/src/api.ts +43 -123
  53. package/src/index.ts +0 -1
  54. package/src/store/actions.ts +61 -46
  55. package/src/store/constants.ts +4 -5
  56. package/src/store/index.ts +2 -34
  57. package/src/store/reducer.ts +27 -33
  58. package/src/store/selectors.ts +3 -7
  59. package/src/store/tests/actions.test.ts +100 -244
  60. package/src/store/tests/reducer.test.ts +0 -451
  61. package/src/store/tests/selectors.test.ts +1 -1
  62. package/src/store/types.ts +19 -0
  63. package/src/tests/api.test.ts +112 -158
  64. package/src/types.ts +14 -26
  65. package/build/store/resolvers.js +0 -107
  66. package/build/store/resolvers.js.map +0 -7
  67. package/build-module/store/resolvers.js +0 -84
  68. package/build-module/store/resolvers.js.map +0 -7
  69. package/build-types/store/resolvers.d.ts +0 -47
  70. package/build-types/store/resolvers.d.ts.map +0 -1
  71. package/src/store/resolvers.ts +0 -151
  72. package/src/store/tests/resolvers.test.ts +0 -642
@@ -7,10 +7,8 @@
7
7
  */
8
8
  import reducer from '../reducer';
9
9
  import {
10
- RECEIVE_ABILITIES,
11
10
  REGISTER_ABILITY,
12
11
  UNREGISTER_ABILITY,
13
- RECEIVE_CATEGORIES,
14
12
  REGISTER_ABILITY_CATEGORY,
15
13
  UNREGISTER_ABILITY_CATEGORY,
16
14
  } from '../constants';
@@ -19,158 +17,6 @@ describe( 'Store Reducer', () => {
19
17
  describe( 'abilitiesByName', () => {
20
18
  const defaultState = {};
21
19
 
22
- describe( 'RECEIVE_ABILITIES', () => {
23
- it( 'should add abilities to the state', () => {
24
- const abilities = [
25
- {
26
- name: 'test/ability1',
27
- label: 'Test Ability 1',
28
- description: 'First test ability',
29
- input_schema: { type: 'object' },
30
- },
31
- {
32
- name: 'test/ability2',
33
- label: 'Test Ability 2',
34
- description: 'Second test ability',
35
- output_schema: { type: 'object' },
36
- },
37
- ];
38
-
39
- const action = {
40
- type: RECEIVE_ABILITIES,
41
- abilities,
42
- };
43
-
44
- const state = reducer(
45
- { abilitiesByName: defaultState },
46
- action
47
- );
48
-
49
- expect( state.abilitiesByName ).toHaveProperty(
50
- 'test/ability1'
51
- );
52
- expect( state.abilitiesByName ).toHaveProperty(
53
- 'test/ability2'
54
- );
55
- expect( state.abilitiesByName[ 'test/ability1' ].label ).toBe(
56
- 'Test Ability 1'
57
- );
58
- expect( state.abilitiesByName[ 'test/ability2' ].label ).toBe(
59
- 'Test Ability 2'
60
- );
61
- } );
62
-
63
- it( 'should filter out _links from server responses', () => {
64
- const abilities = [
65
- {
66
- name: 'test/ability',
67
- label: 'Test Ability',
68
- description: 'Test ability with links',
69
- _links: {
70
- self: {
71
- href: '/wp-abilities/v1/abilities/test/ability',
72
- },
73
- collection: { href: '/wp-abilities/v1/abilities' },
74
- },
75
- },
76
- ];
77
-
78
- const action = {
79
- type: RECEIVE_ABILITIES,
80
- abilities,
81
- };
82
-
83
- const state = reducer(
84
- { abilitiesByName: defaultState },
85
- action
86
- );
87
-
88
- expect(
89
- state.abilitiesByName[ 'test/ability' ]
90
- ).not.toHaveProperty( '_links' );
91
- expect( state.abilitiesByName[ 'test/ability' ].name ).toBe(
92
- 'test/ability'
93
- );
94
- expect( state.abilitiesByName[ 'test/ability' ].label ).toBe(
95
- 'Test Ability'
96
- );
97
- } );
98
-
99
- it( 'should filter out _embedded from server responses', () => {
100
- const abilities = [
101
- {
102
- name: 'test/ability',
103
- label: 'Test Ability',
104
- description: 'Test ability with embedded',
105
- _embedded: {
106
- author: { id: 1, name: 'Admin' },
107
- },
108
- },
109
- ];
110
-
111
- const action = {
112
- type: RECEIVE_ABILITIES,
113
- abilities,
114
- };
115
-
116
- const state = reducer(
117
- { abilitiesByName: defaultState },
118
- action
119
- );
120
-
121
- expect(
122
- state.abilitiesByName[ 'test/ability' ]
123
- ).not.toHaveProperty( '_embedded' );
124
- } );
125
-
126
- it( 'should preserve all valid ability properties', () => {
127
- const abilities = [
128
- {
129
- name: 'test/ability',
130
- label: 'Test Ability',
131
- description: 'Full test ability.',
132
- input_schema: { type: 'object' },
133
- output_schema: { type: 'object' },
134
- meta: {
135
- category: 'test',
136
- },
137
- callback: () => Promise.resolve( {} ),
138
- permissionCallback: () => true,
139
- // Extra properties that should be filtered out
140
- _links: { self: { href: '/test' } },
141
- _embedded: { test: 'value' },
142
- extra_field: 'should be removed',
143
- },
144
- ];
145
-
146
- const action = {
147
- type: RECEIVE_ABILITIES,
148
- abilities,
149
- };
150
-
151
- const state = reducer(
152
- { abilitiesByName: defaultState },
153
- action
154
- );
155
- const ability = state.abilitiesByName[ 'test/ability' ];
156
-
157
- // Should have valid properties
158
- expect( ability.name ).toBe( 'test/ability' );
159
- expect( ability.label ).toBe( 'Test Ability' );
160
- expect( ability.description ).toBe( 'Full test ability.' );
161
- expect( ability.input_schema ).toEqual( { type: 'object' } );
162
- expect( ability.output_schema ).toEqual( { type: 'object' } );
163
- expect( ability.meta ).toEqual( { category: 'test' } );
164
- expect( ability.callback ).toBeDefined();
165
- expect( ability.permissionCallback ).toBeDefined();
166
-
167
- // Should NOT have invalid properties
168
- expect( ability ).not.toHaveProperty( '_links' );
169
- expect( ability ).not.toHaveProperty( '_embedded' );
170
- expect( ability ).not.toHaveProperty( 'extra_field' );
171
- } );
172
- } );
173
-
174
20
  describe( 'REGISTER_ABILITY', () => {
175
21
  it( 'should add ability to the state', () => {
176
22
  const ability = {
@@ -327,20 +173,6 @@ describe( 'Store Reducer', () => {
327
173
  expect( state.abilitiesByName ).toEqual( initialState );
328
174
  } );
329
175
 
330
- it( 'should handle undefined abilities in RECEIVE_ABILITIES', () => {
331
- const action = {
332
- type: RECEIVE_ABILITIES,
333
- abilities: undefined,
334
- };
335
-
336
- const state = reducer(
337
- { abilitiesByName: defaultState },
338
- action
339
- );
340
-
341
- expect( state.abilitiesByName ).toEqual( defaultState );
342
- } );
343
-
344
176
  it( 'should handle undefined ability in REGISTER_ABILITY', () => {
345
177
  const action = {
346
178
  type: REGISTER_ABILITY,
@@ -354,295 +186,12 @@ describe( 'Store Reducer', () => {
354
186
 
355
187
  expect( state.abilitiesByName ).toEqual( defaultState );
356
188
  } );
357
-
358
- it( 'should handle undefined properties gracefully', () => {
359
- const abilities = [
360
- {
361
- name: 'test/minimal',
362
- label: 'Minimal',
363
- description:
364
- 'Minimal ability with undefined properties',
365
- input_schema: undefined,
366
- output_schema: undefined,
367
- meta: undefined,
368
- callback: undefined,
369
- permissionCallback: undefined,
370
- },
371
- ];
372
-
373
- const action = {
374
- type: RECEIVE_ABILITIES,
375
- abilities,
376
- };
377
-
378
- const state = reducer(
379
- { abilitiesByName: defaultState },
380
- action
381
- );
382
- const ability = state.abilitiesByName[ 'test/minimal' ];
383
-
384
- expect( ability.name ).toBe( 'test/minimal' );
385
- expect( ability.label ).toBe( 'Minimal' );
386
- expect( ability.description ).toBe(
387
- 'Minimal ability with undefined properties'
388
- );
389
- // Undefined properties should not be present
390
- expect( ability ).not.toHaveProperty( 'input_schema' );
391
- expect( ability ).not.toHaveProperty( 'output_schema' );
392
- expect( ability ).not.toHaveProperty( 'meta' );
393
- expect( ability ).not.toHaveProperty( 'callback' );
394
- expect( ability ).not.toHaveProperty( 'permissionCallback' );
395
- } );
396
189
  } );
397
190
  } );
398
191
 
399
192
  describe( 'categoriesBySlug', () => {
400
193
  const defaultState = {};
401
194
 
402
- describe( 'RECEIVE_CATEGORIES', () => {
403
- it( 'should add categories to the state', () => {
404
- const categories = [
405
- {
406
- slug: 'data-retrieval',
407
- label: 'Data Retrieval',
408
- description: 'Abilities that retrieve data',
409
- },
410
- {
411
- slug: 'user-management',
412
- label: 'User Management',
413
- description: 'Abilities for managing users',
414
- },
415
- ];
416
-
417
- const action = {
418
- type: RECEIVE_CATEGORIES,
419
- categories,
420
- };
421
-
422
- const state = reducer(
423
- { categoriesBySlug: defaultState, abilitiesByName: {} },
424
- action
425
- );
426
-
427
- expect( state.categoriesBySlug ).toHaveProperty(
428
- 'data-retrieval'
429
- );
430
- expect( state.categoriesBySlug ).toHaveProperty(
431
- 'user-management'
432
- );
433
- expect( state.categoriesBySlug[ 'data-retrieval' ].label ).toBe(
434
- 'Data Retrieval'
435
- );
436
- expect(
437
- state.categoriesBySlug[ 'user-management' ].label
438
- ).toBe( 'User Management' );
439
- } );
440
-
441
- it( 'should filter out _links from server responses', () => {
442
- const categories = [
443
- {
444
- slug: 'data-retrieval',
445
- label: 'Data Retrieval',
446
- description: 'Test category with links',
447
- _links: {
448
- self: {
449
- href: '/wp-abilities/v1/categories/data-retrieval',
450
- },
451
- collection: {
452
- href: '/wp-abilities/v1/categories',
453
- },
454
- },
455
- },
456
- ];
457
-
458
- const action = {
459
- type: RECEIVE_CATEGORIES,
460
- categories,
461
- };
462
-
463
- const state = reducer(
464
- { categoriesBySlug: defaultState, abilitiesByName: {} },
465
- action
466
- );
467
-
468
- expect(
469
- state.categoriesBySlug[ 'data-retrieval' ]
470
- ).not.toHaveProperty( '_links' );
471
- expect( state.categoriesBySlug[ 'data-retrieval' ].slug ).toBe(
472
- 'data-retrieval'
473
- );
474
- expect( state.categoriesBySlug[ 'data-retrieval' ].label ).toBe(
475
- 'Data Retrieval'
476
- );
477
- } );
478
-
479
- it( 'should filter out _embedded from server responses', () => {
480
- const categories = [
481
- {
482
- slug: 'data-retrieval',
483
- label: 'Data Retrieval',
484
- description: 'Test category with embedded',
485
- _embedded: {
486
- author: { id: 1, name: 'Admin' },
487
- },
488
- },
489
- ];
490
-
491
- const action = {
492
- type: RECEIVE_CATEGORIES,
493
- categories,
494
- };
495
-
496
- const state = reducer(
497
- { categoriesBySlug: defaultState, abilitiesByName: {} },
498
- action
499
- );
500
-
501
- expect(
502
- state.categoriesBySlug[ 'data-retrieval' ]
503
- ).not.toHaveProperty( '_embedded' );
504
- } );
505
-
506
- it( 'should preserve all valid category properties', () => {
507
- const categories = [
508
- {
509
- slug: 'data-retrieval',
510
- label: 'Data Retrieval',
511
- description: 'Full test category.',
512
- meta: {
513
- priority: 'high',
514
- color: 'blue',
515
- },
516
- // Extra properties that should be filtered out
517
- _links: { self: { href: '/test' } },
518
- _embedded: { test: 'value' },
519
- extra_field: 'should be removed',
520
- },
521
- ];
522
-
523
- const action = {
524
- type: RECEIVE_CATEGORIES,
525
- categories,
526
- };
527
-
528
- const state = reducer(
529
- { categoriesBySlug: defaultState, abilitiesByName: {} },
530
- action
531
- );
532
- const category = state.categoriesBySlug[ 'data-retrieval' ];
533
-
534
- // Should have valid properties
535
- expect( category.slug ).toBe( 'data-retrieval' );
536
- expect( category.label ).toBe( 'Data Retrieval' );
537
- expect( category.description ).toBe( 'Full test category.' );
538
- expect( category.meta ).toEqual( {
539
- priority: 'high',
540
- color: 'blue',
541
- } );
542
-
543
- // Should NOT have invalid properties
544
- expect( category ).not.toHaveProperty( '_links' );
545
- expect( category ).not.toHaveProperty( '_embedded' );
546
- expect( category ).not.toHaveProperty( 'extra_field' );
547
- } );
548
-
549
- it( 'should overwrite existing categories', () => {
550
- const initialState = {
551
- 'existing-category': {
552
- slug: 'existing-category',
553
- label: 'Existing Category',
554
- description: 'Already in store',
555
- },
556
- };
557
-
558
- const categories = [
559
- {
560
- slug: 'data-retrieval',
561
- label: 'Data Retrieval',
562
- description: 'New category',
563
- },
564
- ];
565
-
566
- const action = {
567
- type: RECEIVE_CATEGORIES,
568
- categories,
569
- };
570
-
571
- const state = reducer(
572
- { categoriesBySlug: initialState, abilitiesByName: {} },
573
- action
574
- );
575
-
576
- // Should only have new categories, old ones are replaced
577
- expect( state.categoriesBySlug ).not.toHaveProperty(
578
- 'existing-category'
579
- );
580
- expect( state.categoriesBySlug ).toHaveProperty(
581
- 'data-retrieval'
582
- );
583
- } );
584
- } );
585
-
586
- describe( 'Edge cases', () => {
587
- it( 'should handle undefined categories in RECEIVE_CATEGORIES', () => {
588
- const action = {
589
- type: RECEIVE_CATEGORIES,
590
- categories: undefined,
591
- };
592
-
593
- const state = reducer(
594
- { categoriesBySlug: defaultState, abilitiesByName: {} },
595
- action
596
- );
597
-
598
- expect( state.categoriesBySlug ).toEqual( defaultState );
599
- } );
600
-
601
- it( 'should handle empty categories array', () => {
602
- const action = {
603
- type: RECEIVE_CATEGORIES,
604
- categories: [],
605
- };
606
-
607
- const state = reducer(
608
- { categoriesBySlug: defaultState, abilitiesByName: {} },
609
- action
610
- );
611
-
612
- expect( state.categoriesBySlug ).toEqual( defaultState );
613
- } );
614
-
615
- it( 'should handle undefined properties gracefully', () => {
616
- const categories = [
617
- {
618
- slug: 'minimal',
619
- label: 'Minimal',
620
- description: 'Minimal category with undefined meta',
621
- meta: undefined,
622
- },
623
- ];
624
-
625
- const action = {
626
- type: RECEIVE_CATEGORIES,
627
- categories,
628
- };
629
-
630
- const state = reducer(
631
- { categoriesBySlug: defaultState, abilitiesByName: {} },
632
- action
633
- );
634
- const category = state.categoriesBySlug.minimal;
635
-
636
- expect( category.slug ).toBe( 'minimal' );
637
- expect( category.label ).toBe( 'Minimal' );
638
- expect( category.description ).toBe(
639
- 'Minimal category with undefined meta'
640
- );
641
- // Undefined properties should not be present
642
- expect( category ).not.toHaveProperty( 'meta' );
643
- } );
644
- } );
645
-
646
195
  describe( 'REGISTER_ABILITY_CATEGORY', () => {
647
196
  it( 'should add category to the state', () => {
648
197
  const category = {
@@ -11,7 +11,7 @@ import {
11
11
  getAbilityCategories,
12
12
  getAbilityCategory,
13
13
  } from '../selectors';
14
- import type { AbilitiesState } from '../../types';
14
+ import type { AbilitiesState } from '../types';
15
15
 
16
16
  describe( 'Store Selectors', () => {
17
17
  describe( 'getAbilities', () => {
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Internal dependencies
3
+ */
4
+ import type { Ability, AbilityCategory } from '../types';
5
+
6
+ /**
7
+ * The state shape for the abilities store.
8
+ */
9
+ export interface AbilitiesState {
10
+ /**
11
+ * Map of ability names to ability objects.
12
+ */
13
+ abilitiesByName: Record< string, Ability >;
14
+
15
+ /**
16
+ * Map of category slugs to category objects.
17
+ */
18
+ categoriesBySlug: Record< string, AbilityCategory >;
19
+ }