Commit 67771198dfc for woocommerce
commit 67771198dfccfb433a6fbbb45fe6cefa623fe3fe
Author: Vlad Olaru <vlad.olaru@automattic.com>
Date: Thu Sep 24 17:55:31 2026 +0300
Revert "[tests] Reduce additional checkout field E2E tests from 12 to 7" (#69049)
revert: Revert "[tests] Reduce additional checkout field E2E tests from 12 to 7" (#68588)
This reverts commit 95e0a58cefcbd16da79faa41ccea9c6ea1407e53.
Rubik asked for the E2E migration's test changes in its areas to be
reverted until the team can review them. #68588 is one of the 11 PRs on
the revert list Rubik agreed on 2026-09-24.
The revert brings back the browser tests the PR removed or cut down and
removes the lower-layer tests it added in their place. Only test code
and changelog entries change; nothing ships.
Refs TESTOPS-234
Refs #68588
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
diff --git a/plugins/woocommerce/changelog/testops-234-checkout-block-fields b/plugins/woocommerce/changelog/testops-234-checkout-block-fields
deleted file mode 100644
index 4352a4c5cd1..00000000000
--- a/plugins/woocommerce/changelog/testops-234-checkout-block-fields
+++ /dev/null
@@ -1,4 +0,0 @@
-Significance: patch
-Type: dev
-Comment: Reduce additional checkout field E2E tests from 12 to 7; a new CheckoutFieldsAdminTest owns the order admin field formatting and update contract.
-
diff --git a/plugins/woocommerce/client/blocks/packages/public-api/blocks-components/checkbox-control/test/validated-checkbox-control.tsx b/plugins/woocommerce/client/blocks/packages/public-api/blocks-components/checkbox-control/test/validated-checkbox-control.tsx
deleted file mode 100644
index 8206124aaa6..00000000000
--- a/plugins/woocommerce/client/blocks/packages/public-api/blocks-components/checkbox-control/test/validated-checkbox-control.tsx
+++ /dev/null
@@ -1,93 +0,0 @@
-/**
- * External dependencies
- */
-import { act, fireEvent, render, screen } from '@testing-library/react';
-import { validationStore } from '@woocommerce/block-data';
-import { dispatch } from '@wordpress/data';
-import { useState } from '@wordpress/element';
-
-/**
- * Internal dependencies
- */
-import ValidatedCheckboxControl from '../validated-checkbox-control';
-
-describe( 'ValidatedCheckboxControl', () => {
- const label = 'Test required checkbox';
- const errorMessage = 'Please check the box or you will be unable to order';
-
- const renderUnchecked = ( props = {} ) =>
- render(
- <ValidatedCheckboxControl
- id="required-checkbox"
- label={ label }
- required
- checked={ false }
- onChange={ () => void 0 }
- { ...props }
- />
- );
-
- it( "Shows the field's own error message once errors are revealed", async () => {
- renderUnchecked( { errorMessage } );
-
- // Validation runs on mount but starts hidden, which is what a shopper sees
- // before they try to place the order.
- expect( screen.queryByText( errorMessage ) ).not.toBeInTheDocument();
-
- await act( async () => {
- dispatch( validationStore ).showAllValidationErrors();
- } );
-
- expect( screen.getByText( errorMessage ) ).toBeInTheDocument();
- } );
-
- it( 'Clears the error once the shopper checks the box', async () => {
- // The two cases above render with checked fixed at false, so they only cover
- // the reveal. The clear runs through a different path entirely --
- // onChange -> validateInput( false ) -> clearValidationError -- and a
- // regression there leaves a blocking error on screen after the shopper has
- // already fixed the problem. Drive it through a stateful parent, the way
- // checkout does, so the checkbox actually ends up checked.
- const StatefulCheckbox = () => {
- const [ checked, setChecked ] = useState( false );
-
- return (
- <ValidatedCheckboxControl
- id="required-checkbox"
- label={ label }
- required
- checked={ checked }
- onChange={ setChecked }
- errorMessage={ errorMessage }
- />
- );
- };
-
- render( <StatefulCheckbox /> );
-
- await act( async () => {
- dispatch( validationStore ).showAllValidationErrors();
- } );
- expect( screen.getByText( errorMessage ) ).toBeInTheDocument();
-
- fireEvent.click( screen.getByRole( 'checkbox' ) );
-
- expect( screen.getByRole( 'checkbox' ) ).toBeChecked();
- expect( screen.queryByText( errorMessage ) ).not.toBeInTheDocument();
- } );
-
- it( 'Falls back to the generated message when the field supplies none', async () => {
- renderUnchecked();
-
- await act( async () => {
- dispatch( validationStore ).showAllValidationErrors();
- } );
-
- // Without this the test above would pass on any message at all, rather than
- // on the one the field asked for.
- expect( screen.queryByText( errorMessage ) ).not.toBeInTheDocument();
- expect(
- screen.getByText( 'Please check this box if you want to proceed.' )
- ).toBeInTheDocument();
- } );
-} );
diff --git a/plugins/woocommerce/tests/e2e/tests/blocks/checkout/additional-fields.guest-shopper.block_theme.spec.ts b/plugins/woocommerce/tests/e2e/tests/blocks/checkout/additional-fields.guest-shopper.block_theme.spec.ts
index c4e0e7c12f1..cf45a55174d 100644
--- a/plugins/woocommerce/tests/e2e/tests/blocks/checkout/additional-fields.guest-shopper.block_theme.spec.ts
+++ b/plugins/woocommerce/tests/e2e/tests/blocks/checkout/additional-fields.guest-shopper.block_theme.spec.ts
@@ -29,6 +29,96 @@ test.describe( 'Shopper → Additional Checkout Fields', () => {
);
} );
+ test( 'Shopper can see an error message when a required field is not filled in the checkout form', async ( {
+ checkoutPageObject,
+ frontendUtils,
+ } ) => {
+ await frontendUtils.goToShop();
+ await frontendUtils.addToCart( REGULAR_PRICED_PRODUCT_NAME );
+ await frontendUtils.goToCheckout();
+
+ await checkoutPageObject.editShippingDetails();
+ await checkoutPageObject.unsyncBillingWithShipping();
+ await checkoutPageObject.editBillingDetails();
+ await checkoutPageObject.fillInCheckoutWithTestData(
+ {},
+ {
+ contact: {
+ 'Alternative Email': 'test@test.com',
+ 'Enter a gift message to include in the package':
+ 'For my non-ascii named friend: niño',
+ },
+ address: {
+ shipping: {
+ 'Government ID': '',
+ 'Confirm government ID': '',
+ },
+ billing: {
+ 'Government ID': '54321',
+ 'Confirm government ID': '54321',
+ },
+ },
+ order: {
+ 'How did you hear about us?': 'Other',
+ 'What is your favourite colour?': 'Blue',
+ },
+ }
+ );
+
+ // Use the data store to specifically unset the field value - this is because it might be saved in the user-state.
+ await checkoutPageObject.page.evaluate( () => {
+ window.wp.data.dispatch( 'wc/store/cart' ).setShippingAddress( {
+ 'first-plugin-namespace/road-size': '',
+ } );
+ } );
+
+ await checkoutPageObject.placeOrder( false );
+
+ // Test that the required checkbox warning shows up after submitting without interacting.
+ await expect(
+ checkoutPageObject.page.getByText(
+ 'Please check the box or you will be unable to order'
+ )
+ ).toBeVisible();
+
+ await checkoutPageObject.page
+ .getByLabel( 'Test required checkbox' )
+ .click();
+
+ await expect(
+ checkoutPageObject.page.getByText(
+ 'Please check the box or you will be unable to order'
+ )
+ ).toBeHidden();
+
+ // Test that unchecking shows and checking again hides the message.
+ await checkoutPageObject.page
+ .getByLabel( 'Test required checkbox' )
+ .uncheck();
+
+ await expect(
+ checkoutPageObject.page.getByText(
+ 'Please check the box or you will be unable to order'
+ )
+ ).toBeVisible();
+
+ await checkoutPageObject.page
+ .getByLabel( 'Test required checkbox' )
+ .click();
+
+ await expect(
+ checkoutPageObject.page.getByText(
+ 'Please check the box or you will be unable to order'
+ )
+ ).toBeHidden();
+
+ await expect(
+ checkoutPageObject.page.getByText(
+ 'Please enter a valid government id'
+ )
+ ).toBeVisible();
+ } );
+
test( 'Shopper can fill in the checkout form with additional fields and can have different value for same field in shipping and billing address', async ( {
checkoutPageObject,
frontendUtils,
@@ -312,21 +402,16 @@ test.describe( 'Shopper → Additional Checkout Fields', () => {
await frontendUtils.addToCart( REGULAR_PRICED_PRODUCT_NAME );
await frontendUtils.goToCheckout();
- // The field is hidden while the cart total is at or below $40.
+ // The shipping insurance field should be hidden by default (cart total < 2000)
await expect(
checkoutPageObject.page.getByLabel( 'Add shipping insurance' )
).toBeHidden();
await frontendUtils.goToShop();
await frontendUtils.addToCart( REGULAR_PRICED_PRODUCT_NAME );
- await frontendUtils.addToCart( REGULAR_PRICED_PRODUCT_NAME );
- await frontendUtils.addToCart( REGULAR_PRICED_PRODUCT_NAME );
await frontendUtils.goToCheckout();
- // Four $20 products against the field's $59 required threshold. Three
- // would also clear it, but only by a dollar, so any sale price or
- // total-computation change would silently drop the field out of
- // "required" and fail the assertions below for an unrelated reason.
+ // The shipping insurance field should now be visible (cart total > 2000)
await expect(
checkoutPageObject.page.getByLabel( 'Add shipping insurance' )
).toBeVisible();
@@ -373,13 +458,10 @@ test.describe( 'Shopper → Additional Checkout Fields', () => {
await checkoutPageObject.waitForCheckoutToFinishUpdating();
- // The error should be gone. Assert the same string the visible check
- // above uses: "Add shipping insurance is a required field." appears
- // nowhere in the plugin, so toBeHidden() was matching zero elements
- // and passing whether or not the error had actually cleared.
+ // The error should be gone
await expect(
checkoutPageObject.page.getByText(
- 'Please check this box if you want to proceed.'
+ 'Add shipping insurance is a required field.'
)
).toBeHidden();
} );
diff --git a/plugins/woocommerce/tests/e2e/tests/blocks/checkout/additional-fields.merchant.block_theme.spec.ts b/plugins/woocommerce/tests/e2e/tests/blocks/checkout/additional-fields.merchant.block_theme.spec.ts
index 070008aedc8..be1fa356551 100644
--- a/plugins/woocommerce/tests/e2e/tests/blocks/checkout/additional-fields.merchant.block_theme.spec.ts
+++ b/plugins/woocommerce/tests/e2e/tests/blocks/checkout/additional-fields.merchant.block_theme.spec.ts
@@ -29,317 +29,387 @@ test.describe( 'Merchant → Additional Checkout Fields', () => {
await frontendUtils.goToCheckout();
} );
- test( 'Merchant can view and edit additional fields in the order admin page', async ( {
+ test( 'Merchant can see additional fields in the order admin page', async ( {
checkoutPageObject,
admin,
} ) => {
- await test.step( 'Merchant can see additional fields in the order admin page', async () => {
- await checkoutPageObject.editShippingDetails();
- await checkoutPageObject.unsyncBillingWithShipping();
- await checkoutPageObject.editBillingDetails();
- await checkoutPageObject.fillInCheckoutWithTestData(
- {},
- {
- contact: {
- 'Alternative Email': 'test@test.com',
- 'Enter a gift message to include in the package':
- 'This is for you!',
- 'Is this a personal purchase or a business purchase?':
- 'business',
+ await checkoutPageObject.editShippingDetails();
+ await checkoutPageObject.unsyncBillingWithShipping();
+ await checkoutPageObject.editBillingDetails();
+ await checkoutPageObject.fillInCheckoutWithTestData(
+ {},
+ {
+ contact: {
+ 'Alternative Email': 'test@test.com',
+ 'Enter a gift message to include in the package':
+ 'This is for you!',
+ 'Is this a personal purchase or a business purchase?':
+ 'business',
+ },
+ address: {
+ shipping: {
+ 'Government ID': '12345',
+ 'Confirm government ID': '12345',
},
- address: {
- shipping: {
- 'Government ID': '12345',
- 'Confirm government ID': '12345',
- },
- billing: {
- 'Government ID': '54321',
- 'Confirm government ID': '54321',
- },
+ billing: {
+ 'Government ID': '54321',
+ 'Confirm government ID': '54321',
},
- order: {
- 'How did you hear about us?': 'Other',
- 'What is your favourite colour?': 'Blue',
+ },
+ order: {
+ 'How did you hear about us?': 'Other',
+ 'What is your favourite colour?': 'Blue',
+ },
+ }
+ );
+
+ // Fill select fields "manually" (Not part of "fillInCheckoutWithTestData"). This is a workaround for select
+ // fields until we recreate th Combobox component. This is because the aria-label includes the value so getting
+ // by label alone is not reliable unless we know the value.
+ await checkoutPageObject.page
+ .getByRole( 'group', {
+ name: 'Shipping address',
+ } )
+ .getByLabel( 'How wide is your road?' )
+ .selectOption( 'wide' );
+ await checkoutPageObject.page
+ .getByRole( 'group', {
+ name: 'Billing address',
+ } )
+ .getByLabel( 'How wide is your road?' )
+ .selectOption( 'narrow' );
+
+ await checkoutPageObject.page.evaluate(
+ 'document.activeElement.blur()'
+ );
+
+ await checkoutPageObject.page
+ .getByLabel( 'Would you like a free gift with your order?' )
+ .check();
+ await checkoutPageObject.page
+ .getByLabel( 'Do you want to subscribe to our newsletter?' )
+ .check();
+ await checkoutPageObject.page
+ .getByRole( 'group', {
+ name: 'Shipping address',
+ } )
+ .getByLabel( 'Can a truck fit down your road?' )
+ .check();
+
+ await checkoutPageObject.page
+ .getByRole( 'group', {
+ name: 'Billing address',
+ } )
+ .getByLabel( 'Can a truck fit down your road?' )
+ .uncheck();
+
+ await checkoutPageObject.page
+ .getByLabel( 'Test required checkbox' )
+ .check();
+
+ await checkoutPageObject.placeOrder();
+
+ const orderId = checkoutPageObject.getOrderId();
+ await admin.page.goto(
+ `wp-admin/post.php?post=${ orderId }&action=edit`
+ );
+
+ await expect(
+ admin.page.getByText( 'Government ID: 12345', { exact: true } )
+ ).toBeVisible();
+ await expect(
+ admin.page.getByText( 'Confirm government ID: 12345', {
+ exact: true,
+ } )
+ ).toBeVisible();
+ await expect(
+ admin.page.getByText( 'Government ID: 54321', { exact: true } )
+ ).toBeVisible();
+ await expect(
+ admin.page.getByText( 'Confirm government ID: 54321', {
+ exact: true,
+ } )
+ ).toBeVisible();
+ await expect(
+ admin.page.getByText( 'What is your favourite colour?: Blue' )
+ ).toBeVisible();
+ await expect(
+ admin.page.getByText(
+ 'Enter a gift message to include in the package: This is for you!'
+ )
+ ).toBeVisible();
+ await expect(
+ admin.page.getByText(
+ 'Do you want to subscribe to our newsletter?: Yes'
+ )
+ ).toBeVisible();
+ await expect(
+ admin.page.getByText(
+ 'Would you like a free gift with your order?: Yes'
+ )
+ ).toBeVisible();
+ await expect(
+ admin.page.getByText( 'Can a truck fit down your road?: Yes' )
+ ).toBeVisible();
+ await expect(
+ admin.page.getByText( 'Can a truck fit down your road?: No' )
+ ).toBeVisible();
+ await expect(
+ admin.page.getByText( 'How wide is your road?: Wide' )
+ ).toBeVisible();
+ await expect(
+ admin.page.getByText( 'How wide is your road?: Narrow' )
+ ).toBeVisible();
+ await expect(
+ admin.page.getByText(
+ 'Is this a personal purchase or a business purchase?: Business'
+ )
+ ).toBeVisible();
+ await expect(
+ admin.page.getByText( 'How did you hear about us?: Other' )
+ ).toBeVisible();
+ } );
+
+ test( 'Merchant can edit custom fields from the order admin page', async ( {
+ checkoutPageObject,
+ admin,
+ } ) => {
+ await checkoutPageObject.editShippingDetails();
+ await checkoutPageObject.unsyncBillingWithShipping();
+ await checkoutPageObject.editBillingDetails();
+ await checkoutPageObject.fillInCheckoutWithTestData(
+ {},
+ {
+ contact: {
+ 'Alternative Email': 'test@test.com',
+ 'Enter a gift message to include in the package':
+ 'This is for you!',
+ 'Is this a personal purchase or a business purchase?':
+ 'business',
+ },
+ address: {
+ shipping: {
+ 'Government ID': '12345',
+ 'Confirm government ID': '12345',
},
- }
- );
-
- // Fill select fields "manually" (Not part of "fillInCheckoutWithTestData"). This is a workaround for select
- // fields until we recreate th Combobox component. This is because the aria-label includes the value so getting
- // by label alone is not reliable unless we know the value.
- await checkoutPageObject.page
- .getByRole( 'group', {
- name: 'Shipping address',
- } )
- .getByLabel( 'How wide is your road?' )
- .selectOption( 'wide' );
- await checkoutPageObject.page
- .getByRole( 'group', {
- name: 'Billing address',
- } )
- .getByLabel( 'How wide is your road?' )
- .selectOption( 'narrow' );
-
- await checkoutPageObject.page.evaluate(
- 'document.activeElement.blur()'
- );
-
- await checkoutPageObject.page
- .getByLabel( 'Would you like a free gift with your order?' )
- .check();
- await checkoutPageObject.page
- .getByLabel( 'Do you want to subscribe to our newsletter?' )
- .check();
- await checkoutPageObject.page
- .getByRole( 'group', {
- name: 'Shipping address',
- } )
- .getByLabel( 'Can a truck fit down your road?' )
- .check();
-
- await checkoutPageObject.page
- .getByRole( 'group', {
- name: 'Billing address',
- } )
- .getByLabel( 'Can a truck fit down your road?' )
- .uncheck();
-
- await checkoutPageObject.page
- .getByLabel( 'Test required checkbox' )
- .check();
-
- await checkoutPageObject.placeOrder();
-
- const orderId = checkoutPageObject.getOrderId();
- await admin.page.goto(
- `wp-admin/admin.php?page=wc-orders&action=edit&id=${ orderId }`
- );
- await expect( admin.page ).toHaveURL(
- new RegExp(
- `admin\\.php\\?page=wc-orders&action=edit&id=${ orderId }$`
- )
- );
-
- await expect(
- admin.page.getByText( 'Government ID: 12345', { exact: true } )
- ).toBeVisible();
- await expect(
- admin.page.getByText( 'Confirm government ID: 12345', {
- exact: true,
- } )
- ).toBeVisible();
- await expect(
- admin.page.getByText( 'Government ID: 54321', { exact: true } )
- ).toBeVisible();
- await expect(
- admin.page.getByText( 'Confirm government ID: 54321', {
- exact: true,
- } )
- ).toBeVisible();
- await expect(
- admin.page.getByText( 'What is your favourite colour?: Blue' )
- ).toBeVisible();
- await expect(
- admin.page.getByText(
- 'Enter a gift message to include in the package: This is for you!'
- )
- ).toBeVisible();
- await expect(
- admin.page.getByText(
- 'Do you want to subscribe to our newsletter?: Yes'
- )
- ).toBeVisible();
- await expect(
- admin.page.getByText(
- 'Would you like a free gift with your order?: Yes'
- )
- ).toBeVisible();
- await expect(
- admin.page.getByText( 'Can a truck fit down your road?: Yes' )
- ).toBeVisible();
- await expect(
- admin.page.getByText( 'Can a truck fit down your road?: No' )
- ).toBeVisible();
- await expect(
- admin.page.getByText( 'How wide is your road?: Wide' )
- ).toBeVisible();
- await expect(
- admin.page.getByText( 'How wide is your road?: Narrow' )
- ).toBeVisible();
- await expect(
- admin.page.getByText(
- 'Is this a personal purchase or a business purchase?: Business'
- )
- ).toBeVisible();
- await expect(
- admin.page.getByText( 'How did you hear about us?: Other' )
- ).toBeVisible();
- } );
+ billing: {
+ 'Government ID': '54321',
+ 'Confirm government ID': '54321',
+ },
+ },
+ order: {
+ 'How did you hear about us?': 'Other',
+ 'What is your favourite colour?': 'Blue',
+ },
+ }
+ );
- const orderAdminUrl = admin.page.url();
-
- await test.step( 'Merchant can edit custom fields from the order admin page', async () => {
- await admin.page.goto( orderAdminUrl );
-
- await admin.page
- .getByRole( 'heading', { name: 'Billing Edit' } )
- .getByRole( 'link' )
- .click();
-
- // Change all the billing details
- await admin.page
- .getByRole( 'textbox', {
- name: 'Government ID',
- exact: true,
- } )
- .fill( '99999' );
- await admin.page
- .getByRole( 'textbox', {
- name: 'Confirm government ID',
- exact: true,
- } )
- .fill( '99999' );
- await admin.page
- .getByRole( 'checkbox', {
- name: 'Can a truck fit down your road?',
- } )
- .check();
-
- // Use Locator here because the select2 box is duplicated in shipping.
- await admin.page
- .locator(
- '[id="\\_wc_billing\\/first-plugin-namespace\\/road-size"]'
- )
- .selectOption( 'wide' );
-
- // Handle changing the contact fields.
- await admin.page
- .getByLabel( 'Do you want to subscribe to our newsletter?' )
- .uncheck();
- await admin.page
- .getByLabel( 'Enter a gift message to include in the package' )
- .fill( 'Some other message' );
- await admin.page
- .getByLabel(
- 'Is this a personal purchase or a business purchase?'
- )
- .selectOption( 'personal' );
-
- const clickPromise = admin.page
- .getByRole( 'button', { name: 'Update' } )
- .first()
- .click();
-
- const navigationPromise =
- admin.page.waitForEvent( 'domcontentloaded' );
-
- // When update is clicked without waiting for DOMContentLoaded the page becomes
- // available before click handlers are attached to the shipping edit link.
- await Promise.all( [ clickPromise, navigationPromise ] );
-
- await admin.page
- .getByRole( 'heading', { name: 'Shipping Edit' } )
- .getByRole( 'link' )
- .click();
-
- // Change all the shipping details
- await admin.page
- .getByRole( 'textbox', {
- name: 'Government ID',
- exact: true,
- } )
- .fill( '88888' );
- await admin.page
- .getByRole( 'textbox', {
- name: 'Confirm government ID',
- exact: true,
- } )
- .fill( '88888' );
- await admin.page
- .getByRole( 'checkbox', {
- name: 'Can a truck fit down your road?',
- } )
- .uncheck();
-
- // Use Locator here because the select2 box is duplicated in billing.
- await admin.page
- .locator(
- '[id="\\_wc_shipping\\/first-plugin-namespace\\/road-size"]'
- )
- .selectOption( 'super-wide' );
-
- // Handle changing the additional information fields.
- await admin.page
- .getByLabel( 'Would you like a free gift with your order?' )
- .uncheck();
- await admin.page
- .getByLabel( 'What is your favourite colour?' )
- .fill( 'Green' );
- await admin.page
- .getByLabel( 'How did you hear about us?' )
- .selectOption( 'google' );
-
- await admin.page
- .getByRole( 'button', { name: 'Update' } )
- .first()
- .click();
-
- await admin.page.waitForLoadState( 'domcontentloaded' );
-
- await expect(
- admin.page.getByText( 'Government ID: 88888', { exact: true } )
- ).toBeVisible();
- await expect(
- admin.page.getByText( 'Confirm government ID: 88888', {
- exact: true,
- } )
- ).toBeVisible();
- await expect(
- admin.page.getByText( 'Government ID: 99999', { exact: true } )
- ).toBeVisible();
- await expect(
- admin.page.getByText( 'Confirm government ID: 99999', {
- exact: true,
- } )
- ).toBeVisible();
- await expect(
- admin.page.getByText( 'What is your favourite colour?: Green' )
- ).toBeVisible();
- await expect(
- admin.page.getByText(
- 'Enter a gift message to include in the package: Some other message'
- )
- ).toBeVisible();
- await expect(
- admin.page.getByText(
- 'Do you want to subscribe to our newsletter?: No'
- )
- ).toBeVisible();
- await expect(
- admin.page.getByText(
- 'Would you like a free gift with your order?: No'
- )
- ).toBeVisible();
- await expect(
- admin.page.getByText( 'Can a truck fit down your road?: Yes' )
- ).toBeVisible();
- await expect(
- admin.page.getByText( 'Can a truck fit down your road?: No' )
- ).toBeVisible();
- await expect(
- admin.page.getByText( 'How wide is your road?: Super wide' )
- ).toBeVisible();
- await expect(
- admin.page.getByText( 'How wide is your road?: Wide' )
- ).toBeVisible();
- await expect(
- admin.page.getByText(
- 'Is this a personal purchase or a business purchase?: Personal'
- )
- ).toBeVisible();
- await expect(
- admin.page.getByText( 'How did you hear about us?: Google' )
- ).toBeVisible();
- } );
+ // Fill select fields "manually" (Not part of "fillInCheckoutWithTestData"). This is a workaround for select
+ // fields until we recreate th Combobox component. This is because the aria-label includes the value so getting
+ // by label alone is not reliable unless we know the value.
+ await checkoutPageObject.page
+ .getByRole( 'group', {
+ name: 'Shipping address',
+ } )
+ .getByLabel( 'How wide is your road?' )
+ .selectOption( 'wide' );
+ await checkoutPageObject.page
+ .getByRole( 'group', {
+ name: 'Billing address',
+ } )
+ .getByLabel( 'How wide is your road?' )
+ .selectOption( 'narrow' );
+
+ await checkoutPageObject.page.evaluate(
+ 'document.activeElement.blur()'
+ );
+
+ await checkoutPageObject.page
+ .getByLabel( 'Would you like a free gift with your order?' )
+ .check();
+ await checkoutPageObject.page
+ .getByLabel( 'Do you want to subscribe to our newsletter?' )
+ .check();
+ await checkoutPageObject.page
+ .getByRole( 'group', {
+ name: 'Shipping address',
+ } )
+ .getByLabel( 'Can a truck fit down your road?' )
+ .check();
+
+ await checkoutPageObject.page
+ .getByRole( 'group', {
+ name: 'Billing address',
+ } )
+ .getByLabel( 'Can a truck fit down your road?' )
+ .uncheck();
+
+ await checkoutPageObject.page
+ .getByLabel( 'Test required checkbox' )
+ .check();
+
+ await checkoutPageObject.placeOrder();
+
+ const orderId = checkoutPageObject.getOrderId();
+ await admin.page.goto(
+ `wp-admin/post.php?post=${ orderId }&action=edit`
+ );
+
+ await admin.page
+ .getByRole( 'heading', { name: 'Billing Edit' } )
+ .getByRole( 'link' )
+ .click();
+
+ // Change all the billing details
+ await admin.page
+ .getByRole( 'textbox', {
+ name: 'Government ID',
+ exact: true,
+ } )
+ .fill( '99999' );
+ await admin.page
+ .getByRole( 'textbox', {
+ name: 'Confirm government ID',
+ exact: true,
+ } )
+ .fill( '99999' );
+ await admin.page
+ .getByRole( 'checkbox', {
+ name: 'Can a truck fit down your road?',
+ } )
+ .check();
+
+ // Use Locator here because the select2 box is duplicated in shipping.
+ await admin.page
+ .locator(
+ '[id="\\_wc_billing\\/first-plugin-namespace\\/road-size"]'
+ )
+ .selectOption( 'wide' );
+
+ // Handle changing the contact fields.
+ await admin.page
+ .getByLabel( 'Do you want to subscribe to our newsletter?' )
+ .uncheck();
+ await admin.page
+ .getByLabel( 'Enter a gift message to include in the package' )
+ .fill( 'Some other message' );
+ await admin.page
+ .getByLabel( 'Is this a personal purchase or a business purchase?' )
+ .selectOption( 'personal' );
+
+ const clickPromise = admin.page
+ .getByRole( 'button', { name: 'Update' } )
+ .first()
+ .click();
+
+ const navigationPromise = admin.page.waitForEvent( 'domcontentloaded' );
+
+ // When update is clicked without waiting for DOMContentLoaded the page becomes
+ // available before click handlers are attached to the shipping edit link.
+ await Promise.all( [ clickPromise, navigationPromise ] );
+
+ await admin.page
+ .getByRole( 'heading', { name: 'Shipping Edit' } )
+ .getByRole( 'link' )
+ .click();
+
+ // Change all the shipping details
+ await admin.page
+ .getByRole( 'textbox', {
+ name: 'Government ID',
+ exact: true,
+ } )
+ .fill( '88888' );
+ await admin.page
+ .getByRole( 'textbox', {
+ name: 'Confirm government ID',
+ exact: true,
+ } )
+ .fill( '88888' );
+ await admin.page
+ .getByRole( 'checkbox', {
+ name: 'Can a truck fit down your road?',
+ } )
+ .uncheck();
+
+ // Use Locator here because the select2 box is duplicated in billing.
+ await admin.page
+ .locator(
+ '[id="\\_wc_shipping\\/first-plugin-namespace\\/road-size"]'
+ )
+ .selectOption( 'super-wide' );
+
+ // Handle changing the additional information fields.
+ await admin.page
+ .getByLabel( 'Would you like a free gift with your order?' )
+ .uncheck();
+ await admin.page
+ .getByLabel( 'What is your favourite colour?' )
+ .fill( 'Green' );
+ await admin.page
+ .getByLabel( 'How did you hear about us?' )
+ .selectOption( 'google' );
+
+ await admin.page
+ .getByRole( 'button', { name: 'Update' } )
+ .first()
+ .click();
+
+ await admin.page.waitForLoadState( 'domcontentloaded' );
+
+ await expect(
+ admin.page.getByText( 'Government ID: 88888', { exact: true } )
+ ).toBeVisible();
+ await expect(
+ admin.page.getByText( 'Confirm government ID: 88888', {
+ exact: true,
+ } )
+ ).toBeVisible();
+ await expect(
+ admin.page.getByText( 'Government ID: 99999', { exact: true } )
+ ).toBeVisible();
+ await expect(
+ admin.page.getByText( 'Confirm government ID: 99999', {
+ exact: true,
+ } )
+ ).toBeVisible();
+ await expect(
+ admin.page.getByText( 'What is your favourite colour?: Green' )
+ ).toBeVisible();
+ await expect(
+ admin.page.getByText(
+ 'Enter a gift message to include in the package: Some other message'
+ )
+ ).toBeVisible();
+ await expect(
+ admin.page.getByText(
+ 'Do you want to subscribe to our newsletter?: No'
+ )
+ ).toBeVisible();
+ await expect(
+ admin.page.getByText(
+ 'Would you like a free gift with your order?: No'
+ )
+ ).toBeVisible();
+ await expect(
+ admin.page.getByText( 'Can a truck fit down your road?: Yes' )
+ ).toBeVisible();
+ await expect(
+ admin.page.getByText( 'Can a truck fit down your road?: No' )
+ ).toBeVisible();
+ await expect(
+ admin.page.getByText( 'How wide is your road?: Super wide' )
+ ).toBeVisible();
+ await expect(
+ admin.page.getByText( 'How wide is your road?: Wide' )
+ ).toBeVisible();
+ await expect(
+ admin.page.getByText(
+ 'Is this a personal purchase or a business purchase?: Personal'
+ )
+ ).toBeVisible();
+ await expect(
+ admin.page.getByText( 'How did you hear about us?: Google' )
+ ).toBeVisible();
} );
} );
diff --git a/plugins/woocommerce/tests/e2e/tests/blocks/checkout/additional-fields.shopper.block_theme.spec.ts b/plugins/woocommerce/tests/e2e/tests/blocks/checkout/additional-fields.shopper.block_theme.spec.ts
index 62ab317b26a..d42f15a840f 100644
--- a/plugins/woocommerce/tests/e2e/tests/blocks/checkout/additional-fields.shopper.block_theme.spec.ts
+++ b/plugins/woocommerce/tests/e2e/tests/blocks/checkout/additional-fields.shopper.block_theme.spec.ts
@@ -29,6 +29,529 @@ test.describe( 'Shopper → Additional Checkout Fields', () => {
);
} );
+ test( 'Shopper can fill in the checkout form with additional fields and can have different value for same field in shipping and billing address', async ( {
+ checkoutPageObject,
+ frontendUtils,
+ } ) => {
+ await frontendUtils.goToShop();
+ await frontendUtils.addToCart( REGULAR_PRICED_PRODUCT_NAME );
+ await frontendUtils.goToCheckout();
+
+ await checkoutPageObject.editShippingDetails();
+ await checkoutPageObject.unsyncBillingWithShipping();
+ await checkoutPageObject.editBillingDetails();
+ await checkoutPageObject.fillInCheckoutWithTestData(
+ {},
+ {
+ contact: {
+ 'Alternative Email': 'test@test.com',
+ 'Enter a gift message to include in the package':
+ 'This is for you!',
+ 'Is this a personal purchase or a business purchase?':
+ 'business',
+ },
+ address: {
+ shipping: {
+ 'Government ID': '12345',
+ 'Confirm government ID': '12345',
+ 'How wide is your road? (optional)': 'wide',
+ },
+ billing: {
+ 'Government ID': '54321',
+ 'Confirm government ID': '54321',
+ 'How wide is your road? (optional)': 'narrow',
+ },
+ },
+ order: {
+ 'How did you hear about us?': 'Other',
+ 'What is your favourite colour?': 'Blue',
+ },
+ }
+ );
+
+ await checkoutPageObject.page
+ .getByLabel( 'Would you like a free gift with your order?' )
+ .check();
+ await checkoutPageObject.page
+ .getByLabel( 'Do you want to subscribe to our newsletter?' )
+ .check();
+ await checkoutPageObject.page
+ .getByRole( 'group', {
+ name: 'Shipping address',
+ } )
+ .getByLabel( 'Can a truck fit down your road?' )
+ .check();
+
+ await checkoutPageObject.page
+ .getByRole( 'group', {
+ name: 'Billing address',
+ } )
+ .getByLabel( 'Can a truck fit down your road?' )
+ .uncheck();
+
+ await checkoutPageObject.page
+ .getByLabel( 'Test required checkbox' )
+ .check();
+
+ await checkoutPageObject.placeOrder();
+
+ expect(
+ await checkoutPageObject.verifyAdditionalFieldsDetails( [
+ [ 'Government ID', '12345' ],
+ [ 'Government ID', '54321' ],
+ [ 'What is your favourite colour?', 'Blue' ],
+ [
+ 'Enter a gift message to include in the package',
+ 'This is for you!',
+ ],
+ [ 'Do you want to subscribe to our newsletter?', 'Yes' ],
+ [ 'Would you like a free gift with your order?', 'Yes' ],
+ [ 'Can a truck fit down your road?', 'Yes' ],
+ [ 'Can a truck fit down your road?', 'No' ],
+ [ 'How wide is your road?', 'Wide' ],
+ [ 'How wide is your road?', 'Narrow' ],
+ [
+ 'Is this a personal purchase or a business purchase?',
+ 'business',
+ ],
+ ] )
+ ).toBe( true );
+
+ await frontendUtils.emptyCart();
+ await frontendUtils.goToShop();
+ await frontendUtils.addToCart( REGULAR_PRICED_PRODUCT_NAME );
+ await frontendUtils.goToCheckout();
+
+ await checkoutPageObject.editShippingDetails();
+ await checkoutPageObject.editBillingDetails();
+
+ // Now check all the fields previously filled are still filled on a fresh checkout.
+ await expect(
+ checkoutPageObject.page
+ .getByRole( 'group', {
+ name: 'Contact information',
+ } )
+ .getByLabel(
+ 'Enter a gift message to include in the package'
+ )
+ ).toHaveValue( 'This is for you!' );
+ await expect(
+ checkoutPageObject.page
+ .getByRole( 'group', {
+ name: 'Contact information',
+ } )
+ .getByLabel(
+ 'Is this a personal purchase or a business purchase?'
+ )
+ ).toHaveValue( 'business' );
+ await expect(
+ checkoutPageObject.page
+ .getByRole( 'group', {
+ name: 'Contact information',
+ } )
+ .getByLabel( 'Do you want to subscribe to our newsletter?' )
+ ).toBeChecked();
+ await expect(
+ checkoutPageObject.page
+ .getByRole( 'group', {
+ name: 'Shipping address',
+ } )
+ .getByLabel( 'Government ID', { exact: true } )
+ ).toHaveValue( '12345' );
+ await expect(
+ checkoutPageObject.page
+ .getByRole( 'group', {
+ name: 'Shipping address',
+ } )
+ .getByLabel( 'Confirm Government ID' )
+ ).toHaveValue( '12345' );
+ await expect(
+ checkoutPageObject.page
+ .getByRole( 'group', {
+ name: 'Shipping address',
+ } )
+ .getByLabel( 'Can a truck fit down your road?' )
+ ).toBeChecked();
+ await expect(
+ checkoutPageObject.page
+ .getByRole( 'group', {
+ name: 'Shipping address',
+ } )
+ .getByLabel( 'How wide is your road? (optional)' )
+ ).toHaveValue( 'wide' );
+ await expect(
+ checkoutPageObject.page
+ .getByRole( 'group', {
+ name: 'Billing address',
+ } )
+ .getByLabel( 'Government ID', { exact: true } )
+ ).toHaveValue( '54321' );
+ await expect(
+ checkoutPageObject.page
+ .getByRole( 'group', {
+ name: 'Billing address',
+ } )
+ .getByLabel( 'Confirm Government ID' )
+ ).toHaveValue( '54321' );
+ await expect(
+ checkoutPageObject.page
+ .getByRole( 'group', {
+ name: 'Billing address',
+ } )
+ .getByLabel( 'Can a truck fit down your road?' )
+ ).not.toBeChecked();
+ await expect(
+ checkoutPageObject.page
+ .getByRole( 'group', {
+ name: 'Billing address',
+ } )
+ .getByLabel( 'How wide is your road? (optional)' )
+ ).toHaveValue( 'narrow' );
+ } );
+
+ test( 'Shopper can change the values of fields multiple times and place the order', async ( {
+ checkoutPageObject,
+ frontendUtils,
+ } ) => {
+ await frontendUtils.goToShop();
+ await frontendUtils.addToCart( REGULAR_PRICED_PRODUCT_NAME );
+ await frontendUtils.goToCheckout();
+
+ await checkoutPageObject.editShippingDetails();
+ await checkoutPageObject.unsyncBillingWithShipping();
+ await checkoutPageObject.editBillingDetails();
+ await checkoutPageObject.fillInCheckoutWithTestData(
+ {},
+ {
+ contact: {
+ 'Alternative Email': 'test@test.com',
+ 'Enter a gift message to include in the package':
+ 'This is for you!',
+ 'Is this a personal purchase or a business purchase?':
+ 'business',
+ },
+ address: {
+ shipping: {
+ 'Government ID': '12345',
+ 'Confirm government ID': '12345',
+ 'How wide is your road? (optional)': 'wide',
+ },
+ billing: {
+ 'Government ID': '54321',
+ 'Confirm government ID': '54321',
+ 'How wide is your road? (optional)': 'narrow',
+ },
+ },
+ order: {
+ 'How did you hear about us?': 'Other',
+ 'What is your favourite colour?': 'Blue',
+ },
+ }
+ );
+
+ // Change the shipping and billing select fields again.
+ await checkoutPageObject.fillInCheckoutWithTestData(
+ {},
+ {
+ address: {
+ shipping: {
+ 'How wide is your road? (optional)': 'super-wide',
+ },
+ billing: {
+ 'How wide is your road? (optional)': 'wide',
+ },
+ },
+ }
+ );
+
+ await checkoutPageObject.page
+ .getByLabel( 'Would you like a free gift with your order?' )
+ .check();
+ await checkoutPageObject.page
+ .getByLabel( 'Do you want to subscribe to our newsletter?' )
+ .check();
+
+ // Check both "Can a truck fit down your road?" checkboxes (one in shipping, one in billing).
+ await checkoutPageObject.page
+ .getByRole( 'group', {
+ name: 'Shipping address',
+ } )
+ .getByLabel( 'Can a truck fit down your road?' )
+ .check();
+ // Check this one here, but don't uncheck it later.
+ await checkoutPageObject.page
+ .getByRole( 'group', {
+ name: 'Billing address',
+ } )
+ .getByLabel( 'Can a truck fit down your road?' )
+ .check();
+
+ await checkoutPageObject.page
+ .getByLabel( 'Would you like a free gift with your order?' )
+ .uncheck();
+ await checkoutPageObject.page
+ .getByLabel( 'Do you want to subscribe to our newsletter?' )
+ .uncheck();
+ await checkoutPageObject.page
+ .getByRole( 'group', {
+ name: 'Shipping address',
+ } )
+ .getByLabel( 'Can a truck fit down your road?' )
+ .uncheck();
+
+ await checkoutPageObject.fillInCheckoutWithTestData(
+ {},
+ {
+ contact: {
+ 'Alternative Email': 'test@test.com',
+ 'Enter a gift message to include in the package':
+ 'This is for you, from me!',
+ 'Is this a personal purchase or a business purchase?':
+ 'personal',
+ },
+ address: {
+ shipping: {
+ 'Government ID': '98765',
+ 'Confirm government ID': '98765',
+ },
+ billing: {
+ 'Government ID': '43210',
+ 'Confirm government ID': '43210',
+ },
+ },
+ order: {
+ 'What is your favourite colour?': 'Red',
+ 'How did you hear about us?':
+ 'Select a how did you hear about us? (optional)',
+ },
+ }
+ );
+ await checkoutPageObject.page
+ .getByLabel( 'Test required checkbox' )
+ .check();
+ await checkoutPageObject.placeOrder();
+
+ expect(
+ await checkoutPageObject.verifyAdditionalFieldsDetails( [
+ [ 'Government ID', '98765' ],
+ [ 'Government ID', '43210' ],
+ [ 'What is your favourite colour?', 'Red' ],
+ [ 'Would you like a free gift with your order?', 'No' ],
+ // One checkbox is checked, the other is unchecked.
+ [ 'Can a truck fit down your road?', 'No' ],
+ [ 'Can a truck fit down your road?', 'Yes' ],
+ // Different values in different address types.
+ [ 'How wide is your road?', 'Wide' ],
+ [ 'How wide is your road?', 'Super wide' ],
+ [
+ 'Enter a gift message to include in the package',
+ 'This is for you, from me!',
+ ],
+ [ 'Do you want to subscribe to our newsletter?', 'No' ],
+ ] )
+ ).toBe( true );
+
+ // This optional select field was unset, so it should not be visible on the confirmation. Can't check this
+ // with the above function so we will check it "manually".
+ await expect(
+ checkoutPageObject.page.getByText(
+ 'How did you hear about us?'
+ )
+ ).toBeHidden();
+
+ // Checking that one of the boxes is checked, and one is unchecked
+ await expect(
+ checkoutPageObject.page.getByText(
+ 'Can a truck fit down your road?No'
+ )
+ ).toBeVisible();
+ await expect(
+ checkoutPageObject.page.getByText(
+ 'Can a truck fit down your road?Yes'
+ )
+ ).toBeVisible();
+ } );
+
+ test( 'Shopper can input unsanitized values that become sanitized after checkout', async ( {
+ checkoutPageObject,
+ frontendUtils,
+ } ) => {
+ await frontendUtils.goToShop();
+ await frontendUtils.addToCart( REGULAR_PRICED_PRODUCT_NAME );
+ await frontendUtils.goToCheckout();
+
+ await checkoutPageObject.editShippingDetails();
+ await checkoutPageObject.unsyncBillingWithShipping();
+ await checkoutPageObject.editBillingDetails();
+ await checkoutPageObject.fillInCheckoutWithTestData(
+ {},
+ {
+ contact: {
+ 'Alternative Email': 'test@test.com',
+ 'Enter a gift message to include in the package':
+ 'This is for you!',
+ 'Is this a personal purchase or a business purchase?':
+ 'business',
+ },
+ address: {
+ shipping: {
+ 'Government ID': ' 1. 2 3 4 5 ',
+ 'Confirm government ID': '1 2345',
+ 'How wide is your road? (optional)': 'wide',
+ },
+ billing: {
+ 'Government ID': ' 5. 4 3 2 1 ',
+ 'Confirm government ID': '543 21',
+ 'How wide is your road? (optional)': 'narrow',
+ },
+ },
+ order: {
+ 'How did you hear about us?': 'Other',
+ 'What is your favourite colour?': 'Blue',
+ },
+ }
+ );
+
+ await checkoutPageObject.page
+ .getByLabel( 'Would you like a free gift with your order?' )
+ .check();
+ await checkoutPageObject.page
+ .getByLabel( 'Do you want to subscribe to our newsletter?' )
+ .check();
+ await checkoutPageObject.page
+ .getByRole( 'group', {
+ name: 'Shipping address',
+ } )
+ .getByLabel( 'Can a truck fit down your road?' )
+ .check();
+
+ await checkoutPageObject.page
+ .getByRole( 'group', {
+ name: 'Billing address',
+ } )
+ .getByLabel( 'Can a truck fit down your road?' )
+ .uncheck();
+
+ await checkoutPageObject.page
+ .getByLabel( 'Test required checkbox' )
+ .check();
+
+ await checkoutPageObject.placeOrder();
+
+ expect(
+ await checkoutPageObject.verifyAdditionalFieldsDetails( [
+ [ 'Government ID', '12345' ],
+ [ 'Government ID', '54321' ],
+ [ 'What is your favourite colour?', 'Blue' ],
+ [
+ 'Enter a gift message to include in the package',
+ 'This is for you!',
+ ],
+ [ 'Do you want to subscribe to our newsletter?', 'Yes' ],
+ [ 'Would you like a free gift with your order?', 'Yes' ],
+ [ 'Can a truck fit down your road?', 'Yes' ],
+ [ 'Can a truck fit down your road?', 'No' ],
+ [ 'How wide is your road?', 'Wide' ],
+ [ 'How wide is your road?', 'Narrow' ],
+ [
+ 'Is this a personal purchase or a business purchase?',
+ 'business',
+ ],
+ ] )
+ ).toBe( true );
+
+ await frontendUtils.emptyCart();
+ await frontendUtils.goToShop();
+ await frontendUtils.addToCart( REGULAR_PRICED_PRODUCT_NAME );
+ await frontendUtils.goToCheckout();
+
+ await checkoutPageObject.editShippingDetails();
+ await checkoutPageObject.editBillingDetails();
+
+ // Now check all the fields previously filled are still filled on a fresh checkout.
+ await expect(
+ checkoutPageObject.page
+ .getByRole( 'group', {
+ name: 'Contact information',
+ } )
+ .getByLabel(
+ 'Enter a gift message to include in the package'
+ )
+ ).toHaveValue( 'This is for you!' );
+ await expect(
+ checkoutPageObject.page
+ .getByRole( 'group', {
+ name: 'Contact information',
+ } )
+ .getByLabel(
+ 'Is this a personal purchase or a business purchase?'
+ )
+ ).toHaveValue( 'business' );
+ await expect(
+ checkoutPageObject.page
+ .getByRole( 'group', {
+ name: 'Contact information',
+ } )
+ .getByLabel( 'Do you want to subscribe to our newsletter?' )
+ ).toBeChecked();
+ await expect(
+ checkoutPageObject.page
+ .getByRole( 'group', {
+ name: 'Shipping address',
+ } )
+ .getByLabel( 'Government ID', { exact: true } )
+ ).toHaveValue( '12345' );
+ await expect(
+ checkoutPageObject.page
+ .getByRole( 'group', {
+ name: 'Shipping address',
+ } )
+ .getByLabel( 'Confirm Government ID' )
+ ).toHaveValue( '12345' );
+ await expect(
+ checkoutPageObject.page
+ .getByRole( 'group', {
+ name: 'Shipping address',
+ } )
+ .getByLabel( 'Can a truck fit down your road?' )
+ ).toBeChecked();
+ await expect(
+ checkoutPageObject.page
+ .getByRole( 'group', {
+ name: 'Shipping address',
+ } )
+ .getByLabel( 'How wide is your road? (optional)' )
+ ).toHaveValue( 'wide' );
+ await expect(
+ checkoutPageObject.page
+ .getByRole( 'group', {
+ name: 'Billing address',
+ } )
+ .getByLabel( 'Government ID', { exact: true } )
+ ).toHaveValue( '54321' );
+ await expect(
+ checkoutPageObject.page
+ .getByRole( 'group', {
+ name: 'Billing address',
+ } )
+ .getByLabel( 'Confirm Government ID' )
+ ).toHaveValue( '54321' );
+ await expect(
+ checkoutPageObject.page
+ .getByRole( 'group', {
+ name: 'Billing address',
+ } )
+ .getByLabel( 'Can a truck fit down your road?' )
+ ).not.toBeChecked();
+ await expect(
+ checkoutPageObject.page
+ .getByRole( 'group', {
+ name: 'Billing address',
+ } )
+ .getByLabel( 'How wide is your road? (optional)' )
+ ).toHaveValue( 'narrow' );
+ } );
+
test( 'Shopper can see server-side validation errors', async ( {
checkoutPageObject,
frontendUtils,
diff --git a/plugins/woocommerce/tests/php/src/Blocks/Domain/Services/CheckoutFieldsAdminTest.php b/plugins/woocommerce/tests/php/src/Blocks/Domain/Services/CheckoutFieldsAdminTest.php
deleted file mode 100644
index 64b324b5856..00000000000
--- a/plugins/woocommerce/tests/php/src/Blocks/Domain/Services/CheckoutFieldsAdminTest.php
+++ /dev/null
@@ -1,310 +0,0 @@
-<?php
-declare( strict_types = 1 );
-
-namespace Automattic\WooCommerce\Tests\Blocks\Domain\Services;
-
-use Automattic\WooCommerce\Blocks\Domain\Services\CheckoutFields;
-use Automattic\WooCommerce\Blocks\Domain\Services\CheckoutFieldsAdmin;
-use Automattic\WooCommerce\Blocks\Package;
-use WC_Order;
-use WC_Unit_Test_Case;
-
-/**
- * Tests for the CheckoutFieldsAdmin class.
- */
-class CheckoutFieldsAdminTest extends WC_Unit_Test_Case {
-
- /**
- * The System Under Test.
- *
- * @var CheckoutFieldsAdmin
- */
- private $sut;
-
- /**
- * Checkout fields controller.
- *
- * @var CheckoutFields
- */
- private $controller;
-
- /**
- * Field IDs registered by the test.
- *
- * @var string[]
- */
- private $registered_fields = array();
-
- /**
- * Whether this test registered the admin hooks.
- *
- * @var bool
- */
- private $registered_hooks = false;
-
- /**
- * Set up test fixtures.
- */
- public function setUp(): void {
- parent::setUp();
-
- $this->sut = Package::container()->get( CheckoutFieldsAdmin::class );
- $this->controller = Package::container()->get( CheckoutFields::class );
-
- if ( false === has_filter( 'woocommerce_admin_billing_fields', array( $this->sut, 'admin_address_fields' ) ) ) {
- $this->sut->init();
- $this->registered_hooks = true;
- }
- }
-
- /**
- * Tear down test fixtures.
- */
- public function tearDown(): void {
- foreach ( $this->registered_fields as $field_id ) {
- __internal_woocommerce_blocks_deregister_checkout_field( $field_id );
- }
-
- if ( $this->registered_hooks ) {
- remove_filter( 'woocommerce_admin_billing_fields', array( $this->sut, 'admin_address_fields' ), 10 );
- remove_filter( 'woocommerce_admin_billing_fields', array( $this->sut, 'admin_contact_fields' ), 10 );
- remove_filter( 'woocommerce_admin_shipping_fields', array( $this->sut, 'admin_address_fields' ), 10 );
- remove_filter( 'woocommerce_admin_shipping_fields', array( $this->sut, 'admin_order_fields' ), 10 );
- }
-
- parent::tearDown();
- }
-
- /**
- * @testdox Should inject formatted fields and persist admin updates in the correct groups.
- */
- public function test_injects_and_updates_additional_fields_for_each_admin_group(): void {
- $address_field = 'test-namespace/delivery-note';
- $contact_field = 'test-namespace/contact-method';
- $order_field = 'test-namespace/gift-wrap';
-
- $this->register_checkout_field(
- array(
- 'id' => $address_field,
- 'label' => 'Delivery note',
- 'location' => 'address',
- 'type' => 'text',
- )
- );
- $this->register_checkout_field(
- array(
- 'id' => $contact_field,
- 'label' => 'Preferred contact method',
- 'location' => 'contact',
- 'type' => 'select',
- 'options' => array(
- array(
- 'label' => 'Email',
- 'value' => 'email',
- ),
- array(
- 'label' => 'Phone',
- 'value' => 'phone',
- ),
- ),
- )
- );
- $this->register_checkout_field(
- array(
- 'id' => $order_field,
- 'label' => 'Add gift wrap',
- 'location' => 'order',
- 'type' => 'checkbox',
- )
- );
-
- $order = \WC_Helper_Order::create_order();
- $order->set_created_via( 'store-api' );
- $this->controller->persist_field_for_order( $address_field, 'Reception', $order, 'billing', false );
- $this->controller->persist_field_for_order( $address_field, 'Side door', $order, 'shipping', false );
- $this->controller->persist_field_for_order( $contact_field, 'email', $order, 'other', false );
- $this->controller->persist_field_for_order( $order_field, true, $order, 'other', false );
- $order->save();
-
- $base_fields = array( 'state' => array( 'label' => 'State' ) );
- // phpcs:ignore WooCommerce.Commenting.CommentHooks.MissingHookComment -- Firing an existing admin filter to exercise its callbacks, not declaring a new hook.
- $billing_fields = apply_filters( 'woocommerce_admin_billing_fields', $base_fields, $order, 'edit' );
- // phpcs:ignore WooCommerce.Commenting.CommentHooks.MissingHookComment -- Firing an existing admin filter to exercise its callbacks, not declaring a new hook.
- $shipping_fields = apply_filters( 'woocommerce_admin_shipping_fields', $base_fields, $order, 'edit' );
- $billing_address_id = '_wc_billing/' . $address_field;
- $shipping_address_id = '_wc_shipping/' . $address_field;
- $contact_admin_id = '_wc_other/' . $contact_field;
- $order_admin_id = '_wc_other/' . $order_field;
- $billing_address = $this->find_field_by_id( $billing_fields, $billing_address_id );
- $shipping_address = $this->find_field_by_id( $shipping_fields, $shipping_address_id );
-
- $this->assertSame( $base_fields['state'], $billing_fields['state'] ?? null, 'The existing billing state field should remain unchanged.' );
- $this->assertSame( $base_fields['state'], $shipping_fields['state'] ?? null, 'The existing shipping state field should remain unchanged.' );
- $this->assert_field_follows_state( $billing_fields, $billing_address_id, 'billing' );
- $this->assert_field_follows_state( $shipping_fields, $shipping_address_id, 'shipping' );
- $this->assertSame( $contact_field, array_key_last( $billing_fields ), 'Contact fields should be appended to the billing field list.' );
- $this->assertSame( $order_field, array_key_last( $shipping_fields ), 'Order fields should be appended to the shipping field list.' );
- $this->assertSame( 1, $this->count_fields_by_id( $billing_fields, $billing_address_id ), 'The billing address field should be injected exactly once.' );
- $this->assertSame( 1, $this->count_fields_by_id( $billing_fields, $contact_admin_id ), 'The contact field should be injected exactly once in billing.' );
- $this->assertSame( 0, $this->count_fields_by_id( $billing_fields, $shipping_address_id ), 'The shipping address field should not be injected in billing.' );
- $this->assertSame( 0, $this->count_fields_by_id( $billing_fields, $order_admin_id ), 'The order field should not be injected in billing.' );
- $this->assertSame( 1, $this->count_fields_by_id( $shipping_fields, $shipping_address_id ), 'The shipping address field should be injected exactly once.' );
- $this->assertSame( 1, $this->count_fields_by_id( $shipping_fields, $order_admin_id ), 'The order field should be injected exactly once in shipping.' );
- $this->assertSame( 0, $this->count_fields_by_id( $shipping_fields, $billing_address_id ), 'The billing address field should not be injected in shipping.' );
- $this->assertSame( 0, $this->count_fields_by_id( $shipping_fields, $contact_admin_id ), 'The contact field should not be injected in shipping.' );
-
- $this->assert_field_properties(
- array(
- 'id' => $billing_address_id,
- 'label' => 'Delivery note',
- 'value' => 'Reception',
- 'type' => 'text',
- 'update_callback' => array( $this->sut, 'update_callback' ),
- 'show' => true,
- 'wrapper_class' => 'form-field-wide',
- ),
- $billing_address,
- 'Billing address fields should be injected with their current value and billing-prefixed ID.'
- );
- $this->assert_field_properties(
- array(
- 'id' => $shipping_address_id,
- 'label' => 'Delivery note',
- 'value' => 'Side door',
- 'type' => 'text',
- 'update_callback' => array( $this->sut, 'update_callback' ),
- 'show' => true,
- 'wrapper_class' => 'form-field-wide',
- ),
- $shipping_address,
- 'Shipping address fields should be injected with their current value and shipping-prefixed ID.'
- );
- $this->assert_field_properties(
- array(
- 'id' => $contact_admin_id,
- 'label' => 'Preferred contact method',
- 'value' => 'email',
- 'type' => 'select',
- 'update_callback' => array( $this->sut, 'update_callback' ),
- 'show' => true,
- 'wrapper_class' => 'form-field-wide',
- 'options' => array(
- 'email' => 'Email',
- 'phone' => 'Phone',
- ),
- ),
- $billing_fields[ $contact_field ],
- 'Contact select fields should expose their labels, options, current value, and update callback.'
- );
- $this->assert_field_properties(
- array(
- 'id' => $order_admin_id,
- 'label' => 'Add gift wrap',
- 'value' => true,
- 'type' => 'checkbox',
- 'update_callback' => array( $this->sut, 'update_callback' ),
- 'show' => true,
- 'wrapper_class' => 'form-field-wide',
- 'checked_value' => '1',
- 'unchecked_value' => '0',
- ),
- $shipping_fields[ $order_field ],
- 'Order checkbox fields should expose their checked and unchecked representations.'
- );
-
- call_user_func( $billing_address['update_callback'], $billing_address['id'], 'Warehouse', $order );
- call_user_func( $shipping_address['update_callback'], $shipping_address['id'], 'Loading bay', $order );
- call_user_func( $billing_fields[ $contact_field ]['update_callback'], $billing_fields[ $contact_field ]['id'], 'phone', $order );
- call_user_func( $shipping_fields[ $order_field ]['update_callback'], $shipping_fields[ $order_field ]['id'], '0', $order );
- $order->save();
-
- $reloaded_order = wc_get_order( $order->get_id() );
- $this->assertInstanceOf( WC_Order::class, $reloaded_order, 'The updated order should reload from storage.' );
- if ( ! $reloaded_order instanceof WC_Order ) {
- throw new \RuntimeException( 'The updated order could not be reloaded from storage.' );
- }
- $this->assertSame( 'Warehouse', $this->controller->get_field_from_object( $address_field, $reloaded_order, 'billing' ), 'Billing updates should remain in the billing group.' );
- $this->assertSame( 'Loading bay', $this->controller->get_field_from_object( $address_field, $reloaded_order, 'shipping' ), 'Shipping updates should remain in the shipping group.' );
- $this->assertSame( 'phone', $this->controller->get_field_from_object( $contact_field, $reloaded_order, 'other' ), 'Contact updates should remain in the other group.' );
- $this->assertFalse( $this->controller->get_field_from_object( $order_field, $reloaded_order, 'other' ), 'Unchecked order fields should reload as false from the other group.' );
- }
-
- /**
- * Register a checkout field and track it for cleanup.
- *
- * @param array $field Field registration arguments.
- */
- private function register_checkout_field( array $field ): void {
- woocommerce_register_additional_checkout_field( $field );
- $this->registered_fields[] = $field['id'];
- }
-
- /**
- * Assert selected field properties without rejecting compatible additions.
- *
- * @param array $expected Expected field properties.
- * @param array $actual Actual field properties.
- * @param string $message Assertion context.
- */
- private function assert_field_properties( array $expected, array $actual, string $message ): void {
- foreach ( $expected as $property => $value ) {
- $this->assertArrayHasKey( $property, $actual, sprintf( '%s Missing property: %s.', $message, $property ) );
- $this->assertSame( $value, $actual[ $property ], sprintf( '%s Unexpected property: %s.', $message, $property ) );
- }
- }
-
- /**
- * Assert that an injected address field immediately follows the state field.
- *
- * @param array $fields Admin field definitions.
- * @param string $field_id Generated address field ID.
- * @param string $group Address group name.
- */
- private function assert_field_follows_state( array $fields, string $field_id, string $group ): void {
- $field_keys = array_keys( $fields );
- $field_values = array_values( $fields );
- $state_index = array_search( 'state', $field_keys, true );
-
- $this->assertNotFalse( $state_index, sprintf( 'The %s state field should remain in the field list.', $group ) );
- $this->assertSame(
- $field_id,
- $field_values[ false === $state_index ? 0 : $state_index + 1 ]['id'] ?? null,
- sprintf( 'The %s address field should be injected immediately after state.', $group )
- );
- }
-
- /**
- * Count formatted admin fields with a generated ID.
- *
- * @param array $fields Admin field definitions.
- * @param string $field_id Generated field ID.
- */
- private function count_fields_by_id( array $fields, string $field_id ): int {
- return count(
- array_filter(
- $fields,
- static function ( array $field ) use ( $field_id ): bool {
- return ( $field['id'] ?? '' ) === $field_id;
- }
- )
- );
- }
-
- /**
- * Find a formatted admin field by its generated ID.
- *
- * @param array $fields Admin field definitions.
- * @param string $field_id Generated field ID.
- * @return array
- */
- private function find_field_by_id( array $fields, string $field_id ): array {
- foreach ( $fields as $field ) {
- if ( ( $field['id'] ?? '' ) === $field_id ) {
- return $field;
- }
- }
-
- $this->fail( sprintf( 'Expected to find admin field with ID %s.', $field_id ) );
- return array();
- }
-}
diff --git a/plugins/woocommerce/tests/php/src/Blocks/StoreApi/Routes/AdditionalFields.php b/plugins/woocommerce/tests/php/src/Blocks/StoreApi/Routes/AdditionalFields.php
index e767ec3f370..1257a1e2122 100644
--- a/plugins/woocommerce/tests/php/src/Blocks/StoreApi/Routes/AdditionalFields.php
+++ b/plugins/woocommerce/tests/php/src/Blocks/StoreApi/Routes/AdditionalFields.php
@@ -2400,98 +2400,6 @@ class AdditionalFields extends \WP_Test_REST_TestCase {
$this->assertEquals( 'shipping-saved-gov-id', ( (array) $data['shipping_address'] )['plugin-namespace/gov-id'], print_r( $data, true ) );
}
- /**
- * Ensures an additional field is sanitized before it is validated, and the sanitized value is what gets stored.
- */
- public function test_additional_field_is_sanitized_before_it_is_validated() {
- $id = 'plugin-namespace/upper-code';
-
- // Uppercasing is the point: no generic sanitizer in the request pipeline does it,
- // so a lowercase value can only reach a passing validation through this field's
- // own `sanitize_callback`. Trimming would not work here, because the address
- // sanitizers already trim whatever the field callback is given.
- \woocommerce_register_additional_checkout_field(
- array(
- 'id' => $id,
- 'label' => 'Upper code',
- 'location' => 'order',
- 'type' => 'text',
- 'required' => true,
- 'sanitize_callback' => function ( $value ) {
- return strtoupper( $value );
- },
- 'validate_callback' => function ( $value ) {
- return ctype_upper( $value );
- },
- )
- );
-
- // Still invalid after uppercasing, so a rejection here is the field's rule doing
- // its job rather than the ordering.
- $response = $this->checkout_with_upper_code( $id, 'ab1' );
- $data = $response->get_data();
-
- $this->assertEquals( 400, $response->get_status(), print_r( $data, true ) );
-
- // Invalid as submitted and valid once uppercased.
- $response = $this->checkout_with_upper_code( $id, 'abc' );
- $data = $response->get_data();
-
- $this->assertEquals( 200, $response->get_status(), print_r( $data, true ) );
- $this->assertEquals( 'ABC', ( (array) $data['additional_fields'] )[ $id ], print_r( $data, true ) );
- }
-
- /**
- * Dispatch a checkout request carrying one value for a registered order-location field.
- *
- * @param string $id Field ID.
- * @param string $value Value to submit.
- * @return \WP_REST_Response
- */
- private function checkout_with_upper_code( string $id, string $value ) {
- $request = new \WP_REST_Request( 'POST', '/wc/store/v1/checkout' );
- $request->set_header( 'Nonce', wp_create_nonce( 'wc_store_api' ) );
- $request->set_body_params(
- array(
- 'billing_address' => (object) array(
- 'first_name' => 'test',
- 'last_name' => 'test',
- 'company' => '',
- 'address_1' => 'test',
- 'address_2' => '',
- 'city' => 'test',
- 'state' => '',
- 'postcode' => 'cb241ab',
- 'country' => 'GB',
- 'phone' => '',
- 'email' => 'testaccount@test.com',
- 'plugin-namespace/gov-id' => 'gov id',
- ),
- 'shipping_address' => (object) array(
- 'first_name' => 'test',
- 'last_name' => 'test',
- 'company' => '',
- 'address_1' => 'test',
- 'address_2' => '',
- 'city' => 'test',
- 'state' => '',
- 'postcode' => 'cb241ab',
- 'country' => 'GB',
- 'phone' => '',
- 'plugin-namespace/gov-id' => 'gov id',
- ),
- 'payment_method' => WC_Gateway_BACS::ID,
- 'additional_fields' => array(
- 'plugin-namespace/job-function' => 'engineering',
- 'plugin-namespace/leave-on-porch' => true,
- $id => $value,
- ),
- )
- );
-
- return rest_get_server()->dispatch( $request );
- }
-
/**
* Ensures that saved values are returned in the checkout response.
*/