Remove WC Core shipping settings if Cart/Checkout blocks are in use (https://github.com/woocommerce/woocommerce-blocks/pull/8679)

* Add CartCheckoutUtils class

This class will store reusable methods relating to Cart/Checkout Blocks, i.e. whether they are used on the Cart/Checkout page.

* Update ShippingController to use the new CartCheckoutUtils function

This will reduce code duplication when checking if the Cart/Checkout blocks are in use on the Cart/Checkout page.

* Add filter to remove shipping settings when Cart/Checkout are default

* Ensure setting displays correctly if cart is default but not checkout

* Add tests to ensure core shipping settings update correctly

* Add setCartCheckoutPages function to update set the cart/checkout page

* Force shipping to be enabled if the Checkout block is in use.

* Add filter to override cost requires address option

* Add shippingCostRequiresAddress option

* Check if the address is required before showing rates

* Show shipping rates in editor

* Add shippingCostRequiresAddress attribute to shipping methods block

* Update frontend type to show shippingCostRequiresAddress is a prop

* Add control to toggle shippingCostRequiresAddress option

* Show address notice in the correct scenario

* Send shippingCostRequiresAddress to Block in front end context

* Add e2e test for editor control

* Add e2e tests for shipping options on the front end

* Add updateAttributeInSiblingBlock function

* Add shippingCostRequiresAddress to shipping method block

* Ensure attribute is updated in both blocks when editing

* In Shipping Methods Block, show correct component based on block setting

* Show correct block in editor

* Remove broken test from PR

* Clean up updateAttributeInSiblingBlock

* Add setCartCheckoutPages function to update set the cart/checkout page

* Add tests to ensure core shipping settings update correctly

* Add isAddressComplete function

Borrowed from woocommerce/woocommerce-blocks#8141

* Check if the address is required before showing rates

* Show shipping rates in editor

* Show address notice in the correct scenario

* Add e2e tests for shipping options on the front end

* Ensure errorId is passed to StateInput

* Add fullShippingAddressPushed action to wc/store/cart

* Add fullShippingAddressPushed case to reducer

* Ensure fullShippingAddressPushed is set when initialising cart store

* Add fullShippingAddressPushed selector and default state entry

* Add shippingAddressHasValidationErrors util function

* Do not overwrite addresses when selecting a rate

* Set whether full address has been pushed when saving address changes

* In Shipping Methods Block, show correct component based on block setting

* Don't show from price if rates should be hidden until address entered

* Check city validation errors to assert if shipping address is valid

* Rename merchant.js to merchant.ts

* Move local pickup functions to common merchant util

* Update local pickup tests to use common merchant utils

* Add test to ensure setting toggles in both blocks

* Add navigating to settings and saving in merchant util

* Create addPickupLocation merchant util

* Add test for local pickup and require full address

* Make sure correct conditions are met to show shipping options

* Ensure checkbox is checked during local pickup tests

* Unset the checkbox when tests are finished running

* Update checkout block fixture

* Prevent error in unit tests

* Import validation store key from constants

Required because importing from the index causes the validation data store to register twice

* Update checkout terms test to wait for button not to be disabled

* Revert "Add isAddressComplete function"

This reverts commit 9967dc0d4f10cf638859ae085e6f4cc2901dd299.
This commit is contained in:
Thomas Roberts 2023-03-13 11:49:28 +00:00 committed by GitHub
parent 95efc38d1f
commit 991e407fa9
32 changed files with 871 additions and 83 deletions

View File

@ -8,6 +8,7 @@ export interface StateInputProps {
onChange: ( value: string ) => void;
required?: boolean;
errorMessage?: string;
errorId?: string;
}
export type StateInputWithStatesProps = StateInputProps & {

View File

@ -36,6 +36,7 @@ const StateInput = ( {
autoComplete = 'off',
value = '',
required = false,
errorId = '',
}: StateInputWithStatesProps ): JSX.Element => {
const countryStates = states[ country ];
const options = useMemo(
@ -102,6 +103,7 @@ const StateInput = ( {
'Please select a state.',
'woo-gutenberg-products-block'
) }
errorId={ errorId }
required={ required }
autoComplete={ autoComplete }
/>

View File

@ -44,4 +44,8 @@ export default {
remove: true,
},
},
shippingCostRequiresAddress: {
type: 'boolean',
default: false,
},
};

View File

@ -19,6 +19,10 @@
"remove": true,
"move": true
}
},
"shippingCostRequiresAddress": {
"type": "boolean",
"default": false
}
},
"parent": [ "woocommerce/checkout-fields-block" ],

View File

@ -17,6 +17,7 @@ import './style.scss';
import { RatePrice, getLocalPickupPrices, getShippingPrices } from './shared';
import type { minMaxPrices } from './shared';
import { defaultLocalPickupText, defaultShippingText } from './constants';
import { shippingAddressHasValidationErrors } from '../../../../data/cart/utils';
const LocalPickupSelector = ( {
checked,
@ -71,15 +72,19 @@ const ShippingSelector = ( {
showPrice,
showIcon,
toggleText,
shippingCostRequiresAddress = false,
}: {
checked: string;
rate: minMaxPrices;
showPrice: boolean;
showIcon: boolean;
shippingCostRequiresAddress: boolean;
toggleText: string;
} ) => {
const rateShouldBeHidden =
shippingCostRequiresAddress && shippingAddressHasValidationErrors();
const Price =
rate.min === undefined ? (
rate.min === undefined || rateShouldBeHidden ? (
<span className="wc-block-checkout__shipping-method-option-price">
{ __(
'calculated with an address',
@ -122,11 +127,13 @@ const Block = ( {
showIcon,
localPickupText,
shippingText,
shippingCostRequiresAddress = false,
}: {
checked: string;
onChange: ( value: string ) => void;
showPrice: boolean;
showIcon: boolean;
shippingCostRequiresAddress: boolean;
localPickupText: string;
shippingText: string;
} ): JSX.Element | null => {
@ -145,6 +152,7 @@ const Block = ( {
rate={ getShippingPrices( shippingRates[ 0 ]?.shipping_rates ) }
showPrice={ showPrice }
showIcon={ showIcon }
shippingCostRequiresAddress={ shippingCostRequiresAddress }
toggleText={ shippingText || defaultShippingText }
/>
<LocalPickupSelector

View File

@ -23,6 +23,8 @@ import { innerBlockAreas } from '@woocommerce/blocks-checkout';
import { useDispatch, useSelect } from '@wordpress/data';
import { CHECKOUT_STORE_KEY } from '@woocommerce/block-data';
import ExternalLinkCard from '@woocommerce/editor-components/external-link-card';
import { Attributes } from '@woocommerce/blocks/checkout/types';
import { updateAttributeInSiblingBlock } from '@woocommerce/utils';
/**
* Internal dependencies
@ -152,7 +154,9 @@ const ShippingSelector = ( {
export const Edit = ( {
attributes,
setAttributes,
clientId,
}: {
clientId: string;
attributes: {
title: string;
description: string;
@ -163,9 +167,16 @@ export const Edit = ( {
showPrice: boolean;
showIcon: boolean;
className: string;
shippingCostRequiresAddress: boolean;
};
setAttributes: ( attributes: Record< string, unknown > ) => void;
} ): JSX.Element | null => {
const toggleAttribute = ( key: keyof Attributes ): void => {
const newAttributes = {} as Partial< Attributes >;
newAttributes[ key ] = ! ( attributes[ key ] as boolean );
setAttributes( newAttributes );
};
const { setPrefersCollection } = useDispatch( CHECKOUT_STORE_KEY );
const { prefersCollection } = useSelect( ( select ) => {
const checkoutStore = select( CHECKOUT_STORE_KEY );
@ -210,6 +221,30 @@ export const Edit = ( {
) }
>
<InspectorControls>
<PanelBody
title={ __(
'Calculations',
'woo-gutenberg-products-block'
) }
>
<ToggleControl
label={ __(
'Hide shipping costs until an address is entered',
'woo-gutenberg-products-block'
) }
checked={ attributes.shippingCostRequiresAddress }
onChange={ ( selected ) => {
updateAttributeInSiblingBlock(
clientId,
'shippingCostRequiresAddress',
selected,
'woocommerce/checkout-shipping-methods-block'
);
toggleAttribute( 'shippingCostRequiresAddress' );
} }
/>
</PanelBody>
<PanelBody
title={ __( 'Appearance', 'woo-gutenberg-products-block' ) }
>

View File

@ -25,10 +25,12 @@ const FrontendBlock = ( {
showIcon,
shippingText,
localPickupText,
shippingCostRequiresAddress,
}: {
title: string;
description: string;
showStepNumber: boolean;
shippingCostRequiresAddress: boolean;
children: JSX.Element;
className?: string;
showPrice: boolean;
@ -90,6 +92,7 @@ const FrontendBlock = ( {
showIcon={ showIcon }
localPickupText={ localPickupText }
shippingText={ shippingText }
shippingCostRequiresAddress={ shippingCostRequiresAddress }
/>
{ children }
</FormStep>

View File

@ -24,4 +24,8 @@ export default {
remove: true,
},
},
shippingCostRequiresAddress: {
type: 'boolean',
default: false,
},
};

View File

@ -19,6 +19,10 @@
"remove": true,
"move": true
}
},
"shippingCostRequiresAddress": {
"type": "boolean",
"default": false
}
},
"parent": [ "woocommerce/checkout-fields-block" ],

View File

@ -21,11 +21,14 @@ import type {
CartShippingPackageShippingRate,
} from '@woocommerce/types';
import type { ReactElement } from 'react';
import { useSelect } from '@wordpress/data';
import { CART_STORE_KEY } from '@woocommerce/block-data';
/**
* Internal dependencies
*/
import './style.scss';
import { shippingAddressHasValidationErrors } from '../../../../data/cart/utils';
/**
* Renders a shipping rate control option.
@ -52,7 +55,10 @@ const renderShippingRatesControlOption = (
};
};
const Block = ( { noShippingPlaceholder = null } ): ReactElement | null => {
const Block = ( {
noShippingPlaceholder = null,
shippingCostRequiresAddress = false,
} ): ReactElement | null => {
const { isEditor } = useEditorContext();
const {
@ -63,6 +69,10 @@ const Block = ( { noShippingPlaceholder = null } ): ReactElement | null => {
isCollectable,
} = useShippingData();
const shippingAddressPushed = useSelect( ( select ) => {
return select( CART_STORE_KEY ).getFullShippingAddressPushed();
} );
const filteredShippingRates = isCollectable
? shippingRates.map( ( shippingRatesPackage ) => {
return {
@ -81,13 +91,15 @@ const Block = ( { noShippingPlaceholder = null } ): ReactElement | null => {
return null;
}
const shippingAddressIsComplete = ! shippingAddressHasValidationErrors();
const shippingRatesPackageCount =
getShippingRatesPackageCount( shippingRates );
if (
! isEditor &&
! hasCalculatedShipping &&
! shippingRatesPackageCount
( ! hasCalculatedShipping && ! shippingRatesPackageCount ) ||
( shippingCostRequiresAddress &&
( ! shippingAddressPushed || ! shippingAddressIsComplete ) )
) {
return (
<p>

View File

@ -4,12 +4,14 @@
import classnames from 'classnames';
import { __ } from '@wordpress/i18n';
import { InspectorControls, useBlockProps } from '@wordpress/block-editor';
import { PanelBody, ExternalLink } from '@wordpress/components';
import { PanelBody, ExternalLink, ToggleControl } from '@wordpress/components';
import { ADMIN_URL, getSetting } from '@woocommerce/settings';
import ExternalLinkCard from '@woocommerce/editor-components/external-link-card';
import { innerBlockAreas } from '@woocommerce/blocks-checkout';
import { useCheckoutAddress } from '@woocommerce/base-context/hooks';
import Noninteractive from '@woocommerce/base-components/noninteractive';
import { Attributes } from '@woocommerce/blocks/checkout/types';
import { updateAttributeInSiblingBlock } from '@woocommerce/utils';
/**
* Internal dependencies
@ -32,12 +34,15 @@ type shippingAdminLink = {
export const Edit = ( {
attributes,
setAttributes,
clientId,
}: {
clientId: string;
attributes: {
title: string;
description: string;
showStepNumber: boolean;
className: string;
shippingCostRequiresAddress: boolean;
};
setAttributes: ( attributes: Record< string, unknown > ) => void;
} ): JSX.Element | null => {
@ -54,6 +59,12 @@ export const Edit = ( {
return null;
}
const toggleAttribute = ( key: keyof Attributes ): void => {
const newAttributes = {} as Partial< Attributes >;
newAttributes[ key ] = ! ( attributes[ key ] as boolean );
setAttributes( newAttributes );
};
return (
<FormStepBlock
attributes={ attributes }
@ -64,6 +75,29 @@ export const Edit = ( {
) }
>
<InspectorControls>
<PanelBody
title={ __(
'Calculations',
'woo-gutenberg-products-block'
) }
>
<ToggleControl
label={ __(
'Hide shipping costs until an address is entered',
'woo-gutenberg-products-block'
) }
checked={ attributes.shippingCostRequiresAddress }
onChange={ ( selected ) => {
updateAttributeInSiblingBlock(
clientId,
'shippingCostRequiresAddress',
selected,
'woocommerce/checkout-shipping-method-block'
);
toggleAttribute( 'shippingCostRequiresAddress' );
} }
/>
</PanelBody>
{ globalShippingMethods.length > 0 && (
<PanelBody
title={ __(
@ -129,7 +163,12 @@ export const Edit = ( {
) }
</InspectorControls>
<Noninteractive>
<Block noShippingPlaceholder={ <NoShippingPlaceholder /> } />
<Block
noShippingPlaceholder={ <NoShippingPlaceholder /> }
shippingCostRequiresAddress={
attributes.shippingCostRequiresAddress
}
/>
</Noninteractive>
<AdditionalFields block={ innerBlockAreas.SHIPPING_METHODS } />
</FormStepBlock>

View File

@ -20,6 +20,7 @@ const FrontendBlock = ( {
showStepNumber,
children,
className,
shippingCostRequiresAddress = false,
}: {
title: string;
description: string;
@ -31,6 +32,7 @@ const FrontendBlock = ( {
showStepNumber: boolean;
children: JSX.Element;
className?: string;
shippingCostRequiresAddress: boolean;
} ) => {
const checkoutIsProcessing = useSelect( ( select ) =>
select( CHECKOUT_STORE_KEY ).isProcessing()
@ -53,7 +55,9 @@ const FrontendBlock = ( {
description={ description }
showStepNumber={ showStepNumber }
>
<Block />
<Block
shippingCostRequiresAddress={ shippingCostRequiresAddress }
/>
{ children }
</FormStep>
);

View File

@ -1,5 +1,6 @@
export const ACTION_TYPES = {
SET_CART_DATA: 'SET_CART_DATA',
SET_FULL_SHIPPING_ADDRESS_PUSHED: 'SET_FULL_SHIPPING_ADDRESS_PUSHED',
SET_ERROR_DATA: 'SET_ERROR_DATA',
APPLYING_COUPON: 'APPLYING_COUPON',
REMOVING_COUPON: 'REMOVING_COUPON',

View File

@ -417,7 +417,14 @@ export const selectShippingRate =
},
cache: 'no-store',
} );
dispatch.receiveCart( response );
// Remove shipping and billing address from the response, so we don't overwrite what the shopper is
// entering in the form if rates suddenly appear mid-edit.
const {
shipping_address: shippingAddress,
billing_address: billingAddress,
...rest
} = response;
dispatch.receiveCart( rest );
return response as CartResponse;
} catch ( error ) {
dispatch.receiveError( error );
@ -474,6 +481,13 @@ export const updateCustomerData =
}
};
export const setFullShippingAddressPushed = (
fullShippingAddressPushed: boolean
) => ( {
type: types.SET_FULL_SHIPPING_ADDRESS_PUSHED,
fullShippingAddressPushed,
} );
type Actions =
| typeof addItemToCart
| typeof applyCoupon
@ -494,6 +508,7 @@ type Actions =
| typeof setShippingAddress
| typeof shippingRatesBeingSelected
| typeof updateCustomerData
| typeof setFullShippingAddressPushed
| typeof updatingCustomerData;
export type CartAction = ReturnOrGeneratorYieldUnion< Actions | Thunks >;

View File

@ -100,6 +100,7 @@ export const defaultCartState: CartState = {
applyingCoupon: '',
removingCoupon: '',
isCartDataStale: false,
fullShippingAddressPushed: false,
},
errors: EMPTY_CART_ERRORS,
};

View File

@ -17,6 +17,7 @@ import isShallowEqual from '@wordpress/is-shallow-equal';
import { STORE_KEY } from './constants';
import { VALIDATION_STORE_KEY } from '../validation';
import { processErrorResponse } from '../utils';
import { shippingAddressHasValidationErrors } from './utils';
type CustomerData = {
billingAddress: CartBillingAddress;
@ -192,6 +193,11 @@ const updateCustomerData = debounce( (): void => {
) as BaseAddressKey[] ),
];
}
} )
.finally( () => {
if ( ! shippingAddressHasValidationErrors() ) {
dispatch( STORE_KEY ).setFullShippingAddressPushed( true );
}
} );
}
}, 1000 );

View File

@ -48,6 +48,15 @@ const reducer: Reducer< CartState > = (
action: Partial< CartAction >
) => {
switch ( action.type ) {
case types.SET_FULL_SHIPPING_ADDRESS_PUSHED:
state = {
...state,
metaData: {
...state.metaData,
fullShippingAddressPushed: action.fullShippingAddressPushed,
},
};
break;
case types.SET_ERROR_DATA:
if ( action.error ) {
state = {

View File

@ -9,6 +9,7 @@ import { CartResponse } from '@woocommerce/types';
*/
import { CART_API_ERROR } from './constants';
import type { CartDispatchFromMap, CartResolveSelectFromMap } from './index';
import { shippingAddressHasValidationErrors } from './utils';
/**
* Resolver for retrieving all cart data.
@ -27,6 +28,10 @@ export const getCartData =
receiveError( CART_API_ERROR );
return;
}
if ( ! shippingAddressHasValidationErrors() ) {
dispatch.setFullShippingAddressPushed( true );
}
receiveCart( cartData );
};

View File

@ -222,3 +222,10 @@ export const getItemsPendingQuantityUpdate = ( state: CartState ): string[] => {
export const getItemsPendingDelete = ( state: CartState ): string[] => {
return state.cartItemsPendingDelete;
};
/**
* Whether the address has changes that have not been synced with the server.
*/
export const getFullShippingAddressPushed = ( state: CartState ): boolean => {
return state.metaData.fullShippingAddressPushed;
};

View File

@ -64,6 +64,7 @@ jest.mock( '../utils', () => ( {
// need to update payment methods, they are not relevant to the tests in this file.
jest.mock( '../update-payment-methods', () => ( {
debouncedUpdatePaymentMethods: jest.fn(),
updatePaymentMethods: jest.fn(),
} ) );
describe( 'pushChanges', () => {

View File

@ -3,9 +3,38 @@
*/
import { camelCase, mapKeys } from 'lodash';
import { Cart, CartResponse } from '@woocommerce/types';
import { select } from '@wordpress/data';
/**
* Internal dependencies
*/
import { STORE_KEY as VALIDATION_STORE_KEY } from '../validation/constants';
export const mapCartResponseToCart = ( responseCart: CartResponse ): Cart => {
return mapKeys( responseCart, ( _, key ) =>
camelCase( key )
) as unknown as Cart;
};
export const shippingAddressHasValidationErrors = () => {
const validationStore = select( VALIDATION_STORE_KEY );
// Check if the shipping address form has validation errors - if not then we know the full required
// address has been pushed to the server.
const stateValidationErrors =
validationStore.getValidationError( 'shipping_state' );
const address1ValidationErrors =
validationStore.getValidationError( 'shipping_address_1' );
const countryValidationErrors =
validationStore.getValidationError( 'shipping_country' );
const postcodeValidationErrors =
validationStore.getValidationError( 'shipping_postcode' );
const cityValidationErrors =
validationStore.getValidationError( 'shipping_city' );
return [
cityValidationErrors,
stateValidationErrors,
address1ValidationErrors,
countryValidationErrors,
postcodeValidationErrors,
].some( ( entry ) => typeof entry !== 'undefined' );
};

View File

@ -210,6 +210,8 @@ export interface CartMeta {
isCartDataStale: boolean;
applyingCoupon: string;
removingCoupon: string;
/* Whether the full address has been previously pushed to the server */
fullShippingAddressPushed: boolean;
}
export interface ExtensionCartUpdateArgs {
data: Record< string, unknown >;

View File

@ -10,6 +10,7 @@ import {
AttributeWithTerms,
isAttributeTerm,
} from '@woocommerce/types';
import { dispatch, select } from '@wordpress/data';
const ATTRIBUTES = getSetting< AttributeSetting[] >( 'attributes', [] );
@ -108,3 +109,35 @@ export const getTaxonomyFromAttributeId = ( attributeId: number ) => {
const attribute = getAttributeFromID( attributeId );
return attribute ? attribute.taxonomy : null;
};
/**
* Updates an attribute in a sibling block. Useful if two settings control the same attribute, but you don't want to
* have this attribute exist on a parent block.
*/
export const updateAttributeInSiblingBlock = (
clientId: string,
attribute: string,
newValue: unknown,
siblingBlockName: string
) => {
const store = select( 'core/block-editor' );
const actions = dispatch( 'core/block-editor' );
const parentBlocks = store.getBlockParents( clientId );
let shippingMethodsBlockClientId = '';
// Loop through parent block's children until we find woocommerce/checkout-shipping-methods-block.
// Also set this attribute in the woocommerce/checkout-shipping-methods-block.
parentBlocks.forEach( ( parent ) => {
const childBlock = store
.getBlock( parent )
.innerBlocks.find( ( child ) => child.name === siblingBlockName );
if ( ! childBlock ) {
return;
}
shippingMethodsBlockClientId = childBlock.clientId;
} );
actions.updateBlockAttributes( shippingMethodsBlockClientId, {
[ attribute ]: newValue,
} );
};

View File

@ -3,6 +3,8 @@ namespace Automattic\WooCommerce\Blocks\Shipping;
use Automattic\WooCommerce\Blocks\Assets\Api as AssetApi;
use Automattic\WooCommerce\Blocks\Assets\AssetDataRegistry;
use Automattic\WooCommerce\Blocks\Tests\BlockTypes\Cart;
use Automattic\WooCommerce\Blocks\Utils\CartCheckoutUtils;
use Automattic\WooCommerce\StoreApi\Utilities\LocalPickupUtils;
use Automattic\WooCommerce\Utilities\ArrayUtil;
@ -51,6 +53,7 @@ class ShippingController {
);
}
$this->asset_data_registry->add( 'collectableMethodIds', array( 'Automattic\WooCommerce\StoreApi\Utilities\LocalPickupUtils', 'get_local_pickup_method_ids' ), true );
$this->asset_data_registry->add( 'shippingCostRequiresAddress', get_option( 'woocommerce_shipping_cost_requires_address', false ) === 'yes' );
add_action( 'rest_api_init', [ $this, 'register_settings' ] );
add_action( 'admin_enqueue_scripts', [ $this, 'admin_scripts' ] );
add_action( 'admin_enqueue_scripts', [ $this, 'hydrate_client_settings' ] );
@ -60,6 +63,105 @@ class ShippingController {
add_filter( 'woocommerce_shipping_packages', array( $this, 'filter_shipping_packages' ) );
add_filter( 'pre_update_option_woocommerce_pickup_location_settings', array( $this, 'flush_cache' ) );
add_filter( 'pre_update_option_pickup_location_pickup_locations', array( $this, 'flush_cache' ) );
add_filter( 'woocommerce_shipping_settings', array( $this, 'remove_shipping_settings' ) );
add_filter( 'wc_shipping_enabled', array( $this, 'force_shipping_enabled' ), 100, 1 );
// This is required to short circuit `show_shipping` from class-wc-cart.php - without it, that function
// returns based on the option's value in the DB and we can't override it any other way.
add_filter( 'option_woocommerce_shipping_cost_requires_address', array( $this, 'override_cost_requires_address_option' ) );
}
/**
* Overrides the option to force shipping calculations NOT to wait until an address is entered, but only if the
* Checkout page contains the Checkout Block.
*
* @param boolean $value Whether shipping cost calculation requires address to be entered.
* @return boolean Whether shipping cost calculation should require an address to be entered before calculating.
*/
public function override_cost_requires_address_option( $value ) {
if ( CartCheckoutUtils::is_checkout_block_default() ) {
return 'no';
}
return $value;
}
/**
* Force shipping to be enabled if the Checkout block is in use on the Checkout page.
*
* @param boolean $enabled Whether shipping is currently enabled.
* @return boolean Whether shipping should continue to be enabled/disabled.
*/
public function force_shipping_enabled( $enabled ) {
if ( CartCheckoutUtils::is_checkout_block_default() ) {
return true;
}
return $enabled;
}
/**
* If the Checkout block Remove shipping settings from WC Core's admin panels that are now block settings.
*
* @param array $settings The default WC shipping settings.
* @return array|mixed The filtered settings with relevant items removed.
*/
public function remove_shipping_settings( $settings ) {
// Do not add the "Hide shipping costs until an address is entered" setting if the Checkout block is not used on the WC checkout page.
if ( CartCheckoutUtils::is_checkout_block_default() ) {
$settings = array_filter(
$settings,
function( $setting ) {
return ! in_array(
$setting['id'],
array(
'woocommerce_shipping_cost_requires_address',
),
true
);
}
);
}
// Do not add the shipping calculator setting if the Cart block is not used on the WC cart page.
if ( CartCheckoutUtils::is_cart_block_default() ) {
// If the Cart is default, but not the checkout, we should ensure the 'Calculations' title is added to the
// `woocommerce_shipping_cost_requires_address` options group, since it is attached to the
// `woocommerce_enable_shipping_calc` option that we're going to remove later.
if ( ! CartCheckoutUtils::is_checkout_block_default() ) {
$calculations_title = '';
// Get Calculations title so we can add it to 'Hide shipping costs until an address is entered' option.
foreach ( $settings as $setting ) {
if ( 'woocommerce_enable_shipping_calc' === $setting['id'] ) {
$calculations_title = $setting['title'];
break;
}
}
// Add Calculations title to 'Hide shipping costs until an address is entered' option.
foreach ( $settings as $index => $setting ) {
if ( 'woocommerce_shipping_cost_requires_address' === $setting['id'] ) {
$settings[ $index ]['title'] = $calculations_title;
$settings[ $index ]['checkboxgroup'] = 'start';
break;
}
}
}
$settings = array_filter(
$settings,
function( $setting ) {
return ! in_array(
$setting['id'],
array(
'woocommerce_enable_shipping_calc',
),
true
);
}
);
}
return $settings;
}
/**
@ -223,8 +325,7 @@ class ShippingController {
* Registers the Local Pickup shipping method used by the Checkout Block.
*/
public function register_local_pickup() {
$checkout_page_id = wc_get_page_id( 'checkout' );
if ( $checkout_page_id && has_block( 'woocommerce/checkout', $checkout_page_id ) ) {
if ( CartCheckoutUtils::is_checkout_block_default() ) {
wc()->shipping->register_shipping_method( new PickupLocation() );
}
}

View File

@ -0,0 +1,28 @@
<?php
namespace Automattic\WooCommerce\Blocks\Utils;
/**
* Class containing utility methods for dealing with the Cart and Checkout blocks.
*/
class CartCheckoutUtils {
/**
* Checks if the default cart page is using the Cart block.
*
* @return bool true if the WC cart page is using the Cart block.
*/
public static function is_cart_block_default() {
$cart_page_id = wc_get_page_id( 'cart' );
return $cart_page_id && has_block( 'woocommerce/cart', $cart_page_id );
}
/**
* Checks if the default checkout page is using the Checkout block.
*
* @return bool true if the WC checkout page is using the Checkout block.
*/
public static function is_checkout_block_default() {
$checkout_page_id = wc_get_page_id( 'checkout' );
return $checkout_page_id && has_block( 'woocommerce/checkout', $checkout_page_id );
}
}

View File

@ -1 +1 @@
{"title":"Checkout Block","pageContent":"<!-- wp:woocommerce/checkout -->\n<div class=\"wp-block-woocommerce-checkout wc-block-checkout is-loading\"><!-- wp:woocommerce/checkout-fields-block -->\n<div class=\"wp-block-woocommerce-checkout-fields-block\"><!-- wp:woocommerce/checkout-express-payment-block -->\n<div class=\"wp-block-woocommerce-checkout-express-payment-block\"></div>\n<!-- /wp:woocommerce/checkout-express-payment-block -->\n\n<!-- wp:woocommerce/checkout-contact-information-block -->\n<div class=\"wp-block-woocommerce-checkout-contact-information-block\"></div>\n<!-- /wp:woocommerce/checkout-contact-information-block -->\n\n<!-- wp:woocommerce/checkout-shipping-address-block -->\n<div class=\"wp-block-woocommerce-checkout-shipping-address-block\"></div>\n<!-- /wp:woocommerce/checkout-shipping-address-block -->\n\n<!-- wp:woocommerce/checkout-billing-address-block -->\n<div class=\"wp-block-woocommerce-checkout-billing-address-block\"></div>\n<!-- /wp:woocommerce/checkout-billing-address-block -->\n\n<!-- wp:woocommerce/checkout-shipping-methods-block -->\n<div class=\"wp-block-woocommerce-checkout-shipping-methods-block\"></div>\n<!-- /wp:woocommerce/checkout-shipping-methods-block -->\n\n<!-- wp:woocommerce/checkout-payment-block -->\n<div class=\"wp-block-woocommerce-checkout-payment-block\"></div>\n<!-- /wp:woocommerce/checkout-payment-block -->\n\n<!-- wp:woocommerce/checkout-order-note-block -->\n<div class=\"wp-block-woocommerce-checkout-order-note-block\"></div>\n<!-- /wp:woocommerce/checkout-order-note-block -->\n\n<!-- wp:woocommerce/checkout-terms-block -->\n<div class=\"wp-block-woocommerce-checkout-terms-block\"></div>\n<!-- /wp:woocommerce/checkout-terms-block -->\n\n<!-- wp:woocommerce/checkout-actions-block -->\n<div class=\"wp-block-woocommerce-checkout-actions-block\"></div>\n<!-- /wp:woocommerce/checkout-actions-block --></div>\n<!-- /wp:woocommerce/checkout-fields-block -->\n\n<!-- wp:woocommerce/checkout-totals-block -->\n<div class=\"wp-block-woocommerce-checkout-totals-block\"><!-- wp:woocommerce/checkout-order-summary-block -->\n<div class=\"wp-block-woocommerce-checkout-order-summary-block\"></div>\n<!-- /wp:woocommerce/checkout-order-summary-block --></div>\n<!-- /wp:woocommerce/checkout-totals-block --></div>\n<!-- /wp:woocommerce/checkout -->"}
{"title":"Checkout Block","pageContent":"<!-- wp:woocommerce/checkout -->\n<div class=\"wp-block-woocommerce-checkout wc-block-checkout is-loading\"><!-- wp:woocommerce/checkout-fields-block -->\n<div class=\"wp-block-woocommerce-checkout-fields-block\"><!-- wp:woocommerce/checkout-express-payment-block -->\n<div class=\"wp-block-woocommerce-checkout-express-payment-block\"></div>\n<!-- /wp:woocommerce/checkout-express-payment-block -->\n\n<!-- wp:woocommerce/checkout-contact-information-block -->\n<div class=\"wp-block-woocommerce-checkout-contact-information-block\"></div>\n<!-- /wp:woocommerce/checkout-contact-information-block -->\n\n<!-- wp:woocommerce/checkout-shipping-address-block -->\n<div class=\"wp-block-woocommerce-checkout-shipping-address-block\"></div>\n<!-- /wp:woocommerce/checkout-shipping-address-block -->\n\n<!-- wp:woocommerce/checkout-billing-address-block -->\n<div class=\"wp-block-woocommerce-checkout-billing-address-block\"></div>\n<!-- /wp:woocommerce/checkout-billing-address-block -->\n\n<!-- wp:woocommerce/checkout-shipping-methods-block {\"shippingCostRequiresAddress\":false} -->\n<div class=\"wp-block-woocommerce-checkout-shipping-methods-block\"></div>\n<!-- /wp:woocommerce/checkout-shipping-methods-block -->\n\n<!-- wp:woocommerce/checkout-payment-block -->\n<div class=\"wp-block-woocommerce-checkout-payment-block\"></div>\n<!-- /wp:woocommerce/checkout-payment-block -->\n\n<!-- wp:woocommerce/checkout-order-note-block -->\n<div class=\"wp-block-woocommerce-checkout-order-note-block\"></div>\n<!-- /wp:woocommerce/checkout-order-note-block -->\n\n<!-- wp:woocommerce/checkout-terms-block -->\n<div class=\"wp-block-woocommerce-checkout-terms-block\"></div>\n<!-- /wp:woocommerce/checkout-terms-block -->\n\n<!-- wp:woocommerce/checkout-actions-block -->\n<div class=\"wp-block-woocommerce-checkout-actions-block\"></div>\n<!-- /wp:woocommerce/checkout-actions-block --></div>\n<!-- /wp:woocommerce/checkout-fields-block -->\n\n<!-- wp:woocommerce/checkout-totals-block -->\n<div class=\"wp-block-woocommerce-checkout-totals-block\"><!-- wp:woocommerce/checkout-order-summary-block -->\n<div class=\"wp-block-woocommerce-checkout-order-summary-block\"></div>\n<!-- /wp:woocommerce/checkout-order-summary-block --></div>\n<!-- /wp:woocommerce/checkout-totals-block --></div>\n<!-- /wp:woocommerce/checkout -->"}

View File

@ -22,6 +22,7 @@ import {
openWidgetEditor,
closeModalIfExists,
} from '../../utils.js';
import { merchant as merchantUtils } from '../../../utils/merchant';
const block = {
name: 'Checkout',
@ -64,6 +65,79 @@ describe( `${ block.name } Block`, () => {
await selectBlockByName( block.slug );
} );
it( 'can toggle "hide shipping costs until an address is entered"', async () => {
await selectBlockByName(
'woocommerce/checkout-shipping-methods-block'
);
const toggleLabel = await findLabelWithText(
'Hide shipping costs until an address is entered'
);
await toggleLabel.click();
const shippingOptionsRequireAddressText = await page.$x(
'//p[contains(text(), "Shipping options will be displayed here after entering your full shipping address.")]'
);
await expect( shippingOptionsRequireAddressText ).toHaveLength(
1
);
await toggleLabel.click();
await expect( page ).toMatchElement(
'.wc-block-components-shipping-rates-control'
);
} );
it( 'toggles the same setting in shipping method and shipping methods blocks', async () => {
await merchantUtils.goToLocalPickupSettingsPage();
await merchantUtils.enableLocalPickup();
await merchantUtils.saveLocalPickupSettingsPageWithRefresh();
await visitBlockPage( `${ block.name } Block` );
await expect( page ).toClick(
'.wc-block-checkout__shipping-method button',
{ text: 'Shipping' }
);
await openDocumentSettingsSidebar();
const toggleLabel = await findLabelWithText(
'Hide shipping costs until an address is entered'
);
await toggleLabel.click();
const [ label ] = await page.$x(
'//label[contains(., "Hide shipping costs until an address is entered")]'
);
const shippingMethodForValue = await page.evaluate(
( passedLabel ) => passedLabel.getAttribute( 'for' ),
label
);
const shippingMethodSettingIsChecked = await page.evaluate(
( passedShippingMethodForValue ) =>
document.getElementById( passedShippingMethodForValue )
.checked,
shippingMethodForValue
);
await expect( shippingMethodSettingIsChecked ).toBe( true );
await selectBlockByName(
'woocommerce/checkout-shipping-methods-block'
);
const [ shippingMethodsLabel ] = await page.$x(
'//label[contains(., "Hide shipping costs until an address is entered")]'
);
const shippingMethodsLabelForValue = await page.evaluate(
( passedShippingMethodsLabel ) =>
passedShippingMethodsLabel.getAttribute( 'for' ),
shippingMethodsLabel
);
const shippingMethodLabelIsChecked = await page.evaluate(
( passedShippingMethodsLabelForValue ) =>
document.getElementById(
passedShippingMethodsLabelForValue
).checked,
shippingMethodsLabelForValue
);
expect( shippingMethodSettingIsChecked ).toBe(
shippingMethodLabelIsChecked
);
} );
it( 'can enable dark mode inputs', async () => {
const toggleLabel = await findLabelWithText(
'Dark mode inputs'

View File

@ -83,6 +83,11 @@ describe( 'Merchant → Checkout → Can adjust T&S and Privacy Policy options',
await shopper.block.goToCheckout();
await shopper.block.fillBillingDetails( BILLING_DETAILS );
// Wait for the "Place Order" button to avoid flakey tests.
await page.waitForSelector(
'.wc-block-components-checkout-place-order-button:not([disabled])'
);
// Placing an order now, must lead to an error.
await page.click( '.wc-block-components-checkout-place-order-button' );

View File

@ -3,52 +3,17 @@
*/
import { switchUserToAdmin, visitAdminPage } from '@wordpress/e2e-test-utils';
import { findLabelWithText } from '@woocommerce/blocks-test-utils';
import WooCommerceRestApi from '@woocommerce/woocommerce-rest-api';
import { default as axios } from 'axios';
const goToSettingsPage = async () => {
await visitAdminPage(
'admin.php',
'page=wc-settings&tab=shipping&section=pickup_location'
);
await page.waitForSelector(
'#wc-shipping-method-pickup-location-settings-container'
);
};
const saveSettingsPageWithRefresh = async () => {
await expect( page ).toClick( 'button', {
text: 'Save changes',
} );
await expect( page ).toMatchElement( '.components-snackbar__content', {
text: 'Local Pickup settings have been saved.',
} );
await goToSettingsPage();
};
/**
* Internal dependencies
*/
import { merchant } from '../../../utils';
const setDefaults = async () => {
const enabledLabel = await findLabelWithText( 'Enable local pickup' );
const enabledChecked = await page.$eval(
'#inspector-checkbox-control-1',
( el ) => ( el as HTMLInputElement ).checked
);
if ( enabledChecked ) {
await enabledLabel.click();
}
await expect( page ).toFill(
'input[name="local_pickup_title"]',
'Local Pickup'
);
const costLabel = await findLabelWithText(
'Add a price for customers who choose local pickup'
);
const costChecked = await page.$eval(
'#inspector-checkbox-control-1',
( el ) => ( el as HTMLInputElement ).checked
);
if ( costChecked ) {
await costLabel.click();
}
await merchant.enableLocalPickup();
await merchant.removeCostForLocalPickup();
};
const clearLocations = async () => {
@ -66,32 +31,123 @@ const clearLocations = async () => {
}
};
/**
* Sets the WC Cart and Checkout page IDs to the IDs of the pages with the given slugs.
*/
const setCartCheckoutPages = async ( {
cartSlug,
checkoutSlug,
}: {
cartSlug: string;
checkoutSlug: string;
} ) => {
const WPAPI = `${ process.env.WORDPRESS_BASE_URL }/wp-json/wp/v2/pages`;
const response = await axios.get( `${ WPAPI }?per_page=100` );
const pages = response.data;
const cartBlock = pages.find( ( page ) => page.slug === cartSlug );
const checkoutBlock = pages.find( ( page ) => page.slug === checkoutSlug );
const WooCommerce = new WooCommerceRestApi( {
url: `${ process.env.WORDPRESS_BASE_URL }/`,
consumerKey: 'consumer_key', // Your consumer key
consumerSecret: 'consumer_secret', // Your consumer secret
version: 'wc/v3',
axiosConfig: {
auth: {
username: process.env.WORDPRESS_LOGIN,
password: process.env.WORDPRESS_PASSWORD,
},
},
} );
const fixture = [
{
id: 'woocommerce_cart_page_id',
value: cartBlock.id.toString() || '',
},
{
id: 'woocommerce_checkout_page_id',
value: checkoutBlock.id.toString() || '',
},
];
await WooCommerce.post( 'settings/advanced/batch', {
update: fixture,
} );
};
describe( `Local Pickup Settings`, () => {
beforeAll( async () => {
await switchUserToAdmin();
await goToSettingsPage();
await merchant.goToLocalPickupSettingsPage();
await setDefaults();
await clearLocations();
await saveSettingsPageWithRefresh();
await merchant.saveLocalPickupSettingsPageWithRefresh();
} );
afterAll( async () => {
await switchUserToAdmin();
await goToSettingsPage();
await merchant.goToLocalPickupSettingsPage();
await setDefaults();
await clearLocations();
await saveSettingsPageWithRefresh();
await merchant.saveLocalPickupSettingsPageWithRefresh();
} );
beforeEach( async () => {
await switchUserToAdmin();
await goToSettingsPage();
await merchant.goToLocalPickupSettingsPage();
} );
it( 'renders without crashing', async () => {
await expect( page ).toMatchElement( '#local-pickup-settings' );
} );
describe( 'Core Settings', () => {
afterAll( async () => {
await setCartCheckoutPages( {
cartSlug: 'cart-block',
checkoutSlug: 'checkout-block',
} );
} );
it( 'hides the correct shipping options if Checkout block is the default', async () => {
await visitAdminPage(
'admin.php',
'page=wc-settings&tab=shipping&section=options'
);
const hideShippingLabel = await findLabelWithText(
'Hide shipping costs until an address is entered'
);
expect( hideShippingLabel ).toBeUndefined();
const shippingCalculatorLabel = await findLabelWithText(
'Enable the shipping calculator on the cart page'
);
expect( shippingCalculatorLabel ).toBeUndefined();
} );
it( 'does not hide the relevant setting if Cart or Checkout block is not the default', async () => {
await setCartCheckoutPages( {
cartSlug: 'cart',
checkoutSlug: 'checkout',
} );
await visitAdminPage(
'admin.php',
'page=wc-settings&tab=advanced'
);
await visitAdminPage(
'admin.php',
'page=wc-settings&tab=shipping&section=options'
);
const hideShippingLabel = await page.$x(
'//label[contains(., "Hide shipping costs until an address is entered")]'
);
await expect( hideShippingLabel ).toHaveLength( 1 );
const shippingCalculatorLabel = await page.$x(
'//label[contains(., "Enable the shipping calculator on the cart page")]'
);
await expect( shippingCalculatorLabel ).toHaveLength( 1 );
} );
} );
describe( 'Global Settings', () => {
it( 'allows toggling of enabled on', async () => {
const initialChecked = await page.$eval(
@ -102,7 +158,7 @@ describe( `Local Pickup Settings`, () => {
'Enable local pickup'
);
await toggleLabel.click();
await saveSettingsPageWithRefresh();
await merchant.saveLocalPickupSettingsPageWithRefresh();
expect(
await page.$eval(
@ -118,7 +174,7 @@ describe( `Local Pickup Settings`, () => {
'Local Pickup Test'
);
await saveSettingsPageWithRefresh();
await merchant.saveLocalPickupSettingsPageWithRefresh();
expect(
await page.$eval(
@ -158,7 +214,7 @@ describe( `Local Pickup Settings`, () => {
'none'
);
await saveSettingsPageWithRefresh();
await merchant.saveLocalPickupSettingsPageWithRefresh();
const refreshChecked = await page.$eval(
'#inspector-checkbox-control-1',
@ -218,7 +274,7 @@ describe( `Local Pickup Settings`, () => {
text: 'Done',
} );
await saveSettingsPageWithRefresh();
await merchant.saveLocalPickupSettingsPageWithRefresh();
await expect( page ).toMatchElement(
'.pickup-locations tbody tr td',
@ -247,7 +303,7 @@ describe( `Local Pickup Settings`, () => {
text: 'Delete location',
} );
await saveSettingsPageWithRefresh();
await merchant.saveLocalPickupSettingsPageWithRefresh();
await expect( page ).not.toMatchElement(
'.pickup-locations tbody tr td',
{

View File

@ -29,7 +29,7 @@ import {
SIMPLE_VIRTUAL_PRODUCT_NAME,
BASE_URL,
} from '../../../../utils';
import { merchant as merchantUtils } from '../../../../utils/merchant';
import { createCoupon } from '../../../utils';
let coupon;
@ -297,6 +297,44 @@ describe( 'Shopper → Checkout', () => {
const NORMAL_SHIPPING_NAME = 'Normal Shipping';
const NORMAL_SHIPPING_PRICE = '$20.00';
afterAll( async () => {
await merchant.login();
await visitBlockPage( 'Checkout Block' );
await openDocumentSettingsSidebar();
await switchBlockInspectorTabWhenGutenbergIsInstalled( 'Settings' );
await selectBlockByName(
'woocommerce/checkout-shipping-methods-block'
);
const [ label ] = await page.$x(
'//label[contains(., "Hide shipping costs until an address is entered")]'
);
const shippingMethodForValue = await page.evaluate(
( passedLabel ) => passedLabel.getAttribute( 'for' ),
label
);
let shippingMethodSettingIsChecked = await page.evaluate(
( passedShippingMethodForValue ) =>
document.getElementById( passedShippingMethodForValue )
.checked,
shippingMethodForValue
);
if ( ! shippingMethodSettingIsChecked ) {
await setCheckbox(
await getToggleIdByLabel(
'Hide shipping costs until an address is entered'
)
);
}
shippingMethodSettingIsChecked = await page.evaluate(
( passedShippingMethodForValue ) =>
document.getElementById( passedShippingMethodForValue )
.checked,
shippingMethodForValue
);
await merchantUtils.disableLocalPickup();
} );
it( 'User can choose free shipping', async () => {
await shopper.block.goToShop();
await shopper.addToCartFromShopPage( SIMPLE_PHYSICAL_PRODUCT_NAME );
@ -326,11 +364,175 @@ describe( 'Shopper → Checkout', () => {
await expect( page ).toMatch( 'Order received' );
await expect( page ).toMatch( NORMAL_SHIPPING_NAME );
} );
it( 'User sees the correct shipping options based on block settings', async () => {
await preventCompatibilityNotice();
await merchant.login();
await visitBlockPage( 'Checkout Block' );
await openDocumentSettingsSidebar();
await switchBlockInspectorTabWhenGutenbergIsInstalled( 'Settings' );
await selectBlockByName(
'woocommerce/checkout-shipping-methods-block'
);
const [ label ] = await page.$x(
'//label[contains(., "Hide shipping costs until an address is entered")]'
);
const shippingMethodForValue = await page.evaluate(
( passedLabel ) => passedLabel.getAttribute( 'for' ),
label
);
let shippingMethodSettingIsChecked = await page.evaluate(
( passedShippingMethodForValue ) =>
document.getElementById( passedShippingMethodForValue )
.checked,
shippingMethodForValue
);
if ( ! shippingMethodSettingIsChecked ) {
await setCheckbox(
await getToggleIdByLabel(
'Hide shipping costs until an address is entered'
)
);
}
shippingMethodSettingIsChecked = await page.evaluate(
( passedShippingMethodForValue ) =>
document.getElementById( passedShippingMethodForValue )
.checked,
shippingMethodForValue
);
await expect( shippingMethodSettingIsChecked ).toBe( true );
await saveOrPublish();
await shopper.block.emptyCart();
// Log out to have a fresh empty cart.
await shopper.logout();
await shopper.block.goToShop();
await shopper.addToCartFromShopPage( SIMPLE_PHYSICAL_PRODUCT_NAME );
await shopper.block.goToCheckout();
// Expect no shipping options to be shown, but with a friendly message.
const shippingOptionsRequireAddressText = await page.$x(
'//p[contains(text(), "Shipping options will be displayed here after entering your full shipping address.")]'
);
expect( shippingOptionsRequireAddressText ).toHaveLength( 1 );
// Enter the address and expect shipping options to be shown.
await shopper.block.fillInCheckoutWithTestData();
await expect( page ).toMatchElement(
'.wc-block-components-shipping-rates-control'
);
// This sequence will reset the checkout form.
await shopper.login();
await shopper.logout();
await preventCompatibilityNotice();
await merchant.login();
await visitBlockPage( 'Checkout Block' );
await openDocumentSettingsSidebar();
await switchBlockInspectorTabWhenGutenbergIsInstalled( 'Settings' );
await selectBlockByName(
'woocommerce/checkout-shipping-methods-block'
);
await unsetCheckbox(
await getToggleIdByLabel(
'Hide shipping costs until an address is entered'
)
);
await saveOrPublish();
await shopper.block.emptyCart();
await shopper.block.goToShop();
await shopper.addToCartFromShopPage( SIMPLE_PHYSICAL_PRODUCT_NAME );
await shopper.block.goToCheckout();
// Expect the shipping options to be displayed without entering an address.
await expect( page ).toMatchElement(
'.wc-block-components-shipping-rates-control'
);
} );
it( 'User does not see shipping rates until full address is entered', async () => {
await preventCompatibilityNotice();
await merchant.login();
await merchantUtils.enableLocalPickup();
await merchantUtils.addLocalPickupLocation();
await visitBlockPage( 'Checkout Block' );
await openDocumentSettingsSidebar();
await switchBlockInspectorTabWhenGutenbergIsInstalled( 'Settings' );
await selectBlockByName(
'woocommerce/checkout-shipping-methods-block'
);
await setCheckbox(
await getToggleIdByLabel(
'Hide shipping costs until an address is entered'
)
);
await saveOrPublish();
await shopper.block.emptyCart();
// Log out to have a fresh empty cart.
await shopper.logout();
await shopper.block.goToShop();
await shopper.addToCartFromShopPage( SIMPLE_PHYSICAL_PRODUCT_NAME );
await shopper.block.goToCheckout();
// Expect no shipping options to be shown, but with a friendly message.
const shippingOptionsRequireAddressText = await page.$x(
'//p[contains(text(), "Shipping options will be displayed here after entering your full shipping address.")]'
);
expect( shippingOptionsRequireAddressText ).toHaveLength( 1 );
await expect( page ).toClick(
'.wc-block-checkout__shipping-method button',
{ text: 'Shipping' }
);
// Enter the address but not city and expect shipping options not to be shown.
await shopper.block.fillInCheckoutWithTestData( { city: '' } );
await expect( page ).not.toMatchElement(
'.wc-block-components-shipping-rates-control'
);
// This sequence will reset the checkout form.
await shopper.login();
await shopper.logout();
await preventCompatibilityNotice();
await merchant.login();
await visitBlockPage( 'Checkout Block' );
await openDocumentSettingsSidebar();
await switchBlockInspectorTabWhenGutenbergIsInstalled( 'Settings' );
await selectBlockByName(
'woocommerce/checkout-shipping-methods-block'
);
await unsetCheckbox(
await getToggleIdByLabel(
'Hide shipping costs until an address is entered'
)
);
await saveOrPublish();
await shopper.block.emptyCart();
await shopper.block.goToShop();
await shopper.addToCartFromShopPage( SIMPLE_PHYSICAL_PRODUCT_NAME );
await shopper.block.goToCheckout();
// Expect the shipping options to be displayed without entering an address.
await expect( page ).toMatchElement(
'.wc-block-components-shipping-rates-control'
);
} );
} );
describe( 'Coupons', () => {
beforeAll( async () => {
coupon = await createCoupon( { usageLimit: 1 } );
await shopper.logout();
await shopper.login();
} );

View File

@ -1,17 +0,0 @@
/**
* External dependencies
*/
import { merchant as wcMerchant } from '@woocommerce/e2e-utils';
import { visitAdminPage } from '@wordpress/e2e-test-utils';
export const merchant = {
...wcMerchant,
changeLanguage: async ( language ) => {
await visitAdminPage( 'options-general.php' );
await page.select( 'select#WPLANG', language );
await page.click( 'input[type="submit"]' );
await page.waitForSelector( '#setting-error-settings_updated', {
visible: true,
} );
},
};

View File

@ -0,0 +1,110 @@
/**
* External dependencies
*/
import { merchant as wcMerchant } from '@woocommerce/e2e-utils';
import { visitAdminPage } from '@wordpress/e2e-test-utils';
import { findLabelWithText } from '@woocommerce/blocks-test-utils';
export const merchant = {
...wcMerchant,
changeLanguage: async ( language ) => {
await visitAdminPage( 'options-general.php' );
await page.select( 'select#WPLANG', language );
await page.click( 'input[type="submit"]' );
await page.waitForSelector( '#setting-error-settings_updated', {
visible: true,
} );
},
goToLocalPickupSettingsPage: async () => {
await visitAdminPage(
'admin.php',
'page=wc-settings&tab=shipping&section=pickup_location'
);
await page.waitForSelector(
'#wc-shipping-method-pickup-location-settings-container'
);
},
saveLocalPickupSettingsPageWithRefresh: async () => {
await expect( page ).toClick( 'button', {
text: 'Save changes',
} );
await expect( page ).toMatchElement( '.components-snackbar__content', {
text: 'Local Pickup settings have been saved.',
} );
await merchant.goToLocalPickupSettingsPage();
},
enableLocalPickup: async () => {
await merchant.goToLocalPickupSettingsPage();
const enabledLabel = await findLabelWithText( 'Enable local pickup' );
const enabledChecked = await page.$eval(
'#inspector-checkbox-control-1',
( el ) => ( el as HTMLInputElement ).checked
);
if ( ! enabledChecked ) {
await enabledLabel.click();
}
await expect( page ).toFill(
'input[name="local_pickup_title"]',
'Local Pickup'
);
await merchant.saveLocalPickupSettingsPageWithRefresh();
},
disableLocalPickup: async () => {
await merchant.goToLocalPickupSettingsPage();
const enabledLabel = await findLabelWithText( 'Enable local pickup' );
const enabledChecked = await page.$eval(
'#inspector-checkbox-control-1',
( el ) => ( el as HTMLInputElement ).checked
);
if ( enabledChecked ) {
await enabledLabel.click();
}
await merchant.saveLocalPickupSettingsPageWithRefresh();
},
removeCostForLocalPickup: async () => {
const costLabel = await findLabelWithText(
'Add a price for customers who choose local pickup'
);
const costChecked = await page.$eval(
'#inspector-checkbox-control-1',
( el ) => ( el as HTMLInputElement ).checked
);
if ( costChecked ) {
await costLabel.click();
}
},
addLocalPickupLocation: async () => {
await merchant.goToLocalPickupSettingsPage();
await expect( page ).toClick( 'button', {
text: 'Add pickup location',
} );
await expect( page ).toFill(
'input[name="location_name"]',
'Test Location'
);
await expect( page ).toFill(
'input[name="location_address"]',
'Test Address 1'
);
await expect( page ).toFill(
'input[name="location_city"]',
'Test City'
);
await expect( page ).toFill(
'input[name="location_postcode"]',
'90210'
);
await expect( page ).toFill(
'input[name="pickup_details"]',
'Collect from store'
);
await expect( page ).toSelect(
'select[name="location_country"]',
'US'
);
await expect( page ).toSelect( 'select[name="location_state"]', 'CA' );
await expect( page ).toClick( 'button', { text: 'Done' } );
await merchant.saveLocalPickupSettingsPageWithRefresh();
},
};