22 * Tests for store actions.
33 */
44
5+ /**
6+ * WordPress dependencies
7+ */
8+ import { resolveSelect } from '@wordpress/data' ;
9+
510/**
611 * Internal dependencies
712 */
@@ -17,6 +22,11 @@ import {
1722} from '../constants' ;
1823import type { Ability } from '../../types' ;
1924
25+ // Mock the WordPress data store
26+ jest . mock ( '@wordpress/data' , ( ) => ( {
27+ resolveSelect : jest . fn ( ) ,
28+ } ) ) ;
29+
2030describe ( 'Store Actions' , ( ) => {
2131 describe ( 'receiveAbilities' , ( ) => {
2232 it ( 'should create an action to receive abilities' , ( ) => {
@@ -224,7 +234,9 @@ describe( 'Store Actions', () => {
224234
225235 await expect (
226236 action ( { select : mockSelect , dispatch : mockDispatch } )
227- ) . rejects . toThrow ( 'Ability "test/ability" must have a description' ) ;
237+ ) . rejects . toThrow (
238+ 'Ability "test/ability" must have a description'
239+ ) ;
228240 expect ( mockDispatch ) . not . toHaveBeenCalled ( ) ;
229241 } ) ;
230242
@@ -294,19 +306,23 @@ describe( 'Store Actions', () => {
294306 callback : jest . fn ( ) ,
295307 } ;
296308
297- // Update mock to include this category
298- mockSelect . getAbilityCategory . mockImplementation (
299- ( slug : string ) => {
300- if ( slug === validCategory || slug === 'test-category' || slug === 'data-retrieval' ) {
301- return {
302- slug,
303- label : 'Test' ,
304- description : 'Test' ,
305- } ;
306- }
307- return null ;
308- }
309- ) ;
309+ mockSelect . getAbilityCategories . mockReturnValue ( [
310+ {
311+ slug : 'test-category' ,
312+ label : 'Test Category' ,
313+ description : 'Test category for testing' ,
314+ } ,
315+ {
316+ slug : 'data-retrieval' ,
317+ label : 'Data Retrieval' ,
318+ description : 'Abilities that retrieve data' ,
319+ } ,
320+ {
321+ slug : validCategory ,
322+ label : 'Test Category' ,
323+ description : 'Test' ,
324+ } ,
325+ ] ) ;
310326
311327 mockSelect . getAbility . mockReturnValue ( null ) ;
312328 mockDispatch . mockClear ( ) ;
@@ -322,8 +338,18 @@ describe( 'Store Actions', () => {
322338 } ) ;
323339
324340 it ( 'should validate and reject ability with non-existent category' , async ( ) => {
325- // Override getAbilityCategory to only return null
326- mockSelect . getAbilityCategory . mockReturnValue ( null ) ;
341+ mockSelect . getAbilityCategories . mockReturnValue ( [
342+ {
343+ slug : 'test-category' ,
344+ label : 'Test Category' ,
345+ description : 'Test category for testing' ,
346+ } ,
347+ {
348+ slug : 'data-retrieval' ,
349+ label : 'Data Retrieval' ,
350+ description : 'Abilities that retrieve data' ,
351+ } ,
352+ ] ) ;
327353
328354 const ability : Ability = {
329355 name : 'test/ability' ,
@@ -344,11 +370,18 @@ describe( 'Store Actions', () => {
344370 } ) ;
345371
346372 it ( 'should accept ability with existing category' , async ( ) => {
347- mockSelect . getAbilityCategory . mockReturnValue ( {
348- slug : 'data-retrieval' ,
349- label : 'Data Retrieval' ,
350- description : 'Abilities that retrieve data' ,
351- } ) ;
373+ mockSelect . getAbilityCategories . mockReturnValue ( [
374+ {
375+ slug : 'test-category' ,
376+ label : 'Test Category' ,
377+ description : 'Test category for testing' ,
378+ } ,
379+ {
380+ slug : 'data-retrieval' ,
381+ label : 'Data Retrieval' ,
382+ description : 'Abilities that retrieve data' ,
383+ } ,
384+ ] ) ;
352385
353386 const ability : Ability = {
354387 name : 'test/ability' ,
@@ -361,9 +394,8 @@ describe( 'Store Actions', () => {
361394 const action = registerAbility ( ability ) ;
362395 await action ( { select : mockSelect , dispatch : mockDispatch } ) ;
363396
364- expect ( mockSelect . getAbilityCategory ) . toHaveBeenCalledWith (
365- 'data-retrieval'
366- ) ;
397+ // We check the categories array now, not calling getAbilityCategory
398+ expect ( mockSelect . getAbilityCategories ) . toHaveBeenCalled ( ) ;
367399 expect ( mockDispatch ) . toHaveBeenCalledWith ( {
368400 type : REGISTER_ABILITY ,
369401 ability,
@@ -414,6 +446,53 @@ describe( 'Store Actions', () => {
414446 ) . rejects . toThrow ( 'Ability "test/ability" is already registered' ) ;
415447 expect ( mockDispatch ) . not . toHaveBeenCalled ( ) ;
416448 } ) ;
449+
450+ it ( 'should load categories when store is empty before validation' , async ( ) => {
451+ // First call returns empty, second call returns loaded categories
452+ let callCount = 0 ;
453+ mockSelect . getAbilityCategories . mockImplementation ( ( ) => {
454+ callCount ++ ;
455+ if ( callCount === 1 ) {
456+ return [ ] ; // Empty on first call
457+ }
458+ return [
459+ {
460+ slug : 'test-category' ,
461+ label : 'Test Category' ,
462+ description : 'Test category' ,
463+ } ,
464+ ] ; // Loaded on second call
465+ } ) ;
466+
467+ // Mock resolveSelect to return a mock that resolves the getAbilityCategories call
468+ const mockResolveSelectFn = jest . fn ( ) . mockReturnValue ( {
469+ getAbilityCategories : jest . fn ( ) . mockResolvedValue ( undefined ) ,
470+ } ) ;
471+ ( resolveSelect as jest . Mock ) . mockImplementation (
472+ mockResolveSelectFn
473+ ) ;
474+
475+ const ability : Ability = {
476+ name : 'test/ability' ,
477+ label : 'Test Ability' ,
478+ description : 'Test description' ,
479+ category : 'test-category' ,
480+ callback : jest . fn ( ) ,
481+ } ;
482+
483+ const action = registerAbility ( ability ) ;
484+ await action ( { select : mockSelect , dispatch : mockDispatch } ) ;
485+
486+ // Should have called getAbilityCategories twice (once to check, once after loading)
487+ expect ( mockSelect . getAbilityCategories ) . toHaveBeenCalledTimes (
488+ 2
489+ ) ;
490+ // Should have successfully registered
491+ expect ( mockDispatch ) . toHaveBeenCalledWith ( {
492+ type : REGISTER_ABILITY ,
493+ ability,
494+ } ) ;
495+ } ) ;
417496 } ) ;
418497
419498 describe ( 'unregisterAbility' , ( ) => {
0 commit comments