[React 18] Use `createRoot().render()` instead of `render()` (#48843)
* Use createRoot().render() instead of render()
* Add changefile(s) from automation for the following project(s): woocommerce-blocks
* Replace `unmountComponentAtNode()` with `createRoot().unmount()`
* Adjust JS unit tests
* Remove obsolete import
* Remove “ReactDOM.render is no longer supported” check
* Update pnpm-lock.yaml
* Migrate JS unit test to React 18
* Revert "Update pnpm-lock.yaml"
This reverts commit 18bfc967aa
.
* Migrate JS unit test to React 18
* Migrate JS unit test to React 18
* Minor refactor
* Unskip previously skipped tests
* Fix JS unit test error related to root.unmount()
* Remove obsolete dependency
* Fix failing blocks e2e test
* Fix failing core e2e tests
* Optimise memoisation to prevent “Maximum update depth exceeded” error.
* Fix Filters JS tests in #48796 (#49973)
* Run cleanup in Rating Filter JS tests
* Run cleanup and wait user interactions in Stock Status Filter JS tests
* Rename constant
* Simplify conditional statement
* Introduce TS type
* Address lint error
* Rename TS type
* Optimise multiple TS types
---------
Co-authored-by: github-actions <github-actions@github.com>
Co-authored-by: Sam Seay <samueljseay@gmail.com>
Co-authored-by: Karol Manijak <20098064+kmanijak@users.noreply.github.com>
This commit is contained in:
parent
33222c20e2
commit
752273e6ce
|
@ -15,6 +15,7 @@ import {
|
||||||
hasInnerBlocks,
|
hasInnerBlocks,
|
||||||
} from '@woocommerce/blocks-checkout';
|
} from '@woocommerce/blocks-checkout';
|
||||||
import BlockErrorBoundary from '@woocommerce/base-components/block-error-boundary';
|
import BlockErrorBoundary from '@woocommerce/base-components/block-error-boundary';
|
||||||
|
import type { ReactRootWithContainer } from '@woocommerce/base-utils';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This file contains logic used on the frontend to convert DOM elements (saved by the block editor) to React
|
* This file contains logic used on the frontend to convert DOM elements (saved by the block editor) to React
|
||||||
|
@ -294,7 +295,7 @@ export const renderParentBlock = ( {
|
||||||
selector: string;
|
selector: string;
|
||||||
// Function to generate the props object for the block.
|
// Function to generate the props object for the block.
|
||||||
getProps: ( el: Element, i: number ) => Record< string, unknown >;
|
getProps: ( el: Element, i: number ) => Record< string, unknown >;
|
||||||
} ): void => {
|
} ): ReactRootWithContainer[] => {
|
||||||
/**
|
/**
|
||||||
* In addition to getProps, we need to render and return the children. This adds children to props.
|
* In addition to getProps, we need to render and return the children. This adds children to props.
|
||||||
*/
|
*/
|
||||||
|
@ -310,7 +311,7 @@ export const renderParentBlock = ( {
|
||||||
/**
|
/**
|
||||||
* The only difference between using renderParentBlock and renderFrontend is that here we provide children.
|
* The only difference between using renderParentBlock and renderFrontend is that here we provide children.
|
||||||
*/
|
*/
|
||||||
renderFrontend( {
|
return renderFrontend( {
|
||||||
Block,
|
Block,
|
||||||
selector,
|
selector,
|
||||||
getProps: getPropsWithChildren,
|
getProps: getPropsWithChildren,
|
||||||
|
|
|
@ -21,16 +21,11 @@ jest.mock( '@wordpress/element', () => {
|
||||||
};
|
};
|
||||||
} );
|
} );
|
||||||
|
|
||||||
const renderInCheckoutProvider = ( ui, options = { legacyRoot: true } ) => {
|
const renderInCheckoutProvider = ( ui, options = {} ) => {
|
||||||
const Wrapper = ( { children } ) => {
|
const Wrapper = ( { children } ) => {
|
||||||
return <CheckoutProvider>{ children }</CheckoutProvider>;
|
return <CheckoutProvider>{ children }</CheckoutProvider>;
|
||||||
};
|
};
|
||||||
const result = render( ui, { wrapper: Wrapper, ...options } );
|
const result = render( ui, { wrapper: Wrapper, ...options } );
|
||||||
// We need to switch to React 17 rendering to allow these tests to keep passing, but as a result the React
|
|
||||||
// rendering error will be shown.
|
|
||||||
expect( console ).toHaveErroredWith(
|
|
||||||
`Warning: ReactDOM.render is no longer supported in React 18. Use createRoot instead. Until you switch to the new API, your app will behave as if it's running React 17. Learn more: https://reactjs.org/link/switch-to-createroot`
|
|
||||||
);
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
@ -129,7 +124,7 @@ describe( 'Form Component', () => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
it( 'updates context value when interacting with form elements', async () => {
|
test( 'updates context value when interacting with form elements', async () => {
|
||||||
renderInCheckoutProvider(
|
renderInCheckoutProvider(
|
||||||
<>
|
<>
|
||||||
<WrappedAddressForm type="shipping" />
|
<WrappedAddressForm type="shipping" />
|
||||||
|
@ -155,7 +150,7 @@ describe( 'Form Component', () => {
|
||||||
);
|
);
|
||||||
} );
|
} );
|
||||||
|
|
||||||
it( 'input fields update when changing the country', async () => {
|
test( 'input fields update when changing the country', async () => {
|
||||||
renderInCheckoutProvider( <WrappedAddressForm type="shipping" /> );
|
renderInCheckoutProvider( <WrappedAddressForm type="shipping" /> );
|
||||||
|
|
||||||
await act( async () => {
|
await act( async () => {
|
||||||
|
@ -182,7 +177,7 @@ describe( 'Form Component', () => {
|
||||||
expect( screen.getByLabelText( /Postal code/ ) ).toBeInTheDocument();
|
expect( screen.getByLabelText( /Postal code/ ) ).toBeInTheDocument();
|
||||||
} );
|
} );
|
||||||
|
|
||||||
it( 'input values are reset after changing the country', async () => {
|
test( 'input values are reset after changing the country', async () => {
|
||||||
renderInCheckoutProvider( <WrappedAddressForm type="shipping" /> );
|
renderInCheckoutProvider( <WrappedAddressForm type="shipping" /> );
|
||||||
|
|
||||||
// First enter an address with no state, but fill the city.
|
// First enter an address with no state, but fill the city.
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
/**
|
/**
|
||||||
* External dependencies
|
* External dependencies
|
||||||
*/
|
*/
|
||||||
import { render, Suspense } from '@wordpress/element';
|
import { createRoot, useEffect, Suspense } from '@wordpress/element';
|
||||||
import BlockErrorBoundary from '@woocommerce/base-components/block-error-boundary';
|
import BlockErrorBoundary from '@woocommerce/base-components/block-error-boundary';
|
||||||
|
import type { Root } from 'react-dom/client';
|
||||||
|
|
||||||
// Some blocks take care of rendering their inner blocks automatically. For
|
// Some blocks take care of rendering their inner blocks automatically. For
|
||||||
// example, the empty cart. In those cases, we don't want to trigger the render
|
// example, the empty cart. In those cases, we don't want to trigger the render
|
||||||
|
@ -27,6 +28,11 @@ export type GetPropsFn<
|
||||||
TAttributes extends Record< string, unknown >
|
TAttributes extends Record< string, unknown >
|
||||||
> = ( el: HTMLElement, i: number ) => BlockProps< TProps, TAttributes >;
|
> = ( el: HTMLElement, i: number ) => BlockProps< TProps, TAttributes >;
|
||||||
|
|
||||||
|
export type ReactRootWithContainer = {
|
||||||
|
container: HTMLElement;
|
||||||
|
root: Root;
|
||||||
|
};
|
||||||
|
|
||||||
interface RenderBlockParams<
|
interface RenderBlockParams<
|
||||||
TProps extends Record< string, unknown >,
|
TProps extends Record< string, unknown >,
|
||||||
TAttributes extends Record< string, unknown >
|
TAttributes extends Record< string, unknown >
|
||||||
|
@ -55,22 +61,34 @@ export const renderBlock = <
|
||||||
attributes = {} as TAttributes,
|
attributes = {} as TAttributes,
|
||||||
props = {} as BlockProps< TProps, TAttributes >,
|
props = {} as BlockProps< TProps, TAttributes >,
|
||||||
errorBoundaryProps = {},
|
errorBoundaryProps = {},
|
||||||
}: RenderBlockParams< TProps, TAttributes > ): void => {
|
}: RenderBlockParams< TProps, TAttributes > ): Root => {
|
||||||
render(
|
const BlockWrapper = () => {
|
||||||
<BlockErrorBoundary { ...errorBoundaryProps }>
|
useEffect( () => {
|
||||||
<Suspense fallback={ <div className="wc-block-placeholder" /> }>
|
|
||||||
{ Block && <Block { ...props } attributes={ attributes } /> }
|
|
||||||
</Suspense>
|
|
||||||
</BlockErrorBoundary>,
|
|
||||||
container,
|
|
||||||
() => {
|
|
||||||
if ( container.classList ) {
|
if ( container.classList ) {
|
||||||
container.classList.remove( 'is-loading' );
|
container.classList.remove( 'is-loading' );
|
||||||
}
|
}
|
||||||
|
}, [] );
|
||||||
|
|
||||||
|
return (
|
||||||
|
<BlockErrorBoundary { ...errorBoundaryProps }>
|
||||||
|
<Suspense
|
||||||
|
fallback={
|
||||||
|
<div className="wc-block-placeholder">Loading...</div>
|
||||||
}
|
}
|
||||||
|
>
|
||||||
|
{ Block && (
|
||||||
|
<Block { ...props } attributes={ attributes } />
|
||||||
|
) }
|
||||||
|
</Suspense>
|
||||||
|
</BlockErrorBoundary>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const root = createRoot( container );
|
||||||
|
root.render( <BlockWrapper /> );
|
||||||
|
return root;
|
||||||
|
};
|
||||||
|
|
||||||
interface RenderBlockInContainersParams<
|
interface RenderBlockInContainersParams<
|
||||||
TProps extends Record< string, unknown >,
|
TProps extends Record< string, unknown >,
|
||||||
TAttributes extends Record< string, unknown >
|
TAttributes extends Record< string, unknown >
|
||||||
|
@ -99,10 +117,14 @@ const renderBlockInContainers = <
|
||||||
containers,
|
containers,
|
||||||
getProps = () => ( {} as BlockProps< TProps, TAttributes > ),
|
getProps = () => ( {} as BlockProps< TProps, TAttributes > ),
|
||||||
getErrorBoundaryProps = () => ( {} ),
|
getErrorBoundaryProps = () => ( {} ),
|
||||||
}: RenderBlockInContainersParams< TProps, TAttributes > ): void => {
|
}: RenderBlockInContainersParams<
|
||||||
|
TProps,
|
||||||
|
TAttributes
|
||||||
|
> ): ReactRootWithContainer[] => {
|
||||||
if ( containers.length === 0 ) {
|
if ( containers.length === 0 ) {
|
||||||
return;
|
return [];
|
||||||
}
|
}
|
||||||
|
const roots: ReactRootWithContainer[] = [];
|
||||||
|
|
||||||
// Use Array.forEach for IE11 compatibility.
|
// Use Array.forEach for IE11 compatibility.
|
||||||
Array.prototype.forEach.call( containers, ( el, i ) => {
|
Array.prototype.forEach.call( containers, ( el, i ) => {
|
||||||
|
@ -114,14 +136,19 @@ const renderBlockInContainers = <
|
||||||
...( props.attributes || {} ),
|
...( props.attributes || {} ),
|
||||||
};
|
};
|
||||||
|
|
||||||
renderBlock( {
|
roots.push( {
|
||||||
|
container: el,
|
||||||
|
root: renderBlock( {
|
||||||
Block,
|
Block,
|
||||||
container: el,
|
container: el,
|
||||||
props,
|
props,
|
||||||
attributes,
|
attributes,
|
||||||
errorBoundaryProps,
|
errorBoundaryProps,
|
||||||
|
} ),
|
||||||
} );
|
} );
|
||||||
} );
|
} );
|
||||||
|
|
||||||
|
return roots;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Given an element and a list of wrappers, check if the element is inside at
|
// Given an element and a list of wrappers, check if the element is inside at
|
||||||
|
@ -157,7 +184,10 @@ const renderBlockOutsideWrappers = <
|
||||||
getErrorBoundaryProps,
|
getErrorBoundaryProps,
|
||||||
selector,
|
selector,
|
||||||
wrappers,
|
wrappers,
|
||||||
}: RenderBlockOutsideWrappersParams< TProps, TAttributes > ): void => {
|
}: RenderBlockOutsideWrappersParams<
|
||||||
|
TProps,
|
||||||
|
TAttributes
|
||||||
|
> ): ReactRootWithContainer[] => {
|
||||||
const containers = document.body.querySelectorAll( selector );
|
const containers = document.body.querySelectorAll( selector );
|
||||||
// Filter out blocks inside the wrappers.
|
// Filter out blocks inside the wrappers.
|
||||||
if ( wrappers && wrappers.length > 0 ) {
|
if ( wrappers && wrappers.length > 0 ) {
|
||||||
|
@ -165,7 +195,8 @@ const renderBlockOutsideWrappers = <
|
||||||
return ! isElementInsideWrappers( el, wrappers );
|
return ! isElementInsideWrappers( el, wrappers );
|
||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
renderBlockInContainers( {
|
|
||||||
|
return renderBlockInContainers( {
|
||||||
Block,
|
Block,
|
||||||
containers,
|
containers,
|
||||||
getProps,
|
getProps,
|
||||||
|
@ -234,20 +265,21 @@ export const renderFrontend = <
|
||||||
props:
|
props:
|
||||||
| RenderBlockOutsideWrappersParams< TProps, TAttributes >
|
| RenderBlockOutsideWrappersParams< TProps, TAttributes >
|
||||||
| RenderBlockInsideWrapperParams< TProps, TAttributes >
|
| RenderBlockInsideWrapperParams< TProps, TAttributes >
|
||||||
): void => {
|
): ReactRootWithContainer[] => {
|
||||||
const wrappersToSkipOnLoad = document.body.querySelectorAll(
|
const wrappersToSkipOnLoad = document.body.querySelectorAll(
|
||||||
selectorsToSkipOnLoad.join( ',' )
|
selectorsToSkipOnLoad.join( ',' )
|
||||||
);
|
);
|
||||||
|
|
||||||
const { Block, getProps, getErrorBoundaryProps, selector } = props;
|
const { Block, getProps, getErrorBoundaryProps, selector } = props;
|
||||||
|
|
||||||
renderBlockOutsideWrappers( {
|
const roots = renderBlockOutsideWrappers( {
|
||||||
Block,
|
Block,
|
||||||
getProps,
|
getProps,
|
||||||
getErrorBoundaryProps,
|
getErrorBoundaryProps,
|
||||||
selector,
|
selector,
|
||||||
wrappers: wrappersToSkipOnLoad,
|
wrappers: wrappersToSkipOnLoad,
|
||||||
} );
|
} );
|
||||||
|
|
||||||
// For each wrapper, add an event listener to render the inner blocks when
|
// For each wrapper, add an event listener to render the inner blocks when
|
||||||
// `wc-blocks_render_blocks_frontend` event is triggered.
|
// `wc-blocks_render_blocks_frontend` event is triggered.
|
||||||
Array.prototype.forEach.call( wrappersToSkipOnLoad, ( wrapper ) => {
|
Array.prototype.forEach.call( wrappersToSkipOnLoad, ( wrapper ) => {
|
||||||
|
@ -255,6 +287,8 @@ export const renderFrontend = <
|
||||||
renderBlockInsideWrapper( { ...props, wrapper } );
|
renderBlockInsideWrapper( { ...props, wrapper } );
|
||||||
} );
|
} );
|
||||||
} );
|
} );
|
||||||
|
|
||||||
|
return roots;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default renderFrontend;
|
export default renderFrontend;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/**
|
/**
|
||||||
* External dependencies
|
* External dependencies
|
||||||
*/
|
*/
|
||||||
import { render, screen } from '@testing-library/react';
|
import { act, render, screen } from '@testing-library/react';
|
||||||
import * as hooks from '@woocommerce/base-context/hooks';
|
import * as hooks from '@woocommerce/base-context/hooks';
|
||||||
import userEvent from '@testing-library/user-event';
|
import userEvent from '@testing-library/user-event';
|
||||||
|
|
||||||
|
@ -106,14 +106,8 @@ const setup = ( params: SetupParams ) => {
|
||||||
results: stubCollectionData(),
|
results: stubCollectionData(),
|
||||||
isLoading: false,
|
isLoading: false,
|
||||||
} );
|
} );
|
||||||
const utils = render( <AttributeFilterBlock attributes={ attributes } />, {
|
const utils = render( <AttributeFilterBlock attributes={ attributes } /> );
|
||||||
legacyRoot: true,
|
|
||||||
} );
|
|
||||||
// We need to switch to React 17 rendering to allow these tests to keep passing, but as a result the React
|
|
||||||
// rendering error will be shown.
|
|
||||||
expect( console ).toHaveErroredWith(
|
|
||||||
`Warning: ReactDOM.render is no longer supported in React 18. Use createRoot instead. Until you switch to the new API, your app will behave as if it's running React 17. Learn more: https://reactjs.org/link/switch-to-createroot`
|
|
||||||
);
|
|
||||||
const applyButton = screen.getByRole( 'button', { name: /apply/i } );
|
const applyButton = screen.getByRole( 'button', { name: /apply/i } );
|
||||||
const smallAttributeCheckbox = screen.getByRole( 'checkbox', {
|
const smallAttributeCheckbox = screen.getByRole( 'checkbox', {
|
||||||
name: /small/i,
|
name: /small/i,
|
||||||
|
@ -164,8 +158,10 @@ describe( 'Filter by Attribute block', () => {
|
||||||
test( 'should enable Apply button when filter attributes are changed', async () => {
|
test( 'should enable Apply button when filter attributes are changed', async () => {
|
||||||
const { applyButton, smallAttributeCheckbox } =
|
const { applyButton, smallAttributeCheckbox } =
|
||||||
setupWithoutSelectedFilterAttributes();
|
setupWithoutSelectedFilterAttributes();
|
||||||
await userEvent.click( smallAttributeCheckbox );
|
|
||||||
|
|
||||||
|
await act( async () => {
|
||||||
|
await userEvent.click( smallAttributeCheckbox );
|
||||||
|
} );
|
||||||
expect( applyButton ).not.toBeDisabled();
|
expect( applyButton ).not.toBeDisabled();
|
||||||
} );
|
} );
|
||||||
} );
|
} );
|
||||||
|
@ -180,18 +176,25 @@ describe( 'Filter by Attribute block', () => {
|
||||||
test( 'should enable Apply button when filter attributes are changed', async () => {
|
test( 'should enable Apply button when filter attributes are changed', async () => {
|
||||||
const { applyButton, smallAttributeCheckbox } =
|
const { applyButton, smallAttributeCheckbox } =
|
||||||
setupWithSelectedFilterAttributes();
|
setupWithSelectedFilterAttributes();
|
||||||
await userEvent.click( smallAttributeCheckbox );
|
|
||||||
|
|
||||||
|
await act( async () => {
|
||||||
|
await userEvent.click( smallAttributeCheckbox );
|
||||||
|
} );
|
||||||
expect( applyButton ).not.toBeDisabled();
|
expect( applyButton ).not.toBeDisabled();
|
||||||
} );
|
} );
|
||||||
|
|
||||||
test( 'should disable Apply button when deselecting the same previously selected attribute', async () => {
|
test( 'should disable Apply button when deselecting the same previously selected attribute', async () => {
|
||||||
const { applyButton, smallAttributeCheckbox } =
|
const { applyButton, smallAttributeCheckbox } =
|
||||||
setupWithSelectedFilterAttributes( { filterSize: 'small' } );
|
setupWithSelectedFilterAttributes( { filterSize: 'small' } );
|
||||||
|
|
||||||
|
await act( async () => {
|
||||||
await userEvent.click( smallAttributeCheckbox );
|
await userEvent.click( smallAttributeCheckbox );
|
||||||
|
} );
|
||||||
expect( applyButton ).not.toBeDisabled();
|
expect( applyButton ).not.toBeDisabled();
|
||||||
|
|
||||||
|
await act( async () => {
|
||||||
await userEvent.click( smallAttributeCheckbox );
|
await userEvent.click( smallAttributeCheckbox );
|
||||||
|
} );
|
||||||
expect( applyButton ).toBeDisabled();
|
expect( applyButton ).toBeDisabled();
|
||||||
} );
|
} );
|
||||||
} );
|
} );
|
||||||
|
|
|
@ -6,6 +6,7 @@ import {
|
||||||
useState,
|
useState,
|
||||||
useEffect,
|
useEffect,
|
||||||
useCallback,
|
useCallback,
|
||||||
|
useMemo,
|
||||||
createInterpolateElement,
|
createInterpolateElement,
|
||||||
} from '@wordpress/element';
|
} from '@wordpress/element';
|
||||||
import { useShippingData, useStoreCart } from '@woocommerce/base-context/hooks';
|
import { useShippingData, useStoreCart } from '@woocommerce/base-context/hooks';
|
||||||
|
@ -138,10 +139,12 @@ const renderPickupLocation = (
|
||||||
const Block = (): JSX.Element | null => {
|
const Block = (): JSX.Element | null => {
|
||||||
const { shippingRates, selectShippingRate } = useShippingData();
|
const { shippingRates, selectShippingRate } = useShippingData();
|
||||||
|
|
||||||
// Get pickup locations from the first shipping package.
|
// Memoize pickup locations to prevent re-rendering when the shipping rates change.
|
||||||
const pickupLocations = ( shippingRates[ 0 ]?.shipping_rates || [] ).filter(
|
const pickupLocations = useMemo( () => {
|
||||||
|
return ( shippingRates[ 0 ]?.shipping_rates || [] ).filter(
|
||||||
isPackageRateCollectable
|
isPackageRateCollectable
|
||||||
);
|
);
|
||||||
|
}, [ shippingRates ] );
|
||||||
|
|
||||||
const [ selectedOption, setSelectedOption ] = useState< string >(
|
const [ selectedOption, setSelectedOption ] = useState< string >(
|
||||||
() => pickupLocations.find( ( rate ) => rate.selected )?.rate_id || ''
|
() => pickupLocations.find( ( rate ) => rate.selected )?.rate_id || ''
|
||||||
|
@ -168,13 +171,19 @@ const Block = (): JSX.Element | null => {
|
||||||
renderPickupLocation,
|
renderPickupLocation,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Update the selected option if there is no rate selected on mount.
|
|
||||||
useEffect( () => {
|
useEffect( () => {
|
||||||
if ( ! selectedOption && pickupLocations[ 0 ] ) {
|
if (
|
||||||
|
! selectedOption &&
|
||||||
|
pickupLocations[ 0 ] &&
|
||||||
|
selectedOption !== pickupLocations[ 0 ].rate_id
|
||||||
|
) {
|
||||||
setSelectedOption( pickupLocations[ 0 ].rate_id );
|
setSelectedOption( pickupLocations[ 0 ].rate_id );
|
||||||
onSelectRate( pickupLocations[ 0 ].rate_id );
|
onSelectRate( pickupLocations[ 0 ].rate_id );
|
||||||
}
|
}
|
||||||
}, [ onSelectRate, pickupLocations, selectedOption ] );
|
// Removing onSelectRate as it lead to an infinite loop when only one pickup location is available.
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [ pickupLocations, selectedOption ] );
|
||||||
|
|
||||||
const packageCount = getShippingRatesPackageCount( shippingRates );
|
const packageCount = getShippingRatesPackageCount( shippingRates );
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|
|
@ -26,6 +26,7 @@ import type {
|
||||||
} from '@woocommerce/types';
|
} from '@woocommerce/types';
|
||||||
import NoticeBanner from '@woocommerce/base-components/notice-banner';
|
import NoticeBanner from '@woocommerce/base-components/notice-banner';
|
||||||
import type { ReactElement } from 'react';
|
import type { ReactElement } from 'react';
|
||||||
|
import { useMemo } from '@wordpress/element';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renders a shipping rate control option.
|
* Renders a shipping rate control option.
|
||||||
|
@ -73,11 +74,13 @@ const Block = ( { noShippingPlaceholder = null } ): ReactElement | null => {
|
||||||
|
|
||||||
const { shippingAddress } = useCustomerData();
|
const { shippingAddress } = useCustomerData();
|
||||||
|
|
||||||
const filteredShippingRates = isCollectable
|
const filteredShippingRates = useMemo( () => {
|
||||||
|
return isCollectable
|
||||||
? shippingRates.map( ( shippingRatesPackage ) => {
|
? shippingRates.map( ( shippingRatesPackage ) => {
|
||||||
return {
|
return {
|
||||||
...shippingRatesPackage,
|
...shippingRatesPackage,
|
||||||
shipping_rates: shippingRatesPackage.shipping_rates.filter(
|
shipping_rates:
|
||||||
|
shippingRatesPackage.shipping_rates.filter(
|
||||||
( shippingRatesPackageRate ) =>
|
( shippingRatesPackageRate ) =>
|
||||||
! hasCollectableRate(
|
! hasCollectableRate(
|
||||||
shippingRatesPackageRate.method_id
|
shippingRatesPackageRate.method_id
|
||||||
|
@ -86,6 +89,7 @@ const Block = ( { noShippingPlaceholder = null } ): ReactElement | null => {
|
||||||
};
|
};
|
||||||
} )
|
} )
|
||||||
: shippingRates;
|
: shippingRates;
|
||||||
|
}, [ shippingRates, isCollectable ] );
|
||||||
|
|
||||||
if ( ! needsShipping ) {
|
if ( ! needsShipping ) {
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -20,15 +20,10 @@ import {
|
||||||
isCartResponseTotals,
|
isCartResponseTotals,
|
||||||
isNumber,
|
isNumber,
|
||||||
} from '@woocommerce/types';
|
} from '@woocommerce/types';
|
||||||
import {
|
import { useCallback, useEffect, useRef, useState } from '@wordpress/element';
|
||||||
unmountComponentAtNode,
|
|
||||||
useCallback,
|
|
||||||
useEffect,
|
|
||||||
useRef,
|
|
||||||
useState,
|
|
||||||
} from '@wordpress/element';
|
|
||||||
import { sprintf, _n } from '@wordpress/i18n';
|
import { sprintf, _n } from '@wordpress/i18n';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
|
import type { ReactRootWithContainer } from '@woocommerce/base-utils';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal dependencies
|
* Internal dependencies
|
||||||
|
@ -110,6 +105,8 @@ const MiniCartBlock = ( attributes: Props ): JSX.Element => {
|
||||||
setContentsNode( node );
|
setContentsNode( node );
|
||||||
}, [] );
|
}, [] );
|
||||||
|
|
||||||
|
const rootRef = useRef< ReactRootWithContainer[] | null >( null );
|
||||||
|
|
||||||
useEffect( () => {
|
useEffect( () => {
|
||||||
const body = document.querySelector( 'body' );
|
const body = document.querySelector( 'body' );
|
||||||
if ( body ) {
|
if ( body ) {
|
||||||
|
@ -134,7 +131,7 @@ const MiniCartBlock = ( attributes: Props ): JSX.Element => {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if ( isOpen ) {
|
if ( isOpen ) {
|
||||||
renderParentBlock( {
|
const renderedBlock = renderParentBlock( {
|
||||||
Block: MiniCartContentsBlock,
|
Block: MiniCartContentsBlock,
|
||||||
blockName,
|
blockName,
|
||||||
getProps: ( el: Element ) => {
|
getProps: ( el: Element ) => {
|
||||||
|
@ -151,16 +148,25 @@ const MiniCartBlock = ( attributes: Props ): JSX.Element => {
|
||||||
selector: '.wp-block-woocommerce-mini-cart-contents',
|
selector: '.wp-block-woocommerce-mini-cart-contents',
|
||||||
blockMap: getRegisteredBlockComponents( blockName ),
|
blockMap: getRegisteredBlockComponents( blockName ),
|
||||||
} );
|
} );
|
||||||
|
rootRef.current = renderedBlock;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
if ( contentsNode instanceof Element && isOpen ) {
|
if ( contentsNode instanceof Element && isOpen ) {
|
||||||
const container = contentsNode.querySelector(
|
const unmountingContainer = contentsNode.querySelector(
|
||||||
'.wp-block-woocommerce-mini-cart-contents'
|
'.wp-block-woocommerce-mini-cart-contents'
|
||||||
);
|
);
|
||||||
if ( container ) {
|
|
||||||
unmountComponentAtNode( container );
|
if ( unmountingContainer ) {
|
||||||
|
const foundRoot = rootRef?.current?.find(
|
||||||
|
( { container } ) => unmountingContainer === container
|
||||||
|
);
|
||||||
|
if ( typeof foundRoot?.root?.unmount === 'function' ) {
|
||||||
|
setTimeout( () => {
|
||||||
|
foundRoot.root.unmount();
|
||||||
|
} );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -111,13 +111,6 @@ describe( 'Testing Mini-Cart', () => {
|
||||||
await waitFor( () =>
|
await waitFor( () =>
|
||||||
expect( screen.getByText( /your cart/i ) ).toBeInTheDocument()
|
expect( screen.getByText( /your cart/i ) ).toBeInTheDocument()
|
||||||
);
|
);
|
||||||
|
|
||||||
// The opening of the drawer uses deprecated ReactDOM.render.
|
|
||||||
expect( console ).toHaveErroredWith(
|
|
||||||
`Warning: ReactDOM.render is no longer supported in React 18. Use createRoot instead. Until you switch to the new API, your app will behave as if it's running React 17. Learn more: https://reactjs.org/link/switch-to-createroot%s`,
|
|
||||||
// The stack trace
|
|
||||||
expect.any( String )
|
|
||||||
);
|
|
||||||
} );
|
} );
|
||||||
|
|
||||||
it( 'closes the drawer when clicking on the close button', async () => {
|
it( 'closes the drawer when clicking on the close button', async () => {
|
||||||
|
@ -132,9 +125,11 @@ describe( 'Testing Mini-Cart', () => {
|
||||||
|
|
||||||
// Close drawer.
|
// Close drawer.
|
||||||
let closeButton = null;
|
let closeButton = null;
|
||||||
|
|
||||||
await waitFor( () => {
|
await waitFor( () => {
|
||||||
closeButton = screen.getByLabelText( /close/i );
|
closeButton = screen.getByLabelText( /close/i );
|
||||||
} );
|
} );
|
||||||
|
|
||||||
if ( closeButton ) {
|
if ( closeButton ) {
|
||||||
await act( async () => {
|
await act( async () => {
|
||||||
await user.click( closeButton );
|
await user.click( closeButton );
|
||||||
|
@ -146,13 +141,6 @@ describe( 'Testing Mini-Cart', () => {
|
||||||
screen.queryByText( /your cart/i )
|
screen.queryByText( /your cart/i )
|
||||||
).not.toBeInTheDocument();
|
).not.toBeInTheDocument();
|
||||||
} );
|
} );
|
||||||
|
|
||||||
// The opening of the drawer uses deprecated ReactDOM.render.
|
|
||||||
expect( console ).toHaveErroredWith(
|
|
||||||
`Warning: ReactDOM.render is no longer supported in React 18. Use createRoot instead. Until you switch to the new API, your app will behave as if it's running React 17. Learn more: https://reactjs.org/link/switch-to-createroot%s`,
|
|
||||||
// The stack trace
|
|
||||||
expect.any( String )
|
|
||||||
);
|
|
||||||
} );
|
} );
|
||||||
|
|
||||||
it( 'renders empty cart if there are no items in the cart', async () => {
|
it( 'renders empty cart if there are no items in the cart', async () => {
|
||||||
|
@ -167,13 +155,6 @@ describe( 'Testing Mini-Cart', () => {
|
||||||
} );
|
} );
|
||||||
|
|
||||||
expect( fetchMock ).toHaveBeenCalledTimes( 1 );
|
expect( fetchMock ).toHaveBeenCalledTimes( 1 );
|
||||||
|
|
||||||
// The opening of the drawer uses deprecated ReactDOM.render.
|
|
||||||
expect( console ).toHaveErroredWith(
|
|
||||||
`Warning: ReactDOM.render is no longer supported in React 18. Use createRoot instead. Until you switch to the new API, your app will behave as if it's running React 17. Learn more: https://reactjs.org/link/switch-to-createroot%s`,
|
|
||||||
// The stack trace
|
|
||||||
expect.any( String )
|
|
||||||
);
|
|
||||||
} );
|
} );
|
||||||
|
|
||||||
it( 'updates contents when removed from cart event is triggered', async () => {
|
it( 'updates contents when removed from cart event is triggered', async () => {
|
||||||
|
|
|
@ -2,7 +2,14 @@
|
||||||
* External dependencies
|
* External dependencies
|
||||||
*/
|
*/
|
||||||
import React from '@wordpress/element';
|
import React from '@wordpress/element';
|
||||||
import { render, screen, waitFor, within } from '@testing-library/react';
|
import {
|
||||||
|
act,
|
||||||
|
cleanup,
|
||||||
|
render,
|
||||||
|
screen,
|
||||||
|
waitFor,
|
||||||
|
within,
|
||||||
|
} from '@testing-library/react';
|
||||||
import * as hooks from '@woocommerce/base-context/hooks';
|
import * as hooks from '@woocommerce/base-context/hooks';
|
||||||
import userEvent from '@testing-library/user-event';
|
import userEvent from '@testing-library/user-event';
|
||||||
|
|
||||||
|
@ -59,6 +66,7 @@ const selectors = {
|
||||||
};
|
};
|
||||||
|
|
||||||
const setup = ( params: SetupParams ) => {
|
const setup = ( params: SetupParams ) => {
|
||||||
|
cleanup();
|
||||||
const url = `http://woo.local/${
|
const url = `http://woo.local/${
|
||||||
params.filterRating ? '?rating_filter=' + params.filterRating : ''
|
params.filterRating ? '?rating_filter=' + params.filterRating : ''
|
||||||
}`;
|
}`;
|
||||||
|
@ -78,14 +86,7 @@ const setup = ( params: SetupParams ) => {
|
||||||
} );
|
} );
|
||||||
|
|
||||||
const { container, ...utils } = render(
|
const { container, ...utils } = render(
|
||||||
<RatingFilterBlock attributes={ attributes } />,
|
<RatingFilterBlock attributes={ attributes } />
|
||||||
{ legacyRoot: true }
|
|
||||||
);
|
|
||||||
|
|
||||||
// We need to switch to React 17 rendering to allow these tests to keep passing, but as a result the React
|
|
||||||
// rendering error will be shown.
|
|
||||||
expect( console ).toHaveErroredWith(
|
|
||||||
`Warning: ReactDOM.render is no longer supported in React 18. Use createRoot instead. Until you switch to the new API, your app will behave as if it's running React 17. Learn more: https://reactjs.org/link/switch-to-createroot`
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const getList = () => container.querySelector( selectors.list );
|
const getList = () => container.querySelector( selectors.list );
|
||||||
|
@ -203,11 +204,13 @@ describe( 'Filter by Rating block', () => {
|
||||||
describe( 'Single choice Dropdown', () => {
|
describe( 'Single choice Dropdown', () => {
|
||||||
test( 'renders dropdown', () => {
|
test( 'renders dropdown', () => {
|
||||||
const { getDropdown, getList } = setupSingleChoiceDropdown();
|
const { getDropdown, getList } = setupSingleChoiceDropdown();
|
||||||
|
|
||||||
expect( getDropdown() ).toBeInTheDocument();
|
expect( getDropdown() ).toBeInTheDocument();
|
||||||
expect( getList() ).toBeNull();
|
expect( getList() ).toBeNull();
|
||||||
} );
|
} );
|
||||||
|
|
||||||
test( 'renders chips based on URL params', () => {
|
test( 'renders chips based on URL params', async () => {
|
||||||
|
await waitFor( async () => {
|
||||||
const ratingParam = '2';
|
const ratingParam = '2';
|
||||||
const { getRating2Chips, getRating4Chips, getRating5Chips } =
|
const { getRating2Chips, getRating4Chips, getRating5Chips } =
|
||||||
setupSingleChoiceDropdown( ratingParam );
|
setupSingleChoiceDropdown( ratingParam );
|
||||||
|
@ -216,8 +219,10 @@ describe( 'Filter by Rating block', () => {
|
||||||
expect( getRating4Chips() ).toBeNull();
|
expect( getRating4Chips() ).toBeNull();
|
||||||
expect( getRating5Chips() ).toBeNull();
|
expect( getRating5Chips() ).toBeNull();
|
||||||
} );
|
} );
|
||||||
|
} );
|
||||||
|
|
||||||
test( 'replaces chosen option when another one is clicked', async () => {
|
test( 'replaces chosen option when another one is clicked', async () => {
|
||||||
|
await waitFor( async () => {
|
||||||
const ratingParam = '2';
|
const ratingParam = '2';
|
||||||
const {
|
const {
|
||||||
getDropdown,
|
getDropdown,
|
||||||
|
@ -245,8 +250,10 @@ describe( 'Filter by Rating block', () => {
|
||||||
expect( getRating2Chips() ).toBeNull();
|
expect( getRating2Chips() ).toBeNull();
|
||||||
expect( getRating4Chips() ).toBeInTheDocument();
|
expect( getRating4Chips() ).toBeInTheDocument();
|
||||||
} );
|
} );
|
||||||
|
} );
|
||||||
|
|
||||||
test( 'removes the option when the X button is clicked', async () => {
|
test( 'removes the option when the X button is clicked', async () => {
|
||||||
|
await waitFor( async () => {
|
||||||
const ratingParam = '4';
|
const ratingParam = '4';
|
||||||
const {
|
const {
|
||||||
getRating2Chips,
|
getRating2Chips,
|
||||||
|
@ -273,6 +280,7 @@ describe( 'Filter by Rating block', () => {
|
||||||
expect( getRating5Chips() ).toBeNull();
|
expect( getRating5Chips() ).toBeNull();
|
||||||
} );
|
} );
|
||||||
} );
|
} );
|
||||||
|
} );
|
||||||
|
|
||||||
describe( 'Multiple choice Dropdown', () => {
|
describe( 'Multiple choice Dropdown', () => {
|
||||||
test( 'renders dropdown', () => {
|
test( 'renders dropdown', () => {
|
||||||
|
@ -281,7 +289,8 @@ describe( 'Filter by Rating block', () => {
|
||||||
expect( getList() ).toBeNull();
|
expect( getList() ).toBeNull();
|
||||||
} );
|
} );
|
||||||
|
|
||||||
test( 'renders chips based on URL params', () => {
|
test( 'renders chips based on URL params', async () => {
|
||||||
|
await waitFor( async () => {
|
||||||
const ratingParam = '2,4';
|
const ratingParam = '2,4';
|
||||||
const { getRating2Chips, getRating4Chips, getRating5Chips } =
|
const { getRating2Chips, getRating4Chips, getRating5Chips } =
|
||||||
setupMultipleChoiceDropdown( ratingParam );
|
setupMultipleChoiceDropdown( ratingParam );
|
||||||
|
@ -290,8 +299,10 @@ describe( 'Filter by Rating block', () => {
|
||||||
expect( getRating4Chips() ).toBeInTheDocument();
|
expect( getRating4Chips() ).toBeInTheDocument();
|
||||||
expect( getRating5Chips() ).toBeNull();
|
expect( getRating5Chips() ).toBeNull();
|
||||||
} );
|
} );
|
||||||
|
} );
|
||||||
|
|
||||||
test( 'adds chosen option to another one that is clicked', async () => {
|
test( 'adds chosen option to another one that is clicked', async () => {
|
||||||
|
await waitFor( async () => {
|
||||||
const ratingParam = '2';
|
const ratingParam = '2';
|
||||||
const {
|
const {
|
||||||
getDropdown,
|
getDropdown,
|
||||||
|
@ -333,8 +344,10 @@ describe( 'Filter by Rating block', () => {
|
||||||
expect( getRating4Chips() ).toBeInTheDocument();
|
expect( getRating4Chips() ).toBeInTheDocument();
|
||||||
expect( getRating5Chips() ).toBeInTheDocument();
|
expect( getRating5Chips() ).toBeInTheDocument();
|
||||||
} );
|
} );
|
||||||
|
} );
|
||||||
|
|
||||||
test( 'removes the option when the X button is clicked', async () => {
|
test( 'removes the option when the X button is clicked', async () => {
|
||||||
|
await waitFor( async () => {
|
||||||
const ratingParam = '2,4,5';
|
const ratingParam = '2,4,5';
|
||||||
const {
|
const {
|
||||||
getRating2Chips,
|
getRating2Chips,
|
||||||
|
@ -360,6 +373,7 @@ describe( 'Filter by Rating block', () => {
|
||||||
expect( getRating5Chips() ).toBeInTheDocument();
|
expect( getRating5Chips() ).toBeInTheDocument();
|
||||||
} );
|
} );
|
||||||
} );
|
} );
|
||||||
|
} );
|
||||||
|
|
||||||
describe( 'Single choice List', () => {
|
describe( 'Single choice List', () => {
|
||||||
test( 'renders list', () => {
|
test( 'renders list', () => {
|
||||||
|
@ -368,7 +382,8 @@ describe( 'Filter by Rating block', () => {
|
||||||
expect( getList() ).toBeInTheDocument();
|
expect( getList() ).toBeInTheDocument();
|
||||||
} );
|
} );
|
||||||
|
|
||||||
test( 'renders checked options based on URL params', () => {
|
test( 'renders checked options based on URL params', async () => {
|
||||||
|
await waitFor( async () => {
|
||||||
const ratingParam = '4';
|
const ratingParam = '4';
|
||||||
const {
|
const {
|
||||||
getRating2Checkbox,
|
getRating2Checkbox,
|
||||||
|
@ -380,8 +395,10 @@ describe( 'Filter by Rating block', () => {
|
||||||
expect( getRating4Checkbox()?.checked ).toBeTruthy();
|
expect( getRating4Checkbox()?.checked ).toBeTruthy();
|
||||||
expect( getRating5Checkbox()?.checked ).toBeFalsy();
|
expect( getRating5Checkbox()?.checked ).toBeFalsy();
|
||||||
} );
|
} );
|
||||||
|
} );
|
||||||
|
|
||||||
test( 'replaces chosen option when another one is clicked', async () => {
|
test( 'replaces chosen option when another one is clicked', async () => {
|
||||||
|
await waitFor( async () => {
|
||||||
const ratingParam = '2';
|
const ratingParam = '2';
|
||||||
const {
|
const {
|
||||||
getRating2Checkbox,
|
getRating2Checkbox,
|
||||||
|
@ -396,15 +413,19 @@ describe( 'Filter by Rating block', () => {
|
||||||
const rating4checkbox = getRating4Checkbox();
|
const rating4checkbox = getRating4Checkbox();
|
||||||
|
|
||||||
if ( rating4checkbox ) {
|
if ( rating4checkbox ) {
|
||||||
|
await act( async () => {
|
||||||
await userEvent.click( rating4checkbox );
|
await userEvent.click( rating4checkbox );
|
||||||
|
} );
|
||||||
}
|
}
|
||||||
|
|
||||||
expect( getRating2Checkbox()?.checked ).toBeFalsy();
|
expect( getRating2Checkbox()?.checked ).toBeFalsy();
|
||||||
expect( getRating4Checkbox()?.checked ).toBeTruthy();
|
expect( getRating4Checkbox()?.checked ).toBeTruthy();
|
||||||
expect( getRating5Checkbox()?.checked ).toBeFalsy();
|
expect( getRating5Checkbox()?.checked ).toBeFalsy();
|
||||||
} );
|
} );
|
||||||
|
} );
|
||||||
|
|
||||||
test( 'removes the option when it is clicked again', async () => {
|
test( 'removes the option when it is clicked again', async () => {
|
||||||
|
await waitFor( async () => {
|
||||||
const ratingParam = '4';
|
const ratingParam = '4';
|
||||||
const {
|
const {
|
||||||
getRating2Checkbox,
|
getRating2Checkbox,
|
||||||
|
@ -422,7 +443,6 @@ describe( 'Filter by Rating block', () => {
|
||||||
await userEvent.click( rating4checkbox );
|
await userEvent.click( rating4checkbox );
|
||||||
}
|
}
|
||||||
|
|
||||||
await waitFor( () => {
|
|
||||||
expect( getRating2Checkbox()?.checked ).toBeFalsy();
|
expect( getRating2Checkbox()?.checked ).toBeFalsy();
|
||||||
expect( getRating4Checkbox()?.checked ).toBeFalsy();
|
expect( getRating4Checkbox()?.checked ).toBeFalsy();
|
||||||
expect( getRating5Checkbox()?.checked ).toBeFalsy();
|
expect( getRating5Checkbox()?.checked ).toBeFalsy();
|
||||||
|
@ -437,7 +457,8 @@ describe( 'Filter by Rating block', () => {
|
||||||
expect( getList() ).toBeInTheDocument();
|
expect( getList() ).toBeInTheDocument();
|
||||||
} );
|
} );
|
||||||
|
|
||||||
test( 'renders chips based on URL params', () => {
|
test( 'renders chips based on URL params', async () => {
|
||||||
|
await waitFor( async () => {
|
||||||
const ratingParam = '4,5';
|
const ratingParam = '4,5';
|
||||||
const {
|
const {
|
||||||
getRating2Checkbox,
|
getRating2Checkbox,
|
||||||
|
@ -449,8 +470,10 @@ describe( 'Filter by Rating block', () => {
|
||||||
expect( getRating4Checkbox()?.checked ).toBeTruthy();
|
expect( getRating4Checkbox()?.checked ).toBeTruthy();
|
||||||
expect( getRating5Checkbox()?.checked ).toBeTruthy();
|
expect( getRating5Checkbox()?.checked ).toBeTruthy();
|
||||||
} );
|
} );
|
||||||
|
} );
|
||||||
|
|
||||||
test( 'adds chosen option to another one that is clicked', async () => {
|
test( 'adds chosen option to another one that is clicked', async () => {
|
||||||
|
await waitFor( async () => {
|
||||||
const ratingParam = '2,4';
|
const ratingParam = '2,4';
|
||||||
const {
|
const {
|
||||||
getRating2Checkbox,
|
getRating2Checkbox,
|
||||||
|
@ -468,7 +491,6 @@ describe( 'Filter by Rating block', () => {
|
||||||
await userEvent.click( rating5checkbox );
|
await userEvent.click( rating5checkbox );
|
||||||
}
|
}
|
||||||
|
|
||||||
await waitFor( () => {
|
|
||||||
expect( getRating2Checkbox()?.checked ).toBeTruthy();
|
expect( getRating2Checkbox()?.checked ).toBeTruthy();
|
||||||
expect( getRating4Checkbox()?.checked ).toBeTruthy();
|
expect( getRating4Checkbox()?.checked ).toBeTruthy();
|
||||||
expect( getRating5Checkbox()?.checked ).toBeTruthy();
|
expect( getRating5Checkbox()?.checked ).toBeTruthy();
|
||||||
|
@ -476,6 +498,7 @@ describe( 'Filter by Rating block', () => {
|
||||||
} );
|
} );
|
||||||
|
|
||||||
test( 'removes the option when it is clicked again', async () => {
|
test( 'removes the option when it is clicked again', async () => {
|
||||||
|
await waitFor( async () => {
|
||||||
const ratingParam = '2,4';
|
const ratingParam = '2,4';
|
||||||
const {
|
const {
|
||||||
getRating2Checkbox,
|
getRating2Checkbox,
|
||||||
|
@ -493,7 +516,6 @@ describe( 'Filter by Rating block', () => {
|
||||||
await userEvent.click( rating2checkbox );
|
await userEvent.click( rating2checkbox );
|
||||||
}
|
}
|
||||||
|
|
||||||
await waitFor( () => {
|
|
||||||
expect( getRating2Checkbox()?.checked ).toBeFalsy();
|
expect( getRating2Checkbox()?.checked ).toBeFalsy();
|
||||||
expect( getRating4Checkbox()?.checked ).toBeTruthy();
|
expect( getRating4Checkbox()?.checked ).toBeTruthy();
|
||||||
expect( getRating5Checkbox()?.checked ).toBeFalsy();
|
expect( getRating5Checkbox()?.checked ).toBeFalsy();
|
||||||
|
|
|
@ -2,7 +2,14 @@
|
||||||
* External dependencies
|
* External dependencies
|
||||||
*/
|
*/
|
||||||
import React from '@wordpress/element';
|
import React from '@wordpress/element';
|
||||||
import { act, render, screen, within, waitFor } from '@testing-library/react';
|
import {
|
||||||
|
act,
|
||||||
|
cleanup,
|
||||||
|
render,
|
||||||
|
screen,
|
||||||
|
within,
|
||||||
|
waitFor,
|
||||||
|
} from '@testing-library/react';
|
||||||
import { default as fetchMock } from 'jest-fetch-mock';
|
import { default as fetchMock } from 'jest-fetch-mock';
|
||||||
import userEvent from '@testing-library/user-event';
|
import userEvent from '@testing-library/user-event';
|
||||||
|
|
||||||
|
@ -68,6 +75,7 @@ const selectors = {
|
||||||
};
|
};
|
||||||
|
|
||||||
const setup = ( params: SetupParams = {} ) => {
|
const setup = ( params: SetupParams = {} ) => {
|
||||||
|
cleanup();
|
||||||
const url = `http://woo.local/${
|
const url = `http://woo.local/${
|
||||||
params.filterStock ? '?filter_stock_status=' + params.filterStock : ''
|
params.filterStock ? '?filter_stock_status=' + params.filterStock : ''
|
||||||
}`;
|
}`;
|
||||||
|
@ -87,14 +95,7 @@ const setup = ( params: SetupParams = {} ) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const { container, ...utils } = render(
|
const { container, ...utils } = render(
|
||||||
<Block attributes={ attributes } />,
|
<Block attributes={ attributes } />
|
||||||
{ legacyRoot: true }
|
|
||||||
);
|
|
||||||
|
|
||||||
// We need to switch to React 17 rendering to allow these tests to keep passing, but as a result the React
|
|
||||||
// rendering error will be shown.
|
|
||||||
expect( console ).toHaveErroredWith(
|
|
||||||
`Warning: ReactDOM.render is no longer supported in React 18. Use createRoot instead. Until you switch to the new API, your app will behave as if it's running React 17. Learn more: https://reactjs.org/link/switch-to-createroot`
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const getList = () => container.querySelector( selectors.list );
|
const getList = () => container.querySelector( selectors.list );
|
||||||
|
@ -227,7 +228,7 @@ describe( 'Filter by Stock block', () => {
|
||||||
fetchMock.resetMocks();
|
fetchMock.resetMocks();
|
||||||
} );
|
} );
|
||||||
|
|
||||||
it( 'renders the stock filter block', async () => {
|
test( 'renders the stock filter block', async () => {
|
||||||
const { container } = setup( {
|
const { container } = setup( {
|
||||||
showFilterButton: false,
|
showFilterButton: false,
|
||||||
showCounts: false,
|
showCounts: false,
|
||||||
|
@ -235,7 +236,7 @@ describe( 'Filter by Stock block', () => {
|
||||||
expect( container ).toMatchSnapshot();
|
expect( container ).toMatchSnapshot();
|
||||||
} );
|
} );
|
||||||
|
|
||||||
it( 'renders the stock filter block with the filter button', async () => {
|
test( 'renders the stock filter block with the filter button', async () => {
|
||||||
const { container } = setup( {
|
const { container } = setup( {
|
||||||
showFilterButton: true,
|
showFilterButton: true,
|
||||||
showCounts: false,
|
showCounts: false,
|
||||||
|
@ -243,7 +244,7 @@ describe( 'Filter by Stock block', () => {
|
||||||
expect( container ).toMatchSnapshot();
|
expect( container ).toMatchSnapshot();
|
||||||
} );
|
} );
|
||||||
|
|
||||||
it( 'renders the stock filter block with the product counts', async () => {
|
test( 'renders the stock filter block with the product counts', async () => {
|
||||||
const { container } = setup( {
|
const { container } = setup( {
|
||||||
showFilterButton: false,
|
showFilterButton: false,
|
||||||
showCounts: true,
|
showCounts: true,
|
||||||
|
@ -254,21 +255,28 @@ describe( 'Filter by Stock block', () => {
|
||||||
describe( 'Single choice Dropdown', () => {
|
describe( 'Single choice Dropdown', () => {
|
||||||
test( 'renders dropdown', () => {
|
test( 'renders dropdown', () => {
|
||||||
const { getDropdown, getList } = setupSingleChoiceDropdown();
|
const { getDropdown, getList } = setupSingleChoiceDropdown();
|
||||||
|
|
||||||
expect( getDropdown() ).toBeInTheDocument();
|
expect( getDropdown() ).toBeInTheDocument();
|
||||||
expect( getList() ).toBeNull();
|
expect( getList() ).toBeNull();
|
||||||
} );
|
} );
|
||||||
|
|
||||||
test( 'renders chips based on URL params', () => {
|
test( 'renders chips based on URL params', async () => {
|
||||||
|
await waitFor( async () => {
|
||||||
const ratingParam = 'instock';
|
const ratingParam = 'instock';
|
||||||
const { getInStockChips, getOutOfStockChips, getOnBackorderChips } =
|
const {
|
||||||
setupSingleChoiceDropdown( ratingParam );
|
getInStockChips,
|
||||||
|
getOutOfStockChips,
|
||||||
|
getOnBackorderChips,
|
||||||
|
} = setupSingleChoiceDropdown( ratingParam );
|
||||||
|
|
||||||
expect( getInStockChips() ).toBeInTheDocument();
|
expect( getInStockChips() ).toBeInTheDocument();
|
||||||
expect( getOutOfStockChips() ).toBeNull();
|
expect( getOutOfStockChips() ).toBeNull();
|
||||||
expect( getOnBackorderChips() ).toBeNull();
|
expect( getOnBackorderChips() ).toBeNull();
|
||||||
} );
|
} );
|
||||||
|
} );
|
||||||
|
|
||||||
test( 'replaces chosen option when another one is clicked', async () => {
|
test( 'replaces chosen option when another one is clicked', async () => {
|
||||||
|
await waitFor( async () => {
|
||||||
const user = userEvent.setup();
|
const user = userEvent.setup();
|
||||||
const ratingParam = 'instock';
|
const ratingParam = 'instock';
|
||||||
const {
|
const {
|
||||||
|
@ -300,8 +308,10 @@ describe( 'Filter by Stock block', () => {
|
||||||
expect( getInStockChips() ).toBeNull();
|
expect( getInStockChips() ).toBeNull();
|
||||||
expect( getOutOfStockChips() ).toBeInTheDocument();
|
expect( getOutOfStockChips() ).toBeInTheDocument();
|
||||||
} );
|
} );
|
||||||
|
} );
|
||||||
|
|
||||||
test( 'removes the option when the X button is clicked', async () => {
|
test( 'removes the option when the X button is clicked', async () => {
|
||||||
|
await waitFor( async () => {
|
||||||
const user = userEvent.setup();
|
const user = userEvent.setup();
|
||||||
const ratingParam = 'outofstock';
|
const ratingParam = 'outofstock';
|
||||||
const {
|
const {
|
||||||
|
@ -311,23 +321,20 @@ describe( 'Filter by Stock block', () => {
|
||||||
getRemoveButtonFromChips,
|
getRemoveButtonFromChips,
|
||||||
} = setupMultipleChoiceDropdown( ratingParam );
|
} = setupMultipleChoiceDropdown( ratingParam );
|
||||||
|
|
||||||
await waitFor( () => {
|
|
||||||
expect( getInStockChips() ).toBeNull();
|
expect( getInStockChips() ).toBeNull();
|
||||||
expect( getOutOfStockChips() ).toBeInTheDocument();
|
expect( getOutOfStockChips() ).toBeInTheDocument();
|
||||||
expect( getOnBackorderChips() ).toBeNull();
|
expect( getOnBackorderChips() ).toBeNull();
|
||||||
} );
|
|
||||||
|
|
||||||
const removeOutOfStockButton = getRemoveButtonFromChips(
|
const removeOutOfStockButton = getRemoveButtonFromChips(
|
||||||
getOutOfStockChips()
|
getOutOfStockChips()
|
||||||
);
|
);
|
||||||
|
|
||||||
if ( removeOutOfStockButton ) {
|
if ( removeOutOfStockButton ) {
|
||||||
act( async () => {
|
await act( async () => {
|
||||||
await user.click( removeOutOfStockButton );
|
await user.click( removeOutOfStockButton );
|
||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
|
|
||||||
await waitFor( () => {
|
|
||||||
expect( getInStockChips() ).toBeNull();
|
expect( getInStockChips() ).toBeNull();
|
||||||
expect( getOutOfStockChips() ).toBeNull();
|
expect( getOutOfStockChips() ).toBeNull();
|
||||||
expect( getOnBackorderChips() ).toBeNull();
|
expect( getOnBackorderChips() ).toBeNull();
|
||||||
|
@ -342,17 +349,23 @@ describe( 'Filter by Stock block', () => {
|
||||||
expect( getList() ).toBeNull();
|
expect( getList() ).toBeNull();
|
||||||
} );
|
} );
|
||||||
|
|
||||||
test( 'renders chips based on URL params', () => {
|
test( 'renders chips based on URL params', async () => {
|
||||||
|
await waitFor( async () => {
|
||||||
const ratingParam = 'instock,onbackorder';
|
const ratingParam = 'instock,onbackorder';
|
||||||
const { getInStockChips, getOutOfStockChips, getOnBackorderChips } =
|
const {
|
||||||
setupMultipleChoiceDropdown( ratingParam );
|
getInStockChips,
|
||||||
|
getOutOfStockChips,
|
||||||
|
getOnBackorderChips,
|
||||||
|
} = setupMultipleChoiceDropdown( ratingParam );
|
||||||
|
|
||||||
expect( getInStockChips() ).toBeInTheDocument();
|
expect( getInStockChips() ).toBeInTheDocument();
|
||||||
expect( getOutOfStockChips() ).toBeNull();
|
expect( getOutOfStockChips() ).toBeNull();
|
||||||
expect( getOnBackorderChips() ).toBeInTheDocument();
|
expect( getOnBackorderChips() ).toBeInTheDocument();
|
||||||
} );
|
} );
|
||||||
|
} );
|
||||||
|
|
||||||
test( 'adds chosen option to another one that is clicked', async () => {
|
test( 'adds chosen option to another one that is clicked', async () => {
|
||||||
|
await waitFor( async () => {
|
||||||
const user = userEvent.setup();
|
const user = userEvent.setup();
|
||||||
const ratingParam = 'onbackorder';
|
const ratingParam = 'onbackorder';
|
||||||
const {
|
const {
|
||||||
|
@ -364,25 +377,20 @@ describe( 'Filter by Stock block', () => {
|
||||||
getOutOfStockSuggestion,
|
getOutOfStockSuggestion,
|
||||||
} = setupMultipleChoiceDropdown( ratingParam );
|
} = setupMultipleChoiceDropdown( ratingParam );
|
||||||
|
|
||||||
await waitFor( () => {
|
|
||||||
expect( getInStockChips() ).toBeNull();
|
expect( getInStockChips() ).toBeNull();
|
||||||
expect( getOutOfStockChips() ).toBeNull();
|
expect( getOutOfStockChips() ).toBeNull();
|
||||||
expect( getOnBackorderChips() ).toBeInTheDocument();
|
expect( getOnBackorderChips() ).toBeInTheDocument();
|
||||||
} );
|
|
||||||
const dropdown = getDropdown();
|
const dropdown = getDropdown();
|
||||||
|
|
||||||
if ( dropdown ) {
|
if ( dropdown ) {
|
||||||
await act( async () => {
|
|
||||||
await user.click( dropdown );
|
await user.click( dropdown );
|
||||||
} );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const inStockSuggestion = getInStockSuggestion();
|
const inStockSuggestion = getInStockSuggestion();
|
||||||
|
|
||||||
if ( inStockSuggestion ) {
|
if ( inStockSuggestion ) {
|
||||||
await act( async () => {
|
|
||||||
await user.click( inStockSuggestion );
|
await user.click( inStockSuggestion );
|
||||||
} );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
expect( getInStockChips() ).toBeInTheDocument();
|
expect( getInStockChips() ).toBeInTheDocument();
|
||||||
|
@ -391,18 +399,15 @@ describe( 'Filter by Stock block', () => {
|
||||||
|
|
||||||
const freshDropdown = getDropdown();
|
const freshDropdown = getDropdown();
|
||||||
if ( freshDropdown ) {
|
if ( freshDropdown ) {
|
||||||
await act( async () => {
|
|
||||||
await user.click( freshDropdown );
|
await user.click( freshDropdown );
|
||||||
} );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const outOfStockSuggestion = getOutOfStockSuggestion();
|
const outOfStockSuggestion = getOutOfStockSuggestion();
|
||||||
|
|
||||||
if ( outOfStockSuggestion ) {
|
if ( outOfStockSuggestion ) {
|
||||||
userEvent.click( outOfStockSuggestion );
|
await userEvent.click( outOfStockSuggestion );
|
||||||
}
|
}
|
||||||
|
|
||||||
await waitFor( () => {
|
|
||||||
expect( getInStockChips() ).toBeInTheDocument();
|
expect( getInStockChips() ).toBeInTheDocument();
|
||||||
expect( getOutOfStockChips() ).toBeInTheDocument();
|
expect( getOutOfStockChips() ).toBeInTheDocument();
|
||||||
expect( getOnBackorderChips() ).toBeInTheDocument();
|
expect( getOnBackorderChips() ).toBeInTheDocument();
|
||||||
|
@ -410,6 +415,7 @@ describe( 'Filter by Stock block', () => {
|
||||||
} );
|
} );
|
||||||
|
|
||||||
test( 'removes the option when the X button is clicked', async () => {
|
test( 'removes the option when the X button is clicked', async () => {
|
||||||
|
await waitFor( async () => {
|
||||||
const user = userEvent.setup();
|
const user = userEvent.setup();
|
||||||
const ratingParam = 'instock,outofstock,onbackorder';
|
const ratingParam = 'instock,outofstock,onbackorder';
|
||||||
const {
|
const {
|
||||||
|
@ -419,23 +425,20 @@ describe( 'Filter by Stock block', () => {
|
||||||
getRemoveButtonFromChips,
|
getRemoveButtonFromChips,
|
||||||
} = setupMultipleChoiceDropdown( ratingParam );
|
} = setupMultipleChoiceDropdown( ratingParam );
|
||||||
|
|
||||||
await waitFor( () => {
|
|
||||||
expect( getInStockChips() ).toBeInTheDocument();
|
expect( getInStockChips() ).toBeInTheDocument();
|
||||||
expect( getOutOfStockChips() ).toBeInTheDocument();
|
expect( getOutOfStockChips() ).toBeInTheDocument();
|
||||||
expect( getOnBackorderChips() ).toBeInTheDocument();
|
expect( getOnBackorderChips() ).toBeInTheDocument();
|
||||||
} );
|
|
||||||
|
|
||||||
const removeOutOfStockButton = getRemoveButtonFromChips(
|
const removeOutOfStockButton = getRemoveButtonFromChips(
|
||||||
getOutOfStockChips()
|
getOutOfStockChips()
|
||||||
);
|
);
|
||||||
|
|
||||||
if ( removeOutOfStockButton ) {
|
if ( removeOutOfStockButton ) {
|
||||||
act( async () => {
|
await act( async () => {
|
||||||
await user.click( removeOutOfStockButton );
|
await user.click( removeOutOfStockButton );
|
||||||
} );
|
} );
|
||||||
}
|
}
|
||||||
|
|
||||||
await waitFor( () => {
|
|
||||||
expect( getInStockChips() ).toBeInTheDocument();
|
expect( getInStockChips() ).toBeInTheDocument();
|
||||||
expect( getOutOfStockChips() ).toBeNull();
|
expect( getOutOfStockChips() ).toBeNull();
|
||||||
expect( getOnBackorderChips() ).toBeInTheDocument();
|
expect( getOnBackorderChips() ).toBeInTheDocument();
|
||||||
|
@ -450,7 +453,8 @@ describe( 'Filter by Stock block', () => {
|
||||||
expect( getList() ).toBeInTheDocument();
|
expect( getList() ).toBeInTheDocument();
|
||||||
} );
|
} );
|
||||||
|
|
||||||
test( 'renders checked options based on URL params', () => {
|
test( 'renders checked options based on URL params', async () => {
|
||||||
|
await waitFor( async () => {
|
||||||
const ratingParam = 'instock';
|
const ratingParam = 'instock';
|
||||||
const {
|
const {
|
||||||
getInStockCheckbox,
|
getInStockCheckbox,
|
||||||
|
@ -462,8 +466,10 @@ describe( 'Filter by Stock block', () => {
|
||||||
expect( getOutOfStockCheckbox()?.checked ).toBeFalsy();
|
expect( getOutOfStockCheckbox()?.checked ).toBeFalsy();
|
||||||
expect( getOnBackorderCheckbox()?.checked ).toBeFalsy();
|
expect( getOnBackorderCheckbox()?.checked ).toBeFalsy();
|
||||||
} );
|
} );
|
||||||
|
} );
|
||||||
|
|
||||||
test( 'replaces chosen option when another one is clicked', async () => {
|
test( 'replaces chosen option when another one is clicked', async () => {
|
||||||
|
await waitFor( async () => {
|
||||||
const user = userEvent.setup();
|
const user = userEvent.setup();
|
||||||
const ratingParam = 'outofstock';
|
const ratingParam = 'outofstock';
|
||||||
const {
|
const {
|
||||||
|
@ -488,8 +494,10 @@ describe( 'Filter by Stock block', () => {
|
||||||
expect( getOutOfStockCheckbox()?.checked ).toBeFalsy();
|
expect( getOutOfStockCheckbox()?.checked ).toBeFalsy();
|
||||||
expect( getOnBackorderCheckbox()?.checked ).toBeTruthy();
|
expect( getOnBackorderCheckbox()?.checked ).toBeTruthy();
|
||||||
} );
|
} );
|
||||||
|
} );
|
||||||
|
|
||||||
test( 'removes the option when it is clicked again', async () => {
|
test( 'removes the option when it is clicked again', async () => {
|
||||||
|
await waitFor( async () => {
|
||||||
const ratingParam = 'onbackorder';
|
const ratingParam = 'onbackorder';
|
||||||
const {
|
const {
|
||||||
getInStockCheckbox,
|
getInStockCheckbox,
|
||||||
|
@ -514,6 +522,7 @@ describe( 'Filter by Stock block', () => {
|
||||||
} );
|
} );
|
||||||
} );
|
} );
|
||||||
} );
|
} );
|
||||||
|
} );
|
||||||
|
|
||||||
describe( 'Multiple choice List', () => {
|
describe( 'Multiple choice List', () => {
|
||||||
test( 'renders list', () => {
|
test( 'renders list', () => {
|
||||||
|
@ -522,7 +531,8 @@ describe( 'Filter by Stock block', () => {
|
||||||
expect( getList() ).toBeInTheDocument();
|
expect( getList() ).toBeInTheDocument();
|
||||||
} );
|
} );
|
||||||
|
|
||||||
test( 'renders chips based on URL params', () => {
|
test( 'renders chips based on URL params', async () => {
|
||||||
|
await waitFor( async () => {
|
||||||
const ratingParam = 'instock,onbackorder';
|
const ratingParam = 'instock,onbackorder';
|
||||||
const {
|
const {
|
||||||
getInStockCheckbox,
|
getInStockCheckbox,
|
||||||
|
@ -534,8 +544,10 @@ describe( 'Filter by Stock block', () => {
|
||||||
expect( getOutOfStockCheckbox()?.checked ).toBeFalsy();
|
expect( getOutOfStockCheckbox()?.checked ).toBeFalsy();
|
||||||
expect( getOnBackorderCheckbox()?.checked ).toBeTruthy();
|
expect( getOnBackorderCheckbox()?.checked ).toBeTruthy();
|
||||||
} );
|
} );
|
||||||
|
} );
|
||||||
|
|
||||||
test( 'adds chosen option to another one that is clicked', async () => {
|
test( 'adds chosen option to another one that is clicked', async () => {
|
||||||
|
await waitFor( async () => {
|
||||||
const ratingParam = 'outofstock,onbackorder';
|
const ratingParam = 'outofstock,onbackorder';
|
||||||
const {
|
const {
|
||||||
getInStockCheckbox,
|
getInStockCheckbox,
|
||||||
|
@ -559,8 +571,10 @@ describe( 'Filter by Stock block', () => {
|
||||||
expect( getOnBackorderCheckbox()?.checked ).toBeTruthy();
|
expect( getOnBackorderCheckbox()?.checked ).toBeTruthy();
|
||||||
} );
|
} );
|
||||||
} );
|
} );
|
||||||
|
} );
|
||||||
|
|
||||||
test( 'removes the option when it is clicked again', async () => {
|
test( 'removes the option when it is clicked again', async () => {
|
||||||
|
await waitFor( async () => {
|
||||||
const ratingParam = 'instock,outofstock';
|
const ratingParam = 'instock,outofstock';
|
||||||
const {
|
const {
|
||||||
getInStockCheckbox,
|
getInStockCheckbox,
|
||||||
|
@ -586,3 +600,4 @@ describe( 'Filter by Stock block', () => {
|
||||||
} );
|
} );
|
||||||
} );
|
} );
|
||||||
} );
|
} );
|
||||||
|
} );
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
Significance: patch
|
||||||
|
Type: dev
|
||||||
|
|
||||||
|
Switch `render()` to `createRoot().render()` to use React 18 features.
|
|
@ -137,9 +137,7 @@ test.describe(
|
||||||
await page
|
await page
|
||||||
.getByRole( 'button', { name: 'Add a coupon' } )
|
.getByRole( 'button', { name: 'Add a coupon' } )
|
||||||
.click();
|
.click();
|
||||||
await page
|
await page.getByLabel( 'Enter code' ).fill( coupons[ i ].code );
|
||||||
.locator( '#wc-block-components-totals-coupon__input-0' )
|
|
||||||
.fill( coupons[ i ].code );
|
|
||||||
await page.getByText( 'Apply', { exact: true } ).click();
|
await page.getByText( 'Apply', { exact: true } ).click();
|
||||||
await expect(
|
await expect(
|
||||||
page
|
page
|
||||||
|
@ -182,9 +180,7 @@ test.describe(
|
||||||
await page
|
await page
|
||||||
.getByRole( 'button', { name: 'Add a coupon' } )
|
.getByRole( 'button', { name: 'Add a coupon' } )
|
||||||
.click();
|
.click();
|
||||||
await page
|
await page.getByLabel( 'Enter code' ).fill( coupons[ i ].code );
|
||||||
.locator( '#wc-block-components-totals-coupon__input-0' )
|
|
||||||
.fill( coupons[ i ].code );
|
|
||||||
await page.getByText( 'Apply', { exact: true } ).click();
|
await page.getByText( 'Apply', { exact: true } ).click();
|
||||||
await expect(
|
await expect(
|
||||||
page
|
page
|
||||||
|
@ -224,9 +220,7 @@ test.describe(
|
||||||
} ) => {
|
} ) => {
|
||||||
// try to add two same coupons and verify the error message
|
// try to add two same coupons and verify the error message
|
||||||
await page.getByRole( 'button', { name: 'Add a coupon' } ).click();
|
await page.getByRole( 'button', { name: 'Add a coupon' } ).click();
|
||||||
await page
|
await page.getByLabel( 'Enter code' ).fill( coupons[ 0 ].code );
|
||||||
.locator( '#wc-block-components-totals-coupon__input-0' )
|
|
||||||
.fill( coupons[ 0 ].code );
|
|
||||||
await page.getByText( 'Apply', { exact: true } ).click();
|
await page.getByText( 'Apply', { exact: true } ).click();
|
||||||
await expect(
|
await expect(
|
||||||
page
|
page
|
||||||
|
@ -236,9 +230,7 @@ test.describe(
|
||||||
)
|
)
|
||||||
).toBeVisible();
|
).toBeVisible();
|
||||||
await page.getByRole( 'button', { name: 'Add a coupon' } ).click();
|
await page.getByRole( 'button', { name: 'Add a coupon' } ).click();
|
||||||
await page
|
await page.getByLabel( 'Enter code' ).fill( coupons[ 0 ].code );
|
||||||
.locator( '#wc-block-components-totals-coupon__input-0' )
|
|
||||||
.fill( coupons[ 0 ].code );
|
|
||||||
await page.getByText( 'Apply', { exact: true } ).click();
|
await page.getByText( 'Apply', { exact: true } ).click();
|
||||||
await expect(
|
await expect(
|
||||||
page
|
page
|
||||||
|
@ -254,9 +246,7 @@ test.describe(
|
||||||
} ) => {
|
} ) => {
|
||||||
// add coupon with usage limit
|
// add coupon with usage limit
|
||||||
await page.getByRole( 'button', { name: 'Add a coupon' } ).click();
|
await page.getByRole( 'button', { name: 'Add a coupon' } ).click();
|
||||||
await page
|
await page.getByLabel( 'Enter code' ).fill( couponLimitedCode );
|
||||||
.locator( '#wc-block-components-totals-coupon__input-0' )
|
|
||||||
.fill( couponLimitedCode );
|
|
||||||
await page.getByText( 'Apply', { exact: true } ).click();
|
await page.getByText( 'Apply', { exact: true } ).click();
|
||||||
await expect(
|
await expect(
|
||||||
page
|
page
|
||||||
|
|
|
@ -138,9 +138,7 @@ test.describe(
|
||||||
await page
|
await page
|
||||||
.getByRole( 'button', { name: 'Add a coupon' } )
|
.getByRole( 'button', { name: 'Add a coupon' } )
|
||||||
.click();
|
.click();
|
||||||
await page
|
await page.getByLabel( 'Enter code' ).fill( coupons[ i ].code );
|
||||||
.locator( '#wc-block-components-totals-coupon__input-0' )
|
|
||||||
.fill( coupons[ i ].code );
|
|
||||||
await page.getByText( 'Apply', { exact: true } ).click();
|
await page.getByText( 'Apply', { exact: true } ).click();
|
||||||
await expect(
|
await expect(
|
||||||
page
|
page
|
||||||
|
@ -183,9 +181,7 @@ test.describe(
|
||||||
await page
|
await page
|
||||||
.getByRole( 'button', { name: 'Add a coupon' } )
|
.getByRole( 'button', { name: 'Add a coupon' } )
|
||||||
.click();
|
.click();
|
||||||
await page
|
await page.getByLabel( 'Enter code' ).fill( coupons[ i ].code );
|
||||||
.locator( '#wc-block-components-totals-coupon__input-0' )
|
|
||||||
.fill( coupons[ i ].code );
|
|
||||||
await page.getByText( 'Apply', { exact: true } ).click();
|
await page.getByText( 'Apply', { exact: true } ).click();
|
||||||
await expect(
|
await expect(
|
||||||
page
|
page
|
||||||
|
@ -225,9 +221,7 @@ test.describe(
|
||||||
} ) => {
|
} ) => {
|
||||||
// try to add two same coupons and verify the error message
|
// try to add two same coupons and verify the error message
|
||||||
await page.getByRole( 'button', { name: 'Add a coupon' } ).click();
|
await page.getByRole( 'button', { name: 'Add a coupon' } ).click();
|
||||||
await page
|
await page.getByLabel( 'Enter code' ).fill( coupons[ 0 ].code );
|
||||||
.locator( '#wc-block-components-totals-coupon__input-0' )
|
|
||||||
.fill( coupons[ 0 ].code );
|
|
||||||
await page.getByText( 'Apply', { exact: true } ).click();
|
await page.getByText( 'Apply', { exact: true } ).click();
|
||||||
await expect(
|
await expect(
|
||||||
page
|
page
|
||||||
|
@ -237,9 +231,7 @@ test.describe(
|
||||||
)
|
)
|
||||||
).toBeVisible();
|
).toBeVisible();
|
||||||
await page.getByRole( 'button', { name: 'Add a coupon' } ).click();
|
await page.getByRole( 'button', { name: 'Add a coupon' } ).click();
|
||||||
await page
|
await page.getByLabel( 'Enter code' ).fill( coupons[ 0 ].code );
|
||||||
.locator( '#wc-block-components-totals-coupon__input-0' )
|
|
||||||
.fill( coupons[ 0 ].code );
|
|
||||||
await page.getByText( 'Apply', { exact: true } ).click();
|
await page.getByText( 'Apply', { exact: true } ).click();
|
||||||
await expect(
|
await expect(
|
||||||
page
|
page
|
||||||
|
@ -255,9 +247,7 @@ test.describe(
|
||||||
} ) => {
|
} ) => {
|
||||||
// add coupon with usage limit
|
// add coupon with usage limit
|
||||||
await page.getByRole( 'button', { name: 'Add a coupon' } ).click();
|
await page.getByRole( 'button', { name: 'Add a coupon' } ).click();
|
||||||
await page
|
await page.getByLabel( 'Enter code' ).fill( couponLimitedCode );
|
||||||
.locator( '#wc-block-components-totals-coupon__input-0' )
|
|
||||||
.fill( couponLimitedCode );
|
|
||||||
await page.getByText( 'Apply', { exact: true } ).click();
|
await page.getByText( 'Apply', { exact: true } ).click();
|
||||||
await expect(
|
await expect(
|
||||||
page
|
page
|
||||||
|
|
Loading…
Reference in New Issue