Commit d2e7a559e16 for woocommerce
commit d2e7a559e16155fe80a27f0dc4af83d357a0070b
Author: Taha Paksu <3295+tpaksu@users.noreply.github.com>
Date: Mon Oct 12 13:40:47 2026 +0300
[tests] Add JS test for the checkout shipping-method block (#69646)
diff --git a/plugins/woocommerce/changelog/69646-wooplug-7841-shipping-tests-block b/plugins/woocommerce/changelog/69646-wooplug-7841-shipping-tests-block
new file mode 100644
index 00000000000..5e35bc49447
--- /dev/null
+++ b/plugins/woocommerce/changelog/69646-wooplug-7841-shipping-tests-block
@@ -0,0 +1,5 @@
+Significance: patch
+Type: dev
+Comment: Add a JS test for the checkout shipping-method block; no production change.
+
+
diff --git a/plugins/woocommerce/client/blocks/assets/js/blocks/checkout/inner-blocks/checkout-shipping-method-block/test/block.tsx b/plugins/woocommerce/client/blocks/assets/js/blocks/checkout/inner-blocks/checkout-shipping-method-block/test/block.tsx
index 1b7acf1c1e4..704069a6294 100644
--- a/plugins/woocommerce/client/blocks/assets/js/blocks/checkout/inner-blocks/checkout-shipping-method-block/test/block.tsx
+++ b/plugins/woocommerce/client/blocks/assets/js/blocks/checkout/inner-blocks/checkout-shipping-method-block/test/block.tsx
@@ -1,36 +1,181 @@
/**
* External dependencies
*/
-import { render } from '@testing-library/react';
+import { render, screen } from '@testing-library/react';
+import { dispatch } from '@wordpress/data';
+import { useShippingData } from '@woocommerce/base-context/hooks';
/**
* Internal dependencies
*/
import Block from '../block';
+import {
+ generateShippingPackage,
+ generateShippingRate,
+} from '../../../../../mocks/shipping-package';
+
+// Read inside the component rather than at module load, so each test can set it.
+let mockShippingCostRequiresAddress = false;
+
jest.mock( '@woocommerce/settings', () => {
+ const actualModule = jest.requireActual( '@woocommerce/settings' );
return {
- ...jest.requireActual( '@woocommerce/settings' ),
- getSetting: jest.fn().mockImplementation( ( key, defaultValue ) => {
- if ( key === 'localPickupText' ) {
+ ...actualModule,
+ getSetting: ( name: string, ...rest: unknown[] ) => {
+ if ( name === 'localPickupText' ) {
return 'Pickup text from settings';
}
- return defaultValue;
- } ),
+ if ( name === 'shippingCostRequiresAddress' ) {
+ return mockShippingCostRequiresAddress;
+ }
+ // Read when the module loads, so a pickup_location rate is seen as collectable
+ // and local pickup counts as on.
+ if ( name === 'collectableMethodIds' ) {
+ return [ 'pickup_location' ];
+ }
+ if ( name === 'localPickupEnabled' ) {
+ return true;
+ }
+ return actualModule.getSetting( name, ...rest );
+ },
};
} );
+
+jest.mock( '@woocommerce/base-context/hooks', () => ( {
+ ...jest.requireActual( '@woocommerce/base-context/hooks' ),
+ useShippingData: jest.fn(),
+} ) );
+
+// The component selects only from the cart store, to decide whether anything is still
+// deliverable; answer that one and let any other store resolve for real. The validation store
+// the tests drive is read inside cart/utils.ts rather than here, so it works because the mock
+// spreads the real module, not because of this fall-through.
+let mockPackages: ReturnType< typeof generateShippingPackage >[] = [];
+
+jest.mock( '@wordpress/data', () => {
+ const actualModule = jest.requireActual( '@wordpress/data' );
+ return {
+ ...actualModule,
+ useSelect: (
+ mapSelect: ( select: typeof actualModule.select ) => unknown
+ ) =>
+ mapSelect( ( storeName: { name?: string } | string ) => {
+ const name =
+ typeof storeName === 'string' ? storeName : storeName?.name;
+ if ( name === 'wc/store/cart' ) {
+ return { getShippingRates: () => mockPackages };
+ }
+ return actualModule.select( storeName );
+ } ),
+ };
+} );
+
+const VALIDATION_STORE = 'wc/store/validation';
+
+const deliveryRate = generateShippingRate( {
+ rateId: 'flat_rate:1',
+ name: 'Flat rate',
+ methodID: 'flat_rate',
+ price: '500',
+ instanceID: 1,
+} );
+
+const collectionRate = generateShippingRate( {
+ rateId: 'pickup_location:1',
+ name: 'Pickup',
+ methodID: 'pickup_location',
+ price: '0',
+ instanceID: 0,
+} );
+
+/**
+ * Put the store in one shape and render the two options.
+ *
+ * @param options The shopper's situation.
+ * @param options.costRequiresAddress Whether the merchant hid costs until an address is entered.
+ * @param options.addressIncomplete Whether the shipping address still has a validation error.
+ * @param options.offers The rates the one package comes back with.
+ * @param options.showPrice Whether the block is set to show prices.
+ */
+const renderWith = ( {
+ costRequiresAddress = false,
+ addressIncomplete = false,
+ offers,
+ showPrice = true,
+}: {
+ costRequiresAddress?: boolean;
+ addressIncomplete?: boolean;
+ offers: ReturnType< typeof generateShippingRate >[];
+ showPrice?: boolean;
+} ) => {
+ mockShippingCostRequiresAddress = costRequiresAddress;
+ mockPackages = [
+ generateShippingPackage( { packageId: 0, shippingRates: offers } ),
+ ];
+
+ dispatch( VALIDATION_STORE ).clearValidationErrors();
+ if ( addressIncomplete ) {
+ dispatch( VALIDATION_STORE ).setValidationErrors( {
+ shipping_city: { message: 'Enter a city', hidden: true },
+ } );
+ }
+
+ ( useShippingData as jest.Mock ).mockReturnValue( {
+ shippingRates: mockPackages,
+ } );
+
+ return render(
+ <Block
+ localPickupText="backup local pickup text"
+ onChange={ () => void 0 }
+ showIcon={ false }
+ showPrice={ showPrice }
+ checked={ 'shipping' }
+ shippingText="backup shipping text"
+ />
+ );
+};
+
+afterEach( () => {
+ mockShippingCostRequiresAddress = false;
+ dispatch( VALIDATION_STORE ).clearValidationErrors();
+} );
+
describe( 'Block', () => {
- it( 'Renders the local pickup text from options', async () => {
- const { getByText } = render(
- <Block
- localPickupText="backup local pickup text"
- onChange={ () => void 0 }
- showIcon={ false }
- showPrice={ false }
- checked={ 'shipping' }
- shippingText="backup shipping text"
- />
- );
-
- expect( getByText( 'Pickup text from settings' ) ).toBeInTheDocument();
+ it( 'renders the local pickup text from options', () => {
+ renderWith( {
+ offers: [ deliveryRate, collectionRate ],
+ showPrice: false,
+ } );
+
+ expect(
+ screen.getByText( 'Pickup text from settings' )
+ ).toBeInTheDocument();
+ } );
+
+ it( 'tells the shopper the delivery price needs an address when nothing can be delivered', () => {
+ renderWith( { offers: [ collectionRate ] } );
+
+ expect(
+ screen.getByText( 'calculated with an address' )
+ ).toBeInTheDocument();
+ } );
+
+ it( 'still shows the delivery price when a rate exists, even with costs hidden and the address incomplete', () => {
+ renderWith( {
+ costRequiresAddress: true,
+ addressIncomplete: true,
+ offers: [ deliveryRate, collectionRate ],
+ } );
+
+ expect(
+ screen.getByRole( 'radio', {
+ name: ( name: string ) =>
+ name.startsWith( 'backup shipping text' ),
+ } )
+ ).toHaveTextContent( '$5.00' );
+ expect(
+ screen.queryByText( 'calculated with an address' )
+ ).not.toBeInTheDocument();
} );
} );