From e94143c8585e691fc8d9b35ae81861f5821fe237 Mon Sep 17 00:00:00 2001 From: Thomas Roberts Date: Tue, 7 Mar 2023 09:21:57 -0800 Subject: [PATCH 01/53] Force shipping to be enabled if the Checkout block is in use. --- src/Shipping/ShippingController.php | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/Shipping/ShippingController.php b/src/Shipping/ShippingController.php index ba4cc3ad623..0061b30b481 100644 --- a/src/Shipping/ShippingController.php +++ b/src/Shipping/ShippingController.php @@ -3,6 +3,7 @@ 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; @@ -62,6 +63,20 @@ function() { 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' ) ); + } + + /** + * 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; } /** From 18af032de98ccae6423f626d542ae9bbafd6aca0 Mon Sep 17 00:00:00 2001 From: Thomas Roberts Date: Tue, 7 Mar 2023 12:21:01 -0800 Subject: [PATCH 02/53] Add filter to override cost requires address option --- src/Shipping/ShippingController.php | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/Shipping/ShippingController.php b/src/Shipping/ShippingController.php index 0061b30b481..12954057870 100644 --- a/src/Shipping/ShippingController.php +++ b/src/Shipping/ShippingController.php @@ -63,7 +63,25 @@ function() { 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' ) ); + 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; } /** From e859292c502653996dd962aece5ba7119f3f5d8a Mon Sep 17 00:00:00 2001 From: Thomas Roberts Date: Tue, 7 Mar 2023 12:21:41 -0800 Subject: [PATCH 03/53] Add shippingCostRequiresAddress option --- src/Shipping/ShippingController.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Shipping/ShippingController.php b/src/Shipping/ShippingController.php index 12954057870..d0bdae7e910 100644 --- a/src/Shipping/ShippingController.php +++ b/src/Shipping/ShippingController.php @@ -53,6 +53,7 @@ function() { ); } $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' ] ); From 871e6b5212988c962dc58d86ee473f35996a45e7 Mon Sep 17 00:00:00 2001 From: Thomas Roberts Date: Tue, 7 Mar 2023 12:33:41 -0800 Subject: [PATCH 04/53] Check if the address is required before showing rates --- .../checkout-shipping-methods-block/block.tsx | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/assets/js/blocks/checkout/inner-blocks/checkout-shipping-methods-block/block.tsx b/assets/js/blocks/checkout/inner-blocks/checkout-shipping-methods-block/block.tsx index fb241284d8a..9895ac7b94d 100644 --- a/assets/js/blocks/checkout/inner-blocks/checkout-shipping-methods-block/block.tsx +++ b/assets/js/blocks/checkout/inner-blocks/checkout-shipping-methods-block/block.tsx @@ -2,11 +2,15 @@ * External dependencies */ import { __ } from '@wordpress/i18n'; -import { useShippingData } from '@woocommerce/base-context/hooks'; +import { + useCustomerData, + useShippingData, +} from '@woocommerce/base-context/hooks'; import { ShippingRatesControl } from '@woocommerce/base-components/cart-checkout'; import { getShippingRatesPackageCount, hasCollectableRate, + isAddressComplete, } from '@woocommerce/base-utils'; import { getCurrencyFromPriceResponse } from '@woocommerce/price-format'; import FormattedMonetaryAmount from '@woocommerce/base-components/formatted-monetary-amount'; @@ -63,6 +67,8 @@ const Block = ( { noShippingPlaceholder = null } ): ReactElement | null => { isCollectable, } = useShippingData(); + const { shippingAddress } = useCustomerData(); + const filteredShippingRates = isCollectable ? shippingRates.map( ( shippingRatesPackage ) => { return { @@ -84,10 +90,18 @@ const Block = ( { noShippingPlaceholder = null } ): ReactElement | null => { const shippingRatesPackageCount = getShippingRatesPackageCount( shippingRates ); + const shippingCostRequiresAddress = getSetting< boolean >( + 'shippingCostRequiresAddress', + false + ); + + const addressComplete = isAddressComplete( shippingAddress ); + if ( - ! isEditor && - ! hasCalculatedShipping && - ! shippingRatesPackageCount + ( ! isEditor && + ! hasCalculatedShipping && + ! shippingRatesPackageCount ) || + ( shippingCostRequiresAddress && ! addressComplete ) ) { return (

From 2815c96a3d997224d16086a999e9c358c2514528 Mon Sep 17 00:00:00 2001 From: Thomas Roberts Date: Tue, 7 Mar 2023 15:26:18 -0800 Subject: [PATCH 05/53] Show shipping rates in editor --- .../inner-blocks/checkout-shipping-methods-block/block.tsx | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/assets/js/blocks/checkout/inner-blocks/checkout-shipping-methods-block/block.tsx b/assets/js/blocks/checkout/inner-blocks/checkout-shipping-methods-block/block.tsx index 9895ac7b94d..4a7206cc9ab 100644 --- a/assets/js/blocks/checkout/inner-blocks/checkout-shipping-methods-block/block.tsx +++ b/assets/js/blocks/checkout/inner-blocks/checkout-shipping-methods-block/block.tsx @@ -98,10 +98,9 @@ const Block = ( { noShippingPlaceholder = null } ): ReactElement | null => { const addressComplete = isAddressComplete( shippingAddress ); if ( - ( ! isEditor && - ! hasCalculatedShipping && - ! shippingRatesPackageCount ) || - ( shippingCostRequiresAddress && ! addressComplete ) + ! isEditor && + ( ( ! hasCalculatedShipping && ! shippingRatesPackageCount ) || + ( shippingCostRequiresAddress && ! addressComplete ) ) ) { return (

From 9b75f1512856c79e530f6d74f3cff40cf55bc8c7 Mon Sep 17 00:00:00 2001 From: Thomas Roberts Date: Tue, 7 Mar 2023 15:43:20 -0800 Subject: [PATCH 06/53] Add shippingCostRequiresAddress attribute to shipping methods block --- .../checkout-shipping-methods-block/attributes.tsx | 4 ++++ .../inner-blocks/checkout-shipping-methods-block/block.json | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/assets/js/blocks/checkout/inner-blocks/checkout-shipping-methods-block/attributes.tsx b/assets/js/blocks/checkout/inner-blocks/checkout-shipping-methods-block/attributes.tsx index b36f320660d..17cb4f6bf2f 100644 --- a/assets/js/blocks/checkout/inner-blocks/checkout-shipping-methods-block/attributes.tsx +++ b/assets/js/blocks/checkout/inner-blocks/checkout-shipping-methods-block/attributes.tsx @@ -24,4 +24,8 @@ export default { remove: true, }, }, + shippingCostRequiresAddress: { + type: 'boolean', + default: false, + }, }; diff --git a/assets/js/blocks/checkout/inner-blocks/checkout-shipping-methods-block/block.json b/assets/js/blocks/checkout/inner-blocks/checkout-shipping-methods-block/block.json index df53c0117c8..b4c5dc5192f 100644 --- a/assets/js/blocks/checkout/inner-blocks/checkout-shipping-methods-block/block.json +++ b/assets/js/blocks/checkout/inner-blocks/checkout-shipping-methods-block/block.json @@ -19,6 +19,10 @@ "remove": true, "move": true } + }, + "shippingCostRequiresAddress": { + "type": "boolean", + "default": false } }, "parent": [ "woocommerce/checkout-fields-block" ], From dbb88e6e8be570b1123fabd623624523c2f36302 Mon Sep 17 00:00:00 2001 From: Thomas Roberts Date: Tue, 7 Mar 2023 15:51:00 -0800 Subject: [PATCH 07/53] Update frontend type to show shippingCostRequiresAddress is a prop --- .../inner-blocks/checkout-shipping-methods-block/frontend.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/assets/js/blocks/checkout/inner-blocks/checkout-shipping-methods-block/frontend.tsx b/assets/js/blocks/checkout/inner-blocks/checkout-shipping-methods-block/frontend.tsx index dd67d39fba5..7c34378fb2f 100644 --- a/assets/js/blocks/checkout/inner-blocks/checkout-shipping-methods-block/frontend.tsx +++ b/assets/js/blocks/checkout/inner-blocks/checkout-shipping-methods-block/frontend.tsx @@ -31,6 +31,7 @@ const FrontendBlock = ( { showStepNumber: boolean; children: JSX.Element; className?: string; + shippingCostRequiresAddress: boolean; } ) => { const checkoutIsProcessing = useSelect( ( select ) => select( CHECKOUT_STORE_KEY ).isProcessing() From 0cd311151987ad0df555a7d33837f84e0f927e4d Mon Sep 17 00:00:00 2001 From: Thomas Roberts Date: Tue, 7 Mar 2023 15:51:34 -0800 Subject: [PATCH 08/53] Add control to toggle shippingCostRequiresAddress option --- .../checkout-shipping-methods-block/edit.tsx | 34 +++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/assets/js/blocks/checkout/inner-blocks/checkout-shipping-methods-block/edit.tsx b/assets/js/blocks/checkout/inner-blocks/checkout-shipping-methods-block/edit.tsx index ac1d3bbd9bf..efd09b53bc1 100644 --- a/assets/js/blocks/checkout/inner-blocks/checkout-shipping-methods-block/edit.tsx +++ b/assets/js/blocks/checkout/inner-blocks/checkout-shipping-methods-block/edit.tsx @@ -4,12 +4,13 @@ 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'; /** * Internal dependencies @@ -38,6 +39,7 @@ export const Edit = ( { description: string; showStepNumber: boolean; className: string; + shippingCostRequiresAddress: boolean; }; setAttributes: ( attributes: Record< string, unknown > ) => void; } ): JSX.Element | null => { @@ -54,6 +56,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 ( + + + toggleAttribute( 'shippingCostRequiresAddress' ) + } + /> + { globalShippingMethods.length > 0 && ( - } /> + } + shippingCostRequiresAddress={ + attributes.shippingCostRequiresAddress + } + /> From 0ea6f946238dd29456a59badf96eb68c973e9973 Mon Sep 17 00:00:00 2001 From: Thomas Roberts Date: Tue, 7 Mar 2023 15:51:56 -0800 Subject: [PATCH 09/53] Show address notice in the correct scenario --- .../checkout-shipping-methods-block/block.tsx | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/assets/js/blocks/checkout/inner-blocks/checkout-shipping-methods-block/block.tsx b/assets/js/blocks/checkout/inner-blocks/checkout-shipping-methods-block/block.tsx index 4a7206cc9ab..f777ecd4220 100644 --- a/assets/js/blocks/checkout/inner-blocks/checkout-shipping-methods-block/block.tsx +++ b/assets/js/blocks/checkout/inner-blocks/checkout-shipping-methods-block/block.tsx @@ -56,7 +56,10 @@ const renderShippingRatesControlOption = ( }; }; -const Block = ( { noShippingPlaceholder = null } ): ReactElement | null => { +const Block = ( { + noShippingPlaceholder = null, + shippingCostRequiresAddress = false, +} ): ReactElement | null => { const { isEditor } = useEditorContext(); const { @@ -90,17 +93,11 @@ const Block = ( { noShippingPlaceholder = null } ): ReactElement | null => { const shippingRatesPackageCount = getShippingRatesPackageCount( shippingRates ); - const shippingCostRequiresAddress = getSetting< boolean >( - 'shippingCostRequiresAddress', - false - ); - const addressComplete = isAddressComplete( shippingAddress ); if ( - ! isEditor && - ( ( ! hasCalculatedShipping && ! shippingRatesPackageCount ) || - ( shippingCostRequiresAddress && ! addressComplete ) ) + ( ! hasCalculatedShipping && ! shippingRatesPackageCount ) || + ( shippingCostRequiresAddress && ! addressComplete ) ) { return (

From 32c71f2f903e2e41c3465abbda31694475e2f0bf Mon Sep 17 00:00:00 2001 From: Thomas Roberts Date: Tue, 7 Mar 2023 15:55:36 -0800 Subject: [PATCH 10/53] Send shippingCostRequiresAddress to Block in front end context --- .../checkout-shipping-methods-block/frontend.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/assets/js/blocks/checkout/inner-blocks/checkout-shipping-methods-block/frontend.tsx b/assets/js/blocks/checkout/inner-blocks/checkout-shipping-methods-block/frontend.tsx index 7c34378fb2f..9fced3fa2e7 100644 --- a/assets/js/blocks/checkout/inner-blocks/checkout-shipping-methods-block/frontend.tsx +++ b/assets/js/blocks/checkout/inner-blocks/checkout-shipping-methods-block/frontend.tsx @@ -20,6 +20,7 @@ const FrontendBlock = ( { showStepNumber, children, className, + shippingCostRequiresAddress = false, }: { title: string; description: string; @@ -54,7 +55,9 @@ const FrontendBlock = ( { description={ description } showStepNumber={ showStepNumber } > - + { children } ); From cec2a6d728c16249c8061cb0d7bf453b3b97d8dc Mon Sep 17 00:00:00 2001 From: Thomas Roberts Date: Tue, 7 Mar 2023 16:11:55 -0800 Subject: [PATCH 11/53] Add e2e test for editor control --- tests/e2e/specs/backend/checkout.test.js | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tests/e2e/specs/backend/checkout.test.js b/tests/e2e/specs/backend/checkout.test.js index bc905565682..5723cdf28b6 100644 --- a/tests/e2e/specs/backend/checkout.test.js +++ b/tests/e2e/specs/backend/checkout.test.js @@ -64,6 +64,27 @@ 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( 'can enable dark mode inputs', async () => { const toggleLabel = await findLabelWithText( 'Dark mode inputs' From 263211eb37c01cd62b96d8a37412d299efac00c0 Mon Sep 17 00:00:00 2001 From: Thomas Roberts Date: Tue, 7 Mar 2023 16:51:50 -0800 Subject: [PATCH 12/53] Add e2e tests for shipping options on the front end --- .../shopper/cart-checkout/checkout.test.js | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/tests/e2e/specs/shopper/cart-checkout/checkout.test.js b/tests/e2e/specs/shopper/cart-checkout/checkout.test.js index 0a3eb12f26f..7ea0484b3c8 100644 --- a/tests/e2e/specs/shopper/cart-checkout/checkout.test.js +++ b/tests/e2e/specs/shopper/cart-checkout/checkout.test.js @@ -238,6 +238,72 @@ 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' + ); + + 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 ); + + // 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' + ); + } ); } ); describe( 'Coupons', () => { From ebc42ba05489970dd3339ac345060436f473f53e Mon Sep 17 00:00:00 2001 From: Thomas Roberts Date: Wed, 8 Mar 2023 11:03:26 -0800 Subject: [PATCH 13/53] Add updateAttributeInSiblingBlock function --- assets/js/utils/attributes.ts | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/assets/js/utils/attributes.ts b/assets/js/utils/attributes.ts index 3815d5a3aec..d8596a9fd53 100644 --- a/assets/js/utils/attributes.ts +++ b/assets/js/utils/attributes.ts @@ -3,6 +3,7 @@ */ import { getSetting } from '@woocommerce/settings'; import { AttributeObject, AttributeSetting } from '@woocommerce/types'; +import { dispatch, select } from '@wordpress/data'; const ATTRIBUTES = getSetting< AttributeSetting[] >( 'attributes', [] ); @@ -82,3 +83,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; + } + //console.log( childBlock ); + shippingMethodsBlockClientId = childBlock.clientId; + } ); + actions.updateBlockAttributes( shippingMethodsBlockClientId, { + [ attribute ]: newValue, + } ); +}; From 72c99b40a186745982b4f4c9ddc4a62c95405f93 Mon Sep 17 00:00:00 2001 From: Thomas Roberts Date: Wed, 8 Mar 2023 11:07:18 -0800 Subject: [PATCH 14/53] Add shippingCostRequiresAddress to shipping method block --- .../attributes.tsx | 4 +++ .../checkout-shipping-method-block/block.json | 4 +++ .../checkout-shipping-method-block/edit.tsx | 35 +++++++++++++++++++ 3 files changed, 43 insertions(+) diff --git a/assets/js/blocks/checkout/inner-blocks/checkout-shipping-method-block/attributes.tsx b/assets/js/blocks/checkout/inner-blocks/checkout-shipping-method-block/attributes.tsx index 42dfb4696fa..9c21e4de517 100644 --- a/assets/js/blocks/checkout/inner-blocks/checkout-shipping-method-block/attributes.tsx +++ b/assets/js/blocks/checkout/inner-blocks/checkout-shipping-method-block/attributes.tsx @@ -44,4 +44,8 @@ export default { remove: true, }, }, + shippingCostRequiresAddress: { + type: 'boolean', + default: false, + }, }; diff --git a/assets/js/blocks/checkout/inner-blocks/checkout-shipping-method-block/block.json b/assets/js/blocks/checkout/inner-blocks/checkout-shipping-method-block/block.json index b080adafec9..b3255985c2d 100644 --- a/assets/js/blocks/checkout/inner-blocks/checkout-shipping-method-block/block.json +++ b/assets/js/blocks/checkout/inner-blocks/checkout-shipping-method-block/block.json @@ -19,6 +19,10 @@ "remove": true, "move": true } + }, + "shippingCostRequiresAddress": { + "type": "boolean", + "default": false } }, "parent": [ "woocommerce/checkout-fields-block" ], diff --git a/assets/js/blocks/checkout/inner-blocks/checkout-shipping-method-block/edit.tsx b/assets/js/blocks/checkout/inner-blocks/checkout-shipping-method-block/edit.tsx index b4df6cfd99a..45660b2f190 100644 --- a/assets/js/blocks/checkout/inner-blocks/checkout-shipping-method-block/edit.tsx +++ b/assets/js/blocks/checkout/inner-blocks/checkout-shipping-method-block/edit.tsx @@ -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 = ( { ) } > + + { + updateAttributeInSiblingBlock( + clientId, + 'shippingCostRequiresAddress', + selected, + 'woocommerce/checkout-shipping-methods-block' + ); + + toggleAttribute( 'shippingCostRequiresAddress' ); + } } + /> + From ff01d030cfd34bcc2438772650dd37ab31d1e8b5 Mon Sep 17 00:00:00 2001 From: Thomas Roberts Date: Wed, 8 Mar 2023 11:07:47 -0800 Subject: [PATCH 15/53] Ensure attribute is updated in both blocks when editing --- .../checkout-shipping-methods-block/edit.tsx | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/assets/js/blocks/checkout/inner-blocks/checkout-shipping-methods-block/edit.tsx b/assets/js/blocks/checkout/inner-blocks/checkout-shipping-methods-block/edit.tsx index efd09b53bc1..0edb15d412a 100644 --- a/assets/js/blocks/checkout/inner-blocks/checkout-shipping-methods-block/edit.tsx +++ b/assets/js/blocks/checkout/inner-blocks/checkout-shipping-methods-block/edit.tsx @@ -11,6 +11,7 @@ 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 @@ -33,7 +34,9 @@ type shippingAdminLink = { export const Edit = ( { attributes, setAttributes, + clientId, }: { + clientId: string; attributes: { title: string; description: string; @@ -84,9 +87,15 @@ export const Edit = ( { 'woo-gutenberg-products-block' ) } checked={ attributes.shippingCostRequiresAddress } - onChange={ () => - toggleAttribute( 'shippingCostRequiresAddress' ) - } + onChange={ ( selected ) => { + updateAttributeInSiblingBlock( + clientId, + 'shippingCostRequiresAddress', + selected, + 'woocommerce/checkout-shipping-method-block' + ); + toggleAttribute( 'shippingCostRequiresAddress' ); + } } /> { globalShippingMethods.length > 0 && ( From 2f25f6940e21456a767ef092e22dc543419111c1 Mon Sep 17 00:00:00 2001 From: Thomas Roberts Date: Wed, 8 Mar 2023 11:49:19 -0800 Subject: [PATCH 16/53] In Shipping Methods Block, show correct component based on block setting --- .../checkout-shipping-methods-block/block.tsx | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/assets/js/blocks/checkout/inner-blocks/checkout-shipping-methods-block/block.tsx b/assets/js/blocks/checkout/inner-blocks/checkout-shipping-methods-block/block.tsx index f777ecd4220..6589bd1bc40 100644 --- a/assets/js/blocks/checkout/inner-blocks/checkout-shipping-methods-block/block.tsx +++ b/assets/js/blocks/checkout/inner-blocks/checkout-shipping-methods-block/block.tsx @@ -2,15 +2,11 @@ * External dependencies */ import { __ } from '@wordpress/i18n'; -import { - useCustomerData, - useShippingData, -} from '@woocommerce/base-context/hooks'; +import { useShippingData } from '@woocommerce/base-context/hooks'; import { ShippingRatesControl } from '@woocommerce/base-components/cart-checkout'; import { getShippingRatesPackageCount, hasCollectableRate, - isAddressComplete, } from '@woocommerce/base-utils'; import { getCurrencyFromPriceResponse } from '@woocommerce/price-format'; import FormattedMonetaryAmount from '@woocommerce/base-components/formatted-monetary-amount'; @@ -25,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. @@ -70,7 +69,9 @@ const Block = ( { isCollectable, } = useShippingData(); - const { shippingAddress } = useCustomerData(); + const shippingAddressPushed = useSelect( ( select ) => { + return select( CART_STORE_KEY ).getFullShippingAddressPushed(); + } ); const filteredShippingRates = isCollectable ? shippingRates.map( ( shippingRatesPackage ) => { @@ -90,14 +91,15 @@ const Block = ( { return null; } + const shippingAddressIsComplete = ! shippingAddressHasValidationErrors(); + const shippingRatesPackageCount = getShippingRatesPackageCount( shippingRates ); - const addressComplete = isAddressComplete( shippingAddress ); - if ( ( ! hasCalculatedShipping && ! shippingRatesPackageCount ) || - ( shippingCostRequiresAddress && ! addressComplete ) + ( shippingCostRequiresAddress && ! shippingAddressPushed ) || + ! shippingAddressIsComplete ) { return (

From f0c1fe790a43996bfb606bf585866f97aebc5185 Mon Sep 17 00:00:00 2001 From: Thomas Roberts Date: Wed, 8 Mar 2023 16:49:03 -0800 Subject: [PATCH 17/53] Show correct block in editor --- .../checkout-shipping-methods-block/block.tsx | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/assets/js/blocks/checkout/inner-blocks/checkout-shipping-methods-block/block.tsx b/assets/js/blocks/checkout/inner-blocks/checkout-shipping-methods-block/block.tsx index 6589bd1bc40..03ec8367e40 100644 --- a/assets/js/blocks/checkout/inner-blocks/checkout-shipping-methods-block/block.tsx +++ b/assets/js/blocks/checkout/inner-blocks/checkout-shipping-methods-block/block.tsx @@ -21,14 +21,11 @@ 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. @@ -69,10 +66,6 @@ const Block = ( { isCollectable, } = useShippingData(); - const shippingAddressPushed = useSelect( ( select ) => { - return select( CART_STORE_KEY ).getFullShippingAddressPushed(); - } ); - const filteredShippingRates = isCollectable ? shippingRates.map( ( shippingRatesPackage ) => { return { @@ -91,15 +84,12 @@ const Block = ( { return null; } - const shippingAddressIsComplete = ! shippingAddressHasValidationErrors(); - const shippingRatesPackageCount = getShippingRatesPackageCount( shippingRates ); if ( ( ! hasCalculatedShipping && ! shippingRatesPackageCount ) || - ( shippingCostRequiresAddress && ! shippingAddressPushed ) || - ! shippingAddressIsComplete + ( shippingCostRequiresAddress && isEditor ) ) { return (

From b5ea6ddef6a6e4e023a4a07bfd125d0526a26445 Mon Sep 17 00:00:00 2001 From: Thomas Roberts Date: Wed, 8 Mar 2023 17:06:36 -0800 Subject: [PATCH 18/53] Remove broken test from PR --- .../shopper/cart-checkout/checkout.test.js | 66 ------------------- 1 file changed, 66 deletions(-) diff --git a/tests/e2e/specs/shopper/cart-checkout/checkout.test.js b/tests/e2e/specs/shopper/cart-checkout/checkout.test.js index 7ea0484b3c8..0a3eb12f26f 100644 --- a/tests/e2e/specs/shopper/cart-checkout/checkout.test.js +++ b/tests/e2e/specs/shopper/cart-checkout/checkout.test.js @@ -238,72 +238,6 @@ 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' - ); - - 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 ); - - // 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' - ); - } ); } ); describe( 'Coupons', () => { From 2516120dca21ab95d0251f23f1008ad4caeec076 Mon Sep 17 00:00:00 2001 From: Thomas Roberts Date: Tue, 7 Mar 2023 11:34:59 -0800 Subject: [PATCH 19/53] Add setCartCheckoutPages function to update set the cart/checkout page --- tests/e2e/specs/merchant/local-pickup.test.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/e2e/specs/merchant/local-pickup.test.ts b/tests/e2e/specs/merchant/local-pickup.test.ts index 196273cf6b5..d179293ef0a 100644 --- a/tests/e2e/specs/merchant/local-pickup.test.ts +++ b/tests/e2e/specs/merchant/local-pickup.test.ts @@ -6,6 +6,11 @@ import { findLabelWithText } from '@woocommerce/blocks-test-utils'; import WooCommerceRestApi from '@woocommerce/woocommerce-rest-api'; import { default as axios } from 'axios'; +/** + * Internal dependencies + */ +import { setupPageSettings } from '../../fixtures/fixture-loaders'; + const goToSettingsPage = async () => { await visitAdminPage( 'admin.php', From 8037e69f4f9e9680ac884497b61695965959f9f1 Mon Sep 17 00:00:00 2001 From: Thomas Roberts Date: Tue, 7 Mar 2023 11:36:25 -0800 Subject: [PATCH 20/53] Add tests to ensure core shipping settings update correctly --- tests/e2e/specs/merchant/local-pickup.test.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/tests/e2e/specs/merchant/local-pickup.test.ts b/tests/e2e/specs/merchant/local-pickup.test.ts index d179293ef0a..196273cf6b5 100644 --- a/tests/e2e/specs/merchant/local-pickup.test.ts +++ b/tests/e2e/specs/merchant/local-pickup.test.ts @@ -6,11 +6,6 @@ import { findLabelWithText } from '@woocommerce/blocks-test-utils'; import WooCommerceRestApi from '@woocommerce/woocommerce-rest-api'; import { default as axios } from 'axios'; -/** - * Internal dependencies - */ -import { setupPageSettings } from '../../fixtures/fixture-loaders'; - const goToSettingsPage = async () => { await visitAdminPage( 'admin.php', From 9967dc0d4f10cf638859ae085e6f4cc2901dd299 Mon Sep 17 00:00:00 2001 From: Thomas Roberts Date: Tue, 7 Mar 2023 12:33:00 -0800 Subject: [PATCH 21/53] Add isAddressComplete function Borrowed from #8141 --- assets/js/base/utils/address.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/assets/js/base/utils/address.ts b/assets/js/base/utils/address.ts index e3738fb614b..0ee4df1cea9 100644 --- a/assets/js/base/utils/address.ts +++ b/assets/js/base/utils/address.ts @@ -100,3 +100,12 @@ export const emptyHiddenAddressFields = < return newAddress; }; + +/** + * Returns true if the address has a city and country. + */ +export const isAddressComplete = ( + address: ShippingAddress | BillingAddress +): boolean => { + return !! address.city && !! address.country; +}; From 222e011e7a36a36f6cd52c289b0b3e7c0fff9bcc Mon Sep 17 00:00:00 2001 From: Thomas Roberts Date: Tue, 7 Mar 2023 12:33:41 -0800 Subject: [PATCH 22/53] Check if the address is required before showing rates --- .../checkout-shipping-methods-block/block.tsx | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/assets/js/blocks/checkout/inner-blocks/checkout-shipping-methods-block/block.tsx b/assets/js/blocks/checkout/inner-blocks/checkout-shipping-methods-block/block.tsx index 03ec8367e40..28b78723b02 100644 --- a/assets/js/blocks/checkout/inner-blocks/checkout-shipping-methods-block/block.tsx +++ b/assets/js/blocks/checkout/inner-blocks/checkout-shipping-methods-block/block.tsx @@ -2,11 +2,15 @@ * External dependencies */ import { __ } from '@wordpress/i18n'; -import { useShippingData } from '@woocommerce/base-context/hooks'; +import { + useCustomerData, + useShippingData, +} from '@woocommerce/base-context/hooks'; import { ShippingRatesControl } from '@woocommerce/base-components/cart-checkout'; import { getShippingRatesPackageCount, hasCollectableRate, + isAddressComplete, } from '@woocommerce/base-utils'; import { getCurrencyFromPriceResponse } from '@woocommerce/price-format'; import FormattedMonetaryAmount from '@woocommerce/base-components/formatted-monetary-amount'; @@ -66,6 +70,8 @@ const Block = ( { isCollectable, } = useShippingData(); + const { shippingAddress } = useCustomerData(); + const filteredShippingRates = isCollectable ? shippingRates.map( ( shippingRatesPackage ) => { return { @@ -87,9 +93,18 @@ const Block = ( { const shippingRatesPackageCount = getShippingRatesPackageCount( shippingRates ); + const shippingCostRequiresAddress = getSetting< boolean >( + 'shippingCostRequiresAddress', + false + ); + + const addressComplete = isAddressComplete( shippingAddress ); + if ( - ( ! hasCalculatedShipping && ! shippingRatesPackageCount ) || - ( shippingCostRequiresAddress && isEditor ) + ( ! isEditor && + ! hasCalculatedShipping && + ! shippingRatesPackageCount ) || + ( shippingCostRequiresAddress && ! addressComplete ) ) { return (

From 633b32f0c0b2ce9640954c2734197c9db9308001 Mon Sep 17 00:00:00 2001 From: Thomas Roberts Date: Tue, 7 Mar 2023 15:26:18 -0800 Subject: [PATCH 23/53] Show shipping rates in editor --- .../inner-blocks/checkout-shipping-methods-block/block.tsx | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/assets/js/blocks/checkout/inner-blocks/checkout-shipping-methods-block/block.tsx b/assets/js/blocks/checkout/inner-blocks/checkout-shipping-methods-block/block.tsx index 28b78723b02..760fd726ef7 100644 --- a/assets/js/blocks/checkout/inner-blocks/checkout-shipping-methods-block/block.tsx +++ b/assets/js/blocks/checkout/inner-blocks/checkout-shipping-methods-block/block.tsx @@ -101,10 +101,9 @@ const Block = ( { const addressComplete = isAddressComplete( shippingAddress ); if ( - ( ! isEditor && - ! hasCalculatedShipping && - ! shippingRatesPackageCount ) || - ( shippingCostRequiresAddress && ! addressComplete ) + ! isEditor && + ( ( ! hasCalculatedShipping && ! shippingRatesPackageCount ) || + ( shippingCostRequiresAddress && ! addressComplete ) ) ) { return (

From c66c760cd9371bfd8cf4f635f2900b6043222db3 Mon Sep 17 00:00:00 2001 From: Thomas Roberts Date: Tue, 7 Mar 2023 15:51:56 -0800 Subject: [PATCH 24/53] Show address notice in the correct scenario --- .../checkout-shipping-methods-block/block.tsx | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/assets/js/blocks/checkout/inner-blocks/checkout-shipping-methods-block/block.tsx b/assets/js/blocks/checkout/inner-blocks/checkout-shipping-methods-block/block.tsx index 760fd726ef7..f777ecd4220 100644 --- a/assets/js/blocks/checkout/inner-blocks/checkout-shipping-methods-block/block.tsx +++ b/assets/js/blocks/checkout/inner-blocks/checkout-shipping-methods-block/block.tsx @@ -93,17 +93,11 @@ const Block = ( { const shippingRatesPackageCount = getShippingRatesPackageCount( shippingRates ); - const shippingCostRequiresAddress = getSetting< boolean >( - 'shippingCostRequiresAddress', - false - ); - const addressComplete = isAddressComplete( shippingAddress ); if ( - ! isEditor && - ( ( ! hasCalculatedShipping && ! shippingRatesPackageCount ) || - ( shippingCostRequiresAddress && ! addressComplete ) ) + ( ! hasCalculatedShipping && ! shippingRatesPackageCount ) || + ( shippingCostRequiresAddress && ! addressComplete ) ) { return (

From a63b169aaa35d201e509c4333038d486f2559079 Mon Sep 17 00:00:00 2001 From: Thomas Roberts Date: Tue, 7 Mar 2023 16:51:50 -0800 Subject: [PATCH 25/53] Add e2e tests for shipping options on the front end --- .../shopper/cart-checkout/checkout.test.js | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/tests/e2e/specs/shopper/cart-checkout/checkout.test.js b/tests/e2e/specs/shopper/cart-checkout/checkout.test.js index 0a3eb12f26f..7ea0484b3c8 100644 --- a/tests/e2e/specs/shopper/cart-checkout/checkout.test.js +++ b/tests/e2e/specs/shopper/cart-checkout/checkout.test.js @@ -238,6 +238,72 @@ 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' + ); + + 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 ); + + // 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' + ); + } ); } ); describe( 'Coupons', () => { From 704a49c75267e5945842c3848699464807949532 Mon Sep 17 00:00:00 2001 From: Thomas Roberts Date: Tue, 7 Mar 2023 17:14:03 -0800 Subject: [PATCH 26/53] Ensure errorId is passed to StateInput --- assets/js/base/components/state-input/StateInputProps.ts | 1 + assets/js/base/components/state-input/state-input.tsx | 2 ++ 2 files changed, 3 insertions(+) diff --git a/assets/js/base/components/state-input/StateInputProps.ts b/assets/js/base/components/state-input/StateInputProps.ts index bf66f2cfba4..341dbe34c27 100644 --- a/assets/js/base/components/state-input/StateInputProps.ts +++ b/assets/js/base/components/state-input/StateInputProps.ts @@ -8,6 +8,7 @@ export interface StateInputProps { onChange: ( value: string ) => void; required?: boolean; errorMessage?: string; + errorId?: string; } export type StateInputWithStatesProps = StateInputProps & { diff --git a/assets/js/base/components/state-input/state-input.tsx b/assets/js/base/components/state-input/state-input.tsx index 15d8b6f61b9..7bb069d5999 100644 --- a/assets/js/base/components/state-input/state-input.tsx +++ b/assets/js/base/components/state-input/state-input.tsx @@ -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 } /> From 6ef99f3f07fe54c47fbd6d14ea3689af22fb193e Mon Sep 17 00:00:00 2001 From: Thomas Roberts Date: Wed, 8 Mar 2023 11:44:01 -0800 Subject: [PATCH 27/53] Add fullShippingAddressPushed action to wc/store/cart --- assets/js/data/cart/action-types.ts | 1 + assets/js/data/cart/actions.ts | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/assets/js/data/cart/action-types.ts b/assets/js/data/cart/action-types.ts index ce5ffa64032..6ee20f94551 100644 --- a/assets/js/data/cart/action-types.ts +++ b/assets/js/data/cart/action-types.ts @@ -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', diff --git a/assets/js/data/cart/actions.ts b/assets/js/data/cart/actions.ts index c7cc97261a1..758e6c46ca6 100644 --- a/assets/js/data/cart/actions.ts +++ b/assets/js/data/cart/actions.ts @@ -474,6 +474,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 +501,7 @@ type Actions = | typeof setShippingAddress | typeof shippingRatesBeingSelected | typeof updateCustomerData + | typeof setFullShippingAddressPushed | typeof updatingCustomerData; export type CartAction = ReturnOrGeneratorYieldUnion< Actions | Thunks >; From 7dc347b27d6f5397963c70689c110b105bf6b344 Mon Sep 17 00:00:00 2001 From: Thomas Roberts Date: Wed, 8 Mar 2023 11:44:23 -0800 Subject: [PATCH 28/53] Add fullShippingAddressPushed case to reducer --- assets/js/data/cart/reducers.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/assets/js/data/cart/reducers.ts b/assets/js/data/cart/reducers.ts index 67a9dcfe471..3ab809ec8c6 100644 --- a/assets/js/data/cart/reducers.ts +++ b/assets/js/data/cart/reducers.ts @@ -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 = { From cd18c239ac23ccb64056484c315a3bf827120456 Mon Sep 17 00:00:00 2001 From: Thomas Roberts Date: Wed, 8 Mar 2023 11:44:44 -0800 Subject: [PATCH 29/53] Ensure fullShippingAddressPushed is set when initialising cart store --- assets/js/data/cart/resolvers.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/assets/js/data/cart/resolvers.ts b/assets/js/data/cart/resolvers.ts index 71b4c386d18..080b021d0db 100644 --- a/assets/js/data/cart/resolvers.ts +++ b/assets/js/data/cart/resolvers.ts @@ -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 ); }; From 49ddfabe5e02d13c40df4ce75c627caf5a275edd Mon Sep 17 00:00:00 2001 From: Thomas Roberts Date: Wed, 8 Mar 2023 11:45:12 -0800 Subject: [PATCH 30/53] Add fullShippingAddressPushed selector and default state entry --- assets/js/data/cart/default-state.ts | 1 + assets/js/data/cart/selectors.ts | 7 +++++++ assets/js/types/type-defs/cart.ts | 2 ++ 3 files changed, 10 insertions(+) diff --git a/assets/js/data/cart/default-state.ts b/assets/js/data/cart/default-state.ts index f7f1d65fdd2..f0411989fb8 100644 --- a/assets/js/data/cart/default-state.ts +++ b/assets/js/data/cart/default-state.ts @@ -98,6 +98,7 @@ export const defaultCartState: CartState = { applyingCoupon: '', removingCoupon: '', isCartDataStale: false, + fullShippingAddressPushed: false, }, errors: EMPTY_CART_ERRORS, }; diff --git a/assets/js/data/cart/selectors.ts b/assets/js/data/cart/selectors.ts index f4644a4b2d2..59041eb804e 100644 --- a/assets/js/data/cart/selectors.ts +++ b/assets/js/data/cart/selectors.ts @@ -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; +}; diff --git a/assets/js/types/type-defs/cart.ts b/assets/js/types/type-defs/cart.ts index 3765f2698c6..7fd2761fbf5 100644 --- a/assets/js/types/type-defs/cart.ts +++ b/assets/js/types/type-defs/cart.ts @@ -209,6 +209,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 >; From 4ea4107578ec9cc0027adf4d79ba39437258a93a Mon Sep 17 00:00:00 2001 From: Thomas Roberts Date: Wed, 8 Mar 2023 11:45:41 -0800 Subject: [PATCH 31/53] Add shippingAddressHasValidationErrors util function --- assets/js/data/cart/utils.ts | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/assets/js/data/cart/utils.ts b/assets/js/data/cart/utils.ts index 1130198a750..0d4ad059489 100644 --- a/assets/js/data/cart/utils.ts +++ b/assets/js/data/cart/utils.ts @@ -3,9 +3,35 @@ */ import { camelCase, mapKeys } from 'lodash'; import { Cart, CartResponse } from '@woocommerce/types'; +import { select } from '@wordpress/data'; + +/** + * Internal dependencies + */ +import { VALIDATION_STORE_KEY } from '../validation'; 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' ); + return [ + stateValidationErrors, + address1ValidationErrors, + countryValidationErrors, + postcodeValidationErrors, + ].some( ( entry ) => typeof entry !== 'undefined' ); +}; From 1234fa82d5c3b8f3a3f59438b1a7062556474e7c Mon Sep 17 00:00:00 2001 From: Thomas Roberts Date: Wed, 8 Mar 2023 11:46:22 -0800 Subject: [PATCH 32/53] Do not overwrite addresses when selecting a rate --- assets/js/data/cart/actions.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/assets/js/data/cart/actions.ts b/assets/js/data/cart/actions.ts index 758e6c46ca6..393680c7cbf 100644 --- a/assets/js/data/cart/actions.ts +++ b/assets/js/data/cart/actions.ts @@ -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 ); From d12d1b2a9f9baf95301c1a753ac5a3a821d78b10 Mon Sep 17 00:00:00 2001 From: Thomas Roberts Date: Wed, 8 Mar 2023 11:47:15 -0800 Subject: [PATCH 33/53] Set whether full address has been pushed when saving address changes --- assets/js/data/cart/push-changes.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/assets/js/data/cart/push-changes.ts b/assets/js/data/cart/push-changes.ts index 8410a3a0a70..2fd720de047 100644 --- a/assets/js/data/cart/push-changes.ts +++ b/assets/js/data/cart/push-changes.ts @@ -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 ); From 5683e802f79a2a18b2b15b34b2ce6e4b869a5be4 Mon Sep 17 00:00:00 2001 From: Thomas Roberts Date: Wed, 8 Mar 2023 11:49:19 -0800 Subject: [PATCH 34/53] In Shipping Methods Block, show correct component based on block setting --- .../checkout-shipping-methods-block/block.tsx | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/assets/js/blocks/checkout/inner-blocks/checkout-shipping-methods-block/block.tsx b/assets/js/blocks/checkout/inner-blocks/checkout-shipping-methods-block/block.tsx index f777ecd4220..6589bd1bc40 100644 --- a/assets/js/blocks/checkout/inner-blocks/checkout-shipping-methods-block/block.tsx +++ b/assets/js/blocks/checkout/inner-blocks/checkout-shipping-methods-block/block.tsx @@ -2,15 +2,11 @@ * External dependencies */ import { __ } from '@wordpress/i18n'; -import { - useCustomerData, - useShippingData, -} from '@woocommerce/base-context/hooks'; +import { useShippingData } from '@woocommerce/base-context/hooks'; import { ShippingRatesControl } from '@woocommerce/base-components/cart-checkout'; import { getShippingRatesPackageCount, hasCollectableRate, - isAddressComplete, } from '@woocommerce/base-utils'; import { getCurrencyFromPriceResponse } from '@woocommerce/price-format'; import FormattedMonetaryAmount from '@woocommerce/base-components/formatted-monetary-amount'; @@ -25,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. @@ -70,7 +69,9 @@ const Block = ( { isCollectable, } = useShippingData(); - const { shippingAddress } = useCustomerData(); + const shippingAddressPushed = useSelect( ( select ) => { + return select( CART_STORE_KEY ).getFullShippingAddressPushed(); + } ); const filteredShippingRates = isCollectable ? shippingRates.map( ( shippingRatesPackage ) => { @@ -90,14 +91,15 @@ const Block = ( { return null; } + const shippingAddressIsComplete = ! shippingAddressHasValidationErrors(); + const shippingRatesPackageCount = getShippingRatesPackageCount( shippingRates ); - const addressComplete = isAddressComplete( shippingAddress ); - if ( ( ! hasCalculatedShipping && ! shippingRatesPackageCount ) || - ( shippingCostRequiresAddress && ! addressComplete ) + ( shippingCostRequiresAddress && ! shippingAddressPushed ) || + ! shippingAddressIsComplete ) { return (

From a9ac41a20c76a508acdf0f65bec9f5a69b1413a8 Mon Sep 17 00:00:00 2001 From: Thomas Roberts Date: Wed, 8 Mar 2023 11:50:44 -0800 Subject: [PATCH 35/53] Don't show from price if rates should be hidden until address entered --- .../checkout-shipping-method-block/block.tsx | 10 +++++++++- .../checkout-shipping-method-block/frontend.tsx | 3 +++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/assets/js/blocks/checkout/inner-blocks/checkout-shipping-method-block/block.tsx b/assets/js/blocks/checkout/inner-blocks/checkout-shipping-method-block/block.tsx index e59b5e91725..d6bbbfac0de 100644 --- a/assets/js/blocks/checkout/inner-blocks/checkout-shipping-method-block/block.tsx +++ b/assets/js/blocks/checkout/inner-blocks/checkout-shipping-method-block/block.tsx @@ -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 ? ( { __( '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 } /> { children } From 953642f70117b47d3b419c3adec50d9b5381194e Mon Sep 17 00:00:00 2001 From: Thomas Roberts Date: Wed, 8 Mar 2023 11:54:11 -0800 Subject: [PATCH 36/53] Check city validation errors to assert if shipping address is valid --- assets/js/data/cart/utils.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/assets/js/data/cart/utils.ts b/assets/js/data/cart/utils.ts index 0d4ad059489..27b1c270333 100644 --- a/assets/js/data/cart/utils.ts +++ b/assets/js/data/cart/utils.ts @@ -28,7 +28,10 @@ export const shippingAddressHasValidationErrors = () => { validationStore.getValidationError( 'shipping_country' ); const postcodeValidationErrors = validationStore.getValidationError( 'shipping_postcode' ); + const cityValidationErrors = + validationStore.getValidationError( 'shipping_city' ); return [ + cityValidationErrors, stateValidationErrors, address1ValidationErrors, countryValidationErrors, From 34bd9797a5add35422059b37f92aaa2974dd46f7 Mon Sep 17 00:00:00 2001 From: Thomas Roberts Date: Wed, 8 Mar 2023 13:05:32 -0800 Subject: [PATCH 37/53] Rename merchant.js to merchant.ts --- tests/utils/{merchant.js => merchant.ts} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/utils/{merchant.js => merchant.ts} (100%) diff --git a/tests/utils/merchant.js b/tests/utils/merchant.ts similarity index 100% rename from tests/utils/merchant.js rename to tests/utils/merchant.ts From 8bb67dc7efb05b5e082e25248f819bf379655f6c Mon Sep 17 00:00:00 2001 From: Thomas Roberts Date: Wed, 8 Mar 2023 13:05:55 -0800 Subject: [PATCH 38/53] Move local pickup functions to common merchant util --- tests/utils/merchant.ts | 56 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/tests/utils/merchant.ts b/tests/utils/merchant.ts index 21b86cfaeda..c247215fca8 100644 --- a/tests/utils/merchant.ts +++ b/tests/utils/merchant.ts @@ -3,6 +3,7 @@ */ 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, @@ -14,4 +15,59 @@ export const merchant = { visible: true, } ); }, + goToLocalPickupSettingsPage: async () => { + await visitAdminPage( + 'admin.php', + 'page=wc-settings&tab=shipping§ion=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 () => { + 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' + ); + }, + disableLocalPickup: 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(); + } + }, + 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(); + } + }, }; From 8d7b1cefd8d5ff2f90741364aa9c9f70e53e8dc8 Mon Sep 17 00:00:00 2001 From: Thomas Roberts Date: Wed, 8 Mar 2023 13:06:35 -0800 Subject: [PATCH 39/53] Update local pickup tests to use common merchant utils --- tests/e2e/specs/merchant/local-pickup.test.ts | 69 +++++-------------- 1 file changed, 16 insertions(+), 53 deletions(-) diff --git a/tests/e2e/specs/merchant/local-pickup.test.ts b/tests/e2e/specs/merchant/local-pickup.test.ts index 196273cf6b5..cc845c53789 100644 --- a/tests/e2e/specs/merchant/local-pickup.test.ts +++ b/tests/e2e/specs/merchant/local-pickup.test.ts @@ -6,51 +6,14 @@ 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§ion=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 () => { @@ -113,23 +76,23 @@ const setCartCheckoutPages = async ( { 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 () => { @@ -195,7 +158,7 @@ describe( `Local Pickup Settings`, () => { 'Enable local pickup' ); await toggleLabel.click(); - await saveSettingsPageWithRefresh(); + await merchant.saveLocalPickupSettingsPageWithRefresh(); expect( await page.$eval( @@ -211,7 +174,7 @@ describe( `Local Pickup Settings`, () => { 'Local Pickup Test' ); - await saveSettingsPageWithRefresh(); + await merchant.saveLocalPickupSettingsPageWithRefresh(); expect( await page.$eval( @@ -251,7 +214,7 @@ describe( `Local Pickup Settings`, () => { 'none' ); - await saveSettingsPageWithRefresh(); + await merchant.saveLocalPickupSettingsPageWithRefresh(); const refreshChecked = await page.$eval( '#inspector-checkbox-control-1', @@ -311,7 +274,7 @@ describe( `Local Pickup Settings`, () => { text: 'Done', } ); - await saveSettingsPageWithRefresh(); + await merchant.saveLocalPickupSettingsPageWithRefresh(); await expect( page ).toMatchElement( '.pickup-locations tbody tr td', @@ -340,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', { From 4da5fc6908f12e3f8d1583684e450784e6f4c208 Mon Sep 17 00:00:00 2001 From: Thomas Roberts Date: Wed, 8 Mar 2023 13:10:54 -0800 Subject: [PATCH 40/53] Add test to ensure setting toggles in both blocks --- tests/e2e/specs/backend/checkout.test.js | 53 ++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/tests/e2e/specs/backend/checkout.test.js b/tests/e2e/specs/backend/checkout.test.js index 5723cdf28b6..cf661d8e429 100644 --- a/tests/e2e/specs/backend/checkout.test.js +++ b/tests/e2e/specs/backend/checkout.test.js @@ -22,6 +22,7 @@ import { openWidgetEditor, closeModalIfExists, } from '../../utils.js'; +import { merchant as merchantUtils } from '../../../utils/merchant'; const block = { name: 'Checkout', @@ -85,6 +86,58 @@ describe( `${ block.name } Block`, () => { ); } ); + 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' From afbf80aa7f5a7f58156468b623e509345958f2e4 Mon Sep 17 00:00:00 2001 From: Thomas Roberts Date: Wed, 8 Mar 2023 15:50:13 -0800 Subject: [PATCH 41/53] Add navigating to settings and saving in merchant util --- tests/utils/merchant.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/utils/merchant.ts b/tests/utils/merchant.ts index c247215fca8..25e39967037 100644 --- a/tests/utils/merchant.ts +++ b/tests/utils/merchant.ts @@ -34,6 +34,7 @@ export const merchant = { await merchant.goToLocalPickupSettingsPage(); }, enableLocalPickup: async () => { + await merchant.goToLocalPickupSettingsPage(); const enabledLabel = await findLabelWithText( 'Enable local pickup' ); const enabledChecked = await page.$eval( '#inspector-checkbox-control-1', @@ -47,16 +48,19 @@ export const merchant = { '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 ) { + if ( enabledChecked ) { await enabledLabel.click(); } + await merchant.saveLocalPickupSettingsPageWithRefresh(); }, removeCostForLocalPickup: async () => { const costLabel = await findLabelWithText( From ae6b6c47100e41c3eb73ef622ae67b611b3ea043 Mon Sep 17 00:00:00 2001 From: Thomas Roberts Date: Wed, 8 Mar 2023 15:50:27 -0800 Subject: [PATCH 42/53] Create addPickupLocation merchant util --- tests/utils/merchant.ts | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/tests/utils/merchant.ts b/tests/utils/merchant.ts index 25e39967037..344170a69a4 100644 --- a/tests/utils/merchant.ts +++ b/tests/utils/merchant.ts @@ -74,4 +74,37 @@ export const merchant = { 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(); + }, }; From 8e94d5af04bef3d7b1a4c75921f2feb4448e40c1 Mon Sep 17 00:00:00 2001 From: Thomas Roberts Date: Wed, 8 Mar 2023 15:51:33 -0800 Subject: [PATCH 43/53] Add test for local pickup and require full address --- .../shopper/cart-checkout/checkout.test.js | 78 ++++++++++++++++++- 1 file changed, 77 insertions(+), 1 deletion(-) diff --git a/tests/e2e/specs/shopper/cart-checkout/checkout.test.js b/tests/e2e/specs/shopper/cart-checkout/checkout.test.js index 7ea0484b3c8..a6b439ffb4b 100644 --- a/tests/e2e/specs/shopper/cart-checkout/checkout.test.js +++ b/tests/e2e/specs/shopper/cart-checkout/checkout.test.js @@ -28,7 +28,7 @@ import { SIMPLE_PHYSICAL_PRODUCT_NAME, SIMPLE_VIRTUAL_PRODUCT_NAME, } from '../../../../utils'; - +import { merchant as merchantUtils } from '../../../../utils/merchant'; import { createCoupon } from '../../../utils'; let coupon; @@ -304,6 +304,82 @@ describe( 'Shopper → Checkout', () => { '.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', () => { From b7fd27558eaa1074c93aed6218fcf79b0df22a10 Mon Sep 17 00:00:00 2001 From: Thomas Roberts Date: Wed, 8 Mar 2023 17:48:20 -0800 Subject: [PATCH 44/53] Make sure correct conditions are met to show shipping options --- .../inner-blocks/checkout-shipping-methods-block/block.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/assets/js/blocks/checkout/inner-blocks/checkout-shipping-methods-block/block.tsx b/assets/js/blocks/checkout/inner-blocks/checkout-shipping-methods-block/block.tsx index 6589bd1bc40..89e67fb766e 100644 --- a/assets/js/blocks/checkout/inner-blocks/checkout-shipping-methods-block/block.tsx +++ b/assets/js/blocks/checkout/inner-blocks/checkout-shipping-methods-block/block.tsx @@ -98,8 +98,8 @@ const Block = ( { if ( ( ! hasCalculatedShipping && ! shippingRatesPackageCount ) || - ( shippingCostRequiresAddress && ! shippingAddressPushed ) || - ! shippingAddressIsComplete + ( shippingCostRequiresAddress && + ( ! shippingAddressPushed || ! shippingAddressIsComplete ) ) ) { return (

From 7c498e86c3612e79ab34d4cfeecc3ebd39db5d12 Mon Sep 17 00:00:00 2001 From: Thomas Roberts Date: Wed, 8 Mar 2023 18:03:57 -0800 Subject: [PATCH 45/53] Clean up updateAttributeInSiblingBlock --- assets/js/utils/attributes.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/js/utils/attributes.ts b/assets/js/utils/attributes.ts index d8596a9fd53..ec0b871ff0d 100644 --- a/assets/js/utils/attributes.ts +++ b/assets/js/utils/attributes.ts @@ -99,6 +99,7 @@ export const updateAttributeInSiblingBlock = ( 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 ) => { @@ -108,7 +109,6 @@ export const updateAttributeInSiblingBlock = ( if ( ! childBlock ) { return; } - //console.log( childBlock ); shippingMethodsBlockClientId = childBlock.clientId; } ); actions.updateBlockAttributes( shippingMethodsBlockClientId, { From 496fe1f0e7b81504780aa5fa97be2bb64d236bee Mon Sep 17 00:00:00 2001 From: Thomas Roberts Date: Wed, 8 Mar 2023 18:03:57 -0800 Subject: [PATCH 46/53] Clean up updateAttributeInSiblingBlock --- assets/js/utils/attributes.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/js/utils/attributes.ts b/assets/js/utils/attributes.ts index d8596a9fd53..ec0b871ff0d 100644 --- a/assets/js/utils/attributes.ts +++ b/assets/js/utils/attributes.ts @@ -99,6 +99,7 @@ export const updateAttributeInSiblingBlock = ( 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 ) => { @@ -108,7 +109,6 @@ export const updateAttributeInSiblingBlock = ( if ( ! childBlock ) { return; } - //console.log( childBlock ); shippingMethodsBlockClientId = childBlock.clientId; } ); actions.updateBlockAttributes( shippingMethodsBlockClientId, { From 37916716006851dc3999637038f1581ee13fc9d1 Mon Sep 17 00:00:00 2001 From: Thomas Roberts Date: Wed, 8 Mar 2023 18:26:24 -0800 Subject: [PATCH 47/53] Ensure checkbox is checked during local pickup tests --- .../shopper/cart-checkout/checkout.test.js | 31 ++++++++++++++++--- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/tests/e2e/specs/shopper/cart-checkout/checkout.test.js b/tests/e2e/specs/shopper/cart-checkout/checkout.test.js index a6b439ffb4b..98fc809aad6 100644 --- a/tests/e2e/specs/shopper/cart-checkout/checkout.test.js +++ b/tests/e2e/specs/shopper/cart-checkout/checkout.test.js @@ -249,11 +249,33 @@ describe( 'Shopper → Checkout', () => { 'woocommerce/checkout-shipping-methods-block' ); - await setCheckbox( - await getToggleIdByLabel( - 'Hide shipping costs until an address is entered' - ) + 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 ); + expect( shippingMethodSettingIsChecked ).toBe( true ); await saveOrPublish(); await shopper.block.emptyCart(); // Log out to have a fresh empty cart. @@ -261,7 +283,6 @@ describe( 'Shopper → Checkout', () => { 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.")]' From 5a54cccc77d48746340fc85772e083c4c686e453 Mon Sep 17 00:00:00 2001 From: Thomas Roberts Date: Wed, 8 Mar 2023 19:19:34 -0800 Subject: [PATCH 48/53] Unset the checkbox when tests are finished running --- .../shopper/cart-checkout/checkout.test.js | 41 ++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/tests/e2e/specs/shopper/cart-checkout/checkout.test.js b/tests/e2e/specs/shopper/cart-checkout/checkout.test.js index 98fc809aad6..f1854b25137 100644 --- a/tests/e2e/specs/shopper/cart-checkout/checkout.test.js +++ b/tests/e2e/specs/shopper/cart-checkout/checkout.test.js @@ -209,6 +209,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 ); @@ -275,7 +313,7 @@ describe( 'Shopper → Checkout', () => { .checked, shippingMethodForValue ); - expect( shippingMethodSettingIsChecked ).toBe( true ); + await expect( shippingMethodSettingIsChecked ).toBe( true ); await saveOrPublish(); await shopper.block.emptyCart(); // Log out to have a fresh empty cart. @@ -406,6 +444,7 @@ describe( 'Shopper → Checkout', () => { describe( 'Coupons', () => { beforeAll( async () => { coupon = await createCoupon( { usageLimit: 1 } ); + await shopper.logout(); await shopper.login(); } ); From 37b03f4b402dcb63893d4fca767ca0baa6b381b7 Mon Sep 17 00:00:00 2001 From: Thomas Roberts Date: Thu, 9 Mar 2023 13:07:16 -0800 Subject: [PATCH 49/53] Update checkout block fixture --- tests/e2e/specs/backend/__fixtures__/checkout.fixture.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/e2e/specs/backend/__fixtures__/checkout.fixture.json b/tests/e2e/specs/backend/__fixtures__/checkout.fixture.json index b0be2edc810..2d9631d676c 100644 --- a/tests/e2e/specs/backend/__fixtures__/checkout.fixture.json +++ b/tests/e2e/specs/backend/__fixtures__/checkout.fixture.json @@ -1 +1 @@ -{"title":"Checkout Block","pageContent":"\n

\n
\n
\n\n\n\n
\n\n\n\n
\n\n\n\n
\n\n\n\n
\n\n\n\n
\n\n\n\n
\n\n\n\n
\n\n\n\n
\n
\n\n\n\n
\n
\n
\n
\n"} +{"title":"Checkout Block","pageContent":"\n
\n
\n
\n\n\n\n
\n\n\n\n
\n\n\n\n
\n\n\n\n
\n\n\n\n
\n\n\n\n
\n\n\n\n
\n\n\n\n
\n
\n\n\n\n
\n
\n
\n
\n"} From e3fd3d05ec39b8a22e1beb11a17b782dde39abe2 Mon Sep 17 00:00:00 2001 From: Thomas Roberts Date: Thu, 9 Mar 2023 13:26:35 -0800 Subject: [PATCH 50/53] Prevent error in unit tests --- assets/js/data/cart/test/push-changes.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/assets/js/data/cart/test/push-changes.ts b/assets/js/data/cart/test/push-changes.ts index fd4ee9b827f..8bf726c2aa5 100644 --- a/assets/js/data/cart/test/push-changes.ts +++ b/assets/js/data/cart/test/push-changes.ts @@ -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', () => { From f3ffa8c533e745c540c24923fd45216fb7cdea89 Mon Sep 17 00:00:00 2001 From: Thomas Roberts Date: Thu, 9 Mar 2023 14:45:55 -0800 Subject: [PATCH 51/53] Import validation store key from constants Required because importing from the index causes the validation data store to register twice --- assets/js/data/cart/utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/js/data/cart/utils.ts b/assets/js/data/cart/utils.ts index 27b1c270333..19afead5670 100644 --- a/assets/js/data/cart/utils.ts +++ b/assets/js/data/cart/utils.ts @@ -8,7 +8,7 @@ import { select } from '@wordpress/data'; /** * Internal dependencies */ -import { VALIDATION_STORE_KEY } from '../validation'; +import { STORE_KEY as VALIDATION_STORE_KEY } from '../validation/constants'; export const mapCartResponseToCart = ( responseCart: CartResponse ): Cart => { return mapKeys( responseCart, ( _, key ) => From c102fd4b7df727ab30cdc64c6a7b71aebb4a5749 Mon Sep 17 00:00:00 2001 From: Thomas Roberts Date: Thu, 9 Mar 2023 14:46:09 -0800 Subject: [PATCH 52/53] Update checkout terms test to wait for button not to be disabled --- tests/e2e/specs/merchant/checkout-terms.test.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/e2e/specs/merchant/checkout-terms.test.js b/tests/e2e/specs/merchant/checkout-terms.test.js index c27733f85cc..70824729214 100644 --- a/tests/e2e/specs/merchant/checkout-terms.test.js +++ b/tests/e2e/specs/merchant/checkout-terms.test.js @@ -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' ); From 484e56ba5135369cc388eff7faaee92f9548aedd Mon Sep 17 00:00:00 2001 From: Thomas Roberts Date: Tue, 7 Mar 2023 12:33:00 -0800 Subject: [PATCH 53/53] Revert "Add isAddressComplete function" This reverts commit 9967dc0d4f10cf638859ae085e6f4cc2901dd299. --- assets/js/base/utils/address.ts | 9 --------- 1 file changed, 9 deletions(-) diff --git a/assets/js/base/utils/address.ts b/assets/js/base/utils/address.ts index 0ee4df1cea9..e3738fb614b 100644 --- a/assets/js/base/utils/address.ts +++ b/assets/js/base/utils/address.ts @@ -100,12 +100,3 @@ export const emptyHiddenAddressFields = < return newAddress; }; - -/** - * Returns true if the address has a city and country. - */ -export const isAddressComplete = ( - address: ShippingAddress | BillingAddress -): boolean => { - return !! address.city && !! address.country; -};