From 8348333f5d1fe6ce5de7befb47e31de07d422149 Mon Sep 17 00:00:00 2001 From: julianajlk Date: Wed, 15 May 2024 10:44:03 -0400 Subject: [PATCH 1/3] feat: Add PageLoadingDynamicPaymentMethods component with timeout --- src/payment/PageLoading.jsx | 20 ------- .../PageLoadingDynamicPaymentMethods.jsx | 58 +++++++++++++++++++ src/payment/PaymentPage.jsx | 4 +- 3 files changed, 60 insertions(+), 22 deletions(-) create mode 100644 src/payment/PageLoadingDynamicPaymentMethods.jsx diff --git a/src/payment/PageLoading.jsx b/src/payment/PageLoading.jsx index 15e55c167..de61fb2b7 100644 --- a/src/payment/PageLoading.jsx +++ b/src/payment/PageLoading.jsx @@ -1,7 +1,5 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; -import { getConfig } from '@edx/frontend-platform'; -import { logInfo } from '@edx/frontend-platform/logging'; export default class PageLoading extends Component { renderSrMessage() { @@ -17,17 +15,6 @@ export default class PageLoading extends Component { } render() { - const { shouldRedirectToReceipt, orderNumber } = this.props; - - if (shouldRedirectToReceipt) { - logInfo(`Dynamic Payment Methods payment succeeded for edX order number ${orderNumber}, redirecting to ecommerce receipt page.`); - const queryParams = `order_number=${orderNumber}&disable_back_button=${Number(true)}&dpm_enabled=${true}`; - if (getConfig().ENVIRONMENT !== 'test') { - /* istanbul ignore next */ - global.location.assign(`${getConfig().ECOMMERCE_BASE_URL}/checkout/receipt/?${queryParams}`); - } - } - return (
{ + useEffect(() => { + const timer = setTimeout(() => { + logInfo(`Dynamic Payment Methods payment succeeded for edX order number ${orderNumber}, redirecting to ecommerce receipt page.`); + const queryParams = `order_number=${orderNumber}&disable_back_button=${Number(true)}&dpm_enabled=${true}`; + + if (getConfig().ENVIRONMENT !== 'test') { + /* istanbul ignore next */ + global.location.assign(`${getConfig().ECOMMERCE_BASE_URL}/checkout/receipt/?${queryParams}`); + } + }, 3000); // Delay the redirect to receipt page by 3 seconds to make sure ecomm order fulfillment is done. + + return () => clearTimeout(timer); // On unmount, clear the timer + }, [srMessage, orderNumber]); + + const renderSrMessage = () => { + if (!srMessage) { + return null; + } + + return ( + + {srMessage} + + ); + }; + + return ( +
+
+
+ {renderSrMessage()} +
+
+
+ ); +}; + +PageLoadingDynamicPaymentMethods.propTypes = { + srMessage: PropTypes.string.isRequired, + orderNumber: PropTypes.string, +}; + +PageLoadingDynamicPaymentMethods.defaultProps = { + orderNumber: null, +}; + +export default PageLoadingDynamicPaymentMethods; diff --git a/src/payment/PaymentPage.jsx b/src/payment/PaymentPage.jsx index f0902f38e..bdf43cd4a 100644 --- a/src/payment/PaymentPage.jsx +++ b/src/payment/PaymentPage.jsx @@ -26,6 +26,7 @@ import EmptyCartMessage from './EmptyCartMessage'; import Cart from './cart/Cart'; import Checkout from './checkout/Checkout'; import { FormattedAlertList } from '../components/formatted-alert-list/FormattedAlertList'; +import PageLoadingDynamicPaymentMethods from './PageLoadingDynamicPaymentMethods'; class PaymentPage extends React.Component { constructor(props) { @@ -113,9 +114,8 @@ class PaymentPage extends React.Component { // lag between when the paymentStatus is no longer null but the redirect hasn't happened yet. if (shouldRedirectToReceipt) { return ( - ); From 0a0d4e498bd93b8dd5e9038b95fc274e655a8a8b Mon Sep 17 00:00:00 2001 From: julianajlk Date: Wed, 15 May 2024 17:54:06 -0400 Subject: [PATCH 2/3] test: Add PageLoadingDynamicPaymentMethods test --- .../PageLoadingDynamicPaymentMethods.test.jsx | 90 +++++++++++++++++++ ...LoadingDynamicPaymentMethods.test.jsx.snap | 18 ++++ 2 files changed, 108 insertions(+) create mode 100644 src/payment/PageLoadingDynamicPaymentMethods.test.jsx create mode 100644 src/payment/__snapshots__/PageLoadingDynamicPaymentMethods.test.jsx.snap diff --git a/src/payment/PageLoadingDynamicPaymentMethods.test.jsx b/src/payment/PageLoadingDynamicPaymentMethods.test.jsx new file mode 100644 index 000000000..733da7431 --- /dev/null +++ b/src/payment/PageLoadingDynamicPaymentMethods.test.jsx @@ -0,0 +1,90 @@ +import React from 'react'; +import { createStore } from 'redux'; +import { Provider } from 'react-redux'; +import { render, act } from '@testing-library/react'; +import { IntlProvider } from '@edx/frontend-platform/i18n'; +import { logInfo } from '@edx/frontend-platform/logging'; + +import createRootReducer from '../data/reducers'; +import PageLoadingDynamicPaymentMethods from './PageLoadingDynamicPaymentMethods'; + +jest.mock('@edx/frontend-platform/logging', () => ({ + logInfo: jest.fn(), +})); + +describe('PageLoadingDynamicPaymentMethods', () => { + let store; + let location; + + beforeAll(() => { + location = global.location; + delete global.location; + global.location = { assign: jest.fn() }; + }); + + afterAll(() => { + global.location = location; + }); + + beforeEach(() => { + store = createStore(createRootReducer()); + jest.useFakeTimers(); + jest.clearAllMocks(); + }); + + afterEach(() => { + jest.runOnlyPendingTimers(); + jest.useRealTimers(); + }); + + it('renders ', () => { + const component = ( + + + + + + ); + const { container: tree } = render(component); + expect(tree).toMatchSnapshot(); + }); + + it('it redirects to receipt page after 3 seconds delay', () => { + const orderNumber = 'EDX-100001'; + const logMessage = `Dynamic Payment Methods payment succeeded for edX order number ${orderNumber}, redirecting to ecommerce receipt page.`; + const queryParams = `order_number=${orderNumber}&disable_back_button=${Number(true)}&dpm_enabled=${true}`; + render( + + + + + , + ); + + act(() => { + jest.advanceTimersByTime(3000); + }); + expect(logInfo).toHaveBeenCalledWith(expect.stringMatching(logMessage)); + expect(global.location.assign).toHaveBeenCalledWith(expect.stringContaining(`/checkout/receipt/?${queryParams}`)); + }); + + it('cleans up the timer on unmount', () => { + const { unmount } = render( + , + ); + unmount(); + act(() => { + jest.advanceTimersByTime(3000); + }); + expect(window.location.assign).not.toHaveBeenCalled(); + }); +}); diff --git a/src/payment/__snapshots__/PageLoadingDynamicPaymentMethods.test.jsx.snap b/src/payment/__snapshots__/PageLoadingDynamicPaymentMethods.test.jsx.snap new file mode 100644 index 000000000..c3dda9f75 --- /dev/null +++ b/src/payment/__snapshots__/PageLoadingDynamicPaymentMethods.test.jsx.snap @@ -0,0 +1,18 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`PageLoadingDynamicPaymentMethods renders 1`] = ` +
+
+
+
+
+
+
+`; From 667e27cb11fa5a615d88a6ab033d36739b7e2aab Mon Sep 17 00:00:00 2001 From: julianajlk Date: Wed, 15 May 2024 18:00:26 -0400 Subject: [PATCH 3/3] test: We dont use global.location on tests --- .../PageLoadingDynamicPaymentMethods.test.jsx | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/src/payment/PageLoadingDynamicPaymentMethods.test.jsx b/src/payment/PageLoadingDynamicPaymentMethods.test.jsx index 733da7431..192fd2e75 100644 --- a/src/payment/PageLoadingDynamicPaymentMethods.test.jsx +++ b/src/payment/PageLoadingDynamicPaymentMethods.test.jsx @@ -14,17 +14,6 @@ jest.mock('@edx/frontend-platform/logging', () => ({ describe('PageLoadingDynamicPaymentMethods', () => { let store; - let location; - - beforeAll(() => { - location = global.location; - delete global.location; - global.location = { assign: jest.fn() }; - }); - - afterAll(() => { - global.location = location; - }); beforeEach(() => { store = createStore(createRootReducer()); @@ -55,7 +44,6 @@ describe('PageLoadingDynamicPaymentMethods', () => { it('it redirects to receipt page after 3 seconds delay', () => { const orderNumber = 'EDX-100001'; const logMessage = `Dynamic Payment Methods payment succeeded for edX order number ${orderNumber}, redirecting to ecommerce receipt page.`; - const queryParams = `order_number=${orderNumber}&disable_back_button=${Number(true)}&dpm_enabled=${true}`; render( @@ -71,7 +59,6 @@ describe('PageLoadingDynamicPaymentMethods', () => { jest.advanceTimersByTime(3000); }); expect(logInfo).toHaveBeenCalledWith(expect.stringMatching(logMessage)); - expect(global.location.assign).toHaveBeenCalledWith(expect.stringContaining(`/checkout/receipt/?${queryParams}`)); }); it('cleans up the timer on unmount', () => { @@ -85,6 +72,6 @@ describe('PageLoadingDynamicPaymentMethods', () => { act(() => { jest.advanceTimersByTime(3000); }); - expect(window.location.assign).not.toHaveBeenCalled(); + expect(logInfo).not.toHaveBeenCalled(); }); });