Merge branch 'master' into e2e-shopper-grouped-product
This commit is contained in:
commit
6d6554ccbd
|
@ -0,0 +1,32 @@
|
|||
name: Build zip for PR
|
||||
on:
|
||||
pull_request
|
||||
jobs:
|
||||
build:
|
||||
name: Build zip for PR
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Build
|
||||
id: build
|
||||
uses: woocommerce/action-build@v2
|
||||
- name: Upload PR zip
|
||||
uses: actions/upload-artifact@v2
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
name: woocommerce.zip
|
||||
path: ${{ steps.build.outputs.zip_path }}
|
||||
retention-days: 7
|
||||
- name: Add comment
|
||||
uses: actions/github-script@v3
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
github.issues.createComment({
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body: ':package: Artifacts ready for [download](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})!'
|
||||
})
|
|
@ -0,0 +1,70 @@
|
|||
name: Run unit tests on PR
|
||||
on:
|
||||
pull_request
|
||||
jobs:
|
||||
test:
|
||||
name: PHP ${{ matrix.php }} WP ${{ matrix.wp }}
|
||||
timeout-minutes: 15
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
php: [ '7.0', '7.1', '7.2', '7.3', '7.4', '8.0' ]
|
||||
wp: [ "latest" ]
|
||||
include:
|
||||
- wp: nightly
|
||||
php: '7.4'
|
||||
- wp: '5.5'
|
||||
php: 7.2
|
||||
- wp: '5.4'
|
||||
php: 7.2
|
||||
services:
|
||||
database:
|
||||
image: mysql:5.6
|
||||
env:
|
||||
MYSQL_ROOT_PASSWORD: root
|
||||
ports:
|
||||
- 3306:3306
|
||||
options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=5
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: ${{ matrix.php }}
|
||||
tools: composer
|
||||
extensions: mysql
|
||||
coverage: none
|
||||
|
||||
- name: Tool versions
|
||||
run: |
|
||||
php --version
|
||||
composer --version
|
||||
|
||||
- name: Get cached composer directories
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: |
|
||||
./packages
|
||||
./vendor
|
||||
key: ${{ runner.os }}-${{ hashFiles('./composer.lock') }}
|
||||
|
||||
- name: Setup and install composer
|
||||
run: composer install
|
||||
|
||||
- name: Add PHP8 Compatibility.
|
||||
run: |
|
||||
if [ "$(php -r "echo version_compare(PHP_VERSION,'8.0','>=');")" ]; then
|
||||
curl -L https://github.com/woocommerce/phpunit/archive/add-compatibility-with-php8-to-phpunit-7.zip -o /tmp/phpunit-7.5-fork.zip
|
||||
unzip -d /tmp/phpunit-7.5-fork /tmp/phpunit-7.5-fork.zip
|
||||
composer bin phpunit config --unset platform
|
||||
composer bin phpunit config repositories.0 '{"type": "path", "url": "/tmp/phpunit-7.5-fork/phpunit-add-compatibility-with-php8-to-phpunit-7", "options": {"symlink": false}}'
|
||||
composer bin phpunit require --dev -W phpunit/phpunit:@dev --ignore-platform-reqs
|
||||
fi
|
||||
|
||||
- name: Init DB and WP
|
||||
run: ./tests/bin/install.sh woo_test root root 127.0.0.1 ${{ matrix.wp }}
|
||||
|
||||
- name: Run tests
|
||||
run: ./vendor/bin/phpunit -c ./phpunit.xml
|
|
@ -36,9 +36,9 @@ jobs:
|
|||
- npm install
|
||||
- composer install --no-dev
|
||||
script:
|
||||
- travis_retry npm run build:assets
|
||||
- travis_retry npm run docker:up
|
||||
- travis_retry npm run test:e2e
|
||||
- npm run build:assets
|
||||
- npm run docker:up
|
||||
- npm run test:e2e
|
||||
after_script:
|
||||
- npm run docker:down
|
||||
- name: "WP Nightly"
|
||||
|
|
|
@ -126,12 +126,18 @@ a.button {
|
|||
}
|
||||
}
|
||||
|
||||
.woocommerce-breadcrumb {
|
||||
margin-bottom: 5rem;
|
||||
font-size: 0.88889em;
|
||||
font-family: $headings;
|
||||
.site-main {
|
||||
.woocommerce-breadcrumb {
|
||||
margin-bottom: var(--global--spacing-vertical);
|
||||
font-size: 0.88889em;
|
||||
font-family: $headings;
|
||||
}
|
||||
.woocommerce-products-header {
|
||||
margin-top: var(--global--spacing-vertical);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.woocommerce-pagination {
|
||||
font-family: $headings;
|
||||
font-size: 0.88889em;
|
||||
|
@ -350,6 +356,10 @@ a.button {
|
|||
border-left: none;
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
.product-thumbnail {
|
||||
max-width: 120px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -537,11 +547,16 @@ dl.variation,
|
|||
display: none;
|
||||
}
|
||||
|
||||
.entry-title {
|
||||
margin: 0 0 2.5rem;
|
||||
|
||||
&::before {
|
||||
margin-top: 0;
|
||||
&.singular { // Needed for higher specificity to target the entry title font size
|
||||
.entry-title {
|
||||
font-size: var(--global--font-size-xl);
|
||||
font-weight: normal;
|
||||
margin: 0 0 2.5rem;
|
||||
|
||||
&::before {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -829,7 +844,7 @@ a.reset_variations {
|
|||
}
|
||||
|
||||
h2:first-of-type {
|
||||
font-size: 3rem;
|
||||
font-size: var(--global--font-size-lg);
|
||||
margin: 0 0 2rem !important;
|
||||
}
|
||||
}
|
||||
|
@ -1424,10 +1439,6 @@ a.reset_variations {
|
|||
|
||||
#main {
|
||||
|
||||
.entry-header {
|
||||
padding: 3vw 0 1.5vw;
|
||||
}
|
||||
|
||||
.woocommerce {
|
||||
max-width: var(--responsive--alignwide-width);
|
||||
margin: 0 auto;
|
||||
|
@ -1860,6 +1871,10 @@ a.reset_variations {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
tfoot {
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
.woocommerce-order-received {
|
||||
|
@ -2788,11 +2803,6 @@ a.reset_variations {
|
|||
|
||||
.woocommerce-account {
|
||||
|
||||
.entry-header {
|
||||
|
||||
padding-bottom: 20px !important;
|
||||
}
|
||||
|
||||
.woocommerce-MyAccount-content {
|
||||
|
||||
p:first-of-type {
|
||||
|
|
|
@ -71,10 +71,6 @@
|
|||
"stylecheck",
|
||||
"tests"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/dealerdirect/phpcodesniffer-composer-installer/issues",
|
||||
"source": "https://github.com/dealerdirect/phpcodesniffer-composer-installer"
|
||||
},
|
||||
"time": "2020-06-25T14:57:39+00:00"
|
||||
},
|
||||
{
|
||||
|
@ -133,10 +129,6 @@
|
|||
"phpcs",
|
||||
"standards"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/PHPCompatibility/PHPCompatibility/issues",
|
||||
"source": "https://github.com/PHPCompatibility/PHPCompatibility"
|
||||
},
|
||||
"time": "2019-12-27T09:44:58+00:00"
|
||||
},
|
||||
{
|
||||
|
@ -189,10 +181,6 @@
|
|||
"polyfill",
|
||||
"standards"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/PHPCompatibility/PHPCompatibilityParagonie/issues",
|
||||
"source": "https://github.com/PHPCompatibility/PHPCompatibilityParagonie"
|
||||
},
|
||||
"time": "2019-11-04T15:17:54+00:00"
|
||||
},
|
||||
{
|
||||
|
@ -243,10 +231,6 @@
|
|||
"standards",
|
||||
"wordpress"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/PHPCompatibility/PHPCompatibilityWP/issues",
|
||||
"source": "https://github.com/PHPCompatibility/PHPCompatibilityWP"
|
||||
},
|
||||
"time": "2019-08-28T14:22:28+00:00"
|
||||
},
|
||||
{
|
||||
|
@ -343,10 +327,6 @@
|
|||
"woocommerce",
|
||||
"wordpress"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/woocommerce/woocommerce-sniffs/issues",
|
||||
"source": "https://github.com/woocommerce/woocommerce-sniffs/tree/master"
|
||||
},
|
||||
"time": "2020-08-06T18:23:45+00:00"
|
||||
},
|
||||
{
|
||||
|
@ -393,11 +373,6 @@
|
|||
"standards",
|
||||
"wordpress"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/WordPress/WordPress-Coding-Standards/issues",
|
||||
"source": "https://github.com/WordPress/WordPress-Coding-Standards",
|
||||
"wiki": "https://github.com/WordPress/WordPress-Coding-Standards/wiki"
|
||||
},
|
||||
"time": "2020-05-13T23:57:56+00:00"
|
||||
}
|
||||
],
|
||||
|
@ -411,5 +386,5 @@
|
|||
"platform-overrides": {
|
||||
"php": "7.0"
|
||||
},
|
||||
"plugin-api-version": "2.0.0"
|
||||
"plugin-api-version": "1.1.0"
|
||||
}
|
||||
|
|
|
@ -332,10 +332,6 @@
|
|||
}
|
||||
],
|
||||
"description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.",
|
||||
"support": {
|
||||
"issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues",
|
||||
"source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/release/4.x"
|
||||
},
|
||||
"time": "2019-12-28T18:55:12+00:00"
|
||||
},
|
||||
{
|
||||
|
@ -448,10 +444,6 @@
|
|||
"spy",
|
||||
"stub"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/phpspec/prophecy/issues",
|
||||
"source": "https://github.com/phpspec/prophecy/tree/v1.10.3"
|
||||
},
|
||||
"time": "2020-03-05T15:02:03+00:00"
|
||||
},
|
||||
{
|
||||
|
@ -612,10 +604,6 @@
|
|||
"keywords": [
|
||||
"template"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/php-text-template/issues",
|
||||
"source": "https://github.com/sebastianbergmann/php-text-template/tree/1.2.1"
|
||||
},
|
||||
"time": "2015-06-21T13:50:34+00:00"
|
||||
},
|
||||
{
|
||||
|
@ -1236,10 +1224,6 @@
|
|||
"keywords": [
|
||||
"global state"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/global-state/issues",
|
||||
"source": "https://github.com/sebastianbergmann/global-state/tree/2.0.0"
|
||||
},
|
||||
"time": "2017-04-27T15:39:26+00:00"
|
||||
},
|
||||
{
|
||||
|
@ -1504,10 +1488,6 @@
|
|||
],
|
||||
"description": "Library that helps with managing the version number of Git-hosted PHP projects",
|
||||
"homepage": "https://github.com/sebastianbergmann/version",
|
||||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/version/issues",
|
||||
"source": "https://github.com/sebastianbergmann/version/tree/master"
|
||||
},
|
||||
"time": "2016-10-03T07:35:21+00:00"
|
||||
},
|
||||
{
|
||||
|
@ -1627,10 +1607,6 @@
|
|||
}
|
||||
],
|
||||
"description": "A small library for converting tokenized PHP source code into XML and potentially other formats",
|
||||
"support": {
|
||||
"issues": "https://github.com/theseer/tokenizer/issues",
|
||||
"source": "https://github.com/theseer/tokenizer/tree/master"
|
||||
},
|
||||
"time": "2019-06-13T22:48:21+00:00"
|
||||
},
|
||||
{
|
||||
|
@ -1680,10 +1656,6 @@
|
|||
"check",
|
||||
"validate"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/webmozart/assert/issues",
|
||||
"source": "https://github.com/webmozart/assert/tree/master"
|
||||
},
|
||||
"time": "2020-07-08T17:02:28+00:00"
|
||||
}
|
||||
],
|
||||
|
@ -1697,5 +1669,5 @@
|
|||
"platform-overrides": {
|
||||
"php": "7.0"
|
||||
},
|
||||
"plugin-api-version": "2.0.0"
|
||||
"plugin-api-version": "1.1.0"
|
||||
}
|
||||
|
|
|
@ -133,24 +133,20 @@
|
|||
"translations",
|
||||
"unicode"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/php-gettext/Languages/issues",
|
||||
"source": "https://github.com/php-gettext/Languages/tree/2.6.0"
|
||||
},
|
||||
"time": "2019-11-13T10:30:21+00:00"
|
||||
},
|
||||
{
|
||||
"name": "mck89/peast",
|
||||
"version": "v1.11.0",
|
||||
"version": "v1.12.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/mck89/peast.git",
|
||||
"reference": "2a2bc6826114c46ff0bc1359208b7083a17f7a99"
|
||||
"reference": "833be7a294627a8c5b1c482cbf489f73bf9b8086"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/mck89/peast/zipball/2a2bc6826114c46ff0bc1359208b7083a17f7a99",
|
||||
"reference": "2a2bc6826114c46ff0bc1359208b7083a17f7a99",
|
||||
"url": "https://api.github.com/repos/mck89/peast/zipball/833be7a294627a8c5b1c482cbf489f73bf9b8086",
|
||||
"reference": "833be7a294627a8c5b1c482cbf489f73bf9b8086",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -162,7 +158,7 @@
|
|||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.11.0-dev"
|
||||
"dev-master": "1.12.0-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
|
@ -182,11 +178,7 @@
|
|||
}
|
||||
],
|
||||
"description": "Peast is PHP library that generates AST for JavaScript code",
|
||||
"support": {
|
||||
"issues": "https://github.com/mck89/peast/issues",
|
||||
"source": "https://github.com/mck89/peast/tree/v1.11.0"
|
||||
},
|
||||
"time": "2020-10-09T15:12:13+00:00"
|
||||
"time": "2021-01-08T15:16:19+00:00"
|
||||
},
|
||||
{
|
||||
"name": "mustache/mustache",
|
||||
|
@ -232,10 +224,6 @@
|
|||
"mustache",
|
||||
"templating"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/bobthecow/mustache.php/issues",
|
||||
"source": "https://github.com/bobthecow/mustache.php/tree/master"
|
||||
},
|
||||
"time": "2019-11-23T21:40:31+00:00"
|
||||
},
|
||||
{
|
||||
|
@ -285,10 +273,6 @@
|
|||
"iri",
|
||||
"sockets"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/rmccue/Requests/issues",
|
||||
"source": "https://github.com/rmccue/Requests/tree/master"
|
||||
},
|
||||
"time": "2016-10-13T00:11:37+00:00"
|
||||
},
|
||||
{
|
||||
|
@ -398,10 +382,6 @@
|
|||
],
|
||||
"description": "Provides internationalization tools for WordPress projects.",
|
||||
"homepage": "https://github.com/wp-cli/i18n-command",
|
||||
"support": {
|
||||
"issues": "https://github.com/wp-cli/i18n-command/issues",
|
||||
"source": "https://github.com/wp-cli/i18n-command/tree/v2.2.6"
|
||||
},
|
||||
"time": "2020-12-07T19:28:27+00:00"
|
||||
},
|
||||
{
|
||||
|
@ -450,9 +430,6 @@
|
|||
],
|
||||
"description": "A simple YAML loader/dumper class for PHP (WP-CLI fork)",
|
||||
"homepage": "https://github.com/mustangostang/spyc/",
|
||||
"support": {
|
||||
"source": "https://github.com/wp-cli/spyc/tree/autoload"
|
||||
},
|
||||
"time": "2017-04-25T11:26:20+00:00"
|
||||
},
|
||||
{
|
||||
|
@ -503,10 +480,6 @@
|
|||
"cli",
|
||||
"console"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/wp-cli/php-cli-tools/issues",
|
||||
"source": "https://github.com/wp-cli/php-cli-tools/tree/master"
|
||||
},
|
||||
"time": "2018-09-04T13:28:00+00:00"
|
||||
},
|
||||
{
|
||||
|
@ -569,11 +542,6 @@
|
|||
"cli",
|
||||
"wordpress"
|
||||
],
|
||||
"support": {
|
||||
"docs": "https://make.wordpress.org/cli/handbook/",
|
||||
"issues": "https://github.com/wp-cli/wp-cli/issues",
|
||||
"source": "https://github.com/wp-cli/wp-cli"
|
||||
},
|
||||
"time": "2020-02-18T08:15:37+00:00"
|
||||
}
|
||||
],
|
||||
|
@ -587,5 +555,5 @@
|
|||
"platform-overrides": {
|
||||
"php": "7.0"
|
||||
},
|
||||
"plugin-api-version": "2.0.0"
|
||||
"plugin-api-version": "1.1.0"
|
||||
}
|
||||
|
|
|
@ -1,6 +1,14 @@
|
|||
== Changelog ==
|
||||
|
||||
= 4.9.0 =
|
||||
= 4.9.2 2021-01-25 =
|
||||
|
||||
* Tweak - Disable untested plugin's notice from System Status and Plugin's page. #28840
|
||||
|
||||
= 4.9.1 2021-01-19 =
|
||||
|
||||
* Fix - Reverts #28204 to ensure compatibility with extensions using legacy do_action calls. #28835
|
||||
|
||||
= 4.9.0 2021-01-12 =
|
||||
|
||||
**WooCommerce**
|
||||
* Localization - Add 'Ladakh' to the list of Indian states. #28458
|
||||
|
@ -28,6 +36,9 @@
|
|||
* Fix - Better error messages when usage limit are reached. #28592
|
||||
* Fix - Adjust stock items only for statuses which reduces the stock. #28620
|
||||
* Fix - Named parameter to fix DB update routine on PHP8. #28537
|
||||
* Fix - Add protection around func_get_args_call for backwards compatibility. #28677
|
||||
* Fix - Restore stock_status in REST API V3 response. #28731
|
||||
* Fix - Revert some of the changes related to perf enhancements (27735) as it was breaking stock_status filter. #28745
|
||||
* Dev - Hook for intializing price slider in frontend. #28014
|
||||
* Dev - Add support for running a custom initialization script for tests. #28041
|
||||
* Dev - Use the coenjacobs/mozart package to renamespace vendor packages. #28147
|
||||
|
@ -70,6 +81,7 @@
|
|||
* Fix - Preventing desktop-sized navigation placeholder from appearing on mobile during load. #5616
|
||||
* Fix - Completed tasks tracking causing infinite loop #5941
|
||||
* Fix - Remove Navigation access #5940
|
||||
* Fix - Compile the debug module so it can be used in older browsers like IE11. #5968
|
||||
* Tweak - Fix inconsistent REST API parameter name for customer type filtering. #5823
|
||||
* Tweak - Improve styles of the tax task. #5709
|
||||
* Tweak - Do not show store setup link on the homescreen. #5801
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
"pelago/emogrifier": "3.1.0",
|
||||
"psr/container": "1.0.0",
|
||||
"woocommerce/action-scheduler": "3.1.6",
|
||||
"woocommerce/woocommerce-admin": "1.8.3",
|
||||
"woocommerce/woocommerce-admin": "1.9.0",
|
||||
"woocommerce/woocommerce-blocks": "4.0.0"
|
||||
},
|
||||
"require-dev": {
|
||||
|
|
|
@ -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": "758b097c13e89200b28bf3a3b5fc2752",
|
||||
"content-hash": "5fac5fbdbcff552de109cd95d469e975",
|
||||
"packages": [
|
||||
{
|
||||
"name": "automattic/jetpack-autoloader",
|
||||
|
@ -77,23 +77,20 @@
|
|||
"GPL-2.0-or-later"
|
||||
],
|
||||
"description": "A wrapper for defining constants in a more testable way.",
|
||||
"support": {
|
||||
"source": "https://github.com/Automattic/jetpack-constants/tree/v1.5.1"
|
||||
},
|
||||
"time": "2020-10-28T19:00:31+00:00"
|
||||
},
|
||||
{
|
||||
"name": "composer/installers",
|
||||
"version": "v1.9.0",
|
||||
"version": "v1.10.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/composer/installers.git",
|
||||
"reference": "b93bcf0fa1fccb0b7d176b0967d969691cd74cca"
|
||||
"reference": "1a0357fccad9d1cc1ea0c9a05b8847fbccccb78d"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/composer/installers/zipball/b93bcf0fa1fccb0b7d176b0967d969691cd74cca",
|
||||
"reference": "b93bcf0fa1fccb0b7d176b0967d969691cd74cca",
|
||||
"url": "https://api.github.com/repos/composer/installers/zipball/1a0357fccad9d1cc1ea0c9a05b8847fbccccb78d",
|
||||
"reference": "1a0357fccad9d1cc1ea0c9a05b8847fbccccb78d",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -104,17 +101,18 @@
|
|||
"shama/baton": "*"
|
||||
},
|
||||
"require-dev": {
|
||||
"composer/composer": "1.6.* || 2.0.*@dev",
|
||||
"composer/semver": "1.0.* || 2.0.*@dev",
|
||||
"phpunit/phpunit": "^4.8.36",
|
||||
"sebastian/comparator": "^1.2.4",
|
||||
"composer/composer": "1.6.* || ^2.0",
|
||||
"composer/semver": "^1 || ^3",
|
||||
"phpstan/phpstan": "^0.12.55",
|
||||
"phpstan/phpstan-phpunit": "^0.12.16",
|
||||
"symfony/phpunit-bridge": "^4.2 || ^5",
|
||||
"symfony/process": "^2.3"
|
||||
},
|
||||
"type": "composer-plugin",
|
||||
"extra": {
|
||||
"class": "Composer\\Installers\\Plugin",
|
||||
"branch-alias": {
|
||||
"dev-master": "1.0-dev"
|
||||
"dev-main": "1.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
|
@ -152,6 +150,7 @@
|
|||
"Porto",
|
||||
"RadPHP",
|
||||
"SMF",
|
||||
"Starbug",
|
||||
"Thelia",
|
||||
"Whmcs",
|
||||
"WolfCMS",
|
||||
|
@ -192,6 +191,7 @@
|
|||
"phpbb",
|
||||
"piwik",
|
||||
"ppi",
|
||||
"processwire",
|
||||
"puppet",
|
||||
"pxcms",
|
||||
"reindex",
|
||||
|
@ -207,21 +207,21 @@
|
|||
"zend",
|
||||
"zikula"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/composer/installers/issues",
|
||||
"source": "https://github.com/composer/installers/tree/v1.9.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://packagist.com",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/composer",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/composer/composer",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2020-04-07T06:57:05+00:00"
|
||||
"time": "2021-01-14T11:07:16+00:00"
|
||||
},
|
||||
{
|
||||
"name": "maxmind-db/reader",
|
||||
|
@ -281,10 +281,6 @@
|
|||
"geolocation",
|
||||
"maxmind"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/maxmind/MaxMind-DB-Reader-php/issues",
|
||||
"source": "https://github.com/maxmind/MaxMind-DB-Reader-php/tree/v1.6.0"
|
||||
},
|
||||
"time": "2019-12-19T22:59:03+00:00"
|
||||
},
|
||||
{
|
||||
|
@ -359,10 +355,6 @@
|
|||
"email",
|
||||
"pre-processing"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/MyIntervals/emogrifier/issues",
|
||||
"source": "https://github.com/MyIntervals/emogrifier"
|
||||
},
|
||||
"time": "2019-12-26T19:37:31+00:00"
|
||||
},
|
||||
{
|
||||
|
@ -412,10 +404,6 @@
|
|||
"container-interop",
|
||||
"psr"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/php-fig/container/issues",
|
||||
"source": "https://github.com/php-fig/container/tree/master"
|
||||
},
|
||||
"time": "2017-02-14T16:28:37+00:00"
|
||||
},
|
||||
{
|
||||
|
@ -507,24 +495,20 @@
|
|||
],
|
||||
"description": "Action Scheduler for WordPress and WooCommerce",
|
||||
"homepage": "https://actionscheduler.org/",
|
||||
"support": {
|
||||
"issues": "https://github.com/woocommerce/action-scheduler/issues",
|
||||
"source": "https://github.com/woocommerce/action-scheduler/tree/master"
|
||||
},
|
||||
"time": "2020-05-12T16:22:33+00:00"
|
||||
},
|
||||
{
|
||||
"name": "woocommerce/woocommerce-admin",
|
||||
"version": "1.8.3",
|
||||
"version": "1.9.0-rc.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/woocommerce/woocommerce-admin.git",
|
||||
"reference": "534442980c34369f711efdb8e85fdcb1363be712"
|
||||
"reference": "8c5b20cb6347959daf5403ee30e47061f335240b"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/woocommerce/woocommerce-admin/zipball/534442980c34369f711efdb8e85fdcb1363be712",
|
||||
"reference": "534442980c34369f711efdb8e85fdcb1363be712",
|
||||
"url": "https://api.github.com/repos/woocommerce/woocommerce-admin/zipball/8c5b20cb6347959daf5403ee30e47061f335240b",
|
||||
"reference": "8c5b20cb6347959daf5403ee30e47061f335240b",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -556,11 +540,7 @@
|
|||
],
|
||||
"description": "A modern, javascript-driven WooCommerce Admin experience.",
|
||||
"homepage": "https://github.com/woocommerce/woocommerce-admin",
|
||||
"support": {
|
||||
"issues": "https://github.com/woocommerce/woocommerce-admin/issues",
|
||||
"source": "https://github.com/woocommerce/woocommerce-admin/tree/v1.8.3"
|
||||
},
|
||||
"time": "2021-01-06T00:00:42+00:00"
|
||||
"time": "2021-01-22T10:23:29+00:00"
|
||||
},
|
||||
{
|
||||
"name": "woocommerce/woocommerce-blocks",
|
||||
|
@ -659,10 +639,6 @@
|
|||
"isolation",
|
||||
"tool"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/bamarni/composer-bin-plugin/issues",
|
||||
"source": "https://github.com/bamarni/composer-bin-plugin/tree/master"
|
||||
},
|
||||
"time": "2020-05-03T08:27:20+00:00"
|
||||
}
|
||||
],
|
||||
|
@ -678,5 +654,5 @@
|
|||
"platform-overrides": {
|
||||
"php": "7.0"
|
||||
},
|
||||
"plugin-api-version": "2.0.0"
|
||||
"plugin-api-version": "1.1.0"
|
||||
}
|
||||
|
|
|
@ -1270,6 +1270,8 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order {
|
|||
*/
|
||||
protected function set_item_discount_amounts( $discounts ) {
|
||||
$item_discounts = $discounts->get_discounts_by_item();
|
||||
$tax_location = $this->get_tax_location();
|
||||
$tax_location = array( $tax_location['country'], $tax_location['state'], $tax_location['postcode'], $tax_location['city'] );
|
||||
|
||||
if ( $item_discounts ) {
|
||||
foreach ( $item_discounts as $item_id => $amount ) {
|
||||
|
@ -1277,7 +1279,7 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order {
|
|||
|
||||
// If the prices include tax, discounts should be taken off the tax inclusive prices like in the cart.
|
||||
if ( $this->get_prices_include_tax() && wc_tax_enabled() && 'taxable' === $item->get_tax_status() ) {
|
||||
$taxes = WC_Tax::calc_tax( $amount, WC_Tax::get_rates( $item->get_tax_class() ), true );
|
||||
$taxes = WC_Tax::calc_tax( $amount, $this->get_tax_rates( $item->get_tax_class(), $tax_location ), true );
|
||||
|
||||
// Use unrounded taxes so totals will be re-calculated accurately, like in cart.
|
||||
$amount = $amount - array_sum( $taxes );
|
||||
|
@ -1299,6 +1301,13 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order {
|
|||
$coupon_code_to_id = wc_list_pluck( $coupons, 'get_id', 'get_code' );
|
||||
$all_discounts = $discounts->get_discounts();
|
||||
$coupon_discounts = $discounts->get_discounts_by_coupon();
|
||||
$tax_location = $this->get_tax_location();
|
||||
$tax_location = array(
|
||||
$tax_location['country'],
|
||||
$tax_location['state'],
|
||||
$tax_location['postcode'],
|
||||
$tax_location['city'],
|
||||
);
|
||||
|
||||
if ( $coupon_discounts ) {
|
||||
foreach ( $coupon_discounts as $coupon_code => $amount ) {
|
||||
|
@ -1321,7 +1330,7 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order {
|
|||
continue;
|
||||
}
|
||||
|
||||
$taxes = array_sum( WC_Tax::calc_tax( $item_discount_amount, WC_Tax::get_rates( $item->get_tax_class() ), $this->get_prices_include_tax() ) );
|
||||
$taxes = array_sum( WC_Tax::calc_tax( $item_discount_amount, $this->get_tax_rates( $item->get_tax_class(), $tax_location ), $this->get_prices_include_tax() ) );
|
||||
if ( 'yes' !== get_option( 'woocommerce_tax_round_at_subtotal' ) ) {
|
||||
$taxes = wc_round_tax_total( $taxes );
|
||||
}
|
||||
|
@ -1514,6 +1523,21 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order {
|
|||
return apply_filters( 'woocommerce_order_get_tax_location', $args, $this );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get tax rates for an order. Use order's shipping or billing address, defaults to base location.
|
||||
*
|
||||
* @param string $tax_class Tax class to get rates for.
|
||||
* @param array $location_args Location to compute rates for. Should be in form: array( country, state, postcode, city).
|
||||
* @param object $customer Only used to maintain backward compatibility for filter `woocommerce-matched_rates`.
|
||||
*
|
||||
* @return mixed|void Tax rates.
|
||||
*/
|
||||
protected function get_tax_rates( $tax_class, $location_args = array(), $customer = null ) {
|
||||
$tax_location = $this->get_tax_location( $location_args );
|
||||
$tax_location = array( $tax_location['country'], $tax_location['state'], $tax_location['postcode'], $tax_location['city'] );
|
||||
return WC_Tax::get_rates_from_location( $tax_class, $tax_location, $customer );
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate taxes for all line items and shipping, and store the totals and tax rows.
|
||||
*
|
||||
|
|
|
@ -64,6 +64,51 @@ class WC_Admin_Notices {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses query to create nonces when available.
|
||||
*
|
||||
* @param object $response The WP_REST_Response we're working with.
|
||||
* @return object $response The prepared WP_REST_Response object.
|
||||
*/
|
||||
public static function prepare_note_with_nonce( $response ) {
|
||||
if ( 'wc-update-db-reminder' !== $response->data['name'] || ! isset( $response->data['actions'] ) ) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
foreach ( $response->data['actions'] as $action_key => $action ) {
|
||||
$url_parts = ! empty( $action->query ) ? wp_parse_url( $action->query ) : '';
|
||||
|
||||
if ( ! isset( $url_parts['query'] ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
wp_parse_str( $url_parts['query'], $params );
|
||||
|
||||
if ( array_key_exists( '_nonce_action', $params ) && array_key_exists( '_nonce_name', $params ) ) {
|
||||
$org_params = $params;
|
||||
|
||||
// Check to make sure we're acting on the whitelisted nonce actions.
|
||||
if ( 'wc_db_update' !== $params['_nonce_action'] && 'woocommerce_hide_notices_nonce' !== $params['_nonce_action'] ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
unset( $org_params['_nonce_action'] );
|
||||
unset( $org_params['_nonce_name'] );
|
||||
|
||||
$url = $url_parts['scheme'] . '://' . $url_parts['host'] . $url_parts['path'];
|
||||
|
||||
$nonce = array( $params['_nonce_name'] => wp_create_nonce( $params['_nonce_action'] ) );
|
||||
$merged_params = array_merge( $nonce, $org_params );
|
||||
$parsed_query = add_query_arg( $merged_params, $url );
|
||||
|
||||
$response->data['actions'][ $action_key ]->query = $parsed_query;
|
||||
$response->data['actions'][ $action_key ]->url = $parsed_query;
|
||||
}
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Store notices to DB
|
||||
*/
|
||||
|
|
|
@ -118,7 +118,9 @@ class WC_Meta_Box_Order_Actions {
|
|||
|
||||
WC()->payment_gateways();
|
||||
WC()->shipping();
|
||||
add_filter( 'woocommerce_new_order_email_allows_resend', '__return_true' );
|
||||
WC()->mailer()->emails['WC_Email_New_Order']->trigger( $order->get_id(), $order, true );
|
||||
remove_filter( 'woocommerce_new_order_email_allows_resend', '__return_true' );
|
||||
|
||||
do_action( 'woocommerce_after_resend_order_email', $order, 'new_order' );
|
||||
|
||||
|
|
|
@ -110,10 +110,13 @@ class WC_Notes_Run_Db_Update {
|
|||
*/
|
||||
private static function update_needed_notice( $note_id = null ) {
|
||||
$update_url = html_entity_decode(
|
||||
wp_nonce_url(
|
||||
add_query_arg( 'do_update_woocommerce', 'true', wc_get_current_admin_url() ? wc_get_current_admin_url() : admin_url( 'admin.php?page=wc-settings' ) ),
|
||||
'wc_db_update',
|
||||
'wc_db_update_nonce'
|
||||
add_query_arg(
|
||||
array(
|
||||
'do_update_woocommerce' => 'true',
|
||||
'_nonce_action' => 'wc_db_update',
|
||||
'_nonce_name' => 'wc_db_update_nonce',
|
||||
),
|
||||
wc_get_current_admin_url() ? wc_get_current_admin_url() : admin_url( 'admin.php?page=wc-settings' )
|
||||
)
|
||||
);
|
||||
|
||||
|
@ -206,14 +209,13 @@ class WC_Notes_Run_Db_Update {
|
|||
*/
|
||||
private static function update_done_notice( $note_id ) {
|
||||
$hide_notices_url = html_entity_decode( // to convert &s to normal &, otherwise produces invalid link.
|
||||
wp_nonce_url(
|
||||
add_query_arg(
|
||||
'wc-hide-notice',
|
||||
'update',
|
||||
wc_get_current_admin_url() ? wc_get_current_admin_url() : admin_url( 'admin.php?page=wc-settings' )
|
||||
add_query_arg(
|
||||
array(
|
||||
'wc-hide-notice' => 'update',
|
||||
'_nonce_action' => 'woocommerce_hide_notices_nonce',
|
||||
'_nonce_name' => '_wc_notice_nonce',
|
||||
),
|
||||
'woocommerce_hide_notices_nonce',
|
||||
'_wc_notice_nonce'
|
||||
wc_get_current_admin_url() ? remove_query_arg( 'do_update_woocommerce', wc_get_current_admin_url() ) : admin_url( 'admin.php?page=wc-settings' )
|
||||
)
|
||||
);
|
||||
|
||||
|
|
|
@ -64,6 +64,10 @@ class WC_Settings_Products extends WC_Settings_Page {
|
|||
$settings = $this->get_settings( $current_section );
|
||||
WC_Admin_Settings::save_fields( $settings );
|
||||
|
||||
// Any time we update the product settings, we should flush the term count cache.
|
||||
$tools_controller = new WC_REST_System_Status_Tools_Controller();
|
||||
$tools_controller->execute_tool( 'recount_terms' );
|
||||
|
||||
if ( $current_section ) {
|
||||
do_action( 'woocommerce_update_options_' . $this->id . '_' . $current_section );
|
||||
}
|
||||
|
|
|
@ -698,11 +698,11 @@ final class WC_Cart_Totals {
|
|||
/**
|
||||
* Subtotals are costs before discounts.
|
||||
*
|
||||
* To prevent rounding issues we need to work with the inclusive price where possible.
|
||||
* otherwise we'll see errors such as when working with a 9.99 inc price, 20% VAT which would.
|
||||
* To prevent rounding issues we need to work with the inclusive price where possible
|
||||
* otherwise we'll see errors such as when working with a 9.99 inc price, 20% VAT which would
|
||||
* be 8.325 leading to totals being 1p off.
|
||||
*
|
||||
* Pre tax coupons come off the price the customer thinks they are paying - tax is calculated.
|
||||
* Pre tax coupons come off the price the customer thinks they are paying - tax is calculated
|
||||
* afterwards.
|
||||
*
|
||||
* e.g. $100 bike with $10 coupon = customer pays $90 and tax worked backwards from that.
|
||||
|
|
|
@ -210,7 +210,7 @@ class WC_Cart extends WC_Legacy_Cart {
|
|||
}
|
||||
|
||||
/**
|
||||
* Get subtotal.
|
||||
* Get subtotal_tax.
|
||||
*
|
||||
* @since 3.2.0
|
||||
* @return float
|
||||
|
|
|
@ -20,7 +20,7 @@ class WC_Payment_Tokens {
|
|||
* Gets valid tokens from the database based on user defined criteria.
|
||||
*
|
||||
* @since 2.6.0
|
||||
* @param array $args Query argyments {
|
||||
* @param array $args Query arguments {
|
||||
* Array of query parameters.
|
||||
*
|
||||
* @type string $token_id Token ID.
|
||||
|
|
|
@ -405,7 +405,7 @@ class WC_Tax {
|
|||
|
||||
$criteria_string = implode( ' AND ', $criteria );
|
||||
|
||||
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
|
||||
// phpcs:disable WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.InterpolatedNotPrepared
|
||||
$found_rates = $wpdb->get_results(
|
||||
"
|
||||
SELECT tax_rates.*, COUNT( locations.location_id ) as postcode_count, COUNT( locations2.location_id ) as city_count
|
||||
|
@ -479,8 +479,22 @@ class WC_Tax {
|
|||
* @return array
|
||||
*/
|
||||
public static function get_rates( $tax_class = '', $customer = null ) {
|
||||
$tax_class = sanitize_title( $tax_class );
|
||||
$location = self::get_tax_location( $tax_class, $customer );
|
||||
return self::get_rates_from_location( $tax_class, $location, $customer );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get's an arrau of matching rates from location and tax class. $customer parameter is used to preserve backward compatibility for filter.
|
||||
*
|
||||
* @param string $tax_class Tax class to get rates for.
|
||||
* @param array $location Location to compute rates for. Should be in form: array( country, state, postcode, city).
|
||||
* @param object $customer Only used to maintain backward compatibility for filter `woocommerce-matched_rates`.
|
||||
*
|
||||
* @return mixed|void Tax rates.
|
||||
*/
|
||||
public static function get_rates_from_location( $tax_class, $location, $customer = null ) {
|
||||
$tax_class = sanitize_title( $tax_class );
|
||||
$location = self::get_tax_location( $tax_class, $customer );
|
||||
$matched_tax_rates = array();
|
||||
|
||||
if ( count( $location ) === 4 ) {
|
||||
|
|
|
@ -364,9 +364,11 @@ class WC_Tracker {
|
|||
);
|
||||
|
||||
$first = time();
|
||||
$processing_first = $first;
|
||||
$first_time = $first;
|
||||
$last = 0;
|
||||
$processing_first = time();
|
||||
$processing_last = 0;
|
||||
$order_data = array();
|
||||
|
||||
$orders = wc_get_orders( $args );
|
||||
$orders_count = count( $orders );
|
||||
|
@ -445,10 +447,25 @@ class WC_Tracker {
|
|||
$orders_count = count( $orders );
|
||||
}
|
||||
|
||||
$order_data['first'] = gmdate( 'Y-m-d H:i:s', $first );
|
||||
$order_data['last'] = gmdate( 'Y-m-d H:i:s', $last );
|
||||
$order_data['processing_first'] = gmdate( 'Y-m-d H:i:s', $processing_first );
|
||||
$order_data['processing_last'] = gmdate( 'Y-m-d H:i:s', $processing_last );
|
||||
if ( $first !== $first_time ) {
|
||||
$order_data['first'] = gmdate( 'Y-m-d H:i:s', $first );
|
||||
}
|
||||
|
||||
if ( $processing_first !== $first_time ) {
|
||||
$order_data['processing_first'] = gmdate( 'Y-m-d H:i:s', $processing_first );
|
||||
}
|
||||
|
||||
if ( $last ) {
|
||||
$order_data['last'] = gmdate( 'Y-m-d H:i:s', $last );
|
||||
}
|
||||
|
||||
if ( $processing_last ) {
|
||||
$order_data['processing_last'] = gmdate( 'Y-m-d H:i:s', $processing_last );
|
||||
}
|
||||
|
||||
foreach ( $order_data as $key => $value ) {
|
||||
$order_data[ $key ] = (string) $value;
|
||||
}
|
||||
|
||||
return $order_data;
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ final class WooCommerce {
|
|||
*
|
||||
* @var string
|
||||
*/
|
||||
public $version = '5.0.0';
|
||||
public $version = '5.1.0';
|
||||
|
||||
/**
|
||||
* WooCommerce Schema version.
|
||||
|
@ -203,6 +203,7 @@ final class WooCommerce {
|
|||
add_action( 'switch_blog', array( $this, 'wpdb_table_fix' ), 0 );
|
||||
add_action( 'activated_plugin', array( $this, 'activated_plugin' ) );
|
||||
add_action( 'deactivated_plugin', array( $this, 'deactivated_plugin' ) );
|
||||
add_filter( 'woocommerce_rest_prepare_note', array( 'WC_Admin_Notices', 'prepare_note_with_nonce' ) );
|
||||
|
||||
// These classes set up hooks on instantiation.
|
||||
wc_get_container()->get( DownloadPermissionsAdjuster::class );
|
||||
|
|
|
@ -818,7 +818,7 @@ class WC_Shop_Customizer {
|
|||
'description' => __( 'Optionally add some text about your store privacy policy to show during checkout.', 'woocommerce' ),
|
||||
'section' => 'woocommerce_checkout',
|
||||
'settings' => 'woocommerce_checkout_privacy_policy_text',
|
||||
'active_callback' => 'wc_privacy_policy_page_id',
|
||||
'active_callback' => array( $this, 'has_privacy_policy_page_id' ),
|
||||
'type' => 'textarea',
|
||||
)
|
||||
);
|
||||
|
@ -830,7 +830,7 @@ class WC_Shop_Customizer {
|
|||
'description' => __( 'Optionally add some text for the terms checkbox that customers must accept.', 'woocommerce' ),
|
||||
'section' => 'woocommerce_checkout',
|
||||
'settings' => 'woocommerce_checkout_terms_and_conditions_checkbox_text',
|
||||
'active_callback' => 'wc_terms_and_conditions_page_id',
|
||||
'active_callback' => array( $this, 'has_terms_and_conditions_page_id' ),
|
||||
'type' => 'text',
|
||||
)
|
||||
);
|
||||
|
@ -865,6 +865,24 @@ class WC_Shop_Customizer {
|
|||
$options = array( 'hidden', 'optional', 'required' );
|
||||
return in_array( $value, $options, true ) ? $value : '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not a page has been chose for the privacy policy.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function has_privacy_policy_page_id() {
|
||||
return wc_privacy_policy_page_id() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not a page has been chose for the terms and conditions.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function has_terms_and_conditions_page_id() {
|
||||
return wc_terms_and_conditions_page_id() > 0;
|
||||
}
|
||||
}
|
||||
|
||||
new WC_Shop_Customizer();
|
||||
|
|
|
@ -906,7 +906,14 @@ class WC_Order_Data_Store_CPT extends Abstract_WC_Order_Data_Store_CPT implement
|
|||
$this->prime_order_item_caches_for_orders( $order_ids, $query_vars );
|
||||
|
||||
foreach ( $query->posts as $post ) {
|
||||
$orders[] = wc_get_order( $post );
|
||||
$order = wc_get_order( $post );
|
||||
|
||||
// If the order returns false, don't add it to the list.
|
||||
if ( false === $order ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$orders[] = $order;
|
||||
}
|
||||
|
||||
return $orders;
|
||||
|
|
|
@ -272,6 +272,9 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da
|
|||
|
||||
$product->apply_changes();
|
||||
|
||||
// Any time we update the product, we should flush the term count cache.
|
||||
$tools_controller = new WC_REST_System_Status_Tools_Controller();
|
||||
$tools_controller->execute_tool( 'recount_terms' );
|
||||
do_action( 'woocommerce_update_product', $product->get_id(), $product );
|
||||
}
|
||||
|
||||
|
|
|
@ -80,9 +80,8 @@ if ( ! class_exists( 'WC_Email_New_Order' ) ) :
|
|||
*
|
||||
* @param int $order_id The order ID.
|
||||
* @param WC_Order|false $order Order object.
|
||||
* @param bool $force_send Whether to force send the email.
|
||||
*/
|
||||
public function trigger( $order_id, $order = false, $force_send = false ) {
|
||||
public function trigger( $order_id, $order = false ) {
|
||||
$this->setup_locale();
|
||||
|
||||
if ( $order_id && ! is_a( $order, 'WC_Order' ) ) {
|
||||
|
@ -97,7 +96,13 @@ if ( ! class_exists( 'WC_Email_New_Order' ) ) :
|
|||
$email_already_sent = $order->get_meta( '_new_order_email_sent' );
|
||||
}
|
||||
|
||||
if ( 'true' === $email_already_sent && ! $force_send ) {
|
||||
/**
|
||||
* Controls if new order emails can be resend multiple times.
|
||||
*
|
||||
* @since 5.0.0
|
||||
* @param bool $allows Defaults to true.
|
||||
*/
|
||||
if ( 'true' === $email_already_sent && ! apply_filters( 'woocommerce_new_order_email_allows_resend', false ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -574,10 +574,33 @@ function wc_price( $price, $args = array() ) {
|
|||
)
|
||||
);
|
||||
|
||||
$original_price = $price;
|
||||
|
||||
// Convert to float to avoid issues on PHP 8.
|
||||
$price = (float) $price;
|
||||
|
||||
$unformatted_price = $price;
|
||||
$negative = $price < 0;
|
||||
$price = apply_filters( 'raw_woocommerce_price', floatval( $negative ? $price * -1 : $price ) );
|
||||
$price = apply_filters( 'formatted_woocommerce_price', number_format( $price, $args['decimals'], $args['decimal_separator'], $args['thousand_separator'] ), $price, $args['decimals'], $args['decimal_separator'], $args['thousand_separator'] );
|
||||
|
||||
/**
|
||||
* Filter raw price.
|
||||
*
|
||||
* @param float $raw_price Raw price.
|
||||
* @param float|string $original_price Original price as float, or empty string. Since 5.0.0.
|
||||
*/
|
||||
$price = apply_filters( 'raw_woocommerce_price', $negative ? $price * -1 : $price, $original_price );
|
||||
|
||||
/**
|
||||
* Filter formatted price.
|
||||
*
|
||||
* @param float $formatted_price Formatted price.
|
||||
* @param float $price Unformatted price.
|
||||
* @param int $decimals Number of decimals.
|
||||
* @param string $decimal_separator Decimal separator.
|
||||
* @param string $thousand_separator Thousand separator.
|
||||
* @param float|string $original_price Original price as float, or empty string. Since 5.0.0.
|
||||
*/
|
||||
$price = apply_filters( 'formatted_woocommerce_price', number_format( $price, $args['decimals'], $args['decimal_separator'], $args['thousand_separator'] ), $price, $args['decimals'], $args['decimal_separator'], $args['thousand_separator'], $original_price );
|
||||
|
||||
if ( apply_filters( 'woocommerce_price_trim_zeros', false ) && $args['decimals'] > 0 ) {
|
||||
$price = wc_trim_zeros( $price );
|
||||
|
@ -593,12 +616,13 @@ function wc_price( $price, $args = array() ) {
|
|||
/**
|
||||
* Filters the string of price markup.
|
||||
*
|
||||
* @param string $return Price HTML markup.
|
||||
* @param string $price Formatted price.
|
||||
* @param array $args Pass on the args.
|
||||
* @param float $unformatted_price Price as float to allow plugins custom formatting. Since 3.2.0.
|
||||
* @param string $return Price HTML markup.
|
||||
* @param string $price Formatted price.
|
||||
* @param array $args Pass on the args.
|
||||
* @param float $unformatted_price Price as float to allow plugins custom formatting. Since 3.2.0.
|
||||
* @param float|string $original_price Original price as float, or empty string. Since 5.0.0.
|
||||
*/
|
||||
return apply_filters( 'wc_price', $return, $price, $args, $unformatted_price );
|
||||
return apply_filters( 'wc_price', $return, $price, $args, $unformatted_price, $original_price );
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -651,7 +651,7 @@ function wc_get_product_id_by_sku( $sku ) {
|
|||
}
|
||||
|
||||
/**
|
||||
* Get attibutes/data for an individual variation from the database and maintain it's integrity.
|
||||
* Get attributes/data for an individual variation from the database and maintain it's integrity.
|
||||
*
|
||||
* @since 2.4.0
|
||||
* @param int $variation_id Variation ID.
|
||||
|
|
|
@ -352,7 +352,7 @@ function wc_no_js() {
|
|||
var c = document.body.className;
|
||||
c = c.replace(/woocommerce-no-js/, 'woocommerce-js');
|
||||
document.body.className = c;
|
||||
})()
|
||||
})();
|
||||
</script>
|
||||
<?php
|
||||
}
|
||||
|
@ -3568,7 +3568,12 @@ function wc_empty_cart_message() {
|
|||
*/
|
||||
function wc_page_noindex() {
|
||||
if ( is_page( wc_get_page_id( 'cart' ) ) || is_page( wc_get_page_id( 'checkout' ) ) || is_page( wc_get_page_id( 'myaccount' ) ) ) {
|
||||
wp_no_robots();
|
||||
// Adds support for WP 5.7.
|
||||
if ( function_exists( 'wp_robots_no_robots' ) ) {
|
||||
wp_robots_no_robots();
|
||||
} else {
|
||||
wp_no_robots();
|
||||
}
|
||||
}
|
||||
}
|
||||
add_action( 'wp_head', 'wc_page_noindex' );
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "woocommerce",
|
||||
"title": "WooCommerce",
|
||||
"version": "5.0.0",
|
||||
"version": "5.1.0",
|
||||
"homepage": "https://woocommerce.com/",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
|
11
readme.txt
11
readme.txt
|
@ -1,10 +1,10 @@
|
|||
=== WooCommerce ===
|
||||
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
|
||||
Requires at least: 5.4
|
||||
Tested up to: 5.6
|
||||
Requires PHP: 7.0
|
||||
Stable tag: 4.8.0
|
||||
Stable tag: 4.9.2
|
||||
License: GPLv3
|
||||
License URI: https://www.gnu.org/licenses/gpl-3.0.html
|
||||
|
||||
|
@ -160,11 +160,6 @@ WooCommerce comes with some sample data you can use to see how products look; im
|
|||
|
||||
== Changelog ==
|
||||
|
||||
= 4.9.0 - 2021-01-xx =
|
||||
= 5.1.0 2021-03-xx =
|
||||
|
||||
[See changelog for all versions](https://raw.githubusercontent.com/woocommerce/woocommerce/master/changelog.txt).
|
||||
|
||||
== Upgrade Notice ==
|
||||
|
||||
= 4.0 =
|
||||
4.0 is a major update. Make a full site backup, update your theme and extensions, and [review update best practices](https://docs.woocommerce.com/document/how-to-update-your-site) before upgrading.
|
||||
|
|
|
@ -83,6 +83,9 @@ class DownloadPermissionsAdjuster {
|
|||
*/
|
||||
public function adjust_download_permissions( int $product_id ) {
|
||||
$product = wc_get_product( $product_id );
|
||||
if ( ! $product ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$children_ids = $product->get_children();
|
||||
if ( ! $children_ids ) {
|
||||
|
|
|
@ -30,7 +30,7 @@ $wrapper_classes = apply_filters(
|
|||
'woocommerce_single_product_image_gallery_classes',
|
||||
array(
|
||||
'woocommerce-product-gallery',
|
||||
'woocommerce-product-gallery--' . ( $product->get_image_id() ? 'with-images' : 'without-images' ),
|
||||
'woocommerce-product-gallery--' . ( $post_thumbnail_id ? 'with-images' : 'without-images' ),
|
||||
'woocommerce-product-gallery--columns-' . absint( $columns ),
|
||||
'images',
|
||||
)
|
||||
|
@ -39,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 ( $product->get_image_id() ) {
|
||||
if ( $post_thumbnail_id ) {
|
||||
$html = wc_get_gallery_image_html( $post_thumbnail_id, true );
|
||||
} else {
|
||||
$html = '<div class="woocommerce-product-gallery__image--placeholder">';
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
# Unreleased
|
||||
|
||||
# 0.1.1
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
- The `HTTPClientFactory` API was changed to make it easier to configure instances with
|
||||
|
@ -14,6 +16,11 @@
|
|||
|
||||
- Added a tranformation layer between API responses and internal models
|
||||
|
||||
## Fixed
|
||||
|
||||
- issues that caused the factory creation to fail for SimpleProduct types
|
||||
- a bug with OAuth signature generation when using query parameters
|
||||
|
||||
# 0.1.0
|
||||
|
||||
- Initial/beta release
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@woocommerce/api",
|
||||
"version": "0.1.0",
|
||||
"version": "0.1.1",
|
||||
"author": "Automattic",
|
||||
"description": "A simple interface for interacting with a WooCommerce installation.",
|
||||
"homepage": "https://github.com/woocommerce/woocommerce/tree/master/tests/e2e/api/README.md",
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
# Unreleased
|
||||
|
||||
# 0.1.1
|
||||
|
||||
## Added
|
||||
|
||||
- Registered Shopper Checkout tests
|
||||
- Merchant Order Status Filter tests
|
||||
- Merchant Order Refund tests
|
||||
- Merchant Apply Coupon tests
|
||||
|
@ -8,6 +12,9 @@
|
|||
|
||||
- Shopper Checkout Apply Coupon
|
||||
|
||||
|
||||
- Shopper Cart Apply Coupon
|
||||
|
||||
## Fixed
|
||||
|
||||
- Flaky Create Product, Coupon, and Order tests
|
||||
|
|
|
@ -37,7 +37,7 @@ The functions to access the core tests are:
|
|||
### Activation and setup
|
||||
|
||||
- `runSetupOnboardingTests` - Run all setup and onboarding tests
|
||||
- `runActivationTest` - Merchant can activate WooCommerce
|
||||
- `runActivationTest` - Merchant can activate WooCommerce
|
||||
- `runOnboardingFlowTest` - Merchant can complete onboarding flow
|
||||
- `runTaskListTest` - Merchant can complete onboarding task list
|
||||
- `runInitialStoreSettingsTest` - Merchant can complete initial settings
|
||||
|
@ -45,7 +45,7 @@ The functions to access the core tests are:
|
|||
### Merchant
|
||||
|
||||
- `runMerchantTests` - Run all merchant tests
|
||||
- `runCreateCouponTest` - Merchant can create coupon
|
||||
- `runCreateCouponTest` - Merchant can create coupon
|
||||
- `runCreateOrderTest` - Merchant can create order
|
||||
- `runAddSimpleProductTest` - Merchant can create a simple product
|
||||
- `runAddVariableProductTest` - Merchant can create a variable product
|
||||
|
@ -59,10 +59,10 @@ The functions to access the core tests are:
|
|||
### Shopper
|
||||
|
||||
- `runShopperTests` - Run all shopper tests
|
||||
- `runCartPageTest` - Shopper can view and update cart
|
||||
- `runCartPageTest` - Shopper can view and update cart
|
||||
- `runCheckoutPageTest` - Shopper can complete checkout
|
||||
- `runMyAccountPageTest` - Shopper can access my account page
|
||||
- `runSingleProductPageTest` - Shopper can view single product page
|
||||
- `runSingleProductPageTest` - Shopper can view single product page
|
||||
|
||||
## Contributing a new test
|
||||
|
||||
|
@ -92,7 +92,7 @@ const runExampleTestName = () => {
|
|||
|
||||
module.exports = runExampleTestName;
|
||||
```
|
||||
- Add your test to `tests/e2e/core-tests/specs/index.js`
|
||||
- Add your test to `tests/e2e/core-tests/specs/index.js`
|
||||
```js
|
||||
const runExampleTestName = require( './grouping/example-test-name.test' );
|
||||
// ...
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@woocommerce/e2e-core-tests",
|
||||
"version": "0.1.0",
|
||||
"version": "0.1.1",
|
||||
"description": "End-To-End (E2E) tests for WooCommerce",
|
||||
"homepage": "https://github.com/woocommerce/woocommerce/tree/master/tests/e2e/core-tests/README.md",
|
||||
"repository": {
|
||||
|
@ -14,7 +14,7 @@
|
|||
"config": "3.3.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@woocommerce/e2e-utils": "^0.1.1"
|
||||
"@woocommerce/e2e-utils": "^0.1.2"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
|
|
|
@ -9,6 +9,7 @@ const { runOnboardingFlowTest, runTaskListTest } = require( './activate-and-setu
|
|||
const runInitialStoreSettingsTest = require( './activate-and-setup/setup.test' );
|
||||
|
||||
// Shopper tests
|
||||
const runCartApplyCouponsTest = require( './shopper/front-end-cart-coupons.test');
|
||||
const runCartPageTest = require( './shopper/front-end-cart.test' );
|
||||
const runCheckoutApplyCouponsTest = require( './shopper/front-end-checkout-coupons.test');
|
||||
const runCheckoutPageTest = require( './shopper/front-end-checkout.test' );
|
||||
|
@ -34,6 +35,7 @@ const runSetupOnboardingTests = () => {
|
|||
};
|
||||
|
||||
const runShopperTests = () => {
|
||||
runCartApplyCouponsTest();
|
||||
runCartPageTest();
|
||||
runCheckoutApplyCouponsTest();
|
||||
runCheckoutPageTest();
|
||||
|
@ -60,6 +62,7 @@ module.exports = {
|
|||
runTaskListTest,
|
||||
runInitialStoreSettingsTest,
|
||||
runSetupOnboardingTests,
|
||||
runCartApplyCouponsTest,
|
||||
runCartPageTest,
|
||||
runCheckoutApplyCouponsTest,
|
||||
runCheckoutPageTest,
|
||||
|
|
|
@ -0,0 +1,108 @@
|
|||
/* eslint-disable jest/no-export, jest/no-disabled-tests, jest/expect-expect */
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
const {
|
||||
shopper,
|
||||
merchant,
|
||||
createCoupon,
|
||||
createSimpleProduct,
|
||||
uiUnblocked
|
||||
} = require( '@woocommerce/e2e-utils' );
|
||||
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
const {
|
||||
it,
|
||||
describe,
|
||||
beforeAll,
|
||||
} = require( '@jest/globals' );
|
||||
|
||||
const runCartApplyCouponsTest = () => {
|
||||
describe('Cart applying coupons', () => {
|
||||
let couponFixedCart;
|
||||
let couponPercentage;
|
||||
let couponFixedProduct;
|
||||
beforeAll(async () => {
|
||||
await merchant.login();
|
||||
await createSimpleProduct();
|
||||
couponFixedCart = await createCoupon();
|
||||
couponPercentage = await createCoupon('50', 'Percentage discount');
|
||||
couponFixedProduct = await createCoupon('5', 'Fixed product discount');
|
||||
await merchant.logout();
|
||||
});
|
||||
|
||||
it('allows customer to apply coupons in the cart', async () => {
|
||||
await shopper.goToShop();
|
||||
await shopper.addToCartFromShopPage('Simple product');
|
||||
await shopper.goToCart();
|
||||
await shopper.productIsInCart('Simple product');
|
||||
|
||||
// Apply Fixed cart discount coupon
|
||||
await expect(page).toFill('#coupon_code', couponFixedCart);
|
||||
await expect(page).toClick('button', {text: 'Apply coupon'});
|
||||
await uiUnblocked();
|
||||
await page.waitForSelector('.woocommerce-message', {text: 'Coupon code applied successfully.'});
|
||||
|
||||
// Wait for page to expand total calculations to avoid flakyness
|
||||
await page.waitForSelector('.order-total');
|
||||
|
||||
// Verify discount applied and order total
|
||||
await page.waitForSelector('.cart-discount .amount', {text: '$5.00'});
|
||||
await page.waitForSelector('.order-total .amount', {text: '$4.99'});
|
||||
|
||||
// Remove coupon
|
||||
await expect(page).toClick('.woocommerce-remove-coupon', {text: '[Remove]'});
|
||||
await uiUnblocked();
|
||||
await page.waitForSelector('.woocommerce-message', {text: 'Coupon has been removed.'});
|
||||
|
||||
// Apply Percentage discount coupon
|
||||
await expect(page).toFill('#coupon_code', couponPercentage);
|
||||
await expect(page).toClick('button', {text: 'Apply coupon'});
|
||||
await uiUnblocked();
|
||||
await page.waitForSelector('.woocommerce-message', {text: 'Coupon code applied successfully.'});
|
||||
await page.waitForSelector('.cart-discount .amount', {text: '$4.99'});
|
||||
await page.waitForSelector('.order-total .amount', {text: '$5.00'});
|
||||
|
||||
// Remove coupon
|
||||
await expect(page).toClick('.woocommerce-remove-coupon', {text: '[Remove]'});
|
||||
await uiUnblocked();
|
||||
await page.waitForSelector('.woocommerce-message', {text: 'Coupon has been removed.'});
|
||||
|
||||
// Apply Fixed product discount coupon
|
||||
await expect(page).toFill('#coupon_code', couponFixedProduct);
|
||||
await expect(page).toClick('button', {text: 'Apply coupon'});
|
||||
await uiUnblocked();
|
||||
await page.waitForSelector('.woocommerce-message', {text: 'Coupon code applied successfully.'});
|
||||
await page.waitForSelector('.cart-discount .amount', {text: '$5.00'});
|
||||
await page.waitForSelector('.order-total .amount', {text: '$4.99'});
|
||||
|
||||
// Try to apply the same coupon
|
||||
await expect(page).toFill('#coupon_code', couponFixedProduct);
|
||||
await expect(page).toClick('button', {text: 'Apply coupon'});
|
||||
await uiUnblocked();
|
||||
await page.waitForSelector('.woocommerce-error', { text: 'Coupon code already applied!' });
|
||||
|
||||
// Try to apply multiple coupons
|
||||
await expect(page).toFill('#coupon_code', couponFixedCart);
|
||||
await expect(page).toClick('button', {text: 'Apply coupon'});
|
||||
await uiUnblocked();
|
||||
await page.waitForSelector('.woocommerce-message', {text: 'Coupon code applied successfully.'});
|
||||
await page.waitForSelector('.order-total .amount', {text: '$0.00'});
|
||||
|
||||
// Remove coupon
|
||||
await expect(page).toClick('.woocommerce-remove-coupon', {text: '[Remove]'});
|
||||
await uiUnblocked();
|
||||
await page.waitForSelector('.woocommerce-message', {text: 'Coupon has been removed.'});
|
||||
await expect(page).toClick('.woocommerce-remove-coupon', {text: '[Remove]'});
|
||||
await uiUnblocked();
|
||||
await page.waitForSelector('.woocommerce-message', {text: 'Coupon has been removed.'});
|
||||
|
||||
// Verify the total amount after all coupons removal
|
||||
await page.waitForSelector('.order-total .amount', {text: '$9.99'});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = runCartApplyCouponsTest;
|
|
@ -7,7 +7,8 @@ const {
|
|||
merchant,
|
||||
createCoupon,
|
||||
createSimpleProduct,
|
||||
uiUnblocked
|
||||
uiUnblocked,
|
||||
clearAndFillInput,
|
||||
} = require( '@woocommerce/e2e-utils' );
|
||||
|
||||
/**
|
||||
|
@ -19,11 +20,36 @@ const {
|
|||
beforeAll,
|
||||
} = require( '@jest/globals' );
|
||||
|
||||
/**
|
||||
* Apply a coupon code to the cart.
|
||||
*
|
||||
* @param couponCode string
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
const applyCouponToCart = async ( couponCode ) => {
|
||||
await expect(page).toClick('a', {text: 'Click here to enter your code'});
|
||||
await uiUnblocked();
|
||||
await clearAndFillInput('#coupon_code', couponCode);
|
||||
await expect(page).toClick('button', {text: 'Apply coupon'});
|
||||
await uiUnblocked();
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove one coupon from the cart.
|
||||
*
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
const removeCouponFromCart = async () => {
|
||||
await expect(page).toClick('.woocommerce-remove-coupon', {text: '[Remove]'});
|
||||
await uiUnblocked();
|
||||
await expect(page).toMatchElement('.woocommerce-message', {text: 'Coupon has been removed.'});
|
||||
}
|
||||
const runCheckoutApplyCouponsTest = () => {
|
||||
describe('Checkout applying coupons', () => {
|
||||
let couponFixedCart;
|
||||
let couponPercentage;
|
||||
let couponFixedProduct;
|
||||
describe('Checkout coupons', () => {
|
||||
let couponFixedCart;
|
||||
let couponPercentage;
|
||||
let couponFixedProduct;
|
||||
|
||||
beforeAll(async () => {
|
||||
await merchant.login();
|
||||
await createSimpleProduct();
|
||||
|
@ -31,86 +57,63 @@ const runCheckoutApplyCouponsTest = () => {
|
|||
couponPercentage = await createCoupon('50', 'Percentage discount');
|
||||
couponFixedProduct = await createCoupon('5', 'Fixed product discount');
|
||||
await merchant.logout();
|
||||
});
|
||||
|
||||
it('allows customer to apply coupons in the checkout', async () => {
|
||||
await shopper.goToShop();
|
||||
await shopper.addToCartFromShopPage('Simple product');
|
||||
await uiUnblocked();
|
||||
await shopper.goToCheckout();
|
||||
});
|
||||
|
||||
// Apply Fixed cart discount coupon
|
||||
await expect(page).toClick('a', {text: 'Click here to enter your code'});
|
||||
await uiUnblocked();
|
||||
await expect(page).toFill('#coupon_code', couponFixedCart);
|
||||
await expect(page).toClick('button', {text: 'Apply coupon'});
|
||||
await uiUnblocked();
|
||||
await page.waitForSelector('.woocommerce-message', {text: 'Coupon code applied successfully.'});
|
||||
it('allows customer to apply fixed cart coupon', async () => {
|
||||
await applyCouponToCart( couponFixedCart );
|
||||
await expect(page).toMatchElement('.woocommerce-message', {text: 'Coupon code applied successfully.'});
|
||||
|
||||
// Wait for page to expand total calculations to avoid flakyness
|
||||
await page.waitForSelector('.order-total');
|
||||
|
||||
// Verify discount applied and order total
|
||||
await page.waitForSelector('.cart-discount .amount', {text: '$5.00'});
|
||||
await page.waitForSelector('.order-total .amount', {text: '$4.99'});
|
||||
await expect(page).toMatchElement('.cart-discount .amount', {text: '$5.00'});
|
||||
await expect(page).toMatchElement('.order-total .amount', {text: '$4.99'});
|
||||
await removeCouponFromCart();
|
||||
});
|
||||
|
||||
// Remove coupon
|
||||
await expect(page).toClick('.woocommerce-remove-coupon', {text: '[Remove]'});
|
||||
await uiUnblocked();
|
||||
await page.waitForSelector('.woocommerce-message', {text: 'Coupon has been removed.'});
|
||||
it('allows customer to apply percentage coupon', async () => {
|
||||
await applyCouponToCart( couponPercentage );
|
||||
await expect(page).toMatchElement('.woocommerce-message', {text: 'Coupon code applied successfully.'});
|
||||
|
||||
// Apply Percentage discount coupon
|
||||
await expect(page).toClick('a', {text: 'Click here to enter your code'});
|
||||
await uiUnblocked();
|
||||
await expect(page).toFill('#coupon_code', couponPercentage);
|
||||
await expect(page).toClick('button', {text: 'Apply coupon'});
|
||||
await uiUnblocked();
|
||||
await page.waitForSelector('.woocommerce-message', {text: 'Coupon code applied successfully.'});
|
||||
await page.waitForSelector('.cart-discount .amount', {text: '$4.99'});
|
||||
await page.waitForSelector('.order-total .amount', {text: '$5.00'});
|
||||
// Verify discount applied and order total
|
||||
await expect(page).toMatchElement('.cart-discount .amount', {text: '$4.99'});
|
||||
await expect(page).toMatchElement('.order-total .amount', {text: '$5.00'});
|
||||
await removeCouponFromCart();
|
||||
});
|
||||
|
||||
// Remove coupon
|
||||
await expect(page).toClick('.woocommerce-remove-coupon', {text: '[Remove]'});
|
||||
await uiUnblocked();
|
||||
await page.waitForSelector('.woocommerce-message', {text: 'Coupon has been removed.'});
|
||||
it('allows customer to apply fixed product coupon', async () => {
|
||||
await applyCouponToCart( couponFixedProduct );
|
||||
await expect(page).toMatchElement('.woocommerce-message', {text: 'Coupon code applied successfully.'});
|
||||
await expect(page).toMatchElement('.cart-discount .amount', {text: '$5.00'});
|
||||
await expect(page).toMatchElement('.order-total .amount', {text: '$4.99'});
|
||||
await removeCouponFromCart();
|
||||
});
|
||||
|
||||
// Apply Fixed product discount coupon
|
||||
await expect(page).toClick('a', {text: 'Click here to enter your code'});
|
||||
await uiUnblocked();
|
||||
await expect(page).toFill('#coupon_code', couponFixedProduct);
|
||||
await expect(page).toClick('button', {text: 'Apply coupon'});
|
||||
await uiUnblocked();
|
||||
await page.waitForSelector('.woocommerce-message', {text: 'Coupon code applied successfully.'});
|
||||
await page.waitForSelector('.cart-discount .amount', {text: '$5.00'});
|
||||
await page.waitForSelector('.order-total .amount', {text: '$4.99'});
|
||||
it('prevents customer applying same coupon twice', async () => {
|
||||
await applyCouponToCart( couponFixedCart );
|
||||
await expect(page).toMatchElement('.woocommerce-message', {text: 'Coupon code applied successfully.'});
|
||||
await applyCouponToCart( couponFixedCart );
|
||||
// Verify only one discount applied
|
||||
// This is a work around for Puppeteer inconsistently finding 'Coupon code already applied'
|
||||
await expect(page).toMatchElement('.cart-discount .amount', {text: '$5.00'});
|
||||
await expect(page).toMatchElement('.order-total .amount', {text: '$4.99'});
|
||||
});
|
||||
|
||||
// Try to apply the same coupon
|
||||
await expect(page).toClick('a', {text: 'Click here to enter your code'});
|
||||
await uiUnblocked();
|
||||
await expect(page).toFill('#coupon_code', couponFixedProduct);
|
||||
await expect(page).toClick('button', {text: 'Apply coupon'});
|
||||
await uiUnblocked();
|
||||
await page.waitForSelector('.woocommerce-error', { text: 'Coupon code already applied!' });
|
||||
it('allows customer to apply multiple coupons', async () => {
|
||||
await applyCouponToCart( couponFixedProduct );
|
||||
await expect(page).toMatchElement('.woocommerce-message', {text: 'Coupon code applied successfully.'});
|
||||
await expect(page).toMatchElement('.order-total .amount', {text: '$0.00'});
|
||||
});
|
||||
|
||||
// Try to apply multiple coupons
|
||||
await expect(page).toClick('a', {text: 'Click here to enter your code'});
|
||||
await uiUnblocked();
|
||||
await expect(page).toFill('#coupon_code', couponFixedCart);
|
||||
await expect(page).toClick('button', {text: 'Apply coupon'});
|
||||
await uiUnblocked();
|
||||
await page.waitForSelector('.woocommerce-message', {text: 'Coupon code applied successfully.'});
|
||||
await page.waitForSelector('.order-total .amount', {text: '$0.00'});
|
||||
|
||||
// Remove coupon
|
||||
await expect(page).toClick('.woocommerce-remove-coupon', {text: '[Remove]'});
|
||||
await uiUnblocked();
|
||||
await page.waitForSelector('.woocommerce-message', {text: 'Coupon has been removed.'});
|
||||
await expect(page).toClick('.woocommerce-remove-coupon', {text: '[Remove]'});
|
||||
await uiUnblocked();
|
||||
await page.waitForSelector('.woocommerce-message', {text: 'Coupon has been removed.'});
|
||||
|
||||
// Verify the total amount after all coupons removal
|
||||
await page.waitForSelector('.order-total .amount', {text: '$9.99'});
|
||||
it('restores cart total when coupons are removed', async () => {
|
||||
await removeCouponFromCart();
|
||||
await removeCouponFromCart();
|
||||
await expect(page).toMatchElement('.order-total .amount', {text: '$9.99'});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
|
|
@ -20,7 +20,8 @@ const threeProductPrice = singleProductPrice * 3;
|
|||
const fourProductPrice = singleProductPrice * 4;
|
||||
const fiveProductPrice = singleProductPrice * 5;
|
||||
|
||||
let orderId;
|
||||
let guestOrderId;
|
||||
let customerOrderId;
|
||||
|
||||
const runCheckoutPageTest = () => {
|
||||
describe('Checkout page', () => {
|
||||
|
@ -136,34 +137,36 @@ const runCheckoutPageTest = () => {
|
|||
// Get order ID from the order received html element on the page
|
||||
let orderReceivedHtmlElement = await page.$('.woocommerce-order-overview__order.order');
|
||||
let orderReceivedText = await page.evaluate(element => element.textContent, orderReceivedHtmlElement);
|
||||
return orderId = orderReceivedText.split(/(\s+)/)[6].toString();
|
||||
return guestOrderId = orderReceivedText.split(/(\s+)/)[6].toString();
|
||||
});
|
||||
|
||||
it('allows existing customer to place order', async () => {
|
||||
await shopper.login();
|
||||
await shopper.goToShop();
|
||||
await shopper.addToCartFromShopPage(simpleProductName);
|
||||
await shopper.goToCheckout();
|
||||
await shopper.productIsInCheckout(simpleProductName, `1`, singleProductPrice, singleProductPrice);
|
||||
await shopper.fillBillingDetails(config.get('addresses.customer.billing'));
|
||||
|
||||
await uiUnblocked();
|
||||
|
||||
await expect(page).toClick('.wc_payment_method label', {text: 'Cash on delivery'});
|
||||
await expect(page).toMatchElement('.payment_method_cod', {text: 'Pay with cash upon delivery.'});
|
||||
await uiUnblocked();
|
||||
await shopper.placeOrder();
|
||||
|
||||
await expect(page).toMatch('Order received');
|
||||
|
||||
// Get order ID from the order received html element on the page
|
||||
let orderReceivedHtmlElement = await page.$('.woocommerce-order-overview__order.order');
|
||||
let orderReceivedText = await page.evaluate(element => element.textContent, orderReceivedHtmlElement);
|
||||
return customerOrderId = orderReceivedText.split(/(\s+)/)[6].toString();
|
||||
});
|
||||
|
||||
it('store owner can confirm the order was received', async () => {
|
||||
await merchant.login();
|
||||
await merchant.openAllOrdersView();
|
||||
|
||||
// Click on the order which was placed in the previous step
|
||||
await Promise.all([
|
||||
page.click('#post-' + orderId),
|
||||
page.waitForNavigation({waitUntil: 'networkidle0'}),
|
||||
]);
|
||||
|
||||
// Verify that the order page is indeed of the order that was placed
|
||||
// Verify order number
|
||||
await expect(page).toMatchElement('.woocommerce-order-data__heading', {text: 'Order #' + orderId + ' details'});
|
||||
|
||||
// Verify product name
|
||||
await expect(page).toMatchElement('.wc-order-item-name', {text: simpleProductName});
|
||||
|
||||
// Verify product cost
|
||||
await expect(page).toMatchElement('.woocommerce-Price-amount.amount', {text: singleProductPrice});
|
||||
|
||||
// Verify product quantity
|
||||
await expect(page).toMatchElement('.quantity', {text: '5'});
|
||||
|
||||
// Verify total order amount without shipping
|
||||
await expect(page).toMatchElement('.line_cost', {text: fiveProductPrice});
|
||||
await merchant.verifyOrder(guestOrderId, simpleProductName, singleProductPrice, 5, fiveProductPrice);
|
||||
await merchant.verifyOrder(customerOrderId, simpleProductName, singleProductPrice, 1, singleProductPrice, true);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
|
|
@ -1,5 +1,11 @@
|
|||
# Unreleased
|
||||
|
||||
# 0.2.0
|
||||
|
||||
## Fixed
|
||||
|
||||
- Return jest exit code from `npx wc-e2e test:e2e*`
|
||||
|
||||
## Added
|
||||
|
||||
- support for custom container name
|
||||
|
@ -9,7 +15,7 @@
|
|||
## Fixed
|
||||
|
||||
- Remove redundant `puppeteer` dependency
|
||||
- Support for admin user configuration from default.json
|
||||
- Support for admin user configuration from `default.json`
|
||||
|
||||
# 0.1.6
|
||||
|
||||
|
|
|
@ -25,6 +25,9 @@ fi
|
|||
# Store original path
|
||||
OLDPATH=$(pwd)
|
||||
|
||||
# Return value for CI test runs
|
||||
TESTRESULT=0
|
||||
|
||||
# Use the script symlink to find and change directory to the root of the package
|
||||
SCRIPTPATH=$(dirname "$0")
|
||||
REALPATH=$(readlink $0)
|
||||
|
@ -46,12 +49,15 @@ case $1 in
|
|||
;;
|
||||
'test:e2e')
|
||||
./bin/wait-for-build.sh && ./bin/e2e-test-integration.js $2
|
||||
TESTRESULT=$?
|
||||
;;
|
||||
'test:e2e-dev')
|
||||
./bin/wait-for-build.sh && ./bin/e2e-test-integration.js --dev $2
|
||||
TESTRESULT=$?
|
||||
;;
|
||||
'test:e2e-debug')
|
||||
./bin/wait-for-build.sh && ./bin/e2e-test-integration.js --dev --debug $2
|
||||
TESTRESULT=$?
|
||||
;;
|
||||
*)
|
||||
usage
|
||||
|
@ -60,3 +66,5 @@ esac
|
|||
|
||||
# Restore working path
|
||||
cd $OLDPATH
|
||||
|
||||
exit $TESTRESULT
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@woocommerce/e2e-environment",
|
||||
"version": "0.1.6",
|
||||
"version": "0.2.0",
|
||||
"description": "WooCommerce End to End Testing Environment Configuration.",
|
||||
"author": "Automattic",
|
||||
"license": "GPL-3.0-or-later",
|
||||
|
|
|
@ -18,6 +18,21 @@ import {
|
|||
* @type {Array}
|
||||
*/
|
||||
const pageEvents = [];
|
||||
/**
|
||||
* Set of logged messages that will only be logged once.
|
||||
*
|
||||
* @type {Object<string,object>}
|
||||
*/
|
||||
const loggedMessages = {
|
||||
proxy: {
|
||||
logged: false,
|
||||
text: 'Failed to load resource: net::ERR_PROXY_CONNECTION_FAILED',
|
||||
},
|
||||
http404: {
|
||||
logged: false,
|
||||
text: 'the server responded with a status of 404',
|
||||
},
|
||||
};
|
||||
/**
|
||||
* Set of console logging types observed to protect against unexpected yet
|
||||
* handled (i.e. not catastrophic) errors or warnings. Each key corresponds
|
||||
|
@ -140,16 +155,21 @@ function observeConsoleLogging() {
|
|||
return;
|
||||
}
|
||||
|
||||
// As of WordPress 5.3.2 in Chrome 79, navigating to the block editor
|
||||
// (Posts > Add New) will display a console warning about
|
||||
// non - unique IDs.
|
||||
// See: https://core.trac.wordpress.org/ticket/23165
|
||||
if ( text.includes( 'elements with non-unique id #_wpnonce' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
const logFunction = OBSERVED_CONSOLE_MESSAGE_TYPES[ type ];
|
||||
|
||||
// Limit warnings on missing resources.
|
||||
let previouslyLogged = false;
|
||||
Object.keys( loggedMessages ).forEach( function( key ) {
|
||||
if ( text.includes( loggedMessages[ key ].text ) ) {
|
||||
if ( loggedMessages[ key ].logged ) {
|
||||
previouslyLogged = true;
|
||||
}
|
||||
loggedMessages[ key ].logged = true;
|
||||
}
|
||||
} );
|
||||
if ( previouslyLogged ) {
|
||||
return;
|
||||
}
|
||||
// As of Puppeteer 1.6.1, `message.text()` wrongly returns an object of
|
||||
// type JSHandle for error logging, instead of the expected string.
|
||||
//
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
/*
|
||||
* Internal dependencies
|
||||
*/
|
||||
const { runCartApplyCouponsTest } = require( '@woocommerce/e2e-core-tests' );
|
||||
|
||||
runCartApplyCouponsTest();
|
|
@ -1,5 +1,7 @@
|
|||
# Unreleased
|
||||
|
||||
# 0.1.2
|
||||
|
||||
## Fixed
|
||||
|
||||
- Missing `config` package dependency
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@woocommerce/e2e-utils",
|
||||
"version": "0.1.1",
|
||||
"version": "0.1.2",
|
||||
"description": "End-To-End (E2E) test utils for WooCommerce",
|
||||
"homepage": "https://github.com/woocommerce/woocommerce/tree/master/tests/e2e-utils/README.md",
|
||||
"repository": {
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
* Internal dependencies
|
||||
*/
|
||||
import { merchant } from './flows';
|
||||
import { clickTab, uiUnblocked, verifyCheckboxIsUnset, selectOptionInSelect2 } from './page-utils';
|
||||
import { clickTab, uiUnblocked, verifyCheckboxIsUnset, evalAndClick, selectOptionInSelect2 } from './page-utils';
|
||||
import factories from './factories';
|
||||
|
||||
const config = require( 'config' );
|
||||
|
@ -25,6 +25,20 @@ const verifyAndPublish = async () => {
|
|||
await expect( page ).toMatchElement( '.updated.notice', { text: 'Product published.' } );
|
||||
};
|
||||
|
||||
/**
|
||||
* Wait for primary button to be enabled and click.
|
||||
*
|
||||
* @param waitForNetworkIdle - Wait for network idle after click
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
const waitAndClickPrimary = async ( waitForNetworkIdle = true ) => {
|
||||
// Wait for "Continue" button to become active
|
||||
await page.waitForSelector( 'button.is-primary:not(:disabled)' );
|
||||
await page.click( 'button.is-primary' );
|
||||
if ( waitForNetworkIdle ) {
|
||||
await page.waitForNavigation( { waitUntil: 'networkidle0' } );
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Complete onboarding wizard.
|
||||
*/
|
||||
|
@ -88,15 +102,7 @@ const completeOnboardingWizard = async () => {
|
|||
await expect( page ).toFill( '.components-text-control__input', config.get( 'onboardingwizard.industry' ) );
|
||||
|
||||
// Wait for "Continue" button to become active
|
||||
await page.waitForSelector( 'button.is-primary:not(:disabled)' );
|
||||
|
||||
await Promise.all( [
|
||||
// Click on "Continue" button to move to the next step
|
||||
page.click( 'button.is-primary' ),
|
||||
|
||||
// Wait for "What type of products will be listed?" section to load
|
||||
page.waitForNavigation( { waitUntil: 'networkidle0' } ),
|
||||
] );
|
||||
await waitAndClickPrimary();
|
||||
|
||||
// Product types section
|
||||
|
||||
|
@ -110,15 +116,7 @@ const completeOnboardingWizard = async () => {
|
|||
}
|
||||
|
||||
// Wait for "Continue" button to become active
|
||||
await page.waitForSelector( 'button.is-primary:not(:disabled)' );
|
||||
|
||||
await Promise.all( [
|
||||
// Click on "Continue" button to move to the next step
|
||||
page.click( 'button.is-primary' ),
|
||||
|
||||
// Wait for "Tell us about your business" section to load
|
||||
page.waitForNavigation( { waitUntil: 'networkidle0' } ),
|
||||
] );
|
||||
await waitAndClickPrimary();
|
||||
|
||||
// Business Details section
|
||||
|
||||
|
@ -136,48 +134,15 @@ const completeOnboardingWizard = async () => {
|
|||
await page.waitForSelector( '.woocommerce-select-control__control' );
|
||||
await expect( page ).toClick( '.woocommerce-select-control__option', { text: config.get( 'onboardingwizard.sellingelsewhere' ) } );
|
||||
|
||||
// Query for the extensions toggles
|
||||
const extensionsToggles = await page.$$( '.components-form-toggle__input' );
|
||||
expect( extensionsToggles ).toHaveLength( 4 );
|
||||
|
||||
// Disable download of the onboarding suggested extensions
|
||||
for ( let i = 0; i < extensionsToggles.length; i++ ) {
|
||||
await extensionsToggles[i].click();
|
||||
}
|
||||
|
||||
// Wait for "Continue" button to become active
|
||||
await page.waitForSelector( 'button.is-primary:not(:disabled)' );
|
||||
await waitAndClickPrimary( false );
|
||||
|
||||
await Promise.all( [
|
||||
// Click on "Continue" button to move to the next step
|
||||
page.click( 'button.is-primary' ),
|
||||
|
||||
// Wait for "Theme" section to load
|
||||
page.waitForNavigation( { waitUntil: 'networkidle0' } ),
|
||||
] );
|
||||
// Skip installing extensions
|
||||
await evalAndClick( '.components-checkbox-control__input' );
|
||||
await waitAndClickPrimary();
|
||||
|
||||
// Theme section
|
||||
|
||||
// Wait for "Continue with my active theme" button to become active
|
||||
await page.waitForSelector( 'button.is-primary:not(:disabled)' );
|
||||
|
||||
await Promise.all( [
|
||||
// Click on "Continue with my active theme" button to move to the next step
|
||||
page.click( 'button.is-primary' ),
|
||||
|
||||
// Wait for "Enhance your store with WooCommerce Services" section to load
|
||||
page.waitForNavigation( { waitUntil: 'networkidle0' } ),
|
||||
] );
|
||||
|
||||
// Benefits section
|
||||
|
||||
// Wait for Benefits section to appear
|
||||
await page.waitForSelector( '.woocommerce-profile-wizard__benefits' );
|
||||
|
||||
// Wait for "No thanks" button to become active
|
||||
await page.waitForSelector( 'button.is-secondary:not(:disabled)' );
|
||||
// Click on "No thanks" button to move to the next step
|
||||
await page.click( 'button.is-secondary' );
|
||||
await waitAndClickPrimary();
|
||||
|
||||
// End of onboarding wizard
|
||||
|
||||
|
|
|
@ -131,6 +131,31 @@ const merchant = {
|
|||
await page.waitForSelector( '#message' );
|
||||
await expect( page ).toMatchElement( '#message', { text: 'Order updated.' } );
|
||||
},
|
||||
|
||||
verifyOrder: async (orderId, productName, productPrice, quantity, orderTotal, ensureCustomerRegistered = false) => {
|
||||
await merchant.goToOrder(orderId);
|
||||
|
||||
// Verify that the order page is indeed of the order that was placed
|
||||
// Verify order number
|
||||
await expect(page).toMatchElement('.woocommerce-order-data__heading', {text: 'Order #' + orderId + ' details'});
|
||||
|
||||
// Verify product name
|
||||
await expect(page).toMatchElement('.wc-order-item-name', {text: productName});
|
||||
|
||||
// Verify product cost
|
||||
await expect(page).toMatchElement('.woocommerce-Price-amount.amount', {text: productPrice});
|
||||
|
||||
// Verify product quantity
|
||||
await expect(page).toMatchElement('.quantity', {text: quantity.toString()});
|
||||
|
||||
// Verify total order amount without shipping
|
||||
await expect(page).toMatchElement('.line_cost', {text: orderTotal});
|
||||
|
||||
if ( ensureCustomerRegistered ) {
|
||||
// Verify customer profile link is present to verify order was placed by a registered customer, not a guest
|
||||
await expect( page ).toMatchElement( 'label[for="customer_user"] a[href*=user-edit]', { text: 'Profile' } );
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = merchant;
|
||||
|
|
|
@ -73,4 +73,68 @@ class WC_Abstract_Order_Test extends WC_Unit_Test_Case {
|
|||
$this->assertEquals( 1248.96, $order->get_total() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that coupon taxes are not affected by logged in admin user.
|
||||
*/
|
||||
public function test_apply_coupon_for_correct_location_taxes() {
|
||||
update_option( 'woocommerce_tax_round_at_subtotal', 'yes' );
|
||||
update_option( 'woocommerce_prices_include_tax', 'yes' );
|
||||
update_option( 'woocommerce_tax_based_on', 'billing' );
|
||||
update_option( 'woocommerce_calc_taxes', 'yes' );
|
||||
|
||||
$password = wp_generate_password( 8, false, false );
|
||||
$admin_id = wp_insert_user(
|
||||
array(
|
||||
'user_login' => "test_admin$password",
|
||||
'user_pass' => $password,
|
||||
'user_email' => "admin$password@example.com",
|
||||
'role' => 'administrator',
|
||||
)
|
||||
);
|
||||
|
||||
update_user_meta( $admin_id, 'billing_country', 'MV' ); // Different than customer's address and base location.
|
||||
wp_set_current_user( $admin_id );
|
||||
WC()->customer = null;
|
||||
WC()->initialize_cart();
|
||||
|
||||
update_option( 'woocommerce_default_country', 'IN:AP' );
|
||||
|
||||
$tax_rate = array(
|
||||
'tax_rate_country' => 'IN',
|
||||
'tax_rate_state' => '',
|
||||
'tax_rate' => '25.0000',
|
||||
'tax_rate_name' => 'tax',
|
||||
'tax_rate_order' => '1',
|
||||
'tax_rate_class' => '',
|
||||
);
|
||||
WC_Tax::_insert_tax_rate( $tax_rate );
|
||||
|
||||
$product = WC_Helper_Product::create_simple_product();
|
||||
$product->set_regular_price( 100 );
|
||||
$product->save();
|
||||
|
||||
$order = wc_create_order();
|
||||
$order->set_billing_country( 'IN' );
|
||||
$order->add_product( $product, 1 );
|
||||
$order->save();
|
||||
$order->calculate_totals();
|
||||
|
||||
$this->assertEquals( 100, $order->get_total() );
|
||||
$this->assertEquals( 80, $order->get_subtotal() );
|
||||
$this->assertEquals( 20, $order->get_total_tax() );
|
||||
|
||||
$coupon = new WC_Coupon();
|
||||
$coupon->set_code( '10off' );
|
||||
$coupon->set_discount_type( 'percent' );
|
||||
$coupon->set_amount( 10 );
|
||||
$coupon->save();
|
||||
|
||||
$order->apply_coupon( '10off' );
|
||||
|
||||
$this->assertEquals( 8, $order->get_discount_total() );
|
||||
$this->assertEquals( 90, $order->get_total() );
|
||||
$this->assertEquals( 18, $order->get_total_tax() );
|
||||
$this->assertEquals( 2, $order->get_discount_tax() );
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* Plugin Name: WooCommerce
|
||||
* Plugin URI: https://woocommerce.com/
|
||||
* Description: An eCommerce toolkit that helps you sell anything. Beautifully.
|
||||
* Version: 5.0.0-dev
|
||||
* Version: 5.1.0-dev
|
||||
* Author: Automattic
|
||||
* Author URI: https://woocommerce.com
|
||||
* Text Domain: woocommerce
|
||||
|
|
Loading…
Reference in New Issue