Merge remote-tracking branch 'upstream/master' into add-data-to-checkout_error

This commit is contained in:
Dominic Vermeulen-Smith 2020-10-29 13:39:40 +00:00
commit 2c17454956
45 changed files with 398 additions and 214 deletions

View File

@ -19,7 +19,7 @@ jobs:
id: build
uses: woocommerce/action-build@v2
- name: Deploy nightly build
uses: WebFreak001/deploy-nightly@v1.0.3
uses: WebFreak001/deploy-nightly@v1.1.0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
@ -34,7 +34,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Update nightly tag
uses: richardsimko/github-tag-action@1.0.0
uses: richardsimko/github-tag-action@v1.0.4
with:
tag_name: nightly
env:

View File

@ -9,7 +9,6 @@ cache:
# Since Xenial services are not started by default, we need to instruct it below to start.
services:
- xvfb
- mysql
- docker
@ -30,16 +29,13 @@ jobs:
fast_finish: true
include:
- name: "Core E2E Tests"
php: 7.4
env: WP_VERSION=latest WP_MULTISITE=0 RUN_E2E=1
install:
- nvm install
- npm install
- composer install
- "/sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_99.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :99 -ac -screen 0 1280x1024x16"
script:
- npm run build:assets
- npm install jest --global
- npm run docker:up
- npm run test:e2e
after_script:
@ -62,7 +58,6 @@ jobs:
allow_failures:
- php: 7.4
env: WP_VERSION=latest WP_MULTISITE=0 RUN_CODE_COVERAGE=1
- name: "WP Nightly"
install:
- export PATH="$HOME/.composer/vendor/bin:$PATH"
@ -80,7 +75,6 @@ install:
bash tests/bin/install.sh woocommerce_test root '' localhost $WP_VERSION
composer global require "phpunit/phpunit=6.5.*|7.5.*"
fi
- "/sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_99.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :99 -ac -screen 0 1280x1024x16"
script:
- bash tests/bin/phpunit.sh
@ -95,3 +89,6 @@ branches:
- master
- /^\d+\.\d+(\.\d+)?(-\S*)?$/
- /^release\//
before_install:
- composer self-update --1

View File

@ -5,7 +5,7 @@
<a href="https://packagist.org/packages/woocommerce/woocommerce"><img src="https://poser.pugx.org/woocommerce/woocommerce/v/stable" alt="Latest Stable Version"></a>
<img src="https://img.shields.io/wordpress/plugin/dt/woocommerce.svg" alt="WordPress.org downloads">
<img src="https://img.shields.io/wordpress/plugin/r/woocommerce.svg" alt="WordPress.org rating">
<a href="https://travis-ci.org/woocommerce/woocommerce"><img src="https://travis-ci.org/woocommerce/woocommerce.svg?branch=master" alt="Build Status"></a>
<a href="https://travis-ci.com/woocommerce/woocommerce"><img src="https://travis-ci.com/woocommerce/woocommerce.svg?branch=master" alt="Build Status"></a>
<a href="https://scrutinizer-ci.com/g/woocommerce/woocommerce/?branch=master"><img src="https://scrutinizer-ci.com/g/woocommerce/woocommerce/badges/quality-score.png?b=master" alt="Scrutinizer Code Quality"></a>
<a href="https://codecov.io/gh/woocommerce/woocommerce"><img src="https://codecov.io/gh/woocommerce/woocommerce/branch/master/graph/badge.svg" alt="codecov"></a>
</p>

View File

@ -507,8 +507,9 @@ jQuery( function( $ ) {
$.ajax({
type: 'POST',
url: wc_checkout_params.checkout_url,
data: $form.serialize(),
dataType: 'json',
data: new FormData( this ),
contentType: false,
processData: false,
success: function( result ) {
// Detach the unload handler that prevents a reload / redirect
wc_checkout_form.detachUnloadEventsOnSubmit();

View File

@ -1 +1 @@
!function(t){t.fn.tipTip=function(e){var o={activation:"hover",keepAlive:!1,maxWidth:"200px",edgeOffset:3,defaultPosition:"bottom",delay:400,fadeIn:200,fadeOut:200,attribute:"title",content:!1,enter:function(){},exit:function(){}},i=t.extend(o,e);if(t("#tiptip_holder").length<=0){var n=t('<div id="tiptip_holder" style="max-width:'+i.maxWidth+';"></div>'),r=t('<div id="tiptip_content"></div>'),a=t('<div id="tiptip_arrow"></div>');t("body").append(n.html(r).prepend(a.html('<div id="tiptip_arrow_inner"></div>')))}else var n=t("#tiptip_holder"),r=t("#tiptip_content"),a=t("#tiptip_arrow");return this.each(function(){function e(){i.enter.call(this),r.html(d),n.hide().removeAttr("class").css("margin","0"),a.removeAttr("style");var e=parseInt(f.offset().top),o=parseInt(f.offset().left),p=parseInt(f.outerWidth()),l=parseInt(f.outerHeight()),h=n.outerWidth(),c=n.outerHeight(),s=Math.round((p-h)/2),_=Math.round((l-c)/2),v=Math.round(o+s),m=Math.round(e+l+i.edgeOffset),g="",b="",M=Math.round(h-12)/2;"bottom"==i.defaultPosition?g="_bottom":"top"==i.defaultPosition?g="_top":"left"==i.defaultPosition?g="_left":"right"==i.defaultPosition&&(g="_right");var w=s+o<parseInt(t(window).scrollLeft()),O=h+o>parseInt(t(window).width());w&&s<0||"_right"==g&&!O||"_left"==g&&o<h+i.edgeOffset+5?(g="_right",b=Math.round(c-13)/2,M=-12,v=Math.round(o+p+i.edgeOffset),m=Math.round(e+_)):(O&&s<0||"_left"==g&&!w)&&(g="_left",b=Math.round(c-13)/2,M=Math.round(h),v=Math.round(o-(h+i.edgeOffset+5)),m=Math.round(e+_));var x=e+l+i.edgeOffset+c+8>parseInt(t(window).height()+t(window).scrollTop()),I=e+l-(i.edgeOffset+c+8)<0;x||"_bottom"==g&&x||"_top"==g&&!I?("_top"==g||"_bottom"==g?g="_top":g+="_top",b=c,m=Math.round(e-(c+5+i.edgeOffset))):(I|("_top"==g&&I)||"_bottom"==g&&!x)&&("_top"==g||"_bottom"==g?g="_bottom":g+="_bottom",b=-12,m=Math.round(e+l+i.edgeOffset)),"_right_top"==g||"_left_top"==g?m+=5:"_right_bottom"!=g&&"_left_bottom"!=g||(m-=5),"_left_top"!=g&&"_left_bottom"!=g||(v+=5),a.css({"margin-left":M+"px","margin-top":b+"px"}),n.css({"margin-left":v+"px","margin-top":m+"px"}).attr("class","tip"+g),u&&clearTimeout(u),u=setTimeout(function(){n.stop(!0,!0).fadeIn(i.fadeIn)},i.delay)}function o(){i.exit.call(this),u&&clearTimeout(u),n.fadeOut(i.fadeOut)}var f=t(this);if(i.content)d=i.content;else var d=f.attr(i.attribute);if(""!=d){i.content||f.removeAttr(i.attribute);var u=!1;"hover"==i.activation?(f.hover(function(){e()},function(){i.keepAlive||o()}),i.keepAlive&&n.hover(function(){},function(){o()})):"focus"==i.activation?f.focus(function(){e()}).blur(function(){o()}):"click"==i.activation&&(f.click(function(){return e(),!1}).hover(function(){},function(){i.keepAlive||o()}),i.keepAlive&&n.hover(function(){},function(){o()}))}})}}(jQuery);
!function(O){O.fn.tipTip=function(t){var g,b,M,w=O.extend({activation:"hover",keepAlive:!1,maxWidth:"200px",edgeOffset:3,defaultPosition:"bottom",delay:400,fadeIn:200,fadeOut:200,attribute:"title",content:!1,enter:function(){},exit:function(){}},t);return O("#tiptip_holder").length<=0?(g=O('<div id="tiptip_holder" style="max-width:'+w.maxWidth+';"></div>'),b=O('<div id="tiptip_content"></div>'),M=O('<div id="tiptip_arrow"></div>'),O("body").append(g.html(b).prepend(M.html('<div id="tiptip_arrow_inner"></div>')))):(g=O("#tiptip_holder"),b=O("#tiptip_content"),M=O("#tiptip_arrow")),this.each(function(){var _,v,m=O(this);function t(){w.enter.call(this),b.html(_),g.hide().removeAttr("class").css("margin","0"),M.removeAttr("style");var t=parseInt(m.offset().top),e=parseInt(m.offset().left),o=parseInt(m.outerWidth()),i=parseInt(m.outerHeight()),n=g.outerWidth(),r=g.outerHeight(),a=Math.round((o-n)/2),f=Math.round((i-r)/2),d=Math.round(e+a),u=Math.round(t+i+w.edgeOffset),p="",h="",l=Math.round(n-12)/2;"bottom"==w.defaultPosition?p="_bottom":"top"==w.defaultPosition?p="_top":"left"==w.defaultPosition?p="_left":"right"==w.defaultPosition&&(p="_right");var c=a+e<parseInt(O(window).scrollLeft()),s=n+e>parseInt(O(window).width());c&&a<0||"_right"==p&&!s||"_left"==p&&e<n+w.edgeOffset+5?(p="_right",h=Math.round(r-13)/2,l=-12,d=Math.round(e+o+w.edgeOffset),u=Math.round(t+f)):(s&&a<0||"_left"==p&&!c)&&(p="_left",h=Math.round(r-13)/2,l=Math.round(n),d=Math.round(e-(n+w.edgeOffset+5)),u=Math.round(t+f));n=t+i+w.edgeOffset+r+8>parseInt(O(window).height()+O(window).scrollTop()),f=t+i-(w.edgeOffset+r+8)<0;n||"_bottom"==p&&n||"_top"==p&&!f?("_top"==p||"_bottom"==p?p="_top":p+="_top",h=r,u=Math.round(t-(r+5+w.edgeOffset))):(f|("_top"==p&&f)||"_bottom"==p&&!n)&&("_top"==p||"_bottom"==p?p="_bottom":p+="_bottom",h=-12,u=Math.round(t+i+w.edgeOffset)),"_right_top"==p||"_left_top"==p?u+=5:"_right_bottom"!=p&&"_left_bottom"!=p||(u-=5),"_left_top"!=p&&"_left_bottom"!=p||(d+=5),M.css({"margin-left":l+"px","margin-top":h+"px"}),g.css({"margin-left":d+"px","margin-top":u+"px"}).attr("class","tip"+p),v&&clearTimeout(v),v=setTimeout(function(){g.stop(!0,!0).fadeIn(w.fadeIn)},w.delay)}function e(){w.exit.call(this),v&&clearTimeout(v),g.fadeOut(w.fadeOut)}""!=(_=w.content?w.content:m.attr(w.attribute))&&(w.content||m.removeAttr(w.attribute),v=!1,"hover"==w.activation?(m.hover(function(){t()},function(){w.keepAlive&&g.is(":hover")||e()}),w.keepAlive&&g.hover(function(){},function(){e()})):"focus"==w.activation?m.focus(function(){t()}).blur(function(){e()}):"click"==w.activation&&(m.click(function(){return t(),!1}).hover(function(){},function(){w.keepAlive||e()}),w.keepAlive&&g.hover(function(){},function(){e()})))})}}(jQuery);

View File

@ -235,16 +235,16 @@
},
{
"name": "squizlabs/php_codesniffer",
"version": "3.5.6",
"version": "3.5.8",
"source": {
"type": "git",
"url": "https://github.com/squizlabs/PHP_CodeSniffer.git",
"reference": "e97627871a7eab2f70e59166072a6b767d5834e0"
"reference": "9d583721a7157ee997f235f327de038e7ea6dac4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/e97627871a7eab2f70e59166072a6b767d5834e0",
"reference": "e97627871a7eab2f70e59166072a6b767d5834e0",
"url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/9d583721a7157ee997f235f327de038e7ea6dac4",
"reference": "9d583721a7157ee997f235f327de038e7ea6dac4",
"shasum": ""
},
"require": {
@ -282,7 +282,7 @@
"phpcs",
"standards"
],
"time": "2020-08-10T04:50:15+00:00"
"time": "2020-10-23T02:01:07+00:00"
},
{
"name": "woocommerce/woocommerce-sniffs",

View File

@ -61,6 +61,20 @@
"constructor",
"instantiate"
],
"funding": [
{
"url": "https://www.doctrine-project.org/sponsorship.html",
"type": "custom"
},
{
"url": "https://www.patreon.com/phpdoctrine",
"type": "patreon"
},
{
"url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator",
"type": "tidelift"
}
],
"time": "2020-05-29T17:27:14+00:00"
},
{
@ -109,6 +123,12 @@
"object",
"object graph"
],
"funding": [
{
"url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy",
"type": "tidelift"
}
],
"time": "2020-06-29T13:22:24+00:00"
},
{
@ -1329,20 +1349,20 @@
},
{
"name": "symfony/polyfill-ctype",
"version": "v1.18.1",
"version": "v1.20.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-ctype.git",
"reference": "1c302646f6efc070cd46856e600e5e0684d6b454"
"reference": "f4ba089a5b6366e453971d3aad5fe8e897b37f41"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/1c302646f6efc070cd46856e600e5e0684d6b454",
"reference": "1c302646f6efc070cd46856e600e5e0684d6b454",
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/f4ba089a5b6366e453971d3aad5fe8e897b37f41",
"reference": "f4ba089a5b6366e453971d3aad5fe8e897b37f41",
"shasum": ""
},
"require": {
"php": ">=5.3.3"
"php": ">=7.1"
},
"suggest": {
"ext-ctype": "For best performance"
@ -1350,7 +1370,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.18-dev"
"dev-main": "1.20-dev"
},
"thanks": {
"name": "symfony/polyfill",
@ -1387,7 +1407,21 @@
"polyfill",
"portable"
],
"time": "2020-07-14T12:35:20+00:00"
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2020-10-23T14:02:19+00:00"
},
{
"name": "theseer/tokenizer",
@ -1488,5 +1522,6 @@
"platform-dev": [],
"platform-overrides": {
"php": "7.1"
}
},
"plugin-api-version": "1.1.0"
}

View File

@ -20,10 +20,10 @@ output 6 "Prefixing the appropriate vendor namespaces with Automattic\WooCommerc
# Replace "League\Container" in "use" and "namespace" with "Automattic\WooCommerce\Vendor\League\Container".
REGEX='s/^[[:space:]]*(use|namespace)[[:space:]]*(League\\Container)/\1 Automattic\\WooCommerce\\Vendor\\\2/g'
find ./vendor/league/container -iname '*.php' -exec sed -i '.bak' -E -e "$REGEX" {} \;
find ./vendor/league/container -iname '*.php' -exec sed -i'.bak' -E -e "$REGEX" {} \;
find ./vendor/league/container -name "*.php.bak" -type f -delete
# Replace too in the composer.json file for the package.
sed -i '.bak' -E -e "s/\"(League\\\\\\\Container)/\"Automattic\\\\\\\WooCommerce\\\\\\\Vendor\\\\\\\\\1/g" vendor/league/container/composer.json
sed -i'.bak' -E -e "s/\"(League\\\\\\\Container)/\"Automattic\\\\\\\WooCommerce\\\\\\\Vendor\\\\\\\\\1/g" vendor/league/container/composer.json
rm -f vendor/league/container/composer.json.bak

View File

@ -1,5 +1,19 @@
== Changelog ==
= 4.6.1 - 2020-10-21 =
**WooCommerce**
* Update woocommerce-admin to 1.6.2. #28006
**WooCommerce Admin - 1.6.2**
* Fix for missing activity panels. #5400
* Adds array casting on onboarding profile option. #5415
* Gutenberg compatibility fix for home screen inbox. #5416
* Gutenberg compat fix for empty reports (leaderboard, customers report). #5409
* i18n fix for Performance Indicators labels Home Screen. #5405
= 4.6.0 - 2020-10-14 =
**WooCommerce**

View File

@ -15,8 +15,8 @@
"pelago/emogrifier": "3.1.0",
"psr/container": "1.0.0",
"woocommerce/action-scheduler": "3.1.6",
"woocommerce/woocommerce-admin": "1.6.1",
"woocommerce/woocommerce-blocks": "3.4.0",
"woocommerce/woocommerce-admin": "1.6.3",
"woocommerce/woocommerce-blocks": "3.6.0",
"league/container": "3.3.3"
},
"require-dev": {

45
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": "177fa175c12c63dd29d3241469dc07b7",
"content-hash": "ed84c4d91482a5c508caaf50e843de58",
"packages": [
{
"name": "automattic/jetpack-autoloader",
@ -259,12 +259,6 @@
"provider",
"service"
],
"funding": [
{
"url": "https://github.com/philipobenito",
"type": "github"
}
],
"time": "2020-09-28T13:38:44+00:00"
},
{
@ -501,20 +495,6 @@
],
"description": "Symfony CssSelector Component",
"homepage": "https://symfony.com",
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2020-03-16T08:31:04+00:00"
},
{
@ -554,16 +534,16 @@
},
{
"name": "woocommerce/woocommerce-admin",
"version": "1.6.1",
"version": "1.6.3",
"source": {
"type": "git",
"url": "https://github.com/woocommerce/woocommerce-admin.git",
"reference": "4be058217d90b7f15b711d018b899260b38d011a"
"reference": "3015abbda8657ef097b7763e4c941daa06dab6f7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/woocommerce/woocommerce-admin/zipball/4be058217d90b7f15b711d018b899260b38d011a",
"reference": "4be058217d90b7f15b711d018b899260b38d011a",
"url": "https://api.github.com/repos/woocommerce/woocommerce-admin/zipball/3015abbda8657ef097b7763e4c941daa06dab6f7",
"reference": "3015abbda8657ef097b7763e4c941daa06dab6f7",
"shasum": ""
},
"require": {
@ -597,20 +577,20 @@
],
"description": "A modern, javascript-driven WooCommerce Admin experience.",
"homepage": "https://github.com/woocommerce/woocommerce-admin",
"time": "2020-10-14T14:21:15+00:00"
"time": "2020-10-26T20:25:00+00:00"
},
{
"name": "woocommerce/woocommerce-blocks",
"version": "v3.4.0",
"version": "v3.6.0",
"source": {
"type": "git",
"url": "https://github.com/woocommerce/woocommerce-gutenberg-products-block.git",
"reference": "d64a2616932f312ec4fd8f4e46195c3f9aca0000"
"reference": "1046697451f5e8906e48f0a7532b7637c3ded108"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/woocommerce/woocommerce-gutenberg-products-block/zipball/d64a2616932f312ec4fd8f4e46195c3f9aca0000",
"reference": "d64a2616932f312ec4fd8f4e46195c3f9aca0000",
"url": "https://api.github.com/repos/woocommerce/woocommerce-gutenberg-products-block/zipball/1046697451f5e8906e48f0a7532b7637c3ded108",
"reference": "1046697451f5e8906e48f0a7532b7637c3ded108",
"shasum": ""
},
"require": {
@ -644,7 +624,7 @@
"gutenberg",
"woocommerce"
],
"time": "2020-09-15T12:10:40+00:00"
"time": "2020-10-12T15:35:42+00:00"
}
],
"packages-dev": [
@ -706,6 +686,5 @@
"platform-dev": [],
"platform-overrides": {
"php": "7.1"
},
"plugin-api-version": "1.1.0"
}
}

View File

@ -3,7 +3,9 @@
* Countries
*
* Returns an array of countries and codes.
* Country codes and names should follow the Unicode CLDR recommendation (http://cldr.unicode.org/translation/country-names).
* Country codes and names should follow the Unicode CLDR recommendation (http://cldr.unicode.org/translation/displaynames/country-names).
*
* See https://github.com/unicode-org/cldr/blob/master/common/subdivisions/en.xml
*
* @package WooCommerce\i18n
* @version 3.8.0

View File

@ -153,6 +153,15 @@ return array(
'weight_unit' => 'kg',
'dimension_unit' => 'cm',
),
'JM' => array(
'currency_code' => 'JMD',
'currency_pos' => 'left',
'thousand_sep' => ',',
'decimal_sep' => '.',
'num_decimals' => 2,
'weight_unit' => 'lbs',
'dimension_unit' => 'in',
),
'JP' => array(
'currency_code' => 'JPY',
'currency_pos' => 'left',

View File

@ -752,6 +752,22 @@ return array(
),
'IL' => array(),
'IM' => array(),
'JM' => array( // Jamaica's Parishes. Ref: https://en.wikipedia.org/wiki/ISO_3166-2:JM.
'JM-01' => __( 'Kingston', 'woocommerce' ),
'JM-02' => __( 'Saint Andrew', 'woocommerce' ),
'JM-03' => __( 'Saint Thomas', 'woocommerce' ),
'JM-04' => __( 'Portland', 'woocommerce' ),
'JM-05' => __( 'Saint Mary', 'woocommerce' ),
'JM-06' => __( 'Saint Ann', 'woocommerce' ),
'JM-07' => __( 'Trelawny', 'woocommerce' ),
'JM-08' => __( 'Saint James', 'woocommerce' ),
'JM-09' => __( 'Hanover', 'woocommerce' ),
'JM-10' => __( 'Westmoreland', 'woocommerce' ),
'JM-11' => __( 'Saint Elizabeth', 'woocommerce' ),
'JM-12' => __( 'Manchester', 'woocommerce' ),
'JM-13' => __( 'Clarendon', 'woocommerce' ),
'JM-14' => __( 'Saint Catherine', 'woocommerce' ),
),
/**
* Japan States.

View File

@ -1738,7 +1738,7 @@ class WC_Product extends WC_Abstract_Legacy_Product {
* @return boolean
*/
public function has_options() {
return false;
return apply_filters( 'woocommerce_product_has_options', false, $this );
}
/*

View File

@ -75,8 +75,8 @@ if ( ! class_exists( 'WC_Admin_Dashboard', false ) ) :
$query['where'] .= "AND posts.post_status IN ( 'wc-" . implode( "','wc-", apply_filters( 'woocommerce_reports_order_statuses', array( 'completed', 'processing', 'on-hold' ) ) ) . "' ) ";
$query['where'] .= "AND order_item_meta.meta_key = '_qty' ";
$query['where'] .= "AND order_item_meta_2.meta_key = '_product_id' ";
$query['where'] .= "AND posts.post_date >= '" . date( 'Y-m-01', current_time( 'timestamp' ) ) . "' ";
$query['where'] .= "AND posts.post_date <= '" . date( 'Y-m-d H:i:s', current_time( 'timestamp' ) ) . "' ";
$query['where'] .= "AND posts.post_date >= '" . gmdate( 'Y-m-01', current_time( 'timestamp' ) ) . "' ";
$query['where'] .= "AND posts.post_date <= '" . gmdate( 'Y-m-d H:i:s', current_time( 'timestamp' ) ) . "' ";
$query['groupby'] = 'GROUP BY product_id';
$query['orderby'] = 'ORDER BY qty DESC';
$query['limits'] = 'LIMIT 1';
@ -93,8 +93,8 @@ if ( ! class_exists( 'WC_Admin_Dashboard', false ) ) :
include_once dirname( __FILE__ ) . '/reports/class-wc-report-sales-by-date.php';
$sales_by_date = new WC_Report_Sales_By_Date();
$sales_by_date->start_date = strtotime( date( 'Y-m-01', current_time( 'timestamp' ) ) );
$sales_by_date->end_date = strtotime( date( 'Y-m-d', current_time( 'timestamp' ) ) );
$sales_by_date->start_date = strtotime( gmdate( 'Y-m-01', current_time( 'timestamp' ) ) );
$sales_by_date->end_date = strtotime( gmdate( 'Y-m-d', current_time( 'timestamp' ) ) );
$sales_by_date->chart_groupby = 'day';
$sales_by_date->group_by_query = 'YEAR(posts.post_date), MONTH(posts.post_date), DAY(posts.post_date)';
@ -117,7 +117,7 @@ if ( ! class_exists( 'WC_Admin_Dashboard', false ) ) :
?>
<li class="sales-this-month">
<a href="<?php echo esc_url( admin_url( 'admin.php?page=wc-reports&tab=orders&range=month' ) ); ?>">
<?php echo $reports->sales_sparkline( '', max( 7, date( 'd', current_time( 'timestamp' ) ) ) ); // phpcs:ignore WordPress.XSS.EscapeOutput.OutputNotEscaped ?>
<?php echo $reports->sales_sparkline( '', max( 7, gmdate( 'd', current_time( 'timestamp' ) ) ) ); // phpcs:ignore WordPress.XSS.EscapeOutput.OutputNotEscaped ?>
<?php
printf(
/* translators: %s: net sales */
@ -135,7 +135,7 @@ if ( ! class_exists( 'WC_Admin_Dashboard', false ) ) :
?>
<li class="best-seller-this-month">
<a href="<?php echo esc_url( admin_url( 'admin.php?page=wc-reports&tab=orders&report=sales_by_product&range=month&product_ids=' . $top_seller->product_id ) ); ?>">
<?php echo $reports->sales_sparkline( $top_seller->product_id, max( 7, date( 'd', current_time( 'timestamp' ) ) ), 'count' ); // phpcs:ignore WordPress.XSS.EscapeOutput.OutputNotEscaped ?>
<?php echo $reports->sales_sparkline( $top_seller->product_id, max( 7, gmdate( 'd', current_time( 'timestamp' ) ) ), 'count' ); // phpcs:ignore WordPress.XSS.EscapeOutput.OutputNotEscaped ?>
<?php
printf(
/* translators: 1: top seller product title 2: top seller quantity */
@ -318,7 +318,7 @@ if ( ! class_exists( 'WC_Admin_Dashboard', false ) ) :
);
$comments = $wpdb->get_results(
"SELECT posts.ID, posts.post_title, comments.comment_author, comments.comment_ID, comments.comment_content {$query_from};" // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
"SELECT posts.ID, posts.post_title, comments.comment_author, comments.comment_author_email, comments.comment_ID, comments.comment_content {$query_from};" // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
);
if ( $comments ) {
@ -327,7 +327,7 @@ if ( ! class_exists( 'WC_Admin_Dashboard', false ) ) :
echo '<li>';
echo get_avatar( $comment->comment_author, '32' );
echo get_avatar( $comment->comment_author_email, '32' );
$rating = intval( get_comment_meta( $comment->comment_ID, 'rating', true ) );

View File

@ -417,7 +417,7 @@ class WC_Admin_List_Table_Products extends WC_Admin_List_Table {
unset( $views['mine'] );
// Add sorting link.
if ( current_user_can( 'edit_others_pages' ) ) {
if ( current_user_can( 'edit_others_products' ) ) {
$class = ( isset( $wp_query->query['orderby'] ) && 'menu_order title' === $wp_query->query['orderby'] ) ? 'current' : '';
$query_string = remove_query_arg( array( 'orderby', 'order' ) );
$query_string = add_query_arg( 'orderby', rawurlencode( 'menu_order title' ), $query_string );

View File

@ -135,7 +135,7 @@ final class WC_Cart_Totals {
}
/**
* Run all calculations methods on the given items in sequence.
* Run all calculation methods on the given items in sequence.
*
* @since 3.2.0
*/

View File

@ -662,10 +662,10 @@ class WC_Cart extends WC_Legacy_Cart {
* Get weight of items in the cart.
*
* @since 2.5.0
* @return int
* @return float
*/
public function get_cart_contents_weight() {
$weight = 0;
$weight = 0.0;
foreach ( $this->get_cart() as $cart_item_key => $values ) {
if ( $values['data']->has_weight() ) {

View File

@ -520,6 +520,7 @@ class WC_Countries {
'IN' => "{company}\n{name}\n{address_1}\n{address_2}\n{city} {postcode}\n{state}, {country}",
'IS' => "{company}\n{name}\n{address_1}\n{address_2}\n{postcode} {city}\n{country}",
'IT' => "{company}\n{name}\n{address_1}\n{address_2}\n{postcode}\n{city}\n{state_upper}\n{country}",
'JM' => "{name}\n{company}\n{address_1}\n{address_2}\n{city}\n{state}\n{postcode_upper}\n{country}",
'JP' => "{postcode}\n{state} {city} {address_1}\n{address_2}\n{company}\n{last_name} {first_name}\n{country}",
'TW' => "{company}\n{last_name} {first_name}\n{address_1}\n{address_2}\n{state}, {city} {postcode}\n{country}",
'LI' => "{company}\n{name}\n{address_1}\n{address_2}\n{postcode} {city}\n{country}",
@ -1069,6 +1070,19 @@ class WC_Countries {
'label' => __( 'Province', 'woocommerce' ),
),
),
'JM' => array(
'city' => array(
'label' => __( 'Town / City / Post Office', 'woocommerce' ),
),
'postcode' => array(
'required' => false,
'label' => __( 'Postal Code', 'woocommerce' ),
),
'state' => array(
'required' => true,
'label' => __( 'Parish', 'woocommerce' ),
),
),
'JP' => array(
'last_name' => array(
'class' => array( 'form-row-first' ),

View File

@ -889,6 +889,8 @@ class WC_Form_Handler {
$quantity = empty( $_REQUEST['quantity'] ) ? 1 : wc_stock_amount( wp_unslash( $_REQUEST['quantity'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
$variations = array();
$product = wc_get_product( $product_id );
foreach ( $_REQUEST as $key => $value ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
if ( 'attribute_' !== substr( $key, 0, 10 ) ) {
continue;
@ -899,7 +901,22 @@ class WC_Form_Handler {
$passed_validation = apply_filters( 'woocommerce_add_to_cart_validation', true, $product_id, $quantity, $variation_id, $variations );
if ( $passed_validation && false !== WC()->cart->add_to_cart( $product_id, $quantity, $variation_id, $variations ) ) {
if ( ! $passed_validation ) {
return false;
}
// Prevent parent variable product from being added to cart.
if ( empty( $variation_id ) && $product->is_type( 'variable' ) ) {
$url = get_permalink( $product_id );
$product_name = $product->get_name();
/* translators: %1$s: Product link, %2$s: Product title, %3$s: Product name. */
wc_add_notice( sprintf( __( 'Please choose product options by visiting <a href="%1$s" title="%2$s">%3$s</a>.', 'woocommerce' ), esc_url( $url ), esc_html( $product_name ), esc_html( $product_name ) ), 'error' );
return false;
}
if ( false !== WC()->cart->add_to_cart( $product_id, $quantity, $variation_id, $variations ) ) {
wc_add_to_cart_message( array( $product_id => $quantity ), true );
return true;
}

View File

@ -497,7 +497,7 @@ class WC_Post_Types {
3 => __( 'Category updated.', 'woocommerce' ),
4 => __( 'Category not added.', 'woocommerce' ),
5 => __( 'Category not updated.', 'woocommerce' ),
6 => __( 'Category not deleted.', 'woocommerce' ),
6 => __( 'Categories deleted.', 'woocommerce' ),
);
$messages['product_tag'] = array(
@ -507,7 +507,7 @@ class WC_Post_Types {
3 => __( 'Tag updated.', 'woocommerce' ),
4 => __( 'Tag not added.', 'woocommerce' ),
5 => __( 'Tag not updated.', 'woocommerce' ),
6 => __( 'Tag not deleted.', 'woocommerce' ),
6 => __( 'Tags deleted.', 'woocommerce' ),
);
$wc_product_attributes = array();
@ -533,7 +533,7 @@ class WC_Post_Types {
/* translators: %s: taxonomy label */
5 => sprintf( _x( '%s not updated', 'taxonomy term messages', 'woocommerce' ), $label ),
/* translators: %s: taxonomy label */
6 => sprintf( _x( '%s not deleted', 'taxonomy term messages', 'woocommerce' ), $label ),
6 => sprintf( _x( '%s deleted', 'taxonomy term messages', 'woocommerce' ), $label ),
);
}
}

View File

@ -135,7 +135,7 @@ class WC_Product_Variable extends WC_Product {
* Note: Variable prices do not show suffixes like other product types. This
* is due to some things like tax classes being set at variation level which
* could differ from the parent price. The only way to show accurate prices
* would be to load the variation and get IT's price, which adds extra
* would be to load the variation and get it's price, which adds extra
* overhead and still has edge cases where the values would be inaccurate.
*
* Additionally, ranges of prices no longer show 'striked out' sale prices
@ -413,7 +413,7 @@ class WC_Product_Variable extends WC_Product {
* Sets an array of children for the product.
*
* @since 3.0.0
* @param array $children Childre products.
* @param array $children Children products.
*/
public function set_children( $children ) {
$this->children = array_filter( wp_parse_id_list( (array) $children ) );
@ -464,7 +464,7 @@ class WC_Product_Variable extends WC_Product {
* Trigger action before saving to the DB. Allows you to adjust object props before save.
*
* @param WC_Data $this The object being saved.
* @param WC_Data_Store_WP $data_store THe data store persisting the data.
* @param WC_Data_Store_WP $data_store The data store persisting the data.
*/
do_action( 'woocommerce_before_' . $this->object_type . '_object_save', $this, $this->data_store );
@ -485,7 +485,7 @@ class WC_Product_Variable extends WC_Product {
* Trigger action after saving to the DB.
*
* @param WC_Data $this The object being saved.
* @param WC_Data_Store_WP $data_store THe data store persisting the data.
* @param WC_Data_Store_WP $data_store The data store persisting the data.
*/
do_action( 'woocommerce_after_' . $this->object_type . '_object_save', $this, $this->data_store );
@ -590,7 +590,7 @@ class WC_Product_Variable extends WC_Product {
* @return boolean
*/
public function has_options() {
return true;
return apply_filters( 'woocommerce_product_has_options', true, $this );
}
@ -662,9 +662,9 @@ class WC_Product_Variable extends WC_Product {
}
/**
* Sort an associativate array of $variation_id => $price pairs in order of min and max prices.
* Sort an associative array of $variation_id => $price pairs in order of min and max prices.
*
* @param array $prices Associativate array of $variation_id => $price pairs.
* @param array $prices associative array of $variation_id => $price pairs.
* @return array
*/
protected function sort_variation_prices( $prices ) {

View File

@ -286,7 +286,7 @@ class WC_Product_Variation extends WC_Product_Simple {
/**
* Returns the tax class.
*
* Does not use get_prop so it can handle 'parent' Inheritance correctly.
* Does not use get_prop so it can handle 'parent' inheritance correctly.
*
* @param string $context view, edit, or unfiltered.
* @return string

View File

@ -22,7 +22,7 @@ final class WooCommerce {
*
* @var string
*/
public $version = '4.7.0';
public $version = '4.8.0';
/**
* WooCommerce Schema version.

View File

@ -66,9 +66,12 @@ abstract class WC_Gateway_Paypal_Response {
if ( ! $order->has_status( array( 'processing', 'completed' ) ) ) {
$order->add_order_note( $note );
$order->payment_complete( $txn_id );
if ( isset( WC()->cart ) ) {
WC()->cart->empty_cart();
}
}
}
/**
* Hold order and add note.
@ -78,6 +81,9 @@ abstract class WC_Gateway_Paypal_Response {
*/
protected function payment_on_hold( $order, $reason = '' ) {
$order->update_status( 'on-hold', $reason );
if ( isset( WC()->cart ) ) {
WC()->cart->empty_cart();
}
}
}

View File

@ -172,6 +172,17 @@ class WC_REST_Orders_V2_Controller extends WC_REST_CRUD_Controller {
$data['price'] = $item->get_quantity() ? $item->get_total() / $item->get_quantity() : 0;
}
// Add parent_name if the product is a variation.
if ( is_callable( array( $item, 'get_product' ) ) ) {
$product = $item->get_product();
if ( is_callable( array( $product, 'get_parent_data' ) ) ) {
$data['parent_name'] = $product->get_title();
} else {
$data['parent_name'] = null;
}
}
// Format taxes.
if ( ! empty( $data['taxes']['total'] ) ) {
$taxes = array();
@ -1231,6 +1242,11 @@ class WC_REST_Orders_V2_Controller extends WC_REST_CRUD_Controller {
'type' => 'mixed',
'context' => array( 'view', 'edit' ),
),
'parent_name' => array(
'description' => __( 'Parent product name if the product is a variation.', 'woocommerce' ),
'type' => 'string',
'context' => array( 'view', 'edit' ),
),
'product_id' => array(
'description' => __( 'Product ID.', 'woocommerce' ),
'type' => 'mixed',

View File

@ -198,7 +198,7 @@ add_action( 'get_header', 'wc_clear_cart_after_payment' );
* Get the subtotal.
*/
function wc_cart_totals_subtotal_html() {
echo WC()->cart->get_cart_subtotal(); // WPCS: XSS ok.
echo WC()->cart->get_cart_subtotal(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
/**
@ -244,7 +244,7 @@ function wc_cart_totals_shipping_html() {
* Get taxes total.
*/
function wc_cart_totals_taxes_total_html() {
echo apply_filters( 'woocommerce_cart_totals_taxes_total_html', wc_price( WC()->cart->get_taxes_total() ) ); // WPCS: XSS ok.
echo apply_filters( 'woocommerce_cart_totals_taxes_total_html', wc_price( WC()->cart->get_taxes_total() ) ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
/**
@ -264,7 +264,7 @@ function wc_cart_totals_coupon_label( $coupon, $echo = true ) {
$label = apply_filters( 'woocommerce_cart_totals_coupon_label', sprintf( esc_html__( 'Coupon: %s', 'woocommerce' ), $coupon->get_code() ), $coupon );
if ( $echo ) {
echo $label; // WPCS: XSS ok.
echo $label; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
} else {
return $label;
}
@ -328,7 +328,7 @@ function wc_cart_totals_order_total_html() {
}
}
echo apply_filters( 'woocommerce_cart_totals_order_total_html', $value ); // WPCS: XSS ok.
echo apply_filters( 'woocommerce_cart_totals_order_total_html', $value ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
/**
@ -339,7 +339,7 @@ function wc_cart_totals_order_total_html() {
function wc_cart_totals_fee_html( $fee ) {
$cart_totals_fee_html = WC()->cart->display_prices_including_tax() ? wc_price( $fee->total + $fee->tax ) : wc_price( $fee->total );
echo apply_filters( 'woocommerce_cart_totals_fee_html', $cart_totals_fee_html, $fee ); // WPCS: XSS ok.
echo apply_filters( 'woocommerce_cart_totals_fee_html', $cart_totals_fee_html, $fee ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
/**

View File

@ -1475,18 +1475,7 @@ if ( ! function_exists( 'woocommerce_show_product_images' ) ) {
* Output the product image before the single product summary.
*/
function woocommerce_show_product_images() {
global $product;
$post_thumbnail_id = $product->get_image_id();
$gallery_image_ids = '';
if ( ! $post_thumbnail_id ) {
$gallery_image_ids = $product->get_gallery_image_ids();
if ( ! empty( $gallery_image_ids ) ) {
$post_thumbnail_id = array_shift( $gallery_image_ids );
}
}
$args = compact( 'post_thumbnail_id', 'gallery_image_ids' );
wc_get_template( 'single-product/product-image.php', $args );
wc_get_template( 'single-product/product-image.php' );
}
}
if ( ! function_exists( 'woocommerce_show_product_thumbnails' ) ) {
@ -1495,13 +1484,7 @@ if ( ! function_exists( 'woocommerce_show_product_thumbnails' ) ) {
* Output the product thumbnails.
*/
function woocommerce_show_product_thumbnails() {
global $product;
$attachment_ids = $product->get_gallery_image_ids();
if ( $attachment_ids && ! $product->get_image_id() ) {
array_shift( $attachment_ids );
}
wc_get_template( 'single-product/product-thumbnails.php', array( 'attachment_ids' => $attachment_ids ) );
wc_get_template( 'single-product/product-thumbnails.php' );
}
}
@ -2874,7 +2857,7 @@ if ( ! function_exists( 'woocommerce_form_field' ) ) {
$field_html = '';
if ( $args['label'] && 'checkbox' !== $args['type'] ) {
$field_html .= '<label for="' . esc_attr( $label_id ) . '" class="' . esc_attr( implode( ' ', $args['label_class'] ) ) . '">' . $args['label'] . $required . '</label>';
$field_html .= '<label for="' . esc_attr( $label_id ) . '" class="' . esc_attr( implode( ' ', $args['label_class'] ) ) . '">' . wp_kses_post( $args['label'] ) . $required . '</label>';
}
$field_html .= '<span class="woocommerce-input-wrapper">' . $field;

View File

@ -1,7 +1,7 @@
{
"name": "woocommerce",
"title": "WooCommerce",
"version": "4.7.0",
"version": "4.8.0",
"homepage": "https://woocommerce.com/",
"repository": {
"type": "git",
@ -22,8 +22,9 @@
"build:zip": "npm run build && composer install && npm run build:dev",
"build:assets": "grunt assets",
"lint:js": "eslint assets/js --ext=js",
"docker:up": "npm explore @woocommerce/e2e-environment -- npm run docker:up",
"docker:down": "npm explore @woocommerce/e2e-environment -- npm run docker:down",
"docker:ssh": "npm explore @woocommerce/e2e-environment -- npm run docker:ssh",
"docker:up": "npm explore @woocommerce/e2e-environment -- npm run docker:up",
"test:e2e": "npm explore @woocommerce/e2e-environment -- npm run test:e2e",
"test:e2e-debug": "npm explore @woocommerce/e2e-environment -- npm run test:e2e-debug",
"test:e2e-dev": "npm explore @woocommerce/e2e-environment -- npm run test:e2e-dev",

View File

@ -1,10 +1,10 @@
=== WooCommerce ===
Contributors: automattic, mikejolley, jameskoster, claudiosanches, kloon, rodrigosprimo, peterfabian1000, vedjain, jamosova, obliviousharmony, konamiman, sadowski
Contributors: automattic, mikejolley, jameskoster, claudiosanches, rodrigosprimo, peterfabian1000, vedjain, jamosova, obliviousharmony, konamiman, sadowski, wpmuguru, royho
Tags: e-commerce, store, sales, sell, woo, shop, cart, checkout, downloadable, downloads, payments, paypal, storefront, stripe, woo commerce
Requires at least: 5.3
Tested up to: 5.5
Requires PHP: 7.0
Stable tag: 4.6.0
Stable tag: 4.6.1
License: GPLv3
License URI: https://www.gnu.org/licenses/gpl-3.0.html
@ -160,7 +160,7 @@ WooCommerce comes with some sample data you can use to see how products look; im
== Changelog ==
= 4.7.0 - 2020-11-xx =
= 4.8.0 - 2020-12-xx =
[See changelog for all versions](https://raw.githubusercontent.com/woocommerce/woocommerce/master/changelog.txt).

View File

@ -45,7 +45,7 @@ abstract class AbstractServiceProvider extends \Automattic\WooCommerce\Vendor\Le
$default_value = $argument->getDefaultValue();
$definition->addArgument( new RawArgument( $default_value ) );
} else {
$argument_class = $argument->getClass();
$argument_class = $this->get_class( $argument );
if ( is_null( $argument_class ) ) {
throw new ContainerException( "Argument '{$argument->getName()}' of class '$class_name' doesn't have a type hint or has one that doesn't specify a class." );
}
@ -61,6 +61,27 @@ abstract class AbstractServiceProvider extends \Automattic\WooCommerce\Vendor\Le
return $definition;
}
/**
* Gets the class of a parameter.
*
* This method is a replacement for ReflectionParameter::getClass,
* which is deprecated as of PHP 8.
*
* @param \ReflectionParameter $parameter The parameter to get the class for.
*
* @return \ReflectionClass|null The class of the parameter, or null if it hasn't any.
*/
private function get_class( \ReflectionParameter $parameter ) {
// TODO: Remove this 'if' block once minimum PHP version for WooCommerce is bumped to at least 7.1.
if ( version_compare( PHP_VERSION, '7.1', '<' ) ) {
return $parameter->getClass();
}
return $parameter->getType() && ! $parameter->getType()->isBuiltin()
? new \ReflectionClass( $parameter->getType()->getName() )
: null;
}
/**
* Check if a combination of class name and concrete is valid for registration.
* Also return the class injection method if the concrete is either a class name or null (then use the supplied class name).

View File

@ -12,7 +12,7 @@
*
* @see https://docs.woocommerce.com/document/template-structure/
* @package WooCommerce\Templates
* @version 4.7.0
* @version 3.5.1
*/
defined( 'ABSPATH' ) || exit;
@ -25,11 +25,12 @@ if ( ! function_exists( 'wc_get_gallery_image_html' ) ) {
global $product;
$columns = apply_filters( 'woocommerce_product_thumbnails_columns', 4 );
$post_thumbnail_id = $product->get_image_id();
$wrapper_classes = apply_filters(
'woocommerce_single_product_image_gallery_classes',
array(
'woocommerce-product-gallery',
'woocommerce-product-gallery--' . ( $post_thumbnail_id ? 'with-images' : 'without-images' ),
'woocommerce-product-gallery--' . ( $product->get_image_id() ? 'with-images' : 'without-images' ),
'woocommerce-product-gallery--columns-' . absint( $columns ),
'images',
)
@ -38,7 +39,7 @@ $wrapper_classes = apply_filters(
<div class="<?php echo esc_attr( implode( ' ', array_map( 'sanitize_html_class', $wrapper_classes ) ) ); ?>" data-columns="<?php echo esc_attr( $columns ); ?>" style="opacity: 0; transition: opacity .25s ease-in-out;">
<figure class="woocommerce-product-gallery__wrapper">
<?php
if ( $post_thumbnail_id ) {
if ( $product->get_image_id() ) {
$html = wc_get_gallery_image_html( $post_thumbnail_id, true );
} else {
$html = '<div class="woocommerce-product-gallery__image--placeholder">';

View File

@ -12,7 +12,7 @@
*
* @see https://docs.woocommerce.com/document/template-structure/
* @package WooCommerce\Templates
* @version 4.7.0
* @version 3.5.1
*/
defined( 'ABSPATH' ) || exit;
@ -24,7 +24,9 @@ if ( ! function_exists( 'wc_get_gallery_image_html' ) ) {
global $product;
if ( $attachment_ids ) {
$attachment_ids = $product->get_gallery_image_ids();
if ( $attachment_ids && $product->get_image_id() ) {
foreach ( $attachment_ids as $attachment_id ) {
echo apply_filters( 'woocommerce_single_product_image_thumbnail_html', wc_get_gallery_image_html( $attachment_id ), $attachment_id ); // phpcs:disable WordPress.XSS.EscapeOutput.OutputNotEscaped
}

View File

@ -5,12 +5,7 @@ if [[ ${RUN_PHPCS} == 1 ]]; then
IGNORE="tests/cli/,includes/libraries/,includes/api/legacy/"
if [ "$CHANGED_FILES" != "" ]; then
if [ ! -f "./vendor/bin/phpcs" ]; then
# Install wpcs globally
composer global require woocommerce/woocommerce-sniffs --update-with-all-dependencies
fi
echo "Running Code Sniffer."
phpcs --ignore=$IGNORE --encoding=utf-8 -s -n -p $CHANGED_FILES
vendor/bin/phpcs --ignore=$IGNORE --encoding=utf-8 -s -n -p $CHANGED_FILES
fi
fi

View File

@ -88,6 +88,7 @@ The test sequencer uses the following default Puppeteer configuration:
launch: {
...jestPuppeteerConfig.launch, // @automattic/puppeteer-utils
ignoreHTTPSErrors: true,
headless: false,
args: [ '--window-size=1920,1080', '--user-agent=chrome' ],
devtools: true,
defaultViewport: {
@ -116,31 +117,13 @@ module.exports = puppeteerConfig;
Jest provides setup and teardown functions similar to PHPUnit. The default setup and teardown is in [`tests/e2e/env/src/setup/jest.setup.js`](src/setup/jest.setup.js). Additional setup and teardown functions can be added to [`tests/e2e/config/jest.setup.js`](../config/jest.setup.js)
### Webpack Config
The E2E environment provides a `@woocommerce/e2e-utils` alias for easy use of the WooCommerce E2E test helpers.
```js
const { webpackAlias: coreE2EAlias } = require( '@woocommerce/e2e-environment' );
module.exports = {
....
resolve: {
alias: {
...coreE2EAlias,
....
},
},
};
```
### Container Setup
Depending on the project and testing scenario, the built in testing environment container might not be the best solution for testing. This could be local testing where there is already a testing container, a repository that isn't a plugin or theme and there are multiple folders mapped into the container, or similar. The `e2e-environment` container supports using either the built in container or an external container. See the the appropriate readme for details:
- [Built In Container](./builtin.md)
- [External Container](./external.md)
- [Built In Container](https://github.com/woocommerce/woocommerce/tree/master/tests/e2e/env/builtin.md)
- [External Container](https://github.com/woocommerce/woocommerce/tree/master/tests/e2e/env/external.md)
## Additional information
Refer to [`tests/e2e/specs`](https://github.com/woocommerce/woocommerce/tree/master/tests/e2e/specs) for some test examples, and [`tests/e2e`](https://github.com/woocommerce/woocommerce/tree/master/tests/e2e) for general information on e2e tests.
Refer to [`tests/e2e/core-tests`](https://github.com/woocommerce/woocommerce/tree/master/tests/e2e/core-tests) for some test examples, and [`tests/e2e`](https://github.com/woocommerce/woocommerce/tree/master/tests/e2e) for general information on e2e tests.

View File

@ -33,3 +33,66 @@ describe( 'Cart page', () => {
} );
} );
~~~
## Test Function
### Merchant `StoreOwnerFlow`
| Function | Description |
|----------|-------------|
| `login` | Log in as merchant |
| `logout` | log out of merchant account |
| `openAllOrdersView` | Go to the orders listing |
| `openDashboard` | Go to the WordPress dashboard |
| `openNewCoupon` | Go to the new coupon editor |
| `openNewOrder` | Go to the new order editor |
| `openNewProduct` | Go to the new product editor |
| `openPermalinkSettings` | Go to Settings -> Permalinks |
| `openPlugins` | Go to the Plugins screen |
| `openSettings` | Go to WooCommerce -> Settings |
| `runSetupWizard` | Open the onboarding profiler |
|----------|-------------|
### Shopper `CustomerFlow`
| Function | Parameters | Description |
|----------|------------|-------------|
| `addToCart` | | Add an item to the cart from a single product page |
| `addToCartFromShopPage` | `productTitle` | Add an item to the cart from a single product page |
| `fillBillingDetails` | `customerBillingDetails` | Fill billing fields in checkout form using configured address |
| `fillShippingDetails` | `customerShippingDetails` | Fill shipping fields in checkout form using configured address |
| `goToAddresses` | | Go to My Account -> Address Details |
| `goToAccountDetails` | | Go to My Account -> Details |
| `goToCart` | | Go to the cart page |
| `goToCheckout` | | Go to the checkout page |
| `goToShop` | | Go to the shop page |
| `goToProduct` | `productId` | Go to a single product in the shop |
| `goToOrders` | | Go to My Account -> Orders |
| `goToDownloads` | | Go to My Account -> Downloads |
| `login` | | Log in as the shopper |
| `placeOrder` | | Place an order from the checkout page |
| `productIsInCheckout` | `productTitle, quantity, total, cartSubtotal` | Verify product is in cart on checkout page |
| `removeFromCart` | `productTitle` | Remove a product from the cart on the cart page |
| `setCartQuantity` | `productTitle, quantityValue` | Change the quantity of a product on the cart page |
|----------|------------|-------------|
### Page Utilities
| Function | Parameters | Description |
|----------|------------|-------------|
| `clearAndFillInput` | `selector, value` | Replace the contents of an input with the passed value |
| `clickTab` | `tabName` | Click on a WooCommerce -> Settings tab |
| `settingsPageSaveChanges` | | Save the current WooCommerce settings page |
| `permalinkSettingsPageSaveChanges` | | Save the current Permalink settings |
| `setCheckbox` | `selector` | Check a checkbox |
| `unsetCheckbox` | `selector` | Uncheck a checkbox |
| `uiUnblocked` | | Wait until the page is unblocked |
| `verifyPublishAndTrash` | `button, publishNotice, publishVerification, trashVerification` | Verify that an item can be published and trashed |
| `verifyCheckboxIsSet` | `selector` | Verify that a checkbox is checked |
| `verifyCheckboxIsUnset` | `selector` | Verify that a checkbox is unchecked |
| `verifyValueOfInputField` | `selector, value` | Verify an input contains the passed value |
|----------|------------|-------------|
### Test Utilities
As of version 0.1.2, all test utilities from [`@wordpress/e2e-test-utils`](https://www.npmjs.com/package/@wordpress/e2e-test-utils) are available through this package.

View File

@ -125,7 +125,6 @@ const CustomerFlow = {
} );
},
goToShop: async () => {
await page.goto(SHOP_PAGE, {
waitUntil: 'networkidle0',

View File

@ -1,44 +1,11 @@
import { CustomerFlow, StoreOwnerFlow } from './flows';
/*
* External dependencies
*/
export * from '@wordpress/e2e-test-utils';
/*
* Internal dependencies
*/
export * from './flows';
export * from './components';
export * from './page-utils';
import {
completeOnboardingWizard,
completeOldSetupWizard,
createSimpleProduct,
createVariableProduct,
verifyAndPublish,
} from './components';
import {
clearAndFillInput,
clickTab,
settingsPageSaveChanges,
permalinkSettingsPageSaveChanges,
setCheckbox,
unsetCheckbox,
uiUnblocked,
verifyPublishAndTrash,
verifyCheckboxIsSet,
verifyCheckboxIsUnset,
verifyValueOfInputField,
} from './page-utils';
module.exports = {
CustomerFlow,
StoreOwnerFlow,
completeOnboardingWizard,
completeOldSetupWizard,
createSimpleProduct,
createVariableProduct,
verifyAndPublish,
clearAndFillInput,
clickTab,
settingsPageSaveChanges,
permalinkSettingsPageSaveChanges,
setCheckbox,
unsetCheckbox,
uiUnblocked,
verifyPublishAndTrash,
verifyCheckboxIsSet,
verifyCheckboxIsUnset,
verifyValueOfInputField
}

View File

@ -5,6 +5,10 @@
* @package WooCommerce\Tests\API
* @since 3.0.0
*/
/**
* Class WC_Tests_API_Orders_V2
*/
class WC_Tests_API_Orders_V2 extends WC_REST_Unit_Test_Case {
/**
@ -77,7 +81,7 @@ class WC_Tests_API_Orders_V2 extends WC_REST_Unit_Test_Case {
$site_level_attribute_id = wc_create_attribute( array( 'name' => 'Site Level Color' ) );
$site_level_attribute_slug = wc_attribute_taxonomy_name_by_id( $site_level_attribute_id );
// Register the attribute so that wp_insert_term will be successful
// Register the attribute so that wp_insert_term will be successful.
register_taxonomy( $site_level_attribute_slug, array( 'product' ), array() );
$site_level_term_insertion_result = wp_insert_term( 'Site Level Value - Blue', $site_level_attribute_slug );
@ -88,9 +92,7 @@ class WC_Tests_API_Orders_V2 extends WC_REST_Unit_Test_Case {
$line_item = new WC_Order_Item_Product();
$line_item->set_product( $variation );
$line_item->set_props( array(
'variation' => array( "attribute_{$site_level_attribute_slug}" => $site_level_term->slug )
) );
$line_item->set_props( array( 'variation' => array( "attribute_{$site_level_attribute_slug}" => $site_level_term->slug ) ) );
$order = \Automattic\WooCommerce\RestApi\UnitTests\Helpers\OrderHelper::create_order();
$order->add_item( $line_item );
@ -358,7 +360,7 @@ class WC_Tests_API_Orders_V2 extends WC_REST_Unit_Test_Case {
wp_set_current_user( $this->user );
$product = \Automattic\WooCommerce\RestApi\UnitTests\Helpers\ProductHelper::create_simple_product();
// non-existent customer
// Non-existent customer.
$request = new WP_REST_Request( 'POST', '/wc/v2/orders' );
$request->set_body_params(
array(
@ -542,6 +544,7 @@ class WC_Tests_API_Orders_V2 extends WC_REST_Unit_Test_Case {
'meta_data' => array(),
'sku' => null,
'price' => 4,
'parent_name' => null,
);
$this->assertEquals( 200, $response->get_status() );
@ -782,12 +785,13 @@ class WC_Tests_API_Orders_V2 extends WC_REST_Unit_Test_Case {
$data = $response->get_data();
$line_item_properties = $data['schema']['properties']['line_items']['items']['properties'];
$this->assertEquals( 14, count( $line_item_properties ) );
$this->assertEquals( 15, count( $line_item_properties ) );
$this->assertArrayHasKey( 'id', $line_item_properties );
$this->assertArrayHasKey( 'meta_data', $line_item_properties );
$this->assertArrayHasKey( 'parent_name', $line_item_properties );
$meta_data_item_properties = $line_item_properties['meta_data']['items']['properties'];
$this->assertEquals( 5, count( $meta_data_item_properties ) );
$this->assertEquals( [ 'id', 'key', 'value', 'display_key', 'display_value' ], array_keys( $meta_data_item_properties ) );
$this->assertEquals( array( 'id', 'key', 'value', 'display_key', 'display_value' ), array_keys( $meta_data_item_properties ) );
}
}

View File

@ -199,6 +199,39 @@ class WC_Tests_API_Orders extends WC_REST_Unit_Test_Case {
$this->assertEquals( $term_name, $color_meta_data['display_value'] );
}
/**
* Tests that line items for variations includes the parent product name.
*/
public function test_get_item_with_variation_parent_name() {
wp_set_current_user( $this->user );
$product = \Automattic\WooCommerce\RestApi\UnitTests\Helpers\ProductHelper::create_variation_product();
$variation = wc_get_product( $product->get_children()[0] );
$line_item = new WC_Order_Item_Product();
$line_item->set_product( $variation );
$order = \Automattic\WooCommerce\RestApi\UnitTests\Helpers\OrderHelper::create_order();
$order->add_item( $line_item );
$order->save();
$response = $this->server->dispatch( new WP_REST_Request( 'GET', '/wc/v3/orders/' . $order->get_id() ) );
$data = $response->get_data();
$this->assertEquals( 200, $response->get_status() );
$this->assertEquals( $order->get_id(), $data['id'] );
$last_line_item = array_slice( $data['line_items'], -1 )[0];
// The name property contains the parent product name and the attribute value (ex. "Dummy Variable Product - small").
$this->assertEquals( $variation->get_name(), $last_line_item['name'] );
$this->assertTrue( false !== strpos( $last_line_item['name'], $variation->get_attribute( 'pa_size' ) ) );
// The parent_name property only contains the parent product name (ex. "Dummy Variable Product").
$this->assertEquals( $product->get_name(), $last_line_item['parent_name'] );
$this->assertNotEquals( $variation->get_name(), $last_line_item['parent_name'] );
$this->assertTrue( false === strpos( $last_line_item['parent_name'], $variation->get_attribute( 'pa_size' ) ) );
}
/**
* Tests getting an order with an invalid ID.
* @since 3.5.0
@ -612,6 +645,7 @@ class WC_Tests_API_Orders extends WC_REST_Unit_Test_Case {
'meta_data' => array(),
'sku' => null,
'price' => 4,
'parent_name' => null,
);
$this->assertEquals( 200, $response->get_status() );
@ -878,9 +912,10 @@ class WC_Tests_API_Orders extends WC_REST_Unit_Test_Case {
$data = $response->get_data();
$line_item_properties = $data['schema']['properties']['line_items']['items']['properties'];
$this->assertEquals( 14, count( $line_item_properties ) );
$this->assertEquals( 15, count( $line_item_properties ) );
$this->assertArrayHasKey( 'id', $line_item_properties );
$this->assertArrayHasKey( 'meta_data', $line_item_properties );
$this->assertArrayHasKey( 'parent_name', $line_item_properties );
$meta_data_item_properties = $line_item_properties['meta_data']['items']['properties'];
$this->assertEquals( 5, count( $meta_data_item_properties ) );

View File

@ -132,4 +132,26 @@ class WC_Cart_Test extends \WC_Unit_Test_Case {
WC()->cart->get_customer()->set_shipping_state( '' );
WC()->cart->get_customer()->set_shipping_postcode( '' );
}
/**
* Test adding a variable product without selecting variations.
*
* @see WC_Form_Handler::add_to_cart_action()
*/
public function test_form_handler_add_to_cart_action_with_parent_variable_product() {
$this->tearDown();
$product = WC_Helper_Product::create_variation_product();
$product_id = $product->get_id();
$url = get_permalink( $product_id );
$_REQUEST['add-to-cart'] = $product_id;
WC_Form_Handler::add_to_cart_action();
$notices = WC()->session->get( 'wc_notices', array() );
$this->assertArrayHasKey( 'error', $notices );
$this->assertCount( 1, $notices['error'] );
$this->assertRegExp( '/Please choose product options by visiting/', $notices['error'][0]['notice'] );
}
}

View File

@ -94,7 +94,7 @@ class AbstractServiceProviderTest extends \WC_Unit_Test_Case {
*/
public function test_add_with_auto_arguments_throws_on_class_private_method_injection() {
$this->expectException( ContainerException::class );
$this->expectExceptionMessage( "Method '" . Definition::INJECTION_METHOD . "' of class '" . ClassWithPrivateInjectionMethod::class . "' isn't 'public', instances can't be created." );
$this->expectExceptionMessage( "Method '" . Definition::INJECTION_METHOD . "' of class '" . ClassWithPrivateInjectionMethod::class . "' isn't 'final public', instances can't be created." );
$this->sut->add_with_auto_arguments( ClassWithPrivateInjectionMethod::class );
}
@ -114,7 +114,7 @@ class AbstractServiceProviderTest extends \WC_Unit_Test_Case {
*/
public function test_add_with_auto_arguments_throws_on_concrete_private_method_injection() {
$this->expectException( ContainerException::class );
$this->expectExceptionMessage( "Method '" . Definition::INJECTION_METHOD . "' of class '" . ClassWithPrivateInjectionMethod::class . "' isn't 'public', instances can't be created." );
$this->expectExceptionMessage( "Method '" . Definition::INJECTION_METHOD . "' of class '" . ClassWithPrivateInjectionMethod::class . "' isn't 'final public', instances can't be created." );
$this->sut->add_with_auto_arguments( ClassWithDependencies::class, ClassWithPrivateInjectionMethod::class );
}

View File

@ -12,13 +12,15 @@ namespace Automattic\WooCommerce\Tests\Internal\DependencyManagement\ExampleClas
*/
class ClassWithPrivateInjectionMethod {
// phpcs:disable WooCommerce.Functions.InternalInjectionMethod.MissingPublic
// phpcs:disable WooCommerce.Functions.InternalInjectionMethod.MissingPublic, WooCommerce.Functions.InternalInjectionMethod.MissingFinal
/**
* Initialize the class instance.
*
* @internal
*/
final private function init() {
private function init() {
}
// phpcs:enable
}

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: 4.7.0-dev
* Version: 4.8.0-dev
* Author: Automattic
* Author URI: https://woocommerce.com
* Text Domain: woocommerce