Merge branch 'master' into issue_21515

This commit is contained in:
Ron Rennick 2018-10-15 11:00:14 -03:00
commit 1ea2d79dd6
35 changed files with 774 additions and 210 deletions

View File

@ -1,48 +0,0 @@
<!-- This form is for reporting bugs and issues specific to the WooCommerce plugin. This is not a support portal. If you need technical support from a human being, please submit a ticket via the helpdesk instead: https://woocommerce.com/contact-us/ -->
<!-- Usage questions can also be directed to the public support forum here: https://wordpress.org/support/plugin/woocommerce, unless this is a question about a premium extension in which case you should use the helpdesk. -->
<!-- If you have a feature request, submit it to: http://ideas.woocommerce.com/forums/133476-woocommerce -->
<!-- If you are a developer who needs a new filter/hook raise a PR instead :) -->
<!-- Please be as descriptive as possible; issues lacking the below details, or for any other reason than to report a bug, may be closed without action. -->
## Prerequisites
<!-- MARK COMPLETED ITEMS WITH AN [x] -->
- [ ] I have searched for similar issues in both open and closed tickets and cannot find a duplicate
- [ ] The issue still exists against the latest `master` branch of WooCommerce on Github (this is **not** the same version as on WordPress.org!)
- [ ] I have attempted to find the simplest possible steps to reproduce the issue
- [ ] I have included a failing test as a pull request (Optional)
## Steps to reproduce the issue
<!-- We need to be able to reproduce the bug in order to fix it so please be descriptive! -->
1.
2.
3.
## Expected/actual behavior
When I follow those steps, I see...
I was expecting to see...
## Isolating the problem
<!-- MARK COMPLETED ITEMS WITH AN [x] -->
- [ ] This bug happens with only WooCommerce plugin active
- [ ] This bug happens with a default WordPress theme active, or [Storefront](https://woocommerce.com/storefront/)
- [ ] I can reproduce this bug consistently using the steps above
## WordPress Environment
<details>
```
Copy and paste the system status report from **WooCommerce > System Status** in WordPress admin here.
```
</details>

View File

@ -1,5 +1,10 @@
== Changelog ==
= 3.4.6 - 2018-10-11 =
* Fix - Security issues
* Fix - Allow percent coupons with sale restrictions to apply to carts with sale items in them. #21241
* Fix - Prevent multiple slashing of variation's SKU. #21019
= 3.4.5 - 2018-08-29 =
* Fix - Tweak sanitization when resetting password cookie. #20901
* Fix - Use `+` instead of `array_merge` when appending parent to tax class to fix issues with numeric tax class names. #20916

View File

@ -83,6 +83,41 @@ class WC_Product_CSV_Importer_Controller {
return new $importer_class( $file, $args );
}
/**
* Check whether a file is a valid CSV file.
*
* @param string $file File path.
* @param bool $check_path Whether to also check the file is located in a valid location (Default: true).
* @return bool
*/
public static function is_file_valid_csv( $file, $check_path = true ) {
if ( $check_path && apply_filters( 'woocommerce_product_csv_importer_check_import_file_path', true ) && 0 !== stripos( $file, ABSPATH ) ) {
return false;
}
$valid_filetypes = self::get_valid_csv_filetypes();
$filetype = wp_check_filetype( $file, $valid_filetypes );
if ( in_array( $filetype['type'], $valid_filetypes, true ) ) {
return true;
}
return false;
}
/**
* Get all the valid filetypes for a CSV file.
*
* @return array
*/
protected static function get_valid_csv_filetypes() {
return apply_filters(
'woocommerce_csv_product_import_valid_filetypes', array(
'csv' => 'text/csv',
'txt' => 'text/plain',
)
);
}
/**
* Constructor.
*/
@ -271,13 +306,6 @@ class WC_Product_CSV_Importer_Controller {
* @return string|WP_Error
*/
public function handle_upload() {
$valid_filetypes = apply_filters(
'woocommerce_csv_product_import_valid_filetypes', array(
'csv' => 'text/csv',
'txt' => 'text/plain',
)
);
// phpcs:disable WordPress.CSRF.NonceVerification.NoNonceVerification -- Nonce already verified in WC_Product_CSV_Importer_Controller::upload_form_handler()
$file_url = isset( $_POST['file_url'] ) ? wc_clean( wp_unslash( $_POST['file_url'] ) ) : '';
@ -286,14 +314,13 @@ class WC_Product_CSV_Importer_Controller {
return new WP_Error( 'woocommerce_product_csv_importer_upload_file_empty', __( 'File is empty. Please upload something more substantial. This error could also be caused by uploads being disabled in your php.ini or by post_max_size being defined as smaller than upload_max_filesize in php.ini.', 'woocommerce' ) );
}
$filetype = wp_check_filetype( wc_clean( wp_unslash( $_FILES['import']['name'] ) ), $valid_filetypes );
if ( ! in_array( $filetype['type'], $valid_filetypes, true ) ) {
if ( ! self::is_file_valid_csv( wc_clean( wp_unslash( $_FILES['import']['name'] ) ), false ) ) {
return new WP_Error( 'woocommerce_product_csv_importer_upload_file_invalid', __( 'Invalid file type. The importer supports CSV and TXT file formats.', 'woocommerce' ) );
}
$overrides = array(
'test_form' => false,
'mimes' => $valid_filetypes,
'mimes' => self::get_valid_csv_filetypes(),
);
$import = $_FILES['import']; // WPCS: sanitization ok, input var ok.
$upload = wp_handle_upload( $import, $overrides );
@ -323,8 +350,7 @@ class WC_Product_CSV_Importer_Controller {
return $upload['file'];
} elseif ( file_exists( ABSPATH . $file_url ) ) {
$filetype = wp_check_filetype( ABSPATH . $file_url, $valid_filetypes );
if ( ! in_array( $filetype['type'], $valid_filetypes, true ) ) {
if ( ! self::is_file_valid_csv( ABSPATH . $file_url ) ) {
return new WP_Error( 'woocommerce_product_csv_importer_upload_file_invalid', __( 'Invalid file type. The importer supports CSV and TXT file formats.', 'woocommerce' ) );
}
@ -372,8 +398,15 @@ class WC_Product_CSV_Importer_Controller {
* Import the file if it exists and is valid.
*/
public function import() {
if ( ! self::is_file_valid_csv( $this->file ) ) {
$this->add_error( __( 'Invalid file type. The importer supports CSV and TXT file formats.', 'woocommerce' ) );
$this->output_errors();
return;
}
if ( ! is_file( $this->file ) ) {
$this->add_error( __( 'The file does not exist, please try again.', 'woocommerce' ) );
$this->output_errors();
return;
}

View File

@ -174,8 +174,11 @@ class WC_Settings_Shipping extends WC_Settings_Page {
switch ( $current_section ) {
case 'options':
WC_Admin_Settings::save_fields( $this->get_settings() );
do_action( 'woocommerce_update_options_' . $this->id . '_options' );
break;
case 'classes':
do_action( 'woocommerce_update_options_' . $this->id . '_classes' );
break;
case '':
break;
default:
@ -189,10 +192,6 @@ class WC_Settings_Shipping extends WC_Settings_Page {
break;
}
if ( $current_section ) {
do_action( 'woocommerce_update_options_' . $this->id . '_' . $current_section );
}
// Increments the transient version to invalidate cache.
WC_Cache_Helper::get_transient_version( 'shipping', true );
}

View File

@ -16,7 +16,7 @@ if ( ! defined( 'ABSPATH' ) ) {
<h2>
<?php echo esc_html( $viewed_log ); ?>
<?php if ( ! empty( $viewed_log ) ) : ?>
<a class="page-title-action" href="<?php echo esc_url( wp_nonce_url( add_query_arg( array( 'handle' => $viewed_log ), admin_url( 'admin.php?page=wc-status&tab=logs' ) ), 'remove_log' ) ); ?>" class="button"><?php esc_html_e( 'Delete log', 'woocommerce' ); ?></a>
<a class="page-title-action" href="<?php echo esc_url( wp_nonce_url( add_query_arg( array( 'handle' => sanitize_title( $viewed_log ) ), admin_url( 'admin.php?page=wc-status&tab=logs' ) ), 'remove_log' ) ); ?>" class="button"><?php esc_html_e( 'Delete log', 'woocommerce' ); ?></a>
<?php endif; ?>
</h2>
</div>

View File

@ -401,7 +401,7 @@ class WC_Cart extends WC_Legacy_Cart {
}
/**
* Return all calculated coupon totals.
* Sets the array of calculated coupon totals.
*
* @since 3.2.0
* @param array $value Value to set.
@ -410,7 +410,7 @@ class WC_Cart extends WC_Legacy_Cart {
$this->coupon_discount_totals = (array) $value;
}
/**
* Return all calculated coupon tax totals.
* Sets the array of calculated coupon tax totals.
*
* @since 3.2.0
* @param array $value Value to set.
@ -1317,6 +1317,7 @@ class WC_Cart extends WC_Legacy_Cart {
'postcode' => $this->get_customer()->get_shipping_postcode(),
'city' => $this->get_customer()->get_shipping_city(),
'address' => $this->get_customer()->get_shipping_address(),
'address_1' => $this->get_customer()->get_shipping_address(), // Provide both address and address_1 for backwards compatibility.
'address_2' => $this->get_customer()->get_shipping_address_2(),
),
'cart_subtotal' => $this->get_displayed_subtotal(),

View File

@ -243,6 +243,14 @@ class WC_Download_Handler {
$parsed_file_path = wp_parse_url( $file_path );
$remote_file = true;
// Paths that begin with '//' are always remote URLs.
if ( '//' === substr( $file_path, 0, 2 ) ) {
return array(
'remote_file' => true,
'file_path' => is_ssl() ? 'https:' . $file_path : 'http:' . $file_path,
);
}
// See if path needs an abspath prepended to work.
if ( file_exists( ABSPATH . $file_path ) ) {
$remote_file = false;

View File

@ -87,7 +87,19 @@ class WC_Product_Download implements ArrayAccess {
* @return boolean
*/
public function is_allowed_filetype() {
if ( 'relative' !== $this->get_type_of_file_path() ) {
$file_path = $this->get_file();
// File types for URL-based files located on the server should get validated.
$is_file_on_server = false;
if ( false !== stripos( $file_path, network_site_url( '/', 'https' ) ) ||
false !== stripos( $file_path, network_site_url( '/', 'http' ) ) ||
false !== stripos( $file_path, site_url( '/', 'https' ) ) ||
false !== stripos( $file_path, site_url( '/', 'http' ) )
) {
$is_file_on_server = true;
}
if ( ! $is_file_on_server && 'relative' !== $this->get_type_of_file_path() ) {
return true;
}
return ! $this->get_file_extension() || in_array( $this->get_file_type(), $this->get_allowed_mime_types(), true );

View File

@ -459,6 +459,9 @@ class WC_Query {
);
switch ( $orderby ) {
case 'id':
$args['orderby'] = 'ID';
break;
case 'menu_order':
$args['orderby'] = 'menu_order title';
break;

View File

@ -442,7 +442,8 @@ class WC_Webhook extends WC_Legacy_Webhook {
);
// Track failures.
if ( intval( $response_code ) >= 200 && intval( $response_code ) < 300 ) {
// Check for a success, which is a 2xx, 301 or 302 Response Code.
if ( intval( $response_code ) >= 200 && intval( $response_code ) < 303 ) {
$this->set_failure_count( 0 );
$this->save();
} else {

View File

@ -63,6 +63,10 @@ class WC_Product_CSV_Importer extends WC_Product_Importer {
* Read file.
*/
protected function read_file() {
if ( ! WC_Product_CSV_Importer_Controller::is_file_valid_csv( $this->file ) ) {
wp_die( __( 'Invalid file type. The importer supports CSV and TXT file formats.', 'woocommerce' ) );
}
$handle = fopen( $this->file, 'r' ); // @codingStandardsIgnoreLine.
if ( false !== $handle ) {

View File

@ -146,8 +146,8 @@ class WC_Log_Handler_DB extends WC_Log_Handler {
$wpdb->query(
$wpdb->prepare(
"DELETE FROM {$wpdb->prefix}woocommerce_log WHERE timestamp < %d",
$timestamp
"DELETE FROM {$wpdb->prefix}woocommerce_log WHERE timestamp < %s",
date( 'Y-m-d H:i:s', $timestamp )
)
);
}

View File

@ -248,9 +248,12 @@ class WC_Log_Handler_File extends WC_Log_Handler {
*/
public function remove( $handle ) {
$removed = false;
$file = trailingslashit( WC_LOG_DIR ) . $handle;
if ( $file ) {
if ( is_file( $file ) && is_writable( $file ) ) { // phpcs:ignore WordPress.VIP.FileSystemWritesDisallow.file_ops_is_writable
$logs = $this->get_log_files();
$handle = sanitize_title( $handle );
if ( isset( $logs[ $handle ] ) && $logs[ $handle ] ) {
$file = realpath( trailingslashit( WC_LOG_DIR ) . $logs[ $handle ] );
if ( 0 === stripos( $file, trailingslashit( WC_LOG_DIR ) ) && is_file( $file ) && is_writable( $file ) ) { // phpcs:ignore WordPress.VIP.FileSystemWritesDisallow.file_ops_is_writable
$this->close( $file ); // Close first to be certain no processes keep it alive after it is unlinked.
$removed = unlink( $file ); // phpcs:ignore WordPress.VIP.FileSystemWritesDisallow.file_ops_unlink
}

View File

@ -536,16 +536,26 @@ function wc_create_attribute( $args ) {
);
// Update product attributes which use this taxonomy.
$old_attribute_name_length = strlen( $old_slug ) + 3;
$attribute_name_length = strlen( $data['attribute_name'] ) + 3;
$wpdb->query(
$old_taxonomy_name = 'pa_' . $old_slug;
$new_taxonomy_name = 'pa_' . $data['attribute_name'];
$metadatas = $wpdb->get_results(
$wpdb->prepare(
"UPDATE {$wpdb->postmeta} SET meta_value = REPLACE( meta_value, %s, %s ) WHERE meta_key = '_product_attributes'",
's:' . $old_attribute_name_length . ':"pa_' . $old_slug . '"',
's:' . $attribute_name_length . ':"pa_' . $data['attribute_name'] . '"'
)
);
"SELECT post_id, meta_value FROM {$wpdb->postmeta} WHERE meta_key = '_product_attributes' AND meta_value LIKE %s",
'%' . $wpdb->esc_like( $old_taxonomy_name ) . '%'
),
ARRAY_A );
foreach ( $metadatas as $metadata ) {
$product_id = $metadata['post_id'];
$unserialized_data = maybe_unserialize( $metadata['meta_value'] );
if ( ! $unserialized_data || ! is_array( $unserialized_data ) || ! isset( $unserialized_data[ $old_taxonomy_name ] ) ) {
continue;
}
$unserialized_data[ $new_taxonomy_name ] = $unserialized_data[ $old_taxonomy_name ];
unset( $unserialized_data[ $old_taxonomy_name ] );
$unserialized_data[ $new_taxonomy_name ]['name'] = $new_taxonomy_name;
update_post_meta( $product_id, '_product_attributes', $unserialized_data );
}
// Update variations which use this taxonomy.
$wpdb->update(

View File

@ -1633,7 +1633,7 @@ function wc_get_logger() {
$class = apply_filters( 'woocommerce_logging_class', 'WC_Logger' );
if ( null !== $logger && is_a( $logger, $class ) ) {
if ( null !== $logger && is_string( $class ) && is_a( $logger, $class ) ) {
return $logger;
}

View File

@ -73,7 +73,7 @@ function wc_get_orders( $args ) {
*
* @param mixed $the_order Post object or post ID of the order.
*
* @return bool|WC_Order|WC_Refund
* @return bool|WC_Order|WC_Order_Refund
*/
function wc_get_order( $the_order = false ) {
if ( ! did_action( 'woocommerce_after_register_post_type' ) ) {

View File

@ -292,7 +292,7 @@ add_action( 'woocommerce_register_form', 'wc_registration_privacy_policy_text',
/**
* Notices.
*/
add_action( 'woocommerce_cart_is_empty', 'woocommerce_output_all_notices', 10 );
add_action( 'woocommerce_cart_is_empty', 'woocommerce_output_all_notices', 5 );
add_action( 'woocommerce_shortcode_before_product_cat_loop', 'woocommerce_output_all_notices', 10 );
add_action( 'woocommerce_before_shop_loop', 'woocommerce_output_all_notices', 10 );
add_action( 'woocommerce_before_single_product', 'woocommerce_output_all_notices', 10 );

View File

@ -352,6 +352,12 @@ function wc_modify_editable_roles( $roles ) {
if ( ! current_user_can( 'administrator' ) ) {
unset( $roles['administrator'] );
}
if ( current_user_can( 'shop_manager' ) ) {
$shop_manager_editable_roles = apply_filters( 'woocommerce_shop_manager_editable_roles', array( 'customer' ) );
return array_intersect_key( $roles, array_flip( $shop_manager_editable_roles ) );
}
return $roles;
}
add_filter( 'editable_roles', 'wc_modify_editable_roles' );
@ -379,6 +385,15 @@ function wc_modify_map_meta_cap( $caps, $cap, $user_id, $args ) {
if ( user_can( $args[0], 'administrator' ) && ! current_user_can( 'administrator' ) ) {
$caps[] = 'do_not_allow';
}
// Shop managers can only edit customer info.
if ( current_user_can( 'shop_manager' ) ) {
$userdata = get_userdata( $args[0] );
$shop_manager_editable_roles = apply_filters( 'woocommerce_shop_manager_editable_roles', array( 'customer' ) );
if ( property_exists( $userdata, 'roles' ) && ! empty( $userdata->roles ) && ! array_intersect( $userdata->roles, $shop_manager_editable_roles ) ) {
$caps[] = 'do_not_allow';
}
}
}
break;
}

View File

@ -13,7 +13,7 @@ WooCommerce is a powerful, extendable eCommerce plugin that helps you sell anyth
WooCommerce is a free eCommerce plugin that allows you to sell anything, beautifully. Built to integrate seamlessly with WordPress, WooCommerce is the worlds favorite eCommerce solution that gives both store owners and developers complete control.
With endless flexibility and access to hundreds of free and premium WordPress extensions, WooCommerce now powers 30% of all online stores -- more than any other platform.
With endless flexibility and access to hundreds of free and premium WordPress extensions, WooCommerce now powers 30% of all online stores &mdash; more than any other platform.
[youtube https://www.youtube.com/watch?v=1KahlicghaE]
@ -28,10 +28,10 @@ Offer free shipping, flat rate shipping, or make real-time calculations. Limit y
= Extensive payment options =
WooCommerce comes bundled with the ability to accept major credit cards, PayPal, BACS (bank transfers), and cash on delivery. Need additional options? More than 140 region-specific gateways integrate with WooCommerce, including popular choices like Stripe, Authorize.Net, and Amazon Payments.
= You control it all -- forever =
= You control it all &mdash; forever =
WooCommerce gives you complete control of your store, from taxes to stock levels to customer accounts. Add and remove extensions, change your design, and switch settings as you please. Its all under your control.
One of the biggest risks of using a hosted eCommerce platform is what happens to your store if the provider closes up shop. With WooCommerce, you have complete control, so theres never any reason to worry. Your data belongs to you -- and its kept secure, thanks to regular audits by industry leaders.
One of the biggest risks of using a hosted eCommerce platform is what happens to your store if the provider closes up shop. With WooCommerce, you have complete control, so theres never any reason to worry. Your data belongs to you &mdash; and its kept secure, thanks to regular audits by industry leaders.
= Define your style with Storefront =
@ -41,11 +41,11 @@ Define your style even further by customizing Storefront to your liking or choos
= Built with developers in mind =
Extendable, adaptable, and open source -- WooCommerce was created with developers in mind. With its strong, robust framework, you can scale your clients store all the way from basic to high-end (infinity and beyond).
Extendable, adaptable, and open source &mdash; WooCommerce was created with developers in mind. With its strong, robust framework, you can scale your clients store all the way from basic to high-end (infinity and beyond).
Built with a REST API, WooCommerce can integrate with virtually any service. Your stores data can be accessed anywhere, anytime, 100% securely. WooCommerce allows developers to easily create, modify, and grow a store that meets their specifications.
No matter the size of the store you want to build, WooCommerce will scale to meet your requirements. With a growing collection of more than 300 extensions, you can enhance each stores features to meet your clients unique needs -- or even create your own solution.
No matter the size of the store you want to build, WooCommerce will scale to meet your requirements. With a growing collection of more than 300 extensions, you can enhance each stores features to meet your clients unique needs &mdash; or even create your own solution.
If security is a concern, rest easy. WooCommerce is audited by a dedicated team of developers working around the clock to identify and patch any and all discovered bugs.

View File

@ -10,24 +10,23 @@
* happen. When this occurs the version of the template file will be bumped and
* the readme will list any important changes.
*
* @see https://docs.woocommerce.com/document/template-structure/
* @author WooThemes
* @package WooCommerce/Templates
* @version 2.3.0
* @see https://docs.woocommerce.com/document/template-structure/
* @package WooCommerce/Templates
* @version 3.5.0
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
?>
<li class="wc_payment_method payment_method_<?php echo $gateway->id; ?>">
<input id="payment_method_<?php echo $gateway->id; ?>" type="radio" class="input-radio" name="payment_method" value="<?php echo esc_attr( $gateway->id ); ?>" <?php checked( $gateway->chosen, true ); ?> data-order_button_text="<?php echo esc_attr( $gateway->order_button_text ); ?>" />
<li class="wc_payment_method payment_method_<?php echo esc_attr( $gateway->id ); ?>">
<input id="payment_method_<?php echo esc_attr( $gateway->id ); ?>" type="radio" class="input-radio" name="payment_method" value="<?php echo esc_attr( $gateway->id ); ?>" <?php checked( $gateway->chosen, true ); ?> data-order_button_text="<?php echo esc_attr( $gateway->order_button_text ); ?>" />
<label for="payment_method_<?php echo $gateway->id; ?>">
<?php echo $gateway->get_title(); ?> <?php echo $gateway->get_icon(); ?>
<label for="payment_method_<?php echo esc_attr( $gateway->id ); ?>">
<?php echo $gateway->get_title(); /* phpcs:ignore WordPress.XSS.EscapeOutput.OutputNotEscaped */ ?> <?php echo $gateway->get_icon(); /* phpcs:ignore WordPress.XSS.EscapeOutput.OutputNotEscaped */ ?>
</label>
<?php if ( $gateway->has_fields() || $gateway->get_description() ) : ?>
<div class="payment_box payment_method_<?php echo $gateway->id; ?>" <?php if ( ! $gateway->chosen ) : ?>style="display:none;"<?php endif; ?>>
<div class="payment_box payment_method_<?php echo esc_attr( $gateway->id ); ?>" <?php if ( ! $gateway->chosen ) : /* phpcs:ignore Squiz.ControlStructures.ControlSignature.NewlineAfterOpenBrace */ ?>style="display:none;"<?php endif; /* phpcs:ignore Squiz.ControlStructures.ControlSignature.NewlineAfterOpenBrace */ ?>>
<?php $gateway->payment_fields(); ?>
</div>
<?php endif; ?>

View File

@ -25,7 +25,7 @@ if ( ! defined( 'ABSPATH' ) ) {
do_action( 'woocommerce_email_header', $email_heading, $email ); ?>
<?php /* translators: %1$s: Customer full name. %2$s: Order numer */ ?>
<p><?php printf( esc_html__( 'Alas. Just to let you know -- %1$s has cancelled order #%2$s:', 'woocommerce' ), esc_html( $order->get_formatted_billing_full_name() ), esc_html( $order->get_order_number() ) ); ?></p>
<p><?php printf( esc_html__( 'Alas. Just to let you know &mdash; %1$s has cancelled order #%2$s:', 'woocommerce' ), esc_html( $order->get_formatted_billing_full_name() ), esc_html( $order->get_order_number() ) ); ?></p>
<?php

View File

@ -27,7 +27,7 @@ do_action( 'woocommerce_email_header', $email_heading, $email ); ?>
<?php /* translators: %s: Customer first name */ ?>
<p><?php printf( esc_html__( 'Hi %s,', 'woocommerce' ), esc_html( $order->get_billing_first_name() ) ); ?></p>
<?php /* translators: %s: Order number */ ?>
<p><?php printf( esc_html__( 'Just to let you know -- your payment has been confirmed, and order #%s is now being processed:', 'woocommerce' ), esc_html( $order->get_order_number() ) ); ?></p>
<p><?php printf( esc_html__( 'Just to let you know &mdash; your payment has been confirmed, and order #%s is now being processed:', 'woocommerce' ), esc_html( $order->get_order_number() ) ); ?></p>
<?php

View File

@ -22,7 +22,7 @@ if ( ! defined( 'ABSPATH' ) ) {
echo '= ' . esc_html( $email_heading ) . " =\n\n";
/* translators: %1$s: Customer full name. %2$s: Order numer */
echo sprintf( esc_html__( 'Alas. Just to let you know -- %1$s has cancelled order #%2$s:', 'woocommerce' ), esc_html( $order->get_formatted_billing_full_name() ), esc_html( $order->get_order_number() ) ) . "\n\n";
echo sprintf( esc_html__( 'Alas. Just to let you know &mdash; %1$s has cancelled order #%2$s:', 'woocommerce' ), esc_html( $order->get_formatted_billing_full_name() ), esc_html( $order->get_order_number() ) ) . "\n\n";
echo "=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n\n";

View File

@ -26,17 +26,10 @@ echo sprintf( esc_html__( 'Hi %s,', 'woocommerce' ), esc_html( $order->get_billi
if ( $order->has_status( 'pending' ) ) {
echo sprintf(
wp_kses(
/* translators: %1$s Site title, %2$s Order pay link */
__( 'An order has been created for you on %1$s. Your invoice is below, with a link to make payment when youre ready: %1$s', 'woocommerce' ),
array(
'a' => array(
'href' => array(),
),
)
),
/* translators: %1$s Site title, %2$s Order pay link */
__( 'An order has been created for you on %1$s. Your invoice is below, with a link to make payment when youre ready: %2$s', 'woocommerce' ),
esc_html( get_bloginfo( 'name', 'display' ) ),
'<a href="' . esc_url( $order->get_checkout_payment_url() ) . '">' . esc_html__( 'Pay for this order', 'woocommerce' ) . '</a>'
esc_url( $order->get_checkout_payment_url() )
) . "\n\n";
} else {

View File

@ -24,7 +24,7 @@ echo '= ' . esc_html( $email_heading ) . " =\n\n";
/* translators: %s: Customer first name */
echo sprintf( esc_html__( 'Hi %s,', 'woocommerce' ), esc_html( $order->get_billing_first_name() ) ) . "\n\n";
/* translators: %s: Order number */
echo sprintf( esc_html__( 'Just to let you know -- your payment has been confirmed, and order #%s is now being processed:', 'woocommerce' ), esc_html( $order->get_order_number() ) ) . "\n\n";
echo sprintf( esc_html__( 'Just to let you know &mdash; your payment has been confirmed, and order #%s is now being processed:', 'woocommerce' ), esc_html( $order->get_order_number() ) ) . "\n\n";
echo "=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n\n";

View File

@ -44,7 +44,7 @@ A text code coverage summary can be displayed using the `--coverage-text` option
* Use the test coverage HTML report (under `tmp/coverage/index.html`) to examine which lines your tests are covering and aim for 100% coverage
* For code that cannot be tested (e.g. they require a certain PHP version), you can exclude them from coverage using a comment: `// @codeCoverageIgnoreStart` and `// @codeCoverageIgnoreEnd`. For example, see [`wc_round_tax_total()`](https://github.com/woocommerce/woocommerce/blob/35f83867736713955fa2c4f463a024578bb88795/includes/wc-formatting-functions.php#L208-L219)
* In addition to covering each line of a method/function, make sure to test common input and edge cases.
* Prefer `assertsEquals()` where possible as it tests both type & equality
* Prefer `assertSame()` where possible as it tests both type & equality
* Remember that only methods prefixed with `test` will be run so use helper methods liberally to keep test methods small and reduce code duplication. If there is a common helper method used in multiple test files, consider adding it to the `WC_Unit_Test_Case` class so it can be shared by all test cases
* Filters persist between test cases so be sure to remove them in your test method or in the `tearDown()` method.
* Use data providers where possible. Be sure that their name is like `data_provider_function_to_test` (i.e. the data provider for `test_is_postcode` would be `data_provider_test_is_postcode`). Read more about data providers [here](https://phpunit.de/manual/current/en/writing-tests-for-phpunit.html#writing-tests-for-phpunit.data-providers).

View File

@ -65,6 +65,95 @@ class WC_Tests_Attributes_Functions extends WC_Unit_Test_Case {
wc_delete_attribute( $id );
}
/**
* Test that updating a global attribute will not modify local attribute data.
*
* @since 3.4.6
*/
public function test_wc_create_attribute_serialized_data() {
global $wpdb;
$global_attribute_data = WC_Helper_Product::create_attribute( 'test', array( 'Chicken', 'Nuggets' ) );
$local_attribute = new WC_Product_Attribute();
$local_attribute->set_id( 0 );
$local_attribute->set_name( 'Test Local Attribute' );
$local_attribute->set_options( array( 'Fish', 'Fingers', 's:7:"pa_test' ) );
$local_attribute->set_position( 0 );
$local_attribute->set_visible( true );
$local_attribute->set_variation( false );
$global_attribute = new WC_Product_Attribute();
$global_attribute->set_id( $global_attribute_data['attribute_id'] );
$global_attribute->set_name( $global_attribute_data['attribute_taxonomy'] );
$global_attribute->set_options( $global_attribute_data['term_ids'] );
$global_attribute->set_position( 1 );
$global_attribute->set_visible( true );
$global_attribute->set_variation( false );
$product = new WC_Product_Simple();
$product->set_attributes(
array(
'test-local' => $local_attribute,
'test-global' => $global_attribute,
)
);
$product->save();
// Check everything looks good before updating the attribute.
$meta_before_update = $wpdb->get_results( $wpdb->prepare( "SELECT meta_value FROM {$wpdb->postmeta} WHERE meta_key = '_product_attributes' AND post_id = %d", $product->get_id() ), ARRAY_A );
$product_meta_before_update = @unserialize( $meta_before_update[0]['meta_value'] ); // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged, WordPress.PHP.DiscouragedPHPFunctions.serialize_unserialize
$this->assertNotFalse( $product_meta_before_update, 'Meta should be an unserializable string' );
$expected_local_attribute_data = array(
'name' => 'Test Local Attribute',
'value' => 'Fish | Fingers | s:7:"pa_test',
'position' => 0,
'is_visible' => 1,
'is_variation' => 0,
'is_taxonomy' => 0,
);
$expected_global_attribute_data = array(
'name' => 'pa_test',
'value' => '',
'position' => 1,
'is_visible' => 1,
'is_variation' => 0,
'is_taxonomy' => 1,
);
$local_before = isset( $product_meta_before_update['Test Local Attribute'] ) ? $product_meta_before_update['Test Local Attribute'] : $product_meta_before_update['test-local-attribute'];
$this->assertEquals( $expected_local_attribute_data, $local_before );
$this->assertEquals( $expected_global_attribute_data, $product_meta_before_update['pa_test'] );
// Update the global attribute.
$updated_global_attribute_id = wc_create_attribute(
array(
'id' => $global_attribute_data['attribute_id'],
'name' => 'Test Update',
'old_slug' => 'test',
'slug' => 'testupdate',
)
);
$this->assertEquals( $updated_global_attribute_id, $global_attribute_data['attribute_id'] );
// Changes to the global attribute should update in the product without causing side-effects.
$meta_after_update = $wpdb->get_results( $wpdb->prepare( "SELECT meta_value FROM {$wpdb->postmeta} WHERE meta_key = '_product_attributes' AND post_id = %d", $product->get_id() ), ARRAY_A );
$product_meta_after_update = @unserialize( $meta_after_update[0]['meta_value'] ); // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged, WordPress.PHP.DiscouragedPHPFunctions.serialize_unserialize
$this->assertNotFalse( $product_meta_after_update, 'Meta should be an unserializable string' );
$expected_global_attribute_data = array(
'name' => 'pa_testupdate',
'value' => '',
'position' => 1,
'is_visible' => 1,
'is_variation' => 0,
'is_taxonomy' => 1,
);
$this->assertEquals( $local_before, isset( $product_meta_after_update['Test Local Attribute'] ) ? $product_meta_after_update['Test Local Attribute'] : $product_meta_after_update['test-local-attribute'] );
$this->assertEquals( $expected_global_attribute_data, $product_meta_after_update['pa_testupdate'] );
$this->assertArrayNotHasKey( 'pa_test', $product_meta_after_update );
}
/**
* Tests wc_update_attribute().
*

View File

@ -27,6 +27,16 @@ class WC_Tests_Product_CSV_Importer extends WC_Unit_Test_Case {
$bootstrap = WC_Unit_Tests_Bootstrap::instance();
require_once $bootstrap->plugin_dir . '/includes/import/class-wc-product-csv-importer.php';
require_once $bootstrap->plugin_dir . '/includes/admin/importers/class-wc-product-csv-importer-controller.php';
add_filter( 'woocommerce_product_csv_importer_check_import_file_path', '__return_false' );
}
/**
* Remove filters.
*/
public function tearDown() {
parent::tearDown();
remove_filter( 'woocommerce_product_csv_importer_check_import_file_path', '__return_false' );
}
/**

View File

@ -8,12 +8,8 @@
class WC_Tests_Log_Handler_DB extends WC_Unit_Test_Case {
public function setUp() {
parent::setUp();
WC_Log_Handler_DB::flush();
}
public function tearDown() {
WC_Log_Handler_DB::flush();
parent::tearDown();
$this->handler = new WC_Log_Handler_DB( array( 'threshold' => 'debug' ) );
}
/**
@ -24,87 +20,92 @@ class WC_Tests_Log_Handler_DB extends WC_Unit_Test_Case {
public function test_handle() {
global $wpdb;
$handler = new WC_Log_Handler_DB( array( 'threshold' => 'debug' ) );
$time = time();
$context = array( 1, 2, 'a', 'b', 'key' => 'value' );
$time = time();
$context = array(
1,
2,
'a',
'b',
'key' => 'value',
);
$handler->handle( $time, 'debug', 'msg_debug', array( 'source' => 'source_debug' ) );
$handler->handle( $time, 'info', 'msg_info', array( 'source' => 'source_info' ) );
$handler->handle( $time, 'notice', 'msg_notice', array( 'source' => 'source_notice' ) );
$handler->handle( $time, 'warning', 'msg_warning', array( 'source' => 'source_warning' ) );
$handler->handle( $time, 'error', 'msg_error', array( 'source' => 'source_error' ) );
$handler->handle( $time, 'critical', 'msg_critical', array( 'source' => 'source_critical' ) );
$handler->handle( $time, 'alert', 'msg_alert', array( 'source' => 'source_alert' ) );
$handler->handle( $time, 'emergency', 'msg_emergency', array( 'source' => 'source_emergency' ) );
$this->handler->handle( $time, 'debug', 'msg_debug', array( 'source' => 'source_debug' ) );
$this->handler->handle( $time, 'info', 'msg_info', array( 'source' => 'source_info' ) );
$this->handler->handle( $time, 'notice', 'msg_notice', array( 'source' => 'source_notice' ) );
$this->handler->handle( $time, 'warning', 'msg_warning', array( 'source' => 'source_warning' ) );
$this->handler->handle( $time, 'error', 'msg_error', array( 'source' => 'source_error' ) );
$this->handler->handle( $time, 'critical', 'msg_critical', array( 'source' => 'source_critical' ) );
$this->handler->handle( $time, 'alert', 'msg_alert', array( 'source' => 'source_alert' ) );
$this->handler->handle( $time, 'emergency', 'msg_emergency', array( 'source' => 'source_emergency' ) );
$handler->handle( $time, 'debug', 'context_test', $context );
$this->handler->handle( $time, 'debug', 'context_test', $context );
$log_entries = $wpdb->get_results( "SELECT timestamp, level, message, source, context FROM {$wpdb->prefix}woocommerce_log", ARRAY_A );
$expected_ts = date( 'Y-m-d H:i:s', $time );
$expected = array(
$expected = array(
array(
'timestamp' => $expected_ts,
'level' => WC_Log_Levels::get_level_severity( 'debug' ),
'message' => 'msg_debug',
'source' => 'source_debug',
'context' => serialize( array( 'source' => 'source_debug' ) ),
'level' => WC_Log_Levels::get_level_severity( 'debug' ),
'message' => 'msg_debug',
'source' => 'source_debug',
'context' => serialize( array( 'source' => 'source_debug' ) ),
),
array(
'timestamp' => $expected_ts,
'level' => WC_Log_Levels::get_level_severity( 'info' ),
'message' => 'msg_info',
'source' => 'source_info',
'context' => serialize( array( 'source' => 'source_info' ) ),
'level' => WC_Log_Levels::get_level_severity( 'info' ),
'message' => 'msg_info',
'source' => 'source_info',
'context' => serialize( array( 'source' => 'source_info' ) ),
),
array(
'timestamp' => $expected_ts,
'level' => WC_Log_Levels::get_level_severity( 'notice' ),
'message' => 'msg_notice',
'source' => 'source_notice',
'context' => serialize( array( 'source' => 'source_notice' ) ),
'level' => WC_Log_Levels::get_level_severity( 'notice' ),
'message' => 'msg_notice',
'source' => 'source_notice',
'context' => serialize( array( 'source' => 'source_notice' ) ),
),
array(
'timestamp' => $expected_ts,
'level' => WC_Log_Levels::get_level_severity( 'warning' ),
'message' => 'msg_warning',
'source' => 'source_warning',
'context' => serialize( array( 'source' => 'source_warning' ) ),
'level' => WC_Log_Levels::get_level_severity( 'warning' ),
'message' => 'msg_warning',
'source' => 'source_warning',
'context' => serialize( array( 'source' => 'source_warning' ) ),
),
array(
'timestamp' => $expected_ts,
'level' => WC_Log_Levels::get_level_severity( 'error' ),
'message' => 'msg_error',
'source' => 'source_error',
'context' => serialize( array( 'source' => 'source_error' ) ),
'level' => WC_Log_Levels::get_level_severity( 'error' ),
'message' => 'msg_error',
'source' => 'source_error',
'context' => serialize( array( 'source' => 'source_error' ) ),
),
array(
'timestamp' => $expected_ts,
'level' => WC_Log_Levels::get_level_severity( 'critical' ),
'message' => 'msg_critical',
'source' => 'source_critical',
'context' => serialize( array( 'source' => 'source_critical' ) ),
'level' => WC_Log_Levels::get_level_severity( 'critical' ),
'message' => 'msg_critical',
'source' => 'source_critical',
'context' => serialize( array( 'source' => 'source_critical' ) ),
),
array(
'timestamp' => $expected_ts,
'level' => WC_Log_Levels::get_level_severity( 'alert' ),
'message' => 'msg_alert',
'source' => 'source_alert',
'context' => serialize( array( 'source' => 'source_alert' ) ),
'level' => WC_Log_Levels::get_level_severity( 'alert' ),
'message' => 'msg_alert',
'source' => 'source_alert',
'context' => serialize( array( 'source' => 'source_alert' ) ),
),
array(
'timestamp' => $expected_ts,
'level' => WC_Log_Levels::get_level_severity( 'emergency' ),
'message' => 'msg_emergency',
'source' => 'source_emergency',
'context' => serialize( array( 'source' => 'source_emergency' ) ),
'level' => WC_Log_Levels::get_level_severity( 'emergency' ),
'message' => 'msg_emergency',
'source' => 'source_emergency',
'context' => serialize( array( 'source' => 'source_emergency' ) ),
),
array(
'timestamp' => $expected_ts,
'level' => WC_Log_Levels::get_level_severity( 'debug' ),
'message' => 'context_test',
'source' => pathinfo( __FILE__, PATHINFO_FILENAME ),
'context' => serialize( $context ),
'level' => WC_Log_Levels::get_level_severity( 'debug' ),
'message' => 'context_test',
'source' => pathinfo( __FILE__, PATHINFO_FILENAME ),
'context' => serialize( $context ),
),
);
@ -120,10 +121,9 @@ class WC_Tests_Log_Handler_DB extends WC_Unit_Test_Case {
public function test_flush() {
global $wpdb;
$handler = new WC_Log_Handler_DB( array( 'threshold' => 'debug' ) );
$time = time();
$handler->handle( $time, 'debug', '', array() );
$this->handler->handle( $time, 'debug', '', array() );
$log_entries = $wpdb->get_results( "SELECT timestamp, level, message, source FROM {$wpdb->prefix}woocommerce_log" );
$this->assertCount( 1, $log_entries );
@ -134,4 +134,17 @@ class WC_Tests_Log_Handler_DB extends WC_Unit_Test_Case {
$this->assertCount( 0, $log_entries );
}
public function test_delete_logs_before_timestamp() {
global $wpdb;
$time = time();
$this->handler->handle( $time, 'debug', '', array() );
$this->handler->handle( $time - 10, 'debug', '', array() );
$this->handler->delete_logs_before_timestamp( $time );
$log_count = $wpdb->get_var( "SELECT count(*) FROM {$wpdb->prefix}woocommerce_log" );
$this->assertEquals( 1, $log_count );
}
}

View File

@ -944,6 +944,28 @@ class WC_Tests_CRUD_Orders extends WC_Unit_Test_Case {
remove_filter( 'woocommerce_payment_complete_order_status', array( $this, 'throwAnException' ) );
}
/**
* Test: status_transition
*/
public function test_status_transition_handles_transition_errors() {
$object = new WC_Order();
$object->save();
add_filter( 'woocommerce_order_status_on-hold', array( $this, 'throwAnException' ) );
$object->update_status( 'on-hold' );
remove_filter( 'woocommerce_order_status_on-hold', array( $this, 'throwAnException' ) );
$note = current(
wc_get_order_notes(
array(
'order_id' => $object->get_id(),
)
)
);
$this->assertContains( __( 'Error during status transition.', 'woocommerce' ), $note->content );
}
/**
* Test: get_billing_first_name
*/

View File

@ -0,0 +1,142 @@
<?php
/**
* Unit tests for the product download class.
*
* @package WooCommerce\Tests\Product
*/
/**
* WC_Product_Download tests.
*
* @package WooCommerce\Tests\Product
* @since 3.4.6
*/
class WC_Tests_Product_Download extends WC_Unit_Test_Case {
/**
* Test the setters and getters.
*
* @since 3.4.6
*/
public function test_setters_getters() {
$download = new WC_Product_Download();
$download->set_id( 'testid' );
$download->set_name( 'Test Name' );
$download->set_file( 'http://example.com/file.jpg' );
$this->assertEquals( 'testid', $download->get_id() );
$this->assertEquals( 'Test Name', $download->get_name() );
$this->assertEquals( 'http://example.com/file.jpg', $download->get_file() );
}
/**
* Test the get_allowed_mime_types method.
*
* @since 3.4.6
*/
public function test_get_allowed_mime_types() {
$download = new WC_Product_Download();
$this->assertEquals( get_allowed_mime_types(), $download->get_allowed_mime_types() );
}
/**
* Test the get_type_of_file_path method.
*
* @since 3.4.6
*/
public function test_get_type_of_file_path() {
$download = new WC_Product_Download();
$this->assertEquals( 'absolute', $download->get_type_of_file_path( 'http://example.com/file.jpg' ) );
$this->assertEquals( 'absolute', $download->get_type_of_file_path( site_url( '/wp-content/uploads/test.jpg' ) ) );
$this->assertEquals( 'relative', $download->get_type_of_file_path( trailingslashit( WP_PLUGIN_DIR ) . 'woocommerce/assets/images/help.png' ) );
$this->assertEquals( 'shortcode', $download->get_type_of_file_path( '[s3 bucket ="" file=""]' ) );
}
/**
* Test the get_file_type method.
*
* @since 3.4.6
*/
public function test_get_file_type() {
$download = new WC_Product_Download();
$download->set_file( 'http://example.com/file.jpg' );
$this->assertEquals( 'image/jpeg', $download->get_file_type() );
$download->set_file( 'http://example.com/file.php' );
$this->assertEquals( '', $download->get_file_type() );
$download->set_file( 'http://example.com/file.php?ext=jpg' );
$this->assertEquals( '', $download->get_file_type() );
$download->set_file( site_url( '/wp-content/plugins/woocommerce/assets/images/help.png' ) );
$this->assertEquals( 'image/png', $download->get_file_type() );
$download->set_file( site_url( '/wp-content/plugins/woocommerce/woocommerce.php' ) );
$this->assertEquals( '', $download->get_file_type() );
$download->set_file( trailingslashit( WP_PLUGIN_DIR ) . 'woocommerce/assets/images/help.png' );
$this->assertEquals( 'image/png', $download->get_file_type() );
$download->set_file( trailingslashit( WP_PLUGIN_DIR ) . 'woocommerce/woocommerce.php' );
$this->assertEquals( false, $download->get_file_type() );
}
/**
* Test the get_file_extension method.
*
* @since 3.4.6
*/
public function test_get_file_extension() {
$download = new WC_Product_Download();
$download->set_file( 'http://example.com/file.jpg' );
$this->assertEquals( 'jpg', $download->get_file_extension() );
$download->set_file( 'http://example.com/file.php' );
$this->assertEquals( 'php', $download->get_file_extension() );
$download->set_file( 'http://example.com/file.php?ext=jpg' );
$this->assertEquals( 'php', $download->get_file_extension() );
$download->set_file( site_url( '/wp-content/plugins/woocommerce/assets/images/help.png' ) );
$this->assertEquals( 'png', $download->get_file_extension() );
$download->set_file( site_url( '/wp-content/plugins/woocommerce/woocommerce.php' ) );
$this->assertEquals( 'php', $download->get_file_extension() );
$download->set_file( trailingslashit( WP_PLUGIN_DIR ) . 'woocommerce/assets/images/help.png' );
$this->assertEquals( 'png', $download->get_file_extension() );
$download->set_file( trailingslashit( WP_PLUGIN_DIR ) . 'woocommerce/woocommerce.php' );
$this->assertEquals( 'php', $download->get_file_extension() );
}
/**
* Test the is_allowed_filetype method.
*
* @since 3.4.6
*/
public function test_is_allowed_filetype() {
$download = new WC_Product_Download();
$download->set_file( 'http://example.com/file.jpg' );
$this->assertEquals( true, $download->is_allowed_filetype() );
$download->set_file( 'http://example.com/file.php' );
$this->assertEquals( true, $download->is_allowed_filetype() );
$download->set_file( site_url( '/wp-content/plugins/woocommerce/assets/images/help.png' ) );
$this->assertEquals( true, $download->is_allowed_filetype() );
$download->set_file( site_url( '/wp-content/plugins/woocommerce/woocommerce.php' ) );
$this->assertEquals( false, $download->is_allowed_filetype() );
$download->set_file( trailingslashit( WP_PLUGIN_DIR ) . 'woocommerce/assets/images/help.png' );
$this->assertEquals( true, $download->is_allowed_filetype() );
$download->set_file( trailingslashit( WP_PLUGIN_DIR ) . 'woocommerce/woocommerce.php' );
$this->assertEquals( false, $download->is_allowed_filetype() );
}
}

View File

@ -86,7 +86,7 @@ class WC_Test_Shortcode_Products extends WC_Unit_Test_Case {
// products shortcode with attributes.
$shortcode2 = new WC_Shortcode_Products( array(
'orderby' => 'id',
'orderby' => 'ID',
'order' => 'DESC',
) );
$expected2 = array(
@ -94,7 +94,7 @@ class WC_Test_Shortcode_Products extends WC_Unit_Test_Case {
'post_status' => 'publish',
'ignore_sticky_posts' => true,
'no_found_rows' => true,
'orderby' => 'id',
'orderby' => 'ID',
'order' => 'DESC',
'posts_per_page' => '-1',
'meta_query' => $meta_query,

View File

@ -268,8 +268,8 @@ class WC_Tests_Core_Functions extends WC_Unit_Test_Case {
* @since 2.4
*/
public function test_wc_get_log_file_path() {
$log_dir = trailingslashit( WC_LOG_DIR );
$hash_name = sanitize_file_name( wp_hash( 'unit-tests' ) );
$log_dir = trailingslashit( WC_LOG_DIR );
$hash_name = sanitize_file_name( wp_hash( 'unit-tests' ) );
$date_suffix = date( 'Y-m-d', current_time( 'timestamp', true ) );
$this->assertEquals( $log_dir . 'unit-tests-' . $date_suffix . '-' . $hash_name . '.log', wc_get_log_file_path( 'unit-tests' ) );
@ -293,6 +293,26 @@ class WC_Tests_Core_Functions extends WC_Unit_Test_Case {
$this->assertSame( $log_a, $log_b, '`wc_get_logger()` should return the same instance' );
}
/**
* Test wc_get_logger() to check if can return instance when given in filter.
*/
public function test_wc_get_logger_for_instance() {
add_filter( 'woocommerce_logging_class', array( $this, 'return_valid_logger_instance' ) );
$logger = wc_get_logger();
$this->assertInstanceOf( 'WC_Logger_Interface', $logger, '`wc_get_logger()` should return valid Dummy_WC_Logger instance' );
}
/**
* Return valid logger instance that implements WC_Logger_Interface.
*
* @return WC_Logger_Interface
*/
public function return_valid_logger_instance() {
return new Dummy_WC_Logger();
}
/**
* Return class which does not implement WC_Logger_Interface
*
@ -326,7 +346,7 @@ class WC_Tests_Core_Functions extends WC_Unit_Test_Case {
$this->assertEquals(
array(
'country' => 'US',
'state' => 'CA',
'state' => 'CA',
),
wc_format_country_state_string( 'US:CA' )
);
@ -335,7 +355,7 @@ class WC_Tests_Core_Functions extends WC_Unit_Test_Case {
$this->assertEquals(
array(
'country' => 'US-CA',
'state' => '',
'state' => '',
),
wc_format_country_state_string( 'US-CA' )
);
@ -483,26 +503,32 @@ class WC_Tests_Core_Functions extends WC_Unit_Test_Case {
update_option( 'woocommerce_currency', $new_currency );
// New order should be created using shop currency.
$order = wc_create_order( array(
'status' => 'pending',
'customer_id' => 1,
'created_via' => 'unit tests',
'cart_hash' => '',
) );
$order = wc_create_order(
array(
'status' => 'pending',
'customer_id' => 1,
'created_via' => 'unit tests',
'cart_hash' => '',
)
);
$this->assertEquals( $new_currency, $order->get_currency() );
update_option( 'woocommerce_currency', $old_currency );
// Currency should not change when order is updated.
$order = wc_update_order( array(
'customer_id' => 2,
'order_id' => $order->get_id(),
) );
$order = wc_update_order(
array(
'customer_id' => 2,
'order_id' => $order->get_id(),
)
);
$this->assertEquals( $new_currency, $order->get_currency() );
$order = wc_update_order( array(
'customer_id' => 2,
) );
$order = wc_update_order(
array(
'customer_id' => 2,
)
);
$this->assertInstanceOf( 'WP_Error', $order );
}
@ -577,25 +603,29 @@ class WC_Tests_Core_Functions extends WC_Unit_Test_Case {
* @return void
*/
public function test_wc_get_page_children() {
$page_id = wp_insert_post( array(
'post_title' => 'Parent Page',
'post_type' => 'page',
'post_name' => 'parent-page',
'post_status' => 'publish',
'post_author' => 1,
'menu_order' => 0,
) );
$page_id = wp_insert_post(
array(
'post_title' => 'Parent Page',
'post_type' => 'page',
'post_name' => 'parent-page',
'post_status' => 'publish',
'post_author' => 1,
'menu_order' => 0,
)
);
$child_page_id = wp_insert_post( array(
'post_parent' => $page_id,
'post_title' => 'Parent Page',
'post_type' => 'page',
'post_name' => 'parent-page',
'post_status' => 'publish',
'post_author' => 1,
'menu_order' => 0,
) );
$children = wc_get_page_children( $page_id );
$child_page_id = wp_insert_post(
array(
'post_parent' => $page_id,
'post_title' => 'Parent Page',
'post_type' => 'page',
'post_name' => 'parent-page',
'post_status' => 'publish',
'post_author' => 1,
'menu_order' => 0,
)
);
$children = wc_get_page_children( $page_id );
$this->assertEquals( $child_page_id, $children[0] );
wp_delete_post( $page_id, true );
@ -721,7 +751,7 @@ class WC_Tests_Core_Functions extends WC_Unit_Test_Case {
foreach ( $test_cases as $test_case ) {
list( $value, $options, $result ) = $test_case;
$actual_result = $result ? " selected='selected'" : '';
$actual_result = $result ? " selected='selected'" : '';
$this->assertEquals( wc_selected( $value, $options ), $actual_result );
}
}
@ -815,7 +845,7 @@ class WC_Tests_Core_Functions extends WC_Unit_Test_Case {
* @return void
*/
public function test_wc_get_user_agent() {
$example_user_agent = 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36';
$example_user_agent = 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36';
$_SERVER['HTTP_USER_AGENT'] = $example_user_agent;
$this->assertEquals( $example_user_agent, wc_get_user_agent() );
}

View File

@ -0,0 +1,117 @@
<?php
/**
* Unit tests for the user functions.
*
* @package WooCommerce\Tests\Util
* @since 3.4.6
*/
/**
* Core function unit tests.
*/
class WC_Tests_User_Functions extends WC_Unit_Test_Case {
/**
* Test the logic of wc_modify_editable_roles.
*
* @since 3.4.6
*/
public function test_wc_modify_editable_roles() {
$password = wp_generate_password();
$admin_id = wp_insert_user( array(
'user_login' => 'test_admin',
'user_pass' => $password,
'user_email' => 'admin@example.com',
'role' => 'administrator',
) );
$editor_id = wp_insert_user( array(
'user_login' => 'test_editor',
'user_pass' => $password,
'user_email' => 'editor@example.com',
'role' => 'editor',
) );
$manager_id = wp_insert_user( array(
'user_login' => 'test_manager',
'user_pass' => $password,
'user_email' => 'manager@example.com',
'role' => 'shop_manager',
) );
// Admins should be able to edit anyone.
wp_set_current_user( $admin_id );
$admin_editable_roles = array_keys( get_editable_roles() );
$this->assertContains( 'administrator', $admin_editable_roles );
$this->assertContains( 'editor', $admin_editable_roles );
$this->assertContains( 'shop_manager', $admin_editable_roles );
$this->assertContains( 'customer', $admin_editable_roles );
// Editors should be able to edit non-admins.
wp_set_current_user( $editor_id );
$editor_editable_roles = array_keys( get_editable_roles() );
$this->assertNotContains( 'administrator', $editor_editable_roles );
$this->assertContains( 'editor', $editor_editable_roles );
$this->assertContains( 'shop_manager', $editor_editable_roles );
$this->assertContains( 'customer', $editor_editable_roles );
// Shop manager should only be able to edit customers.
wp_set_current_user( $manager_id );
$manager_editable_roles = array_keys( get_editable_roles() );
$this->assertEquals( array( 'customer' ), $manager_editable_roles );
}
/**
* Test the logic of wc_modify_map_meta_cap.
*
* @since 3.4.6
*/
public function test_wc_modify_map_meta_cap() {
$password = wp_generate_password();
$admin_id = wp_insert_user( array(
'user_login' => 'test_admin',
'user_pass' => $password,
'user_email' => 'admin@example.com',
'role' => 'administrator',
) );
$editor_id = wp_insert_user( array(
'user_login' => 'test_editor',
'user_pass' => $password,
'user_email' => 'editor@example.com',
'role' => 'editor',
) );
$manager_id = wp_insert_user( array(
'user_login' => 'test_manager',
'user_pass' => $password,
'user_email' => 'manager@example.com',
'role' => 'shop_manager',
) );
$customer_id = wp_insert_user( array(
'user_login' => 'test_customer',
'user_pass' => $password,
'user_email' => 'customer@example.com',
'role' => 'customer',
) );
// Admins should be able to edit or promote anyone.
wp_set_current_user( $admin_id );
$caps = map_meta_cap( 'edit_user', $admin_id, $editor_id );
$this->assertEquals( array( 'edit_users' ), $caps );
$caps = map_meta_cap( 'promote_user', $admin_id, $manager_id );
$this->assertEquals( array( 'promote_users' ), $caps );
// Shop managers should only be able to edit themselves or customers.
wp_set_current_user( $manager_id );
$caps = map_meta_cap( 'edit_user', $manager_id, $admin_id );
$this->assertContains( 'do_not_allow', $caps );
$caps = map_meta_cap( 'edit_user', $manager_id, $editor_id );
$this->assertContains( 'do_not_allow', $caps );
$caps = map_meta_cap( 'edit_user', $manager_id, $customer_id );
$this->assertEquals( array( 'edit_users' ), $caps );
}
}

View File

@ -0,0 +1,103 @@
<?php
/**
* Dummy Logger implements WC_Logger_Interface.
*/
class Dummy_WC_Logger implements WC_Logger_Interface {
/**
* Do nothing.
*
* @param string $handle
* @param string $message
* @param string $level
*
* @return bool|void
*/
public function add( $handle, $message, $level = WC_Log_Levels::NOTICE ) {
}
/**
* Do nothing.
*
* @param string $level
* @param string $message
* @param array $context
*/
public function log( $level, $message, $context = array() ) {
}
/**
* Do nothing.
*
* @param string $message
* @param array $context
*/
public function emergency( $message, $context = array() ) {
}
/**
* Do nothing.
*
* @param string $message
* @param array $context
*/
public function alert( $message, $context = array() ) {
}
/**
* Do nothing.
*
* @param string $message
* @param array $context
*/
public function critical( $message, $context = array() ) {
}
/**
* Do nothing.
*
* @param string $message
* @param array $context
*/
public function error( $message, $context = array() ) {
}
/**
* Do nothing.
*
* @param string $message
* @param array $context
*/
public function warning( $message, $context = array() ) {
}
/**
* Do nothing.
*
* @param string $message
* @param array $context
*/
public function notice( $message, $context = array() ) {
}
/**
* Do nothing.
*
* @param string $message
* @param array $context
*/
public function info( $message, $context = array() ) {
}
/**
* Do nothing.
*
* @param string $message
* @param array $context
*/
public function debug( $message, $context = array() ) {
}
}