/** * External dependencies */ import { render, screen, waitFor, act } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { previewCart } from '@woocommerce/resource-previews'; import * as wpDataFunctions from '@wordpress/data'; import { CART_STORE_KEY as storeKey, PAYMENT_STORE_KEY, } from '@woocommerce/block-data'; import { registerPaymentMethod, registerExpressPaymentMethod, __experimentalDeRegisterPaymentMethod, __experimentalDeRegisterExpressPaymentMethod, } from '@woocommerce/blocks-registry'; import { default as fetchMock } from 'jest-fetch-mock'; /** * Internal dependencies */ import { CheckoutExpressPayment, SavedPaymentMethodOptions, } from '../../../blocks/cart-checkout-shared/payment-methods'; import { defaultCartState } from '../../cart/default-state'; const originalSelect = jest.requireActual( '@wordpress/data' ).select; jest.spyOn( wpDataFunctions, 'select' ).mockImplementation( ( storeName ) => { const originalStore = originalSelect( storeName ); if ( storeName === storeKey ) { return { ...originalStore, hasFinishedResolution: jest .fn() .mockImplementation( ( selectorName ) => { if ( selectorName === 'getCartTotals' ) { return true; } return originalStore.hasFinishedResolution( selectorName ); } ), }; } return originalStore; } ); jest.mock( '@woocommerce/settings', () => { const originalModule = jest.requireActual( '@woocommerce/settings' ); return { // @ts-ignore We know @woocommerce/settings is an object. ...originalModule, getSetting: ( setting, ...rest ) => { if ( setting === 'customerPaymentMethods' ) { return { cc: [ { method: { gateway: 'credit-card', last4: '4242', brand: 'Visa', }, expires: '12/22', is_default: true, tokenId: 1, }, ], }; } return originalModule.getSetting( setting, ...rest ); }, }; } ); const registerMockPaymentMethods = ( savedCards = true ) => { [ 'cheque', 'bacs' ].forEach( ( name ) => { registerPaymentMethod( { name, label: name, content:
A payment method
, edit:
A payment method
, icons: null, canMakePayment: () => true, supports: { features: [ 'products' ], }, ariaLabel: name, } ); } ); [ 'credit-card' ].forEach( ( name ) => { registerPaymentMethod( { name, label: name, content:
A payment method
, edit:
A payment method
, icons: null, canMakePayment: () => true, supports: { showSavedCards: savedCards, showSaveOption: true, features: [ 'products' ], }, ariaLabel: name, } ); } ); [ 'express-payment' ].forEach( ( name ) => { const Content = ( { onClose = () => void null, onClick = () => void null, } ) => { return ( <> ); }; registerExpressPaymentMethod( { name, content: , edit:
An express payment method
, canMakePayment: () => true, paymentMethodId: name, supports: { features: [ 'products' ], }, } ); } ); wpDataFunctions .dispatch( PAYMENT_STORE_KEY ) .__internalUpdateAvailablePaymentMethods(); }; const resetMockPaymentMethods = () => { [ 'cheque', 'bacs', 'credit-card' ].forEach( ( name ) => { __experimentalDeRegisterPaymentMethod( name ); } ); [ 'express-payment' ].forEach( ( name ) => { __experimentalDeRegisterExpressPaymentMethod( name ); } ); }; describe( 'Payment method data store selectors/thunks', () => { beforeEach( () => { act( () => { registerMockPaymentMethods( false ); fetchMock.mockResponse( ( req ) => { if ( req.url.match( /wc\/store\/v1\/cart/ ) ) { return Promise.resolve( JSON.stringify( previewCart ) ); } return Promise.resolve( '' ); } ); // need to clear the store resolution state between tests. wpDataFunctions.dispatch( storeKey ).invalidateResolutionForStore(); wpDataFunctions .dispatch( storeKey ) .receiveCart( defaultCartState.cartData ); } ); } ); afterEach( async () => { act( () => { resetMockPaymentMethods(); fetchMock.resetMocks(); } ); } ); it( 'toggles active payment method correctly for express payment activation and close', async () => { const TriggerActiveExpressPaymentMethod = () => { const activePaymentMethod = wpDataFunctions.useSelect( ( select ) => { return select( PAYMENT_STORE_KEY ).getActivePaymentMethod(); } ); return ( <> { 'Active Payment Method: ' + activePaymentMethod } ); }; const TestComponent = () => { return ; }; render( ); // should initialize by default the first payment method. await waitFor( () => { const activePaymentMethod = screen.queryByText( /Active Payment Method: credit-card/ ); expect( activePaymentMethod ).not.toBeNull(); } ); // Express payment method clicked. userEvent.click( screen.getByText( 'express-payment express payment method' ) ); await waitFor( () => { const activePaymentMethod = screen.queryByText( /Active Payment Method: express-payment/ ); expect( activePaymentMethod ).not.toBeNull(); } ); // Express payment method closed. userEvent.click( screen.getByText( 'express-payment express payment method close' ) ); await waitFor( () => { const activePaymentMethod = screen.queryByText( /Active Payment Method: credit-card/ ); expect( activePaymentMethod ).not.toBeNull(); } ); } ); } ); describe( 'Testing Payment Methods work correctly with saved cards turned on', () => { beforeEach( () => { act( () => { registerMockPaymentMethods( true ); fetchMock.mockResponse( ( req ) => { if ( req.url.match( /wc\/store\/v1\/cart/ ) ) { return Promise.resolve( JSON.stringify( previewCart ) ); } return Promise.resolve( '' ); } ); // need to clear the store resolution state between tests. wpDataFunctions.dispatch( storeKey ).invalidateResolutionForStore(); wpDataFunctions .dispatch( storeKey ) .receiveCart( defaultCartState.cartData ); } ); } ); afterEach( async () => { act( () => { resetMockPaymentMethods(); fetchMock.resetMocks(); } ); } ); it( 'resets saved payment method data after starting and closing an express payment method', async () => { const TriggerActiveExpressPaymentMethod = () => { const { activePaymentMethod, paymentMethodData } = wpDataFunctions.useSelect( ( select ) => { const store = select( PAYMENT_STORE_KEY ); return { activePaymentMethod: store.getActivePaymentMethod(), paymentMethodData: store.getPaymentMethodData(), }; } ); return ( <> void null } /> { 'Active Payment Method: ' + activePaymentMethod } { paymentMethodData[ 'wc-credit-card-payment-token' ] && ( credit-card token ) } ); }; const TestComponent = () => { return ; }; render( ); // Should initialize by default the default saved payment method. await waitFor( () => { const activePaymentMethod = screen.queryByText( /Active Payment Method: credit-card/ ); expect( activePaymentMethod ).not.toBeNull(); } ); await waitFor( () => { const creditCardToken = screen.queryByText( /credit-card token/ ); expect( creditCardToken ).not.toBeNull(); } ); // Express payment method clicked. userEvent.click( screen.getByText( 'express-payment express payment method' ) ); await waitFor( () => { const activePaymentMethod = screen.queryByText( /Active Payment Method: express-payment/ ); expect( activePaymentMethod ).not.toBeNull(); } ); await waitFor( () => { const creditCardToken = screen.queryByText( /credit-card token/ ); expect( creditCardToken ).toBeNull(); } ); // Express payment method closed. userEvent.click( screen.getByText( 'express-payment express payment method close' ) ); await waitFor( () => { const activePaymentMethod = screen.queryByText( /Active Payment Method: credit-card/ ); expect( activePaymentMethod ).not.toBeNull(); } ); await waitFor( () => { const creditCardToken = screen.queryByText( /credit-card token/ ); expect( creditCardToken ).not.toBeNull(); } ); } ); } );