Merge branch 'trunk' of github.com:woocommerce/woocommerce into dev/bump-woocommerce-admin-2-1-1

This commit is contained in:
Adrian Duffell 2021-03-18 06:34:42 +08:00
commit 5e988399dc
16 changed files with 241 additions and 37 deletions

View File

@ -4,6 +4,7 @@ on:
- cron: '0 0 * * *' # Run at 12 AM UTC.
jobs:
build:
if: github.repository_owner == 'woocommerce'
name: Nightly builds
strategy:
fail-fast: false

View File

@ -4398,7 +4398,6 @@ S2.define('select2/dropdown/attachBody',[
var parentOffset = $offsetParent.offset();
css.top -= parentOffset.top;
css.left -= parentOffset.left;
if (!isCurrentlyAbove && !isCurrentlyBelow) {
@ -4413,7 +4412,7 @@ S2.define('select2/dropdown/attachBody',[
if (newDirection == 'above' ||
(isCurrentlyAbove && newDirection !== 'below')) {
css.top = container.top - parentOffset.top - dropdown.height;
css.top = container.top - dropdown.height;
}
if (newDirection != null) {

View File

@ -31,11 +31,11 @@ if ( ! defined( 'ABSPATH' ) ) {
$found_method = false;
foreach ( $shipping_methods as $method ) {
$current_method = ( 0 === strpos( $item->get_method_id(), $method->id ) ) ? $item->get_method_id() : $method->id;
$is_active = $item->get_method_id() === $method->id;
echo '<option value="' . esc_attr( $current_method ) . '" ' . selected( $item->get_method_id() === $current_method, true, false ) . '>' . esc_html( $method->get_method_title() ) . '</option>';
echo '<option value="' . esc_attr( $method->id ) . '" ' . selected( true, $is_active, false ) . '>' . esc_html( $method->get_method_title() ) . '</option>';
if ( $item->get_method_id() === $current_method ) {
if ( $is_active ) {
$found_method = true;
}
}

View File

@ -1801,10 +1801,26 @@ function wc_ascii_uasort_comparison( $a, $b ) {
function wc_asort_by_locale( &$data, $locale = '' ) {
// Use Collator if PHP Internationalization Functions (php-intl) is available.
if ( class_exists( 'Collator' ) ) {
$locale = $locale ? $locale : get_locale();
$collator = new Collator( $locale );
$collator->asort( $data, Collator::SORT_STRING );
return $data;
try {
$locale = $locale ? $locale : get_locale();
$collator = new Collator( $locale );
$collator->asort( $data, Collator::SORT_STRING );
return $data;
} catch ( IntlException $e ) {
/*
* Just skip if some error got caused.
* It may be caused in installations that doesn't include ICU TZData.
*/
if ( Constants::is_true( 'WP_DEBUG' ) ) {
error_log( // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
sprintf(
'An unexpected error occurred while trying to use PHP Intl Collator class, it may be caused by an incorrect installation of PHP Intl and ICU, and could be fixed by reinstallaing PHP Intl, see more details about PHP Intl installation: %1$s. Error message: %2$s',
'https://www.php.net/manual/en/intl.installation.php',
$e->getMessage()
)
);
}
}
}
$raw_data = $data;

View File

@ -390,7 +390,7 @@ add_action( 'woocommerce_order_status_on-hold', 'wc_release_stock_for_order', 11
/**
* Return low stock amount to determine if notification needs to be sent
*
* Since 5.3.0, this function no longer redirects from variation to its parent product.
* Since 5.2.0, this function no longer redirects from variation to its parent product.
* Low stock amount can now be attached to the variation itself and if it isn't, only
* then we check the parent product, and if it's not there, then we take the default
* from the store-wide setting.

View File

@ -24,6 +24,7 @@
- Shopper Shop Browse Search Sort
- Merchant Orders Customer Checkout Page
- Shopper Cart Apply Coupon
- Merchant Order Searching
- Merchant Settings Shipping Zones
- Shopper Variable product info updates on different variations
- Merchant order emails flow

View File

@ -58,6 +58,7 @@ The functions to access the core tests are:
- `runProductEditDetailsTest` - Merchant can edit an existing product
- `runProductSearchTest` - Merchant can search for a product and view it
- `runMerchantOrdersCustomerPaymentPage` - Merchant can visit the customer payment page
- `runOrderSearchingTest` - Merchant can search for order via different terms
- `runAddNewShippingZoneTest` - Merchant can create shipping zones and let shopper test them
- `runMerchantOrderEmailsTest` - Merchant can receive order emails and resend emails by Order Actions

View File

@ -35,6 +35,7 @@ const runProductEditDetailsTest = require( './merchant/wp-admin-product-edit-det
const runProductSearchTest = require( './merchant/wp-admin-product-search.test' );
const runMerchantOrdersCustomerPaymentPage = require( './merchant/wp-admin-order-customer-payment-page.test' );
const runMerchantOrderEmailsTest = require( './merchant/wp-admin-order-emails.test' );
const runOrderSearchingTest = require( './merchant/wp-admin-order-searching.test' );
// REST API tests
const runExternalProductAPITest = require( './api/external-product.test' );
@ -62,6 +63,7 @@ const runShopperTests = () => {
};
const runMerchantTests = () => {
runOrderSearchingTest();
runAddNewShippingZoneTest();
runCreateCouponTest();
runCreateOrderTest();
@ -121,6 +123,7 @@ module.exports = {
runMerchantOrdersCustomerPaymentPage,
runMerchantOrderEmailsTest,
runMerchantTests,
runOrderSearchingTest,
runAddNewShippingZoneTest,
runProductBrowseSearchSortTest,
runApiTests,

View File

@ -0,0 +1,138 @@
/* eslint-disable jest/no-export, jest/no-disabled-tests, */
/**
* Internal dependencies
*/
const {
merchant,
clearAndFillInput,
selectOptionInSelect2,
searchForOrder,
createSimpleProduct,
addProductToOrder,
clickUpdateOrder,
} = require( '@woocommerce/e2e-utils' );
const runOrderSearchingTest = () => {
describe('WooCommerce Orders > Search orders', () => {
let orderId;
beforeAll(async () => {
await merchant.login();
await createSimpleProduct('Wanted Product');
await Promise.all([
// Create new order for testing
await merchant.openNewOrder(),
await page.waitForSelector('#order_status'),
await page.click('#customer_user'),
await page.click('span.select2-search > input.select2-search__field'),
await page.type('span.select2-search > input.select2-search__field', 'Customer'),
await page.waitFor(2000), // to avoid flakyness
await page.keyboard.press('Enter'),
]);
await Promise.all([
// Change the shipping data
await page.waitFor(1000), // to avoid flakiness
await page.waitForSelector('#_shipping_first_name'),
await clearAndFillInput('#_shipping_first_name', 'Tim'),
await clearAndFillInput('#_shipping_last_name', 'Clark'),
await clearAndFillInput('#_shipping_address_1', 'Oxford Ave'),
await clearAndFillInput('#_shipping_address_2', 'Linwood Ave'),
await clearAndFillInput('#_shipping_city', 'Buffalo'),
await clearAndFillInput('#_shipping_postcode', '14201'),
await page.keyboard.press('Tab'),
await page.keyboard.press('Tab'),
await page.keyboard.press('Enter'),
await page.select('select[name="_shipping_state"]', 'NY'),
]);
// Get the post id
const variablePostId = await page.$('#post_ID');
orderId = (await(await variablePostId.getProperty('value')).jsonValue());
// Save new order
await clickUpdateOrder('Order updated.', true);
await addProductToOrder(orderId, 'Wanted Product');
await merchant.openAllOrdersView();
});
it('can search for order by order id', async () => {
await searchForOrder(orderId, orderId, 'John Doe');
});
it('can search for order by billing first name', async () => {
await searchForOrder('John', orderId, 'John Doe');
})
it('can search for order by billing last name', async () => {
await searchForOrder('Doe', orderId, 'John Doe');
})
it('can search for order by billing company name', async () => {
await searchForOrder('Automattic', orderId, 'John Doe');
})
it('can search for order by billing first address', async () => {
await searchForOrder('addr 1', orderId, 'John Doe');
})
it('can search for order by billing second address', async () => {
await searchForOrder('addr 2', orderId, 'John Doe');
})
it('can search for order by billing city name', async () => {
await searchForOrder('San Francisco', orderId, 'John Doe');
})
it('can search for order by billing post code', async () => {
await searchForOrder('94107', orderId, 'John Doe');
})
it('can search for order by billing email', async () => {
await searchForOrder('john.doe@example.com', orderId, 'John Doe');
})
it('can search for order by billing phone', async () => {
await searchForOrder('123456789', orderId, 'John Doe');
})
it('can search for order by billing state', async () => {
await searchForOrder('CA', orderId, 'John Doe');
})
it('can search for order by shipping first name', async () => {
await searchForOrder('Tim', orderId, 'John Doe');
})
it('can search for order by shipping last name', async () => {
await searchForOrder('Clark', orderId, 'John Doe');
})
it('can search for order by shipping first address', async () => {
await searchForOrder('Oxford Ave', orderId, 'John Doe');
})
it('can search for order by shipping second address', async () => {
await searchForOrder('Linwood Ave', orderId, 'John Doe');
})
it('can search for order by shipping city name', async () => {
await searchForOrder('Buffalo', orderId, 'John Doe');
})
it('can search for order by shipping postcode name', async () => {
await searchForOrder('14201', orderId, 'John Doe');
})
it('can search for order by shipping state name', async () => {
await searchForOrder('NY', orderId, 'John Doe');
})
it('can search for order by item name', async () => {
await searchForOrder('Wanted Product', orderId, 'John Doe');
})
});
};
module.exports = runOrderSearchingTest;

View File

@ -11,6 +11,7 @@ const {
clearAndFillInput,
selectOptionInSelect2,
evalAndClick,
uiUnblocked,
} = require( '@woocommerce/e2e-utils' );
const config = require( 'config' );
@ -29,14 +30,24 @@ const runAddNewShippingZoneTest = () => {
await createSimpleProduct();
await merchant.openSettings('shipping');
// Check if you can go via blank shipping zones, otherwise remove one existing zone
// This is a workaround to avoid flakyness and to give this test more confidence
// Delete existing shipping zones.
try {
await page.click('.wc-shipping-zones-blank-state > a.wc-shipping-zone-add');
let zone = await page.$( '.wc-shipping-zone-delete' );
if ( zone ) {
// WP action links aren't clickable because they are hidden with a left=-9999 style.
await page.evaluate(() => {
document.querySelector('.wc-shipping-zone-name .row-actions')
.style
.left = '0';
});
while ( zone ) {
await evalAndClick( '.wc-shipping-zone-delete' );
await uiUnblocked();
zone = await page.$( '.wc-shipping-zone-delete' );
}
}
} catch (error) {
await evalAndClick('.wc-shipping-zone-delete');
await page.keyboard.press('Enter');
// Prevent an error here causing the test to fail.
}
});
@ -55,9 +66,9 @@ const runAddNewShippingZoneTest = () => {
await addShippingZoneAndMethod(shippingZoneNameUS);
// Set Flat rate cost
await expect(page).toClick('a.wc-shipping-zone-method-settings', {text: 'Edit'});
await expect(page).toClick('a.wc-shipping-zone-method-settings', {text: 'Flat rate'});
await clearAndFillInput('#woocommerce_flat_rate_cost', '10');
await expect(page).toClick('button#btn-ok');
await expect(page).toClick('.wc-backbone-modal-main button#btn-ok');
await merchant.logout();
});

View File

@ -66,6 +66,9 @@ const runCheckoutApplyCouponsTest = () => {
it('allows customer to apply fixed product coupon', async () => {
await applyCoupon(couponFixedProduct);
await expect(page).toMatchElement('.woocommerce-message', {text: 'Coupon code applied successfully.'});
// Verify discount applied and order total
await page.waitForSelector('.order-total');
await expect(page).toMatchElement('.cart-discount .amount', {text: '$5.00'});
await expect(page).toMatchElement('.order-total .amount', {text: '$4.99'});
await removeCoupon(couponFixedProduct);
@ -84,6 +87,9 @@ const runCheckoutApplyCouponsTest = () => {
it('allows customer to apply multiple coupons', async () => {
await applyCoupon(couponFixedProduct);
await expect(page).toMatchElement('.woocommerce-message', {text: 'Coupon code applied successfully.'});
// Verify discount applied and order total
await page.waitForSelector('.order-total');
await expect(page).toMatchElement('.order-total .amount', {text: '$0.00'});
});

View File

@ -0,0 +1,6 @@
/*
* Internal dependencies
*/
const { runOrderSearchingTest } = require( '@woocommerce/e2e-core-tests' );
runOrderSearchingTest();

View File

@ -17,6 +17,7 @@
- `createCoupon( couponAmount )` component which accepts a coupon amount string (it defaults to 5) and creates a basic coupon. Returns the generated coupon code.
- `evalAndClick( selector )` use Puppeteer page.$eval to select and click and element.
- `selectOptionInSelect2( selector, value )` util helper method that search and select in any select2 type field
- `searchForOrder( value, orderId, customerName )` util helper method that search order with different terms
- `addShippingZoneAndMethod( zoneName, zoneLocation, zipCode, zoneMethod )` util helper method for adding shipping zones with shipping methods
- `createSimpleProductWithCategory` component which creates a simple product with categories, containing three parameters for title, price and category name.
- `applyCoupon( couponName )` util helper method which applies previously created coupon to cart or checkout

View File

@ -103,10 +103,11 @@ describe( 'Cart page', () => {
| `clickFilter` | `selector` | Click on a list page filter |
| `moveAllItemsToTrash` | | Moves all items in a list view to the Trash |
| `verifyAndPublish` | `noticeText` | Verify that an item can be published |
| `selectOptionInSelect2` | `selector, value` | helper method that searchs for select2 type fields and select plus insert value inside
| `addShippingZoneAndMethod` | `zoneName, zoneLocation, zipCode, zoneMethod` | util helper method for adding shipping zones with shipping methods
| `applyCoupon` | `couponName` | helper method which applies a coupon in cart or checkout
| `removeCoupon` | | helper method that removes a single coupon within cart or checkout
| `selectOptionInSelect2` | `selector, value` | helper method that searchs for select2 type fields and select plus insert value inside |
| `searchForOrder` | `value, orderId, customerName` | helper method that searchs for an order via many different terms |
| `addShippingZoneAndMethod` | `zoneName, zoneLocation, zipCode, zoneMethod` | util helper method for adding shipping zones with shipping methods |
| `applyCoupon` | `couponName` | helper method which applies a coupon in cart or checkout |
| `removeCoupon` | | helper method that removes a single coupon within cart or checkout |
| `selectOrderAction` | `action` | Helper method to select an order action in the `Order Actions` postbox |
| `clickUpdateOrder` | `noticeText`, `waitForSave` | Helper method to click the Update button on the order details page |

View File

@ -175,11 +175,14 @@ const completeOnboardingWizard = async () => {
/**
* Create simple product.
*
* @param productTitle - Defaults to Simple Product. Customizable title.
* @param productPrice - Defaults to $9.99. Customizable pricing.
*/
const createSimpleProduct = async () => {
const createSimpleProduct = async ( productTitle = simpleProductName, productPrice = simpleProductPrice ) => {
const product = await factories.products.simple.create( {
name: simpleProductName,
regularPrice: simpleProductPrice
name: productTitle,
regularPrice: productPrice
} );
return product.id;
} ;
@ -467,7 +470,7 @@ const createCoupon = async ( couponAmount = '5', discountType = 'Fixed cart disc
*/
const addShippingZoneAndMethod = async ( zoneName, zoneLocation = 'United States (US)', zipCode = ' ', zoneMethod = 'flat_rate' ) => {
await merchant.openNewShipping();
// Fill shipping zone name
await page.waitForSelector('input#zone_name');
await expect(page).toFill('input#zone_name', zoneName);
@ -475,9 +478,9 @@ const addShippingZoneAndMethod = async ( zoneName, zoneLocation = 'United States
// Select shipping zone location
// (.toSelect is not best option here because a lot of &nbsp are present in country/state names)
await expect(page).toFill('#zone_locations', zoneLocation);
await page.waitFor(1000); // avoiding flakiness
await uiUnblocked();
await page.keyboard.press('Tab');
await page.waitFor(1000); // avoiding flakiness
await uiUnblocked();
await page.keyboard.press('Enter');
// Fill shipping zone postcode if needed otherwise just put empty space
@ -488,15 +491,14 @@ const addShippingZoneAndMethod = async ( zoneName, zoneLocation = 'United States
await expect(page).toClick('button#submit');
// Add shipping zone method
await page.waitFor(2000); // avoiding flakiness
await uiUnblocked();
await expect(page).toClick('button.wc-shipping-zone-add-method', {text:'Add shipping method'});
await page.waitFor(2000); // avoiding flakiness
await page.waitForSelector('.wc-shipping-zone-method-description');
await page.waitForSelector('.wc-shipping-zone-method-selector');
await expect(page).toSelect('select[name="add_method_id"]', zoneMethod);
await page.waitFor(1000); // avoiding flakiness
await uiUnblocked();
await expect(page).toClick('button#btn-ok');
await page.waitForSelector('#zone_locations');
await page.waitFor(1000); // avoiding flakiness
await uiUnblocked();
};
/**
@ -546,6 +548,6 @@ export {
createCoupon,
addShippingZoneAndMethod,
createSimpleProductWithCategory,
clickUpdateOrder,
clickUpdateOrder,
deleteAllEmailLogs,
};

View File

@ -198,18 +198,35 @@ const evalAndClick = async ( selector ) => {
};
/**
* Select a value from select2 input field.
* Select a value from select2 search input field.
*
* @param {string} value Value of what to be selected
* @param {string} selector Selector of the select2
* @param {string} selector Selector of the select2 search field
*/
const selectOptionInSelect2 = async ( value, selector = 'input.select2-search__field' ) => {
await page.waitForSelector(selector);
await page.click(selector);
await page.type(selector, value);
await page.waitFor(2000); // to avoid flakyness, must wait before pressing Enter
await page.keyboard.press('Enter');
};
/**
* Search by any term for an order
*
* @param {string} value Value to be entered into the search field
* @param {string} orderId Order ID
* @param {string} customerName Customer's full name attached to order ID.
*/
const searchForOrder = async (value, orderId, customerName) => {
await clearAndFillInput('#post-search-input', value);
await expect(page).toMatchElement('#post-search-input', value);
await expect(page).toClick('#search-submit');
await page.waitForSelector('#the-list');
await page.waitFor(1000);
await expect(page).toMatchElement('.order_number > a.order-view', {text: `#${orderId} ${customerName}`});
};
/**
* Apply a coupon code within cart or checkout.
* Method will try to apply a coupon in the checkout, otherwise will try to apply in the cart.
@ -273,7 +290,8 @@ export {
moveAllItemsToTrash,
evalAndClick,
selectOptionInSelect2,
applyCoupon,
searchForOrder,
applyCoupon,
removeCoupon,
selectOrderAction,
};