From 74de1525357f70b8999c559052a5cbda452210f0 Mon Sep 17 00:00:00 2001 From: vedanshujain Date: Thu, 9 Jul 2020 01:42:12 +0530 Subject: [PATCH 01/66] Use tax location from order while computing tax in discount. We were not passing tax location while computing discount in orders, hence it was defaulting to shop's base address resulting in incorrect tax calculation. This commit refactors `get_rates` method into another method that allows getting rates from location directly. --- includes/abstracts/abstract-wc-order.php | 13 +++++++++++-- includes/class-wc-tax.php | 18 ++++++++++++++++-- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/includes/abstracts/abstract-wc-order.php b/includes/abstracts/abstract-wc-order.php index a694d1aa151..4c794c8c1b2 100644 --- a/includes/abstracts/abstract-wc-order.php +++ b/includes/abstracts/abstract-wc-order.php @@ -1270,6 +1270,8 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order { */ protected function set_item_discount_amounts( $discounts ) { $item_discounts = $discounts->get_discounts_by_item(); + $tax_location = $this->get_tax_location(); + $tax_location = array( $tax_location['country'], $tax_location['state'], $tax_location['postcode'], $tax_location['city'] ); if ( $item_discounts ) { foreach ( $item_discounts as $item_id => $amount ) { @@ -1277,7 +1279,7 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order { // If the prices include tax, discounts should be taken off the tax inclusive prices like in the cart. if ( $this->get_prices_include_tax() && wc_tax_enabled() && 'taxable' === $item->get_tax_status() ) { - $taxes = WC_Tax::calc_tax( $amount, WC_Tax::get_rates( $item->get_tax_class() ), true ); + $taxes = WC_Tax::calc_tax( $amount, WC_Tax::get_rates_from_location( $item->get_tax_class(), $tax_location ), true ); // Use unrounded taxes so totals will be re-calculated accurately, like in cart. $amount = $amount - array_sum( $taxes ); @@ -1299,6 +1301,13 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order { $coupon_code_to_id = wc_list_pluck( $coupons, 'get_id', 'get_code' ); $all_discounts = $discounts->get_discounts(); $coupon_discounts = $discounts->get_discounts_by_coupon(); + $tax_location = $this->get_tax_location(); + $tax_location = array( + $tax_location['country'], + $tax_location['state'], + $tax_location['postcode'], + $tax_location['city'], + ); if ( $coupon_discounts ) { foreach ( $coupon_discounts as $coupon_code => $amount ) { @@ -1321,7 +1330,7 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order { continue; } - $taxes = array_sum( WC_Tax::calc_tax( $item_discount_amount, WC_Tax::get_rates( $item->get_tax_class() ), $this->get_prices_include_tax() ) ); + $taxes = array_sum( WC_Tax::calc_tax( $item_discount_amount, WC_Tax::get_rates_from_location( $item->get_tax_class(), $tax_location ), $this->get_prices_include_tax() ) ); if ( 'yes' !== get_option( 'woocommerce_tax_round_at_subtotal' ) ) { $taxes = wc_round_tax_total( $taxes ); } diff --git a/includes/class-wc-tax.php b/includes/class-wc-tax.php index f80a7d8422d..cb72d9bbe1d 100644 --- a/includes/class-wc-tax.php +++ b/includes/class-wc-tax.php @@ -405,7 +405,7 @@ class WC_Tax { $criteria_string = implode( ' AND ', $criteria ); - // phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared + // phpcs:disable WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.InterpolatedNotPrepared $found_rates = $wpdb->get_results( " SELECT tax_rates.*, COUNT( locations.location_id ) as postcode_count, COUNT( locations2.location_id ) as city_count @@ -479,8 +479,22 @@ class WC_Tax { * @return array */ public static function get_rates( $tax_class = '', $customer = null ) { + $tax_class = sanitize_title( $tax_class ); + $location = self::get_tax_location( $tax_class, $customer ); + return self::get_rates_from_location( $tax_class, $location, $customer ); + } + + /** + * Get's an arrau of matching rates from location and tax class. $customer parameter is used to preserve backward compatibility for filter. + * + * @param string $tax_class Tax class to get rates for. + * @param array $location Location to compute rates for. Should be in form: array( country, state, postcode, city). + * @param object $customer Only used to maintain backward compatibility for filter `woocommerce-matched_rates`. + * + * @return mixed|void + */ + public static function get_rates_from_location( $tax_class, $location, $customer = null ) { $tax_class = sanitize_title( $tax_class ); - $location = self::get_tax_location( $tax_class, $customer ); $matched_tax_rates = array(); if ( count( $location ) === 4 ) { From 24a69fd249516e8bc970715633fbc4308081147c Mon Sep 17 00:00:00 2001 From: vedanshujain Date: Fri, 10 Jul 2020 00:10:00 +0530 Subject: [PATCH 02/66] Add return param comment --- includes/class-wc-tax.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/class-wc-tax.php b/includes/class-wc-tax.php index cb72d9bbe1d..f185f74eeb3 100644 --- a/includes/class-wc-tax.php +++ b/includes/class-wc-tax.php @@ -491,7 +491,7 @@ class WC_Tax { * @param array $location Location to compute rates for. Should be in form: array( country, state, postcode, city). * @param object $customer Only used to maintain backward compatibility for filter `woocommerce-matched_rates`. * - * @return mixed|void + * @return mixed|void Tax rates. */ public static function get_rates_from_location( $tax_class, $location, $customer = null ) { $tax_class = sanitize_title( $tax_class ); From 00b7b40c379095057e08fc29b32dbefb04bf913e Mon Sep 17 00:00:00 2001 From: vedanshujain Date: Fri, 10 Jul 2020 00:11:37 +0530 Subject: [PATCH 03/66] Add method to get rates based on order. This method will prioritize getting rates from billing/shipping address instead of `WC()->customer` which in irrevelant in context of editing order from admin screen. --- includes/abstracts/abstract-wc-order.php | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/includes/abstracts/abstract-wc-order.php b/includes/abstracts/abstract-wc-order.php index 4c794c8c1b2..c8469de8c84 100644 --- a/includes/abstracts/abstract-wc-order.php +++ b/includes/abstracts/abstract-wc-order.php @@ -1279,7 +1279,7 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order { // If the prices include tax, discounts should be taken off the tax inclusive prices like in the cart. if ( $this->get_prices_include_tax() && wc_tax_enabled() && 'taxable' === $item->get_tax_status() ) { - $taxes = WC_Tax::calc_tax( $amount, WC_Tax::get_rates_from_location( $item->get_tax_class(), $tax_location ), true ); + $taxes = WC_Tax::calc_tax( $amount, $this->get_tax_rates( $item->get_tax_class(), $tax_location ), true ); // Use unrounded taxes so totals will be re-calculated accurately, like in cart. $amount = $amount - array_sum( $taxes ); @@ -1330,7 +1330,7 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order { continue; } - $taxes = array_sum( WC_Tax::calc_tax( $item_discount_amount, WC_Tax::get_rates_from_location( $item->get_tax_class(), $tax_location ), $this->get_prices_include_tax() ) ); + $taxes = array_sum( WC_Tax::calc_tax( $item_discount_amount, $this->get_tax_rates( $item->get_tax_class(), $tax_location ), $this->get_prices_include_tax() ) ); if ( 'yes' !== get_option( 'woocommerce_tax_round_at_subtotal' ) ) { $taxes = wc_round_tax_total( $taxes ); } @@ -1524,6 +1524,21 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order { return apply_filters( 'woocommerce_order_get_tax_location', $args, $this ); } + /** + * Get tax rates for an order. Use order's shipping or billing address, defaults to base location. + * + * @param string $tax_class Tax class to get rates for. + * @param array $location_args Location to compute rates for. Should be in form: array( country, state, postcode, city). + * @param object $customer Only used to maintain backward compatibility for filter `woocommerce-matched_rates`. + * + * @return mixed|void Tax rates. + */ + protected function get_tax_rates( $tax_class, $location_args = array(), $customer = null ) { + $tax_location = $this->get_tax_location( $location_args ); + $tax_location = array( $tax_location['country'], $tax_location['state'], $tax_location['postcode'], $tax_location['city'] ); + return WC_Tax::get_rates_from_location( $tax_class, $tax_location, $customer ); + } + /** * Calculate taxes for all line items and shipping, and store the totals and tax rows. * From a2231c4387b20811d9bf5fd6a9ab6d27fcc8434e Mon Sep 17 00:00:00 2001 From: vedanshujain Date: Tue, 14 Jul 2020 00:35:41 +0530 Subject: [PATCH 04/66] Add unit test --- .../class-wc-abstract-order-test.php | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/tests/php/includes/abstracts/class-wc-abstract-order-test.php b/tests/php/includes/abstracts/class-wc-abstract-order-test.php index bcb04fa28b5..a26f5cc565c 100644 --- a/tests/php/includes/abstracts/class-wc-abstract-order-test.php +++ b/tests/php/includes/abstracts/class-wc-abstract-order-test.php @@ -73,4 +73,68 @@ class WC_Abstract_Order_Test extends WC_Unit_Test_Case { $this->assertEquals( 1248.96, $order->get_total() ); } + /** + * Test that coupon taxes are not affected by logged in admin user. + */ + public function test_apply_coupon_for_correct_location_taxes() { + update_option( 'woocommerce_tax_round_at_subtotal', 'yes' ); + update_option( 'woocommerce_prices_include_tax', 'yes' ); + update_option( 'woocommerce_tax_based_on', 'billing' ); + update_option( 'woocommerce_calc_taxes', 'yes' ); + + $password = wp_generate_password( 8, false, false ); + $admin_id = wp_insert_user( + array( + 'user_login' => "test_admin$password", + 'user_pass' => $password, + 'user_email' => "admin$password@example.com", + 'role' => 'administrator', + ) + ); + + update_user_meta( $admin_id, 'billing_country', 'MV' ); // Different than customer's address and base location. + wp_set_current_user( $admin_id ); + WC()->customer = null; + WC()->initialize_cart(); + + update_option( 'woocommerce_default_country', 'IN:AP' ); + + $tax_rate = array( + 'tax_rate_country' => 'IN', + 'tax_rate_state' => '', + 'tax_rate' => '25.0000', + 'tax_rate_name' => 'tax', + 'tax_rate_order' => '1', + 'tax_rate_class' => '', + ); + WC_Tax::_insert_tax_rate( $tax_rate ); + + $product = WC_Helper_Product::create_simple_product(); + $product->set_regular_price( 100 ); + $product->save(); + + $order = wc_create_order(); + $order->set_billing_country( 'IN' ); + $order->add_product( $product, 1 ); + $order->save(); + $order->calculate_totals(); + + $this->assertEquals( 100, $order->get_total() ); + $this->assertEquals( 80, $order->get_subtotal() ); + $this->assertEquals( 20, $order->get_total_tax() ); + + $coupon = new WC_Coupon(); + $coupon->set_code( '10off' ); + $coupon->set_discount_type( 'percent' ); + $coupon->set_amount( 10 ); + $coupon->save(); + + $order->apply_coupon( '10off' ); + + $this->assertEquals( 8, $order->get_discount_total() ); + $this->assertEquals( 90, $order->get_total() ); + $this->assertEquals( 18, $order->get_total_tax() ); + $this->assertEquals( 2, $order->get_discount_tax() ); + } + } From 4a2943d45219028ce0e8bcdee0c26ccb2d664a6a Mon Sep 17 00:00:00 2001 From: roykho Date: Fri, 4 Dec 2020 13:17:24 -0800 Subject: [PATCH 05/66] Defer nonce creation until displayed by WC Admin closes #27424 --- .../notes/class-wc-notes-run-db-update.php | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/includes/admin/notes/class-wc-notes-run-db-update.php b/includes/admin/notes/class-wc-notes-run-db-update.php index c723d231d06..f59074679d5 100644 --- a/includes/admin/notes/class-wc-notes-run-db-update.php +++ b/includes/admin/notes/class-wc-notes-run-db-update.php @@ -110,10 +110,13 @@ class WC_Notes_Run_Db_Update { */ private static function update_needed_notice( $note_id = null ) { $update_url = html_entity_decode( - wp_nonce_url( - add_query_arg( 'do_update_woocommerce', 'true', wc_get_current_admin_url() ? wc_get_current_admin_url() : admin_url( 'admin.php?page=wc-settings' ) ), - 'wc_db_update', - 'wc_db_update_nonce' + add_query_arg( + array( + 'do_update_woocommerce' => 'true', + '_nonce_action' => 'wc_db_update', + '_nonce_name' => 'wc_db_update_nonce', + ), + wc_get_current_admin_url() ? wc_get_current_admin_url() : admin_url( 'admin.php?page=wc-settings' ) ) ); @@ -206,14 +209,13 @@ class WC_Notes_Run_Db_Update { */ private static function update_done_notice( $note_id ) { $hide_notices_url = html_entity_decode( // to convert &s to normal &, otherwise produces invalid link. - wp_nonce_url( - add_query_arg( - 'wc-hide-notice', - 'update', - wc_get_current_admin_url() ? wc_get_current_admin_url() : admin_url( 'admin.php?page=wc-settings' ) + add_query_arg( + array( + 'wc-hide-notice' => 'update', + '_nonce_action' => 'woocommerce_hide_notices_nonce', + '_nonce_name' => '_wc_notice_nonce', ), - 'woocommerce_hide_notices_nonce', - '_wc_notice_nonce' + wc_get_current_admin_url() ? wc_get_current_admin_url() : admin_url( 'admin.php?page=wc-settings' ) ) ); From 4b62b2358060ae537c9fc1c0c4291be37ce06060 Mon Sep 17 00:00:00 2001 From: Veljko V Date: Wed, 23 Dec 2020 15:04:14 +0100 Subject: [PATCH 06/66] Add new e2e test shopper cart apply coupon --- tests/e2e/core-tests/specs/index.js | 3 + .../shopper/front-end-cart-coupons.test.js | 91 +++++++++++++++++++ .../e2e/specs/front-end/test-cart-coupons.js | 6 ++ tests/e2e/utils/src/components.js | 7 +- 4 files changed, 104 insertions(+), 3 deletions(-) create mode 100644 tests/e2e/core-tests/specs/shopper/front-end-cart-coupons.test.js create mode 100644 tests/e2e/specs/front-end/test-cart-coupons.js diff --git a/tests/e2e/core-tests/specs/index.js b/tests/e2e/core-tests/specs/index.js index 8c8bd03b152..5a40f310afb 100644 --- a/tests/e2e/core-tests/specs/index.js +++ b/tests/e2e/core-tests/specs/index.js @@ -9,6 +9,7 @@ const { runOnboardingFlowTest, runTaskListTest } = require( './activate-and-setu const runInitialStoreSettingsTest = require( './activate-and-setup/setup.test' ); // Shopper tests +const runCartApplyCouponsTest = require( './shopper/front-end-cart-coupons.test') const runCartPageTest = require( './shopper/front-end-cart.test' ); const runCheckoutPageTest = require( './shopper/front-end-checkout.test' ); const runMyAccountPageTest = require( './shopper/front-end-my-account.test' ); @@ -33,6 +34,7 @@ const runSetupOnboardingTests = () => { }; const runShopperTests = () => { + runCartApplyCouponsTest(); runCartPageTest(); runCheckoutPageTest(); runMyAccountPageTest(); @@ -58,6 +60,7 @@ module.exports = { runTaskListTest, runInitialStoreSettingsTest, runSetupOnboardingTests, + runCartApplyCouponsTest, runCartPageTest, runCheckoutPageTest, runMyAccountPageTest, diff --git a/tests/e2e/core-tests/specs/shopper/front-end-cart-coupons.test.js b/tests/e2e/core-tests/specs/shopper/front-end-cart-coupons.test.js new file mode 100644 index 00000000000..1a0bc994121 --- /dev/null +++ b/tests/e2e/core-tests/specs/shopper/front-end-cart-coupons.test.js @@ -0,0 +1,91 @@ +/* eslint-disable jest/no-export, jest/no-disabled-tests, jest/expect-expect */ +/** + * Internal dependencies + */ +const { + shopper, + merchant, + createCoupon, + createSimpleProduct +} = require( '@woocommerce/e2e-utils' ); + +/** + * External dependencies + */ +const { + it, + describe, + beforeAll, +} = require( '@jest/globals' ); + +const runCartApplyCouponsTest = () => { + describe('Cart applying coupons', () => { + beforeAll(async () => { + await merchant.login(); + await createSimpleProduct(); + await createCoupon('Fixed cart discount'); + await createCoupon('Percentage discount', '50'); + await createCoupon('Fixed product discount'); + await merchant.logout(); + }); + + it('allows customer to apply a fixed cart coupon in the cart', async () => { + await shopper.goToShop(); + await shopper.addToCartFromShopPage('Simple product'); + + await shopper.goToCart(); + await shopper.productIsInCart('Simple product'); + + // Apply Fixed cart discount coupon + await expect(page).toFill('#coupon_code', 'Code-Fixed cart discount'); + await expect(page).toClick('button', {text: 'Apply coupon'}); + await expect(page).toMatchElement('.woocommerce-message', {text: 'Coupon code applied successfully.'}); + + // Wait for page to expand total calculations to avoid flakyness + await page.waitForSelector('.order-total'); + + // Verify discount applied and order total + await expect(page).toMatchElement('.cart-discount .amount', {text: '$5.00'}); + await expect(page).toMatchElement('.order-total .amount', {text: '$4.99'}); + + // Remove coupon + await expect(page).toClick('.woocommerce-remove-coupon'); + await expect(page).toMatchElement('.woocommerce-message', {text: 'Coupon has been removed.'}); + + // Apply Percentage discount coupon + await expect(page).toFill('#coupon_code', 'Code-Percentage discount'); + await expect(page).toClick('button', {text: 'Apply coupon'}); + await expect(page).toMatchElement('.woocommerce-message', {text: 'Coupon code applied successfully.'}); + await expect(page).toMatchElement('.cart-discount .amount', {text: '$4.99'}); + await expect(page).toMatchElement('.order-total .amount', {text: '$5.00'}); + + // Remove coupon + await expect(page).toClick('.woocommerce-remove-coupon'); + await expect(page).toMatchElement('.woocommerce-message', {text: 'Coupon has been removed.'}); + + // Apply Fixed product discount coupon + await expect(page).toFill('#coupon_code', 'Code-Fixed product discount'); + await expect(page).toClick('button', {text: 'Apply coupon'}); + await expect(page).toMatchElement('.woocommerce-message', {text: 'Coupon code applied successfully.'}); + await expect(page).toMatchElement('.cart-discount .amount', {text: '$5.00'}); + await expect(page).toMatchElement('.order-total .amount', {text: '$4.99'}); + + // Try to apply the same coupon + await expect(page).toFill('#coupon_code', 'Code-Fixed product discount'); + await expect(page).toClick('button', {text: 'Apply coupon'}); + await expect(page).toMatchElement('.woocommerce-error', { text: 'Coupon code already applied!' }); + + // Try to apply multiple coupons + await expect(page).toFill('#coupon_code', 'Code-Fixed cart discount'); + await expect(page).toClick('button', {text: 'Apply coupon'}); + await expect(page).toMatchElement('.woocommerce-message', {text: 'Coupon code applied successfully.'}); + await expect(page).toMatchElement('.order-total .amount', {text: '$0.00'}); + + // Remove coupon + await expect(page).toClick('.woocommerce-remove-coupon'); + await expect(page).toMatchElement('.woocommerce-message', {text: 'Coupon has been removed.'}); + }); + }); +}; + +module.exports = runCartApplyCouponsTest; diff --git a/tests/e2e/specs/front-end/test-cart-coupons.js b/tests/e2e/specs/front-end/test-cart-coupons.js new file mode 100644 index 00000000000..66c70adbb88 --- /dev/null +++ b/tests/e2e/specs/front-end/test-cart-coupons.js @@ -0,0 +1,6 @@ +/* + * Internal dependencies + */ +const { runCartApplyCouponsTest } = require( '@woocommerce/e2e-core-tests' ); + +runCartApplyCouponsTest(); diff --git a/tests/e2e/utils/src/components.js b/tests/e2e/utils/src/components.js index 09d467011d5..848226a5bcd 100644 --- a/tests/e2e/utils/src/components.js +++ b/tests/e2e/utils/src/components.js @@ -401,18 +401,19 @@ const addProductToOrder = async ( orderId, productName ) => { /** * Creates a basic coupon with the provided coupon amount. Returns the coupon code. * + * @param discountType Type of a coupon. Defaults to Fixed cart discount. * @param couponAmount Amount to be applied. Defaults to 5. */ -const createCoupon = async ( couponAmount = '5' ) => { +const createCoupon = async ( discountType = 'Fixed cart discount', couponAmount = '5' ) => { await merchant.openNewCoupon(); // Fill in coupon code - let couponCode = 'code-' + new Date().getTime().toString(); + let couponCode = 'Code-' + discountType; await expect(page).toFill( '#title', couponCode ); // Set general coupon data await clickTab( 'General' ); - await expect(page).toSelect( '#discount_type', 'Fixed cart discount' ); + await expect(page).toSelect( '#discount_type', discountType ); await expect(page).toFill( '#coupon_amount', couponAmount ); // Publish coupon From e6ebfc51541ec2ebd948ac56cf1ebced2a6836d9 Mon Sep 17 00:00:00 2001 From: Veljko V Date: Wed, 23 Dec 2020 17:19:48 +0100 Subject: [PATCH 07/66] Update existing code to be headless compatible --- .../shopper/front-end-cart-coupons.test.js | 42 ++++++++++--------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/tests/e2e/core-tests/specs/shopper/front-end-cart-coupons.test.js b/tests/e2e/core-tests/specs/shopper/front-end-cart-coupons.test.js index 1a0bc994121..808e62f5af9 100644 --- a/tests/e2e/core-tests/specs/shopper/front-end-cart-coupons.test.js +++ b/tests/e2e/core-tests/specs/shopper/front-end-cart-coupons.test.js @@ -32,58 +32,62 @@ const runCartApplyCouponsTest = () => { it('allows customer to apply a fixed cart coupon in the cart', async () => { await shopper.goToShop(); await shopper.addToCartFromShopPage('Simple product'); - await shopper.goToCart(); await shopper.productIsInCart('Simple product'); // Apply Fixed cart discount coupon await expect(page).toFill('#coupon_code', 'Code-Fixed cart discount'); await expect(page).toClick('button', {text: 'Apply coupon'}); - await expect(page).toMatchElement('.woocommerce-message', {text: 'Coupon code applied successfully.'}); + await page.waitForSelector('.woocommerce-message', {text: 'Coupon code applied successfully.'}); // Wait for page to expand total calculations to avoid flakyness await page.waitForSelector('.order-total'); // Verify discount applied and order total - await expect(page).toMatchElement('.cart-discount .amount', {text: '$5.00'}); - await expect(page).toMatchElement('.order-total .amount', {text: '$4.99'}); + await page.waitForSelector('.cart-discount .amount', {text: '$5.00'}); + await page.waitForSelector('.order-total .amount', {text: '$4.99'}); // Remove coupon - await expect(page).toClick('.woocommerce-remove-coupon'); - await expect(page).toMatchElement('.woocommerce-message', {text: 'Coupon has been removed.'}); + await expect(page).toClick('.woocommerce-remove-coupon', {text: '[Remove]'}); + await page.waitForSelector('.woocommerce-message', {text: 'Coupon has been removed.'}); // Apply Percentage discount coupon await expect(page).toFill('#coupon_code', 'Code-Percentage discount'); await expect(page).toClick('button', {text: 'Apply coupon'}); - await expect(page).toMatchElement('.woocommerce-message', {text: 'Coupon code applied successfully.'}); - await expect(page).toMatchElement('.cart-discount .amount', {text: '$4.99'}); - await expect(page).toMatchElement('.order-total .amount', {text: '$5.00'}); + await page.waitForSelector('.woocommerce-message', {text: 'Coupon code applied successfully.'}); + await page.waitForSelector('.cart-discount .amount', {text: '$4.99'}); + await page.waitForSelector('.order-total .amount', {text: '$5.00'}); // Remove coupon - await expect(page).toClick('.woocommerce-remove-coupon'); - await expect(page).toMatchElement('.woocommerce-message', {text: 'Coupon has been removed.'}); + await expect(page).toClick('.woocommerce-remove-coupon', {text: '[Remove]'}); + await page.waitForSelector('.woocommerce-message', {text: 'Coupon has been removed.'}); // Apply Fixed product discount coupon await expect(page).toFill('#coupon_code', 'Code-Fixed product discount'); await expect(page).toClick('button', {text: 'Apply coupon'}); - await expect(page).toMatchElement('.woocommerce-message', {text: 'Coupon code applied successfully.'}); - await expect(page).toMatchElement('.cart-discount .amount', {text: '$5.00'}); - await expect(page).toMatchElement('.order-total .amount', {text: '$4.99'}); + await page.waitForSelector('.woocommerce-message', {text: 'Coupon code applied successfully.'}); + await page.waitForSelector('.cart-discount .amount', {text: '$5.00'}); + await page.waitForSelector('.order-total .amount', {text: '$4.99'}); // Try to apply the same coupon await expect(page).toFill('#coupon_code', 'Code-Fixed product discount'); await expect(page).toClick('button', {text: 'Apply coupon'}); - await expect(page).toMatchElement('.woocommerce-error', { text: 'Coupon code already applied!' }); + await page.waitForSelector('.woocommerce-error', { text: 'Coupon code already applied!' }); // Try to apply multiple coupons await expect(page).toFill('#coupon_code', 'Code-Fixed cart discount'); await expect(page).toClick('button', {text: 'Apply coupon'}); - await expect(page).toMatchElement('.woocommerce-message', {text: 'Coupon code applied successfully.'}); - await expect(page).toMatchElement('.order-total .amount', {text: '$0.00'}); + await page.waitForSelector('.woocommerce-message', {text: 'Coupon code applied successfully.'}); + await page.waitForSelector('.order-total .amount', {text: '$0.00'}); // Remove coupon - await expect(page).toClick('.woocommerce-remove-coupon'); - await expect(page).toMatchElement('.woocommerce-message', {text: 'Coupon has been removed.'}); + await expect(page).toClick('.woocommerce-remove-coupon', {text: '[Remove]'}); + await page.waitForSelector('.woocommerce-message', {text: 'Coupon has been removed.'}); + await expect(page).toClick('.woocommerce-remove-coupon', {text: '[Remove]'}); + await page.waitForSelector('.woocommerce-message', {text: 'Coupon has been removed.'}); + + // Verify the total amount after all coupons removal + await page.waitForSelector('.order-total .amount', {text: '$9.99'}); }); }); }; From f4be2afa2a2c2f4ddaa6fb91d6f9e57ec5b36888 Mon Sep 17 00:00:00 2001 From: Veljko V Date: Thu, 24 Dec 2020 10:54:21 +0100 Subject: [PATCH 08/66] Updated changelogs to utils and core tests --- tests/e2e/core-tests/CHANGELOG.md | 1 + tests/e2e/utils/CHANGELOG.md | 1 + 2 files changed, 2 insertions(+) diff --git a/tests/e2e/core-tests/CHANGELOG.md b/tests/e2e/core-tests/CHANGELOG.md index 12363b5ad98..3ae9b1c2a16 100644 --- a/tests/e2e/core-tests/CHANGELOG.md +++ b/tests/e2e/core-tests/CHANGELOG.md @@ -4,6 +4,7 @@ - Merchant Order Status Filter tests - Merchant Order Refund tests - Merchant Apply Coupon tests +- Shopper Cart Apply Coupon ## Fixed diff --git a/tests/e2e/utils/CHANGELOG.md b/tests/e2e/utils/CHANGELOG.md index ac35c538321..48f5ad35391 100644 --- a/tests/e2e/utils/CHANGELOG.md +++ b/tests/e2e/utils/CHANGELOG.md @@ -17,6 +17,7 @@ - Deprecated `StoreOwnerFlow`, `CustomerFlow` in favour of `merchant`,`shopper` - `createSimpleOrder( status )` returns the ID of the order that was created +- Updated `createCoupon( couponAmount )` component to be more dynamic in functioning, now you can use any coupon discount type in tests # 0.1.1 From 121099afe7c3f29d0d0bfbd7716a3f75e936290b Mon Sep 17 00:00:00 2001 From: Veljko V Date: Thu, 24 Dec 2020 11:53:45 +0100 Subject: [PATCH 09/66] Fix problem in headless by adding uiUnblocked --- .../specs/shopper/front-end-cart-coupons.test.js | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/tests/e2e/core-tests/specs/shopper/front-end-cart-coupons.test.js b/tests/e2e/core-tests/specs/shopper/front-end-cart-coupons.test.js index 808e62f5af9..64d7dc40ad0 100644 --- a/tests/e2e/core-tests/specs/shopper/front-end-cart-coupons.test.js +++ b/tests/e2e/core-tests/specs/shopper/front-end-cart-coupons.test.js @@ -6,7 +6,8 @@ const { shopper, merchant, createCoupon, - createSimpleProduct + createSimpleProduct, + uiUnblocked } = require( '@woocommerce/e2e-utils' ); /** @@ -38,6 +39,7 @@ const runCartApplyCouponsTest = () => { // Apply Fixed cart discount coupon await expect(page).toFill('#coupon_code', 'Code-Fixed cart discount'); await expect(page).toClick('button', {text: 'Apply coupon'}); + await uiUnblocked(); await page.waitForSelector('.woocommerce-message', {text: 'Coupon code applied successfully.'}); // Wait for page to expand total calculations to avoid flakyness @@ -49,22 +51,26 @@ const runCartApplyCouponsTest = () => { // Remove coupon await expect(page).toClick('.woocommerce-remove-coupon', {text: '[Remove]'}); + await uiUnblocked(); await page.waitForSelector('.woocommerce-message', {text: 'Coupon has been removed.'}); // Apply Percentage discount coupon await expect(page).toFill('#coupon_code', 'Code-Percentage discount'); await expect(page).toClick('button', {text: 'Apply coupon'}); + await uiUnblocked(); await page.waitForSelector('.woocommerce-message', {text: 'Coupon code applied successfully.'}); await page.waitForSelector('.cart-discount .amount', {text: '$4.99'}); await page.waitForSelector('.order-total .amount', {text: '$5.00'}); // Remove coupon await expect(page).toClick('.woocommerce-remove-coupon', {text: '[Remove]'}); + await uiUnblocked(); await page.waitForSelector('.woocommerce-message', {text: 'Coupon has been removed.'}); // Apply Fixed product discount coupon await expect(page).toFill('#coupon_code', 'Code-Fixed product discount'); await expect(page).toClick('button', {text: 'Apply coupon'}); + await uiUnblocked(); await page.waitForSelector('.woocommerce-message', {text: 'Coupon code applied successfully.'}); await page.waitForSelector('.cart-discount .amount', {text: '$5.00'}); await page.waitForSelector('.order-total .amount', {text: '$4.99'}); @@ -72,18 +78,22 @@ const runCartApplyCouponsTest = () => { // Try to apply the same coupon await expect(page).toFill('#coupon_code', 'Code-Fixed product discount'); await expect(page).toClick('button', {text: 'Apply coupon'}); + await uiUnblocked(); await page.waitForSelector('.woocommerce-error', { text: 'Coupon code already applied!' }); // Try to apply multiple coupons await expect(page).toFill('#coupon_code', 'Code-Fixed cart discount'); await expect(page).toClick('button', {text: 'Apply coupon'}); + await uiUnblocked(); await page.waitForSelector('.woocommerce-message', {text: 'Coupon code applied successfully.'}); await page.waitForSelector('.order-total .amount', {text: '$0.00'}); // Remove coupon - await expect(page).toClick('.woocommerce-remove-coupon', {text: '[Remove]'}); + await expect(page).toClick('.woocommerce-remove-coupon', {text: '[Remove]'}); + await uiUnblocked(); await page.waitForSelector('.woocommerce-message', {text: 'Coupon has been removed.'}); - await expect(page).toClick('.woocommerce-remove-coupon', {text: '[Remove]'}); + await expect(page).toClick('.woocommerce-remove-coupon', {text: '[Remove]'}); + await uiUnblocked(); await page.waitForSelector('.woocommerce-message', {text: 'Coupon has been removed.'}); // Verify the total amount after all coupons removal From 2204867a20be1ce9a23ed35ce11b65fe78f522ef Mon Sep 17 00:00:00 2001 From: Veljko V Date: Mon, 4 Jan 2021 15:11:41 +0100 Subject: [PATCH 10/66] Fix conflict with another tests We're using dynamic coupon creation now. I updated other test to search for specific coupon name because in order edit page there's only lowercased coupon names, but we're using uppercased for two reasons at least. --- .../specs/merchant/wp-admin-order-apply-coupon.test.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/e2e/core-tests/specs/merchant/wp-admin-order-apply-coupon.test.js b/tests/e2e/core-tests/specs/merchant/wp-admin-order-apply-coupon.test.js index 4319e8606cb..a51d435e1d0 100644 --- a/tests/e2e/core-tests/specs/merchant/wp-admin-order-apply-coupon.test.js +++ b/tests/e2e/core-tests/specs/merchant/wp-admin-order-apply-coupon.test.js @@ -50,7 +50,7 @@ const runOrderApplyCouponTest = () => { // Verify the coupon list is showing await page.waitForSelector('.wc-used-coupons'); await expect(page).toMatchElement('.wc_coupon_list', { text: 'Coupon(s)' }); - await expect(page).toMatchElement('.wc_coupon_list li.code.editable', { text: couponCode }); + await expect(page).toMatchElement('.wc_coupon_list li.code.editable', { text: 'code-fixed cart discount' }); // Check that the coupon has been applied await expect(page).toMatchElement('.wc-order-item-discount', { text: '5.00' }); @@ -60,7 +60,7 @@ const runOrderApplyCouponTest = () => { it('can remove a coupon', async () => { // Make sure we have a coupon on the page to use await page.waitForSelector('.wc-used-coupons'); - await expect(page).toMatchElement('.wc_coupon_list li.code.editable', { text: couponCode }); + await expect(page).toMatchElement('.wc_coupon_list li.code.editable', { text: 'code-fixed cart discount' }); // We need to use this here as `expect(page).toClick()` was unable to find the element // See: https://github.com/puppeteer/puppeteer/issues/1769#issuecomment-637645219 @@ -69,7 +69,7 @@ const runOrderApplyCouponTest = () => { await uiUnblocked(); // Verify the coupon pricing has been removed - await expect(page).not.toMatchElement('.wc_coupon_list li.code.editable', { text: couponCode }); + await expect(page).not.toMatchElement('.wc_coupon_list li.code.editable', { text: 'code-fixed cart discount' }); await expect(page).not.toMatchElement('.wc-order-item-discount', { text: '5.00' }); await expect(page).not.toMatchElement('.line-cost .view .woocommerce-Price-amount', { text: '4.99' }); From f782bcdc8699205ed7b980460cdd749ef23e5310 Mon Sep 17 00:00:00 2001 From: Veljko V Date: Mon, 4 Jan 2021 15:28:09 +0100 Subject: [PATCH 11/66] Update changelog information --- tests/e2e/core-tests/CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/e2e/core-tests/CHANGELOG.md b/tests/e2e/core-tests/CHANGELOG.md index 3ae9b1c2a16..1f1fcfa67a0 100644 --- a/tests/e2e/core-tests/CHANGELOG.md +++ b/tests/e2e/core-tests/CHANGELOG.md @@ -4,6 +4,8 @@ - Merchant Order Status Filter tests - Merchant Order Refund tests - Merchant Apply Coupon tests + + - Shopper Cart Apply Coupon ## Fixed From a0049c10ffadc186a4b4f0cd356bc88e079c177c Mon Sep 17 00:00:00 2001 From: Veljko V Date: Tue, 5 Jan 2021 22:48:34 +0100 Subject: [PATCH 12/66] Fix test title to be more concise --- .../e2e/core-tests/specs/shopper/front-end-cart-coupons.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/e2e/core-tests/specs/shopper/front-end-cart-coupons.test.js b/tests/e2e/core-tests/specs/shopper/front-end-cart-coupons.test.js index 64d7dc40ad0..6850401abb7 100644 --- a/tests/e2e/core-tests/specs/shopper/front-end-cart-coupons.test.js +++ b/tests/e2e/core-tests/specs/shopper/front-end-cart-coupons.test.js @@ -30,7 +30,7 @@ const runCartApplyCouponsTest = () => { await merchant.logout(); }); - it('allows customer to apply a fixed cart coupon in the cart', async () => { + it('allows customer to apply coupons in the cart', async () => { await shopper.goToShop(); await shopper.addToCartFromShopPage('Simple product'); await shopper.goToCart(); From fac6d3a246e9c004096b395896e397cb817f5349 Mon Sep 17 00:00:00 2001 From: roykho Date: Tue, 5 Jan 2021 14:16:46 -0800 Subject: [PATCH 13/66] Remove do_update_woocommerce query arg to prevent actions taken on it --- includes/admin/notes/class-wc-notes-run-db-update.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/admin/notes/class-wc-notes-run-db-update.php b/includes/admin/notes/class-wc-notes-run-db-update.php index f59074679d5..1fad100ff9e 100644 --- a/includes/admin/notes/class-wc-notes-run-db-update.php +++ b/includes/admin/notes/class-wc-notes-run-db-update.php @@ -215,7 +215,7 @@ class WC_Notes_Run_Db_Update { '_nonce_action' => 'woocommerce_hide_notices_nonce', '_nonce_name' => '_wc_notice_nonce', ), - wc_get_current_admin_url() ? wc_get_current_admin_url() : admin_url( 'admin.php?page=wc-settings' ) + wc_get_current_admin_url() ? remove_query_arg( 'do_update_woocommerce', wc_get_current_admin_url() ) : admin_url( 'admin.php?page=wc-settings' ) ) ); From a6334370a85672ca6c52c50fa55503f1a990bd64 Mon Sep 17 00:00:00 2001 From: Lee Willis Date: Wed, 6 Jan 2021 16:01:37 +0000 Subject: [PATCH 14/66] Fix visibility of checkout field settings in customizer --- .../customizer/class-wc-shop-customizer.php | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/includes/customizer/class-wc-shop-customizer.php b/includes/customizer/class-wc-shop-customizer.php index dd966b8de39..2d633188612 100644 --- a/includes/customizer/class-wc-shop-customizer.php +++ b/includes/customizer/class-wc-shop-customizer.php @@ -818,7 +818,7 @@ class WC_Shop_Customizer { 'description' => __( 'Optionally add some text about your store privacy policy to show during checkout.', 'woocommerce' ), 'section' => 'woocommerce_checkout', 'settings' => 'woocommerce_checkout_privacy_policy_text', - 'active_callback' => 'wc_privacy_policy_page_id', + 'active_callback' => array( $this, 'has_privacy_policy_page_id' ), 'type' => 'textarea', ) ); @@ -830,7 +830,7 @@ class WC_Shop_Customizer { 'description' => __( 'Optionally add some text for the terms checkbox that customers must accept.', 'woocommerce' ), 'section' => 'woocommerce_checkout', 'settings' => 'woocommerce_checkout_terms_and_conditions_checkbox_text', - 'active_callback' => 'wc_terms_and_conditions_page_id', + 'active_callback' => array( $this, 'has_terms_and_conditions_page_id' ), 'type' => 'text', ) ); @@ -865,6 +865,24 @@ class WC_Shop_Customizer { $options = array( 'hidden', 'optional', 'required' ); return in_array( $value, $options, true ) ? $value : ''; } + + /** + * Whether or not a page has been chose for the privacy policy. + * + * @return bool + */ + public function has_privacy_policy_page_id() { + return wc_privacy_policy_page_id() > 0; + } + + /** + * Whether or not a page has been chose for the terms and conditions. + * + * @return bool + */ + public function has_terms_and_conditions_page_id() { + return wc_terms_and_conditions_page_id() > 0; + } } new WC_Shop_Customizer(); From cf0a4df04b2c6bf08b0322c4054443a99f8bbac7 Mon Sep 17 00:00:00 2001 From: Veljko Date: Thu, 7 Jan 2021 20:59:53 +0100 Subject: [PATCH 15/66] Improve test case and components --- .../shopper/front-end-cart-coupons.test.js | 19 +++++++++++-------- tests/e2e/utils/src/components.js | 6 +++--- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/tests/e2e/core-tests/specs/shopper/front-end-cart-coupons.test.js b/tests/e2e/core-tests/specs/shopper/front-end-cart-coupons.test.js index 6850401abb7..d77bf76274b 100644 --- a/tests/e2e/core-tests/specs/shopper/front-end-cart-coupons.test.js +++ b/tests/e2e/core-tests/specs/shopper/front-end-cart-coupons.test.js @@ -21,12 +21,15 @@ const { const runCartApplyCouponsTest = () => { describe('Cart applying coupons', () => { + let couponFixedCart; + let couponPercentage; + let couponFixedProduct; beforeAll(async () => { await merchant.login(); await createSimpleProduct(); - await createCoupon('Fixed cart discount'); - await createCoupon('Percentage discount', '50'); - await createCoupon('Fixed product discount'); + couponFixedCart = await createCoupon(); + couponPercentage = await createCoupon('50', 'Percentage discount'); + couponFixedProduct = await createCoupon('5', 'Fixed product discount'); await merchant.logout(); }); @@ -37,7 +40,7 @@ const runCartApplyCouponsTest = () => { await shopper.productIsInCart('Simple product'); // Apply Fixed cart discount coupon - await expect(page).toFill('#coupon_code', 'Code-Fixed cart discount'); + await expect(page).toFill('#coupon_code', couponFixedCart); await expect(page).toClick('button', {text: 'Apply coupon'}); await uiUnblocked(); await page.waitForSelector('.woocommerce-message', {text: 'Coupon code applied successfully.'}); @@ -55,7 +58,7 @@ const runCartApplyCouponsTest = () => { await page.waitForSelector('.woocommerce-message', {text: 'Coupon has been removed.'}); // Apply Percentage discount coupon - await expect(page).toFill('#coupon_code', 'Code-Percentage discount'); + await expect(page).toFill('#coupon_code', couponPercentage); await expect(page).toClick('button', {text: 'Apply coupon'}); await uiUnblocked(); await page.waitForSelector('.woocommerce-message', {text: 'Coupon code applied successfully.'}); @@ -68,7 +71,7 @@ const runCartApplyCouponsTest = () => { await page.waitForSelector('.woocommerce-message', {text: 'Coupon has been removed.'}); // Apply Fixed product discount coupon - await expect(page).toFill('#coupon_code', 'Code-Fixed product discount'); + await expect(page).toFill('#coupon_code', couponFixedProduct); await expect(page).toClick('button', {text: 'Apply coupon'}); await uiUnblocked(); await page.waitForSelector('.woocommerce-message', {text: 'Coupon code applied successfully.'}); @@ -76,13 +79,13 @@ const runCartApplyCouponsTest = () => { await page.waitForSelector('.order-total .amount', {text: '$4.99'}); // Try to apply the same coupon - await expect(page).toFill('#coupon_code', 'Code-Fixed product discount'); + await expect(page).toFill('#coupon_code', couponFixedProduct); await expect(page).toClick('button', {text: 'Apply coupon'}); await uiUnblocked(); await page.waitForSelector('.woocommerce-error', { text: 'Coupon code already applied!' }); // Try to apply multiple coupons - await expect(page).toFill('#coupon_code', 'Code-Fixed cart discount'); + await expect(page).toFill('#coupon_code', couponFixedCart); await expect(page).toClick('button', {text: 'Apply coupon'}); await uiUnblocked(); await page.waitForSelector('.woocommerce-message', {text: 'Coupon code applied successfully.'}); diff --git a/tests/e2e/utils/src/components.js b/tests/e2e/utils/src/components.js index 848226a5bcd..9338772cb41 100644 --- a/tests/e2e/utils/src/components.js +++ b/tests/e2e/utils/src/components.js @@ -401,14 +401,14 @@ const addProductToOrder = async ( orderId, productName ) => { /** * Creates a basic coupon with the provided coupon amount. Returns the coupon code. * - * @param discountType Type of a coupon. Defaults to Fixed cart discount. * @param couponAmount Amount to be applied. Defaults to 5. + * @param discountType Type of a coupon. Defaults to Fixed cart discount. */ -const createCoupon = async ( discountType = 'Fixed cart discount', couponAmount = '5' ) => { +const createCoupon = async ( couponAmount = '5', discountType = 'Fixed cart discount' ) => { await merchant.openNewCoupon(); // Fill in coupon code - let couponCode = 'Code-' + discountType; + let couponCode = 'Code-' + discountType + new Date().getTime().toString(); await expect(page).toFill( '#title', couponCode ); // Set general coupon data From 5ceda1766456e15fb5921d4bd8a092a9d80db13a Mon Sep 17 00:00:00 2001 From: Veljko Date: Sat, 9 Jan 2021 18:53:12 +0100 Subject: [PATCH 16/66] Update code to be more concise --- tests/e2e/core-tests/specs/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/e2e/core-tests/specs/index.js b/tests/e2e/core-tests/specs/index.js index 5a40f310afb..b204f371310 100644 --- a/tests/e2e/core-tests/specs/index.js +++ b/tests/e2e/core-tests/specs/index.js @@ -9,7 +9,7 @@ const { runOnboardingFlowTest, runTaskListTest } = require( './activate-and-setu const runInitialStoreSettingsTest = require( './activate-and-setup/setup.test' ); // Shopper tests -const runCartApplyCouponsTest = require( './shopper/front-end-cart-coupons.test') +const runCartApplyCouponsTest = require( './shopper/front-end-cart-coupons.test'); const runCartPageTest = require( './shopper/front-end-cart.test' ); const runCheckoutPageTest = require( './shopper/front-end-checkout.test' ); const runMyAccountPageTest = require( './shopper/front-end-my-account.test' ); From 633251009bb86ab216459d15d2f48a591e201dc4 Mon Sep 17 00:00:00 2001 From: Veljko V Date: Mon, 11 Jan 2021 16:34:10 +0100 Subject: [PATCH 17/66] Update test to include lower case method --- .../specs/merchant/wp-admin-order-apply-coupon.test.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/e2e/core-tests/specs/merchant/wp-admin-order-apply-coupon.test.js b/tests/e2e/core-tests/specs/merchant/wp-admin-order-apply-coupon.test.js index a51d435e1d0..5f745e6af6b 100644 --- a/tests/e2e/core-tests/specs/merchant/wp-admin-order-apply-coupon.test.js +++ b/tests/e2e/core-tests/specs/merchant/wp-admin-order-apply-coupon.test.js @@ -50,7 +50,7 @@ const runOrderApplyCouponTest = () => { // Verify the coupon list is showing await page.waitForSelector('.wc-used-coupons'); await expect(page).toMatchElement('.wc_coupon_list', { text: 'Coupon(s)' }); - await expect(page).toMatchElement('.wc_coupon_list li.code.editable', { text: 'code-fixed cart discount' }); + await expect(page).toMatchElement('.wc_coupon_list li.code.editable', { text: couponCode.toLowerCase() }); // Check that the coupon has been applied await expect(page).toMatchElement('.wc-order-item-discount', { text: '5.00' }); @@ -60,7 +60,7 @@ const runOrderApplyCouponTest = () => { it('can remove a coupon', async () => { // Make sure we have a coupon on the page to use await page.waitForSelector('.wc-used-coupons'); - await expect(page).toMatchElement('.wc_coupon_list li.code.editable', { text: 'code-fixed cart discount' }); + await expect(page).toMatchElement('.wc_coupon_list li.code.editable', { text: couponCode.toLowerCase() }); // We need to use this here as `expect(page).toClick()` was unable to find the element // See: https://github.com/puppeteer/puppeteer/issues/1769#issuecomment-637645219 @@ -69,7 +69,7 @@ const runOrderApplyCouponTest = () => { await uiUnblocked(); // Verify the coupon pricing has been removed - await expect(page).not.toMatchElement('.wc_coupon_list li.code.editable', { text: 'code-fixed cart discount' }); + await expect(page).not.toMatchElement('.wc_coupon_list li.code.editable', { text: couponCode.toLowerCase() }); await expect(page).not.toMatchElement('.wc-order-item-discount', { text: '5.00' }); await expect(page).not.toMatchElement('.line-cost .view .woocommerce-Price-amount', { text: '4.99' }); From 760a879cb9aa4def74b574ac4de598cdf38e6277 Mon Sep 17 00:00:00 2001 From: Veljko V Date: Mon, 11 Jan 2021 16:45:31 +0100 Subject: [PATCH 18/66] Fix white spaces issue --- .../shopper/front-end-cart-coupons.test.js | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/tests/e2e/core-tests/specs/shopper/front-end-cart-coupons.test.js b/tests/e2e/core-tests/specs/shopper/front-end-cart-coupons.test.js index d77bf76274b..288c11eea48 100644 --- a/tests/e2e/core-tests/specs/shopper/front-end-cart-coupons.test.js +++ b/tests/e2e/core-tests/specs/shopper/front-end-cart-coupons.test.js @@ -5,7 +5,7 @@ const { shopper, merchant, - createCoupon, + createCoupon, createSimpleProduct, uiUnblocked } = require( '@woocommerce/e2e-utils' ); @@ -26,8 +26,8 @@ const runCartApplyCouponsTest = () => { let couponFixedProduct; beforeAll(async () => { await merchant.login(); - await createSimpleProduct(); - couponFixedCart = await createCoupon(); + await createSimpleProduct(); + couponFixedCart = await createCoupon(); couponPercentage = await createCoupon('50', 'Percentage discount'); couponFixedProduct = await createCoupon('5', 'Fixed product discount'); await merchant.logout(); @@ -44,19 +44,19 @@ const runCartApplyCouponsTest = () => { await expect(page).toClick('button', {text: 'Apply coupon'}); await uiUnblocked(); await page.waitForSelector('.woocommerce-message', {text: 'Coupon code applied successfully.'}); - + // Wait for page to expand total calculations to avoid flakyness await page.waitForSelector('.order-total'); // Verify discount applied and order total await page.waitForSelector('.cart-discount .amount', {text: '$5.00'}); await page.waitForSelector('.order-total .amount', {text: '$4.99'}); - + // Remove coupon await expect(page).toClick('.woocommerce-remove-coupon', {text: '[Remove]'}); await uiUnblocked(); await page.waitForSelector('.woocommerce-message', {text: 'Coupon has been removed.'}); - + // Apply Percentage discount coupon await expect(page).toFill('#coupon_code', couponPercentage); await expect(page).toClick('button', {text: 'Apply coupon'}); @@ -64,7 +64,7 @@ const runCartApplyCouponsTest = () => { await page.waitForSelector('.woocommerce-message', {text: 'Coupon code applied successfully.'}); await page.waitForSelector('.cart-discount .amount', {text: '$4.99'}); await page.waitForSelector('.order-total .amount', {text: '$5.00'}); - + // Remove coupon await expect(page).toClick('.woocommerce-remove-coupon', {text: '[Remove]'}); await uiUnblocked(); @@ -77,7 +77,7 @@ const runCartApplyCouponsTest = () => { await page.waitForSelector('.woocommerce-message', {text: 'Coupon code applied successfully.'}); await page.waitForSelector('.cart-discount .amount', {text: '$5.00'}); await page.waitForSelector('.order-total .amount', {text: '$4.99'}); - + // Try to apply the same coupon await expect(page).toFill('#coupon_code', couponFixedProduct); await expect(page).toClick('button', {text: 'Apply coupon'}); @@ -94,13 +94,13 @@ const runCartApplyCouponsTest = () => { // Remove coupon await expect(page).toClick('.woocommerce-remove-coupon', {text: '[Remove]'}); await uiUnblocked(); - await page.waitForSelector('.woocommerce-message', {text: 'Coupon has been removed.'}); + await page.waitForSelector('.woocommerce-message', {text: 'Coupon has been removed.'}); await expect(page).toClick('.woocommerce-remove-coupon', {text: '[Remove]'}); await uiUnblocked(); - await page.waitForSelector('.woocommerce-message', {text: 'Coupon has been removed.'}); - - // Verify the total amount after all coupons removal - await page.waitForSelector('.order-total .amount', {text: '$9.99'}); + await page.waitForSelector('.woocommerce-message', {text: 'Coupon has been removed.'}); + + // Verify the total amount after all coupons removal + await page.waitForSelector('.order-total .amount', {text: '$9.99'}); }); }); }; From 0ae2110cd810045b5e91f350dce7c93c920daeb4 Mon Sep 17 00:00:00 2001 From: Veljko Date: Fri, 15 Jan 2021 18:20:20 +0100 Subject: [PATCH 19/66] Update test to avoid conflict --- .../specs/merchant/wp-admin-order-apply-coupon.test.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tests/e2e/core-tests/specs/merchant/wp-admin-order-apply-coupon.test.js b/tests/e2e/core-tests/specs/merchant/wp-admin-order-apply-coupon.test.js index 5f745e6af6b..41e6f4348a9 100644 --- a/tests/e2e/core-tests/specs/merchant/wp-admin-order-apply-coupon.test.js +++ b/tests/e2e/core-tests/specs/merchant/wp-admin-order-apply-coupon.test.js @@ -61,10 +61,7 @@ const runOrderApplyCouponTest = () => { // Make sure we have a coupon on the page to use await page.waitForSelector('.wc-used-coupons'); await expect(page).toMatchElement('.wc_coupon_list li.code.editable', { text: couponCode.toLowerCase() }); - - // We need to use this here as `expect(page).toClick()` was unable to find the element - // See: https://github.com/puppeteer/puppeteer/issues/1769#issuecomment-637645219 - page.$eval('a.remove-coupon', elem => elem.click()); + await evalAndClick( 'a.remove-coupon' ); await uiUnblocked(); From 00172c5085994ad2c5d0090a3e43acdeb0d12d13 Mon Sep 17 00:00:00 2001 From: Veljko Date: Fri, 15 Jan 2021 18:30:05 +0100 Subject: [PATCH 20/66] Update util changelog --- tests/e2e/utils/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/e2e/utils/CHANGELOG.md b/tests/e2e/utils/CHANGELOG.md index 48f5ad35391..4a8694eb2d9 100644 --- a/tests/e2e/utils/CHANGELOG.md +++ b/tests/e2e/utils/CHANGELOG.md @@ -17,7 +17,7 @@ - Deprecated `StoreOwnerFlow`, `CustomerFlow` in favour of `merchant`,`shopper` - `createSimpleOrder( status )` returns the ID of the order that was created -- Updated `createCoupon( couponAmount )` component to be more dynamic in functioning, now you can use any coupon discount type in tests +- Updated `createCoupon( couponAmount )` component by adding a new parameter `discountType` which allows you to use any coupon discount type in tests # 0.1.1 From 82d759c7b66f193bcf169e38a6f83045d89762f5 Mon Sep 17 00:00:00 2001 From: Jeff Ong Date: Fri, 15 Jan 2021 12:30:44 -0500 Subject: [PATCH 21/66] Adjust spacing and font sizes. --- assets/css/twenty-twenty-one.scss | 42 ++++++++++++++++++------------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/assets/css/twenty-twenty-one.scss b/assets/css/twenty-twenty-one.scss index c8fbba84047..597c6f9e3ef 100644 --- a/assets/css/twenty-twenty-one.scss +++ b/assets/css/twenty-twenty-one.scss @@ -126,12 +126,18 @@ a.button { } } -.woocommerce-breadcrumb { - margin-bottom: 5rem; - font-size: 0.88889em; - font-family: $headings; +.site-main { + .woocommerce-breadcrumb { + margin-bottom: var(--global--spacing-vertical); + font-size: 0.88889em; + font-family: $headings; + } + .woocommerce-products-header { + margin-top: var(--global--spacing-vertical); + } } + .woocommerce-pagination { font-family: $headings; font-size: 0.88889em; @@ -350,6 +356,10 @@ a.button { border-left: none; border-right: none; } + + .product-thumbnail { + max-width: 120px; + } } } @@ -537,11 +547,16 @@ dl.variation, display: none; } - .entry-title { - margin: 0 0 2.5rem; - &::before { - margin-top: 0; + &.singular { // Needed for higher specificity to target the entry title font size + .entry-title { + font-size: var(--global--font-size-xl); + font-weight: normal; + margin: 0 0 2.5rem; + + &::before { + margin-top: 0; + } } } @@ -829,7 +844,7 @@ a.reset_variations { } h2:first-of-type { - font-size: 3rem; + font-size: var(--global--font-size-lg); margin: 0 0 2rem !important; } } @@ -1424,10 +1439,6 @@ a.reset_variations { #main { - .entry-header { - padding: 3vw 0 1.5vw; - } - .woocommerce { max-width: var(--responsive--alignwide-width); margin: 0 auto; @@ -2788,11 +2799,6 @@ a.reset_variations { .woocommerce-account { - .entry-header { - - padding-bottom: 20px !important; - } - .woocommerce-MyAccount-content { p:first-of-type { From aea6e02a2a888ef57d5a8698b729599153fe0210 Mon Sep 17 00:00:00 2001 From: Claudio Sanches Date: Tue, 19 Jan 2021 17:51:31 -0300 Subject: [PATCH 22/66] Fixed method with signature changed and introduced new filter instead --- .../admin/meta-boxes/class-wc-meta-box-order-actions.php | 1 + includes/emails/class-wc-email-new-order.php | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/includes/admin/meta-boxes/class-wc-meta-box-order-actions.php b/includes/admin/meta-boxes/class-wc-meta-box-order-actions.php index 27a00dfe14a..8b47c45830b 100644 --- a/includes/admin/meta-boxes/class-wc-meta-box-order-actions.php +++ b/includes/admin/meta-boxes/class-wc-meta-box-order-actions.php @@ -118,6 +118,7 @@ class WC_Meta_Box_Order_Actions { WC()->payment_gateways(); WC()->shipping(); + add_filter( 'woocommerce_new_order_email_allows_resend', '__return_true' ); WC()->mailer()->emails['WC_Email_New_Order']->trigger( $order->get_id(), $order, true ); do_action( 'woocommerce_after_resend_order_email', $order, 'new_order' ); diff --git a/includes/emails/class-wc-email-new-order.php b/includes/emails/class-wc-email-new-order.php index 0b50d563777..3465ae8f813 100644 --- a/includes/emails/class-wc-email-new-order.php +++ b/includes/emails/class-wc-email-new-order.php @@ -80,9 +80,8 @@ if ( ! class_exists( 'WC_Email_New_Order' ) ) : * * @param int $order_id The order ID. * @param WC_Order|false $order Order object. - * @param bool $force_send Whether to force send the email. */ - public function trigger( $order_id, $order = false, $force_send = false ) { + public function trigger( $order_id, $order = false ) { $this->setup_locale(); if ( $order_id && ! is_a( $order, 'WC_Order' ) ) { @@ -97,7 +96,8 @@ if ( ! class_exists( 'WC_Email_New_Order' ) ) : $email_already_sent = $order->get_meta( '_new_order_email_sent' ); } - if ( 'true' === $email_already_sent && ! $force_send ) { + // Prevent sending multiple times. + if ( 'true' === $email_already_sent && ! apply_filters( 'woocommerce_new_order_email_allows_resend', false ) ) { return; } From de81f2d686eb3fad1aa1f87dd89db9cabf92f2d9 Mon Sep 17 00:00:00 2001 From: Claudio Sanches Date: Tue, 19 Jan 2021 17:57:48 -0300 Subject: [PATCH 23/66] Remove filter after email is sent --- includes/admin/meta-boxes/class-wc-meta-box-order-actions.php | 1 + 1 file changed, 1 insertion(+) diff --git a/includes/admin/meta-boxes/class-wc-meta-box-order-actions.php b/includes/admin/meta-boxes/class-wc-meta-box-order-actions.php index 8b47c45830b..092493b4a29 100644 --- a/includes/admin/meta-boxes/class-wc-meta-box-order-actions.php +++ b/includes/admin/meta-boxes/class-wc-meta-box-order-actions.php @@ -120,6 +120,7 @@ class WC_Meta_Box_Order_Actions { WC()->shipping(); add_filter( 'woocommerce_new_order_email_allows_resend', '__return_true' ); WC()->mailer()->emails['WC_Email_New_Order']->trigger( $order->get_id(), $order, true ); + remove_filter( 'woocommerce_new_order_email_allows_resend', '__return_true' ); do_action( 'woocommerce_after_resend_order_email', $order, 'new_order' ); From eca9ae7e4c5548448d05b6de3bf53f23d57df037 Mon Sep 17 00:00:00 2001 From: Claudio Sanches Date: Tue, 19 Jan 2021 18:07:52 -0300 Subject: [PATCH 24/66] Updated docblock --- includes/emails/class-wc-email-new-order.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/includes/emails/class-wc-email-new-order.php b/includes/emails/class-wc-email-new-order.php index 3465ae8f813..9cc757495e3 100644 --- a/includes/emails/class-wc-email-new-order.php +++ b/includes/emails/class-wc-email-new-order.php @@ -96,7 +96,12 @@ if ( ! class_exists( 'WC_Email_New_Order' ) ) : $email_already_sent = $order->get_meta( '_new_order_email_sent' ); } - // Prevent sending multiple times. + /** + * Controls if new order emails can be resend multiple times. + * + * @since 5.0.0 + * @param bool $allows Defaults to true. + */ if ( 'true' === $email_already_sent && ! apply_filters( 'woocommerce_new_order_email_allows_resend', false ) ) { return; } From 52a2ce0300968ba78b823584c7189f38df493603 Mon Sep 17 00:00:00 2001 From: roykho Date: Wed, 20 Jan 2021 07:06:23 -0800 Subject: [PATCH 25/66] Move nonce prepartion into core utilizing filter hook from WC Admin --- includes/admin/class-wc-admin-notices.php | 46 +++++++++++++++++++++++ includes/class-woocommerce.php | 1 + 2 files changed, 47 insertions(+) diff --git a/includes/admin/class-wc-admin-notices.php b/includes/admin/class-wc-admin-notices.php index 70c76ee6120..8435ccbc68b 100644 --- a/includes/admin/class-wc-admin-notices.php +++ b/includes/admin/class-wc-admin-notices.php @@ -64,6 +64,52 @@ class WC_Admin_Notices { } } + /** + * Parses query to create nonces when available. + * + * @param object $response The WP_REST_Response we're working with. + * @return object $response The prepared WP_REST_Response object. + */ + public static function prepare_note_with_nonce( $response ) { + if ( 'wc-update-db-reminder' !== $response->data['name'] || ! isset( $response->data['actions'] ) ) { + return $response; + } + + foreach ( $response->data['actions'] as $action_key => $action ) { + $url_parts = ! empty( $action->query ) ? wp_parse_url( $action->query ) : ''; + + if ( ! isset( $url_parts['query'] ) ) { + $response->data['actions'][ $action_key ] = $action; + continue; + } + + wp_parse_str( $url_parts['query'], $params ); + + if ( array_key_exists( '_nonce_action', $params ) && array_key_exists( '_nonce_name', $params ) ) { + $_params = $params; + + unset( $_params['_nonce_action'] ); + unset( $_params['_nonce_name'] ); + + $url = $url_parts['scheme'] . '://' . $url_parts['host'] . $url_parts['path']; + + $parsed_query = wp_nonce_url( + add_query_arg( + $_params, + $url + ), + $params['_nonce_action'], + $params['_nonce_name'] + ); + + $response->data['actions'][ $action_key ]->query = html_entity_decode( $parsed_query ); + $response->data['actions'][ $action_key ]->url = html_entity_decode( $parsed_query ); + } + } + + return $response; + } + /** * Store notices to DB */ diff --git a/includes/class-woocommerce.php b/includes/class-woocommerce.php index 6a03eecbb55..15992e2ecd4 100644 --- a/includes/class-woocommerce.php +++ b/includes/class-woocommerce.php @@ -202,6 +202,7 @@ final class WooCommerce { add_action( 'switch_blog', array( $this, 'wpdb_table_fix' ), 0 ); add_action( 'activated_plugin', array( $this, 'activated_plugin' ) ); add_action( 'deactivated_plugin', array( $this, 'deactivated_plugin' ) ); + add_filter( 'woocommerce_rest_prepare_note', array( 'WC_Admin_Notices', 'prepare_note_with_nonce' ) ); } /** From 1b1b87b91805455b3404ac4814eef7d66a47a537 Mon Sep 17 00:00:00 2001 From: vedanshujain Date: Thu, 21 Jan 2021 12:35:37 +0530 Subject: [PATCH 26/66] Add changes from 4.9 RC2 and 4.9.1 to changelog. --- changelog.txt | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/changelog.txt b/changelog.txt index 44e4695e3dd..4b914e52f28 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,6 +1,10 @@ == Changelog == -= 4.9.0 = += 4.9.1 2021-01-19 = + +* Fix - Reverts #28204 to ensure compatibility with extensions using legacy do_action calls. #28835 + += 4.9.0 2021-01-12 = **WooCommerce** * Localization - Add 'Ladakh' to the list of Indian states. #28458 @@ -28,6 +32,9 @@ * Fix - Better error messages when usage limit are reached. #28592 * Fix - Adjust stock items only for statuses which reduces the stock. #28620 * Fix - Named parameter to fix DB update routine on PHP8. #28537 +* Fix - Add protection around func_get_args_call for backwards compatibility. #28677 +* Fix - Restore stock_status in REST API V3 response. #28731 +* Fix - Revert some of the changes related to perf enhancements (27735) as it was breaking stock_status filter. #28745 * Dev - Hook for intializing price slider in frontend. #28014 * Dev - Add support for running a custom initialization script for tests. #28041 * Dev - Use the coenjacobs/mozart package to renamespace vendor packages. #28147 @@ -70,6 +77,7 @@ * Fix - Preventing desktop-sized navigation placeholder from appearing on mobile during load. #5616 * Fix - Completed tasks tracking causing infinite loop #5941 * Fix - Remove Navigation access #5940 +* Fix - Compile the debug module so it can be used in older browsers like IE11. #5968 * Tweak - Fix inconsistent REST API parameter name for customer type filtering. #5823 * Tweak - Improve styles of the tax task. #5709 * Tweak - Do not show store setup link on the homescreen. #5801 From 06c8fb0c4513a311a1c03111743c82d957cc7e28 Mon Sep 17 00:00:00 2001 From: Vedanshu Jain Date: Thu, 21 Jan 2021 13:09:19 +0530 Subject: [PATCH 27/66] Adds a workflow to be triggered when a PR is creted/updated: 1. Build a zip file for that PR. 2. Migrate all unit tests from Travis to Github Actions. --- .github/workflows/pr-build.yml | 88 ++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 .github/workflows/pr-build.yml diff --git a/.github/workflows/pr-build.yml b/.github/workflows/pr-build.yml new file mode 100644 index 00000000000..5f482b006de --- /dev/null +++ b/.github/workflows/pr-build.yml @@ -0,0 +1,88 @@ +name: Build zip for PR +on: + pull_request +jobs: + build: + name: Build zip for PR + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v2 + - name: Build + id: build + uses: woocommerce/action-build@v2 + - name: Upload PR zip + uses: actions/upload-artifact@v2 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + name: woocommerce.zip + path: ${{ steps.build.outputs.zip_path }} + retention-days: 7 + + test: + name: PHP ${{ matrix.php }} WP ${{ matrix.wp }} unit tests + timeout-minutes: 15 + runs-on: ubuntu-latest + strategy: + matrix: + php: [ '7.0', '7.1', '7.2', '7.3', '7.4', '8.0' ] + wp: [ "latest" ] + include: + - wp: nightly + php: '7.4' + - wp: '5.5' + php: 7.2 + - wp: '5.4' + php: 7.2 + services: + database: + image: mysql:5.6 + env: + MYSQL_ROOT_PASSWORD: root + ports: + - 3306:3306 + options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=5 + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + tools: composer + extensions: mysql + coverage: none + + - name: Tool versions + run: | + php --version + composer --version + + - name: Get cached composer directories + uses: actions/cache@v2 + with: + path: | + ./packages + ./vendor + key: ${{ runner.os }}-${{ hashFiles('./composer.lock') }} + + - name: Setup and install composer + run: composer install + + - name: Add PHP8 Compatibility. + run: | + if [ "$(php -r "echo version_compare(PHP_VERSION,'8.0','>=');")" ]; then + curl -L https://github.com/woocommerce/phpunit/archive/add-compatibility-with-php8-to-phpunit-7.zip -o /tmp/phpunit-7.5-fork.zip + unzip -d /tmp/phpunit-7.5-fork /tmp/phpunit-7.5-fork.zip + composer bin phpunit config --unset platform + composer bin phpunit config repositories.0 '{"type": "path", "url": "/tmp/phpunit-7.5-fork/phpunit-add-compatibility-with-php8-to-phpunit-7", "options": {"symlink": false}}' + composer bin phpunit require --dev -W phpunit/phpunit:@dev --ignore-platform-reqs + fi + + - name: Init DB and WP + run: ./tests/bin/install.sh woo_test root root 127.0.0.1 ${{ matrix.wp }} + + - name: Run tests + run: ./vendor/bin/phpunit -c ./phpunit.xml From cf6cf3a35e55ca744327c6cc43ee127b4177c705 Mon Sep 17 00:00:00 2001 From: vedanshujain Date: Thu, 21 Jan 2021 17:33:07 +0530 Subject: [PATCH 28/66] Split builds into different files. --- .github/workflows/pr-build.yml | 67 --------------------------- .github/workflows/pr-unit-tests.yml | 70 +++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 67 deletions(-) create mode 100644 .github/workflows/pr-unit-tests.yml diff --git a/.github/workflows/pr-build.yml b/.github/workflows/pr-build.yml index 5f482b006de..445317ac548 100644 --- a/.github/workflows/pr-build.yml +++ b/.github/workflows/pr-build.yml @@ -19,70 +19,3 @@ jobs: name: woocommerce.zip path: ${{ steps.build.outputs.zip_path }} retention-days: 7 - - test: - name: PHP ${{ matrix.php }} WP ${{ matrix.wp }} unit tests - timeout-minutes: 15 - runs-on: ubuntu-latest - strategy: - matrix: - php: [ '7.0', '7.1', '7.2', '7.3', '7.4', '8.0' ] - wp: [ "latest" ] - include: - - wp: nightly - php: '7.4' - - wp: '5.5' - php: 7.2 - - wp: '5.4' - php: 7.2 - services: - database: - image: mysql:5.6 - env: - MYSQL_ROOT_PASSWORD: root - ports: - - 3306:3306 - options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=5 - steps: - - name: Checkout code - uses: actions/checkout@v2 - - - name: Setup PHP - uses: shivammathur/setup-php@v2 - with: - php-version: ${{ matrix.php }} - tools: composer - extensions: mysql - coverage: none - - - name: Tool versions - run: | - php --version - composer --version - - - name: Get cached composer directories - uses: actions/cache@v2 - with: - path: | - ./packages - ./vendor - key: ${{ runner.os }}-${{ hashFiles('./composer.lock') }} - - - name: Setup and install composer - run: composer install - - - name: Add PHP8 Compatibility. - run: | - if [ "$(php -r "echo version_compare(PHP_VERSION,'8.0','>=');")" ]; then - curl -L https://github.com/woocommerce/phpunit/archive/add-compatibility-with-php8-to-phpunit-7.zip -o /tmp/phpunit-7.5-fork.zip - unzip -d /tmp/phpunit-7.5-fork /tmp/phpunit-7.5-fork.zip - composer bin phpunit config --unset platform - composer bin phpunit config repositories.0 '{"type": "path", "url": "/tmp/phpunit-7.5-fork/phpunit-add-compatibility-with-php8-to-phpunit-7", "options": {"symlink": false}}' - composer bin phpunit require --dev -W phpunit/phpunit:@dev --ignore-platform-reqs - fi - - - name: Init DB and WP - run: ./tests/bin/install.sh woo_test root root 127.0.0.1 ${{ matrix.wp }} - - - name: Run tests - run: ./vendor/bin/phpunit -c ./phpunit.xml diff --git a/.github/workflows/pr-unit-tests.yml b/.github/workflows/pr-unit-tests.yml new file mode 100644 index 00000000000..6563779641f --- /dev/null +++ b/.github/workflows/pr-unit-tests.yml @@ -0,0 +1,70 @@ +name: Run unit tests on PR +on: + pull_request +jobs: + test: + name: PHP ${{ matrix.php }} WP ${{ matrix.wp }} + timeout-minutes: 15 + runs-on: ubuntu-latest + strategy: + matrix: + php: [ '7.0', '7.1', '7.2', '7.3', '7.4', '8.0' ] + wp: [ "latest" ] + include: + - wp: nightly + php: '7.4' + - wp: '5.5' + php: 7.2 + - wp: '5.4' + php: 7.2 + services: + database: + image: mysql:5.6 + env: + MYSQL_ROOT_PASSWORD: root + ports: + - 3306:3306 + options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=5 + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + tools: composer + extensions: mysql + coverage: none + + - name: Tool versions + run: | + php --version + composer --version + + - name: Get cached composer directories + uses: actions/cache@v2 + with: + path: | + ./packages + ./vendor + key: ${{ runner.os }}-${{ hashFiles('./composer.lock') }} + + - name: Setup and install composer + run: composer install + + - name: Add PHP8 Compatibility. + run: | + if [ "$(php -r "echo version_compare(PHP_VERSION,'8.0','>=');")" ]; then + curl -L https://github.com/woocommerce/phpunit/archive/add-compatibility-with-php8-to-phpunit-7.zip -o /tmp/phpunit-7.5-fork.zip + unzip -d /tmp/phpunit-7.5-fork /tmp/phpunit-7.5-fork.zip + composer bin phpunit config --unset platform + composer bin phpunit config repositories.0 '{"type": "path", "url": "/tmp/phpunit-7.5-fork/phpunit-add-compatibility-with-php8-to-phpunit-7", "options": {"symlink": false}}' + composer bin phpunit require --dev -W phpunit/phpunit:@dev --ignore-platform-reqs + fi + + - name: Init DB and WP + run: ./tests/bin/install.sh woo_test root root 127.0.0.1 ${{ matrix.wp }} + + - name: Run tests + run: ./vendor/bin/phpunit -c ./phpunit.xml From 0bdf9a63411f1b613cd2211ae8aee8a8dcb50e7a Mon Sep 17 00:00:00 2001 From: roykho Date: Thu, 21 Jan 2021 06:27:44 -0800 Subject: [PATCH 29/66] Add in optional semicolon closes #28805 --- includes/wc-template-functions.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/wc-template-functions.php b/includes/wc-template-functions.php index 3cb0ff50245..35ed9e8b15c 100644 --- a/includes/wc-template-functions.php +++ b/includes/wc-template-functions.php @@ -352,7 +352,7 @@ function wc_no_js() { var c = document.body.className; c = c.replace(/woocommerce-no-js/, 'woocommerce-js'); document.body.className = c; - })() + })(); Date: Thu, 21 Jan 2021 12:08:20 -0800 Subject: [PATCH 30/66] Addressed feedback by adding whitelisted nonce actions --- includes/admin/class-wc-admin-notices.php | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/includes/admin/class-wc-admin-notices.php b/includes/admin/class-wc-admin-notices.php index 8435ccbc68b..7013d9b39e6 100644 --- a/includes/admin/class-wc-admin-notices.php +++ b/includes/admin/class-wc-admin-notices.php @@ -79,28 +79,27 @@ class WC_Admin_Notices { $url_parts = ! empty( $action->query ) ? wp_parse_url( $action->query ) : ''; if ( ! isset( $url_parts['query'] ) ) { - $response->data['actions'][ $action_key ] = $action; continue; } wp_parse_str( $url_parts['query'], $params ); if ( array_key_exists( '_nonce_action', $params ) && array_key_exists( '_nonce_name', $params ) ) { - $_params = $params; + $org_params = $params; - unset( $_params['_nonce_action'] ); - unset( $_params['_nonce_name'] ); + // Check to make sure we're acting on the whitelisted nonce actions. + if ( 'wc_db_update' !== $params['_nonce_action'] && 'woocommerce_hide_notices_nonce' !== $params['_nonce_action'] ) { + continue; + } + + unset( $org_params['_nonce_action'] ); + unset( $org_params['_nonce_name'] ); $url = $url_parts['scheme'] . '://' . $url_parts['host'] . $url_parts['path']; - $parsed_query = wp_nonce_url( - add_query_arg( - $_params, - $url - ), - $params['_nonce_action'], - $params['_nonce_name'] - ); + $nonce = array( $params['_nonce_name'] => wp_create_nonce( $params['_nonce_action'] ) ); + $merged_params = array_merge( $nonce, $org_params ); + $parsed_query = add_query_arg( $merged_params, $url ); $response->data['actions'][ $action_key ]->query = html_entity_decode( $parsed_query ); $response->data['actions'][ $action_key ]->url = html_entity_decode( $parsed_query ); From e86dc6a9ee10a9ca4f566090bb80c13a363da459 Mon Sep 17 00:00:00 2001 From: roykho Date: Thu, 21 Jan 2021 12:17:22 -0800 Subject: [PATCH 31/66] Remove html_entity_decode --- includes/admin/class-wc-admin-notices.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/includes/admin/class-wc-admin-notices.php b/includes/admin/class-wc-admin-notices.php index 7013d9b39e6..fa9716cc498 100644 --- a/includes/admin/class-wc-admin-notices.php +++ b/includes/admin/class-wc-admin-notices.php @@ -101,8 +101,8 @@ class WC_Admin_Notices { $merged_params = array_merge( $nonce, $org_params ); $parsed_query = add_query_arg( $merged_params, $url ); - $response->data['actions'][ $action_key ]->query = html_entity_decode( $parsed_query ); - $response->data['actions'][ $action_key ]->url = html_entity_decode( $parsed_query ); + $response->data['actions'][ $action_key ]->query = $parsed_query; + $response->data['actions'][ $action_key ]->url = $parsed_query; } } From 64f9b8e4b56cfc4ceb5053fbb680868580c6ef65 Mon Sep 17 00:00:00 2001 From: Claudio Sanches Date: Thu, 21 Jan 2021 22:51:15 -0300 Subject: [PATCH 32/66] Introduced "add comment" step to pr-build workflow --- .github/workflows/pr-build.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.github/workflows/pr-build.yml b/.github/workflows/pr-build.yml index 445317ac548..48ecb8e644d 100644 --- a/.github/workflows/pr-build.yml +++ b/.github/workflows/pr-build.yml @@ -19,3 +19,14 @@ jobs: name: woocommerce.zip path: ${{ steps.build.outputs.zip_path }} retention-days: 7 + - name: Add comment + uses: actions/github-script@v3 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + github.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: 'Testing comment...' + }) From 56ee106f34d3c09f2c8c8110a4df41b287561d46 Mon Sep 17 00:00:00 2001 From: Claudio Sanches Date: Thu, 21 Jan 2021 23:49:33 -0300 Subject: [PATCH 33/66] Add new message --- .github/workflows/pr-build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pr-build.yml b/.github/workflows/pr-build.yml index 48ecb8e644d..49fd2b5ee9a 100644 --- a/.github/workflows/pr-build.yml +++ b/.github/workflows/pr-build.yml @@ -28,5 +28,5 @@ jobs: issue_number: context.issue.number, owner: context.repo.owner, repo: context.repo.repo, - body: 'Testing comment...' + body: ':package: Artifacts ready for [download](https://github.com/${{ context.repo.owner }}/${{ context.repo.repo }}/actions/runs/${{ github.run_id }})!' }) From bac26b82b9a07d962890fdd02993b3c7154e8ccc Mon Sep 17 00:00:00 2001 From: Claudio Sanches Date: Thu, 21 Jan 2021 23:54:00 -0300 Subject: [PATCH 34/66] Fixed params --- .github/workflows/pr-build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pr-build.yml b/.github/workflows/pr-build.yml index 49fd2b5ee9a..a984cea0d62 100644 --- a/.github/workflows/pr-build.yml +++ b/.github/workflows/pr-build.yml @@ -28,5 +28,5 @@ jobs: issue_number: context.issue.number, owner: context.repo.owner, repo: context.repo.repo, - body: ':package: Artifacts ready for [download](https://github.com/${{ context.repo.owner }}/${{ context.repo.repo }}/actions/runs/${{ github.run_id }})!' + body: ':package: Artifacts ready for [download](https://github.com/${{ github.repository_owner }}/${{ github.repository }}/actions/runs/${{ github.run_id }})!' }) From 615bcfdbc28a60b15b330b33550abfd8eefedc29 Mon Sep 17 00:00:00 2001 From: Claudio Sanches Date: Fri, 22 Jan 2021 00:18:07 -0300 Subject: [PATCH 35/66] Fixed repository URL --- .github/workflows/pr-build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pr-build.yml b/.github/workflows/pr-build.yml index a984cea0d62..f7252f21690 100644 --- a/.github/workflows/pr-build.yml +++ b/.github/workflows/pr-build.yml @@ -28,5 +28,5 @@ jobs: issue_number: context.issue.number, owner: context.repo.owner, repo: context.repo.repo, - body: ':package: Artifacts ready for [download](https://github.com/${{ github.repository_owner }}/${{ github.repository }}/actions/runs/${{ github.run_id }})!' + body: ':package: Artifacts ready for [download](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})!' }) From 3a53c31aea635f2f789cc810a2163497c091636f Mon Sep 17 00:00:00 2001 From: Menaka S Date: Fri, 22 Jan 2021 10:31:22 +0530 Subject: [PATCH 36/66] Do not return default values for first/last order times --- includes/class-wc-tracker.php | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/includes/class-wc-tracker.php b/includes/class-wc-tracker.php index 1576cc1b23d..0c15c2446e8 100644 --- a/includes/class-wc-tracker.php +++ b/includes/class-wc-tracker.php @@ -364,8 +364,9 @@ class WC_Tracker { ); $first = time(); + $processing_first = $first; + $first_time = $first; $last = 0; - $processing_first = time(); $processing_last = 0; $orders = wc_get_orders( $args ); @@ -445,10 +446,21 @@ class WC_Tracker { $orders_count = count( $orders ); } - $order_data['first'] = gmdate( 'Y-m-d H:i:s', $first ); - $order_data['last'] = gmdate( 'Y-m-d H:i:s', $last ); - $order_data['processing_first'] = gmdate( 'Y-m-d H:i:s', $processing_first ); - $order_data['processing_last'] = gmdate( 'Y-m-d H:i:s', $processing_last ); + if( $first !== $first_time ) { + $order_data['first'] = gmdate( 'Y-m-d H:i:s', $first ); + } + + if( $processing_first !== $first_time ) { + $order_data['processing_first'] = gmdate( 'Y-m-d H:i:s', $processing_first ); + } + + if( $last ) { + $order_data['last'] = gmdate( 'Y-m-d H:i:s', $last ); + } + + if( $processing_last ) { + $order_data['processing_last'] = gmdate( 'Y-m-d H:i:s', $processing_last ); + } return $order_data; } From 197a126aab19e16ed281d160d09f4528fd71ea27 Mon Sep 17 00:00:00 2001 From: Leif Singer Date: Wed, 20 Jan 2021 17:15:46 +0100 Subject: [PATCH 37/66] E2E: add registered shopper checkout test --- tests/e2e/core-tests/CHANGELOG.md | 2 + tests/e2e/core-tests/README.md | 11 +- tests/e2e/core-tests/specs/index.js | 3 + .../front-end-checkout-registered.test.js | 130 ++++++++++++++++++ .../front-end/test-checkout-registered.js | 6 + 5 files changed, 147 insertions(+), 5 deletions(-) create mode 100644 tests/e2e/core-tests/specs/shopper/front-end-checkout-registered.test.js create mode 100644 tests/e2e/specs/front-end/test-checkout-registered.js diff --git a/tests/e2e/core-tests/CHANGELOG.md b/tests/e2e/core-tests/CHANGELOG.md index e8e488b130d..4317c941d13 100644 --- a/tests/e2e/core-tests/CHANGELOG.md +++ b/tests/e2e/core-tests/CHANGELOG.md @@ -1,6 +1,8 @@ # Unreleased ## Added + +- Registered Shopper Checkout tests - Merchant Order Status Filter tests - Merchant Order Refund tests - Merchant Apply Coupon tests diff --git a/tests/e2e/core-tests/README.md b/tests/e2e/core-tests/README.md index e6947caf6c3..135b8803a64 100644 --- a/tests/e2e/core-tests/README.md +++ b/tests/e2e/core-tests/README.md @@ -37,7 +37,7 @@ The functions to access the core tests are: ### Activation and setup - `runSetupOnboardingTests` - Run all setup and onboarding tests - - `runActivationTest` - Merchant can activate WooCommerce + - `runActivationTest` - Merchant can activate WooCommerce - `runOnboardingFlowTest` - Merchant can complete onboarding flow - `runTaskListTest` - Merchant can complete onboarding task list - `runInitialStoreSettingsTest` - Merchant can complete initial settings @@ -45,7 +45,7 @@ The functions to access the core tests are: ### Merchant - `runMerchantTests` - Run all merchant tests - - `runCreateCouponTest` - Merchant can create coupon + - `runCreateCouponTest` - Merchant can create coupon - `runCreateOrderTest` - Merchant can create order - `runAddSimpleProductTest` - Merchant can create a simple product - `runAddVariableProductTest` - Merchant can create a variable product @@ -59,10 +59,11 @@ The functions to access the core tests are: ### Shopper - `runShopperTests` - Run all shopper tests - - `runCartPageTest` - Shopper can view and update cart + - `runCartPageTest` - Shopper can view and update cart - `runCheckoutPageTest` - Shopper can complete checkout + - `runCheckoutRegisteredPageTest` - Registered shopper can complete checkout - `runMyAccountPageTest` - Shopper can access my account page - - `runSingleProductPageTest` - Shopper can view single product page + - `runSingleProductPageTest` - Shopper can view single product page ## Contributing a new test @@ -92,7 +93,7 @@ const runExampleTestName = () => { module.exports = runExampleTestName; ``` -- Add your test to `tests/e2e/core-tests/specs/index.js` +- Add your test to `tests/e2e/core-tests/specs/index.js` ```js const runExampleTestName = require( './grouping/example-test-name.test' ); // ... diff --git a/tests/e2e/core-tests/specs/index.js b/tests/e2e/core-tests/specs/index.js index 9012ab881aa..68175eaf46b 100644 --- a/tests/e2e/core-tests/specs/index.js +++ b/tests/e2e/core-tests/specs/index.js @@ -12,6 +12,7 @@ const runInitialStoreSettingsTest = require( './activate-and-setup/setup.test' ) const runCartPageTest = require( './shopper/front-end-cart.test' ); const runCheckoutApplyCouponsTest = require( './shopper/front-end-checkout-coupons.test'); const runCheckoutPageTest = require( './shopper/front-end-checkout.test' ); +const runCheckoutRegisteredPageTest = require( './shopper/front-end-checkout-registered.test' ); const runMyAccountPageTest = require( './shopper/front-end-my-account.test' ); const runSingleProductPageTest = require( './shopper/front-end-single-product.test' ); @@ -37,6 +38,7 @@ const runShopperTests = () => { runCartPageTest(); runCheckoutApplyCouponsTest(); runCheckoutPageTest(); + runCheckoutRegisteredPageTest(); runMyAccountPageTest(); runSingleProductPageTest(); }; @@ -63,6 +65,7 @@ module.exports = { runCartPageTest, runCheckoutApplyCouponsTest, runCheckoutPageTest, + runCheckoutRegisteredPageTest, runMyAccountPageTest, runSingleProductPageTest, runShopperTests, diff --git a/tests/e2e/core-tests/specs/shopper/front-end-checkout-registered.test.js b/tests/e2e/core-tests/specs/shopper/front-end-checkout-registered.test.js new file mode 100644 index 00000000000..446d69120fb --- /dev/null +++ b/tests/e2e/core-tests/specs/shopper/front-end-checkout-registered.test.js @@ -0,0 +1,130 @@ +/* eslint-disable jest/no-export, jest/no-disabled-tests, jest/expect-expect, jest/no-standalone-expect */ +/** + * Internal dependencies + */ +const { + shopper, + merchant, + createSimpleProduct, + setCheckbox, + settingsPageSaveChanges, + uiUnblocked, + verifyCheckboxIsSet +} = require( '@woocommerce/e2e-utils' ); + +const config = require( 'config' ); +const simpleProductName = config.get( 'products.simple.name' ); +const singleProductPrice = config.has('products.simple.price') ? config.get('products.simple.price') : '9.99'; + +let orderId; + +const runCheckoutRegisteredPageTest = () => { + describe('Checkout page', () => { + beforeAll(async () => { + await merchant.login(); + await createSimpleProduct(); + + // Go to general settings page + await merchant.openSettings('general'); + + // Set base location with state CA. + await expect(page).toSelect('select[name="woocommerce_default_country"]', 'United States (US) — California'); + // Sell to all countries + await expect(page).toSelect('#woocommerce_allowed_countries', 'Sell to all countries'); + // Set currency to USD + await expect(page).toSelect('#woocommerce_currency', 'United States (US) dollar ($)'); + // Tax calculation should have been enabled by another test - no-op + // Save + await settingsPageSaveChanges(); + + // Verify that settings have been saved + await Promise.all([ + expect(page).toMatchElement('#message', {text: 'Your settings have been saved.'}), + expect(page).toMatchElement('select[name="woocommerce_default_country"]', {text: 'United States (US) — California'}), + expect(page).toMatchElement('#woocommerce_allowed_countries', {text: 'Sell to all countries'}), + expect(page).toMatchElement('#woocommerce_currency', {text: 'United States (US) dollar ($)'}), + ]); + + // Enable BACS payment method + await merchant.openSettings('checkout', 'bacs'); + await setCheckbox('#woocommerce_bacs_enabled'); + await settingsPageSaveChanges(); + + // Verify that settings have been saved + await verifyCheckboxIsSet('#woocommerce_bacs_enabled'); + + // Enable COD payment method + await merchant.openSettings('checkout', 'cod'); + await setCheckbox('#woocommerce_cod_enabled'); + await settingsPageSaveChanges(); + + // Verify that settings have been saved + await verifyCheckboxIsSet('#woocommerce_cod_enabled'); + + // Enable PayPal payment method + await merchant.openSettings('checkout', 'paypal'); + await setCheckbox('#woocommerce_paypal_enabled'); + await settingsPageSaveChanges(); + + // Verify that settings have been saved + await verifyCheckboxIsSet('#woocommerce_paypal_enabled'); + + await merchant.logout(); + }); + + it('allows existing customer to place order', async () => { + await shopper.login(); + await shopper.goToShop(); + await shopper.addToCartFromShopPage(simpleProductName); + await shopper.goToCheckout(); + await shopper.productIsInCheckout(simpleProductName, `1`, singleProductPrice, singleProductPrice); + await shopper.fillBillingDetails(config.get('addresses.customer.billing')); + + await uiUnblocked(); + + await expect(page).toClick('.wc_payment_method label', {text: 'Cash on delivery'}); + await expect(page).toMatchElement('.payment_method_cod', {text: 'Pay with cash upon delivery.'}); + await uiUnblocked(); + await shopper.placeOrder(); + + await expect(page).toMatch('Order received'); + + // Get order ID from the order received html element on the page + let orderReceivedHtmlElement = await page.$('.woocommerce-order-overview__order.order'); + let orderReceivedText = await page.evaluate(element => element.textContent, orderReceivedHtmlElement); + return orderId = orderReceivedText.split(/(\s+)/)[6].toString(); + }); + + it('store owner can confirm the order was received', async () => { + await merchant.login(); + await merchant.openAllOrdersView(); + + // Click on the order which was placed in the previous step + await Promise.all([ + page.click('#post-' + orderId), + page.waitForNavigation({waitUntil: 'networkidle0'}), + ]); + + // Verify that the order page is indeed of the order that was placed + // Verify order number + await expect(page).toMatchElement('.woocommerce-order-data__heading', {text: 'Order #' + orderId + ' details'}); + + // Verify product name + await expect(page).toMatchElement('.wc-order-item-name', {text: simpleProductName}); + + // Verify product cost + await expect(page).toMatchElement('.woocommerce-Price-amount.amount', {text: singleProductPrice}); + + // Verify product quantity + await expect(page).toMatchElement('.quantity', {text: '1'}); + + // Verify total order amount without shipping + await expect(page).toMatchElement('.line_cost', {text: singleProductPrice}); + + // Verify customer profile link is present to verify order was placed by a registered customer, not a guest + await expect( page ).toMatchElement( 'label[for="customer_user"] a[href*=user-edit]', { text: 'Profile' } ); + }); + }); +}; + +module.exports = runCheckoutRegisteredPageTest; diff --git a/tests/e2e/specs/front-end/test-checkout-registered.js b/tests/e2e/specs/front-end/test-checkout-registered.js new file mode 100644 index 00000000000..f87d0d4715c --- /dev/null +++ b/tests/e2e/specs/front-end/test-checkout-registered.js @@ -0,0 +1,6 @@ +/* + * Internal dependencies + */ +const { runCheckoutRegisteredPageTest } = require( '@woocommerce/e2e-core-tests' ); + +runCheckoutRegisteredPageTest(); From 1bafa98a66b41d1a9f7a2bc5a7ae85a34960a9fb Mon Sep 17 00:00:00 2001 From: Leif Singer Date: Fri, 22 Jan 2021 11:16:01 +0100 Subject: [PATCH 38/66] move registered customer checkout test to general checkout tests --- tests/e2e/core-tests/README.md | 1 - tests/e2e/core-tests/specs/index.js | 3 - .../front-end-checkout-registered.test.js | 130 ------------------ .../specs/shopper/front-end-checkout.test.js | 74 +++++++--- .../front-end/test-checkout-registered.js | 6 - 5 files changed, 55 insertions(+), 159 deletions(-) delete mode 100644 tests/e2e/core-tests/specs/shopper/front-end-checkout-registered.test.js delete mode 100644 tests/e2e/specs/front-end/test-checkout-registered.js diff --git a/tests/e2e/core-tests/README.md b/tests/e2e/core-tests/README.md index 135b8803a64..a75a039f18c 100644 --- a/tests/e2e/core-tests/README.md +++ b/tests/e2e/core-tests/README.md @@ -61,7 +61,6 @@ The functions to access the core tests are: - `runShopperTests` - Run all shopper tests - `runCartPageTest` - Shopper can view and update cart - `runCheckoutPageTest` - Shopper can complete checkout - - `runCheckoutRegisteredPageTest` - Registered shopper can complete checkout - `runMyAccountPageTest` - Shopper can access my account page - `runSingleProductPageTest` - Shopper can view single product page diff --git a/tests/e2e/core-tests/specs/index.js b/tests/e2e/core-tests/specs/index.js index 68175eaf46b..9012ab881aa 100644 --- a/tests/e2e/core-tests/specs/index.js +++ b/tests/e2e/core-tests/specs/index.js @@ -12,7 +12,6 @@ const runInitialStoreSettingsTest = require( './activate-and-setup/setup.test' ) const runCartPageTest = require( './shopper/front-end-cart.test' ); const runCheckoutApplyCouponsTest = require( './shopper/front-end-checkout-coupons.test'); const runCheckoutPageTest = require( './shopper/front-end-checkout.test' ); -const runCheckoutRegisteredPageTest = require( './shopper/front-end-checkout-registered.test' ); const runMyAccountPageTest = require( './shopper/front-end-my-account.test' ); const runSingleProductPageTest = require( './shopper/front-end-single-product.test' ); @@ -38,7 +37,6 @@ const runShopperTests = () => { runCartPageTest(); runCheckoutApplyCouponsTest(); runCheckoutPageTest(); - runCheckoutRegisteredPageTest(); runMyAccountPageTest(); runSingleProductPageTest(); }; @@ -65,7 +63,6 @@ module.exports = { runCartPageTest, runCheckoutApplyCouponsTest, runCheckoutPageTest, - runCheckoutRegisteredPageTest, runMyAccountPageTest, runSingleProductPageTest, runShopperTests, diff --git a/tests/e2e/core-tests/specs/shopper/front-end-checkout-registered.test.js b/tests/e2e/core-tests/specs/shopper/front-end-checkout-registered.test.js deleted file mode 100644 index 446d69120fb..00000000000 --- a/tests/e2e/core-tests/specs/shopper/front-end-checkout-registered.test.js +++ /dev/null @@ -1,130 +0,0 @@ -/* eslint-disable jest/no-export, jest/no-disabled-tests, jest/expect-expect, jest/no-standalone-expect */ -/** - * Internal dependencies - */ -const { - shopper, - merchant, - createSimpleProduct, - setCheckbox, - settingsPageSaveChanges, - uiUnblocked, - verifyCheckboxIsSet -} = require( '@woocommerce/e2e-utils' ); - -const config = require( 'config' ); -const simpleProductName = config.get( 'products.simple.name' ); -const singleProductPrice = config.has('products.simple.price') ? config.get('products.simple.price') : '9.99'; - -let orderId; - -const runCheckoutRegisteredPageTest = () => { - describe('Checkout page', () => { - beforeAll(async () => { - await merchant.login(); - await createSimpleProduct(); - - // Go to general settings page - await merchant.openSettings('general'); - - // Set base location with state CA. - await expect(page).toSelect('select[name="woocommerce_default_country"]', 'United States (US) — California'); - // Sell to all countries - await expect(page).toSelect('#woocommerce_allowed_countries', 'Sell to all countries'); - // Set currency to USD - await expect(page).toSelect('#woocommerce_currency', 'United States (US) dollar ($)'); - // Tax calculation should have been enabled by another test - no-op - // Save - await settingsPageSaveChanges(); - - // Verify that settings have been saved - await Promise.all([ - expect(page).toMatchElement('#message', {text: 'Your settings have been saved.'}), - expect(page).toMatchElement('select[name="woocommerce_default_country"]', {text: 'United States (US) — California'}), - expect(page).toMatchElement('#woocommerce_allowed_countries', {text: 'Sell to all countries'}), - expect(page).toMatchElement('#woocommerce_currency', {text: 'United States (US) dollar ($)'}), - ]); - - // Enable BACS payment method - await merchant.openSettings('checkout', 'bacs'); - await setCheckbox('#woocommerce_bacs_enabled'); - await settingsPageSaveChanges(); - - // Verify that settings have been saved - await verifyCheckboxIsSet('#woocommerce_bacs_enabled'); - - // Enable COD payment method - await merchant.openSettings('checkout', 'cod'); - await setCheckbox('#woocommerce_cod_enabled'); - await settingsPageSaveChanges(); - - // Verify that settings have been saved - await verifyCheckboxIsSet('#woocommerce_cod_enabled'); - - // Enable PayPal payment method - await merchant.openSettings('checkout', 'paypal'); - await setCheckbox('#woocommerce_paypal_enabled'); - await settingsPageSaveChanges(); - - // Verify that settings have been saved - await verifyCheckboxIsSet('#woocommerce_paypal_enabled'); - - await merchant.logout(); - }); - - it('allows existing customer to place order', async () => { - await shopper.login(); - await shopper.goToShop(); - await shopper.addToCartFromShopPage(simpleProductName); - await shopper.goToCheckout(); - await shopper.productIsInCheckout(simpleProductName, `1`, singleProductPrice, singleProductPrice); - await shopper.fillBillingDetails(config.get('addresses.customer.billing')); - - await uiUnblocked(); - - await expect(page).toClick('.wc_payment_method label', {text: 'Cash on delivery'}); - await expect(page).toMatchElement('.payment_method_cod', {text: 'Pay with cash upon delivery.'}); - await uiUnblocked(); - await shopper.placeOrder(); - - await expect(page).toMatch('Order received'); - - // Get order ID from the order received html element on the page - let orderReceivedHtmlElement = await page.$('.woocommerce-order-overview__order.order'); - let orderReceivedText = await page.evaluate(element => element.textContent, orderReceivedHtmlElement); - return orderId = orderReceivedText.split(/(\s+)/)[6].toString(); - }); - - it('store owner can confirm the order was received', async () => { - await merchant.login(); - await merchant.openAllOrdersView(); - - // Click on the order which was placed in the previous step - await Promise.all([ - page.click('#post-' + orderId), - page.waitForNavigation({waitUntil: 'networkidle0'}), - ]); - - // Verify that the order page is indeed of the order that was placed - // Verify order number - await expect(page).toMatchElement('.woocommerce-order-data__heading', {text: 'Order #' + orderId + ' details'}); - - // Verify product name - await expect(page).toMatchElement('.wc-order-item-name', {text: simpleProductName}); - - // Verify product cost - await expect(page).toMatchElement('.woocommerce-Price-amount.amount', {text: singleProductPrice}); - - // Verify product quantity - await expect(page).toMatchElement('.quantity', {text: '1'}); - - // Verify total order amount without shipping - await expect(page).toMatchElement('.line_cost', {text: singleProductPrice}); - - // Verify customer profile link is present to verify order was placed by a registered customer, not a guest - await expect( page ).toMatchElement( 'label[for="customer_user"] a[href*=user-edit]', { text: 'Profile' } ); - }); - }); -}; - -module.exports = runCheckoutRegisteredPageTest; diff --git a/tests/e2e/core-tests/specs/shopper/front-end-checkout.test.js b/tests/e2e/core-tests/specs/shopper/front-end-checkout.test.js index 2928b6e38ec..c7c9567ae80 100644 --- a/tests/e2e/core-tests/specs/shopper/front-end-checkout.test.js +++ b/tests/e2e/core-tests/specs/shopper/front-end-checkout.test.js @@ -20,7 +20,8 @@ const threeProductPrice = singleProductPrice * 3; const fourProductPrice = singleProductPrice * 4; const fiveProductPrice = singleProductPrice * 5; -let orderId; +let guestOrderId; +let customerOrderId; const runCheckoutPageTest = () => { describe('Checkout page', () => { @@ -136,34 +137,69 @@ const runCheckoutPageTest = () => { // Get order ID from the order received html element on the page let orderReceivedHtmlElement = await page.$('.woocommerce-order-overview__order.order'); let orderReceivedText = await page.evaluate(element => element.textContent, orderReceivedHtmlElement); - return orderId = orderReceivedText.split(/(\s+)/)[6].toString(); + return guestOrderId = orderReceivedText.split(/(\s+)/)[6].toString(); + }); + + it('allows existing customer to place order', async () => { + await shopper.login(); + await shopper.goToShop(); + await shopper.addToCartFromShopPage(simpleProductName); + await shopper.goToCheckout(); + await shopper.productIsInCheckout(simpleProductName, `1`, singleProductPrice, singleProductPrice); + await shopper.fillBillingDetails(config.get('addresses.customer.billing')); + + await uiUnblocked(); + + await expect(page).toClick('.wc_payment_method label', {text: 'Cash on delivery'}); + await expect(page).toMatchElement('.payment_method_cod', {text: 'Pay with cash upon delivery.'}); + await uiUnblocked(); + await shopper.placeOrder(); + + await expect(page).toMatch('Order received'); + + // Get order ID from the order received html element on the page + let orderReceivedHtmlElement = await page.$('.woocommerce-order-overview__order.order'); + let orderReceivedText = await page.evaluate(element => element.textContent, orderReceivedHtmlElement); + return customerOrderId = orderReceivedText.split(/(\s+)/)[6].toString(); }); it('store owner can confirm the order was received', async () => { await merchant.login(); - await merchant.openAllOrdersView(); + // await merchant.openAllOrdersView(); - // Click on the order which was placed in the previous step - await Promise.all([ - page.click('#post-' + orderId), - page.waitForNavigation({waitUntil: 'networkidle0'}), - ]); + const checkOrder = async function(orderId, productName, productPrice, quantity, orderTotal, ensureCustomerRegistered = false) { + await merchant.openAllOrdersView(); - // Verify that the order page is indeed of the order that was placed - // Verify order number - await expect(page).toMatchElement('.woocommerce-order-data__heading', {text: 'Order #' + orderId + ' details'}); + // Click on the order which was placed in the previous step + await Promise.all([ + page.click('#post-' + orderId), + page.waitForNavigation({waitUntil: 'networkidle0'}), + ]); - // Verify product name - await expect(page).toMatchElement('.wc-order-item-name', {text: simpleProductName}); + // Verify that the order page is indeed of the order that was placed + // Verify order number + await expect(page).toMatchElement('.woocommerce-order-data__heading', {text: 'Order #' + orderId + ' details'}); - // Verify product cost - await expect(page).toMatchElement('.woocommerce-Price-amount.amount', {text: singleProductPrice}); + // Verify product name + await expect(page).toMatchElement('.wc-order-item-name', {text: productName}); - // Verify product quantity - await expect(page).toMatchElement('.quantity', {text: '5'}); + // Verify product cost + await expect(page).toMatchElement('.woocommerce-Price-amount.amount', {text: productPrice}); - // Verify total order amount without shipping - await expect(page).toMatchElement('.line_cost', {text: fiveProductPrice}); + // Verify product quantity + await expect(page).toMatchElement('.quantity', {text: quantity.toString()}); + + // Verify total order amount without shipping + await expect(page).toMatchElement('.line_cost', {text: orderTotal}); + + if ( ensureCustomerRegistered ) { + // Verify customer profile link is present to verify order was placed by a registered customer, not a guest + await expect( page ).toMatchElement( 'label[for="customer_user"] a[href*=user-edit]', { text: 'Profile' } ); + } + }; + + await checkOrder(guestOrderId, simpleProductName, singleProductPrice, 5, fiveProductPrice); + await checkOrder(customerOrderId, simpleProductName, singleProductPrice, 1, singleProductPrice, true); }); }); }; diff --git a/tests/e2e/specs/front-end/test-checkout-registered.js b/tests/e2e/specs/front-end/test-checkout-registered.js deleted file mode 100644 index f87d0d4715c..00000000000 --- a/tests/e2e/specs/front-end/test-checkout-registered.js +++ /dev/null @@ -1,6 +0,0 @@ -/* - * Internal dependencies - */ -const { runCheckoutRegisteredPageTest } = require( '@woocommerce/e2e-core-tests' ); - -runCheckoutRegisteredPageTest(); From f7cc34779ab98cb658e1f57a82bbb1d2fc0941c9 Mon Sep 17 00:00:00 2001 From: Paul Sealock Date: Fri, 22 Jan 2021 23:44:27 +1300 Subject: [PATCH 39/66] update wc-admin to 1.9.0-rc.3 --- bin/composer/phpcs/composer.lock | 27 +---------- bin/composer/phpunit/composer.lock | 30 +------------ bin/composer/wp/composer.lock | 46 +++---------------- composer.json | 2 +- composer.lock | 72 ++++++++++-------------------- 5 files changed, 34 insertions(+), 143 deletions(-) diff --git a/bin/composer/phpcs/composer.lock b/bin/composer/phpcs/composer.lock index 363928fcb9a..5a70d30ff7e 100644 --- a/bin/composer/phpcs/composer.lock +++ b/bin/composer/phpcs/composer.lock @@ -71,10 +71,6 @@ "stylecheck", "tests" ], - "support": { - "issues": "https://github.com/dealerdirect/phpcodesniffer-composer-installer/issues", - "source": "https://github.com/dealerdirect/phpcodesniffer-composer-installer" - }, "time": "2020-06-25T14:57:39+00:00" }, { @@ -133,10 +129,6 @@ "phpcs", "standards" ], - "support": { - "issues": "https://github.com/PHPCompatibility/PHPCompatibility/issues", - "source": "https://github.com/PHPCompatibility/PHPCompatibility" - }, "time": "2019-12-27T09:44:58+00:00" }, { @@ -189,10 +181,6 @@ "polyfill", "standards" ], - "support": { - "issues": "https://github.com/PHPCompatibility/PHPCompatibilityParagonie/issues", - "source": "https://github.com/PHPCompatibility/PHPCompatibilityParagonie" - }, "time": "2019-11-04T15:17:54+00:00" }, { @@ -243,10 +231,6 @@ "standards", "wordpress" ], - "support": { - "issues": "https://github.com/PHPCompatibility/PHPCompatibilityWP/issues", - "source": "https://github.com/PHPCompatibility/PHPCompatibilityWP" - }, "time": "2019-08-28T14:22:28+00:00" }, { @@ -343,10 +327,6 @@ "woocommerce", "wordpress" ], - "support": { - "issues": "https://github.com/woocommerce/woocommerce-sniffs/issues", - "source": "https://github.com/woocommerce/woocommerce-sniffs/tree/master" - }, "time": "2020-08-06T18:23:45+00:00" }, { @@ -393,11 +373,6 @@ "standards", "wordpress" ], - "support": { - "issues": "https://github.com/WordPress/WordPress-Coding-Standards/issues", - "source": "https://github.com/WordPress/WordPress-Coding-Standards", - "wiki": "https://github.com/WordPress/WordPress-Coding-Standards/wiki" - }, "time": "2020-05-13T23:57:56+00:00" } ], @@ -411,5 +386,5 @@ "platform-overrides": { "php": "7.0" }, - "plugin-api-version": "2.0.0" + "plugin-api-version": "1.1.0" } diff --git a/bin/composer/phpunit/composer.lock b/bin/composer/phpunit/composer.lock index 0f86a7f43fe..fe06e30dd0d 100644 --- a/bin/composer/phpunit/composer.lock +++ b/bin/composer/phpunit/composer.lock @@ -332,10 +332,6 @@ } ], "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", - "support": { - "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", - "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/release/4.x" - }, "time": "2019-12-28T18:55:12+00:00" }, { @@ -448,10 +444,6 @@ "spy", "stub" ], - "support": { - "issues": "https://github.com/phpspec/prophecy/issues", - "source": "https://github.com/phpspec/prophecy/tree/v1.10.3" - }, "time": "2020-03-05T15:02:03+00:00" }, { @@ -612,10 +604,6 @@ "keywords": [ "template" ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-text-template/issues", - "source": "https://github.com/sebastianbergmann/php-text-template/tree/1.2.1" - }, "time": "2015-06-21T13:50:34+00:00" }, { @@ -1236,10 +1224,6 @@ "keywords": [ "global state" ], - "support": { - "issues": "https://github.com/sebastianbergmann/global-state/issues", - "source": "https://github.com/sebastianbergmann/global-state/tree/2.0.0" - }, "time": "2017-04-27T15:39:26+00:00" }, { @@ -1504,10 +1488,6 @@ ], "description": "Library that helps with managing the version number of Git-hosted PHP projects", "homepage": "https://github.com/sebastianbergmann/version", - "support": { - "issues": "https://github.com/sebastianbergmann/version/issues", - "source": "https://github.com/sebastianbergmann/version/tree/master" - }, "time": "2016-10-03T07:35:21+00:00" }, { @@ -1627,10 +1607,6 @@ } ], "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", - "support": { - "issues": "https://github.com/theseer/tokenizer/issues", - "source": "https://github.com/theseer/tokenizer/tree/master" - }, "time": "2019-06-13T22:48:21+00:00" }, { @@ -1680,10 +1656,6 @@ "check", "validate" ], - "support": { - "issues": "https://github.com/webmozart/assert/issues", - "source": "https://github.com/webmozart/assert/tree/master" - }, "time": "2020-07-08T17:02:28+00:00" } ], @@ -1697,5 +1669,5 @@ "platform-overrides": { "php": "7.0" }, - "plugin-api-version": "2.0.0" + "plugin-api-version": "1.1.0" } diff --git a/bin/composer/wp/composer.lock b/bin/composer/wp/composer.lock index ed08b92c203..24fdbbf28cb 100644 --- a/bin/composer/wp/composer.lock +++ b/bin/composer/wp/composer.lock @@ -133,24 +133,20 @@ "translations", "unicode" ], - "support": { - "issues": "https://github.com/php-gettext/Languages/issues", - "source": "https://github.com/php-gettext/Languages/tree/2.6.0" - }, "time": "2019-11-13T10:30:21+00:00" }, { "name": "mck89/peast", - "version": "v1.11.0", + "version": "v1.12.0", "source": { "type": "git", "url": "https://github.com/mck89/peast.git", - "reference": "2a2bc6826114c46ff0bc1359208b7083a17f7a99" + "reference": "833be7a294627a8c5b1c482cbf489f73bf9b8086" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/mck89/peast/zipball/2a2bc6826114c46ff0bc1359208b7083a17f7a99", - "reference": "2a2bc6826114c46ff0bc1359208b7083a17f7a99", + "url": "https://api.github.com/repos/mck89/peast/zipball/833be7a294627a8c5b1c482cbf489f73bf9b8086", + "reference": "833be7a294627a8c5b1c482cbf489f73bf9b8086", "shasum": "" }, "require": { @@ -162,7 +158,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.11.0-dev" + "dev-master": "1.12.0-dev" } }, "autoload": { @@ -182,11 +178,7 @@ } ], "description": "Peast is PHP library that generates AST for JavaScript code", - "support": { - "issues": "https://github.com/mck89/peast/issues", - "source": "https://github.com/mck89/peast/tree/v1.11.0" - }, - "time": "2020-10-09T15:12:13+00:00" + "time": "2021-01-08T15:16:19+00:00" }, { "name": "mustache/mustache", @@ -232,10 +224,6 @@ "mustache", "templating" ], - "support": { - "issues": "https://github.com/bobthecow/mustache.php/issues", - "source": "https://github.com/bobthecow/mustache.php/tree/master" - }, "time": "2019-11-23T21:40:31+00:00" }, { @@ -285,10 +273,6 @@ "iri", "sockets" ], - "support": { - "issues": "https://github.com/rmccue/Requests/issues", - "source": "https://github.com/rmccue/Requests/tree/master" - }, "time": "2016-10-13T00:11:37+00:00" }, { @@ -398,10 +382,6 @@ ], "description": "Provides internationalization tools for WordPress projects.", "homepage": "https://github.com/wp-cli/i18n-command", - "support": { - "issues": "https://github.com/wp-cli/i18n-command/issues", - "source": "https://github.com/wp-cli/i18n-command/tree/v2.2.6" - }, "time": "2020-12-07T19:28:27+00:00" }, { @@ -450,9 +430,6 @@ ], "description": "A simple YAML loader/dumper class for PHP (WP-CLI fork)", "homepage": "https://github.com/mustangostang/spyc/", - "support": { - "source": "https://github.com/wp-cli/spyc/tree/autoload" - }, "time": "2017-04-25T11:26:20+00:00" }, { @@ -503,10 +480,6 @@ "cli", "console" ], - "support": { - "issues": "https://github.com/wp-cli/php-cli-tools/issues", - "source": "https://github.com/wp-cli/php-cli-tools/tree/master" - }, "time": "2018-09-04T13:28:00+00:00" }, { @@ -569,11 +542,6 @@ "cli", "wordpress" ], - "support": { - "docs": "https://make.wordpress.org/cli/handbook/", - "issues": "https://github.com/wp-cli/wp-cli/issues", - "source": "https://github.com/wp-cli/wp-cli" - }, "time": "2020-02-18T08:15:37+00:00" } ], @@ -587,5 +555,5 @@ "platform-overrides": { "php": "7.0" }, - "plugin-api-version": "2.0.0" + "plugin-api-version": "1.1.0" } diff --git a/composer.json b/composer.json index 130063f3882..65792a01784 100644 --- a/composer.json +++ b/composer.json @@ -21,7 +21,7 @@ "pelago/emogrifier": "3.1.0", "psr/container": "1.0.0", "woocommerce/action-scheduler": "3.1.6", - "woocommerce/woocommerce-admin": "1.8.3", + "woocommerce/woocommerce-admin": "1.9.0-rc.3", "woocommerce/woocommerce-blocks": "4.0.0" }, "require-dev": { diff --git a/composer.lock b/composer.lock index 254877de3a7..1a0ffd4d455 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "758b097c13e89200b28bf3a3b5fc2752", + "content-hash": "5fac5fbdbcff552de109cd95d469e975", "packages": [ { "name": "automattic/jetpack-autoloader", @@ -77,23 +77,20 @@ "GPL-2.0-or-later" ], "description": "A wrapper for defining constants in a more testable way.", - "support": { - "source": "https://github.com/Automattic/jetpack-constants/tree/v1.5.1" - }, "time": "2020-10-28T19:00:31+00:00" }, { "name": "composer/installers", - "version": "v1.9.0", + "version": "v1.10.0", "source": { "type": "git", "url": "https://github.com/composer/installers.git", - "reference": "b93bcf0fa1fccb0b7d176b0967d969691cd74cca" + "reference": "1a0357fccad9d1cc1ea0c9a05b8847fbccccb78d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/installers/zipball/b93bcf0fa1fccb0b7d176b0967d969691cd74cca", - "reference": "b93bcf0fa1fccb0b7d176b0967d969691cd74cca", + "url": "https://api.github.com/repos/composer/installers/zipball/1a0357fccad9d1cc1ea0c9a05b8847fbccccb78d", + "reference": "1a0357fccad9d1cc1ea0c9a05b8847fbccccb78d", "shasum": "" }, "require": { @@ -104,17 +101,18 @@ "shama/baton": "*" }, "require-dev": { - "composer/composer": "1.6.* || 2.0.*@dev", - "composer/semver": "1.0.* || 2.0.*@dev", - "phpunit/phpunit": "^4.8.36", - "sebastian/comparator": "^1.2.4", + "composer/composer": "1.6.* || ^2.0", + "composer/semver": "^1 || ^3", + "phpstan/phpstan": "^0.12.55", + "phpstan/phpstan-phpunit": "^0.12.16", + "symfony/phpunit-bridge": "^4.2 || ^5", "symfony/process": "^2.3" }, "type": "composer-plugin", "extra": { "class": "Composer\\Installers\\Plugin", "branch-alias": { - "dev-master": "1.0-dev" + "dev-main": "1.x-dev" } }, "autoload": { @@ -152,6 +150,7 @@ "Porto", "RadPHP", "SMF", + "Starbug", "Thelia", "Whmcs", "WolfCMS", @@ -192,6 +191,7 @@ "phpbb", "piwik", "ppi", + "processwire", "puppet", "pxcms", "reindex", @@ -207,21 +207,21 @@ "zend", "zikula" ], - "support": { - "issues": "https://github.com/composer/installers/issues", - "source": "https://github.com/composer/installers/tree/v1.9.0" - }, "funding": [ { "url": "https://packagist.com", "type": "custom" }, + { + "url": "https://github.com/composer", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/composer/composer", "type": "tidelift" } ], - "time": "2020-04-07T06:57:05+00:00" + "time": "2021-01-14T11:07:16+00:00" }, { "name": "maxmind-db/reader", @@ -281,10 +281,6 @@ "geolocation", "maxmind" ], - "support": { - "issues": "https://github.com/maxmind/MaxMind-DB-Reader-php/issues", - "source": "https://github.com/maxmind/MaxMind-DB-Reader-php/tree/v1.6.0" - }, "time": "2019-12-19T22:59:03+00:00" }, { @@ -359,10 +355,6 @@ "email", "pre-processing" ], - "support": { - "issues": "https://github.com/MyIntervals/emogrifier/issues", - "source": "https://github.com/MyIntervals/emogrifier" - }, "time": "2019-12-26T19:37:31+00:00" }, { @@ -412,10 +404,6 @@ "container-interop", "psr" ], - "support": { - "issues": "https://github.com/php-fig/container/issues", - "source": "https://github.com/php-fig/container/tree/master" - }, "time": "2017-02-14T16:28:37+00:00" }, { @@ -507,24 +495,20 @@ ], "description": "Action Scheduler for WordPress and WooCommerce", "homepage": "https://actionscheduler.org/", - "support": { - "issues": "https://github.com/woocommerce/action-scheduler/issues", - "source": "https://github.com/woocommerce/action-scheduler/tree/master" - }, "time": "2020-05-12T16:22:33+00:00" }, { "name": "woocommerce/woocommerce-admin", - "version": "1.8.3", + "version": "1.9.0-rc.3", "source": { "type": "git", "url": "https://github.com/woocommerce/woocommerce-admin.git", - "reference": "534442980c34369f711efdb8e85fdcb1363be712" + "reference": "8c5b20cb6347959daf5403ee30e47061f335240b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/woocommerce/woocommerce-admin/zipball/534442980c34369f711efdb8e85fdcb1363be712", - "reference": "534442980c34369f711efdb8e85fdcb1363be712", + "url": "https://api.github.com/repos/woocommerce/woocommerce-admin/zipball/8c5b20cb6347959daf5403ee30e47061f335240b", + "reference": "8c5b20cb6347959daf5403ee30e47061f335240b", "shasum": "" }, "require": { @@ -556,11 +540,7 @@ ], "description": "A modern, javascript-driven WooCommerce Admin experience.", "homepage": "https://github.com/woocommerce/woocommerce-admin", - "support": { - "issues": "https://github.com/woocommerce/woocommerce-admin/issues", - "source": "https://github.com/woocommerce/woocommerce-admin/tree/v1.8.3" - }, - "time": "2021-01-06T00:00:42+00:00" + "time": "2021-01-22T10:23:29+00:00" }, { "name": "woocommerce/woocommerce-blocks", @@ -659,10 +639,6 @@ "isolation", "tool" ], - "support": { - "issues": "https://github.com/bamarni/composer-bin-plugin/issues", - "source": "https://github.com/bamarni/composer-bin-plugin/tree/master" - }, "time": "2020-05-03T08:27:20+00:00" } ], @@ -678,5 +654,5 @@ "platform-overrides": { "php": "7.0" }, - "plugin-api-version": "2.0.0" + "plugin-api-version": "1.1.0" } From 7154b612839a93698a8572c2c2cf230c67a1c819 Mon Sep 17 00:00:00 2001 From: Leif Singer Date: Fri, 22 Jan 2021 12:44:18 +0100 Subject: [PATCH 40/66] move verifyOrder into merchant --- .../specs/shopper/front-end-checkout.test.js | 37 +------------------ tests/e2e/utils/src/flows/merchant.js | 25 +++++++++++++ 2 files changed, 27 insertions(+), 35 deletions(-) diff --git a/tests/e2e/core-tests/specs/shopper/front-end-checkout.test.js b/tests/e2e/core-tests/specs/shopper/front-end-checkout.test.js index c7c9567ae80..17e84c339c7 100644 --- a/tests/e2e/core-tests/specs/shopper/front-end-checkout.test.js +++ b/tests/e2e/core-tests/specs/shopper/front-end-checkout.test.js @@ -165,41 +165,8 @@ const runCheckoutPageTest = () => { it('store owner can confirm the order was received', async () => { await merchant.login(); - // await merchant.openAllOrdersView(); - - const checkOrder = async function(orderId, productName, productPrice, quantity, orderTotal, ensureCustomerRegistered = false) { - await merchant.openAllOrdersView(); - - // Click on the order which was placed in the previous step - await Promise.all([ - page.click('#post-' + orderId), - page.waitForNavigation({waitUntil: 'networkidle0'}), - ]); - - // Verify that the order page is indeed of the order that was placed - // Verify order number - await expect(page).toMatchElement('.woocommerce-order-data__heading', {text: 'Order #' + orderId + ' details'}); - - // Verify product name - await expect(page).toMatchElement('.wc-order-item-name', {text: productName}); - - // Verify product cost - await expect(page).toMatchElement('.woocommerce-Price-amount.amount', {text: productPrice}); - - // Verify product quantity - await expect(page).toMatchElement('.quantity', {text: quantity.toString()}); - - // Verify total order amount without shipping - await expect(page).toMatchElement('.line_cost', {text: orderTotal}); - - if ( ensureCustomerRegistered ) { - // Verify customer profile link is present to verify order was placed by a registered customer, not a guest - await expect( page ).toMatchElement( 'label[for="customer_user"] a[href*=user-edit]', { text: 'Profile' } ); - } - }; - - await checkOrder(guestOrderId, simpleProductName, singleProductPrice, 5, fiveProductPrice); - await checkOrder(customerOrderId, simpleProductName, singleProductPrice, 1, singleProductPrice, true); + await merchant.verifyOrder(guestOrderId, simpleProductName, singleProductPrice, 5, fiveProductPrice); + await merchant.verifyOrder(customerOrderId, simpleProductName, singleProductPrice, 1, singleProductPrice, true); }); }); }; diff --git a/tests/e2e/utils/src/flows/merchant.js b/tests/e2e/utils/src/flows/merchant.js index ce512174f36..f58d184a829 100644 --- a/tests/e2e/utils/src/flows/merchant.js +++ b/tests/e2e/utils/src/flows/merchant.js @@ -131,6 +131,31 @@ const merchant = { await page.waitForSelector( '#message' ); await expect( page ).toMatchElement( '#message', { text: 'Order updated.' } ); }, + + verifyOrder: async (orderId, productName, productPrice, quantity, orderTotal, ensureCustomerRegistered = false) => { + await merchant.goToOrder(orderId); + + // Verify that the order page is indeed of the order that was placed + // Verify order number + await expect(page).toMatchElement('.woocommerce-order-data__heading', {text: 'Order #' + orderId + ' details'}); + + // Verify product name + await expect(page).toMatchElement('.wc-order-item-name', {text: productName}); + + // Verify product cost + await expect(page).toMatchElement('.woocommerce-Price-amount.amount', {text: productPrice}); + + // Verify product quantity + await expect(page).toMatchElement('.quantity', {text: quantity.toString()}); + + // Verify total order amount without shipping + await expect(page).toMatchElement('.line_cost', {text: orderTotal}); + + if ( ensureCustomerRegistered ) { + // Verify customer profile link is present to verify order was placed by a registered customer, not a guest + await expect( page ).toMatchElement( 'label[for="customer_user"] a[href*=user-edit]', { text: 'Profile' } ); + } + }, }; module.exports = merchant; From 09dedb5520598493097c4976767f5655e8fe2a02 Mon Sep 17 00:00:00 2001 From: Ron Rennick Date: Fri, 22 Jan 2021 09:22:33 -0400 Subject: [PATCH 41/66] bump version in E2E packages (#28823) * bump version in core-tests, utils * bump api package version, update changelog * bump e2e-environment to 0.2.0 --- tests/e2e/api/CHANGELOG.md | 7 +++++++ tests/e2e/api/package.json | 2 +- tests/e2e/core-tests/CHANGELOG.md | 2 ++ tests/e2e/core-tests/package.json | 4 ++-- tests/e2e/env/CHANGELOG.md | 4 +++- tests/e2e/env/package.json | 2 +- tests/e2e/utils/CHANGELOG.md | 2 ++ tests/e2e/utils/package.json | 2 +- 8 files changed, 19 insertions(+), 6 deletions(-) diff --git a/tests/e2e/api/CHANGELOG.md b/tests/e2e/api/CHANGELOG.md index 99f2fd40686..ced421cefaa 100644 --- a/tests/e2e/api/CHANGELOG.md +++ b/tests/e2e/api/CHANGELOG.md @@ -1,5 +1,7 @@ # Unreleased +# 0.1.1 + ## Breaking Changes - The `HTTPClientFactory` API was changed to make it easier to configure instances with @@ -14,6 +16,11 @@ - Added a tranformation layer between API responses and internal models +## Fixed + +- issues that caused the factory creation to fail for SimpleProduct types +- a bug with OAuth signature generation when using query parameters + # 0.1.0 - Initial/beta release diff --git a/tests/e2e/api/package.json b/tests/e2e/api/package.json index 4b8dbc990aa..76ce5abae51 100644 --- a/tests/e2e/api/package.json +++ b/tests/e2e/api/package.json @@ -1,6 +1,6 @@ { "name": "@woocommerce/api", - "version": "0.1.0", + "version": "0.1.1", "author": "Automattic", "description": "A simple interface for interacting with a WooCommerce installation.", "homepage": "https://github.com/woocommerce/woocommerce/tree/master/tests/e2e/api/README.md", diff --git a/tests/e2e/core-tests/CHANGELOG.md b/tests/e2e/core-tests/CHANGELOG.md index e8e488b130d..a662968517b 100644 --- a/tests/e2e/core-tests/CHANGELOG.md +++ b/tests/e2e/core-tests/CHANGELOG.md @@ -1,5 +1,7 @@ # Unreleased +# 0.1.1 + ## Added - Merchant Order Status Filter tests - Merchant Order Refund tests diff --git a/tests/e2e/core-tests/package.json b/tests/e2e/core-tests/package.json index d742c0613f2..463f960069c 100644 --- a/tests/e2e/core-tests/package.json +++ b/tests/e2e/core-tests/package.json @@ -1,6 +1,6 @@ { "name": "@woocommerce/e2e-core-tests", - "version": "0.1.0", + "version": "0.1.1", "description": "End-To-End (E2E) tests for WooCommerce", "homepage": "https://github.com/woocommerce/woocommerce/tree/master/tests/e2e/core-tests/README.md", "repository": { @@ -14,7 +14,7 @@ "config": "3.3.3" }, "peerDependencies": { - "@woocommerce/e2e-utils": "^0.1.1" + "@woocommerce/e2e-utils": "^0.1.2" }, "publishConfig": { "access": "public" diff --git a/tests/e2e/env/CHANGELOG.md b/tests/e2e/env/CHANGELOG.md index 84051ae8c25..512c6f2547d 100644 --- a/tests/e2e/env/CHANGELOG.md +++ b/tests/e2e/env/CHANGELOG.md @@ -1,5 +1,7 @@ # Unreleased +# 0.2.0 + ## Added - support for custom container name @@ -9,7 +11,7 @@ ## Fixed - Remove redundant `puppeteer` dependency -- Support for admin user configuration from default.json +- Support for admin user configuration from `default.json` # 0.1.6 diff --git a/tests/e2e/env/package.json b/tests/e2e/env/package.json index c68d945b2c7..e9136a75c09 100644 --- a/tests/e2e/env/package.json +++ b/tests/e2e/env/package.json @@ -1,6 +1,6 @@ { "name": "@woocommerce/e2e-environment", - "version": "0.1.6", + "version": "0.2.0", "description": "WooCommerce End to End Testing Environment Configuration.", "author": "Automattic", "license": "GPL-3.0-or-later", diff --git a/tests/e2e/utils/CHANGELOG.md b/tests/e2e/utils/CHANGELOG.md index 00902bedb44..62aaae9edc1 100644 --- a/tests/e2e/utils/CHANGELOG.md +++ b/tests/e2e/utils/CHANGELOG.md @@ -1,5 +1,7 @@ # Unreleased +# 0.1.2 + ## Fixed - Missing `config` package dependency diff --git a/tests/e2e/utils/package.json b/tests/e2e/utils/package.json index 64d2618e17a..36bd9dce48a 100644 --- a/tests/e2e/utils/package.json +++ b/tests/e2e/utils/package.json @@ -1,6 +1,6 @@ { "name": "@woocommerce/e2e-utils", - "version": "0.1.1", + "version": "0.1.2", "description": "End-To-End (E2E) test utils for WooCommerce", "homepage": "https://github.com/woocommerce/woocommerce/tree/master/tests/e2e-utils/README.md", "repository": { From 49be111d8746a137baeef252e15075a11e7dc049 Mon Sep 17 00:00:00 2001 From: Ron Rennick Date: Fri, 22 Jan 2021 10:02:34 -0400 Subject: [PATCH 42/66] remove travis_retry from e2e test script (#28841) --- .travis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 3bac1469de0..e637ca63572 100644 --- a/.travis.yml +++ b/.travis.yml @@ -36,9 +36,9 @@ jobs: - npm install - composer install --no-dev script: - - travis_retry npm run build:assets - - travis_retry npm run docker:up - - travis_retry npm run test:e2e + - npm run build:assets + - npm run docker:up + - npm run test:e2e after_script: - npm run docker:down - name: "WP Nightly" From 11ae40f66c1e02db63c4021a480bac8b8b3d56d5 Mon Sep 17 00:00:00 2001 From: Claudio Sanches Date: Fri, 22 Jan 2021 12:58:45 -0300 Subject: [PATCH 43/66] Fixed "Unsupported operand types" error when using empty strings Prevents wc_price() to throw PHP 8 warnings in case of an empty string --- includes/wc-formatting-functions.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/includes/wc-formatting-functions.php b/includes/wc-formatting-functions.php index 94331f2ecc5..66e3351b437 100644 --- a/includes/wc-formatting-functions.php +++ b/includes/wc-formatting-functions.php @@ -574,6 +574,9 @@ function wc_price( $price, $args = array() ) { ) ); + // Convert to float to avoid issues on PHP 8. + $price = (float) $price; + $unformatted_price = $price; $negative = $price < 0; $price = apply_filters( 'raw_woocommerce_price', floatval( $negative ? $price * -1 : $price ) ); From 23db1a0e1ff60b9ddab507206c4da01cd15230e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Albert=20Juh=C3=A9=20Lluveras?= Date: Mon, 18 Jan 2021 10:32:29 +0100 Subject: [PATCH 44/66] Fix typos in comments --- includes/class-wc-cart.php | 2 +- includes/class-wc-payment-tokens.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/includes/class-wc-cart.php b/includes/class-wc-cart.php index d84da807701..b5683f69bc1 100644 --- a/includes/class-wc-cart.php +++ b/includes/class-wc-cart.php @@ -210,7 +210,7 @@ class WC_Cart extends WC_Legacy_Cart { } /** - * Get subtotal. + * Get subtotal_tax. * * @since 3.2.0 * @return float diff --git a/includes/class-wc-payment-tokens.php b/includes/class-wc-payment-tokens.php index 85625d02acc..2185895f0df 100644 --- a/includes/class-wc-payment-tokens.php +++ b/includes/class-wc-payment-tokens.php @@ -20,7 +20,7 @@ class WC_Payment_Tokens { * Gets valid tokens from the database based on user defined criteria. * * @since 2.6.0 - * @param array $args Query argyments { + * @param array $args Query arguments { * Array of query parameters. * * @type string $token_id Token ID. From d896ed13e879b7dbae146cbdf7363e002848aab1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Albert=20Juh=C3=A9=20Lluveras?= Date: Fri, 22 Jan 2021 17:13:41 +0100 Subject: [PATCH 45/66] Remove unnecessary dots --- includes/class-wc-cart-totals.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/includes/class-wc-cart-totals.php b/includes/class-wc-cart-totals.php index 84a04b2b80f..facee43a729 100644 --- a/includes/class-wc-cart-totals.php +++ b/includes/class-wc-cart-totals.php @@ -698,11 +698,11 @@ final class WC_Cart_Totals { /** * Subtotals are costs before discounts. * - * To prevent rounding issues we need to work with the inclusive price where possible. - * otherwise we'll see errors such as when working with a 9.99 inc price, 20% VAT which would. + * To prevent rounding issues we need to work with the inclusive price where possible + * otherwise we'll see errors such as when working with a 9.99 inc price, 20% VAT which would * be 8.325 leading to totals being 1p off. * - * Pre tax coupons come off the price the customer thinks they are paying - tax is calculated. + * Pre tax coupons come off the price the customer thinks they are paying - tax is calculated * afterwards. * * e.g. $100 bike with $10 coupon = customer pays $90 and tax worked backwards from that. From 1412d2ac96098fc4ea1248d8a109f55c3cff619d Mon Sep 17 00:00:00 2001 From: Claudio Sanches Date: Fri, 22 Jan 2021 14:57:43 -0300 Subject: [PATCH 46/66] Pass original price in filters to improve compatibility --- includes/wc-formatting-functions.php | 35 ++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/includes/wc-formatting-functions.php b/includes/wc-formatting-functions.php index 66e3351b437..0760773665b 100644 --- a/includes/wc-formatting-functions.php +++ b/includes/wc-formatting-functions.php @@ -574,13 +574,33 @@ function wc_price( $price, $args = array() ) { ) ); + $original_price = $price; + // Convert to float to avoid issues on PHP 8. $price = (float) $price; $unformatted_price = $price; $negative = $price < 0; - $price = apply_filters( 'raw_woocommerce_price', floatval( $negative ? $price * -1 : $price ) ); - $price = apply_filters( 'formatted_woocommerce_price', number_format( $price, $args['decimals'], $args['decimal_separator'], $args['thousand_separator'] ), $price, $args['decimals'], $args['decimal_separator'], $args['thousand_separator'] ); + + /** + * Filter raw price. + * + * @param float $raw_price Raw price. + * @param float|string $original_price Original price as float, or empty string. Since 5.0.0. + */ + $price = apply_filters( 'raw_woocommerce_price', floatval( $negative ? $price * -1 : $price ), $original_price ); + + /** + * Filter formatted price. + * + * @param float $formatted_price Formatted price. + * @param float $price Unformatted price. + * @param int $decimals Number of decimals. + * @param string $decimal_separator Decimal separator. + * @param string $thousand_separator Thousand separator. + * @param float|string $original_price Original price as float, or empty string. Since 5.0.0. + */ + $price = apply_filters( 'formatted_woocommerce_price', number_format( $price, $args['decimals'], $args['decimal_separator'], $args['thousand_separator'] ), $price, $args['decimals'], $args['decimal_separator'], $args['thousand_separator'], $original_price ); if ( apply_filters( 'woocommerce_price_trim_zeros', false ) && $args['decimals'] > 0 ) { $price = wc_trim_zeros( $price ); @@ -596,12 +616,13 @@ function wc_price( $price, $args = array() ) { /** * Filters the string of price markup. * - * @param string $return Price HTML markup. - * @param string $price Formatted price. - * @param array $args Pass on the args. - * @param float $unformatted_price Price as float to allow plugins custom formatting. Since 3.2.0. + * @param string $return Price HTML markup. + * @param string $price Formatted price. + * @param array $args Pass on the args. + * @param float $unformatted_price Price as float to allow plugins custom formatting. Since 3.2.0. + * @param float|string $original_price Original price as float, or empty string. Since 5.0.0. */ - return apply_filters( 'wc_price', $return, $price, $args, $unformatted_price ); + return apply_filters( 'wc_price', $return, $price, $args, $unformatted_price, $original_price ); } /** From 5d70d1919dadc4ab404376cbd081ce819fff8ed4 Mon Sep 17 00:00:00 2001 From: Claudio Sanches Date: Fri, 22 Jan 2021 15:43:21 -0300 Subject: [PATCH 47/66] Remove duplicated float --- includes/wc-formatting-functions.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/wc-formatting-functions.php b/includes/wc-formatting-functions.php index 0760773665b..916ecbcd6b8 100644 --- a/includes/wc-formatting-functions.php +++ b/includes/wc-formatting-functions.php @@ -588,7 +588,7 @@ function wc_price( $price, $args = array() ) { * @param float $raw_price Raw price. * @param float|string $original_price Original price as float, or empty string. Since 5.0.0. */ - $price = apply_filters( 'raw_woocommerce_price', floatval( $negative ? $price * -1 : $price ), $original_price ); + $price = apply_filters( 'raw_woocommerce_price', $negative ? $price * -1 : $price, $original_price ); /** * Filter formatted price. From e67003b568ab86e466f6290865876609d1e69aa0 Mon Sep 17 00:00:00 2001 From: Mehrshad Darzi Date: Sat, 23 Jan 2021 15:39:01 +0330 Subject: [PATCH 48/66] change attibutes to attributes --- includes/wc-product-functions.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/wc-product-functions.php b/includes/wc-product-functions.php index 27c2ed9ff62..8e473271159 100644 --- a/includes/wc-product-functions.php +++ b/includes/wc-product-functions.php @@ -651,7 +651,7 @@ function wc_get_product_id_by_sku( $sku ) { } /** - * Get attibutes/data for an individual variation from the database and maintain it's integrity. + * Get attributes/data for an individual variation from the database and maintain it's integrity. * * @since 2.4.0 * @param int $variation_id Variation ID. From eec4c7370142b8607ad4ff1fbc9713b8aa5c1bec Mon Sep 17 00:00:00 2001 From: Peter Fabian Date: Mon, 25 Jan 2021 12:19:55 +0100 Subject: [PATCH 49/66] Update changelogs for 4.9.2. --- changelog.txt | 4 ++++ readme.txt | 6 ++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/changelog.txt b/changelog.txt index 4b914e52f28..5bb4adee9e1 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,9 @@ == Changelog == += 4.9.2 2021-01-25 = + +* Tweak - Disable untested plugin's notice from System Status and Plugin's page. #28840 + = 4.9.1 2021-01-19 = * Fix - Reverts #28204 to ensure compatibility with extensions using legacy do_action calls. #28835 diff --git a/readme.txt b/readme.txt index 62267eeb801..0300b932bdc 100644 --- a/readme.txt +++ b/readme.txt @@ -4,7 +4,7 @@ Tags: e-commerce, store, sales, sell, woo, shop, cart, checkout, downloadable, d Requires at least: 5.3 Tested up to: 5.6 Requires PHP: 7.0 -Stable tag: 4.8.0 +Stable tag: 4.9.2 License: GPLv3 License URI: https://www.gnu.org/licenses/gpl-3.0.html @@ -160,7 +160,9 @@ WooCommerce comes with some sample data you can use to see how products look; im == Changelog == -= 4.9.0 - 2021-01-xx = += 4.9.2 2021-01-25 = + +* Tweak - Disable untested plugin's notice from System Status and Plugin's page. #28840 [See changelog for all versions](https://raw.githubusercontent.com/woocommerce/woocommerce/master/changelog.txt). From fe38d20f51e221bb69a135e7bb330c3d39ce9a34 Mon Sep 17 00:00:00 2001 From: jahir07 Date: Mon, 25 Jan 2021 17:42:27 +0600 Subject: [PATCH 50/66] Added instead of ->get_image_id() --- templates/single-product/product-image.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/single-product/product-image.php b/templates/single-product/product-image.php index 0399028c8d8..d892b4b69b0 100644 --- a/templates/single-product/product-image.php +++ b/templates/single-product/product-image.php @@ -30,7 +30,7 @@ $wrapper_classes = apply_filters( 'woocommerce_single_product_image_gallery_classes', array( 'woocommerce-product-gallery', - 'woocommerce-product-gallery--' . ( $product->get_image_id() ? 'with-images' : 'without-images' ), + 'woocommerce-product-gallery--' . ( $post_thumbnail_id ? 'with-images' : 'without-images' ), 'woocommerce-product-gallery--columns-' . absint( $columns ), 'images', ) @@ -39,7 +39,7 @@ $wrapper_classes = apply_filters(