Merge branch 'trunk' into fix/27902

This commit is contained in:
Jonathan Lane 2022-04-27 09:28:17 -07:00 committed by GitHub
commit 31d4b8f7e7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2630 changed files with 266715 additions and 7901 deletions

View File

@ -8,8 +8,12 @@ coverage:
range: "50...100" range: "50...100"
status: status:
project: off project:
patch: off default:
informational: true
patch:
default:
informational: true
changes: off changes: off
parsers: parsers:

View File

@ -13,7 +13,7 @@ There are many ways to contribute to the project!
If you wish to contribute code, please read the information in the sections below. Then [fork](https://help.github.com/articles/fork-a-repo/) WooCommerce, commit your changes, and [submit a pull request](https://help.github.com/articles/using-pull-requests/) 🎉 If you wish to contribute code, please read the information in the sections below. Then [fork](https://help.github.com/articles/fork-a-repo/) WooCommerce, commit your changes, and [submit a pull request](https://help.github.com/articles/using-pull-requests/) 🎉
We use the `good first issue` label to mark issues that are suitable for new contributors. You can find all the issues with this label [here](https://github.com/woocommerce/woocommerce/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22). We use the `good first issue` label to mark issues that are suitable for new contributors. You can find all the issues with this label [here](https://github.com/woocommerce/woocommerce/issues?q=is%3Aopen+is%3Aissue+label%3A%22type%3A+good+first+issue%22).
WooCommerce is licensed under the GPLv3+, and all contributions to the project will be released under the same license. You maintain copyright over any contribution you make, and by submitting a pull request, you are agreeing to release that contribution under the GPLv3+ license. WooCommerce is licensed under the GPLv3+, and all contributions to the project will be released under the same license. You maintain copyright over any contribution you make, and by submitting a pull request, you are agreeing to release that contribution under the GPLv3+ license.
@ -26,8 +26,8 @@ If you have questions about the process to contribute code or want to discuss de
- [Minification of SCSS and JS](https://github.com/woocommerce/woocommerce/wiki/Minification-of-SCSS-and-JS) - [Minification of SCSS and JS](https://github.com/woocommerce/woocommerce/wiki/Minification-of-SCSS-and-JS)
- [Naming conventions](https://github.com/woocommerce/woocommerce/wiki/Naming-conventions) - [Naming conventions](https://github.com/woocommerce/woocommerce/wiki/Naming-conventions)
- [String localisation guidelines](https://github.com/woocommerce/woocommerce/wiki/String-localisation-guidelines) - [String localisation guidelines](https://github.com/woocommerce/woocommerce/wiki/String-localisation-guidelines)
- [Running unit tests](https://github.com/woocommerce/woocommerce/blob/trunk/tests/README.md) - [Running unit tests](https://github.com/woocommerce/woocommerce/blob/trunk/plugins/woocommerce/tests/README.md)
- [Running e2e tests](https://github.com/woocommerce/woocommerce/wiki/End-to-end-Testing) - [Running e2e tests](https://github.com/woocommerce/woocommerce/blob/trunk/plugins/woocommerce/tests/e2e/README.md)
## Coding Guidelines and Development 🛠 ## Coding Guidelines and Development 🛠
@ -41,15 +41,14 @@ If you have questions about the process to contribute code or want to discuss de
- Make sure to write good and detailed commit messages (see [this post](https://chris.beams.io/posts/git-commit/) for more on this) and follow all the applicable sections of the pull request template. - Make sure to write good and detailed commit messages (see [this post](https://chris.beams.io/posts/git-commit/) for more on this) and follow all the applicable sections of the pull request template.
- Please avoid modifying the changelog directly or updating the .pot files. These will be updated by the WooCommerce team. - Please avoid modifying the changelog directly or updating the .pot files. These will be updated by the WooCommerce team.
If you are contributing code to the (Javascript-driven) WooCommerce Admin project or to Gutenberg blocks, note that these are developed in external packages. If you are contributing code to the (Javascript-driven) Gutenberg blocks, note that it's developed in an external package.
- [WooCommerce Admin](https://github.com/woocommerce/woocommerce-admin)
- [Blocks](https://github.com/woocommerce/woocommerce-gutenberg-products-block) - [Blocks](https://github.com/woocommerce/woocommerce-gutenberg-products-block)
## Feature Requests 🚀 ## Feature Requests 🚀
Feature requests can be [submitted to our issue tracker](https://github.com/woocommerce/woocommerce/issues/new?template=6-Feature-request.md). Be sure to include a description of the expected behavior and use case, and before submitting a request, please search for similar ones in the closed issues. Feature requests can be [submitted to our issue tracker](https://github.com/woocommerce/woocommerce/issues/new?assignees=&labels=type%3A+enhancement%2Cstatus%3A+awaiting+triage&template=2-enhancement.yml&title=%5BEnhancement%5D%3A+). Be sure to include a description of the expected behavior and use case, and before submitting a request, please search for similar ones in the closed issues.
Feature request issues will remain closed until we see sufficient interest via comments and [👍 reactions](https://help.github.com/articles/about-discussions-in-issues-and-pull-requests/) from the community. Feature request issues will remain closed until we see sufficient interest via comments and [👍 reactions](https://help.github.com/articles/about-discussions-in-issues-and-pull-requests/) from the community.
You can see a [list of current feature requests which require votes here](https://github.com/woocommerce/woocommerce/issues?q=is%3Aclosed+label%3A%22type%3A+enhancement%22+label%3A%22votes+needed%22+sort%3Areactions-%2B1-desc). You can see a [list of current feature requests which require votes here](https://github.com/woocommerce/woocommerce/issues?q=is%3Aissue+sort%3Areactions-%2B1-desc+label%3A%22needs%3A+votes%22+).

View File

@ -1,7 +1,7 @@
name: ✨ Enhancement Request name: ✨ Enhancement Request
description: If you have an idea to improve an existing feature in core or need something for development (such as a new hook) please let us know or submit a Pull Request! description: If you have an idea to improve an existing feature in core or need something for development (such as a new hook) please let us know or submit a Pull Request!
title: "[Enhancement]: " title: "[Enhancement]: "
labels: ["type: enhancement"] labels: ["type: enhancement", "status: awaiting triage"]
body: body:
- type: markdown - type: markdown
attributes: attributes:
@ -10,7 +10,7 @@ body:
Please provide us with the information requested in this form. Please provide us with the information requested in this form.
Make sure to look through [existing `type: enhancement` issues](https://github.com/woocommerce/woocommerce/issues?q=is%3Aopen+is%3Aissue+label%3A%22type%3A+enhancement%22) and [existing `votes needed` issues](https://github.com/woocommerce/woocommerce/issues?q=is%3Aissue+sort%3Areactions-%2B1-desc+label%3A%22votes+needed%22) to see whether your idea is already being discussed. Make sure to look through [existing `type: enhancement` issues](https://github.com/woocommerce/woocommerce/issues?q=is%3Aopen+is%3Aissue+label%3A%22type%3A+enhancement%22) and [existing `votes needed` issues](https://github.com/woocommerce/woocommerce/issues?q=is%3Aissue+sort%3Areactions-%2B1-desc+label%3A%22needs%3A+votes%22+) to see whether your idea is already being discussed.
Feel free to contribute to any existing issues. Feel free to contribute to any existing issues.
Search tip: You can filter our issues using [our enhancement label](https://github.com/woocommerce/woocommerce/issues?q=is%3Aissue+label%3A%22type%3A+enhancement%22+). Search tip: You can filter our issues using [our enhancement label](https://github.com/woocommerce/woocommerce/issues?q=is%3Aissue+label%3A%22type%3A+enhancement%22+).
Search tip: Make use of [GitHub's search syntax to refine your search](https://help.github.com/en/github/searching-for-information-on-github/searching-issues-and-pull-requests). Search tip: Make use of [GitHub's search syntax to refine your search](https://help.github.com/en/github/searching-for-information-on-github/searching-issues-and-pull-requests).

View File

@ -6,9 +6,6 @@ contact_links:
- name: ❓ Support Question - name: ❓ Support Question
url: https://woocommerce.com/document/woocommerce-self-service-guide/ url: https://woocommerce.com/document/woocommerce-self-service-guide/
about: If you have a question please see our docs or use our forums, helpdesk, or Slack community! about: If you have a question please see our docs or use our forums, helpdesk, or Slack community!
- name: WooCommerce Admin
url: https://github.com/woocommerce/woocommerce-admin
about: Please report issues for WooCommerce Admin (such as Analytics and Onboarding) directly to it's repository.
- name: WooCommerce Blocks - name: WooCommerce Blocks
url: https://github.com/woocommerce/woocommerce-gutenberg-products-block url: https://github.com/woocommerce/woocommerce-gutenberg-products-block
about: Please report issues for WooCommerce Blocks directly to it's repository. about: Please report issues for WooCommerce Blocks directly to it's repository.

View File

@ -25,13 +25,10 @@ Closes # .
* [ ] Have you added an explanation of what your changes do and why you'd like us to include them? * [ ] Have you added an explanation of what your changes do and why you'd like us to include them?
* [ ] Have you written new tests for your changes, as applicable? * [ ] Have you written new tests for your changes, as applicable?
* [ ] Have you successfully run tests with your changes locally? * [ ] Have you successfully run tests with your changes locally?
* [ ] Have you created a changelog file by running `pnpm nx affected --target=changelog`?
<!-- Mark completed items with an [x] --> <!-- Mark completed items with an [x] -->
### Changelog entry
> Enter a summary of all changes on this Pull Request. This will appear in the changelog if accepted.
### FOR PR REVIEWER ONLY: ### FOR PR REVIEWER ONLY:
* [ ] I have reviewed that everything is sanitized/escaped appropriately for any SQL or XSS injection possibilities. I made sure Linting is not ignored or disabled. * [ ] I have reviewed that everything is sanitized/escaped appropriately for any SQL or XSS injection possibilities. I made sure Linting is not ignored or disabled.

76
.github/project-pr-labeler.yml vendored Normal file
View File

@ -0,0 +1,76 @@
'package: @woocommerce/api':
- packages/js/api/**/*
'package: @woocommerce/e2e-utils':
- packages/js/e2e-utils/**/*
'package: @woocommerce/e2e-environment':
- packages/js/e2e-environment/**/*
'package: @woocommerce/api-core-tests':
- packages/js/api-core-tests/**/*
'package: @woocommerce/e2e-core-tests':
- packages/js/e2e-core-tests/**/*
'package: @woocommerce/admin-e2e-tests':
- packages/js/admin-e2e-tests/**/*
'package: @woocommerce/components':
- packages/js/components/**/*
'package: @woocommerce/csv-export':
- packages/js/csv-export/**/*
'package: @woocommerce/currency':
- packages/js/currency/**/*
'package: @woocommerce/customer-effort-score':
- packages/js/customer-effort-score/**/*
'package: @woocommerce/data':
- packages/js/data/**/*
'package: @woocommerce/date':
- packages/js/date/**/*
'package: dependency-extraction-webpack-plugin':
- packages/js/dependency-extraction-webpack-plugin/**/*
'package: @woocommerce/eslint-plugin':
- packages/js/eslint-plugin/**/*
'package: @woocommerce/experimental':
- packages/js/experimental/**/*
'package: @woocommerce/explat':
- packages/js/explat/**/*
'package: @woocommerce/js-tests':
- packages/js/js-tests/**/*
'package: @woocommerce/navigation':
- packages/js/navigation/**/*
'package: @woocommerce/notices':
- packages/js/notices/**/*
'package: @woocommerce/number':
- packages/js/number/**/*
'package: @woocommerce/onboarding':
- packages/js/onboarding/**/*
'package: @woocommerce/style-build':
- packages/js/style-build/**/*
'package: @woocommerce/tracks':
- packages/js/tracks/**/*
'plugin: woocommerce':
- plugins/woocommerce/**/*
'focus: react admin':
- plugins/woocommerce/src/Admin/**/*
- plugins/woocommerce/src/Internal/Admin/**/*
- plugins/woocommerce-admin/**/*

View File

@ -9,10 +9,10 @@ on:
jobs: jobs:
build: build:
name: Build release zip file name: Build release zip file
runs-on: ubuntu-latest runs-on: ubuntu-20.04
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v2 uses: actions/checkout@v3
with: with:
ref: ${{ github.event.inputs.ref || github.ref }} ref: ${{ github.event.inputs.ref || github.ref }}
- name: Build the zip file - name: Build the zip file

View File

@ -5,10 +5,10 @@ on:
jobs: jobs:
build: build:
name: Build release asset name: Build release asset
runs-on: ubuntu-latest runs-on: ubuntu-20.04
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v2 uses: actions/checkout@v3
- name: Build - name: Build
id: build id: build
uses: woocommerce/action-build@trunk uses: woocommerce/action-build@trunk
@ -25,7 +25,7 @@ jobs:
if: github.event.release.prerelease == false && github.event.release.draft == false && github.repository_owner == 'woocommerce' if: github.event.release.prerelease == false && github.event.release.draft == false && github.repository_owner == 'woocommerce'
name: Update Code Reference name: Update Code Reference
needs: build needs: build
runs-on: ubuntu-latest runs-on: ubuntu-20.04
steps: steps:
- name: Invoke Code Reference build and deploy workflow - name: Invoke Code Reference build and deploy workflow
uses: aurelien-baudet/workflow-dispatch@v2 uses: aurelien-baudet/workflow-dispatch@v2
@ -35,3 +35,16 @@ jobs:
token: ${{ secrets.CUSTOM_GH_TOKEN }} token: ${{ secrets.CUSTOM_GH_TOKEN }}
ref: refs/heads/trunk ref: refs/heads/trunk
inputs: '{ "version": "${{ github.event.release.tag_name }}" }' inputs: '{ "version": "${{ github.event.release.tag_name }}" }'
run-release-smoke-tests:
name: Execute Smoke test release
needs: build
runs-on: ubuntu-20.04
steps:
- name: Invoke release smoke testing workflow
uses: aurelien-baudet/workflow-dispatch@v2
with:
workflow: Smoke test release
repo: ${{ github.repository }}
token: ${{ secrets.E2E_WORKFLOW_GH_TOKEN }}
ref: refs/heads/trunk
inputs: '{ "release_id": "${{ github.event.release.id }}" }'

View File

@ -7,24 +7,26 @@ on:
defaults: defaults:
run: run:
shell: bash shell: bash
working-directory: plugins/woocommerce concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs: jobs:
test: test:
name: PHP ${{ matrix.php }} WP ${{ matrix.wp }} name: PHP ${{ matrix.php }} WP ${{ matrix.wp }}
timeout-minutes: 15 timeout-minutes: 20
runs-on: ubuntu-latest runs-on: ubuntu-20.04
continue-on-error: ${{ matrix.wp == 'nightly' }} continue-on-error: ${{ matrix.wp == 'nightly' }}
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
php: [ '7.0', '7.1', '7.2', '7.3', '7.4', '8.0' ] php: [ '7.2', '7.3', '7.4', '8.0' ]
wp: [ 'latest' ] wp: [ 'latest' ]
include: include:
- wp: nightly - wp: nightly
php: '7.4' php: '7.4'
- wp: '5.7' - wp: '5.8'
php: 7.2 php: 7.2
- wp: '5.6' - wp: '5.7'
php: 7.2 php: 7.2
services: services:
database: database:
@ -36,7 +38,7 @@ jobs:
options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=5 options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=5
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v2 uses: actions/checkout@v3
- name: Setup PHP - name: Setup PHP
uses: shivammathur/setup-php@v2 uses: shivammathur/setup-php@v2
@ -51,25 +53,33 @@ jobs:
php --version php --version
composer --version composer --version
- name: Get cached composer directories - name: Get cached composer and pnpm directories
uses: actions/cache@v2 uses: actions/cache@v3
id: cache-deps
with: with:
path: | path: |
./packages ~/.pnpm-store
./vendor plugins/woocommerce/packages
key: ${{ runner.os }}-${{ hashFiles('./composer.lock') }} plugins/woocommerce/**/vendor
key: ${{ runner.os }}-npm-composer-${{ hashFiles('**/composer.lock', '**/pnpm-lock.yaml') }}
- name: Install PNPM and install dependencies - name: Install PNPM
run: | run: npm install -g pnpm
npm install -g pnpm
pnpm install
- name: Setup and install composer - name: Install dependencies
run: pnpm install
- name: Install Composer dependencies
if: steps.cache-deps.outputs.cache-hit != 'true'
run: pnpm nx composer-install woocommerce run: pnpm nx composer-install woocommerce
- name: Build Admin feature config
run: pnpm nx build:feature-config woocommerce
- name: Add PHP8 Compatibility. - name: Add PHP8 Compatibility.
run: | run: |
if [ "$(php -r "echo version_compare(PHP_VERSION,'8.0','>=');")" ]; then if [ "$(php -r "echo version_compare(PHP_VERSION,'8.0','>=');")" ]; then
cd plugins/woocommerce
curl -L https://github.com/woocommerce/phpunit/archive/add-compatibility-with-php8-to-phpunit-7.zip -o /tmp/phpunit-7.5-fork.zip curl -L https://github.com/woocommerce/phpunit/archive/add-compatibility-with-php8-to-phpunit-7.zip -o /tmp/phpunit-7.5-fork.zip
unzip -d /tmp/phpunit-7.5-fork /tmp/phpunit-7.5-fork.zip unzip -d /tmp/phpunit-7.5-fork /tmp/phpunit-7.5-fork.zip
composer bin phpunit config --unset platform composer bin phpunit config --unset platform
@ -80,6 +90,7 @@ jobs:
fi fi
- name: Init DB and WP - name: Init DB and WP
working-directory: plugins/woocommerce
run: ./tests/bin/install.sh woo_test root root 127.0.0.1 ${{ matrix.wp }} run: ./tests/bin/install.sh woo_test root root 127.0.0.1 ${{ matrix.wp }}
- name: Run tests - name: Run tests

View File

@ -1,21 +1,20 @@
name: Mirrors name: Mirrors
on: on:
push: push:
branches: ['trunk', 'release/**'] branches: ["trunk", "release/**"]
jobs: jobs:
build: build:
if: github.repository == 'woocommerce/woocommerce'
name: Build WooCommerce zip name: Build WooCommerce zip
runs-on: ubuntu-latest runs-on: ubuntu-20.04
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v2 uses: actions/checkout@v3
- name: Build - name: Build
id: build id: build
uses: woocommerce/action-build@trunk uses: woocommerce/action-build@trunk
env: env:
BUILD_ENV: mirrors BUILD_ENV: mirrors
- name: Upload PR zip - name: Upload PR zip
uses: actions/upload-artifact@v2 uses: actions/upload-artifact@v2
env: env:
@ -24,28 +23,25 @@ jobs:
name: woocommerce name: woocommerce
path: ${{ steps.build.outputs.zip_path }} path: ${{ steps.build.outputs.zip_path }}
retention-days: 7 retention-days: 7
mirror: mirror:
if: github.repository == 'woocommerce/woocommerce'
name: Push to Mirror name: Push to Mirror
runs-on: ubuntu-latest runs-on: ubuntu-20.04
needs: [build] needs: [build]
steps: steps:
- name: Create directories - name: Create directories
run: | run: |
mkdir -p tmp/woocommerce-build mkdir -p tmp/woocommerce-build
mkdir -p monorepo mkdir -p monorepo
- name: Checkout monorepo - name: Checkout monorepo
uses: actions/checkout@v2 uses: actions/checkout@v2
with: with:
path: monorepo path: monorepo
- name: Download WooCommerce ZIP - name: Download WooCommerce ZIP
uses: actions/download-artifact@v2 uses: actions/download-artifact@v2
with: with:
name: woocommerce name: woocommerce
path: tmp/woocommerce-build path: tmp/woocommerce-build
- name: Extract and replace WooCommerce zip. - name: Extract and replace WooCommerce zip.
working-directory: tmp/woocommerce-build working-directory: tmp/woocommerce-build
run: | run: |
@ -53,13 +49,11 @@ jobs:
unzip woocommerce.zip -d woocommerce/woocommerce-production unzip woocommerce.zip -d woocommerce/woocommerce-production
mv woocommerce/woocommerce-production/woocommerce/* woocommerce/woocommerce-production mv woocommerce/woocommerce-production/woocommerce/* woocommerce/woocommerce-production
rm -rf woocommerce/woocommerce-production/woocommerce rm -rf woocommerce/woocommerce-production/woocommerce
- name: Set up mirror - name: Set up mirror
working-directory: tmp/woocommerce-build working-directory: tmp/woocommerce-build
run: | run: |
touch mirrors.txt touch mirrors.txt
echo "woocommerce/woocommerce-production" >> mirrors.txt echo "woocommerce/woocommerce-production" >> mirrors.txt
- name: Push to mirror - name: Push to mirror
uses: Automattic/action-push-to-mirrors@v1 uses: Automattic/action-push-to-mirrors@v1
with: with:

View File

@ -10,10 +10,10 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
build: [trunk] build: [trunk]
runs-on: ubuntu-latest runs-on: ubuntu-20.04
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v2 uses: actions/checkout@v3
with: with:
ref: ${{ matrix.build }} ref: ${{ matrix.build }}
- name: Build - name: Build
@ -32,7 +32,7 @@ jobs:
max_releases: 1 max_releases: 1
update: update:
name: Update nightly tag commit ref name: Update nightly tag commit ref
runs-on: ubuntu-latest runs-on: ubuntu-20.04
steps: steps:
- name: Update nightly tag - name: Update nightly tag
uses: richardsimko/github-tag-action@v1.0.5 uses: richardsimko/github-tag-action@v1.0.5

View File

@ -1,12 +1,16 @@
name: Build zip for PR name: Build zip for PR
on: pull_request on: pull_request
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs: jobs:
build: build:
name: Build zip for PR name: Build zip for PR
runs-on: ubuntu-latest runs-on: ubuntu-20.04
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v2 uses: actions/checkout@v3
- name: Build - name: Build
id: build id: build
@ -15,7 +19,7 @@ jobs:
BUILD_ENV: e2e BUILD_ENV: e2e
- name: Upload PR zip - name: Upload PR zip
uses: actions/upload-artifact@v2 uses: actions/upload-artifact@v3
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with: with:
@ -25,7 +29,7 @@ jobs:
e2e-tests-run: e2e-tests-run:
name: Runs E2E tests. name: Runs E2E tests.
runs-on: ubuntu-18.04 runs-on: ubuntu-20.04
needs: [build] needs: [build]
steps: steps:
- name: Create dirs. - name: Create dirs.
@ -33,28 +37,17 @@ jobs:
mkdir -p code/woocommerce mkdir -p code/woocommerce
mkdir -p package/woocommerce mkdir -p package/woocommerce
mkdir -p tmp/woocommerce mkdir -p tmp/woocommerce
mkdir -p node_modules
- name: Checkout code. - name: Checkout code.
uses: actions/checkout@v2 uses: actions/checkout@v3
with: with:
path: package/woocommerce path: package/woocommerce
- name: Install PNPM and install dependencies
working-directory: package/woocommerce
run: |
npm install -g pnpm
pnpm install
- name: Load docker images and start containers.
working-directory: package/woocommerce/plugins/woocommerce
run: pnpx wc-e2e docker:up
- name: Move current directory to code. We will install zip file in this dir later. - name: Move current directory to code. We will install zip file in this dir later.
run: mv ./package/woocommerce/plugins/woocommerce/* ./code/woocommerce run: mv ./package/woocommerce/plugins/woocommerce/* ./code/woocommerce
- name: Download WooCommerce ZIP. - name: Download WooCommerce ZIP.
uses: actions/download-artifact@v2 uses: actions/download-artifact@v3
with: with:
name: woocommerce name: woocommerce
path: tmp path: tmp
@ -65,11 +58,23 @@ jobs:
unzip woocommerce.zip -d woocommerce unzip woocommerce.zip -d woocommerce
mv woocommerce/woocommerce/* ../package/woocommerce/plugins/woocommerce/ mv woocommerce/woocommerce/* ../package/woocommerce/plugins/woocommerce/
- name: Install dependencies again - name: Cache modules
uses: actions/cache@v3
with:
path: |
~/.pnpm-store
key: ${{ runner.os }}-npm-${{ hashFiles('**/pnpm-lock.yaml') }}
- name: Install PNPM
run: npm install -g pnpm
- name: Install dependencies
working-directory: package/woocommerce working-directory: package/woocommerce
run: | run: pnpm install
npm install -g pnpm
pnpm install - name: Load docker images and start containers.
working-directory: package/woocommerce/plugins/woocommerce
run: pnpm exec wc-e2e docker:up
- name: Run tests command. - name: Run tests command.
working-directory: package/woocommerce/plugins/woocommerce working-directory: package/woocommerce/plugins/woocommerce
@ -77,11 +82,20 @@ jobs:
WC_E2E_SCREENSHOTS: 1 WC_E2E_SCREENSHOTS: 1
E2E_SLACK_TOKEN: ${{ secrets.E2E_SLACK_TOKEN }} E2E_SLACK_TOKEN: ${{ secrets.E2E_SLACK_TOKEN }}
E2E_SLACK_CHANNEL: ${{ secrets.E2E_SLACK_CHANNEL }} E2E_SLACK_CHANNEL: ${{ secrets.E2E_SLACK_CHANNEL }}
run: pnpx wc-e2e test:e2e run: pnpm exec wc-e2e test:e2e
- name: Archive E2E test screenshots
uses: actions/upload-artifact@v3
if: always()
with:
name: E2E Screenshots
path: package/woocommerce/plugins/woocommerce/tests/e2e/screenshots
if-no-files-found: ignore
retention-days: 5
api-tests-run: api-tests-run:
name: Runs API tests. name: Runs API tests.
runs-on: ubuntu-18.04 runs-on: ubuntu-20.04
needs: [build] needs: [build]
steps: steps:
- name: Create dirs. - name: Create dirs.
@ -89,28 +103,17 @@ jobs:
mkdir -p code/woocommerce mkdir -p code/woocommerce
mkdir -p package/woocommerce mkdir -p package/woocommerce
mkdir -p tmp/woocommerce mkdir -p tmp/woocommerce
mkdir -p node_modules
- name: Checkout code. - name: Checkout code.
uses: actions/checkout@v2 uses: actions/checkout@v3
with: with:
path: package/woocommerce path: package/woocommerce
- name: Install PNPM and install dependencies
working-directory: package/woocommerce
run: |
npm install -g pnpm
pnpm install
- name: Load docker images and start containers.
working-directory: package/woocommerce/plugins/woocommerce
run: pnpx wc-e2e docker:up
- name: Move current directory to code. We will install zip file in this dir later. - name: Move current directory to code. We will install zip file in this dir later.
run: mv ./package/woocommerce/plugins/woocommerce/* ./code/woocommerce run: mv ./package/woocommerce/plugins/woocommerce/* ./code/woocommerce
- name: Download WooCommerce ZIP. - name: Download WooCommerce ZIP.
uses: actions/download-artifact@v2 uses: actions/download-artifact@v3
with: with:
name: woocommerce name: woocommerce
path: tmp path: tmp
@ -121,11 +124,23 @@ jobs:
unzip woocommerce.zip -d woocommerce unzip woocommerce.zip -d woocommerce
mv woocommerce/woocommerce/* ../package/woocommerce/plugins/woocommerce/ mv woocommerce/woocommerce/* ../package/woocommerce/plugins/woocommerce/
- name: Install dependencies again - name: Cache modules
uses: actions/cache@v3
with:
path: |
~/.pnpm-store
key: ${{ runner.os }}-npm-${{ hashFiles('**/pnpm-lock.yaml') }}
- name: Install PNPM
run: npm install -g pnpm
- name: Install dependencies
working-directory: package/woocommerce working-directory: package/woocommerce
run: | run: pnpm install
npm install -g pnpm
pnpm install - name: Load docker images and start containers.
working-directory: package/woocommerce/plugins/woocommerce
run: pnpm exec wc-e2e docker:up
- name: Run tests command. - name: Run tests command.
working-directory: package/woocommerce/plugins/woocommerce working-directory: package/woocommerce/plugins/woocommerce
@ -133,4 +148,75 @@ jobs:
BASE_URL: http://localhost:8084 BASE_URL: http://localhost:8084
USER_KEY: admin USER_KEY: admin
USER_SECRET: password USER_SECRET: password
run: pnpx wc-api-tests test api run: pnpm exec wc-api-tests test api
- name: Upload API test report
uses: actions/upload-artifact@v3
with:
name: api-test-report---pr-${{ github.event.number }}
path: |
package/woocommerce/packages/js/api-core-tests/allure-results
package/woocommerce/packages/js/api-core-tests/allure-report
retention-days: 7
k6-tests-run:
name: Runs k6 Performance tests
runs-on: ubuntu-20.04
needs: [build]
steps:
- name: Create dirs.
run: |
mkdir -p code/woocommerce
mkdir -p package/woocommerce
mkdir -p tmp/woocommerce
- name: Checkout code.
uses: actions/checkout@v3
with:
path: package/woocommerce
- name: Move current directory to code. We will install zip file in this dir later.
run: mv ./package/woocommerce/plugins/woocommerce/* ./code/woocommerce
- name: Download WooCommerce ZIP.
uses: actions/download-artifact@v3
with:
name: woocommerce
path: tmp
- name: Extract and replace WooCommerce zip.
working-directory: tmp
run: |
unzip woocommerce.zip -d woocommerce
mv woocommerce/woocommerce/* ../package/woocommerce/plugins/woocommerce/
- name: Cache modules
uses: actions/cache@v3
with:
path: |
~/.pnpm-store
key: ${{ runner.os }}-npm-${{ hashFiles('**/pnpm-lock.yaml') }}
- name: Install PNPM
run: npm install -g pnpm
- name: Install dependencies
working-directory: package/woocommerce
run: pnpm install
- name: Workaround to use initialization file with prepopulated data.
working-directory: package/woocommerce/plugins/woocommerce/tests/e2e/docker
run: |
cp init-sample-products.sh initialize.sh
- name: Load docker images and start containers.
working-directory: package/woocommerce/plugins/woocommerce
run: pnpm exec wc-e2e docker:up
- name: Install k6
run: |
curl https://github.com/grafana/k6/releases/download/v0.33.0/k6-v0.33.0-linux-amd64.tar.gz -L | tar xvz --strip-components 1
- name: Run k6 tests
run: |
./k6 run package/woocommerce/plugins/woocommerce/tests/performance/tests/gh-action-pr-requests.js

View File

@ -4,12 +4,14 @@ on:
defaults: defaults:
run: run:
shell: bash shell: bash
working-directory: plugins/woocommerce concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs: jobs:
test: test:
name: Code coverage (PHP 7.4, WP Latest) name: Code coverage (PHP 7.4, WP Latest)
timeout-minutes: 15 timeout-minutes: 20
runs-on: ubuntu-latest runs-on: ubuntu-20.04
services: services:
database: database:
image: mysql:5.6 image: mysql:5.6
@ -20,7 +22,7 @@ jobs:
options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=5 options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=5
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v2 uses: actions/checkout@v3
with: with:
fetch-depth: 100 fetch-depth: 100
@ -37,22 +39,30 @@ jobs:
php --version php --version
composer --version composer --version
- name: Get cached composer directories - name: Get cached composer and pnpm directories
uses: actions/cache@v2 uses: actions/cache@v3
id: cache-deps
with: with:
path: | path: |
./packages ~/.pnpm-store
./vendor plugins/woocommerce/packages
key: ${{ runner.os }}-${{ hashFiles('./composer.lock') }} plugins/woocommerce/**/vendor
key: ${{ runner.os }}-npm-composer-${{ hashFiles('**/composer.lock', '**/pnpm-lock.yaml') }}
- name: Install PNPM and install dependencies - name: Install PNPM
run: | run: npm install -g pnpm
npm install -g pnpm
pnpm install
- name: Setup and install composer - name: Install dependencies
run: pnpm install
- name: Install Composer dependencies
if: steps.cache-deps.outputs.cache-hit != 'true'
run: pnpm nx composer-install woocommerce run: pnpm nx composer-install woocommerce
- name: Build Admin feature config
run: |
pnpm nx build:feature-config woocommerce
- name: Init DB and WP - name: Init DB and WP
run: pnpm nx install-unit-test-db woocommerce run: pnpm nx install-unit-test-db woocommerce

View File

@ -4,15 +4,17 @@ on:
defaults: defaults:
run: run:
shell: bash shell: bash
working-directory: plugins/woocommerce concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs: jobs:
test: test:
name: Code sniff (PHP 7.4, WP Latest) name: Code sniff (PHP 7.4, WP Latest)
timeout-minutes: 15 timeout-minutes: 15
runs-on: ubuntu-latest runs-on: ubuntu-20.04
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v2 uses: actions/checkout@v3
with: with:
fetch-depth: 100 fetch-depth: 100
@ -27,25 +29,31 @@ jobs:
php --version php --version
composer --version composer --version
- name: Get cached composer directories - name: Get cached composer and pnpm directories
uses: actions/cache@v2 uses: actions/cache@v3
id: cache-deps
with: with:
path: | path: |
./packages ~/.pnpm-store
./vendor plugins/woocommerce/packages
key: ${{ runner.os }}-${{ hashFiles('./composer.lock') }} plugins/woocommerce/**/vendor
key: ${{ runner.os }}-npm-composer-${{ hashFiles('**/composer.lock', '**/pnpm-lock.yaml') }}
- name: Install PNPM and install dependencies - name: Install PNPM
run: | run: npm install -g pnpm
npm install -g pnpm
pnpm install
- name: Setup and install composer - name: Install dependencies
run: pnpm install
- name: Install Composer dependencies
if: steps.cache-deps.outputs.cache-hit != 'true'
run: pnpm nx composer-install woocommerce run: pnpm nx composer-install woocommerce
- name: Run code sniff - name: Run code sniff
continue-on-error: true continue-on-error: true
working-directory: plugins/woocommerce
run: ./tests/bin/phpcs.sh "${{ github.event.pull_request.base.sha }}" "${{ github.event.after }}" run: ./tests/bin/phpcs.sh "${{ github.event.pull_request.base.sha }}" "${{ github.event.after }}"
- name: Show PHPCS results in PR - name: Show PHPCS results in PR
working-directory: plugins/woocommerce
run: cs2pr ./phpcs-report.xml run: cs2pr ./phpcs-report.xml

26
.github/workflows/pr-lint-monorepo.yml vendored Normal file
View File

@ -0,0 +1,26 @@
name: Run lint checks potentially affecting projects across the monorepo
on: pull_request
concurrency:
group: changelogger-${{ github.event_name }}-${{ github.ref }}
cancel-in-progress: true
jobs:
changelogger_used:
name: Changelogger use
runs-on: ubuntu-20.04
timeout-minutes: 5
steps:
- uses: actions/checkout@v3
with:
ref: ${{ github.event.pull_request.head.sha }}
fetch-depth: 0
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '7.4'
- name: Check change files are touched for touched projects
env:
BASE: ${{ github.event.pull_request.base.sha }}
HEAD: ${{ github.event.pull_request.head.sha }}
run: php tools/monorepo/check-changelogger-use.php --debug "$BASE" "$HEAD"

47
.github/workflows/pr-lint-test-js.yml vendored Normal file
View File

@ -0,0 +1,47 @@
name: Lint and tests for JS packages and woocommerce-admin/client
on:
pull_request:
paths:
- 'packages/js/**/**'
- 'plugins/woocommerce-admin/client/**'
- '!**.md'
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
lint-test-js:
name: Lint and Test JS
runs-on: ubuntu-20.04
steps:
- name: Check out repository code
uses: actions/checkout@v3
- uses: actions/setup-node@v2
with:
node-version: '16'
- name: Cache modules
uses: actions/cache@v3
with:
path: |
~/.pnpm-store
key: ${{ runner.os }}-npm-${{ hashFiles('**/pnpm-lock.yaml') }}
- name: Install PNPM
run: npm install -g pnpm
- name: Install dependencies
run: pnpm install
- name: Lint
run: |
pnpm nx lint woocommerce-admin
pnpm nx lint:js-packages woocommerce-admin
- name: Test
run: |
pnpm nx build woocommerce-admin
pnpm nx test woocommerce-admin
pnpm nx test:packages woocommerce-admin

18
.github/workflows/pr-project-label.yml vendored Normal file
View File

@ -0,0 +1,18 @@
name: 'Label Pull Request Project'
on:
pull_request_target:
types:
- opened
- synchronize
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
label_project:
runs-on: ubuntu-20.04
steps:
- uses: actions/labeler@v3
with:
repo-token: "${{ secrets.GITHUB_TOKEN }}"
configuration-path: .github/project-pr-labeler.yml

View File

@ -5,31 +5,51 @@ on:
- trunk - trunk
types: types:
- labeled - labeled
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs: jobs:
prcheck: prcheck:
name: Smoke test a pull request. name: Smoke test a pull request.
if: "${{ contains(github.event.label.name, 'run: smoke tests') }}" if: "${{ contains(github.event.label.name, 'run: smoke tests') }}"
runs-on: ubuntu-18.04 runs-on: ubuntu-20.04
steps: steps:
- name: Create dirs. - name: Create dirs.
run: | run: |
mkdir -p code/woocommerce mkdir -p code/woocommerce
mkdir -p package/woocommerce mkdir -p package/woocommerce
mkdir -p tmp/woocommerce mkdir -p tmp/woocommerce
mkdir -p node_modules
- name: Checkout code. - name: Checkout code.
uses: actions/checkout@v2 uses: actions/checkout@v3
with: with:
path: package/woocommerce path: package/woocommerce
- name: Get cached composer and pnpm directories
uses: actions/cache@v3
id: cache-deps
with:
path: |
~/.pnpm-store
package/woocommerce/plugins/woocommerce/packages
package/woocommerce/plugins/woocommerce/**/vendor
key: ${{ runner.os }}-smoke-test-npm-composer-${{ hashFiles('**/composer.lock', '**/pnpm-lock.yaml') }}
- name: Install PNPM
run: npm install -g pnpm
- name: Install dependencies
run: pnpm install
- name: Install Composer dependencies
if: steps.cache-deps.outputs.cache-hit != 'true'
run: pnpm nx composer-install-no-dev woocommerce
- name: Install prerequisites. - name: Install prerequisites.
working-directory: package/woocommerce/plugins/woocommerce working-directory: package/woocommerce/plugins/woocommerce
id: installation id: installation
run: | run: |
npm install -g pnpm
pnpm install
pnpm nx composer-install-no-dev woocommerce
pnpm nx build-assets woocommerce pnpm nx build-assets woocommerce
pnpm install jest pnpm install jest
@ -50,7 +70,7 @@ jobs:
UPDATE_WC: 1 UPDATE_WC: 1
DEFAULT_TIMEOUT_OVERRIDE: 120000 DEFAULT_TIMEOUT_OVERRIDE: 120000
run: | run: |
pnpx wc-e2e test:e2e tests/e2e/specs/smoke-tests/update-woocommerce.js pnpm exec wc-e2e test:e2e tests/e2e/specs/smoke-tests/update-woocommerce.js
- name: Post Smoke tests results comment on PR - name: Post Smoke tests results comment on PR
if: always() if: always()
@ -81,7 +101,7 @@ jobs:
UPDATE_WC: 1 UPDATE_WC: 1
DEFAULT_TIMEOUT_OVERRIDE: 120000 DEFAULT_TIMEOUT_OVERRIDE: 120000
run: | run: |
pnpx wc-e2e test:e2e pnpm exec wc-e2e test:e2e
- name: Post E2E tests results comment on PR - name: Post E2E tests results comment on PR
if: always() if: always()

View File

@ -4,24 +4,27 @@ on:
defaults: defaults:
run: run:
shell: bash shell: bash
working-directory: plugins/woocommerce concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs: jobs:
test: test:
name: PHP ${{ matrix.php }} WP ${{ matrix.wp }} name: PHP ${{ matrix.php }} WP ${{ matrix.wp }}
timeout-minutes: 15 timeout-minutes: 20
runs-on: ubuntu-latest runs-on: ubuntu-20.04
continue-on-error: ${{ matrix.wp == 'nightly' }} continue-on-error: ${{ matrix.wp == 'nightly' }}
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
php: [ '7.0', '7.1', '7.2', '7.3', '7.4', '8.0' ] php: [ '7.2', '7.3', '7.4', '8.0' ]
wp: [ "latest" ] wp: [ "latest" ]
include: include:
- wp: nightly - wp: nightly
php: '7.4' php: '7.4'
- wp: '5.7' - wp: '5.8'
php: 7.2 php: 7.2
- wp: '5.6' - wp: '5.7'
php: 7.2 php: 7.2
services: services:
database: database:
@ -48,33 +51,45 @@ jobs:
php --version php --version
composer --version composer --version
- name: Get cached composer directories - name: Get cached composer and pnpm directories
uses: actions/cache@v2 uses: actions/cache@v3
id: cache-deps
with: with:
path: | path: |
./packages ~/.pnpm-store
./vendor plugins/woocommerce/packages
key: ${{ runner.os }}-${{ hashFiles('./composer.lock') }} plugins/woocommerce/**/vendor
key: ${{ runner.os }}-npm-composer-${{ hashFiles('**/composer.lock', '**/pnpm-lock.yaml') }}
- name: Install PNPM and install dependencies - name: Install PNPM
run: npm install -g pnpm
- name: Install dependencies
run: pnpm install
- name: Install Composer dependencies
if: steps.cache-deps.outputs.cache-hit != 'true'
run: pnpm nx composer-install woocommerce
- name: Build Admin feature config
run: | run: |
npm install -g pnpm pnpm nx build:feature-config woocommerce
pnpm install
pnpm nx composer-install woocommerce
- name: Add PHP8 Compatibility. - name: Add PHP8 Compatibility.
run: | run: |
if [ "$(php -r "echo version_compare(PHP_VERSION,'8.0','>=');")" ]; then if [ "$(php -r "echo version_compare(PHP_VERSION,'8.0','>=');")" ]; then
cd plugins/woocommerce
curl -L https://github.com/woocommerce/phpunit/archive/add-compatibility-with-php8-to-phpunit-7.zip -o /tmp/phpunit-7.5-fork.zip curl -L https://github.com/woocommerce/phpunit/archive/add-compatibility-with-php8-to-phpunit-7.zip -o /tmp/phpunit-7.5-fork.zip
unzip -d /tmp/phpunit-7.5-fork /tmp/phpunit-7.5-fork.zip unzip -d /tmp/phpunit-7.5-fork /tmp/phpunit-7.5-fork.zip
composer bin phpunit config --unset platform composer bin phpunit config --unset platform
composer bin phpunit config repositories.0 '{"type": "path", "url": "/tmp/phpunit-7.5-fork/phpunit-add-compatibility-with-php8-to-phpunit-7", "options": {"symlink": false}}' composer bin phpunit config repositories.0 '{"type": "path", "url": "/tmp/phpunit-7.5-fork/phpunit-add-compatibility-with-php8-to-phpunit-7", "options": {"symlink": false}}'
composer bin phpunit require --dev -W phpunit/phpunit:@dev --ignore-platform-reqs composer bin phpunit require --dev -W phpunit/phpunit:@dev --ignore-platform-reqs
rm -rf ./vendor/phpunit/ rm -rf ./vendor/phpunit/
composer dump-autoload pnpm nx composer-dump-autoload woocommerce
fi fi
- name: Init DB and WP - name: Init DB and WP
working-directory: plugins/woocommerce
run: ./tests/bin/install.sh woo_test root root 127.0.0.1 ${{ matrix.wp }} run: ./tests/bin/install.sh woo_test root root 127.0.0.1 ${{ matrix.wp }}
- name: Run tests - name: Run tests

View File

@ -7,7 +7,7 @@ jobs:
process-pull-request-after-merge: process-pull-request-after-merge:
name: "Process a pull request after it's merged" name: "Process a pull request after it's merged"
if: github.event.pull_request.merged == true if: github.event.pull_request.merged == true
runs-on: ubuntu-latest runs-on: ubuntu-20.04
steps: steps:
- name: "Get the action scripts" - name: "Get the action scripts"
run: | run: |
@ -30,12 +30,16 @@ jobs:
with: with:
php-version: '7.4' php-version: '7.4'
- name: "Run the script to assign a milestone" - name: "Run the script to assign a milestone"
if: "!github.event.pull_request.milestone" if: |
contains(github.event.pull_request.labels.*.name, 'plugin: woocommerce') &&
!github.event.pull_request.milestone &&
github.event.pull_request.base.ref == 'trunk'
run: php assign-milestone-to-merged-pr.php run: php assign-milestone-to-merged-pr.php
env: env:
PULL_REQUEST_ID: ${{ github.event.pull_request.node_id }} PULL_REQUEST_ID: ${{ github.event.pull_request.node_id }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: "Run the script to post a comment with next steps hint" - name: "Run the script to post a comment with next steps hint"
if: "contains(github.event.pull_request.labels.*.name, 'plugin: woocommerce')"
run: php add-post-merge-comment.php run: php add-post-merge-comment.php
env: env:
PULL_REQUEST_ID: ${{ github.event.pull_request.node_id }} PULL_REQUEST_ID: ${{ github.event.pull_request.node_id }}

View File

@ -0,0 +1,35 @@
name: "Enforce release code freeze"
on:
schedule:
- cron: '0 16 * * 4' # Run at 1600 UTC on Thursdays.
jobs:
maybe-create-next-milestone-and-release-branch:
name: "Maybe create next milestone and release branch"
runs-on: ubuntu-20.04
steps:
- name: "Get the action script"
run: |
scripts="post-request-shared.php release-code-freeze.php"
for script in $scripts
do
curl \
--silent \
--fail \
--header 'Authorization: bearer ${{ secrets.GITHUB_TOKEN }}' \
--header 'User-Agent: GitHub action to enforce release code freeze' \
--header 'Accept: application/vnd.github.v3.raw' \
--output $script \
--location "$GITHUB_API_URL/repos/${{ github.repository }}/contents/.github/workflows/scripts/$script?ref=$GITHUB_REF"
done
env:
GITHUB_API_URL: ${{ env.GITHUB_API_URL }}
GITHUB_REF: ${{ env.GITHUB_REF }}
- name: "Install PHP"
uses: shivammathur/setup-php@v2
with:
php-version: '7.4'
- name: "Run the script to enforce the code freeze"
run: php release-code-freeze.php
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@ -57,8 +57,7 @@ echo "The pull request was merged by: $merger_user_name\n";
$comment_body = "Hi @$merger_user_name, thanks for merging this pull request. Please take a look at these follow-up tasks you may need to perform: $comment_body = "Hi @$merger_user_name, thanks for merging this pull request. Please take a look at these follow-up tasks you may need to perform:
- [ ] Add the `status: needs changelog` label - [ ] Add the `release: add testing instructions` label";
- [ ] Add the `status: needs testing instructions` label";
$add_comment_mutation = " $add_comment_mutation = "
addComment(input: {subjectId: \"$pr_id\", body: \"$comment_body\", clientMutationId: \"$github_token\"}) { addComment(input: {subjectId: \"$pr_id\", body: \"$comment_body\", clientMutationId: \"$github_token\"}) {

View File

@ -9,73 +9,7 @@
require_once __DIR__ . '/post-request-shared.php'; require_once __DIR__ . '/post-request-shared.php';
/* $chosen_milestone = get_latest_milestone_from_api( true );
* Select the milestone to be added:
*
* 1. Get the first 10 milestones sorted by creation date descending.
* (we'll never have more than 2 or 3 active milestones but let's get 10 to be sure).
* 2. Discard those not open or whose title is not a proper version number ("X.Y.Z").
* 3. Sort descending using version_compare.
* 4. Get the oldest one that does not have a corresponding "release/X.Y" branch.
*/
echo "Getting the list of milestones...\n";
$query = "
repository(owner:\"$repo_owner\", name:\"$repo_name\") {
milestones(first: 10, states: [OPEN], orderBy: {field: CREATED_AT, direction: DESC}) {
nodes {
id
title
state
}
}
}
";
$json = do_graphql_api_request( $query );
$milestones = $json['data']['repository']['milestones']['nodes'];
$milestones = array_map(
function( $x ) {
return 1 === preg_match( '/^\d+\.\d+\.\d+$/D', $x['title'] ) ? $x : null;
},
$milestones
);
$milestones = array_filter( $milestones );
usort(
$milestones,
function( $a, $b ) {
return version_compare( $b['title'], $a['title'] );
}
);
echo 'Latest open milestone: ' . $milestones[0]['title'] . "\n";
$chosen_milestone = null;
foreach ( $milestones as $milestone ) {
$milestone_title_parts = explode( '.', $milestone['title'] );
$milestone_release_branch = 'release/' . $milestone_title_parts[0] . '.' . $milestone_title_parts[1];
$query = "
repository(owner:\"$repo_owner\", name:\"$repo_name\") {
ref(qualifiedName: \"refs/heads/$milestone_release_branch\") {
id
}
}
";
$result = do_graphql_api_request( $query );
if ( is_null( $result['data']['repository']['ref'] ) ) {
$chosen_milestone = $milestone;
} else {
break;
}
}
// If all the milestones have a release branch, just take the newest one.
if ( is_null( $chosen_milestone ) ) {
echo "WARNING: No milestone without release branch found, the newest one will be assigned.\n";
$chosen_milestone = $milestones[0];
}
echo 'Milestone that will be assigned: ' . $chosen_milestone['title'] . "\n"; echo 'Milestone that will be assigned: ' . $chosen_milestone['title'] . "\n";

View File

@ -0,0 +1,54 @@
/**
* A script that fetches the asset id of a given release and sets it as the output for the step that calls it
*/
const https = require('https');
const options = {
hostname: 'api.github.com',
port: 443,
path: `/repos/${process.env.REPO}/releases/${process.env.RELEASE_ID}/assets`,
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${ process.env.GITHUB_TOKEN }`,
'User-Agent': 'WooCommerce Smoke Build'
},
};
/**
*
* @returns {Promise}
*/
const fetchAssetId = () => {
return new Promise( ( resolve, reject ) => {
const request = https.get( options, ( response ) => {
response.setEncoding('utf8');
let responseBody = '';
response.on( 'data', ( chunk ) => {
responseBody += chunk;
} );
response.on( 'end', () => {
const assets = JSON.parse( responseBody );
// use the most recently uploaded asset
resolve( assets[ assets.length - 1 ].id );
} );
} );
request.on('error', ( error ) => {
reject( error );
} );
request.end();
} );
}
module.exports = async ( { github, context, core } ) => {
const id = await fetchAssetId();
// set asset_id as the output
core.setOutput( 'asset_id', id );
}

View File

@ -19,9 +19,237 @@ $repo_name = $repo_parts[1];
$pr_id = getenv( 'PULL_REQUEST_ID' ); $pr_id = getenv( 'PULL_REQUEST_ID' );
$github_token = getenv( 'GITHUB_TOKEN' ); $github_token = getenv( 'GITHUB_TOKEN' );
$github_api_url = getenv( 'GITHUB_API_URL' );
$graphql_api_url = getenv( 'GITHUB_GRAPHQL_URL' ); $graphql_api_url = getenv( 'GITHUB_GRAPHQL_URL' );
/**
* Function to get the latest milestone.
*
* @param bool $use_latest_when_null When true, the function returns the latest milestone regardless of release branch status.
* @return string The title of the latest milestone.
*/
function get_latest_milestone_from_api( $use_latest_when_null = false ) {
global $repo_owner, $repo_name;
echo 'Getting the list of milestones...' . PHP_EOL;
$query = "
repository(owner:\"$repo_owner\", name:\"$repo_name\") {
milestones(first: 10, states: [OPEN], orderBy: {field: CREATED_AT, direction: DESC}) {
nodes {
id
title
state
}
}
}
";
$json = do_graphql_api_request( $query );
$milestones = $json['data']['repository']['milestones']['nodes'];
$milestones = array_map(
function( $x ) {
return 1 === preg_match( '/^\d+\.\d+\.\d+$/D', $x['title'] ) ? $x : null;
},
$milestones
);
$milestones = array_filter( $milestones );
usort(
$milestones,
function( $a, $b ) {
return version_compare( $b['title'], $a['title'] );
}
);
echo 'Latest open milestone: ' . $milestones[0]['title'] . PHP_EOL;
$chosen_milestone = null;
foreach ( $milestones as $milestone ) {
$milestone_title_parts = explode( '.', $milestone['title'] );
$milestone_release_branch = 'release/' . $milestone_title_parts[0] . '.' . $milestone_title_parts[1];
$query = "
repository(owner:\"$repo_owner\", name:\"$repo_name\") {
ref(qualifiedName: \"refs/heads/$milestone_release_branch\") {
id
}
}
";
$result = do_graphql_api_request( $query );
if ( is_null( $result['data']['repository']['ref'] ) ) {
$chosen_milestone = $milestone;
} else {
break;
}
}
// If all the milestones have a release branch, just take the newest one.
if ( $use_latest_when_null && is_null( $chosen_milestone ) ) {
echo 'WARNING: No milestone without release branch found, the newest one will be assigned.' . PHP_EOL;
$chosen_milestone = $milestones[0];
}
return $chosen_milestone;
}
/**
* Function to get the last major.minor version with a release from the API.
*
* @return string Returns the latest version with a release formatted as "major.minor".
*/
function get_latest_version_with_release() {
global $repo_owner, $repo_name;
echo 'Getting the list of releases...' . PHP_EOL;
$query = "
repository(owner:\"$repo_owner\", name:\"$repo_name\") {
releases(first: 25, orderBy: { field: CREATED_AT, direction: DESC}) {
nodes {
tagName
}
}
}
";
$json = do_graphql_api_request( $query );
$releases = $json['data']['repository']['releases']['nodes'];
$releases = array_map(
function( $x ) {
return 1 === preg_match( '/^\d+\.\d+\.\d+/D', $x['tagName'] ) ? $x : null;
},
$releases
);
$releases = array_filter( $releases );
usort(
$releases,
function( $a, $b ) {
return version_compare( $b['tagName'], $a['tagName'] );
}
);
$major_minor = preg_replace( '/(^\d+\.\d+).*?$/', '\1', $releases[0]['tagName'] );
echo 'Most recent version with a release: ' . $major_minor . PHP_EOL;
return $major_minor;
}
/**
* Function to retreive the sha1 reference for a given branch name.
*
* @param string $branch The name of the branch.
* @return string Returns the name of the branch, or a falsey value on error.
*/
function get_ref_from_branch( $branch ) {
global $repo_owner, $repo_name;
$query = "
repository(owner:\"$repo_owner\", name:\"$repo_name\") {
ref(qualifiedName: \"refs/heads/{$branch}\") {
target {
... on Commit {
history(first: 1) {
edges{ node{ oid } }
}
}
}
}
}
";
$result = do_graphql_api_request( $query );
// Warnings suppressed here because traversing this level of arrays with isset / is_array checks would be messy.
return @$result['data']['repository']['ref']['target']['history']['edges'][0]['node']['oid'];
}
/**
* Function to create milestone using the GitHub REST API.
*
* @param string $title The title of the milestone to be created.
* @return bool True on success, False otherwise.
*/
function create_github_milestone( $title ) {
global $repo_owner, $repo_name;
$result = do_github_api_post_request( "/repos/{$repo_owner}/{$repo_name}/milestones", array(
'title' => $title,
) );
return is_array( $result ) && $result['title'] === $title;
}
/**
* Function to create branch using the GitHub REST API.
*
* @param string $branch The branch to be created.
* @param string $sha The sha1 reference for the branch.
* @return bool True on success, False otherwise.
*/
function create_github_branch( $branch, $sha ) {
global $repo_owner, $repo_name;
$ref = "refs/heads/{$branch}";
$result = do_github_api_post_request( "/repos/{$repo_owner}/{$repo_name}/git/refs", array(
'ref' => $ref,
'sha' => $sha,
) );
return is_array( $result ) && $result['ref'] === $ref;
}
/**
* Function to create branch using the GitHub REST API from an existing branch.
*
* @param string $source The branch from which to create.
* @param string $target The branch to be created.
* @return bool True on success, False otherwise.
*/
function create_github_branch_from_branch( $source, $target ) {
$ref = get_ref_from_branch( $source );
if ( ! $ref ) {
return false;
}
return create_github_branch( $target, $ref );
}
/**
* Function to do a GitHub API POST Request.
*
* @param array $request_url
* @param array $body The body of the request to be json encoded.
* @return mixed The json-decoded response if a response is received, 'false' (or whatever file_get_contents returns) otherwise.
*/
function do_github_api_post_request( $request_path, $body ) {
global $github_token, $github_api_url, $github_api_response_code;
$context = stream_context_create(
array(
'http' => array(
'method' => 'POST',
'header' => array(
'Accept: application/vnd.github.v3+json',
'Content-Type: application/json',
'User-Agent: GitHub Actions for creation of milestones',
'Authorization: bearer ' . $github_token,
),
'content' => json_encode( $body ),
),
)
);
$full_request_url = rtrim( $github_api_url, '/' ) . '/' . ltrim( $request_path, '/' );
$result = @file_get_contents( $full_request_url, false, $context );
// Verify that the post request was sucessful.
$status_line = $http_response_header[0];
preg_match( "/^HTTPS?\/\d\.\d\s+(\d{3})\s+/i", $status_line, $matches );
$github_api_response_code = $matches[1];
if ( '2' !== substr( $github_api_response_code, 0, 1 ) ) {
return false;
}
return is_string( $result ) ? json_decode( $result, true ) : $result;
}
/** /**
* Function to query the GitHub GraphQL API. * Function to query the GitHub GraphQL API.
* *

View File

@ -0,0 +1,65 @@
<?php
/**
* Script to automatically enforce the release code freeze.
*
* @package WooCommerce/GithubActions
*/
require_once __DIR__ . '/post-request-shared.php';
$now = time();
if ( getenv( 'TIME_OVERRIDE' ) ) {
$now = strtotime( getenv( 'TIME_OVERRIDE' ) );
}
// Code freeze comes 26 days prior to release day.
$release_time = strtotime( '+26 days', $now );
$release_day_of_week = date( 'l', $release_time );
$release_day_of_month = (int) date( 'j', $release_time );
// If 26 days from now isn't the second Tuesday, then it's not code freeze day.
if ( 'Tuesday' !== $release_day_of_week || $release_day_of_month < 8 || $release_day_of_month > 14 ) {
echo 'Info: Today is not the Thursday of the code freeze.' . PHP_EOL;
return;
}
$latest_version_with_release = get_latest_version_with_release();
if ( empty( $latest_version_with_release ) ) {
echo '*** Error: Unable to get latest version with release' . PHP_EOL;
return;
}
// Because we go from 5.9 to 6.0, we can get the next major_minor by adding 0.1 and formatting appropriately.
$latest_float = (float) $latest_version_with_release;
$branch_major_minor = number_format( $latest_float + 0.1, 1 );
$milestone_major_minor = number_format( $latest_float + 0.2, 1 );
// We use those values to get the release branch and next milestones that we need to create.
$release_branch_to_create = "release/{$branch_major_minor}";
$milestone_to_create = "{$milestone_major_minor}.0";
if ( getenv( 'DRY_RUN' ) ) {
echo 'DRY RUN: Skipping actual creation of release branch and milestone...' . PHP_EOL;
echo "Release Branch: {$release_branch_to_create}" . PHP_EOL;
echo "Milestone: {$milestone_to_create}" . PHP_EOL;
return;
}
if ( create_github_milestone( $milestone_to_create ) ) {
echo "Created milestone {$milestone_to_create}" . PHP_EOL;
} else if ( '422' === $github_api_response_code ) {
// The milestone already existed when GitHub returns a 422 status.
echo "Notice: Unable to create {$milestone_to_create} milestone. Maybe it already exists? Skipping..." . PHP_EOL;
} else {
echo "*** Error: Unable to create {$milestone_to_create} milestone" . PHP_EOL;
}
if ( create_github_branch_from_branch( 'trunk', $release_branch_to_create ) ) {
echo "Created branch {$release_branch_to_create}" . PHP_EOL;
} else if ( '422' === $github_api_response_code ) {
// The release branch already existed when GitHub returns a 422 status.
echo "Notice: Unable to create {$release_branch_to_create} branch. Maybe it already exists? Skipping..." . PHP_EOL;
} else {
echo "*** Error: Unable to create {$release_branch_to_create}" . PHP_EOL;
}

View File

@ -0,0 +1,24 @@
name: Check daily smoke test site status.
on:
schedule:
- cron: '25 7 * * *'
jobs:
ping_site:
runs-on: ubuntu-20.04
name: Check site and notify if not found
steps:
- name: Check site status
id: sitecheck
uses: srt32/uptime@958231f4d95c117f08eb0fc70907e80d0dfedf2b
with:
url-to-hit: "${{ secrets.SMOKE_TEST_URL }}ready/"
expected-statuses: "200,301"
- name: Send message to Slack API
if: failure()
uses: archive/github-actions-slack@deecc2edc496dc642d643de1d7cf3a47f51fb27a
id: notify
with:
slack-bot-user-oauth-access-token: ${{ secrets.SMOKE_TEST_SLACK_TOKEN }}
slack-channel: ${{ secrets.SMOKE_TEST_SLACK_CHANNEL }}
slack-text: ':warning: <!subteam^${{ secrets.SMOKE_TEST_SLACK_GROUP }}> FYI the URL ${{ secrets.SMOKE_TEST_URL }}ready/ appears to be returning `404 not found` :x:'

View File

@ -5,9 +5,8 @@ on:
jobs: jobs:
login-run: login-run:
name: Daily smoke test on trunk. name: Daily smoke test on trunk.
runs-on: ubuntu-18.04 runs-on: ubuntu-20.04
steps: steps:
- name: Create dirs. - name: Create dirs.
run: | run: |
mkdir -p code/woocommerce mkdir -p code/woocommerce
@ -16,7 +15,7 @@ jobs:
mkdir -p node_modules mkdir -p node_modules
- name: Checkout code. - name: Checkout code.
uses: actions/checkout@v2 uses: actions/checkout@v3
with: with:
path: package/woocommerce path: package/woocommerce
ref: trunk ref: trunk
@ -42,23 +41,23 @@ jobs:
WC_E2E_SCREENSHOTS: 1 WC_E2E_SCREENSHOTS: 1
E2E_RETEST: 1 E2E_RETEST: 1
E2E_SLACK_TOKEN: ${{ secrets.SMOKE_TEST_SLACK_TOKEN }} E2E_SLACK_TOKEN: ${{ secrets.SMOKE_TEST_SLACK_TOKEN }}
E2E_SLACK_CHANNEL: ${{ secrets.SMOKE_TEST_SLACK_CHANNEL }} E2E_SLACK_CHANNEL: 'C01U0H617MY'
UPDATE_WC: 1 UPDATE_WC: 1
DEFAULT_TIMEOUT_OVERRIDE: 120000 DEFAULT_TIMEOUT_OVERRIDE: 120000
BASE_URL: ${{ secrets.SMOKE_TEST_URL }} BASE_URL: ${{ secrets.SMOKE_TEST_URL }}
USER_KEY: ${{ secrets.SMOKE_TEST_ADMIN_USER }} USER_KEY: ${{ secrets.SMOKE_TEST_ADMIN_USER }}
USER_SECRET: ${{ secrets.SMOKE_TEST_ADMIN_PASSWORD }} USER_SECRET: ${{ secrets.SMOKE_TEST_ADMIN_PASSWORD }}
run: | run: |
pnpx wc-e2e test:e2e tests/e2e/specs/smoke-tests/update-woocommerce.js pnpm exec wc-e2e test:e2e tests/e2e/specs/smoke-tests/update-woocommerce.js
pnpx wc-e2e test:e2e pnpm exec wc-e2e test:e2e
pnpx wc-api-tests test api pnpm exec wc-api-tests test api
build: build:
name: Build zip for PR name: Build zip for PR
runs-on: ubuntu-latest runs-on: ubuntu-20.04
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v2 uses: actions/checkout@v3
- name: Build - name: Build
id: build id: build
@ -67,7 +66,7 @@ jobs:
BUILD_ENV: e2e BUILD_ENV: e2e
- name: Upload PR zip - name: Upload PR zip
uses: actions/upload-artifact@v2 uses: actions/upload-artifact@v3
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with: with:
@ -77,7 +76,7 @@ jobs:
test-plugins: test-plugins:
name: Smoke tests with ${{ matrix.plugin }} plugin installed name: Smoke tests with ${{ matrix.plugin }} plugin installed
runs-on: ubuntu-18.04 runs-on: ubuntu-20.04
needs: [build] needs: [build]
strategy: strategy:
fail-fast: false fail-fast: false
@ -117,13 +116,13 @@ jobs:
- name: Load docker images and start containers. - name: Load docker images and start containers.
working-directory: package/woocommerce/plugins/woocommerce working-directory: package/woocommerce/plugins/woocommerce
run: pnpx wc-e2e docker:up run: pnpm exec wc-e2e docker:up
- name: Move current directory to code. We will install zip file in this dir later. - name: Move current directory to code. We will install zip file in this dir later.
run: mv ./package/woocommerce/plugins/woocommerce/* ./code/woocommerce run: mv ./package/woocommerce/plugins/woocommerce/* ./code/woocommerce
- name: Download WooCommerce ZIP. - name: Download WooCommerce ZIP.
uses: actions/download-artifact@v2 uses: actions/download-artifact@v3
with: with:
name: woocommerce name: woocommerce
path: tmp path: tmp
@ -150,5 +149,5 @@ jobs:
PLUGIN_NAME: ${{ matrix.plugin }} PLUGIN_NAME: ${{ matrix.plugin }}
GITHUB_TOKEN: ${{ secrets.E2E_GH_TOKEN }} GITHUB_TOKEN: ${{ secrets.E2E_GH_TOKEN }}
run: | run: |
pnpx wc-e2e test:e2e tests/e2e/specs/smoke-tests/upload-plugin.js pnpm exec wc-e2e test:e2e tests/e2e/specs/smoke-tests/upload-plugin.js
pnpx wc-e2e test:e2e pnpm exec wc-e2e test:e2e

View File

@ -1,13 +1,15 @@
name: Smoke test release name: Smoke test release
on: on:
release: workflow_dispatch:
types: [published] inputs:
release_id:
description: 'WooCommerce Release Id'
required: true
jobs: jobs:
login-run: login-run:
name: Daily smoke test on release. name: Daily smoke test on release.
runs-on: ubuntu-18.04 runs-on: ubuntu-20.04
steps: steps:
- name: Create dirs. - name: Create dirs.
run: | run: |
mkdir -p code/woocommerce mkdir -p code/woocommerce
@ -16,7 +18,7 @@ jobs:
mkdir -p node_modules mkdir -p node_modules
- name: Checkout code. - name: Checkout code.
uses: actions/checkout@v2 uses: actions/checkout@v3
with: with:
path: package/woocommerce path: package/woocommerce
ref: trunk ref: trunk
@ -42,7 +44,7 @@ jobs:
WC_E2E_SCREENSHOTS: 1 WC_E2E_SCREENSHOTS: 1
E2E_RETEST: 1 E2E_RETEST: 1
E2E_SLACK_TOKEN: ${{ secrets.SMOKE_TEST_SLACK_TOKEN }} E2E_SLACK_TOKEN: ${{ secrets.SMOKE_TEST_SLACK_TOKEN }}
E2E_SLACK_CHANNEL: ${{ secrets.RELEASE_TEST_SLACK_CHANNEL }} E2E_SLACK_CHANNEL: 'C02DS4NE72S'
TEST_RELEASE: 1 TEST_RELEASE: 1
UPDATE_WC: 1 UPDATE_WC: 1
DEFAULT_TIMEOUT_OVERRIDE: 120000 DEFAULT_TIMEOUT_OVERRIDE: 120000
@ -50,17 +52,16 @@ jobs:
USER_KEY: ${{ secrets.RELEASE_TEST_ADMIN_USER }} USER_KEY: ${{ secrets.RELEASE_TEST_ADMIN_USER }}
USER_SECRET: ${{ secrets.RELEASE_TEST_ADMIN_PASSWORD }} USER_SECRET: ${{ secrets.RELEASE_TEST_ADMIN_PASSWORD }}
run: | run: |
pnpx wc-e2e test:e2e tests/e2e/specs/smoke-tests/update-woocommerce.js pnpm exec wc-e2e test:e2e tests/e2e/specs/smoke-tests/update-woocommerce.js
pnpx wc-e2e test:e2e pnpm exec wc-e2e test:e2e
pnpx wc-api-tests test api pnpm exec wc-api-tests test api
test-wp-version: test-wp-version:
name: Smoke test on L-${{ matrix.wp }} WordPress version name: Smoke test on L-${{ matrix.wp }} WordPress version
runs-on: ubuntu-18.04 runs-on: ubuntu-20.04
strategy: strategy:
matrix: matrix:
wp: ['1', '2'] wp: ['1', '2']
steps: steps:
- name: Create dirs. - name: Create dirs.
run: | run: |
mkdir -p code/woocommerce mkdir -p code/woocommerce
@ -69,9 +70,20 @@ jobs:
mkdir -p node_modules mkdir -p node_modules
- name: Checkout code. - name: Checkout code.
uses: actions/checkout@v2 uses: actions/checkout@v3
with: with:
path: package/woocommerce path: package/woocommerce
- name: Fetch Asset ID
id: fetch_asset_id
uses: actions/github-script@v5
env:
RELEASE_ID: ${{ github.event.inputs.release_id }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
REPO: ${{ github.repository }}
with:
script: |
const script = require( './package/woocommerce/.github/workflows/scripts/fetch-asset-id.js' )
await script({github, context, core})
- name: Install PNPM and install dependencies - name: Install PNPM and install dependencies
working-directory: package/woocommerce working-directory: package/woocommerce
@ -91,9 +103,7 @@ jobs:
- name: Download WooCommerce release zip - name: Download WooCommerce release zip
working-directory: tmp working-directory: tmp
run: | run: |
ASSET_ID=$(jq ".release.assets[0].id" $GITHUB_EVENT_PATH) curl https://api.github.com/repos/${{ github.repository }}/releases/assets/${{ steps.fetch_asset_id.outputs.asset_id }} -LJOH 'Accept: application/octet-stream'
curl https://api.github.com/repos/woocommerce/woocommerce/releases/assets/${ASSET_ID} -LJOH 'Accept: application/octet-stream'
unzip woocommerce.zip -d woocommerce unzip woocommerce.zip -d woocommerce
mv woocommerce/woocommerce/* ../package/woocommerce/plugins/woocommerce/ mv woocommerce/woocommerce/* ../package/woocommerce/plugins/woocommerce/
@ -108,7 +118,7 @@ jobs:
test-plugins: test-plugins:
name: Smoke tests with ${{ matrix.plugin }} plugin installed name: Smoke tests with ${{ matrix.plugin }} plugin installed
runs-on: ubuntu-18.04 runs-on: ubuntu-20.04
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
@ -138,6 +148,17 @@ jobs:
uses: actions/checkout@v2 uses: actions/checkout@v2
with: with:
path: package/woocommerce path: package/woocommerce
- name: Fetch Asset ID
id: fetch_asset_id
uses: actions/github-script@v5
env:
RELEASE_ID: ${{ github.event.inputs.release_id }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
REPO: ${{ github.repository }}
with:
script: |
const script = require( './package/woocommerce/.github/workflows/scripts/fetch-asset-id.js' )
await script({github, context, core})
- name: Install PNPM and install dependencies - name: Install PNPM and install dependencies
working-directory: package/woocommerce working-directory: package/woocommerce
@ -157,9 +178,7 @@ jobs:
- name: Download WooCommerce release zip - name: Download WooCommerce release zip
working-directory: tmp working-directory: tmp
run: | run: |
ASSET_ID=$(jq ".release.assets[0].id" $GITHUB_EVENT_PATH) curl https://api.github.com/repos/${{ github.repository }}/releases/assets/${{ steps.fetch_asset_id.outputs.asset_id }} -LJOH 'Accept: application/octet-stream'
curl https://api.github.com/repos/woocommerce/woocommerce/releases/assets/${ASSET_ID} -LJOH 'Accept: application/octet-stream'
unzip woocommerce.zip -d woocommerce unzip woocommerce.zip -d woocommerce
mv woocommerce/woocommerce/* ../package/woocommerce/plugins/woocommerce/ mv woocommerce/woocommerce/* ../package/woocommerce/plugins/woocommerce/
@ -174,5 +193,5 @@ jobs:
PLUGIN_NAME: ${{ matrix.plugin }} PLUGIN_NAME: ${{ matrix.plugin }}
GITHUB_TOKEN: ${{ secrets.E2E_GH_TOKEN }} GITHUB_TOKEN: ${{ secrets.E2E_GH_TOKEN }}
run: | run: |
pnpx wc-e2e test:e2e tests/e2e/specs/smoke-tests/upload-plugin.js pnpm exec wc-e2e test:e2e tests/e2e/specs/smoke-tests/upload-plugin.js
pnpx wc-e2e test:e2e pnpm exec wc-e2e test:e2e

View File

@ -6,17 +6,19 @@ on:
jobs: jobs:
stale: stale:
if: | if: |
! contains(github.event.issue.labels.*.name, 'enhancement') ! contains(github.event.issue.labels.*.name, 'type: enhancement')
runs-on: ubuntu-latest runs-on: ubuntu-20.04
steps: steps:
- uses: actions/stale@v3 - uses: actions/stale@v3
with: with:
repo-token: ${{ secrets.GITHUB_TOKEN }} repo-token: ${{ secrets.GITHUB_TOKEN }}
stale-issue-message: "As a part of this repositorys maintenance, this issue is being marked as stale due to inactivity. Please feel free to comment on it in case we missed something.\n\n###### After 7 days with no activity this issue will be automatically be closed." stale-issue-message: "As a part of this repository's maintenance, this issue is being marked as stale due to inactivity. Please feel free to comment on it in case we missed something.\n\n###### After 7 days with no activity this issue will be automatically be closed."
close-issue-message: 'This issue was closed because it has been 14 days with no activity.' close-issue-message: 'This issue was closed because it has been 14 days with no activity.'
days-before-issue-stale: 7 days-before-issue-stale: 7
days-before-issue-close: 7 days-before-issue-close: 7
days-before-pr-close: -1 days-before-pr-close: -1
only-issue-labels: 'needs feedback' stale-issue-label: 'status: stale'
close-issue-label: "category: can't reproduce" stale-pr-label: 'status: stale'
only-issue-labels: 'needs: author feedback'
close-issue-label: "status: can't reproduce"
ascending: true ascending: true

15
.github/workflows/triage-label.yml vendored Normal file
View File

@ -0,0 +1,15 @@
name: Add Triage Label
on:
issues:
types: opened
jobs:
add_label:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v3
- uses: actions-ecosystem/action-add-labels@v1
if: github.event.issue.labels[0] == null
with:
labels: 'status: awaiting triage'

View File

@ -5,8 +5,8 @@ on:
- labeled - labeled
jobs: jobs:
add-dev-comment: add-dev-comment:
if: "github.event.label.name == 'needs developer feedback'" if: "github.event.label.name == 'needs: developer feedback'"
runs-on: ubuntu-latest runs-on: ubuntu-20.04
permissions: permissions:
issues: write issues: write
steps: steps:
@ -25,8 +25,8 @@ jobs:
Please note it may take a few days for them to get to this issue. Thank you for your patience.' Please note it may take a few days for them to get to this issue. Thank you for your patience.'
}) })
add-reproduction-comment: add-reproduction-comment:
if: "github.event.label.name == 'status: needs reproduction'" if: "github.event.label.name == 'status: reproduction'"
runs-on: ubuntu-latest runs-on: ubuntu-20.04
permissions: permissions:
issues: write issues: write
steps: steps:
@ -45,7 +45,7 @@ jobs:
}) })
add-support-comment: add-support-comment:
if: "github.event.label.name == 'type: support request'" if: "github.event.label.name == 'type: support request'"
runs-on: ubuntu-latest runs-on: ubuntu-20.04
permissions: permissions:
issues: write issues: write
steps: steps:
@ -84,8 +84,8 @@ jobs:
state: 'closed' state: 'closed'
}) })
add-votes-comment: add-votes-comment:
if: "github.event.label.name == 'votes needed'" if: "github.event.label.name == 'needs: votes'"
runs-on: ubuntu-latest runs-on: ubuntu-20.04
permissions: permissions:
issues: write issues: write
steps: steps:
@ -98,14 +98,14 @@ jobs:
issue_number: context.issue.number, issue_number: context.issue.number,
owner: context.repo.owner, owner: context.repo.owner,
repo: context.repo.repo, repo: context.repo.repo,
body: 'Thanks for the suggestion @${{ github.event.issue.user.login }},\n\n\ body: "Thanks for the suggestion @${{ github.event.issue.user.login }},\n\n\
While we appreciate you sharing your ideas with us, it doesnt fit in with our current priorities for the project.\n\ While we appreciate you sharing your ideas with us, it doesn't fit in with our current priorities for the project.\n\
At some point, we may revisit our priorities and look through the list of suggestions like this one to see if it \ At some point, we may revisit our priorities and look through the list of suggestions like this one to see if it \
warrants a second look.\n\n\ warrants a second look.\n\n\
In the meantime, we are going to close this issue with the `votes needed` label and evaluate over time if this \ In the meantime, we are going to close this issue with the `votes needed` label and evaluate over time if this \
issue collects more feedback.\n\n\ issue collects more feedback.\n\n\
Dont be alarmed if you dont see any activity on this issue for a while. \ Don't be alarmed if you don't see any activity on this issue for a while. \
We'll keep an eye on the popularity of this request.' We'll keep an eye on the popularity of this request."
}) })
- name: Close votes needed issue - name: Close votes needed issue
uses: actions/github-script@v5 uses: actions/github-script@v5
@ -118,3 +118,49 @@ jobs:
issue_number: context.issue.number, issue_number: context.issue.number,
state: 'closed' state: 'closed'
}) })
fill-template-comment:
if: "github.event.label.name == 'needs: template'"
runs-on: ubuntu-20.04
permissions:
issues: write
steps:
- name: Add reply to fill template
uses: actions/github-script@v5
with:
github-token: ${{ secrets.WC_BOT_TRIAGE_TOKEN }}
script: |
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: "Hi @${{ github.event.issue.user.login }},\n\n\
Thank you for submitting the issue. However, you didn't fill out the details of the bug report template that we ask for. Without these details, we can't fully evaluate this issue. Please provide us with the information requested so we could take a look further.\n\n\
**Describe the bug**\n\n\
A clear and concise description of what the bug is. Please be as descriptive as possible; issues lacking detail, or for any other reason than to report a bug, may be closed without action.\n\n\
**To Reproduce**\n\n\
Steps to reproduce the behavior:\n\n\
1. Go to '...'\n\
2. Click on '....'\n\
3. Scroll down to '....'\n\
4. See error\n\n\
**Screenshots**\n\n\
If applicable, add screenshots to help explain your problem.\n\n\
**Expected behavior**\n\n\
A clear and concise description of what you expected to happen.\n\n\
**Isolating the problem (mark completed items with an [x]):**\n\n\
- [ ] I have deactivated other plugins and confirmed this bug occurs when only WooCommerce plugin is active.\n\
- [ ] This bug happens with a default WordPress theme active, or [Storefront](https://woocommerce.com/storefront/).\n\
- [ ] I can reproduce this bug consistently using the steps above.\n\n\
**WordPress Environment**\n\n\
Copy and paste the system status report from **WooCommerce > System Status** in WordPress admin."
})
- name: remove-needs-template-label
uses: actions-ecosystem/action-remove-labels@v1
with:
github-token: ${{ secrets.WC_BOT_TRIAGE_TOKEN }}
labels: 'needs: template'
- name: add-needs-author-feedback-label
uses: actions-ecosystem/action-add-labels@v1
with:
github-token: ${{ secrets.WC_BOT_TRIAGE_TOKEN }}
labels: 'needs: author feedback'

View File

@ -5,23 +5,24 @@ jobs:
feedback: feedback:
if: | if: |
github.actor != 'github-actions' && github.actor != 'github-actions' &&
github.actor == github.event.issue.user.login &&
github.event.issue && github.event.issue &&
github.event.issue.state == 'open' && github.event.issue.state == 'open' &&
contains(github.event.issue.labels.*.name, 'needs feedback') contains(github.event.issue.labels.*.name, 'needs: author feedback')
runs-on: ubuntu-latest runs-on: ubuntu-20.04
steps: steps:
- name: Add has feedback - name: Add has feedback
uses: actions-ecosystem/action-add-labels@v1 uses: actions-ecosystem/action-add-labels@v1
with: with:
github_token: ${{ secrets.GITHUB_TOKEN }} github_token: ${{ secrets.GITHUB_TOKEN }}
labels: 'has feedback' labels: 'needs: triage feedback'
- name: remove needs feedback - name: remove needs feedback
uses: actions-ecosystem/action-remove-labels@v1 uses: actions-ecosystem/action-remove-labels@v1
with: with:
github_token: ${{ secrets.GITHUB_TOKEN }} github_token: ${{ secrets.GITHUB_TOKEN }}
labels: 'needs feedback' labels: 'needs: author feedback'
- name: remove stale - name: remove stale
uses: actions-ecosystem/action-remove-labels@v1 uses: actions-ecosystem/action-remove-labels@v1
with: with:
github_token: ${{ secrets.GITHUB_TOKEN }} github_token: ${{ secrets.GITHUB_TOKEN }}
labels: Stale labels: 'status: stale'

12
.gitignore vendored
View File

@ -4,7 +4,7 @@ Thumbs.db
# IDE files # IDE files
.idea .idea
.vscode/ .vscode/*
project.xml project.xml
project.properties project.properties
.project .project
@ -13,6 +13,9 @@ project.properties
*.sublime-workspace *.sublime-workspace
.sublimelinterrc .sublimelinterrc
# Excluded IDE Files for developer experience tooling within workspace
!.vscode/tasks.json
# Grunt # Grunt
none none
@ -40,6 +43,7 @@ npm-debug.log
build/ build/
build-module/ build-module/
build-style/ build-style/
build-types/
dist/ dist/
# Project files # Project files
@ -73,3 +77,9 @@ nbproject/private/
# Test Results # Test Results
test-results.json test-results.json
# Admin Feature config
plugins/woocommerce/includes/react-admin/feature-config.php
# PHP lint
phpcs-report.xml

5
.husky/post-merge Executable file
View File

@ -0,0 +1,5 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
pnpm install
pnpm nx affected --target="composer-install" --base=ORIG_HEAD --head=HEAD

4
.husky/pre-commit Executable file
View File

@ -0,0 +1,4 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
pnpm exec lint-staged

4
.husky/pre-push Executable file
View File

@ -0,0 +1,4 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
./bin/pre-push.sh

29
.vscode/tasks.json vendored Normal file
View File

@ -0,0 +1,29 @@
{
"version": "2.0.0",
"tasks": [
{
"command": "pnpm tsc -b tsconfig.base.json",
"type": "shell",
"problemMatcher": [ "$tsc" ],
"label": "Typescript compile",
"detail": "Run tsc against tsconfig.base.json",
"runOptions": {
"runOn": "default"
}
},
{
"command": "pnpm tsc -b tsconfig.base.json --watch",
"type": "shell",
"problemMatcher": {
"base": "$tsc-watch",
"applyTo": "allDocuments"
},
"isBackground": true,
"label": "Incremental Typescript compile",
"detail": "Incremental background type checks",
"runOptions": {
"runOn": "folderOpen"
}
}
]
}

View File

@ -45,6 +45,21 @@ You might also want to run `pnpm start` to watch your CSS and JS changes if you
You're now ready to develop! You're now ready to develop!
### Typescript Checking
Typescript is progressively being implemented in this repository, and you might come across some files that are `.ts` or `.tsx`. By default, a VSCode environment will run type checking on such files that are currently open.
As of now, some parts of the codebase that were imported from the Woocommerce-Admin repository, into the `plugins/woocommerce-admin/client` directory, still fail Typescript checking. This has been scheduled on the team's backlog to be fixed.
In order to run type checking across the entire repository, you can run this command in your shell, from the root of this repository:
```sh
pnpm tsc -b tsconfig.base.json
```
For better developer experience, the folder `.vscode/tasks.json` has two VSCode tasks to run these commands automatically as well as to parse the output and highlight the errors in the `Problems` tab and in the file explorer pane. The first task runs it once, the second one runs it in the background upon saving of any modified files. This task is also automatically prompted by VSCode to be run upon opening the folder.
## Using Xdebug ## Using Xdebug
Please refer to [WP-ENV official README](https://github.com/WordPress/gutenberg/tree/master/packages/env#using-xdebug) section for setting up Xdebug. Please refer to [WP-ENV official README](https://github.com/WordPress/gutenberg/tree/master/packages/env#using-xdebug) section for setting up Xdebug.
@ -102,13 +117,12 @@ You can get the current MySQL port from the output of `wp-env start` command.
1. Open your choice of MySQL tool. 1. Open your choice of MySQL tool.
2. Use the following values to access the MySQL container. 2. Use the following values to access the MySQL container.
3. You can omit the username and password.
| Name | Value | | Name | Value |
| -------- | --------------------- | | -------- | --------------------- |
| Host | 127.0.0.1 | | Host | 127.0.0.1 |
| Username | | | Username | root |
| Password | | | Password | password |
| Port | Port from the command | | Port | Port from the command |
## HOWTOs ## HOWTOs

View File

@ -18,6 +18,7 @@ If you are not a developer, please use the [WooCommerce plugin page](https://wor
* [WooCommerce Developer Documentation](https://github.com/woocommerce/woocommerce/wiki) * [WooCommerce Developer Documentation](https://github.com/woocommerce/woocommerce/wiki)
* [WooCommerce Code Reference](https://docs.woocommerce.com/wc-apidocs/) * [WooCommerce Code Reference](https://docs.woocommerce.com/wc-apidocs/)
* [WooCommerce REST API Docs](https://woocommerce.github.io/woocommerce-rest-api-docs/) * [WooCommerce REST API Docs](https://woocommerce.github.io/woocommerce-rest-api-docs/)
* [Setting up a development environment](https://github.com/woocommerce/woocommerce/wiki/How-to-set-up-WooCommerce-development-environment)
## Reporting Security Issues ## Reporting Security Issues
To disclose a security issue to our team, [please submit a report via HackerOne here](https://hackerone.com/automattic/). To disclose a security issue to our team, [please submit a report via HackerOne here](https://hackerone.com/automattic/).

24
bin/pre-push.sh Executable file
View File

@ -0,0 +1,24 @@
#!/bin/sh
PROTECTED_BRANCH="trunk"
CURRENT_BRANCH=$(git branch --show-current)
if [ $PROTECTED_BRANCH = $CURRENT_BRANCH ]; then
if [ "$TERM" = "dumb" ]; then
>&2 echo "Sorry, you are unable to push to $PROTECTED_BRANCH using a GUI client! Please use git CLI."
exit 1
fi
printf "%sYou're about to push to $PROTECTED_BRANCH, is that what you intended? [y/N]: %s" "$(tput setaf 3)" "$(tput sgr0)"
read -r PROCEED < /dev/tty
echo
if [ "$(echo "${PROCEED:-n}" | tr "[:upper:]" "[:lower:]")" = "y" ]; then
echo "$(tput setaf 2)Brace yourself! Pushing to the $PROTECTED_BRANCH branch...$(tput sgr0)"
echo
exit 0
fi
echo "$(tput setaf 2)Push to $PROTECTED_BRANCH cancelled!$(tput sgr0)"
echo
exit 1
fi

View File

@ -1,5 +1,347 @@
== Changelog == == Changelog ==
= 6.4.1 2022-04-15 =
**WooCommerce**
- Revert - incorrect position value for registering menu pages. ([#32623](https://github.com/woocommerce/woocommerce/pull/32623))
**WooCommerce Blocks - 7.2.2**
- Fix - page load problem due to incorrect URL to certain assets. ([#6260](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/6260))
= 6.4.0 2022-04-12 =
**WooCommerce**
- Add - Scaffolding for the custom orders table feature. ([#31692](https://github.com/woocommerce/woocommerce/pull/31692))
- Add - Add DB table structure for custom order tables. ([#31811](https://github.com/woocommerce/woocommerce/pull/31811))
- Add - Primary key for the product attributes lookup table. ([#32067](https://github.com/woocommerce/woocommerce/pull/32067))
- Add - Tracks to the dashboard status widget and setup widget. ([#31857](https://github.com/woocommerce/woocommerce/pull/31857))
- Add - Check around setup widget display when features are disabled. ([#31884](https://github.com/woocommerce/woocommerce/pull/31884))
- Add - 'woocommerce_get_formatted_meta_data_include_all_meta_lines' filter hook. This can be used to control whether metadata lines are shown in the order meta box. ([#30948](https://github.com/woocommerce/woocommerce/pull/30948))
- Enhancement - Introduce rate_limit_remaining column in the wc_rate_limits table. ([#32041](https://github.com/woocommerce/woocommerce/pull/32041))
- Tweak - Update PayPal Standard JS used in the admin environment to avoid deprecated functionality. ([#32076](https://github.com/woocommerce/woocommerce/pull/32076))
- Tweak - Change level of escaping used to render the CSV import error log. ([#32000](https://github.com/woocommerce/woocommerce/pull/32000))
- Tweak - Make the payment_url field available via the REST API's orders endpoint. ([#31826](https://github.com/woocommerce/woocommerce/pull/31826))
- Tweak - Rename WC_API_Exception code woocommerce_api_cannot_edit_product_catgory into woocommerce_api_cannot_edit_product_category ([#31785](https://github.com/woocommerce/woocommerce/pull/31785))
- Tweak - Updated default email color to new Woo purple ([#30586](https://github.com/woocommerce/woocommerce/pull/30586))
- Fix - Avoid depending on the presence of a theme header template to clear the cart after payment is made. ([#31877](https://github.com/woocommerce/woocommerce/pull/31877))
- Fix - Payments tab tracking. ([#31844](https://github.com/woocommerce/woocommerce/pull/31844))
- Fix - Remove unnecessary duplicate style in email-styles template. ([#31860](https://github.com/woocommerce/woocommerce/pull/31860))
- Fix - incorrect position value for registering menu pages. ([#31779](https://github.com/woocommerce/woocommerce/pull/31779))
- Fix - SZL currency symbol. Updated from 'L' to 'E'. ([#30602](https://github.com/woocommerce/woocommerce/pull/30602))
- Fix - Removed execution of at least one hook ignoring the `woocommerce_load_webhooks_limit` filter value. ([#29002](https://github.com/woocommerce/woocommerce/pull/29002))
- Dev - Added has_options() to REST API v3 product endpoint response. ([#32031](https://github.com/woocommerce/woocommerce/pull/32031))
- Dev - Added woocommerce_admin_order_should_render_refunds hook to allow control over the refunds UI within the order editor. ([#31414](https://github.com/woocommerce/woocommerce/pull/31414))
**WooCommerce Admin - 3.3.0 & 3.3.1 & 3.3.2**
- Add - Add asynchronous plugin install and activation endpoints ([#8079](https://github.com/woocommerce/woocommerce-admin/pull/8079))
- Performance - Avoid expensive get_notes() queries in CouponPageMoved admin_init actions by using new Notes::get_note_by_name() helper method. ([#8202](https://github.com/woocommerce/woocommerce-admin/pull/8202))
- Enhancement - Add chart color filter for overriding default chart colors. ([#8258](https://github.com/woocommerce/woocommerce-admin/pull/8258))
- Enhancement - Added Typescript type declarations to build for @woocommerce/components ([#8282](https://github.com/woocommerce/woocommerce-admin/pull/8282))
- Enhancement - Increase color selection limit to ten and add additional colors. ([#8258](https://github.com/woocommerce/woocommerce-admin/pull/8258))
- Enhancement - Made @woocommerce/components/Stepper a Typescript file ([#8286](https://github.com/woocommerce/woocommerce-admin/pull/8286))
- Enhancement - Prompts a modal to save any unsaved changes when the users try to move to a different step ([#8278](https://github.com/woocommerce/woocommerce-admin/pull/8278))
- Tweak - OBW: Override Country/Region label line-height style to avoid truncated descenders. ([#8186](https://github.com/woocommerce/woocommerce-admin/pull/8186))
- Tweak - Show single success message for theme install and activation ([#8236](https://github.com/woocommerce/woocommerce-admin/pull/8236))
- Tweak - Use WC_VERSION as cache buster for assets ([#8308](https://github.com/woocommerce/woocommerce-admin/pull/8308))
- Update - Adjust time range and add an image for the Jetpack Backup note. ([#8293](https://github.com/woocommerce/woocommerce-admin/pull/8293))
- Update - Implement MailChimp API request threshold for MailchimpScheduler. ([#8342](https://github.com/woocommerce/woocommerce-admin/pull/8342))
- Update - Reintroduce CES on product add, product update, and order update. ([#8238](https://github.com/woocommerce/woocommerce-admin/pull/8238))
- Update - Replace mysql image with mariadb ([#8220](https://github.com/woocommerce/woocommerce-admin/pull/8220))
- Update - Update country support list for WooCommerce Payments Task. ([#8517](https://github.com/woocommerce/woocommerce-admin/pull/8517))
- Fix - Fix handling of paid themes in purchase task. ([#8493](https://github.com/woocommerce/woocommerce-admin/pull/8493))
- Fix - Make sure the paid extension task is also shown for themes. ([#8412](https://github.com/woocommerce/woocommerce-admin/pull/8412))
- Fix - Reintroduce emphasis on inbox note action button. ([#8411](https://github.com/woocommerce/woocommerce-admin/pull/8411))
- Fix - Remove class ExtendedPayments. ([#8461](https://github.com/woocommerce/woocommerce-admin/pull/8461))
- Fix - Added random IDs to SVG checkmarks in stepper component ([#8222](https://github.com/woocommerce/woocommerce-admin/pull/8222))
- Fix - Fix Google Listings plugin is always shown in free features despite already activated. ([#8330](https://github.com/woocommerce/woocommerce-admin/pull/8330))
- Fix - Fix hidden notes in `admin/notes` endpoint when the user is not in the tasklist experiment. ([#8328](https://github.com/woocommerce/woocommerce-admin/pull/8328))
- Fix - Fix missing product name in variation analytic page for the deleted products. ([#8255](https://github.com/woocommerce/woocommerce-admin/pull/8255))
- Fix - Fix payments extensions displayed below the offline payments options. ([#8232](https://github.com/woocommerce/woocommerce-admin/pull/8232))
- Fix - Fix setup wizard title and flash of content ([#8201](https://github.com/woocommerce/woocommerce-admin/pull/8201))
- Fix - Fix too many pending run_remote_notifications actions. ([#8285](https://github.com/woocommerce/woocommerce-admin/pull/8285))
- Fix - Fix view logic for Setup additional payment providers task. ([#8391](https://github.com/woocommerce/woocommerce-admin/pull/8391))
- Fix - OBW: fix copy on Business Details when "WooCommerce Shipping" is not listed ([#8324](https://github.com/woocommerce/woocommerce-admin/pull/8324))
- Fix - Only add product data on REST requests and task list ([#8235](https://github.com/woocommerce/woocommerce-admin/pull/8235))
- Fix - Stop showing actioned inbox items ([#8394](https://github.com/woocommerce/woocommerce-admin/pull/8394))
- Fix - WC Payments task is not visible after installing the plugin ([#8514](https://github.com/woocommerce/woocommerce-admin/pull/8514))
- Fix - PHP warning when default param is missing in payments spec. ([#8519](https://github.com/woocommerce/woocommerce-admin/pull/8519))
- Dev - Added a test for tracks event recording for PaymentGatewaySuggestions ([#8306](https://github.com/woocommerce/woocommerce-admin/pull/8306))
- Dev - Add README to hook reference generation script ([#8004](https://github.com/woocommerce/woocommerce-admin/pull/8004))
- Dev - Add reset WooCommerce functionality to E2E tests, so tests have a fresh state. ([#8219](https://github.com/woocommerce/woocommerce-admin/pull/8219))
- Dev - Enabled optional typescript checking on ./client subfolder ([#8372](https://github.com/woocommerce/woocommerce-admin/pull/8372))
- Dev - Fix formatting and add filter param for changelog types for the testing instructions script. ([#8256](https://github.com/woocommerce/woocommerce-admin/pull/8256))
- Dev - Refactor MerchantEmailNotifications ([#8304](https://github.com/woocommerce/woocommerce-admin/pull/8304))
- Dev - Remove preloaded countries from data endpoints and use data store instead. ([#8380](https://github.com/woocommerce/woocommerce-admin/pull/8380))
- Dev - Remove unused pre loaded setting data displaying all the routes. ([#8379](https://github.com/woocommerce/woocommerce-admin/pull/8379))
- Dev - Remove unused task styling classes ([#8234](https://github.com/woocommerce/woocommerce-admin/pull/8234))
- Dev - Update dependencies to support react 17 and drop support for IE11. ([#8305](https://github.com/woocommerce/woocommerce-admin/pull/8305))
- Dev - Update task list data structure to better handle new designs. ([#8332](https://github.com/woocommerce/woocommerce-admin/pull/8332))
**WooCommerce Blocks - 7.2.0 & 7.2.1**
- Enhancement - Add Global Styles support to the Product Price block. ([5950](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5950))
- Enhancement - Add Global Styles support to the Add To Cart Button block. ([5816](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5816))
- Enhancement - Store API - Introduced `wc/store/v1` namespace. ([5911](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5911))
- Enhancement - Renamed WooCommerce block templates to more e-commerce related names. ([5935](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5935))
- Enhancement - Featured Product block: Add the ability to reset to a previously set custom background image. ([5886](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5886))
- Enhancement - Add a remove image button to the WooCommerce Feature Category block. ([5719](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5719))
- Enhancement - Add support for the global style for the On-Sale Badge block. ([5565](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5565))
- Enhancement - Add support for the global style for the Attribute Filter block. ([5557](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5557))
- Enhancement - Category List block: Add support for global style. ([5516](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5516))
- Fix - Fixed typo in `wooocommerce_store_api_validate_add_to_cart` and `wooocommerce_store_api_validate_cart_item` hook names. ([5926](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5926))
- Fix - Fix loading WC core translations in locales where WC Blocks is not localized for some strings. ([5910](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5910))
- Fix - Fixed an issue where clear customizations functionality was not working for WooCommerce templates. ([5746](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5746))
- Fix - Fixed hover and focus states for button components. ([5712](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5712))
- Fix - Add to Cart button on Products listing blocks will respect the "Redirect to the cart page after successful addition" setting. ([5708](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5708))
- Fix - Fixes Twenty Twenty Two issues with sales price and added to cart "View Cart" call out styling in the "Products by Category" block. ([5684](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5684))
- Fix - StoreAPI: Clear all wc notice types in the cart validation context [#5983](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5983)
- Fix - Don't trigger class deprecations notices if headers are already sent [#6074](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/6074)
- Various - Remove v1 string from Store Keys. ([5987](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5987))
- Various - Introduce the `InvalidCartException` for handling cart validation. ([5904](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5904))
- Various - Renamed Store API custom headers to remove `X-WC-Store-API` prefixes. [#5983](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5983)
- Various - Normalised Store API error codes [#5992](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5992)
- Various - Deprecated `woocommerce_blocks_checkout_order_processed` in favour of `woocommerce_store_api_checkout_order_processed`
- Various - Deprecated `woocommerce_blocks_checkout_update_order_meta` in favour of `woocommerce_store_api_checkout_update_order_meta`
- Various - Deprecated `woocommerce_blocks_checkout_update_order_from_request` in favour of `woocommerce_store_api_checkout_update_order_from_request`
= 6.3.1 2022-03-10 =
**WooCommerce**
* Security - Address an issue with the PayPal Standard Payment Gateway. See https://developer.woocommerce.com/2022/03/10/woocommerce-3-5-10-6-3-1-security-releases/. ([#32057](https://github.com/woocommerce/woocommerce/pull/32057))
= 6.3.0 2022-03-08 =
**WooCommerce**
* Add - Add states for Germany. ([#31825](https://github.com/woocommerce/woocommerce/pull/31825))
* Add - Track WcPay settings in WC Tracker. ([#31663](https://github.com/woocommerce/woocommerce/pull/31663))
* Add - Add filter woocommerce_set_cookie_enabled to allow disabling specific cookies.([#31317](https://github.com/woocommerce/woocommerce/pull/31317))
* Tweak - Enhancement - Add indices to the product attributes lookup table. ([#31819](https://github.com/woocommerce/woocommerce/pull/31819))
* Tweak - Adjust input styles for the Twenty Twenty One theme. ([#31734](https://github.com/woocommerce/woocommerce/pull/31734))
* Tweak - Adjust input styles for the Twenty Twenty theme. ([#31698](https://github.com/woocommerce/woocommerce/pull/31698))
* Tweak - 2022 theme store notice styling. ([#31683](https://github.com/woocommerce/woocommerce/pull/31683))
* Tweak - Guatemalan postcode field is now visible by default but remains unrequired. ([#31303](https://github.com/woocommerce/woocommerce/pull/31303))
* Tweak - Ensure the WC_CSV_Batch_Exporter::get_percent_complete() method returns an int. ([#31138](https://github.com/woocommerce/woocommerce/pull/31138))
* Fix - Support custom Product Taxonomies in block template loader. ([#31610](https://github.com/woocommerce/woocommerce/pull/31610))
* Fix - Support special chars in email subjects using wp_specialchars_decode. ([#31589](https://github.com/woocommerce/woocommerce/pull/31589))
* Fix - Ensure that WooCommerce panel within the Customizer is showing a back button. ([#31508](https://github.com/woocommerce/woocommerce/pull/31508))
* Fix - Avoids incorrectly setting the search argument under some conditions, when the Customers endpoint of the REST API is used. ([#31295](https://github.com/woocommerce/woocommerce/pull/31295))
* Fix - Reverted #31593 that caused the returned line item price to be a string instead of a float. ([#31935](https://github.com/woocommerce/woocommerce/pull/31935))
* Fix - Add prefix to easy identify guest sessions.
* Dev - The cart session is now updated later on during the woocommerce_after_calculate_totals action (priority 1000, instead of priority 10 as previously). ([#31711](https://github.com/woocommerce/woocommerce/pull/31711))
* Dev - Enable browser-suggested passwords on checkout signup. ([#31701](https://github.com/woocommerce/woocommerce/pull/31701))
* Dev - Add method can_view_woocommerce_menu_item to check if the user can access the top-level WooCommerce menu item. ([#31689](https://github.com/woocommerce/woocommerce/pull/31689))
* Dev - Wrap terms and conditions required asterisk symbol with <abbr> tag. ([#31673](https://github.com/woocommerce/woocommerce/pull/31673))
* Dev - Allow to use use all get_image function parameters by woocommerce_get_product_thumbnail to customize image. ([#31605](https://github.com/woocommerce/woocommerce/pull/31605))
* Dev - Format price decimal places correctly in the order API. ([#31593](https://github.com/woocommerce/woocommerce/pull/31593))
* Dev - Update text for generating an account in admin menu to be more clear. ([#31590](https://github.com/woocommerce/woocommerce/pull/31590))
* Dev - Adds the option to filter coupons by status when calling the GET /coupons endpoint. ([#31577](https://github.com/woocommerce/woocommerce/pull/31577))
* Dev - Made the $loop position counter available via the 'woocommerce_variation_header' hook. ([#31565](https://github.com/woocommerce/woocommerce/pull/31565))
* Dev - Change '__return_true' to true in the apply_filters() call for the woocommerce_product_recount_terms filter. ([#31506](https://github.com/woocommerce/woocommerce/pull/31506))
* Dev - Add $key field as well to woocommerce_checkout_required_field_notice filter. ([#31435](https://github.com/woocommerce/woocommerce/pull/31435))
* Dev - Add product meta data to published product tracks. ([#31355](https://github.com/woocommerce/woocommerce/pull/31355))
* Dev - Allow auto-draft in API orders. ([#31290](https://github.com/woocommerce/woocommerce/pull/31290))
* Dev - A $file param is now available via the woocommerce_[product_]csv_importer_check_import_file_path filter hooks. ([#31266](https://github.com/woocommerce/woocommerce/pull/31266))
* Dev - Data migration to create and activate the product attributes lookup table. ([#31256](https://github.com/woocommerce/woocommerce/pull/31256))
* Dev - A new filter hook woocommerce_cart_item_removed_because_modified_message($message, $product) which allows to update the notice message if a product is modified and page is loaded while product is in cart. ([#31193](https://github.com/woocommerce/woocommerce/pull/31193))
* Security - Add prefix to identify guest sessions.
**WooCommerce Admin - 3.2.0 & 3.2.1**
* Fix - Adjusted task list logic to fix conflict between current and experimental task list. ([#8321](https://github.com/woocommerce/woocommerce-admin/pull/8321))
* Fix - changed email validation in Store Details onboarding task to more closely match PHP backend validation. ([#8197](https://github.com/woocommerce/woocommerce-admin/pull/8197))
* Fix - Disallow whitespace as the platform name input. ([#8090](https://github.com/woocommerce/woocommerce-admin/pull/8090))
* Fix - Ensure setup-wizard redirection on homescreen is stable. ([#8114](https://github.com/woocommerce/woocommerce-admin/pull/8114))
* Fix - Fix category report query returns invalid net sales. ([#8153](https://github.com/woocommerce/woocommerce-admin/pull/8153))
* Fix - Fix clicking the error message opens the dropdown. ([#8094](https://github.com/woocommerce/woocommerce-admin/pull/8094))
* Fix - Fix country/region selection not preserved in store details task. ([#8228](https://github.com/woocommerce/woocommerce-admin/pull/8228))
* Fix - Fixed email address not being optional in OBW ([#8263](https://github.com/woocommerce/woocommerce-admin/pull/8263))
* Fix - Fix get_automated_tax_supported_countries doesn't include UK. ([#8180](https://github.com/woocommerce/woocommerce-admin/pull/8180))
* Fix - Fix incorrect date options when the "Default Date Range" is set from Analytics settings. ([#8189](https://github.com/woocommerce/woocommerce-admin/pull/8189))
* Fix - Fix incorrectly displayed note created date. ([#8179](https://github.com/woocommerce/woocommerce-admin/pull/8179))
* Fix - Fix incorrect screen reader text generated for data points on charts table. ([#8181](https://github.com/woocommerce/woocommerce-admin/pull/8181))
* Fix - Fix incorrect total count of downloads on the analytics download report. ([#8182](https://github.com/woocommerce/woocommerce-admin/pull/8182))
* Fix - Fix misaligned status column on order report. ([#8121](https://github.com/woocommerce/woocommerce-admin/pull/8121))
* Fix - Fix shipping rate error message overlaps with the 'Proceed' button. ([#8165](https://github.com/woocommerce/woocommerce-admin/pull/8165))
* Fix - Fix Shipping task sometimes skipping the set shipping costs step. ([#8260](https://github.com/woocommerce/woocommerce-admin/pull/8260))
* Fix - Fix Uncaught TypeError count(NULL) for php8+ in Marketing.php. ([#8213](https://github.com/woocommerce/woocommerce-admin/pull/8213))
* Fix - Fix undefined derived_currency value for the track 'wcadmin_storeprofiler_store_details_continue'. ([#8193](https://github.com/woocommerce/woocommerce-admin/pull/8193))
* Fix - Fix variations table product filter query. ([#8120](https://github.com/woocommerce/woocommerce-admin/pull/8120))
* Fix - Make sure free subscriptions does not show when cbd industry is selected. ([#8323](https://github.com/woocommerce/woocommerce-admin/pull/8323))
* Fix - Make sure WooCommerce Payments tasklist_payment_setup is triggered again. ([#8146](https://github.com/woocommerce/woocommerce-admin/pull/8146))
* Fix - Preserve HTML markup in server-side error messages received from sample product import request. ([#8173](https://github.com/woocommerce/woocommerce-admin/pull/8173))
* Fix - Remove border between email input and newsletter checkbox in OBW store details. ([#8148](https://github.com/woocommerce/woocommerce-admin/pull/8148))
* Fix - Reset "install_timestamp" if it's not numeric to avoid TypeError. ([#8100](https://github.com/woocommerce/woocommerce-admin/pull/8100))
* Fix - Truncate the long site title with an ellipses on the second line. ([#8112](https://github.com/woocommerce/woocommerce-admin/pull/8112))
* Fix - Fix backwards compatibility with SkyVerge payment gateway.([#8371](https://github.com/woocommerce/woocommerce-admin/pull/8371))
* Add - Add additional store profiler track for the business details tab. ([#8265](https://github.com/woocommerce/woocommerce-admin/pull/8265))
* Add - Add countries data store ([#8119](https://github.com/woocommerce/woocommerce-admin/pull/8119))
* Add - Add extra tracking for plugin installation performance during onboarding. ([#8042](https://github.com/woocommerce/woocommerce-admin/pull/8042))
* Add - Adding tooltip to describe the lack of refund deductions from revenue summaries. ([#8187](https://github.com/woocommerce/woocommerce-admin/pull/8187))
* Add - Add localized validation to store address ([#8123](https://github.com/woocommerce/woocommerce-admin/pull/8123))
* Add - Add Magento migration note ([#8145](https://github.com/woocommerce/woocommerce-admin/pull/8145))
* Add - Add REST endpoint to retrieve address locales ([#8116](https://github.com/woocommerce/woocommerce-admin/pull/8116))
* Add - Add Spain to Square country suggestion list. ([#8210](https://github.com/woocommerce/woocommerce-admin/pull/8210))
* Add - Add wc_version property to the store profile onboarding tracks for view and complete steps. ([#8290](https://github.com/woocommerce/woocommerce-admin/pull/8290))
* Add - Change the reviews empty state panels logic ([#8147](https://github.com/woocommerce/woocommerce-admin/pull/8147))
* Update - Add custom error for store details email and allow continue ([#8110](https://github.com/woocommerce/woocommerce-admin/pull/8110))
* Update - Adding "allow-plugins" property for composer configuration. ([#8139](https://github.com/woocommerce/woocommerce-admin/pull/8139))
* Dev - Remove wc-admin-settings package and rename getSetting to getAdminSetting. ([#8057](https://github.com/woocommerce/woocommerce-admin/pull/8057))
* Tweak - Fix WCPay in core texts and promo slug ([#8296](https://github.com/woocommerce/woocommerce-admin/pull/8296))
* Tweak - Grow and center buttons in all WooCommerce ellipsis menu popover containers. ([#8168](https://github.com/woocommerce/woocommerce-admin/pull/8168))
* Tweak - Hide store address fields in regions that specify hidden ([#8172](https://github.com/woocommerce/woocommerce-admin/pull/8172))
* Tweak - Make activity panel badges margin consistent. ([#8152](https://github.com/woocommerce/woocommerce-admin/pull/8152))
* Tweak - Padding tweak for marketing tools plugin list headings. ([#8171](https://github.com/woocommerce/woocommerce-admin/pull/8171))
* Performance - Speed up customer syncing action. ([#8021](https://github.com/woocommerce/woocommerce-admin/pull/8021))
* Enhancement - Enhance report chart i18n support. ([#8129](https://github.com/woocommerce/woocommerce-admin/pull/8129))
* Enhancement - Make ExPlat request URL args filterable. Added woocommerce_explat_request_args filter. ([#8231](https://github.com/woocommerce/woocommerce-admin/pull/8231))
* Enhancement - Show MailPoet in Installed marketing extensions. ([#8091](https://github.com/woocommerce/woocommerce-admin/pull/8091))
* Enhancement - Update headercard to use filter to add ExPlat parameter ([#8233](https://github.com/woocommerce/woocommerce-admin/pull/8233))
**WooCommerce Blocks - 6.8.0 & 6.9.0**
* Add - Add support for the global style for the Price Filter block. ([#5559](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5559))
* Add - Hold stock for 60mins if the order is pending payment. ([#5546](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5546))
* Add - Allow users to reinsert the WooCommerce Legacy Template block in their block template if it is a WooCommerce block template. ([#5545](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5545))
* Add - Add support for the global style for the Stock Indicator block. ([#5525](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5525))
* Add - Add support for the global style for the Summary Product block. ([#5524](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5524))
* Add - Add support for the global style for the Product Title block. ([#5515](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5515))
* Add - Store API: Throw errors when attempting to pay with a non-available payment method. ([#5440](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5440))
* Add - Add support for the wide and full alignment for the legacy template block. ([#5433](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5433))
* Add - Store API and Cart block now support defining a quantity stepper and a minimum quantity. ([#5406](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5406))
* Add - Added controls to product grid blocks for filtering by stock levels. ([#4943](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/4943))
* Add - Add support for the global style for the Featured Category block. ([#5542](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5542))
* Fix duplicated checkout error notices. ([#5476](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5476))
* Fix - Use consistent HTML code for all rating sections, so that screen readers pronounce the rating correctly. ([#5552](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5552))
* Fix - All Products block displays thumbnails. ([#5551](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5551))
* Fix - Fixed a styling issue in the Checkout block when an order has multiple shipping packages. ([#5529](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5529))
* Fix - Fixed a visual bug (#5152) with the points and rewards plugin. ([#5430](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5430))
* Fix - Filter Products By Price block: Don't allow to insert negative values on inputs. ([#5123](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5123))
* Fix - Enable Mini Cart template-parts only for experimental builds. ([#5606](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5606))
* Fix - Show express payment button in full width if only one express payment method is available. ([#5601](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5601))
* Fix - Wrapped cart item product contents in inner div. ([#5240](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5240))
* Fix - Fix alignment issue with the "create account" section on the checkout block in the editor ([#5633](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5633))
* Dev - Remove invalid `$wpdb->prepare()` statement in Featured Category Block. ([#5471](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5471))
* Dev - Remove Stripe Payment Method Integration (which is now part of the Stripe Payment Method extension itself). ([#5449](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5449))
* Dev - Update the block theme folders to latest Gutenberg convention (i.e. `templates` and `parts`). ([#5464](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5464))
* Dev - Revert "Allow LegacyTemplate block to be reinserted, only on WooCommerce block templates.". ([#5643](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5643))
= 6.2.2 2022-03-10 =
**WooCommerce**
* Security - Address an issue with the PayPal Standard Payment Gateway. See https://developer.woocommerce.com/2022/03/10/woocommerce-3-5-10-6-3-1-security-releases/. ([#32057](https://github.com/woocommerce/woocommerce/pull/32057))
= 6.2.1 2022-02-22 =
**WooCommerce**
* Security - Fixed permission check for reviews in v1 & v2 REST API.
* Security - Fixed Path Traversal in Importers.
= 6.2.0 2022-02-08 =
**WooCommerce**
* Add - Admin notice warning about the upcoming minimum PHP 7.2 version bump coming in WooCommerce 6.5. ([#31557](https://github.com/woocommerce/woocommerce/pull/31557))
* Tweak - Removed images referred to from deprecated functions. ([#31395](https://github.com/woocommerce/woocommerce/pull/31395))
* Tweak - Update store setup widget to use task list API. ([#31755](https://github.com/woocommerce/woocommerce/pull/31755))
* Fix - Fixed styling of "pay for order" form for 2022 theme. ([#31682](https://github.com/woocommerce/woocommerce/pull/31682))
* Fix - Search Blocks form for 2022 theme. ([#31687](https://github.com/woocommerce/woocommerce/pull/31687))
* Fix - Checkout scroll to notices fallback scroll element. ([#30955](https://github.com/woocommerce/woocommerce/pull/30955))
* Fix - Prevent PhotoSwipe tap from interacting with elements directly underneath. Props @Edsuns and @andi34. ([#31591](https://github.com/woocommerce/woocommerce/pull/31591))
* Fix - Double notice about the upcoming change in the PHP version requirement. ([#31744](https://github.com/woocommerce/woocommerce/pull/31744))
* Dev - Added logic to `do_variation_action` prematurely return on custom actions. ([#31088](https://github.com/woocommerce/woocommerce/pull/31088))
* Dev - REST API - Adds `status` field to the GET `/coupons` endpoint. ([#31561](https://github.com/woocommerce/woocommerce/pull/31561))
* Dev - Use `calc` function to prevent deprecated warnings when building SCSS.
**WooCommerce Admin - 3.1.0**
* Enhancement - Add SlotFill areas to header. ([#7805](https://github.com/woocommerce/woocommerce-admin/pull/7805))
* Add - Add featured pill for MailPoet and Google Listings in marketing task. ([#8009](https://github.com/woocommerce/woocommerce-admin/pull/8009))
* Add - Add inbox_action_click track when a note gets clicked. ([#8086](https://github.com/woocommerce/woocommerce-admin/pull/8086))
* Add - Activate promo note after WC Pay is activated. ([#8104](https://github.com/woocommerce/woocommerce-admin/pull/8104))
* Add - Add payment remind me later note. ([#8085](https://github.com/woocommerce/woocommerce-admin/pull/8085))
* Add - Add WC Pay welcome page. ([#8083](https://github.com/woocommerce/woocommerce-admin/pull/8083))
* Update - Allow content data note props to be passed from remote sources ([#8047](https://github.com/woocommerce/woocommerce-admin/pull/8047))
* Update - Update @woocommerce/e2e-environment package to latest. ([#8000](https://github.com/woocommerce/woocommerce-admin/pull/8000))
* Tweak - OBW Update WC Pay label on recommended extensions list ([#8038](https://github.com/woocommerce/woocommerce-admin/pull/8038))
* Fix - Fix Onboarding flow where extensions might not be selected and installed. ([#7979](https://github.com/woocommerce/woocommerce-admin/pull/7979))
* Fix - Fix pagination issue with Analytics Coupons page. ([#8001](https://github.com/woocommerce/woocommerce-admin/pull/8001))
* Fix - Fix select-control component label/value alignment. ([#8045](https://github.com/woocommerce/woocommerce-admin/pull/8045))
* Fix - Fix unexpected analytics report table filter results. ([#8072](https://github.com/woocommerce/woocommerce-admin/pull/8072))
* Fix - Prevent coupon move notice for new installs. ([#7995](https://github.com/woocommerce/woocommerce-admin/pull/7995))
* Fix - Remove calls to read_meta_data in the Note DataStore. ([#7988](https://github.com/woocommerce/woocommerce-admin/pull/7988))
* Fix - Fix free extensions list isn't updated after store location or industry is changed. ([#8099](https://github.com/woocommerce/woocommerce-admin/pull/8099))
* Fix - Fix misaligned "Rows per page" dropdown. ([#8113](https://github.com/woocommerce/woocommerce-admin/pull/8113))
* Fix - Hide the extensions header when no available plugins in the category. ([#8089](https://github.com/woocommerce/woocommerce-admin/pull/8089))
* Fix - Replace all docs.woocommerce.com links with woocommerce.com/document. ([#8105](https://github.com/woocommerce/woocommerce-admin/pull/8105))
* Fix - Fixing marketing task not displaying on Atomic sites ([#8150](https://github.com/woocommerce/woocommerce-admin/pull/8150))
* Fix - Fix setup wizard free features checkbox re-check itself. ([#8169](https://github.com/woocommerce/woocommerce-admin/pull/8169))
* Dev - Add payment gateway suggestion docs and example extensions ([#7966](https://github.com/woocommerce/woocommerce-admin/pull/7966))
* Dev - Remove low performing inbox notes. ([#8054](https://github.com/woocommerce/woocommerce-admin/pull/8054))
* Dev - Remove navigation feedback note. ([#8055](https://github.com/woocommerce/woocommerce-admin/pull/8055))
* Dev - Fix task ID class check and add tests around tracking ([#8185](https://github.com/woocommerce/woocommerce-admin/pull/8185))
**WooCommerce Blocks - 6.6.0 & 6.7.0 & 6.7.1 & 6.7.2 & 6.7.3**
* Enhancement - Added global styles (text color) to the Active Filters block. ([5465](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5465))
* Enhancement - Prevent a 0 value shipping price being shown in the Checkout if no shipping methods are available. ([5444](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5444))
* Fix - Convert token to string when setting the active payment method. ([5535](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5535))
* Fix - Fixed an issue where the checkout address fields would be blank for logged in customers. ([5473](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5473))
* Fix - Account for products without variations in the On Sale Products block. ([5470](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5470))
* Fix - Update the template retrieving logic to allow for older Gutenberg convention and newer one (`block-templates`/`block-template-parts` vs. `templates`/`parts`). ([5455](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5455))
* Fix - Ensure that the translation of the "Proceed to Checkout" button is working. ([5453](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5453))
* Fix - Fix custom templates with fallback to archive being incorrectly attributed to the user in the editor instead of the parent theme. ([5447](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5447))
* Fix - Remove text decorations from product filtering blocks items. ([5384](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5384))
* Fix - "Added By" template column value is user friendly for modified WooCommerce block templates. ([5420](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5420))
* Fix - Fixed a performance issue with the cart by preventing an extra network request on mount. ([5394](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5394))
* Fix - Use the themes product archive block template for product category & product tag pages if the theme does not have more specific templates for those. ([5380](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5380))
* Fix - Cart block: Switch to correct view if inner block is selected. ([5358](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5358))
* Fix - Respect implicit quantity updates coming from server or directly from data stores. ([5352](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5352))
* Fix - Fixed a case where payments could fail after validation errors when using saved cards. ([5350](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5350))
* Fix - Add error handling for network errors during checkout. ([5341](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5341))
* Fix - Fix cart and checkout margin problem by removing the full-width option. ([5315](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5315))
* Fix - Enable Mini Cart template parts only for experimental builds. ([#5606](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5606))
* Tweak - Sync draft orders whenever cart data changes. ([5379](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5379))
* Tweak - Removed legacy handling for shipping_phone in Store API. ([5326](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5326))
* Tweak - Site Editor template list: Fix wrong icon displayed on WooCommerce templates after they have been edited. ([5375](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5375))
* Tweak - Fix validation error handling after using browser autofill. ([5373](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5373))
* Tweak - Update loading skeleton animations. ([5362](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5362))
* Tweak - Add error handling to `get_routes_from_namespace` method. ([5319](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5319))
* Tweak - Update WooCommerce plugin slug for Block Templates. ([#5519](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5519))
= 6.1.2 2022-03-10 =
**WooCommerce**
* Security - Address an issue with the PayPal Standard Payment Gateway. See https://developer.woocommerce.com/2022/03/10/woocommerce-3-5-10-6-3-1-security-releases/. ([#32057](https://github.com/woocommerce/woocommerce/pull/32057))
= 6.1.1 2022-01-20 =
**WooCommerce**
* Enhancement - Add support for 2022 theme shop and product pages. ( [#31536](https://github.com/woocommerce/woocommerce/pull/31536) )
* Enhancement - Add support for 2022 my account pages. ( [#31575](https://github.com/woocommerce/woocommerce/pull/31575) )
* Enhancement - Add support for 2022 checkout page. ( [#31630](https://github.com/woocommerce/woocommerce/pull/31630) )
* Fix - Use inline `onload` event instead of jQuery `load`. ( [#31623](https://github.com/woocommerce/woocommerce/pull/31623) )
* Fix - Removes the revert warning about is_ajax. ( [#31672](https://github.com/woocommerce/woocommerce/pull/31672) )
* Tweak - Better styling for checkout form for 2022 theme. ( [#31619](https://github.com/woocommerce/woocommerce/pull/31619) )
* Tweak - Center product cards in the 2022 theme. ( [#31626](https://github.com/woocommerce/woocommerce/pull/31626) )
* Tweak - Fix font sizes in single product tabs area in 2022 theme. ( [#31632](https://github.com/woocommerce/woocommerce/pull/31632) )
* Tweak - Modify background color for `mark` elements in 2022 theme. ( [#31631](https://github.com/woocommerce/woocommerce/pull/31631) )
* Tweak - Adjusts basis of overlay in 2022 theme checkout. ( [#31633](https://github.com/woocommerce/woocommerce/pull/31633) )
* Tweak - Improve order details table on narrow viewports in 2022 theme. ( [#31634](https://github.com/woocommerce/woocommerce/pull/31634) )
**WooCommerce Blocks - 6.5.2**
* Fix - Update WooCommerce plugin slug for Block Templates. ( [#5519](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5519) )
= 6.1.0 2022-01-11 = = 6.1.0 2022-01-11 =
**WooCommerce** **WooCommerce**
@ -98,6 +440,12 @@
* Fix - Fix error when reverting WooCommerce templates. ( [#5342](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5342) ) * Fix - Fix error when reverting WooCommerce templates. ( [#5342](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5342) )
* Fix - Fix: WooCommerce block templates loading for WP 5.9 without Gutenberg plugin. ( [#5335](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5335) ) * Fix - Fix: WooCommerce block templates loading for WP 5.9 without Gutenberg plugin. ( [#5335](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5335) )
= 6.0.1 2022-03-10 =
**WooCommerce**
* Security - Address an issue with the PayPal Standard Payment Gateway. See https://developer.woocommerce.com/2022/03/10/woocommerce-3-5-10-6-3-1-security-releases/. ([#32057](https://github.com/woocommerce/woocommerce/pull/32057))
= 6.0.0 2021-12-14 = = 6.0.0 2021-12-14 =
**WooCommerce** **WooCommerce**
@ -203,6 +551,12 @@
* Fix - Store API Ensure returned customer address state is valid. ( [#4844](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/4844) ) * Fix - Store API Ensure returned customer address state is valid. ( [#4844](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/4844) )
* Fix - fatal error in certain WP 5.9 pre-release versions. ( [#5183](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5183) ) * Fix - fatal error in certain WP 5.9 pre-release versions. ( [#5183](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5183) )
= 5.9.1 2022-03-10 =
**WooCommerce**
* Security - Address an issue with the PayPal Standard Payment Gateway. See https://developer.woocommerce.com/2022/03/10/woocommerce-3-5-10-6-3-1-security-releases/. ([#32057](https://github.com/woocommerce/woocommerce/pull/32057))
= 5.9.0 2021-11-09 = = 5.9.0 2021-11-09 =
**WooCommerce** **WooCommerce**
@ -260,6 +614,12 @@
* Fix - Remove IntersectionObserver shim in favor of dropping IE11 support. #4808 * Fix - Remove IntersectionObserver shim in favor of dropping IE11 support. #4808
* Enhancement - Added global styles to All Reviews, Reviews by Category and Reviews by Product blocks. Now it's possible to change the text color and font size of those blocks. #4323 * Enhancement - Added global styles to All Reviews, Reviews by Category and Reviews by Product blocks. Now it's possible to change the text color and font size of those blocks. #4323
= 5.8.2 2022-03-10 =
**WooCommerce**
* Security - Address an issue with the PayPal Standard Payment Gateway. See https://developer.woocommerce.com/2022/03/10/woocommerce-3-5-10-6-3-1-security-releases/. ([#32057](https://github.com/woocommerce/woocommerce/pull/32057))
= 5.8.0 2021-10-12 = = 5.8.0 2021-10-12 =
**WooCommerce** **WooCommerce**
@ -333,6 +693,12 @@
* Fix - Improves compatibility with environments where NO_ZERO_DATE is enabled. #519 * Fix - Improves compatibility with environments where NO_ZERO_DATE is enabled. #519
* Fix - Adds safety checks to guard against errors when our database tables cannot be created. #645 * Fix - Adds safety checks to guard against errors when our database tables cannot be created. #645
= 5.7.2 2022-03-10 =
**WooCommerce**
* Security - Address an issue with the PayPal Standard Payment Gateway. See https://developer.woocommerce.com/2022/03/10/woocommerce-3-5-10-6-3-1-security-releases/. ([#32057](https://github.com/woocommerce/woocommerce/pull/32057))
= 5.7.1 2021-09-23 = = 5.7.1 2021-09-23 =
**WooCommerce** **WooCommerce**
@ -440,6 +806,12 @@
- Fix - Fix memory leak when previewing transform options for the All reviews block. #4428 - Fix - Fix memory leak when previewing transform options for the All reviews block. #4428
- Fix - Disable Cart, Checkout, All Products & filters blocks from the widgets screen. #4646 - Fix - Disable Cart, Checkout, All Products & filters blocks from the widgets screen. #4646
= 5.6.2 2022-03-10 =
**WooCommerce**
* Security - Address an issue with the PayPal Standard Payment Gateway. See https://developer.woocommerce.com/2022/03/10/woocommerce-3-5-10-6-3-1-security-releases/. ([#32057](https://github.com/woocommerce/woocommerce/pull/32057))
= 5.6.0 2021-08-17 = = 5.6.0 2021-08-17 =
**WooCommerce** **WooCommerce**
@ -546,6 +918,12 @@
- Tweak - Allow products to be added by SKU in the Hand-Picked Products block. #4366 - Tweak - Allow products to be added by SKU in the Hand-Picked Products block. #4366
- Tweak - Add Slot in the Discounts section of the Checkout sidebar to allow third party extensions to render their own components there. #4310 - Tweak - Add Slot in the Discounts section of the Checkout sidebar to allow third party extensions to render their own components there. #4310
= 5.5.4 2022-03-10 =
**WooCommerce**
* Security - Address an issue with the PayPal Standard Payment Gateway. See https://developer.woocommerce.com/2022/03/10/woocommerce-3-5-10-6-3-1-security-releases/. ([#32057](https://github.com/woocommerce/woocommerce/pull/32057))
= 5.5.2 2021-07-22 = = 5.5.2 2021-07-22 =
* Fix - Add a new option allowing product downloads to be served using redirects as a last resort. #30288 * Fix - Add a new option allowing product downloads to be served using redirects as a last resort. #30288
@ -749,6 +1127,12 @@
* Fix - Add extra safety/account for different versions of AS and different loading patterns. #714 * Fix - Add extra safety/account for different versions of AS and different loading patterns. #714
* Fix - Handle hidden columns (Tools → Scheduled Actions) | #600. * Fix - Handle hidden columns (Tools → Scheduled Actions) | #600.
= 5.4.4 2022-03-10 =
**WooCommerce**
* Security - Address an issue with the PayPal Standard Payment Gateway. See https://developer.woocommerce.com/2022/03/10/woocommerce-3-5-10-6-3-1-security-releases/. ([#32057](https://github.com/woocommerce/woocommerce/pull/32057))
= 5.4.2 2021-07-14 = = 5.4.2 2021-07-14 =
**WooCommerce** **WooCommerce**
@ -848,6 +1232,12 @@
* Fix - Prevent parts of old addresses being displayed in the shipping calculator when changing countries. #4038 * Fix - Prevent parts of old addresses being displayed in the shipping calculator when changing countries. #4038
* Fix - issue in which email and phone fields are cleared when using a separate billing address. #4162 * Fix - issue in which email and phone fields are cleared when using a separate billing address. #4162
= 5.3.3 2022-03-10 =
**WooCommerce**
* Security - Address an issue with the PayPal Standard Payment Gateway. See https://developer.woocommerce.com/2022/03/10/woocommerce-3-5-10-6-3-1-security-releases/. ([#32057](https://github.com/woocommerce/woocommerce/pull/32057))
= 5.3.1 2021-07-14 = = 5.3.1 2021-07-14 =
**WooCommerce** **WooCommerce**
@ -985,6 +1375,12 @@
* Tweak - Store profiler - Changed MailPoet's title and description #6886 * Tweak - Store profiler - Changed MailPoet's title and description #6886
* Tweak - Update PayU logo #6829 * Tweak - Update PayU logo #6829
= 5.2.5 2022-03-10 =
**WooCommerce**
* Security - Address an issue with the PayPal Standard Payment Gateway. See https://developer.woocommerce.com/2022/03/10/woocommerce-3-5-10-6-3-1-security-releases/. ([#32057](https://github.com/woocommerce/woocommerce/pull/32057))
= 5.2.3 2021-07-14 = = 5.2.3 2021-07-14 =
**WooCommerce** **WooCommerce**
@ -1154,6 +1550,12 @@
* Fix - Ensure sale badges have a uniform height in the Cart block. ([3897](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/3897)) * Fix - Ensure sale badges have a uniform height in the Cart block. ([3897](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/3897))
* Note - Internally, this release has modified how `AbstractBlock` (the base class for all of our blocks) functions, and how it loads assets. `AbstractBlock` is internal to this project and does not seem like something that would ever need to be extended by 3rd parties, but note if you are doing so for whatever reason, your implementation would need to be updated to match. ([3829](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/3829)) * Note - Internally, this release has modified how `AbstractBlock` (the base class for all of our blocks) functions, and how it loads assets. `AbstractBlock` is internal to this project and does not seem like something that would ever need to be extended by 3rd parties, but note if you are doing so for whatever reason, your implementation would need to be updated to match. ([3829](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/3829))
= 5.1.3 2022-03-10 =
**WooCommerce**
* Security - Address an issue with the PayPal Standard Payment Gateway. See https://developer.woocommerce.com/2022/03/10/woocommerce-3-5-10-6-3-1-security-releases/. ([#32057](https://github.com/woocommerce/woocommerce/pull/32057))
= 5.1.1 2021-07-14 = = 5.1.1 2021-07-14 =
**WooCommerce** **WooCommerce**
@ -1268,6 +1670,12 @@
* Dev - Added formatting classes to the Store API for extensions to consume. * 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)) * 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.3 2022-03-10 =
**WooCommerce**
* Security - Address an issue with the PayPal Standard Payment Gateway. See https://developer.woocommerce.com/2022/03/10/woocommerce-3-5-10-6-3-1-security-releases/. ([#32057](https://github.com/woocommerce/woocommerce/pull/32057))
= 5.0.1 2021-07-14 = = 5.0.1 2021-07-14 =
**WooCommerce** **WooCommerce**
@ -1341,6 +1749,12 @@
* Enhancement - Add an "unread" indicator to inbox messages. #6047 * Enhancement - Add an "unread" indicator to inbox messages. #6047
* Add - Manage activity from home screen inbox message. #6072 * Add - Manage activity from home screen inbox message. #6072
= 4.9.5 2022-03-10 =
**WooCommerce**
* Security - Address an issue with the PayPal Standard Payment Gateway. See https://developer.woocommerce.com/2022/03/10/woocommerce-3-5-10-6-3-1-security-releases/. ([#32057](https://github.com/woocommerce/woocommerce/pull/32057))
= 4.9.3 2021-07-14 = = 4.9.3 2021-07-14 =
**WooCommerce** **WooCommerce**
@ -1471,6 +1885,12 @@
* Dev - Expose store/cart via ExtendRestApi to extensions. ([3445](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/3445)) * 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 - Added formatting classes to the Store API for extensions to consume.
= 4.8.3 2022-03-10 =
**WooCommerce**
* Security - Address an issue with the PayPal Standard Payment Gateway. See https://developer.woocommerce.com/2022/03/10/woocommerce-3-5-10-6-3-1-security-releases/. ([#32057](https://github.com/woocommerce/woocommerce/pull/32057))
= 4.8.1 2021-07-14 = = 4.8.1 2021-07-14 =
**WooCommerce** **WooCommerce**
@ -1590,6 +2010,12 @@
* Fix - Twenty Twenty One Button and Placeholder Styling. #3443 * Fix - Twenty Twenty One Button and Placeholder Styling. #3443
* Fix - checkbox and textarea styles in Twenty Twenty One when it has dark controls active. #3450 * Fix - checkbox and textarea styles in Twenty Twenty One when it has dark controls active. #3450
= 4.7.4 2022-03-10 =
**WooCommerce**
* Security - Address an issue with the PayPal Standard Payment Gateway. See https://developer.woocommerce.com/2022/03/10/woocommerce-3-5-10-6-3-1-security-releases/. ([#32057](https://github.com/woocommerce/woocommerce/pull/32057))
= 4.7.2 2021-07-14 = = 4.7.2 2021-07-14 =
**WooCommerce** **WooCommerce**
@ -1658,6 +2084,12 @@
* Tweak: Add BR and IN to list of stripe countries [#5377](https://github.com/woocommerce/woocommerce-admin/pull/5377) * Tweak: Add BR and IN to list of stripe countries [#5377](https://github.com/woocommerce/woocommerce-admin/pull/5377)
* Fix: Redirect instead of stalling on WCPay Inbox note action [#5413](https://github.com/woocommerce/woocommerce-admin/pull/5413) * Fix: Redirect instead of stalling on WCPay Inbox note action [#5413](https://github.com/woocommerce/woocommerce-admin/pull/5413)
= 4.6.5 2022-03-10 =
**WooCommerce**
* Security - Address an issue with the PayPal Standard Payment Gateway. See https://developer.woocommerce.com/2022/03/10/woocommerce-3-5-10-6-3-1-security-releases/. ([#32057](https://github.com/woocommerce/woocommerce/pull/32057))
= 4.6.3 2021-07-14 = = 4.6.3 2021-07-14 =
**WooCommerce** **WooCommerce**
@ -1786,6 +2218,12 @@
- Create DebouncedValidatedTextInput component. ([3108](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/3108)) - Create DebouncedValidatedTextInput component. ([3108](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/3108))
- Merge ProductPrice atomic block and component. ([3065](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/3065)) - Merge ProductPrice atomic block and component. ([3065](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/3065))
= 4.5.5 2022-03-10 =
**WooCommerce**
* Security - Address an issue with the PayPal Standard Payment Gateway. See https://developer.woocommerce.com/2022/03/10/woocommerce-3-5-10-6-3-1-security-releases/. ([#32057](https://github.com/woocommerce/woocommerce/pull/32057))
= 4.5.3 2021-07-14 = = 4.5.3 2021-07-14 =
**WooCommerce** **WooCommerce**
@ -1840,6 +2278,12 @@
* Dev - Task list - add a shortcut back to store setup. #4853 * Dev - Task list - add a shortcut back to store setup. #4853
* Dev - Update the colors of the illustrations in the welcome modal. #4945 * Dev - Update the colors of the illustrations in the welcome modal. #4945
= 4.4.4 2022-03-10 =
**WooCommerce**
* Security - Address an issue with the PayPal Standard Payment Gateway. See https://developer.woocommerce.com/2022/03/10/woocommerce-3-5-10-6-3-1-security-releases/. ([#32057](https://github.com/woocommerce/woocommerce/pull/32057))
= 4.4.2 2021-07-14 = = 4.4.2 2021-07-14 =
**WooCommerce** **WooCommerce**
@ -1994,6 +2438,12 @@
* Fix - 'Product Summary' in All Products block is not pulling in the short description of the product. #2913 * Fix - 'Product Summary' in All Products block is not pulling in the short description of the product. #2913
* Dev - Add query filter when searching for a table. #2886 * Dev - Add query filter when searching for a table. #2886
= 4.3.6 2022-03-10 =
**WooCommerce**
* Security - Address an issue with the PayPal Standard Payment Gateway. See https://developer.woocommerce.com/2022/03/10/woocommerce-3-5-10-6-3-1-security-releases/. ([#32057](https://github.com/woocommerce/woocommerce/pull/32057))
= 4.3.4 2021-07-14 = = 4.3.4 2021-07-14 =
**WooCommerce** **WooCommerce**
@ -2181,6 +2631,12 @@
* Dev - Table creation validation for install routine #2287 * Dev - Table creation validation for install routine #2287
* Dev - Update the icons used in the blocks. #1644 * Dev - Update the icons used in the blocks. #1644
= 4.2.5 2022-03-10 =
**WooCommerce**
* Security - Address an issue with the PayPal Standard Payment Gateway. See https://developer.woocommerce.com/2022/03/10/woocommerce-3-5-10-6-3-1-security-releases/. ([#32057](https://github.com/woocommerce/woocommerce/pull/32057))
= 4.2.3 2021-07-14 = = 4.2.3 2021-07-14 =
**WooCommerce** **WooCommerce**
@ -2265,6 +2721,12 @@
* Dev - Dynamic Currency with Context API #4027 * Dev - Dynamic Currency with Context API #4027
* Dev - Remove Duplicate array entry #4049 * Dev - Remove Duplicate array entry #4049
= 4.1.4 2022-03-10 =
**WooCommerce**
* Security - Address an issue with the PayPal Standard Payment Gateway. See https://developer.woocommerce.com/2022/03/10/woocommerce-3-5-10-6-3-1-security-releases/. ([#32057](https://github.com/woocommerce/woocommerce/pull/32057))
= 4.1.2 2021-07-14 = = 4.1.2 2021-07-14 =
**WooCommerce** **WooCommerce**
@ -2330,6 +2792,12 @@
* Dev - Adds usage data for the of cart & checkout blocks (currently in development in WooCommmerce Blocks plugin) to the WC Tracker snapshot. #26084 * Dev - Adds usage data for the of cart & checkout blocks (currently in development in WooCommmerce Blocks plugin) to the WC Tracker snapshot. #26084
* Dev - Implement some additional tracks for coupons, orders, and products. #26085 * Dev - Implement some additional tracks for coupons, orders, and products. #26085
= 4.0.4 2022-03-10 =
**WooCommerce**
* Security - Address an issue with the PayPal Standard Payment Gateway. See https://developer.woocommerce.com/2022/03/10/woocommerce-3-5-10-6-3-1-security-releases/. ([#32057](https://github.com/woocommerce/woocommerce/pull/32057))
= 4.0.2 2021-07-14 = = 4.0.2 2021-07-14 =
**WooCommerce** **WooCommerce**
@ -2454,6 +2922,12 @@
* Dev - Applies woocommerce_maxmind_geolocation_database_path in MaxMind database migration. #25681 * Dev - Applies woocommerce_maxmind_geolocation_database_path in MaxMind database migration. #25681
* Dev - Support both .data() and .dataset for formdata in add to cart requests. #25726 * Dev - Support both .data() and .dataset for formdata in add to cart requests. #25726
= 3.9.5 2022-03-10 =
**WooCommerce**
* Security - Address an issue with the PayPal Standard Payment Gateway. See https://developer.woocommerce.com/2022/03/10/woocommerce-3-5-10-6-3-1-security-releases/. ([#32057](https://github.com/woocommerce/woocommerce/pull/32057))
= 3.9.4 2021-07-14 = = 3.9.4 2021-07-14 =
**WooCommerce** **WooCommerce**
@ -2569,6 +3043,12 @@
* Localization - Fixed translatable string comments for translators. #24928 * Localization - Fixed translatable string comments for translators. #24928
* Localization - Add postcode validation for Slovenia. #25174 * Localization - Add postcode validation for Slovenia. #25174
= 3.8.3 2022-03-10 =
**WooCommerce**
* Security - Address an issue with the PayPal Standard Payment Gateway. See https://developer.woocommerce.com/2022/03/10/woocommerce-3-5-10-6-3-1-security-releases/. ([#32057](https://github.com/woocommerce/woocommerce/pull/32057))
= 3.8.2 2021-07-14 = = 3.8.2 2021-07-14 =
**WooCommerce** **WooCommerce**
@ -2691,6 +3171,12 @@
* Security - Add an exit after the redirect when checking author archive capabilities for customers. * Security - Add an exit after the redirect when checking author archive capabilities for customers.
* Security - Ensure 404 pages with single product urls cannot be exploited using Open Redirect. * Security - Ensure 404 pages with single product urls cannot be exploited using Open Redirect.
= 3.7.3 2022-03-10 =
**WooCommerce**
* Security - Address an issue with the PayPal Standard Payment Gateway. See https://developer.woocommerce.com/2022/03/10/woocommerce-3-5-10-6-3-1-security-releases/. ([#32057](https://github.com/woocommerce/woocommerce/pull/32057))
= 3.7.2 2021-07-14 = = 3.7.2 2021-07-14 =
**WooCommerce** **WooCommerce**
@ -2803,6 +3289,12 @@
* Localization - Add new currency for São Tomé, Príncipe dobra and Mauritanian ouguiya. #23950 * Localization - Add new currency for São Tomé, Príncipe dobra and Mauritanian ouguiya. #23950
* Localization - Change Canada poscode label to `Postal code`. #23740 * Localization - Change Canada poscode label to `Postal code`. #23740
= 3.6.7 2022-03-10 =
**WooCommerce**
* Security - Address an issue with the PayPal Standard Payment Gateway. See https://developer.woocommerce.com/2022/03/10/woocommerce-3-5-10-6-3-1-security-releases/. ([#32057](https://github.com/woocommerce/woocommerce/pull/32057))
= 3.6.6 2021-07-14 = = 3.6.6 2021-07-14 =
**WooCommerce** **WooCommerce**
@ -3132,6 +3624,12 @@
* Localization - Update CA address format. #22692 * Localization - Update CA address format. #22692
* Localization - Updated JP field order. #22774 * Localization - Updated JP field order. #22774
= 3.5.10 2022-03-10 =
**WooCommerce**
* Security - Address an issue with the PayPal Standard Payment Gateway. See https://developer.woocommerce.com/2022/03/10/woocommerce-3-5-10-6-3-1-security-releases/. ([#32057](https://github.com/woocommerce/woocommerce/pull/32057))
= 3.5.9 2021-07-14 = = 3.5.9 2021-07-14 =
**WooCommerce** **WooCommerce**

View File

@ -14,22 +14,28 @@
"url": "https://github.com/woocommerce/woocommerce/issues" "url": "https://github.com/woocommerce/woocommerce/issues"
}, },
"scripts": { "scripts": {
"preinstall": "npx only-allow pnpm" "preinstall": "npx only-allow pnpm",
"postinstall": "pnpm git:update-hooks",
"git:update-hooks": "rm -r .git/hooks && mkdir -p .git/hooks && husky install",
"create-extension": "node ./tools/create-extension/index.js"
}, },
"devDependencies": { "devDependencies": {
"@automattic/nx-composer": "^0.1.0", "@automattic/nx-composer": "^0.1.0",
"@nrwl/cli": "^13.3.4", "@nrwl/cli": "^13.3.4",
"@nrwl/linter": "^13.3.4",
"@nrwl/devkit": "^13.1.4", "@nrwl/devkit": "^13.1.4",
"@nrwl/linter": "^13.3.4",
"@nrwl/tao": "13.3.4", "@nrwl/tao": "13.3.4",
"@nrwl/web": "^13.3.4", "@nrwl/web": "^13.3.4",
"@nrwl/workspace": "^13.3.4", "@nrwl/workspace": "^13.3.4",
"@types/node": "14.14.33", "@types/node": "14.14.33",
"@woocommerce/eslint-plugin": "^1.3.0", "@woocommerce/eslint-plugin": "workspace:*",
"@wordpress/eslint-plugin": "^11.0.0",
"@wordpress/prettier-config": "^1.1.1", "@wordpress/prettier-config": "^1.1.1",
"chalk": "^4.1.2", "chalk": "^4.1.2",
"glob": "^7.2.0", "glob": "^7.2.0",
"husky": "^7.0.4",
"jest": "^27.3.1", "jest": "^27.3.1",
"lint-staged": "^12.3.7",
"mkdirp": "^1.0.4", "mkdirp": "^1.0.4",
"node-stream-zip": "^1.15.0", "node-stream-zip": "^1.15.0",
"prettier": "npm:wp-prettier@^2.2.1-beta-1", "prettier": "npm:wp-prettier@^2.2.1-beta-1",
@ -40,7 +46,9 @@
"@babel/core": "7.12.9", "@babel/core": "7.12.9",
"@wordpress/babel-plugin-import-jsx-pragma": "^3.1.0", "@wordpress/babel-plugin-import-jsx-pragma": "^3.1.0",
"@wordpress/babel-preset-default": "^6.4.1", "@wordpress/babel-preset-default": "^6.4.1",
"fs-extra": "^10.0.1",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"promptly": "^3.2.0",
"wp-textdomain": "1.0.1" "wp-textdomain": "1.0.1"
} }
} }

85
packages/js/README.md Normal file
View File

@ -0,0 +1,85 @@
# WooCommerce Packages
Currently we have a small set of public-facing packages that can be dowloaded from [npm](https://www.npmjs.com/org/woocommerce) and used in external applications.
- `@woocommerce/components`: A library of components that can be used to create pages in the WooCommerce dashboard and reports pages.
- `@woocommerce/csv-export`: A set of functions to convert data into CSV values, and enable a browser download of the CSV data.
- `@woocommerce/currency`: A class to display and work with currency values.
- `@woocommerce/date`: A collection of utilities to display and work with date values.
- `@woocommerce/navigation`: A collection of navigation-related functions for handling query parameter objects, serializing query parameters, updating query parameters, and triggering path changes.
- `@woocommerce/tracks`: User event tracking utility functions for Automattic based projects.
## Working with existing packages
- You can make changes to packages files as normal, and running `pnpm start` will compile and watch both app files and packages.
- :warning: Make sure any dependencies you add to a package are also added to that package's `package.json`, not just the woocommerce-admin package.json
- :warning: Make sure you're not importing from any woocommerce-admin files outside of the package (you can import from other packages, just use the `import from @woocommerce/package` syntax).
- Add your change to the CHANGELOG for that package under the next version number, creating one if necessary (we use semantic versioning for packages, [see these guidelines](https://github.com/WordPress/gutenberg/blob/master/CONTRIBUTING.md#maintaining-changelogs)).
- Don't change the version in `package.json`.
- Label your PR with the `Packages` label.
- Once merged, you can wait for the next package release roundup, or you can publish a release now (see below, "Publishing packages").
---
## Creating a new package
Most of this is pulled [from the Gutenberg workflow](https://github.com/WordPress/gutenberg/blob/master/CONTRIBUTING.md#creating-new-package).
To create a new package, add a new folder to `/packages`, containing…
1. `package.json` based on the template:
```json
{
"name": "@woocommerce/package-name",
"version": "1.0.0-beta.0",
"description": "Package description.",
"author": "Automattic",
"license": "GPL-2.0-or-later",
"keywords": [ "wordpress", "woocommerce" ],
"homepage": "https://github.com/woocommerce/woocommerce/tree/main/packages/[_YOUR_PACKAGE_]/README.md",
"repository": {
"type": "git",
"url": "https://github.com/woocommerce/woocommerce.git"
},
"bugs": {
"url": "https://github.com/woocommerce/woocommerce/issues"
},
"main": "build/index.js",
"module": "build-module/index.js",
"react-native": "src/index",
"publishConfig": {
"access": "public"
}
}
```
2. `.npmrc` file which disables creating `package-lock.json` file for the package:
```
package-lock=false
```
3. `README.md` file containing at least:
- Package name
- Package description
- Installation details
- Usage example
4. A `src` directory for the source of your module, which will be built by default using the `pnpm run build:packages` command. Note that you'll want an `index.js` file that exports the package contents, see other packages for examples.
5. Add the new package name to `packages/dependency-extraction-webpack-plugin/assets/packages.js` so that users of that plugin will also be able to use the new package without enqueuing it.
---
## Publishing packages
- Run `pnpm run publish-packages:check` to run pnpm publish with the `--dry-run` option
- Create a PR with a CHANGELOG for each updated package (or try to add to the CHANGELOG with any PR editing `packages/`)
- Run `pnpm run publish-packages:prod` to publish the package
- _OR_ Run `pnpm run publish-packages:dev` to publish "next" releases (installed as `pnpm i @woocommerce/package@next`). Only use `:dev` if you have a reason to.
- Both commands will run `build:packages` before the publishing task, just to catch any last updates.
### Publishing a single package
Sometimes, its helpful to release a singular package. This can be done directly through pnpm. Be sure versions and builds are correct.
- Bump the version in the package's package.json as well as its CHANGELOG file.
- `pnpm install && pnpm run build:packages` to build packages.
- `cd packages/<package-name>`
- `pnpm publish`

View File

@ -0,0 +1,4 @@
module.exports = {
extends: [ 'plugin:@woocommerce/eslint-plugin/recommended' ],
root: true,
};

View File

@ -0,0 +1 @@
package-lock=false

View File

@ -0,0 +1,56 @@
# Unreleased
- Add E2E tests to disabled welcome modal #32505
- Update test for payment task. #32467
- Increase timeout threshold for payment task. #32605
# 1.0.0
- Add returned type annotations and remove unused vars. #8020
- Add E2E tests for checking store currency if it matches the onboarded country. #7712
- Make unchecking free features more robust. #7761
- Fix typescript type error in admin-e2e-tests package #7765
- Add extension deactivation util function addition. #7804
- Add tests to Subscriptions inclusion. #7804
- Add missing dependencies. #8349
- Update all js packages with minor/patch version changes. #8392
- Add E2E test for checking onboarding tab clickable after going back. #8469
## Breaking changes
- Update `@types/jest` to v27
- Update the peer dependency constraint `@typescript-eslint/eslint-plugin` to ^5.
- eslint-plugin: ban-types no longer reports object by default.
# 0.1.2
- Add Customers to analytics pages tested #7573
- Add `waitForTimeout` utility function #7572
- Update analytics overview tests to allow re-running the tests.
# 0.1.1
- Allow packages to be built in isolation. #7286
- Add scope to BACS slotfill #7405
- Update e2e matcher for tasklist header #7406
- Update homescreen, utils, payment task, payments setup. #7338
- Refactor package style builds #7531
- Updated onboarding tests to include email prefill and move client setup checkbox to business step.
- Payment task update. #7577
- Add test cases for the home screen tasklist and activity panels. #7509
- Add wait for orders text on activity panel. #7550
- Allow CBD to be optional in business details in E2E. #7675
# 0.1.0
- Released initial package

View File

@ -0,0 +1,56 @@
# Admin E2E Tests
An end-to-end test suite for WooCommerce setup, onboarding, home screen/task list, and analytics.
## Installation
Install the module
```bash
pnpm install @woocommerce/admin-e2e-tests --save
```
## Usage
Create a E2E test specification file under `/tests/e2e/specs/example.test.js`:
```js
const { testAdminBasicSetup } = require( '@woocommerce/admin-e2e-tests' );
testAdminBasicSetup();
```
See the [wooCommerce E2E Boilerplate](https://github.com/woocommerce/woocommerce-e2e-boilerplate) for instructions on setting up an E2E test environment.
### Configuration
Add the following entries to `tests/e2e/config/default.json`
```json
"onboardingwizard": {
"industry": "Test industry",
"numberofproducts": "1 - 10",
"sellingelsewhere": "No"
},
"settings": {
"shipping": {
"zonename": "United States",
"zoneregions": "United States (US)",
"shippingmethod": "Free shipping"
}
}
```
### Available tests
The following test functions are included in the package:
| Function | Description |
| --- | --- |
| `testAdminBasicSetup` | Test that WooCommerce can be activated with pretty permalinks |
| `testAdminOnboardingWizard` | Complete the onboarding wizard with US merchant |
| `testAdminNonUSRecommendedFeatures` | Complete the onboarding wizard with non-US merchant |
| `testSelectiveBundleWCPay` | Ensure onboarding wizard offers WC Payments in appropriate contexts |
| `testAdminAnalyticsPages` | Test that the React App is functional on Analytics pages |
| `testAdminCouponsPage` | Test that the Coupons is functional |
| `testAdminPaymentSetupTask` | Test that payment methods can be configured |

View File

@ -0,0 +1,65 @@
{
"name": "@woocommerce/admin-e2e-tests",
"version": "1.0.0",
"author": "Automattic",
"description": "E2E tests for the new WooCommerce interface.",
"homepage": "https://github.com/woocommerce/woocommerce/tree/trunk/packages/js/admin-e2e-tests/README.md",
"repository": {
"type": "git",
"url": "https://github.com/woocommerce/woocommerce.git"
},
"keywords": [
"woocommerce",
"e2e"
],
"license": "GPL-3.0+",
"main": "build/index.js",
"types": "build/index.d.ts",
"files": [
"/build/",
"!*.ts.map",
"!*.tsbuildinfo"
],
"sideEffects": false,
"dependencies": {
"@jest/globals": "^26.6.2",
"@types/jest": "^27.4.1",
"config": "^3.3.7"
},
"peerDependencies": {
"@woocommerce/e2e-environment": "^0.2.3 || ^0.3.0",
"@woocommerce/e2e-utils": "^0.2.0",
"puppeteer": "^2.0.0"
},
"devDependencies": {
"@babel/core": "^7.17.5",
"@types/expect-puppeteer": "^4.4.7",
"@types/puppeteer": "^5.4.5",
"@typescript-eslint/eslint-plugin": "^5.14.0",
"@woocommerce/api": "^0.2.0",
"@wordpress/eslint-plugin": "^11.0.0",
"eslint": "^8.12.0",
"jest": "^27.5.1",
"jest-cli": "^27.5.1",
"jest-mock-extended": "^1.0.18",
"rimraf": "^3.0.2",
"ts-jest": "^27.1.3",
"typescript": "^4.6.2"
},
"publishConfig": {
"access": "public"
},
"scripts": {
"prepare": "pnpm run build",
"build": "tsc --build",
"start": "tsc --build --watch",
"clean": "pnpm exec rimraf tsconfig.tsbuildinfo build build-*",
"lint": "eslint src",
"prepack": "pnpm run clean && pnpm run build"
},
"lint-staged": {
"*.(t|j)s?(x)": [
"eslint --fix"
]
}
}

View File

@ -0,0 +1,44 @@
{
"root": "packages/js/admin-e2e-tests",
"sourceRoot": "packages/js/admin-e2e-tests/src",
"projectType": "library",
"targets": {
"changelog": {
"executor": "./tools/executors/changelogger:changelog",
"options": {
"action": "add",
"cwd": "packages/js/admin-e2e-tests"
}
},
"build": {
"executor": "@nrwl/workspace:run-script",
"options": {
"script": "build"
}
},
"build-watch": {
"executor": "@nrwl/workspace:run-script",
"options": {
"script": "start"
}
},
"test": {
"executor": "@nrwl/workspace:run-script",
"options": {
"script": "test"
}
},
"clean": {
"executor": "@nrwl/workspace:run-script",
"options": {
"script": "clean"
}
},
"prepare": {
"executor": "@nrwl/workspace:run-script",
"options": {
"script": "prepare"
}
}
}
}

View File

@ -0,0 +1,10 @@
export const TaskTitles = {
storeDetails: 'Store details',
addPayments: 'Set up payments',
wooPayments:
'Set up WooCommerce PaymentsBy setting up, you are agreeing to the Terms of Service2 minutes',
addProducts: 'Add my products',
taxSetup: 'Set up tax',
setUpShippingCosts: 'Set up shipping',
personalizeStore: 'Personalize my store',
};

View File

@ -0,0 +1,14 @@
/**
* External dependencies
*/
import { Page } from 'puppeteer';
export abstract class BaseElement {
protected page: Page;
protected selector: string;
constructor( page: Page, selector: string ) {
this.page = page;
this.selector = selector;
}
}

View File

@ -0,0 +1,29 @@
/**
* Internal dependencies
*/
import { getElementByText, getInputValue } from '../utils/actions';
import { BaseElement } from './BaseElement';
export class DropdownField extends BaseElement {
async select( value: string ): Promise< void > {
const currentVal = await getInputValue( this.selector + ' input' );
if ( currentVal !== value ) {
await this.page.click(
this.selector + ' .woocommerce-select-control__control'
);
const button = await getElementByText(
'button',
value,
this.selector
);
await button?.click();
await this.checkSelected( value );
}
return undefined;
}
async checkSelected( value: string ): Promise< void > {
const currentVal = await getInputValue( this.selector + ' input' );
expect( currentVal ).toBe( value );
}
}

View File

@ -0,0 +1,31 @@
/**
* Internal dependencies
*/
import { BaseElement } from './BaseElement';
/* eslint-disable @typescript-eslint/no-var-requires */
const { clearAndFillInput } = require( '@woocommerce/e2e-utils' );
/* eslint-enable @typescript-eslint/no-var-requires */
export class DropdownTypeaheadField extends BaseElement {
async search( text: string ): Promise< void > {
await clearAndFillInput( this.selector + '-0__control-input', text );
}
async select( selector: string ): Promise< void > {
await this.page.click( this.selector + `__option-0-${ selector }` );
}
async checkSelected( value: string ): Promise< void > {
const selector = this.selector + '-0__control-input';
await page.focus( selector );
const field = await this.page.$( selector );
const curValue = await field?.getProperty( 'value' );
if ( curValue ) {
const fieldValue = ( await curValue.jsonValue() ) as string;
// Only compare alphanumeric characters
expect( fieldValue?.replace( /\W/g, '' ) ).toBe(
value.replace( /\W/g, '' )
);
}
}
}

View File

@ -0,0 +1,70 @@
/**
* External dependencies
*/
import type { ElementHandle } from 'puppeteer';
/**
* Internal dependencies
*/
import { BaseElement } from './BaseElement';
import { hasClass } from '../utils/actions';
export class FormToggle extends BaseElement {
// Represents a FormToggle input. Use `selector` to represent the container its found in.
async switchOn(): Promise< void > {
const container = await this.getCheckboxContainer();
if ( container && ! ( await hasClass( container, 'is-checked' ) ) ) {
const input = await this.getCheckboxInput();
if ( ! input ) {
throw new Error(
`Could not find form toggle with selector ${ this.selector }`
);
}
input.click();
// Wait for it to be checked.
await this.page.waitForSelector(
`${ this.selector } .components-form-toggle.is-checked`
);
}
}
async switchOff(): Promise< void > {
const container = await this.getCheckboxContainer();
if ( container && ( await hasClass( container, 'is-checked' ) ) ) {
const input = await this.getCheckboxInput();
if ( ! input ) {
throw new Error(
`Could not find form toggle with selector ${ this.selector }`
);
}
input.click();
// Wait for a not checked toggle to be present.
await page.waitForFunction(
( selector ) => {
return document.querySelectorAll( selector ).length;
},
{},
`${ this.selector } .components-form-toggle:not(.is-checked)`
);
}
}
async getCheckboxContainer(): Promise< ElementHandle< Element > | null > {
return this.page.$( `${ this.selector } .components-form-toggle` );
}
async getCheckboxInput(): Promise< ElementHandle< Element > | null > {
return this.page.$(
`${ this.selector } .components-form-toggle__input`
);
}
async isEnabled(): Promise< void > {
await this.page.waitForSelector(
`${ this.selector } .components-form-toggle.is-checked`
);
}
}

View File

@ -0,0 +1,38 @@
/**
* External dependencies
*/
import { Page } from 'puppeteer';
/**
* Internal dependencies
*/
import { getElementByText, waitForElementByText } from '../utils/actions';
import { BaseElement } from './BaseElement';
export class HelpMenu extends BaseElement {
protected helpMenuId = '#contextual-help-columns';
constructor( page: Page ) {
super( page, '' );
}
async openHelpMenu(): Promise< void > {
const el = await getElementByText( 'button', 'Help' );
await el?.click();
}
async openSetupWizardTab(): Promise< void > {
const el = await waitForElementByText( '*', 'Setup wizard' );
await el?.click();
}
async enableTaskList(): Promise< void > {
await this.openSetupWizardTab();
const enableLink = await getElementByText(
'*',
'Enable',
this.helpMenuId
);
await enableLink?.click();
}
}

View File

@ -0,0 +1,32 @@
/**
* External dependencies
*/
import { ElementHandle, Page } from 'puppeteer';
/**
* Internal dependencies
*/
import { BaseElement } from './BaseElement';
export class OrdersActivityPanel extends BaseElement {
constructor( page: Page ) {
super( page, '.woocommerce-order-activity-card' );
}
async getDisplayedOrders(): Promise< string[] > {
await this.page.waitForSelector(
'.woocommerce-order-activity-card h3'
);
const list = await this.page.$$(
'.woocommerce-order-activity-card h3'
);
return Promise.all(
list.map( async ( item: ElementHandle ) => {
const textContent = await page.evaluate(
( el ) => el.textContent,
item
);
return textContent.trim();
} )
);
}
}

View File

@ -0,0 +1,13 @@
/**
* Internal dependencies
*/
import { httpClient } from './http-client';
const actionSchedulerEndpoint = '/woocommerce-reset/v1/cron/run';
export async function runActionScheduler() {
const response = await httpClient.post( actionSchedulerEndpoint );
if ( response.statusCode !== 404 ) {
expect( response.statusCode ).toEqual( 200 );
}
}

View File

@ -0,0 +1,15 @@
/**
* External dependencies
*/
import { HTTPClientFactory } from '@woocommerce/api';
/* eslint-disable @typescript-eslint/no-var-requires */
const config = require( 'config' );
// Prepare the HTTP client that will be consumed by the repository.
// This is necessary so that it can make requests to the REST API.
const admin = config.get( 'users.admin' );
const url = config.get( 'url' );
export const httpClient = HTTPClientFactory.build( url )
.withBasicAuth( admin.username, admin.password )
.create();

View File

@ -0,0 +1,4 @@
export * from './orders';
export * from './options';
export * from './reset';
export * from './action-scheduler';

View File

@ -0,0 +1,23 @@
/**
* Internal dependencies
*/
import { httpClient } from './http-client';
const optionsEndpoint = '/wc-admin/options';
export async function updateOption(
optionName: string,
optionValue: string
): Promise< void > {
const response = await httpClient.post( optionsEndpoint, {
[ optionName ]: optionValue,
} );
expect( response.statusCode ).toEqual( 200 );
}
export async function unhideTaskList( id: string ): Promise< void > {
const response = await httpClient.post(
`/wc-admin/onboarding/tasks/${ id }/unhide`
);
expect( response.statusCode ).toEqual( 200 );
}

View File

@ -0,0 +1,27 @@
/**
* External dependencies
*/
import { Order } from '@woocommerce/api';
/**
* Internal dependencies
*/
import { httpClient } from './http-client';
const repository = Order.restRepository( httpClient );
export async function createOrder( status = 'completed' ): Promise< Order > {
// The repository can now be used to create models.
return await repository.create( {
paymentMethod: 'cod',
status,
} );
}
export async function removeAllOrders(): Promise< ( boolean | undefined )[] > {
const products = await repository.list();
return await Promise.all(
products
.map( ( pr ) => ( pr.id ? repository.delete( pr.id ) : undefined ) )
.filter( ( pr ) => !! pr )
);
}

View File

@ -0,0 +1,70 @@
/**
* Internal dependencies
*/
import { httpClient } from './http-client';
/* eslint-disable @typescript-eslint/no-var-requires */
const { utils } = require( '@woocommerce/e2e-utils' );
const wpPluginsEndpoint = '/wp/v2/plugins';
type Plugin = {
author: string;
name: string;
plugin: string;
plugin_uri: string;
status: 'active' | 'inactive';
version: string;
description: {
raw: string;
rendered: string;
};
};
export async function getPlugins(): Promise< Plugin[] > {
const response = await httpClient.get( wpPluginsEndpoint );
expect( response.statusCode ).toEqual( 200 );
return response.data;
}
export async function deletePlugin( pluginName: string ) {
const response = await httpClient.delete(
wpPluginsEndpoint + '/' + pluginName
);
expect( response.statusCode ).toEqual( 200 );
}
export async function deactivatePlugin( pluginName: string ) {
const response = await httpClient.post(
wpPluginsEndpoint + '/' + pluginName,
{
status: 'inactive',
}
);
expect( response.statusCode ).toEqual( 200 );
}
async function deactivateAndDeletePlugin( pluginName: string ) {
await deactivatePlugin( pluginName );
await deletePlugin( pluginName );
}
export async function deactivateAndDeleteAllPlugins( except: string[] = [] ) {
let plugins = await getPlugins();
const skippedPlugins = [];
const promises = [];
for ( const plugin of plugins ) {
const splitPluginName = plugin.plugin.split( '/' );
const slug = splitPluginName[ 1 ] || splitPluginName[ 0 ];
const slugFromName = utils.getSlug(
plugin.name.replace( ' &amp;', '' )
);
if ( ! except.includes( slug ) && ! except.includes( slugFromName ) ) {
promises.push( deactivateAndDeletePlugin( plugin.plugin ) );
} else {
skippedPlugins.push( slug );
}
}
await Promise.all( promises );
plugins = await getPlugins();
expect( plugins.length ).toEqual( skippedPlugins.length );
}

View File

@ -0,0 +1,33 @@
/**
* Internal dependencies
*/
import { httpClient } from './http-client';
import { deactivateAndDeleteAllPlugins } from './plugins';
/* eslint-disable @typescript-eslint/no-var-requires */
const { utils } = require( '@woocommerce/e2e-utils' );
const { PLUGIN_NAME } = process.env;
const resetEndpoint = '/woocommerce-reset/v1/state';
const pluginName = PLUGIN_NAME ? PLUGIN_NAME : 'WooCommerce';
const pluginNameSlug = utils.getSlug( pluginName );
const skippedPlugins = [
'woocommerce',
'woocommerce-admin',
'woocommerce-reset',
'basic-auth',
'wp-mail-logging',
pluginNameSlug,
];
export async function resetWooCommerceState() {
const response = await httpClient.delete( resetEndpoint );
expect( response.data.options ).toEqual( true );
expect( response.data.transients ).toEqual( true );
expect( response.data.notes ).toEqual( true );
expect( response.statusCode ).toEqual( 200 );
await deactivateAndDeleteAllPlugins( skippedPlugins );
}

View File

@ -0,0 +1,10 @@
/**
* External dependencies
*/
import { Browser, Page } from 'puppeteer';
declare global {
const page: Page;
const browser: Browser;
const browserName: string;
}

View File

@ -0,0 +1 @@
export * from './specs';

View File

@ -0,0 +1,8 @@
/**
* Internal dependencies
*/
import { BasePage } from './BasePage';
export class AllOrdersView extends BasePage {
url = 'wp-admin/edit.php?post_type=shop_order';
}

View File

@ -0,0 +1,32 @@
/**
* Internal dependencies
*/
import { BasePage } from './BasePage';
export type AnalyticsSection =
| 'overview'
| 'products'
| 'revenue'
| 'orders'
| 'variations'
| 'categories'
| 'coupons'
| 'taxes'
| 'downloads'
| 'stock'
| 'settings';
export class Analytics extends BasePage {
// If you need to navigate to the base analytics page you can go to the overview
url = 'wp-admin/admin.php?page=wc-admin&path=%2Fanalytics%2Foverview';
// If you need to go to a specific single page of the analytics use `navigateToSection`
async navigateToSection( section: AnalyticsSection ): Promise< void > {
await this.goto( this.url.replace( 'overview', section ) );
}
async isDisplayed(): Promise< void > {
// This is a smoke test that ensures the single page was rendered without crashing
await this.page.waitForSelector( '#woocommerce-layout__primary' );
}
}

View File

@ -0,0 +1,154 @@
/**
* External dependencies
*/
import { ElementHandle } from 'puppeteer';
/**
* Internal dependencies
*/
import {
waitForElementByText,
waitUntilElementStopsMoving,
} from '../utils/actions';
import { Analytics } from './Analytics';
type Section = {
title: string;
element: ElementHandle< Element >;
};
const isSection = ( item: Section | undefined ): item is Section => {
return !! item;
};
export class AnalyticsOverview extends Analytics {
async navigate(): Promise< void > {
await this.navigateToSection( 'overview' );
}
async getSections(): Promise< Section[] > {
const list = await this.page.$$(
'.woocommerce-dashboard-section .woocommerce-section-header'
);
const sections = await Promise.all(
list.map( async ( item ) => {
const title = await item.evaluate( ( element ) => {
const header = element.querySelector( 'h2' );
return header?.textContent;
} );
if ( title ) {
return {
title,
element: item,
};
}
return undefined;
} )
);
return sections.filter( isSection );
}
async getSectionTitles(): Promise< string[] > {
const sections = ( await this.getSections() ).map(
( section ) => section.title
);
return sections;
}
async openSectionEllipsis( sectionTitle: string ): Promise< void > {
const section = ( await this.getSections() ).find(
( thisSection ) => thisSection.title === sectionTitle
);
if ( section ) {
const ellipsisMenu = await section.element.$(
'.woocommerce-ellipsis-menu .woocommerce-ellipsis-menu__toggle'
);
await ellipsisMenu?.click();
await this.page.waitForSelector(
'.woocommerce-ellipsis-menu div[role=menu]'
);
}
}
async closeSectionEllipsis( sectionTitle: string ): Promise< void > {
const section = ( await this.getSections() ).find(
( thisSection ) => thisSection.title === sectionTitle
);
if ( section ) {
const ellipsisMenu = await section.element.$(
'.woocommerce-ellipsis-menu .woocommerce-ellipsis-menu__toggle'
);
await ellipsisMenu?.click();
await page.waitForFunction(
() =>
! document.querySelector(
'.woocommerce-ellipsis-menu div[role=menu]'
)
);
}
}
async removeSection( sectionTitle: string ): Promise< void > {
await this.openSectionEllipsis( sectionTitle );
const item = await waitForElementByText( 'div', 'Remove section' );
await item?.click();
}
async addSection( sectionTitle: string ): Promise< void > {
await this.page.waitForSelector( "button[title='Add more sections']" );
await this.page.click( "button[title='Add more sections']" );
const addSectionSelector = `button[title='Add ${ sectionTitle } section']`;
await this.page.waitForSelector( addSectionSelector );
await waitUntilElementStopsMoving( addSectionSelector );
await this.page.click( addSectionSelector );
}
async moveSectionDown( sectionTitle: string ): Promise< void > {
await this.openSectionEllipsis( sectionTitle );
const item = await waitForElementByText( 'div', 'Move down' );
await item?.click();
}
async moveSectionUp( sectionTitle: string ): Promise< void > {
await this.openSectionEllipsis( sectionTitle );
const item = await waitForElementByText( 'div', 'Move up' );
await item?.click();
}
async getEllipsisMenuItems(
sectionTitle: string
): Promise<
{ title: string | null; element: ElementHandle< Element > }[]
> {
await this.openSectionEllipsis( sectionTitle );
const list = await this.page.$$(
'.woocommerce-ellipsis-menu div[role=menuitem]'
);
return Promise.all(
list.map( async ( item ) => ( {
title: await item.evaluate(
( element ) => element?.textContent
),
element: item,
} ) )
);
}
async getEllipsisMenuCheckboxItems(
sectionTitle: string
): Promise<
{ title: string | null; element: ElementHandle< Element > }[]
> {
await this.openSectionEllipsis( sectionTitle );
const list = await this.page.$$(
'.woocommerce-ellipsis-menu div[role=menuitemcheckbox]'
);
return Promise.all(
list.map( async ( item ) => ( {
title: await item.evaluate(
( element ) => element?.textContent
),
element: item,
} ) )
);
}
}

View File

@ -0,0 +1,162 @@
/**
* External dependencies
*/
import { ElementHandle, Page } from 'puppeteer';
/**
* Internal dependencies
*/
import { DropdownField } from '../elements/DropdownField';
import { DropdownTypeaheadField } from '../elements/DropdownTypeaheadField';
import { FormToggle } from '../elements/FormToggle';
import { getElementByText, waitForTimeout } from '../utils/actions';
/* eslint-disable @typescript-eslint/no-var-requires */
const config = require( 'config' );
/* eslint-enable @typescript-eslint/no-var-requires */
const baseUrl = config.get( 'url' );
// Represents a page that can be navigated to
export abstract class BasePage {
protected page: Page;
protected url = '';
protected baseUrl: string = baseUrl;
// cache of elements that have been setup, note that they are unique "per page/per selector"
private dropDownElements: Record< string, DropdownField > = {};
private dropDownTypeAheadElements: Record<
string,
DropdownTypeaheadField
> = {};
private formToggleElements: Record< string, FormToggle > = {};
constructor( page: Page ) {
this.page = page;
}
getDropdownField( selector: string ): DropdownField {
if ( ! this.dropDownElements[ selector ] ) {
this.dropDownElements[ selector ] = new DropdownField(
page,
selector
);
}
return this.dropDownElements[ selector ];
}
getDropdownTypeahead( selector: string ): DropdownTypeaheadField {
if ( ! this.dropDownTypeAheadElements[ selector ] ) {
this.dropDownTypeAheadElements[
selector
] = new DropdownTypeaheadField( page, selector );
}
return this.dropDownTypeAheadElements[ selector ];
}
getFormToggle( selector: string ): FormToggle {
if ( ! this.formToggleElements[ selector ] ) {
this.formToggleElements[ selector ] = new FormToggle(
page,
selector
);
}
return this.formToggleElements[ selector ];
}
async click( selector: string ): Promise< void > {
await this.page.waitForSelector( selector );
await this.page.click( selector );
}
async clickButtonWithText( text: string ): Promise< void > {
const el = await getElementByText( 'button', text );
await el?.click();
}
async clickElementWithText(
element: string,
text: string
): Promise< void > {
const el = await getElementByText( element, text );
await el?.click();
}
async setCheckboxWithText( text: string ): Promise< void > {
let checkbox = await getElementByText( 'label', text );
if ( ! checkbox ) {
checkbox = await getElementByText( 'span', text );
}
if ( checkbox ) {
const checkboxStatus = await (
await checkbox.getProperty( 'checked' )
).jsonValue();
if ( checkboxStatus !== true ) {
await checkbox.click();
}
} else {
throw new Error( `Could not find checkbox with text "${ text }"` );
}
}
async unsetAllCheckboxes( selector: string ): Promise< void > {
const checkboxes = await page.$$( selector );
// Uncheck all checkboxes, to avoid installing plugins
for ( const checkbox of checkboxes ) {
await this.toggleCheckbox( checkbox, false );
await waitForTimeout( 200 );
}
}
async setAllCheckboxes( selector: string ): Promise< void > {
const checkboxes = await page.$$( selector );
// Uncheck all checkboxes, to avoid installing plugins
for ( const checkbox of checkboxes ) {
await this.toggleCheckbox( checkbox, true );
await waitForTimeout( 200 );
}
}
// Set or unset a checkbox based on `checked` value passed.
async toggleCheckbox(
checkbox: ElementHandle< Element >,
checked: boolean
): Promise< void > {
const checkboxStatus = await (
await checkbox.getProperty( 'checked' )
).jsonValue();
if ( checkboxStatus !== checked ) {
await checkbox.click();
}
}
async navigate(): Promise< void > {
if ( ! this.url ) {
throw new Error( 'You must define a url for the page object' );
}
await this.goto( this.url );
}
protected async goto( url: string ): Promise< void > {
const fullUrl = baseUrl + url;
try {
await this.page.goto( fullUrl, {
waitUntil: 'networkidle0',
timeout: 10000,
} );
} catch ( e ) {
if ( e instanceof Error ) {
throw new Error(
`Could not navigate to url: ${ fullUrl } with error: ${ e.message }`
);
}
}
}
}

View File

@ -0,0 +1,13 @@
/**
* Internal dependencies
*/
import { BasePage } from './BasePage';
export class Coupons extends BasePage {
url = 'wp-admin/edit.php?post_type=shop_coupon&legacy_coupon_menu=1';
async isDisplayed(): Promise< void > {
// This is a smoke test that ensures the single page was rendered without crashing
await this.page.waitForSelector( '#woocommerce-layout__primary' );
}
}

View File

@ -0,0 +1,9 @@
/**
* Internal dependencies
*/
import { Analytics } from './Analytics';
export class Customers extends Analytics {
// The analytics pages are `analytics-{slug}`.
url = 'wp-admin/admin.php?page=wc-admin&path=%2Fcustomers';
}

View File

@ -0,0 +1,8 @@
/**
* Internal dependencies
*/
import { BasePage } from './BasePage';
export class Dashboard extends BasePage {
url = 'wp-admin';
}

View File

@ -0,0 +1,53 @@
/**
* Internal dependencies
*/
import { getElementByText } from '../utils/actions';
import { BasePage } from './BasePage';
/* eslint-disable @typescript-eslint/no-var-requires */
const { clearAndFillInput } = require( '@woocommerce/e2e-utils' );
const config = require( 'config' );
export class Login extends BasePage {
url = 'wp-login.php';
async login(): Promise< void > {
await this.navigate();
await getElementByText( 'label', 'Username or Email Address' );
await clearAndFillInput( '#user_login', ' ' );
await this.page.type(
'#user_login',
config.get( 'users.admin.username' )
);
await this.page.type(
'#user_pass',
config.get( 'users.admin.password' )
);
await Promise.all( [
this.page.click( 'input[type=submit]' ),
this.page.waitForNavigation( {
waitUntil: 'networkidle0',
timeout: 10000,
} ),
] );
}
async logout(): Promise< void > {
// Log out link in admin bar is not visible so can't be clicked directly.
const logoutLinks = await this.page.$$eval(
'#wp-admin-bar-logout a',
( am ) =>
am
.filter( ( e ) => ( e as HTMLLinkElement ).href )
.map( ( e ) => ( e as HTMLLinkElement ).href )
);
await page.goto( logoutLinks[ 0 ], {
waitUntil: 'networkidle0',
timeout: 10000,
} );
}
}

View File

@ -0,0 +1,8 @@
/**
* Internal dependencies
*/
import { BasePage } from './BasePage';
export class NewCoupon extends BasePage {
url = 'wp-admin/post-new.php?post_type=shop_coupon';
}

View File

@ -0,0 +1,8 @@
/**
* Internal dependencies
*/
import { BasePage } from './BasePage';
export class NewOrder extends BasePage {
url = 'wp-admin/post-new.php?post_type=shop_order';
}

View File

@ -0,0 +1,8 @@
/**
* Internal dependencies
*/
import { BasePage } from './BasePage';
export class NewProduct extends BasePage {
url = 'wp-admin/post-new.php?post_type=product';
}

View File

@ -0,0 +1,157 @@
/**
* External dependencies
*/
import { Page } from 'puppeteer';
/**
* Internal dependencies
*/
import { BusinessSection } from '../sections/onboarding/BusinessSection';
import { IndustrySection } from '../sections/onboarding/IndustrySection';
import { ProductTypeSection } from '../sections/onboarding/ProductTypesSection';
import {
StoreDetails,
StoreDetailsSection,
} from '../sections/onboarding/StoreDetailsSection';
import { ThemeSection } from '../sections/onboarding/ThemeSection';
import { BasePage } from './BasePage';
/* eslint-disable @typescript-eslint/no-var-requires */
const { expect } = require( '@jest/globals' );
const config = require( 'config' );
export class OnboardingWizard extends BasePage {
url = 'wp-admin/admin.php?page=wc-admin&path=/setup-wizard';
storeDetails: StoreDetailsSection;
industry: IndustrySection;
productTypes: ProductTypeSection;
business: BusinessSection;
themes: ThemeSection;
constructor( page: Page ) {
super( page );
this.storeDetails = new StoreDetailsSection( page );
this.industry = new IndustrySection( page );
this.productTypes = new ProductTypeSection( page );
this.business = new BusinessSection( page );
this.themes = new ThemeSection( page );
}
async skipStoreSetup(): Promise< void > {
await this.clickButtonWithText( 'Skip setup store details' );
await this.optionallySelectUsageTracking( false );
}
async continue(): Promise< void > {
await this.clickButtonWithText( 'Continue' );
}
async optionallySelectUsageTracking( select = false ): Promise< void > {
const usageTrackingHeader = await this.page.waitForSelector(
'.components-modal__header-heading',
{
timeout: 5000,
}
);
if ( ! usageTrackingHeader ) {
return;
}
await expect( page ).toMatchElement(
'.components-modal__header-heading',
{
text: 'Build a better WooCommerce',
}
);
// Query for primary buttons: "Continue" and "Yes, count me in"
const primaryButtons = await this.page.$$( 'button.is-primary' );
expect( primaryButtons ).toHaveLength( 2 );
if ( select ) {
await this.clickButtonWithText( 'Yes, count me in' );
} else {
await this.clickButtonWithText( 'No thanks' );
}
await this.page.waitForNavigation( {
waitUntil: 'networkidle0',
timeout: 4000,
} );
}
async goToOBWStep( step: string ): Promise< void > {
await this.clickElementWithText( 'span', step );
}
async walkThroughAndCompleteOnboardingWizard(
options: {
storeDetails?: StoreDetails;
industries?: string[];
products?: string[];
businessDetails?: {
productNumber: string;
currentlySelling: string;
};
themeTitle?: string;
} = {}
): Promise< void > {
await this.navigate();
await this.storeDetails.completeStoreDetailsSection(
options.storeDetails
);
// Wait for "Continue" button to become active
await this.continue();
// Wait for usage tracking pop-up window to appear
await this.optionallySelectUsageTracking();
// Query for the industries checkboxes
await this.industry.isDisplayed();
const industries = options.industries || [ 'Other' ];
for ( const industry of industries ) {
await this.industry.selectIndustry( industry );
}
await this.continue();
await this.productTypes.isDisplayed( 7 );
const products = options.products || [
'Physical products',
'Downloads',
];
for ( const product of products ) {
await this.productTypes.selectProduct( product );
}
await this.continue();
await page.waitForNavigation( {
waitUntil: 'networkidle0',
} );
await this.business.isDisplayed();
const businessDetails = options.businessDetails || {
productNumber: config.get( 'onboardingwizard.numberofproducts' ),
currentlySelling: config.get( 'onboardingwizard.sellingelsewhere' ),
};
await this.business.selectProductNumber(
businessDetails.productNumber
);
await this.business.selectCurrentlySelling(
businessDetails.currentlySelling
);
await this.continue();
await this.business.freeFeaturesIsDisplayed();
await this.business.expandRecommendedBusinessFeatures();
await this.business.uncheckAllRecommendedBusinessFeatures();
await this.continue();
await this.themes.isDisplayed();
// This navigates to the home screen
if ( options.themeTitle ) {
await this.themes.continueWithTheme( options.themeTitle );
} else {
await this.themes.continueWithActiveTheme();
}
}
}

View File

@ -0,0 +1,63 @@
/**
* Internal dependencies
*/
import { waitForElementByText, getElementByText } from '../utils/actions';
import { BasePage } from './BasePage';
type PaymentMethodWithSetupButton =
| 'wcpay'
| 'stripe'
| 'paypal'
| 'klarna_payments'
| 'mollie'
| 'bacs';
type PaymentMethod = PaymentMethodWithSetupButton | 'cod';
export class PaymentsSetup extends BasePage {
url = 'wp-admin/admin.php?page=wc-admin&task=payments';
async isDisplayed(): Promise< void > {
await waitForElementByText( 'h1', 'Set up payments' );
}
async possiblyCloseHelpModal(): Promise< void > {
try {
await waitForElementByText( 'div', "We're here for help", {
timeout: 2000,
} );
await this.clickButtonWithText( 'Got it' );
} catch ( e ) {}
}
async showOtherPaymentMethods(): Promise< void > {
const selector = '.woocommerce-task-payments button.toggle-button';
await this.page.waitForSelector( selector );
const toggleButton = await this.page.$(
`${ selector }[aria-expanded=false]`
);
await toggleButton?.click();
await waitForElementByText( 'h2', 'Offline payment methods' );
}
async goToPaymentMethodSetup(
method: PaymentMethodWithSetupButton
): Promise< void > {
const selector = `.woocommerce-task-payment-${ method } button`;
await this.page.waitForSelector( selector );
const button = await this.page.$( selector );
if ( ! button ) {
throw new Error(
`Could not find button with selector: ${ selector }`
);
} else {
await button.click();
}
}
async enableCashOnDelivery(): Promise< void > {
await this.page.waitForSelector( '.woocommerce-task-payment-cod' );
await this.clickButtonWithText( 'Enable' );
}
}

View File

@ -0,0 +1,8 @@
/**
* Internal dependencies
*/
import { BasePage } from './BasePage';
export class PermalinkSettings extends BasePage {
url = 'wp-admin/options-permalink.php';
}

View File

@ -0,0 +1,8 @@
/**
* Internal dependencies
*/
import { BasePage } from './BasePage';
export class Plugins extends BasePage {
url = 'wp-admin/plugins.php';
}

View File

@ -0,0 +1,28 @@
/**
* Internal dependencies
*/
import { waitForElementByText } from '../utils/actions';
import { BasePage } from './BasePage';
export class ProductsSetup extends BasePage {
url = 'wp-admin/admin.php?page=wc-admin&task=products';
async isDisplayed(): Promise< void > {
await waitForElementByText( 'h1', 'Add my products' );
}
async isStartWithATemplateDisplayed(
templatesCount: number
): Promise< void > {
await waitForElementByText( 'h1', 'Start with a template' );
const length = await this.page.$$eval(
'.components-radio-control__input',
( items ) => items.length
);
expect( length === templatesCount ).toBeTruthy();
}
async clickStartWithTemplate(): Promise< void > {
await this.clickElementWithText( '*', 'Start with a template' );
}
}

View File

@ -0,0 +1,145 @@
/**
* External dependencies
*/
import { ElementHandle } from 'puppeteer';
/**
* Internal dependencies
*/
import {
waitForElementByText,
getElementByAttributeAndValue,
waitForElementByTextWithoutThrow,
getElementByText,
waitForTimeout,
} from '../utils/actions';
import { BasePage } from './BasePage';
export class WcHomescreen extends BasePage {
url = 'wp-admin/admin.php?page=wc-admin';
async isDisplayed(): Promise< void > {
// Wait for Benefits section to appear
await waitForElementByText( 'h1', 'Home' );
}
async possiblyDismissWelcomeModal(): Promise< void > {
const modal = await this.isWelcomeModalVisible();
if ( modal ) {
await this.clickButtonWithText( 'Next' );
await waitForTimeout( 1000 );
await this.clickButtonWithText( 'Next' );
await waitForTimeout( 1000 );
await this.click( '.components-guide__finish-button' );
await waitForTimeout( 500 );
}
}
async isWelcomeModalVisible(): Promise< boolean > {
const modalText = 'Welcome to your WooCommerce stores online HQ!';
const modal = await waitForElementByTextWithoutThrow(
'h2',
modalText,
10
);
return modal;
}
async getTaskList(): Promise< Array< string | null > > {
await page.waitForSelector(
'.woocommerce-task-card .woocommerce-task-list__item-title'
);
await waitForElementByText( '*', 'Get ready to start selling' );
const list = await this.page.$$eval(
'.woocommerce-task-card .woocommerce-task-list__item-title',
( items ) => items.map( ( item ) => item.textContent )
);
return list.map( ( item: string | null ) => {
const match = item?.match( /(.+)[0-9] minute/ );
if ( match && match.length > 1 ) {
return match[ 1 ];
}
return item;
} );
}
async isTaskListDisplayed(): Promise< boolean > {
return !! ( await waitForElementByTextWithoutThrow(
'*',
'Get ready to start selling'
) );
}
async clickOnTaskList( taskTitle: string ): Promise< void > {
const item = await waitForElementByText( '*', taskTitle );
if ( ! item ) {
throw new Error(
`Could not find task list item with title: ${ taskTitle }`
);
} else {
await item.click();
await waitForElementByText( 'h1', taskTitle );
}
}
async hideTaskList(): Promise< void > {
const taskListOptions = await getElementByAttributeAndValue(
'button',
'title',
'Task List Options'
);
await taskListOptions?.click();
await waitForElementByText( 'button', 'Hide this' );
await waitForTimeout( 200 ); // Transition of popup.
const hideThisButton = await getElementByText( 'button', 'Hide this' );
await hideThisButton?.click();
await waitForTimeout( 500 );
}
async waitForNotesRequestToBeLoaded(): Promise< void > {
await this.page.waitForResponse( ( response ) => {
const url = encodeURIComponent( response.url() );
return url.includes( '/wc-analytics/admin/notes' ) && response.ok();
} );
}
async isActivityPanelShown(): Promise< boolean > {
return !! ( await this.page.$( '.woocommerce-activity-panel' ) );
}
async getActivityPanels(): Promise<
Array< { title: string; count?: number; element?: ElementHandle } >
> {
const panelContainer = await page.waitForSelector(
'.woocommerce-activity-panel'
);
const list = await panelContainer.$$( 'h2' );
return Promise.all(
list.map( async ( item: ElementHandle ) => {
const textContent = await page.evaluate(
( el ) => el.textContent,
item
);
const match = textContent?.match( /([a-zA-Z]+)([0-9]+)/ );
if ( match && match.length > 2 ) {
return {
title: match[ 1 ],
count: parseInt( match[ 2 ], 10 ),
element: item,
};
}
return { title: textContent };
} )
);
}
async expandActivityPanel( title: string ): Promise< void > {
const activityPanels = await this.getActivityPanels();
const panel = activityPanels.find( ( p ) => p.title === title );
if ( panel ) {
await panel.element?.click();
}
}
}

View File

@ -0,0 +1,76 @@
/**
* Internal dependencies
*/
import { getAttribute, hasClass, waitForElementByText } from '../utils/actions';
import { BasePage } from './BasePage';
/* eslint-disable @typescript-eslint/no-var-requires */
const { setCheckbox } = require( '@woocommerce/e2e-utils' );
/* eslint-enable @typescript-eslint/no-var-requires */
export class WcSettings extends BasePage {
url = 'wp-admin/admin.php?page=wc-settings';
async navigate( tab = 'general', section = '' ): Promise< void > {
let settingsUrl = this.url + `&tab=${ tab }`;
if ( section ) {
settingsUrl += `&section=${ section }`;
}
await this.goto( settingsUrl );
await waitForElementByText( 'a', 'General' );
}
async enableTaxRates(): Promise< void > {
await waitForElementByText( 'th', 'Enable taxes' );
await setCheckbox( '#woocommerce_calc_taxes' );
}
async getTaxRateValue(): Promise< unknown > {
return await getAttribute( '#woocommerce_calc_taxes', 'checked' );
}
async saveSettings(): Promise< void > {
this.clickButtonWithText( 'Save changes' );
await this.page.waitForNavigation( {
waitUntil: 'networkidle0',
} );
await waitForElementByText(
'strong',
'Your settings have been saved.'
);
}
async paymentMethodIsEnabled( method = '' ): Promise< boolean > {
await this.navigate( 'checkout' );
await waitForElementByText( 'h2', 'Payment methods' );
const className = await getAttribute(
`tr[data-gateway_id=${ method }] .woocommerce-input-toggle`,
'className'
);
return (
( className as string ).indexOf(
'woocommerce-input-toggle--disabled'
) === -1
);
}
async cleanPaymentMethods(): Promise< void > {
await this.navigate( 'checkout' );
await waitForElementByText( 'h2', 'Payment methods' );
const paymentMethods = await page.$$( 'span.woocommerce-input-toggle' );
for ( const method of paymentMethods ) {
if (
method &&
( await hasClass(
method,
'woocommerce-input-toggle--enabled'
) )
) {
await method?.click();
}
}
await this.saveSettings();
}
}

View File

@ -0,0 +1,20 @@
/**
* Internal dependencies
*/
import { waitForElementByText } from '../utils/actions';
import { BasePage } from './BasePage';
export class WpSettings extends BasePage {
url = 'wp-admin/options-permalink.php';
async openPermalinkSettings(): Promise< void > {
await waitForElementByText( 'h1', 'Permalink Settings' );
}
async saveSettings(): Promise< void > {
await this.click( '#submit' );
await this.page.waitForNavigation( {
waitUntil: 'networkidle0',
} );
}
}

View File

@ -0,0 +1,114 @@
/**
* Internal dependencies
*/
import { BasePage } from '../../pages/BasePage';
import { waitForElementByText } from '../../utils/actions';
/* eslint-disable @typescript-eslint/no-var-requires */
const {
setCheckbox,
unsetCheckbox,
verifyCheckboxIsSet,
verifyCheckboxIsUnset,
} = require( '@woocommerce/e2e-utils' );
/* eslint-enable @typescript-eslint/no-var-requires */
export class BusinessSection extends BasePage {
async isDisplayed(): Promise< void > {
await waitForElementByText( 'h2', 'Tell us about your business' );
}
async freeFeaturesIsDisplayed(): Promise< void > {
await waitForElementByText( 'h2', 'Included business features' );
}
async selectProductNumber( productLabel: string ): Promise< void > {
const howManyProductsDropdown = this.getDropdownField(
'.woocommerce-profile-wizard__product-count'
);
await howManyProductsDropdown.select( productLabel );
}
async selectCurrentlySelling( currentlySelling: string ): Promise< void > {
const sellingElsewhereDropdown = this.getDropdownField(
'.woocommerce-profile-wizard__selling-venues'
);
await sellingElsewhereDropdown.select( currentlySelling );
}
async selectEmployeesNumber( employeesNumber: string ) {
const employeesNumberDropdown = this.getDropdownField(
'.woocommerce-profile-wizard__number-employees'
);
await employeesNumberDropdown.select( employeesNumber );
}
async selectRevenue( revenue: string ) {
const revenueDropdown = this.getDropdownField(
'.woocommerce-profile-wizard__revenue'
);
await revenueDropdown.select( revenue );
}
async selectOtherPlatformName( otherPlatformName: string ) {
const otherPlatformNameDropdown = this.getDropdownField(
'.woocommerce-profile-wizard__other-platform'
);
await otherPlatformNameDropdown.select( otherPlatformName );
}
async selectInstallFreeBusinessFeatures(
select: boolean
): Promise< void > {
if ( select ) {
await setCheckbox( '#woocommerce-business-extensions__checkbox' );
} else {
await unsetCheckbox( '#woocommerce-business-extensions__checkbox' );
}
}
async expandRecommendedBusinessFeatures(): Promise< void > {
const expandButtonSelector =
'.woocommerce-admin__business-details__selective-extensions-bundle__expand';
await this.page.waitForSelector(
expandButtonSelector + ':not([disabled])'
);
await this.click( expandButtonSelector );
// Confirm that expanding the list shows all the extensions available to install.
await this.page.waitForFunction( () => {
const inputsNum = document.querySelectorAll(
'.components-checkbox-control__input'
).length;
return inputsNum > 1;
} );
}
async uncheckAllRecommendedBusinessFeatures(): Promise< void > {
await this.unsetAllCheckboxes( '.components-checkbox-control__input' );
}
// The old list displayed on the dropdown page
async uncheckBusinessFeatures(): Promise< void > {
await this.unsetAllCheckboxes(
'.woocommerce-profile-wizard__benefit .components-form-toggle__input'
);
}
async selectSetupForClient(): Promise< void > {
await setCheckbox( '.components-checkbox-control__input' );
}
async checkClientSetupCheckbox( selected: boolean ): Promise< void > {
if ( selected ) {
await verifyCheckboxIsSet( '.components-checkbox-control__input' );
} else {
await verifyCheckboxIsUnset(
'.components-checkbox-control__input'
);
}
}
}

View File

@ -0,0 +1,40 @@
/**
* Internal dependencies
*/
import { BasePage } from '../../pages/BasePage';
import { waitForElementByText } from '../../utils/actions';
export class IndustrySection extends BasePage {
async isDisplayed(
industryCount?: number,
industryCountMax?: number
): Promise< void > {
await waitForElementByText(
'h2',
'In which industry does the store operate?'
);
if ( industryCount ) {
const length = await this.page.$$eval(
'.components-checkbox-control__input',
( items ) => items.length
);
if ( industryCountMax ) {
expect(
length >= industryCount && length <= industryCountMax
).toBeTruthy();
} else {
expect( length === industryCount ).toBeTruthy();
}
}
}
async uncheckIndustries(): Promise< void > {
await this.unsetAllCheckboxes( '.components-checkbox-control__input' );
}
async selectIndustry( industryLabel: string ): Promise< void > {
await this.setCheckboxWithText( industryLabel );
}
}

View File

@ -0,0 +1,27 @@
/**
* Internal dependencies
*/
import { BasePage } from '../../pages/BasePage';
import { waitForElementByText } from '../../utils/actions';
export class ProductTypeSection extends BasePage {
async isDisplayed( productCount: number ): Promise< void > {
await waitForElementByText(
'h2',
'What type of products will be listed?'
);
const length = await this.page.$$eval(
'.components-checkbox-control__input',
( items ) => items.length
);
expect( length === productCount ).toBeTruthy();
}
async uncheckProducts(): Promise< void > {
await this.unsetAllCheckboxes( '.components-checkbox-control__input' );
}
async selectProduct( productLabel: string ): Promise< void > {
await this.setCheckboxWithText( productLabel );
}
}

View File

@ -0,0 +1,124 @@
/**
* Internal dependencies
*/
import { DropdownTypeaheadField } from '../../elements/DropdownTypeaheadField';
import { BasePage } from '../../pages/BasePage';
import { waitForElementByText } from '../../utils/actions';
/* eslint-disable @typescript-eslint/no-var-requires */
const {
clearAndFillInput,
verifyCheckboxIsSet,
verifyCheckboxIsUnset,
} = require( '@woocommerce/e2e-utils' );
const config = require( 'config' );
/* eslint-enable @typescript-eslint/no-var-requires */
export interface StoreDetails {
addressLine1?: string;
addressLine2?: string;
countryRegionSubstring?: string;
countryRegionSelector?: string;
countryRegion?: string;
city?: string;
postcode?: string;
storeEmail?: string;
}
export class StoreDetailsSection extends BasePage {
private get countryDropdown(): DropdownTypeaheadField {
return this.getDropdownTypeahead( '#woocommerce-select-control' );
}
async isDisplayed(): Promise< void > {
await waitForElementByText( 'h2', 'Welcome to WooCommerce' );
}
async completeStoreDetailsSection(
storeDetails: StoreDetails = {}
): Promise< void > {
// const onboardingWizard = new OnboardingWizard( page );
// Fill store's address - first line
await this.fillAddress(
storeDetails.addressLine1 ||
config.get( 'addresses.admin.store.addressfirstline' )
);
// Fill store's address - second line
await this.fillAddressLineTwo(
storeDetails.addressLine2 ||
config.get( 'addresses.admin.store.addresssecondline' )
);
// Type the requested country/region substring or 'cali' in the
// country/region select, then select the requested country/region
// substring or 'US:CA'.
await this.selectCountry(
storeDetails.countryRegionSubstring || 'cali',
storeDetails.countryRegionSelector || 'US\\:CA'
);
if ( storeDetails.countryRegion ) {
await this.checkCountrySelected( storeDetails.countryRegion );
}
// Fill the city where the store is located
await this.fillCity(
storeDetails.city || config.get( 'addresses.admin.store.city' )
);
// Fill postcode of the store
await this.fillPostalCode(
storeDetails.postcode ||
config.get( 'addresses.admin.store.postcode' )
);
// Fill store's email address
await this.fillEmailAddress(
storeDetails.storeEmail ||
config.get( 'addresses.admin.store.email' )
);
// Verify that checkbox next to "Get tips, product updates and inspiration straight to your mailbox" is selected
await this.checkMarketingCheckbox( true );
}
async fillAddress( address: string ): Promise< void > {
await clearAndFillInput( '#inspector-text-control-0', address );
}
async fillAddressLineTwo( address: string ): Promise< void > {
await clearAndFillInput( '#inspector-text-control-1', address );
}
async selectCountry( search: string, selector: string ): Promise< void > {
await this.countryDropdown.search( search );
await this.countryDropdown.select( selector );
}
async checkCountrySelected( country: string ): Promise< void > {
await this.countryDropdown.checkSelected( country );
}
async fillCity( city: string ): Promise< void > {
await clearAndFillInput( '#inspector-text-control-2', city );
}
async fillPostalCode( postalCode: string ): Promise< void > {
await clearAndFillInput( '#inspector-text-control-3', postalCode );
}
async fillEmailAddress( email: string ): Promise< void > {
await clearAndFillInput( '#inspector-text-control-4', email );
}
async checkMarketingCheckbox( selected: boolean ): Promise< void > {
if ( selected ) {
await verifyCheckboxIsSet( '.components-checkbox-control__input' );
} else {
await verifyCheckboxIsUnset(
'.components-checkbox-control__input'
);
}
}
}

View File

@ -0,0 +1,29 @@
/**
* Internal dependencies
*/
import { BasePage } from '../../pages/BasePage';
import { waitForElementByText } from '../../utils/actions';
export class ThemeSection extends BasePage {
async isDisplayed(): Promise< void > {
await waitForElementByText( 'h2', 'Choose a theme' );
await waitForElementByText( 'button', 'All themes' );
}
async continueWithActiveTheme(): Promise< void > {
await this.clickButtonWithText( 'Continue with my active theme' );
}
async continueWithTheme( themeTitle: string ): Promise< void > {
const title = await waitForElementByText( 'h2', themeTitle );
const chooseButton = await title?.evaluateHandle( ( element ) => {
const card = element.closest( '.components-card' );
return Array.from( card?.querySelectorAll( 'button' ) || [] ).find(
( el ) => el.textContent === 'Choose'
);
} );
if ( chooseButton ) {
await chooseButton.asElement()?.click();
}
}
}

View File

@ -0,0 +1,42 @@
/**
* Internal dependencies
*/
import { BasePage } from '../../pages/BasePage';
/* eslint-disable @typescript-eslint/no-var-requires */
const { clearAndFillInput } = require( '@woocommerce/e2e-utils' );
/* eslint-enable @typescript-eslint/no-var-requires */
type AccountDetails = {
accountName: string;
accountNumber: string;
bankName: string;
sortCode: string;
iban: string;
swiftCode: string;
};
export class BankAccountTransferSetup extends BasePage {
url = 'wp-admin/admin.php?page=wc-admin&task=payments&method=bacs';
async saveAccountDetails( {
accountName,
accountNumber,
bankName,
sortCode,
iban,
swiftCode,
}: AccountDetails ): Promise< void > {
await clearAndFillInput( '[placeholder="Account name"]', accountName );
await clearAndFillInput(
'[placeholder="Account number"]',
accountNumber
);
await clearAndFillInput( '[placeholder="Bank name"]', bankName );
await clearAndFillInput( '[placeholder="Sort code"]', sortCode );
await clearAndFillInput( '[placeholder="IBAN"]', iban );
await clearAndFillInput( '[placeholder="BIC / Swift"]', swiftCode );
await this.clickButtonWithText( 'Save' );
}
}

View File

@ -0,0 +1,84 @@
/**
* Internal dependencies
*/
import { WcSettings } from '../../pages/WcSettings';
import { WpSettings } from '../../pages/WpSettings';
import { Login } from '../../pages/Login';
/* eslint-disable @typescript-eslint/no-var-requires */
const {
clearAndFillInput,
verifyValueOfInputField,
} = require( '@woocommerce/e2e-utils' );
const {
afterAll,
beforeAll,
describe,
it,
expect,
} = require( '@jest/globals' );
/* eslint-enable @typescript-eslint/no-var-requires */
const testAdminBasicSetup = () => {
describe( 'Store owner can finish initial store setup', () => {
const wcSettings = new WcSettings( page );
const wpSettings = new WpSettings( page );
const login = new Login( page );
beforeAll( async () => {
await login.login();
} );
afterAll( async () => {
await login.logout();
} );
it( 'can enable tax rates and calculations', async () => {
// Go to general settings page
await wcSettings.navigate( 'general' );
await wcSettings.enableTaxRates();
await wcSettings.saveSettings();
// Verify that settings have been saved
const taxRate = await wcSettings.getTaxRateValue();
expect( taxRate ).toEqual( true );
} );
it( 'can configure permalink settings', async () => {
// Go to Permalink Settings page
await wpSettings.navigate();
await wpSettings.openPermalinkSettings();
// Select "Post name" option in common settings section
await page.click( 'input[value="/%postname%/"]' );
// Select "Custom base" in product permalinks section
await page.click( '#woocommerce_custom_selection' );
// Fill custom base slug to use
await clearAndFillInput( '#woocommerce_permalink_structure', '' );
await page.type( '#woocommerce_permalink_structure', '/product/' );
await wpSettings.saveSettings();
// Verify that settings have been saved
await Promise.all( [
expect( page ).toMatchElement(
'#setting-error-settings_updated',
{
text: 'Permalink structure updated.',
}
),
verifyValueOfInputField(
'#permalink_structure',
'/%postname%/'
),
verifyValueOfInputField(
'#woocommerce_permalink_structure',
'/product/'
),
] );
} );
} );
};
module.exports = { testAdminBasicSetup };

View File

@ -0,0 +1,633 @@
/**
* Internal dependencies
*/
import { OnboardingWizard } from '../../pages/OnboardingWizard';
import { WcHomescreen } from '../../pages/WcHomescreen';
import { TaskTitles } from '../../constants/taskTitles';
import { Login } from '../../pages/Login';
import { WcSettings } from '../../pages/WcSettings';
import { ProductsSetup } from '../../pages/ProductsSetup';
import { resetWooCommerceState } from '../../fixtures/reset';
/* eslint-disable @typescript-eslint/no-var-requires */
const {
afterAll,
beforeAll,
describe,
it,
expect,
} = require( '@jest/globals' );
const config = require( 'config' );
const { verifyValueOfInputField } = require( '@woocommerce/e2e-utils' );
/* eslint-enable @typescript-eslint/no-var-requires */
/**
* This tests a default, happy path for the onboarding wizard.
*/
const testAdminOnboardingWizard = () => {
describe( 'Store owner can complete onboarding wizard', () => {
const profileWizard = new OnboardingWizard( page );
const login = new Login( page );
beforeAll( async () => {
await login.login();
await resetWooCommerceState();
} );
afterAll( async () => {
await login.logout();
} );
it( 'can start the profile wizard', async () => {
await profileWizard.navigate();
} );
it( 'can complete the store details section', async () => {
await profileWizard.storeDetails.isDisplayed();
await profileWizard.storeDetails.completeStoreDetailsSection();
// Wait for "Continue" button to become active
await profileWizard.continue();
// Wait for usage tracking pop-up window to appear
await profileWizard.optionallySelectUsageTracking();
} );
it( 'can complete the industry section', async () => {
// Query for the industries checkboxes
await profileWizard.industry.isDisplayed( 7, 8 );
// Select just "fashion" and "health/beauty" to get the single checkbox business section when
// filling out details for a US store.
await profileWizard.industry.selectIndustry(
'Fashion, apparel, and accessories'
);
await profileWizard.industry.selectIndustry( 'Health and beauty' );
await profileWizard.continue();
} );
it( 'can click industry tab after going back', async () => {
await profileWizard.navigate();
await profileWizard.goToOBWStep( 'Store Details' );
await profileWizard.storeDetails.isDisplayed();
await profileWizard.goToOBWStep( 'Industry' );
await profileWizard.industry.isDisplayed();
await profileWizard.continue();
} );
it( 'can complete the product types section', async () => {
await profileWizard.productTypes.isDisplayed( 7 );
// Select Physical and Downloadable products
await profileWizard.productTypes.selectProduct(
'Physical products'
);
await profileWizard.productTypes.selectProduct( 'Downloads' );
await profileWizard.continue();
} );
it( 'can complete the business section', async () => {
await profileWizard.business.isDisplayed();
await profileWizard.business.selectProductNumber(
config.get( 'onboardingwizard.numberofproducts' )
);
await profileWizard.business.selectCurrentlySelling(
config.get( 'onboardingwizard.sellingelsewhere' )
);
await profileWizard.business.checkClientSetupCheckbox( false );
await profileWizard.continue();
} );
it( 'can unselect all business features and continue', async () => {
await profileWizard.business.freeFeaturesIsDisplayed();
// Add WC Pay check
await profileWizard.business.expandRecommendedBusinessFeatures();
expect( page ).toMatchElement( 'a', {
text: 'WooCommerce Payments',
} );
await profileWizard.business.uncheckAllRecommendedBusinessFeatures();
await profileWizard.continue();
} );
it( 'can complete the theme selection section', async () => {
await profileWizard.themes.isDisplayed();
await profileWizard.themes.continueWithActiveTheme();
} );
it( 'can select the right currency on settings page related to the onboarding country', async () => {
const settingsScreen = new WcSettings( page );
await settingsScreen.navigate();
verifyValueOfInputField( '#woocommerce_currency', 'USD' );
} );
} );
};
const testSelectiveBundleWCPay = () => {
describe( 'A japanese store can complete the selective bundle install but does not include WCPay.', () => {
const profileWizard = new OnboardingWizard( page );
const login = new Login( page );
beforeAll( async () => {
await login.login();
await resetWooCommerceState();
} );
afterAll( async () => {
await login.logout();
} );
it( 'can start the profile wizard', async () => {
await profileWizard.navigate();
} );
it( 'can complete the store details section', async () => {
await profileWizard.storeDetails.completeStoreDetailsSection( {
countryRegionSubstring: 'japan',
countryRegionSelector: 'JP\\:JP01',
countryRegion: 'Japan — Hokkaido',
} );
// Wait for "Continue" button to become active
await profileWizard.continue();
// Wait for usage tracking pop-up window to appear
await profileWizard.optionallySelectUsageTracking();
} );
// JP:JP01
it( 'can choose the "Other" industry', async () => {
// Query for the industries checkboxes
await profileWizard.industry.isDisplayed();
await profileWizard.industry.selectIndustry( 'Other' );
await profileWizard.continue();
} );
it( 'can complete the product types section', async () => {
await profileWizard.productTypes.isDisplayed( 7 );
// Select Physical and Downloadable products
await profileWizard.productTypes.selectProduct(
'Physical products'
);
await profileWizard.productTypes.selectProduct( 'Downloads' );
await profileWizard.continue();
await page.waitForNavigation( { waitUntil: 'networkidle0' } );
} );
it( 'can complete the business details tab', async () => {
await profileWizard.business.isDisplayed();
await profileWizard.business.selectProductNumber(
config.get( 'onboardingwizard.numberofproducts' )
);
await profileWizard.business.selectCurrentlySelling(
config.get( 'onboardingwizard.sellingelsewhere' )
);
await profileWizard.continue();
} );
it( 'can choose not to install any extensions', async () => {
await profileWizard.business.freeFeaturesIsDisplayed();
// Add WC Pay check
await profileWizard.business.expandRecommendedBusinessFeatures();
expect( page ).not.toMatchElement( 'a', {
text: 'WooCommerce Payments',
} );
await profileWizard.business.uncheckAllRecommendedBusinessFeatures();
await profileWizard.continue();
} );
it( 'can finish the rest of the wizard successfully', async () => {
await profileWizard.themes.isDisplayed();
// This navigates to the home screen
await profileWizard.themes.continueWithActiveTheme();
} );
it( 'should display the choose payments task, and not the woocommerce payments task', async () => {
const homescreen = new WcHomescreen( page );
await homescreen.isDisplayed();
await homescreen.possiblyDismissWelcomeModal();
const tasks = await homescreen.getTaskList();
expect( tasks ).toContain( TaskTitles.addPayments );
expect( tasks ).not.toContain( TaskTitles.wooPayments );
} );
it( 'can select the right currency on settings page related to the onboarding country', async () => {
const settingsScreen = new WcSettings( page );
await settingsScreen.navigate();
verifyValueOfInputField( '#woocommerce_currency', 'JPY' );
} );
} );
};
const testDifferentStoreCurrenciesWCPay = () => {
const testCountryCurrencyPairs = [
{
countryRegionSubstring: 'australia',
countryRegionSelector: 'AU\\:QLD',
countryRegion: 'Australia — Queensland',
expectedCurrency: 'AUD',
isWCPaySupported: true,
},
{
countryRegionSubstring: 'canada',
countryRegionSelector: 'CA\\:QC',
countryRegion: 'Canada — Quebec',
expectedCurrency: 'CAD',
isWCPaySupported: true,
},
{
countryRegionSubstring: 'china',
countryRegionSelector: 'CN\\:CN2',
countryRegion: 'China — Beijing',
expectedCurrency: 'CNY',
isWCPaySupported: false,
},
{
countryRegionSubstring: 'spain',
countryRegionSelector: 'ES\\:CO',
countryRegion: 'Spain — Córdoba',
expectedCurrency: 'EUR',
isWCPaySupported: true,
},
{
countryRegionSubstring: 'india',
countryRegionSelector: 'IN\\:DL',
countryRegion: 'India — Delhi',
expectedCurrency: 'INR',
isWCPaySupported: false,
},
{
countryRegionSubstring: 'kingd',
countryRegionSelector: 'GB',
countryRegion: 'United Kingdom (UK)',
expectedCurrency: 'GBP',
isWCPaySupported: true,
},
];
testCountryCurrencyPairs.forEach( ( spec ) => {
describe( 'A store can onboard with any country and have the correct currency selected after onboarding.', () => {
const profileWizard = new OnboardingWizard( page );
const login = new Login( page );
beforeAll( async () => {
await login.login();
await resetWooCommerceState();
} );
afterAll( async () => {
await login.logout();
} );
it( `can complete the profile wizard with selecting ${ spec.countryRegion } as the country`, async () => {
await profileWizard.navigate();
await profileWizard.storeDetails.completeStoreDetailsSection( {
countryRegionSubstring: spec.countryRegionSubstring,
countryRegionSelector: spec.countryRegionSelector,
countryRegion: spec.countryRegion,
} );
// Wait for "Continue" button to become active
await profileWizard.continue();
// Wait for usage tracking pop-up window to appear
await profileWizard.optionallySelectUsageTracking();
// Query for the industries checkboxes
await profileWizard.industry.isDisplayed();
await profileWizard.industry.selectIndustry( 'Other' );
await profileWizard.continue();
await profileWizard.productTypes.isDisplayed( 7 );
await profileWizard.productTypes.selectProduct(
'Physical products'
);
await profileWizard.productTypes.selectProduct( 'Downloads' );
await profileWizard.continue();
await page.waitForNavigation( {
waitUntil: 'networkidle0',
} );
await profileWizard.business.isDisplayed();
await profileWizard.business.selectProductNumber(
config.get( 'onboardingwizard.numberofproducts' )
);
await profileWizard.business.selectCurrentlySelling(
config.get( 'onboardingwizard.sellingelsewhere' )
);
await profileWizard.continue();
await profileWizard.business.freeFeaturesIsDisplayed();
// Add WC Pay check
await profileWizard.business.expandRecommendedBusinessFeatures();
if ( spec.isWCPaySupported ) {
expect( page ).toMatchElement( 'a', {
text: 'WooCommerce Payments',
} );
} else {
expect( page ).not.toMatchElement( 'a', {
text: 'WooCommerce Payments',
} );
}
await profileWizard.business.uncheckAllRecommendedBusinessFeatures();
await profileWizard.continue();
await profileWizard.themes.isDisplayed();
// This navigates to the home screen
await profileWizard.themes.continueWithActiveTheme();
} );
it( `can select ${ spec.expectedCurrency } as the currency for ${ spec.countryRegion }`, async () => {
const settingsScreen = new WcSettings( page );
await settingsScreen.navigate();
verifyValueOfInputField(
'#woocommerce_currency',
spec.expectedCurrency
);
} );
} );
} );
};
const testSubscriptionsInclusion = () => {
describe( 'A non-US store will not see the Subscriptions inclusion', () => {
const profileWizard = new OnboardingWizard( page );
const login = new Login( page );
beforeAll( async () => {
await login.login();
await resetWooCommerceState();
} );
it( 'can complete the store details section', async () => {
await profileWizard.navigate();
await profileWizard.storeDetails.completeStoreDetailsSection( {
countryRegionSubstring: 'fran',
countryRegionSelector: 'FR',
countryRegion: 'France',
} );
// Wait for "Continue" button to become active
await profileWizard.continue();
// Wait for usage tracking pop-up window to appear
await profileWizard.optionallySelectUsageTracking();
} );
it( 'can complete the product types section, Subscriptions copy is not visible', async () => {
// Query for the industries checkboxes
await profileWizard.industry.isDisplayed();
await profileWizard.industry.selectIndustry( 'Health and beauty' );
await profileWizard.continue();
await profileWizard.productTypes.isDisplayed( 7 );
await profileWizard.productTypes.selectProduct( 'Subscriptions' );
await expect( page ).not.toMatchElement( 'p', {
text:
'The following extensions will be added to your site for free: WooCommerce Payments. An account is required to use this feature.',
} );
await profileWizard.continue();
await page.waitForNavigation( { waitUntil: 'networkidle0' } );
} );
it( 'can complete the business details tab', async () => {
await profileWizard.business.isDisplayed();
await profileWizard.business.selectProductNumber(
config.get( 'onboardingwizard.numberofproducts' )
);
await profileWizard.business.selectCurrentlySelling(
config.get( 'onboardingwizard.sellingelsewhere' )
);
await profileWizard.continue();
} );
it( 'should display the WooCommerce Payments extension after it has been installed', async () => {
await profileWizard.business.freeFeaturesIsDisplayed();
await profileWizard.business.expandRecommendedBusinessFeatures();
expect( page ).toMatchElement( 'a', {
text: 'WooCommerce Payments',
} );
} );
it( 'should display the task "Add Subscriptions to my store"', async () => {
await profileWizard.navigate();
await profileWizard.goToOBWStep( 'Store Details' );
await profileWizard.skipStoreSetup();
const homescreen = new WcHomescreen( page );
await homescreen.isDisplayed();
await homescreen.possiblyDismissWelcomeModal();
const tasks = await homescreen.getTaskList();
expect( tasks ).toContain( 'Add Subscriptions to my store' );
} );
it( 'can select the Subscription option in the "Start with a template" modal', async () => {
const productsSetup = new ProductsSetup( page );
await productsSetup.navigate();
await productsSetup.isDisplayed();
await productsSetup.clickStartWithTemplate();
await productsSetup.isStartWithATemplateDisplayed( 3 );
} );
} );
describe( 'A US store will see the Subscriptions inclusion', () => {
const profileWizard = new OnboardingWizard( page );
const login = new Login( page );
beforeAll( async () => {
await resetWooCommerceState();
} );
it( 'can complete the store details section', async () => {
await profileWizard.navigate();
await profileWizard.storeDetails.completeStoreDetailsSection( {
countryRegionSubstring: 'cali',
countryRegionSelector: 'US\\:CA',
countryRegion: 'United States (US) — California',
} );
// Wait for "Continue" button to become active
await profileWizard.continue();
// Wait for usage tracking pop-up window to appear
await profileWizard.optionallySelectUsageTracking();
} );
it( 'can complete the product types section, the Subscriptions copy now is visible', async () => {
// Query for the industries checkboxes
await profileWizard.industry.isDisplayed();
await profileWizard.industry.selectIndustry( 'Health and beauty' );
await profileWizard.continue();
await profileWizard.productTypes.isDisplayed( 7 );
await profileWizard.productTypes.selectProduct( 'Subscriptions' );
await expect( page ).toMatchElement( 'p', {
text:
'The following extensions will be added to your site for free: WooCommerce Payments. An account is required to use this feature.',
} );
await profileWizard.continue();
await page.waitForNavigation( { waitUntil: 'networkidle0' } );
} );
it( 'can complete the business details tab', async () => {
await profileWizard.business.isDisplayed();
await profileWizard.business.selectProductNumber(
config.get( 'onboardingwizard.numberofproducts' )
);
await profileWizard.business.selectCurrentlySelling(
config.get( 'onboardingwizard.sellingelsewhere' )
);
await profileWizard.continue();
} );
it( 'cannot see the WooCommerce Payments extension after it has been installed', async () => {
await profileWizard.business.freeFeaturesIsDisplayed();
await profileWizard.business.expandRecommendedBusinessFeatures();
expect( page ).not.toMatchElement( 'a', {
text: 'WooCommerce Payments',
} );
} );
it( 'should not display the task "Add Subscriptions to my store"', async () => {
await profileWizard.navigate();
await profileWizard.goToOBWStep( 'Store Details' );
await profileWizard.skipStoreSetup();
const homescreen = new WcHomescreen( page );
await homescreen.isDisplayed();
await homescreen.possiblyDismissWelcomeModal();
const tasks = await homescreen.getTaskList();
expect( tasks ).not.toContain( 'Add Subscriptions to my store' );
} );
it( 'can select the Subscription option in the "Start with a template" modal', async () => {
const productsSetup = new ProductsSetup( page );
await productsSetup.navigate();
await productsSetup.isDisplayed();
await productsSetup.clickStartWithTemplate();
await productsSetup.isStartWithATemplateDisplayed( 4 );
} );
} );
};
const testBusinessDetailsForm = () => {
describe( 'A store that is selling elsewhere will see the "Number of employees” dropdown menu', () => {
const profileWizard = new OnboardingWizard( page );
const login = new Login( page );
beforeAll( async () => {
await resetWooCommerceState();
} );
afterAll( async () => {
await login.logout();
} );
it( 'can complete the store details and product types sections', async () => {
await profileWizard.navigate();
await profileWizard.storeDetails.isDisplayed();
await profileWizard.storeDetails.completeStoreDetailsSection();
// Wait for "Continue" button to become active
await profileWizard.continue();
// Wait for usage tracking pop-up window to appear
await profileWizard.optionallySelectUsageTracking();
// Query for the industries checkboxes
await profileWizard.industry.isDisplayed();
await profileWizard.industry.selectIndustry(
'Fashion, apparel, and accessories'
);
await profileWizard.continue();
await profileWizard.productTypes.isDisplayed( 7 );
// Select Physical
await profileWizard.productTypes.selectProduct(
'Physical products'
);
await profileWizard.productTypes.selectProduct( 'Downloads' );
await profileWizard.continue();
await page.waitForNavigation( {
waitUntil: 'networkidle0',
} );
} );
it( 'can complete the business details tab', async () => {
await profileWizard.business.isDisplayed();
await profileWizard.business.selectProductNumber(
config.get( 'onboardingwizard.numberofproducts' )
);
await profileWizard.business.selectCurrentlySelling(
config.get( 'onboardingwizard.sellingOnAnotherPlatform' )
);
expect( page ).toMatchElement( 'label', {
text: 'How many employees do you have?',
} );
await profileWizard.business.selectEmployeesNumber(
config.get( 'onboardingwizard.number_employees' )
);
await profileWizard.business.selectRevenue(
config.get( 'onboardingwizard.revenue' )
);
await profileWizard.business.selectOtherPlatformName(
config.get( 'onboardingwizard.other_platform_name' )
);
await profileWizard.continue();
await profileWizard.business.expandRecommendedBusinessFeatures();
await profileWizard.business.uncheckAllRecommendedBusinessFeatures();
await profileWizard.continue();
await profileWizard.themes.isDisplayed();
} );
} );
};
const testAdminHomescreen = () => {
describe( 'Homescreen', () => {
const profileWizard = new OnboardingWizard( page );
const homeScreen = new WcHomescreen( page );
const login = new Login( page );
beforeAll( async () => {
await login.login();
await resetWooCommerceState();
await profileWizard.navigate();
await profileWizard.skipStoreSetup();
} );
afterAll( async () => {
await login.logout();
} );
it( 'should not show welcome modal', async () => {
await homeScreen.isDisplayed();
await expect( homeScreen.isWelcomeModalVisible() ).resolves.toBe(
false
);
} );
} );
};
module.exports = {
testAdminOnboardingWizard,
testSelectiveBundleWCPay,
testDifferentStoreCurrenciesWCPay,
testSubscriptionsInclusion,
testBusinessDetailsForm,
testAdminHomescreen,
};

View File

@ -0,0 +1,98 @@
/**
* Internal dependencies
*/
import { AnalyticsOverview } from '../../pages/AnalyticsOverview';
import { Login } from '../../pages/Login';
/* eslint-disable @typescript-eslint/no-var-requires */
const { afterAll, beforeAll, describe, it } = require( '@jest/globals' );
const testAdminAnalyticsOverview = () => {
describe( 'Analytics pages', () => {
const analyticsPage = new AnalyticsOverview( page );
const login = new Login( page );
const sectionTitles = [ 'Performance', 'Charts', 'Leaderboards' ];
const titlesString = sectionTitles.join( ', ' );
beforeAll( async () => {
await login.login();
await analyticsPage.navigate();
await analyticsPage.isDisplayed();
// Restore original order to sections
for ( let t = 0; t < sectionTitles.length; t++ ) {
const visibleSections = await analyticsPage.getSectionTitles();
if ( visibleSections.indexOf( sectionTitles[ t ] ) < 0 ) {
await analyticsPage.addSection( sectionTitles[ t ] );
}
}
} );
afterAll( async () => {
await login.logout();
} );
it( `a user should see ${ sectionTitles.length } sections by default - ${ titlesString }`, async () => {
const sections = await analyticsPage.getSectionTitles();
for ( let t = 0; t < sectionTitles.length; t++ ) {
expect( sections ).toContain( sectionTitles[ t ] );
}
} );
it( 'should allow a user to remove a section', async () => {
await analyticsPage.removeSection( sectionTitles[ 0 ] );
const sections = await analyticsPage.getSectionTitles();
expect( sections ).not.toContain( sectionTitles[ 0 ] );
} );
it( 'should allow a user to add a section back in', async () => {
let sections = await analyticsPage.getSectionTitles();
expect( sections ).not.toContain( sectionTitles[ 0 ] );
await analyticsPage.addSection( sectionTitles[ 0 ] );
sections = await analyticsPage.getSectionTitles();
expect( sections ).toContain( sectionTitles[ 0 ] );
} );
describe( 'moving sections', () => {
it( 'should not display move up for the top, or move down for the bottom section', async () => {
const sections = await analyticsPage.getSections();
for ( const section of sections ) {
const index = sections.indexOf( section );
const menuItems = (
await analyticsPage.getEllipsisMenuItems(
section.title
)
).map( ( item ) => item.title );
if ( index === 0 ) {
expect( menuItems ).toContain( 'Move down' );
expect( menuItems ).not.toContain( 'Move up' );
} else if ( index === sections.length - 1 ) {
expect( menuItems ).not.toContain( 'Move down' );
expect( menuItems ).toContain( 'Move up' );
} else {
expect( menuItems ).toContain( 'Move down' );
expect( menuItems ).toContain( 'Move up' );
}
await analyticsPage.closeSectionEllipsis( section.title );
}
} );
it( 'should allow a user to move a section down', async () => {
const sections = await analyticsPage.getSectionTitles();
await analyticsPage.moveSectionDown( sections[ 0 ] );
const newSections = await analyticsPage.getSectionTitles();
expect( sections[ 0 ] ).toEqual( newSections[ 1 ] );
expect( sections[ 1 ] ).toEqual( newSections[ 0 ] );
} );
it( 'should allow a user to move a section up', async () => {
const sections = await analyticsPage.getSectionTitles();
await analyticsPage.moveSectionUp( sections[ 1 ] );
const newSections = await analyticsPage.getSectionTitles();
expect( sections[ 0 ] ).toEqual( newSections[ 1 ] );
expect( sections[ 1 ] ).toEqual( newSections[ 0 ] );
} );
} );
} );
};
module.exports = { testAdminAnalyticsOverview };

View File

@ -0,0 +1,86 @@
/**
* Internal dependencies
*/
import { Analytics } from '../../pages/Analytics';
import { Customers } from '../../pages/Customers';
import { Login } from '../../pages/Login';
/* eslint-disable @typescript-eslint/no-var-requires */
const { afterAll, beforeAll, describe, it } = require( '@jest/globals' );
const testAdminAnalyticsPages = () => {
describe( 'Analytics pages', () => {
const analyticsPage = new Analytics( page );
const customersPage = new Customers( page );
const login = new Login( page );
beforeAll( async () => {
await login.login();
} );
afterAll( async () => {
await login.logout();
} );
it( 'A user can view the analytics overview without it crashing', async () => {
await analyticsPage.navigate();
await analyticsPage.isDisplayed();
} );
it( 'A user can view the analytics for products without it crashing', async () => {
await analyticsPage.navigateToSection( 'products' );
await analyticsPage.isDisplayed();
} );
it( 'A user can view the analytics for revenue without it crashing', async () => {
await analyticsPage.navigateToSection( 'revenue' );
await analyticsPage.isDisplayed();
} );
it( 'A user can view the analytics for orders without it crashing', async () => {
await analyticsPage.navigateToSection( 'orders' );
await analyticsPage.isDisplayed();
} );
it( 'A user can view the analytics for variations without it crashing', async () => {
await analyticsPage.navigateToSection( 'variations' );
await analyticsPage.isDisplayed();
} );
it( 'A user can view the analytics for categories without it crashing', async () => {
await analyticsPage.navigateToSection( 'categories' );
await analyticsPage.isDisplayed();
} );
it( 'A user can view the analytics for coupons without it crashing', async () => {
await analyticsPage.navigateToSection( 'coupons' );
await analyticsPage.isDisplayed();
} );
it( 'A user can view the analytics for taxes without it crashing', async () => {
await analyticsPage.navigateToSection( 'taxes' );
await analyticsPage.isDisplayed();
} );
it( 'A user can view the analytics for downloads without it crashing', async () => {
await analyticsPage.navigateToSection( 'downloads' );
await analyticsPage.isDisplayed();
} );
it( 'A user can view the analytics for stock without it crashing', async () => {
await analyticsPage.navigateToSection( 'stock' );
await analyticsPage.isDisplayed();
} );
it( 'A user can view the analytics for settings without it crashing', async () => {
await analyticsPage.navigateToSection( 'settings' );
await analyticsPage.isDisplayed();
} );
it( 'A user can view the customers page without it crashing', async () => {
await customersPage.navigate();
await customersPage.isDisplayed();
} );
} );
};
module.exports = { testAdminAnalyticsPages };

View File

@ -0,0 +1,135 @@
/**
* External dependencies
*/
import { createSimpleProduct, withRestApi } from '@woocommerce/e2e-utils';
/**
* Internal dependencies
*/
import { Login } from '../../pages/Login';
import { OnboardingWizard } from '../../pages/OnboardingWizard';
import { WcHomescreen } from '../../pages/WcHomescreen';
import {
createOrder,
removeAllOrders,
unhideTaskList,
runActionScheduler,
updateOption,
resetWooCommerceState,
} from '../../fixtures';
import { OrdersActivityPanel } from '../../elements/OrdersActivityPanel';
import { addReviewToProduct, waitForElementByText } from '../../utils/actions';
/* eslint-disable @typescript-eslint/no-var-requires */
const { afterAll, beforeAll, describe, it } = require( '@jest/globals' );
/* eslint-enable @typescript-eslint/no-var-requires */
const simpleProductName = 'Simple order';
const testAdminHomescreenActivityPanel = () => {
describe( 'Homescreen activity panel', () => {
const profileWizard = new OnboardingWizard( page );
const homeScreen = new WcHomescreen( page );
const ordersPanel = new OrdersActivityPanel( page );
const login = new Login( page );
beforeAll( async () => {
await login.login();
await resetWooCommerceState();
await profileWizard.navigate();
await profileWizard.skipStoreSetup();
await homeScreen.isDisplayed();
await homeScreen.possiblyDismissWelcomeModal();
} );
afterAll( async () => {
await withRestApi.deleteAllProducts();
await removeAllOrders();
await unhideTaskList( 'setup' );
await runActionScheduler();
await updateOption( 'woocommerce_task_list_hidden', 'no' );
await login.logout();
} );
it( 'should not show activity panel while task list is displayed', async () => {
await expect( homeScreen.isTaskListDisplayed() ).resolves.toBe(
true
);
await expect( homeScreen.isActivityPanelShown() ).resolves.toBe(
false
);
} );
it( 'should not show panels when there are no orders or products yet with task list hidden', async () => {
await homeScreen.hideTaskList();
await expect( homeScreen.isTaskListDisplayed() ).resolves.toBe(
false
);
await expect( homeScreen.isActivityPanelShown() ).resolves.toBe(
false
);
} );
it( 'should show Reviews panel when we have at-least one product', async () => {
const productId = await createSimpleProduct(
simpleProductName,
'9.99'
);
await addReviewToProduct( productId, simpleProductName );
await homeScreen.navigate();
await homeScreen.isDisplayed();
const activityPanels = await homeScreen.getActivityPanels();
expect( activityPanels ).toHaveLength( 1 );
expect( activityPanels ).toEqual(
expect.arrayContaining( [
expect.objectContaining( { title: 'Reviews' } ),
] )
);
} );
it( 'should show Orders and Stock panels when at-least one order is added', async () => {
await createOrder();
await page.reload( {
waitUntil: [ 'networkidle0', 'domcontentloaded' ],
} );
const activityPanels = await homeScreen.getActivityPanels();
expect( activityPanels ).toHaveLength( 3 );
expect( activityPanels ).toEqual(
expect.arrayContaining( [
expect.objectContaining( { title: 'Orders' } ),
] )
);
expect( activityPanels ).toEqual(
expect.arrayContaining( [
expect.objectContaining( { title: 'Stock' } ),
] )
);
} );
describe( 'Orders panel', () => {
it( 'should show: "you have fullfilled all your orders" when expanding Orders panel if no actionable orders', async () => {
await homeScreen.expandActivityPanel( 'Orders' );
await waitForElementByText(
'h4',
'Youve fulfilled all your orders'
);
await expect( page ).toMatchElement( 'h4', {
text: 'Youve fulfilled all your orders',
} );
} );
it( 'should show actionable Orders when expanding Orders panel', async () => {
const order1 = await createOrder( 'processing' );
const order2 = await createOrder( 'on-hold' );
await homeScreen.navigate();
await homeScreen.expandActivityPanel( 'Orders' );
const orders = await ordersPanel.getDisplayedOrders();
expect( orders ).toHaveLength( 2 );
expect( orders ).toContain( `Order #${ order1.id }` );
expect( orders ).toContain( `Order #${ order2.id }` );
} );
} );
} );
};
module.exports = { testAdminHomescreenActivityPanel };

View File

@ -0,0 +1,77 @@
/**
* External dependencies
*/
import { takeScreenshotFor } from '@woocommerce/e2e-environment';
/**
* Internal dependencies
*/
import { Login } from '../../pages/Login';
import { OnboardingWizard } from '../../pages/OnboardingWizard';
import { WcHomescreen } from '../../pages/WcHomescreen';
import { TaskTitles } from '../../constants/taskTitles';
import { HelpMenu } from '../../elements/HelpMenu';
import { WcSettings } from '../../pages/WcSettings';
import { resetWooCommerceState, unhideTaskList } from '../../fixtures';
/* eslint-disable @typescript-eslint/no-var-requires */
const { afterAll, beforeAll, describe, it } = require( '@jest/globals' );
/* eslint-enable @typescript-eslint/no-var-requires */
const testAdminHomescreenTasklist = () => {
describe( 'Homescreen task list', () => {
const profileWizard = new OnboardingWizard( page );
const homeScreen = new WcHomescreen( page );
const helpMenu = new HelpMenu( page );
const settings = new WcSettings( page );
const login = new Login( page );
beforeAll( async () => {
await login.login();
await resetWooCommerceState();
// This makes this test more isolated, by always navigating to the
// profile wizard and skipping, this behaves the same as if the
// profile wizard had not been run yet and the user is redirected
// to it when trying to go to wc-admin.
await profileWizard.navigate();
await profileWizard.skipStoreSetup();
await homeScreen.isDisplayed();
await homeScreen.possiblyDismissWelcomeModal();
await takeScreenshotFor( 'WooCommerce Admin Home Screen' );
} );
afterAll( async () => {
await unhideTaskList( 'setup' );
await login.logout();
} );
it( 'should show 6 or more tasks on the home screen', async () => {
const tasks = await homeScreen.getTaskList();
expect( tasks.length ).toBeGreaterThanOrEqual( 6 );
expect( tasks ).toContain( TaskTitles.storeDetails );
expect( tasks ).toContain( TaskTitles.addProducts );
expect( tasks ).toContain( TaskTitles.taxSetup );
expect( tasks ).toContain( TaskTitles.personalizeStore );
} );
it( 'should be able to hide the task list', async () => {
await homeScreen.hideTaskList();
expect( await homeScreen.isTaskListDisplayed() ).toBe( false );
} );
it( 'should be able to show the task list again through the help menu', async () => {
await settings.navigate();
await helpMenu.openHelpMenu();
await helpMenu.enableTaskList();
// redirects to homescreen
await homeScreen.isDisplayed();
await expect( homeScreen.isTaskListDisplayed() ).resolves.toBe(
true
);
} );
} );
};
module.exports = { testAdminHomescreenTasklist };

View File

@ -0,0 +1,9 @@
export * from './activate-and-setup/basic-setup';
export * from './activate-and-setup/complete-onboarding-wizard';
export * from './analytics/analytics';
export * from './analytics/analytics-overview';
export * from './marketing/coupons';
export * from './tasks/payment';
export * from './tasks/purchase';
export * from './homescreen/task-list';
export * from './homescreen/activity-panel';

View File

@ -0,0 +1,30 @@
/**
* Internal dependencies
*/
import { Coupons } from '../../pages/Coupons';
import { Login } from '../../pages/Login';
/* eslint-disable @typescript-eslint/no-var-requires */
const { afterAll, beforeAll, describe, it } = require( '@jest/globals' );
/* eslint-enable @typescript-eslint/no-var-requires */
const testAdminCouponsPage = () => {
describe( 'Coupons page', () => {
const couponsPage = new Coupons( page );
const login = new Login( page );
beforeAll( async () => {
await login.login();
} );
afterAll( async () => {
await login.logout();
} );
it( 'A user can view the coupons overview without it crashing', async () => {
await couponsPage.navigate();
await couponsPage.isDisplayed();
} );
} );
};
module.exports = { testAdminCouponsPage };

View File

@ -0,0 +1,90 @@
/**
* External dependencies
*/
import { takeScreenshotFor } from '@woocommerce/e2e-environment';
/**
* Internal dependencies
*/
import { Login } from '../../pages/Login';
import { OnboardingWizard } from '../../pages/OnboardingWizard';
import { PaymentsSetup } from '../../pages/PaymentsSetup';
import { WcHomescreen } from '../../pages/WcHomescreen';
import { BankAccountTransferSetup } from '../../sections/payment-setup/BankAccountTransferSetup';
import { waitForTimeout } from '../../utils/actions';
import { WcSettings } from '../../pages/WcSettings';
/* eslint-disable @typescript-eslint/no-var-requires */
const { afterAll, beforeAll, describe, it } = require( '@jest/globals' );
/* eslint-enable @typescript-eslint/no-var-requires */
const testAdminPaymentSetupTask = () => {
describe( 'Payment setup task', () => {
const profileWizard = new OnboardingWizard( page );
const homeScreen = new WcHomescreen( page );
const paymentsSetup = new PaymentsSetup( page );
const bankTransferSetup = new BankAccountTransferSetup( page );
const login = new Login( page );
const settings = new WcSettings( page );
beforeAll( async () => {
await login.login();
// This makes this test more isolated, by always navigating to the
// profile wizard and skipping, this behaves the same as if the
// profile wizard had not been run yet and the user is redirected
// to it when trying to go to wc-admin.
await profileWizard.navigate();
await profileWizard.skipStoreSetup();
await homeScreen.isDisplayed();
await takeScreenshotFor( 'Payment setup task home screen' );
await homeScreen.possiblyDismissWelcomeModal();
} );
afterAll( async () => {
await login.logout();
} );
it( 'Can visit the payment setup task from the homescreen if the setup wizard has been skipped', async () => {
await homeScreen.clickOnTaskList( 'Set up payments' );
await paymentsSetup.possiblyCloseHelpModal();
await paymentsSetup.isDisplayed();
} );
it( 'Saving valid bank account transfer details enables the payment method', async () => {
await paymentsSetup.showOtherPaymentMethods();
await paymentsSetup.goToPaymentMethodSetup( 'bacs' );
await bankTransferSetup.saveAccountDetails( {
accountNumber: '1234',
accountName: 'Savings',
bankName: 'TestBank',
sortCode: '12',
iban: '12 3456 7890',
swiftCode: 'ABBA',
} );
await waitForTimeout( 1500 );
expect( await settings.paymentMethodIsEnabled( 'bacs' ) ).toBe(
true
);
} );
it( 'Enabling cash on delivery enables the payment method', async () => {
await settings.cleanPaymentMethods();
await homeScreen.navigate();
await homeScreen.isDisplayed();
await waitForTimeout( 1000 );
await homeScreen.clickOnTaskList( 'Set up payments' );
await paymentsSetup.possiblyCloseHelpModal();
await paymentsSetup.isDisplayed();
await paymentsSetup.showOtherPaymentMethods();
await paymentsSetup.enableCashOnDelivery();
await waitForTimeout( 1500 );
expect( await settings.paymentMethodIsEnabled( 'cod' ) ).toBe(
true
);
} );
} );
};
module.exports = { testAdminPaymentSetupTask };

Some files were not shown because too many files have changed in this diff Show More