woocommerce/plugins/woocommerce-blocks/assets/js/blocks/checkout/test/block.js

331 lines
9.7 KiB
JavaScript
Raw Normal View History

/**
* External dependencies
*/
import { render, screen, waitFor, act } from '@testing-library/react';
import { previewCart } from '@woocommerce/resource-previews';
import { dispatch } from '@wordpress/data';
import { CART_STORE_KEY, CHECKOUT_STORE_KEY } from '@woocommerce/block-data';
import { default as fetchMock } from 'jest-fetch-mock';
import { allSettings } from '@woocommerce/settings';
/**
* Internal dependencies
*/
import Fields from '../inner-blocks/checkout-fields-block/frontend';
import ExpressPayment from '../inner-blocks/checkout-express-payment-block/block';
import ContactInformation from '../inner-blocks/checkout-contact-information-block/frontend';
import ShippingMethod from '../inner-blocks/checkout-shipping-method-block/frontend';
import PickupOptions from '../inner-blocks/checkout-pickup-options-block/frontend';
import ShippingAddress from '../inner-blocks/checkout-shipping-address-block/frontend';
import BillingAddress from '../inner-blocks/checkout-billing-address-block/frontend';
import ShippingMethods from '../inner-blocks/checkout-shipping-methods-block/frontend';
import Payment from '../inner-blocks/checkout-payment-block/frontend';
import AdditionalInformation from '../inner-blocks/checkout-additional-information-block/frontend';
import OrderNote from '../inner-blocks/checkout-order-note-block/block';
import Terms from '../inner-blocks/checkout-terms-block/frontend';
import { termsCheckboxDefaultText } from '../inner-blocks/checkout-terms-block/constants';
import Actions from '../inner-blocks/checkout-actions-block/frontend';
import Totals from '../inner-blocks/checkout-totals-block/frontend';
import OrderSummary from '../inner-blocks/checkout-order-summary-block/frontend';
import CartItems from '../inner-blocks/checkout-order-summary-cart-items/frontend';
import CouponForm from '../inner-blocks/checkout-order-summary-coupon-form/block';
import Subtotal from '../inner-blocks/checkout-order-summary-subtotal/frontend';
import Fee from '../inner-blocks/checkout-order-summary-fee/frontend';
import Discount from '../inner-blocks/checkout-order-summary-discount/frontend';
import Shipping from '../inner-blocks/checkout-order-summary-shipping/frontend';
import Taxes from '../inner-blocks/checkout-order-summary-taxes/frontend';
import { defaultCartState } from '../../../data/cart/default-state';
import Checkout from '../block';
jest.mock( '@wordpress/compose', () => ( {
...jest.requireActual( '@wordpress/compose' ),
useResizeObserver: jest.fn().mockReturnValue( [ null, { width: 0 } ] ),
} ) );
global.ResizeObserver = jest.fn().mockImplementation( () => ( {
observe: jest.fn(),
unobserve: jest.fn(),
disconnect: jest.fn(),
} ) );
global.IntersectionObserver = jest.fn().mockImplementation( () => ( {
observe: jest.fn(),
unobserve: jest.fn(),
disconnect: jest.fn(),
} ) );
jest.mock( '@wordpress/element', () => {
return {
...jest.requireActual( '@wordpress/element' ),
useId: () => {
return 'mock-id';
},
};
} );
jest.mock( '../context', () => {
return {
...jest.requireActual( '../context' ),
useCheckoutBlockContext: jest.fn().mockReturnValue( {
showFormStepNumbers: false,
cartPageId: 0,
requireCompanyField: false,
requirePhoneField: false,
showApartmentField: false,
showCompanyField: false,
showOrderNotes: false,
showPhoneField: false,
showPolicyLinks: false,
showRateAfterTaxName: false,
showReturnToCart: false,
} ),
};
} );
import { useCheckoutBlockContext } from '../context';
const CheckoutBlock = () => {
return (
<Checkout attributes={ {} }>
<Fields>
<ShippingAddress />
<ExpressPayment />
<ContactInformation />
<BillingAddress />
<ShippingMethod />
<PickupOptions />
<ShippingMethods />
<Payment />
<AdditionalInformation />
<OrderNote />
<Terms checkbox={ true } text={ termsCheckboxDefaultText } />
<Actions />
</Fields>
<Totals>
<OrderSummary>
<CartItems />
<CouponForm />
<Subtotal />
<Fee />
<Discount />
<Shipping />
<Taxes />
</OrderSummary>
</Totals>
</Checkout>
);
};
describe( 'Testing Checkout', () => {
beforeEach( () => {
act( () => {
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.
dispatch( CART_STORE_KEY ).invalidateResolutionForStore();
dispatch( CART_STORE_KEY ).receiveCart( defaultCartState.cartData );
} );
} );
afterEach( () => {
fetchMock.resetMocks();
} );
it( 'Renders checkout if there are items in the cart', async () => {
render( <CheckoutBlock /> );
// TODO: Fix a recent deprecation of showSpinner prop of Button called in this component.
expect( console ).toHaveWarned();
await waitFor( () => expect( fetchMock ).toHaveBeenCalled() );
expect( screen.getByText( /Place Order/i ) ).toBeInTheDocument();
expect( fetchMock ).toHaveBeenCalledTimes( 1 );
} );
it( 'Renders the address card if the address is filled', async () => {
act( () => {
const cartWithAddress = {
...previewCart,
shipping_address: {
first_name: 'First Name',
last_name: 'Last Name',
company: '',
address_1: 'Address 1',
address_2: '',
city: 'Toronto',
state: 'ON',
postcode: 'M4W 1A6',
country: 'CA',
phone: '',
},
billing_address: {
first_name: 'First Name',
last_name: 'Last Name',
company: '',
address_1: 'Address 1',
address_2: '',
city: 'Toronto',
state: 'ON',
postcode: 'M4W 1A6',
country: 'CA',
phone: '',
email: '',
},
};
fetchMock.mockResponse( ( req ) => {
if ( req.url.match( /wc\/store\/v1\/cart/ ) ) {
return Promise.resolve( JSON.stringify( cartWithAddress ) );
}
return Promise.resolve( '' );
} );
} );
const { rerender } = render( <CheckoutBlock /> );
await waitFor( () => expect( fetchMock ).toHaveBeenCalled() );
expect(
screen.getByRole( 'button', { name: 'Edit address' } )
).toBeInTheDocument();
expect(
screen.getByText( 'Toronto ON M4W 1A6', {
selector: '.wc-block-components-address-card span',
} )
).toBeInTheDocument();
// Async is needed here despite the IDE warnings. Testing Library gives a warning if not awaited.
await act( () =>
dispatch( CART_STORE_KEY ).setShippingAddress( {
first_name: 'First Name JP',
last_name: 'Last Name JP',
company: '',
address_1: 'Address 1 JP',
address_2: '',
city: 'Kobe',
state: 'JP28',
postcode: '650-0000',
country: 'JP',
phone: '',
} )
);
rerender( <CheckoutBlock /> );
expect(
screen.getByText( 'Hyogo Kobe Address 1 JP', {
selector: '.wc-block-components-address-card span',
} )
).toBeInTheDocument();
// Testing the default address format
await act( () =>
dispatch( CART_STORE_KEY ).setShippingAddress( {
first_name: 'First Name GB',
last_name: 'Last Name GB',
company: '',
address_1: 'Address 1 GB',
address_2: '',
city: 'Liverpool',
state: 'Merseyside',
postcode: 'L1 0BP',
country: 'GB',
phone: '',
} )
);
rerender( <CheckoutBlock /> );
expect(
screen.getByText( 'Liverpool', {
selector: '.wc-block-components-address-card span',
} )
).toBeInTheDocument();
expect(
screen.getByText( 'Merseyside', {
selector: '.wc-block-components-address-card span',
} )
).toBeInTheDocument();
expect(
screen.getByText( 'L1 0BP', {
selector: '.wc-block-components-address-card span',
} )
).toBeInTheDocument();
expect( fetchMock ).toHaveBeenCalledTimes( 1 );
} );
it( 'Ensures checkbox labels have unique IDs', async () => {
await act( async () => {
// Set required settings
allSettings.checkoutAllowsGuest = true;
allSettings.checkoutAllowsSignup = true;
dispatch( CHECKOUT_STORE_KEY ).__internalSetCustomerId( 0 );
} );
// Render the CheckoutBlock
render( <CheckoutBlock /> );
// Wait for the component to fully load, assuming fetch calls or state updates
await waitFor( () => expect( fetchMock ).toHaveBeenCalled() );
// Query for all checkboxes
const checkboxes = screen.getAllByRole( 'checkbox' );
// Extract IDs from checkboxes
const ids = checkboxes.map( ( checkbox ) => checkbox.id );
// Ensure all IDs are unique
const uniqueIds = new Set( ids );
expect( uniqueIds.size ).toBe( ids.length );
await act( async () => {
// Restore initial settings
allSettings.checkoutAllowsGuest = undefined;
allSettings.checkoutAllowsSignup = undefined;
dispatch( CHECKOUT_STORE_KEY ).__internalSetCustomerId( 1 );
} );
} );
it( 'Ensures correct classes are applied to FormStep when step numbers are shown/hidden', async () => {
const mockReturnValue = {
showFormStepNumbers: false,
cartPageId: 0,
requireCompanyField: false,
requirePhoneField: false,
showApartmentField: false,
showCompanyField: false,
showOrderNotes: false,
showPhoneField: false,
showPolicyLinks: false,
showRateAfterTaxName: false,
showReturnToCart: false,
};
useCheckoutBlockContext.mockReturnValue( mockReturnValue );
// Render the CheckoutBlock
const { container, rerender } = render( <CheckoutBlock /> );
let formStepsWithNumber = container.querySelectorAll(
'.wc-block-components-checkout-step--with-step-number'
);
expect( formStepsWithNumber ).toHaveLength( 0 );
useCheckoutBlockContext.mockReturnValue( {
...mockReturnValue,
showFormStepNumbers: true,
} );
rerender( <CheckoutBlock /> );
formStepsWithNumber = container.querySelectorAll(
'.wc-block-components-checkout-step--with-step-number'
);
expect( formStepsWithNumber.length ).not.toBe( 0 );
} );
} );