UNPKG

@wordpress/data

Version:
467 lines (420 loc) 14.2 kB
/** * Internal dependencies */ import { createRegistry } from '../registry'; import createReduxStore from '../redux-store'; import { unlock } from '../lock-unlock'; import { createRegistrySelector } from '../factory'; describe( 'Private data APIs', () => { let registry; beforeEach( () => { registry = createRegistry(); } ); function getPublicPrice( state ) { return state.price; } function getSecretDiscount( state ) { return state.secretDiscount; } function setSecretDiscount( price ) { return { type: 'SET_PRIVATE_PRICE', price }; } function setPublicPrice( price ) { return { type: 'SET_PUBLIC_PRICE', price }; } const storeName = 'grocer'; const storeDescriptor = { selectors: { getPublicPrice, getState: ( state ) => state, }, actions: { setPublicPrice }, reducer: ( state = { price: 1000, secretDiscount: 800 }, action ) => { if ( action.type === 'SET_PRIVATE_PRICE' ) { return { ...state, secretDiscount: action.price, }; } else if ( action.type === 'SET_PUBLIC_PRICE' ) { return { ...state, price: action.price, }; } return state; }, }; function createStore() { const groceryStore = createReduxStore( storeName, storeDescriptor ); registry.register( groceryStore ); return groceryStore; } describe( 'private selectors', () => { it( 'should expose public selectors by default', () => { const groceryStore = createStore(); unlock( groceryStore ).registerPrivateSelectors( groceryStore, { getSecretDiscount, } ); const publicSelectors = registry.select( groceryStore ); expect( publicSelectors.getPublicPrice ).toEqual( expect.any( Function ) ); } ); it( 'should not expose private selectors by default', () => { const groceryStore = createStore(); unlock( groceryStore ).registerPrivateSelectors( { getSecretDiscount, } ); const publicSelectors = registry.select( groceryStore ); expect( publicSelectors.getSecretDiscount ).toEqual( undefined ); } ); it( 'should make private selectors available via unlock()', () => { const groceryStore = createStore(); unlock( groceryStore ).registerPrivateSelectors( { getSecretDiscount, } ); const privateSelectors = unlock( registry.select( groceryStore ) ); expect( privateSelectors.getSecretDiscount ).toEqual( expect.any( Function ) ); // The public selector is still accessible: expect( privateSelectors.getPublicPrice ).toEqual( expect.any( Function ) ); } ); it( 'should support combination of private selectors and resolvers', async () => { const testStore = createReduxStore( 'test', { reducer: ( state = {}, action ) => { if ( action.type === 'RECEIVE' ) { return { ...state, [ action.key ]: action.value }; } return state; }, selectors: { get: ( state, key ) => state[ key ], }, resolvers: { get: ( key ) => async ( { dispatch } ) => { const value = await ( 'resolved-' + key ); dispatch( { type: 'RECEIVE', key, value } ); }, }, } ); unlock( testStore ).registerPrivateSelectors( { peek: ( state, key ) => state[ key ], } ); registry.register( testStore ); await registry.resolveSelect( testStore ).get( 'x' ); expect( unlock( registry.select( testStore ) ).peek( 'x' ) ).toBe( 'resolved-x' ); } ); it( 'should support private selectors with resolvers', async () => { const testStore = createReduxStore( 'test', { reducer: ( state = {}, action ) => { if ( action.type === 'RECEIVE' ) { return { ...state, [ action.key ]: action.value }; } return state; }, selectors: {}, resolvers: { get: ( key ) => async ( { dispatch } ) => { const value = await ( 'resolved-' + key ); dispatch( { type: 'RECEIVE', key, value } ); }, }, } ); registry.register( testStore ); unlock( testStore ).registerPrivateSelectors( { get: ( state, key ) => state[ key ], } ); // Verify that the private selector is available in resolveSelect. const resolved = await unlock( registry.resolveSelect( testStore ) ).get( 'x' ); expect( resolved ).toBe( 'resolved-x' ); // Verify that the private selector is available in suspendSelect. let suspended; try { unlock( registry.suspendSelect( testStore ) ).get( 'y' ); } catch ( error ) { suspended = error; } expect( suspended ).toBeInstanceOf( Promise ); await suspended; // After the suspense promise resolves, the result should be ready. expect( unlock( registry.suspendSelect( testStore ) ).get( 'y' ) ).toBe( 'resolved-y' ); } ); it( 'should give private selectors access to the state', () => { const groceryStore = createStore(); unlock( groceryStore ).registerPrivateSelectors( { getSecretDiscount, } ); const privateSelectors = unlock( registry.select( groceryStore ) ); expect( privateSelectors.getSecretDiscount() ).toEqual( 800 ); } ); it( 'should support public selectors accessed via unlock()', () => { const groceryStore = createStore(); unlock( groceryStore ).registerPrivateSelectors( { getSecretDiscount, } ); const unlockedSelectors = unlock( registry.select( groceryStore ) ); expect( unlockedSelectors.getPublicPrice() ).toEqual( 1000 ); } ); it( 'should return stable references to selectors', () => { const groceryStore = createStore(); unlock( groceryStore ).registerPrivateSelectors( { getSecretDiscount, } ); const select = unlock( registry.select( groceryStore ) ); expect( select.getPublicPrice ).toBe( select.getPublicPrice ); expect( select.getSecretDiscount ).toBe( select.getSecretDiscount ); } ); it( 'should support registerStore', () => { const groceryStore = registry.registerStore( storeName, storeDescriptor ); unlock( groceryStore ).registerPrivateSelectors( { getSecretDiscount, } ); const privateSelectors = unlock( registry.select( storeName ) ); expect( privateSelectors.getSecretDiscount() ).toEqual( 800 ); } ); it( 'should support mixing createReduxStore and registerStore', () => { createReduxStore( storeName, storeDescriptor ); const groceryStore2 = registry.registerStore( storeName, storeDescriptor ); unlock( groceryStore2 ).registerPrivateSelectors( { getSecretDiscount, } ); const privateSelectors = unlock( registry.select( storeName ) ); expect( privateSelectors.getSecretDiscount() ).toEqual( 800 ); } ); it( 'should support sub registries', () => { const groceryStore = registry.registerStore( storeName, storeDescriptor ); unlock( groceryStore ).registerPrivateSelectors( { getSecretDiscount, } ); const subRegistry = createRegistry( {}, registry ); subRegistry.registerStore( storeName, storeDescriptor ); const parentPrivateSelectors = unlock( registry.select( storeName ) ); expect( parentPrivateSelectors.getSecretDiscount() ).toEqual( 800 ); const subPrivateSelectors = unlock( subRegistry.select( storeName ) ); expect( subPrivateSelectors.getSecretDiscount() ).toEqual( 800 ); } ); it( 'should support private registry selectors', () => { const groceryStore = createStore(); const otherStore = createReduxStore( 'other', { reducer: ( state = {} ) => state, } ); unlock( otherStore ).registerPrivateSelectors( { getPrice: createRegistrySelector( ( select ) => () => select( groceryStore ).getPublicPrice() ), } ); registry.register( otherStore ); const privateSelectors = unlock( registry.select( otherStore ) ); expect( privateSelectors.getPrice() ).toEqual( 1000 ); } ); it( 'should support calling a private registry selector from a public selector', () => { const groceryStore = createStore(); const getPriceWithShipping = createRegistrySelector( ( select ) => () => select( groceryStore ).getPublicPrice() + 5 ); const store = createReduxStore( 'a', { reducer: ( state = {} ) => state, selectors: { getPriceWithShippingAndTax: ( state ) => getPriceWithShipping( state ) * 1.1, }, } ); unlock( store ).registerPrivateSelectors( { getPriceWithShipping, } ); registry.register( store ); expect( registry.select( store ).getPriceWithShippingAndTax() ).toEqual( 1105.5 ); } ); } ); describe( 'private actions', () => { it( 'should expose public actions by default', () => { const groceryStore = createStore(); unlock( groceryStore ).registerPrivateActions( groceryStore, { setSecretDiscount, } ); const publicActions = registry.dispatch( groceryStore ); expect( publicActions.setPublicPrice ).toEqual( expect.any( Function ) ); } ); it( 'should not expose private actions by default', () => { const groceryStore = createStore(); unlock( groceryStore ).registerPrivateActions( { setSecretDiscount, } ); const publicActions = registry.dispatch( groceryStore ); expect( publicActions.setSecretDiscount ).toEqual( undefined ); } ); it( 'should make private actions available via unlock)', () => { const groceryStore = createStore(); unlock( groceryStore ).registerPrivateActions( { setSecretDiscount, } ); const privateActions = unlock( registry.dispatch( groceryStore ) ); expect( privateActions.setSecretDiscount ).toEqual( expect.any( Function ) ); // The public action is still accessible: expect( privateActions.setPublicPrice ).toEqual( expect.any( Function ) ); } ); it( 'should work with both private actions and private selectors at the same time', () => { const groceryStore = createStore(); unlock( groceryStore ).registerPrivateActions( { setSecretDiscount, } ); unlock( groceryStore ).registerPrivateSelectors( { getSecretDiscount, } ); const privateActions = unlock( registry.dispatch( groceryStore ) ); const privateSelectors = unlock( registry.select( groceryStore ) ); expect( privateActions.setSecretDiscount ).toEqual( expect.any( Function ) ); expect( privateSelectors.getSecretDiscount ).toEqual( expect.any( Function ) ); } ); it( 'should dispatch private actions like regular actions', () => { const groceryStore = createStore(); unlock( groceryStore ).registerPrivateActions( { setSecretDiscount, } ); const privateActions = unlock( registry.dispatch( groceryStore ) ); privateActions.setSecretDiscount( 400 ); expect( registry.select( groceryStore ).getState().secretDiscount ).toEqual( 400 ); } ); it( 'should return stable references to actions', () => { const groceryStore = createStore(); unlock( groceryStore ).registerPrivateActions( { setSecretDiscount, } ); const disp = unlock( registry.dispatch( groceryStore ) ); expect( disp.setPublicPrice ).toBe( disp.setPublicPrice ); expect( disp.setSecretDiscount ).toBe( disp.setSecretDiscount ); } ); it( 'should dispatch public actions on the unlocked store', () => { const groceryStore = createStore(); unlock( groceryStore ).registerPrivateActions( { setSecretDiscount, } ); const privateActions = unlock( registry.dispatch( groceryStore ) ); privateActions.setPublicPrice( 400 ); expect( registry.select( groceryStore ).getState().price ).toEqual( 400 ); } ); it( 'should dispatch private action thunks like regular actions', () => { const groceryStore = createStore(); unlock( groceryStore ).registerPrivateActions( { setSecretDiscountThunk: ( price ) => ( { dispatch } ) => { dispatch( { type: 'SET_PRIVATE_PRICE', price } ); }, } ); unlock( groceryStore ).registerPrivateSelectors( { getSecretDiscount, } ); const privateActions = unlock( registry.dispatch( groceryStore ) ); privateActions.setSecretDiscountThunk( 100 ); expect( unlock( registry.select( groceryStore ) ).getSecretDiscount() ).toEqual( 100 ); } ); it( 'should expose unlocked private selectors and actions to thunks', () => { const groceryStore = createStore(); unlock( groceryStore ).registerPrivateSelectors( { getSecretDiscount, } ); unlock( groceryStore ).registerPrivateActions( { setSecretDiscount, doubleSecretDiscount() { return ( { dispatch, select } ) => { dispatch.setSecretDiscount( select.getSecretDiscount() * 2 ); }; }, } ); const privateActions = unlock( registry.dispatch( groceryStore ) ); privateActions.setSecretDiscount( 100 ); privateActions.doubleSecretDiscount(); expect( unlock( registry.select( groceryStore ) ).getSecretDiscount() ).toEqual( 200 ); } ); it( 'should support registerStore', () => { const groceryStore = registry.registerStore( storeName, storeDescriptor ); unlock( groceryStore ).registerPrivateActions( { setSecretDiscount, } ); const privateActions = unlock( registry.dispatch( storeName ) ); privateActions.setSecretDiscount( 400 ); expect( registry.select( storeName ).getState().secretDiscount ).toEqual( 400 ); } ); it( 'should support sub registries', () => { const groceryStore = createStore(); unlock( groceryStore ).registerPrivateSelectors( { getSecretDiscount, } ); unlock( groceryStore ).registerPrivateActions( { setSecretDiscount, } ); const subRegistry = createRegistry( {}, registry ); subRegistry.registerStore( storeName, storeDescriptor ); const parentPrivateActions = unlock( registry.dispatch( storeName ) ); const parentPrivateSelectors = unlock( registry.select( storeName ) ); const subPrivateActions = unlock( subRegistry.dispatch( storeName ) ); const subPrivateSelectors = unlock( subRegistry.select( storeName ) ); parentPrivateActions.setSecretDiscount( 400 ); subPrivateActions.setSecretDiscount( 478 ); expect( parentPrivateSelectors.getSecretDiscount() ).toEqual( 400 ); expect( subPrivateSelectors.getSecretDiscount() ).toEqual( 478 ); } ); } ); } );