Skip to content
This repository has been archived by the owner on Feb 23, 2024. It is now read-only.

Commit

Permalink
Refactor registration of payment methods and update unit tests for pa…
Browse files Browse the repository at this point in the history
…yment data store (#6669)

* Mock getCartTotals

* Change test to use data store instead of context

* Move payment method context test to data store selectors

* Change description of test suite

* Bump commit to trigger tests

* Fix path in test

* update package lock

* Set correct state payment method reducer tests/use correct actions

* Get saved payment methods from store not context

* Mock stores and update tests to allow switching payment methods

* Update tests to get onSubmit from checkoutEventsContext

* Remove cartTotalsLoaded check from payment method initialize check

* Make PaymentMethods test wait until payments initialized

* initialize payment method data store when cart is loaded

* Remove unneeded actions and add initializePaymentMethodDataStore

* Remove check for cart totals loaded in checkPaymentMethods

* Remove updateAvilablePaymentMethods from registry

* Remove unneeded mock

* Remove unused import

* Rename imports to fix eslint errors

* Remove unused imports

* Remove return false from checkPaymentMethods

* Remove unnecessary setPaymentMethodsInitialized call

* Add todo comment to track refactoring opportunity

* Remove savedpayment methods from payment method context and rename it

* Rename payment method data context to payment method events context

* Add tests for setDefaultPaymentMethods

* Optimize the availablePaymentMethods state data

Store only the "name" attribute for now.

* Get list of payment methods from the registry instead of the store

We are using this hook to get some React elements in the payment method
object. So, we are getting the raw data directly from the registry instead
of the store.

* Fix payment state not loading on the Checkout edit page

* Handle checkout edit page case

* Fix infinite loop error on C&C Blocks

* Include @wordpress/redux-routine in transformIgnorePatterns jest config

Co-authored-by: Saad Tarhi <[email protected]>
  • Loading branch information
opr and tarhi-saad authored Aug 22, 2022
1 parent aefc445 commit c5b173d
Show file tree
Hide file tree
Showing 19 changed files with 408 additions and 235 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import { useStoreCart } from '../cart/use-store-cart';
import { useStoreCartCoupons } from '../cart/use-store-cart-coupons';
import { noticeContexts, responseTypes } from '../../event-emit';
import { useCheckoutEventsContext } from '../../providers/cart-checkout/checkout-events';
import { usePaymentMethodDataContext } from '../../providers/cart-checkout/payment-methods';
import { usePaymentMethodEventsContext } from '../../providers/cart-checkout/payment-methods';
import { useShippingDataContext } from '../../providers/cart-checkout/shipping';
import { useCustomerDataContext } from '../../providers/cart-checkout/customer';
import { prepareTotalItems } from './utils';
Expand Down Expand Up @@ -69,7 +69,7 @@ export const usePaymentMethodInterface = (): PaymentMethodInterface => {
PAYMENT_METHOD_DATA_STORE_KEY
);

const { onPaymentProcessing } = usePaymentMethodDataContext();
const { onPaymentProcessing } = usePaymentMethodEventsContext();
const {
shippingErrorStatus,
shippingErrorTypes,
Expand Down
36 changes: 18 additions & 18 deletions assets/js/base/context/hooks/payment-methods/use-payment-methods.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ import type {
PaymentMethods,
ExpressPaymentMethods,
} from '@woocommerce/type-defs/payments';
import {
getPaymentMethods,
getExpressPaymentMethods,
} from '@woocommerce/blocks-registry';
import { useSelect } from '@wordpress/data';
import { PAYMENT_METHOD_DATA_STORE_KEY } from '@woocommerce/block-data';

Expand All @@ -21,27 +25,23 @@ interface ExpressPaymentMethodState {
const usePaymentMethodState = (
express = false
): PaymentMethodState | ExpressPaymentMethodState => {
const {
paymentMethodsInitialized,
expressPaymentMethodsInitialized,
availablePaymentMethods,
availableExpressPaymentMethods,
} = useSelect( ( select ) => {
const store = select( PAYMENT_METHOD_DATA_STORE_KEY );
const { paymentMethodsInitialized, expressPaymentMethodsInitialized } =
useSelect( ( select ) => {
const store = select( PAYMENT_METHOD_DATA_STORE_KEY );

return {
paymentMethodsInitialized: store.paymentMethodsInitialized(),
expressPaymentMethodsInitialized:
store.expressPaymentMethodsInitialized(),
availablePaymentMethods: store.getAvailablePaymentMethods(),
availableExpressPaymentMethods:
store.getAvailableExpressPaymentMethods(),
};
} );
return {
paymentMethodsInitialized: store.paymentMethodsInitialized(),
expressPaymentMethodsInitialized:
store.expressPaymentMethodsInitialized(),
};
} );

const currentPaymentMethods = useShallowEqual( availablePaymentMethods );
const paymentMethods = getPaymentMethods();
const expressPaymentMethods = getExpressPaymentMethods();

const currentPaymentMethods = useShallowEqual( paymentMethods );
const currentExpressPaymentMethods = useShallowEqual(
availableExpressPaymentMethods
expressPaymentMethods
);

return {
Expand Down
39 changes: 18 additions & 21 deletions assets/js/base/context/hooks/test/use-checkout-submit.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,24 +12,22 @@ import {
CHECKOUT_STORE_KEY,
config as checkoutStoreConfig,
} from '../../../../data/checkout';
import {
PAYMENT_METHOD_DATA_STORE_KEY,
config as paymentDataStoreConfig,
} from '../../../../data/payment-methods';

const mockUseCheckoutEventsContext = {
onSubmit: jest.fn(),
};
const mockUsePaymentMethodDataContext = {
activePaymentMethod: '',
currentStatus: {
isDoingExpressPayment: false,
},
};

jest.mock( '../../providers/cart-checkout/checkout-events', () => ( {
useCheckoutEventsContext: () => mockUseCheckoutEventsContext,
} ) );

jest.mock( '../../providers/cart-checkout/payment-methods', () => ( {
usePaymentMethodDataContext: () => mockUsePaymentMethodDataContext,
} ) );
jest.mock( '../../providers/cart-checkout/checkout-events', () => {
const original = jest.requireActual(
'../../providers/cart-checkout/checkout-events'
);
return {
...original,
useCheckoutEventsContext: () => {
return { onSubmit: jest.fn() };
},
};
} );

describe( 'useCheckoutSubmit', () => {
let registry, renderer;
Expand All @@ -48,11 +46,12 @@ describe( 'useCheckoutSubmit', () => {
beforeEach( () => {
registry = createRegistry( {
[ CHECKOUT_STORE_KEY ]: checkoutStoreConfig,
[ PAYMENT_METHOD_DATA_STORE_KEY ]: paymentDataStoreConfig,
} );
renderer = null;
} );

it( 'onSubmit calls the correct action in the checkout context', () => {
it( 'onSubmit calls the correct action in the checkout events context', () => {
const TestComponent = getTestComponent();

act( () => {
Expand All @@ -66,8 +65,6 @@ describe( 'useCheckoutSubmit', () => {

onSubmit();

expect( mockUseCheckoutEventsContext.onSubmit ).toHaveBeenCalledTimes(
1
);
expect( onSubmit ).toHaveBeenCalledTimes( 1 );
} );
} );
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export {
PaymentMethodDataProvider,
usePaymentMethodDataContext,
} from './payment-method-data-context';
usePaymentMethodEventsContext,
} from './payment-method-events-context';
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import {
useReducer,
useRef,
useEffect,
useMemo,
} from '@wordpress/element';
import { useDispatch, useSelect } from '@wordpress/data';
import {
Expand All @@ -19,20 +18,17 @@ import {
/**
* Internal dependencies
*/
import type {
SavedPaymentMethods,
PaymentMethodDataContextType,
} from '../../../../../data/payment-methods/types';
import type { PaymentMethodEventsContextType } from '../../../../../data/payment-methods/types';
import { DEFAULT_PAYMENT_METHOD_DATA } from './constants';
import { useEditorContext } from '../../editor-context';
import { useEventEmitters, reducer as emitReducer } from './event-emit';
import { useCustomerData } from '../../../hooks/use-customer-data';

const PaymentMethodDataContext = createContext( DEFAULT_PAYMENT_METHOD_DATA );
const PaymentMethodEventsContext = createContext( DEFAULT_PAYMENT_METHOD_DATA );

export const usePaymentMethodDataContext = (): PaymentMethodDataContextType => {
return useContext( PaymentMethodDataContext );
};
export const usePaymentMethodEventsContext =
(): PaymentMethodEventsContextType => {
return useContext( PaymentMethodEventsContext );
};

/**
* PaymentMethodDataProvider is automatically included in the CheckoutDataProvider.
Expand Down Expand Up @@ -61,11 +57,7 @@ export const PaymentMethodDataProvider = ( {
isCalculating: store.isCalculating(),
};
} );
const {
currentStatus,
activeSavedPaymentMethods,
paymentMethodsInitialized,
} = useSelect( ( select ) => {
const { currentStatus } = useSelect( ( select ) => {
const store = select( PAYMENT_METHOD_DATA_STORE_KEY );

return {
Expand All @@ -74,7 +66,7 @@ export const PaymentMethodDataProvider = ( {
paymentMethodsInitialized: store.paymentMethodsInitialized(),
};
} );
const { isEditor, getPreviewData } = useEditorContext();

const { createErrorNotice, removeNotice } = useDispatch( 'core/notices' );
const { setValidationErrors } = useDispatch( VALIDATION_STORE_KEY );
const [ observers, observerDispatch ] = useReducer( emitReducer, {} );
Expand All @@ -93,20 +85,6 @@ export const PaymentMethodDataProvider = ( {
} = useDispatch( PAYMENT_METHOD_DATA_STORE_KEY );
const { setBillingAddress, setShippingAddress } = useCustomerData();

const savedPaymentMethods = useMemo( (): SavedPaymentMethods => {
if ( isEditor ) {
return getPreviewData(
'previewSavedPaymentMethods'
) as SavedPaymentMethods;
}
return paymentMethodsInitialized ? activeSavedPaymentMethods : {};
}, [
isEditor,
getPreviewData,
paymentMethodsInitialized,
activeSavedPaymentMethods,
] );

// flip payment to processing if checkout processing is complete, there are no errors, and payment status is started.
useEffect( () => {
if (
Expand Down Expand Up @@ -163,14 +141,13 @@ export const PaymentMethodDataProvider = ( {
emitPaymentProcessingEvent,
] );

const paymentContextData: PaymentMethodDataContextType = {
const paymentContextData: PaymentMethodEventsContextType = {
onPaymentProcessing,
savedPaymentMethods,
};

return (
<PaymentMethodDataContext.Provider value={ paymentContextData }>
<PaymentMethodEventsContext.Provider value={ paymentContextData }>
{ children }
</PaymentMethodDataContext.Provider>
</PaymentMethodEventsContext.Provider>
);
};
8 changes: 0 additions & 8 deletions assets/js/blocks-registry/payment-methods/registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,7 @@ export const registerPaymentMethod = (
paymentMethodConfig = new PaymentMethodConfig( options );
}
if ( paymentMethodConfig instanceof PaymentMethodConfig ) {
const { updateAvailablePaymentMethods } = dispatch(
PAYMENT_METHOD_DATA_STORE_KEY
);
paymentMethods[ paymentMethodConfig.name ] = paymentMethodConfig;
updateAvailablePaymentMethods();
}
};

Expand All @@ -78,11 +74,7 @@ export const registerExpressPaymentMethod = (
paymentMethodConfig = new ExpressPaymentMethodConfig( options );
}
if ( paymentMethodConfig instanceof ExpressPaymentMethodConfig ) {
const { updateAvailableExpressPaymentMethods } = dispatch(
PAYMENT_METHOD_DATA_STORE_KEY
);
expressPaymentMethods[ paymentMethodConfig.name ] = paymentMethodConfig;
updateAvailableExpressPaymentMethods();
}
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,7 @@ import {
useStoreEvents,
} from '@woocommerce/base-context/hooks';
import { cloneElement, useCallback } from '@wordpress/element';
import {
useEditorContext,
usePaymentMethodDataContext,
} from '@woocommerce/base-context';
import { useEditorContext } from '@woocommerce/base-context';
import classNames from 'classnames';
import RadioControlAccordion from '@woocommerce/base-components/radio-control-accordion';
import { useDispatch, useSelect } from '@wordpress/data';
Expand All @@ -28,30 +25,26 @@ import { STORE_KEY as PAYMENT_METHOD_DATA_STORE_KEY } from '../../../data/paymen
* @return {*} The rendered component.
*/
const PaymentMethodOptions = () => {
const { savedPaymentMethods } = usePaymentMethodDataContext(); //TODO: Move this state from the context file

const {
activeSavedToken,
activePaymentMethod,
isExpressPaymentMethodActive,
savedPaymentMethods,
availablePaymentMethods,
} = useSelect( ( select ) => {
const store = select( PAYMENT_METHOD_DATA_STORE_KEY );
return {
activeSavedToken: store.getActiveSavedToken(),
activePaymentMethod: store.getActivePaymentMethod(),
isExpressPaymentMethodActive: store.isExpressPaymentMethodActive(),
savedPaymentMethods: store.getSavedPaymentMethods(),
availablePaymentMethods: store.getAvailablePaymentMethods(),
};
} );
const { setActivePaymentMethod } = useDispatch(
PAYMENT_METHOD_DATA_STORE_KEY
);
const paymentMethods = getPaymentMethods();
const { availablePaymentMethods } = useSelect( ( select ) => {
const store = select( PAYMENT_METHOD_DATA_STORE_KEY );
return {
availablePaymentMethods: store.getAvailablePaymentMethods(),
};
} );
const { ...paymentMethodInterface } = usePaymentMethodInterface();
const { removeNotice } = useDispatch( 'core/notices' );
const { dispatchCheckoutEvent } = useStoreEvents();
Expand Down
Loading

0 comments on commit c5b173d

Please sign in to comment.