fix merge conflicts

This commit is contained in:
Ron Rennick 2021-03-17 16:40:49 -03:00
commit e25ee7f36c
32 changed files with 693 additions and 24 deletions

View File

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

View File

@ -763,6 +763,7 @@ jQuery( function( $ ) {
case 'variable_regular_price' : case 'variable_regular_price' :
case 'variable_sale_price' : case 'variable_sale_price' :
case 'variable_stock' : case 'variable_stock' :
case 'variable_low_stock_amount' :
case 'variable_weight' : case 'variable_weight' :
case 'variable_length' : case 'variable_length' :
case 'variable_width' : case 'variable_width' :

View File

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

View File

@ -509,6 +509,7 @@ class WC_Meta_Box_Product_Data {
), ),
'manage_stock' => isset( $_POST['variable_manage_stock'][ $i ] ), 'manage_stock' => isset( $_POST['variable_manage_stock'][ $i ] ),
'stock_quantity' => $stock, 'stock_quantity' => $stock,
'low_stock_amount' => isset( $_POST['variable_low_stock_amount'][ $i ] ) && '' !== $_POST['variable_low_stock_amount'][ $i ] ? wc_stock_amount( wp_unslash( $_POST['variable_low_stock_amount'][ $i ] ) ) : '',
'backorders' => isset( $_POST['variable_backorders'], $_POST['variable_backorders'][ $i ] ) ? wc_clean( wp_unslash( $_POST['variable_backorders'][ $i ] ) ) : null, 'backorders' => isset( $_POST['variable_backorders'], $_POST['variable_backorders'][ $i ] ) ? wc_clean( wp_unslash( $_POST['variable_backorders'][ $i ] ) ) : null,
'stock_status' => isset( $_POST['variable_stock_status'][ $i ] ) ? wc_clean( wp_unslash( $_POST['variable_stock_status'][ $i ] ) ) : null, 'stock_status' => isset( $_POST['variable_stock_status'][ $i ] ) ? wc_clean( wp_unslash( $_POST['variable_stock_status'][ $i ] ) ) : null,
'image_id' => isset( $_POST['upload_image_id'][ $i ] ) ? wc_clean( wp_unslash( $_POST['upload_image_id'][ $i ] ) ) : null, 'image_id' => isset( $_POST['upload_image_id'][ $i ] ) ? wc_clean( wp_unslash( $_POST['upload_image_id'][ $i ] ) ) : null,

View File

@ -75,10 +75,14 @@ if ( ! defined( 'ABSPATH' ) ) {
array( array(
'id' => '_low_stock_amount', 'id' => '_low_stock_amount',
'value' => $product_object->get_low_stock_amount( 'edit' ), 'value' => $product_object->get_low_stock_amount( 'edit' ),
'placeholder' => get_option( 'woocommerce_notify_low_stock_amount' ), 'placeholder' => sprintf(
/* translators: %d: Amount of stock left */
esc_attr__( 'Store-wide threshold (%d)', 'woocommerce' ),
esc_attr( get_option( 'woocommerce_notify_low_stock_amount' ) )
),
'label' => __( 'Low stock threshold', 'woocommerce' ), 'label' => __( 'Low stock threshold', 'woocommerce' ),
'desc_tip' => true, 'desc_tip' => true,
'description' => __( 'When product stock reaches this amount you will be notified by email', 'woocommerce' ), 'description' => __( 'When product stock reaches this amount you will be notified by email. It is possible to define different values for each variation individually. The shop default value can be set in Settings > Products > Inventory.', 'woocommerce' ),
'type' => 'number', 'type' => 'number',
'custom_attributes' => array( 'custom_attributes' => array(
'step' => 'any', 'step' => 'any',

View File

@ -75,6 +75,7 @@ if ( ! defined( 'ABSPATH' ) ) {
<option value="variable_stock_status_instock"><?php esc_html_e( 'Set Status - In stock', 'woocommerce' ); ?></option> <option value="variable_stock_status_instock"><?php esc_html_e( 'Set Status - In stock', 'woocommerce' ); ?></option>
<option value="variable_stock_status_outofstock"><?php esc_html_e( 'Set Status - Out of stock', 'woocommerce' ); ?></option> <option value="variable_stock_status_outofstock"><?php esc_html_e( 'Set Status - Out of stock', 'woocommerce' ); ?></option>
<option value="variable_stock_status_onbackorder"><?php esc_html_e( 'Set Status - On backorder', 'woocommerce' ); ?></option> <option value="variable_stock_status_onbackorder"><?php esc_html_e( 'Set Status - On backorder', 'woocommerce' ); ?></option>
<option value="variable_low_stock_amount"><?php esc_html_e( 'Low stock threshold', 'woocommerce' ); ?></option>
</optgroup> </optgroup>
<optgroup label="<?php esc_attr_e( 'Shipping', 'woocommerce' ); ?>"> <optgroup label="<?php esc_attr_e( 'Shipping', 'woocommerce' ); ?>">
<option value="variable_length"><?php esc_html_e( 'Length', 'woocommerce' ); ?></option> <option value="variable_length"><?php esc_html_e( 'Length', 'woocommerce' ); ?></option>

View File

@ -210,6 +210,35 @@ defined( 'ABSPATH' ) || exit;
) )
); );
$low_stock_placeholder = ( $product_object->get_manage_stock() && '' !== $product_object->get_low_stock_amount() )
? sprintf(
/* translators: %d: Amount of stock left */
esc_attr__( 'Parent product\'s threshold (%d)', 'woocommerce' ),
esc_attr( $product_object->get_low_stock_amount() )
)
: sprintf(
/* translators: %d: Amount of stock left */
esc_attr__( 'Store-wide threshold (%d)', 'woocommerce' ),
esc_attr( get_option( 'woocommerce_notify_low_stock_amount' ) )
);
woocommerce_wp_text_input(
array(
'id' => "variable_low_stock_amount{$loop}",
'name' => "variable_low_stock_amount[{$loop}]",
'value' => $variation_object->get_low_stock_amount( 'edit' ),
'placeholder' => $low_stock_placeholder,
'label' => __( 'Low stock threshold', 'woocommerce' ),
'desc_tip' => true,
'description' => __( 'When variation stock reaches this amount you will be notified by email. The default value for all variations can be set in the product Inventory tab. The shop default value can be set in Settings > Products > Inventory.', 'woocommerce' ),
'type' => 'number',
'custom_attributes' => array(
'step' => 'any',
),
'wrapper_class' => 'form-row',
)
);
/** /**
* Variation options inventory action. * Variation options inventory action.
* *

View File

@ -2301,6 +2301,32 @@ class WC_AJAX {
} }
} }
/**
* Bulk action - Set Low Stock Amount.
*
* @param array $variations List of variations.
* @param array $data Data to set.
*
* @used-by bulk_edit_variations
*/
private static function variation_bulk_action_variable_low_stock_amount( $variations, $data ) {
if ( ! isset( $data['value'] ) ) {
return;
}
$low_stock_amount = wc_stock_amount( wc_clean( $data['value'] ) );
foreach ( $variations as $variation_id ) {
$variation = wc_get_product( $variation_id );
if ( $variation->managing_stock() ) {
$variation->set_low_stock_amount( $low_stock_amount );
} else {
$variation->set_low_stock_amount( '' );
}
$variation->save();
}
}
/** /**
* Bulk action - Set Weight. * Bulk action - Set Weight.
* *
@ -2547,6 +2573,7 @@ class WC_AJAX {
* @uses WC_AJAX::variation_bulk_action_toggle_virtual() * @uses WC_AJAX::variation_bulk_action_toggle_virtual()
* @uses WC_AJAX::variation_bulk_action_toggle_downloadable() * @uses WC_AJAX::variation_bulk_action_toggle_downloadable()
* @uses WC_AJAX::variation_bulk_action_toggle_enabled * @uses WC_AJAX::variation_bulk_action_toggle_enabled
* @uses WC_AJAX::variation_bulk_action_variable_low_stock_amount()
*/ */
public static function bulk_edit_variations() { public static function bulk_edit_variations() {
ob_start(); ob_start();

View File

@ -357,6 +357,7 @@ class WC_Product_Variation_Data_Store_CPT extends WC_Product_Data_Store_CPT impl
'date_on_sale_to' => get_post_meta( $id, '_sale_price_dates_to', true ), 'date_on_sale_to' => get_post_meta( $id, '_sale_price_dates_to', true ),
'manage_stock' => get_post_meta( $id, '_manage_stock', true ), 'manage_stock' => get_post_meta( $id, '_manage_stock', true ),
'stock_status' => get_post_meta( $id, '_stock_status', true ), 'stock_status' => get_post_meta( $id, '_stock_status', true ),
'low_stock_amount' => get_post_meta( $id, '_low_stock_amount', true ),
'shipping_class_id' => current( $this->get_term_ids( $id, 'product_shipping_class' ) ), 'shipping_class_id' => current( $this->get_term_ids( $id, 'product_shipping_class' ) ),
'virtual' => get_post_meta( $id, '_virtual', true ), 'virtual' => get_post_meta( $id, '_virtual', true ),
'downloadable' => get_post_meta( $id, '_downloadable', true ), 'downloadable' => get_post_meta( $id, '_downloadable', true ),
@ -404,7 +405,6 @@ class WC_Product_Variation_Data_Store_CPT extends WC_Product_Data_Store_CPT impl
'sku' => get_post_meta( $product->get_parent_id(), '_sku', true ), 'sku' => get_post_meta( $product->get_parent_id(), '_sku', true ),
'manage_stock' => get_post_meta( $product->get_parent_id(), '_manage_stock', true ), 'manage_stock' => get_post_meta( $product->get_parent_id(), '_manage_stock', true ),
'backorders' => get_post_meta( $product->get_parent_id(), '_backorders', true ), 'backorders' => get_post_meta( $product->get_parent_id(), '_backorders', true ),
'low_stock_amount' => get_post_meta( $product->get_parent_id(), '_low_stock_amount', true ),
'stock_quantity' => wc_stock_amount( get_post_meta( $product->get_parent_id(), '_stock', true ) ), 'stock_quantity' => wc_stock_amount( get_post_meta( $product->get_parent_id(), '_stock', true ) ),
'weight' => get_post_meta( $product->get_parent_id(), '_weight', true ), 'weight' => get_post_meta( $product->get_parent_id(), '_weight', true ),
'length' => get_post_meta( $product->get_parent_id(), '_length', true ), 'length' => get_post_meta( $product->get_parent_id(), '_length', true ),

View File

@ -766,6 +766,9 @@ class WC_REST_Products_V2_Controller extends WC_REST_CRUD_Controller {
case 'backordered': case 'backordered':
$base_data['backordered'] = $product->is_on_backorder(); $base_data['backordered'] = $product->is_on_backorder();
break; break;
case 'low_stock_amount':
$base_data['low_stock_amount'] = '' === $product->get_low_stock_amount() ? null : $product->get_low_stock_amount();
break;
case 'sold_individually': case 'sold_individually':
$base_data['sold_individually'] = $product->is_sold_individually(); $base_data['sold_individually'] = $product->is_sold_individually();
break; break;

View File

@ -65,6 +65,7 @@ class WC_REST_Product_Variations_Controller extends WC_REST_Product_Variations_V
'backorders' => $object->get_backorders(), 'backorders' => $object->get_backorders(),
'backorders_allowed' => $object->backorders_allowed(), 'backorders_allowed' => $object->backorders_allowed(),
'backordered' => $object->is_on_backorder(), 'backordered' => $object->is_on_backorder(),
'low_stock_amount' => '' === $object->get_low_stock_amount() ? null : $object->get_low_stock_amount(),
'weight' => $object->get_weight(), 'weight' => $object->get_weight(),
'dimensions' => array( 'dimensions' => array(
'length' => $object->get_length(), 'length' => $object->get_length(),
@ -185,9 +186,18 @@ class WC_REST_Product_Variations_Controller extends WC_REST_Product_Variations_V
$stock_quantity += wc_stock_amount( $request['inventory_delta'] ); $stock_quantity += wc_stock_amount( $request['inventory_delta'] );
$variation->set_stock_quantity( $stock_quantity ); $variation->set_stock_quantity( $stock_quantity );
} }
// isset() returns false for value null, thus we need to check whether the value has been sent by the request.
if ( array_key_exists( 'low_stock_amount', $request->get_params() ) ) {
if ( null === $request['low_stock_amount'] ) {
$variation->set_low_stock_amount( '' );
} else {
$variation->set_low_stock_amount( wc_stock_amount( $request['low_stock_amount'] ) );
}
}
} else { } else {
$variation->set_backorders( 'no' ); $variation->set_backorders( 'no' );
$variation->set_stock_quantity( '' ); $variation->set_stock_quantity( '' );
$variation->set_low_stock_amount( '' );
} }
// Regular Price. // Regular Price.
@ -597,6 +607,11 @@ class WC_REST_Product_Variations_Controller extends WC_REST_Product_Variations_V
'context' => array( 'view', 'edit' ), 'context' => array( 'view', 'edit' ),
'readonly' => true, 'readonly' => true,
), ),
'low_stock_amount' => array(
'description' => __( 'Low Stock amount for the variation.', 'woocommerce' ),
'type' => array( 'integer', 'null' ),
'context' => array( 'view', 'edit' ),
),
'weight' => array( 'weight' => array(
/* translators: %s: weight unit */ /* translators: %s: weight unit */
'description' => sprintf( __( 'Variation weight (%s).', 'woocommerce' ), $weight_unit ), 'description' => sprintf( __( 'Variation weight (%s).', 'woocommerce' ), $weight_unit ),

View File

@ -551,11 +551,22 @@ class WC_REST_Products_Controller extends WC_REST_Products_V2_Controller {
$stock_quantity += wc_stock_amount( $request['inventory_delta'] ); $stock_quantity += wc_stock_amount( $request['inventory_delta'] );
$product->set_stock_quantity( wc_stock_amount( $stock_quantity ) ); $product->set_stock_quantity( wc_stock_amount( $stock_quantity ) );
} }
// Low stock amount.
// isset() returns false for value null, thus we need to check whether the value has been sent by the request.
if ( array_key_exists( 'low_stock_amount', $request->get_params() ) ) {
if ( null === $request['low_stock_amount'] ) {
$product->set_low_stock_amount( '' );
} else {
$product->set_low_stock_amount( wc_stock_amount( $request['low_stock_amount'] ) );
}
}
} else { } else {
// Don't manage stock. // Don't manage stock.
$product->set_manage_stock( 'no' ); $product->set_manage_stock( 'no' );
$product->set_stock_quantity( '' ); $product->set_stock_quantity( '' );
$product->set_stock_status( $stock_status ); $product->set_stock_status( $stock_status );
$product->set_low_stock_amount( '' );
} }
} elseif ( ! $product->is_type( 'variable' ) ) { } elseif ( ! $product->is_type( 'variable' ) ) {
$product->set_stock_status( $stock_status ); $product->set_stock_status( $stock_status );
@ -985,6 +996,11 @@ class WC_REST_Products_Controller extends WC_REST_Products_V2_Controller {
'context' => array( 'view', 'edit' ), 'context' => array( 'view', 'edit' ),
'readonly' => true, 'readonly' => true,
), ),
'low_stock_amount' => array(
'description' => __( 'Low Stock amount for the product.', 'woocommerce' ),
'type' => array( 'integer', 'null' ),
'context' => array( 'view', 'edit' ),
),
'sold_individually' => array( 'sold_individually' => array(
'description' => __( 'Allow one item to be bought in a single order.', 'woocommerce' ), 'description' => __( 'Allow one item to be bought in a single order.', 'woocommerce' ),
'type' => 'boolean', 'type' => 'boolean',

View File

@ -1801,10 +1801,26 @@ function wc_ascii_uasort_comparison( $a, $b ) {
function wc_asort_by_locale( &$data, $locale = '' ) { function wc_asort_by_locale( &$data, $locale = '' ) {
// Use Collator if PHP Internationalization Functions (php-intl) is available. // Use Collator if PHP Internationalization Functions (php-intl) is available.
if ( class_exists( 'Collator' ) ) { if ( class_exists( 'Collator' ) ) {
$locale = $locale ? $locale : get_locale(); try {
$collator = new Collator( $locale ); $locale = $locale ? $locale : get_locale();
$collator->asort( $data, Collator::SORT_STRING ); $collator = new Collator( $locale );
return $data; $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; $raw_data = $data;

View File

@ -390,15 +390,23 @@ 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 * Return low stock amount to determine if notification needs to be sent
* *
* 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.
*
* @param WC_Product $product Product to get data from. * @param WC_Product $product Product to get data from.
* @since 3.5.0 * @since 3.5.0
* @return int * @return int
*/ */
function wc_get_low_stock_amount( WC_Product $product ) { function wc_get_low_stock_amount( WC_Product $product ) {
if ( $product->is_type( 'variation' ) ) {
$product = wc_get_product( $product->get_parent_id() );
}
$low_stock_amount = $product->get_low_stock_amount(); $low_stock_amount = $product->get_low_stock_amount();
if ( '' === $low_stock_amount && $product->is_type( 'variation' ) ) {
$product = wc_get_product( $product->get_parent_id() );
$low_stock_amount = $product->get_low_stock_amount();
}
if ( '' === $low_stock_amount ) { if ( '' === $low_stock_amount ) {
$low_stock_amount = get_option( 'woocommerce_notify_low_stock_amount', 2 ); $low_stock_amount = get_option( 'woocommerce_notify_low_stock_amount', 2 );
} }

View File

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

View File

@ -45,6 +45,7 @@ The functions to access the core tests are:
### Merchant ### Merchant
- `runMerchantTests` - Run all merchant tests - `runMerchantTests` - Run all merchant tests
- `runAddNewShippingZoneTest` - Merchant can create shipping zones and let shopper test them
- `runAddSimpleProductTest` - Merchant can create a simple product - `runAddSimpleProductTest` - Merchant can create a simple product
- `runAddVariableProductTest` - Merchant can create a variable product - `runAddVariableProductTest` - Merchant can create a variable product
- `runCreateCouponTest` - Merchant can create coupon - `runCreateCouponTest` - Merchant can create coupon
@ -54,11 +55,13 @@ The functions to access the core tests are:
- `runOrderStatusFilterTest` - Merchant can filter orders by order status - `runOrderStatusFilterTest` - Merchant can filter orders by order status
- `runOrderRefundTest` - Merchant can refund an order - `runOrderRefundTest` - Merchant can refund an order
- `runOrderApplyCouponTest` - Merchant can apply a coupon to an order - `runOrderApplyCouponTest` - Merchant can apply a coupon to an order
- `runOrderSearchingTest` - Merchant can search for order via different terms
- `runProductEditDetailsTest` - Merchant can edit an existing product - `runProductEditDetailsTest` - Merchant can edit an existing product
- `runProductSearchTest` - Merchant can search for a product and view it - `runProductSearchTest` - Merchant can search for a product and view it
- `runProductSettingsTest` - Merchant can update product settings - `runProductSettingsTest` - Merchant can update product settings
- `runTaxSettingsTest` - Merchant can update tax settings - `runTaxSettingsTest` - Merchant can update tax settings
- `runUpdateGeneralSettingsTest` - Merchant can update general settings - `runUpdateGeneralSettingsTest` - Merchant can update general settings
- `runMerchantOrderEmailsTest` - Merchant can receive order emails and resend emails by Order Actions
### Shopper ### Shopper

View File

@ -20,6 +20,7 @@ const runSingleProductPageTest = require( './shopper/front-end-single-product.te
const runVariableProductUpdateTest = require( './shopper/front-end-variable-product-updates.test' ); const runVariableProductUpdateTest = require( './shopper/front-end-variable-product-updates.test' );
// Merchant tests // Merchant tests
const runAddNewShippingZoneTest = require ( './merchant/wp-admin-settings-shipping-zones.test' );
const runCreateCouponTest = require( './merchant/wp-admin-coupon-new.test' ); const runCreateCouponTest = require( './merchant/wp-admin-coupon-new.test' );
const runCreateOrderTest = require( './merchant/wp-admin-order-new.test' ); const runCreateOrderTest = require( './merchant/wp-admin-order-new.test' );
const runEditOrderTest = require( './merchant/wp-admin-order-edit.test' ); const runEditOrderTest = require( './merchant/wp-admin-order-edit.test' );
@ -34,6 +35,7 @@ const runProductEditDetailsTest = require( './merchant/wp-admin-product-edit-det
const runProductSearchTest = require( './merchant/wp-admin-product-search.test' ); const runProductSearchTest = require( './merchant/wp-admin-product-search.test' );
const runMerchantOrdersCustomerPaymentPage = require( './merchant/wp-admin-order-customer-payment-page.test' ); const runMerchantOrdersCustomerPaymentPage = require( './merchant/wp-admin-order-customer-payment-page.test' );
const runMerchantOrderEmailsTest = require( './merchant/wp-admin-order-emails.test' ); const runMerchantOrderEmailsTest = require( './merchant/wp-admin-order-emails.test' );
const runOrderSearchingTest = require( './merchant/wp-admin-order-searching.test' );
// REST API tests // REST API tests
const runExternalProductAPITest = require( './api/external-product.test' ); const runExternalProductAPITest = require( './api/external-product.test' );
@ -61,6 +63,8 @@ const runShopperTests = () => {
}; };
const runMerchantTests = () => { const runMerchantTests = () => {
runOrderSearchingTest();
runAddNewShippingZoneTest();
runCreateCouponTest(); runCreateCouponTest();
runCreateOrderTest(); runCreateOrderTest();
runEditOrderTest(); runEditOrderTest();
@ -119,6 +123,8 @@ module.exports = {
runMerchantOrdersCustomerPaymentPage, runMerchantOrdersCustomerPaymentPage,
runMerchantOrderEmailsTest, runMerchantOrderEmailsTest,
runMerchantTests, runMerchantTests,
runOrderSearchingTest,
runAddNewShippingZoneTest,
runProductBrowseSearchSortTest, runProductBrowseSearchSortTest,
runApiTests, 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

@ -0,0 +1,137 @@
/* eslint-disable jest/no-export, jest/no-disabled-tests */
/**
* Internal dependencies
*/
const {
shopper,
merchant,
createSimpleProduct,
addShippingZoneAndMethod,
clearAndFillInput,
selectOptionInSelect2,
evalAndClick,
uiUnblocked,
} = require( '@woocommerce/e2e-utils' );
const config = require( 'config' );
const simpleProductPrice = config.has( 'products.simple.price' ) ? config.get( 'products.simple.price' ) : '9.99';
const simpleProductName = config.get( 'products.simple.name' );
const california = 'California, United States (US)';
const sanFranciscoZIP = '94107';
const shippingZoneNameUS = 'US with Flat rate';
const shippingZoneNameFL = 'CA with Free shipping';
const shippingZoneNameSF = 'SF with Local pickup';
const runAddNewShippingZoneTest = () => {
describe('WooCommerce Shipping Settings - Add new shipping zone', () => {
beforeAll(async () => {
await merchant.login();
await createSimpleProduct();
await merchant.openSettings('shipping');
// Delete existing shipping zones.
try {
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) {
// Prevent an error here causing the test to fail.
}
});
it('add shipping zone for San Francisco with free Local pickup', async () => {
// Add a new shipping zone for San Francisco 94107, CA, US with Local pickup
await addShippingZoneAndMethod(shippingZoneNameSF, california, sanFranciscoZIP, 'local_pickup');
});
it('add shipping zone for California with Free shipping', async () => {
// Add a new shipping zone for CA, US with Free shipping
await addShippingZoneAndMethod(shippingZoneNameFL, california, ' ', 'free_shipping');
});
it('add shipping zone for the US with Flat rate', async () => {
// Add a new shipping zone for the US with Flat rate
await addShippingZoneAndMethod(shippingZoneNameUS);
// Set Flat rate cost
await expect(page).toClick('a.wc-shipping-zone-method-settings', {text: 'Flat rate'});
await clearAndFillInput('#woocommerce_flat_rate_cost', '10');
await expect(page).toClick('.wc-backbone-modal-main button#btn-ok');
await merchant.logout();
});
it('allows customer to pay for a Flat rate shipping method', async() => {
await shopper.login();
// Add product to cart as a shopper
await shopper.goToShop();
await shopper.addToCartFromShopPage(simpleProductName);
await shopper.goToCart();
// Set shipping country to United States (US)
await expect(page).toClick('a.shipping-calculator-button');
await expect(page).toClick('#select2-calc_shipping_country-container');
await selectOptionInSelect2('United States (US)');
// Set shipping state to New York
await expect(page).toClick('#select2-calc_shipping_state-container');
await selectOptionInSelect2('New York');
await expect(page).toClick('button[name="calc_shipping"]');
// Set shipping postcode to 10010
await clearAndFillInput('#calc_shipping_postcode', '10010');
await expect(page).toClick('button[name="calc_shipping"]');
// Verify shipping costs
await page.waitForSelector('.order-total');
await expect(page).toMatchElement('.shipping .amount', {text: '$10.00'});
await expect(page).toMatchElement('.order-total .amount', {text: `$1${simpleProductPrice}`});
});
it('allows customer to benefit from a Free shipping if in CA', async () => {
await page.reload();
// Set shipping state to California
await expect(page).toClick('a.shipping-calculator-button');
await expect(page).toClick('#select2-calc_shipping_state-container');
await selectOptionInSelect2('California');
// Set shipping postcode to 94000
await clearAndFillInput('#calc_shipping_postcode', '94000');
await expect(page).toClick('button[name="calc_shipping"]');
// Verify shipping method and cost
await page.waitForSelector('.order-total');
await expect(page).toMatchElement('.shipping ul#shipping_method > li', {text: 'Free shipping'});
await expect(page).toMatchElement('.order-total .amount', {text: `$${simpleProductPrice}`});
});
it('allows customer to benefit from a free Local pickup if in SF', async () => {
await page.reload();
// Set shipping postcode to 94107
await expect(page).toClick('a.shipping-calculator-button');
await clearAndFillInput('#calc_shipping_postcode', '94107');
await expect(page).toClick('button[name="calc_shipping"]');
// Verify shipping method and cost
await page.waitForSelector('.order-total');
await expect(page).toMatchElement('.shipping ul#shipping_method > li', {text: 'Local pickup'});
await expect(page).toMatchElement('.order-total .amount', {text: `$${simpleProductPrice}`});
await shopper.removeFromCart(simpleProductName);
});
});
};
module.exports = runAddNewShippingZoneTest;

View File

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

View File

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

View File

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

View File

@ -17,6 +17,8 @@
- `createCoupon( couponAmount )` component which accepts a coupon amount string (it defaults to 5) and creates a basic coupon. Returns the generated coupon code. - `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. - `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 - `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. - `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 - `applyCoupon( couponName )` util helper method which applies previously created coupon to cart or checkout
- `removeCoupon()` util helper method that removes a single coupon within cart or checkout - `removeCoupon()` util helper method that removes a single coupon within cart or checkout

View File

@ -106,11 +106,21 @@ describe( 'Cart page', () => {
| `settingsPageSaveChanges` | | Save the current WooCommerce settings page | | `settingsPageSaveChanges` | | Save the current WooCommerce settings page |
| `uiUnblocked` | | Wait until the page is unblocked | | `uiUnblocked` | | Wait until the page is unblocked |
| `unsetCheckbox` | `selector` | Uncheck a checkbox | | `unsetCheckbox` | `selector` | Uncheck a checkbox |
trashVerification` | Verify that an item can be published and trashed |
| `verifyAndPublish` | `noticeText` | Verify that an item can be published | | `verifyAndPublish` | `noticeText` | Verify that an item can be published |
| `verifyCheckboxIsSet` | `selector` | Verify that a checkbox is checked | | `verifyCheckboxIsSet` | `selector` | Verify that a checkbox is checked |
| `verifyCheckboxIsUnset` | `selector` | Verify that a checkbox is unchecked | | `verifyCheckboxIsUnset` | `selector` | Verify that a checkbox is unchecked |
| `verifyPublishAndTrash` | `button, publishNotice, publishVerification, | `verifyValueOfInputField` | `selector, value` | Verify an input contains the passed value | | `verifyPublishAndTrash` | `button, publishNotice, publishVerification, trashVerification` | Verify that an item can be published and trashed |
| `verifyValueOfInputField` | `selector, value` | Verify an input contains the passed value |
| `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 |
| `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 |
### Test Utilities ### Test Utilities

View File

@ -175,11 +175,14 @@ const completeOnboardingWizard = async () => {
/** /**
* Create simple product. * 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( { const product = await factories.products.simple.create( {
name: simpleProductName, name: productTitle,
regularPrice: simpleProductPrice regularPrice: productPrice
} ); } );
return product.id; return product.id;
} ; } ;
@ -457,6 +460,47 @@ const createCoupon = async ( couponAmount = '5', discountType = 'Fixed cart disc
return couponCode; return couponCode;
}; };
/**
* Adds a shipping zone along with a shipping method.
*
* @param zoneName Shipping zone name.
* @param zoneLocation Shiping zone location. Defaults to United States (US).
* @param zipCode Shipping zone zip code. Defaults to empty one space.
* @param zoneMethod Shipping method type. Defaults to flat_rate (use also: free_shipping or local_pickup)
*/
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);
// 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 uiUnblocked();
await page.keyboard.press('Tab');
await uiUnblocked();
await page.keyboard.press('Enter');
// Fill shipping zone postcode if needed otherwise just put empty space
await page.waitForSelector('a.wc-shipping-zone-postcodes-toggle');
await expect(page).toClick('a.wc-shipping-zone-postcodes-toggle');
await expect(page).toFill('#zone_postcodes', zipCode);
await expect(page).toMatchElement('#zone_postcodes', zipCode);
await expect(page).toClick('button#submit');
// Add shipping zone method
await uiUnblocked();
await expect(page).toClick('button.wc-shipping-zone-add-method', {text:'Add shipping method'});
await page.waitForSelector('.wc-shipping-zone-method-selector');
await expect(page).toSelect('select[name="add_method_id"]', zoneMethod);
await uiUnblocked();
await expect(page).toClick('button#btn-ok');
await page.waitForSelector('#zone_locations');
await uiUnblocked();
};
/** /**
* Click the Update button on the order details page. * Click the Update button on the order details page.
* *
@ -468,7 +512,7 @@ const clickUpdateOrder = async ( noticeText, waitForSave = false ) => {
await page.waitFor( 2000 ); await page.waitFor( 2000 );
} }
// PUpdate order // Update order
await expect( page ).toClick( 'button.save_order' ); await expect( page ).toClick( 'button.save_order' );
await page.waitForSelector( '.updated.notice' ); await page.waitForSelector( '.updated.notice' );
@ -502,6 +546,7 @@ export {
verifyAndPublish, verifyAndPublish,
addProductToOrder, addProductToOrder,
createCoupon, createCoupon,
addShippingZoneAndMethod,
createSimpleProductWithCategory, createSimpleProductWithCategory,
clickUpdateOrder, clickUpdateOrder,
deleteAllEmailLogs, deleteAllEmailLogs,

View File

@ -16,6 +16,7 @@ export const WP_ADMIN_NEW_ORDER = baseUrl + 'wp-admin/post-new.php?post_type=sho
export const WP_ADMIN_NEW_PRODUCT = baseUrl + 'wp-admin/post-new.php?post_type=product'; export const WP_ADMIN_NEW_PRODUCT = baseUrl + 'wp-admin/post-new.php?post_type=product';
export const WP_ADMIN_WC_SETTINGS = baseUrl + 'wp-admin/admin.php?page=wc-settings&tab='; export const WP_ADMIN_WC_SETTINGS = baseUrl + 'wp-admin/admin.php?page=wc-settings&tab=';
export const WP_ADMIN_PERMALINK_SETTINGS = baseUrl + 'wp-admin/options-permalink.php'; export const WP_ADMIN_PERMALINK_SETTINGS = baseUrl + 'wp-admin/options-permalink.php';
export const WP_ADMIN_NEW_SHIPPING_ZONE = baseUrl + 'wp-admin/admin.php?page=wc-settings&tab=shipping&zone_id=new';
export const SHOP_PAGE = baseUrl + 'shop'; export const SHOP_PAGE = baseUrl + 'shop';
export const SHOP_PRODUCT_PAGE = baseUrl + '?p='; export const SHOP_PRODUCT_PAGE = baseUrl + '?p=';

View File

@ -18,7 +18,8 @@ const {
WP_ADMIN_PERMALINK_SETTINGS, WP_ADMIN_PERMALINK_SETTINGS,
WP_ADMIN_PLUGINS, WP_ADMIN_PLUGINS,
WP_ADMIN_SETUP_WIZARD, WP_ADMIN_SETUP_WIZARD,
WP_ADMIN_WC_SETTINGS WP_ADMIN_WC_SETTINGS,
WP_ADMIN_NEW_SHIPPING_ZONE
} = require( './constants' ); } = require( './constants' );
const baseUrl = config.get( 'url' ); const baseUrl = config.get( 'url' );
@ -170,11 +171,17 @@ const merchant = {
} }
}, },
openNewShipping: async () => {
await page.goto( WP_ADMIN_NEW_SHIPPING_ZONE, {
waitUntil: 'networkidle0',
} );
},
openEmailLog: async () => { openEmailLog: async () => {
await page.goto( `${baseUrl}wp-admin/tools.php?page=wpml_plugin_log`, { await page.goto( `${baseUrl}wp-admin/tools.php?page=wpml_plugin_log`, {
waitUntil: 'networkidle0', waitUntil: 'networkidle0',
} ); } );
} },
}; };
module.exports = merchant; module.exports = merchant;

View File

@ -10,6 +10,7 @@ import { pressKeyWithModifier } from '@wordpress/e2e-test-utils';
* @param {string} value * @param {string} value
*/ */
const clearAndFillInput = async ( selector, value ) => { const clearAndFillInput = async ( selector, value ) => {
await page.waitForSelector( selector );
await page.focus( selector ); await page.focus( selector );
await pressKeyWithModifier( 'primary', 'a' ); await pressKeyWithModifier( 'primary', 'a' );
await page.type( selector, value ); await page.type( selector, value );
@ -197,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} 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' ) => { const selectOptionInSelect2 = async ( value, selector = 'input.select2-search__field' ) => {
await page.waitForSelector(selector); await page.waitForSelector(selector);
await page.click(selector);
await page.type(selector, value); await page.type(selector, value);
await page.waitFor(2000); // to avoid flakyness, must wait before pressing Enter await page.waitFor(2000); // to avoid flakyness, must wait before pressing Enter
await page.keyboard.press('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. * 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. * Method will try to apply a coupon in the checkout, otherwise will try to apply in the cart.
@ -272,6 +290,7 @@ export {
moveAllItemsToTrash, moveAllItemsToTrash,
evalAndClick, evalAndClick,
selectOptionInSelect2, selectOptionInSelect2,
searchForOrder,
applyCoupon, applyCoupon,
removeCoupon, removeCoupon,
selectOrderAction, selectOrderAction,

View File

@ -6,6 +6,9 @@
* @since 3.5.0 * @since 3.5.0
*/ */
/**
* Product_Variations_API class.
*/
class Product_Variations_API extends WC_REST_Unit_Test_Case { class Product_Variations_API extends WC_REST_Unit_Test_Case {
/** /**
@ -397,7 +400,7 @@ class Product_Variations_API extends WC_REST_Unit_Test_Case {
$data = $response->get_data(); $data = $response->get_data();
$properties = $data['schema']['properties']; $properties = $data['schema']['properties'];
$this->assertEquals( 37, count( $properties ) ); $this->assertEquals( 38, count( $properties ) );
$this->assertArrayHasKey( 'id', $properties ); $this->assertArrayHasKey( 'id', $properties );
$this->assertArrayHasKey( 'date_created', $properties ); $this->assertArrayHasKey( 'date_created', $properties );
$this->assertArrayHasKey( 'date_modified', $properties ); $this->assertArrayHasKey( 'date_modified', $properties );
@ -424,6 +427,7 @@ class Product_Variations_API extends WC_REST_Unit_Test_Case {
$this->assertArrayHasKey( 'backorders', $properties ); $this->assertArrayHasKey( 'backorders', $properties );
$this->assertArrayHasKey( 'backorders_allowed', $properties ); $this->assertArrayHasKey( 'backorders_allowed', $properties );
$this->assertArrayHasKey( 'backordered', $properties ); $this->assertArrayHasKey( 'backordered', $properties );
$this->assertArrayHasKey( 'low_stock_amount', $properties );
$this->assertArrayHasKey( 'weight', $properties ); $this->assertArrayHasKey( 'weight', $properties );
$this->assertArrayHasKey( 'dimensions', $properties ); $this->assertArrayHasKey( 'dimensions', $properties );
$this->assertArrayHasKey( 'shipping_class', $properties ); $this->assertArrayHasKey( 'shipping_class', $properties );

View File

@ -642,7 +642,7 @@ class WC_Tests_API_Product extends WC_REST_Unit_Test_Case {
$response = $this->server->dispatch( $request ); $response = $this->server->dispatch( $request );
$data = $response->get_data(); $data = $response->get_data();
$properties = $data['schema']['properties']; $properties = $data['schema']['properties'];
$this->assertEquals( 65, count( $properties ) ); $this->assertEquals( 66, count( $properties ) );
} }
/** /**

View File

@ -65,6 +65,7 @@ class WC_REST_Products_Controller_Tests extends WC_REST_Unit_Test_Case {
'backorders', 'backorders',
'backorders_allowed', 'backorders_allowed',
'backordered', 'backordered',
'low_stock_amount',
'sold_individually', 'sold_individually',
'weight', 'weight',
'dimensions', 'dimensions',

View File

@ -191,4 +191,159 @@ class WC_Stock_Functions_Tests extends \WC_Unit_Test_Case {
} }
} }
} }
/**
* Test wc_get_low_stock_amount with a simple product which has low stock amount set.
*/
public function test_wc_get_low_stock_amount_simple_set() {
$product_low_stock_amount = 5;
$site_wide_low_stock_amount = 3;
// Set the store-wide default.
update_option( 'woocommerce_notify_low_stock_amount', $site_wide_low_stock_amount );
// Simple product, set low stock amount.
$product = WC_Helper_Product::create_simple_product(
true,
array(
'manage_stock' => true,
'stock_quantity' => 10,
'low_stock_amount' => $product_low_stock_amount,
)
);
$this->assertEquals( $product_low_stock_amount, wc_get_low_stock_amount( $product ) );
}
/**
* Test wc_get_low_stock_amount with a simple product which doesn't have low stock amount set.
*/
public function test_wc_get_low_stock_amount_simple_unset() {
$site_wide_low_stock_amount = 3;
// Set the store-wide default.
update_option( 'woocommerce_notify_low_stock_amount', $site_wide_low_stock_amount );
// Simple product, don't set low stock amount.
$product = WC_Helper_Product::create_simple_product(
true,
array(
'manage_stock' => true,
'stock_quantity' => 10,
)
);
$this->assertEquals( $site_wide_low_stock_amount, wc_get_low_stock_amount( $product ) );
}
/**
* Test wc_get_low_stock_amount with a variable product which has low stock amount set on the variation level,
* but not on the parent level. Should use the value from the variation.
*/
public function test_wc_get_low_stock_amount_variation_set_parent_unset() {
$site_wide_low_stock_amount = 3;
$variation_low_stock_amount = 7;
// Set the store-wide default.
update_option( 'woocommerce_notify_low_stock_amount', $site_wide_low_stock_amount );
// Parent low stock amount NOT set.
$variable_product = WC_Helper_Product::create_variation_product();
$variable_product->set_manage_stock( false );
$variable_product->save();
// Set the variation low stock amount.
$variations = $variable_product->get_available_variations( 'objects' );
$var1 = $variations[0];
$var1->set_manage_stock( true );
$var1->set_low_stock_amount( $variation_low_stock_amount );
$var1->save();
$this->assertEquals( $variation_low_stock_amount, wc_get_low_stock_amount( $var1 ) );
// Even after turning on manage stock on the parent, but with no value.
$variable_product->set_manage_stock( true );
$variable_product->save();
$this->assertEquals( $variation_low_stock_amount, wc_get_low_stock_amount( $var1 ) );
// Ans also after turning the manage stock off again on the parent.
$variable_product->set_manage_stock( false );
$variable_product->save();
$this->assertEquals( $variation_low_stock_amount, wc_get_low_stock_amount( $var1 ) );
}
/**
* Test wc_get_low_stock_amount with a variable product which has low stock amount set on the variation level,
* and also on the parent level. Should use the value from the variation.
*/
public function test_wc_get_low_stock_amount_variation_set_parent_set() {
$site_wide_low_stock_amount = 3;
$parent_low_stock_amount = 5;
$variation_low_stock_amount = 7;
// Set the store-wide default.
update_option( 'woocommerce_notify_low_stock_amount', $site_wide_low_stock_amount );
// Set the parent low stock amount.
$variable_product = WC_Helper_Product::create_variation_product();
$variable_product->set_manage_stock( true );
$variable_product->set_low_stock_amount( $parent_low_stock_amount );
$variable_product->save();
// Set the variation low stock amount.
$variations = $variable_product->get_available_variations( 'objects' );
$var1 = $variations[0];
$var1->set_manage_stock( true );
$var1->set_low_stock_amount( $variation_low_stock_amount );
$var1->save();
$this->assertEquals( $variation_low_stock_amount, wc_get_low_stock_amount( $var1 ) );
}
/**
* Test wc_get_low_stock_amount with a variable product which has low stock amount set on the parent level,
* but NOT on the variation level. Should use the value from the parent.
*/
public function test_wc_get_low_stock_amount_variation_unset_parent_set() {
$site_wide_low_stock_amount = 3;
$parent_low_stock_amount = 5;
// Set the store-wide default.
update_option( 'woocommerce_notify_low_stock_amount', $site_wide_low_stock_amount );
// Set the parent low stock amount.
$variable_product = WC_Helper_Product::create_variation_product();
$variable_product->set_manage_stock( true );
$variable_product->set_low_stock_amount( $parent_low_stock_amount );
$variable_product->save();
// Don't set the variation low stock amount.
$variations = $variable_product->get_available_variations( 'objects' );
$var1 = $variations[0];
$this->assertEquals( $parent_low_stock_amount, wc_get_low_stock_amount( $var1 ) );
}
/**
* Test wc_get_low_stock_amount with a variable product which *doesn't have* low stock amount set either on the parent level,
* or on the variation level. Should use the value from the site-wide setting.
*/
public function test_wc_get_low_stock_amount_variation_unset_parent_unset() {
$site_wide_low_stock_amount = 3;
// Set the store-wide default.
update_option( 'woocommerce_notify_low_stock_amount', $site_wide_low_stock_amount );
// Set the parent low stock amount.
$variable_product = WC_Helper_Product::create_variation_product();
$variable_product->set_manage_stock( false );
// Don't set the variation low stock amount.
$variations = $variable_product->get_available_variations( 'objects' );
$var1 = $variations[0];
$var1->set_manage_stock( false );
$this->assertEquals( $site_wide_low_stock_amount, wc_get_low_stock_amount( $var1 ) );
}
} }