From e9d227263beaf0be7eb274139b87ffbcbd924f6f Mon Sep 17 00:00:00 2001 From: "Glenn R. Martin" Date: Wed, 5 Jul 2023 11:30:43 -0400 Subject: [PATCH] chore: Updating Payment State Polling to use a Custom Routine --- src/payment/data/actions.js | 13 ++++----- src/payment/data/reducers.js | 3 +-- src/payment/data/redux.test.js | 3 +-- src/payment/data/sagas.js | 6 ++--- src/payment/data/utils.js | 15 +++++++++++ src/payment/data/utils.test.js | 49 ++++++++++++++++++++++++++++++++++ 6 files changed, 74 insertions(+), 15 deletions(-) diff --git a/src/payment/data/actions.js b/src/payment/data/actions.js index a7cf502ae..40ab6867e 100644 --- a/src/payment/data/actions.js +++ b/src/payment/data/actions.js @@ -1,4 +1,5 @@ import { createRoutine } from 'redux-saga-routines'; +import { createCustomRoutine } from './utils'; // Routines are action + action creator pairs in a series. // Actions adhere to the flux standard action format. @@ -10,8 +11,11 @@ import { createRoutine } from 'redux-saga-routines'; // fetchBasket.SUCCESS | fetchBasket.success() // fetchBasket.FAILURE | fetchBasket.failure() // fetchBasket.FULFILL | fetchBasket.fulfill() +// fetchBasket.REQUEST | fetchBasket.request() +// fetchBasket. | fetchBasket.() // // Created with redux-saga-routines + export const fetchCaptureKey = createRoutine('FETCH_CAPTURE_KEY'); export const fetchClientSecret = createRoutine('FETCH_CLIENT_SECRET'); export const submitPayment = createRoutine('SUBMIT_PAYMENT'); @@ -20,7 +24,7 @@ export const fetchActiveOrder = createRoutine('FETCH_ACTIVE_ORDER'); export const addCoupon = createRoutine('ADD_COUPON'); export const removeCoupon = createRoutine('REMOVE_COUPON'); export const updateQuantity = createRoutine('UPDATE_QUANTITY'); -export const pollPaymentState = createRoutine('UPDATE_PAYMENT_STATE'); +export const pollPaymentState = createCustomRoutine('UPDATE_PAYMENT_STATE', ['RECEIVED']); // Actions and their action creators export const BASKET_DATA_RECEIVED = 'BASKET_DATA_RECEIVED'; @@ -76,10 +80,3 @@ export const clientSecretDataReceived = clientSecret => ({ type: CLIENT_SECRET_DATA_RECEIVED, payload: clientSecret, }); - -export const PAYMENT_STATE_DATA_RECEIVED = 'PAYMENT_STATE_DATA_RECEIVED'; - -export const paymentStateDataReceived = paymentState => ({ - type: PAYMENT_STATE_DATA_RECEIVED, - payload: paymentState, -}); diff --git a/src/payment/data/reducers.js b/src/payment/data/reducers.js index 9f08b651c..bca6a50bf 100644 --- a/src/payment/data/reducers.js +++ b/src/payment/data/reducers.js @@ -9,7 +9,6 @@ import { CLIENT_SECRET_DATA_RECEIVED, CLIENT_SECRET_PROCESSING, MICROFORM_STATUS, - PAYMENT_STATE_DATA_RECEIVED, fetchBasket, submitPayment, fetchCaptureKey, @@ -197,7 +196,7 @@ const paymentState = (state = basketInitialState, action = null) => { }, }; - case PAYMENT_STATE_DATA_RECEIVED: + case pollPaymentState.RECEIVED: return { ...state, paymentState: action.payload.state, diff --git a/src/payment/data/redux.test.js b/src/payment/data/redux.test.js index 471c3c5c7..b3b793926 100644 --- a/src/payment/data/redux.test.js +++ b/src/payment/data/redux.test.js @@ -9,7 +9,6 @@ import { fetchBasket, fetchActiveOrder, pollPaymentState, - PAYMENT_STATE_DATA_RECEIVED, } from './actions'; import { currencyDisclaimerSelector, paymentSelector } from './selectors'; import { localizedCurrencySelector } from './utils'; @@ -258,7 +257,7 @@ describe('redux tests', () => { expect(triggerStore.getState().payment.basket.paymentStatePolling.keepPolling).toBe(true); expect(triggerStore.getState().payment.basket.paymentState).toBe(PAYMENT_STATE.PENDING); - triggerStore.dispatch({ type: PAYMENT_STATE_DATA_RECEIVED, payload: { state: PAYMENT_STATE.COMPLETED } }); + triggerStore.dispatch(pollPaymentState.received({ state: PAYMENT_STATE.COMPLETED })); expect(triggerStore.getState().payment.basket.paymentStatePolling.keepPolling).toBe(false); expect(triggerStore.getState().payment.basket.paymentState).toBe(PAYMENT_STATE.COMPLETED); diff --git a/src/payment/data/sagas.js b/src/payment/data/sagas.js index 20e237553..61c1e0a49 100644 --- a/src/payment/data/sagas.js +++ b/src/payment/data/sagas.js @@ -26,7 +26,6 @@ import { fetchCaptureKey, clientSecretProcessing, fetchClientSecret, - paymentStateDataReceived, pollPaymentState, } from './actions'; @@ -335,7 +334,8 @@ export function* handlePaymentState() { } const result = yield call(PaymentApiService.getCurrentPaymentState, paymentNumber, basketId); - yield put(paymentStateDataReceived(result)); + + yield put(pollPaymentState.received(result)); if (!(yield select(state => state.payment.basket.paymentStatePolling.keepPolling))) { yield put(pollPaymentState.fulfill()); @@ -354,7 +354,7 @@ export function* handlePaymentState() { throw innerError; } - yield put(paymentStateDataReceived({ state: PAYMENT_STATE.HTTP_ERROR })); + yield put(pollPaymentState.received({ state: PAYMENT_STATE.HTTP_ERROR })); if (yield select(state => state.payment.basket.paymentStatePolling.errorCount) < 1) { // noinspection ExceptionCaughtLocallyJS diff --git a/src/payment/data/utils.js b/src/payment/data/utils.js index 107b7499f..cf54caafc 100644 --- a/src/payment/data/utils.js +++ b/src/payment/data/utils.js @@ -2,6 +2,7 @@ import camelCase from 'lodash.camelcase'; import snakeCase from 'lodash.snakecase'; import { getConfig } from '@edx/frontend-platform'; import Cookies from 'universal-cookie'; +import { createRoutineCreator, defaultRoutineStages } from 'redux-saga-routines'; import { ORDER_TYPES } from './constants'; export function modifyObjectKeys(object, modify) { @@ -273,3 +274,17 @@ export const chainReducers = (reducers) => { ); }; }; + +/** + * Create a Routine with Custom Steps + * @param {string} name Name of the Routine + * @param {string[]} addtlStages An Array of Additional Steps (these will be uppercased) + * @param {boolean=} keepDefaults If we should include the normal Routine Steps + * @returns {*} A Redux Saga Routine + */ +export function createCustomRoutine(name, addtlStages, keepDefaults = true) { + return createRoutineCreator([ + ...(keepDefaults ? defaultRoutineStages : []), + ...addtlStages.map(x => x.toUpperCase()), + ])(name); +} diff --git a/src/payment/data/utils.test.js b/src/payment/data/utils.test.js index 49bd1aff4..0167bd565 100644 --- a/src/payment/data/utils.test.js +++ b/src/payment/data/utils.test.js @@ -1,6 +1,7 @@ import { Factory } from 'rosie'; import '../__factories__/basket.factory'; +import { defaultRoutineStages } from 'redux-saga-routines'; import { ORDER_TYPES } from './constants'; import { AsyncActionType, @@ -17,6 +18,7 @@ import { SECS_AS_MS, MINS_AS_MS, chainReducers, + createCustomRoutine, } from './utils'; describe('modifyObjectKeys', () => { @@ -370,3 +372,50 @@ describe('chainReducers([reducers])', () => { expect(firstStateResult.second_reducer_value).toEqual(undefined); }); }); + +describe('createCustomRoutine', () => { + const tests = [ + { + name: 'Additional Stage (UC) + Inherts Default Stages', + params: { name: 'TEST_ROUTINE_1', addtlStages: ['MEOW'], inheritDefaults: true }, + }, + { + name: 'Additional Stage (LC) + Inherts Default Stages', + params: { name: 'TEST_ROUTINE_2', addtlStages: ['meow'], inheritDefaults: true }, + }, + { + name: 'Additional Stage (LC) + Doesnt Inherit Default Stages', + params: { name: 'TEST_ROUTINE_3', addtlStages: ['woof'], inheritDefaults: false }, + }, + ]; + + for (let i = 0, testPlan = tests[i]; i < tests.length; i++, testPlan = tests[i]) { + it(testPlan.name, () => { + /* eslint-disable no-underscore-dangle */ // We don't control the fact that we have to access _ props here. + const routineUnderTest = createCustomRoutine( + testPlan.params.name, + testPlan.params.addtlStages, + testPlan.params.inheritDefaults, + ); + + expect(routineUnderTest._PREFIX).toEqual(testPlan.params.name); + + for (let si = 0, stageName = defaultRoutineStages[si]; + si < defaultRoutineStages.length; + si++, stageName = defaultRoutineStages[si]) { + if (testPlan.params.inheritDefaults) { + expect(routineUnderTest._STAGES).toContain(stageName); + } else { + expect(routineUnderTest._STAGES).not.toContain(stageName); + } + } + + for (let si = 0, stageName = testPlan.params.addtlStages[si]; + si < testPlan.params.addtlStages.length; + si++, stageName = testPlan.params.addtlStages[si]) { + expect(routineUnderTest._STAGES).toContain(stageName.toUpperCase()); + } + /* eslint-enable no-underscore-dangle */ + }); + } +});