Merge branch 'trunk' into add/e2e-implement-describe-each

This commit is contained in:
Rodel Calasagsag 2021-07-01 13:45:34 +08:00
commit 7934c9505b
38 changed files with 1292 additions and 971 deletions

View File

@ -154,11 +154,23 @@
border: 1px solid #e6e6e6; border: 1px solid #e6e6e6;
margin-right: 2em; margin-right: 2em;
padding: 4em; padding: 4em;
max-width: 200px;
.addons-img { .addons-img {
max-height: 86px; max-height: 86px;
max-width: 97px; max-width: 97px;
} }
&.is-full-image {
padding: 0;
background: none;
border: none;
.addons-img {
max-height: 100%;
max-width: 100%;
}
}
} }
.addons-promotion-block { .addons-promotion-block {
@ -219,8 +231,27 @@
margin-bottom: 0; margin-bottom: 0;
} }
.wcs-logos-container {
display: flex;
align-items: center;
flex-direction: row;
justify-content: center;
@media screen and (min-width: 500px) {
justify-content: left;
}
li {
margin-right: 8px;
&:last-child {
margin-right: 0;
}
}
}
.wcs-service-logo { .wcs-service-logo {
max-width: 40px; max-width: 45px;
} }
} }

View File

@ -4,8 +4,8 @@
* Sass variables * Sass variables
*/ */
$headings: -apple-system, blinkmacsystemfont, "Helvetica Neue", helvetica, sans-serif; $headings: var(--heading--font-family);
$body: nonbreakingspaceoverride, "Hoefler Text", garamond, "Times New Roman", serif; $body: var(--global--font-secondary);
$body-color: currentColor; $body-color: currentColor;
$highlights-color: #88a171; $highlights-color: #88a171;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 7.2 KiB

View File

@ -88,7 +88,7 @@ jQuery( function( $ ) {
tolerance : 'pointer', tolerance : 'pointer',
stop: function() { stop: function() {
$( $list.find( '.select2-selection__choice' ).get().reverse() ).each( function() { $( $list.find( '.select2-selection__choice' ).get().reverse() ).each( function() {
var id = $( self ).data( 'data' ).id; var id = $( this ).data( 'data' ).id;
var option = $select.find( 'option[value="' + id + '"]' )[0]; var option = $select.find( 'option[value="' + id + '"]' )[0];
$select.prepend( option ); $select.prepend( option );
} ); } );

View File

@ -81,6 +81,10 @@
$( window ).on( 'beforeunload', { view: this }, this.unloadConfirmation ); $( window ).on( 'beforeunload', { view: this }, this.unloadConfirmation );
$( document.body ).on( 'click', '.wc-shipping-zone-add', { view: this }, this.onAddNewRow ); $( document.body ).on( 'click', '.wc-shipping-zone-add', { view: this }, this.onAddNewRow );
}, },
onAddNewRow: function() {
var $link = $( this );
window.location.href = $link.attr( 'href' );
},
block: function() { block: function() {
$( this.el ).block({ $( this.el ).block({
message: null, message: null,

View File

@ -58,7 +58,7 @@
} }
if(org_title != ""){ if(org_title != ""){
if(!opts.content){ if(!opts.content){
org_elem.prop(opts.attribute, false); //remove original Attribute org_elem.removeAttr(opts.attribute); //remove original Attribute
} }
var timeout = false; var timeout = false;

View File

@ -68,16 +68,16 @@
}, },
{ {
"name": "league/flysystem", "name": "league/flysystem",
"version": "1.1.3", "version": "1.1.4",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/thephpleague/flysystem.git", "url": "https://github.com/thephpleague/flysystem.git",
"reference": "9be3b16c877d477357c015cec057548cf9b2a14a" "reference": "f3ad69181b8afed2c9edf7be5a2918144ff4ea32"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/thephpleague/flysystem/zipball/9be3b16c877d477357c015cec057548cf9b2a14a", "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/f3ad69181b8afed2c9edf7be5a2918144ff4ea32",
"reference": "9be3b16c877d477357c015cec057548cf9b2a14a", "reference": "f3ad69181b8afed2c9edf7be5a2918144ff4ea32",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -93,7 +93,6 @@
"phpunit/phpunit": "^8.5.8" "phpunit/phpunit": "^8.5.8"
}, },
"suggest": { "suggest": {
"ext-fileinfo": "Required for MimeType",
"ext-ftp": "Allows you to use FTP server storage", "ext-ftp": "Allows you to use FTP server storage",
"ext-openssl": "Allows you to use FTPS server storage", "ext-openssl": "Allows you to use FTPS server storage",
"league/flysystem-aws-s3-v2": "Allows you to use S3 storage with AWS SDK v2", "league/flysystem-aws-s3-v2": "Allows you to use S3 storage with AWS SDK v2",
@ -151,7 +150,7 @@
], ],
"support": { "support": {
"issues": "https://github.com/thephpleague/flysystem/issues", "issues": "https://github.com/thephpleague/flysystem/issues",
"source": "https://github.com/thephpleague/flysystem/tree/1.x" "source": "https://github.com/thephpleague/flysystem/tree/1.1.4"
}, },
"funding": [ "funding": [
{ {
@ -159,7 +158,7 @@
"type": "other" "type": "other"
} }
], ],
"time": "2020-08-23T07:39:11+00:00" "time": "2021-06-23T21:56:05+00:00"
}, },
{ {
"name": "league/mime-type-detection", "name": "league/mime-type-detection",
@ -1152,5 +1151,5 @@
"platform-overrides": { "platform-overrides": {
"php": "7.3" "php": "7.3"
}, },
"plugin-api-version": "2.0.0" "plugin-api-version": "2.1.0"
} }

View File

@ -254,16 +254,16 @@
}, },
{ {
"name": "rmccue/requests", "name": "rmccue/requests",
"version": "v1.8.0", "version": "v1.8.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/WordPress/Requests.git", "url": "https://github.com/WordPress/Requests.git",
"reference": "afbe4790e4def03581c4a0963a1e8aa01f6030f1" "reference": "82e6936366eac3af4d836c18b9d8c31028fe4cd5"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/WordPress/Requests/zipball/afbe4790e4def03581c4a0963a1e8aa01f6030f1", "url": "https://api.github.com/repos/WordPress/Requests/zipball/82e6936366eac3af4d836c18b9d8c31028fe4cd5",
"reference": "afbe4790e4def03581c4a0963a1e8aa01f6030f1", "reference": "82e6936366eac3af4d836c18b9d8c31028fe4cd5",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -306,11 +306,7 @@
"iri", "iri",
"sockets" "sockets"
], ],
"support": { "time": "2021-06-04T09:56:25+00:00"
"issues": "https://github.com/WordPress/Requests/issues",
"source": "https://github.com/WordPress/Requests/tree/v1.8.0"
},
"time": "2021-04-27T11:05:25+00:00"
}, },
{ {
"name": "symfony/finder", "name": "symfony/finder",
@ -612,5 +608,5 @@
"platform-overrides": { "platform-overrides": {
"php": "7.0" "php": "7.0"
}, },
"plugin-api-version": "2.0.0" "plugin-api-version": "1.1.0"
} }

View File

@ -1,5 +1,181 @@
== Changelog == == Changelog ==
= 5.5.0-beta.1 2021-06-22 =
**WooCommerce**
* Performance - Set Geolocation fallback transients to expire in one day instead of one week. #29987
* Enhancement - [Transparency] CLI command for viewing tracking data for your store. #30010
* Enhancement - All settings pages can now be extended consistently with new sections and settings. Also, unit tests have been added. #27684
* Enhancement - Set checkout fields value with the default defined value where form is not presented to the user. #29820
* Tweak - Show legacy widget instance in Rest API. #30012
* Tweak - No longer load PayPal Standard by default on new installs. #29971
* Tweak - Rename Products, Products by Rating, and Recent Viewed Products widgets to Products list, Products by Rating list, and Recently Viewed Products list. #29941
* Tweak - By default the postcode field will no longer be used, and the state field will become optional, for Curaçao. #29848
* Tweak - Handle WP_Error while creating placeholder image during install. #29783
* Fix - Allow block templates for WooCommerce pages. #30013
* Fix - Download IDs are included in export CSV and imported when updating existing products to maintain download permissions. #29970
* Fix - Fees added to an order from wp-admin are now calculated correctly the first time. #29945
* Fix - Prevent caching of cart/checkout page when using Chrome browser. #29912
* Fix - Invoice emails now contain payment link if the order needs payment, not just when the order is "pending". #29833
* Fix - Introduce meta to track stocks that refunded and restocked to properly handle stock recalculation. #29762
* Fix - Resolved a console error that could occur when clicking Add Shipping Zone. #30015
* Dev - Added an if condition block to check for new install before creating Zero and Reduced rate tax classes in class-wc-install.php. #29938
* Dev - Product attributes lookup table usage when enabled. #29896
* Dev - Set $woocommerce_loop name propriety to widget "Products". #29847
* Dev - Reduce the potential for errors when plugins implement REST API endpoints based on WooCommerce's own products controller. #29835
* Dev - Remove ABSPATH check in interfaces. #30124
* Dev - Add ability to bulk update order status to cancelled. #30116
* Dev - Register woocommerce.css in editor screens so it can be enqueued in the editor. #30093
* Dev - Add Customize WooCommerce link for block-based themes. #30044
**WooCommerce Admin - 2.4.0 **
* Add - SlotFill to Abbreviated Notification panel #7091
* Add - Consume remote payment methods on frontend #6867
* Add - Extend payment gateways REST endpoint #6919
* Add - Add remote payment gateway recommendations initial docs #6962
* Add - Add loading placeholders for payment gateways task #7123
* Add - Note date range logic for GivingFeedback, and InsightFirstSale note. #6969
* Add - Add transient notices feature #6809
* Add - Add transformers in remote inbox notifications #6948
* Add - Add Mercado Pago as default fallback payment gateway #7043
* Add - Add in Razorpay as default fallback payment gateway #7096
* Add - Get post install scripts from gateway and enqueue in client #6967
* Add - Add eWAY as default fallback gateway #7108
* Add - Free extension list powered by remote config #6952
* Add - Add PayPal to fallback payment gateways #7001
* Add - Add a data store for WC Payments REST APIs #6918
* Add - Progressive setup checklist copy and call to action buttons. #6956
* Add - Add Paystack as fallback gateway #7025
* Add - Add Square as default fallback gateway #7107
* Add - Add COD method to default payment gateway recommendations #7057
* Add - Add BACS as default fallback payment gateway #7073
* Add - A/B test of progressive checklist features. #7089
* Add - Add payment gateway return URL and action #7095
* Add - Add Mollie to the default payment gateways. #7092
* Add - Show task and activity notifications in the Inbox panel #7017
* Add - Adding WCPay payment configuration defaults. #7097
* Add - Create onboarding package to house refactored WCPay card and relevant components #7058
* Dev - Add Jetpack Backup admin note #6738
* Dev - Reduce the specificity and complexity of the ReportError component #6846
* Dev - Converting <SettingsForm /> component to TypeScript. #6981
* Dev - Update package-lock to fix versioning of local packages. #6843
* Dev - Use rule processing for remote payment methods #6830
* Dev - Update E2E jest config, so it correctly creates screenshots on failure. #6858
* Dev - Fixed storybook build script #6875
* Dev - Removed allowed keys list for adding woocommerce_meta data. #6889 🎉 @xristos3490
* Dev - Delete all products when running product import tests, unskip previously skipped test. #6905
* Dev - Add payment method selector to onboarding store #6921
* Dev - Add disabled prop to SelectControl #6902
* Dev - Add filter variation to tracks data in products analytics. #6913
* Dev - Offload remote inbox notifications engine run using action-scheduler. #6995
* Dev - Add source param support for notes query. #6979
* Dev - Remove the use of Dashicons and replace with @wordpress/icons or gridicons. #7020
* Dev - Refactor inbox panel components and moved to experimental package. #7006
* Dev - Business features uncheck creative mail by default #7139
* Dev - Remove support for IE11. #7112
* Dev - Drop styling support for IE11. #7137
* Dev - Remove react-docgen docs in favor of Storybook #7055
* Enhancement - Add expand/collapse to extendable task list. #6910
* Enhancement - Add task hierarchy support to extended task list. #6916
* Enhancement - Add remind me later option to task list. #6923
* Enhancement - Enable Remote Free Extensions List #7144
* Enhancement - Adding Slotfills for remote payments and SettingsForm component. #6932
* Fix - Update the wordpress/babel-preset to avoid crashes in WP5.8 beta2 #7202
* Fix - Add fallback for the select/dispatch data-controls for older WP versions #7204
* Fix - RemoteFreeExtension hide bundle when all of its plugins are not visible #7182
* Fix - Issue where summary stats were not showing in Analytics > Stock. #7161
* Fix - Rule Processing Transformer to handle dotNotation default value #7009
* Fix - Remove Navigation's uneeded SlotFill context #6832
* Fix - Report filters expecting specific ordering. #6847
* Fix - Render bug with report comparison mode selections. #6862
* Fix - Throw exception if the data store cannot be loaded when trying to use notes. #6771
* Fix - Autocompleter for custom Search in FilterPicker #6880
* Fix - Get currency from CurrencyContext #6723
* Fix - Correct the left position of transient notices when the new nav is used. #6914
* Fix - Exclude WC Shipping for store that are only offering downloadable products #6917
* Fix - SelectControl focus and de-focus bug #6906
* Fix - Multiple preload tag output bug. #6998
* Fix - Call existing filters for leaderboards in analytics. #6626
* Fix - Set target to blank for the external links #6999
* Fix style regression with the Chart header. #7002
* Fix styling of the advanced filter operator selection. #7005
* Fix - Deprecated warnings from select control @wordpress/data-controls. #7007
* Fix - Bug with Orders Report coupon exclusion filter. #7021
* Fix - Show Google Listing and Ads in installed marketing extensions section. #7029
* Fix - Notices not dissapearing. #7077
* Fix - Keyboard accessibility on the free features tab. #7149
* Fix - Fix error handling when remote free extension API returns empty array. #7147
* Fix - Transformer casing is incorrect and creates an error on case-sensitive systems #7104
* Fix - Preventing redundant notices when installing plugins via payments task list. #7026
* Fix - Autocompleter for custom Search in CompareFilter #6911
* Fix - Add target to the button to open it in a new tab #7110
* Fix - Make `Search` accept synchronous `autocompleter.options`. #6884
* Fix - Set autoload to false for all remote inbox notifications options. #7060
* Tweak - Setup checklist copy revert. #7015
* Tweak - Revert Card component removal #7167
* Update - Task list component with new Experimental Task list. #6849
* Update - Optimize payment gateway resolution #7124
* Update - Experimental task list import to the experimental package. #6950
* Update - Redirect to WC Home after setting up a payment method #6891
* Update - Hook up payments gateway data store #7038
* Update - Update remote payment docs gateway methods #7079
* Update - Remove original business step flow #7103
* Update - WooCommerce Shipping copy on onboarding steps #7148
** WooCommerce Blocks Package - 5.2.0 & 5.3.0 & 5.3.1 **
* Enhancement - Hide legacy widgets with a feature-complete block equivalent from the widget area block inserter. #4237
* Enhancement - Provide block transforms for legacy widgets with a feature-complete block equivalent. #4292
* Enhancement - Hide the All Products Block from the Customizer Widget Areas until full support is achieved. #4225
* Enhancement - Improved accessibility and styling of the controls of several of ours blocks. #4100
* Enhancement - Fix duplicate react keys in ProductDetails component. #4187
* Fix - Fix a bug in which Cart Widget didnt update when adding items from the All Products block. #4291
* Fix - Fix an issue where an attempt to add an out-of-stock product to the cart was made when clicking the “Read more” button. #4265
* Fix - Fix Product Categories List block display in Site Editor #4335.
* Fix - Make links in the Product Categories List block unclickable in the editor #4339.
* Fix - Fix rating stars not being shown in the Site Editor #4345.
** WooCommerce Blocks Feature Plugin - 5.2.0 & 5.3.0 & 5.3.1 **
* Enhancement - Added a key prop to each CartTotalItem within usePaymentMethodInterface. (4240)
* Enhancement - Sync customer data during checkout with draft orders. (4197)
* Enhancement - Update the display of the sidebar/order summary in the Cart and Checkout blocks. (4180)
* Enhancement - Hide the Cart and Checkout blocks from the new block-based widget editor. (4303)
* Fix - Hide tax breakdown if the total amount of tax to be paid is 0. (4262)
* Fix - Prevent Coupon code panel from appearing in stores were coupons are disabled. (4202)
* Fix - For payment methods, only use canMakePayment in the frontend (not the editor) context. (4188)
* Fix - Fix sending of confirmation emails for orders when no payment is needed. (4186)
* Fix - Stopped a warning being shown when using WooCommerce Force Sells and adding a product with a Synced Force Sell to the cart. (4182)
* Fix - Fix some missing translations from the Cart and Checkout blocks. (4295)
* Fix - Fix the flickering of the Proceed to Checkout button on quantity update in the Cart Block. (4293)
* Fix - Fix a display issue when itemized taxes are enabled, but no products in the cart are taxable. (4284)
* Compatibility - Add the ability for extensions to register callbacks to be executed by Blocks when the cart/extensions endpoint is hit. Extensions can now tell Blocks they need to do some server-side processing which will update the cart. (4298)
* Tweak - Add couponName filter to allow extensions to modify how coupons are displayed in the Cart and Checkout summary. (4166)
* Tweak - Move Button and Label components to @woocommerce/blocks-checkout package. (4222)
* Tweak - Add Slot in the Discounts section of the cart sidebar to allow third party extensions to render their own components there. (4248)
** ActionScheduler 3.2.0 & 3.2.1 **
* Fix - Add "no ordering" option to as_next_scheduled_action().
* Fix - Add secondary scheduled date checks when claiming actions (DBStore) | #634.
* Fix - Add secondary scheduled date checks when claiming actions (wpPostStore) | #634.
* Fix - Adds a new index to the action table, reducing the potential for deadlocks (props: @glagonikas).
* Fix - Fix unit tests infrastructure and adapt tests to PHP 8.
* Fix - Identify in-use data store.
* Fix - Improve test_migration_is_scheduled.
* Fix - PHP notice on list table.
* Fix - Speed up clean up and batch selects.
* Fix - Update pending dependencies.
* Fix - [PHP 8.0] Only pass action arg values through to do_action_ref_array().
* Fix - [PHP 8] Set the PHP version to 7.1 in composer.json for PHP 8 compatibility.
* Fix - add is_initialized() to docs.
* Fix - fix file permissions.
* Fix - fixes #664 by replacing __ with esc_html__.
* Fix - Add extra safety/account for different versions of AS and different loading patterns. #714
* Fix - Handle hidden columns (Tools → Scheduled Actions) | #600.
= 5.4.1 2021-06-10 = = 5.4.1 2021-06-10 =
**WooCommerce** **WooCommerce**

View File

@ -20,8 +20,8 @@
"maxmind-db/reader": "1.6.0", "maxmind-db/reader": "1.6.0",
"pelago/emogrifier": "3.1.0", "pelago/emogrifier": "3.1.0",
"psr/container": "1.0.0", "psr/container": "1.0.0",
"woocommerce/action-scheduler": "3.2.0", "woocommerce/action-scheduler": "3.2.1",
"woocommerce/woocommerce-admin": "2.3.1", "woocommerce/woocommerce-admin": "2.4.0-rc.2",
"woocommerce/woocommerce-blocks": "5.3.1" "woocommerce/woocommerce-blocks": "5.3.1"
}, },
"require-dev": { "require-dev": {

26
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "21b06e63b5f65deec4b635463a10402b", "content-hash": "6fb169d13104940f13185353a857a799",
"packages": [ "packages": [
{ {
"name": "automattic/jetpack-autoloader", "name": "automattic/jetpack-autoloader",
@ -493,16 +493,16 @@
}, },
{ {
"name": "woocommerce/action-scheduler", "name": "woocommerce/action-scheduler",
"version": "3.2.0", "version": "3.2.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/woocommerce/action-scheduler.git", "url": "https://github.com/woocommerce/action-scheduler.git",
"reference": "12c91fd20af0cdfb6107b8db9df78d133f512b04" "reference": "3c24064f115cd55e972d73d5d48f14133ac60b0d"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/woocommerce/action-scheduler/zipball/12c91fd20af0cdfb6107b8db9df78d133f512b04", "url": "https://api.github.com/repos/woocommerce/action-scheduler/zipball/3c24064f115cd55e972d73d5d48f14133ac60b0d",
"reference": "12c91fd20af0cdfb6107b8db9df78d133f512b04", "reference": "3c24064f115cd55e972d73d5d48f14133ac60b0d",
"shasum": "" "shasum": ""
}, },
"require-dev": { "require-dev": {
@ -526,22 +526,22 @@
"homepage": "https://actionscheduler.org/", "homepage": "https://actionscheduler.org/",
"support": { "support": {
"issues": "https://github.com/woocommerce/action-scheduler/issues", "issues": "https://github.com/woocommerce/action-scheduler/issues",
"source": "https://github.com/woocommerce/action-scheduler/tree/3.2.0" "source": "https://github.com/woocommerce/action-scheduler/tree/3.2.1"
}, },
"time": "2021-06-03T16:41:18+00:00" "time": "2021-06-21T20:21:35+00:00"
}, },
{ {
"name": "woocommerce/woocommerce-admin", "name": "woocommerce/woocommerce-admin",
"version": "2.3.1", "version": "2.4.0-rc.2",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/woocommerce/woocommerce-admin.git", "url": "https://github.com/woocommerce/woocommerce-admin.git",
"reference": "f28cf3f027e27a6679e4fa8173d8b6859ec84838" "reference": "ed72985cd459831c555dff2ff5f75111b6e210c1"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/woocommerce/woocommerce-admin/zipball/f28cf3f027e27a6679e4fa8173d8b6859ec84838", "url": "https://api.github.com/repos/woocommerce/woocommerce-admin/zipball/ed72985cd459831c555dff2ff5f75111b6e210c1",
"reference": "f28cf3f027e27a6679e4fa8173d8b6859ec84838", "reference": "ed72985cd459831c555dff2ff5f75111b6e210c1",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -578,9 +578,9 @@
"homepage": "https://github.com/woocommerce/woocommerce-admin", "homepage": "https://github.com/woocommerce/woocommerce-admin",
"support": { "support": {
"issues": "https://github.com/woocommerce/woocommerce-admin/issues", "issues": "https://github.com/woocommerce/woocommerce-admin/issues",
"source": "https://github.com/woocommerce/woocommerce-admin/tree/v2.3.1" "source": "https://github.com/woocommerce/woocommerce-admin/tree/v2.4.0-rc.2"
}, },
"time": "2021-05-24T09:48:40+00:00" "time": "2021-06-18T05:58:25+00:00"
}, },
{ {
"name": "woocommerce/woocommerce-blocks", "name": "woocommerce/woocommerce-blocks",

View File

@ -375,7 +375,7 @@ class WC_Admin_Addons {
$location = wc_get_base_location(); $location = wc_get_base_location();
if ( if (
! in_array( $location['country'], array( 'US', 'CA' ), true ) || ! in_array( $location['country'], array( 'US' ), true ) ||
$is_active || $is_active ||
! current_user_can( 'install_plugins' ) || ! current_user_can( 'install_plugins' ) ||
! current_user_can( 'activate_plugins' ) ! current_user_can( 'activate_plugins' )
@ -393,32 +393,16 @@ class WC_Admin_Addons {
); );
$defaults = array( $defaults = array(
'image' => WC()->plugin_url() . '/assets/images/wcs-extensions-banner-3x.png', 'image' => WC()->plugin_url() . '/assets/images/wcs-extensions-banner-3x.jpg',
'image_alt' => __( 'WooCommerce Shipping', 'woocommerce' ), 'image_alt' => __( 'WooCommerce Shipping', 'woocommerce' ),
'title' => __( 'Buy discounted shipping labels — then print them from your dashboard.', 'woocommerce' ), 'title' => __( 'Save time and money with WooCommerce Shipping', 'woocommerce' ),
'description' => __( 'Integrate your store with USPS to buy discounted shipping labels, and print them directly from your WooCommerce dashboard. Powered by WooCommerce Shipping.', 'woocommerce' ), 'description' => __( 'Print discounted USPS and DHL labels straight from your WooCommerce dashboard and save on shipping.', 'woocommerce' ),
'button' => __( 'Free - Install now', 'woocommerce' ), 'button' => __( 'Free - Install now', 'woocommerce' ),
'href' => $button_url, 'href' => $button_url,
'logos' => array(), 'logos' => array(),
); );
switch ( $location['country'] ) { switch ( $location['country'] ) {
case 'CA':
$local_defaults = array(
'image' => WC()->plugin_url() . '/assets/images/wcs-truck-banner-3x.png',
'title' => __( 'Show Canada Post shipping rates', 'woocommerce' ),
'description' => __( 'Display live rates from Canada Post at checkout to make shipping a breeze. Powered by WooCommerce Shipping.', 'woocommerce' ),
'logos' => array_merge(
$defaults['logos'],
array(
array(
'link' => WC()->plugin_url() . '/assets/images/wcs-canada-post-logo.jpg',
'alt' => 'Canada Post logo',
),
)
),
);
break;
case 'US': case 'US':
$local_defaults = array( $local_defaults = array(
'logos' => array_merge( 'logos' => array_merge(
@ -428,6 +412,10 @@ class WC_Admin_Addons {
'link' => WC()->plugin_url() . '/assets/images/wcs-usps-logo.png', 'link' => WC()->plugin_url() . '/assets/images/wcs-usps-logo.png',
'alt' => 'USPS logo', 'alt' => 'USPS logo',
), ),
array(
'link' => WC()->plugin_url() . '/assets/images/wcs-dhlexpress-logo.png',
'alt' => 'DHL Express logo',
),
) )
), ),
); );
@ -439,7 +427,7 @@ class WC_Admin_Addons {
$block_data = array_merge( $defaults, $local_defaults, $block ); $block_data = array_merge( $defaults, $local_defaults, $block );
?> ?>
<div class="addons-wcs-banner-block"> <div class="addons-wcs-banner-block">
<div class="addons-wcs-banner-block-image"> <div class="addons-wcs-banner-block-image is-full-image">
<img <img
class="addons-img" class="addons-img"
src="<?php echo esc_url( $block_data['image'] ); ?>" src="<?php echo esc_url( $block_data['image'] ); ?>"
@ -449,7 +437,7 @@ class WC_Admin_Addons {
<div class="addons-wcs-banner-block-content"> <div class="addons-wcs-banner-block-content">
<h1><?php echo esc_html( $block_data['title'] ); ?></h1> <h1><?php echo esc_html( $block_data['title'] ); ?></h1>
<p><?php echo esc_html( $block_data['description'] ); ?></p> <p><?php echo esc_html( $block_data['description'] ); ?></p>
<ul> <ul class="wcs-logos-container">
<?php foreach ( $block_data['logos'] as $logo ) : ?> <?php foreach ( $block_data['logos'] as $logo ) : ?>
<li> <li>
<img <img

View File

@ -67,6 +67,8 @@ class WC_Admin_Meta_Boxes {
// Error handling (for showing errors from meta boxes on next page load). // Error handling (for showing errors from meta boxes on next page load).
add_action( 'admin_notices', array( $this, 'output_errors' ) ); add_action( 'admin_notices', array( $this, 'output_errors' ) );
add_action( 'shutdown', array( $this, 'save_errors' ) ); add_action( 'shutdown', array( $this, 'save_errors' ) );
add_filter( 'theme_product_templates', array( $this, 'remove_block_templates' ), 10, 1 );
} }
/** /**
@ -222,6 +224,32 @@ class WC_Admin_Meta_Boxes {
do_action( 'woocommerce_process_' . $post->post_type . '_meta', $post_id, $post ); do_action( 'woocommerce_process_' . $post->post_type . '_meta', $post_id, $post );
} }
} }
/**
* Remove block-based templates from the list of available templates for products.
*
* @param string[] $templates Array of template header names keyed by the template file name.
*
* @return string[] Templates array excluding block-based templates.
*/
public function remove_block_templates( $templates ) {
if ( count( $templates ) === 0 || ! function_exists( 'gutenberg_get_block_template' ) ) {
return $templates;
}
$theme = wp_get_theme()->get_stylesheet();
$filtered_templates = array();
foreach ( $templates as $template_key => $template_name ) {
$gutenberg_template = gutenberg_get_block_template( $theme . '//' . $template_key );
if ( ! $gutenberg_template ) {
$filtered_templates[ $template_key ] = $template_name;
}
}
return $filtered_templates;
}
} }
new WC_Admin_Meta_Boxes(); new WC_Admin_Meta_Boxes();

View File

@ -314,7 +314,7 @@ class WC_Download_Handler {
/** /**
* Fallback on force download method for remote files. This is because: * Fallback on force download method for remote files. This is because:
* 1. xsendfile needs proxy configuration to work for remote files, which cannot be assumed to be available on most hosts. * 1. xsendfile needs proxy configuration to work for remote files, which cannot be assumed to be available on most hosts.
* 2. Force download method is more secure than redirect method if `allow_url_fopen` is enabled in `php.ini`. We fallback to redirect method in force download method anyway in case `allow_url_fopen` is not enabled. * 2. Force download method is more secure than redirect method if `allow_url_fopen` is enabled in `php.ini`.
*/ */
if ( $parsed_file_path['remote_file'] && ! apply_filters( 'woocommerce_use_xsendfile_for_remote', false ) ) { if ( $parsed_file_path['remote_file'] && ! apply_filters( 'woocommerce_use_xsendfile_for_remote', false ) ) {
do_action( 'woocommerce_download_file_force', $file_path, $filename ); do_action( 'woocommerce_download_file_force', $file_path, $filename );
@ -435,11 +435,7 @@ class WC_Download_Handler {
$start = isset( $download_range['start'] ) ? $download_range['start'] : 0; $start = isset( $download_range['start'] ) ? $download_range['start'] : 0;
$length = isset( $download_range['length'] ) ? $download_range['length'] : 0; $length = isset( $download_range['length'] ) ? $download_range['length'] : 0;
if ( ! self::readfile_chunked( $parsed_file_path['file_path'], $start, $length ) ) { if ( ! self::readfile_chunked( $parsed_file_path['file_path'], $start, $length ) ) {
if ( $parsed_file_path['remote_file'] ) { self::download_error( __( 'File not found', 'woocommerce' ) );
self::download_file_redirect( $file_path );
} else {
self::download_error( __( 'File not found', 'woocommerce' ) );
}
} }
exit; exit;
@ -620,6 +616,15 @@ class WC_Download_Handler {
* @param integer $status Error status. * @param integer $status Error status.
*/ */
private static function download_error( $message, $title = '', $status = 404 ) { private static function download_error( $message, $title = '', $status = 404 ) {
/*
* Since we will now render a message instead of serving a download, we should unwind some of the previously set
* headers.
*/
header( 'Content-Type: ' . get_option( 'html_type' ) . '; charset=' . get_option( 'blog_charset' ) );
header_remove( 'Content-Description;' );
header_remove( 'Content-Disposition' );
header_remove( 'Content-Transfer-Encoding' );
if ( ! strstr( $message, '<a ' ) ) { if ( ! strstr( $message, '<a ' ) ) {
$message .= ' <a href="' . esc_url( wc_get_page_permalink( 'shop' ) ) . '" class="wc-forward">' . esc_html__( 'Go to shop', 'woocommerce' ) . '</a>'; $message .= ' <a href="' . esc_url( wc_get_page_permalink( 'shop' ) ) . '" class="wc-forward">' . esc_html__( 'Go to shop', 'woocommerce' ) . '</a>';
} }

View File

@ -26,7 +26,7 @@ final class WooCommerce {
* *
* @var string * @var string
*/ */
public $version = '5.5.0'; public $version = '5.6.0';
/** /**
* WooCommerce Schema version. * WooCommerce Schema version.

View File

@ -272,7 +272,8 @@ class WC_Webhook_Data_Store implements WC_Webhook_Data_Store_Interface {
'post_modified' => 'date_modified_gmt', 'post_modified' => 'date_modified_gmt',
); );
$orderby = isset( $orderby_mapping[ $args['orderby'] ] ) ? $orderby_mapping[ $args['orderby'] ] : 'webhook_id'; $orderby = isset( $orderby_mapping[ $args['orderby'] ] ) ? $orderby_mapping[ $args['orderby'] ] : 'webhook_id';
$order = "ORDER BY {$orderby} " . esc_sql( strtoupper( $args['order'] ) ); $sort = 'ASC' === strtoupper( $args['order'] ) ? 'ASC' : 'DESC';
$order = "ORDER BY {$orderby} {$sort}";
$limit = -1 < $args['limit'] ? $wpdb->prepare( 'LIMIT %d', $args['limit'] ) : ''; $limit = -1 < $args['limit'] ? $wpdb->prepare( 'LIMIT %d', $args['limit'] ) : '';
$offset = 0 < $args['offset'] ? $wpdb->prepare( 'OFFSET %d', $args['offset'] ) : ''; $offset = 0 < $args['offset'] ? $wpdb->prepare( 'OFFSET %d', $args['offset'] ) : '';
$status = ! empty( $args['status'] ) ? $wpdb->prepare( 'AND `status` = %s', isset( $statuses[ $args['status'] ] ) ? $statuses[ $args['status'] ] : $args['status'] ) : ''; $status = ! empty( $args['status'] ) ? $wpdb->prepare( 'AND `status` = %s', isset( $statuses[ $args['status'] ] ) ? $statuses[ $args['status'] ] : $args['status'] ) : '';

View File

@ -658,6 +658,16 @@ class WC_Email extends WC_Settings_API {
remove_filter( 'wp_mail_from_name', array( $this, 'get_from_name' ) ); remove_filter( 'wp_mail_from_name', array( $this, 'get_from_name' ) );
remove_filter( 'wp_mail_content_type', array( $this, 'get_content_type' ) ); remove_filter( 'wp_mail_content_type', array( $this, 'get_content_type' ) );
/**
* Action hook fired when an email is sent.
*
* @since 5.6.0
* @param bool $return Whether the email was sent successfully.
* @param int $id Email ID.
* @param WC_Email $this WC_Email instance.
*/
do_action( 'woocommerce_email_sent', $return, $this->id, $this );
return $return; return $return;
} }

View File

@ -46,6 +46,32 @@ abstract class WC_CSV_Batch_Exporter extends WC_CSV_Exporter {
return trailingslashit( $upload_dir['basedir'] ) . $this->get_filename(); return trailingslashit( $upload_dir['basedir'] ) . $this->get_filename();
} }
/**
* Get CSV headers row file path to export to.
*
* @return string
*/
protected function get_headers_row_file_path() {
return $this->get_file_path() . '.headers';
}
/**
* Get the contents of the CSV headers row file. Defaults to the original known headers.
*
* @since 3.1.0
* @return string
*/
public function get_headers_row_file() {
$file = chr( 239 ) . chr( 187 ) . chr( 191 ) . $this->export_column_headers();
if ( @file_exists( $this->get_headers_row_file_path() ) ) { // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged
$file = @file_get_contents( $this->get_headers_row_file_path() ); // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged, WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents, WordPress.WP.AlternativeFunctions.file_system_read_file_get_contents
}
return $file;
}
/** /**
* Get the file contents. * Get the file contents.
* *
@ -70,8 +96,9 @@ abstract class WC_CSV_Batch_Exporter extends WC_CSV_Exporter {
*/ */
public function export() { public function export() {
$this->send_headers(); $this->send_headers();
$this->send_content( $this->get_file() ); $this->send_content( $this->get_headers_row_file() . $this->get_file() );
@unlink( $this->get_file_path() ); // phpcs:ignore WordPress.VIP.FileSystemWritesDisallow.file_ops_unlink, Generic.PHP.NoSilencedErrors.Discouraged @unlink( $this->get_file_path() ); // phpcs:ignore WordPress.VIP.FileSystemWritesDisallow.file_ops_unlink, Generic.PHP.NoSilencedErrors.Discouraged
@unlink( $this->get_headers_row_file_path() ); // phpcs:ignore WordPress.VIP.FileSystemWritesDisallow.file_ops_unlink, Generic.PHP.NoSilencedErrors.Discouraged
die(); die();
} }
@ -83,6 +110,9 @@ abstract class WC_CSV_Batch_Exporter extends WC_CSV_Exporter {
public function generate_file() { public function generate_file() {
if ( 1 === $this->get_page() ) { if ( 1 === $this->get_page() ) {
@unlink( $this->get_file_path() ); // phpcs:ignore WordPress.VIP.FileSystemWritesDisallow.file_ops_unlink, Generic.PHP.NoSilencedErrors.Discouraged, @unlink( $this->get_file_path() ); // phpcs:ignore WordPress.VIP.FileSystemWritesDisallow.file_ops_unlink, Generic.PHP.NoSilencedErrors.Discouraged,
// We need to initialize the file here.
$this->get_file();
} }
$this->prepare_data_to_export(); $this->prepare_data_to_export();
$this->write_csv_data( $this->get_csv_data() ); $this->write_csv_data( $this->get_csv_data() );
@ -95,15 +125,26 @@ abstract class WC_CSV_Batch_Exporter extends WC_CSV_Exporter {
* @param string $data Data. * @param string $data Data.
*/ */
protected function write_csv_data( $data ) { protected function write_csv_data( $data ) {
$file = $this->get_file();
// Add columns when finished. if ( ! file_exists( $this->get_file_path() ) || ! is_writeable( $this->get_file_path() ) ) {
if ( 100 === $this->get_percent_complete() ) { return false;
$file = chr( 239 ) . chr( 187 ) . chr( 191 ) . $this->export_column_headers() . $file; }
$fp = fopen( $this->get_file_path(), 'a+' );
if ( $fp ) {
fwrite( $fp, $data );
fclose( $fp );
}
// Add all columns when finished.
if ( 100 === $this->get_percent_complete() ) {
$header = chr( 239 ) . chr( 187 ) . chr( 191 ) . $this->export_column_headers();
// We need to use a temporary file to store headers, this will make our life so much easier.
@file_put_contents( $this->get_headers_row_file_path(), $header ); //phpcs:ignore WordPress.VIP.FileSystemWritesDisallow.file_ops_file_put_contents, Generic.PHP.NoSilencedErrors.Discouraged, WordPress.WP.AlternativeFunctions.file_system_read_file_put_contents
} }
$file .= $data;
@file_put_contents( $this->get_file_path(), $file ); // phpcs:ignore WordPress.VIP.FileSystemWritesDisallow.file_ops_file_put_contents, Generic.PHP.NoSilencedErrors.Discouraged, WordPress.WP.AlternativeFunctions.file_system_read_file_put_contents
} }
/** /**

View File

@ -976,14 +976,14 @@ function wc_get_image_size( $image_size ) {
$size['height'] = ''; $size['height'] = '';
$size['crop'] = 0; $size['crop'] = 0;
} elseif ( 'custom' === $cropping ) { } elseif ( 'custom' === $cropping ) {
$width = max( 1, get_option( 'woocommerce_thumbnail_cropping_custom_width', '4' ) ); $width = max( 1, (float) get_option( 'woocommerce_thumbnail_cropping_custom_width', '4' ) );
$height = max( 1, get_option( 'woocommerce_thumbnail_cropping_custom_height', '3' ) ); $height = max( 1, (float) get_option( 'woocommerce_thumbnail_cropping_custom_height', '3' ) );
$size['height'] = absint( NumberUtil::round( ( $size['width'] / $width ) * $height ) ); $size['height'] = absint( NumberUtil::round( ( $size['width'] / $width ) * $height ) );
$size['crop'] = 1; $size['crop'] = 1;
} else { } else {
$cropping_split = explode( ':', $cropping ); $cropping_split = explode( ':', $cropping );
$width = max( 1, current( $cropping_split ) ); $width = max( 1, (float) current( $cropping_split ) );
$height = max( 1, end( $cropping_split ) ); $height = max( 1, (float) end( $cropping_split ) );
$size['height'] = absint( NumberUtil::round( ( $size['width'] / $width ) * $height ) ); $size['height'] = absint( NumberUtil::round( ( $size['width'] / $width ) * $height ) );
$size['crop'] = 1; $size['crop'] = 1;
} }

View File

@ -36,7 +36,7 @@ function wc_template_redirect() {
// Logout. // Logout.
if ( isset( $wp->query_vars['customer-logout'] ) && ! empty( $_REQUEST['_wpnonce'] ) && wp_verify_nonce( sanitize_key( $_REQUEST['_wpnonce'] ), 'customer-logout' ) ) { if ( isset( $wp->query_vars['customer-logout'] ) && ! empty( $_REQUEST['_wpnonce'] ) && wp_verify_nonce( sanitize_key( $_REQUEST['_wpnonce'] ), 'customer-logout' ) ) {
wp_safe_redirect( str_replace( '&amp;', '&', wp_logout_url( wc_get_page_permalink( 'myaccount' ) ) ) ); wp_safe_redirect( str_replace( '&amp;', '&', wp_logout_url( apply_filters( 'woocommerce_logout_default_redirect_url', wc_get_page_permalink( 'myaccount' ) ) ) ) );
exit; exit;
} }

1067
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
{ {
"name": "woocommerce", "name": "woocommerce",
"title": "WooCommerce", "title": "WooCommerce",
"version": "5.5.0", "version": "5.6.0",
"homepage": "https://woocommerce.com/", "homepage": "https://woocommerce.com/",
"repository": { "repository": {
"type": "git", "type": "git",
@ -88,8 +88,8 @@
"mocha": "7.2.0", "mocha": "7.2.0",
"node-sass": "4.14.1", "node-sass": "4.14.1",
"prettier": "npm:wp-prettier@2.0.5", "prettier": "npm:wp-prettier@2.0.5",
"stylelint": "12.0.1", "stylelint": "^13.8.0",
"stylelint-config-wordpress": "16.0.0", "stylelint-config-wordpress": "17.0.0",
"typescript": "3.9.7", "typescript": "3.9.7",
"webpack": "4.44.2", "webpack": "4.44.2",
"webpack-cli": "3.3.12", "webpack-cli": "3.3.12",

View File

@ -4,7 +4,7 @@ Tags: e-commerce, store, sales, sell, woo, shop, cart, checkout, downloadable, d
Requires at least: 5.5 Requires at least: 5.5
Tested up to: 5.7 Tested up to: 5.7
Requires PHP: 7.0 Requires PHP: 7.0
Stable tag: 5.3.0 Stable tag: 5.4.1
License: GPLv3 License: GPLv3
License URI: https://www.gnu.org/licenses/gpl-3.0.html License URI: https://www.gnu.org/licenses/gpl-3.0.html
@ -159,136 +159,180 @@ If you encounter issues with the shop/category pages after an update, flush the
WooCommerce comes with some sample data you can use to see how products look; import sample_products.xml via the [WordPress importer](https://wordpress.org/plugins/wordpress-importer/). You can also use the core [CSV importer](https://docs.woocommerce.com/document/product-csv-importer-exporter/?utm_source=wp%20org%20repo%20listing&utm_content=3.6) or our [CSV Import Suite extension](https://woocommerce.com/products/product-csv-import-suite/?utm_source=wp%20org%20repo%20listing&utm_content=3.6) to import sample_products.csv WooCommerce comes with some sample data you can use to see how products look; import sample_products.xml via the [WordPress importer](https://wordpress.org/plugins/wordpress-importer/). You can also use the core [CSV importer](https://docs.woocommerce.com/document/product-csv-importer-exporter/?utm_source=wp%20org%20repo%20listing&utm_content=3.6) or our [CSV Import Suite extension](https://woocommerce.com/products/product-csv-import-suite/?utm_source=wp%20org%20repo%20listing&utm_content=3.6) to import sample_products.csv
== Changelog == == Changelog ==
= 5.5.0-beta.1 2021-06-22 =
= 5.3.0 2021-05-11 =
**WooCommerce** **WooCommerce**
* Dev - Add support for "cities" and "postcodes" fields to the REST API endpoints to create/update tax rates. #29495 * Performance - Set Geolocation fallback transients to expire in one day instead of one week. #29987
* Dev - The taxes GET endpoint now supports sorting by priority. #29495 * Enhancement - [Transparency] CLI command for viewing tracking data for your store. #30010
* Enhancement - Add a new `woocommerce_cart_product_not_enough_stock_already_in_cart_message` filter to allow developers to filter an add-to-cart error when there isn't enough in stock taking into account what's already in the cart. #29304 * Enhancement - All settings pages can now be extended consistently with new sections and settings. Also, unit tests have been added. #27684
* Enhancement - Pass `$handler`, and prevent logging from `woocommerce_logger_log_message` filter when the message is null. #29572 * Enhancement - Set checkout fields value with the default defined value where form is not presented to the user. #29820
* Fix - Added parameter `$item` (instance of WC_Order_Item) to both the function `wc_downloadable_file_permission` and the filter hook `woocommerce_downloadable_file_permission`. #23188 * Tweak - Show legacy widget instance in Rest API. #30012
* Fix - Add array-typed "cities" and "postcodes" to the response of the "tax" endpoint in the REST API to overcome the limitation of "city" and "postcode" returning always one single value. #27751 * Tweak - No longer load PayPal Standard by default on new installs. #29971
* Fix - Update tax rate for existing taxes when recalculating order totals. #27985 * Tweak - Rename Products, Products by Rating, and Recent Viewed Products widgets to Products list, Products by Rating list, and Recently Viewed Products list. #29941
* Fix - Replace page setup dropdowns with AJAX powered search selects. #29181 * Tweak - By default the postcode field will no longer be used, and the state field will become optional, for Curaçao. #29848
* Fix - Return 0 if order isn't available in `WC_Payment_Gateway::get_order_total()`. #29314 * Tweak - Handle WP_Error while creating placeholder image during install. #29783
* Fix - Fix console error on IE11 when opening admin pages. #29322 * Fix - Allow block templates for WooCommerce pages. #30013
* Fix - Prevent crash when log file can't be opened for writing. #29396 * Fix - Download IDs are included in export CSV and imported when updating existing products to maintain download permissions. #29970
* Fix - Closes the section "Store management insights" in email settings. #29447 * Fix - Fees added to an order from wp-admin are now calculated correctly the first time. #29945
* Fix - Fixed return type of `WC_Shortcode_Products::get_type()`. #29452 * Fix - Prevent caching of cart/checkout page when using Chrome browser. #29912
* Fix - Fix syntax error in the admin (products/orders) list table. #29469 * Fix - Invoice emails now contain payment link if the order needs payment, not just when the order is "pending". #29833
* Fix - Cart duplicate debug shipping notices in certain situations. #29480 * Fix - Introduce meta to track stocks that refunded and restocked to properly handle stock recalculation. #29762
* Fix - Trying to update the cities or postcodes (only) or a tax rate via REST API returned an error. #29495 * Fix - Resolved a console error that could occur when clicking Add Shipping Zone. #30015
* Fix - Unneeded browser popup message of unsaved changes when adding a shipping zone with a shipping method. #29510 * Dev - Added an if condition block to check for new install before creating Zero and Reduced rate tax classes in class-wc-install.php. #29938
* Fix - Update the persistent cart after it's loaded on customer login. Fixes an issue whereby unavailable products would be validated on every login. #29517 * Dev - Product attributes lookup table usage when enabled. #29896
* Fix - Updated `$customer->get_shipping()` and `$customer->get_billing()` to return the full address after updating individual fields. #29538 * Dev - Set $woocommerce_loop name propriety to widget "Products". #29847
* Fix - Prevent cart to reset all nonce in front-end. #29542 * Dev - Reduce the potential for errors when plugins implement REST API endpoints based on WooCommerce's own products controller. #29835
* Fix - Bump the version of the "Grouped product add to cart" template to 4.8.0 (was still at 4.0.0 by mistake). #29601 * Dev - Remove ABSPATH check in interfaces. #30124
* Fix - If we have a non-empty shipping address then do not overwrite the state or country fields with billing address data. #29605 * Dev - Add ability to bulk update order status to cancelled. #30116
* Tweak - Add the support to `optgroups` in single select on Settings API. #29145 * Dev - Register woocommerce.css in editor screens so it can be enqueued in the editor. #30093
* Tweak - Improves performance by avoiding an unnecessary redirect if custom permalink structure does not contain trailing slashes. #29422 * Dev - Add Customize WooCommerce link for block-based themes. #30044
* Tweak - Update SSR db version tooltip to more accurately state the db version. #29438
* Tweak - Adjust Twenty Twenty One order items header alignment. #29485
* Tweak - Lost password form alignment issues. #29496
* Tweak - Improve accessibility by adding `aria-hidden="true"` on strikethrough prices. #29603
* Tweak - Default store location to US California. #29654
* Tweak - Default store currency to USD. #29752
** WooCommerce Blocks - 4.8.0 & 4.9.0 & 4.9.1 ** **WooCommerce Admin - 2.4.0 **
* Dev - Removed legacy handling for SSR blocks that rendered shortcodes. #4010 * Add - SlotFill to Abbreviated Notification panel #7091
* Fix - Customer address country saving to orders in certain circumstances. #4013 * Add - Consume remote payment methods on frontend #6867
* Fix - Prevent error messages returned by the API from displaying raw HTML. #4005 * Add - Extend payment gateways REST endpoint #6919
* Fix - Proceed to checkout button click bug happening when the Coupon error is visible in the Cart block. #3996 * Add - Add remote payment gateway recommendations initial docs #6962
* Fix - Use font color in payment methods border. #4051 * Add - Add loading placeholders for payment gateways task #7123
* Fix - Load translation file for JS files that has translatable strings. #4050 * Add - Note date range logic for GivingFeedback, and InsightFirstSale note. #6969
* Fix - Stop shipping package titles line-breaks occurring in the middle of a word. #4049 * Add - Add transient notices feature #6809
* Fix - Fixed styling issues on the cart and checkout page in Twenty(X) themes. #4046 * Add - Add transformers in remote inbox notifications #6948
* Fix - Fix headline alignment in the empty state of the cart block. #4044 * Add - Add Mercado Pago as default fallback payment gateway #7043
* Fix - Fix button alignment in Featured Product and Featured Category blocks. #4028 * Add - Add in Razorpay as default fallback payment gateway #7096
* Fix - Check if Cart and Checkout are registered before removing payment methods. #4056 * Add - Get post install scripts from gateway and enqueue in client #6967
* Enhancement - Registered payment methods now have access to the `shouldSavePayment` prop in their components (which indicates whether the shopper has checked the save payment method checkbox. #3990 * Add - Add eWAY as default fallback gateway #7108
* Enhancement - Payment methods implementing the `savedTokenComponent` configuration property will now have the `onPaymentProcessing` event available to the registered component. #3982 * Add - Free extension list powered by remote config #6952
* Add - Add PayPal to fallback payment gateways #7001
* Add - Add a data store for WC Payments REST APIs #6918
* Add - Progressive setup checklist copy and call to action buttons. #6956
* Add - Add Paystack as fallback gateway #7025
* Add - Add Square as default fallback gateway #7107
* Add - Add COD method to default payment gateway recommendations #7057
* Add - Add BACS as default fallback payment gateway #7073
* Add - A/B test of progressive checklist features. #7089
* Add - Add payment gateway return URL and action #7095
* Add - Add Mollie to the default payment gateways. #7092
* Add - Show task and activity notifications in the Inbox panel #7017
* Add - Adding WCPay payment configuration defaults. #7097
* Add - Create onboarding package to house refactored WCPay card and relevant components #7058
* Dev - Add Jetpack Backup admin note #6738
* Dev - Reduce the specificity and complexity of the ReportError component #6846
* Dev - Converting <SettingsForm /> component to TypeScript. #6981
* Dev - Update package-lock to fix versioning of local packages. #6843
* Dev - Use rule processing for remote payment methods #6830
* Dev - Update E2E jest config, so it correctly creates screenshots on failure. #6858
* Dev - Fixed storybook build script #6875
* Dev - Removed allowed keys list for adding woocommerce_meta data. #6889 🎉 @xristos3490
* Dev - Delete all products when running product import tests, unskip previously skipped test. #6905
* Dev - Add payment method selector to onboarding store #6921
* Dev - Add disabled prop to SelectControl #6902
* Dev - Add filter variation to tracks data in products analytics. #6913
* Dev - Offload remote inbox notifications engine run using action-scheduler. #6995
* Dev - Add source param support for notes query. #6979
* Dev - Remove the use of Dashicons and replace with @wordpress/icons or gridicons. #7020
* Dev - Refactor inbox panel components and moved to experimental package. #7006
* Dev - Business features uncheck creative mail by default #7139
* Dev - Remove support for IE11. #7112
* Dev - Drop styling support for IE11. #7137
* Dev - Remove react-docgen docs in favor of Storybook #7055
* Enhancement - Add expand/collapse to extendable task list. #6910
* Enhancement - Add task hierarchy support to extended task list. #6916
* Enhancement - Add remind me later option to task list. #6923
* Enhancement - Enable Remote Free Extensions List #7144
* Enhancement - Adding Slotfills for remote payments and SettingsForm component. #6932
* Fix - Update the wordpress/babel-preset to avoid crashes in WP5.8 beta2 #7202
* Fix - Add fallback for the select/dispatch data-controls for older WP versions #7204
* Fix - RemoteFreeExtension hide bundle when all of its plugins are not visible #7182
* Fix - Issue where summary stats were not showing in Analytics > Stock. #7161
* Fix - Rule Processing Transformer to handle dotNotation default value #7009
* Fix - Remove Navigation's uneeded SlotFill context #6832
* Fix - Report filters expecting specific ordering. #6847
* Fix - Render bug with report comparison mode selections. #6862
* Fix - Throw exception if the data store cannot be loaded when trying to use notes. #6771
* Fix - Autocompleter for custom Search in FilterPicker #6880
* Fix - Get currency from CurrencyContext #6723
* Fix - Correct the left position of transient notices when the new nav is used. #6914
* Fix - Exclude WC Shipping for store that are only offering downloadable products #6917
* Fix - SelectControl focus and de-focus bug #6906
* Fix - Multiple preload tag output bug. #6998
* Fix - Call existing filters for leaderboards in analytics. #6626
* Fix - Set target to blank for the external links #6999
* Fix style regression with the Chart header. #7002
* Fix styling of the advanced filter operator selection. #7005
* Fix - Deprecated warnings from select control @wordpress/data-controls. #7007
* Fix - Bug with Orders Report coupon exclusion filter. #7021
* Fix - Show Google Listing and Ads in installed marketing extensions section. #7029
* Fix - Notices not dissapearing. #7077
* Fix - Keyboard accessibility on the free features tab. #7149
* Fix - Fix error handling when remote free extension API returns empty array. #7147
* Fix - Transformer casing is incorrect and creates an error on case-sensitive systems #7104
* Fix - Preventing redundant notices when installing plugins via payments task list. #7026
* Fix - Autocompleter for custom Search in CompareFilter #6911
* Fix - Add target to the button to open it in a new tab #7110
* Fix - Make `Search` accept synchronous `autocompleter.options`. #6884
* Fix - Set autoload to false for all remote inbox notifications options. #7060
* Tweak - Setup checklist copy revert. #7015
* Tweak - Revert Card component removal #7167
* Update - Task list component with new Experimental Task list. #6849
* Update - Optimize payment gateway resolution #7124
* Update - Experimental task list import to the experimental package. #6950
* Update - Redirect to WC Home after setting up a payment method #6891
* Update - Hook up payments gateway data store #7038
* Update - Update remote payment docs gateway methods #7079
* Update - Remove original business step flow #7103
* Update - WooCommerce Shipping copy on onboarding steps #7148
** WooCommerce Admin - 2.2.0 & 2.2.1 & 2.2.2 & 2.2.3 & 2.2.4 & 2.2.5 & 2.2.6 ** ** WooCommerce Blocks Package - 5.2.0 & 5.3.0 & 5.3.1 **
* Add - Next new novel navigation nudge note #6610
* Add - Add legacy report items to new navigation #6507 * Enhancement - Hide legacy widgets with a feature-complete block equivalent from the widget area block inserter. #4237
* Add - Add preview site button on the appearance task #6457 * Enhancement - Provide block transforms for legacy widgets with a feature-complete block equivalent. #4292
* Add - Back button to go to home screen from tasks in the task list. #6397 * Enhancement - Hide the All Products Block from the Customizer Widget Areas until full support is achieved. #4225
* Add - Add a "rather not say" option to revenue in the profile wizard. #6475 * Enhancement - Improved accessibility and styling of the controls of several of ours blocks. #4100
* Add - Remove Mollie promo note on install #6510 * Enhancement - Fix duplicate react keys in ProductDetails component. #4187
* Add - Remote Inbox Notifications rule to trigger when WooCommerce Admin is upgraded. #6040 * Fix - Fix a bug in which Cart Widget didnt update when adding items from the All Products block. #4291
* Add - CES survey for search product, order, customer #6420 * Fix - Fix an issue where an attempt to add an out-of-stock product to the cart was made when clicking the “Read more” button. #4265
* Add - CES survey for importing products #6419 * Fix - Fix Product Categories List block display in Site Editor #4335.
* Add - CES survey for adding product categories, tags, and attributes #6418 * Fix - Make links in the Product Categories List block unclickable in the editor #4339.
* Add - Additional analytics tracking for the business details step. #6575 * Fix - Fix rating stars not being shown in the Site Editor #4345.
* Add - Include tracking for mail poet installs in the selective bundle install #6603
* Add - Paystack payment provider to several african countries. #6579 ** WooCommerce Blocks Feature Plugin - 5.2.0 & 5.3.0 & 5.3.1 **
* Dev - Close activity panel tabs by default and track #6566
* Dev - Update undefined task name properties for help panel tracks #6565 * Enhancement - Added a key prop to each CartTotalItem within usePaymentMethodInterface. (4240)
* Dev - Refactor profile wizard benefits step and add tests #6583 * Enhancement - Sync customer data during checkout with draft orders. (4197)
* Dev - Add filter to profile wizard steps #6564 * Enhancement - Update the display of the sidebar/order summary in the Cart and Checkout blocks. (4180)
* Dev - Add nav intro modal tests #6518 * Enhancement - Hide the Cart and Checkout blocks from the new block-based widget editor. (4303)
* Dev - Use wc filter to get status tabs for tools category #6525 * Fix - Hide tax breakdown if the total amount of tax to be paid is 0. (4262)
* Dev - Add nav header component tests #6509 * Fix - Prevent Coupon code panel from appearing in stores were coupons are disabled. (4202)
* Dev - Add initial tests for navigation Menu class #6492 * Fix - For payment methods, only use canMakePayment in the frontend (not the editor) context. (4188)
* Dev - Remove active item from navigation store #6486 * Fix - Fix sending of confirmation emails for orders when no payment is needed. (4186)
* Dev - Add navigation container tests #6464 * Fix - Stopped a warning being shown when using WooCommerce Force Sells and adding a product with a Synced Force Sell to the cart. (4182)
* Dev - Add nav favorite button tests #6446 * Fix - Fix some missing translations from the Cart and Checkout blocks. (4295)
* Dev - Add a changelog lint check to PRs. #6414 * Fix - Fix the flickering of the Proceed to Checkout button on quantity update in the Cart Block. (4293)
* Dev - Add navigation favorites tests #6409 * Fix - Fix a display issue when itemized taxes are enabled, but no products in the cart are taxable. (4284)
* Dev - support use of Array.flat in client and packages. #6411 * Compatibility - Add the ability for extensions to register callbacks to be executed by Blocks when the cart/extensions endpoint is hit. Extensions can now tell Blocks they need to do some server-side processing which will update the cart. (4298)
* Dev - Deprecate Onboarding::has_woocommerce_support. #6401 * Tweak - Add couponName filter to allow extensions to modify how coupons are displayed in the Cart and Checkout summary. (4166)
* Dev - Add Dependency Extraction Webpack Plugin #5762 * Tweak - Move Button and Label components to @woocommerce/blocks-checkout package. (4222)
* Dev - Add client-side filter for Navigation rootBackUrl #6505 * Tweak - Add Slot in the Discounts section of the cart sidebar to allow third party extensions to render their own components there. (4248)
* Dev - Remove `items_purchased` and `account_type` props from onboarding profile API. #6520
* Dev - Added warning when WC-Admin is active but not being used #6453 ** ActionScheduler 3.2.0 **
* Dev - Store profiler - Added MailPoet to Business Details step #6503
* Dev - Store profiler - Added MailPoet to new Business Details step #6515 * Fix - Add "no ordering" option to as_next_scheduled_action().
* Dev - Add tilde (~) to represent client root directory for imports. #6517 * Fix - Add secondary scheduled date checks when claiming actions (DBStore) | #634.
* Dev - Add script automation for gathering hooks and filters. #6454 * Fix - Add secondary scheduled date checks when claiming actions (wpPostStore) | #634.
* Dev - Add TypeScript and page objects to the E2E test suite. #6582 * Fix - Adds a new index to the action table, reducing the potential for deadlocks (props: @glagonikas).
* Dev - Introduce Typescript to Navigation utils #6477 * Fix - Fix unit tests infrastructure and adapt tests to PHP 8.
* Dev - Payments task: include Mercado Pago #6572 * Fix - Identify in-use data store.
* Dev - Ensure script asset.php files are included in builds #6635 * Fix - Improve test_migration_is_scheduled.
* Dev - Ensure production script asset names don't include .min suffix #6681 * Fix - PHP notice on list table.
* Dev - Do a git clean before the core release. #6945 * Fix - Speed up clean up and batch selects.
* Dev - Fix a bug where trying to load an asset registry causes a crash. #6951 * Fix - Update pending dependencies.
* Fix - Add check for navigating being enabled. #6462 * Fix - [PHP 8.0] Only pass action arg values through to do_action_ref_array().
* Fix - Move the shipping input and text 1px lower. #6408 * Fix - [PHP 8] Set the PHP version to 7.1 in composer.json for PHP 8 compatibility.
* Fix - Correct the Klarna slug #6440 * Fix - add is_initialized() to docs.
* Fix - Broken link anchors to online documentation. #6455 * Fix - fix file permissions.
* Fix - Update payment card style on mobile #6413 * Fix - fixes #664 by replacing __ with esc_html__.
* Fix - Missing i18n in Welcome modal. #6456 * Fix - Add extra safety/account for different versions of AS and different loading patterns. #714
* Fix - Restore visual styles back to Analytics tabs. #5913 * Fix - Handle hidden columns (Tools → Scheduled Actions) | #600.
* Fix - Update contrast and hover / active colors for analytics dropdown buttons #6504
* Fix - Associated Order Number for refunds was hidden #6428
* Fix - Fix issue where Loader::is_admin_page() would error if WooCommerce admin is disabled. #6563
* Fix - Correct a bug where the JP connection flow would not happen when installing JP in the OBW. #6521
* Fix - Show management links when the task list is complete (even if its not hidden). #6657
* Fix - Adding New Zealand and Ireland to selective bundle option, previously missed. #6649
* Fix - Update the Mercado option used for enabling/disabling. #6677
* Fix - Improve AddFirstProduct email note contents. #6617
* Fix - Check if features are currently being enabled #6688
* Fix - Fix the activity panel toggle not closing on click #6679
* Fix - Fix use of feature checks and remove deprecated method calls #6687
* Fix - Allow the manager role to query certain options #6577
* Fix - Delete customer data on network user deletion #6574
* Fix - Fix Themes step visibility in IE 11 #6578
* Fix - Fix hidden menu title on smaller screens #6562
* Fix - Add gross sales column to CSV export #6567
* Fix - Disable the continue btn on OBW when requested are being made #6838
* Fix - Calling of get_script_asset_filename with extra parameter #6955
* Fix - Address an issue with OBW when installing only WooCommerce payments and Jetpack. #6957
* Tweak - Add default value for contains op #6622
* Tweak - Adjust targeting store age for the Add First Product note #6554
* Tweak - Improve WC Shipping & Tax logic #6547
* Tweak - Update Insight inbox note content #6555
* Tweak - Remove mobile activity panel toggle #6539
* Tweak - Refactor autoloader to remove global variable. #6412
* Tweak - Revert WCPay international support for bundled package #6901
* Tweak - Store profiler - Changed MailPoet's title and description #6886
* Tweak - Update PayU logo #6829
[See changelog for all versions](https://raw.githubusercontent.com/woocommerce/woocommerce/trunk/changelog.txt). [See changelog for all versions](https://raw.githubusercontent.com/woocommerce/woocommerce/trunk/changelog.txt).

View File

@ -34,7 +34,7 @@ client = HTTPClientFactory.build( 'https://example.com' )
.create(); .create();
// You can then use the client to make API requests. // You can then use the client to make API requests.
httpClient.get( '/wc/v3/products' ).then( ( response ) => { client.get( '/wc/v3/products' ).then( ( response ) => {
// Access the status code from the response. // Access the status code from the response.
response.statusCode; response.statusCode;
// Access the headers from the response. // Access the headers from the response.

View File

@ -4,18 +4,37 @@
*/ */
const { const {
shopper, shopper,
merchant,
createSimpleProduct, createSimpleProduct,
createVariableProduct, createVariableProduct,
createGroupedProduct, createGroupedProduct,
uiUnblocked uiUnblocked
} = require( '@woocommerce/e2e-utils' ); } = require( '@woocommerce/e2e-utils' );
let simplePostIdValue;
let variablePostIdValue;
let groupedPostIdValue;
const config = require( 'config' ); const config = require( 'config' );
// Variables for simple product
const simpleProductName = config.get( 'products.simple.name' ); const simpleProductName = config.get( 'products.simple.name' );
let simplePostIdValue;
// Variables for variable product
const defaultVariableProduct = config.get( 'products.variable' );
let variableProductId;
// Variables for grouped product
const simpleProductPrice = config.has('products.simple.price') ? config.get('products.simple.price') : '9.99';
const simple1 = {
name: simpleProductName + ' 1',
regularPrice: simpleProductPrice
};
const simple2 = {
name: simpleProductName + ' 2',
regularPrice: simpleProductPrice
};
const groupedProduct = {
name: 'Grouped Product',
groupedProducts: [simple1, simple2]
};
let groupedPostIdValue;
const runSingleProductPageTest = () => { const runSingleProductPageTest = () => {
describe('Single Product Page', () => { describe('Single Product Page', () => {
@ -43,30 +62,34 @@ const runSingleProductPageTest = () => {
}); });
}); });
describe.skip('Variable Product Page', () => { describe('Variable Product Page', () => {
beforeAll(async () => { beforeAll(async () => {
await merchant.login(); variableProductId = await createVariableProduct();
variablePostIdValue = await createVariableProduct();
await merchant.logout();
}); });
it('should be able to add variation products to the cart', async () => { it('should be able to add variation products to the cart', async () => {
// Add a product with one set of variations to cart // Add a product with one set of variations to cart
await shopper.goToProduct(variablePostIdValue); await shopper.goToProduct(variableProductId);
await expect(page).toSelect('#attr-1', 'val1');
await expect(page).toSelect('#attr-2', 'val1'); for (const attr of defaultVariableProduct.attributes) {
await expect(page).toSelect('#attr-3', 'val1'); const { name, options } = attr;
const selectElem = `#${name.toLowerCase()}`;
const value = options[0];
await expect(page).toSelect(selectElem, value);
}
await shopper.addToCart(); await shopper.addToCart();
await expect(page).toMatchElement('.woocommerce-message', {text: 'has been added to your cart.'}); await expect(page).toMatchElement('.woocommerce-message', {text: 'has been added to your cart.'});
// Verify cart contents // Verify cart contents
await shopper.goToCart(); await shopper.goToCart();
await shopper.productIsInCart('Variable Product with Three Variations'); await shopper.productIsInCart(defaultVariableProduct.name);
}); });
it('should be able to remove variation products from the cart', async () => { it('should be able to remove variation products from the cart', async () => {
// Remove items from cart // Remove items from cart
await shopper.removeFromCart('Variable Product with Three Variations'); await shopper.removeFromCart(defaultVariableProduct.name);
await uiUnblocked(); await uiUnblocked();
await expect(page).toMatchElement('.cart-empty', {text: 'Your cart is currently empty.'}); await expect(page).toMatchElement('.cart-empty', {text: 'Your cart is currently empty.'});
}); });
@ -74,9 +97,7 @@ const runSingleProductPageTest = () => {
describe('Grouped Product Page', () => { describe('Grouped Product Page', () => {
beforeAll(async () => { beforeAll(async () => {
await merchant.login(); groupedPostIdValue = await createGroupedProduct(groupedProduct);
groupedPostIdValue = await createGroupedProduct();
await merchant.logout();
}); });
it('should be able to add grouped products to the cart', async () => { it('should be able to add grouped products to the cart', async () => {
@ -93,7 +114,7 @@ const runSingleProductPageTest = () => {
await quantityFields[1].type('5'); await quantityFields[1].type('5');
await shopper.addToCart(); await shopper.addToCart();
await expect(page).toMatchElement('.woocommerce-message', await expect(page).toMatchElement('.woocommerce-message',
{text: '“'+simpleProductName+' 1” and “'+simpleProductName+' 2” have been added to your cart.'}); {text: '“'+simpleProductName+' 1” and “'+simpleProductName+' 2” have been added to your cart.'});
// Verify cart contents // Verify cart contents
await shopper.goToCart(); await shopper.goToCart();

View File

@ -4,69 +4,99 @@
*/ */
const { const {
shopper, shopper,
merchant,
createVariableProduct, createVariableProduct,
} = require( '@woocommerce/e2e-utils' ); } = require( '@woocommerce/e2e-utils' );
const config = require('config');
let variablePostIdValue; let variablePostIdValue;
const cartDialogMessage = 'Please select some product options before adding this product to your cart.'; const cartDialogMessage = 'Please select some product options before adding this product to your cart.';
const attributes = config.get( 'products.variable.attributes' )
const runVariableProductUpdateTest = () => { const runVariableProductUpdateTest = () => {
describe('Shopper > Update variable product',() => { describe('Shopper > Update variable product',() => {
beforeAll(async () => { beforeAll(async () => {
await merchant.login();
variablePostIdValue = await createVariableProduct(); variablePostIdValue = await createVariableProduct();
await merchant.logout();
}); });
it('shopper can change variable attributes to the same value', async () => { it('shopper can change variable attributes to the same value', async () => {
await shopper.goToProduct(variablePostIdValue); await shopper.goToProduct(variablePostIdValue);
await expect(page).toSelect('#attr-1', 'val1');
await expect(page).toSelect('#attr-2', 'val1');
await expect(page).toSelect('#attr-3', 'val1');
await expect(page).toMatchElement('.woocommerce-variation-price', { text: '9.99' }); for (const a of attributes) {
const { name, options } = a;
const attrHTMLId = `#${name.toLowerCase()}`;
await expect(page).toSelect(attrHTMLId, options[0]);
}
await expect(page).toMatchElement('.woocommerce-variation-price', {
text: '9.99'
});
}); });
it('shopper can change attributes to combination with dimensions and weight', async () => { it('shopper can change attributes to combination with dimensions and weight', async () => {
await shopper.goToProduct(variablePostIdValue); await shopper.goToProduct(variablePostIdValue);
await expect(page).toSelect('#attr-1', 'val1'); await expect(page).toSelect(
await expect(page).toSelect('#attr-2', 'val2'); `#${attributes[0].name.toLowerCase()}`,
await expect(page).toSelect('#attr-3', 'val1'); attributes[0].options[0]
);
await expect(page).toSelect(
`#${attributes[1].name.toLowerCase()}`,
attributes[1].options[1]
);
await expect(page).toSelect(
`#${attributes[2].name.toLowerCase()}`,
attributes[2].options[0]
);
await expect(page).toMatchElement('.woocommerce-variation-price', { text: '20.00' }); await expect(page).toMatchElement('.woocommerce-variation-price', { text: '20.00' });
await expect(page).toMatchElement('.woocommerce-variation-availability', { text: 'Out of stock' }); await expect(page).toMatchElement('.woocommerce-variation-availability', { text: 'Out of stock' });
await expect(page).toMatchElement('.woocommerce-product-attributes-item--weight', { text: '200 kg' }); await expect(page).toMatchElement('.woocommerce-product-attributes-item--weight', { text: '200 kg' });
await expect(page).toMatchElement('.woocommerce-product-attributes-item--dimensions', { text: '10 × 20 × 15 cm' }); await expect(page).toMatchElement('.woocommerce-product-attributes-item--dimensions', { text: '10 × 20 × 15 cm' });
}); });
it('shopper can change variable product attributes to variation with a different price', async () => { it('shopper can change variable product attributes to variation with a different price', async () => {
await shopper.goToProduct(variablePostIdValue); await shopper.goToProduct(variablePostIdValue);
await expect(page).toSelect('#attr-1', 'val1'); await expect(page).toSelect(
await expect(page).toSelect('#attr-2', 'val1'); `#${attributes[0].name.toLowerCase()}`,
await expect(page).toSelect('#attr-3', 'val2'); attributes[0].options[0]
);
await expect(page).toSelect(
`#${attributes[1].name.toLowerCase()}`,
attributes[1].options[0]
);
await expect(page).toSelect(
`#${attributes[2].name.toLowerCase()}`,
attributes[2].options[1]
);
await expect(page).toMatchElement('.woocommerce-variation-price', { text: '11.99' }); await expect(page).toMatchElement('.woocommerce-variation-price', { text: '11.99' });
}); });
it('shopper can reset variations', async () => { it('shopper can reset variations', async () => {
await shopper.goToProduct(variablePostIdValue); await shopper.goToProduct(variablePostIdValue);
await expect(page).toSelect('#attr-1', 'val1'); await expect(page).toSelect(
await expect(page).toSelect('#attr-2', 'val2'); `#${attributes[0].name.toLowerCase()}`,
await expect(page).toSelect('#attr-3', 'val1'); attributes[0].options[0]
);
await expect(page).toSelect(
`#${attributes[1].name.toLowerCase()}`,
attributes[1].options[1]
);
await expect(page).toSelect(
`#${attributes[2].name.toLowerCase()}`,
attributes[2].options[0]
);
await expect(page).toClick('.reset_variations'); await expect(page).toClick('.reset_variations');
// Verify the reset by attempting to add the product to the cart // Verify the reset by attempting to add the product to the cart
const couponDialog = await expect(page).toDisplayDialog(async () => { const couponDialog = await expect(page).toDisplayDialog(async () => {
await expect(page).toClick('.single_add_to_cart_button'); await expect(page).toClick('.single_add_to_cart_button');
}); });
expect(couponDialog.message()).toMatch(cartDialogMessage); expect(couponDialog.message()).toMatch(cartDialogMessage);
// Accept the dialog
await couponDialog.accept();
}); });
}); });

View File

@ -1,5 +1,9 @@
# Unreleased # Unreleased
## Added
- Factories for variable product, variation, and grouped product
# 0.1.5 # 0.1.5
## Added ## Added

View File

@ -7,10 +7,8 @@
*/ */
import { merchant, IS_RETEST_MODE } from './flows'; import { merchant, IS_RETEST_MODE } from './flows';
import { import {
clickTab,
uiUnblocked, uiUnblocked,
verifyCheckboxIsUnset, verifyCheckboxIsUnset,
selectOptionInSelect2,
setCheckbox, setCheckbox,
unsetCheckbox, unsetCheckbox,
evalAndClick, evalAndClick,
@ -24,6 +22,8 @@ const client = factories.api.withDefaultPermalinks;
const config = require( 'config' ); const config = require( 'config' );
const simpleProductName = config.get( 'products.simple.name' ); const simpleProductName = config.get( 'products.simple.name' );
const simpleProductPrice = config.has('products.simple.price') ? config.get('products.simple.price') : '9.99'; const simpleProductPrice = config.has('products.simple.price') ? config.get('products.simple.price') : '9.99';
const defaultVariableProduct = config.get('products.variable');
const defaultGroupedProduct = config.get('products.grouped');
/** /**
* Verify and publish * Verify and publish
@ -135,6 +135,9 @@ const completeOnboardingWizard = async () => {
// Business Details section // Business Details section
// Temporarily add delay to reduce test flakiness
await page.waitFor( 2000 );
// Query for the <SelectControl>s // Query for the <SelectControl>s
const selectControls = await page.$$( '.woocommerce-select-control' ); const selectControls = await page.$$( '.woocommerce-select-control' );
expect( selectControls ).toHaveLength( 2 ); expect( selectControls ).toHaveLength( 2 );
@ -220,172 +223,104 @@ const createSimpleProductWithCategory = async ( productName, productPrice, categ
/** /**
* Create variable product. * Create variable product.
* Also, create variations for all attributes.
*
* @param varProduct Defaults to the variable product object in `default.json`
* @returns the ID of the created variable product
*/ */
const createVariableProduct = async () => { const createVariableProduct = async (varProduct = defaultVariableProduct) => {
const { attributes } = varProduct;
const { id } = await factories.products.variable.create(varProduct); // create the variable product
const variations = [];
const buffer = []; // accumulated attributes while looping
const aIdx = 0; // attributes[] index
// We need to remove any listeners on the `dialog` event otherwise we can't catch the dialogs below // Create variation for all attributes
page.removeAllListeners('dialog'); const createVariation = (aIdx) => {
const { name, options } = attributes[aIdx];
const isLastAttribute = aIdx === attributes.length - 1;
// Go to "add product" page // Add each attribute value to the buffer.
await merchant.openNewProduct(); options.forEach((opt) => {
buffer.push({
name: name,
option: opt
});
// Make sure we're on the add product page if (isLastAttribute) {
await expect( page.title() ).resolves.toMatch( 'Add new product' ); // If this is the last attribute, it means the variation is now complete.
// Save whatever's been accumulated in the buffer to the `variations[]` array.
variations.push({
attributes: [...buffer]
});
} else {
// Otherwise, move to the next attribute first
// before proceeding to the next value in this attribute.
createVariation(aIdx + 1);
}
// Set product data buffer.pop();
await expect( page ).toFill( '#title', 'Variable Product with Three Variations' ); });
await expect( page ).toSelect( '#product-type', 'Variable product' ); };
createVariation(aIdx);
// Create attributes for variations // Set some properties of 1st variation
await clickTab( 'Attributes' ); variations[0].regularPrice = '9.99';
await expect( page ).toSelect( 'select[name="attribute_taxonomy"]', 'Custom product attribute' ); variations[0].virtual = true;
for ( let i = 0; i < 3; i++ ) { // Set some properties of 2nd variation
await expect( page ).toClick( 'button.add_attribute', { text: 'Add' } ); variations[1].regularPrice = '11.99';
// Wait for attribute form to load variations[1].virtual = true;
await uiUnblocked();
await page.focus( `input[name="attribute_names[${ i }]"]` ); // Set some properties of 3rd variation
await expect( page ).toFill( `input[name="attribute_names[${ i }]"]`, 'attr #' + ( i + 1 ) ); variations[2].regularPrice = '20';
await expect( page ).toFill( `textarea[name="attribute_values[${ i }]"]`, 'val1 | val2' ); variations[2].weight = '200';
await expect( page ).toClick( `input[name="attribute_variation[${ i }]"]` ); variations[2].dimensions = {
length: '10',
width: '20',
height: '15'
};
variations[2].manage_stock = true;
// Use API to create each variation
for (const v of variations) {
await factories.products.variation.create({
productId: id,
variation: v
});
} }
await expect( page ).toClick( 'button', { text: 'Save attributes' } ); return id;
// Wait for attribute form to save (triggers 2 UI blocks)
await uiUnblocked();
await page.waitFor( 1000 );
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( 'Product published.' );
const variablePostId = await page.$( '#post_ID' );
let variablePostIdValue = ( await ( await variablePostId.getProperty( 'value' ) ).jsonValue() );
return variablePostIdValue;
}; };
/** /**
* Create grouped product. * Create grouped product.
*
* @param groupedProduct Defaults to the grouped product object in `default.json`
* @returns ID of the grouped product
*/ */
const createGroupedProduct = async () => { const createGroupedProduct = async (groupedProduct = defaultGroupedProduct) => {
// Create two products to be linked in a grouped product after const { name, groupedProducts } = groupedProduct;
await factories.products.simple.create( { const simpleProductIds = [];
name: simpleProductName + ' 1', let groupedProductRequest;
regularPrice: simpleProductPrice
} );
await factories.products.simple.create( {
name: simpleProductName + ' 2',
regularPrice: simpleProductPrice
} );
// Go to "add product" page // Using the api, create simple products to be grouped
await merchant.openNewProduct(); for (const simpleProduct of groupedProducts) {
const { id } = await factories.products.simple.create(simpleProduct);
simpleProductIds.push(id);
}
// Make sure we're on the add product page // Using the api, create the grouped product
await expect( page.title() ).resolves.toMatch( 'Add new product' ); groupedProductRequest = {
name: name,
groupedProducts: simpleProductIds
};
const { id } = await factories.products.grouped.create(
groupedProductRequest
);
// Set product data and save the product return id;
await expect( page ).toFill( '#title', 'Grouped Product' ); };
await expect( page ).toSelect( '#product-type', 'Grouped product' );
await clickTab( 'Linked Products' );
await selectOptionInSelect2( simpleProductName + ' 1' );
await selectOptionInSelect2( simpleProductName + ' 2' );
await verifyAndPublish();
// Get product ID
const groupedPostId = await page.$( '#post_ID' );
let groupedPostIdValue = ( await ( await groupedPostId.getProperty( 'value' ) ).jsonValue() );
return groupedPostIdValue;
}
/** /**
* Create a basic order with the provided order status. * Create a basic order with the provided order status.

View File

@ -1,6 +1,9 @@
import { HTTPClientFactory } from '@woocommerce/api'; import { HTTPClientFactory } from '@woocommerce/api';
const config = require( 'config' ); const config = require( 'config' );
import { simpleProductFactory } from './factories/simple-product'; import { simpleProductFactory } from './factories/simple-product';
import { variableProductFactory } from './factories/variable-product';
import { variationFactory } from './factories/variation';
import { groupedProductFactory } from './factories/grouped-product';
const apiUrl = config.get( 'url' ); const apiUrl = config.get( 'url' );
const adminUsername = config.get( 'users.admin.username' ); const adminUsername = config.get( 'users.admin.username' );
@ -20,6 +23,9 @@ const factories = {
}, },
products: { products: {
simple: simpleProductFactory( withDefaultPermalinks ), simple: simpleProductFactory( withDefaultPermalinks ),
variable: variableProductFactory( withDefaultPermalinks ),
variation: variationFactory( withDefaultPermalinks ),
grouped: groupedProductFactory( withDefaultPermalinks )
}, },
}; };

View File

@ -0,0 +1,26 @@
import { GroupedProduct } from '@woocommerce/api';
import { Factory } from 'fishery';
/**
* Creates a new factory for creating variable products.
* This does not include creating product variations.
* Instead, use `variationFactory()` for that.
*
* @param {HTTPClient} httpClient The HTTP client we will give the repository.
* @return {AsyncFactory} The factory for creating models.
*/
export function groupedProductFactory(httpClient) {
const repository = GroupedProduct.restRepository(httpClient);
return Factory.define(({ params, onCreate }) => {
onCreate((model) => {
return repository.create(model);
});
return {
name: params.name,
type: 'grouped',
groupedProducts: params.groupedProducts
};
});
}

View File

@ -0,0 +1,27 @@
import { VariableProduct } from '@woocommerce/api';
import { Factory } from 'fishery';
/**
* Creates a new factory for creating variable products.
* This does not include creating product variations.
* Instead, use `variationFactory()` for that.
*
* @param {HTTPClient} httpClient The HTTP client we will give the repository.
* @return {AsyncFactory} The factory for creating models.
*/
export function variableProductFactory(httpClient) {
const repository = VariableProduct.restRepository(httpClient);
return Factory.define(({ params, onCreate }) => {
onCreate((model) => {
return repository.create(model);
});
return {
name: params.name,
type: 'variable',
defaultAttributes: params.defaultAttributes,
attributes: params.attributes
};
});
}

View File

@ -0,0 +1,22 @@
import { ProductVariation } from '@woocommerce/api';
import { Factory } from 'fishery';
/**
* Creates a new factory for creating a product variation.
*
* @param {HTTPClient} httpClient The HTTP client we will give the repository.
* @return {AsyncFactory} The factory for creating models.
*/
export function variationFactory(httpClient) {
const repository = ProductVariation.restRepository(httpClient);
return Factory.define(({ params, onCreate }) => {
const { productId, variation } = params;
onCreate((model) => {
return repository.create(productId, model);
});
return variation;
});
}

View File

@ -3,7 +3,7 @@
* Plugin Name: WooCommerce * Plugin Name: WooCommerce
* Plugin URI: https://woocommerce.com/ * Plugin URI: https://woocommerce.com/
* Description: An eCommerce toolkit that helps you sell anything. Beautifully. * Description: An eCommerce toolkit that helps you sell anything. Beautifully.
* Version: 5.5.0-dev * Version: 5.6.0-dev
* Author: Automattic * Author: Automattic
* Author URI: https://woocommerce.com * Author URI: https://woocommerce.com
* Text Domain: woocommerce * Text Domain: woocommerce