diff --git a/.github/workflows/pr-build-and-e2e-tests.yml b/.github/workflows/pr-build-and-e2e-tests.yml index bb66e43bc44..169224a0b2f 100644 --- a/.github/workflows/pr-build-and-e2e-tests.yml +++ b/.github/workflows/pr-build-and-e2e-tests.yml @@ -21,38 +21,11 @@ jobs: name: woocommerce path: ${{ steps.build.outputs.zip_path }} retention-days: 7 - - e2e-tests-cache: - name: Set e2e caches for running tests - runs-on: ubuntu-latest - steps: - - name: Checkout code. - uses: actions/checkout@v2 - - - name: Load Node.js. - uses: actions/setup-node@v2 - with: - node-version: '12' - - # From https://docs.github.com/en/actions/guides/caching-dependencies-to-speed-up-workflows#using-the-cache-action - - name: Cache node modules - uses: actions/cache@v2 - id: cache_node_modules - env: - cache-name: cache-node-modules - with: - path: ./node_modules - key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('package-lock.json') }} - restore-keys: | - ${{ runner.os }}-build-${{ env.cache-name }}- - - name: Run npm install, and cache if they aren't. - run: npm install - e2e-tests-run: name: Runs E2E tests. - runs-on: ubuntu-latest - needs: [ build, e2e-tests-cache ] + runs-on: ubuntu-18.04 + needs: [ build ] steps: - name: Create dirs. @@ -66,20 +39,6 @@ jobs: uses: actions/checkout@v2 with: path: package/woocommerce - - # From https://docs.github.com/en/actions/guides/caching-dependencies-to-speed-up-workflows#using-the-cache-action - - name: Cache node modules - uses: actions/cache@v2 - id: cache_node_modules - env: - cache-name: cache-node-modules - with: - path: ./node_modules - key: ${{ runner.os }}-build-${{ hashFiles('package-lock.json') }} - restore-keys: ${{ runner.os }}-build-${{ env.cache-name }}- - - - name: Restore node modules from cache, if available. - run: mv ./node_modules package/woocommerce/node_modules - name: Run npm install. working-directory: package/woocommerce @@ -87,6 +46,8 @@ jobs: - name: Load docker images and start containers. working-directory: package/woocommerce + env: + WP_VERSION: 5.6.2 run: npx wc-e2e docker:up - name: Move current directory to code. We will install zip file in this dir later. @@ -106,4 +67,8 @@ jobs: - name: Run tests command. working-directory: code/woocommerce + env: + WC_E2E_SCREENSHOTS: 1 + E2E_SLACK_TOKEN: ${{ secrets.E2E_SLACK_TOKEN }} + E2E_SLACK_CHANNEL: ${{ secrets.E2E_SLACK_CHANNEL }} run: npx wc-e2e test:e2e diff --git a/.gitignore b/.gitignore index c86c350cc91..d5584c21452 100644 --- a/.gitignore +++ b/.gitignore @@ -50,6 +50,7 @@ tests/cli/vendor /tests/e2e/env/docker/wp-cli/initialize.sh /tests/e2e/env/build/ /tests/e2e/env/build-module/ +/tests/e2e/screenshots /tests/e2e/utils/build/ /tests/e2e/utils/build-module/ diff --git a/assets/images/mercadopago.png b/assets/images/mercadopago.png new file mode 100644 index 00000000000..39a790e5bf4 Binary files /dev/null and b/assets/images/mercadopago.png differ diff --git a/assets/js/frontend/cart.js b/assets/js/frontend/cart.js index 622c8eb1437..b4455119c08 100644 --- a/assets/js/frontend/cart.js +++ b/assets/js/frontend/cart.js @@ -180,6 +180,7 @@ jQuery( function( $ ) { */ toggle_shipping: function() { $( '.shipping-calculator-form' ).slideToggle( 'slow' ); + $( 'select.country_to_state, input.country_to_state' ).trigger( 'change' ); $( document.body ).trigger( 'country_to_state_changed' ); // Trigger select2 to load. return false; }, diff --git a/bin/composer/mozart/composer.lock b/bin/composer/mozart/composer.lock index 1a3ce3075fe..1a6fcb7090b 100644 --- a/bin/composer/mozart/composer.lock +++ b/bin/composer/mozart/composer.lock @@ -206,27 +206,22 @@ }, { "name": "psr/container", - "version": "1.0.0", + "version": "1.1.1", "source": { "type": "git", "url": "https://github.com/php-fig/container.git", - "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f" + "reference": "8622567409010282b7aeebe4bb841fe98b58dcaf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f", - "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f", + "url": "https://api.github.com/repos/php-fig/container/zipball/8622567409010282b7aeebe4bb841fe98b58dcaf", + "reference": "8622567409010282b7aeebe4bb841fe98b58dcaf", "shasum": "" }, "require": { - "php": ">=5.3.0" + "php": ">=7.2.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, "autoload": { "psr-4": { "Psr\\Container\\": "src/" @@ -239,7 +234,7 @@ "authors": [ { "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" + "homepage": "https://www.php-fig.org/" } ], "description": "Common Container Interface (PHP FIG PSR-11)", @@ -251,24 +246,20 @@ "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" + "time": "2021-03-05T17:36:06+00:00" }, { "name": "symfony/console", - "version": "v5.2.3", + "version": "v5.2.4", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "89d4b176d12a2946a1ae4e34906a025b7b6b135a" + "reference": "d6d0cc30d8c0fda4e7b213c20509b0159a8f4556" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/89d4b176d12a2946a1ae4e34906a025b7b6b135a", - "reference": "89d4b176d12a2946a1ae4e34906a025b7b6b135a", + "url": "https://api.github.com/repos/symfony/console/zipball/d6d0cc30d8c0fda4e7b213c20509b0159a8f4556", + "reference": "d6d0cc30d8c0fda4e7b213c20509b0159a8f4556", "shasum": "" }, "require": { @@ -349,20 +340,20 @@ "type": "tidelift" } ], - "time": "2021-01-28T22:06:19+00:00" + "time": "2021-02-23T10:08:49+00:00" }, { "name": "symfony/finder", - "version": "v5.2.3", + "version": "v5.2.4", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "4adc8d172d602008c204c2e16956f99257248e03" + "reference": "0d639a0943822626290d169965804f79400e6a04" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/4adc8d172d602008c204c2e16956f99257248e03", - "reference": "4adc8d172d602008c204c2e16956f99257248e03", + "url": "https://api.github.com/repos/symfony/finder/zipball/0d639a0943822626290d169965804f79400e6a04", + "reference": "0d639a0943822626290d169965804f79400e6a04", "shasum": "" }, "require": { @@ -407,11 +398,11 @@ "type": "tidelift" } ], - "time": "2021-01-28T22:06:19+00:00" + "time": "2021-02-15T18:55:04+00:00" }, { "name": "symfony/polyfill-ctype", - "version": "v1.22.0", + "version": "v1.22.1", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", @@ -487,16 +478,16 @@ }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.22.0", + "version": "v1.22.1", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "267a9adeb8ecb8071040a740930e077cdfb987af" + "reference": "5601e09b69f26c1828b13b6bb87cb07cddba3170" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/267a9adeb8ecb8071040a740930e077cdfb987af", - "reference": "267a9adeb8ecb8071040a740930e077cdfb987af", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/5601e09b69f26c1828b13b6bb87cb07cddba3170", + "reference": "5601e09b69f26c1828b13b6bb87cb07cddba3170", "shasum": "" }, "require": { @@ -561,20 +552,20 @@ "type": "tidelift" } ], - "time": "2021-01-07T16:49:33+00:00" + "time": "2021-01-22T09:19:47+00:00" }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.22.0", + "version": "v1.22.1", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "6e971c891537eb617a00bb07a43d182a6915faba" + "reference": "43a0283138253ed1d48d352ab6d0bdb3f809f248" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/6e971c891537eb617a00bb07a43d182a6915faba", - "reference": "6e971c891537eb617a00bb07a43d182a6915faba", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/43a0283138253ed1d48d352ab6d0bdb3f809f248", + "reference": "43a0283138253ed1d48d352ab6d0bdb3f809f248", "shasum": "" }, "require": { @@ -642,20 +633,20 @@ "type": "tidelift" } ], - "time": "2021-01-07T17:09:11+00:00" + "time": "2021-01-22T09:19:47+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.22.0", + "version": "v1.22.1", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "f377a3dd1fde44d37b9831d68dc8dea3ffd28e13" + "reference": "5232de97ee3b75b0360528dae24e73db49566ab1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/f377a3dd1fde44d37b9831d68dc8dea3ffd28e13", - "reference": "f377a3dd1fde44d37b9831d68dc8dea3ffd28e13", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/5232de97ee3b75b0360528dae24e73db49566ab1", + "reference": "5232de97ee3b75b0360528dae24e73db49566ab1", "shasum": "" }, "require": { @@ -719,11 +710,11 @@ "type": "tidelift" } ], - "time": "2021-01-07T16:49:33+00:00" + "time": "2021-01-22T09:19:47+00:00" }, { "name": "symfony/polyfill-php73", - "version": "v1.22.0", + "version": "v1.22.1", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php73.git", @@ -799,7 +790,7 @@ }, { "name": "symfony/polyfill-php80", - "version": "v1.22.0", + "version": "v1.22.1", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php80.git", @@ -955,16 +946,16 @@ }, { "name": "symfony/string", - "version": "v5.2.3", + "version": "v5.2.4", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "c95468897f408dd0aca2ff582074423dd0455122" + "reference": "4e78d7d47061fa183639927ec40d607973699609" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/c95468897f408dd0aca2ff582074423dd0455122", - "reference": "c95468897f408dd0aca2ff582074423dd0455122", + "url": "https://api.github.com/repos/symfony/string/zipball/4e78d7d47061fa183639927ec40d607973699609", + "reference": "4e78d7d47061fa183639927ec40d607973699609", "shasum": "" }, "require": { @@ -1031,7 +1022,7 @@ "type": "tidelift" } ], - "time": "2021-01-25T15:14:59+00:00" + "time": "2021-02-16T10:20:28+00:00" } ], "aliases": [], diff --git a/bin/composer/phpcs/composer.lock b/bin/composer/phpcs/composer.lock index 5a70d30ff7e..10aedae9334 100644 --- a/bin/composer/phpcs/composer.lock +++ b/bin/composer/phpcs/composer.lock @@ -133,28 +133,28 @@ }, { "name": "phpcompatibility/phpcompatibility-paragonie", - "version": "1.3.0", + "version": "1.3.1", "source": { "type": "git", "url": "https://github.com/PHPCompatibility/PHPCompatibilityParagonie.git", - "reference": "b862bc32f7e860d0b164b199bd995e690b4b191c" + "reference": "ddabec839cc003651f2ce695c938686d1086cf43" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibilityParagonie/zipball/b862bc32f7e860d0b164b199bd995e690b4b191c", - "reference": "b862bc32f7e860d0b164b199bd995e690b4b191c", + "url": "https://api.github.com/repos/PHPCompatibility/PHPCompatibilityParagonie/zipball/ddabec839cc003651f2ce695c938686d1086cf43", + "reference": "ddabec839cc003651f2ce695c938686d1086cf43", "shasum": "" }, "require": { "phpcompatibility/php-compatibility": "^9.0" }, "require-dev": { - "dealerdirect/phpcodesniffer-composer-installer": "^0.5", + "dealerdirect/phpcodesniffer-composer-installer": "^0.7", "paragonie/random_compat": "dev-master", "paragonie/sodium_compat": "dev-master" }, "suggest": { - "dealerdirect/phpcodesniffer-composer-installer": "^0.5 || This Composer plugin will sort out the PHP_CodeSniffer 'installed_paths' automatically.", + "dealerdirect/phpcodesniffer-composer-installer": "^0.7 || This Composer plugin will sort out the PHP_CodeSniffer 'installed_paths' automatically.", "roave/security-advisories": "dev-master || Helps prevent installing dependencies with known security issues." }, "type": "phpcodesniffer-standard", @@ -181,7 +181,7 @@ "polyfill", "standards" ], - "time": "2019-11-04T15:17:54+00:00" + "time": "2021-02-15T10:24:51+00:00" }, { "name": "phpcompatibility/phpcompatibility-wp", @@ -282,11 +282,6 @@ "phpcs", "standards" ], - "support": { - "issues": "https://github.com/squizlabs/PHP_CodeSniffer/issues", - "source": "https://github.com/squizlabs/PHP_CodeSniffer", - "wiki": "https://github.com/squizlabs/PHP_CodeSniffer/wiki" - }, "time": "2020-10-23T02:01:07+00:00" }, { diff --git a/bin/composer/phpunit/composer.lock b/bin/composer/phpunit/composer.lock index d1c92edd290..efc67dcf121 100644 --- a/bin/composer/phpunit/composer.lock +++ b/bin/composer/phpunit/composer.lock @@ -59,10 +59,6 @@ "constructor", "instantiate" ], - "support": { - "issues": "https://github.com/doctrine/instantiator/issues", - "source": "https://github.com/doctrine/instantiator/tree/master" - }, "time": "2015-06-14T21:17:01+00:00" }, { @@ -108,10 +104,6 @@ "object", "object graph" ], - "support": { - "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.x" - }, "time": "2017-10-19T19:58:43+00:00" }, { @@ -167,10 +159,6 @@ } ], "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", - "support": { - "issues": "https://github.com/phar-io/manifest/issues", - "source": "https://github.com/phar-io/manifest/tree/master" - }, "time": "2017-03-05T18:14:27+00:00" }, { @@ -218,10 +206,6 @@ } ], "description": "Library for handling version information and constraints", - "support": { - "issues": "https://github.com/phar-io/version/issues", - "source": "https://github.com/phar-io/version/tree/master" - }, "time": "2017-03-05T17:38:23+00:00" }, { @@ -276,10 +260,6 @@ "reflection", "static analysis" ], - "support": { - "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", - "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/master" - }, "time": "2017-09-11T18:02:19+00:00" }, { @@ -377,10 +357,6 @@ "email": "me@mikevanriel.com" } ], - "support": { - "issues": "https://github.com/phpDocumentor/TypeResolver/issues", - "source": "https://github.com/phpDocumentor/TypeResolver/tree/master" - }, "time": "2017-12-30T13:23:38+00:00" }, { @@ -507,10 +483,6 @@ "testing", "xunit" ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/5.3" - }, "time": "2018-04-06T15:36:58+00:00" }, { @@ -558,11 +530,6 @@ "filesystem", "iterator" ], - "support": { - "irc": "irc://irc.freenode.net/phpunit", - "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", - "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/1.4.5" - }, "time": "2017-11-27T13:52:08+00:00" }, { @@ -653,10 +620,6 @@ "keywords": [ "timer" ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-timer/issues", - "source": "https://github.com/sebastianbergmann/php-timer/tree/master" - }, "time": "2017-02-26T11:10:40+00:00" }, { @@ -706,10 +669,6 @@ "keywords": [ "tokenizer" ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-token-stream/issues", - "source": "https://github.com/sebastianbergmann/php-token-stream/tree/master" - }, "abandoned": true, "time": "2017-11-27T05:48:46+00:00" }, @@ -795,10 +754,6 @@ "testing", "xunit" ], - "support": { - "issues": "https://github.com/sebastianbergmann/phpunit/issues", - "source": "https://github.com/sebastianbergmann/phpunit/tree/6.5.14" - }, "time": "2019-02-01T05:22:47+00:00" }, { @@ -858,10 +813,6 @@ "mock", "xunit" ], - "support": { - "issues": "https://github.com/sebastianbergmann/phpunit-mock-objects/issues", - "source": "https://github.com/sebastianbergmann/phpunit-mock-objects/tree/5.0.10" - }, "abandoned": true, "time": "2018-08-09T05:50:03+00:00" }, @@ -908,10 +859,6 @@ ], "description": "Looks up which function or method a line of code belongs to", "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", - "support": { - "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", - "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/1.0.2" - }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -982,10 +929,6 @@ "compare", "equality" ], - "support": { - "issues": "https://github.com/sebastianbergmann/comparator/issues", - "source": "https://github.com/sebastianbergmann/comparator/tree/master" - }, "time": "2018-02-01T13:46:46+00:00" }, { @@ -1038,10 +981,6 @@ "keywords": [ "diff" ], - "support": { - "issues": "https://github.com/sebastianbergmann/diff/issues", - "source": "https://github.com/sebastianbergmann/diff/tree/master" - }, "time": "2017-08-03T08:09:46+00:00" }, { @@ -1092,10 +1031,6 @@ "environment", "hhvm" ], - "support": { - "issues": "https://github.com/sebastianbergmann/environment/issues", - "source": "https://github.com/sebastianbergmann/environment/tree/master" - }, "time": "2017-07-01T08:51:00+00:00" }, { @@ -1163,10 +1098,6 @@ "export", "exporter" ], - "support": { - "issues": "https://github.com/sebastianbergmann/exporter/issues", - "source": "https://github.com/sebastianbergmann/exporter/tree/3.1.3" - }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -1271,10 +1202,6 @@ ], "description": "Traverses array structures and object graphs to enumerate all referenced objects", "homepage": "https://github.com/sebastianbergmann/object-enumerator/", - "support": { - "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", - "source": "https://github.com/sebastianbergmann/object-enumerator/tree/3.0.4" - }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -1326,10 +1253,6 @@ ], "description": "Allows reflection of object attributes, including inherited and non-public ones", "homepage": "https://github.com/sebastianbergmann/object-reflector/", - "support": { - "issues": "https://github.com/sebastianbergmann/object-reflector/issues", - "source": "https://github.com/sebastianbergmann/object-reflector/tree/1.1.2" - }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -1389,10 +1312,6 @@ ], "description": "Provides functionality to recursively process PHP variables", "homepage": "http://www.github.com/sebastianbergmann/recursion-context", - "support": { - "issues": "https://github.com/sebastianbergmann/recursion-context/issues", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/3.0.1" - }, "funding": [ { "url": "https://github.com/sebastianbergmann", @@ -1441,10 +1360,6 @@ ], "description": "Provides a list of PHP built-in functions that operate on resources", "homepage": "https://www.github.com/sebastianbergmann/resource-operations", - "support": { - "issues": "https://github.com/sebastianbergmann/resource-operations/issues", - "source": "https://github.com/sebastianbergmann/resource-operations/tree/master" - }, "time": "2015-07-28T20:34:47+00:00" }, { @@ -1550,9 +1465,6 @@ "polyfill", "portable" ], - "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.19.0" - }, "funding": [ { "url": "https://symfony.com/sponsor", diff --git a/bin/composer/wp/composer.lock b/bin/composer/wp/composer.lock index 24fdbbf28cb..f0f61f98695 100644 --- a/bin/composer/wp/composer.lock +++ b/bin/composer/wp/composer.lock @@ -67,11 +67,6 @@ "po", "translation" ], - "support": { - "email": "oom@oscarotero.com", - "issues": "https://github.com/oscarotero/Gettext/issues", - "source": "https://github.com/php-gettext/Gettext/tree/v4.8.3" - }, "time": "2020-11-18T22:35:49+00:00" }, { @@ -322,9 +317,6 @@ ], "description": "Symfony Finder Component", "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/finder/tree/3.3" - }, "time": "2017-06-01T21:01:25+00:00" }, { @@ -434,16 +426,16 @@ }, { "name": "wp-cli/php-cli-tools", - "version": "v0.11.11", + "version": "v0.11.12", "source": { "type": "git", "url": "https://github.com/wp-cli/php-cli-tools.git", - "reference": "fe9c7c44a9e1bf2196ec51dc38da0593dbf2993f" + "reference": "e472e08489f7504d9e8c5c5a057e1419cd1b2b3e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/wp-cli/php-cli-tools/zipball/fe9c7c44a9e1bf2196ec51dc38da0593dbf2993f", - "reference": "fe9c7c44a9e1bf2196ec51dc38da0593dbf2993f", + "url": "https://api.github.com/repos/wp-cli/php-cli-tools/zipball/e472e08489f7504d9e8c5c5a057e1419cd1b2b3e", + "reference": "e472e08489f7504d9e8c5c5a057e1419cd1b2b3e", "shasum": "" }, "require": { @@ -463,15 +455,15 @@ "MIT" ], "authors": [ - { - "name": "James Logsdon", - "email": "jlogsdon@php.net", - "role": "Developer" - }, { "name": "Daniel Bachhuber", "email": "daniel@handbuilt.co", "role": "Maintainer" + }, + { + "name": "James Logsdon", + "email": "jlogsdon@php.net", + "role": "Developer" } ], "description": "Console utilities for PHP", @@ -480,7 +472,7 @@ "cli", "console" ], - "time": "2018-09-04T13:28:00+00:00" + "time": "2021-03-03T12:43:49+00:00" }, { "name": "wp-cli/wp-cli", diff --git a/changelog.txt b/changelog.txt index a0491ef9f77..0c69319d959 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,113 @@ == Changelog == += 5.1.0 2021-03-09 = + +**WooCommerce** + +* Update - WooCommerce Admin package 2.0.2. #29229 +* Update - WooCommerce Blocks package 4.4.3. #29016 +* Fix - Error in notice message of reports when WC Admin is disabled via a filter. #29095 +* Fix - Error when calculating orders with tax option rounding at subtotal level in PHP 8. #29089 +* Fix - price filtering not working properly with variable products whose variations have different prices. #29043 +* Fix - Removed extra closing brace from the Zone regions help text. #29036 +* Fix - Tax name/label is not being updated in the order when it is changed. #28983 +* Fix - Additional protection after wc_get_product to account for invalid ID. #28962 +* Fix - orders list from returning false values if orders are missing. #28927 +* Fix - Terms and Policy checkbox wording settings not shown in customizer. #28735 +* Fix - Admin notices sometimes were persisting even after dismissing. #28500 +* Fix - Calculate discount based on order location in the admin view. #26983 +* Fix - SASS variables not being compile correctly due to recent SASS version. #29120 +* Fix - Placeholder image height in cart. #29139 +* Dev - Updated admin bar icons to use SVG and Dashicons instead of custom font. #29094 +* Dev - Admin menu modification has been moved from admin_head hooks to admin_menu hooks. #29088 +* Dev - status report generation time to the Status Report. #28980 +* Dev - Add the 'woocommerce_exporter_product_types' filter to allow third-parties to filter the product types which can be imported and exported. #28950 +* Dev - Filter added to allow 'woocommerce_hold_stock_minutes' to be customized. #28933 +* Dev - Add optional semicolon to JS code for better compatibility. #28880 +* Dev - Added Guatemala states. #28755 +* Dev - jQuery 3 deprecated functions update. #28753 +* Dev - Add Woo Version as global prop in track events. #28627 +* Dev - Added orders count by payment method to Tracker data and replaced direct DB calls with CRUD. #28584 +* Dev - WC_Tax::get_tax_rate_classes() is now public. #27671 +* Dev - "Store management insights" option now is turned off by default. #29105 +* Dev - Update the woo widget stock links to the new analytics page. #29093 +* Tweak - Updated WooCommerce logo color. #29054 +* Tweak - Correctly aligns content in the checkout with Twenty Twenty-One. #28951 +* Tweak - Use assigned variable for $post_thumbnail_id instead of calling function more than once. #28919 +* Tweak - Ensure that all tracker values collected for orders are of string type. #28893 +* Tweak - Adjust CSS font size and spacing for Twenty Twenty One. #28827 + +**WooCommerce Admin - 2.0.0 & 2.0.1 & 2.0.2** + +* Tweak - Bump minimum supported version of PHP to 7.0. #6046 +* Tweak - update the content and timing of the NeedSomeInspiration note. #6076 +* Tweak - Adjust the Marketing note not to show until store is at least 5 days. #6083 +* Tweak - Refactored extended task list. #6081 +* Fix - Add support for a floating-point number as a SummaryNumber's delta. #5926 +* Fix - allow for more terms to be shown for product attributes in the Analytics orders report. #5868 +* Fix - Fixed the Add First Product email note checks. #6260 +* Fix - Onboarding - Fixed "Business Details" error. #6271 +* Fix - Show management links when only main task list is hidden. #6291 +* Fix - Correct the Klarna slug. #6440 +* Add - new inbox message - Getting started in Ecommerce - watch this webinar. #6086 +* Add - Remote inbox notifications contains comparison and fix product rule. #6073 +* Add - Task list payments - include Mollie as an option. #6257 +* Update - store deprecation welcome modal support doc link #6094 +* Update - Homescreen layout, moving Inbox panel for better interaction. #6122 +* Enhancement - Allowing users to create products by selecting a template. #5892 +* Enhancement - Use the new Paypal payments plugin for onboarding. #6261 +* Dev - Add wait script for mysql to be ready for phpunit tests in docker. #6185 +* Dev - Remove old debug code for connecting to Calypso / Wordpress.com. #6097 +* Dev - Allow highlight tooltip to use body tag as parent. #6309 + +**WooCommerce Blocks - 4.1.0 & 4.2.0 & 4.3.0 & 4.4.0 & 4.4.1 & 4.4.2 & 4.4.3** + +* Update - Jetpack Autoloader to 2.9.1. +* Update - Update package for WooCommerce core inclusion. +* Enhancements - Design tweaks to the cart page which move the quantity picker below each cart item and improve usability on mobile. ([3734](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/3734)) +* Enhancements - Store API - Fix selected rate in cart shipping rates response. ([3680](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/3680)) +* Enhancements - Create get_item_responses_from_schema abstraction. ([3679](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/3679)) +* Enhancements - Show itemized fee rows in the cart/checkout blocks. ([3678](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/3678)) +* Enhancements - Extensibility: Show item data in Cart and Checkout blocks and update the variation data styles. ([3665](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/3665)) +* Enhancements - Introduce SlotFill for Sidebar. ([3361](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/3361)) +* Enhancements - Add the ability to directly upload an image in Featured Category and Featured Product blocks. ([3579](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/3579)) +* Enhancements - Fix coupon code button height not adapting to the font size. ([3575](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/3575)) +* Enhancements - Fixed Coupon Code panel not expanding/contracting in some themes. ([3569](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/3569)) +* Enhancements - Fix: Added fallback styling for screen reader text. ([3557](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/3557)) +* Fix - Ensure empty categories are correctly hidden in the product categories block. ([3765](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/3765)) +* Fix - Added missing wrapper div within FeaturedCategory and FeatureProduct blocks. ([3746](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/3746)) +* Fix - Set correct text color in BlockErrorBoundry notices. ([3738](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/3738)) +* Fix - Hidden cart item meta data will not be rendered in the Cart and Checkout blocks. ([3732](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/3732)) +* Fix - Improved accessibility of product image links in the products block by using correct aria tags and hiding empty image placeholders. ([3722](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/3722)) +* Fix - Add missing aria-label for stars image in the review-list-item component. ([3706](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/3706)) +* Fix - Prevent "X-WC-Store-API-Nonce is invalid" error when going back to a page with the products block using the browser back button. ([3770](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/3770)) +* Fix - Adds a default "features" array for payment methods which do not define supported features. Fixes conflicts with 3rd Party payment method integrations. +* Fix - Fix an error that was blocking checkout with some user saved payment methods. ([3627](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/3627)) +* Fix - Fix nonce issues when adding product to cart from All Products. ([3598](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/3598)) +* Fix - Fix bug inside Product Search in the editor. ([3578](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/3578)) +* Fix - Fix console warnings in WordPress 5.6. ([3577](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/3577)) +* Fix - Fixed text visibility in select inputs when using Twenty Twenty-One theme's dark mode. ([3554](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/3554)) +* Fix - Fix product list images skewed in Widgets editor. ([3553](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/3553)) +* Add address validation to values posted to the Checkout via StoreApi. ([3552](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/3552)) +* Fix - Fix Fees not visible in Cart & Checkout blocks when order doesn't need shipping. ([3521](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/3521)) +* Fix - Fix All Products block edit screen. ([3547](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/3547)) +* Fix - Checkout block: Prevent `Create an account` from creating up a user account if the order fails coupon validation. ([3423](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/3423)) +* Fix - Make sure cart is initialized before the CartItems route is used in the Store API. ([3488](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/3488)) +* Fix - Fix notice close button color in Twenty Twenty One dark mode. ([3472](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/3472)) +* Fix - Remove held stock for a draft order if an item is removed from the cart. ([3468](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/3468)) +* Fix - Ensure correct alignment of checkout notice's dismiss button. ([3455](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/3455)) +* Fix - Fixed a bug in Checkout block (Store API) causing checkout to fail when using an invalid coupon and creating an account. +* Fix - Checkout block: Correctly handle cases where the order fails with an error (e.g. invalid coupon) and a new user account is created. ([3429](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/3429)) +* Update - Hide the All Products Block from the new Gutenberg Widget Areas until full support is achieved. ([3737](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/3737)) +* Update - Legacy `star-rating` class name has been removed from Product rating block (inside All Products block). That element is still selectable with the `.wc-block-components-product-rating` class name. ([3717](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/3717)) +* Update - Update input colors and alignment. ([3597](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/3597)) +* Update - Removed compatibility with packages in WordPress 5.3. ([3541](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/3541)) +* Update - Bumped the minimum WP required version to 5.4. ([3537](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/3537)) +* Dev - Change register_endpoint_data to use an array of params instead of individual params. ([3478](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/3478)) +* Dev - Expose store/cart via ExtendRestApi to extensions. ([3445](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/3445)) +* Dev - Added formatting classes to the Store API for extensions to consume. +* Dev - Refactored and reordered Store API checkout processing to handle various edge cases and better support future extensibility. ([3454](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/3454)) + = 5.0.0 - 2021-02-09 = **WooCommerce** diff --git a/composer.json b/composer.json index 33cecfc6758..448c83aa0ea 100644 --- a/composer.json +++ b/composer.json @@ -21,7 +21,7 @@ "pelago/emogrifier": "3.1.0", "psr/container": "1.0.0", "woocommerce/action-scheduler": "3.1.6", - "woocommerce/woocommerce-admin": "2.0.2", + "woocommerce/woocommerce-admin": "2.0.3", "woocommerce/woocommerce-blocks": "4.4.3" }, "require-dev": { diff --git a/composer.lock b/composer.lock index 24b5bc27a02..81a61a3b15b 100644 --- a/composer.lock +++ b/composer.lock @@ -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": "f24a600ea103061d766dd7b06c13e8f2", + "content-hash": "d60ef90fa87857445878bb83ba4c592c", "packages": [ { "name": "automattic/jetpack-autoloader", @@ -44,9 +44,6 @@ "GPL-2.0-or-later" ], "description": "Creates a custom autoloader for a plugin or theme.", - "support": { - "source": "https://github.com/Automattic/jetpack-autoloader/tree/v2.9.1" - }, "time": "2021-02-05T19:07:06+00:00" }, { @@ -78,9 +75,6 @@ "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" }, { @@ -211,10 +205,6 @@ "zend", "zikula" ], - "support": { - "issues": "https://github.com/composer/installers/issues", - "source": "https://github.com/composer/installers/tree/v1.10.0" - }, "funding": [ { "url": "https://packagist.com", @@ -289,10 +279,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" }, { @@ -367,10 +353,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" }, { @@ -420,10 +402,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" }, { @@ -477,9 +455,6 @@ ], "description": "Symfony CssSelector Component", "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/css-selector/tree/master" - }, "time": "2017-05-01T15:01:29+00:00" }, { @@ -515,24 +490,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": "2.0.2", + "version": "2.0.3", "source": { "type": "git", "url": "https://github.com/woocommerce/woocommerce-admin.git", - "reference": "c4ffd90ebc72652f9d1bc8943a56d7723acc5bf4" + "reference": "15f43c9a1e81258c245ff50ba416a36a2cba7e0b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/woocommerce/woocommerce-admin/zipball/c4ffd90ebc72652f9d1bc8943a56d7723acc5bf4", - "reference": "c4ffd90ebc72652f9d1bc8943a56d7723acc5bf4", + "url": "https://api.github.com/repos/woocommerce/woocommerce-admin/zipball/15f43c9a1e81258c245ff50ba416a36a2cba7e0b", + "reference": "15f43c9a1e81258c245ff50ba416a36a2cba7e0b", "shasum": "" }, "require": { @@ -564,11 +535,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/v2.0.2" - }, - "time": "2021-02-25T07:29:24+00:00" + "time": "2021-03-10T02:58:43+00:00" }, { "name": "woocommerce/woocommerce-blocks", @@ -615,10 +582,6 @@ "gutenberg", "woocommerce" ], - "support": { - "issues": "https://github.com/woocommerce/woocommerce-gutenberg-products-block/issues", - "source": "https://github.com/woocommerce/woocommerce-gutenberg-products-block/tree/v4.4.3" - }, "time": "2021-02-11T18:07:48+00:00" } ], @@ -667,10 +630,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" } ], @@ -686,5 +645,5 @@ "platform-overrides": { "php": "7.0" }, - "plugin-api-version": "2.0.0" + "plugin-api-version": "1.1.0" } diff --git a/includes/abstracts/abstract-wc-payment-token.php b/includes/abstracts/abstract-wc-payment-token.php index ad958991981..6fc6f634d29 100644 --- a/includes/abstracts/abstract-wc-payment-token.php +++ b/includes/abstracts/abstract-wc-payment-token.php @@ -2,7 +2,7 @@ /** * Abstract payment tokens * - * Generic payment tokens functionality which can be extended by idividual types of payment tokens. + * Generic payment tokens functionality which can be extended by individual types of payment tokens. * * @class WC_Payment_Token * @package WooCommerce\Abstracts diff --git a/includes/admin/settings/class-wc-settings-products.php b/includes/admin/settings/class-wc-settings-products.php index 8346e63c3ec..5bf11bde2dd 100644 --- a/includes/admin/settings/class-wc-settings-products.php +++ b/includes/admin/settings/class-wc-settings-products.php @@ -64,6 +64,12 @@ class WC_Settings_Products extends WC_Settings_Page { $settings = $this->get_settings( $current_section ); WC_Admin_Settings::save_fields( $settings ); + /* + * Product->Inventory has a setting `Out of stock visibility`. + * Because of this, we need to recount the terms to keep them in-sync. + */ + wc_recount_all_terms(); + if ( $current_section ) { do_action( 'woocommerce_update_options_' . $this->id . '_' . $current_section ); } diff --git a/includes/class-wc-order.php b/includes/class-wc-order.php index f33c5db1b36..516f905b10b 100644 --- a/includes/class-wc-order.php +++ b/includes/class-wc-order.php @@ -907,7 +907,7 @@ class WC_Order extends WC_Abstract_Order { $address = WC()->countries->get_formatted_address( $raw_address ); /** - * Filter orders formatterd billing address. + * Filter orders formatted billing address. * * @since 3.8.0 * @param string $address Formatted billing address string. @@ -933,7 +933,7 @@ class WC_Order extends WC_Abstract_Order { } /** - * Filter orders formatterd shipping address. + * Filter orders formatted shipping address. * * @since 3.8.0 * @param string $address Formatted billing address string. diff --git a/includes/class-woocommerce.php b/includes/class-woocommerce.php index 85a7c5bad71..715c05883cb 100644 --- a/includes/class-woocommerce.php +++ b/includes/class-woocommerce.php @@ -23,7 +23,7 @@ final class WooCommerce { * * @var string */ - public $version = '5.1.0'; + public $version = '5.2.0'; /** * WooCommerce Schema version. diff --git a/includes/data-stores/class-wc-product-data-store-cpt.php b/includes/data-stores/class-wc-product-data-store-cpt.php index ed626f9a9b3..8a71ed6de96 100644 --- a/includes/data-stores/class-wc-product-data-store-cpt.php +++ b/includes/data-stores/class-wc-product-data-store-cpt.php @@ -710,6 +710,8 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da if ( $force || array_key_exists( 'shipping_class_id', $changes ) ) { wp_set_post_terms( $product->get_id(), array( $product->get_shipping_class_id( 'edit' ) ), 'product_shipping_class', false ); } + + _wc_recount_terms_by_product( $product->get_id() ); } /** diff --git a/includes/emails/class-wc-email-new-order.php b/includes/emails/class-wc-email-new-order.php index 9cc757495e3..f4b2f383d5f 100644 --- a/includes/emails/class-wc-email-new-order.php +++ b/includes/emails/class-wc-email-new-order.php @@ -100,7 +100,7 @@ if ( ! class_exists( 'WC_Email_New_Order' ) ) : * Controls if new order emails can be resend multiple times. * * @since 5.0.0 - * @param bool $allows Defaults to true. + * @param bool $allows Defaults to false. */ if ( 'true' === $email_already_sent && ! apply_filters( 'woocommerce_new_order_email_allows_resend', false ) ) { return; diff --git a/includes/rest-api/Controllers/Version2/class-wc-rest-system-status-tools-v2-controller.php b/includes/rest-api/Controllers/Version2/class-wc-rest-system-status-tools-v2-controller.php index 06838eb5469..19f212da2b6 100644 --- a/includes/rest-api/Controllers/Version2/class-wc-rest-system-status-tools-v2-controller.php +++ b/includes/rest-api/Controllers/Version2/class-wc-rest-system-status-tools-v2-controller.php @@ -504,22 +504,7 @@ class WC_REST_System_Status_Tools_V2_Controller extends WC_REST_Controller { break; case 'recount_terms': - $product_cats = get_terms( - 'product_cat', - array( - 'hide_empty' => false, - 'fields' => 'id=>parent', - ) - ); - _wc_term_recount( $product_cats, get_taxonomy( 'product_cat' ), true, false ); - $product_tags = get_terms( - 'product_tag', - array( - 'hide_empty' => false, - 'fields' => 'id=>parent', - ) - ); - _wc_term_recount( $product_tags, get_taxonomy( 'product_tag' ), true, false ); + wc_recount_all_terms(); $message = __( 'Terms successfully recounted', 'woocommerce' ); break; diff --git a/includes/rest-api/Controllers/Version3/class-wc-rest-shipping-zone-methods-controller.php b/includes/rest-api/Controllers/Version3/class-wc-rest-shipping-zone-methods-controller.php index 32b59d0771f..f03234e0633 100644 --- a/includes/rest-api/Controllers/Version3/class-wc-rest-shipping-zone-methods-controller.php +++ b/includes/rest-api/Controllers/Version3/class-wc-rest-shipping-zone-methods-controller.php @@ -24,4 +24,20 @@ class WC_REST_Shipping_Zone_Methods_Controller extends WC_REST_Shipping_Zone_Met * @var string */ protected $namespace = 'wc/v3'; + + /** + * Get the settings schema, conforming to JSON Schema. + * + * @return array + */ + public function get_item_schema() { + // Get parent schema to append additional supported settings types for shipping zone method. + $schema = parent::get_item_schema(); + + // Append additional settings supported types (class, order). + $schema['properties']['settings']['properties']['type']['enum'][] = 'class'; + $schema['properties']['settings']['properties']['type']['enum'][] = 'order'; + + return $this->add_additional_fields_schema( $schema ); + } } diff --git a/includes/wc-term-functions.php b/includes/wc-term-functions.php index f40a89fb6b8..c6fd42ab966 100644 --- a/includes/wc-term-functions.php +++ b/includes/wc-term-functions.php @@ -392,6 +392,19 @@ function wc_set_term_order( $term_id, $index, $taxonomy, $recursive = false ) { function _wc_term_recount( $terms, $taxonomy, $callback = true, $terms_are_term_taxonomy_ids = true ) { global $wpdb; + /** + * Filter to allow/prevent recounting of terms as it could be expensive. + * A likely scenario for this is when bulk importing products. We could + * then prevent it from recounting per product but instead recount it once + * when import is done. Of course this means the import logic has to support this. + * + * @since 5.2 + * @param bool + */ + if ( ! apply_filters( 'woocommerce_product_recount_terms', '__return_true' ) ) { + return; + } + // Standard callback. if ( $callback ) { _update_post_term_count( $terms, $taxonomy ); @@ -492,29 +505,7 @@ function wc_recount_after_stock_change( $product_id ) { return; } - $product_terms = get_the_terms( $product_id, 'product_cat' ); - - if ( $product_terms ) { - $product_cats = array(); - - foreach ( $product_terms as $term ) { - $product_cats[ $term->term_id ] = $term->parent; - } - - _wc_term_recount( $product_cats, get_taxonomy( 'product_cat' ), false, false ); - } - - $product_terms = get_the_terms( $product_id, 'product_tag' ); - - if ( $product_terms ) { - $product_tags = array(); - - foreach ( $product_terms as $term ) { - $product_tags[ $term->term_id ] = $term->parent; - } - - _wc_term_recount( $product_tags, get_taxonomy( 'product_tag' ), false, false ); - } + _wc_recount_terms_by_product( $product_id ); } add_action( 'woocommerce_product_set_stock_status', 'wc_recount_after_stock_change' ); @@ -636,3 +627,65 @@ function wc_get_product_visibility_term_ids() { ) ); } + +/** + * Recounts all terms. + * + * @since 5.2 + * @return void + */ +function wc_recount_all_terms() { + $product_cats = get_terms( + 'product_cat', + array( + 'hide_empty' => false, + 'fields' => 'id=>parent', + ) + ); + _wc_term_recount( $product_cats, get_taxonomy( 'product_cat' ), true, false ); + $product_tags = get_terms( + 'product_tag', + array( + 'hide_empty' => false, + 'fields' => 'id=>parent', + ) + ); + _wc_term_recount( $product_tags, get_taxonomy( 'product_tag' ), true, false ); +} + +/** + * Recounts terms by product. + * + * @since 5.2 + * @param int $product_id The ID of the product. + * @return void + */ +function _wc_recount_terms_by_product( $product_id = '' ) { + if ( empty( $product_id ) ) { + return; + } + + $product_terms = get_the_terms( $product_id, 'product_cat' ); + + if ( $product_terms ) { + $product_cats = array(); + + foreach ( $product_terms as $term ) { + $product_cats[ $term->term_id ] = $term->parent; + } + + _wc_term_recount( $product_cats, get_taxonomy( 'product_cat' ), false, false ); + } + + $product_terms = get_the_terms( $product_id, 'product_tag' ); + + if ( $product_terms ) { + $product_tags = array(); + + foreach ( $product_terms as $term ) { + $product_tags[ $term->term_id ] = $term->parent; + } + + _wc_term_recount( $product_tags, get_taxonomy( 'product_tag' ), false, false ); + } +} diff --git a/package-lock.json b/package-lock.json index 687029b381d..a23aaf9bbeb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3890,6 +3890,70 @@ "@sinonjs/commons": "^1.7.0" } }, + "@slack/logger": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@slack/logger/-/logger-2.0.0.tgz", + "integrity": "sha512-OkIJpiU2fz6HOJujhlhfIGrc8hB4ibqtf7nnbJQDerG0BqwZCfmgtK5sWzZ0TkXVRBKD5MpLrTmCYyMxoMCgPw==", + "dev": true, + "requires": { + "@types/node": ">=8.9.0" + } + }, + "@slack/types": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@slack/types/-/types-1.10.0.tgz", + "integrity": "sha512-tA7GG7Tj479vojfV3AoxbckalA48aK6giGjNtgH6ihpLwTyHE3fIgRrvt8TWfLwW8X8dyu7vgmAsGLRG7hWWOg==", + "dev": true + }, + "@slack/web-api": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@slack/web-api/-/web-api-6.1.0.tgz", + "integrity": "sha512-9MVHw+rDBaFvkvzm8lDNH/nlkvJCDKRIjFGMdpbyZlVLsm4rcht4qyiL71bqdyLATHXJnWknb/sl0FQGLLobIA==", + "dev": true, + "requires": { + "@slack/logger": ">=1.0.0 <3.0.0", + "@slack/types": "^1.7.0", + "@types/is-stream": "^1.1.0", + "@types/node": ">=12.0.0", + "axios": "^0.21.1", + "eventemitter3": "^3.1.0", + "form-data": "^2.5.0", + "is-stream": "^1.1.0", + "p-queue": "^6.6.1", + "p-retry": "^4.0.0" + }, + "dependencies": { + "form-data": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", + "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "p-queue": { + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-6.6.2.tgz", + "integrity": "sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==", + "dev": true, + "requires": { + "eventemitter3": "^4.0.4", + "p-timeout": "^3.2.0" + }, + "dependencies": { + "eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "dev": true + } + } + } + } + }, "@tannin/compile": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@tannin/compile/-/compile-1.1.0.tgz", @@ -4002,6 +4066,15 @@ "@types/node": "*" } }, + "@types/is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@types/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha512-jkZatu4QVbR60mpIzjINmtS1ZF4a/FqdTUTBeQDVOQ2PYyidtwFKr0B5G6ERukKwliq+7mIXvxyppwzG5EgRYg==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/istanbul-lib-coverage": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", @@ -4069,6 +4142,12 @@ "integrity": "sha512-5qOlnZscTn4xxM5MeGXAMOsIOIKIbh9e85zJWfBRVPlRMEVawzoPhINYbRGkBZCI8LxvBe7tJCdWiarA99OZfQ==", "dev": true }, + "@types/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", + "dev": true + }, "@types/stack-utils": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz", @@ -9137,10 +9216,12 @@ "requires": { "@automattic/puppeteer-utils": "github:Automattic/puppeteer-utils#0f3ec50", "@jest/test-sequencer": "^25.5.4", + "@slack/web-api": "^6.1.0", "@wordpress/e2e-test-utils": "^4.15.0", "@wordpress/jest-preset-default": "^6.4.0", "app-root-path": "^3.0.0", "jest": "^25.1.0", + "jest-each": "25.5.0", "jest-puppeteer": "^4.4.0" }, "dependencies": { @@ -9161,6 +9242,24 @@ "puppeteer": "^2.0.0" }, "dependencies": { + "@slack/web-api": { + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/@slack/web-api/-/web-api-5.15.0.tgz", + "integrity": "sha512-tjQ8Zqv/Fmj9SOL9yIEd7IpTiKfKHi9DKAkfRVeotoX0clMr3SqQtBqO+KZMX27gm7dmgJsQaDKlILyzdCO+IA==", + "dev": true, + "requires": { + "@slack/logger": ">=1.0.0 <3.0.0", + "@slack/types": "^1.7.0", + "@types/is-stream": "^1.1.0", + "@types/node": ">=8.9.0", + "axios": "^0.21.1", + "eventemitter3": "^3.1.0", + "form-data": "^2.5.0", + "is-stream": "^1.1.0", + "p-queue": "^6.6.1", + "p-retry": "^4.0.0" + } + }, "@wordpress/e2e-test-utils": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@wordpress/e2e-test-utils/-/e2e-test-utils-3.0.0.tgz", @@ -9208,7 +9307,7 @@ } }, "prettier": { - "version": "npm:prettier@1.19.1", + "version": "npm:wp-prettier@1.19.1", "resolved": "https://registry.npmjs.org/wp-prettier/-/wp-prettier-1.19.1.tgz", "integrity": "sha512-mqAC2r1NDmRjG+z3KCJ/i61tycKlmADIjxnDhQab+KBxSAGbF/W7/zwB2guy/ypIeKrrftNsIYkNZZQKf3vJcg==", "dev": true @@ -11900,40 +11999,6 @@ "type-detect": "4.0.8" } }, - "@slack/logger": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@slack/logger/-/logger-2.0.0.tgz", - "integrity": "sha512-OkIJpiU2fz6HOJujhlhfIGrc8hB4ibqtf7nnbJQDerG0BqwZCfmgtK5sWzZ0TkXVRBKD5MpLrTmCYyMxoMCgPw==", - "dev": true, - "requires": { - "@types/node": ">=8.9.0" - } - }, - "@slack/types": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@slack/types/-/types-1.9.0.tgz", - "integrity": "sha512-RmwgMWqOtzd2JPXdiaD/tyrDD0vtjjRDFdxN1I3tAxwBbg4aryzDUVqFc8na16A+3Xik/UN8X1hvVTw8J4EB9w==", - "dev": true - }, - "@slack/web-api": { - "version": "5.12.0", - "resolved": "https://registry.npmjs.org/@slack/web-api/-/web-api-5.12.0.tgz", - "integrity": "sha512-ygSnNHVid7PltGo7W36f2SNVHyliemkzxn9uSwgnWNF7CHmWBKWAylU/eoDml9l5K7akMOxbousiurOw4XqOFg==", - "dev": true, - "requires": { - "@slack/logger": ">=1.0.0 <3.0.0", - "@slack/types": "^1.7.0", - "@types/is-stream": "^1.1.0", - "@types/node": ">=8.9.0", - "@types/p-queue": "^2.3.2", - "axios": "^0.19.0", - "eventemitter3": "^3.1.0", - "form-data": "^2.5.0", - "is-stream": "^1.1.0", - "p-queue": "^2.4.2", - "p-retry": "^4.0.0" - } - }, "@tannin/compile": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@tannin/compile/-/compile-1.1.0.tgz", @@ -12021,15 +12086,6 @@ "@types/node": "*" } }, - "@types/is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@types/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha512-jkZatu4QVbR60mpIzjINmtS1ZF4a/FqdTUTBeQDVOQ2PYyidtwFKr0B5G6ERukKwliq+7mIXvxyppwzG5EgRYg==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, "@types/istanbul-lib-coverage": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", @@ -12078,24 +12134,12 @@ "integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==", "dev": true }, - "@types/p-queue": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/@types/p-queue/-/p-queue-2.3.2.tgz", - "integrity": "sha512-eKAv5Ql6k78dh3ULCsSBxX6bFNuGjTmof5Q/T6PiECDq0Yf8IIn46jCyp3RJvCi8owaEmm3DZH1PEImjBMd/vQ==", - "dev": true - }, "@types/prettier": { "version": "1.19.1", "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-1.19.1.tgz", "integrity": "sha512-5qOlnZscTn4xxM5MeGXAMOsIOIKIbh9e85zJWfBRVPlRMEVawzoPhINYbRGkBZCI8LxvBe7tJCdWiarA99OZfQ==", "dev": true }, - "@types/retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", - "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", - "dev": true - }, "@types/stack-utils": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz", @@ -12626,15 +12670,6 @@ "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-3.5.5.tgz", "integrity": "sha512-5P0QZ6J5xGikH780pghEdbEKijCTrruK9KxtPZCFWUpef0f6GipO+xEZ5GKCb020mmqgbiNO6TcA55CriL784Q==" }, - "axios": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.19.2.tgz", - "integrity": "sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==", - "dev": true, - "requires": { - "follow-redirects": "1.5.10" - } - }, "axobject-query": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz", @@ -14024,12 +14059,6 @@ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" }, - "eventemitter3": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz", - "integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==", - "dev": true - }, "exec-sh": { "version": "0.3.4", "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.4.tgz", @@ -14404,26 +14433,6 @@ "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==", "dev": true }, - "follow-redirects": { - "version": "1.5.10", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz", - "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==", - "dev": true, - "requires": { - "debug": "=3.1.0" - }, - "dependencies": { - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - } - } - }, "for-in": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", @@ -16894,16 +16903,134 @@ } }, "jest-each": { - "version": "24.9.0", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-24.9.0.tgz", - "integrity": "sha512-ONi0R4BvW45cw8s2Lrx8YgbeXL1oCQ/wIDwmsM3CqM/nlblNCPmnC3IPQlMbRFZu3wKdQ2U8BqM6lh3LJ5Bsog==", + "version": "25.5.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-25.5.0.tgz", + "integrity": "sha512-QBogUxna3D8vtiItvn54xXde7+vuzqRrEeaw8r1s+1TG9eZLVJE5ZkKoSUlqFwRjnlaA4hyKGiu9OlkFIuKnjA==", "dev": true, "requires": { - "@jest/types": "^24.9.0", - "chalk": "^2.0.1", - "jest-get-type": "^24.9.0", - "jest-util": "^24.9.0", - "pretty-format": "^24.9.0" + "@jest/types": "^25.5.0", + "chalk": "^3.0.0", + "jest-get-type": "^25.2.6", + "jest-util": "^25.5.0", + "pretty-format": "^25.5.0" + }, + "dependencies": { + "@jest/types": { + "version": "25.5.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.5.0.tgz", + "integrity": "sha512-OXD0RgQ86Tu3MazKo8bnrkDRaDXXMGUqd+kTtLtK1Zb7CRzQcaSRPPPV37SvYTdevXEBVxe0HXylEjs8ibkmCw==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^15.0.0", + "chalk": "^3.0.0" + } + }, + "@types/yargs": { + "version": "15.0.13", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.13.tgz", + "integrity": "sha512-kQ5JNTrbDv3Rp5X2n/iUu37IJBDU2gsZ5R/g1/KHOOEc5IKfUFjXT6DENPGduh08I/pamwtEq4oul7gUqKTQDQ==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "jest-get-type": { + "version": "25.2.6", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-25.2.6.tgz", + "integrity": "sha512-DxjtyzOHjObRM+sM1knti6or+eOgcGU4xVSb2HNP1TqO4ahsT+rqZg+nyqHWJSvWgKC5cG3QjGFBqxLghiF/Ig==", + "dev": true + }, + "jest-util": { + "version": "25.5.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-25.5.0.tgz", + "integrity": "sha512-KVlX+WWg1zUTB9ktvhsg2PXZVdkI1NBevOJSkTKYAyXyH4QSvh+Lay/e/v+bmaFfrkfx43xD8QTfgobzlEXdIA==", + "dev": true, + "requires": { + "@jest/types": "^25.5.0", + "chalk": "^3.0.0", + "graceful-fs": "^4.2.4", + "is-ci": "^2.0.0", + "make-dir": "^3.0.0" + } + }, + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "requires": { + "semver": "^6.0.0" + } + }, + "pretty-format": { + "version": "25.5.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-25.5.0.tgz", + "integrity": "sha512-kbo/kq2LQ/A/is0PQwsEHM7Ca6//bGPPvU6UnsdDRSKTWxT/ru/xb88v4BJf6a69H+uTytOEsTusT9ksd/1iWQ==", + "dev": true, + "requires": { + "@jest/types": "^25.5.0", + "ansi-regex": "^5.0.0", + "ansi-styles": "^4.0.0", + "react-is": "^16.12.0" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } } }, "jest-environment-jsdom": { @@ -17045,6 +17172,21 @@ "jest-util": "^24.9.0", "pretty-format": "^24.9.0", "throat": "^4.0.0" + }, + "dependencies": { + "jest-each": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-24.9.0.tgz", + "integrity": "sha512-ONi0R4BvW45cw8s2Lrx8YgbeXL1oCQ/wIDwmsM3CqM/nlblNCPmnC3IPQlMbRFZu3wKdQ2U8BqM6lh3LJ5Bsog==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "chalk": "^2.0.1", + "jest-get-type": "^24.9.0", + "jest-util": "^24.9.0", + "pretty-format": "^24.9.0" + } + } } }, "jest-leak-detector": { @@ -18271,10 +18413,22 @@ } }, "p-queue": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-2.4.2.tgz", - "integrity": "sha512-n8/y+yDJwBjoLQe1GSJbbaYQLTI7QHNZI2+rpmCDbe++WLf9HC3gf6iqj5yfPAV71W4UF3ql5W1+UBPXoXTxng==", - "dev": true + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-6.6.2.tgz", + "integrity": "sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==", + "dev": true, + "requires": { + "eventemitter3": "^4.0.4", + "p-timeout": "^3.2.0" + }, + "dependencies": { + "eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "dev": true + } + } }, "p-reduce": { "version": "1.0.0", @@ -18282,16 +18436,6 @@ "integrity": "sha1-GMKw3ZNqRpClKfgjH1ig/bakffo=", "dev": true }, - "p-retry": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.2.0.tgz", - "integrity": "sha512-jPH38/MRh263KKcq0wBNOGFJbm+U6784RilTmHjB/HM9kH9V8WlCpVUcdOmip9cjXOh6MxZ5yk1z2SjDUJfWmA==", - "dev": true, - "requires": { - "@types/retry": "^0.12.0", - "retry": "^0.12.0" - } - }, "p-try": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", @@ -19053,12 +19197,6 @@ "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", "dev": true }, - "retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", - "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=", - "dev": true - }, "rimraf": { "version": "2.6.3", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", @@ -20923,16 +21061,27 @@ } }, "@wordpress/e2e-test-utils": { - "version": "4.15.0", - "resolved": "https://registry.npmjs.org/@wordpress/e2e-test-utils/-/e2e-test-utils-4.15.0.tgz", - "integrity": "sha512-mCOlNDX/yERd7hIAFB+y9x56iCQ2XyDZkWNlQNMYRH0+EdrQ5H5zE7MSxzycideIC7grxKw/j4RcuyxUdSWGDw==", + "version": "4.16.1", + "resolved": "https://registry.npmjs.org/@wordpress/e2e-test-utils/-/e2e-test-utils-4.16.1.tgz", + "integrity": "sha512-Dpsq5m0VSvjIhro2MjACSzkOkOf1jGEryzgEMW1ikbT6YI+motspHfGtisKXgYhZJOnjV4PwuEg+9lPVnd971g==", "dev": true, "requires": { - "@babel/runtime": "^7.11.2", - "@wordpress/keycodes": "^2.16.0", - "@wordpress/url": "^2.19.0", + "@babel/runtime": "^7.12.5", + "@wordpress/keycodes": "^2.18.0", + "@wordpress/url": "^2.21.0", "lodash": "^4.17.19", "node-fetch": "^2.6.0" + }, + "dependencies": { + "@babel/runtime": { + "version": "7.13.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.9.tgz", + "integrity": "sha512-aY2kU+xgJ3dJ1eU6FMB9EH8dIe8dmusF1xEku52joLvw6eAFN0AI+WxCLDnpev2LEejWBAy2sBvBOBAjI3zmvA==", + "dev": true, + "requires": { + "regenerator-runtime": "^0.13.4" + } + } } }, "@wordpress/eslint-plugin": { @@ -21001,17 +21150,29 @@ } }, "@wordpress/i18n": { - "version": "3.16.0", - "resolved": "https://registry.npmjs.org/@wordpress/i18n/-/i18n-3.16.0.tgz", - "integrity": "sha512-ZyRWplETgD90caVaBuGBFcnYVpcogji1g9Ctbb5AO2bGFeHpmPpjvWm0NE64iQTtLFEJoaCiq6oqUvAOPIQJpw==", + "version": "3.18.0", + "resolved": "https://registry.npmjs.org/@wordpress/i18n/-/i18n-3.18.0.tgz", + "integrity": "sha512-e1uFWhWYnT0B6s3hyy+xS0S3bwabrvkZA84xxitiIcQvGnZDUPntqv6M9+VrgJVlmd2MR2TbCGJ5xKFAVFr/gA==", "dev": true, "requires": { - "@babel/runtime": "^7.11.2", + "@babel/runtime": "^7.12.5", + "@wordpress/hooks": "^2.11.1", "gettext-parser": "^1.3.1", "lodash": "^4.17.19", "memize": "^1.1.0", "sprintf-js": "^1.1.1", "tannin": "^1.2.0" + }, + "dependencies": { + "@babel/runtime": { + "version": "7.13.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.9.tgz", + "integrity": "sha512-aY2kU+xgJ3dJ1eU6FMB9EH8dIe8dmusF1xEku52joLvw6eAFN0AI+WxCLDnpev2LEejWBAy2sBvBOBAjI3zmvA==", + "dev": true, + "requires": { + "regenerator-runtime": "^0.13.4" + } + } } }, "@wordpress/jest-console": { @@ -21040,14 +21201,25 @@ } }, "@wordpress/keycodes": { - "version": "2.16.0", - "resolved": "https://registry.npmjs.org/@wordpress/keycodes/-/keycodes-2.16.0.tgz", - "integrity": "sha512-8CfxB+9f08FXMUsaO625abmbx2ZinFUz6upzXbe0Da8W3oy7+/TZz6EWsMVBEWz+alSR3Z2FUZ7xUuopHZFcow==", + "version": "2.18.3", + "resolved": "https://registry.npmjs.org/@wordpress/keycodes/-/keycodes-2.18.3.tgz", + "integrity": "sha512-Lenyw+K2KgiqddBv5fDCh2JRfXFrONWNvPfv1DKXzHXTvBSI0JkU1RVP5WZTcVuEtctCZWL5JbhrkG2I26w68g==", "dev": true, "requires": { - "@babel/runtime": "^7.11.2", - "@wordpress/i18n": "^3.16.0", + "@babel/runtime": "^7.12.5", + "@wordpress/i18n": "^3.18.0", "lodash": "^4.17.19" + }, + "dependencies": { + "@babel/runtime": { + "version": "7.13.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.9.tgz", + "integrity": "sha512-aY2kU+xgJ3dJ1eU6FMB9EH8dIe8dmusF1xEku52joLvw6eAFN0AI+WxCLDnpev2LEejWBAy2sBvBOBAjI3zmvA==", + "dev": true, + "requires": { + "regenerator-runtime": "^0.13.4" + } + } } }, "@wordpress/prettier-config": { @@ -21057,15 +21229,25 @@ "dev": true }, "@wordpress/url": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/@wordpress/url/-/url-2.19.0.tgz", - "integrity": "sha512-RizWbBxYmWBlNd+q89r3N6Y2XO8eCG3VncnXDgbGnhV4e+2z9fjzp1/9C/SORftEn+ix/qBKbqygmkmBqb+wuw==", + "version": "2.21.2", + "resolved": "https://registry.npmjs.org/@wordpress/url/-/url-2.21.2.tgz", + "integrity": "sha512-bLHg4pTo/9mQUkK1s1MU/Sjgnzfy2AkPvPn4ObGA8/4CFkMsDhQGAVhhw5YuezcxvaJkBiKJ+BxgFJ1QKksF6w==", "dev": true, "requires": { - "@babel/runtime": "^7.11.2", + "@babel/runtime": "^7.12.5", "lodash": "^4.17.19", - "qs": "^6.5.2", "react-native-url-polyfill": "^1.1.2" + }, + "dependencies": { + "@babel/runtime": { + "version": "7.13.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.13.9.tgz", + "integrity": "sha512-aY2kU+xgJ3dJ1eU6FMB9EH8dIe8dmusF1xEku52joLvw6eAFN0AI+WxCLDnpev2LEejWBAy2sBvBOBAjI3zmvA==", + "dev": true, + "requires": { + "regenerator-runtime": "^0.13.4" + } + } } }, "@xtuc/ieee754": { @@ -21722,6 +21904,15 @@ "integrity": "sha512-arU1h31OGFu+LPrOLGZ7nB45v940NMDMEJeNmbutu57P+UFDVnkZg3e+J1I2HJRZ9hT7gO8J91dn/PMrAiKakA==", "dev": true }, + "axios": { + "version": "0.21.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz", + "integrity": "sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==", + "dev": true, + "requires": { + "follow-redirects": "^1.10.0" + } + }, "axobject-query": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz", @@ -25926,6 +26117,12 @@ "readable-stream": "^2.3.6" } }, + "follow-redirects": { + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.3.tgz", + "integrity": "sha512-DUgl6+HDzB0iEptNQEXLx/KhTmDb8tZUHSeLqpnjpknR70H0nC2t9N73BK6fN4hOvJ84pKlIQVQ4k5FFlBedKA==", + "dev": true + }, "for-in": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", @@ -33596,6 +33793,33 @@ "integrity": "sha1-GMKw3ZNqRpClKfgjH1ig/bakffo=", "dev": true }, + "p-retry": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.4.0.tgz", + "integrity": "sha512-gVB/tBsG+3AHI1SyDHRrX6n9ZL0Bcbifps9W9/Bgu3Oyu4/OrAh8SvDzDsvpP0oxfCt3oWNT+0fQ9LyUGwBTLg==", + "dev": true, + "requires": { + "@types/retry": "^0.12.0", + "retry": "^0.12.0" + }, + "dependencies": { + "retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=", + "dev": true + } + } + }, + "p-timeout": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz", + "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==", + "dev": true, + "requires": { + "p-finally": "^1.0.0" + } + }, "p-try": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", @@ -34417,9 +34641,9 @@ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, "react-native-url-polyfill": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/react-native-url-polyfill/-/react-native-url-polyfill-1.2.0.tgz", - "integrity": "sha512-hpLZ8RyS3oGVyTOe/HjoqVoCOSkeJvrCoEB3bJsY7t9uh7kpQDV6kgvdlECEafYpxe3RzMrKLVcmWRbPU7CuAw==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/react-native-url-polyfill/-/react-native-url-polyfill-1.3.0.tgz", + "integrity": "sha512-w9JfSkvpqqlix9UjDvJjm1EjSt652zVQ6iwCIj1cVVkwXf4jQhQgTNXY6EVTwuAmUjg6BC6k9RHCBynoLFo3IQ==", "dev": true, "requires": { "whatwg-url-without-unicode": "8.0.0-3" diff --git a/package.json b/package.json index 210c07c286d..da06d881283 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,7 @@ "@woocommerce/e2e-utils": "file:tests/e2e/utils", "@wordpress/babel-plugin-import-jsx-pragma": "1.1.3", "@wordpress/babel-preset-default": "3.0.2", - "@wordpress/e2e-test-utils": "^4.6.0", + "@wordpress/e2e-test-utils": "^4.16.1", "@wordpress/eslint-plugin": "7.3.0", "autoprefixer": "9.8.6", "babel-eslint": "10.1.0", diff --git a/readme.txt b/readme.txt index eab39a7fb1f..e29b492dfc0 100644 --- a/readme.txt +++ b/readme.txt @@ -2,9 +2,9 @@ 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.4 -Tested up to: 5.6 +Tested up to: 5.7 Requires PHP: 7.0 -Stable tag: 5.0.0 +Stable tag: 5.1.0 License: GPLv3 License URI: https://www.gnu.org/licenses/gpl-3.0.html diff --git a/tests/e2e/api/src/framework/model-repository.ts b/tests/e2e/api/src/framework/model-repository.ts index ca598404b93..9c30993a83a 100644 --- a/tests/e2e/api/src/framework/model-repository.ts +++ b/tests/e2e/api/src/framework/model-repository.ts @@ -401,7 +401,7 @@ export class ModelRepository< T extends ModelRepositoryParams > implements } return ( this.listHook as ListChildFn< T > )( - paramsOrParent as ParentID< T >, + ( paramsOrParent as unknown ) as ParentID< T >, params, ); } @@ -428,7 +428,7 @@ export class ModelRepository< T extends ModelRepositoryParams > implements } return ( this.createHook as CreateChildFn< T > )( - propertiesOrParent as ParentID, + ( propertiesOrParent as unknown ) as ParentID, properties as Partial< ModelClass >, ); } @@ -455,7 +455,7 @@ export class ModelRepository< T extends ModelRepositoryParams > implements } return ( this.readHook as ReadChildFn< T > )( - idOrParent as ParentID< T >, + ( idOrParent as unknown ) as ParentID< T >, childID, ); } @@ -480,14 +480,14 @@ export class ModelRepository< T extends ModelRepositoryParams > implements if ( properties === undefined ) { return ( this.updateHook as UpdateFn< T > )( idOrParent as ModelID, - propertiesOrChildID as UpdateParams< T >, + ( propertiesOrChildID as unknown ) as UpdateParams< T >, ); } return ( this.updateHook as UpdateChildFn< T > )( - idOrParent as ParentID< T >, + ( idOrParent as unknown ) as ParentID< T >, propertiesOrChildID as ModelID, - properties, + ( properties as unknown ) as UpdateParams< T >, ); } @@ -513,7 +513,7 @@ export class ModelRepository< T extends ModelRepositoryParams > implements } return ( this.deleteHook as DeleteChildFn< T > )( - idOrParent as ParentID< T >, + ( idOrParent as unknown ) as ParentID< T >, childID, ); } diff --git a/tests/e2e/core-tests/CHANGELOG.md b/tests/e2e/core-tests/CHANGELOG.md index 14211b6fb77..05432dda4b5 100644 --- a/tests/e2e/core-tests/CHANGELOG.md +++ b/tests/e2e/core-tests/CHANGELOG.md @@ -25,6 +25,7 @@ - Shopper Cart Apply Coupon - Merchant Settings Shipping Zones - Shopper Variable product info updates on different variations +- Merchant order emails flow ## Fixed diff --git a/tests/e2e/core-tests/README.md b/tests/e2e/core-tests/README.md index 6500831b8c7..82b20e01e91 100644 --- a/tests/e2e/core-tests/README.md +++ b/tests/e2e/core-tests/README.md @@ -59,6 +59,7 @@ The functions to access the core tests are: - `runProductSearchTest` - Merchant can search for a product and view it - `runMerchantOrdersCustomerPaymentPage` - Merchant can visit the customer payment page - `runAddNewShippingZoneTest` - Merchant can create shipping zones and let shopper test them + - `runMerchantOrderEmailsTest` - Merchant can receive order emails and resend emails by Order Actions ### Shopper diff --git a/tests/e2e/core-tests/specs/api/coupon.test.js b/tests/e2e/core-tests/specs/api/coupon.test.js index ec5200afa2b..6ba22bb4d3e 100644 --- a/tests/e2e/core-tests/specs/api/coupon.test.js +++ b/tests/e2e/core-tests/specs/api/coupon.test.js @@ -74,10 +74,10 @@ const runCouponApiTest = () => { it('can delete a coupon', async () => { // Delete the coupon - const deletedCoupon = await repository.delete( coupon.id ); + const status = await repository.delete( coupon.id ); // If the delete is successful, the response comes back truthy - expect( deletedCoupon ).toBeTruthy(); + expect( status ).toBeTruthy(); }); }); }; diff --git a/tests/e2e/core-tests/specs/api/external-product.test.js b/tests/e2e/core-tests/specs/api/external-product.test.js index 674cbdbae25..463f248b726 100644 --- a/tests/e2e/core-tests/specs/api/external-product.test.js +++ b/tests/e2e/core-tests/specs/api/external-product.test.js @@ -69,6 +69,11 @@ const runExternalProductAPITest = () => { const transformed = await repository.read( product.id ); expect( transformed ).toEqual( expect.objectContaining( transformedProperties ) ); }); + + it('can delete an external product', async () => { + const status = repository.delete( product.id ); + expect( status ).toBeTruthy(); + }); }); }; diff --git a/tests/e2e/core-tests/specs/api/grouped-product.test.js b/tests/e2e/core-tests/specs/api/grouped-product.test.js index 11dea1ba7a8..d1f12567305 100644 --- a/tests/e2e/core-tests/specs/api/grouped-product.test.js +++ b/tests/e2e/core-tests/specs/api/grouped-product.test.js @@ -71,11 +71,16 @@ const runGroupedProductAPITest = () => { expect( response.data ).toEqual( expect.objectContaining( rawProperties ) ); }); - it('can retrieve a transformed external product', async () => { + it('can retrieve a transformed grouped product', async () => { // Read product via the repository. const transformed = await repository.read( product.id ); expect( transformed ).toEqual( expect.objectContaining( baseGroupedProduct ) ); }); + + it('can delete a grouped product', async () => { + const status = repository.delete( product.id ); + expect( status ).toBeTruthy(); + }); }); }; diff --git a/tests/e2e/core-tests/specs/index.js b/tests/e2e/core-tests/specs/index.js index 4702576f5d9..b6611d0b235 100644 --- a/tests/e2e/core-tests/specs/index.js +++ b/tests/e2e/core-tests/specs/index.js @@ -33,6 +33,7 @@ const runProductEditDetailsTest = require( './merchant/wp-admin-product-edit-det const runProductSearchTest = require( './merchant/wp-admin-product-search.test' ); const runMerchantOrdersCustomerPaymentPage = require( './merchant/wp-admin-order-customer-payment-page.test' ); const runAddNewShippingZoneTest = require ( './merchant/wp-admin-settings-shipping-zones.test' ); +const runMerchantOrderEmailsTest = require( './merchant/wp-admin-order-emails.test' ); // REST API tests const runExternalProductAPITest = require( './api/external-product.test' ); @@ -78,6 +79,7 @@ const runMerchantTests = () => { const runApiTests = () => { runExternalProductAPITest(); + runGroupedProductAPITest(); runVariableProductAPITest(); runCouponApiTest(); } @@ -114,6 +116,7 @@ module.exports = { runProductEditDetailsTest, runProductSearchTest, runMerchantOrdersCustomerPaymentPage, + runMerchantOrderEmailsTest, runMerchantTests, runAddNewShippingZoneTest, runProductBrowseSearchSortTest, diff --git a/tests/e2e/core-tests/specs/merchant/wp-admin-order-emails.test.js b/tests/e2e/core-tests/specs/merchant/wp-admin-order-emails.test.js new file mode 100644 index 00000000000..ad18a8de6d1 --- /dev/null +++ b/tests/e2e/core-tests/specs/merchant/wp-admin-order-emails.test.js @@ -0,0 +1,73 @@ +/* eslint-disable jest/no-export, jest/no-disabled-tests, jest/no-standalone-expect */ +/** + * Internal dependencies + */ +const { + merchant, + clickUpdateOrder, + createSimpleOrder, + selectOrderAction, + deleteAllEmailLogs, +} = require( '@woocommerce/e2e-utils' ); + +const config = require( 'config' ); +const simpleProductName = config.get( 'products.simple.name' ); +const customerEmail = config.get( 'addresses.customer.billing.email' ); +const adminEmail = 'admin@woocommercecoree2etestsuite.com'; +const storeName = 'WooCommerce Core E2E Test Suite'; + +let orderId; + +const runMerchantOrderEmailsTest = () => { + + describe('Merchant > Order Action emails received', () => { + beforeAll( async () => { + await merchant.login(); + + // Clear out the existing email logs if any + await deleteAllEmailLogs(); + + orderId = await createSimpleOrder( 'Processing' ); + + await Promise.all( [ + // Select the billing email address field and add the customer billing email from the config + await page.click( 'div.order_data_column:nth-child(2) > h3:nth-child(1) > a:nth-child(1)' ), + await expect( page ).toFill( '#_billing_email', customerEmail ), + await clickUpdateOrder( 'Order updated.' ), + ] ); + } ); + + afterEach( async () => { + // Clear out any emails after each test + await deleteAllEmailLogs(); + } ); + + // New order emails are sent automatically when we create the simple order above, so let's verify we get these emails + it('can receive new order email', async () => { + await merchant.openEmailLog(); + await expect( page ).toMatchElement( '.column-receiver', { text: adminEmail } ); + await expect( page ).toMatchElement( '.column-subject', { text: `[${storeName}]: New order #${orderId}` } ); + } ); + + it('can resend new order notification', async () => { + await merchant.goToOrder( orderId ); + await selectOrderAction( 'send_order_details_admin' ); + + await merchant.openEmailLog(); + await expect( page ).toMatchElement( '.column-receiver', { text: adminEmail } ); + await expect( page ).toMatchElement( '.column-subject', { text: `[${storeName}]: New order #${orderId}` } ); + } ); + + it('can email invoice/order details to customer', async () => { + await merchant.goToOrder( orderId ); + await selectOrderAction( 'send_order_details' ); + + await merchant.openEmailLog(); + await expect( page ).toMatchElement( '.column-receiver', { text: customerEmail } ); + await expect( page ).toMatchElement( '.column-subject', { text: `Invoice for order #${orderId} on ${storeName}` } ); + } ); + + } ); +} + +module.exports = runMerchantOrderEmailsTest; diff --git a/tests/e2e/core-tests/specs/shopper/front-end-product-browse-search-sort.test.js b/tests/e2e/core-tests/specs/shopper/front-end-product-browse-search-sort.test.js index c852cf1996e..1b0535bd76d 100644 --- a/tests/e2e/core-tests/specs/shopper/front-end-product-browse-search-sort.test.js +++ b/tests/e2e/core-tests/specs/shopper/front-end-product-browse-search-sort.test.js @@ -21,8 +21,8 @@ const { const config = require( 'config' ); const simpleProductName = config.get( 'products.simple.name' ); const singleProductPrice = config.has('products.simple.price') ? config.get('products.simple.price') : '9.99'; -const singleProductPrice2 = config.has('products.simple.price') ? config.get('products.simple.price') : '19.99'; -const singleProductPrice3 = config.has('products.simple.price') ? config.get('products.simple.price') : '29.99'; +const singleProductPrice2 = config.has('products.simple.price') ? '1' + singleProductPrice : '19.99'; +const singleProductPrice3 = config.has('products.simple.price') ? '2' + singleProductPrice : '29.99'; const clothing = 'Clothing'; const audio = 'Audio'; const hardware = 'Hardware'; @@ -31,34 +31,32 @@ const productTitle = 'li.first > a > h2.woocommerce-loop-product__title'; const runProductBrowseSearchSortTest = () => { describe('Search, browse by categories and sort items in the shop', () => { beforeAll(async () => { - await merchant.login(); - // Create 1st product with Clothing category + // Create 1st product with Clothing category await createSimpleProductWithCategory(simpleProductName + ' 1', singleProductPrice, clothing); - // Create 2nd product with Audio category + // Create 2nd product with Audio category await createSimpleProductWithCategory(simpleProductName + ' 2', singleProductPrice2, audio); - // Create 3rd product with Hardware category + // Create 3rd product with Hardware category await createSimpleProductWithCategory(simpleProductName + ' 3', singleProductPrice3, hardware); - await merchant.logout(); }); it('should let user search the store', async () => { await shopper.goToShop(); await shopper.searchForProduct(simpleProductName + ' 1'); + page.waitForNavigation({waitUntil: 'networkidle0'}); }); it('should let user browse products by categories', async () => { // Browse through Clothing category link await Promise.all([ - page.waitForNavigation({waitUntil: 'networkidle0'}), page.click('span.posted_in > a', {text: clothing}), + page.waitForNavigation({waitUntil: 'networkidle0'}), ]); - await uiUnblocked(); // Verify Clothing category page await page.waitForSelector(productTitle); await expect(page).toMatchElement(productTitle, {text: simpleProductName + ' 1'}); await expect(page).toClick(productTitle, {text: simpleProductName + ' 1'}); - await uiUnblocked(); + page.waitForNavigation({waitUntil: 'networkidle0'}); await page.waitForSelector('h1.entry-title'); await expect(page).toMatchElement('h1.entry-title', simpleProductName + ' 1'); }); diff --git a/tests/e2e/docker/initialize.sh b/tests/e2e/docker/initialize.sh index c5cbea47181..0a9592f5379 100755 --- a/tests/e2e/docker/initialize.sh +++ b/tests/e2e/docker/initialize.sh @@ -8,3 +8,6 @@ wp user create customer customer@woocommercecoree2etestsuite.com --user_pass=pas # we cannot create API keys for the API, so we using basic auth, this plugin allows that. wp plugin install https://github.com/WP-API/Basic-Auth/archive/master.zip --activate + +# install the WP Mail Logging plugin to test emails +wp plugin install wp-mail-logging --activate diff --git a/tests/e2e/env/CHANGELOG.md b/tests/e2e/env/CHANGELOG.md index 94be2bfd1f8..67cfcb4979a 100644 --- a/tests/e2e/env/CHANGELOG.md +++ b/tests/e2e/env/CHANGELOG.md @@ -1,19 +1,26 @@ # Unreleased -# 0.2.0 +## Added + +- Support for screenshots on test errors +- Slackbot to report errors to Slack channel ## Fixed -- Return jest exit code from `npx wc-e2e test:e2e*` +- Update `wc-e2e` script to fix an issue with directories with a space in their name + +# 0.2.0 ## Added - support for custom container name - Insert a 12 hour delay in using new docker image tags - Package `bin` script `wc-e2e` +- WP Mail Log plugin as part of container initialization ## Fixed +- Return jest exit code from `npx wc-e2e test:e2e*` - Remove redundant `puppeteer` dependency - Support for admin user configuration from `default.json` diff --git a/tests/e2e/env/README.md b/tests/e2e/env/README.md index f9998325c85..6ab21205bab 100644 --- a/tests/e2e/env/README.md +++ b/tests/e2e/env/README.md @@ -71,6 +71,16 @@ module.exports = jestConfig; **NOTE:** Your project's Jest config file is expected to be: `tests/e2e/config/jest.config.js`. +#### Test Screenshots + +The test sequencer provides a screenshot function for test failures. To enable screenshots on test failure use + +```shell script +WC_E2E_SCREENSHOTS=1 npx wc-e2e test:e2e +``` + +Screenshots will be saved to `tests/e2e/screenshots` + ### Jest Puppeteer Config The test sequencer uses the following default Puppeteer configuration: @@ -124,6 +134,27 @@ Depending on the project and testing scenario, the built in testing environment - [Built In Container](https://github.com/woocommerce/woocommerce/tree/trunk/tests/e2e/env/builtin.md) - [External Container](https://github.com/woocommerce/woocommerce/tree/trunk/tests/e2e/env/external.md) +### Slackbot Setup + +The test runner has support for posting a message and screenshot to a Slack channel when there is an error in a test. It currently supports both Travis CI and Github actions. + +To implement the Slackbot in your CI: + +- Create a [Slackbot App](https://slack.com/help/articles/115005265703-Create-a-bot-for-your-workspace) +- Give the app the following permissions: + - `channel:join` + - `chat:write` + - `files:write` + - `incoming-webhook` +- Add the app to your channel +- In your CI environment + - Add the environment variable `WC_E2E_SCREENSHOTS=1` + - Add your app Oauth token to a CI secret `E2E_SLACK_TOKEN` + - Add the Slack channel name (without the #) to a CI secret `E2E_SLACK_CHANNEL` + - Add the secrets to the test run using the same variable names + +To test your setup, create a pull request that triggers an error in the E2E tests. + ## Additional information Refer to [`tests/e2e/core-tests`](https://github.com/woocommerce/woocommerce/tree/trunk/tests/e2e/core-tests) for some test examples, and [`tests/e2e`](https://github.com/woocommerce/woocommerce/tree/trunk/tests/e2e) for general information on e2e tests. diff --git a/tests/e2e/env/bin/e2e-test-integration.js b/tests/e2e/env/bin/e2e-test-integration.js index 4279bf652b8..46d457d974b 100755 --- a/tests/e2e/env/bin/e2e-test-integration.js +++ b/tests/e2e/env/bin/e2e-test-integration.js @@ -5,7 +5,7 @@ const program = require( 'commander' ); const path = require( 'path' ); const fs = require( 'fs' ); const { getAppRoot } = require( '../utils' ); -const { JEST_PUPPETEER_CONFIG } = process.env; +const { WC_E2E_SCREENSHOTS, JEST_PUPPETEER_CONFIG } = process.env; program .usage( ' [options]' ) @@ -15,6 +15,17 @@ program const appPath = getAppRoot(); +// clear the screenshots folder before running tests. +if ( WC_E2E_SCREENSHOTS ) { + const screenshotPath = path.resolve(appPath, 'tests/e2e/screenshots'); + if (fs.existsSync(screenshotPath)) { + fs.readdirSync(screenshotPath).forEach((file, index) => { + const filename = path.join(screenshotPath, file); + fs.unlinkSync(filename); + }); + } +} + const nodeConfigDirs = [ path.resolve( __dirname, '../config' ), ]; diff --git a/tests/e2e/env/bin/wc-e2e.sh b/tests/e2e/env/bin/wc-e2e.sh index 0f5eee0d23e..d5fee2dc478 100755 --- a/tests/e2e/env/bin/wc-e2e.sh +++ b/tests/e2e/env/bin/wc-e2e.sh @@ -30,8 +30,8 @@ TESTRESULT=0 # Use the script symlink to find and change directory to the root of the package SCRIPTPATH=$(dirname "$0") -REALPATH=$(readlink $0) -cd $SCRIPTPATH/$(dirname "$REALPATH")/.. +REALPATH=$(readlink "$0") +cd "$SCRIPTPATH/$(dirname "$REALPATH")/.." # Run scripts case $1 in @@ -65,6 +65,6 @@ case $1 in esac # Restore working path -cd $OLDPATH +cd "$OLDPATH" exit $TESTRESULT diff --git a/tests/e2e/env/config/jest.config.js b/tests/e2e/env/config/jest.config.js index abbaaa1b60e..0f87fa06527 100644 --- a/tests/e2e/env/config/jest.config.js +++ b/tests/e2e/env/config/jest.config.js @@ -2,6 +2,7 @@ * External Dependencies */ const { jestConfig } = require( '@automattic/puppeteer-utils' ); +const { WC_E2E_SCREENSHOTS } = process.env; const path = require( 'path' ); const fs = require( 'fs' ); @@ -10,8 +11,13 @@ const fs = require( 'fs' ); */ const { getAppRoot } = require( '../utils' ); +let failureSetup = []; +if ( WC_E2E_SCREENSHOTS ) { + failureSetup.push( path.resolve( __dirname, '../build/setup/jest.failure.js' ) ); +} let setupFilesAfterEnv = [ path.resolve( __dirname, '../build/setup/jest.setup.js' ), + ...failureSetup, 'expect-puppeteer', ]; diff --git a/tests/e2e/env/package.json b/tests/e2e/env/package.json index 92d3348cc2a..210eba5dcc8 100644 --- a/tests/e2e/env/package.json +++ b/tests/e2e/env/package.json @@ -23,10 +23,12 @@ "dependencies": { "@automattic/puppeteer-utils": "github:Automattic/puppeteer-utils#0f3ec50", "@jest/test-sequencer": "^25.5.4", + "@slack/web-api": "^6.1.0", "@wordpress/e2e-test-utils": "^4.15.0", "@wordpress/jest-preset-default": "^6.4.0", "app-root-path": "^3.0.0", "jest": "^25.1.0", + "jest-each": "25.5.0", "jest-puppeteer": "^4.4.0" }, "devDependencies": { diff --git a/tests/e2e/env/src/setup/jest.failure.js b/tests/e2e/env/src/setup/jest.failure.js new file mode 100644 index 00000000000..31a906a10ee --- /dev/null +++ b/tests/e2e/env/src/setup/jest.failure.js @@ -0,0 +1,110 @@ +/** @format */ +import { + sendFailedTestScreenshotToSlack, + sendFailedTestMessageToSlack, +} from '../slack'; + +const path = require( 'path' ); +const mkdirp = require( 'mkdirp' ); +import { bind } from 'jest-each'; +const { getAppRoot } = require( '../../utils' ); + +/** + * Override the test case method so we can take screenshots of assertion failures. + * + * See: https://github.com/smooth-code/jest-puppeteer/issues/131#issuecomment-469439666 + */ + +/** + * We need to reference the original version of Jest. + */ +const originalDescribe = global.describe; +const originalIt = global.it; + +/** + * A custom describe function that stores the name of the describe block. + * @type {describe} + */ +global.describe = (() => { + const describe = ( blockName, callback ) => { + + try { + originalDescribe( blockName, callback ); + } catch ( e ) { + throw e; + } + + }; + const only = ( blockName, callback ) => { + originalDescribe.only( blockName, callback ); + }; + const skip = ( blockName, callback ) => { + originalDescribe.skip( blockName, callback ); + }; + + describe.each = bind( describe, false ); + only.each = bind( only, false ); + skip.each = bind( skip, false ); + describe.only = only; + describe.skip = skip; + + return describe; +})(); + +/** + * A custom it function that wraps the test function in a callback + * which takes a screenshot on test failure. + * + * @type {function(*=, *=): *} + */ +global.it = (() => { + const test = async ( testName, callback ) => { + const testCallback = async () => screenshotTest( testName, callback ); + return originalIt( testName, testCallback ); + }; + const only = ( testName, callback ) => { + return originalIt.only( testName, callback ); + }; + const skip = ( testName, callback ) => { + return originalIt.skip( testName, callback ); + }; + + test.each = bind( test, false ); + only.each = bind( only, false ); + skip.each = bind( skip, false ); + test.only = only; + test.skip = skip; + + return test; +})(); + +/** + * Save a screenshot during a test if the test fails. + * @param testName + * @param callback + * @returns {Promise} + */ +const screenshotTest = async ( testName, callback ) => { + try { + await callback(); + } catch ( e ) { + const testTitle = testName.replace( /\.$/, '' ); + const appPath = getAppRoot(); + const savePath = path.resolve( appPath, 'tests/e2e/screenshots' ); + const filePath = path.join( + savePath, + `${ testTitle }.png`.replace( /[^a-z0-9.-]+/gi, '-' ) + ); + + mkdirp.sync( savePath ); + await page.screenshot( { + path: filePath, + fullPage: true, + } ); + + await sendFailedTestMessageToSlack( testTitle ); + await sendFailedTestScreenshotToSlack( filePath ); + + throw ( e ); + } +}; diff --git a/tests/e2e/env/src/slack/index.js b/tests/e2e/env/src/slack/index.js new file mode 100644 index 00000000000..aeab8f42dd6 --- /dev/null +++ b/tests/e2e/env/src/slack/index.js @@ -0,0 +1 @@ +export * from './reporter'; diff --git a/tests/e2e/env/src/slack/reporter.js b/tests/e2e/env/src/slack/reporter.js new file mode 100644 index 00000000000..7e690102dbf --- /dev/null +++ b/tests/e2e/env/src/slack/reporter.js @@ -0,0 +1,148 @@ +const { createReadStream } = require( 'fs' ); +const { WebClient, ErrorCode } = require( '@slack/web-api' ); +const { + GITHUB_ACTIONS, + GITHUB_REF, + GITHUB_SHA, + GITHUB_REPOSITORY, + GITHUB_RUN_ID, + TRAVIS_PULL_REQUEST_BRANCH, + TRAVIS_COMMIT, + TRAVIS_BUILD_WEB_URL, + E2E_SLACK_TOKEN, + E2E_SLACK_CHANNEL, + WC_E2E_SCREENSHOTS, +} = process.env; + +let web; + +/** + * Initialize the Slack web client. + * + * @returns {WebClient} + */ +const initializeWeb = () => { + if ( ! web ) { + web = new WebClient( E2E_SLACK_TOKEN ); + } + return web; +}; + +/** + * Initialize Slack parameters if tests are running in CI. + * @returns {Object|boolean} + */ +const initializeSlack = () => { + if ( ! WC_E2E_SCREENSHOTS || ! E2E_SLACK_TOKEN ) { + return false; + } + if ( ! GITHUB_ACTIONS && ! TRAVIS_PULL_REQUEST_BRANCH ) { + return false; + } + // Build PR info + if ( GITHUB_ACTIONS ) { + const refArray = GITHUB_REF.split( '/' ); + const branch = refArray.pop(); + return { + branch, + commit: GITHUB_SHA, + webUrl: `https://github.com/${ GITHUB_REPOSITORY }/actions/runs/${ GITHUB_RUN_ID }`, + }; + } + + return { + branch: TRAVIS_PULL_REQUEST_BRANCH, + commit: TRAVIS_COMMIT, + webUrl: TRAVIS_BUILD_WEB_URL, + }; +}; + +/** + * Post a message to a Slack channel for a failed test. + * + * @param testName + * @returns {Promise} + */ +export async function sendFailedTestMessageToSlack( testName ) { + const { branch, commit, webUrl } = initializeSlack(); + if ( ! branch ) { + return; + } + const web = initializeWeb(); + + try { + // Adding the app does not add the app user to the channel + await web.conversations.join({ + channel: E2E_SLACK_CHANNEL, + token: E2E_SLACK_TOKEN, + }); + } catch ( error ) { + // Check the code property and log the response + if ( error.code === ErrorCode.PlatformError || error.code === ErrorCode.RequestError || + error.code === ErrorCode.RateLimitedError || error.code === ErrorCode.HTTPError ) { + console.log( error.data ); + } else { + // Some other error, oh no! + console.log( + 'Error joining channel', + error + ); + } + } + try { + // For details, see: https://api.slack.com/methods/chat.postMessage + await web.chat.postMessage({ + channel: E2E_SLACK_CHANNEL, + token: E2E_SLACK_TOKEN, + text: `Test failed on *${ branch }* branch. \n + The commit this build is testing is *${ commit }*. \n + The name of the test that failed: *${ testName }*. \n + See screenshot of the failed test below. *Build log* could be found here: ${ webUrl }`, + }); + } catch ( error ) { + // Check the code property and log the response + if ( error.code === ErrorCode.PlatformError || error.code === ErrorCode.RequestError || + error.code === ErrorCode.RateLimitedError || error.code === ErrorCode.HTTPError ) { + console.log( error.data ); + } else { + // Some other error, oh no! + console.log( + 'The error occurred does not match an error we are checking for in this block.', + error + ); + } + } +} + +/** + * Post a screenshot to a Slack channel for a failed test. + * @param screenshotOfFailedTest + * @returns {Promise} + */ +export async function sendFailedTestScreenshotToSlack( screenshotOfFailedTest ) { + const pr = initializeSlack(); + if ( ! pr ) { + return; + } + const web = initializeWeb(); + const filename = 'screenshot_of_failed_test.png'; + + try { + // For details, see: https://api.slack.com/methods/files.upload + await web.files.upload({ + channels: E2E_SLACK_CHANNEL, + token: E2E_SLACK_TOKEN, + filename, + file: createReadStream( screenshotOfFailedTest ), + }); + } catch ( error ) { + // Check the code property and log the response + if ( error.code === ErrorCode.PlatformError || error.code === ErrorCode.RequestError || + error.code === ErrorCode.RateLimitedError || error.code === ErrorCode.HTTPError ) { + console.log( error.data ); + } else { + // Some other error, oh no! + console.log( 'The error occurred does not match an error we are checking for in this block.' ); + } + } +} diff --git a/tests/e2e/specs/rest-api/grouped-product.test.js b/tests/e2e/specs/rest-api/grouped-product.js similarity index 100% rename from tests/e2e/specs/rest-api/grouped-product.test.js rename to tests/e2e/specs/rest-api/grouped-product.js diff --git a/tests/e2e/specs/wp-admin/order-emails.test.js b/tests/e2e/specs/wp-admin/order-emails.test.js new file mode 100644 index 00000000000..e0597074bda --- /dev/null +++ b/tests/e2e/specs/wp-admin/order-emails.test.js @@ -0,0 +1,6 @@ +/* + * Internal dependencies + */ +const { runMerchantOrderEmailsTest } = require( '@woocommerce/e2e-core-tests' ); + +runMerchantOrderEmailsTest(); diff --git a/tests/e2e/utils/CHANGELOG.md b/tests/e2e/utils/CHANGELOG.md index b3ab25bd878..c595d647cab 100644 --- a/tests/e2e/utils/CHANGELOG.md +++ b/tests/e2e/utils/CHANGELOG.md @@ -21,6 +21,10 @@ - `createSimpleProductWithCategory` component which creates a simple product with categories, containing three parameters for title, price and category name. - `applyCoupon( couponName )` util helper method which applies previously created coupon to cart or checkout - `removeCoupon()` util helper method that removes a single coupon within cart or checkout +- `selectOrderAction( action )` util helper method to select and initiate an order action in the Order Action postbox +- `merchant.openEmailLog()` go to the WP Mail Log page +- `deleteAllEmailLogs` delete all email logs in the WP Mail Log plugin +- `clickUpdateOrder( noticeText, waitForSave )` util helper that clicks the `Update` button on an order ## Changes diff --git a/tests/e2e/utils/README.md b/tests/e2e/utils/README.md index 82d10b1bae8..23a5896be46 100644 --- a/tests/e2e/utils/README.md +++ b/tests/e2e/utils/README.md @@ -54,6 +54,7 @@ describe( 'Cart page', () => { | `openSettings` | | Go to WooCommerce -> Settings | | `runSetupWizard` | | Open the onboarding profiler | | `updateOrderStatus` | `orderId, status` | Update the status of an order | +| `openEmailLog` | | Open the WP Mail Log page | ### Shopper `shopper` @@ -106,6 +107,8 @@ describe( 'Cart page', () => { | `addShippingZoneAndMethod` | `zoneName, zoneLocation, zipCode, zoneMethod` | util helper method for adding shipping zones with shipping methods | `applyCoupon` | `couponName` | helper method which applies a coupon in cart or checkout | `removeCoupon` | | helper method that removes a single coupon within cart or checkout +| `selectOrderAction` | `action` | Helper method to select an order action in the `Order Actions` postbox | +| `clickUpdateOrder` | `noticeText`, `waitForSave` | Helper method to click the Update button on the order details page | ### Test Utilities diff --git a/tests/e2e/utils/src/components.js b/tests/e2e/utils/src/components.js index 3547b378c24..7b26bbae682 100644 --- a/tests/e2e/utils/src/components.js +++ b/tests/e2e/utils/src/components.js @@ -6,7 +6,7 @@ * Internal dependencies */ import { merchant } from './flows'; -import { clickTab, uiUnblocked, verifyCheckboxIsUnset, evalAndClick, selectOptionInSelect2 } from './page-utils'; +import { clickTab, uiUnblocked, verifyCheckboxIsUnset, evalAndClick, selectOptionInSelect2, setCheckbox } from './page-utils'; import factories from './factories'; const config = require( 'config' ); @@ -192,34 +192,18 @@ const createSimpleProduct = async () => { * @param categoryName Product's category which can be changed when writing a test */ const createSimpleProductWithCategory = async ( productName, productPrice, categoryName ) => { - // Go to "add product" page - await merchant.openNewProduct(); + const product = await factories.products.simple.create( { + name: productName, + regularPrice: productPrice, + categories: [ + { + name: categoryName, + } + ], + isVirtual: true, + } ); - // Add title and regular price - await expect(page).toFill('#title', productName); - await expect(page).toClick('#_virtual'); - await clickTab('General'); - await expect(page).toFill('#_regular_price', productPrice); - - // Try to select the existing category if present already, otherwise add a new and select it - try { - const [checkbox] = await page.$x('//label[contains(text(), "'+categoryName+'")]'); - await checkbox.click(); - } catch (error) { - await expect(page).toClick('#product_cat-add-toggle'); - await expect(page).toFill('#newproduct_cat', categoryName); - await expect(page).toClick('#product_cat-add-submit'); - } - - // Publish the product - await expect(page).toClick('#publish'); - await uiUnblocked(); - await page.waitForSelector('.updated.notice', {text:'Product published.'}); - - // Get the product ID - const variablePostId = await page.$('#post_ID'); - let variablePostIdValue = (await(await variablePostId.getProperty('value')).jsonValue()); - return variablePostIdValue; + return product.id; }; /** @@ -515,6 +499,42 @@ const addShippingZoneAndMethod = async ( zoneName, zoneLocation = 'United States await page.waitFor(1000); // avoiding flakiness }; +/** + * Click the Update button on the order details page. + * + * @param noticeText The text that appears in the notice after updating the order. + * @param waitForSave Optionally wait for auto save. + */ +const clickUpdateOrder = async ( noticeText, waitForSave = false ) => { + if ( waitForSave ) { + await page.waitFor( 2000 ); + } + + // Update order + await expect( page ).toClick( 'button.save_order' ); + await page.waitForSelector( '.updated.notice' ); + + // Verify + await expect( page ).toMatchElement( '.updated.notice', { text: noticeText } ); +}; + +/** + * Delete all email logs in the WP Mail Logging plugin page. + */ +const deleteAllEmailLogs = async () => { + await merchant.openEmailLog(); + + // Make sure we have emails to delete. If we don't, this selector will return null. + if ( await page.$( '#bulk-action-selector-top' ) !== null ) { + await setCheckbox( '#cb-select-all-1' ); + await expect( page ).toSelect( '#bulk-action-selector-top', 'Delete' ); + await Promise.all( [ + page.click( '#doaction' ), + page.waitForNavigation( { waitUntil: 'networkidle0' } ), + ] ); + } +}; + export { completeOnboardingWizard, createSimpleProduct, @@ -526,4 +546,6 @@ export { createCoupon, addShippingZoneAndMethod, createSimpleProductWithCategory, + clickUpdateOrder, + deleteAllEmailLogs, }; diff --git a/tests/e2e/utils/src/flows/merchant.js b/tests/e2e/utils/src/flows/merchant.js index d203af119a9..c12fd64ed6f 100644 --- a/tests/e2e/utils/src/flows/merchant.js +++ b/tests/e2e/utils/src/flows/merchant.js @@ -176,6 +176,12 @@ const merchant = { waitUntil: 'networkidle0', } ); }, + + openEmailLog: async () => { + await page.goto( `${baseUrl}wp-admin/tools.php?page=wpml_plugin_log`, { + waitUntil: 'networkidle0', + } ); + }, }; module.exports = merchant; diff --git a/tests/e2e/utils/src/page-utils.js b/tests/e2e/utils/src/page-utils.js index ac1fe97ebb9..ca5ff92f18a 100644 --- a/tests/e2e/utils/src/page-utils.js +++ b/tests/e2e/utils/src/page-utils.js @@ -243,6 +243,20 @@ const removeCoupon = async ( couponCode ) => { await expect(page).toMatchElement('.woocommerce-message', {text: 'Coupon has been removed.'}); }; +/** + * + * Select and perform an order action in the `Order actions` postbox. + * + * @param {string} action The action to take on the order. + */ +const selectOrderAction = async ( action ) => { + await page.select( 'select[name=wc_order_action]', action ); + await Promise.all( [ + page.click( '.wc-reload' ), + page.waitForNavigation( { waitUntil: 'networkidle0' } ), + ] ); +} + export { clearAndFillInput, clickTab, @@ -259,6 +273,7 @@ export { moveAllItemsToTrash, evalAndClick, selectOptionInSelect2, - applyCoupon, + applyCoupon, removeCoupon, + selectOrderAction, }; diff --git a/tests/legacy/unit-tests/checkout/checkout.php b/tests/legacy/unit-tests/checkout/checkout.php index 73586cab609..953351a9350 100644 --- a/tests/legacy/unit-tests/checkout/checkout.php +++ b/tests/legacy/unit-tests/checkout/checkout.php @@ -141,7 +141,7 @@ class WC_Tests_Checkout extends WC_Unit_Test_Case { } /** - * Test usage limit for guest users uasge limit per user is set. + * Test usage limit for guest users usage limit per user is set. * * @throws Exception When unable to create order. */ diff --git a/tests/legacy/unit-tests/widgets/class-dummy-widget.php b/tests/legacy/unit-tests/widgets/class-dummy-widget.php index f8aa56223d6..67ea55bfccb 100644 --- a/tests/legacy/unit-tests/widgets/class-dummy-widget.php +++ b/tests/legacy/unit-tests/widgets/class-dummy-widget.php @@ -18,7 +18,7 @@ class Dummy_Widget extends WC_Widget { * Output widget. * * @param mixed $args Arguments. - * @param WP_Widget $instance Intance of WP_Widget. + * @param WP_Widget $instance Instance of WP_Widget. * @return void */ public function widget( $args, $instance ) { diff --git a/tests/php/includes/wc-stock-functions-tests.php b/tests/php/includes/wc-stock-functions-tests.php index 56d2d0eeb13..d815eeee5d8 100644 --- a/tests/php/includes/wc-stock-functions-tests.php +++ b/tests/php/includes/wc-stock-functions-tests.php @@ -85,7 +85,7 @@ class WC_Stock_Functions_Tests extends \WC_Unit_Test_Case { } /** - * Test inventory count after order status transtions which reduces stock to another status which also reduces stock. + * Test inventory count after order status transitions which reduces stock to another status which also reduces stock. * Stock should have reduced once already, and should not reduce again. */ public function test_status_transition_stock_reduce_to_stock_reduce() { @@ -97,7 +97,7 @@ class WC_Stock_Functions_Tests extends \WC_Unit_Test_Case { } /** - * Test inventory count after order status transtions which reduces stock to another status which restores stock. + * Test inventory count after order status transitions which reduces stock to another status which restores stock. * Should should have already reduced once, and will increase again after transitioning. */ public function test_status_transition_stock_reduce_to_stock_restore() { @@ -109,7 +109,7 @@ class WC_Stock_Functions_Tests extends \WC_Unit_Test_Case { } /** - * Test inventory count after order status transtions which reduces stock to another status which don't affect inventory. + * Test inventory count after order status transitions which reduces stock to another status which don't affect inventory. * Stock should have already reduced, and will not change on transitioning. */ public function test_status_transition_stock_reduce_to_stock_no_effect() { @@ -133,7 +133,7 @@ class WC_Stock_Functions_Tests extends \WC_Unit_Test_Case { } /** - * Test inventory count after order status transtions which restores stock to another status which also restores stock. + * Test inventory count after order status transitions which restores stock to another status which also restores stock. * Stock should not have reduced, and will remain the same even after transition (i.e. should not be restocked again). */ public function test_status_transition_stock_restore_to_stock_restore() { @@ -145,7 +145,7 @@ class WC_Stock_Functions_Tests extends \WC_Unit_Test_Case { } /** - * Test inventory count after order status transtions which restores stock to another status which don't affect inventory. + * Test inventory count after order status transitions which restores stock to another status which don't affect inventory. * Stock should not have reduced, and will remain the same even after transition. */ public function test_status_transition_stock_restore_to_stock_no_effect() { @@ -157,7 +157,7 @@ class WC_Stock_Functions_Tests extends \WC_Unit_Test_Case { } /** - * Test inventory count after order status transtions which don't affect inventory stock to another status which reduces stock. + * Test inventory count after order status transitions which don't affect inventory stock to another status which reduces stock. * Stock would not have been affected, but will reduce after transition. */ public function test_status_transition_stock_no_effect_to_stock_reduce() { @@ -169,7 +169,7 @@ class WC_Stock_Functions_Tests extends \WC_Unit_Test_Case { } /** - * Test inventory count after order status transtions which don't affect inventory stock to another status which restores stock. + * Test inventory count after order status transitions which don't affect inventory stock to another status which restores stock. * Stock would not have been affected, and will not be restored after transition (since it was not reduced to begin with). */ public function test_status_transition_stock_no_effect_to_stock_restore() { @@ -181,7 +181,7 @@ class WC_Stock_Functions_Tests extends \WC_Unit_Test_Case { } /** - * Test inventory count after order status transtions which don't affect inventory stock to another status which also don't affect inventory. + * Test inventory count after order status transitions which don't affect inventory stock to another status which also don't affect inventory. * Stock levels will not change before or after the transition. */ public function test_status_transition_stock_no_effect_to_stock_no_effect() {