Resolving conflict with migration branch

This commit is contained in:
Julia Amosova 2019-11-28 12:47:28 +00:00
commit 3a7aeb04e6
48 changed files with 3023 additions and 1215 deletions

View File

@ -13,7 +13,7 @@ There are many ways to contribute to the project!
If you wish to contribute code, please read the information in the sections below. Then [fork](https://help.github.com/articles/fork-a-repo/) WooCommerce, commit your changes, and [submit a pull request](https://help.github.com/articles/using-pull-requests/) 🎉
We use the `help wanted` label to mark issues that are suitable for new contributors. You can find all the issues with this label [here](https://github.com/woocommerce/woocommerce/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22).
We use the `good first issue` label to mark issues that are suitable for new contributors. You can find all the issues with this label [here](https://github.com/woocommerce/woocommerce/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22).
WooCommerce is licensed under the GPLv3+, and all contributions to the project will be released under the same license. You maintain copyright over any contribution you make, and by submitting a pull request, you are agreeing to release that contribution under the GPLv3+ license.

View File

@ -5,6 +5,8 @@ dist: xenial
services:
- xvfb
- mysql
- docker
- docker-compose
sudo: false
@ -32,14 +34,10 @@ matrix:
- name: "Coding standard check"
php: 7.2
env: WP_VERSION=latest WP_MULTISITE=0 RUN_PHPCS=1
- name: "e2e tests"
php: 7.2
env: WP_VERSION=latest WP_MULTISITE=0 RUN_E2E=1
addons:
chrome: beta
apt:
packages:
- nginx
- name: "E2E tests"
env: RUN_E2E=1
script:
- docker-compose up -d
- name: "Unit tests code coverage"
php: 7.3
env: WP_VERSION=latest WP_MULTISITE=0 RUN_CODE_COVERAGE=1

View File

@ -1,5 +1,225 @@
== Changelog ==
= 3.8.0 - 2019-11-05 =
* Enhancement - Show error message in "My Account - view order" if order does not exist. #24435
* Enhancement - Add support to allow connect and install for in-app purchase flow. #24451
* Enhancement - Declared support to Unicode CLDR. #24564
* Enhancement - Declared support for PHP 7.4 in CI by removing PHP 7.4 from list of allowed failures. #24668
* Enhancement - Update the recommended PHP version to 7.0 and the recommended WP version to 5.0. #24730
* Enhancement - Change On Boarding Wizard to opt out of usage tracking by default. #24680
* Enhancement - Add `Available on backorder` message for products available on backorder. #24559
* Tweak - Hide Vietnam's state field. #24158
* Tweak - Better wording when no downloads are available on My Account > Downloads. #24172
* Tweak - Only consider orders created via checkout to hold stock. #24159
* Tweak - Stop to load order data twice in "View order" screen on "My Account" page. #24437
* Tweak - Prevent PHP warnings in tracker if order doesn't have a created date yet. #24441
* Tweak - Use `wc_get_checkout_url()` to get checkout URL. #24544
* Tweak - Maintain value of select fields in credit card form. #24720
* Tweak - Prevent filter per category while exporting product variations. #24517
* Tweak - Better wording for subtotal of items in cart and review order. #24440
* Tweak - Prevent new lines in product quantity in checkout details. #24311
* Tweak - Add a tooltip in the "Coupon expity date" field. #24749
* Tweak - CSS styling changes for WP 5.3. #24832
* Template - Moved HTML for displaying product price filter widget to a new template `product price filter widget`. #23384
* Accessibility - Make $subtext color darker. #24739
* Dev - Consistent register/login submit button css class names. Preserve old class names as well for backwards compatibility. #23701
* Dev - Added woocommerce_disable_password_change_notification filter, set to false by default. #24154
* Dev - Add filter `woocommerce_cart_item_removed_message` and `woocommerce_cart_product_cannot_be_purchased_message` to filter notice content when an item is removed from cart, or when a product cannot be purchased. #24176
* Dev - Prevent fatal error if trying to run `wc_load_cart()` before `before_woocommerce_init`. #24198
* Dev - Add woocommerce_get_zone_criteria filter hook to add custom criteria for matching zone ID. #24199
* Dev - Fire actions `woocommerce_before_delete_product_variation`, `woocommerce_delete_product_variation` and `woocommerce_trash_product_variation` appropriately when deleting or trashing product variations. #24239
* Dev - In filter `woocommerce_update_product_stock_query` use `$new_stock` instead of `$stock_quantity`. #24229
* Dev - Introduced woocommerce_prevent_adjust_line_item_product_stock filter. #24279
* Dev - Introduced `woocommerce_logout_default_redirect_url` filter to allow custom default URL. #24282
* Dev - Added woocommerce_sort_fees_callback filter. #24280
* Dev - Remove deprecated i18n/states directory. #24364
* Dev - Add filter `woocommerce_shipping_show_shipping_calculator` for showing shipping calculator. #24375
* Dev - Corrected UG states location. #24388
* Dev - Remove deprecations notices with PHP 7.4 by changing parameter order for `implode`. #24396
* Dev - Add ``$this` param to email filter for additional_content added in 3.7. #24399
* Dev - Add `woocommerce_product_import_process_item_data` filter to allow modifying a row before importing. #24412
* Dev - Fixed warnings when error_get_last() returns null. #24442
* Dev - Use brackets instead of braces to avoid deprecation notice in PHP 7.4. #24460
* Dev - Implement Puppeteer's architecture for running E2E tests locally. #24479
* Dev - Remove call to get_magic_quotes_runtime() as it is deprecated as of PHP 7.4. #24485
* Dev - Return value of `$mailer->send()` in `wc_mail` function. #24505
* Dev - Remove Selenium e2e tests & add Puppeteer new product e2e test. #24513
* Dev - Add a filter `woocommerce_ajax_add_order_item_validation` to allow validations in `add_order_item` function. #24518
* Dev - Use `wc_get_cart_url` instead of `wc_get_page_permalink( 'cart' )` because former has a filter `woocommerce_get_cart_url` to allow customization. #24530
* Dev - New `woocommerce_product_after_tabs` action hook added. #24694
* Dev - Enable append hashes on custom events (like ajax requests) #24665
* Dev - Introduced `woocommerce_order_get_formatted_billing_address` and `woocommerce_order_get_formatted_shipping_address` filters. #24677
* Dev - WC_Abstract_Order::recalculate_coupons() is public now. #24740
* Dev - Added 'applied_coupon' trigger to checkout.js. #24406
* Dev - Introduced woocommerce_output_cart_shortcode_content filter. #24738
* Dev - Add WPML compatibility to the geolocation URL. #24722
* Dev - Validate server requirements in WCCCOM Site Installer endpoints. #24690
* Dev - Introduced woocommerce_payment_token_class filter. #24542
* Dev - Add support for post type count to system status report. #24536
* Dev - Check for max discount to be -ve to prevent overwriting refunded fee amount. #24341
* Dev: Add filter woocommerce_european_union_countries to the method WC_Countries::get_european_union_countries(). #24741
* Dev - Allow WC_Product_Query sort products by include order. #24294
* Dev - Removed duplicated include of WC_Admin_Importers. #24751
* Dev - Refactor minimum requirement notice to use constant for easier changes in the future. #24830
* Fix - Clean products transients when term is removed. #23991
* Fix - Only add the image node to structured data if product has image. #24191
* Fix - Product attribute terms endpoint in legacy REST API v3 by converting `attribute_id` to int. #24203
* Fix - Prevent duplicate processing/stock reduction when IPN or PDT notifications are enabled by checking if order is not already in `processing` or `completed` status. #24249
* Fix - Position of ID section in mobile rows actions when displaying the list of products in the admin. #24277
* Fix - Endpoints URLs to follow slashes preferences from WordPress permalinks. #24283
* Fix - Ensure that postcode validation doesn't return any whitespace on beginning and end of a postcode. #24284
* Fix - Shipping classes sort under products alphabetically. #24291
* Fix - Shipping rates layout in admin settings. #24327
* Fix - Retain the focus on the select box when product variation is changed. #24339
* Fix - Prevent PHP notice in WC_Order_Item_Data_Store::get_order_item_type() when there is no entry in the database for the order item ID passed. #24353
* Fix - Add translation in class-wc-shipping.php for matching zone message. #24366
* Fix - Previewing email template. #24380
* Fix - Check if order exist before generate hash for downloads. #24384
* Fix - Ensure WC_Product_Data_Store_CPT::is_existing_sku() return boolean. #24385
* Fix - `function_exists` check for wc_hex_is_light(). #24391
* Fix - Removed Emoji from default additional email content due problem on some database that doesn't allow Emojis. #24450
* Fix - Fixed escaping on an attribute in `class-wc-admin-menus.php`. #24466
* Fix - Add translation for image alt attribute text in multiple places. #24467, #24468, #24469
* Fix - Uneven padding in the input field with class "location-input" in on boarding wizard. #24476
* Fix - Duplicate id in pages settings. #24478
* Fix - Use `esc_attr_e` instead of `esc_html_e` for escaping an attribute in multiple places. #24481, #24520, #24521, #24522, #24523, #24524
* Fix - Use `esc_attr__` instead of `esc_html__` in escaping attributes string. #24525, #24529
* Fix - Typo fix in payment captured error message. #24501
* Fix - Documentation URL in failed order email content. #24535
* Fix - "Add to cart" links in feed. #24545
* Fix - Escaping in Storefront banner. #24546
* Fix - Use `wp_kses_post` to allow certain html tags in extensions page. #24589
* Fix - Escape `$prefix` in item download links. #24601
* Fix - Use `esc_attr` instead of `esc_html` in multiple places for proper escaping. #24613, #24614
* Fix - Use `esc_html` instead of `esc_attr` in multiple places for proper escaping. #24616
* Fix - Use `esc_html_e` instead of `_e` in multiple places for proper escaping. #24615, #24618, #24630
* Fix - Use `esc_html__` instead of `esc_html` for proper escaping and some typo fixes. #24639
* Fix - Add no-store and remove max-age header (no longer needed). #24652
* Fix - Use correct meta value for `downloadable` column in product lookup table regenerate function. #24681
* Fix - Admin sub-menus becoming unaligned when scrolling down in the orders page when there are no orders. #24688
* Fix - OWB country and sell in person alignment. #24700
* Fix - Domain replacement script now replaces both double and single quoted `woo-gutenberg-products-block` with `woocommerce` in both PHP and JavaScript files. #24696
* Fix - Convert `current_user_id` to string in some places to prevent unnecessary session database updates. #24016
* Fix - Fixed description of failed order emails. #24737
* Fix - Fixed Photoswipe styles. #24296
* Fix - Also consider refunded item when updating order and adjusting stocks. #24745
* Fix - Handle 0 attribute value for variations correctly. #24750
* Fix - Fixed spaces in form fields of External Products. #24295
* Fix - Removed links to downloadable products from refund emails. #24952
* Localization - Add Zambia's Provinces to the list of states. #24307
* Localization - Adaptation of the order of last name and first name and addresses in Japan. #24336
* Localization - Fixed Namibian dollar symbol. #24438
* Localization - Change localization tag `action-scheduler` to `woocommerce`. #24474
* Localization - Fixed missing US Minor Outlying Islands states. #24826
* Security - Add an exit after the redirect when checking author archive capabilities for customers.
* Security - Ensure 404 pages with single product urls cannot be exploited using Open Redirect.
= 3.7.1 - 2019-10-09
* Security - Add an exit after the redirect when checking author archive capabilities for customers.
* Security - Ensure 404 pages with single product urls cannot be exploited using Open Redirect.
= 3.7.0 - 2019-08-12 =
* Enhancement - Added table ENGINE to system status report for debugging purposes. #23101
* Enhancement - Format empty cart message as information notice. #23152
* Enhancement - Add taxonomy-specific classes to active filters widget. #23122
* Enhancement - Allow emails `Thanks` wording to be modified via the email settings. #22927
* Enhancement - Move tax classes from WordPress Options to a new `wc_tax_rate_classes` table. #23093
* Enhancement - Make WooCommerce shop roles translatable. #23150
* Enhancement - Prevent the Cart, Checkout and My Account pages from being set to the same pages. #23479
* Enhancement - New coupon code generate button on the coupon page. #24069
* Enhancement - Add `tag_operator` paramater to the `[products]` shortcode for use with the `tag` paramater ie. `[products tag='tag1,tag2' tag_operator='AND']`. #24111
* Tweak - When cleaning up variations due to product type change, force delete them instead of trashing. #23478
* Tweak - Change wording on the link to change the address so reflect if an address is already present or not. #23532
* Tweak - If variations are missing prices, show a notice in the product data panel. #23133
* Tweak - Use `determine_locale()` to properly load custom translation files. #23785
* Tweak - OBW: Switch shipping labels and shipping zones placement. #23781
* Tweak - Show the quantity refunded on customer facing order screens. #23038
* Tweak - CSV product import now allows true/false values for the published field, as well as the original 0 (private), -1 (draft), 1 (publish) values. #23207
* Tweak - Update product attribute sorting tooltip to clarify usage. #23222
* Tweak - Store tax rate percentage in the tax line items on orders. #23268
* Tweak - Remove the left and right margin from the logo in emails. #23360
* Tweak - Use the high res version of the WP spinner in the coupon Block UI. #23364
* Tweak - Improve user registration validation messages. #23468
* Tweak - Auto generate a new username when a username is blacklisted by WordPress. #23672
* Tweak - Guest cart sessions now gets deleted when a user logs in, preventing duplicate cart sessions. #23687
* Tweak - Include the store's base postcode and city when calculating order taxes. #23695
* Tweak - Update the generate username setting description label to reflect how the username is actually generated. #23911
* Tweak - OBW: Adjust plugin highlight container sizes to avoid overlap. #23997
* Tweak - Round tax amounts late when the round at subtotal level setting is enabled to reduce rounding errors. #24024
* Tweak - OBW: Now includes WooCommerce Admin as a recommended plugin. #24058
* Template - Review and update all template files escaping. #23460
* Template - Remove mention of shipping section from the checkout/form-login.php template as shipping is not always a requirement for an order. #23941
* Template - Add new filter `woocommerce_before_thankyou` to the checkout/thankyou.php template. #23538
* Template - Add new `woocommerce_widget_shopping_cart_total` hook to replace hardcoded subtotal in cart/mini-cart.php template. #24145
* Template - Add new `woocommerce_widget_shopping_cart_after_buttons` hook in cart/mini-cart.php template. #24145
* Template - Add new `woocommerce_before_cart_collaterals` hook in cart/cart.php template. #24145
* Template - Correct the plural forms usage in loop/result-count.php template. #24005
* Dev - Introduce new PHP 5.6 minimum requirement. #23924
* Dev - Introduce new WordPress 4.9 minimum requirement. #24156
* Dev - Move the settings save functionality from the `settings_page_init` function to the `wp_loaded` action so it is not saved after the settings page renders. #23091
* Dev - Add quantity input action hooks `woocommerce_before_add_to_cart_quantity` and `woocommerce_after_add_to_cart_quantity`. #23166
* Dev - Add `$this` parameter to email class filters. #23250
* Dev - Add new `WC_Abstract_Order::get_coupons()` method for returning all coupon line item objects on an order. #23663
* Dev - Added new action `woocommerce_product_read` to `WC_Product_Data_Store_CPT::read()`. #23181
* Dev - Add new filter `woocommerce_admin_order_buyer_name` to the `WC_Admin_List_Table_Orders::render_order_number_column()` method to change the buyer name in orders list screen. #23741
* Dev - Add new actions to `WC_Helper` class for when WooCommerce.com Product Subscription statuses change `woocommerce_helper_subscription_activate_success`, `woocommerce_helper_subscription_activate_error`, `woocommerce_helper_subscription_deactivate_success`, and `woocommerce_helper_subscription_deactivate_error`. #23041
* Dev - Extend usage and event tracking (if opted in) to system status, admin order and admin coupon pages. #23190 #23189 #23883
* Dev - Add `woocommerce_after_X_object_save` actions, and passed objects to `woocommerce_new_x` and `woocommerce_update_x` actions. #23338
* Dev - Update customer order and lifetime spend totals in `wc_update_new_customer_past_orders()` to trigger `customer.updated` webhooks for paid orders. #23402
* Dev - Preserve the State field's custom css classes when selecting an option from the Country dropdown. #23433
* Dev - Add new `woocommerce_product_related_posts_shuffle` filter in `wc_get_related_products()` to enable/disable related product shuffling, defaults to true. #23562
* Dev - Deprecate the `WC_Abstract_Order::get_used_coupons()` method and replace it with a new method `WC_Abstract_Order::get_coupon_codes()`. #23689
* Dev - Add new `woocommerce_prices_include_tax` filter in the `wc_prices_include_tax()` function. #23697
* Dev - Add new `woocommerce_admin_after_product_gallery_item` filter in the `WC_Meta_Box_Product_Images::output()` method for adding additional markup after product gallery items. #23743
* Dev - Remove unused images `assets/images/klarna-white.png` and `assets/images/square-white.png`. #23748
* Dev - Move Free Shipping method JavaScript code from outputting on all shipping setting pages to just the Free Shipping page using the `admin_footer` hook. #23776
* Dev - Prevent PHP fatal error while throwing exceptions in `woocommerce_rest_insert_{post_type}_object` hooks. #23793
* Dev - Add new `woocommerce_enforce_password_strength_meter_on_checkout` filter in the `WC_Frontend_Scripts::get_script_data()` method to allow enforcing the password strength meter on checkout. #23811
* Dev - Add new `woocommerce_search_products_post_statuses` filter in the `WC_Product_Data_Store_CPT::search_products()` method for controlling what post statuses to include in product searches. #23838
* Dev - Allow filtering `woocommerce_order_formatted_shipping_address` even when no shipping address is defined. #23859
* Dev - Change the query in the `WC_Product_Data_Store::find_matching_product_variation()` method to always respect the ordering of variations. #23881
* Dev - Move all feature plugin features out from the WooCommerce codebase and utilize composer and an autoloader for including it in WooCommerce core, affects WC REST API and WC Blocks. #23957
* Dev - Allow displaying multiple error messages through the registration validation. #23968
* Dev - Add new `woocommerce_cart_item_removed_notice_type`, `woocommerce_cart_updated_notice_type` and `woocommerce_add_to_cart_notice_type` filters for changing the default notice types for cart notices. #24021
* Dev - Add namespaced support for Jetpack 7.5 tracking library. #24140
* Dev - Add support for an improved WooCommerce.com Marketplace browse and purchase experience (in progress). #24075 #24123
* Dev - Added `$order` and `$product` as parameters to the `woocommerce_ajax_order_item` filter in `WC_Ajax::add_order_item()`. #24108
* Dev - Add new `woocommerce_product_import_image_separator` filter in `WC_Product_CSV_Importer::parse_images_field()` for adjusting the product images seperator. #24120
* Dev - Add new `woocommerce_widget_shopping_cart_subtotal()` template function that hooks into the `woocommerce_widget_shopping_cart_total` action to output the mini cart subtotal. #24145
* Dev - Deprecate the `woocommerce_before_cart_item_quantity_zero` action from `WC_Cart::restore_cart_item()` in favor of existing `woocommerce_cart_item_removed` action. #23112
* Dev - Deprecate WC_Tax::maybe_remove_tax_class_rates which hooked into the WP Options update hook for taxes in favor of new function WC_Tax::delete_tax_class_by which works on the new tax classes table. #24213
* Fix - Use version_compare for determining the maximum WooCommerce database version number. #23092
* Fix - Missing space and closing `<strong>` tag in WooCommerce.com disconnect message. #24073
* Fix - CSV Importer - Skip rows during update if a SKU column exists, but the value is empty. #23262
* Fix - Allow matching `Any` attributes when calling `WC_Product_Data_Store::find_matching_product_variation()`. #23067
* Fix - Switch coupon amount validation based on decimal seperator setting. #23137
* Fix - Show the correct results for shortcodes on static homepages when sorting. #23159
* Fix - Queue AJAX add to cart events to avoid overwriting session data. #23293
* Fix - Wrong subtotals when changing tax classes via the `woocommerce_product_get_tax_class` filter. #23344
* Fix - Fatal error on plain text order emails where products were deleted. #23754
* Fix - Do not pass the `no_shipping` argument to PayPal when the order contains shippable items. #23773
* Fix - Product review form does respects the `require_name_email` WordPress core option. #23786
* Fix - Do not cache expired sessions, negative expiry causes errors in some caching modules. #23863
* Fix - WC_Log_Handler_DB logs now uses the same timestamp format as text logs, Y-m-d H:i:s. #23863
* Fix - Display line breaks for customer notes in emails, and order details. #23969
* Fix - Correct plural forms usage in `WC_Admin_Report` class. #24020
* Fix - System status database info section throwing a PHP notice on some DB environments. #24023
* Fix - On the system status database info section display a message informing users that WooCommerce was unable to get database information instead of an error, when a database sharding plugin is active. #24034
* Fix - Usage and event tracking (if opted in) was not working correctly in the OBW. #24056
* Fix - Fatal error on downloads report when some download files were missing. #24118
* Fix - Prevents the taxes columns from being removed when the order is no longer editable in admin. #23884
* Performance - Improve the speed of the admin dashboard by only updating transients once per class. #23011
* Performance - Reduce number of queries needed to populate variations data by priming post caches. #23272
* Performance - Persistant cart improvements, only update the persistent cart if the cart items actually change. #23112
* Performance - Exclude `action_log` comment types from `wp_count_comments`. #24071
* Localization - Added validation for Italian postcodes. #23269
* Localization - Remove unused tax locale defaults since we now promote auto tax services instead. #23431
* Localization - Define correct address format for Uganda. #23178
* Localization - Hide the postcode and update the state label to "Province" for Mozambique. #23764
* Localization - OBW: Make postal code optional based on locale data. #23915
* Localization - Add new currency for São Tomé, Príncipe dobra and Mauritanian ouguiya. #23950
* Localization - Change Canada poscode label to `Postal code`. #23740
= 3.6.5 - 2019-07-02 =
* Security - Introduce file type check for tax rate importer.
* Security - Added nonce check to CSV importer actions.

3
Dockerfile Normal file
View File

@ -0,0 +1,3 @@
FROM wordpress:latest
COPY . /var/www/html/wp-content/plugins/woocommerce

View File

@ -4183,6 +4183,10 @@ img.help_tip {
right: -8px;
padding: 2px;
display: none;
@media (max-width: 768px) {
display: block;
}
li {
float: right;

View File

@ -360,9 +360,12 @@ jQuery( function( $ ) {
// Always update the fragments
if ( data && data.fragments ) {
$.each( data.fragments, function ( key, value ) {
$( key ).replaceWith( value );
if ( ! wc_checkout_form.fragments || wc_checkout_form.fragments[ key ] !== value ) {
$( key ).replaceWith( value );
}
$( key ).unblock();
} );
wc_checkout_form.fragments = data.fragments;
}
// Recheck the terms and conditions box, if needed

View File

@ -10,12 +10,12 @@
"automattic/jetpack-autoloader": "^1.2.0",
"php": ">=5.6|>=7.0",
"composer/installers": "1.7.0",
"woocommerce/woocommerce-blocks": "2.4.3",
"woocommerce/woocommerce-blocks": "2.4.5",
"woocommerce/woocommerce-rest-api": "1.0.3"
},
"require-dev": {
"phpunit/phpunit": "7.5.16",
"woocommerce/woocommerce-sniffs": "0.0.8"
"phpunit/phpunit": "7.5.17",
"woocommerce/woocommerce-sniffs": "0.0.9"
},
"config": {
"platform": {

88
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "c955d86ce8d65e6fb1e0e97282642d7b",
"content-hash": "a5cb448a1d94f10a40207b6f0dfa1c50",
"packages": [
{
"name": "automattic/jetpack-autoloader",
@ -166,16 +166,16 @@
},
{
"name": "woocommerce/woocommerce-blocks",
"version": "v2.4.3",
"version": "v2.4.5",
"source": {
"type": "git",
"url": "https://github.com/woocommerce/woocommerce-gutenberg-products-block.git",
"reference": "9a4b30a18f055ed83d819959cbc1386111f401a5"
"reference": "130bc40824f844c0870c94c0ad0aa7a6abb74f6f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/woocommerce/woocommerce-gutenberg-products-block/zipball/9a4b30a18f055ed83d819959cbc1386111f401a5",
"reference": "9a4b30a18f055ed83d819959cbc1386111f401a5",
"url": "https://api.github.com/repos/woocommerce/woocommerce-gutenberg-products-block/zipball/130bc40824f844c0870c94c0ad0aa7a6abb74f6f",
"reference": "130bc40824f844c0870c94c0ad0aa7a6abb74f6f",
"shasum": ""
},
"require": {
@ -209,7 +209,7 @@
"gutenberg",
"woocommerce"
],
"time": "2019-10-14T13:20:09+00:00"
"time": "2019-11-02T13:48:18+00:00"
},
{
"name": "woocommerce/woocommerce-rest-api",
@ -527,16 +527,16 @@
},
{
"name": "phpcompatibility/php-compatibility",
"version": "9.3.1",
"version": "9.3.4",
"source": {
"type": "git",
"url": "https://github.com/PHPCompatibility/PHPCompatibility.git",
"reference": "9999344e47e7af6b00e1a898eacc4e4368fb7196"
"reference": "1f37659196e4f3113ea506a7efba201c52303bf1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibility/zipball/9999344e47e7af6b00e1a898eacc4e4368fb7196",
"reference": "9999344e47e7af6b00e1a898eacc4e4368fb7196",
"url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibility/zipball/1f37659196e4f3113ea506a7efba201c52303bf1",
"reference": "1f37659196e4f3113ea506a7efba201c52303bf1",
"shasum": ""
},
"require": {
@ -581,20 +581,20 @@
"phpcs",
"standards"
],
"time": "2019-09-05T18:36:49+00:00"
"time": "2019-11-15T04:12:02+00:00"
},
{
"name": "phpcompatibility/phpcompatibility-paragonie",
"version": "1.1.0",
"version": "1.3.0",
"source": {
"type": "git",
"url": "https://github.com/PHPCompatibility/PHPCompatibilityParagonie.git",
"reference": "b1bb79a7cab1fb856b56f1b5cf110b6e52d8e936"
"reference": "b862bc32f7e860d0b164b199bd995e690b4b191c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibilityParagonie/zipball/b1bb79a7cab1fb856b56f1b5cf110b6e52d8e936",
"reference": "b1bb79a7cab1fb856b56f1b5cf110b6e52d8e936",
"url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibilityParagonie/zipball/b862bc32f7e860d0b164b199bd995e690b4b191c",
"reference": "b862bc32f7e860d0b164b199bd995e690b4b191c",
"shasum": ""
},
"require": {
@ -633,7 +633,7 @@
"polyfill",
"standards"
],
"time": "2019-08-28T15:58:19+00:00"
"time": "2019-11-04T15:17:54+00:00"
},
{
"name": "phpcompatibility/phpcompatibility-wp",
@ -837,22 +837,22 @@
},
{
"name": "phpspec/prophecy",
"version": "1.8.1",
"version": "1.9.0",
"source": {
"type": "git",
"url": "https://github.com/phpspec/prophecy.git",
"reference": "1927e75f4ed19131ec9bcc3b002e07fb1173ee76"
"reference": "f6811d96d97bdf400077a0cc100ae56aa32b9203"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpspec/prophecy/zipball/1927e75f4ed19131ec9bcc3b002e07fb1173ee76",
"reference": "1927e75f4ed19131ec9bcc3b002e07fb1173ee76",
"url": "https://api.github.com/repos/phpspec/prophecy/zipball/f6811d96d97bdf400077a0cc100ae56aa32b9203",
"reference": "f6811d96d97bdf400077a0cc100ae56aa32b9203",
"shasum": ""
},
"require": {
"doctrine/instantiator": "^1.0.2",
"php": "^5.3|^7.0",
"phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0",
"phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0|^5.0",
"sebastian/comparator": "^1.1|^2.0|^3.0",
"sebastian/recursion-context": "^1.0|^2.0|^3.0"
},
@ -896,7 +896,7 @@
"spy",
"stub"
],
"time": "2019-06-13T12:50:23+00:00"
"time": "2019-10-03T11:07:50+00:00"
},
{
"name": "phpunit/php-code-coverage",
@ -1152,16 +1152,16 @@
},
{
"name": "phpunit/phpunit",
"version": "7.5.16",
"version": "7.5.17",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "316afa6888d2562e04aeb67ea7f2017a0eb41661"
"reference": "4c92a15296e58191a4cd74cff3b34fc8e374174a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/316afa6888d2562e04aeb67ea7f2017a0eb41661",
"reference": "316afa6888d2562e04aeb67ea7f2017a0eb41661",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/4c92a15296e58191a4cd74cff3b34fc8e374174a",
"reference": "4c92a15296e58191a4cd74cff3b34fc8e374174a",
"shasum": ""
},
"require": {
@ -1232,7 +1232,7 @@
"testing",
"xunit"
],
"time": "2019-09-14T09:08:39+00:00"
"time": "2019-10-28T10:37:36+00:00"
},
{
"name": "sebastian/code-unit-reverse-lookup",
@ -1802,16 +1802,16 @@
},
{
"name": "squizlabs/php_codesniffer",
"version": "3.5.0",
"version": "3.5.2",
"source": {
"type": "git",
"url": "https://github.com/squizlabs/PHP_CodeSniffer.git",
"reference": "0afebf16a2e7f1e434920fa976253576151effe9"
"reference": "65b12cdeaaa6cd276d4c3033a95b9b88b12701e7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/0afebf16a2e7f1e434920fa976253576151effe9",
"reference": "0afebf16a2e7f1e434920fa976253576151effe9",
"url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/65b12cdeaaa6cd276d4c3033a95b9b88b12701e7",
"reference": "65b12cdeaaa6cd276d4c3033a95b9b88b12701e7",
"shasum": ""
},
"require": {
@ -1849,7 +1849,7 @@
"phpcs",
"standards"
],
"time": "2019-09-26T23:12:26+00:00"
"time": "2019-10-28T04:36:32+00:00"
},
{
"name": "symfony/polyfill-ctype",
@ -2001,23 +2001,23 @@
},
{
"name": "woocommerce/woocommerce-sniffs",
"version": "0.0.8",
"version": "0.0.9",
"source": {
"type": "git",
"url": "https://github.com/woocommerce/woocommerce-sniffs.git",
"reference": "ccdae93ba678d59cd9741bec077d0c63c0a82958"
"reference": "7677a84e9a355fe1e088f704090be891e7a6d427"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/woocommerce/woocommerce-sniffs/zipball/ccdae93ba678d59cd9741bec077d0c63c0a82958",
"reference": "ccdae93ba678d59cd9741bec077d0c63c0a82958",
"url": "https://api.github.com/repos/woocommerce/woocommerce-sniffs/zipball/7677a84e9a355fe1e088f704090be891e7a6d427",
"reference": "7677a84e9a355fe1e088f704090be891e7a6d427",
"shasum": ""
},
"require": {
"dealerdirect/phpcodesniffer-composer-installer": "0.5.0",
"php": ">=7.0",
"phpcompatibility/phpcompatibility-wp": "2.1.0",
"wp-coding-standards/wpcs": "2.1.1"
"wp-coding-standards/wpcs": "2.2.0"
},
"type": "phpcodesniffer-standard",
"notification-url": "https://packagist.org/downloads/",
@ -2037,20 +2037,20 @@
"woocommerce",
"wordpress"
],
"time": "2019-10-16T18:25:21+00:00"
"time": "2019-11-11T15:48:34+00:00"
},
{
"name": "wp-coding-standards/wpcs",
"version": "2.1.1",
"version": "2.2.0",
"source": {
"type": "git",
"url": "https://github.com/WordPress/WordPress-Coding-Standards.git",
"reference": "bd9c33152115e6741e3510ff7189605b35167908"
"reference": "f90e8692ce97b693633db7ab20bfa78d930f536a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/WordPress/WordPress-Coding-Standards/zipball/bd9c33152115e6741e3510ff7189605b35167908",
"reference": "bd9c33152115e6741e3510ff7189605b35167908",
"url": "https://api.github.com/repos/WordPress/WordPress-Coding-Standards/zipball/f90e8692ce97b693633db7ab20bfa78d930f536a",
"reference": "f90e8692ce97b693633db7ab20bfa78d930f536a",
"shasum": ""
},
"require": {
@ -2073,7 +2073,7 @@
"authors": [
{
"name": "Contributors",
"homepage": "https://github.com/WordPress-Coding-Standards/WordPress-Coding-Standards/graphs/contributors"
"homepage": "https://github.com/WordPress/WordPress-Coding-Standards/graphs/contributors"
}
],
"description": "PHP_CodeSniffer rules (sniffs) to enforce WordPress coding conventions",
@ -2082,7 +2082,7 @@
"standards",
"wordpress"
],
"time": "2019-05-21T02:50:00+00:00"
"time": "2019-11-11T12:34:03+00:00"
}
],
"aliases": [],

53
docker-compose.yaml Normal file
View File

@ -0,0 +1,53 @@
version: '3.7'
services:
db:
image: mariadb:10.4
restart: on-failure
environment:
MYSQL_DATABASE: testdb
MYSQL_USER: wordpress
MYSQL_PASSWORD: wordpress
MYSQL_RANDOM_ROOT_PASSWORD: 'yes'
volumes:
- db:/var/lib/mysql
wordpress-woocomerce-dev:
depends_on:
- db
build:
context: .
dockerfile: Dockerfile
ports:
- 8084:80
restart: on-failure
environment:
WORDPRESS_DB_HOST: db
WORDPRESS_DB_NAME: testdb
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: wordpress
WORDPRESS_TABLE_PREFIX: "wp_"
WORDPRESS_DEBUG: 1
volumes:
- wordpress:/var/www/html
wordpress-cli:
depends_on:
- db
- wordpress-woocomerce-dev
image: wordpress:cli
restart: on-failure
user: xfs
command: >
/bin/sh -c '
wp core install --url=http://localhost:8084 --title="WooCommerce Core E2E Test Suite" --admin_user=admin --admin_password=password --admin_email=admin@woocommercecoree2etestsuite.com --path=/var/www/html --skip-email;
wp plugin activate woocommerce;
wp user create customer customer@woocommercecoree2etestsuite.com --user_pass=password --role=customer --path=/var/www/html;
'
volumes:
- wordpress:/var/www/html
volumes:
db:
wordpress:

View File

@ -599,7 +599,7 @@ class WC_Admin_Setup_Wizard {
</article>
<footer>
<div class="inner">
<button class="button button-primary button-large" id="wc_tracker_submit" aria-label="<?php esc_attr_e( 'Continue', 'woocommerce' ); ?>"><?php esc_html_e( 'Continue', 'woocommerce' ); ?></a>
<button class="button button-primary button-large" id="wc_tracker_submit" aria-label="<?php esc_attr_e( 'Continue', 'woocommerce' ); ?>"><?php esc_html_e( 'Continue', 'woocommerce' ); ?></button>
</div>
</footer>
</section>

View File

@ -1592,7 +1592,7 @@ class WC_Helper {
/**
* Flush subscriptions cache.
*/
private static function _flush_subscriptions_cache() {
public static function _flush_subscriptions_cache() {
delete_transient( '_woocommerce_helper_subscriptions' );
}

View File

@ -18,6 +18,7 @@ if ( ! $tab_exists ) {
}
?>
<div class="wrap woocommerce">
<?php do_action( 'woocommerce_before_settings_' . $current_tab ); ?>
<form method="<?php echo esc_attr( apply_filters( 'woocommerce_settings_form_method_tab_' . $current_tab, 'post' ) ); ?>" id="mainform" action="" enctype="multipart/form-data">
<nav class="nav-tab-wrapper woo-nav-tab-wrapper">
<?php
@ -46,4 +47,5 @@ if ( ! $tab_exists ) {
<?php wp_nonce_field( 'woocommerce-settings' ); ?>
</p>
</form>
<?php do_action( 'woocommerce_after_settings_' . $current_tab ); ?>
</div>

View File

@ -637,7 +637,7 @@ class WC_Cart extends WC_Legacy_Cart {
*/
public function empty_cart( $clear_persistent_cart = true ) {
do_action( 'woocommerce_before_cart_emptied' );
do_action( 'woocommerce_before_cart_emptied', $clear_persistent_cart );
$this->cart_contents = array();
$this->removed_cart_contents = array();
@ -653,7 +653,7 @@ class WC_Cart extends WC_Legacy_Cart {
$this->fees_api->remove_all_fees();
do_action( 'woocommerce_cart_emptied' );
do_action( 'woocommerce_cart_emptied', $clear_persistent_cart );
}
/**

View File

@ -366,6 +366,17 @@ class WC_Countries {
return apply_filters( 'woocommerce_european_union_countries', $countries, $type );
}
/**
* Gets an array of Non-EU countries that use VAT as the Local name for their taxes based on this list - https://en.wikipedia.org/wiki/Value-added_tax#Non-European_Union_countries
*
* @return string[]
*/
public function countries_using_vat() {
$countries = array( 'AL', 'AR', 'AZ', 'BS', 'BH', 'BY', 'BB', 'BO', 'EG', 'ET', 'CL', 'CO', 'EC', 'SV', 'FJ', 'GM', 'GH', 'GT', 'IN', 'IR', 'IL', 'KZ', 'MU', 'MK', 'MX', 'MD', 'MN', 'ME', 'NA', 'NP', 'NG', 'PS', 'PY', 'RU', 'RW', 'KN', 'SA', 'RS', 'ZA', 'KR', 'LK', 'TH', 'TR', 'UA', 'UY', 'UZ', 'VE', 'VN', 'AE' );
return apply_filters( 'woocommerce_countries_using_vat', $countries );
}
/**
* Gets the correct string for shipping - either 'to the' or 'to'.
*
@ -400,7 +411,7 @@ class WC_Countries {
* @return string
*/
public function tax_or_vat() {
$return = in_array( $this->get_base_country(), array_merge( $this->get_european_union_countries( 'eu_vat' ), array( 'NO' ) ), true ) ? __( 'VAT', 'woocommerce' ) : __( 'Tax', 'woocommerce' );
$return = in_array( $this->get_base_country(), array_merge( $this->get_european_union_countries( 'eu_vat' ), array( 'NO' ), $this->countries_using_vat() ), true ) ? __( 'VAT', 'woocommerce' ) : __( 'Tax', 'woocommerce' );
return apply_filters( 'woocommerce_countries_tax_or_vat', $return );
}
@ -411,7 +422,7 @@ class WC_Countries {
* @return string
*/
public function inc_tax_or_vat() {
$return = in_array( $this->get_base_country(), array_merge( $this->get_european_union_countries( 'eu_vat' ), array( 'NO' ) ), true ) ? __( '(incl. VAT)', 'woocommerce' ) : __( '(incl. tax)', 'woocommerce' );
$return = in_array( $this->get_base_country(), array_merge( $this->get_european_union_countries( 'eu_vat' ), array( 'NO' ), $this->countries_using_vat() ), true ) ? __( '(incl. VAT)', 'woocommerce' ) : __( '(incl. tax)', 'woocommerce' );
return apply_filters( 'woocommerce_countries_inc_tax_or_vat', $return );
}
@ -422,7 +433,7 @@ class WC_Countries {
* @return string
*/
public function ex_tax_or_vat() {
$return = in_array( $this->get_base_country(), array_merge( $this->get_european_union_countries( 'eu_vat' ), array( 'NO' ) ), true ) ? __( '(ex. VAT)', 'woocommerce' ) : __( '(ex. tax)', 'woocommerce' );
$return = in_array( $this->get_base_country(), array_merge( $this->get_european_union_countries( 'eu_vat' ), array( 'NO' ), $this->countries_using_vat() ), true ) ? __( '(ex. VAT)', 'woocommerce' ) : __( '(ex. tax)', 'woocommerce' );
return apply_filters( 'woocommerce_countries_ex_tax_or_vat', $return );
}

View File

@ -428,7 +428,7 @@ class WC_Emails {
* @param string $email Email address.
*/
public function order_downloads( $order, $sent_to_admin = false, $plain_text = false, $email = '' ) {
$show_downloads = $order->has_downloadable_item() && $order->is_download_permitted() && ! $sent_to_admin;
$show_downloads = $order->has_downloadable_item() && $order->is_download_permitted() && ! $sent_to_admin && ! is_a( $email, 'WC_Email_Customer_Refunded_Order' );
if ( ! $show_downloads ) {
return;

View File

@ -247,7 +247,7 @@ class WC_Frontend_Scripts {
),
'wc-country-select' => array(
'src' => self::get_asset_url( 'assets/js/frontend/country-select' . $suffix . '.js' ),
'deps' => array( 'jquery', 'selectWoo' ),
'deps' => array( 'jquery' ),
'version' => WC_VERSION,
),
'wc-credit-card-form' => array(

View File

@ -366,15 +366,18 @@ class WC_Order extends WC_Abstract_Order {
/* translators: 1: old order status 2: new order status */
$transition_note = sprintf( __( 'Order status changed from %1$s to %2$s.', 'woocommerce' ), wc_get_order_status_name( $status_transition['from'] ), wc_get_order_status_name( $status_transition['to'] ) );
// Note the transition occurred.
$this->add_status_transition_note( $transition_note, $status_transition );
do_action( 'woocommerce_order_status_' . $status_transition['from'] . '_to_' . $status_transition['to'], $this->get_id(), $this );
do_action( 'woocommerce_order_status_changed', $this->get_id(), $status_transition['from'], $status_transition['to'], $this );
} else {
/* translators: %s: new order status */
$transition_note = sprintf( __( 'Order status set to %s.', 'woocommerce' ), wc_get_order_status_name( $status_transition['to'] ) );
}
// Note the transition occurred.
$this->add_order_note( trim( $status_transition['note'] . ' ' . $transition_note ), 0, $status_transition['manual'] );
// Note the transition occurred.
$this->add_status_transition_note( $transition_note, $status_transition );
}
} catch ( Exception $e ) {
$logger = wc_get_logger();
$logger->error(
@ -883,10 +886,11 @@ class WC_Order extends WC_Abstract_Order {
* Filter orders formatterd billing address.
*
* @since 3.8.0
* @param string $address Formatted billing address string.
* @param array $raw_address Raw billing address.
* @param string $address Formatted billing address string.
* @param array $raw_address Raw billing address.
* @param WC_Order $order Order data. @since 3.9.0
*/
return apply_filters( 'woocommerce_order_get_formatted_billing_address', $address ? $address : $empty_content, $raw_address );
return apply_filters( 'woocommerce_order_get_formatted_billing_address', $address ? $address : $empty_content, $raw_address, $this );
}
/**
@ -908,10 +912,11 @@ class WC_Order extends WC_Abstract_Order {
* Filter orders formatterd shipping address.
*
* @since 3.8.0
* @param string $address Formatted shipping address string.
* @param array $raw_address Raw shipping address.
* @param string $address Formatted billing address string.
* @param array $raw_address Raw billing address.
* @param WC_Order $order Order data. @since 3.9.0
*/
return apply_filters( 'woocommerce_order_get_formatted_shipping_address', $address ? $address : $empty_content, $raw_address );
return apply_filters( 'woocommerce_order_get_formatted_shipping_address', $address ? $address : $empty_content, $raw_address, $this );
}
/**
@ -1705,6 +1710,19 @@ class WC_Order extends WC_Abstract_Order {
return $comment_id;
}
/**
* Add an order note for status transition
*
* @since 3.9.0
* @uses WC_Order::add_order_note()
* @param string $note Note to be added giving status transition from and to details.
* @param bool $transition Details of the status transition.
* @return int Comment ID.
*/
private function add_status_transition_note( $note, $transition ) {
return $this->add_order_note( trim( $transition['note'] . ' ' . $note ), 0, $transition['manual'] );
}
/**
* List order notes (public) for the customer.
*

View File

@ -8,9 +8,7 @@
* @version 3.1.0
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
defined( 'ABSPATH' ) || exit;
/**
* Include dependencies.

View File

@ -72,6 +72,7 @@ class WC_Gateway_Paypal extends WC_Payment_Gateway {
add_action( 'woocommerce_update_options_payment_gateways_' . $this->id, array( $this, 'process_admin_options' ) );
add_action( 'woocommerce_order_status_processing', array( $this, 'capture_payment' ) );
add_action( 'woocommerce_order_status_completed', array( $this, 'capture_payment' ) );
add_filter( 'woocommerce_thankyou_order_received_text', array( $this, 'order_received_text' ), 10, 2 );
if ( ! $this->is_valid_for_use() ) {
$this->enabled = 'no';
@ -380,17 +381,17 @@ class WC_Gateway_Paypal extends WC_Payment_Gateway {
$this->log( 'Refund Result: ' . wc_print_r( $result, true ) );
switch ( strtolower( $result->ACK ) ) { // phpcs:ignore WordPress.NamingConventions.ValidVariableName.NotSnakeCaseMemberVar
switch ( strtolower( $result->ACK ) ) { // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
case 'success':
case 'successwithwarning':
$order->add_order_note(
/* translators: 1: Refund amount, 2: Refund ID */
sprintf( __( 'Refunded %1$s - Refund ID: %2$s', 'woocommerce' ), $result->GROSSREFUNDAMT, $result->REFUNDTRANSACTIONID ) // phpcs:ignore WordPress.NamingConventions.ValidVariableName.NotSnakeCaseMemberVar
sprintf( __( 'Refunded %1$s - Refund ID: %2$s', 'woocommerce' ), $result->GROSSREFUNDAMT, $result->REFUNDTRANSACTIONID ) // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
);
return true;
}
return isset( $result->L_LONGMESSAGE0 ) ? new WP_Error( 'error', $result->L_LONGMESSAGE0 ) : false; // phpcs:ignore WordPress.NamingConventions.ValidVariableName.NotSnakeCaseMemberVar
return isset( $result->L_LONGMESSAGE0 ) ? new WP_Error( 'error', $result->L_LONGMESSAGE0 ) : false; // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
}
/**
@ -414,7 +415,7 @@ class WC_Gateway_Paypal extends WC_Payment_Gateway {
$this->log( 'Capture Result: ' . wc_print_r( $result, true ) );
// phpcs:disable WordPress.NamingConventions.ValidVariableName.NotSnakeCaseMemberVar
// phpcs:disable WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase
if ( ! empty( $result->PAYMENTSTATUS ) ) {
switch ( $result->PAYMENTSTATUS ) {
case 'Completed':
@ -450,4 +451,20 @@ class WC_Gateway_Paypal extends WC_Payment_Gateway {
wp_enqueue_script( 'woocommerce_paypal_admin', WC()->plugin_url() . '/includes/gateways/paypal/assets/js/paypal-admin' . $suffix . '.js', array(), WC_VERSION, true );
}
/**
* Custom PayPal order received text.
*
* @since 3.9.0
* @param string $text Default text.
* @param WC_Order $order Order data.
* @return string
*/
public function order_received_text( $text, $order ) {
if ( $this->id === $order->get_payment_method() ) {
return esc_html__( 'Thank you for your payment. Your transaction has been completed, and a receipt for your purchase has been emailed to you. Log into your PayPal account to view transaction details.', 'woocommerce' );
}
return $text;
}
}

View File

@ -420,7 +420,7 @@ abstract class WP_Background_Process extends WP_Async_Request {
// Adds every 5 minutes to the existing schedules.
$schedules[ $this->identifier . '_cron_interval' ] = array(
'interval' => MINUTE_IN_SECONDS * $interval,
'display' => sprintf( __( 'Every %d Minutes' ), $interval ),
'display' => sprintf( __( 'Every %d minutes', 'woocommerce' ), $interval ),
);
return $schedules;

View File

@ -43,6 +43,11 @@ class WC_Tracks_Client {
* @return void
*/
public static function maybe_set_identity_cookie() {
// Do not set on AJAX requests.
if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
return;
}
// Bail if cookie already set.
if ( isset( $_COOKIE['tk_ai'] ) ) {
return;

View File

@ -27,9 +27,9 @@ class WC_Orders_Tracking {
* Send a Tracks event when the Orders page is viewed.
*/
public function track_orders_view() {
if ( isset( $_GET['post_type'] ) && 'shop_order' === wp_unslash( $_GET['post_type'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.NoNonceVerification, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
if ( isset( $_GET['post_type'] ) && 'shop_order' === wp_unslash( $_GET['post_type'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
// phpcs:disable WordPress.Security.NonceVerification.NoNonceVerification, WordPress.Security.ValidatedSanitizedInput
// phpcs:disable WordPress.Security.NonceVerification, WordPress.Security.ValidatedSanitizedInput
$properties = array(
'status' => isset( $_GET['post_status'] ) ? sanitize_text_field( $_GET['post_status'] ) : 'all',
);
@ -77,8 +77,8 @@ class WC_Orders_Tracking {
}
$order = wc_get_order( $id );
$date_created = $order->get_date_created()->date( 'Y-m-d H:i:s' );
// phpcs:disable WordPress.Security.NonceVerification.NoNonceVerification
$date_created = $order->get_date_created() ? $order->get_date_created()->date( 'Y-m-d H:i:s' ) : '';
// phpcs:disable WordPress.Security.NonceVerification
$new_date = sprintf(
'%s %2d:%2d:%2d',
isset( $_POST['order_date'] ) ? wc_clean( wp_unslash( $_POST['order_date'] ) ) : '',
@ -104,7 +104,7 @@ class WC_Orders_Tracking {
* @param int $order_id Order ID.
*/
public function track_order_action( $order_id ) {
// phpcs:disable WordPress.Security.NonceVerification.NoNonceVerification
// phpcs:disable WordPress.Security.NonceVerification
if ( ! empty( $_POST['wc_order_action'] ) ) {
$order = wc_get_order( $order_id );
$action = wc_clean( wp_unslash( $_POST['wc_order_action'] ) );
@ -123,7 +123,7 @@ class WC_Orders_Tracking {
* Track "add order" button on the Edit Order screen.
*/
public function track_add_order_from_edit() {
// phpcs:ignore WordPress.Security.NonceVerification.NoNonceVerification, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
// phpcs:ignore WordPress.Security.NonceVerification, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
if ( isset( $_GET['post_type'] ) && 'shop_order' === wp_unslash( $_GET['post_type'] ) ) {
$referer = wp_get_referer();

View File

@ -151,6 +151,8 @@ function wc_get_account_menu_item_classes( $endpoint ) {
$current = true; // Dashboard is not an endpoint, so needs a custom check.
} elseif ( 'orders' === $endpoint && isset( $wp->query_vars['view-order'] ) ) {
$current = true; // When looking at individual order, highlight Orders list item (to signify where in the menu the user currently is).
} elseif ( 'payment-methods' === $endpoint && isset( $wp->query_vars['add-payment-method'] ) ) {
$current = true;
}
if ( $current ) {

View File

@ -1140,7 +1140,7 @@ function wc_get_customer_default_location() {
$allowed_country_codes = WC()->countries->get_allowed_countries();
if ( ! empty( $location['country'] ) && ! array_key_exists( $location['country'], $allowed_country_codes ) ) {
$location['country'] = current( $allowed_country_codes );
$location['country'] = current( array_keys( $allowed_country_codes ) );
$location['state'] = '';
}

View File

@ -1948,6 +1948,7 @@ if ( ! function_exists( 'woocommerce_upsell_display' ) ) {
array(
'posts_per_page' => $limit,
'orderby' => $orderby,
'order' => $order,
'columns' => $columns,
)
);
@ -1955,6 +1956,7 @@ if ( ! function_exists( 'woocommerce_upsell_display' ) ) {
wc_set_loop_prop( 'columns', apply_filters( 'woocommerce_upsells_columns', isset( $args['columns'] ) ? $args['columns'] : $columns ) );
$orderby = apply_filters( 'woocommerce_upsells_orderby', isset( $args['orderby'] ) ? $args['orderby'] : $orderby );
$order = apply_filters( 'woocommerce_upsells_order', isset( $args['order'] ) ? $args['order'] : $order );
$limit = apply_filters( 'woocommerce_upsells_total', isset( $args['posts_per_page'] ) ? $args['posts_per_page'] : $limit );
// Get visible upsells then sort them at random, then limit result set.

View File

@ -123,6 +123,10 @@ class WC_WCCOM_Site_Installer {
'products' => $products,
);
// Clear the cache of customer's subscription before asking for them.
// Thus, they will be re-fetched from WooCommerce.com after a purchase.
WC_Helper::_flush_subscriptions_cache();
WC()->queue()->cancel_all( 'woocommerce_wccom_install_products', $args );
WC()->queue()->add( 'woocommerce_wccom_install_products', $args );
@ -257,7 +261,7 @@ class WC_WCCOM_Site_Installer {
*
* @since 3.7.0
* @param int $product_id Product ID.
* @return bool|\WP_Error
* @return array|\WP_Error
*/
private static function get_product_info( $product_id ) {
$product_info = array(

2368
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -14,6 +14,7 @@
"build-watch": "grunt watch",
"lint:js": "eslint assets/js --ext=js",
"test:e2e": "./tests/bin/e2e-test-integration.js",
"test:e2e-dev": "./tests/bin/e2e-test-integration.js --dev",
"makepot": "grunt makepot",
"git:update-hooks": "rm -r .git/hooks && mkdir -p .git/hooks && node ./node_modules/husky/husky.js install"
},
@ -24,17 +25,16 @@
"@babel/preset-env": "^7.5.5",
"@babel/register": "^7.5.5",
"@wordpress/e2e-test-utils": "^2.2.0",
"autoprefixer": "9.6.5",
"autoprefixer": "9.7.2",
"babel-eslint": "10.0.3",
"chai": "4.2.0",
"chai-as-promised": "7.1.1",
"commander": "^3.0.0",
"config": "3.2.3",
"cross-env": "6.0.3",
"eslint": "6.5.1",
"eslint-config-wpcalypso": "4.0.1",
"eslint-plugin-jest": "22.13.6",
"eslint-plugin-wpcalypso": "4.1.0",
"config": "3.2.4",
"cross-env": "6.0.3",
"eslint": "6.7.0",
"eslint-config-wpcalypso": "5.0.0",
"github-contributors-list": "https://github.com/woocommerce/github-contributors-list/tarball/master",
"grunt": "1.0.4",
"grunt-checktextdomain": "1.0.1",
@ -51,18 +51,18 @@
"grunt-rtlcss": "2.0.1",
"grunt-sass": "3.1.0",
"grunt-shell": "3.0.1",
"grunt-stylelint": "0.11.1",
"grunt-stylelint": "0.12.0",
"grunt-wp-i18n": "1.0.3",
"husky": "3.0.9",
"husky": "3.1.0",
"istanbul": "1.0.0-alpha.2",
"jest": "24.8.0",
"jest-puppeteer": "4.3.0",
"lint-staged": "9.4.2",
"lint-staged": "9.4.3",
"mocha": "6.2.2",
"node-sass": "4.12.0",
"node-sass": "4.13.0",
"prettier": "github:automattic/calypso-prettier#c56b4251",
"puppeteer": "2.0.0",
"stylelint": "11.1.1",
"stylelint": "12.0.0",
"stylelint-config-wordpress": "15.0.0"
},
"engines": {
@ -92,5 +92,8 @@
"> 0.1%",
"ie 8",
"ie 9"
]
],
"dependencies": {
"@jest/test-sequencer": "^24.9.0"
}
}

View File

@ -4,7 +4,7 @@ Tags: ecommerce, e-commerce, store, sales, sell, shop, cart, checkout, downloada
Requires at least: 4.9
Tested up to: 5.2
Requires PHP: 5.6
Stable tag: 3.7.0
Stable tag: 3.8.0
License: GPLv3
License URI: https://www.gnu.org/licenses/gpl-3.0.html
@ -179,120 +179,7 @@ INTERESTED IN DEVELOPMENT?
== Changelog ==
= 3.8.0 - 2019-01-10 =
* Enhancement - Show error message in "My Account - view order" if order does not exist. #24435
* Enhancement - Add support to allow connect and install for in-app purchase flow. #24451
* Enhancement - Declared support to Unicode CLDR. #24564
* Enhancement - Declared support for PHP 7.4 in CI by removing PHP 7.4 from list of allowed failures. #24668
* Enhancement - Update the recommended PHP version to 7.0 and the recommended WP version to 5.0. #24730
* Enhancement - Change On Boarding Wizard to opt out of usage tracking by default. #24680
* Enhancement - Add `Available on backorder` message for products available on backorder. #24559
* Tweak - Hide Vietnam's state field. #24158
* Tweak - Better wording when no downloads are available on My Account > Downloads. #24172
* Tweak - Only consider orders created via checkout to hold stock. #24159
* Tweak - Stop to load order data twice in "View order" screen on "My Account" page. #24437
* Tweak - Prevent PHP warnings in tracker if order doesn't have a created date yet. #24441
* Tweak - Use `wc_get_checkout_url()` to get checkout URL. #24544
* Tweak - Maintain value of select fields in credit card form. #24720
* Tweak - Prevent filter per category while exporting product variations. #24517
* Tweak - Better wording for subtotal of items in cart and review order. #24440
* Tweak - Prevent new lines in product quantity in checkout details. #24311
* Tweak - Add a tooltip in the "Coupon expity date" field. #24749
* Tweak - CSS styling changes for WP 5.3. #24832
* Template - Moved HTML for displaying product price filter widget to a new template `product price filter widget`. #23384
* Accessibility - Make $subtext color darker. #24739
* Dev - Consistent register/login submit button css class names. Preserve old class names as well for backwards compatibility. #23701
* Dev - Added woocommerce_disable_password_change_notification filter, set to false by default. #24154
* Dev - Add filter `woocommerce_cart_item_removed_message` and `woocommerce_cart_product_cannot_be_purchased_message` to filter notice content when an item is removed from cart, or when a product cannot be purchased. #24176
* Dev - Prevent fatal error if trying to run `wc_load_cart()` before `before_woocommerce_init`. #24198
* Dev - Add woocommerce_get_zone_criteria filter hook to add custom criteria for matching zone ID. #24199
* Dev - Fire actions `woocommerce_before_delete_product_variation`, `woocommerce_delete_product_variation` and `woocommerce_trash_product_variation` appropriately when deleting or trashing product variations. #24239
* Dev - In filter `woocommerce_update_product_stock_query` use `$new_stock` instead of `$stock_quantity`. #24229
* Dev - Introduced woocommerce_prevent_adjust_line_item_product_stock filter. #24279
* Dev - Introduced `woocommerce_logout_default_redirect_url` filter to allow custom default URL. #24282
* Dev - Added woocommerce_sort_fees_callback filter. #24280
* Dev - Remove deprecated i18n/states directory. #24364
* Dev - Add filter `woocommerce_shipping_show_shipping_calculator` for showing shipping calculator. #24375
* Dev - Corrected UG states location. #24388
* Dev - Remove deprecations notices with PHP 7.4 by changing parameter order for `implode`. #24396
* Dev - Add ``$this` param to email filter for additional_content added in 3.7. #24399
* Dev - Add `woocommerce_product_import_process_item_data` filter to allow modifying a row before importing. #24412
* Dev - Fixed warnings when error_get_last() returns null. #24442
* Dev - Use brackets instead of braces to avoid deprecation notice in PHP 7.4. #24460
* Dev - Implement Puppeteer's architecture for running E2E tests locally. #24479
* Dev - Remove call to get_magic_quotes_runtime() as it is deprecated as of PHP 7.4. #24485
* Dev - Return value of `$mailer->send()` in `wc_mail` function. #24505
* Dev - Remove Selenium e2e tests & add Puppeteer new product e2e test. #24513
* Dev - Add a filter `woocommerce_ajax_add_order_item_validation` to allow validations in `add_order_item` function. #24518
* Dev - Use `wc_get_cart_url` instead of `wc_get_page_permalink( 'cart' )` because former has a filter `woocommerce_get_cart_url` to allow customization. #24530
* Dev - New `woocommerce_product_after_tabs` action hook added. #24694
* Dev - Enable append hashes on custom events (like ajax requests) #24665
* Dev - Introduced `woocommerce_order_get_formatted_billing_address` and `woocommerce_order_get_formatted_shipping_address` filters. #24677
* Dev - WC_Abstract_Order::recalculate_coupons() is public now. #24740
* Dev - Added 'applied_coupon' trigger to checkout.js. #24406
* Dev - Introduced woocommerce_output_cart_shortcode_content filter. #24738
* Dev - Add WPML compatibility to the geolocation URL. #24722
* Dev - Validate server requirements in WCCCOM Site Installer endpoints. #24690
* Dev - Introduced woocommerce_payment_token_class filter. #24542
* Dev - Add support for post type count to system status report. #24536
* Dev - Check for max discount to be -ve to prevent overwriting refunded fee amount. #24341
* Dev: Add filter woocommerce_european_union_countries to the method WC_Countries::get_european_union_countries(). #24741
* Dev - Allow WC_Product_Query sort products by include order. #24294
* Dev - Removed duplicated include of WC_Admin_Importers. #24751
* Dev - Refactor minimum requirement notice to use constant for easier changes in the future. #24830
* Fix - Clean products transients when term is removed. #23991
* Fix - Only add the image node to structured data if product has image. #24191
* Fix - Product attribute terms endpoint in legacy REST API v3 by converting `attribute_id` to int. #24203
* Fix - Prevent duplicate processing/stock reduction when IPN or PDT notifications are enabled by checking if order is not already in `processing` or `completed` status. #24249
* Fix - Position of ID section in mobile rows actions when displaying the list of products in the admin. #24277
* Fix - Endpoints URLs to follow slashes preferences from WordPress permalinks. #24283
* Fix - Ensure that postcode validation doesn't return any whitespace on beginning and end of a postcode. #24284
* Fix - Shipping classes sort under products alphabetically. #24291
* Fix - Shipping rates layout in admin settings. #24327
* Fix - Retain the focus on the select box when product variation is changed. #24339
* Fix - Prevent PHP notice in WC_Order_Item_Data_Store::get_order_item_type() when there is no entry in the database for the order item ID passed. #24353
* Fix - Add translation in class-wc-shipping.php for matching zone message. #24366
* Fix - Previewing email template. #24380
* Fix - Check if order exist before generate hash for downloads. #24384
* Fix - Ensure WC_Product_Data_Store_CPT::is_existing_sku() return boolean. #24385
* Fix - `function_exists` check for wc_hex_is_light(). #24391
* Fix - Removed Emoji from default additional email content due problem on some database that doesn't allow Emojis. #24450
* Fix - Fixed escaping on an attribute in `class-wc-admin-menus.php`. #24466
* Fix - Add translation for image alt attribute text in multiple places. #24467, #24468, #24469
* Fix - Uneven padding in the input field with class "location-input" in on boarding wizard. #24476
* Fix - Duplicate id in pages settings. #24478
* Fix - Use `esc_attr_e` instead of `esc_html_e` for escaping an attribute in multiple places. #24481, #24520, #24521, #24522, #24523, #24524
* Fix - Use `esc_attr__` instead of `esc_html__` in escaping attributes string. #24525, #24529
* Fix - Typo fix in payment captured error message. #24501
* Fix - Remove broken download link for downloadable product in refund emails. #24526
* Fix - Documentation URL in failed order email content. #24535
* Fix - "Add to cart" links in feed. #24545
* Fix - Escaping in Storefront banner. #24546
* Fix - Use `wp_kses_post` to allow certain html tags in extensions page. #24589
* Fix - Escape `$prefix` in item download links. #24601
* Fix - Use `esc_attr` instead of `esc_html` in multiple places for proper escaping. #24613, #24614
* Fix - Use `esc_html` instead of `esc_attr` in multiple places for proper escaping. #24616
* Fix - Use `esc_html_e` instead of `_e` in multiple places for proper escaping. #24615, #24618, #24630
* Fix - Use `esc_html__` instead of `esc_html` for proper escaping and some typo fixes. #24639
* Fix - Add no-store and remove max-age header (no longer needed). #24652
* Fix - Use correct meta value for `downloadable` column in product lookup table regenerate function. #24681
* Fix - Admin sub-menus becoming unaligned when scrolling down in the orders page when there are no orders. #24688
* Fix - OWB country and sell in person alignment. #24700
* Fix - Add selectWoo as dependency of country-select. #24347
* Fix - Domain replacement script now replaces both double and single quoted `woo-gutenberg-products-block` with `woocommerce` in both PHP and JavaScript files. #24696
* Fix - Convert `current_user_id` to string in some places to prevent unnecessary session database updates. #24016
* Fix - Fixed description of failed order emails. #24737
* Fix - Fixed Photoswipe styles. #24296
* Fix - Also consider refunded item when updating order and adjusting stocks. #24745
* Fix - Handle 0 attribute value for variations correctly. #24750
* Fix - Fixed spaces in form fields of External Products. #24295
* Localization - Add Zambia's Provinces to the list of states. #24307
* Localization - Adaptation of the order of last name and first name and addresses in Japan. #24336
* Localization - Fixed Namibian dollar symbol. #24438
* Localization - Change localization tag `action-scheduler` to `woocommerce`. #24474
* Localization - Fixed missing US Minor Outlying Islands states. #24826
* Security - Add an exit after the redirect when checking author archive capabilities for customers.
* Security - Ensure 404 pages with single product urls cannot be exploited using Open Redirect.
= 3.9.0 - 2020-01-xx =
[See changelog for all versions](https://raw.githubusercontent.com/woocommerce/woocommerce/master/CHANGELOG.txt).

View File

@ -1,11 +1,5 @@
#!/usr/bin/env bash
if [[ ${RUN_E2E} == 1 ]]; then
WP_SITE_URL="http://localhost:8080"
# Set base url to that of e2e test suite.
export BASE_URL="$WP_SITE_URL"
# Run the tests
npm test
npm install
npm run test:e2e
fi

View File

@ -1,3 +0,0 @@
{
"url": "BASE_URL"
}

View File

@ -1,3 +1,13 @@
{
"url": "https://example.com/"
"url": "http://localhost:8084/",
"users": {
"admin": {
"username": "admin",
"password": "password"
},
"customer": {
"username": "customer",
"password": "password"
}
}
}

View File

@ -0,0 +1,12 @@
const Sequencer = require('@jest/test-sequencer').default;
class CustomSequencer extends Sequencer {
sort(tests) {
// Test structure information
// https://github.com/facebook/jest/blob/6b8b1404a1d9254e7d5d90a8934087a9c9899dab/packages/jest-runner/src/types.ts#L17-L21
const copyTests = Array.from(tests);
return copyTests.sort((testA, testB) => (testA.path > testB.path ? 1 : -1));
}
}
module.exports = CustomSequencer;

View File

@ -28,4 +28,7 @@ module.exports = {
// The glob patterns Jest uses to detect test files
testMatch: [ '**/*.(test|spec).js' ],
// Sort test path alphabetically. This is needed so that `activate-and-setup` tests run first
testSequencer: '<rootDir>/tests/e2e-tests/config/jest-custom-sequencer.js',
};

View File

@ -0,0 +1 @@
{}

View File

@ -0,0 +1,197 @@
/**
* @format
*/
/**
* Internal dependencies
*/
import { StoreOwnerFlow } from '../../utils/flows';
import {
permalinkSettingsPageSaveChanges,
setCheckbox,
settingsPageSaveChanges,
verifyCheckboxIsSet,
verifyCheckboxIsUnset, verifyValueOfInputField
} from "../../utils";
describe( 'Store owner can login and make sure WooCommerce is activated', () => {
it( 'Can login', async () => {
await StoreOwnerFlow.login();
} );
it( 'Can make sure WooCommerce is activated. If not, activate it', async () => {
const slug = 'woocommerce';
await StoreOwnerFlow.openPlugins();
const disableLink = await page.$( `tr[data-slug="${ slug }"] .deactivate a` );
if ( disableLink ) {
return;
}
await page.click( `tr[data-slug="${ slug }"] .activate a` );
await page.waitForSelector( `tr[data-slug="${ slug }"] .deactivate a` );
});
} );
describe( 'Store owner can go through store Setup Wizard', () => {
it( 'Can start Setup Wizard', async () => {
await StoreOwnerFlow.runSetupWizard();
} );
it( 'Can fill out Store setup details', async () => {
// Fill out store address details
await expect( page ).toSelect( 'select[name="store_country"]', 'United States (US)' );
await expect( page ).toFill( '#store_address', 'addr 1' );
await expect( page ).toFill( '#store_address_2', 'addr 2' );
await expect( page ).toFill( '#store_city', 'San Francisco' );
await expect( page ).toSelect( 'select[name="store_state"]', 'California' );
await expect( page ).toFill( '#store_postcode', '94107' );
// Select currency and type of products to sell details
await expect( page ).toSelect( 'select[name="currency_code"]', '\n' +
'\t\t\t\t\t\tUnited States (US) dollar ($ USD)\t\t\t\t\t' );
await expect( page ).toSelect( 'select[name="product_type"]', 'I plan to sell both physical and digital products' );
// Verify that checkbox next to "I will also be selling products or services in person." is not selected
await verifyCheckboxIsUnset( '#woocommerce_sell_in_person' );
// Click on "Let's go!" button to move to the next step
await page.$eval( 'button[name=save_step]', elem => elem.click() );
// Wait for usage tracking pop-up window to appear
await page.waitForSelector( '#wc-backbone-modal-dialog' );
await expect( page ).toMatchElement( '.wc-backbone-modal-header', { text: 'Help improve WooCommerce with usage tracking' } );
await page.waitForSelector('#wc_tracker_checkbox_dialog');
// Verify that checkbox next to "Enable usage tracking and help improve WooCommerce" is not selected
await verifyCheckboxIsUnset('#wc_tracker_checkbox_dialog');
await Promise.all([
// Click on "Continue" button to move to the next step
page.$eval( '#wc_tracker_submit', elem => elem.click() ),
// Wait for the Payment section to load
page.waitForNavigation( { waitUntil: 'networkidle0' } ),
]);
} );
it( 'Can fill out Payment details', async () => {
// Turn off Stripe account toggle
await page.click( '.wc-wizard-service-toggle' );
// Click on "Continue" button to move to the next step
await page.click( 'button[name=save_step]', { text: 'Continue' } );
} );
it( 'Can fill out Shipping details', async () => {
// Turn off WooCommerce Shipping option
await page.$eval( '#wc_recommended_woocommerce_services', elem => elem.click() );
await page.waitForSelector( 'select[name="shipping_zones[domestic][method]"]' );
await page.waitForSelector( 'select[name="shipping_zones[intl][method]"]' );
// Select Free Shipping method for domestic shipping zone
await page.evaluate( () => {
document.querySelector( 'select[name="shipping_zones[domestic][method]"] > option:nth-child(2)' ).selected = true;
let element = document.querySelector( 'select[name="shipping_zones[domestic][method]"]' );
let event = new Event( 'change', { bubbles: true } );
event.simulated=true;
element.dispatchEvent( event );
} );
// Select Free Shipping method for the rest of the world shipping zone
await page.evaluate( () => {
document.querySelector( 'select[name="shipping_zones[intl][method]"] > option:nth-child(2)' ).selected = true;
let element = document.querySelector( 'select[name="shipping_zones[intl][method]"]' );
let event = new Event( 'change', { bubbles: true } );
event.simulated=true;
element.dispatchEvent( event );
});
// Select product weight and product dimensions options
await expect( page ).toSelect( 'select[name="weight_unit"]', 'Pounds' );
await expect( page ).toSelect( 'select[name="dimension_unit"]', 'Inches' );
// Click on "Continue" button to move to the next step
await page.click( 'button[name=save_step]', { text: 'Continue' } );
} );
it( 'Can fill out Recommended details', async () => {
// Turn off Storefront Theme option
await page.$eval( '#wc_recommended_storefront_theme', elem => elem.click() );
// Turn off Automated Taxes option
await page.$eval( '#wc_recommended_automated_taxes', elem => elem.click() );
// Turn off WooCommerce Admin option
await page.$eval( '#wc_recommended_wc_admin', elem => elem.click() );
// Turn off Mailchimp option
await page.$eval( '#wc_recommended_mailchimp', elem => elem.click() );
// Turn off Facebook option
await page.$eval( '#wc_recommended_facebook', elem => elem.click() );
// Click on "Continue" button to move to the next step
await page.click( 'button[name=save_step]', { text: 'Continue' } );
} );
it( 'Can skip Activate Jetpack section', async () => {
// Click on "Skip this step" in order to skip Jetpack installation
await page.click( '.wc-setup-footer-links' );
} );
it( 'Can finish Setup Wizard - Ready! section', async () => {
// Visit Dashboard
await StoreOwnerFlow.openDashboard();
} );
} );
describe( 'Store owner can finish initial store setup', () => {
it('Can enable tax rates and calculations', async () => {
// Go to general settings page
await StoreOwnerFlow.openSettings( 'general' );
// Make sure the general tab is active
await expect( page ).toMatchElement( 'a.nav-tab-active', { text: 'General' } );
// Enable tax rates and calculations
await setCheckbox( '#woocommerce_calc_taxes' );
await settingsPageSaveChanges();
// Verify that settings have been saved
await Promise.all( [
expect( page ).toMatchElement( '#message', { text: 'Your settings have been saved.' } ),
verifyCheckboxIsSet( '#woocommerce_calc_taxes' ),
] );
});
it('Can configure permalink settings', async () => {
// Go to Permalink Settings page
await StoreOwnerFlow.openPermalinkSettings();
// Select "Post name" option in common settings section
await page.click( 'input[value="/%postname%/"]', { text: ' Post name' } );
// Select "Custom base" in product permalinks section
await page.click( '#woocommerce_custom_selection' );
// Fill custom base slug to use
await expect( page ).toFill( '#woocommerce_permalink_structure', '/product/' );
await permalinkSettingsPageSaveChanges();
// Verify that settings have been saved
await Promise.all( [
expect( page ).toMatchElement( '#setting-error-settings_updated', { text: 'Permalink structure updated.' } ),
verifyValueOfInputField( '#permalink_structure', '/%postname%/' ),
verifyValueOfInputField( '#woocommerce_permalink_structure', '/product/' ),
] );
});
} );

View File

@ -0,0 +1,65 @@
/**
* @format
*/
/**
* Internal dependencies
*/
import { createSimpleProduct, createVariableProduct } from '../../utils/components';
import { CustomerFlow, StoreOwnerFlow } from '../../utils/flows';
import { uiUnblocked } from '../../utils';
let simplePostIdValue;
let variablePostIdValue;
describe( 'Single Product Page', () => {
beforeAll( async () => {
await StoreOwnerFlow.login();
simplePostIdValue = await createSimpleProduct();
await StoreOwnerFlow.logout();
} );
it( 'should be able to add simple products to the cart', async () => {
// Add 5 simple products to cart
await CustomerFlow.goToProduct( simplePostIdValue );
await expect( page ).toFill( 'div.quantity input.qty', '5' );
await CustomerFlow.addToCart();
await expect( page ).toMatchElement( '.woocommerce-message', { text: 'have been added to your cart.' } );
// Verify cart contents
await CustomerFlow.goToCart();
await CustomerFlow.productIsInCart( 'Simple product', 5 );
// Remove items from cart
await CustomerFlow.removeFromCart( 'Simple product' );
await uiUnblocked();
await expect( page ).toMatchElement( '.cart-empty', { text: 'Your cart is currently empty.' } );
} );
} );
describe( 'Variable Product Page', () => {
beforeAll( async () => {
await StoreOwnerFlow.login();
variablePostIdValue = await createVariableProduct();
await StoreOwnerFlow.logout();
} );
it( 'should be able to add variation products to the cart', async () => {
// Add a product with one set of variations to cart
await CustomerFlow.goToProduct( variablePostIdValue );
await expect( page ).toSelect( '#attr-1', 'val1' );
await expect( page ).toSelect( '#attr-2', 'val1' );
await expect( page ).toSelect( '#attr-3', 'val1' );
await CustomerFlow.addToCart();
await expect( page ).toMatchElement( '.woocommerce-message', { text: 'has been added to your cart.' } );
// Verify cart contents
await CustomerFlow.goToCart();
await CustomerFlow.productIsInCart( 'Variable Product with Three Variations' );
// Remove items from cart
await CustomerFlow.removeFromCart( 'Variable Product with Three Variations' );
await uiUnblocked();
await expect( page ).toMatchElement( '.cart-empty', { text: 'Your cart is currently empty.' } );
} );
} );

View File

@ -0,0 +1,41 @@
/**
* @format
*/
/**
* Internal dependencies
*/
import { StoreOwnerFlow } from '../../utils/flows';
import { clickTab, verifyPublishAndTrash } from '../../utils';
describe( 'Add New Coupon Page', () => {
beforeAll( async () => {
await StoreOwnerFlow.login();
} );
it( 'can create new coupon', async () => {
// Go to "add coupon" page
await StoreOwnerFlow.openNewCoupon();
// Make sure we're on the add coupon page
await expect( page.title() ).resolves.toMatch( 'Add new coupon' );
// Fill in coupon code and description
await expect( page ).toFill( '#title', 'code-' + new Date().getTime().toString() );
await expect( page ).toFill( '#woocommerce-coupon-description', 'test coupon' );
// Set general coupon data
await clickTab( 'General' );
await expect( page ).toSelect( '#discount_type', 'Fixed cart discount' );
await expect( page ).toFill( '#coupon_amount', '100' );
// Publish coupon, verify that it was published. Trash coupon, verify that it was trashed.
await verifyPublishAndTrash(
'#publish',
'#message',
'Coupon updated.',
'1 coupon moved to the Trash.'
);
} );
} );

View File

@ -0,0 +1,37 @@
/**
* @format
*/
/**
* Internal dependencies
*/
import { StoreOwnerFlow } from '../../utils/flows';
import { verifyPublishAndTrash } from '../../utils';
describe( 'Add New Order Page', () => {
beforeAll( async () => {
await StoreOwnerFlow.login();
} );
it( 'can create new order', async () => {
// Go to "add order" page
await StoreOwnerFlow.openNewOrder();
// Make sure we're on the add order page
await expect( page.title() ).resolves.toMatch( 'Add new order' );
// Set order data
await expect( page ).toSelect( '#order_status', 'Processing' );
await expect( page ).toFill( 'input[name=order_date]', '2018-12-13' );
await expect( page ).toFill( 'input[name=order_date_hour]', '18' );
await expect( page ).toFill( 'input[name=order_date_minute]', '55' );
// Create order, verify that it was created. Trash order, verify that it was trashed.
await verifyPublishAndTrash(
'.order_actions li .save_order',
'#message',
'Order updated.',
'1 order moved to the Trash.'
);
} );
} );

View File

@ -2,11 +2,6 @@
* @format
*/
/**
* External dependencies
*/
import { activatePlugin } from '@wordpress/e2e-test-utils';
/**
* Internal dependencies
*/
@ -35,7 +30,7 @@ const verifyPublishAndTrash = async () => {
describe( 'Add New Simple Product Page', () => {
beforeAll( async () => {
await activatePlugin( 'woocommerce' );
await StoreOwnerFlow.login();
} );
it( 'can create simple virtual product titled "Simple Product" with regular price $9.99', async () => {
@ -51,15 +46,21 @@ describe( 'Add New Simple Product Page', () => {
await clickTab( 'General' );
await expect( page ).toFill( '#_regular_price', '9.99' );
await verifyPublishAndTrash();
// Publish product, verify that it was published. Trash product, verify that it was trashed.
await verifyPublishAndTrash(
'#publish',
'.updated.notice',
'Product published.',
'Move to Trash',
'1 product moved to the Trash.'
);
} );
} );
describe( 'Add New Variable Product Page', () => {
beforeAll( async () => {
await activatePlugin( 'woocommerce' );
await StoreOwnerFlow.login();
} );
it( 'can create product with variations', async () => {
// Go to "add product" page
await StoreOwnerFlow.openNewProduct();
@ -179,6 +180,13 @@ describe( 'Add New Variable Product Page', () => {
await page.focus( 'button.save-variation-changes' );
await expect( page ).toClick( 'button.save-variation-changes', { text: 'Save changes' } );
await verifyPublishAndTrash();
// Publish product, verify that it was published. Trash product, verify that it was trashed.
await verifyPublishAndTrash(
'#publish',
'.updated.notice',
'Product published.',
'Move to Trash',
'1 product moved to the Trash.'
);
} );
} );

View File

@ -0,0 +1,71 @@
/**
* @format
*/
/**
* Internal dependencies
*/
import { StoreOwnerFlow } from '../../utils/flows';
import { settingsPageSaveChanges, verifyValueOfInputField } from '../../utils';
describe( 'WooCommerce General Settings', () => {
beforeAll( async () => {
await StoreOwnerFlow.login();
} );
it( 'can update settings', async () => {
// Go to general settings page
await StoreOwnerFlow.openSettings( 'general' );
// Make sure the general tab is active
await expect( page ).toMatchElement( 'a.nav-tab-active', { text: 'General' } );
// Set selling location to all countries first,
// so we can choose california as base location.
await expect( page ).toSelect( '#woocommerce_allowed_countries', 'Sell to all countries' );
await settingsPageSaveChanges();
// Verify that settings have been saved
await Promise.all( [
expect( page ).toMatchElement( '#message', { text: 'Your settings have been saved.' } ),
expect( page ).toMatchElement( '#woocommerce_allowed_countries', { text: 'Sell to all countries' } ),
] );
// Set base location with state CA.
await expect( page ).toSelect( 'select[name="woocommerce_default_country"]', 'United States (US) — California' );
await settingsPageSaveChanges();
// Verify that settings have been saved
await Promise.all( [
expect( page ).toMatchElement( '#message', { text: 'Your settings have been saved.' } ),
expect( page ).toMatchElement( 'select[name="woocommerce_default_country"]', { text: 'United States (US) — California' } ),
] );
// Set selling location to specific countries first, so we can choose U.S as base location (without state).
// This will makes specific countries option appears.
await expect( page ).toSelect( '#woocommerce_allowed_countries', 'Sell to specific countries' );
await expect( page ).toSelect( 'select[name="woocommerce_specific_allowed_countries[]"]', 'United States (US)' );
await settingsPageSaveChanges();
// Verify that settings have been saved
await Promise.all( [
expect( page ).toMatchElement( '#message', { text: 'Your settings have been saved.' } ),
expect( page ).toMatchElement( '#woocommerce_allowed_countries', { text: 'Sell to specific countries' } ),
expect( page ).toMatchElement( 'select[name="woocommerce_specific_allowed_countries[]"]', { text: 'United States (US)' } ),
] );
// Set currency options.
await expect( page ).toFill( '#woocommerce_price_thousand_sep', ',' );
await expect( page ).toFill( '#woocommerce_price_decimal_sep', '.' );
await expect( page ).toFill( '#woocommerce_price_num_decimals', '2' );
await settingsPageSaveChanges();
// Verify that settings have been saved
await Promise.all( [
expect( page ).toMatchElement( '#message', { text: 'Your settings have been saved.' } ),
verifyValueOfInputField( '#woocommerce_price_thousand_sep', ',' ),
verifyValueOfInputField( '#woocommerce_price_decimal_sep', '.' ),
verifyValueOfInputField( '#woocommerce_price_num_decimals', '2' ),
] );
} );
} );

View File

@ -0,0 +1,50 @@
/**
* @format
*/
/**
* Internal dependencies
*/
import { StoreOwnerFlow } from '../../utils/flows';
import { setCheckbox, settingsPageSaveChanges, unsetCheckbox, verifyCheckboxIsSet, verifyCheckboxIsUnset } from '../../utils';
describe( 'WooCommerce Products > Downloadable Products Settings', () => {
beforeAll( async () => {
await StoreOwnerFlow.login();
} );
it( 'can update settings', async () => {
// Go to downloadable products settings page
await StoreOwnerFlow.openSettings( 'products', 'downloadable' );
// Make sure the product tab is active
await expect( page ).toMatchElement( 'a.nav-tab-active', { text: 'Products' } );
await expect( page ).toMatchElement( 'ul.subsubsub > li > a.current', { text: 'Downloadable products' } );
await expect( page ).toSelect( '#woocommerce_file_download_method', 'Redirect only' );
await setCheckbox( '#woocommerce_downloads_require_login' );
await setCheckbox( '#woocommerce_downloads_grant_access_after_payment' );
await settingsPageSaveChanges();
// Verify that settings have been saved
await Promise.all( [
expect( page ).toMatchElement( '#message', { text: 'Your settings have been saved.' } ),
expect( page ).toMatchElement( '#woocommerce_file_download_method', { text: 'Redirect only' } ),
verifyCheckboxIsSet( '#woocommerce_downloads_require_login' ),
verifyCheckboxIsSet( '#woocommerce_downloads_grant_access_after_payment' ),
] );
await expect( page ).toSelect( '#woocommerce_file_download_method', 'Force downloads' );
await unsetCheckbox( '#woocommerce_downloads_require_login' );
await unsetCheckbox( '#woocommerce_downloads_grant_access_after_payment' );
await settingsPageSaveChanges();
// Verify that settings have been saved
await Promise.all( [
expect( page ).toMatchElement( '#message', { text: 'Your settings have been saved.' } ),
expect( page ).toMatchElement( '#woocommerce_file_download_method', { text: 'Force downloads' } ),
verifyCheckboxIsUnset( '#woocommerce_downloads_require_login' ),
verifyCheckboxIsUnset( '#woocommerce_downloads_grant_access_after_payment' ),
] );
} );
} );

View File

@ -0,0 +1,170 @@
/**
* @format
*/
/**
* Internal dependencies
*/
import { StoreOwnerFlow } from '../../utils/flows';
import {
clearAndFillInput,
setCheckbox,
settingsPageSaveChanges,
uiUnblocked,
verifyCheckboxIsSet,
verifyValueOfInputField
} from '../../utils';
describe( 'WooCommerce Tax Settings', () => {
beforeAll( async () => {
await StoreOwnerFlow.login();
} );
it( 'can enable tax calculation', async() => {
// Go to general settings page
await StoreOwnerFlow.openSettings( 'general' );
// Make sure the general tab is active
await expect( page ).toMatchElement( 'a.nav-tab-active', { text: 'General' } );
// Enable tax calculation
await setCheckbox( 'input[name="woocommerce_calc_taxes"]' );
await settingsPageSaveChanges();
// Verify that settings have been saved
await Promise.all( [
expect( page ).toMatchElement( '#message', { text: 'Your settings have been saved.' } ),
verifyCheckboxIsSet( '#woocommerce_calc_taxes' ),
] );
// Verify that tax settings are now present
await expect( page ).toMatchElement( 'a.nav-tab', { text: 'Tax' } );
} );
it( 'can set tax options', async () => {
// Go to tax settings page
await StoreOwnerFlow.openSettings( 'tax' );
// Make sure the tax tab is active
await expect( page ).toMatchElement( 'a.nav-tab-active', { text: 'Tax' } );
// Prices exclusive of tax
await expect( page ).toClick( 'input[name="woocommerce_prices_include_tax"][value="no"]' );
// Tax based on customer shipping address
await expect( page ).toSelect( '#woocommerce_tax_based_on', 'Customer shipping address' );
// Standard tax class for shipping
await expect( page ).toSelect( '#woocommerce_shipping_tax_class', 'Standard' );
// Leave rounding unchecked (no-op)
// Display prices excluding tax
await expect( page ).toSelect( '#woocommerce_tax_display_shop', 'Excluding tax' );
// Display prices including tax in cart and at checkout
await expect( page ).toSelect( '#woocommerce_tax_display_cart', 'Including tax' );
// Display a single tax total
await expect( page ).toSelect( '#woocommerce_tax_total_display', 'As a single total' );
await settingsPageSaveChanges();
// Verify that settings have been saved
await Promise.all( [
expect( page ).toMatchElement( '#message', { text: 'Your settings have been saved.' } ),
verifyValueOfInputField( 'input[name="woocommerce_prices_include_tax"][value="no"]', 'no' ),
expect( page ).toMatchElement( '#woocommerce_tax_based_on', { text: 'Customer shipping address' } ),
expect( page ).toMatchElement( '#woocommerce_shipping_tax_class', { text: 'Standard' } ),
expect( page ).toMatchElement( '#woocommerce_tax_display_shop', { text: 'Excluding tax' } ),
expect( page ).toMatchElement( '#woocommerce_tax_display_cart', { text: 'Including tax' } ),
expect( page ).toMatchElement( '#woocommerce_tax_total_display', { text: 'As a single total' } ),
] );
} );
it( 'can add tax classes', async () => {
// Go to tax settings page
await StoreOwnerFlow.openSettings( 'tax' );
// Make sure the tax tab is active
await expect( page ).toMatchElement( 'a.nav-tab-active', { text: 'Tax' } );
// Remove additional tax classes
await clearAndFillInput( '#woocommerce_tax_classes', '' );
await settingsPageSaveChanges();
// Verify that settings have been saved
await Promise.all( [
expect( page ).toMatchElement( '#message', { text: 'Your settings have been saved.' } ),
expect( page ).toMatchElement( '#woocommerce_tax_classes', { text: '' } ),
] );
// Add a "fancy" tax class
await clearAndFillInput( '#woocommerce_tax_classes', 'Fancy' );
await settingsPageSaveChanges();
// Verify that settings have been saved
await Promise.all( [
expect( page ).toMatchElement( '#message', { text: 'Your settings have been saved.' } ),
expect( page ).toMatchElement( 'ul.subsubsub > li > a', { text: 'Fancy rates' } ),
] );
} );
it( 'can set rate settings', async () => {
// Go to "fancy" rates tax settings page
await StoreOwnerFlow.openSettings( 'tax', 'fancy' );
// Make sure the tax tab is active, with the "fancy" subsection
await expect( page ).toMatchElement( 'a.nav-tab-active', { text: 'Tax' } );
await expect( page ).toMatchElement( 'ul.subsubsub > li > a.current', { text: 'Fancy rates' } );
// Create a state tax
await expect( page ).toClick( '.wc_tax_rates a.insert' );
await expect( page ).toFill( 'input[name^="tax_rate_country[new-0"]', 'US' );
await expect( page ).toFill( 'input[name^="tax_rate_state[new-0"]', 'CA' );
await expect( page ).toFill( 'input[name^="tax_rate[new-0"]', '7.5' );
await expect( page ).toFill( 'input[name^="tax_rate_name[new-0"]', 'CA State Tax' );
// Create a federal tax
await expect( page ).toClick( '.wc_tax_rates a.insert' );
await expect( page ).toFill( 'input[name^="tax_rate_country[new-1"]', 'US' );
await expect( page ).toFill( 'input[name^="tax_rate[new-1"]', '1.5' );
await expect( page ).toFill( 'input[name^="tax_rate_priority[new-1"]', '2' );
await expect( page ).toFill( 'input[name^="tax_rate_name[new-1"]', 'Federal Tax' );
await expect( page ).toClick( 'input[name^="tax_rate_shipping[new-1"]' );
// Save changes (AJAX here)
await expect( page ).toClick( 'button.woocommerce-save-button' );
await uiUnblocked();
// Verify 2 tax rates
expect( await page.$$( '#rates tr' ) ).toHaveLength( 2 );
// Delete federal rate
await expect( page ).toClick( '#rates tr:nth-child(2) input' );
await expect( page ).toClick( '.wc_tax_rates a.remove_tax_rates' );
// Save changes (AJAX here)
await expect( page ).toClick( 'button.woocommerce-save-button' );
await uiUnblocked();
// Verify 1 rate
expect( await page.$$( '#rates tr' ) ).toHaveLength( 1 );
await expect( page ).toMatchElement(
'#rates tr:first-of-type input[name^="tax_rate_state"][value="CA"]'
);
} );
it( 'can remove tax classes', async () => {
// Go to tax settings page
await StoreOwnerFlow.openSettings( 'tax' );
// Make sure the tax tab is active
await expect( page ).toMatchElement( 'a.nav-tab-active', { text: 'Tax' } );
// Remove "fancy" tax class
await clearAndFillInput( '#woocommerce_tax_classes', ' ' );
await settingsPageSaveChanges();
// Verify that settings have been saved
await Promise.all( [
expect( page ).toMatchElement( '#message', { text: 'Your settings have been saved.' } ),
expect( page ).not.toMatchElement( 'ul.subsubsub > li > a', { text: 'Fancy rates' } ),
] );
await page.waitFor( 10000 );
} );
} );

View File

@ -0,0 +1,176 @@
/**
* @format
*/
/**
* Internal dependencies
*/
import { StoreOwnerFlow } from "./flows";
import { clickTab, uiUnblocked } from "./index";
const verifyAndPublish = async () => {
// Wait for auto save
await page.waitFor( 2000 );
// Publish product
await expect( page ).toClick( '#publish' );
await page.waitForSelector( '.updated.notice' );
// Verify
await expect( page ).toMatchElement( '.updated.notice', { text: 'Product published.' } );
};
/**
* Create simple product.
*/
const createSimpleProduct = async () => {
// Go to "add product" page
await StoreOwnerFlow.openNewProduct();
// Make sure we're on the add order page
await expect( page.title() ).resolves.toMatch( 'Add new product' );
// Set product data
await expect( page ).toFill( '#title', 'Simple product' );
await expect( page ).toClick( '#_virtual' );
await clickTab( 'General' );
await expect( page ).toFill( '#_regular_price', '9.99' );
await verifyAndPublish();
const simplePostId = await page.$( '#post_ID' );
let simplePostIdValue = ( await ( await simplePostId.getProperty( 'value' ) ).jsonValue() );
return simplePostIdValue;
} ;
/**
* Create variable product.
*/
const createVariableProduct = async () => {
// Go to "add product" page
await StoreOwnerFlow.openNewProduct();
// Make sure we're on the add order page
await expect( page.title() ).resolves.toMatch( 'Add new product' );
// Set product data
await expect( page ).toFill( '#title', 'Variable Product with Three Variations' );
await expect( page ).toSelect( '#product-type', 'Variable product' );
// Create attributes for variations
await clickTab( 'Attributes' );
await expect( page ).toSelect( 'select[name="attribute_taxonomy"]', 'Custom product attribute' );
for ( let i = 0; i < 3; i++ ) {
await expect( page ).toClick( 'button.add_attribute', { text: 'Add' } );
// Wait for attribute form to load
await uiUnblocked();
await page.focus( `input[name="attribute_names[${ i }]"]` );
await expect( page ).toFill( `input[name="attribute_names[${ i }]"]`, 'attr #' + ( i + 1 ) );
await expect( page ).toFill( `textarea[name="attribute_values[${ i }]"]`, 'val1 | val2' );
await expect( page ).toClick( `input[name="attribute_variation[${ i }]"]` );
}
await expect( page ).toClick( 'button', { text: 'Save attributes' } );
// Wait for attribute form to save (triggers 2 UI blocks)
await uiUnblocked();
await uiUnblocked();
// Create variations from attributes
await clickTab( 'Variations' );
await page.waitForSelector( 'select.variation_actions:not([disabled])' );
await page.focus( 'select.variation_actions' );
await expect( page ).toSelect( 'select.variation_actions', 'Create variations from all attributes' );
const firstDialog = await expect( page ).toDisplayDialog( async () => {
// Using this technique since toClick() isn't working.
// See: https://github.com/GoogleChrome/puppeteer/issues/1805#issuecomment-464802876
page.$eval( 'a.do_variation_action', elem => elem.click() );
} );
expect( firstDialog.message() ).toMatch( 'Are you sure you want to link all variations?' );
const secondDialog = await expect( page ).toDisplayDialog( async () => {
await firstDialog.accept();
} );
expect( secondDialog.message() ).toMatch( '8 variations added' );
await secondDialog.dismiss();
// Set some variation data
await uiUnblocked();
await uiUnblocked();
await page.waitForSelector( '.woocommerce_variation .handlediv' );
// Verify that variations were created
await Promise.all( [
expect( page ).toMatchElement( 'select[name="attribute_attr-1[0]"]', { text: 'val1' } ),
expect( page ).toMatchElement( 'select[name="attribute_attr-2[0]"]', { text: 'val1' } ),
expect( page ).toMatchElement( 'select[name="attribute_attr-3[0]"]', { text: 'val1' } ),
expect( page ).toMatchElement( 'select[name="attribute_attr-1[1]"]', { text: 'val1' } ),
expect( page ).toMatchElement( 'select[name="attribute_attr-2[1]"]', { text: 'val1' } ),
expect( page ).toMatchElement( 'select[name="attribute_attr-3[1]"]', { text: 'val2' } ),
expect( page ).toMatchElement( 'select[name="attribute_attr-1[2]"]', { text: 'val1' } ),
expect( page ).toMatchElement( 'select[name="attribute_attr-2[2]"]', { text: 'val2' } ),
expect( page ).toMatchElement( 'select[name="attribute_attr-3[2]"]', { text: 'val1' } ),
expect( page ).toMatchElement( 'select[name="attribute_attr-1[3]"]', { text: 'val1' } ),
expect( page ).toMatchElement( 'select[name="attribute_attr-2[3]"]', { text: 'val2' } ),
expect( page ).toMatchElement( 'select[name="attribute_attr-3[3]"]', { text: 'val2' } ),
expect( page ).toMatchElement( 'select[name="attribute_attr-1[4]"]', { text: 'val2' } ),
expect( page ).toMatchElement( 'select[name="attribute_attr-2[4]"]', { text: 'val1' } ),
expect( page ).toMatchElement( 'select[name="attribute_attr-3[4]"]', { text: 'val1' } ),
expect( page ).toMatchElement( 'select[name="attribute_attr-1[5]"]', { text: 'val2' } ),
expect( page ).toMatchElement( 'select[name="attribute_attr-2[5]"]', { text: 'val1' } ),
expect( page ).toMatchElement( 'select[name="attribute_attr-3[5]"]', { text: 'val2' } ),
expect( page ).toMatchElement( 'select[name="attribute_attr-1[6]"]', { text: 'val2' } ),
expect( page ).toMatchElement( 'select[name="attribute_attr-2[6]"]', { text: 'val2' } ),
expect( page ).toMatchElement( 'select[name="attribute_attr-3[6]"]', { text: 'val1' } ),
expect( page ).toMatchElement( 'select[name="attribute_attr-1[7]"]', { text: 'val2' } ),
expect( page ).toMatchElement( 'select[name="attribute_attr-2[7]"]', { text: 'val2' } ),
expect( page ).toMatchElement( 'select[name="attribute_attr-3[7]"]', { text: 'val2' } ),
] );
await expect( page ).toClick( '.woocommerce_variation:nth-of-type(2) .handlediv' );
await page.waitFor( 2000 );
await page.focus( 'input[name="variable_is_virtual[0]"]' );
await expect( page ).toClick( 'input[name="variable_is_virtual[0]"]' );
await expect( page ).toFill( 'input[name="variable_regular_price[0]"]', '9.99' );
await expect( page ).toClick( '.woocommerce_variation:nth-of-type(3) .handlediv' );
await page.waitFor( 2000 );
await page.focus( 'input[name="variable_is_virtual[1]"]' );
await expect( page ).toClick( 'input[name="variable_is_virtual[1]"]' );
await expect( page ).toFill( 'input[name="variable_regular_price[1]"]', '11.99' );
await expect( page ).toClick( '.woocommerce_variation:nth-of-type(4) .handlediv' );
await page.waitFor( 2000 );
await page.focus( 'input[name="variable_manage_stock[2]"]' );
await expect( page ).toClick( 'input[name="variable_manage_stock[2]"]' );
await expect( page ).toFill( 'input[name="variable_regular_price[2]"]', '20' );
await expect( page ).toFill( 'input[name="variable_weight[2]"]', '200' );
await expect( page ).toFill( 'input[name="variable_length[2]"]', '10' );
await expect( page ).toFill( 'input[name="variable_width[2]"]', '20' );
await expect( page ).toFill( 'input[name="variable_height[2]"]', '15' );
await page.focus( 'button.save-variation-changes' );
await expect( page ).toClick( 'button.save-variation-changes', { text: 'Save changes' } );
await verifyAndPublish();
const variablePostId = await page.$( '#post_ID' );
let variablePostIdValue = ( await ( await variablePostId.getProperty( 'value' ) ).jsonValue() );
return variablePostIdValue;
};
export { createSimpleProduct, createVariableProduct };

View File

@ -2,9 +2,97 @@
* @format
*/
const baseUrl = process.env.WP_BASE_URL;
/**
* Internal dependencies
*/
import { clearAndFillInput } from './index';
const WP_ADMIN_NEW_PRODUCT = baseUrl + '/wp-admin/post-new.php?post_type=product';
const config = require( 'config' );
const baseUrl = config.get( 'url' );
const WP_ADMIN_LOGIN = baseUrl + 'wp-login.php';
const WP_ADMIN_DASHBOARD = baseUrl + 'wp-admin';
const WP_ADMIN_PLUGINS = baseUrl + 'wp-admin/plugins.php';
const WP_ADMIN_SETUP_WIZARD = baseUrl + 'wp-admin/admin.php?page=wc-setup';
const WP_ADMIN_NEW_COUPON = baseUrl + 'wp-admin/post-new.php?post_type=shop_coupon';
const WP_ADMIN_NEW_ORDER = baseUrl + 'wp-admin/post-new.php?post_type=shop_order';
const WP_ADMIN_NEW_PRODUCT = baseUrl + 'wp-admin/post-new.php?post_type=product';
const WP_ADMIN_WC_SETTINGS = baseUrl + 'wp-admin/admin.php?page=wc-settings&tab=';
const WP_ADMIN_PERMALINK_SETTINGS = baseUrl + 'wp-admin/options-permalink.php';
const SHOP_PRODUCT = baseUrl + '?p=';
const SHOP_CART_PAGE = baseUrl + 'cart/';
const getProductColumnExpression = ( productTitle ) => (
'td[@class="product-name" and ' +
`a[contains(text(), "${ productTitle }")]` +
']'
);
const getQtyColumnExpression = ( args ) => (
'td[@class="product-quantity" and ' +
'.//' + getQtyInputExpression( args ) +
']'
);
const getQtyInputExpression = ( args = {} ) => {
let qtyValue = '';
if ( args.checkQty ) {
qtyValue = ` and @value="${ args.qty }"`;
}
return 'input[contains(@class, "input-text")' + qtyValue + ']';
};
const getCartItemExpression = ( productTitle, args ) => (
'//tr[contains(@class, "cart_item") and ' +
getProductColumnExpression( productTitle ) +
' and ' +
getQtyColumnExpression( args ) +
']'
);
const getRemoveExpression = () => (
'td[@class="product-remove"]//a[@class="remove"]'
);
const CustomerFlow = {
addToCart: async () => {
await Promise.all( [
page.waitForNavigation( { waitUntil: 'networkidle0' } ),
page.click( '.single_add_to_cart_button' ),
] );
},
removeFromCart: async ( productTitle ) => {
const cartItemXPath = getCartItemExpression( productTitle );
const removeItemXPath = cartItemXPath + '//' + getRemoveExpression();
const [ removeButton ] = await page.$x( removeItemXPath );
await removeButton.click();
},
goToProduct: async ( postID ) => {
await page.goto( SHOP_PRODUCT + postID, {
waitUntil: 'networkidle0',
} );
},
goToCart: async () => {
await page.goto( SHOP_CART_PAGE, {
waitUntil: 'networkidle0',
} );
},
productIsInCart: async ( productTitle, quantity = null ) => {
const cartItemArgs = quantity ? { qty: quantity } : {};
const cartItemXPath = getCartItemExpression( productTitle, cartItemArgs );
await expect( page.$x( cartItemXPath ) ).resolves.toHaveLength( 1 );
},
};
const MY_ACCOUNT_ORDERS = baseUrl + '/my-account/orders/';
const MY_ACCOUNT_DOWNLOADS = baseUrl + '/my-account/downloads/';
@ -38,11 +126,90 @@ const CustomerFlow = {
};
const StoreOwnerFlow = {
login: async () => {
await page.goto( WP_ADMIN_LOGIN, {
waitUntil: 'networkidle0',
} );
await expect( page.title() ).resolves.toMatch( 'Log In' );
await clearAndFillInput( '#user_login', ' ' );
await page.type( '#user_login', config.get( 'users.admin.username' ) );
await page.type( '#user_pass', config.get( 'users.admin.password' ) );
await Promise.all( [
page.click( 'input[type=submit]' ),
page.waitForNavigation( { waitUntil: 'networkidle0' } ),
] );
},
logout: async () => {
await page.goto(baseUrl + 'wp-login.php?action=logout', {
waitUntil: 'networkidle0',
});
await expect(page).toMatch('You are attempting to log out');
await Promise.all([
page.waitForNavigation({ waitUntil: 'networkidle0' }),
page.click('a'),
]);
},
openDashboard: async () => {
await page.goto( WP_ADMIN_DASHBOARD, {
waitUntil: 'networkidle0',
} );
},
openNewCoupon: async () => {
await page.goto( WP_ADMIN_NEW_COUPON, {
waitUntil: 'networkidle0',
} );
},
openNewOrder: async () => {
await page.goto( WP_ADMIN_NEW_ORDER, {
waitUntil: 'networkidle0',
} );
},
openNewProduct: async () => {
await page.goto( WP_ADMIN_NEW_PRODUCT, {
waitUntil: 'networkidle0',
} );
},
openPermalinkSettings: async () => {
await page.goto( WP_ADMIN_PERMALINK_SETTINGS, {
waitUntil: 'networkidle0',
} );
},
openPlugins: async () => {
await page.goto( WP_ADMIN_PLUGINS, {
waitUntil: 'networkidle0',
} );
},
openSettings: async ( tab, section = null ) => {
let settingsUrl = WP_ADMIN_WC_SETTINGS + tab;
if ( section ) {
settingsUrl += `&section=${ section }`;
}
await page.goto( settingsUrl, {
waitUntil: 'networkidle0',
} );
},
runSetupWizard: async () => {
await page.goto( WP_ADMIN_SETUP_WIZARD, {
waitUntil: 'networkidle0',
} );
},
};
export { CustomerFlow, StoreOwnerFlow };

View File

@ -1,17 +1,84 @@
/**
* External dependencies
*/
import { pressKeyWithModifier } from '@wordpress/e2e-test-utils';
/**
* Internal dependencies
*/
const flows = require( './flows' );
/**
* Perform a "select all" and then fill a input.
*
* @param {string} selector
* @param {string} value
*/
const clearAndFillInput = async ( selector, value ) => {
await page.focus( selector );
await pressKeyWithModifier( 'primary', 'a' );
await page.type( selector, value );
};
/**
* Click a tab (on post type edit screen).
*
* @param {string} tabName Tab label.
* @param {string} tabName Tab label
*/
const clickTab = async ( tabName ) => {
await expect( page ).toClick( '.wc-tabs > li > a', { text: tabName } );
};
/**
* Save changes on a WooCommerce settings page.
*/
const settingsPageSaveChanges = async () => {
await page.focus( 'button.woocommerce-save-button' );
await Promise.all( [
page.waitForNavigation( { waitUntil: 'networkidle0' } ),
page.click( 'button.woocommerce-save-button' ),
] );
};
/**
* Save changes on Permalink settings page.
*/
const permalinkSettingsPageSaveChanges = async () => {
await page.focus( '.wp-core-ui .button-primary' );
await Promise.all( [
page.waitForNavigation( { waitUntil: 'networkidle0' } ),
page.click( '.wp-core-ui .button-primary' ),
] );
};
/**
* Set checkbox.
*
* @param {string} selector
*/
const setCheckbox = async( selector ) => {
await page.focus( selector );
const checkbox = await page.$( selector );
const checkboxStatus = ( await ( await checkbox.getProperty( 'checked' ) ).jsonValue() );
if ( checkboxStatus !== true ) {
await page.click( selector );
}
};
/**
* Unset checkbox.
*
* @param {string} selector
*/
const unsetCheckbox = async( selector ) => {
await page.focus( selector );
const checkbox = await page.$( selector );
const checkboxStatus = ( await ( await checkbox.getProperty( 'checked' ) ).jsonValue() );
if ( checkboxStatus === true ) {
await page.click( selector );
}
};
/**
* Wait for UI blocking to end.
*/
@ -19,8 +86,90 @@ const uiUnblocked = async () => {
await page.waitForFunction( () => ! Boolean( document.querySelector( '.blockUI' ) ) );
};
/**
* Publish, verify that item was published. Trash, verify that item was trashed.
*
* @param {string} button (Publish)
* @param {string} publishNotice
* @param {string} publishVerification
* @param {string} trashVerification
*/
const verifyPublishAndTrash = async ( button, publishNotice, publishVerification, trashVerification ) => {
// Wait for auto save
await page.waitFor( 2000 );
// Publish
await expect( page ).toClick( button );
await page.waitForSelector( publishNotice );
// Verify
await expect( page ).toMatchElement( publishNotice, { text: publishVerification } );
if ( button === '.order_actions li .save_order' ) {
await expect( page ).toMatchElement( '#select2-order_status-container', { text: 'Processing' } );
await expect( page ).toMatchElement(
'#woocommerce-order-notes .note_content',
{
text: 'Order status changed from Pending payment to Processing.',
}
);
}
// Trash
await expect( page ).toClick( 'a', { text: "Move to Trash" } );
await page.waitForSelector( '#message' );
// Verify
await expect( page ).toMatchElement( publishNotice, { text: trashVerification } );
};
/**
* Verify that checkbox is set.
*
* @param {string} selector Selector of the checkbox that needs to be verified.
*/
const verifyCheckboxIsSet = async( selector ) => {
await page.focus( selector );
const checkbox = await page.$( selector );
const checkboxStatus = ( await ( await checkbox.getProperty( 'checked' ) ).jsonValue() );
await expect( checkboxStatus ).toBe( true );
};
/**
* Verify that checkbox is unset.
*
* @param {string} selector Selector of the checkbox that needs to be verified.
*/
const verifyCheckboxIsUnset = async( selector ) => {
await page.focus( selector );
const checkbox = await page.$( selector );
const checkboxStatus = ( await ( await checkbox.getProperty( 'checked' ) ).jsonValue() );
await expect( checkboxStatus ).not.toBe( true );
};
/**
* Verify the value of input field once it was saved (can be used for radio buttons verification as well).
*
* @param {string} selector Selector of the input field that needs to be verified.
* @param {string} value Value of the input field that needs to be verified.
*/
const verifyValueOfInputField = async( selector, value ) => {
await page.focus( selector );
const field = await page.$( selector );
const fieldValue = ( await ( await field.getProperty( 'value' ) ).jsonValue() );
await expect( fieldValue ).toBe( value );
};
module.exports = {
...flows,
clearAndFillInput,
clickTab,
settingsPageSaveChanges,
permalinkSettingsPageSaveChanges,
setCheckbox,
unsetCheckbox,
uiUnblocked,
verifyPublishAndTrash,
verifyCheckboxIsSet,
verifyCheckboxIsUnset,
verifyValueOfInputField,
};

View File

@ -13,7 +13,7 @@ class WC_Tests_CustomerCRUD extends WC_Unit_Test_Case {
public function test_create_customer() {
$username = 'testusername-' . time();
$customer = new WC_Customer();
$customer->set_username( 'testusername-' . time() );
$customer->set_username( $username );
$customer->set_password( 'test123' );
$customer->set_email( 'test@woo.local' );
$customer->save();

View File

@ -3,7 +3,7 @@
* Plugin Name: WooCommerce
* Plugin URI: https://woocommerce.com/
* Description: An eCommerce toolkit that helps you sell anything. Beautifully.
* Version: 3.8.0-beta.1
* Version: 3.9.0-dev
* Author: Automattic
* Author URI: https://woocommerce.com
* Text Domain: woocommerce
@ -39,7 +39,7 @@ if ( version_compare( PHP_VERSION, '5.6.0', '>=' ) ) {
// Include the main WooCommerce class.
if ( ! class_exists( 'WooCommerce', false ) ) {
include_once dirname( __FILE__ ) . '/includes/class-woocommerce.php';
include_once dirname( WC_PLUGIN_FILE ) . '/includes/class-woocommerce.php';
}
/**