Merge branch 'trunk' into fix/27873
This commit is contained in:
commit
7261813fc9
|
@ -0,0 +1,52 @@
|
||||||
|
/**
|
||||||
|
* dashboard-setup.scss
|
||||||
|
* Styles for WooCommerce dashboard finish setup widgets
|
||||||
|
* only loaded on the dashboard itself.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Styling begins
|
||||||
|
*/
|
||||||
|
|
||||||
|
.dashboard-widget-finish-setup {
|
||||||
|
|
||||||
|
.progress-wrapper {
|
||||||
|
border: 1px solid #757575;
|
||||||
|
border-radius: 16px;
|
||||||
|
font-size: 0.9em;
|
||||||
|
padding: 2px 8px 2px 8px;
|
||||||
|
display: inline-block;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-wrapper span {
|
||||||
|
position: relative;
|
||||||
|
top: -3px;
|
||||||
|
color: #757575;
|
||||||
|
}
|
||||||
|
|
||||||
|
.description div {
|
||||||
|
margin-top: 11px;
|
||||||
|
float: left;
|
||||||
|
width: 70%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.description img {
|
||||||
|
float: right;
|
||||||
|
width: 30%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.circle-progress {
|
||||||
|
margin-top: 1px;
|
||||||
|
margin-left: -3px;
|
||||||
|
|
||||||
|
circle {
|
||||||
|
stroke: #f0f0f0;
|
||||||
|
stroke-width: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bar {
|
||||||
|
stroke: #949494;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Binary file not shown.
After Width: | Height: | Size: 5.3 KiB |
|
@ -2,7 +2,7 @@
|
||||||
/**
|
/**
|
||||||
* Abstract payment tokens
|
* Abstract payment tokens
|
||||||
*
|
*
|
||||||
* Generic payment tokens functionality which can be extended by idividual types of payment tokens.
|
* Generic payment tokens functionality which can be extended by individual types of payment tokens.
|
||||||
*
|
*
|
||||||
* @class WC_Payment_Token
|
* @class WC_Payment_Token
|
||||||
* @package WooCommerce\Abstracts
|
* @package WooCommerce\Abstracts
|
||||||
|
|
|
@ -0,0 +1,211 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Admin Dashboard - Setup
|
||||||
|
*
|
||||||
|
* @package WooCommerce\Admin
|
||||||
|
* @version 2.1.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
use Automattic\Jetpack\Constants;
|
||||||
|
|
||||||
|
if ( ! defined( 'ABSPATH' ) ) {
|
||||||
|
exit; // Exit if accessed directly.
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! class_exists( 'WC_Admin_Dashboard_Setup', false ) ) :
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WC_Admin_Dashboard_Setup Class.
|
||||||
|
*/
|
||||||
|
class WC_Admin_Dashboard_Setup {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of tasks.
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private $tasks = array(
|
||||||
|
'store_details' => array(
|
||||||
|
'completed' => false,
|
||||||
|
'button_link' => 'admin.php?page=wc-admin&path=%2Fsetup-wizard',
|
||||||
|
),
|
||||||
|
'products' => array(
|
||||||
|
'completed' => false,
|
||||||
|
'button_link' => 'admin.php?page=wc-admin&task=products',
|
||||||
|
),
|
||||||
|
'woocommerce-payments' => array(
|
||||||
|
'completed' => false,
|
||||||
|
'button_link' => 'admin.php?page=wc-admin&path=%2Fpayments%2Fconnect',
|
||||||
|
),
|
||||||
|
'payments' => array(
|
||||||
|
'completed' => false,
|
||||||
|
'button_link' => 'admin.php?page=wc-admin&task=payments',
|
||||||
|
),
|
||||||
|
'tax' => array(
|
||||||
|
'completed' => false,
|
||||||
|
'button_link' => 'admin.php?page=wc-admin&task=tax',
|
||||||
|
),
|
||||||
|
'shipping' => array(
|
||||||
|
'completed' => false,
|
||||||
|
'button_link' => 'admin.php?page=wc-admin&task=shipping',
|
||||||
|
),
|
||||||
|
'appearance' => array(
|
||||||
|
'completed' => false,
|
||||||
|
'button_link' => 'admin.php?page=wc-admin&task=appearance',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* # of completed tasks.
|
||||||
|
*
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
private $completed_tasks_count = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WC_Admin_Dashboard_Setup constructor.
|
||||||
|
*/
|
||||||
|
public function __construct() {
|
||||||
|
if ( $this->should_display_widget() ) {
|
||||||
|
$this->populate_general_tasks();
|
||||||
|
$this->populate_payment_tasks();
|
||||||
|
$this->completed_tasks_count = $this->get_completed_tasks_count();
|
||||||
|
add_meta_box(
|
||||||
|
'wc_admin_dashboard_setup',
|
||||||
|
__( 'WooCommerce Setup', 'woocommerce' ),
|
||||||
|
array( $this, 'render' ),
|
||||||
|
'dashboard',
|
||||||
|
'normal',
|
||||||
|
'high'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render meta box output.
|
||||||
|
*/
|
||||||
|
public function render() {
|
||||||
|
$version = Constants::get_constant( 'WC_VERSION' );
|
||||||
|
wp_enqueue_style( 'wc-dashboard-setup', WC()->plugin_url() . '/assets/css/dashboard-setup.css', array(), $version );
|
||||||
|
|
||||||
|
$task = $this->get_next_task();
|
||||||
|
if ( ! $task ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$button_link = $task['button_link'];
|
||||||
|
$completed_tasks_count = $this->completed_tasks_count;
|
||||||
|
$tasks_count = count( $this->tasks );
|
||||||
|
|
||||||
|
// Given 'r' (circle element's r attr), dashoffset = ((100-$desired_percentage)/100) * PI * (r*2).
|
||||||
|
$progress_percentage = ( $completed_tasks_count / $tasks_count ) * 100;
|
||||||
|
$circle_r = 6.5;
|
||||||
|
$circle_dashoffset = ( ( 100 - $progress_percentage ) / 100 ) * ( pi() * ( $circle_r * 2 ) );
|
||||||
|
|
||||||
|
include __DIR__ . '/views/html-admin-dashboard-setup.php';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Populate tasks from the database.
|
||||||
|
*/
|
||||||
|
private function populate_general_tasks() {
|
||||||
|
$tasks = get_option( 'woocommerce_task_list_tracked_completed_tasks', array() );
|
||||||
|
foreach ( $tasks as $task ) {
|
||||||
|
if ( isset( $this->tasks[ $task ] ) ) {
|
||||||
|
$this->tasks[ $task ]['completed'] = true;
|
||||||
|
$this->tasks[ $task ]['button_link'] = wc_admin_url( $this->tasks[ $task ]['button_link'] );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Getter for $tasks
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function get_tasks() {
|
||||||
|
return $this->tasks;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return # of completed tasks
|
||||||
|
*/
|
||||||
|
public function get_completed_tasks_count() {
|
||||||
|
$completed_tasks = array_filter(
|
||||||
|
$this->tasks,
|
||||||
|
function( $task ) {
|
||||||
|
return $task['completed'];
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return count( $completed_tasks );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the next task.
|
||||||
|
*
|
||||||
|
* @return array|null
|
||||||
|
*/
|
||||||
|
private function get_next_task() {
|
||||||
|
foreach ( $this->get_tasks() as $task ) {
|
||||||
|
if ( false === $task['completed'] ) {
|
||||||
|
return $task;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check to see if we should display the widget
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
private function should_display_widget() {
|
||||||
|
return 'yes' !== get_option( 'woocommerce_task_list_complete' ) && 'yes' !== get_option( 'woocommerce_task_list_hidden' );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Populate payment tasks's visibility and completion
|
||||||
|
*/
|
||||||
|
private function populate_payment_tasks() {
|
||||||
|
$is_woo_payment_installed = is_plugin_active( 'woocommerce-payments/woocommerce-payments.php' );
|
||||||
|
$country = explode( ':', get_option( 'woocommerce_default_country', '' ) )[0];
|
||||||
|
|
||||||
|
// woocommerce-payments requires its plugin activated and country must be US.
|
||||||
|
if ( ! $is_woo_payment_installed || 'US' !== $country ) {
|
||||||
|
unset( $this->tasks['woocommerce-payments'] );
|
||||||
|
}
|
||||||
|
|
||||||
|
// payments can't be used when woocommerce-payments exists and country is US.
|
||||||
|
if ( $is_woo_payment_installed || 'US' === $country ) {
|
||||||
|
unset( $this->tasks['payments'] );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( isset( $this->tasks['payments'] ) ) {
|
||||||
|
$gateways = WC()->payment_gateways->get_available_payment_gateways();
|
||||||
|
$enabled_gateways = array_filter(
|
||||||
|
$gateways,
|
||||||
|
function ( $gateway ) {
|
||||||
|
return 'yes' === $gateway->enabled;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
$this->tasks['payments']['completed'] = ! empty( $enabled_gateways );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( isset( $this->tasks['woocommerce-payments'] ) ) {
|
||||||
|
$wc_pay_is_connected = false;
|
||||||
|
if ( class_exists( '\WC_Payments' ) ) {
|
||||||
|
$wc_payments_gateway = \WC_Payments::get_gateway();
|
||||||
|
$wc_pay_is_connected = method_exists( $wc_payments_gateway, 'is_connected' )
|
||||||
|
? $wc_payments_gateway->is_connected()
|
||||||
|
: false;
|
||||||
|
}
|
||||||
|
$this->tasks['woocommerce-payments']['completed'] = $wc_pay_is_connected;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
endif;
|
||||||
|
|
||||||
|
return new WC_Admin_Dashboard_Setup();
|
|
@ -24,7 +24,7 @@ if ( ! class_exists( 'WC_Admin_Dashboard', false ) ) :
|
||||||
*/
|
*/
|
||||||
public function __construct() {
|
public function __construct() {
|
||||||
// Only hook in admin parts if the user has admin access.
|
// Only hook in admin parts if the user has admin access.
|
||||||
if ( current_user_can( 'view_woocommerce_reports' ) || current_user_can( 'manage_woocommerce' ) || current_user_can( 'publish_shop_orders' ) ) {
|
if ( $this->should_display_widget() ) {
|
||||||
// If on network admin, only load the widget that works in that context and skip the rest.
|
// If on network admin, only load the widget that works in that context and skip the rest.
|
||||||
if ( is_multisite() && is_network_admin() ) {
|
if ( is_multisite() && is_network_admin() ) {
|
||||||
add_action( 'wp_network_dashboard_setup', array( $this, 'register_network_order_widget' ) );
|
add_action( 'wp_network_dashboard_setup', array( $this, 'register_network_order_widget' ) );
|
||||||
|
@ -57,6 +57,17 @@ if ( ! class_exists( 'WC_Admin_Dashboard', false ) ) :
|
||||||
wp_add_dashboard_widget( 'woocommerce_network_orders', __( 'WooCommerce Network Orders', 'woocommerce' ), array( $this, 'network_orders' ) );
|
wp_add_dashboard_widget( 'woocommerce_network_orders', __( 'WooCommerce Network Orders', 'woocommerce' ), array( $this, 'network_orders' ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check to see if we should display the widget.
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
private function should_display_widget() {
|
||||||
|
$has_permission = current_user_can( 'view_woocommerce_reports' ) || current_user_can( 'manage_woocommerce' ) || current_user_can( 'publish_shop_orders' );
|
||||||
|
$task_completed_or_hidden = 'yes' === get_option( 'woocommerce_task_list_complete' ) || 'yes' === get_option( 'woocommerce_task_list_hidden' );
|
||||||
|
return $task_completed_or_hidden && $has_permission;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get top seller from DB.
|
* Get top seller from DB.
|
||||||
*
|
*
|
||||||
|
|
|
@ -94,6 +94,7 @@ class WC_Admin {
|
||||||
switch ( $screen->id ) {
|
switch ( $screen->id ) {
|
||||||
case 'dashboard':
|
case 'dashboard':
|
||||||
case 'dashboard-network':
|
case 'dashboard-network':
|
||||||
|
include __DIR__ . '/class-wc-admin-dashboard-setup.php';
|
||||||
include __DIR__ . '/class-wc-admin-dashboard.php';
|
include __DIR__ . '/class-wc-admin-dashboard.php';
|
||||||
break;
|
break;
|
||||||
case 'options-permalink':
|
case 'options-permalink':
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Admin View: Dashboard - Finish Setup
|
||||||
|
*
|
||||||
|
* @package WooCommerce\Admin
|
||||||
|
*/
|
||||||
|
|
||||||
|
if ( ! defined( 'ABSPATH' ) ) {
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<div class="dashboard-widget-finish-setup">
|
||||||
|
<span class='progress-wrapper'>
|
||||||
|
<svg class="circle-progress" width="17" height="17" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<circle r="6.5" cx="10" cy="10" fill="transparent" stroke-dasharray="40.859" stroke-dashoffset="0"></circle>
|
||||||
|
<circle class="bar" r="6.5" cx="190" cy="10" fill="transparent" stroke-dasharray="40.859" stroke-dashoffset="<?php echo esc_attr( $circle_dashoffset ); ?>" transform='rotate(-90 100 100)'></circle>
|
||||||
|
</svg>
|
||||||
|
<span><?php echo esc_html_e( 'Step', 'woocommerce' ); ?> <?php echo esc_html( $completed_tasks_count ); ?> <?php echo esc_html_e( 'of', 'woocommerce' ); ?> <?php echo esc_html( $tasks_count ); ?></span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<div class="description">
|
||||||
|
<div>
|
||||||
|
<?php echo esc_html_e( 'You\'re almost there! Once you complete store setup you can start receiving orders.', 'woocommerce' ); ?>
|
||||||
|
<div><a href='<?php echo esc_attr( $button_link ); ?>' class='button button-primary'><?php echo esc_html_e( 'Start selling', 'woocommerce' ); ?></a></div>
|
||||||
|
</div>
|
||||||
|
<img src="<?php echo esc_url( WC()->plugin_url() ); ?>/assets/images/dashboard-widget-setup.png" />
|
||||||
|
</div>
|
||||||
|
<div class="clear"></div>
|
||||||
|
</div>
|
|
@ -602,7 +602,12 @@ class WC_Countries {
|
||||||
array(
|
array(
|
||||||
'{first_name}' => $args['first_name'],
|
'{first_name}' => $args['first_name'],
|
||||||
'{last_name}' => $args['last_name'],
|
'{last_name}' => $args['last_name'],
|
||||||
'{name}' => $args['first_name'] . ' ' . $args['last_name'],
|
'{name}' => sprintf(
|
||||||
|
/* translators: 1: first name 2: last name */
|
||||||
|
_x( '%1$s %2$s', 'full name', 'woocommerce' ),
|
||||||
|
$args['first_name'],
|
||||||
|
$args['last_name']
|
||||||
|
),
|
||||||
'{company}' => $args['company'],
|
'{company}' => $args['company'],
|
||||||
'{address_1}' => $args['address_1'],
|
'{address_1}' => $args['address_1'],
|
||||||
'{address_2}' => $args['address_2'],
|
'{address_2}' => $args['address_2'],
|
||||||
|
@ -612,7 +617,14 @@ class WC_Countries {
|
||||||
'{country}' => $full_country,
|
'{country}' => $full_country,
|
||||||
'{first_name_upper}' => wc_strtoupper( $args['first_name'] ),
|
'{first_name_upper}' => wc_strtoupper( $args['first_name'] ),
|
||||||
'{last_name_upper}' => wc_strtoupper( $args['last_name'] ),
|
'{last_name_upper}' => wc_strtoupper( $args['last_name'] ),
|
||||||
'{name_upper}' => wc_strtoupper( $args['first_name'] . ' ' . $args['last_name'] ),
|
'{name_upper}' => wc_strtoupper(
|
||||||
|
sprintf(
|
||||||
|
/* translators: 1: first name 2: last name */
|
||||||
|
_x( '%1$s %2$s', 'full name', 'woocommerce' ),
|
||||||
|
$args['first_name'],
|
||||||
|
$args['last_name']
|
||||||
|
)
|
||||||
|
),
|
||||||
'{company_upper}' => wc_strtoupper( $args['company'] ),
|
'{company_upper}' => wc_strtoupper( $args['company'] ),
|
||||||
'{address_1_upper}' => wc_strtoupper( $args['address_1'] ),
|
'{address_1_upper}' => wc_strtoupper( $args['address_1'] ),
|
||||||
'{address_2_upper}' => wc_strtoupper( $args['address_2'] ),
|
'{address_2_upper}' => wc_strtoupper( $args['address_2'] ),
|
||||||
|
|
|
@ -907,7 +907,7 @@ class WC_Order extends WC_Abstract_Order {
|
||||||
$address = WC()->countries->get_formatted_address( $raw_address );
|
$address = WC()->countries->get_formatted_address( $raw_address );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Filter orders formatterd billing address.
|
* Filter orders formatted billing address.
|
||||||
*
|
*
|
||||||
* @since 3.8.0
|
* @since 3.8.0
|
||||||
* @param string $address Formatted billing address string.
|
* @param string $address Formatted billing address string.
|
||||||
|
@ -933,7 +933,7 @@ class WC_Order extends WC_Abstract_Order {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Filter orders formatterd shipping address.
|
* Filter orders formatted shipping address.
|
||||||
*
|
*
|
||||||
* @since 3.8.0
|
* @since 3.8.0
|
||||||
* @param string $address Formatted billing address string.
|
* @param string $address Formatted billing address string.
|
||||||
|
|
|
@ -162,7 +162,7 @@ class WC_Shipping_Rate {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set ID for the rate. This is usually a combination of the method and instance IDs.
|
* Get ID for the rate. This is usually a combination of the method and instance IDs.
|
||||||
*
|
*
|
||||||
* @since 3.2.0
|
* @since 3.2.0
|
||||||
* @return string
|
* @return string
|
||||||
|
@ -172,7 +172,7 @@ class WC_Shipping_Rate {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set shipping method ID the rate belongs to.
|
* Get shipping method ID the rate belongs to.
|
||||||
*
|
*
|
||||||
* @since 3.2.0
|
* @since 3.2.0
|
||||||
* @return string
|
* @return string
|
||||||
|
@ -182,7 +182,7 @@ class WC_Shipping_Rate {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set instance ID the rate belongs to.
|
* Get instance ID the rate belongs to.
|
||||||
*
|
*
|
||||||
* @since 3.2.0
|
* @since 3.2.0
|
||||||
* @return int
|
* @return int
|
||||||
|
@ -192,7 +192,7 @@ class WC_Shipping_Rate {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set rate label.
|
* Get rate label.
|
||||||
*
|
*
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
|
@ -201,7 +201,7 @@ class WC_Shipping_Rate {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set rate cost.
|
* Get rate cost.
|
||||||
*
|
*
|
||||||
* @since 3.2.0
|
* @since 3.2.0
|
||||||
* @return string
|
* @return string
|
||||||
|
@ -211,7 +211,7 @@ class WC_Shipping_Rate {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set rate taxes.
|
* Get rate taxes.
|
||||||
*
|
*
|
||||||
* @since 3.2.0
|
* @since 3.2.0
|
||||||
* @return array
|
* @return array
|
||||||
|
|
|
@ -2298,12 +2298,14 @@ if ( ! function_exists( 'woocommerce_checkout_coupon_form' ) ) {
|
||||||
* Output the Coupon form for the checkout.
|
* Output the Coupon form for the checkout.
|
||||||
*/
|
*/
|
||||||
function woocommerce_checkout_coupon_form() {
|
function woocommerce_checkout_coupon_form() {
|
||||||
wc_get_template(
|
if ( is_user_logged_in() || WC()->checkout()->is_registration_enabled() || ! WC()->checkout()->is_registration_required() ) {
|
||||||
'checkout/form-coupon.php',
|
wc_get_template(
|
||||||
array(
|
'checkout/form-coupon.php',
|
||||||
'checkout' => WC()->checkout(),
|
array(
|
||||||
)
|
'checkout' => WC()->checkout(),
|
||||||
);
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -39,8 +39,8 @@ if ( $total <= 1 ) {
|
||||||
'add_args' => false,
|
'add_args' => false,
|
||||||
'current' => max( 1, $current ),
|
'current' => max( 1, $current ),
|
||||||
'total' => $total,
|
'total' => $total,
|
||||||
'prev_text' => '←',
|
'prev_text' => is_rtl() ? '→' : '←',
|
||||||
'next_text' => '→',
|
'next_text' => is_rtl() ? '←' : '→',
|
||||||
'type' => 'list',
|
'type' => 'list',
|
||||||
'end_size' => 3,
|
'end_size' => 3,
|
||||||
'mid_size' => 3,
|
'mid_size' => 3,
|
||||||
|
|
|
@ -51,8 +51,8 @@ if ( ! comments_open() ) {
|
||||||
apply_filters(
|
apply_filters(
|
||||||
'woocommerce_comment_pagination_args',
|
'woocommerce_comment_pagination_args',
|
||||||
array(
|
array(
|
||||||
'prev_text' => '←',
|
'prev_text' => is_rtl() ? '→' : '←',
|
||||||
'next_text' => '→',
|
'next_text' => is_rtl() ? '←' : '→',
|
||||||
'type' => 'list',
|
'type' => 'list',
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
{
|
{
|
||||||
"url": "http://localhost:8084/",
|
"url": "http://localhost:8084/",
|
||||||
"appName": "woocommerce_e2e",
|
|
||||||
"users": {
|
"users": {
|
||||||
"admin": {
|
"admin": {
|
||||||
"username": "admin",
|
"username": "admin",
|
||||||
|
|
|
@ -20,9 +20,11 @@
|
||||||
- Merchant Product Search tests
|
- Merchant Product Search tests
|
||||||
- Shopper Single Product tests
|
- Shopper Single Product tests
|
||||||
- Shopper Checkout Apply Coupon
|
- Shopper Checkout Apply Coupon
|
||||||
|
- Shopper Shop Browse Search Sort
|
||||||
- Merchant Orders Customer Checkout Page
|
- Merchant Orders Customer Checkout Page
|
||||||
- Shopper Cart Apply Coupon
|
- Shopper Cart Apply Coupon
|
||||||
- Shopper Variable product info updates on different variations
|
- Shopper Variable product info updates on different variations
|
||||||
|
- Merchant order emails flow
|
||||||
|
|
||||||
## Fixed
|
## Fixed
|
||||||
|
|
||||||
|
|
|
@ -58,6 +58,7 @@ The functions to access the core tests are:
|
||||||
- `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
|
||||||
- `runMerchantOrdersCustomerPaymentPage` - Merchant can visit the customer payment page
|
- `runMerchantOrdersCustomerPaymentPage` - Merchant can visit the customer payment page
|
||||||
|
- `runMerchantOrderEmailsTest` - Merchant can receive order emails and resend emails by Order Actions
|
||||||
|
|
||||||
### Shopper
|
### Shopper
|
||||||
|
|
||||||
|
@ -68,7 +69,8 @@ The functions to access the core tests are:
|
||||||
- `runCheckoutPageTest` - Shopper can complete checkout
|
- `runCheckoutPageTest` - Shopper can complete checkout
|
||||||
- `runMyAccountPageTest` - Shopper can access my account page
|
- `runMyAccountPageTest` - Shopper can access my account page
|
||||||
- `runSingleProductPageTest` - Shopper can view single product page in many variations (simple, variable, grouped)
|
- `runSingleProductPageTest` - Shopper can view single product page in many variations (simple, variable, grouped)
|
||||||
- `runVariableProductUpdateTest` - Shopper can view and update variations on a variable product
|
- `runProductBrowseSearchSortTest` - Shopper can browse, search & sort products
|
||||||
|
- `runVariableProductUpdateTest` - Shopper can view and update variations on a variable product
|
||||||
|
|
||||||
## Contributing a new test
|
## Contributing a new test
|
||||||
|
|
||||||
|
|
|
@ -74,10 +74,10 @@ const runCouponApiTest = () => {
|
||||||
|
|
||||||
it('can delete a coupon', async () => {
|
it('can delete a coupon', async () => {
|
||||||
// Delete the coupon
|
// Delete the coupon
|
||||||
const deletedCoupon = await repository.delete( coupon.id );
|
const status = await repository.delete( coupon.id );
|
||||||
|
|
||||||
// If the delete is successful, the response comes back truthy
|
// If the delete is successful, the response comes back truthy
|
||||||
expect( deletedCoupon ).toBeTruthy();
|
expect( status ).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
@ -69,6 +69,11 @@ const runExternalProductAPITest = () => {
|
||||||
const transformed = await repository.read( product.id );
|
const transformed = await repository.read( product.id );
|
||||||
expect( transformed ).toEqual( expect.objectContaining( transformedProperties ) );
|
expect( transformed ).toEqual( expect.objectContaining( transformedProperties ) );
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('can delete an external product', async () => {
|
||||||
|
const status = repository.delete( product.id );
|
||||||
|
expect( status ).toBeTruthy();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -71,11 +71,16 @@ const runGroupedProductAPITest = () => {
|
||||||
expect( response.data ).toEqual( expect.objectContaining( rawProperties ) );
|
expect( response.data ).toEqual( expect.objectContaining( rawProperties ) );
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can retrieve a transformed external product', async () => {
|
it('can retrieve a transformed grouped product', async () => {
|
||||||
// Read product via the repository.
|
// Read product via the repository.
|
||||||
const transformed = await repository.read( product.id );
|
const transformed = await repository.read( product.id );
|
||||||
expect( transformed ).toEqual( expect.objectContaining( baseGroupedProduct ) );
|
expect( transformed ).toEqual( expect.objectContaining( baseGroupedProduct ) );
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('can delete a grouped product', async () => {
|
||||||
|
const status = repository.delete( product.id );
|
||||||
|
expect( status ).toBeTruthy();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@ const { runOnboardingFlowTest, runTaskListTest } = require( './activate-and-setu
|
||||||
const runInitialStoreSettingsTest = require( './activate-and-setup/setup.test' );
|
const runInitialStoreSettingsTest = require( './activate-and-setup/setup.test' );
|
||||||
|
|
||||||
// Shopper tests
|
// Shopper tests
|
||||||
|
const runProductBrowseSearchSortTest = require( './shopper/front-end-product-browse-search-sort.test' );
|
||||||
const runCartApplyCouponsTest = require( './shopper/front-end-cart-coupons.test');
|
const runCartApplyCouponsTest = require( './shopper/front-end-cart-coupons.test');
|
||||||
const runCartPageTest = require( './shopper/front-end-cart.test' );
|
const runCartPageTest = require( './shopper/front-end-cart.test' );
|
||||||
const runCheckoutApplyCouponsTest = require( './shopper/front-end-checkout-coupons.test');
|
const runCheckoutApplyCouponsTest = require( './shopper/front-end-checkout-coupons.test');
|
||||||
|
@ -31,6 +32,7 @@ const runOrderApplyCouponTest = require( './merchant/wp-admin-order-apply-coupon
|
||||||
const runProductEditDetailsTest = require( './merchant/wp-admin-product-edit-details.test' );
|
const runProductEditDetailsTest = require( './merchant/wp-admin-product-edit-details.test' );
|
||||||
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' );
|
||||||
|
|
||||||
// REST API tests
|
// REST API tests
|
||||||
const runExternalProductAPITest = require( './api/external-product.test' );
|
const runExternalProductAPITest = require( './api/external-product.test' );
|
||||||
|
@ -46,6 +48,7 @@ const runSetupOnboardingTests = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const runShopperTests = () => {
|
const runShopperTests = () => {
|
||||||
|
runProductBrowseSearchSortTest();
|
||||||
runCartApplyCouponsTest();
|
runCartApplyCouponsTest();
|
||||||
runCartPageTest();
|
runCartPageTest();
|
||||||
runCheckoutApplyCouponsTest();
|
runCheckoutApplyCouponsTest();
|
||||||
|
@ -110,6 +113,8 @@ module.exports = {
|
||||||
runProductEditDetailsTest,
|
runProductEditDetailsTest,
|
||||||
runProductSearchTest,
|
runProductSearchTest,
|
||||||
runMerchantOrdersCustomerPaymentPage,
|
runMerchantOrdersCustomerPaymentPage,
|
||||||
|
runMerchantOrderEmailsTest,
|
||||||
runMerchantTests,
|
runMerchantTests,
|
||||||
|
runProductBrowseSearchSortTest,
|
||||||
runApiTests,
|
runApiTests,
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
/* eslint-disable jest/no-export, jest/no-disabled-tests, jest/no-standalone-expect */
|
||||||
|
/**
|
||||||
|
* Internal dependencies
|
||||||
|
*/
|
||||||
|
const {
|
||||||
|
merchant,
|
||||||
|
clickUpdateOrder,
|
||||||
|
createSimpleOrder,
|
||||||
|
selectOrderAction,
|
||||||
|
deleteAllEmailLogs,
|
||||||
|
} = require( '@woocommerce/e2e-utils' );
|
||||||
|
|
||||||
|
const config = require( 'config' );
|
||||||
|
const simpleProductName = config.get( 'products.simple.name' );
|
||||||
|
const customerEmail = config.get( 'addresses.customer.billing.email' );
|
||||||
|
const adminEmail = 'admin@woocommercecoree2etestsuite.com';
|
||||||
|
const storeName = 'WooCommerce Core E2E Test Suite';
|
||||||
|
|
||||||
|
let orderId;
|
||||||
|
|
||||||
|
const runMerchantOrderEmailsTest = () => {
|
||||||
|
|
||||||
|
describe('Merchant > Order Action emails received', () => {
|
||||||
|
beforeAll( async () => {
|
||||||
|
await merchant.login();
|
||||||
|
|
||||||
|
// Clear out the existing email logs if any
|
||||||
|
await deleteAllEmailLogs();
|
||||||
|
|
||||||
|
orderId = await createSimpleOrder( 'Processing' );
|
||||||
|
|
||||||
|
await Promise.all( [
|
||||||
|
// Select the billing email address field and add the customer billing email from the config
|
||||||
|
await page.click( 'div.order_data_column:nth-child(2) > h3:nth-child(1) > a:nth-child(1)' ),
|
||||||
|
await expect( page ).toFill( '#_billing_email', customerEmail ),
|
||||||
|
await clickUpdateOrder( 'Order updated.' ),
|
||||||
|
] );
|
||||||
|
} );
|
||||||
|
|
||||||
|
afterEach( async () => {
|
||||||
|
// Clear out any emails after each test
|
||||||
|
await deleteAllEmailLogs();
|
||||||
|
} );
|
||||||
|
|
||||||
|
// New order emails are sent automatically when we create the simple order above, so let's verify we get these emails
|
||||||
|
it('can receive new order email', async () => {
|
||||||
|
await merchant.openEmailLog();
|
||||||
|
await expect( page ).toMatchElement( '.column-receiver', { text: adminEmail } );
|
||||||
|
await expect( page ).toMatchElement( '.column-subject', { text: `[${storeName}]: New order #${orderId}` } );
|
||||||
|
} );
|
||||||
|
|
||||||
|
it('can resend new order notification', async () => {
|
||||||
|
await merchant.goToOrder( orderId );
|
||||||
|
await selectOrderAction( 'send_order_details_admin' );
|
||||||
|
|
||||||
|
await merchant.openEmailLog();
|
||||||
|
await expect( page ).toMatchElement( '.column-receiver', { text: adminEmail } );
|
||||||
|
await expect( page ).toMatchElement( '.column-subject', { text: `[${storeName}]: New order #${orderId}` } );
|
||||||
|
} );
|
||||||
|
|
||||||
|
it('can email invoice/order details to customer', async () => {
|
||||||
|
await merchant.goToOrder( orderId );
|
||||||
|
await selectOrderAction( 'send_order_details' );
|
||||||
|
|
||||||
|
await merchant.openEmailLog();
|
||||||
|
await expect( page ).toMatchElement( '.column-receiver', { text: customerEmail } );
|
||||||
|
await expect( page ).toMatchElement( '.column-subject', { text: `Invoice for order #${orderId} on ${storeName}` } );
|
||||||
|
} );
|
||||||
|
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = runMerchantOrderEmailsTest;
|
|
@ -8,7 +8,8 @@ const {
|
||||||
createCoupon,
|
createCoupon,
|
||||||
createSimpleProduct,
|
createSimpleProduct,
|
||||||
uiUnblocked,
|
uiUnblocked,
|
||||||
clearAndFillInput,
|
applyCoupon,
|
||||||
|
removeCoupon,
|
||||||
} = require( '@woocommerce/e2e-utils' );
|
} = require( '@woocommerce/e2e-utils' );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -20,28 +21,6 @@ const {
|
||||||
beforeAll,
|
beforeAll,
|
||||||
} = require( '@jest/globals' );
|
} = require( '@jest/globals' );
|
||||||
|
|
||||||
/**
|
|
||||||
* Apply a coupon code to the cart.
|
|
||||||
*
|
|
||||||
* @param couponCode string
|
|
||||||
* @returns {Promise<void>}
|
|
||||||
*/
|
|
||||||
const applyCouponToCart = async ( couponCode ) => {
|
|
||||||
await clearAndFillInput('#coupon_code', couponCode);
|
|
||||||
await expect(page).toClick('button', {text: 'Apply coupon'});
|
|
||||||
await uiUnblocked();
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove one coupon from the cart.
|
|
||||||
*
|
|
||||||
* @returns {Promise<void>}
|
|
||||||
*/
|
|
||||||
const removeCouponFromCart = async () => {
|
|
||||||
await expect(page).toClick('.woocommerce-remove-coupon', {text: '[Remove]'});
|
|
||||||
await uiUnblocked();
|
|
||||||
await expect(page).toMatchElement('.woocommerce-message', {text: 'Coupon has been removed.'});
|
|
||||||
}
|
|
||||||
const runCartApplyCouponsTest = () => {
|
const runCartApplyCouponsTest = () => {
|
||||||
describe('Cart applying coupons', () => {
|
describe('Cart applying coupons', () => {
|
||||||
let couponFixedCart;
|
let couponFixedCart;
|
||||||
|
@ -62,42 +41,42 @@ const runCartApplyCouponsTest = () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('allows customer to apply fixed cart coupon', async () => {
|
it('allows customer to apply fixed cart coupon', async () => {
|
||||||
await applyCouponToCart( couponFixedCart );
|
await applyCoupon(couponFixedCart);
|
||||||
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
|
// Verify discount applied and order total
|
||||||
await page.waitForSelector('.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 removeCouponFromCart();
|
await removeCoupon(couponFixedCart);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('allows customer to apply percentage coupon', async () => {
|
it('allows customer to apply percentage coupon', async () => {
|
||||||
await applyCouponToCart( couponPercentage );
|
await applyCoupon(couponPercentage);
|
||||||
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
|
// Verify discount applied and order total
|
||||||
await page.waitForSelector('.order-total');
|
await page.waitForSelector('.order-total');
|
||||||
await expect(page).toMatchElement('.cart-discount .amount', {text: '$4.99'});
|
await expect(page).toMatchElement('.cart-discount .amount', {text: '$4.99'});
|
||||||
await expect(page).toMatchElement('.order-total .amount', {text: '$5.00'});
|
await expect(page).toMatchElement('.order-total .amount', {text: '$5.00'});
|
||||||
await removeCouponFromCart();
|
await removeCoupon(couponPercentage);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('allows customer to apply fixed product coupon', async () => {
|
it('allows customer to apply fixed product coupon', async () => {
|
||||||
await applyCouponToCart( 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
|
// Verify discount applied and order total
|
||||||
await page.waitForSelector('.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 removeCouponFromCart();
|
await removeCoupon(couponFixedProduct);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('prevents customer applying same coupon twice', async () => {
|
it('prevents customer applying same coupon twice', async () => {
|
||||||
await applyCouponToCart( couponFixedCart );
|
await applyCoupon(couponFixedCart);
|
||||||
await expect(page).toMatchElement('.woocommerce-message', {text: 'Coupon code applied successfully.'});
|
await expect(page).toMatchElement('.woocommerce-message', {text: 'Coupon code applied successfully.'});
|
||||||
await applyCouponToCart( couponFixedCart );
|
await applyCoupon(couponFixedCart);
|
||||||
// Verify only one discount applied
|
// Verify only one discount applied
|
||||||
// This is a work around for Puppeteer inconsistently finding 'Coupon code already applied'
|
// This is a work around for Puppeteer inconsistently finding 'Coupon code already applied'
|
||||||
await expect(page).toMatchElement('.cart-discount .amount', {text: '$5.00'});
|
await expect(page).toMatchElement('.cart-discount .amount', {text: '$5.00'});
|
||||||
|
@ -105,7 +84,7 @@ const runCartApplyCouponsTest = () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('allows customer to apply multiple coupons', async () => {
|
it('allows customer to apply multiple coupons', async () => {
|
||||||
await applyCouponToCart( 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
|
// Verify discount applied and order total
|
||||||
|
@ -114,8 +93,8 @@ const runCartApplyCouponsTest = () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('restores cart total when coupons are removed', async () => {
|
it('restores cart total when coupons are removed', async () => {
|
||||||
await removeCouponFromCart();
|
await removeCoupon(couponFixedCart);
|
||||||
await removeCouponFromCart();
|
await removeCoupon(couponFixedProduct);
|
||||||
await expect(page).toMatchElement('.order-total .amount', {text: '$9.99'});
|
await expect(page).toMatchElement('.order-total .amount', {text: '$9.99'});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -8,7 +8,8 @@ const {
|
||||||
createCoupon,
|
createCoupon,
|
||||||
createSimpleProduct,
|
createSimpleProduct,
|
||||||
uiUnblocked,
|
uiUnblocked,
|
||||||
clearAndFillInput,
|
applyCoupon,
|
||||||
|
removeCoupon,
|
||||||
} = require( '@woocommerce/e2e-utils' );
|
} = require( '@woocommerce/e2e-utils' );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -20,30 +21,6 @@ const {
|
||||||
beforeAll,
|
beforeAll,
|
||||||
} = require( '@jest/globals' );
|
} = require( '@jest/globals' );
|
||||||
|
|
||||||
/**
|
|
||||||
* Apply a coupon code to the cart.
|
|
||||||
*
|
|
||||||
* @param couponCode string
|
|
||||||
* @returns {Promise<void>}
|
|
||||||
*/
|
|
||||||
const applyCouponToCart = async ( couponCode ) => {
|
|
||||||
await expect(page).toClick('a', {text: 'Click here to enter your code'});
|
|
||||||
await uiUnblocked();
|
|
||||||
await clearAndFillInput('#coupon_code', couponCode);
|
|
||||||
await expect(page).toClick('button', {text: 'Apply coupon'});
|
|
||||||
await uiUnblocked();
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove one coupon from the cart.
|
|
||||||
*
|
|
||||||
* @returns {Promise<void>}
|
|
||||||
*/
|
|
||||||
const removeCouponFromCart = async () => {
|
|
||||||
await expect(page).toClick('.woocommerce-remove-coupon', {text: '[Remove]'});
|
|
||||||
await uiUnblocked();
|
|
||||||
await expect(page).toMatchElement('.woocommerce-message', {text: 'Coupon has been removed.'});
|
|
||||||
}
|
|
||||||
const runCheckoutApplyCouponsTest = () => {
|
const runCheckoutApplyCouponsTest = () => {
|
||||||
describe('Checkout coupons', () => {
|
describe('Checkout coupons', () => {
|
||||||
let couponFixedCart;
|
let couponFixedCart;
|
||||||
|
@ -64,7 +41,7 @@ const runCheckoutApplyCouponsTest = () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('allows customer to apply fixed cart coupon', async () => {
|
it('allows customer to apply fixed cart coupon', async () => {
|
||||||
await applyCouponToCart( couponFixedCart );
|
await applyCoupon(couponFixedCart);
|
||||||
await expect(page).toMatchElement('.woocommerce-message', {text: 'Coupon code applied successfully.'});
|
await expect(page).toMatchElement('.woocommerce-message', {text: 'Coupon code applied successfully.'});
|
||||||
|
|
||||||
// Wait for page to expand total calculations to avoid flakyness
|
// Wait for page to expand total calculations to avoid flakyness
|
||||||
|
@ -73,31 +50,31 @@ const runCheckoutApplyCouponsTest = () => {
|
||||||
// Verify discount applied and order total
|
// Verify discount applied and 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 removeCouponFromCart();
|
await removeCoupon(couponFixedCart);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('allows customer to apply percentage coupon', async () => {
|
it('allows customer to apply percentage coupon', async () => {
|
||||||
await applyCouponToCart( couponPercentage );
|
await applyCoupon(couponPercentage);
|
||||||
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
|
// Verify discount applied and order total
|
||||||
await expect(page).toMatchElement('.cart-discount .amount', {text: '$4.99'});
|
await expect(page).toMatchElement('.cart-discount .amount', {text: '$4.99'});
|
||||||
await expect(page).toMatchElement('.order-total .amount', {text: '$5.00'});
|
await expect(page).toMatchElement('.order-total .amount', {text: '$5.00'});
|
||||||
await removeCouponFromCart();
|
await removeCoupon(couponPercentage);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('allows customer to apply fixed product coupon', async () => {
|
it('allows customer to apply fixed product coupon', async () => {
|
||||||
await applyCouponToCart( 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.'});
|
||||||
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 removeCouponFromCart();
|
await removeCoupon(couponFixedProduct);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('prevents customer applying same coupon twice', async () => {
|
it('prevents customer applying same coupon twice', async () => {
|
||||||
await applyCouponToCart( couponFixedCart );
|
await applyCoupon(couponFixedCart);
|
||||||
await expect(page).toMatchElement('.woocommerce-message', {text: 'Coupon code applied successfully.'});
|
await expect(page).toMatchElement('.woocommerce-message', {text: 'Coupon code applied successfully.'});
|
||||||
await applyCouponToCart( couponFixedCart );
|
await applyCoupon(couponFixedCart);
|
||||||
// Verify only one discount applied
|
// Verify only one discount applied
|
||||||
// This is a work around for Puppeteer inconsistently finding 'Coupon code already applied'
|
// This is a work around for Puppeteer inconsistently finding 'Coupon code already applied'
|
||||||
await expect(page).toMatchElement('.cart-discount .amount', {text: '$5.00'});
|
await expect(page).toMatchElement('.cart-discount .amount', {text: '$5.00'});
|
||||||
|
@ -105,14 +82,14 @@ const runCheckoutApplyCouponsTest = () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('allows customer to apply multiple coupons', async () => {
|
it('allows customer to apply multiple coupons', async () => {
|
||||||
await applyCouponToCart( 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.'});
|
||||||
await expect(page).toMatchElement('.order-total .amount', {text: '$0.00'});
|
await expect(page).toMatchElement('.order-total .amount', {text: '$0.00'});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('restores cart total when coupons are removed', async () => {
|
it('restores cart total when coupons are removed', async () => {
|
||||||
await removeCouponFromCart();
|
await removeCoupon(couponFixedCart);
|
||||||
await removeCouponFromCart();
|
await removeCoupon(couponFixedProduct);
|
||||||
await expect(page).toMatchElement('.order-total .amount', {text: '$9.99'});
|
await expect(page).toMatchElement('.order-total .amount', {text: '$9.99'});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,87 @@
|
||||||
|
/* eslint-disable jest/no-export, jest/no-disabled-tests */
|
||||||
|
/**
|
||||||
|
* Internal dependencies
|
||||||
|
*/
|
||||||
|
const {
|
||||||
|
shopper,
|
||||||
|
merchant,
|
||||||
|
createSimpleProductWithCategory,
|
||||||
|
uiUnblocked,
|
||||||
|
} = require( '@woocommerce/e2e-utils' );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* External dependencies
|
||||||
|
*/
|
||||||
|
const {
|
||||||
|
it,
|
||||||
|
describe,
|
||||||
|
beforeAll,
|
||||||
|
} = require( '@jest/globals' );
|
||||||
|
|
||||||
|
const config = require( 'config' );
|
||||||
|
const simpleProductName = config.get( 'products.simple.name' );
|
||||||
|
const singleProductPrice = config.has('products.simple.price') ? config.get('products.simple.price') : '9.99';
|
||||||
|
const singleProductPrice2 = config.has('products.simple.price') ? config.get('products.simple.price') : '19.99';
|
||||||
|
const singleProductPrice3 = config.has('products.simple.price') ? config.get('products.simple.price') : '29.99';
|
||||||
|
const clothing = 'Clothing';
|
||||||
|
const audio = 'Audio';
|
||||||
|
const hardware = 'Hardware';
|
||||||
|
const productTitle = 'li.first > a > h2.woocommerce-loop-product__title';
|
||||||
|
|
||||||
|
const runProductBrowseSearchSortTest = () => {
|
||||||
|
describe('Search, browse by categories and sort items in the shop', () => {
|
||||||
|
beforeAll(async () => {
|
||||||
|
await merchant.login();
|
||||||
|
// Create 1st product with Clothing category
|
||||||
|
await createSimpleProductWithCategory(simpleProductName + ' 1', singleProductPrice, clothing);
|
||||||
|
// Create 2nd product with Audio category
|
||||||
|
await createSimpleProductWithCategory(simpleProductName + ' 2', singleProductPrice2, audio);
|
||||||
|
// Create 3rd product with Hardware category
|
||||||
|
await createSimpleProductWithCategory(simpleProductName + ' 3', singleProductPrice3, hardware);
|
||||||
|
await merchant.logout();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should let user search the store', async () => {
|
||||||
|
await shopper.goToShop();
|
||||||
|
await shopper.searchForProduct(simpleProductName + ' 1');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should let user browse products by categories', async () => {
|
||||||
|
// Browse through Clothing category link
|
||||||
|
await Promise.all([
|
||||||
|
page.waitForNavigation({waitUntil: 'networkidle0'}),
|
||||||
|
page.click('span.posted_in > a', {text: clothing}),
|
||||||
|
]);
|
||||||
|
await uiUnblocked();
|
||||||
|
|
||||||
|
// Verify Clothing category page
|
||||||
|
await page.waitForSelector(productTitle);
|
||||||
|
await expect(page).toMatchElement(productTitle, {text: simpleProductName + ' 1'});
|
||||||
|
await expect(page).toClick(productTitle, {text: simpleProductName + ' 1'});
|
||||||
|
await uiUnblocked();
|
||||||
|
await page.waitForSelector('h1.entry-title');
|
||||||
|
await expect(page).toMatchElement('h1.entry-title', simpleProductName + ' 1');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should let user sort the products in the shop', async () => {
|
||||||
|
await shopper.goToShop();
|
||||||
|
|
||||||
|
// Sort by price high to low
|
||||||
|
await page.select('.orderby', 'price-desc');
|
||||||
|
// Verify the first product in sort order
|
||||||
|
await expect(page).toMatchElement(productTitle, {text: simpleProductName + ' 3'});
|
||||||
|
|
||||||
|
// Sort by price low to high
|
||||||
|
await page.select('.orderby', 'price');
|
||||||
|
// Verify the first product in sort order
|
||||||
|
await expect(page).toMatchElement(productTitle, {text: simpleProductName + ' 1'});
|
||||||
|
|
||||||
|
// Sort by date of creation, latest to oldest
|
||||||
|
await page.select('.orderby', 'date');
|
||||||
|
// Verify the first product in sort order
|
||||||
|
await expect(page).toMatchElement(productTitle, {text: simpleProductName + ' 3'});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = runProductBrowseSearchSortTest;
|
|
@ -8,3 +8,6 @@ wp user create customer customer@woocommercecoree2etestsuite.com --user_pass=pas
|
||||||
|
|
||||||
# we cannot create API keys for the API, so we using basic auth, this plugin allows that.
|
# we cannot create API keys for the API, so we using basic auth, this plugin allows that.
|
||||||
wp plugin install https://github.com/WP-API/Basic-Auth/archive/master.zip --activate
|
wp plugin install https://github.com/WP-API/Basic-Auth/archive/master.zip --activate
|
||||||
|
|
||||||
|
# install the WP Mail Logging plugin to test emails
|
||||||
|
wp plugin install wp-mail-logging --activate
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
- support for custom container name
|
- support for custom container name
|
||||||
- Insert a 12 hour delay in using new docker image tags
|
- Insert a 12 hour delay in using new docker image tags
|
||||||
- Package `bin` script `wc-e2e`
|
- Package `bin` script `wc-e2e`
|
||||||
|
- WP Mail Log plugin as part of container initialization
|
||||||
|
|
||||||
## Fixed
|
## Fixed
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
/*
|
||||||
|
* Internal dependencies
|
||||||
|
*/
|
||||||
|
const { runProductBrowseSearchSortTest } = require( '@woocommerce/e2e-core-tests' );
|
||||||
|
|
||||||
|
runProductBrowseSearchSortTest();
|
|
@ -0,0 +1,6 @@
|
||||||
|
/*
|
||||||
|
* Internal dependencies
|
||||||
|
*/
|
||||||
|
const { runMerchantOrderEmailsTest } = require( '@woocommerce/e2e-core-tests' );
|
||||||
|
|
||||||
|
runMerchantOrderEmailsTest();
|
|
@ -17,6 +17,13 @@
|
||||||
- `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
|
||||||
|
- `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
|
||||||
|
- `removeCoupon()` util helper method that removes a single coupon within cart or checkout
|
||||||
|
- `selectOrderAction( action )` util helper method to select and initiate an order action in the Order Action postbox
|
||||||
|
- `merchant.openEmailLog()` go to the WP Mail Log page
|
||||||
|
- `deleteAllEmailLogs` delete all email logs in the WP Mail Log plugin
|
||||||
|
- `clickUpdateOrder( noticeText, waitForSave )` util helper that clicks the `Update` button on an order
|
||||||
|
|
||||||
## Changes
|
## Changes
|
||||||
|
|
||||||
|
|
|
@ -54,6 +54,7 @@ describe( 'Cart page', () => {
|
||||||
| `openSettings` | | Go to WooCommerce -> Settings |
|
| `openSettings` | | Go to WooCommerce -> Settings |
|
||||||
| `runSetupWizard` | | Open the onboarding profiler |
|
| `runSetupWizard` | | Open the onboarding profiler |
|
||||||
| `updateOrderStatus` | `orderId, status` | Update the status of an order |
|
| `updateOrderStatus` | `orderId, status` | Update the status of an order |
|
||||||
|
| `openEmailLog` | | Open the WP Mail Log page |
|
||||||
|
|
||||||
### Shopper `shopper`
|
### Shopper `shopper`
|
||||||
|
|
||||||
|
@ -77,6 +78,7 @@ describe( 'Cart page', () => {
|
||||||
| `productIsInCheckout` | `productTitle, quantity, total, cartSubtotal` | Verify product is in cart on checkout page |
|
| `productIsInCheckout` | `productTitle, quantity, total, cartSubtotal` | Verify product is in cart on checkout page |
|
||||||
| `removeFromCart` | `productTitle` | Remove a product from the cart on the cart page |
|
| `removeFromCart` | `productTitle` | Remove a product from the cart on the cart page |
|
||||||
| `setCartQuantity` | `productTitle, quantityValue` | Change the quantity of a product on the cart page |
|
| `setCartQuantity` | `productTitle, quantityValue` | Change the quantity of a product on the cart page |
|
||||||
|
| `searchForProduct` | Searching for a product name and landing on its detail page |
|
||||||
|
|
||||||
### Page Utilities
|
### Page Utilities
|
||||||
|
|
||||||
|
@ -101,7 +103,11 @@ describe( 'Cart page', () => {
|
||||||
| `clickFilter` | `selector` | Click on a list page filter |
|
| `clickFilter` | `selector` | Click on a list page filter |
|
||||||
| `moveAllItemsToTrash` | | Moves all items in a list view to the Trash |
|
| `moveAllItemsToTrash` | | Moves all items in a list view to the Trash |
|
||||||
| `verifyAndPublish` | `noticeText` | Verify that an item can be published |
|
| `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
|
| `selectOptionInSelect2` | `selector, value` | helper method that searchs for select2 type fields and select plus insert value inside |
|
||||||
|
| `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
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
* Internal dependencies
|
* Internal dependencies
|
||||||
*/
|
*/
|
||||||
import { merchant } from './flows';
|
import { merchant } from './flows';
|
||||||
import { clickTab, uiUnblocked, verifyCheckboxIsUnset, evalAndClick, selectOptionInSelect2 } from './page-utils';
|
import { clickTab, uiUnblocked, verifyCheckboxIsUnset, evalAndClick, selectOptionInSelect2, setCheckbox } from './page-utils';
|
||||||
import factories from './factories';
|
import factories from './factories';
|
||||||
|
|
||||||
const config = require( 'config' );
|
const config = require( 'config' );
|
||||||
|
@ -184,6 +184,44 @@ const createSimpleProduct = async () => {
|
||||||
return product.id;
|
return product.id;
|
||||||
} ;
|
} ;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create simple product with categories
|
||||||
|
*
|
||||||
|
* @param productName Product's name which can be changed when writing a test
|
||||||
|
* @param productPrice Product's price which can be changed when writing a test
|
||||||
|
* @param categoryName Product's category which can be changed when writing a test
|
||||||
|
*/
|
||||||
|
const createSimpleProductWithCategory = async ( productName, productPrice, categoryName ) => {
|
||||||
|
// Go to "add product" page
|
||||||
|
await merchant.openNewProduct();
|
||||||
|
|
||||||
|
// Add title and regular price
|
||||||
|
await expect(page).toFill('#title', productName);
|
||||||
|
await expect(page).toClick('#_virtual');
|
||||||
|
await clickTab('General');
|
||||||
|
await expect(page).toFill('#_regular_price', productPrice);
|
||||||
|
|
||||||
|
// Try to select the existing category if present already, otherwise add a new and select it
|
||||||
|
try {
|
||||||
|
const [checkbox] = await page.$x('//label[contains(text(), "'+categoryName+'")]');
|
||||||
|
await checkbox.click();
|
||||||
|
} catch (error) {
|
||||||
|
await expect(page).toClick('#product_cat-add-toggle');
|
||||||
|
await expect(page).toFill('#newproduct_cat', categoryName);
|
||||||
|
await expect(page).toClick('#product_cat-add-submit');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Publish the product
|
||||||
|
await expect(page).toClick('#publish');
|
||||||
|
await uiUnblocked();
|
||||||
|
await page.waitForSelector('.updated.notice', {text:'Product published.'});
|
||||||
|
|
||||||
|
// Get the product ID
|
||||||
|
const variablePostId = await page.$('#post_ID');
|
||||||
|
let variablePostIdValue = (await(await variablePostId.getProperty('value')).jsonValue());
|
||||||
|
return variablePostIdValue;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create variable product.
|
* Create variable product.
|
||||||
*/
|
*/
|
||||||
|
@ -435,6 +473,42 @@ const createCoupon = async ( couponAmount = '5', discountType = 'Fixed cart disc
|
||||||
return couponCode;
|
return couponCode;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Click the Update button on the order details page.
|
||||||
|
*
|
||||||
|
* @param noticeText The text that appears in the notice after updating the order.
|
||||||
|
* @param waitForSave Optionally wait for auto save.
|
||||||
|
*/
|
||||||
|
const clickUpdateOrder = async ( noticeText, waitForSave = false ) => {
|
||||||
|
if ( waitForSave ) {
|
||||||
|
await page.waitFor( 2000 );
|
||||||
|
}
|
||||||
|
|
||||||
|
// PUpdate order
|
||||||
|
await expect( page ).toClick( 'button.save_order' );
|
||||||
|
await page.waitForSelector( '.updated.notice' );
|
||||||
|
|
||||||
|
// Verify
|
||||||
|
await expect( page ).toMatchElement( '.updated.notice', { text: noticeText } );
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete all email logs in the WP Mail Logging plugin page.
|
||||||
|
*/
|
||||||
|
const deleteAllEmailLogs = async () => {
|
||||||
|
await merchant.openEmailLog();
|
||||||
|
|
||||||
|
// Make sure we have emails to delete. If we don't, this selector will return null.
|
||||||
|
if ( await page.$( '#bulk-action-selector-top' ) !== null ) {
|
||||||
|
await setCheckbox( '#cb-select-all-1' );
|
||||||
|
await expect( page ).toSelect( '#bulk-action-selector-top', 'Delete' );
|
||||||
|
await Promise.all( [
|
||||||
|
page.click( '#doaction' ),
|
||||||
|
page.waitForNavigation( { waitUntil: 'networkidle0' } ),
|
||||||
|
] );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
export {
|
export {
|
||||||
completeOnboardingWizard,
|
completeOnboardingWizard,
|
||||||
createSimpleProduct,
|
createSimpleProduct,
|
||||||
|
@ -444,4 +518,7 @@ export {
|
||||||
verifyAndPublish,
|
verifyAndPublish,
|
||||||
addProductToOrder,
|
addProductToOrder,
|
||||||
createCoupon,
|
createCoupon,
|
||||||
|
createSimpleProductWithCategory,
|
||||||
|
clickUpdateOrder,
|
||||||
|
deleteAllEmailLogs,
|
||||||
};
|
};
|
||||||
|
|
|
@ -169,6 +169,12 @@ const merchant = {
|
||||||
await expect( page ).toMatchElement( 'label[for="customer_user"] a[href*=user-edit]', { text: 'Profile' } );
|
await expect( page ).toMatchElement( 'label[for="customer_user"] a[href*=user-edit]', { text: 'Profile' } );
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
openEmailLog: async () => {
|
||||||
|
await page.goto( `${baseUrl}wp-admin/tools.php?page=wpml_plugin_log`, {
|
||||||
|
waitUntil: 'networkidle0',
|
||||||
|
} );
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = merchant;
|
module.exports = merchant;
|
||||||
|
|
|
@ -137,6 +137,17 @@ const shopper = {
|
||||||
await quantityInput.type( quantityValue.toString() );
|
await quantityInput.type( quantityValue.toString() );
|
||||||
},
|
},
|
||||||
|
|
||||||
|
searchForProduct: async ( prouductName ) => {
|
||||||
|
await expect(page).toFill('.search-field', prouductName);
|
||||||
|
await expect(page).toClick('.search-submit');
|
||||||
|
await page.waitForSelector('h2.entry-title');
|
||||||
|
await expect(page).toMatchElement('h2.entry-title', {text: prouductName});
|
||||||
|
await expect(page).toClick('h2.entry-title', {text: prouductName});
|
||||||
|
await page.waitForSelector('h1.entry-title');
|
||||||
|
await expect(page.title()).resolves.toMatch(prouductName);
|
||||||
|
await expect(page).toMatchElement('h1.entry-title', prouductName);
|
||||||
|
},
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* My Accounts flows.
|
* My Accounts flows.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -209,6 +209,53 @@ const selectOptionInSelect2 = async ( value, selector = 'input.select2-search__f
|
||||||
await page.keyboard.press('Enter');
|
await page.keyboard.press('Enter');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* @param couponCode string
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
const applyCoupon = async ( couponCode ) => {
|
||||||
|
try {
|
||||||
|
await expect(page).toClick('a', {text: 'Click here to enter your code'});
|
||||||
|
await uiUnblocked();
|
||||||
|
await clearAndFillInput('#coupon_code', couponCode);
|
||||||
|
await expect(page).toClick('button', {text: 'Apply coupon'});
|
||||||
|
await uiUnblocked();
|
||||||
|
} catch (error) {
|
||||||
|
await clearAndFillInput('#coupon_code', couponCode);
|
||||||
|
await expect(page).toClick('button', {text: 'Apply coupon'});
|
||||||
|
await uiUnblocked();
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove one coupon within cart or checkout.
|
||||||
|
*
|
||||||
|
* @param couponCode Coupon name.
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
const removeCoupon = async ( couponCode ) => {
|
||||||
|
await expect(page).toClick('[data-coupon="'+couponCode.toLowerCase()+'"]', {text: '[Remove]'});
|
||||||
|
await uiUnblocked();
|
||||||
|
await expect(page).toMatchElement('.woocommerce-message', {text: 'Coupon has been removed.'});
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Select and perform an order action in the `Order actions` postbox.
|
||||||
|
*
|
||||||
|
* @param {string} action The action to take on the order.
|
||||||
|
*/
|
||||||
|
const selectOrderAction = async ( action ) => {
|
||||||
|
await page.select( 'select[name=wc_order_action]', action );
|
||||||
|
await Promise.all( [
|
||||||
|
page.click( '.wc-reload' ),
|
||||||
|
page.waitForNavigation( { waitUntil: 'networkidle0' } ),
|
||||||
|
] );
|
||||||
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
clearAndFillInput,
|
clearAndFillInput,
|
||||||
clickTab,
|
clickTab,
|
||||||
|
@ -225,4 +272,7 @@ export {
|
||||||
moveAllItemsToTrash,
|
moveAllItemsToTrash,
|
||||||
evalAndClick,
|
evalAndClick,
|
||||||
selectOptionInSelect2,
|
selectOptionInSelect2,
|
||||||
|
applyCoupon,
|
||||||
|
removeCoupon,
|
||||||
|
selectOrderAction,
|
||||||
};
|
};
|
||||||
|
|
|
@ -141,7 +141,7 @@ class WC_Tests_Checkout extends WC_Unit_Test_Case {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test usage limit for guest users uasge limit per user is set.
|
* Test usage limit for guest users usage limit per user is set.
|
||||||
*
|
*
|
||||||
* @throws Exception When unable to create order.
|
* @throws Exception When unable to create order.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -18,7 +18,7 @@ class Dummy_Widget extends WC_Widget {
|
||||||
* Output widget.
|
* Output widget.
|
||||||
*
|
*
|
||||||
* @param mixed $args Arguments.
|
* @param mixed $args Arguments.
|
||||||
* @param WP_Widget $instance Intance of WP_Widget.
|
* @param WP_Widget $instance Instance of WP_Widget.
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
public function widget( $args, $instance ) {
|
public function widget( $args, $instance ) {
|
||||||
|
|
|
@ -0,0 +1,136 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Tests for the WC_Admin_Dashboard_Setup class.
|
||||||
|
*
|
||||||
|
* @package WooCommerce\Tests\Admin
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class WC_Admin_Dashboard_Setup_Test
|
||||||
|
*/
|
||||||
|
class WC_Admin_Dashboard_Setup_Test extends WC_Unit_Test_Case {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set up
|
||||||
|
*/
|
||||||
|
public function setUp() {
|
||||||
|
// set default country to US so that 'payments' task does not get added.
|
||||||
|
// we want to remove payment tasks as they depend on installation & activation.
|
||||||
|
update_option( 'woocommerce_default_country', 'US' );
|
||||||
|
parent::setUp();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Includes widget class and return the class.
|
||||||
|
*
|
||||||
|
* @return WC_Admin_Dashboard_Setup
|
||||||
|
*/
|
||||||
|
public function get_widget() {
|
||||||
|
return include __DIR__ . '/../../../../includes/admin/class-wc-admin-dashboard-setup.php';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return widget output (HTML).
|
||||||
|
*
|
||||||
|
* @return string Render widget HTML
|
||||||
|
*/
|
||||||
|
public function get_widget_output() {
|
||||||
|
update_option( 'woocommerce_task_list_hidden', 'no' );
|
||||||
|
|
||||||
|
ob_start();
|
||||||
|
$this->get_widget()->render();
|
||||||
|
return ob_get_clean();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests widget does not get rendered when woocommerce_task_list_hidden or woocommerce_task_list_hidden
|
||||||
|
* is true.
|
||||||
|
*
|
||||||
|
* @dataProvider should_display_widget_data_provider
|
||||||
|
*
|
||||||
|
* @param array $options a set of options.
|
||||||
|
*/
|
||||||
|
public function test_widget_does_not_get_rendered( array $options ) {
|
||||||
|
global $wp_meta_boxes;
|
||||||
|
|
||||||
|
foreach ( $options as $name => $value ) {
|
||||||
|
update_option( $name, $value );
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->get_widget();
|
||||||
|
$this->assertNull( $wp_meta_boxes );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given both woocommerce_task_list_hidden and woocommerce_task_list_complete are false
|
||||||
|
* Then the widget should be added to the $wp_meta_boxes
|
||||||
|
*/
|
||||||
|
public function test_widget_gets_rendered_when_both_options_are_false() {
|
||||||
|
global $wp_meta_boxes;
|
||||||
|
update_option( 'woocommerce_task_list_complete', false );
|
||||||
|
update_option( 'woocommerce_task_list_hidden', false );
|
||||||
|
|
||||||
|
$this->get_widget();
|
||||||
|
$this->assertArrayHasKey( 'wc_admin_dashboard_setup', $wp_meta_boxes['dashboard']['normal']['high'] );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests the widget output when 0 task has been completed.
|
||||||
|
*/
|
||||||
|
public function test_initial_widget_output() {
|
||||||
|
$html = $this->get_widget_output();
|
||||||
|
|
||||||
|
$required_strings = array(
|
||||||
|
'Step 0 of 5',
|
||||||
|
'You're almost there! Once you complete store setup you can start receiving orders.',
|
||||||
|
'Start selling',
|
||||||
|
'admin.php\?page=wc-admin&path=%2Fsetup-wizard',
|
||||||
|
);
|
||||||
|
|
||||||
|
foreach ( $required_strings as $required_string ) {
|
||||||
|
$this->assertRegexp( "/${required_string}/", $html );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests completed task count as it completes one by one
|
||||||
|
*/
|
||||||
|
public function test_widget_renders_completed_task_count() {
|
||||||
|
$completed_tasks = array();
|
||||||
|
$tasks = $this->get_widget()->get_tasks();
|
||||||
|
$tasks_count = count( $tasks );
|
||||||
|
foreach ( $tasks as $key => $task ) {
|
||||||
|
array_push( $completed_tasks, $key );
|
||||||
|
update_option( 'woocommerce_task_list_tracked_completed_tasks', $completed_tasks );
|
||||||
|
$completed_tasks_count = count( $completed_tasks );
|
||||||
|
// When all tasks are completed, assert that the widget output is empty.
|
||||||
|
// As widget won't be rendered when tasks are completed.
|
||||||
|
if ( $completed_tasks_count === $tasks_count ) {
|
||||||
|
$this->assertEmpty( $this->get_widget_output() );
|
||||||
|
} else {
|
||||||
|
$this->assertRegexp( "/Step ${completed_tasks_count} of 5/", $this->get_widget_output() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides dataset that controls output of `should_display_widget`
|
||||||
|
*/
|
||||||
|
public function should_display_widget_data_provider() {
|
||||||
|
return array(
|
||||||
|
array(
|
||||||
|
array(
|
||||||
|
'woocommerce_task_list_complete' => 'yes',
|
||||||
|
'woocommerce_task_list_hidden' => 'no',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
array(
|
||||||
|
'woocommerce_task_list_complete' => 'no',
|
||||||
|
'woocommerce_task_list_hidden' => 'yes',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -85,7 +85,7 @@ class WC_Stock_Functions_Tests extends \WC_Unit_Test_Case {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test inventory count after order status transtions which reduces stock to another status which also reduces stock.
|
* Test inventory count after order status transitions which reduces stock to another status which also reduces stock.
|
||||||
* Stock should have reduced once already, and should not reduce again.
|
* Stock should have reduced once already, and should not reduce again.
|
||||||
*/
|
*/
|
||||||
public function test_status_transition_stock_reduce_to_stock_reduce() {
|
public function test_status_transition_stock_reduce_to_stock_reduce() {
|
||||||
|
@ -97,7 +97,7 @@ class WC_Stock_Functions_Tests extends \WC_Unit_Test_Case {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test inventory count after order status transtions which reduces stock to another status which restores stock.
|
* Test inventory count after order status transitions which reduces stock to another status which restores stock.
|
||||||
* Should should have already reduced once, and will increase again after transitioning.
|
* Should should have already reduced once, and will increase again after transitioning.
|
||||||
*/
|
*/
|
||||||
public function test_status_transition_stock_reduce_to_stock_restore() {
|
public function test_status_transition_stock_reduce_to_stock_restore() {
|
||||||
|
@ -109,7 +109,7 @@ class WC_Stock_Functions_Tests extends \WC_Unit_Test_Case {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test inventory count after order status transtions which reduces stock to another status which don't affect inventory.
|
* Test inventory count after order status transitions which reduces stock to another status which don't affect inventory.
|
||||||
* Stock should have already reduced, and will not change on transitioning.
|
* Stock should have already reduced, and will not change on transitioning.
|
||||||
*/
|
*/
|
||||||
public function test_status_transition_stock_reduce_to_stock_no_effect() {
|
public function test_status_transition_stock_reduce_to_stock_no_effect() {
|
||||||
|
@ -133,7 +133,7 @@ class WC_Stock_Functions_Tests extends \WC_Unit_Test_Case {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test inventory count after order status transtions which restores stock to another status which also restores stock.
|
* Test inventory count after order status transitions which restores stock to another status which also restores stock.
|
||||||
* Stock should not have reduced, and will remain the same even after transition (i.e. should not be restocked again).
|
* Stock should not have reduced, and will remain the same even after transition (i.e. should not be restocked again).
|
||||||
*/
|
*/
|
||||||
public function test_status_transition_stock_restore_to_stock_restore() {
|
public function test_status_transition_stock_restore_to_stock_restore() {
|
||||||
|
@ -145,7 +145,7 @@ class WC_Stock_Functions_Tests extends \WC_Unit_Test_Case {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test inventory count after order status transtions which restores stock to another status which don't affect inventory.
|
* Test inventory count after order status transitions which restores stock to another status which don't affect inventory.
|
||||||
* Stock should not have reduced, and will remain the same even after transition.
|
* Stock should not have reduced, and will remain the same even after transition.
|
||||||
*/
|
*/
|
||||||
public function test_status_transition_stock_restore_to_stock_no_effect() {
|
public function test_status_transition_stock_restore_to_stock_no_effect() {
|
||||||
|
@ -157,7 +157,7 @@ class WC_Stock_Functions_Tests extends \WC_Unit_Test_Case {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test inventory count after order status transtions which don't affect inventory stock to another status which reduces stock.
|
* Test inventory count after order status transitions which don't affect inventory stock to another status which reduces stock.
|
||||||
* Stock would not have been affected, but will reduce after transition.
|
* Stock would not have been affected, but will reduce after transition.
|
||||||
*/
|
*/
|
||||||
public function test_status_transition_stock_no_effect_to_stock_reduce() {
|
public function test_status_transition_stock_no_effect_to_stock_reduce() {
|
||||||
|
@ -169,7 +169,7 @@ class WC_Stock_Functions_Tests extends \WC_Unit_Test_Case {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test inventory count after order status transtions which don't affect inventory stock to another status which restores stock.
|
* Test inventory count after order status transitions which don't affect inventory stock to another status which restores stock.
|
||||||
* Stock would not have been affected, and will not be restored after transition (since it was not reduced to begin with).
|
* Stock would not have been affected, and will not be restored after transition (since it was not reduced to begin with).
|
||||||
*/
|
*/
|
||||||
public function test_status_transition_stock_no_effect_to_stock_restore() {
|
public function test_status_transition_stock_no_effect_to_stock_restore() {
|
||||||
|
@ -181,7 +181,7 @@ class WC_Stock_Functions_Tests extends \WC_Unit_Test_Case {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test inventory count after order status transtions which don't affect inventory stock to another status which also don't affect inventory.
|
* Test inventory count after order status transitions which don't affect inventory stock to another status which also don't affect inventory.
|
||||||
* Stock levels will not change before or after the transition.
|
* Stock levels will not change before or after the transition.
|
||||||
*/
|
*/
|
||||||
public function test_status_transition_stock_no_effect_to_stock_no_effect() {
|
public function test_status_transition_stock_no_effect_to_stock_no_effect() {
|
||||||
|
|
Loading…
Reference in New Issue