[Experimental] Add E2E tests for Additional Checkout Fields (#43836)

* Add additional-checkout-fields-test-helper wp-env plugin

* Ensure Checkout page waits for email to be visible before filling

* Handle additional fields in checkout page utils

* Add test for filling additional checkout fields

* Add additional check for gov-id validation

* Close context used for plugin-checking

* Update types for additional fields to be key/value objects

* Check billing gov-id is different to shipping one

* Move additional fields plugin to inline with checkout tests

* Use additional fields plugin from local dir

* Await email input to check Checkout block is loaded

* Add test to verify error message shows when leaving a field blank

* Update comment

* Add test for checking/unchecking checkboxes

* Update check in additional field test plugin

* Change fields multiple times in one test

* Add server-side validation tests

* Get exact matches for checkout form fields

* Remove unnecessarily translated strings

* update hook in test plugin to compare to confirmation gov id

* Fill in gov ID confirmation field

* Register a field of each type in each location

* Don't validate field unless both are filled

* Fill additional fields and check their values

* Fill additional fields

* Check select values in order confirmation

* Change the values of all field types multiple times

* Make checkout wait until not calculating before submitting

* Update tests to use all additional field types

* Blur after editing select box

* Add customer area checks

* Check shipping isn't calculating before submitting

* Add merchant-side tests for additional checkout fields

* Ensure customer data is done updating before submitting

* use waitForFunction to check for updating shipping

* Add changelog

* Ensure fields are blurred before pressing button

* Update validation error check

* Add test for logged out shopper

* make specific function to wait for checkout to be finished updating

* Add test to ensure fields are saved across orders

* Add sanitize and validate callbacks to gov id fields

* Make purchase type select field required

* Add sanitization test

* Use experimental function in test plugin

* Add standalone sanitization filter test

* Update testing plugin to use new and renamed filters

* Fix typo

* Move empty value test to logged out shopper block and check empty val

* Update tests to include tests for new validation/sanitization filters

* Add verifyAdditionalFieldsDetails function to checkout page

* Use new verifyAdditionalFieldsDetails function to improve readability

* Update second test to use new function to improve readability

* Update third test to improve readability

* Update fourth function to improve readability

* Update fourth test to improve readability

* Update guest shopper test to be more readable

* Update helper function to only return value not assert

* Remove check causing test to fail in CI

* Make check for guest shopper same as logged in

* Move guest shopper tests to their own file

* Ensure unchecking checkbox works OK
This commit is contained in:
Thomas Roberts 2024-03-14 12:39:30 +00:00 committed by GitHub
parent a7383579be
commit 5cbb0ad5bf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 2105 additions and 6 deletions

View File

@ -0,0 +1,243 @@
<?php
/**
* Plugin Name: Additional checkout fields plugin
* Description: Add additional checkout fields
* @package WordPress
*/
class Additional_Checkout_Fields_Test_Helper {
/**
* Constructor.
*/
public function __construct() {
add_action( 'plugins_loaded', array( $this, 'enable_custom_checkout_fields' ) );
add_action( 'plugins_loaded', array( $this, 'disable_custom_checkout_fields' ) );
add_action( 'woocommerce_loaded', array( $this, 'register_custom_checkout_fields' ) );
}
/**
* @var string Define option name to decide if additional fields should be turned on.
*/
private $additional_checkout_fields_option_name = 'woocommerce_additional_checkout_fields';
/**
* Define URL endpoint for enabling additional checkout fields.
*/
public function enable_custom_checkout_fields() {
// phpcs:disable WordPress.Security.NonceVerification.Recommended
if ( isset( $_GET['enable_custom_checkout_fields'] ) ) {
update_option( $this->additional_checkout_fields_option_name, 'yes' );
echo 'Enabled custom checkout fields';
}
}
/**
* Define URL endpoint for disabling additional checkout fields.
*/
public function disable_custom_checkout_fields() {
// phpcs:disable WordPress.Security.NonceVerification.Recommended
if ( isset( $_GET['disable_custom_checkout_fields'] ) ) {
update_option( $this->additional_checkout_fields_option_name, 'no' );
echo 'Disabled custom checkout fields';
}
}
/**
* Registers custom checkout fields for the WooCommerce checkout form.
*
* @return void
* @throws Exception If there is an error during the registration of the checkout fields.
*/
public function register_custom_checkout_fields() {
// Address fields, checkbox, textbox, select
__experimental_woocommerce_blocks_register_checkout_field(
array(
'id' => 'first-plugin-namespace/government-ID',
'label' => 'Government ID',
'location' => 'address',
'type' => 'text',
'required' => true,
'sanitize_callback' => function( $field_value ) {
return str_replace( ' ', '', $field_value );
},
'validate_callback' => function( $field_value ) {
$match = preg_match( '/^[0-9]{5}$/', $field_value );
if ( 0 === $match || false === $match ) {
return new \WP_Error( 'invalid_government_id', 'Invalid government ID.' );
}
},
),
);
__experimental_woocommerce_blocks_register_checkout_field(
array(
'id' => 'first-plugin-namespace/confirm-government-ID',
'label' => 'Confirm government ID',
'location' => 'address',
'type' => 'text',
'required' => true,
'sanitize_callback' => function( $field_value ) {
return str_replace( ' ', '', $field_value );
},
'validate_callback' => function( $field_value ) {
$match = preg_match( '/^[0-9]{5}$/', $field_value );
if ( 0 === $match || false === $match ) {
return new \WP_Error( 'invalid_government_id', 'Invalid government ID.' );
}
},
),
);
__experimental_woocommerce_blocks_register_checkout_field(
array(
'id' => 'first-plugin-namespace/truck-size-ok',
'label' => 'Can a truck fit down your road?',
'location' => 'address',
'type' => 'checkbox',
)
);
__experimental_woocommerce_blocks_register_checkout_field(
array(
'id' => 'first-plugin-namespace/road-size',
'label' => 'How wide is your road?',
'location' => 'address',
'type' => 'select',
'options' => array(
array(
'label' => 'Wide',
'value' => 'wide',
),
array(
'label' => 'Super wide',
'value' => 'super-wide',
),
array(
'label' => 'Narrow',
'value' => 'narrow',
),
),
)
);
// Fake sanitization function that removes full stops from the Government ID string.
add_filter(
'__experimental_woocommerce_blocks_sanitize_additional_field',
function ( $field_value, $field_key ) {
if ( 'first-plugin-namespace/government-ID' === $field_key ) {
$field_value = str_replace( '.', '', $field_value );
}
return $field_value;
},
10,
2
);
add_action(
'__experimental_woocommerce_blocks_validate_additional_field',
function ( WP_Error $errors, $field_key, $field_value ) {
if ( 'first-plugin-namespace/government-ID' === $field_key || 'first-plugin-namespace/confirm-government-ID' === $field_key ) {
$match = preg_match( '/[A-Z0-9]{5}/', $field_value );
if ( 0 === $match || false === $match ) {
$errors->add( 'invalid_gov_id', 'Please ensure your government ID matches the correct format.' );
}
}
},
10,
4
);
add_action(
'__experimental_woocommerce_blocks_validate_location_address_fields',
function ( \WP_Error $errors, $fields, $group ) {
if ( $fields['first-plugin-namespace/government-ID'] !== $fields['first-plugin-namespace/confirm-government-ID'] ) {
$errors->add( 'gov_id_mismatch', 'Please ensure your government ID matches the confirmation.' );
}
},
10,
3
);
// Contact fields, one checkbox, select, and text input.
__experimental_woocommerce_blocks_register_checkout_field(
array(
'id' => 'second-plugin-namespace/marketing-opt-in',
'label' => 'Do you want to subscribe to our newsletter?',
'location' => 'contact',
'type' => 'checkbox',
)
);
__experimental_woocommerce_blocks_register_checkout_field(
array(
'id' => 'second-plugin-namespace/gift-message-in-package',
'label' => 'Enter a gift message to include in the package',
'location' => 'contact',
'type' => 'text',
)
);
__experimental_woocommerce_blocks_register_checkout_field(
array(
'id' => 'second-plugin-namespace/type-of-purchase',
'label' => 'Is this a personal purchase or a business purchase?',
'location' => 'contact',
'required' => true,
'type' => 'select',
'options' => [
[
'label' => 'Personal',
'value' => 'personal',
],
[
'label' => 'Business',
'value' => 'business',
],
],
)
);
// A field of each type in additional information section.
__experimental_woocommerce_blocks_register_checkout_field(
array(
'id' => 'third-plugin-namespace/please-send-me-a-free-gift',
'label' => 'Would you like a free gift with your order?',
'location' => 'additional',
'type' => 'checkbox',
)
);
__experimental_woocommerce_blocks_register_checkout_field(
array(
'id' => 'third-plugin-namespace/what-is-your-favourite-colour',
'label' => 'What is your favourite colour?',
'location' => 'additional',
'type' => 'text',
)
);
__experimental_woocommerce_blocks_register_checkout_field(
array(
'id' => 'third-plugin-namespace/how-did-you-hear-about-us',
'label' => 'How did you hear about us?',
'location' => 'additional',
'type' => 'select',
'options' => array(
array(
'value' => 'google',
'label' => 'Google',
),
array(
'value' => 'facebook',
'label' => 'Facebook',
),
array(
'value' => 'friend',
'label' => 'From a friend',
),
array(
'value' => 'other',
'label' => 'Other',
),
),
)
);
}
}
new Additional_Checkout_Fields_Test_Helper();

View File

@ -0,0 +1,302 @@
/**
* External dependencies
*/
import { expect, test as base } from '@woocommerce/e2e-playwright-utils';
import { guestFile } from '@woocommerce/e2e-utils';
import {
installPluginFromPHPFile,
uninstallPluginFromPHPFile,
} from '@woocommerce/e2e-mocks/custom-plugins';
/**
* Internal dependencies
*/
import { REGULAR_PRICED_PRODUCT_NAME } from './constants';
import { CheckoutPage } from './checkout.page';
const test = base.extend< { checkoutPageObject: CheckoutPage } >( {
checkoutPageObject: async ( { page }, use ) => {
const pageObject = new CheckoutPage( {
page,
} );
await use( pageObject );
},
} );
test.describe( 'Shopper → Additional Checkout Fields', () => {
test.describe( 'Guest shopper', () => {
test.use( { storageState: guestFile } );
test.beforeAll( async () => {
await installPluginFromPHPFile(
`${ __dirname }/additional-checkout-fields-plugin.php`
);
} );
test.afterAll( async () => {
await uninstallPluginFromPHPFile(
`${ __dirname }/additional-checkout-fields-plugin.php`
);
} );
test.beforeEach( async ( { frontendUtils } ) => {
await frontendUtils.emptyCart();
await frontendUtils.goToShop();
await frontendUtils.addToCart( REGULAR_PRICED_PRODUCT_NAME );
await frontendUtils.goToCheckout();
} );
test( 'Shopper can see an error message when a required field is not filled in the checkout form', async ( {
checkoutPageObject,
} ) => {
await checkoutPageObject.editShippingDetails();
await checkoutPageObject.unsyncBillingWithShipping();
await checkoutPageObject.editBillingDetails();
await checkoutPageObject.fillInCheckoutWithTestData(
{},
{
contact: {
'Enter a gift message to include in the package':
'This is for you!',
},
address: {
shipping: {
'Government ID': '',
'Confirm government ID': '',
},
billing: {
'Government ID': '54321',
'Confirm government ID': '54321',
},
},
additional: {
'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 );
await expect(
checkoutPageObject.page.getByText(
'Please enter a valid government id'
)
).toBeVisible();
await expect(
checkoutPageObject.page.getByText(
'Please select a valid option'
)
).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,
} ) => {
await checkoutPageObject.unsyncBillingWithShipping();
await checkoutPageObject.fillInCheckoutWithTestData(
{},
{
contact: {
'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',
},
billing: {
'Government ID': '54321',
'Confirm government ID': '54321',
},
},
additional: {
'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?' )
.fill( 'wide' );
await checkoutPageObject.page
.getByRole( 'group', {
name: 'Billing address',
} )
.getByLabel( 'How wide is your road?' )
.fill( '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?' )
.check();
await checkoutPageObject.page.waitForRequest( ( req ) => {
return req.url().includes( 'batch' );
} );
await checkoutPageObject.page
.getByRole( 'group', {
name: 'Billing address',
} )
.getByLabel( 'Can a truck fit down your road?' )
.uncheck();
await checkoutPageObject.page.waitForRequest( ( req ) => {
return req.url().includes( 'batch' );
} );
await checkoutPageObject.waitForCheckoutToFinishUpdating();
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?' )
).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?' )
).toHaveValue( 'Narrow' );
} );
} );
} );

View File

@ -0,0 +1,411 @@
/**
* External dependencies
*/
import { expect, test as base } from '@woocommerce/e2e-playwright-utils';
import { adminFile } from '@woocommerce/e2e-utils';
import {
installPluginFromPHPFile,
uninstallPluginFromPHPFile,
} from '@woocommerce/e2e-mocks/custom-plugins';
/**
* Internal dependencies
*/
import { REGULAR_PRICED_PRODUCT_NAME } from './constants';
import { CheckoutPage } from './checkout.page';
const test = base.extend< { checkoutPageObject: CheckoutPage } >( {
checkoutPageObject: async ( { page }, use ) => {
const pageObject = new CheckoutPage( {
page,
} );
await use( pageObject );
},
} );
test.describe( 'Merchant → Additional Checkout Fields', () => {
test.use( { storageState: adminFile } );
test.beforeAll( async () => {
await installPluginFromPHPFile(
`${ __dirname }/additional-checkout-fields-plugin.php`
);
} );
test.afterAll( async () => {
await uninstallPluginFromPHPFile(
`${ __dirname }/additional-checkout-fields-plugin.php`
);
} );
test.beforeEach( async ( { frontendUtils } ) => {
await frontendUtils.emptyCart();
await frontendUtils.goToShop();
await frontendUtils.addToCart( REGULAR_PRICED_PRODUCT_NAME );
await frontendUtils.goToCheckout();
} );
test( 'Merchant can see additional fields in the order admin page', async ( {
checkoutPageObject,
admin,
} ) => {
await checkoutPageObject.editShippingDetails();
await checkoutPageObject.unsyncBillingWithShipping();
await checkoutPageObject.editBillingDetails();
await checkoutPageObject.fillInCheckoutWithTestData(
{},
{
contact: {
'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',
},
billing: {
'Government ID': '54321',
'Confirm government ID': '54321',
},
},
additional: {
'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?' )
.fill( 'wide' );
await checkoutPageObject.page
.getByRole( 'group', {
name: 'Billing address',
} )
.getByLabel( 'How wide is your road?' )
.fill( '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.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: {
'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',
},
billing: {
'Government ID': '54321',
'Confirm government ID': '54321',
},
},
additional: {
'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?' )
.fill( 'wide' );
await checkoutPageObject.page
.getByRole( 'group', {
name: 'Billing address',
} )
.getByLabel( 'How wide is your road?' )
.fill( '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.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="\\/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' );
await admin.page
.getByRole( 'button', { name: 'Update' } )
.first()
.click();
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="\\/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();
} );
} );

View File

@ -52,7 +52,21 @@ export class CheckoutPage {
return nameIsVisible && priceIsVisible;
}
async fillInCheckoutWithTestData( overrideData = {} ) {
async fillInCheckoutWithTestData(
overrideData = {},
additionalFields: {
address?: {
shipping?: Record< string, string >;
billing?: Record< string, string >;
};
contact?: Record< string, string >;
additional?: Record< string, string >;
} = {
address: { shipping: {}, billing: {} },
additional: {},
contact: {},
}
) {
const isShippingOpen = await this.page
.getByRole( 'group', {
name: 'Shipping address',
@ -67,17 +81,100 @@ export class CheckoutPage {
const testData = { ...this.testData, ...overrideData };
await this.page.getByLabel( 'Email address' ).fill( testData.email );
await this.fillContactInformation(
testData.email,
additionalFields.contact || {}
);
if ( isShippingOpen ) {
await this.fillShippingDetails( testData );
await this.fillShippingDetails(
testData,
additionalFields.address?.shipping || {}
);
}
if ( isBillingOpen ) {
await this.fillBillingDetails( testData );
// Additional billing details
await this.fillBillingDetails( testData, {
...( additionalFields.address?.shipping || {} ),
...( additionalFields.address?.billing || {} ),
} );
}
if (
typeof additionalFields.additional !== 'undefined' &&
Object.keys( additionalFields.additional ).length > 0
) {
await this.fillAdditionalInformationSection(
additionalFields.additional
);
}
// Blur active field to trigger shipping rates update, then wait for requests to finish.
await this.page.evaluate( 'document.activeElement.blur()' );
}
async fillContactInformation(
email: string,
additionalFields: Record< string, string >
) {
const contactSection = this.page.getByRole( 'group', {
name: 'Contact information',
} );
await contactSection.getByLabel( 'Email address' ).fill( email );
// Rest of additional data passed in from the overrideData object.
for ( const [ label, value ] of Object.entries( additionalFields ) ) {
const field = contactSection.getByLabel( label );
await field.fill( value );
}
}
async fillAdditionalInformationSection(
additionalFields: Record< string, string >
) {
const contactSection = this.page.getByRole( 'group', {
name: 'Additional order information',
} );
// Rest of additional data passed in from the overrideData object.
for ( const [ label, value ] of Object.entries( additionalFields ) ) {
const field = contactSection.getByLabel( label );
await field.fill( value );
}
}
async fillAdditionalInformation(
email: string,
additionalFields: { label: string; value: string }[]
) {
await this.page.getByLabel( 'Email address' ).fill( email );
// Rest of additional data passed in from the overrideData object.
for ( const { label, value } of additionalFields ) {
const field = this.page.getByLabel( label );
await field.fill( value );
}
}
/**
* Blurs the current input and waits for the checkout to finish any loading or calculating.
*/
async waitForCheckoutToFinishUpdating() {
await this.page.evaluate( 'document.activeElement.blur()' );
await this.page.waitForFunction( () => {
return (
! window.wp.data
.select( 'wc/store/checkout' )
.isCalculating() &&
! window.wp.data
.select( 'wc/store/cart' )
.isShippingRateBeingSelected() &&
! window.wp.data
.select( 'wc/store/cart' )
.isCustomerDataUpdating()
);
} );
}
/**
* Place order and wait for redirect to order received page.
*
@ -85,12 +182,32 @@ export class CheckoutPage {
* when testing for errors on the checkout page.
*/
async placeOrder( waitForRedirect = true ) {
await this.waitForCheckoutToFinishUpdating();
await this.page.getByText( 'Place Order', { exact: true } ).click();
if ( waitForRedirect ) {
await this.page.waitForURL( /order-received/ );
}
}
/**
* Verifies that the additional field values are visible on the confirmation page.
*/
async verifyAdditionalFieldsDetails( values: [ string, string ][] ) {
for ( const [ label, value ] of values ) {
const visible = await this.page
.getByText(
`${ label }${ value }` // No space between these due to the way the markup is output on the confirmation page.
)
.isVisible();
if ( ! visible ) {
return false;
}
}
// If one of the fields above is false the function would have returned early.
return true;
}
async verifyAddressDetails(
shippingOrBilling: 'shipping' | 'billing',
overrideAddressDetails = {}
@ -161,7 +278,10 @@ export class CheckoutPage {
}
}
async fillBillingDetails( customerBillingDetails ) {
async fillBillingDetails(
customerBillingDetails: Record< string, string >,
additionalFields: Record< string, string > = {}
) {
await this.editBillingDetails();
const billingForm = this.page.getByRole( 'group', {
name: 'Billing address',
@ -210,11 +330,21 @@ export class CheckoutPage {
if ( await postcode.isVisible() ) {
await postcode.fill( customerBillingDetails.postcode );
}
// Rest of additional data passed in from the overrideData object.
for ( const [ label, value ] of Object.entries( additionalFields ) ) {
const field = billingForm.getByLabel( label, { exact: true } );
await field.fill( value );
}
// Blur active field to trigger customer address update.
await this.page.evaluate( 'document.activeElement.blur()' );
}
async fillShippingDetails( customerShippingDetails ) {
async fillShippingDetails(
customerShippingDetails: Record< string, string >,
additionalFields: Record< string, string > = {}
) {
await this.editShippingDetails();
const shippingForm = this.page.getByRole( 'group', {
name: 'Shipping address',
@ -262,6 +392,12 @@ export class CheckoutPage {
await postcode.fill( customerShippingDetails.postcode );
}
// Rest of additional data passed in from the overrideData object.
for ( const [ label, value ] of Object.entries( additionalFields ) ) {
const field = shippingForm.getByLabel( label, { exact: true } );
await field.fill( value );
}
// Blur active field to trigger customer address update.
await this.page.evaluate( 'document.activeElement.blur()' );
}

View File

@ -48,6 +48,8 @@ export class FrontendUtils {
await this.page.goto( '/checkout', {
waitUntil: 'domcontentloaded',
} );
await this.page.waitForSelector( '#email' );
}
async goToCart() {

View File

@ -0,0 +1,5 @@
Significance: patch
Type: update
Comment: This only adds E2E tests and the feature is behind a feature flag too