From dd053d618d7f9dfae54b674503185b4fa6fa2e8e Mon Sep 17 00:00:00 2001 From: Mike Jolley Date: Fri, 28 Oct 2022 13:41:28 +0100 Subject: [PATCH] Fix/flakey checkout tests (https://github.com/woocommerce/woocommerce-blocks/pull/7508) * Update installed to setup crosssells for all matching products in case of duplicates * Improve shopper utils to wait for page elements to render * Taxes should wait for totals wrapper * Use shopper.block.goToShop() * Inline docs * Wait for errors * partial match please fill error --- .../shopper/cart-checkout/account.test.js | 3 +- .../specs/shopper/cart-checkout/cart.test.js | 10 ++--- .../shopper/cart-checkout/checkout.test.js | 45 ++++++++++--------- .../specs/shopper/cart-checkout/tax.test.js | 4 +- .../mocks/woo-test-helper/woo-test-helper.php | 12 ++--- .../woocommerce-blocks/tests/utils/shopper.js | 25 ++++++++--- .../woocommerce-blocks/tests/utils/taxes.ts | 3 ++ 7 files changed, 63 insertions(+), 39 deletions(-) diff --git a/plugins/woocommerce-blocks/tests/e2e/specs/shopper/cart-checkout/account.test.js b/plugins/woocommerce-blocks/tests/e2e/specs/shopper/cart-checkout/account.test.js index 14ee93b0a1f..29bf00dfb53 100644 --- a/plugins/woocommerce-blocks/tests/e2e/specs/shopper/cart-checkout/account.test.js +++ b/plugins/woocommerce-blocks/tests/e2e/specs/shopper/cart-checkout/account.test.js @@ -58,7 +58,7 @@ describe( 'Shopper → Checkout → Account', () => { beforeEach( async () => { await shopper.block.emptyCart(); - await shopper.goToShop(); + await shopper.block.goToShop(); await shopper.addToCartFromShopPage( SIMPLE_PHYSICAL_PRODUCT_NAME ); await shopper.block.goToCheckout(); } ); @@ -77,6 +77,7 @@ describe( 'Shopper → Checkout → Account', () => { } ); it( 'user can can create an account', async () => { + await page.waitForSelector( '.wc-block-checkout__create-account' ); await expect( page ).toClick( 'span', { text: 'Create an account?', } ); diff --git a/plugins/woocommerce-blocks/tests/e2e/specs/shopper/cart-checkout/cart.test.js b/plugins/woocommerce-blocks/tests/e2e/specs/shopper/cart-checkout/cart.test.js index ba1d4a4b906..3ac00fad279 100644 --- a/plugins/woocommerce-blocks/tests/e2e/specs/shopper/cart-checkout/cart.test.js +++ b/plugins/woocommerce-blocks/tests/e2e/specs/shopper/cart-checkout/cart.test.js @@ -41,7 +41,7 @@ describe( 'Shopper → Cart', () => { } ); it( 'User can remove a product from cart', async () => { - await shopper.goToShop(); + await shopper.block.goToShop(); await shopper.addToCartFromShopPage( SIMPLE_VIRTUAL_PRODUCT_NAME ); await shopper.block.goToCart(); const removeProductLink = await page.$( @@ -59,7 +59,7 @@ describe( 'Shopper → Cart', () => { } ); it( 'User can update product quantity via the input field', async () => { - await shopper.goToShop(); + await shopper.block.goToShop(); await shopper.addToCartFromShopPage( SIMPLE_VIRTUAL_PRODUCT_NAME ); await shopper.block.goToCart(); await shopper.block.setCartQuantity( SIMPLE_VIRTUAL_PRODUCT_NAME, 4 ); @@ -107,10 +107,10 @@ describe( 'Shopper → Cart', () => { it( 'User can see Cross-Sells products block', async () => { await shopper.block.emptyCart(); - await shopper.goToShop(); + await shopper.block.goToShop(); await shopper.addToCartFromShopPage( SIMPLE_PHYSICAL_PRODUCT_NAME ); await shopper.block.goToCart(); - await expect( page ).toMatchElement( + await page.waitForSelector( '.wp-block-woocommerce-cart-cross-sells-block' ); await shopper.block.addCrossSellsProductToCart(); @@ -122,7 +122,7 @@ describe( 'Shopper → Cart', () => { } ); it( 'User can proceed to checkout', async () => { - await shopper.goToShop(); + await shopper.block.goToShop(); await shopper.addToCartFromShopPage( SIMPLE_VIRTUAL_PRODUCT_NAME ); await shopper.block.goToCart(); diff --git a/plugins/woocommerce-blocks/tests/e2e/specs/shopper/cart-checkout/checkout.test.js b/plugins/woocommerce-blocks/tests/e2e/specs/shopper/cart-checkout/checkout.test.js index e29292b846d..9231bd9a673 100644 --- a/plugins/woocommerce-blocks/tests/e2e/specs/shopper/cart-checkout/checkout.test.js +++ b/plugins/woocommerce-blocks/tests/e2e/specs/shopper/cart-checkout/checkout.test.js @@ -47,7 +47,7 @@ describe( 'Shopper → Checkout', () => { describe( 'Payment Methods', () => { it( 'User can change payment methods', async () => { await shopper.block.emptyCart(); - await shopper.goToShop(); + await shopper.block.goToShop(); await shopper.addToCartFromShopPage( SIMPLE_PHYSICAL_PRODUCT_NAME ); await shopper.block.goToCheckout(); await expect( page ).toClick( @@ -104,7 +104,7 @@ describe( 'Shopper → Checkout', () => { // eslint-disable-next-line jest/expect-expect it( 'User can have different shipping and billing addresses', async () => { - await shopper.goToShop(); + await shopper.block.goToShop(); await shopper.addToCartFromShopPage( SIMPLE_PHYSICAL_PRODUCT_NAME ); await shopper.block.goToCheckout(); await page.waitForSelector( '#checkbox-control-0' ); @@ -132,18 +132,21 @@ describe( 'Shopper → Checkout', () => { } ); it( 'User can see errors when form is incomplete', async () => { - await shopper.goToShop(); + await shopper.block.goToShop(); await shopper.addToCartFromShopPage( SIMPLE_VIRTUAL_PRODUCT_NAME ); await shopper.block.goToCheckout(); - // Click on "Place Order" button - await expect( page ).toClick( - '.wc-block-components-checkout-place-order-button', - { - text: 'Place Order', - } + // Wait for the "Place Order" button to avoid flakey tests. + await page.waitForSelector( + '.wc-block-components-checkout-place-order-button:not([disabled])' ); + // Click on "Place Order" button + await expect( page ).toClick( + '.wc-block-components-checkout-place-order-button' + ); + + // Wait for the error messages to appear await page.waitForSelector( '.wc-block-components-validation-error' ); @@ -152,37 +155,37 @@ describe( 'Shopper → Checkout', () => { await expect( page ).toMatchElement( '#email ~ .wc-block-components-validation-error p', { - text: 'Please fill out this field.', + text: 'Please fill', } ); await expect( page ).toMatchElement( '#billing-first_name ~ .wc-block-components-validation-error p', { - text: 'Please fill out this field.', + text: 'Please fill', } ); await expect( page ).toMatchElement( '#billing-last_name ~ .wc-block-components-validation-error p', { - text: 'Please fill out this field.', + text: 'Please fill', } ); await expect( page ).toMatchElement( '#billing-address_1 ~ .wc-block-components-validation-error p', { - text: 'Please fill out this field.', + text: 'Please fill', } ); await expect( page ).toMatchElement( '#billing-city ~ .wc-block-components-validation-error p', { - text: 'Please fill out this field.', + text: 'Please fill', } ); await expect( page ).toMatchElement( '#billing-postcode ~ .wc-block-components-validation-error p', { - text: 'Please fill out this field.', + text: 'Please fill', } ); } ); @@ -193,7 +196,7 @@ describe( 'Shopper → Checkout', () => { if ( await shopper.isLoggedIn() ) { await shopper.logout(); } - await shopper.goToShop(); + await shopper.block.goToShop(); await shopper.addToCartFromShopPage( SIMPLE_VIRTUAL_PRODUCT_NAME ); await shopper.block.goToCheckout(); await shopper.block.fillBillingDetails( BILLING_DETAILS ); @@ -203,7 +206,7 @@ describe( 'Shopper → Checkout', () => { it( 'Logged in user can place an order', async () => { await shopper.login(); - await shopper.goToShop(); + await shopper.block.goToShop(); await shopper.addToCartFromShopPage( SIMPLE_VIRTUAL_PRODUCT_NAME ); await shopper.block.goToCheckout(); await shopper.block.fillBillingDetails( BILLING_DETAILS ); @@ -220,7 +223,7 @@ describe( 'Shopper → Checkout', () => { const NORMAL_SHIPPING_PRICE = '$20.00'; it( 'User can choose free shipping', async () => { - await shopper.goToShop(); + await shopper.block.goToShop(); await shopper.addToCartFromShopPage( SIMPLE_PHYSICAL_PRODUCT_NAME ); await shopper.block.goToCheckout(); await shopper.block.selectAndVerifyShippingOption( @@ -235,7 +238,7 @@ describe( 'Shopper → Checkout', () => { } ); it( 'User can choose flat rate shipping', async () => { - await shopper.goToShop(); + await shopper.block.goToShop(); await shopper.addToCartFromShopPage( SIMPLE_PHYSICAL_PRODUCT_NAME ); await shopper.block.goToCheckout(); await shopper.block.selectAndVerifyShippingOption( @@ -262,7 +265,7 @@ describe( 'Shopper → Checkout', () => { } ); it( 'Logged in user can apply single-use coupon and place order', async () => { - await shopper.goToShop(); + await shopper.block.goToShop(); await shopper.addToCartFromShopPage( SIMPLE_VIRTUAL_PRODUCT_NAME ); await shopper.block.goToCheckout(); await shopper.block.applyCouponFromCheckout( coupon.code ); @@ -303,7 +306,7 @@ describe( 'Shopper → Checkout', () => { } ); it( 'Logged in user cannot apply single-use coupon twice', async () => { - await shopper.goToShop(); + await shopper.block.goToShop(); await shopper.addToCartFromShopPage( SIMPLE_VIRTUAL_PRODUCT_NAME ); await shopper.block.goToCheckout(); await shopper.block.applyCouponFromCheckout( coupon.code ); diff --git a/plugins/woocommerce-blocks/tests/e2e/specs/shopper/cart-checkout/tax.test.js b/plugins/woocommerce-blocks/tests/e2e/specs/shopper/cart-checkout/tax.test.js index 76ade05e704..8c9eacaa685 100644 --- a/plugins/woocommerce-blocks/tests/e2e/specs/shopper/cart-checkout/tax.test.js +++ b/plugins/woocommerce-blocks/tests/e2e/specs/shopper/cart-checkout/tax.test.js @@ -30,7 +30,7 @@ describe( 'Shopper → Cart & Checkout → Taxes', () => { describe( '"Enable tax rate calculations" is unchecked in WC settings -> general', () => { it( 'User cannot view the tax on Cart, Checkout & Order Summary', async () => { await showTaxes( false ); - await shopper.goToShop(); + await shopper.block.goToShop(); await shopper.addToCartFromShopPage( SIMPLE_VIRTUAL_PRODUCT_NAME ); await shopper.block.goToCart(); @@ -54,7 +54,7 @@ describe( 'Shopper → Cart & Checkout → Taxes', () => { describe( '"Enable tax rate calculations" is checked in WC settings -> general', () => { it( 'User can view the tax on Cart, Checkout & Order Summary', async () => { await showTaxes( true ); - await shopper.goToShop(); + await shopper.block.goToShop(); await shopper.addToCartFromShopPage( SIMPLE_VIRTUAL_PRODUCT_NAME ); await shopper.block.goToCart(); diff --git a/plugins/woocommerce-blocks/tests/mocks/woo-test-helper/woo-test-helper.php b/plugins/woocommerce-blocks/tests/mocks/woo-test-helper/woo-test-helper.php index 4e689448df2..491b7f4e0d4 100644 --- a/plugins/woocommerce-blocks/tests/mocks/woo-test-helper/woo-test-helper.php +++ b/plugins/woocommerce-blocks/tests/mocks/woo-test-helper/woo-test-helper.php @@ -114,12 +114,14 @@ function setup_cross_sells() { global $wpdb; // phpcs:disable WordPress.DB.PreparedSQL.NotPrepared - $select = "SELECT * FROM {$wpdb->prefix}posts WHERE post_title = '128GB USB Stick' AND post_status = 'publish' AND post_type = 'product'"; - $id_product = $wpdb->get_row( $select ); + $select = "SELECT * FROM {$wpdb->prefix}posts WHERE post_title = '128GB USB Stick' AND post_status = 'publish' AND post_type = 'product'"; + $id_products = $wpdb->get_results( $select ); // phpcs:disable WordPress.DB.PreparedSQL.NotPrepared - $select = "SELECT * FROM {$wpdb->prefix}posts WHERE post_title = '32GB USB Stick' AND post_status = 'publish' AND post_type = 'product'"; - $id_cross_sell = $wpdb->get_row( $select ); + $select = "SELECT * FROM {$wpdb->prefix}posts WHERE post_title = '32GB USB Stick' AND post_status = 'publish' AND post_type = 'product'"; + $id_cross_sells = $wpdb->get_results( $select ); - add_post_meta( $id_product->ID, '_crosssell_ids', $id_cross_sell->ID ); + foreach ( $id_products as $id_product ) { + update_post_meta( $id_product->ID, '_crosssell_ids', wp_list_pluck( $id_cross_sells, 'ID' ) ); + } } diff --git a/plugins/woocommerce-blocks/tests/utils/shopper.js b/plugins/woocommerce-blocks/tests/utils/shopper.js index e952750492a..8e7c8cfd2e5 100644 --- a/plugins/woocommerce-blocks/tests/utils/shopper.js +++ b/plugins/woocommerce-blocks/tests/utils/shopper.js @@ -5,6 +5,7 @@ import { shopper as wcShopper, uiUnblocked, SHOP_CART_PAGE, + SHOP_PAGE, } from '@woocommerce/e2e-utils'; import { pressKeyWithModifier } from '@wordpress/e2e-test-utils'; @@ -36,12 +37,17 @@ export const shopper = { } ); }, + goToShop: async () => { + await page.goto( SHOP_PAGE ); + // Wait for Shop block to finish loading, otherwise we get flakey tests + await page.waitForSelector( '.add_to_cart_button' ); + }, + goToCart: async () => { await shopper.block.goToBlockPage( 'Cart' ); // Wait for Cart block to finish loading, otherwise we get flakey tests await page.waitForSelector( - '.wp-block-woocommerce-cart.is-loading', - { hidden: true } + '.wp-block-woocommerce-cart:not(.is-loading)' ); }, @@ -49,8 +55,7 @@ export const shopper = { await shopper.block.goToBlockPage( 'Checkout' ); // Wait for Checkout block to finish loading, otherwise we get flakey tests await page.waitForSelector( - '.wp-block-woocommerce-checkout.is-loading', - { hidden: true } + '.wp-block-woocommerce-checkout:not(.is-loading)' ); }, @@ -110,6 +115,14 @@ export const shopper = { }, placeOrder: async () => { + // Wait for payment methods to be shown, otherwise we get flakey tests + await page.waitForSelector( + '.wc-block-components-payment-method-label' + ); + // Wait for place order button to be clickable, otherwise we get flakey tests + await page.waitForSelector( + '.wc-block-components-checkout-place-order-button:not([disabled])' + ); await Promise.all( [ page.click( '.wc-block-components-checkout-place-order-button' @@ -324,13 +337,15 @@ export const shopper = { shippingName, shippingPrice ) => { + await page.waitForSelector( + '.wc-block-components-radio-control__label' + ); await expect( page ).toClick( '.wc-block-components-radio-control__label', { text: shippingName, } ); - //eslint-disable-next-line no-shadow const checkIfShippingHasChanged = ( el, shippingName ) => { const checkShippingTotal = () => { diff --git a/plugins/woocommerce-blocks/tests/utils/taxes.ts b/plugins/woocommerce-blocks/tests/utils/taxes.ts index f4ffedc3ce1..58e4bdc99b3 100644 --- a/plugins/woocommerce-blocks/tests/utils/taxes.ts +++ b/plugins/woocommerce-blocks/tests/utils/taxes.ts @@ -44,6 +44,9 @@ export async function getTaxesFromCurrentPage(): Promise< value: string; } > > { + // Wait for totals area otherwise we get flaky results. + await page.waitForSelector( '.wc-block-components-totals-wrapper' ); + return await page.$$eval( '.wc-block-components-totals-taxes', ( nodes ) => nodes.map( ( node ) => { const label = node.querySelector(