Merged trunk into add/post-external-smoke-test-results-to-pr
This commit is contained in:
commit
5b63fa9a75
|
@ -1,3 +1,4 @@
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
root: true,
|
||||||
extends: [ 'plugin:@woocommerce/eslint-plugin/recommended' ],
|
extends: [ 'plugin:@woocommerce/eslint-plugin/recommended' ],
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
---
|
|
||||||
name: "\U0001F512 Security issue"
|
|
||||||
about: Please report security issues *only* via https://www.hackerone.com
|
|
||||||
title: ''
|
|
||||||
labels: ''
|
|
||||||
assignees: ''
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
For security reasons, please report all security issues via https://hackerone.com/automattic/. Also, if the issue is valid, a bug bounty will be paid out to you.
|
|
||||||
|
|
||||||
Please disclose responsibly and not via GitHub (which allows for exploiting issues in the wild before the patch is released).
|
|
|
@ -0,0 +1,95 @@
|
||||||
|
name: 🐞 Bug Report
|
||||||
|
description: Report a bug if something isn't working as expected in WooCommerce Core.
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
### Thanks for contributing!
|
||||||
|
|
||||||
|
Please provide us with the information requested in this bug report.
|
||||||
|
Without these details, we won't be able to fully evaluate this issue.
|
||||||
|
Bug reports lacking detail, or for any other reason than to report a bug, may be closed without action.
|
||||||
|
|
||||||
|
While our goal is to address all the issues reported in this repository, GitHub should be treated as a place to report confirmed bugs only.
|
||||||
|
- If you have a support request or custom code related question please follow one of the steps below:
|
||||||
|
- Review [WooCommerce Self-Service Guide](https://woocommerce.com/document/woocommerce-self-service-guide/) to see if the solutions listed there apply to your case;
|
||||||
|
- If you are a paying customer of WooCommerce, contact WooCommerce support by [opening a ticket or starting a live chat](https://woocommerce.com/contact-us/);
|
||||||
|
- Make a post on [WooCommerce community forum](https://wordpress.org/support/plugin/woocommerce/)
|
||||||
|
- To get help on custom code questions go to the [WooCommerce Community Slack](https://woocommerce.com/community-slack/) and visit the `#developers` channel.
|
||||||
|
|
||||||
|
Make sure to look through the [existing `type: bug` issues](https://github.com/woocommerce/woocommerce/issues?q=is%3Aopen+is%3Aissue+label%3A%22type%3A+bug%22) to see whether your bug has already been submitted.
|
||||||
|
Feel free to contribute to any existing issues.
|
||||||
|
Search tip: You can filter our issues using [our labels](https://github.com/woocommerce/woocommerce/labels).
|
||||||
|
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).
|
||||||
|
- type: checkboxes
|
||||||
|
id: prerequisites
|
||||||
|
attributes:
|
||||||
|
label: Prerequisites
|
||||||
|
description: Please confirm these before submitting the issue.
|
||||||
|
options:
|
||||||
|
- label: I have carried out troubleshooting steps and I believe I have found a bug.
|
||||||
|
- label: I have searched for similar bugs in both open and closed issues and cannot find a duplicate.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: summary
|
||||||
|
attributes:
|
||||||
|
label: Describe the bug
|
||||||
|
description: A clear and concise description of what the bug is.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: expected
|
||||||
|
attributes:
|
||||||
|
label: Expected behavior
|
||||||
|
placeholder: |
|
||||||
|
A clear and concise description of what you expected to happen.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: actual
|
||||||
|
attributes:
|
||||||
|
label: Actual behavior
|
||||||
|
placeholder: |
|
||||||
|
A clear and concise description of what actually happens. Please be as descriptive as possible;
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: steps
|
||||||
|
attributes:
|
||||||
|
label: Steps to reproduce
|
||||||
|
description: Attach screenshot(s) or recording(s) directly by dragging & dropping.
|
||||||
|
placeholder: |
|
||||||
|
1. Go to
|
||||||
|
2. Click on
|
||||||
|
3. Scroll down to
|
||||||
|
4. See error
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: environment
|
||||||
|
attributes:
|
||||||
|
label: WordPress Environment
|
||||||
|
description: |
|
||||||
|
We use the [WooCommerce System Status Report](https://woocommerce.com/document/understanding-the-woocommerce-system-status-report/) to help us evaluate the issue.
|
||||||
|
Without this report we won't be able to fully evaluate this issue.
|
||||||
|
placeholder: |
|
||||||
|
The System Status Report is found in your WordPress admin under **WooCommerce > Status**.
|
||||||
|
Please select “Get system report”, then “Copy for support”, and then paste it here.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: checkboxes
|
||||||
|
id: isolating
|
||||||
|
attributes:
|
||||||
|
label: Isolating the problem
|
||||||
|
description: |
|
||||||
|
Please try testing your site for theme and plugins conflict.
|
||||||
|
To do that deactivate all plugins except for WooCommerce and switch to a default WordPress theme or [Storefront](https://en-gb.wordpress.org/themes/storefront/). Then test again.
|
||||||
|
If the issue is resolved with the default theme and all plugins deactivated, it means that one of your plugins or a theme is causing the issue.
|
||||||
|
You will then need to enable it one by one and test every time you do that in order to figure out which plugin is causing the issue.
|
||||||
|
options:
|
||||||
|
- label: I have deactivated other plugins and confirmed this bug occurs when only WooCommerce plugin is active.
|
||||||
|
- label: This bug happens with a default WordPress theme active, or [Storefront](https://woocommerce.com/storefront/).
|
||||||
|
- label: I can reproduce this bug consistently using the steps above.
|
||||||
|
validations:
|
||||||
|
required: true
|
|
@ -1,20 +0,0 @@
|
||||||
---
|
|
||||||
name: "\U0001F47D External issues"
|
|
||||||
about: Please report WooCommerce Admin, WooCommerce Gutenberg Products Blocks or Action Scheduler issues directly to their respective repositories.
|
|
||||||
title: ''
|
|
||||||
labels: ''
|
|
||||||
assignees: ''
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
Please report issues for the following features directly to their respective repositories.
|
|
||||||
|
|
||||||
WooCommerce Admin: https://github.com/woocommerce/woocommerce-admin
|
|
||||||
|
|
||||||
WooCommerce Gutenberg Products Blocks: https://github.com/woocommerce/woocommerce-gutenberg-products-block
|
|
||||||
|
|
||||||
Action Scheduler: https://github.com/woocommerce/action-scheduler
|
|
||||||
|
|
||||||
WooCommerce REST API Docs: https://github.com/woocommerce/woocommerce-rest-api-docs
|
|
||||||
|
|
||||||
WooCommerce Code Reference: https://github.com/woocommerce/code-reference
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
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!
|
||||||
|
title: "[Enhancement]: "
|
||||||
|
labels: ["type: enhancement"]
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
### Thanks for contributing!
|
||||||
|
|
||||||
|
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.
|
||||||
|
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: 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).
|
||||||
|
- type: textarea
|
||||||
|
id: summary
|
||||||
|
attributes:
|
||||||
|
label: Describe the solution you'd like
|
||||||
|
description: A clear and concise description of what you want to happen.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: alternative
|
||||||
|
attributes:
|
||||||
|
label: Describe alternatives you've considered
|
||||||
|
description: A clear and concise description of any alternative solutions or features you've considered.
|
||||||
|
- type: textarea
|
||||||
|
id: context
|
||||||
|
attributes:
|
||||||
|
label: Additional context
|
||||||
|
description: Add any other context or screenshots about the feature request here.
|
|
@ -1,28 +0,0 @@
|
||||||
---
|
|
||||||
name: "❓ Support Question"
|
|
||||||
about: "If you have a question \U0001F4AC please see our docs or use our forums, helpdesk,
|
|
||||||
or Slack Community!"
|
|
||||||
title: ''
|
|
||||||
labels: ''
|
|
||||||
assignees: ''
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
We don't offer technical support on GitHub so we recommend using the following:
|
|
||||||
|
|
||||||
**Reading our documentation**
|
|
||||||
Usage docs can be found here: https://docs.woocommerce.com/
|
|
||||||
|
|
||||||
If you have a problem, you may want to start with the self help guide here: https://docs.woocommerce.com/document/woocommerce-self-service-guide/
|
|
||||||
|
|
||||||
**Technical support for premium extensions or if you're a WooCommerce.com customer**
|
|
||||||
Contact WooCommerce support by opening a ticket.
|
|
||||||
https://woocommerce.com/contact-us/
|
|
||||||
|
|
||||||
**For help with custom code**
|
|
||||||
WooCommerce Slack Community: https://woocommerce.com/community-slack/ in the `#developers` channel.
|
|
||||||
|
|
||||||
**General usage and development questions**
|
|
||||||
- WooCommerce Slack Community: https://woocommerce.com/community-slack/
|
|
||||||
- WordPress.org Forums: https://wordpress.org/support/plugin/woocommerce
|
|
||||||
- The Official WooCommerce Facebook Group https://www.facebook.com/groups/advanced.woocommerce/
|
|
|
@ -1,58 +0,0 @@
|
||||||
---
|
|
||||||
name: "\U0001F41E Bug report"
|
|
||||||
about: Report a bug if something isn't working as expected in the core WooCommerce
|
|
||||||
plugin.
|
|
||||||
title: ''
|
|
||||||
labels: ''
|
|
||||||
assignees: ''
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
Please provide us with the information requested in this bug report. Without these details, we won't be able to fully evaluate this issue.
|
|
||||||
Bug reports lacking detail, or for any other reason than to report a bug, may be closed without action.
|
|
||||||
|
|
||||||
<!-- This template is for confirmed bugs only. If you have a support request or custom code related question please see our docs or use our forums, helpdesk, or Slack Community! https://github.com/woocommerce/woocommerce/issues/new?assignees=&labels=&template=3-Support.md&title= -->
|
|
||||||
|
|
||||||
<!-- Make sure to look through the existing issues to see whether your bug has already been submitted. Feel free to contribute to any existing issues. -->
|
|
||||||
<!-- Search tip: You can filter our issues using our component labels https://github.com/woocommerce/woocommerce/labels?q=component -->
|
|
||||||
<!-- 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 -->
|
|
||||||
|
|
||||||
**Prerequisites (mark completed items with an [x]):**
|
|
||||||
- [ ] I have carried out troubleshooting steps and I believe I have found a bug.
|
|
||||||
- [ ] I have searched for similar bugs in both open and closed issues and cannot find a duplicate.
|
|
||||||
|
|
||||||
**Describe the bug**
|
|
||||||
A clear and concise description of what the bug is.
|
|
||||||
|
|
||||||
**Expected behavior**
|
|
||||||
A clear and concise description of what you expected to happen.
|
|
||||||
|
|
||||||
**Actual behavior**
|
|
||||||
A clear and concise description of what actually happens. Please be as descriptive as possible;
|
|
||||||
|
|
||||||
**Steps to reproduce the bug (We need to be able to reproduce the bug in order to fix it.)**
|
|
||||||
Steps to reproduce the bug:
|
|
||||||
1. Go to '...'
|
|
||||||
2. Click on '....'
|
|
||||||
3. Scroll down to '....'
|
|
||||||
4. See error
|
|
||||||
|
|
||||||
**Screenshots**
|
|
||||||
If applicable, add screenshots to help explain your problem.
|
|
||||||
|
|
||||||
<!-- Please try testing your site for theme and plugins conflict. To do that deactivate all plugins except for WooCommerce and switch to a default WordPress theme or [Storefront](https://en-gb.wordpress.org/themes/storefront/). Then test again. If the issue is resolved with the default theme and all plugins deactivated, it means that one of your plugins or a theme is causing the issue. You will then need to enable it one by one and test every time you do that in order to figure out which plugin is causing the issue. -->
|
|
||||||
|
|
||||||
**Isolating the problem (mark completed items with an [x]):**
|
|
||||||
- [ ] I have deactivated other plugins and confirmed this bug occurs when only WooCommerce plugin is active.
|
|
||||||
- [ ] This bug happens with a default WordPress theme active, or [Storefront](https://woocommerce.com/storefront/).
|
|
||||||
- [ ] I can reproduce this bug consistently using the steps above.
|
|
||||||
|
|
||||||
**WordPress Environment**
|
|
||||||
We use the [WooCommerce System Status Report](https://docs.woocommerce.com/document/understanding-the-woocommerce-system-status-report/) to help us evaluate the issue.
|
|
||||||
Without this report we won't be able to fully evaluate this issue.
|
|
||||||
<details>
|
|
||||||
```
|
|
||||||
The System Status Report is found in your WordPress admin under **WooCommerce > Status**.
|
|
||||||
Please select “Get system report”, then “Copy for support”, and then paste it here.
|
|
||||||
```
|
|
||||||
</details>
|
|
|
@ -1,25 +0,0 @@
|
||||||
---
|
|
||||||
name: "✨ New Enhancement"
|
|
||||||
about: 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: ''
|
|
||||||
labels: ''
|
|
||||||
assignees: ''
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
<!-- Make sure to look through existing issues to see whether your idea is already being discussed. Feel free to contribute to any existing issues. -->
|
|
||||||
|
|
||||||
<!-- Search tip: You can filter issues using our enhancement label https://github.com/woocommerce/woocommerce/issues?q=is%3Aissue+label%3Aenhancement -->
|
|
||||||
|
|
||||||
**Is your feature request related to a problem? Please describe.**
|
|
||||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
|
||||||
|
|
||||||
**Describe the solution you'd like**
|
|
||||||
A clear and concise description of what you want to happen.
|
|
||||||
|
|
||||||
**Describe alternatives you've considered**
|
|
||||||
A clear and concise description of any alternative solutions or features you've considered.
|
|
||||||
|
|
||||||
**Additional context**
|
|
||||||
Add any other context or screenshots about the feature request here.
|
|
|
@ -1,25 +0,0 @@
|
||||||
---
|
|
||||||
name: "\U0001F680 Feature request"
|
|
||||||
about: "Suggest a new feature \U0001F389 We'll consider building it if it receives
|
|
||||||
sufficient interest! \U0001F44D"
|
|
||||||
title: ''
|
|
||||||
labels: ''
|
|
||||||
assignees: ''
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
<!-- Make sure to look through existing issues to see whether your idea is already being discussed. Feel free to contribute to any existing issues. -->
|
|
||||||
|
|
||||||
<!-- Search tip: You can filter issues using our enhancement label https://github.com/woocommerce/woocommerce/issues?q=is%3Aissue+label%3Aenhancement -->
|
|
||||||
|
|
||||||
**Is your feature request related to a problem? Please describe.**
|
|
||||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
|
||||||
|
|
||||||
**Describe the solution you'd like**
|
|
||||||
A clear and concise description of what you want to happen.
|
|
||||||
|
|
||||||
**Describe alternatives you've considered**
|
|
||||||
A clear and concise description of any alternative solutions or features you've considered.
|
|
||||||
|
|
||||||
**Additional context**
|
|
||||||
Add any other context or screenshots about the feature request here.
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
blank_issues_enabled: true
|
||||||
|
contact_links:
|
||||||
|
- name: 🔒 Security issue
|
||||||
|
url: https://hackerone.com/automattic/
|
||||||
|
about: For security reasons, please report all security issues via HackerOne. If the issue is valid, a bug bounty will be paid out to you. Please disclose responsibly and not via GitHub (which allows for exploiting issues in the wild before the patch is released).
|
||||||
|
- name: ❓ Support Question
|
||||||
|
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!
|
||||||
|
- 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
|
||||||
|
url: https://github.com/woocommerce/woocommerce-gutenberg-products-block
|
||||||
|
about: Please report issues for WooCommerce Blocks directly to it's repository.
|
|
@ -52,3 +52,103 @@ jobs:
|
||||||
pnpx wc-e2e test:e2e plugins/woocommerce/tests/e2e/specs/smoke-tests/update-woocommerce.js
|
pnpx wc-e2e test:e2e plugins/woocommerce/tests/e2e/specs/smoke-tests/update-woocommerce.js
|
||||||
pnpx wc-e2e test:e2e
|
pnpx wc-e2e test:e2e
|
||||||
pnpx wc-api-tests test api
|
pnpx wc-api-tests test api
|
||||||
|
|
||||||
|
build:
|
||||||
|
name: Build zip for PR
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
id: build
|
||||||
|
uses: woocommerce/action-build@trunk
|
||||||
|
env:
|
||||||
|
BUILD_ENV: e2e
|
||||||
|
|
||||||
|
- name: Upload PR zip
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
name: woocommerce
|
||||||
|
path: ${{ steps.build.outputs.zip_path }}
|
||||||
|
retention-days: 7
|
||||||
|
|
||||||
|
test-plugins:
|
||||||
|
name: Smoke tests with ${{ matrix.plugin }} plugin installed
|
||||||
|
runs-on: ubuntu-18.04
|
||||||
|
needs: [build]
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- plugin: 'WooCommerce Payments'
|
||||||
|
repo: 'automattic/woocommerce-payments'
|
||||||
|
- plugin: 'WooCommerce PayPal Payments'
|
||||||
|
repo: 'woocommerce/woocommerce-paypal-payments'
|
||||||
|
- plugin: 'WooCommerce Shipping & Tax'
|
||||||
|
repo: 'woocommerce/woocommerce-services'
|
||||||
|
- plugin: 'WooCommerce Subscriptions'
|
||||||
|
repo: WC_SUBSCRIPTIONS_REPO
|
||||||
|
private: true
|
||||||
|
- plugin: 'WordPress SEO' # Yoast SEO in the UI, but the slug is wordpress-seo
|
||||||
|
repo: 'Yoast/wordpress-seo'
|
||||||
|
- plugin: 'Contact Form 7'
|
||||||
|
repo: 'takayukister/contact-form-7'
|
||||||
|
steps:
|
||||||
|
- name: Create dirs.
|
||||||
|
run: |
|
||||||
|
mkdir -p code/woocommerce
|
||||||
|
mkdir -p package/woocommerce
|
||||||
|
mkdir -p tmp/woocommerce
|
||||||
|
mkdir -p node_modules
|
||||||
|
|
||||||
|
- name: Checkout code.
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
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.
|
||||||
|
run: mv ./package/woocommerce/plugins/woocommerce/* ./code/woocommerce
|
||||||
|
|
||||||
|
- name: Download WooCommerce ZIP.
|
||||||
|
uses: actions/download-artifact@v2
|
||||||
|
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: Install dependencies again
|
||||||
|
working-directory: package/woocommerce
|
||||||
|
run: |
|
||||||
|
npm install -g pnpm
|
||||||
|
pnpm install
|
||||||
|
|
||||||
|
- name: Run tests command.
|
||||||
|
working-directory: package/woocommerce/plugins/woocommerce
|
||||||
|
env:
|
||||||
|
WC_E2E_SCREENSHOTS: 1
|
||||||
|
E2E_SLACK_TOKEN: ${{ secrets.SMOKE_TEST_SLACK_TOKEN }}
|
||||||
|
E2E_SLACK_CHANNEL: ${{ secrets.RELEASE_TEST_SLACK_CHANNEL }}
|
||||||
|
GITHUB_REPOSITORY: ${{ matrix.private && secrets[matrix.repo] || matrix.repo }}
|
||||||
|
PLUGIN_NAME: ${{ matrix.plugin }}
|
||||||
|
GITHUB_TOKEN: ${{ secrets.E2E_GH_TOKEN }}
|
||||||
|
run: |
|
||||||
|
pnpx wc-e2e test:e2e plugins/woocommerce/tests/e2e/specs/smoke-tests/upload-plugin.js
|
||||||
|
pnpm nx test-e2e woocommerce
|
||||||
|
|
|
@ -105,3 +105,74 @@ jobs:
|
||||||
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: ${{ secrets.RELEASE_TEST_SLACK_CHANNEL }}
|
||||||
run: pnpm nx test-e2e woocommerce
|
run: pnpm nx test-e2e woocommerce
|
||||||
|
|
||||||
|
test-plugins:
|
||||||
|
name: Smoke tests with ${{ matrix.plugin }} plugin installed
|
||||||
|
runs-on: ubuntu-18.04
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- plugin: 'WooCommerce Payments'
|
||||||
|
repo: 'automattic/woocommerce-payments'
|
||||||
|
- plugin: 'WooCommerce PayPal Payments'
|
||||||
|
repo: 'woocommerce/woocommerce-paypal-payments'
|
||||||
|
- plugin: 'WooCommerce Shipping & Tax'
|
||||||
|
repo: 'woocommerce/woocommerce-services'
|
||||||
|
- plugin: 'WooCommerce Subscriptions'
|
||||||
|
repo: WC_SUBSCRIPTIONS_REPO
|
||||||
|
private: true
|
||||||
|
- plugin: 'WordPress SEO' # Yoast SEO in the UI, but the slug is wordpress-seo
|
||||||
|
repo: 'Yoast/wordpress-seo'
|
||||||
|
- plugin: 'Contact Form 7'
|
||||||
|
repo: 'takayukister/contact-form-7'
|
||||||
|
steps:
|
||||||
|
- name: Create dirs.
|
||||||
|
run: |
|
||||||
|
mkdir -p code/woocommerce
|
||||||
|
mkdir -p package/woocommerce
|
||||||
|
mkdir -p tmp/woocommerce
|
||||||
|
mkdir -p node_modules
|
||||||
|
|
||||||
|
- name: Checkout code.
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
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
|
||||||
|
env:
|
||||||
|
LATEST_WP_VERSION_MINUS: ${{ matrix.wp }}
|
||||||
|
run: pnpm nx docker-up 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 release zip
|
||||||
|
working-directory: tmp
|
||||||
|
run: |
|
||||||
|
ASSET_ID=$(jq ".release.assets[0].id" $GITHUB_EVENT_PATH)
|
||||||
|
|
||||||
|
curl https://api.github.com/repos/woocommerce/woocommerce/releases/assets/${ASSET_ID} -LJOH 'Accept: application/octet-stream'
|
||||||
|
|
||||||
|
unzip woocommerce.zip -d woocommerce
|
||||||
|
mv woocommerce/woocommerce/* ../package/woocommerce/plugins/woocommerce/
|
||||||
|
|
||||||
|
- name: Run tests command.
|
||||||
|
working-directory: package/woocommerce/plugins/woocommerce
|
||||||
|
env:
|
||||||
|
WC_E2E_SCREENSHOTS: 1
|
||||||
|
E2E_SLACK_TOKEN: ${{ secrets.SMOKE_TEST_SLACK_TOKEN }}
|
||||||
|
E2E_SLACK_CHANNEL: ${{ secrets.RELEASE_TEST_SLACK_CHANNEL }}
|
||||||
|
GITHUB_REPOSITORY: ${{ matrix.private && secrets[matrix.repo] || matrix.repo }}
|
||||||
|
PLUGIN_NAME: ${{ matrix.plugin }}
|
||||||
|
GITHUB_TOKEN: ${{ secrets.E2E_GH_TOKEN }}
|
||||||
|
run: |
|
||||||
|
pnpx wc-e2e test:e2e plugins/woocommerce/tests/e2e/specs/smoke-tests/upload-plugin.js
|
||||||
|
pnpm nx test-e2e woocommerce
|
||||||
|
|
|
@ -13,6 +13,15 @@ project.properties
|
||||||
*.sublime-workspace
|
*.sublime-workspace
|
||||||
.sublimelinterrc
|
.sublimelinterrc
|
||||||
|
|
||||||
|
# Grunt
|
||||||
|
none
|
||||||
|
|
||||||
|
# Sass
|
||||||
|
.sass-cache/
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
logs/
|
||||||
|
|
||||||
# Eslint Cache
|
# Eslint Cache
|
||||||
.eslintcache
|
.eslintcache
|
||||||
|
|
||||||
|
@ -40,13 +49,27 @@ vendor/
|
||||||
# TypeScript files
|
# TypeScript files
|
||||||
tsconfig.tsbuildinfo
|
tsconfig.tsbuildinfo
|
||||||
|
|
||||||
|
# Node Package Dependencies
|
||||||
|
package-lock.json
|
||||||
|
|
||||||
# wp-env config
|
# wp-env config
|
||||||
.wp-env.override.json
|
.wp-env.override.json
|
||||||
|
|
||||||
# Unit tests
|
# Unit tests
|
||||||
/tmp
|
tmp/
|
||||||
packages/js/e2e-environment/config/default.json
|
|
||||||
packages/js/e2e-environment/docker/wp-cli/initialize.sh
|
# Composer
|
||||||
|
vendor/
|
||||||
|
bin/composer/**/vendor/
|
||||||
|
lib/vendor/
|
||||||
|
contributors.md
|
||||||
|
contributors.html
|
||||||
|
|
||||||
|
# Yarn
|
||||||
|
yarn.lock
|
||||||
|
|
||||||
|
# Editors
|
||||||
|
nbproject/private/
|
||||||
|
|
||||||
# Test Results
|
# Test Results
|
||||||
test-results.json
|
test-results.json
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
{
|
{
|
||||||
"extends": "stylelint-config-wordpress",
|
"extends": "@wordpress/stylelint-config",
|
||||||
}
|
}
|
||||||
|
|
105
changelog.txt
105
changelog.txt
|
@ -1,5 +1,110 @@
|
||||||
== Changelog ==
|
== Changelog ==
|
||||||
|
|
||||||
|
= 6.0.0 2021-12-14 =
|
||||||
|
|
||||||
|
**WooCommerce**
|
||||||
|
|
||||||
|
* Localization - Improve internationalization and add regions for Chile. ([#30875](https://github.com/woocommerce/woocommerce/pull/30875))
|
||||||
|
* Localization - Add 'GB' back to countries that are recommended to use automated taxes. ([#31100](https://github.com/woocommerce/woocommerce/pull/31100))
|
||||||
|
* Enhancement - Improve the performance of the filtering by attributes using the new lookup table. ([#31212](https://github.com/woocommerce/woocommerce/pull/31212))
|
||||||
|
* Enhancement - Stop using options table to store rate limits. ([#30960](https://github.com/woocommerce/woocommerce/pull/30960))
|
||||||
|
* Enhancement - Support for dynamic price period in in-app marketplace product cards. ([#31026](https://github.com/woocommerce/woocommerce/pull/31026))
|
||||||
|
* Enhancement - warning to developers to avoid gotcha with shipping rates. ([#30958](https://github.com/woocommerce/woocommerce/pull/30958))
|
||||||
|
* Enhancement - Add woocommerce_product_options_shipping_product_data hook to product data metabox. ([#30876](https://github.com/woocommerce/woocommerce/pull/30876))
|
||||||
|
* Enhancement - Ensure empty arrays can be cached. ([#31077](https://github.com/woocommerce/woocommerce/pull/31077))
|
||||||
|
* Tweak - Remove the need to invoke LookupDataStore->show_feature() to use the product attributes lookup table. ([#31228](https://github.com/woocommerce/woocommerce/pull/31228))
|
||||||
|
* Tweak - Add new action hook woocommerce_after_variations_table after the product variations table (within the add-to-cart form). ([#29642](https://github.com/woocommerce/woocommerce/pull/29642))
|
||||||
|
* Tweak - Email password reset link instead of password for new customers. ([#31257](https://github.com/woocommerce/woocommerce/pull/31257))
|
||||||
|
* Tweak - Disable autocomplete on quantity input field to prevent stale values in Firefox. ([#31196](https://github.com/woocommerce/woocommerce/pull/31196))
|
||||||
|
* Tweak - Correct the Iraqi Dinar (IQD) symbol. ([#31070](https://github.com/woocommerce/woocommerce/pull/31070))
|
||||||
|
* Tweak - Add Product Reviews filter for review comment type to the WordPress comment page. ([#31004](https://github.com/woocommerce/woocommerce/pull/31004))
|
||||||
|
* Tweak - has_block_template method: Add apply_filters to the function which will enable third-party plugins to override the return value. ([#30997](https://github.com/woocommerce/woocommerce/pull/30997))
|
||||||
|
* Tweak - Clarify tooltip for when the on-hold email is sent. ([#30970](https://github.com/woocommerce/woocommerce/pull/30970))
|
||||||
|
* Tweak - Perform check for has product archive if current theme is an FSE theme, and not just if it has current_theme_supports( 'woocommerce' ); ([#31094](https://github.com/woocommerce/woocommerce/pull/31094))
|
||||||
|
* Tweak - Remove the absolute path to the currency-info.php from within locale-info.php. ([#30935](https://github.com/woocommerce/woocommerce/pull/30935))
|
||||||
|
* Tweak - Update track properties to follow correct format. ([#30899](https://github.com/woocommerce/woocommerce/pull/30899))
|
||||||
|
* Tweak - Merge Marketplace and My Subscriptions pages back into one Extensions page. ([#31085](https://github.com/woocommerce/woocommerce/pull/31085))
|
||||||
|
* Fix - Fixes an issue that prevented database update notices from being dismissed. ([#31075](https://github.com/woocommerce/woocommerce/pull/31075))
|
||||||
|
* Fix - Duplicate coupon usage count when order is created via admin/API and status is changed. ([#31147](https://github.com/woocommerce/woocommerce/pull/31147))
|
||||||
|
* Fix - Corrects the display of negative refund values within the order editor screen. ([#30957](https://github.com/woocommerce/woocommerce/pull/30957))
|
||||||
|
* Fix - Fix bug when creating REST API keys with very long descriptions. ([#30901](https://github.com/woocommerce/woocommerce/pull/30901))
|
||||||
|
* Fix - Fix products API orderby slug and include. ([#30873](https://github.com/woocommerce/woocommerce/pull/30873))
|
||||||
|
* Dev - Remove defunct AJAX events. ([#30931](https://github.com/woocommerce/woocommerce/pull/30931))
|
||||||
|
|
||||||
|
**WooCommerce Admin - 2.9.0 & 2.9.1 & 2.9.2 **
|
||||||
|
|
||||||
|
* Dev - Remove task status endpoint ( [#7841](https://github.com/woocommerce/woocommerce-admin/issues/7841) )
|
||||||
|
* Fix - Fix ordering and styling issue with WooCommerce Payments payment method promotion. ( [#7943](https://github.com/woocommerce/woocommerce-admin/issues/7943) )
|
||||||
|
* Fix - Fix ExPlat PHP client ( [#7926](https://github.com/woocommerce/woocommerce-admin/issues/7926) )
|
||||||
|
* Fix - Fix marketing extensions tracks ( [#7908](https://github.com/woocommerce/woocommerce-admin/issues/7908) )
|
||||||
|
* Fix - Fix shipping task completion status ( [#8031](https://github.com/woocommerce/woocommerce-admin/issues/8031) )
|
||||||
|
* Update - Increased number of possible items in Recommended Extensions list from 6 to 9 ( [#7887](https://github.com/woocommerce/woocommerce-admin/issues/7887) )
|
||||||
|
* Update - Reverts addition of Marketplace and My Subscriptions pages to the Marketplace menu. ( [#7902](https://github.com/woocommerce/woocommerce-admin/issues/7902) )
|
||||||
|
* Update - Add marketing extensions back to onboarding wizard ( [#7831](https://github.com/woocommerce/woocommerce-admin/issues/7831) )
|
||||||
|
* Update - Add profile notes. ( [#7861](https://github.com/woocommerce/woocommerce-admin/issues/7861) )
|
||||||
|
* Update - Change CTA text for personalize store task after completion ( [#7852](https://github.com/woocommerce/woocommerce-admin/issues/7852) )
|
||||||
|
* Update - Refactor data source poller for re-usability. ( [#7671](https://github.com/woocommerce/woocommerce-admin/issues/7671) )
|
||||||
|
* Update - Update WC Pay card to include in-person information ( [#7830](https://github.com/woocommerce/woocommerce-admin/issues/7830) )
|
||||||
|
* Update - Updating navigation link colors ( [#7833](https://github.com/woocommerce/woocommerce-admin/issues/7833) )
|
||||||
|
* Tweak - Use page title Extensions for Marketplace and My Subscriptions pages. ( [#7901](https://github.com/woocommerce/woocommerce-admin/issues/7901) )
|
||||||
|
* Tweak - Remove the Spinner component to prevent undesired page flickering. ( [#7886](https://github.com/woocommerce/woocommerce-admin/issues/7886) )
|
||||||
|
* Tweak - Add route and layout for unmatched path ( [#7503](https://github.com/woocommerce/woocommerce-admin/issues/7503) )
|
||||||
|
* Tweak - Avoid caching extended info ( [#7819](https://github.com/woocommerce/woocommerce-admin/issues/7819) )
|
||||||
|
* Tweak - Minor design update for Marketing task. ( [#7732](https://github.com/woocommerce/woocommerce-admin/issues/7732) )
|
||||||
|
* Fix - Do not clear `current` class from the entire page when updating wp-admin's menu. ( [#7773](https://github.com/woocommerce/woocommerce-admin/issues/7773) )
|
||||||
|
* Fix - Fix calendar not being dismissed when clicking outside. ( [#7714](https://github.com/woocommerce/woocommerce-admin/issues/7714) )
|
||||||
|
* Fix - fixed warnings when using AdvancedFilters component. ( [#7704](https://github.com/woocommerce/woocommerce-admin/issues/7704) )
|
||||||
|
* Fix - Fix Tasklist UI illustrations styling ( [#7858](https://github.com/woocommerce/woocommerce-admin/issues/7858) )
|
||||||
|
* Fix - Revert experiment task titles back to original ( [#7853](https://github.com/woocommerce/woocommerce-admin/issues/7853) )
|
||||||
|
* Fix - Ensure homescreen defaults to single column layout. ( [#7969](https://github.com/woocommerce/woocommerce-admin/issues/7969) )
|
||||||
|
* Fix: Fix shipping task not offering step 3. ( [#7985](https://github.com/woocommerce/woocommerce-admin/issues/7985) )
|
||||||
|
* Add - Add Avalara to tax task ( [#7874](https://github.com/woocommerce/woocommerce-admin/issues/7874) )
|
||||||
|
* Add - Add 2col expirement. ( [#7872](https://github.com/woocommerce/woocommerce-admin/issues/7872) )
|
||||||
|
* Add - Added two column experimental task list ( [#7669](https://github.com/woocommerce/woocommerce-admin/issues/7669) )
|
||||||
|
* Add - Add header cards for all tasks in Tasklist UI experiment ( [#7838](https://github.com/woocommerce/woocommerce-admin/issues/7838) )
|
||||||
|
* Add - Add onboarding task docs ( [#7762](https://github.com/woocommerce/woocommerce-admin/issues/7762) )
|
||||||
|
* Dev - Add method to check for install status ( [#7808](https://github.com/woocommerce/woocommerce-admin/issues/7808) )
|
||||||
|
* Dev - Refactor tax task into separate components
|
||||||
|
* Dev - Update the task list to use the new task list REST API ( [#7736](https://github.com/woocommerce/woocommerce-admin/issues/7736) )
|
||||||
|
* Performance - Only load default tasks during REST requests ( [#7904](https://github.com/woocommerce/woocommerce-admin/issues/7904) )
|
||||||
|
|
||||||
|
**WooCommerce Blocks - 6.2.0 & 6.3.0 & 6.3.1 & 6.3.2**
|
||||||
|
|
||||||
|
* Enhancement - Legacy Template Block: allow users to delete the block. ( [#5176](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5176) )
|
||||||
|
* Enhancement - Add placeholder text when modifying product search input in the editor. ( [#5122](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5122) )
|
||||||
|
* Enhancement - FSE: Add basic product archive block template. ( [#5049](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5049) )
|
||||||
|
* Enhancement - FSE: Add basic taxonomy block templates. ( [#5063](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5063) )
|
||||||
|
* Enhancement - FSE: Add single product block template. ( [#5054](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5054) )
|
||||||
|
* Enhancement - FSE: Remove the do_action( ‘woocommerce_sidebar’ ); action from the LegacyTemplate.php block. ( [#5097](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5097) )
|
||||||
|
* Enhancement - Fix duplicate queries in product grids. ( [#5002](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5002) )
|
||||||
|
* Enhancement - FSE: Add abstract block legacy template for core PHP templates. ( [#4991](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/4991) )
|
||||||
|
* Enhancement - FSE: Add render logic to BlockTemplateController. ( [#4984](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/4984) )
|
||||||
|
* Enhancement - Improve accessibility by using self-explaining edit button titles. ( [#5113](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5113) )
|
||||||
|
* Enhancement - Improve readability of terms and condition text by not displaying the text justified. ( [#5120](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5120) )
|
||||||
|
* Enhancement - Improve rendering performance for Single Product block. ( [#5107](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5107) )
|
||||||
|
* Enhancement - Improve the product images placeholder display by adding a light gray border to it. ( [#4950](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/4950) )
|
||||||
|
* Enhancement - Deprecate the __experimental_woocommerce_blocks_checkout_update_order_from_request action in favour of woocommerce_blocks_checkout_update_order_from_request. ( [#5015](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5015) )
|
||||||
|
* Enhancement - Deprecate the __experimental_woocommerce_blocks_checkout_update_order_meta action in favour of woocommerce_blocks_checkout_update_order_meta. ( [#5017](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5017) )
|
||||||
|
* Enhancement - Deprecate the __experimental_woocommerce_blocks_checkout_order_processed action in favour of woocommerce_blocks_checkout_order_processed. ( [#5014](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5014) )
|
||||||
|
* Enhancement - Cart v2: The cart block, like checkout block, now supports inner blocks that allow for greater customizability. ( [#4973](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/4973) )
|
||||||
|
* Enhancement - BlockTemplateController: Adds the ability to load and manage block template files. ( [#4981](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/4981) )
|
||||||
|
* Enhancement - Improve accessibility for the editor view of the Product search block. ( [#4905](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/4905) )
|
||||||
|
* Fix - Removed WooCommerce block templates from appearing in the template dropdown for a page or post. ( [#5167](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5167) )
|
||||||
|
* Fix - Fix ‘Country is required’ error on the Cart block when updating shipping address ( [#5129](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5129) )
|
||||||
|
* Fix - Fix state validation to compare state codes, and only validate if a country is given ( [#5132](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5132) )
|
||||||
|
* Fix - Make order note block removable ( [#5139](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5139) )
|
||||||
|
* Fix - Fix label alignment of the product search in the editor. ( [#5072](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5072) )
|
||||||
|
* Fix - Fix sale badge alignment on smaller screen. ( [#5061](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5061) )
|
||||||
|
* Fix - FSE: Fix missing is_custom property for WooCommerce block template objects. ( [#5067](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5067) )
|
||||||
|
* Fix - Replace incorrect with correct text domain. ( [#5020](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5020) )
|
||||||
|
* Fix - Scripts using wc-settings or script that depend on it would be enqueued in the footer if they’re enqueued in the header. ( [#5059](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/5059) )
|
||||||
|
* Fix - Fix custom classname support for inner checkout blocks. ( [#4978](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/4978) )
|
||||||
|
* Fix - Fix a bug in free orders and trial subscription products. ( [#4955](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/4955) )
|
||||||
|
* Fix - Remove duplicate attributes in saved block HTML. ( [#4941](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/4941) )
|
||||||
|
* Fix - Fix render error of Filter by Attribute block when no attribute is selected. ( [#4847](https://github.com/woocommerce/woocommerce-gutenberg-products-block/pull/4847) )
|
||||||
|
* 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) )
|
||||||
|
|
||||||
= 5.9.0 2021-11-09 =
|
= 5.9.0 2021-11-09 =
|
||||||
|
|
||||||
**WooCommerce**
|
**WooCommerce**
|
||||||
|
|
11
package.json
11
package.json
|
@ -18,11 +18,12 @@
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@automattic/nx-composer": "^0.1.0",
|
"@automattic/nx-composer": "^0.1.0",
|
||||||
"@nrwl/cli": "latest",
|
"@nrwl/cli": "^13.3.4",
|
||||||
"@nrwl/linter": "^13.1.4",
|
"@nrwl/linter": "^13.3.4",
|
||||||
"@nrwl/tao": "latest",
|
"@nrwl/devkit": "^13.1.4",
|
||||||
"@nrwl/web": "^13.1.4",
|
"@nrwl/tao": "13.3.4",
|
||||||
"@nrwl/workspace": "latest",
|
"@nrwl/web": "^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": "^1.3.0",
|
||||||
"@wordpress/prettier-config": "^1.1.1",
|
"@wordpress/prettier-config": "^1.1.1",
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
## Added
|
## Added
|
||||||
- Shipping Zones API Tests
|
- Shipping Zones API Tests
|
||||||
- Shipping Methods API Tests
|
- Shipping Methods API Tests
|
||||||
|
- Complex Order API Tests
|
||||||
|
|
||||||
# 0.1.0
|
# 0.1.0
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,16 @@
|
||||||
const { order, getOrderExample } = require( './order' );
|
const { order, getOrderExample } = require( './order' );
|
||||||
const { coupon } = require( './coupon' );
|
const { coupon } = require( './coupon' );
|
||||||
const { refund } = require( './refund' );
|
const { refund } = require( './refund' );
|
||||||
|
const { getTaxRateExamples } = require( './tax-rate' );
|
||||||
|
const { getVariationExample } = require( './variation' );
|
||||||
|
const {
|
||||||
|
simpleProduct,
|
||||||
|
variableProduct,
|
||||||
|
variation,
|
||||||
|
virtualProduct,
|
||||||
|
groupedProduct,
|
||||||
|
externalProduct,
|
||||||
|
} = require( './products-crud' );
|
||||||
const { getShippingZoneExample } = require( './shipping-zone' );
|
const { getShippingZoneExample } = require( './shipping-zone' );
|
||||||
const { getShippingMethodExample } = require( './shipping-method' );
|
const { getShippingMethodExample } = require( './shipping-method' );
|
||||||
const shared = require( './shared' );
|
const shared = require( './shared' );
|
||||||
|
@ -11,6 +21,14 @@ module.exports = {
|
||||||
coupon,
|
coupon,
|
||||||
shared,
|
shared,
|
||||||
refund,
|
refund,
|
||||||
|
getTaxRateExamples,
|
||||||
|
getVariationExample,
|
||||||
|
simpleProduct,
|
||||||
|
variableProduct,
|
||||||
|
variation,
|
||||||
|
virtualProduct,
|
||||||
|
groupedProduct,
|
||||||
|
externalProduct,
|
||||||
getShippingZoneExample,
|
getShippingZoneExample,
|
||||||
getShippingMethodExample,
|
getShippingMethodExample,
|
||||||
};
|
};
|
||||||
|
|
|
@ -54,8 +54,27 @@ const variableProduct = {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* External product example
|
||||||
|
*/
|
||||||
|
const externalProduct = {
|
||||||
|
name: 'An External Product',
|
||||||
|
regular_price: '1.00',
|
||||||
|
type: 'external',
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Grouped product example
|
||||||
|
*/
|
||||||
|
const groupedProduct = {
|
||||||
|
name: 'A Grouped Product',
|
||||||
|
type: 'grouped',
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
simpleProduct,
|
simpleProduct,
|
||||||
virtualProduct,
|
virtualProduct,
|
||||||
variableProduct,
|
variableProduct,
|
||||||
|
externalProduct,
|
||||||
|
groupedProduct,
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
/**
|
||||||
|
* A standard tax rate.
|
||||||
|
*
|
||||||
|
* For more details on the tax rate properties, see:
|
||||||
|
*
|
||||||
|
* https://woocommerce.github.io/woocommerce-rest-api-docs/#tax-rate-properties
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
const standardTaxRate = {
|
||||||
|
name: 'Standard Rate',
|
||||||
|
rate: '10.0000',
|
||||||
|
class: 'standard',
|
||||||
|
};
|
||||||
|
|
||||||
|
const reducedTaxRate = {
|
||||||
|
name: 'Reduced Rate',
|
||||||
|
rate: '1.0000',
|
||||||
|
class: 'reduced-rate',
|
||||||
|
};
|
||||||
|
|
||||||
|
const zeroTaxRate = {
|
||||||
|
name: 'Zero Rate',
|
||||||
|
rate: '0.0000',
|
||||||
|
class: 'zero-rate',
|
||||||
|
};
|
||||||
|
|
||||||
|
const getTaxRateExamples = () => {
|
||||||
|
return { standardTaxRate, reducedTaxRate, zeroTaxRate };
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
getTaxRateExamples,
|
||||||
|
};
|
|
@ -0,0 +1,29 @@
|
||||||
|
/**
|
||||||
|
* A basic product variation.
|
||||||
|
*
|
||||||
|
* For more details on the product variation properties, see:
|
||||||
|
*
|
||||||
|
* https://woocommerce.github.io/woocommerce-rest-api-docs/#product-variations
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
const variation = {
|
||||||
|
regular_price: '1.00',
|
||||||
|
attributes: [
|
||||||
|
{
|
||||||
|
name: 'Size',
|
||||||
|
option: 'Large',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Colour',
|
||||||
|
option: 'Red',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const getVariationExample = () => {
|
||||||
|
return variation;
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
getVariationExample,
|
||||||
|
};
|
|
@ -2,6 +2,8 @@ const { ordersApi } = require( './orders' );
|
||||||
const { couponsApi } = require( './coupons' );
|
const { couponsApi } = require( './coupons' );
|
||||||
const { productsApi } = require( './products' );
|
const { productsApi } = require( './products' );
|
||||||
const { refundsApi } = require( './refunds' );
|
const { refundsApi } = require( './refunds' );
|
||||||
|
const { taxRatesApi } = require( './tax-rates' );
|
||||||
|
const { variationsApi } = require( './variations' );
|
||||||
const { shippingZonesApi } = require( './shipping-zones' );
|
const { shippingZonesApi } = require( './shipping-zones' );
|
||||||
const { shippingMethodsApi } = require( './shipping-methods' );
|
const { shippingMethodsApi } = require( './shipping-methods' );
|
||||||
|
|
||||||
|
@ -10,6 +12,8 @@ module.exports = {
|
||||||
couponsApi,
|
couponsApi,
|
||||||
productsApi,
|
productsApi,
|
||||||
refundsApi,
|
refundsApi,
|
||||||
|
taxRatesApi,
|
||||||
|
variationsApi,
|
||||||
shippingZonesApi,
|
shippingZonesApi,
|
||||||
shippingMethodsApi,
|
shippingMethodsApi,
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
/**
|
||||||
|
* Internal dependencies
|
||||||
|
*/
|
||||||
|
const {
|
||||||
|
getRequest,
|
||||||
|
postRequest,
|
||||||
|
putRequest,
|
||||||
|
deleteRequest,
|
||||||
|
} = require( '../utils/request' );
|
||||||
|
const { getTaxRateExamples, shared } = require( '../data' );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WooCommerce Tax Rates endpoints.
|
||||||
|
*
|
||||||
|
* https://woocommerce.github.io/woocommerce-rest-api-docs/#tax-rates
|
||||||
|
*/
|
||||||
|
const taxRatesApi = {
|
||||||
|
name: 'Tax Rates',
|
||||||
|
create: {
|
||||||
|
name: 'Create a tax rate',
|
||||||
|
method: 'POST',
|
||||||
|
path: 'taxes',
|
||||||
|
responseCode: 201,
|
||||||
|
payload: getTaxRateExamples(),
|
||||||
|
taxRate: async ( taxRate ) => postRequest( 'taxes', taxRate ),
|
||||||
|
},
|
||||||
|
retrieve: {
|
||||||
|
name: 'Retrieve a tax rate',
|
||||||
|
method: 'GET',
|
||||||
|
path: 'taxes/<id>',
|
||||||
|
responseCode: 200,
|
||||||
|
taxRate: async ( taxRateId ) => taxes( `coupons/${ taxRateId }` ),
|
||||||
|
},
|
||||||
|
listAll: {
|
||||||
|
name: 'List all tax rates',
|
||||||
|
method: 'GET',
|
||||||
|
path: 'taxes',
|
||||||
|
responseCode: 200,
|
||||||
|
taxRates: async ( queryString = {} ) =>
|
||||||
|
getRequest( 'taxes', queryString ),
|
||||||
|
},
|
||||||
|
update: {
|
||||||
|
name: 'Update a tax rate',
|
||||||
|
method: 'PUT',
|
||||||
|
path: 'taxes/<id>',
|
||||||
|
responseCode: 200,
|
||||||
|
payload: getTaxRateExamples(),
|
||||||
|
taxRate: async ( taxRateId, taxRateDetails ) =>
|
||||||
|
putRequest( `taxes/${ taxRateId }`, taxRateDetails ),
|
||||||
|
},
|
||||||
|
delete: {
|
||||||
|
name: 'Delete a tax rate',
|
||||||
|
method: 'DELETE',
|
||||||
|
path: 'taxes/<id>',
|
||||||
|
responseCode: 200,
|
||||||
|
payload: {
|
||||||
|
force: false,
|
||||||
|
},
|
||||||
|
taxRate: async ( taxRateId, deletePermanently ) =>
|
||||||
|
deleteRequest( `taxes/${ taxRateId }`, deletePermanently ),
|
||||||
|
},
|
||||||
|
batch: {
|
||||||
|
name: 'Batch update tax rates',
|
||||||
|
method: 'POST',
|
||||||
|
path: 'taxes/batch',
|
||||||
|
responseCode: 200,
|
||||||
|
payload: shared.getBatchPayloadExample( getTaxRateExamples() ),
|
||||||
|
taxRates: async ( batchUpdatePayload ) =>
|
||||||
|
postRequest( `taxes/batch`, batchUpdatePayload ),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = { taxRatesApi };
|
|
@ -0,0 +1,84 @@
|
||||||
|
/**
|
||||||
|
* Internal dependencies
|
||||||
|
*/
|
||||||
|
const {
|
||||||
|
getRequest,
|
||||||
|
postRequest,
|
||||||
|
putRequest,
|
||||||
|
deleteRequest,
|
||||||
|
} = require( '../utils/request' );
|
||||||
|
const { getVariationExample, shared } = require( '../data' );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WooCommerce Product Variation endpoints.
|
||||||
|
*
|
||||||
|
* https://woocommerce.github.io/woocommerce-rest-api-docs/#product-variations
|
||||||
|
*/
|
||||||
|
const variationsApi = {
|
||||||
|
name: 'Product variations',
|
||||||
|
create: {
|
||||||
|
name: 'Create a product variation',
|
||||||
|
method: 'POST',
|
||||||
|
path: 'products/<product_id>/variations',
|
||||||
|
responseCode: 201,
|
||||||
|
payload: getVariationExample(),
|
||||||
|
variation: async ( productId, variation ) =>
|
||||||
|
postRequest( `products/${ productId }/variations`, variation ),
|
||||||
|
},
|
||||||
|
retrieve: {
|
||||||
|
name: 'Retrieve a product variation',
|
||||||
|
method: 'GET',
|
||||||
|
path: 'products/<product_id>/variations/<id>',
|
||||||
|
responseCode: 200,
|
||||||
|
variation: async ( productId, variationId ) =>
|
||||||
|
`products/${ productId }/variations/${ variationId }`,
|
||||||
|
},
|
||||||
|
listAll: {
|
||||||
|
name: 'List all product variations',
|
||||||
|
method: 'GET',
|
||||||
|
path: 'products/<product_id>/variations',
|
||||||
|
responseCode: 200,
|
||||||
|
variations: async ( productId, queryString = {} ) =>
|
||||||
|
getRequest( `products/${ productId }/variations`, queryString ),
|
||||||
|
},
|
||||||
|
update: {
|
||||||
|
name: 'Update a product variation',
|
||||||
|
method: 'PUT',
|
||||||
|
path: 'products/<product_id>/variations/<id>',
|
||||||
|
responseCode: 200,
|
||||||
|
payload: getVariationExample(),
|
||||||
|
variation: async ( productId, variationId, variationDetails ) =>
|
||||||
|
putRequest(
|
||||||
|
`products/${ productId }/variations/${ variationId }`,
|
||||||
|
taxRateDetails
|
||||||
|
),
|
||||||
|
},
|
||||||
|
delete: {
|
||||||
|
name: 'Delete a product variation',
|
||||||
|
method: 'DELETE',
|
||||||
|
path: 'products/<product_id>/variations/<id>',
|
||||||
|
responseCode: 200,
|
||||||
|
payload: {
|
||||||
|
force: false,
|
||||||
|
},
|
||||||
|
variation: async ( productId, variationId, deletePermanently ) =>
|
||||||
|
deleteRequest(
|
||||||
|
`products/${ productId }/variations/${ variationId }`,
|
||||||
|
deletePermanently
|
||||||
|
),
|
||||||
|
},
|
||||||
|
batch: {
|
||||||
|
name: 'Batch update product variations',
|
||||||
|
method: 'POST',
|
||||||
|
path: 'products/<product_id>/variations/batch',
|
||||||
|
responseCode: 200,
|
||||||
|
payload: shared.getBatchPayloadExample( getVariationExample() ),
|
||||||
|
variations: async ( batchUpdatePayload ) =>
|
||||||
|
postRequest(
|
||||||
|
`products/${ productId }/variations/${ variationId }`,
|
||||||
|
batchUpdatePayload
|
||||||
|
),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = { variationsApi };
|
|
@ -4,6 +4,7 @@
|
||||||
"description": "API tests for WooCommerce",
|
"description": "API tests for WooCommerce",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
"preinstall": "npx only-allow pnpm",
|
||||||
"test": "jest",
|
"test": "jest",
|
||||||
"test:api": "jest --group=api",
|
"test:api": "jest --group=api",
|
||||||
"test:hello": "jest --group=hello",
|
"test:hello": "jest --group=hello",
|
||||||
|
|
|
@ -3,6 +3,13 @@
|
||||||
"sourceRoot": "packages/js/api-core-tests",
|
"sourceRoot": "packages/js/api-core-tests",
|
||||||
"projectType": "library",
|
"projectType": "library",
|
||||||
"targets": {
|
"targets": {
|
||||||
|
"changelog": {
|
||||||
|
"executor": "./tools/executors/changelogger:changelog",
|
||||||
|
"options": {
|
||||||
|
"action": "add",
|
||||||
|
"cwd": "packages/js/api-core-tests"
|
||||||
|
}
|
||||||
|
},
|
||||||
"test": {
|
"test": {
|
||||||
"executor": "@nrwl/workspace:run-script",
|
"executor": "@nrwl/workspace:run-script",
|
||||||
"options": {
|
"options": {
|
||||||
|
|
|
@ -0,0 +1,208 @@
|
||||||
|
const {
|
||||||
|
taxRatesApi,
|
||||||
|
productsApi,
|
||||||
|
ordersApi,
|
||||||
|
variationsApi,
|
||||||
|
} = require( '../../endpoints' );
|
||||||
|
const {
|
||||||
|
getOrderExample,
|
||||||
|
getTaxRateExamples,
|
||||||
|
getVariationExample,
|
||||||
|
simpleProduct: defaultSimpleProduct,
|
||||||
|
variableProduct: defaultVariableProduct,
|
||||||
|
groupedProduct: defaultGroupedProduct,
|
||||||
|
externalProduct: defaultExternalProduct,
|
||||||
|
} = require( '../../data' );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple product with Standard tax rate
|
||||||
|
*/
|
||||||
|
const simpleProduct = {
|
||||||
|
...defaultSimpleProduct,
|
||||||
|
regular_price: '10.00',
|
||||||
|
tax_class: 'standard',
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Variable product with 1 variation with Reduced tax rate
|
||||||
|
*/
|
||||||
|
const variableProduct = {
|
||||||
|
...defaultVariableProduct,
|
||||||
|
tax_class: 'reduced-rate',
|
||||||
|
};
|
||||||
|
|
||||||
|
const variation = {
|
||||||
|
...getVariationExample(),
|
||||||
|
regular_price: '20.00',
|
||||||
|
tax_class: 'reduced-rate',
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* External product with Zero rate tax
|
||||||
|
*/
|
||||||
|
const externalProduct = {
|
||||||
|
...defaultExternalProduct,
|
||||||
|
regular_price: '400.00',
|
||||||
|
tax_class: 'zero-rate',
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Grouped product
|
||||||
|
*/
|
||||||
|
const groupedProduct = defaultGroupedProduct;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tax rates for each tax class
|
||||||
|
*/
|
||||||
|
const { standardTaxRate, reducedTaxRate, zeroTaxRate } = getTaxRateExamples();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete all pre-existing tax rates.
|
||||||
|
*/
|
||||||
|
const deletePreExistingTaxRates = async () => {
|
||||||
|
const { body } = await taxRatesApi.listAll.taxRates( {
|
||||||
|
_fields: 'id',
|
||||||
|
} );
|
||||||
|
|
||||||
|
if ( Array.isArray( body ) && body.length > 0 ) {
|
||||||
|
const ids = body.map( ( { id } ) => id );
|
||||||
|
await taxRatesApi.batch.taxRates( { delete: ids } );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a tax rate for each tax class, and save their ID's.
|
||||||
|
*/
|
||||||
|
const createTaxRates = async () => {
|
||||||
|
const taxRates = [ standardTaxRate, reducedTaxRate, zeroTaxRate ];
|
||||||
|
|
||||||
|
for ( const taxRate of taxRates ) {
|
||||||
|
const { body } = await taxRatesApi.create.taxRate( taxRate );
|
||||||
|
taxRate.id = body.id;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create simple, variable, grouped, and external products.
|
||||||
|
*/
|
||||||
|
const createProducts = async () => {
|
||||||
|
// Create a simple product
|
||||||
|
const { body: createdSimpleProduct } = await productsApi.create.product(
|
||||||
|
simpleProduct
|
||||||
|
);
|
||||||
|
simpleProduct.id = createdSimpleProduct.id;
|
||||||
|
|
||||||
|
// Create a variable product with 1 variation
|
||||||
|
const { body: createdVariableProduct } = await productsApi.create.product(
|
||||||
|
variableProduct
|
||||||
|
);
|
||||||
|
variableProduct.id = createdVariableProduct.id;
|
||||||
|
await variationsApi.create.variation( variableProduct.id, variation );
|
||||||
|
|
||||||
|
// Create a grouped product using the simple product created earlier.
|
||||||
|
groupedProduct.grouped_products = [ simpleProduct.id ];
|
||||||
|
const { body: createdGroupedProduct } = await productsApi.create.product(
|
||||||
|
groupedProduct
|
||||||
|
);
|
||||||
|
groupedProduct.id = createdGroupedProduct.id;
|
||||||
|
|
||||||
|
// Create an external product
|
||||||
|
const { body: createdExternalProduct } = await productsApi.create.product(
|
||||||
|
externalProduct
|
||||||
|
);
|
||||||
|
externalProduct.id = createdExternalProduct.id;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The complex order to be created.
|
||||||
|
*/
|
||||||
|
const order = {
|
||||||
|
...getOrderExample(),
|
||||||
|
shipping_lines: [],
|
||||||
|
fee_lines: [],
|
||||||
|
coupon_lines: [],
|
||||||
|
line_items: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Expected totals
|
||||||
|
*/
|
||||||
|
const expectedOrderTotal = '442.20';
|
||||||
|
const expectedTaxTotal = '2.20';
|
||||||
|
const expectedSimpleProductTaxTotal = '1.00';
|
||||||
|
const expectedVariableProductTaxTotal = '0.20';
|
||||||
|
const expectedExternalProductTaxTotal = '0.00';
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Test for adding a complex order with different product types and tax classes.
|
||||||
|
*
|
||||||
|
* @group api
|
||||||
|
* @group orders
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
describe( 'Orders API test', () => {
|
||||||
|
beforeAll( async () => {
|
||||||
|
await deletePreExistingTaxRates();
|
||||||
|
await createTaxRates();
|
||||||
|
await createProducts();
|
||||||
|
|
||||||
|
// Add line items to the order
|
||||||
|
order.line_items = [
|
||||||
|
{ product_id: simpleProduct.id },
|
||||||
|
{ product_id: variableProduct.id },
|
||||||
|
{ product_id: externalProduct.id },
|
||||||
|
{ product_id: groupedProduct.id },
|
||||||
|
];
|
||||||
|
} );
|
||||||
|
|
||||||
|
afterAll( async () => {
|
||||||
|
// Delete order
|
||||||
|
await ordersApi.delete.order( order.id, true );
|
||||||
|
|
||||||
|
// Delete products
|
||||||
|
await productsApi.batch.products( {
|
||||||
|
delete: [
|
||||||
|
simpleProduct.id,
|
||||||
|
variableProduct.id,
|
||||||
|
externalProduct.id,
|
||||||
|
groupedProduct.id,
|
||||||
|
],
|
||||||
|
} );
|
||||||
|
|
||||||
|
// Delete tax rates
|
||||||
|
await taxRatesApi.batch.taxRates( {
|
||||||
|
delete: [ standardTaxRate.id, zeroTaxRate.id, reducedTaxRate.id ],
|
||||||
|
} );
|
||||||
|
} );
|
||||||
|
|
||||||
|
it( 'can add complex order', async () => {
|
||||||
|
// Create the complex order and save its ID.
|
||||||
|
const { status, body } = await ordersApi.create.order( order );
|
||||||
|
order.id = body.id;
|
||||||
|
|
||||||
|
expect( status ).toEqual( ordersApi.create.responseCode );
|
||||||
|
|
||||||
|
// Verify order and tax totals
|
||||||
|
expect( body.total ).toEqual( expectedOrderTotal );
|
||||||
|
expect( body.total_tax ).toEqual( expectedTaxTotal );
|
||||||
|
|
||||||
|
// Verify total tax of each product line item
|
||||||
|
const expectedTaxTotalsPerLineItem = [
|
||||||
|
[ simpleProduct, expectedSimpleProductTaxTotal ],
|
||||||
|
[ variableProduct, expectedVariableProductTaxTotal ],
|
||||||
|
[ groupedProduct, expectedSimpleProductTaxTotal ],
|
||||||
|
[ externalProduct, expectedExternalProductTaxTotal ],
|
||||||
|
];
|
||||||
|
for ( const [
|
||||||
|
product,
|
||||||
|
expectedLineTaxTotal,
|
||||||
|
] of expectedTaxTotalsPerLineItem ) {
|
||||||
|
const { total_tax: actualLineTaxTotal } = body.line_items.find(
|
||||||
|
( { product_id } ) => product_id === product.id
|
||||||
|
);
|
||||||
|
|
||||||
|
expect( actualLineTaxTotal ).toEqual( expectedLineTaxTotal );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
} );
|
|
@ -1,2 +0,0 @@
|
||||||
# Editors
|
|
||||||
/nbproject/private/
|
|
|
@ -26,6 +26,7 @@
|
||||||
],
|
],
|
||||||
"sideEffects": false,
|
"sideEffects": false,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
"preinstall": "npx only-allow pnpm",
|
||||||
"clean": "rm -rf ./dist ./tsconfig.tsbuildinfo",
|
"clean": "rm -rf ./dist ./tsconfig.tsbuildinfo",
|
||||||
"compile": "tsc -b",
|
"compile": "tsc -b",
|
||||||
"build": "pnpm run clean && npm run compile",
|
"build": "pnpm run clean && npm run compile",
|
||||||
|
|
|
@ -3,6 +3,13 @@
|
||||||
"sourceRoot": "packages/js/api/src",
|
"sourceRoot": "packages/js/api/src",
|
||||||
"projectType": "library",
|
"projectType": "library",
|
||||||
"targets": {
|
"targets": {
|
||||||
|
"changelog": {
|
||||||
|
"executor": "./tools/executors/changelogger:changelog",
|
||||||
|
"options": {
|
||||||
|
"action": "add",
|
||||||
|
"cwd": "packages/js/api"
|
||||||
|
}
|
||||||
|
},
|
||||||
"build": {
|
"build": {
|
||||||
"executor": "@nrwl/workspace:run-script",
|
"executor": "@nrwl/workspace:run-script",
|
||||||
"options": {
|
"options": {
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
- A `specs/data` folder to store page element data.
|
- A `specs/data` folder to store page element data.
|
||||||
- Tests to verify that different top-level menu and their associated sub-menus load successfully.
|
- Tests to verify that different top-level menu and their associated sub-menus load successfully.
|
||||||
|
- Test scaffolding via `npx wc-e2e install @woocommerce/e2e-core-tests`
|
||||||
|
|
||||||
## Changed
|
## Changed
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,18 @@ Follow [E2E setup instructions](https://github.com/woocommerce/woocommerce/blob/
|
||||||
|
|
||||||
### Setting up core tests
|
### Setting up core tests
|
||||||
|
|
||||||
|
#### Version 0.2.0 or newer
|
||||||
|
|
||||||
|
Version 0.2.0 added a test installer that will populate the `tests/e2e/specs` folder with test scripts for all the current core test suite. It also creates sample configuration files including all the configuration data needed to run the core tests.
|
||||||
|
|
||||||
|
- Install the e2e-environment `npm install @woocommerce/e2e-environment --save-dev`
|
||||||
|
- Run the installer `npx wc-e2e install @woocommerce/e2e-core-tests`
|
||||||
|
- Merge the sample configuration files:
|
||||||
|
- `tests/e2e/docker/woocommerce.e2e-core-tests.sh` => `initialize.sh`
|
||||||
|
- `tests/e2e/config/default-woocommerce.e2e-core-tests.json` => `default.json`
|
||||||
|
|
||||||
|
#### Version 0.1.X or other test runner
|
||||||
|
|
||||||
- Create the folder `tests/e2e/specs` in your repository if it does not exist.
|
- Create the folder `tests/e2e/specs` in your repository if it does not exist.
|
||||||
- To add a core test to your test suite, create a new `.test.js` file within `tests/e2e/specs` . Example code to run all the shopper tests:
|
- To add a core test to your test suite, create a new `.test.js` file within `tests/e2e/specs` . Example code to run all the shopper tests:
|
||||||
```js
|
```js
|
||||||
|
@ -104,7 +116,7 @@ The functions to access the core tests are:
|
||||||
|
|
||||||
## Contributing a new test
|
## Contributing a new test
|
||||||
|
|
||||||
- In your branch create a new `example-test-name.test.js` under the `tests/e2e/core-tests/specs` folder.
|
- In your branch create a new `example-test-name.test.js` under the appropriate folder in the [`specs`](specs) directory.
|
||||||
- Jest does not allow its global functions to be accessed outside the jest environment. To allow the test code to be published in a package import any jest global functions used in your test
|
- Jest does not allow its global functions to be accessed outside the jest environment. To allow the test code to be published in a package import any jest global functions used in your test
|
||||||
```js
|
```js
|
||||||
const {
|
const {
|
||||||
|
@ -130,7 +142,7 @@ const runExampleTestName = () => {
|
||||||
|
|
||||||
module.exports = runExampleTestName;
|
module.exports = runExampleTestName;
|
||||||
```
|
```
|
||||||
- Add your test to `tests/e2e/core-tests/specs/index.js`
|
- Add your test to [`specs/index.js`](specs/index.js)
|
||||||
```js
|
```js
|
||||||
const runExampleTestName = require( './grouping/example-test-name.test' );
|
const runExampleTestName = require( './grouping/example-test-name.test' );
|
||||||
// ...
|
// ...
|
||||||
|
|
|
@ -0,0 +1,195 @@
|
||||||
|
{
|
||||||
|
"url": "http://localhost:8084/",
|
||||||
|
"users": {
|
||||||
|
"admin": {
|
||||||
|
"username": "admin",
|
||||||
|
"password": "password"
|
||||||
|
},
|
||||||
|
"customer": {
|
||||||
|
"username": "customer",
|
||||||
|
"password": "password"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"products": {
|
||||||
|
"simple": {
|
||||||
|
"name": "Simple product"
|
||||||
|
},
|
||||||
|
"variable": {
|
||||||
|
"name": "Variable Product with Three Attributes",
|
||||||
|
"defaultAttributes": [
|
||||||
|
{
|
||||||
|
"id": 0,
|
||||||
|
"name": "Size",
|
||||||
|
"option": "Medium"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 0,
|
||||||
|
"name": "Colour",
|
||||||
|
"option": "Blue"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"attributes": [
|
||||||
|
{
|
||||||
|
"id": 0,
|
||||||
|
"name": "Colour",
|
||||||
|
"isVisibleOnProductPage": true,
|
||||||
|
"isForVariations": true,
|
||||||
|
"options": [
|
||||||
|
"Red",
|
||||||
|
"Green",
|
||||||
|
"Blue"
|
||||||
|
],
|
||||||
|
"sortOrder": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 0,
|
||||||
|
"name": "Size",
|
||||||
|
"isVisibleOnProductPage": true,
|
||||||
|
"isForVariations": true,
|
||||||
|
"options": [
|
||||||
|
"Small",
|
||||||
|
"Medium",
|
||||||
|
"Large"
|
||||||
|
],
|
||||||
|
"sortOrder": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 0,
|
||||||
|
"name": "Logo",
|
||||||
|
"isVisibleOnProductPage": true,
|
||||||
|
"isForVariations": true,
|
||||||
|
"options": [
|
||||||
|
"Woo",
|
||||||
|
"WordPress"
|
||||||
|
],
|
||||||
|
"sortOrder": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"variations": [
|
||||||
|
{
|
||||||
|
"regularPrice": "19.99",
|
||||||
|
"attributes": [
|
||||||
|
{
|
||||||
|
"name": "Size",
|
||||||
|
"option": "Large"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Colour",
|
||||||
|
"option": "Red"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"regularPrice": "18.99",
|
||||||
|
"attributes": [
|
||||||
|
{
|
||||||
|
"name": "Size",
|
||||||
|
"option": "Medium"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Colour",
|
||||||
|
"option": "Green"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"regularPrice": "17.99",
|
||||||
|
"attributes": [
|
||||||
|
{
|
||||||
|
"name": "Size",
|
||||||
|
"option": "Small"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Colour",
|
||||||
|
"option": "Blue"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"grouped": {
|
||||||
|
"name": "Grouped Product with Three Children",
|
||||||
|
"groupedProducts": [
|
||||||
|
{
|
||||||
|
"name": "Base Unit",
|
||||||
|
"regularPrice": "29.99"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Add-on A",
|
||||||
|
"regularPrice": "11.95"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Add-on B",
|
||||||
|
"regularPrice": "18.97"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"external": {
|
||||||
|
"name": "External product",
|
||||||
|
"regularPrice": "24.99",
|
||||||
|
"buttonText": "Buy now",
|
||||||
|
"externalUrl": "https://wordpress.org/plugins/woocommerce"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"coupons": {
|
||||||
|
"percentage": {
|
||||||
|
"code": "20percent",
|
||||||
|
"discountType": "percent",
|
||||||
|
"amount": "20.00"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"addresses": {
|
||||||
|
"admin": {
|
||||||
|
"store": {
|
||||||
|
"email": "admin@woocommercecoree2etestsuite.com",
|
||||||
|
"firstname": "John",
|
||||||
|
"lastname": "Doe",
|
||||||
|
"company": "Automattic",
|
||||||
|
"country": "United States (US)",
|
||||||
|
"addressfirstline": "addr 1",
|
||||||
|
"addresssecondline": "addr 2",
|
||||||
|
"countryandstate": "United States (US) — California",
|
||||||
|
"city": "San Francisco",
|
||||||
|
"state": "CA",
|
||||||
|
"postcode": "94107"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"customer": {
|
||||||
|
"billing": {
|
||||||
|
"firstname": "John",
|
||||||
|
"lastname": "Doe",
|
||||||
|
"company": "Automattic",
|
||||||
|
"country": "United States (US)",
|
||||||
|
"addressfirstline": "addr 1",
|
||||||
|
"addresssecondline": "addr 2",
|
||||||
|
"city": "San Francisco",
|
||||||
|
"state": "CA",
|
||||||
|
"postcode": "94107",
|
||||||
|
"phone": "123456789",
|
||||||
|
"email": "john.doe@example.com"
|
||||||
|
},
|
||||||
|
"shipping": {
|
||||||
|
"firstname": "John",
|
||||||
|
"lastname": "Doe",
|
||||||
|
"company": "Automattic",
|
||||||
|
"country": "United States (US)",
|
||||||
|
"addressfirstline": "addr 1",
|
||||||
|
"addresssecondline": "addr 2",
|
||||||
|
"city": "San Francisco",
|
||||||
|
"state": "CA",
|
||||||
|
"postcode": "94107"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"orders": {
|
||||||
|
"basicPaidOrder": {
|
||||||
|
"paymentMethod": "cod",
|
||||||
|
"status": "processing",
|
||||||
|
"billing": {
|
||||||
|
"firstName": "John",
|
||||||
|
"lastName": "Doe",
|
||||||
|
"email": "john.doe@example.com"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
module.exports = {
|
||||||
|
testSpecs: 'installFiles/scaffold-tests.json',
|
||||||
|
defaultJson: 'installFiles/default-test-config.json',
|
||||||
|
initializeSh: 'installFiles/initialize.sh.default',
|
||||||
|
};
|
|
@ -0,0 +1,25 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
echo "Initializing WooCommerce E2E"
|
||||||
|
|
||||||
|
# This is a workaround to accommodate different directory names.
|
||||||
|
wp plugin activate --all
|
||||||
|
wp plugin deactivate akismet
|
||||||
|
wp plugin deactivate hello
|
||||||
|
|
||||||
|
wp theme install twentynineteen --activate
|
||||||
|
wp user create customer customer@woocommercecoree2etestsuite.com \
|
||||||
|
--user_pass=password \
|
||||||
|
--role=subscriber \
|
||||||
|
--first_name='Jane' \
|
||||||
|
--last_name='Smith' \
|
||||||
|
--path=/var/www/html
|
||||||
|
|
||||||
|
# we cannot create API keys for the API, so we using basic auth, this plugin allows that.
|
||||||
|
wp plugin install https://github.com/WP-API/Basic-Auth/archive/master.zip --activate
|
||||||
|
|
||||||
|
# install the WP Mail Logging plugin to test emails
|
||||||
|
wp plugin install wp-mail-logging --activate
|
||||||
|
|
||||||
|
# initialize pretty permalinks
|
||||||
|
wp rewrite structure /%postname%/
|
|
@ -0,0 +1,138 @@
|
||||||
|
{
|
||||||
|
"active": [
|
||||||
|
{
|
||||||
|
"name": "front-end",
|
||||||
|
"description": "Shopper tests",
|
||||||
|
"testFiles": [
|
||||||
|
{
|
||||||
|
"name": "cart-begin",
|
||||||
|
"functions": [ "runCartPageTest" ]
|
||||||
|
}, {
|
||||||
|
"name": "cart-calculate-shipping",
|
||||||
|
"functions": [ "runCartCalculateShippingTest" ]
|
||||||
|
}, {
|
||||||
|
"name": "cart-coupons",
|
||||||
|
"functions": [ "runCartApplyCouponsTest" ]
|
||||||
|
}, {
|
||||||
|
"name": "checkout-begin",
|
||||||
|
"functions": [ "runCheckoutPageTest" ]
|
||||||
|
}, {
|
||||||
|
"name": "checkout-coupons",
|
||||||
|
"functions": [ "runCheckoutApplyCouponsTest" ]
|
||||||
|
}, {
|
||||||
|
"name": "checkout-create-account",
|
||||||
|
"functions": [ "runCheckoutCreateAccountTest" ]
|
||||||
|
}, {
|
||||||
|
"name": "checkout-login-account",
|
||||||
|
"functions": [ "runCheckoutLoginAccountTest" ]
|
||||||
|
}, {
|
||||||
|
"name": "my-account-create-account",
|
||||||
|
"functions": [ "runMyAccountCreateAccountTest" ]
|
||||||
|
}, {
|
||||||
|
"name": "my-account-pay-order",
|
||||||
|
"functions": [ "runMyAccountPayOrderTest" ]
|
||||||
|
}, {
|
||||||
|
"name": "my-account",
|
||||||
|
"functions": [ "runMyAccountPageTest" ]
|
||||||
|
}, {
|
||||||
|
"name": "order-email-receiving",
|
||||||
|
"functions": [ "runOrderEmailReceivingTest" ]
|
||||||
|
}, {
|
||||||
|
"name": "product-browse-search-sort",
|
||||||
|
"functions": [ "runProductBrowseSearchSortTest" ]
|
||||||
|
}, {
|
||||||
|
"name": "single-product-page",
|
||||||
|
"functions": [ "runSingleProductPageTest" ]
|
||||||
|
}, {
|
||||||
|
"name": "variable-product-updates",
|
||||||
|
"functions": [ "runVariableProductUpdateTest" ]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}, {
|
||||||
|
"name": "rest-api",
|
||||||
|
"description": "REST API tests",
|
||||||
|
"testFiles": [
|
||||||
|
{
|
||||||
|
"name": "api",
|
||||||
|
"functions": [ "runApiTests" ]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}, {
|
||||||
|
"name": "wp-admin",
|
||||||
|
"description": "Merchant tests",
|
||||||
|
"testFiles": [
|
||||||
|
{
|
||||||
|
"name": "create-coupon",
|
||||||
|
"functions": [ "runCreateCouponTest" ]
|
||||||
|
}, {
|
||||||
|
"name": "create-order",
|
||||||
|
"functions": [ "runCreateOrderTest" ]
|
||||||
|
}, {
|
||||||
|
"name": "create-shipping-classes",
|
||||||
|
"functions": [ "runAddShippingClassesTest" ]
|
||||||
|
}, {
|
||||||
|
"name": "create-shipping-zones",
|
||||||
|
"functions": [ "runAddNewShippingZoneTest" ]
|
||||||
|
}, {
|
||||||
|
"name": "create-simple-product",
|
||||||
|
"functions": [ "runAddSimpleProductTest" ]
|
||||||
|
}, {
|
||||||
|
"name": "create-variable-product",
|
||||||
|
"functions": [ "runAddVariableProductTest" ]
|
||||||
|
}, {
|
||||||
|
"name": "order-coupon",
|
||||||
|
"functions": [ "runOrderApplyCouponTest" ]
|
||||||
|
}, {
|
||||||
|
"name": "order-customer-payment-page",
|
||||||
|
"functions": [ "runMerchantOrdersCustomerPaymentPage" ]
|
||||||
|
}, {
|
||||||
|
"name": "order-edit",
|
||||||
|
"functions": [ "runEditOrderTest" ]
|
||||||
|
}, {
|
||||||
|
"name": "order-emails",
|
||||||
|
"functions": [ "runMerchantOrderEmailsTest" ]
|
||||||
|
}, {
|
||||||
|
"name": "order-refund",
|
||||||
|
"functions": [ "runOrderRefundTest" ]
|
||||||
|
}, {
|
||||||
|
"name": "order-searching",
|
||||||
|
"functions": [ "runOrderSearchingTest" ]
|
||||||
|
}, {
|
||||||
|
"name": "order-status-filters",
|
||||||
|
"functions": [ "runOrderStatusFiltersTest" ]
|
||||||
|
}, {
|
||||||
|
"name": "product-edit",
|
||||||
|
"functions": [ "runProductEditDetailsTest" ]
|
||||||
|
}, {
|
||||||
|
"name": "product-import-csv",
|
||||||
|
"functions": [ "runImportProductsTest" ]
|
||||||
|
}, {
|
||||||
|
"name": "product-search",
|
||||||
|
"functions": [ "runProductSearchTest" ]
|
||||||
|
}, {
|
||||||
|
"name": "update-general-settings",
|
||||||
|
"functions": [ "runUpdateGeneralSettingsTest" ]
|
||||||
|
}, {
|
||||||
|
"name": "update-product-settings",
|
||||||
|
"functions": [ "runProductSettingsTest" ]
|
||||||
|
}, {
|
||||||
|
"name": "update-tax-settings",
|
||||||
|
"functions": [ "runTaxSettingsTest" ]
|
||||||
|
}, {
|
||||||
|
"name": "wccom-connect",
|
||||||
|
"functions": [ "runInitiateWccomConnectionTest" ]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"deprecated": [
|
||||||
|
{
|
||||||
|
"name": "example-folder",
|
||||||
|
"testFiles": [
|
||||||
|
{ "name": "any-filename-to-deprecate" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
"access": "public"
|
"access": "public"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
"preinstall": "npx only-allow pnpm",
|
||||||
"build": "./bin/build.sh",
|
"build": "./bin/build.sh",
|
||||||
"prepare": "pnpm run build"
|
"prepare": "pnpm run build"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,14 @@
|
||||||
{
|
{
|
||||||
"root": "packages/js/e2e-core-tests/",
|
"root": "packages/js/e2e-core-tests/",
|
||||||
"sourceRoot": "packages/js/e2e-core-tests",
|
"sourceRoot": "packages/js/e2e-core-tests",
|
||||||
"projectType": "library"
|
"projectType": "library",
|
||||||
|
"targets": {
|
||||||
|
"changelog": {
|
||||||
|
"executor": "./tools/executors/changelogger:changelog",
|
||||||
|
"options": {
|
||||||
|
"action": "add",
|
||||||
|
"cwd": "packages/js/e2e-core-tests"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
config/default.json
|
||||||
|
docker/wp-cli/initialize.sh
|
|
@ -8,10 +8,14 @@
|
||||||
## Added
|
## Added
|
||||||
|
|
||||||
- Added `await` for every call to `shopper.logout`
|
- Added `await` for every call to `shopper.logout`
|
||||||
|
- Updated `getLatestReleaseZipUrl()` to allow passing in an authorization token and simplified arguments to just the repository name
|
||||||
|
- Added `upload.ini` which increases the limits for uploading files (such as for plugins) in the Docker environment
|
||||||
|
- Test setup, scaffolding, and removal via `wc-e2e install` and `wc-e2e uninstall`
|
||||||
|
|
||||||
## Fixed
|
## Fixed
|
||||||
|
|
||||||
- Updated the browserViewport in `jest.setup.js` to match the `defaultViewport` dimensions defined in `jest-puppeteer.config.js`
|
- Updated the browserViewport in `jest.setup.js` to match the `defaultViewport` dimensions defined in `jest-puppeteer.config.js`
|
||||||
|
|
||||||
## Added
|
## Added
|
||||||
|
|
||||||
- Added quotes around `WORDPRESS_TITLE` value in .env file to address issue with docker compose 2 "key cannot contain a space" error.
|
- Added quotes around `WORDPRESS_TITLE` value in .env file to address issue with docker compose 2 "key cannot contain a space" error.
|
||||||
|
|
|
@ -9,6 +9,19 @@ npm install @woocommerce/e2e-environment --save
|
||||||
npm install jest --global
|
npm install jest --global
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Version 0.3.0 and newer
|
||||||
|
|
||||||
|
Version 0.3.0 added a test installer that will populate the `tests/e2e/*` folder with test scripts and configuration files. The installer will create test scripts for E2E test packages that include support for the installer.
|
||||||
|
|
||||||
|
- [Adding test scaffolding to E2E test packages](https://github.com/woocommerce/woocommerce/tree/trunk/packages/js/e2e-environment/test-packages.md)
|
||||||
|
|
||||||
|
#### Using the installer
|
||||||
|
|
||||||
|
- Install a default test environment: `npx wc-e2e install`
|
||||||
|
- Install test specs from an E2E tests package: `npx wc-e2e install @woocommerce-e2e-tests [--format cjs] [--ext spec.js]`
|
||||||
|
- The default test spec format and extension are `ES6` and `test.js`
|
||||||
|
- Remove test specs for an E2E tests package: `npx wc-e2e uninstall @woocommerce-e2e-tests`
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
The `@woocommerce/e2e-environment` package exports configuration objects that can be consumed in JavaScript config files in your project. Additionally, it includes a basic hosting container for running tests and includes instructions for creating your Travis CI setup.
|
The `@woocommerce/e2e-environment` package exports configuration objects that can be consumed in JavaScript config files in your project. Additionally, it includes a basic hosting container for running tests and includes instructions for creating your Travis CI setup.
|
||||||
|
@ -60,10 +73,10 @@ The E2E environment uses Jest as a test runner. Extending the base config is nec
|
||||||
|
|
||||||
```js
|
```js
|
||||||
const path = require( 'path' );
|
const path = require( 'path' );
|
||||||
const { useE2EJestConfig } = require( '@woocommerce/e2e-environment' );
|
const { useE2EJestConfig, resolveLocalE2ePath } = require( '@woocommerce/e2e-environment' );
|
||||||
|
|
||||||
const jestConfig = useE2EJestConfig( {
|
const jestConfig = useE2EJestConfig( {
|
||||||
roots: [ path.resolve( __dirname, '../specs' ) ],
|
roots: [ resolveLocalE2ePath( 'specs' ) ],
|
||||||
} );
|
} );
|
||||||
|
|
||||||
module.exports = jestConfig;
|
module.exports = jestConfig;
|
||||||
|
@ -71,6 +84,20 @@ module.exports = jestConfig;
|
||||||
|
|
||||||
**NOTE:** Your project's Jest config file is: `tests/e2e/config/jest.config.js`.
|
**NOTE:** Your project's Jest config file is: `tests/e2e/config/jest.config.js`.
|
||||||
|
|
||||||
|
### The Jest Object
|
||||||
|
|
||||||
|
The E2E environment has the following methods to let us control Jest's overall behavior.
|
||||||
|
|
||||||
|
| Function | Parameters | Description |
|
||||||
|
|-----------|-------------|--------------|
|
||||||
|
| `setupJestRetries` | `retries` | Sets the amount of retries on failed tests
|
||||||
|
|
||||||
|
**NOTE:** The amount of times failed tests are retried can also be set using the `E2E_RETRY_TIMES` environment variable when executing tests. This can be done using the command below:
|
||||||
|
|
||||||
|
```
|
||||||
|
E2E_RETRY_TIMES=2 pnpx wc-e2e test:e2e
|
||||||
|
```
|
||||||
|
|
||||||
#### Test Screenshots
|
#### Test Screenshots
|
||||||
|
|
||||||
The test sequencer provides a screenshot function for test failures. To enable screenshots on test failure use
|
The test sequencer provides a screenshot function for test failures. To enable screenshots on test failure use
|
||||||
|
@ -142,7 +169,7 @@ The test sequencer uses the following default Puppeteer configuration:
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
You can customize the configuration in `tests/e2e/config/jest-puppeteer.config.js`
|
You can customize the configuration in [`config/jest-puppeteer.config.js`](config/jest-puppeteer.config.js)
|
||||||
|
|
||||||
```js
|
```js
|
||||||
const { useE2EJestPuppeteerConfig } = require( '@woocommerce/e2e-environment' );
|
const { useE2EJestPuppeteerConfig } = require( '@woocommerce/e2e-environment' );
|
||||||
|
@ -158,7 +185,7 @@ module.exports = puppeteerConfig;
|
||||||
|
|
||||||
### Jest Setup
|
### Jest Setup
|
||||||
|
|
||||||
Jest provides [setup and teardown functions](https://jestjs.io/docs/setup-teardown) similar to PHPUnit. The default setup and teardown is in [`tests/e2e/env/src/setup/jest.setup.js`](src/setup/jest.setup.js). Additional setup and teardown functions can be added to [`tests/e2e/config/jest.setup.js`](../config/jest.setup.js)
|
Jest provides [setup and teardown functions](https://jestjs.io/docs/setup-teardown) similar to PHPUnit. The default setup and teardown is in [`src/setup/jest.setup.js`](src/setup/jest.setup.js). Additional setup and teardown functions can be added to [`tests/e2e/config/jest.setup.js`](../../../plugins/woocommerce/tests/e2e/config/jest.setup.js)
|
||||||
|
|
||||||
#### Console filtering
|
#### Console filtering
|
||||||
|
|
||||||
|
@ -227,9 +254,9 @@ The above method also makes use of the following utility methods which can also
|
||||||
|
|
||||||
If you would like to get the latest release zip URL, which can be used in the methods mentioned above, you can use the following helper function to do so:
|
If you would like to get the latest release zip URL, which can be used in the methods mentioned above, you can use the following helper function to do so:
|
||||||
|
|
||||||
`getLatestReleaseZipUrl( owner, repository, getPrerelease, perPage )`
|
`getLatestReleaseZipUrl( repository, authorizationToken, getPrerelease, perPage )`
|
||||||
|
|
||||||
This will return a string with the latest release URL. Optionally, you can use the `getPrerelease` boolean flag, which defaults to false, on whether or not to get a prerelease instead. The `perPage` flag can be used to return more results when getting the list of releases. The default value is 3.
|
This will return a string with the latest release URL. Optionally, you can use the `getPrerelease` boolean flag, which defaults to false, on whether or not to get a prerelease instead. The `perPage` flag can be used to return more results when getting the list of releases. The default value is 3. If the repository requires authorization to access, the authorization token can be passed in to the `authorizationToken` argument.
|
||||||
|
|
||||||
## Additional information
|
## Additional information
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@ const {
|
||||||
getAppName,
|
getAppName,
|
||||||
getTestConfig,
|
getTestConfig,
|
||||||
resolveLocalE2ePath,
|
resolveLocalE2ePath,
|
||||||
|
resolvePackagePath,
|
||||||
} = require( '../utils' );
|
} = require( '../utils' );
|
||||||
|
|
||||||
const dockerArgs = [];
|
const dockerArgs = [];
|
||||||
|
@ -63,7 +64,7 @@ if ( appPath ) {
|
||||||
if ( fs.existsSync( appInitFile ) ) {
|
if ( fs.existsSync( appInitFile ) ) {
|
||||||
fs.copyFileSync(
|
fs.copyFileSync(
|
||||||
appInitFile,
|
appInitFile,
|
||||||
path.resolve( __dirname, '../docker/wp-cli/initialize.sh' )
|
resolvePackagePath( 'docker/wp-cli/initialize.sh' )
|
||||||
);
|
);
|
||||||
console.log( 'Initializing ' + appInitFile );
|
console.log( 'Initializing ' + appInitFile );
|
||||||
}
|
}
|
||||||
|
@ -90,7 +91,7 @@ if ( ! process.env.WORDPRESS_URL ) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure that the first Docker compose file loaded is from our local env.
|
// Ensure that the first Docker compose file loaded is from our local env.
|
||||||
dockerArgs.unshift( '-f', path.resolve( __dirname, '../docker-compose.yaml' ) );
|
dockerArgs.unshift( '-f', resolvePackagePath( 'docker-compose.yaml' ) );
|
||||||
|
|
||||||
const dockerProcess = spawnSync( 'docker-compose', dockerArgs, {
|
const dockerProcess = spawnSync( 'docker-compose', dockerArgs, {
|
||||||
stdio: 'inherit',
|
stdio: 'inherit',
|
||||||
|
|
|
@ -4,7 +4,11 @@ const { spawnSync } = require( 'child_process' );
|
||||||
const program = require( 'commander' );
|
const program = require( 'commander' );
|
||||||
const path = require( 'path' );
|
const path = require( 'path' );
|
||||||
const fs = require( 'fs' );
|
const fs = require( 'fs' );
|
||||||
const { getAppRoot, resolveLocalE2ePath } = require( '../utils' );
|
const {
|
||||||
|
getAppRoot,
|
||||||
|
resolveLocalE2ePath,
|
||||||
|
resolvePackagePath,
|
||||||
|
} = require( '../utils' );
|
||||||
const {
|
const {
|
||||||
WC_E2E_SCREENSHOTS,
|
WC_E2E_SCREENSHOTS,
|
||||||
JEST_PUPPETEER_CONFIG,
|
JEST_PUPPETEER_CONFIG,
|
||||||
|
@ -30,7 +34,7 @@ if ( WC_E2E_SCREENSHOTS ) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const nodeConfigDirs = [ path.resolve( __dirname, '../config' ) ];
|
const nodeConfigDirs = [ resolvePackagePath( 'config' ) ];
|
||||||
|
|
||||||
if ( appPath ) {
|
if ( appPath ) {
|
||||||
nodeConfigDirs.unshift( resolveLocalE2ePath( 'config' ) );
|
nodeConfigDirs.unshift( resolveLocalE2ePath( 'config' ) );
|
||||||
|
@ -51,10 +55,7 @@ if ( ! JEST_PUPPETEER_CONFIG ) {
|
||||||
// Use local Puppeteer config if there is one.
|
// Use local Puppeteer config if there is one.
|
||||||
// Load test configuration file into an object.
|
// Load test configuration file into an object.
|
||||||
const localJestConfigFile = resolveLocalE2ePath( 'config/jest-puppeteer.config.js' );
|
const localJestConfigFile = resolveLocalE2ePath( 'config/jest-puppeteer.config.js' );
|
||||||
const jestConfigFile = path.resolve(
|
const jestConfigFile = resolvePackagePath( 'config/jest-puppeteer.config.js' );
|
||||||
__dirname,
|
|
||||||
'../config/jest-puppeteer.config.js'
|
|
||||||
);
|
|
||||||
|
|
||||||
testEnvVars.JEST_PUPPETEER_CONFIG = fs.existsSync( localJestConfigFile )
|
testEnvVars.JEST_PUPPETEER_CONFIG = fs.existsSync( localJestConfigFile )
|
||||||
? localJestConfigFile
|
? localJestConfigFile
|
||||||
|
@ -90,7 +91,7 @@ if ( program.debug ) {
|
||||||
|
|
||||||
const envVars = Object.assign( {}, process.env, testEnvVars );
|
const envVars = Object.assign( {}, process.env, testEnvVars );
|
||||||
|
|
||||||
let configPath = path.resolve( __dirname, '../config/jest.config.js' );
|
let configPath = resolvePackagePath( 'config/jest.config.js' );
|
||||||
|
|
||||||
// Look for a Jest config in the dependent app's path.
|
// Look for a Jest config in the dependent app's path.
|
||||||
if ( appPath ) {
|
if ( appPath ) {
|
||||||
|
|
|
@ -0,0 +1,233 @@
|
||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
/**
|
||||||
|
* External dependencies.
|
||||||
|
*/
|
||||||
|
const fs = require( 'fs' );
|
||||||
|
const path = require( 'path' );
|
||||||
|
const sprintf = require( 'sprintf-js' ).sprintf;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal dependencies.
|
||||||
|
*/
|
||||||
|
const {
|
||||||
|
resolvePackage,
|
||||||
|
resolvePackagePath,
|
||||||
|
} = require( '../utils' );
|
||||||
|
const {
|
||||||
|
createLocalE2ePath,
|
||||||
|
confirm,
|
||||||
|
confirmLocalCopy,
|
||||||
|
confirmLocalDelete,
|
||||||
|
getPackageData,
|
||||||
|
installDefaults
|
||||||
|
} = require( '../utils/scaffold' );
|
||||||
|
|
||||||
|
const args = process.argv.slice( 2 );
|
||||||
|
const [ command, packageName ] = args;
|
||||||
|
|
||||||
|
// Allow multiple spec file extensions and formats.
|
||||||
|
let testExtension = 'test.js';
|
||||||
|
let testFormat = '';
|
||||||
|
for ( let a = 2; a < args.length; a++ ) {
|
||||||
|
const nextArg = a + 1;
|
||||||
|
if ( nextArg >= args.length ) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
switch ( args[ a ] ) {
|
||||||
|
case '--format':
|
||||||
|
testFormat = args[ nextArg ];
|
||||||
|
break;
|
||||||
|
case '--ext':
|
||||||
|
testExtension = args[ nextArg ];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Install the test scripts and sample default.json configuration
|
||||||
|
*/
|
||||||
|
if ( command == 'install' ) {
|
||||||
|
// Install some environment defaults if no package is requested.
|
||||||
|
if ( ! packageName ) {
|
||||||
|
installDefaults();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// `package` is a reserved word
|
||||||
|
const pkg = resolvePackage( packageName ).name;
|
||||||
|
if ( ! pkg.length ) {
|
||||||
|
//@todo add error message
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const { packageSlug, testSpecs, defaultJson, initializeSh } = getPackageData( pkg );
|
||||||
|
|
||||||
|
// Write sample default.json
|
||||||
|
if ( defaultJson ) {
|
||||||
|
const defaultJsonName = `config${path.sep}default-${packageSlug}.json`;
|
||||||
|
createLocalE2ePath( 'config' );
|
||||||
|
if ( confirmLocalCopy( defaultJsonName, defaultJson, pkg ) ) {
|
||||||
|
console.log( `Created sample test configuration to 'tests/e2e/${defaultJsonName}'.` );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write sample initialize.sh
|
||||||
|
if ( initializeSh ) {
|
||||||
|
const defaultInitName = `docker${path.sep}${packageSlug}.sh`;
|
||||||
|
createLocalE2ePath( 'docker' );
|
||||||
|
if ( confirmLocalCopy( defaultInitName, initializeSh, pkg ) ) {
|
||||||
|
console.log( `Created sample test container initialization script to 'tests/e2e/${defaultInitName}'.` );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! testSpecs ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write test files
|
||||||
|
const testsSpecFile = resolvePackagePath( testSpecs, pkg );
|
||||||
|
const specs = fs.readFileSync( testsSpecFile );
|
||||||
|
const tests = JSON.parse( specs );
|
||||||
|
const { active, deprecated } = tests;
|
||||||
|
|
||||||
|
if ( active && active.length ) {
|
||||||
|
const blankLine = '';
|
||||||
|
const eol = "\n";
|
||||||
|
const autoGenerate = sprintf( '/* This file was auto-generated by the command `npx wc-e2e install %s`. */', packageName );
|
||||||
|
let importLineFormat;
|
||||||
|
let overwriteFiles;
|
||||||
|
let confirmPrompt;
|
||||||
|
if ( testFormat.toLowerCase() == 'cjs' ) {
|
||||||
|
importLineFormat = sprintf( "const {%%s} = require( '%s' );", pkg );
|
||||||
|
} else {
|
||||||
|
importLineFormat = sprintf( "import {%%s} from '%s';", pkg );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the specs folder if not present
|
||||||
|
let specFolderPath = createLocalE2ePath( 'specs' );
|
||||||
|
|
||||||
|
// Loop through folders and files to write test scripts.
|
||||||
|
for ( let f = 0; f < active.length; f++ ) {
|
||||||
|
if ( overwriteFiles == 'q' ) {
|
||||||
|
overwriteFiles = '';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const testFolder = active[ f ];
|
||||||
|
const { testFiles } = testFolder;
|
||||||
|
|
||||||
|
if ( ! testFiles || ! testFiles.length ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let specFolder;
|
||||||
|
if ( testFolder.name.length ) {
|
||||||
|
specFolder = createLocalE2ePath( `specs${path.sep}${testFolder.name}` );
|
||||||
|
} else {
|
||||||
|
specFolder = specFolderPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the test files.
|
||||||
|
for ( let t = 0; t < testFiles.length; t++ ) {
|
||||||
|
const testFile = testFiles[ t ];
|
||||||
|
if ( ! testFile.functions.length ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const testFileName = `${testFolder.name}${path.sep}${testFile.name}.${testExtension}`;
|
||||||
|
const testFilePath = `${specFolder}${path.sep}${testFile.name}.${testExtension}`;
|
||||||
|
|
||||||
|
// Check to see if file exists.
|
||||||
|
if ( fs.existsSync( testFilePath ) ) {
|
||||||
|
if ( overwriteFiles != 'a' ) {
|
||||||
|
confirmPrompt = `${testFileName} already exists. Overwrite? [y]es/[n]o/[a]ll/[q]uit: `;
|
||||||
|
overwriteFiles = confirm( confirmPrompt, 'anqy' );
|
||||||
|
overwriteFiles = overwriteFiles.toLowerCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( overwriteFiles == 'q' ) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if ( overwriteFiles != 'a' && overwriteFiles != 'y' ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log( 'Writing tests/e2e/specs/' + testFileName );
|
||||||
|
let buffer = [ autoGenerate ];
|
||||||
|
let testSeparator, testTerminator, importPrefix;
|
||||||
|
|
||||||
|
// Add the import line.
|
||||||
|
if ( testFile.functions.length > 3 ) {
|
||||||
|
testSeparator = ',' + eol;
|
||||||
|
testTerminator = eol;
|
||||||
|
importPrefix = eol;
|
||||||
|
} else {
|
||||||
|
testSeparator = ', ';
|
||||||
|
testTerminator = ' ';
|
||||||
|
importPrefix = ' ';
|
||||||
|
}
|
||||||
|
const testImport = testFile.functions.join( testSeparator ) + testTerminator;
|
||||||
|
buffer.push( sprintf( importLineFormat, importPrefix + testImport ), blankLine );
|
||||||
|
|
||||||
|
// Add test function calls and write the file
|
||||||
|
let functionCalls = testFile.functions.map( functionName => functionName + '();' );
|
||||||
|
buffer.push( ...functionCalls, blankLine );
|
||||||
|
fs.writeFileSync( testFilePath, buffer.join( eol ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// @todo: deprecated files.
|
||||||
|
} else if ( command == 'uninstall' ) {
|
||||||
|
if ( ! packageName ) {
|
||||||
|
// @todo: write error message
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const pkg = resolvePackage( packageName ).name;
|
||||||
|
const { packageSlug, testSpecs, defaultJson, initializeSh } = getPackageData( pkg );
|
||||||
|
|
||||||
|
// Delete sample default.json
|
||||||
|
if ( defaultJson ) {
|
||||||
|
const defaultJsonName = `config${path.sep}default-${packageSlug}.json`;
|
||||||
|
confirmLocalDelete( defaultJsonName );
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete sample initialize.sh
|
||||||
|
if ( initializeSh ) {
|
||||||
|
const defaultInitName = `docker${path.sep}${packageSlug}.sh`;
|
||||||
|
confirmLocalDelete( defaultInitName );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! testSpecs ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const testsSpecFile = resolvePackagePath( testSpecs, pkg );
|
||||||
|
const specs = fs.readFileSync( testsSpecFile );
|
||||||
|
const tests = JSON.parse( specs );
|
||||||
|
const { active } = tests;
|
||||||
|
|
||||||
|
if ( ! active || ! active.length ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loop through folders and files to delete test scripts.
|
||||||
|
for ( let f = 0; f < active.length; f++ ) {
|
||||||
|
const testFolder = active[ f ];
|
||||||
|
const { testFiles } = testFolder;
|
||||||
|
|
||||||
|
if ( ! testFiles || ! testFiles.length ) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const specFolder = testFolder.name.length ? `specs${path.sep}${testFolder.name}` : 'specs';
|
||||||
|
for ( let t = 0; t < testFiles.length; t++ ) {
|
||||||
|
const testFile = testFiles[ t ];
|
||||||
|
const testFilePath = `${specFolder}${path.sep}${testFile.name}.${testExtension}`;
|
||||||
|
|
||||||
|
confirmLocalDelete( testFilePath );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -71,6 +71,10 @@ case $1 in
|
||||||
./bin/wait-for-build.sh && ./bin/e2e-test-integration.js --dev --debug $2
|
./bin/wait-for-build.sh && ./bin/e2e-test-integration.js --dev --debug $2
|
||||||
TESTRESULT=$?
|
TESTRESULT=$?
|
||||||
;;
|
;;
|
||||||
|
'install' | \
|
||||||
|
'uninstall')
|
||||||
|
./bin/scaffold.js $@
|
||||||
|
;;
|
||||||
*)
|
*)
|
||||||
usage
|
usage
|
||||||
;;
|
;;
|
||||||
|
|
|
@ -3,15 +3,17 @@
|
||||||
*/
|
*/
|
||||||
const jestConfig = require( './jest.config' );
|
const jestConfig = require( './jest.config' );
|
||||||
const jestPuppeteerConfig = require( './jest-puppeteer.config' );
|
const jestPuppeteerConfig = require( './jest-puppeteer.config' );
|
||||||
|
const jestobjectConfig = require('./jest-object.config');
|
||||||
const {
|
const {
|
||||||
useE2EBabelConfig,
|
useE2EBabelConfig,
|
||||||
useE2EEsLintConfig,
|
useE2EEsLintConfig,
|
||||||
useE2EJestConfig,
|
useE2EJestConfig,
|
||||||
useE2EJestPuppeteerConfig
|
useE2EJestPuppeteerConfig,
|
||||||
} = require( './use-config' );
|
} = require( './use-config' );
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
jestConfig,
|
jestConfig,
|
||||||
|
...jestobjectConfig,
|
||||||
jestPuppeteerConfig,
|
jestPuppeteerConfig,
|
||||||
useE2EBabelConfig,
|
useE2EBabelConfig,
|
||||||
useE2EEsLintConfig,
|
useE2EEsLintConfig,
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
/**
|
||||||
|
* External Dependencies
|
||||||
|
*/
|
||||||
|
const { E2E_RETRY_TIMES } = process.env;
|
||||||
|
|
||||||
|
const setupJestRetries = ( retries = 0 ) => {
|
||||||
|
const retryTimes = E2E_RETRY_TIMES ? E2E_RETRY_TIMES : retries;
|
||||||
|
|
||||||
|
if ( retryTimes > 0 ) {
|
||||||
|
jest.retryTimes( retryTimes );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// If more methods are added to setupJestObject, it should be include in the readme
|
||||||
|
const setupJestObject = () => {
|
||||||
|
setupJestRetries();
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
setupJestObject,
|
||||||
|
setupJestRetries,
|
||||||
|
};
|
|
@ -55,6 +55,9 @@ const combinedConfig = {
|
||||||
transformIgnorePatterns: [
|
transformIgnorePatterns: [
|
||||||
'node_modules/(?!(woocommerce)/)',
|
'node_modules/(?!(woocommerce)/)',
|
||||||
],
|
],
|
||||||
|
|
||||||
|
testRunner: 'jest-circus/runner',
|
||||||
|
|
||||||
roots: [ testSpecs ],
|
roots: [ testSpecs ],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,7 @@ services:
|
||||||
WORDPRESS_DEBUG: 1
|
WORDPRESS_DEBUG: 1
|
||||||
volumes:
|
volumes:
|
||||||
- wordpress:/var/www/html
|
- wordpress:/var/www/html
|
||||||
|
- ./upload.ini:/usr/local/etc/php/conf.d/uploads.ini
|
||||||
- "../../../${WC_E2E_FOLDER}:${WC_E2E_FOLDER_MAPPING}"
|
- "../../../${WC_E2E_FOLDER}:${WC_E2E_FOLDER_MAPPING}"
|
||||||
|
|
||||||
wordpress-cli:
|
wordpress-cli:
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
echo "Initializing WooCommerce E2E"
|
||||||
|
|
||||||
|
# This is a workaround to accommodate different directory names.
|
||||||
|
wp plugin activate --all
|
||||||
|
wp plugin deactivate akismet
|
||||||
|
wp plugin deactivate hello
|
||||||
|
|
||||||
|
wp theme install twentynineteen --activate
|
||||||
|
wp user create customer customer@woocommercecoree2etestsuite.com \
|
||||||
|
--user_pass=password \
|
||||||
|
--role=subscriber \
|
||||||
|
--first_name='Jane' \
|
||||||
|
--last_name='Smith' \
|
||||||
|
--path=/var/www/html
|
||||||
|
|
||||||
|
# we cannot create API keys for the API, so we using basic auth, this plugin allows that.
|
||||||
|
wp plugin install https://github.com/WP-API/Basic-Auth/archive/master.zip --activate
|
||||||
|
|
||||||
|
# install the WP Mail Logging plugin to test emails
|
||||||
|
wp plugin install wp-mail-logging --activate
|
||||||
|
|
||||||
|
# initialize pretty permalinks
|
||||||
|
wp rewrite structure /%postname%/
|
|
@ -0,0 +1,8 @@
|
||||||
|
const path = require( 'path' );
|
||||||
|
const { useE2EJestConfig, getAppRoot } = require( '@woocommerce/e2e-environment' );
|
||||||
|
|
||||||
|
const jestConfig = useE2EJestConfig( {
|
||||||
|
roots: [ path.resolve( __dirname, '../specs' ) ],
|
||||||
|
} );
|
||||||
|
|
||||||
|
module.exports = jestConfig;
|
|
@ -0,0 +1,80 @@
|
||||||
|
import {
|
||||||
|
clearLocalStorage,
|
||||||
|
setBrowserViewport,
|
||||||
|
withRestApi,
|
||||||
|
WP_ADMIN_LOGIN
|
||||||
|
} from '@woocommerce/e2e-utils';
|
||||||
|
|
||||||
|
const config = require( 'config' );
|
||||||
|
const { HTTPClientFactory } = require( '@woocommerce/api' );
|
||||||
|
const { addConsoleSuppression, updateReadyPageStatus } = require( '@woocommerce/e2e-environment' );
|
||||||
|
const { DEFAULT_TIMEOUT_OVERRIDE } = process.env;
|
||||||
|
|
||||||
|
// @todo: remove this once https://github.com/woocommerce/woocommerce-admin/issues/6992 has been addressed
|
||||||
|
addConsoleSuppression( 'woocommerce_shared_settings', false );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uses the WordPress API to delete all existing posts
|
||||||
|
*/
|
||||||
|
async function trashExistingPosts() {
|
||||||
|
const apiUrl = config.get('url');
|
||||||
|
const wpPostsEndpoint = '/wp/v2/posts';
|
||||||
|
const adminUsername = config.get('users.admin.username');
|
||||||
|
const adminPassword = config.get('users.admin.password');
|
||||||
|
const client = HTTPClientFactory.build(apiUrl)
|
||||||
|
.withBasicAuth(adminUsername, adminPassword)
|
||||||
|
.create();
|
||||||
|
|
||||||
|
// List all existing posts
|
||||||
|
const response = await client.get(wpPostsEndpoint);
|
||||||
|
const posts = response.data;
|
||||||
|
|
||||||
|
// Delete each post
|
||||||
|
for (const post of posts) {
|
||||||
|
await client.delete(`${wpPostsEndpoint}/${post.id}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Before every test suite run, delete all content created by the test. This ensures
|
||||||
|
// other posts/comments/etc. aren't dirtying tests and tests don't depend on
|
||||||
|
// each other's side-effects.
|
||||||
|
beforeAll(async () => {
|
||||||
|
|
||||||
|
if ( DEFAULT_TIMEOUT_OVERRIDE ) {
|
||||||
|
page.setDefaultNavigationTimeout( DEFAULT_TIMEOUT_OVERRIDE );
|
||||||
|
page.setDefaultTimeout( DEFAULT_TIMEOUT_OVERRIDE );
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Update the ready page to prevent concurrent test runs
|
||||||
|
await updateReadyPageStatus('draft');
|
||||||
|
await trashExistingPosts();
|
||||||
|
await withRestApi.deleteAllProducts();
|
||||||
|
await withRestApi.deleteAllCoupons();
|
||||||
|
await withRestApi.deleteAllOrders();
|
||||||
|
} catch ( error ) {
|
||||||
|
// Prevent an error here causing tests to fail.
|
||||||
|
}
|
||||||
|
|
||||||
|
await page.goto(WP_ADMIN_LOGIN);
|
||||||
|
await clearLocalStorage();
|
||||||
|
await setBrowserViewport( {
|
||||||
|
width: 1280,
|
||||||
|
height: 800,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Clear browser cookies and cache using DevTools.
|
||||||
|
// This is to ensure that each test ends with no user logged in.
|
||||||
|
afterAll(async () => {
|
||||||
|
// Reset the ready page to published to allow future test runs
|
||||||
|
try {
|
||||||
|
await updateReadyPageStatus('publish');
|
||||||
|
} catch ( error ) {
|
||||||
|
// Prevent an error here causing tests to fail.
|
||||||
|
}
|
||||||
|
|
||||||
|
const client = await page.target().createCDPSession();
|
||||||
|
await client.send('Network.clearBrowserCookies');
|
||||||
|
await client.send('Network.clearBrowserCache');
|
||||||
|
});
|
|
@ -26,14 +26,16 @@
|
||||||
"@slack/web-api": "^6.1.0",
|
"@slack/web-api": "^6.1.0",
|
||||||
"@woocommerce/api": "^0.2.0",
|
"@woocommerce/api": "^0.2.0",
|
||||||
"@wordpress/e2e-test-utils": "^4.16.1",
|
"@wordpress/e2e-test-utils": "^4.16.1",
|
||||||
"@wordpress/jest-preset-default": "^6.4.0",
|
"@wordpress/jest-preset-default": "^7.1.3",
|
||||||
"app-root-path": "^3.0.0",
|
"app-root-path": "^3.0.0",
|
||||||
"commander": "4.1.1",
|
"commander": "4.1.1",
|
||||||
"jest": "^25.1.0",
|
"jest": "^25.1.0",
|
||||||
"jest-each": "25.5.0",
|
"jest-each": "25.5.0",
|
||||||
"jest-puppeteer": "^4.4.0",
|
"jest-puppeteer": "^4.4.0",
|
||||||
|
"node-stream-zip": "^1.13.6",
|
||||||
|
"readline-sync": "^1.4.10",
|
||||||
"request": "^2.88.2",
|
"request": "^2.88.2",
|
||||||
"node-stream-zip": "^1.13.6"
|
"sprintf-js": "^1.1.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/cli": "7.12.8",
|
"@babel/cli": "7.12.8",
|
||||||
|
@ -42,6 +44,7 @@
|
||||||
"@babel/preset-env": "7.12.7",
|
"@babel/preset-env": "7.12.7",
|
||||||
"@wordpress/eslint-plugin": "7.3.0",
|
"@wordpress/eslint-plugin": "7.3.0",
|
||||||
"eslint": "^8.1.0",
|
"eslint": "^8.1.0",
|
||||||
|
"jest-circus": "25.1.0",
|
||||||
"ndb": "^1.1.5",
|
"ndb": "^1.1.5",
|
||||||
"semver": "^7.3.2"
|
"semver": "^7.3.2"
|
||||||
},
|
},
|
||||||
|
@ -49,6 +52,7 @@
|
||||||
"access": "public"
|
"access": "public"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
"preinstall": "npx only-allow pnpm",
|
||||||
"clean": "rm -rf ./build ./build-module",
|
"clean": "rm -rf ./build ./build-module",
|
||||||
"compile": "node ./../bin/build.js",
|
"compile": "node ./../bin/build.js",
|
||||||
"build": "pnpm run clean && pnpm run compile",
|
"build": "pnpm run clean && pnpm run compile",
|
||||||
|
|
|
@ -3,6 +3,13 @@
|
||||||
"sourceRoot": "packages/js/e2e-environment/src",
|
"sourceRoot": "packages/js/e2e-environment/src",
|
||||||
"projectType": "library",
|
"projectType": "library",
|
||||||
"targets": {
|
"targets": {
|
||||||
|
"changelog": {
|
||||||
|
"executor": "./tools/executors/changelogger:changelog",
|
||||||
|
"options": {
|
||||||
|
"action": "add",
|
||||||
|
"cwd": "packages/js/e2e-environment"
|
||||||
|
}
|
||||||
|
},
|
||||||
"build": {
|
"build": {
|
||||||
"executor": "@nrwl/workspace:run-script",
|
"executor": "@nrwl/workspace:run-script",
|
||||||
"options": {
|
"options": {
|
||||||
|
|
|
@ -9,6 +9,7 @@ import {
|
||||||
setBrowserViewport,
|
setBrowserViewport,
|
||||||
} from '@wordpress/e2e-test-utils';
|
} from '@wordpress/e2e-test-utils';
|
||||||
import { consoleShouldSuppress, addConsoleSuppression } from '../../utils';
|
import { consoleShouldSuppress, addConsoleSuppression } from '../../utils';
|
||||||
|
import { setupJestRetries } from '../../config/jest-object.config';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Array of page event tuples of [ eventName, handler ].
|
* Array of page event tuples of [ eventName, handler ].
|
||||||
|
@ -175,6 +176,7 @@ beforeAll( async () => {
|
||||||
capturePageEventsForTearDown();
|
capturePageEventsForTearDown();
|
||||||
enablePageDialogAccept();
|
enablePageDialogAccept();
|
||||||
observeConsoleLogging();
|
observeConsoleLogging();
|
||||||
|
setupJestRetries();
|
||||||
} );
|
} );
|
||||||
|
|
||||||
afterEach( async () => {
|
afterEach( async () => {
|
||||||
|
|
|
@ -0,0 +1,104 @@
|
||||||
|
# WooCommerce End-to-End Test Packages
|
||||||
|
|
||||||
|
There are two limitations which significantly impact the architecture of E2E test packages:
|
||||||
|
|
||||||
|
- Referencing the `jest` functions `describe`, `it`, `beforeAll`, etc. throws a fatal error outside the `jest` environment.
|
||||||
|
- `jest` will not scan for tests in any path containing `node_mdules`.
|
||||||
|
|
||||||
|
## Creating a tests package
|
||||||
|
|
||||||
|
The way to create a tests package with the above limitations is
|
||||||
|
|
||||||
|
- **In the tests package**, wrap each test in a function
|
||||||
|
|
||||||
|
```js
|
||||||
|
/**
|
||||||
|
* Require the necessary jest functions to prevent the package build from referencing them
|
||||||
|
* `import` references imported functions during package build
|
||||||
|
*/
|
||||||
|
|
||||||
|
const { describe, it, beforeAll } = require( '@jest/globals' );
|
||||||
|
|
||||||
|
const testMyCriticalFlow = () => {
|
||||||
|
describe( 'My Critical Flow', () => {
|
||||||
|
beforeAll( async () => {
|
||||||
|
// Test setup
|
||||||
|
} );
|
||||||
|
it( 'can complete first step', async () => {
|
||||||
|
// Do stuff
|
||||||
|
expect( someValue ).toBeTruthy();
|
||||||
|
} );
|
||||||
|
} );
|
||||||
|
};
|
||||||
|
|
||||||
|
modules.exports = testMyFlow;
|
||||||
|
```
|
||||||
|
|
||||||
|
- **In the `tests/e2e/specs` folder**, create a test spec that calls the test function
|
||||||
|
|
||||||
|
```js
|
||||||
|
import { testMyCriticalFlow } from 'MyTestsPackage';
|
||||||
|
|
||||||
|
testMyCriticalFlow();
|
||||||
|
```
|
||||||
|
|
||||||
|
## Adding the scaffolds for the test installer
|
||||||
|
|
||||||
|
To work with the limitations outlined above, the test installer needs to access the test scaffolding information without accessing the package index. As a result, the `installFiles` is a required path in the steps below
|
||||||
|
|
||||||
|
- Create an `installFiles` folder in the root of the package
|
||||||
|
- Add an `index.js` to the folder which exports an object with some or all of three properties
|
||||||
|
```js
|
||||||
|
module.exports = {
|
||||||
|
defaultJson: 'installFiles/default-test-config.json',
|
||||||
|
initializeSh: 'installFiles/initialize.sh.default',
|
||||||
|
testSpecs: 'installFiles/scaffold-tests.json',
|
||||||
|
};
|
||||||
|
```
|
||||||
|
- The value of each of the properties should be a relative path from the package `index.js`. The test installer will remove `dist`, `build`, and `build-modules` from the end of the package index path.
|
||||||
|
- `defaultJson`: Path to a JSON file containing all `default.json` entries needed for the tests in the package.
|
||||||
|
- `initializeSh`: Path to a bash script containing the WP CLI commands needed to initialize the `e2e-environment` test container.
|
||||||
|
- `testSpecs`: Path to a JSON file containing a nested object
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"active": [
|
||||||
|
{
|
||||||
|
"name": "first-folder-name",
|
||||||
|
"description": "First tests",
|
||||||
|
"testFiles": [
|
||||||
|
{
|
||||||
|
"name": "test-name-a",
|
||||||
|
"functions": [
|
||||||
|
"testMyCriticalFlow"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "test-name-b",
|
||||||
|
"functions": [
|
||||||
|
"testSecondCriticalFlow",
|
||||||
|
"testThirdCriticalFlow"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "second-folder-name",
|
||||||
|
"description": "Second tests",
|
||||||
|
"testFiles": [
|
||||||
|
....
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The test installer uses the `testSpecs` nested object to create test specs. Using the example above, create `tests/e2e/specs/first-folder-name/test-name-b.test.js`:
|
||||||
|
|
||||||
|
```js
|
||||||
|
/* This file was auto-generated by the command `npx wc-e2e install your-package-name`. */
|
||||||
|
import { testSecondCriticalFlow, testThirdCriticalFlow } from 'your-package-name';
|
||||||
|
|
||||||
|
testSecondCriticalFlow();
|
||||||
|
testThirdCriticalFlow();
|
||||||
|
```
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
file_uploads = On
|
||||||
|
memory_limit = 500M
|
||||||
|
upload_max_filesize = 500M
|
||||||
|
post_max_size = 500M
|
||||||
|
max_execution_time = 600
|
|
@ -1,30 +1,27 @@
|
||||||
const path = require( 'path' );
|
const path = require( 'path' );
|
||||||
const getAppRoot = require( './app-root' );
|
|
||||||
const fs = require( 'fs' );
|
const fs = require( 'fs' );
|
||||||
const mkdirp = require( 'mkdirp' );
|
const mkdirp = require( 'mkdirp' );
|
||||||
const request = require( 'request' );
|
const request = require( 'request' );
|
||||||
const StreamZip = require( 'node-stream-zip' );
|
const StreamZip = require( 'node-stream-zip' );
|
||||||
|
const { resolveLocalE2ePath } = require( './test-config' );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Upload a plugin zip from a remote location, such as a GitHub URL or other hosted location.
|
* Upload a plugin zip from a remote location, such as a GitHub URL or other hosted location.
|
||||||
*
|
*
|
||||||
* @param {string} fileUrl The URL where the zip file is located.
|
* @param {string} fileUrl The URL where the zip file is located.
|
||||||
|
* @param {string} authorizationToken Authorization token used to authenticate with the GitHub API if required.
|
||||||
* @return {string} The path where the zip file is located.
|
* @return {string} The path where the zip file is located.
|
||||||
*/
|
*/
|
||||||
const getRemotePluginZip = async ( fileUrl ) => {
|
const getRemotePluginZip = async ( fileUrl, authorizationToken = '' ) => {
|
||||||
const appPath = getAppRoot();
|
const savePath = resolveLocalE2ePath( 'plugins' );
|
||||||
const savePath = path.resolve(
|
|
||||||
appPath,
|
|
||||||
'plugins/woocommerce/tests/e2e/plugins'
|
|
||||||
);
|
|
||||||
mkdirp.sync( savePath );
|
mkdirp.sync( savePath );
|
||||||
|
|
||||||
// Pull the filename from the end of the URL
|
// Pull the version from the end of the URL
|
||||||
const fileName = fileUrl.split( '/' ).pop();
|
const fileName = fileUrl.split( '/' ).pop();
|
||||||
let filePath = path.join( savePath, fileName );
|
let filePath = path.join( savePath, fileName );
|
||||||
|
|
||||||
// First, download the zip file
|
// First, download the zip file
|
||||||
await downloadZip( fileUrl, filePath );
|
await downloadZip( fileUrl, filePath, authorizationToken );
|
||||||
|
|
||||||
// Check for a nested zip and update the filepath
|
// Check for a nested zip and update the filepath
|
||||||
filePath = await checkNestedZip( filePath, savePath );
|
filePath = await checkNestedZip( filePath, savePath );
|
||||||
|
@ -35,24 +32,24 @@ const getRemotePluginZip = async ( fileUrl ) => {
|
||||||
/**
|
/**
|
||||||
* Get the latest release zip for a plugin from a GiHub repository.
|
* Get the latest release zip for a plugin from a GiHub repository.
|
||||||
*
|
*
|
||||||
* @param {string} owner The owner of the plugin repository.
|
* @param {string} repository The repository owner and name. For example: `woocommerce/woocommerce`.
|
||||||
* @param {string} repository The repository name.
|
* @param {string} authorizationToken Authorization token used to authenticate with the GitHub API if required.
|
||||||
* @param {boolean} getPrerelease Flag on whether to get a prelease or not.
|
* @param {boolean} getPrerelease Flag on whether to get a prelease or not.
|
||||||
* @param {number} perPage Limit of entries returned from the latest releases list, defaults to 3.
|
* @param {number} perPage Limit of entries returned from the latest releases list, defaults to 3.
|
||||||
* @return {Promise<string>}} Returns the URL for the release zip file.
|
* @return {Promise<string>}} Returns the URL for the release zip file.
|
||||||
*/
|
*/
|
||||||
const getLatestReleaseZipUrl = async (
|
const getLatestReleaseZipUrl = async (
|
||||||
owner,
|
|
||||||
repository,
|
repository,
|
||||||
|
authorizationToken = '',
|
||||||
getPrerelease = false,
|
getPrerelease = false,
|
||||||
perPage = 3
|
perPage = 3
|
||||||
) => {
|
) => {
|
||||||
let requesturl;
|
let requesturl;
|
||||||
|
|
||||||
if ( getPrerelease ) {
|
if ( getPrerelease ) {
|
||||||
requesturl = `https://api.github.com/repos/${ owner }/${ repository }/releases?per_page=${ perPage }`;
|
requesturl = `https://api.github.com/repos/${ repository }/releases?per_page=${ perPage }`;
|
||||||
} else {
|
} else {
|
||||||
requesturl = `https://api.github.com/repos/${ owner }/${ repository }/releases/latest`;
|
requesturl = `https://api.github.com/repos/${ repository }/releases/latest`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
|
@ -62,6 +59,11 @@ const getLatestReleaseZipUrl = async (
|
||||||
headers: { 'user-agent': 'node.js' },
|
headers: { 'user-agent': 'node.js' },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// If provided with a token, use it for authorization
|
||||||
|
if ( authorizationToken ) {
|
||||||
|
options.headers.Authorization = `token ${ authorizationToken }`;
|
||||||
|
}
|
||||||
|
|
||||||
// Wrap in a promise to make the request async
|
// Wrap in a promise to make the request async
|
||||||
return new Promise( function ( resolve, reject ) {
|
return new Promise( function ( resolve, reject ) {
|
||||||
request.get( options, function ( err, resp, body ) {
|
request.get( options, function ( err, resp, body ) {
|
||||||
|
@ -74,6 +76,12 @@ const getLatestReleaseZipUrl = async (
|
||||||
resolve( release.assets[ 0 ].browser_download_url );
|
resolve( release.assets[ 0 ].browser_download_url );
|
||||||
}
|
}
|
||||||
} );
|
} );
|
||||||
|
} else if ( authorizationToken ) {
|
||||||
|
// If it's a private repo, we need to download the archive this way
|
||||||
|
const tagName = body.tag_name;
|
||||||
|
resolve(
|
||||||
|
`https://github.com/${ repository }/archive/${ tagName }.zip`
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
resolve( body.assets[ 0 ].browser_download_url );
|
resolve( body.assets[ 0 ].browser_download_url );
|
||||||
}
|
}
|
||||||
|
@ -93,7 +101,7 @@ const checkNestedZip = async ( zipFilePath, savePath ) => {
|
||||||
const entries = await zip.entries();
|
const entries = await zip.entries();
|
||||||
|
|
||||||
for ( const entry of Object.values( entries ) ) {
|
for ( const entry of Object.values( entries ) ) {
|
||||||
if ( entry.name.match( /.zip/ ) ) {
|
if ( entry.name.match( /\.zip/ ) ) {
|
||||||
await zip.extract( null, savePath );
|
await zip.extract( null, savePath );
|
||||||
await zip.close();
|
await zip.close();
|
||||||
return path.join( savePath, entry.name );
|
return path.join( savePath, entry.name );
|
||||||
|
@ -108,15 +116,22 @@ const checkNestedZip = async ( zipFilePath, savePath ) => {
|
||||||
*
|
*
|
||||||
* @param {string} fileUrl The URL where the zip file is located.
|
* @param {string} fileUrl The URL where the zip file is located.
|
||||||
* @param {string} downloadPath The location where to download the zip to.
|
* @param {string} downloadPath The location where to download the zip to.
|
||||||
|
* @param {string} authorizationToken Authorization token used to authenticate with the GitHub API if required.
|
||||||
* @return {Promise<void>}
|
* @return {Promise<void>}
|
||||||
*/
|
*/
|
||||||
const downloadZip = async ( fileUrl, downloadPath ) => {
|
const downloadZip = async ( fileUrl, downloadPath, authorizationToken ) => {
|
||||||
const options = {
|
const options = {
|
||||||
url: fileUrl,
|
url: fileUrl,
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
encoding: null,
|
encoding: null,
|
||||||
|
headers: { 'user-agent': 'node.js' },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// If provided with a token, use it for authorization
|
||||||
|
if ( authorizationToken ) {
|
||||||
|
options.headers.Authorization = `token ${ authorizationToken }`;
|
||||||
|
}
|
||||||
|
|
||||||
// Wrap in a promise to make the request async
|
// Wrap in a promise to make the request async
|
||||||
return new Promise( function ( resolve, reject ) {
|
return new Promise( function ( resolve, reject ) {
|
||||||
request
|
request
|
||||||
|
@ -131,9 +146,27 @@ const downloadZip = async ( fileUrl, downloadPath ) => {
|
||||||
} );
|
} );
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete the downloaded plugin files.
|
||||||
|
*/
|
||||||
|
const deleteDownloadedPluginFiles = async () => {
|
||||||
|
const pluginSavePath = resolveLocalE2ePath( 'plugins' );
|
||||||
|
|
||||||
|
fs.readdir( pluginSavePath, ( err, files ) => {
|
||||||
|
if ( err ) throw err;
|
||||||
|
|
||||||
|
for ( const file of files ) {
|
||||||
|
fs.unlink( path.join( pluginSavePath, file ), ( error ) => {
|
||||||
|
if ( error ) throw error;
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
};
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
getRemotePluginZip,
|
getRemotePluginZip,
|
||||||
getLatestReleaseZipUrl,
|
getLatestReleaseZipUrl,
|
||||||
checkNestedZip,
|
checkNestedZip,
|
||||||
downloadZip,
|
downloadZip,
|
||||||
|
deleteDownloadedPluginFiles,
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,21 +1,24 @@
|
||||||
const getAppRoot = require( './app-root' );
|
const getAppRoot = require( './app-root' );
|
||||||
const { getAppName, getAppBase } = require( './app-name' );
|
const { getAppName, getAppBase } = require( './app-name' );
|
||||||
const { getTestConfig, getAdminConfig, resolveLocalE2ePath } = require( './test-config' );
|
const testConfig = require( './test-config' );
|
||||||
const { getRemotePluginZip, getLatestReleaseZipUrl } = require('./get-plugin-zip');
|
const {
|
||||||
|
getRemotePluginZip,
|
||||||
|
getLatestReleaseZipUrl,
|
||||||
|
deleteDownloadedPluginFiles,
|
||||||
|
} = require( './get-plugin-zip' );
|
||||||
const takeScreenshotFor = require( './take-screenshot' );
|
const takeScreenshotFor = require( './take-screenshot' );
|
||||||
const updateReadyPageStatus = require('./update-ready-page');
|
const updateReadyPageStatus = require( './update-ready-page' );
|
||||||
const consoleUtils = require( './filter-console' );
|
const consoleUtils = require( './filter-console' );
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
getAppBase,
|
getAppBase,
|
||||||
getAppRoot,
|
getAppRoot,
|
||||||
getAppName,
|
getAppName,
|
||||||
getTestConfig,
|
|
||||||
getAdminConfig,
|
|
||||||
resolveLocalE2ePath,
|
|
||||||
getRemotePluginZip,
|
getRemotePluginZip,
|
||||||
getLatestReleaseZipUrl,
|
getLatestReleaseZipUrl,
|
||||||
|
deleteDownloadedPluginFiles,
|
||||||
takeScreenshotFor,
|
takeScreenshotFor,
|
||||||
updateReadyPageStatus,
|
updateReadyPageStatus,
|
||||||
|
...testConfig,
|
||||||
...consoleUtils,
|
...consoleUtils,
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,124 @@
|
||||||
|
/**
|
||||||
|
* External dependencies.
|
||||||
|
*/
|
||||||
|
const fs = require( 'fs' );
|
||||||
|
const path = require( 'path' );
|
||||||
|
const readlineSync = require( 'readline-sync' );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal dependencies.
|
||||||
|
*/
|
||||||
|
const { resolveLocalE2ePath, resolvePackagePath } = require( './test-config' );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a path relative to the local `tests/e2e` folder.
|
||||||
|
* @param relativePath
|
||||||
|
* @return {string}
|
||||||
|
*/
|
||||||
|
const createLocalE2ePath = ( relativePath ) => {
|
||||||
|
let specFolderPath = '';
|
||||||
|
const folders = [ `..${path.sep}..${path.sep}tests`, `..${path.sep}e2e`, relativePath ];
|
||||||
|
folders.forEach( ( folder ) => {
|
||||||
|
specFolderPath = resolveLocalE2ePath( folder );
|
||||||
|
if ( ! fs.existsSync( specFolderPath ) ) {
|
||||||
|
console.log( `Creating folder ${specFolderPath}` );
|
||||||
|
fs.mkdirSync( specFolderPath );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
|
||||||
|
return specFolderPath;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prompt the console for confirmation.
|
||||||
|
*
|
||||||
|
* @param {string} prompt Prompt for the user.
|
||||||
|
* @param {string} choices valid responses.
|
||||||
|
* @return {string}
|
||||||
|
*/
|
||||||
|
const confirm = ( prompt, choices ) => {
|
||||||
|
const answer = readlineSync.keyIn( prompt, choices );
|
||||||
|
return answer;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {string} localE2ePath Destination path
|
||||||
|
* @param {string} packageE2ePath Source path
|
||||||
|
* @param {string} packageName Source package. Default @woocommerce/e2e-environment package.
|
||||||
|
* @return {boolean}
|
||||||
|
*/
|
||||||
|
const confirmLocalCopy = ( localE2ePath, packageE2ePath, packageName = '' ) => {
|
||||||
|
const localPath = resolveLocalE2ePath( localE2ePath );
|
||||||
|
const packagePath = resolvePackagePath( packageE2ePath, packageName );
|
||||||
|
const confirmPrompt = `${localE2ePath} already exists. Overwrite? [Y]es/[n]o: `;
|
||||||
|
|
||||||
|
let overwriteFiles;
|
||||||
|
if ( fs.existsSync( localPath ) ) {
|
||||||
|
overwriteFiles = confirm( confirmPrompt, 'ny' );
|
||||||
|
overwriteFiles = overwriteFiles.toLowerCase();
|
||||||
|
} else {
|
||||||
|
overwriteFiles = 'y';
|
||||||
|
}
|
||||||
|
if ( overwriteFiles == 'y' ) {
|
||||||
|
fs.copyFileSync( packagePath, localPath );
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prompt for confirmation before deleting a local E2E file.
|
||||||
|
*
|
||||||
|
* @param {string} localE2ePath Relative path to local E2E file.
|
||||||
|
*/
|
||||||
|
const confirmLocalDelete = ( localE2ePath ) => {
|
||||||
|
const localPath = resolveLocalE2ePath( localE2ePath );
|
||||||
|
if ( ! fs.existsSync( localPath ) ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const confirmPrompt = `${localE2ePath} exists. Delete? [y]es/[n]o: `;
|
||||||
|
const deleteFile = confirm( confirmPrompt, 'ny' );
|
||||||
|
if ( deleteFile == 'y' ) {
|
||||||
|
fs.unlinkSync( localPath );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the install data for a tests package.
|
||||||
|
*
|
||||||
|
* @param {string} packageName npm package name
|
||||||
|
* @return {string}
|
||||||
|
*/
|
||||||
|
const getPackageData = ( packageName ) => {
|
||||||
|
const packageSlug = packageName.replace( '@', '' ).replace( /\//g, '.' );
|
||||||
|
const installFiles = require( `${packageName}${path.sep}installFiles` );
|
||||||
|
|
||||||
|
return { packageSlug, ...installFiles };
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Install test runner and test container defaults
|
||||||
|
*/
|
||||||
|
const installDefaults = () => {
|
||||||
|
createLocalE2ePath( 'docker' );
|
||||||
|
console.log( 'Writing tests/e2e/docker/initialize.sh' );
|
||||||
|
confirmLocalCopy( `docker${path.sep}initialize.sh`, `installFiles${path.sep}initialize.sh` );
|
||||||
|
|
||||||
|
createLocalE2ePath( 'config' );
|
||||||
|
console.log( 'Writing tests/e2e/config/jest.config.js' );
|
||||||
|
confirmLocalCopy( `config${path.sep}jest.config.js`, `installFiles${path.sep}jest.config.js` );
|
||||||
|
console.log( 'Writing tests/e2e/config/jest.setup.js' );
|
||||||
|
confirmLocalCopy( `config${path.sep}jest.setup.js`, `installFiles${path.sep}jest.setup.js` );
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
createLocalE2ePath,
|
||||||
|
confirm,
|
||||||
|
confirmLocalCopy,
|
||||||
|
confirmLocalDelete,
|
||||||
|
getPackageData,
|
||||||
|
installDefaults,
|
||||||
|
};
|
|
@ -19,15 +19,84 @@ const resolveLocalE2ePath = ( filename = '' ) => {
|
||||||
);
|
);
|
||||||
|
|
||||||
return resolvedPath;
|
return resolvedPath;
|
||||||
}
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve a package name installable by npm install.
|
||||||
|
*
|
||||||
|
* @param {string} packageName Name of the installed package.
|
||||||
|
* @param {boolean} allowRecurse Allow a recursive call. Default true.
|
||||||
|
* @return {object}
|
||||||
|
*/
|
||||||
|
const resolvePackage = ( packageName, allowRecurse = true ) => {
|
||||||
|
const resolvedPackage = {};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const resolvedPath = path.dirname( require.resolve( packageName ) );
|
||||||
|
const buildPaths = [ 'dist', 'build', 'build-modules' ];
|
||||||
|
|
||||||
|
// Remove build paths from the resolved path.
|
||||||
|
let resolvedParts = resolvedPath.split( path.sep );
|
||||||
|
for ( let rp = resolvedParts.length - 1; rp >= 0; rp-- ) {
|
||||||
|
if ( buildPaths.includes( resolvedParts[ rp ] ) ) {
|
||||||
|
resolvedParts = resolvedParts.slice( 0, -1 );
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
resolvedPackage.path = resolvedParts.join( path.sep );
|
||||||
|
resolvedPackage.name = packageName;
|
||||||
|
} catch ( e ) {
|
||||||
|
// Package name installed is not the package name.
|
||||||
|
resolvedPackage.path = '';
|
||||||
|
resolvedPackage.name = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempt to find the package through the project package lock file.
|
||||||
|
if ( ! resolvedPackage.path.length && allowRecurse ) {
|
||||||
|
const packageLockPath = path.resolve( appPath, 'package-lock.json' );
|
||||||
|
const packageLockContent = fs.readFileSync( packageLockPath );
|
||||||
|
const { dependencies } = JSON.parse( packageLockContent );
|
||||||
|
|
||||||
|
for ( const [ key, value ] of Object.entries( dependencies ) ) {
|
||||||
|
if ( value.version.indexOf( packageName ) == 0 ) {
|
||||||
|
resolvedPackage = resolvePackage( key, false );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return resolvedPackage;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve a file in a package.
|
||||||
|
*
|
||||||
|
* @param {string} filename Filename to append to the path.
|
||||||
|
* @param {string} packageName Name of the installed package. Default @woocommerce/e2e-environment.
|
||||||
|
* @return {string}
|
||||||
|
*/
|
||||||
|
const resolvePackagePath = ( filename, packageName = '' ) => {
|
||||||
|
let packagePath;
|
||||||
|
if ( ! packageName.length ) {
|
||||||
|
packagePath = path.resolve( __dirname, '../' );
|
||||||
|
} else {
|
||||||
|
const pkg = resolvePackage( packageName );
|
||||||
|
packagePath = pkg.path;
|
||||||
|
}
|
||||||
|
|
||||||
|
const resolvedPath = path.resolve(
|
||||||
|
packagePath,
|
||||||
|
filename.indexOf( '/' ) == 0 ? filename.slice( 1 ) : filename
|
||||||
|
);
|
||||||
|
|
||||||
|
return resolvedPath;
|
||||||
|
};
|
||||||
|
|
||||||
// Copy local test configuration file if it exists.
|
// Copy local test configuration file if it exists.
|
||||||
const localTestConfigFile = resolveLocalE2ePath( 'config/default.json' );
|
const localTestConfigFile = resolveLocalE2ePath( 'config/default.json' );
|
||||||
const defaultConfigFile = path.resolve(
|
const defaultConfigFile = resolvePackagePath( 'config/default/default.json' );
|
||||||
__dirname,
|
const testConfigFile = resolvePackagePath( 'config/default.json' );
|
||||||
'../config/default/default.json'
|
|
||||||
);
|
|
||||||
const testConfigFile = path.resolve( __dirname, '../config/default.json' );
|
|
||||||
|
|
||||||
if ( fs.existsSync( localTestConfigFile ) ) {
|
if ( fs.existsSync( localTestConfigFile ) ) {
|
||||||
fs.copyFileSync( localTestConfigFile, testConfigFile );
|
fs.copyFileSync( localTestConfigFile, testConfigFile );
|
||||||
|
@ -94,4 +163,6 @@ module.exports = {
|
||||||
getTestConfig,
|
getTestConfig,
|
||||||
getAdminConfig,
|
getAdminConfig,
|
||||||
resolveLocalE2ePath,
|
resolveLocalE2ePath,
|
||||||
|
resolvePackage,
|
||||||
|
resolvePackagePath,
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
# Unreleased
|
# Unreleased
|
||||||
|
|
||||||
|
## Fixed
|
||||||
|
|
||||||
|
- Identified the default product category using `slug == 'uncategorized'` in `deleteAllProductCategories`
|
||||||
|
|
||||||
## Changes
|
## Changes
|
||||||
|
|
||||||
- Removed `page.waitForNavigation()` from `shopper.logout()`
|
- Removed `page.waitForNavigation()` from `shopper.logout()`
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
"access": "public"
|
"access": "public"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
"preinstall": "npx only-allow pnpm",
|
||||||
"clean": "rm -rf ./build ./build-module",
|
"clean": "rm -rf ./build ./build-module",
|
||||||
"compile": "node ./../bin/build.js",
|
"compile": "node ./../bin/build.js",
|
||||||
"build": "pnpm run clean && pnpm run compile",
|
"build": "pnpm run clean && pnpm run compile",
|
||||||
|
|
|
@ -3,6 +3,13 @@
|
||||||
"sourceRoot": "packages/js/e2e-utils/src",
|
"sourceRoot": "packages/js/e2e-utils/src",
|
||||||
"projectType": "library",
|
"projectType": "library",
|
||||||
"targets": {
|
"targets": {
|
||||||
|
"changelog": {
|
||||||
|
"executor": "./tools/executors/changelogger:changelog",
|
||||||
|
"options": {
|
||||||
|
"action": "add",
|
||||||
|
"cwd": "packages/js/e2e-utils"
|
||||||
|
}
|
||||||
|
},
|
||||||
"build": {
|
"build": {
|
||||||
"executor": "@nrwl/workspace:run-script",
|
"executor": "@nrwl/workspace:run-script",
|
||||||
"options": {
|
"options": {
|
||||||
|
|
|
@ -78,7 +78,7 @@ export const withRestApi = {
|
||||||
};
|
};
|
||||||
|
|
||||||
const response = await client.put( onboardingProfileEndpoint, onboardingReset );
|
const response = await client.put( onboardingProfileEndpoint, onboardingReset );
|
||||||
expect( response.status ).toEqual( 200 );
|
expect( response.statusCode ).toEqual( 200 );
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Use api package to delete coupons.
|
* Use api package to delete coupons.
|
||||||
|
@ -138,7 +138,7 @@ export const withRestApi = {
|
||||||
if ( productCategories.data && productCategories.data.length ) {
|
if ( productCategories.data && productCategories.data.length ) {
|
||||||
for ( let c = 0; c < productCategories.data.length; c++ ) {
|
for ( let c = 0; c < productCategories.data.length; c++ ) {
|
||||||
// The default `uncategorized` category can't be deleted
|
// The default `uncategorized` category can't be deleted
|
||||||
if ( productCategories.data[c].id == 0 ) {
|
if ( productCategories.data[c].slug == 'uncategorized' ) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const response = await client.delete( productCategoriesPath + `/${productCategories.data[c].id}?force=true` );
|
const response = await client.delete( productCategoriesPath + `/${productCategories.data[c].id}?force=true` );
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
/** @format */
|
/** @format */
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
root: true,
|
|
||||||
env: {
|
env: {
|
||||||
browser: true,
|
browser: true,
|
||||||
es6: true,
|
es6: true,
|
||||||
|
|
|
@ -1,12 +1,3 @@
|
||||||
# Editors
|
|
||||||
/nbproject/private/
|
|
||||||
|
|
||||||
# Grunt
|
|
||||||
none
|
|
||||||
|
|
||||||
# Sass
|
|
||||||
.sass-cache/
|
|
||||||
|
|
||||||
# All CSS
|
# All CSS
|
||||||
/assets/css/**
|
/assets/css/**
|
||||||
/assets/css/*.css
|
/assets/css/*.css
|
||||||
|
@ -23,7 +14,6 @@ tests/cli/composer.json
|
||||||
tests/cli/vendor
|
tests/cli/vendor
|
||||||
|
|
||||||
# Unit tests
|
# Unit tests
|
||||||
/tmp
|
|
||||||
/tests/bin/tmp
|
/tests/bin/tmp
|
||||||
/tests/e2e/config/local-*.json
|
/tests/e2e/config/local-*.json
|
||||||
/tests/e2e/config/local.json
|
/tests/e2e/config/local.json
|
||||||
|
@ -32,22 +22,6 @@ tests/cli/vendor
|
||||||
/tests/e2e/screenshots
|
/tests/e2e/screenshots
|
||||||
/tests/e2e/plugins
|
/tests/e2e/plugins
|
||||||
|
|
||||||
# Logs
|
|
||||||
/logs
|
|
||||||
|
|
||||||
# TypeScript files
|
|
||||||
tsconfig.tsbuildinfo
|
|
||||||
|
|
||||||
# Composer
|
|
||||||
/vendor/
|
|
||||||
/bin/composer/**/vendor/
|
|
||||||
/lib/vendor/
|
|
||||||
contributors.md
|
|
||||||
contributors.html
|
|
||||||
|
|
||||||
# Yarn
|
|
||||||
yarn.lock
|
|
||||||
|
|
||||||
# Packages
|
# Packages
|
||||||
/packages/*
|
/packages/*
|
||||||
!/packages/README.md
|
!/packages/README.md
|
||||||
|
|
|
@ -21,8 +21,8 @@
|
||||||
"pelago/emogrifier": "3.1.0",
|
"pelago/emogrifier": "3.1.0",
|
||||||
"psr/container": "1.0.0",
|
"psr/container": "1.0.0",
|
||||||
"woocommerce/action-scheduler": "3.4.0",
|
"woocommerce/action-scheduler": "3.4.0",
|
||||||
"woocommerce/woocommerce-admin": "2.9.1",
|
"woocommerce/woocommerce-admin": "3.0.0-rc.1",
|
||||||
"woocommerce/woocommerce-blocks": "6.3.3"
|
"woocommerce/woocommerce-blocks": "6.5.0"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"bamarni/composer-bin-plugin": "^1.4",
|
"bamarni/composer-bin-plugin": "^1.4",
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "20885acd22c0a58cff8852e7cf4ebf20",
|
"content-hash": "97d29d724f0342a99e7bf3f9d1d3c262",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "automattic/jetpack-autoloader",
|
"name": "automattic/jetpack-autoloader",
|
||||||
|
@ -543,16 +543,16 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "woocommerce/woocommerce-admin",
|
"name": "woocommerce/woocommerce-admin",
|
||||||
"version": "2.9.1",
|
"version": "3.0.0-rc.1",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/woocommerce/woocommerce-admin.git",
|
"url": "https://github.com/woocommerce/woocommerce-admin.git",
|
||||||
"reference": "fdffbfef084c65a3e2141f0aff41cef3bad27553"
|
"reference": "7c0cdd01ae98be058d684dd19023b0f40094cb63"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/woocommerce/woocommerce-admin/zipball/fdffbfef084c65a3e2141f0aff41cef3bad27553",
|
"url": "https://api.github.com/repos/woocommerce/woocommerce-admin/zipball/7c0cdd01ae98be058d684dd19023b0f40094cb63",
|
||||||
"reference": "fdffbfef084c65a3e2141f0aff41cef3bad27553",
|
"reference": "7c0cdd01ae98be058d684dd19023b0f40094cb63",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
@ -608,22 +608,22 @@
|
||||||
"homepage": "https://github.com/woocommerce/woocommerce-admin",
|
"homepage": "https://github.com/woocommerce/woocommerce-admin",
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/woocommerce/woocommerce-admin/issues",
|
"issues": "https://github.com/woocommerce/woocommerce-admin/issues",
|
||||||
"source": "https://github.com/woocommerce/woocommerce-admin/tree/v2.9.1"
|
"source": "https://github.com/woocommerce/woocommerce-admin/tree/v3.0.0-rc.1"
|
||||||
},
|
},
|
||||||
"time": "2021-12-08T02:59:25+00:00"
|
"time": "2021-12-14T23:55:42+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "woocommerce/woocommerce-blocks",
|
"name": "woocommerce/woocommerce-blocks",
|
||||||
"version": "v6.3.3",
|
"version": "v6.5.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/woocommerce/woocommerce-gutenberg-products-block.git",
|
"url": "https://github.com/woocommerce/woocommerce-gutenberg-products-block.git",
|
||||||
"reference": "38975ad6de9c6a556059c4de6e9ffc586ab82245"
|
"reference": "655a9c1de46262304cc8ac187ca8fcc0abf23b6d"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/woocommerce/woocommerce-gutenberg-products-block/zipball/38975ad6de9c6a556059c4de6e9ffc586ab82245",
|
"url": "https://api.github.com/repos/woocommerce/woocommerce-gutenberg-products-block/zipball/655a9c1de46262304cc8ac187ca8fcc0abf23b6d",
|
||||||
"reference": "38975ad6de9c6a556059c4de6e9ffc586ab82245",
|
"reference": "655a9c1de46262304cc8ac187ca8fcc0abf23b6d",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
@ -662,9 +662,9 @@
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/woocommerce/woocommerce-gutenberg-products-block/issues",
|
"issues": "https://github.com/woocommerce/woocommerce-gutenberg-products-block/issues",
|
||||||
"source": "https://github.com/woocommerce/woocommerce-gutenberg-products-block/tree/v6.3.3"
|
"source": "https://github.com/woocommerce/woocommerce-gutenberg-products-block/tree/v6.5.0"
|
||||||
},
|
},
|
||||||
"time": "2021-11-25T09:47:27+00:00"
|
"time": "2021-12-07T11:28:05+00:00"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"packages-dev": [
|
"packages-dev": [
|
||||||
|
@ -2926,5 +2926,5 @@
|
||||||
"platform-overrides": {
|
"platform-overrides": {
|
||||||
"php": "7.0.33"
|
"php": "7.0.33"
|
||||||
},
|
},
|
||||||
"plugin-api-version": "2.0.0"
|
"plugin-api-version": "2.1.0"
|
||||||
}
|
}
|
||||||
|
|
|
@ -514,7 +514,7 @@ $untested_plugins = $plugin_updates->get_untested_plugins( WC()->version, Cons
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td data-export-label="WC Database Version"><?php esc_html_e( 'WooCommerce database version', 'woocommerce' ); ?>:</td>
|
<td data-export-label="WC Database Version"><?php esc_html_e( 'WooCommerce database version', 'woocommerce' ); ?>:</td>
|
||||||
<td class="help"><?php echo wc_help_tip( esc_html__( 'The database version for WooCommerce. Note that it may not match WooCommerce core version and that is normal.', 'woocommerce' ) ); /* phpcs:ignore WordPress.XSS.EscapeOutput.OutputNotEscaped */ ?></td>
|
<td class="help"><?php echo wc_help_tip( esc_html__( 'The database version for WooCommerce. This should be the same as your WooCommerce version.', 'woocommerce' ) ); /* phpcs:ignore WordPress.XSS.EscapeOutput.OutputNotEscaped */ ?></td>
|
||||||
<td><?php echo esc_html( $database['wc_database_version'] ); ?></td>
|
<td><?php echo esc_html( $database['wc_database_version'] ); ?></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
|
|
@ -150,9 +150,8 @@ class WC_Geolocation {
|
||||||
|
|
||||||
if ( empty( $ip_address ) ) {
|
if ( empty( $ip_address ) ) {
|
||||||
$ip_address = self::get_ip_address();
|
$ip_address = self::get_ip_address();
|
||||||
}
|
|
||||||
|
|
||||||
$country_code = self::get_country_code_from_headers();
|
$country_code = self::get_country_code_from_headers();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get geolocation filter.
|
* Get geolocation filter.
|
||||||
|
|
|
@ -180,6 +180,7 @@ class WC_Install {
|
||||||
add_action( 'admin_init', array( __CLASS__, 'wc_admin_db_update_notice' ) );
|
add_action( 'admin_init', array( __CLASS__, 'wc_admin_db_update_notice' ) );
|
||||||
add_action( 'admin_init', array( __CLASS__, 'add_admin_note_after_page_created' ) );
|
add_action( 'admin_init', array( __CLASS__, 'add_admin_note_after_page_created' ) );
|
||||||
add_action( 'woocommerce_run_update_callback', array( __CLASS__, 'run_update_callback' ) );
|
add_action( 'woocommerce_run_update_callback', array( __CLASS__, 'run_update_callback' ) );
|
||||||
|
add_action( 'woocommerce_update_db_to_current_version', array( __CLASS__, 'update_db_version' ) );
|
||||||
add_action( 'admin_init', array( __CLASS__, 'install_actions' ) );
|
add_action( 'admin_init', array( __CLASS__, 'install_actions' ) );
|
||||||
add_action( 'woocommerce_page_created', array( __CLASS__, 'page_created' ), 10, 2 );
|
add_action( 'woocommerce_page_created', array( __CLASS__, 'page_created' ), 10, 2 );
|
||||||
add_filter( 'plugin_action_links_' . WC_PLUGIN_BASENAME, array( __CLASS__, 'plugin_action_links' ) );
|
add_filter( 'plugin_action_links_' . WC_PLUGIN_BASENAME, array( __CLASS__, 'plugin_action_links' ) );
|
||||||
|
@ -487,6 +488,20 @@ class WC_Install {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// After the callbacks finish, update the db version to the current WC version.
|
||||||
|
$current_wc_version = WC()->version;
|
||||||
|
if ( version_compare( $current_db_version, $current_wc_version, '<' ) &&
|
||||||
|
! WC()->queue()->get_next( 'woocommerce_update_db_to_current_version' ) ) {
|
||||||
|
WC()->queue()->schedule_single(
|
||||||
|
time() + $loop,
|
||||||
|
'woocommerce_update_db_to_current_version',
|
||||||
|
array(
|
||||||
|
'version' => $current_wc_version,
|
||||||
|
),
|
||||||
|
'woocommerce-db-updates'
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -782,7 +782,7 @@ class WC_Email extends WC_Settings_API {
|
||||||
protected function save_template( $template_code, $template_path ) {
|
protected function save_template( $template_code, $template_path ) {
|
||||||
if ( current_user_can( 'edit_themes' ) && ! empty( $template_code ) && ! empty( $template_path ) ) {
|
if ( current_user_can( 'edit_themes' ) && ! empty( $template_code ) && ! empty( $template_path ) ) {
|
||||||
$saved = false;
|
$saved = false;
|
||||||
$file = get_stylesheet_directory() . '/' . WC()->template_path() . $template_path;
|
$file = $this->get_theme_template_file( $template_path );
|
||||||
$code = wp_unslash( $template_code );
|
$code = wp_unslash( $template_code );
|
||||||
|
|
||||||
if ( is_writeable( $file ) ) { // phpcs:ignore WordPress.VIP.FileSystemWritesDisallow.file_ops_is_writeable
|
if ( is_writeable( $file ) ) { // phpcs:ignore WordPress.VIP.FileSystemWritesDisallow.file_ops_is_writeable
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
{
|
{
|
||||||
"extends": "stylelint-config-wordpress",
|
"extends": "@wordpress/stylelint-config",
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
module.exports = function ( grunt ) {
|
module.exports = function ( grunt ) {
|
||||||
'use strict';
|
'use strict';
|
||||||
var sass = require( 'node-sass' );
|
var sass = require( 'sass' );
|
||||||
|
|
||||||
grunt.initConfig( {
|
grunt.initConfig( {
|
||||||
// Setting folder templates.
|
// Setting folder templates.
|
||||||
|
|
|
@ -453,12 +453,13 @@ p.demo_store,
|
||||||
td,
|
td,
|
||||||
th {
|
th {
|
||||||
border: 0;
|
border: 0;
|
||||||
vertical-align: top;
|
|
||||||
line-height: 2em;
|
line-height: 2em;
|
||||||
|
vertical-align: top;
|
||||||
}
|
}
|
||||||
|
|
||||||
label {
|
label {
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
|
text-align: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
select {
|
select {
|
||||||
|
|
|
@ -807,6 +807,10 @@ jQuery( function( $ ) {
|
||||||
default :
|
default :
|
||||||
$( 'select.variation_actions' ).trigger( do_variation_action );
|
$( 'select.variation_actions' ).trigger( do_variation_action );
|
||||||
data = $( 'select.variation_actions' ).triggerHandler( do_variation_action + '_ajax_data', data );
|
data = $( 'select.variation_actions' ).triggerHandler( do_variation_action + '_ajax_data', data );
|
||||||
|
|
||||||
|
if ( null === data ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -242,8 +242,15 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
item = $( '<a></a>' ).attr( 'href', '#' ).text( j );
|
item = $( '<a></a>' ).attr( 'href', '#' ).text( j );
|
||||||
if ( slider.vars.controlNav === "thumbnails" ) {
|
if (slider.vars.controlNav === "thumbnails") {
|
||||||
item = $( '<img/>' ).attr( 'src', slide.attr( 'data-thumb' ) );
|
item = $('<img/>', {
|
||||||
|
load: function (el) {
|
||||||
|
el.currentTarget.width = el.currentTarget.naturalWidth;
|
||||||
|
el.currentTarget.height = el.currentTarget.naturalHeight;
|
||||||
|
},
|
||||||
|
src: slide.attr('data-thumb'),
|
||||||
|
alt: slide.attr('alt')
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( '' !== slide.attr( 'data-thumb-alt' ) ) {
|
if ( '' !== slide.attr( 'data-thumb-alt' ) ) {
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
"private": true,
|
"private": true,
|
||||||
"main": "Gruntfile.js",
|
"main": "Gruntfile.js",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@wordpress/stylelint-config": "19.1.0",
|
||||||
"autoprefixer": "9.8.6",
|
"autoprefixer": "9.8.6",
|
||||||
"browserslist": "4.14.5",
|
"browserslist": "4.14.5",
|
||||||
"caniuse-lite": "1.0.30001146",
|
"caniuse-lite": "1.0.30001146",
|
||||||
|
@ -24,8 +25,7 @@
|
||||||
"grunt-sass": "3.1.0",
|
"grunt-sass": "3.1.0",
|
||||||
"grunt-stylelint": "0.16.0",
|
"grunt-stylelint": "0.16.0",
|
||||||
"gruntify-eslint": "5.0.0",
|
"gruntify-eslint": "5.0.0",
|
||||||
"node-sass": "6.0.1",
|
"sass": "^1.45.0",
|
||||||
"stylelint": "13.8.0",
|
"stylelint": "13.8.0"
|
||||||
"stylelint-config-wordpress": "17.0.0"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,7 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/cli": "7.12.8",
|
"@babel/cli": "7.12.8",
|
||||||
"@babel/core": "7.12.9",
|
"@babel/core": "7.12.9",
|
||||||
"@babel/polyfill": "7.12.1",
|
"babel-eslint": "10.1.0",
|
||||||
"@babel/preset-env": "7.12.7",
|
"@babel/preset-env": "7.12.7",
|
||||||
"@babel/register": "7.12.1",
|
"@babel/register": "7.12.1",
|
||||||
"@typescript-eslint/eslint-plugin": "3.10.1",
|
"@typescript-eslint/eslint-plugin": "3.10.1",
|
||||||
|
@ -48,8 +48,8 @@
|
||||||
"@wordpress/babel-plugin-import-jsx-pragma": "1.1.3",
|
"@wordpress/babel-plugin-import-jsx-pragma": "1.1.3",
|
||||||
"@wordpress/babel-preset-default": "3.0.2",
|
"@wordpress/babel-preset-default": "3.0.2",
|
||||||
"@wordpress/eslint-plugin": "7.3.0",
|
"@wordpress/eslint-plugin": "7.3.0",
|
||||||
|
"@wordpress/stylelint-config": "19.1.0",
|
||||||
"autoprefixer": "9.8.6",
|
"autoprefixer": "9.8.6",
|
||||||
"babel-eslint": "10.1.0",
|
|
||||||
"chai": "4.2.0",
|
"chai": "4.2.0",
|
||||||
"chai-as-promised": "7.1.1",
|
"chai-as-promised": "7.1.1",
|
||||||
"config": "3.3.3",
|
"config": "3.3.3",
|
||||||
|
@ -64,18 +64,16 @@
|
||||||
"jest": "^25.1.0",
|
"jest": "^25.1.0",
|
||||||
"lint-staged": "9.5.0",
|
"lint-staged": "9.5.0",
|
||||||
"mocha": "7.2.0",
|
"mocha": "7.2.0",
|
||||||
"node-sass": "6.0.1",
|
|
||||||
"prettier": "npm:wp-prettier@2.0.5",
|
"prettier": "npm:wp-prettier@2.0.5",
|
||||||
"stylelint": "^13.8.0",
|
"stylelint": "^13.8.0",
|
||||||
"stylelint-config-wordpress": "17.0.0",
|
|
||||||
"typescript": "3.9.7",
|
"typescript": "3.9.7",
|
||||||
"webpack": "4.44.2",
|
"webpack": "4.44.2",
|
||||||
"webpack-cli": "3.3.12",
|
"webpack-cli": "3.3.12",
|
||||||
"wp-textdomain": "1.0.1"
|
"wp-textdomain": "1.0.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^12.20.1",
|
"node": "^16.13.1",
|
||||||
"npm": "^6.14.6"
|
"npm": "^8.1.2"
|
||||||
},
|
},
|
||||||
"husky": {
|
"husky": {
|
||||||
"hooks": {
|
"hooks": {
|
||||||
|
|
|
@ -4,6 +4,13 @@
|
||||||
"projectType": "application",
|
"projectType": "application",
|
||||||
"implicitDependencies": [ "woocommerce-legacy-assets" ],
|
"implicitDependencies": [ "woocommerce-legacy-assets" ],
|
||||||
"targets": {
|
"targets": {
|
||||||
|
"changelog": {
|
||||||
|
"executor": "./tools/executors/changelogger:changelog",
|
||||||
|
"options": {
|
||||||
|
"action": "add",
|
||||||
|
"cwd": "plugins/woocommerce"
|
||||||
|
}
|
||||||
|
},
|
||||||
"composer-install": {
|
"composer-install": {
|
||||||
"executor": "@nrwl/workspace:run-commands",
|
"executor": "@nrwl/workspace:run-commands",
|
||||||
"options": {
|
"options": {
|
||||||
|
|
|
@ -4,7 +4,7 @@ Tags: e-commerce, store, sales, sell, woo, shop, cart, checkout, downloadable, d
|
||||||
Requires at least: 5.6
|
Requires at least: 5.6
|
||||||
Tested up to: 5.8
|
Tested up to: 5.8
|
||||||
Requires PHP: 7.0
|
Requires PHP: 7.0
|
||||||
Stable tag: 5.9.0
|
Stable tag: 6.0.0
|
||||||
License: GPLv3
|
License: GPLv3
|
||||||
License URI: https://www.gnu.org/licenses/gpl-3.0.html
|
License URI: https://www.gnu.org/licenses/gpl-3.0.html
|
||||||
|
|
||||||
|
@ -160,61 +160,6 @@ WooCommerce comes with some sample data you can use to see how products look; im
|
||||||
|
|
||||||
== Changelog ==
|
== Changelog ==
|
||||||
|
|
||||||
= 5.9.0 2021-11-09 =
|
= 6.1.0 2021-XX-XX =
|
||||||
|
|
||||||
**WooCommerce**
|
|
||||||
|
|
||||||
* Fix - Bug in the handling of remote file names for downloadable files.
|
|
||||||
* Fix - Remove the absolute path to the currency-info.php from within locale-info.php. #31036
|
|
||||||
* Fix - wc_get_price_excluding_tax when an order with no customer is passed. #31015
|
|
||||||
* Fix - Rename transient used to cache data for Featured page of In-App Marketplace. #31002
|
|
||||||
* Fix - Variable product price caching bug with VAT exemption. #30889
|
|
||||||
* Fix - Allow to pass null as the email for billing addresses in REST API. #30850
|
|
||||||
* Fix - Ensure woocommerce_cancel_unpaid_orders event is always re-scheduled. #30830
|
|
||||||
* Fix - Use a more standard way to check if the product attributes lookup table exists. #30745
|
|
||||||
* Fix - Undefined variable notice when trying to add product in orders without specifying a product. #30739
|
|
||||||
* Fix - Use proper location for taxes when adding products via admin. #30692
|
|
||||||
* Dev - Add mobile data to WCTracker. #30415
|
|
||||||
* Tweak - Remove hardcode category banners in Settings > Marketplace and use the WooCommerce.com API instead. #30938
|
|
||||||
* Tweak - Show a search again message when marketplace results are empty. #30642
|
|
||||||
* Tweak - Add promoted cards styling to marketplace section. #30861
|
|
||||||
* Enhancement - Add ratings, reviews and icons into Marketplace's Product Cards. #30840
|
|
||||||
* Enhancement - Update Storefront banner width and track links in the marketplace page. #30882
|
|
||||||
* Enhancement - Revamp the WooCommerce Marketplace page. #30900
|
|
||||||
|
|
||||||
**WooCommerce Admin - 2.8.0 **
|
|
||||||
|
|
||||||
* Fix - Issue where stock activity panel was not rendering correctly. #7817
|
|
||||||
* Fix - Increase CSS specificity to avoid conflicts and broken panel styling. #7813
|
|
||||||
* Fix - Updated link to WooCommerce Developers Blog in readme.txt. #7824
|
|
||||||
* Fix - Fixed navigation menu text color after Gutenberg 11.6.0. #7771
|
|
||||||
* Fix - Add status param to notes/delete/all REST endpoint, to correctly delete all notes. #7743
|
|
||||||
* Fix - Allow already installed marketing extensions to be activated. #7740
|
|
||||||
* Fix - Add missing title text for marketing task. #7640
|
|
||||||
* Fix - Assign parent order status as children order status if refund order. #7253
|
|
||||||
* Fix - Fix category lookup logic to update children correctly. #7709
|
|
||||||
* Fix - Fixing an unwanted page refresh when using Woo Navigation. #7615
|
|
||||||
* Fix - Fix naming of event names and properties. #7677
|
|
||||||
* Fix - Fix white screen for variation analytic data without a name. #7686
|
|
||||||
* Add - Store Profiler and Product task - include Subscriptions. #7734
|
|
||||||
* Update - Update WC pay supported country list for the default free extensions. #7873
|
|
||||||
* Update - Update back up copy of free extension for Google Listing & Ads plugin. #7798
|
|
||||||
* Update - Update Eway payment gateway capitalization (was eWAY). #7678
|
|
||||||
* Update - Enable Square in France. #7679
|
|
||||||
* Enhancement - Only load tasks during rest api requests. #7856
|
|
||||||
* Enhancement - Add experiment for promoting WooCommerce Payments in payment methods table. #7666
|
|
||||||
|
|
||||||
**WooCommerce Blocks - 6.0.0 & 6.0.1 & 6.0.2 & 6.1.0**
|
|
||||||
|
|
||||||
* Fix - Infinite recursion when removing an attribute filter from the Active filters block. #4816
|
|
||||||
* Fix - Update All Reviews block so it honors 'ratings enabled' and 'show avatars' preferences. #4764
|
|
||||||
* Fix - Products by Category: Moved renderEmptyResponsePlaceholder to separate method to prevent unnecessary rerender. #4751
|
|
||||||
* Fix - Calculation of number of reviews in the Reviews by Category block. #4729
|
|
||||||
* Fix - Dropdown list in Product Category List Block for nested categories #4920
|
|
||||||
* Fix - String translations within the All Products Block. #4897
|
|
||||||
* Fix - Filter By Price: Update aria values to be more representative of the actual values presented. #4839
|
|
||||||
* Fix - Filter button from Filter Products by Attribute block is not aligned with the input field. #4814
|
|
||||||
* 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
|
|
||||||
|
|
||||||
[See changelog for all versions](https://raw.githubusercontent.com/woocommerce/woocommerce/trunk/changelog.txt).
|
[See changelog for all versions](https://raw.githubusercontent.com/woocommerce/woocommerce/trunk/changelog.txt).
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
*
|
*
|
||||||
* @see https://docs.woocommerce.com/document/template-structure/
|
* @see https://docs.woocommerce.com/document/template-structure/
|
||||||
* @package WooCommerce\Templates
|
* @package WooCommerce\Templates
|
||||||
* @version 3.5.5
|
* @version 6.1.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
defined( 'ABSPATH' ) || exit;
|
defined( 'ABSPATH' ) || exit;
|
||||||
|
@ -35,7 +35,7 @@ do_action( 'woocommerce_before_add_to_cart_form' ); ?>
|
||||||
<tbody>
|
<tbody>
|
||||||
<?php foreach ( $attributes as $attribute_name => $options ) : ?>
|
<?php foreach ( $attributes as $attribute_name => $options ) : ?>
|
||||||
<tr>
|
<tr>
|
||||||
<td class="label"><label for="<?php echo esc_attr( sanitize_title( $attribute_name ) ); ?>"><?php echo wc_attribute_label( $attribute_name ); // WPCS: XSS ok. ?></label></td>
|
<th class="label"><label for="<?php echo esc_attr( sanitize_title( $attribute_name ) ); ?>"><?php echo wc_attribute_label( $attribute_name ); // WPCS: XSS ok. ?></label></th>
|
||||||
<td class="value">
|
<td class="value">
|
||||||
<?php
|
<?php
|
||||||
wc_dropdown_variation_attribute_options(
|
wc_dropdown_variation_attribute_options(
|
||||||
|
|
|
@ -186,6 +186,15 @@ For example:
|
||||||
- `PUPPETEER_SLOWMO=10` - will run tests faster
|
- `PUPPETEER_SLOWMO=10` - will run tests faster
|
||||||
- `PUPPETEER_SLOWMO=70` - will run tests slower
|
- `PUPPETEER_SLOWMO=70` - will run tests slower
|
||||||
|
|
||||||
|
### How to retry failed tests
|
||||||
|
|
||||||
|
Sometimes tests may fail for different reasons such as network issues, or lost connection. To mitigate against test flakiess, failed tests are rerun up to 3 times before being marked as failed. The amount of retry attempts can be adjusted by passing the `E2E_RETRY_TIMES` variable when running tests. For example:
|
||||||
|
|
||||||
|
```
|
||||||
|
cd plugins/woocommerce
|
||||||
|
E2E_RETRY_TIMES=2 pnpx wc-e2e test:e2e
|
||||||
|
```
|
||||||
|
|
||||||
### How to run tests in debug mode
|
### How to run tests in debug mode
|
||||||
|
|
||||||
Tests run in headless mode by default. While writing tests it may be useful to have the debugger loaded while running a test in non-headless mode. To run tests in debug mode:
|
Tests run in headless mode by default. While writing tests it may be useful to have the debugger loaded while running a test in non-headless mode. To run tests in debug mode:
|
||||||
|
|
|
@ -7,7 +7,7 @@ import {
|
||||||
|
|
||||||
const config = require( 'config' );
|
const config = require( 'config' );
|
||||||
const { HTTPClientFactory } = require( '@woocommerce/api' );
|
const { HTTPClientFactory } = require( '@woocommerce/api' );
|
||||||
const { addConsoleSuppression, updateReadyPageStatus } = require( '@woocommerce/e2e-environment' );
|
const { addConsoleSuppression, updateReadyPageStatus, setupJestRetries } = require( '@woocommerce/e2e-environment' );
|
||||||
const { DEFAULT_TIMEOUT_OVERRIDE } = process.env;
|
const { DEFAULT_TIMEOUT_OVERRIDE } = process.env;
|
||||||
|
|
||||||
// @todo: remove this once https://github.com/woocommerce/woocommerce-admin/issues/6992 has been addressed
|
// @todo: remove this once https://github.com/woocommerce/woocommerce-admin/issues/6992 has been addressed
|
||||||
|
@ -40,6 +40,8 @@ async function trashExistingPosts() {
|
||||||
// each other's side-effects.
|
// each other's side-effects.
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
|
|
||||||
|
setupJestRetries( 2 );
|
||||||
|
|
||||||
if ( DEFAULT_TIMEOUT_OVERRIDE ) {
|
if ( DEFAULT_TIMEOUT_OVERRIDE ) {
|
||||||
page.setDefaultNavigationTimeout( DEFAULT_TIMEOUT_OVERRIDE );
|
page.setDefaultNavigationTimeout( DEFAULT_TIMEOUT_OVERRIDE );
|
||||||
page.setDefaultTimeout( DEFAULT_TIMEOUT_OVERRIDE );
|
page.setDefaultTimeout( DEFAULT_TIMEOUT_OVERRIDE );
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
*/
|
*/
|
||||||
const { merchant, utils } = require( '@woocommerce/e2e-utils' );
|
const { merchant, utils } = require( '@woocommerce/e2e-utils' );
|
||||||
|
|
||||||
const { getRemotePluginZip, getLatestReleaseZipUrl } = require( '@woocommerce/e2e-environment' );
|
const { getRemotePluginZip, getLatestReleaseZipUrl, deleteDownloadedPluginFiles } = require( '@woocommerce/e2e-environment' );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* External dependencies
|
* External dependencies
|
||||||
|
@ -24,7 +24,7 @@ utils.describeIf( UPDATE_WC )( 'WooCommerce plugin can be uploaded and activated
|
||||||
beforeAll( async () => {
|
beforeAll( async () => {
|
||||||
|
|
||||||
if ( TEST_RELEASE ) {
|
if ( TEST_RELEASE ) {
|
||||||
zipUrl = await getLatestReleaseZipUrl( 'woocommerce', 'woocommerce' );
|
zipUrl = await getLatestReleaseZipUrl( 'woocommerce/woocommerce' );
|
||||||
} else {
|
} else {
|
||||||
zipUrl = 'https://github.com/woocommerce/woocommerce/releases/download/nightly/woocommerce-trunk-nightly.zip';
|
zipUrl = 'https://github.com/woocommerce/woocommerce/releases/download/nightly/woocommerce-trunk-nightly.zip';
|
||||||
}
|
}
|
||||||
|
@ -35,6 +35,7 @@ utils.describeIf( UPDATE_WC )( 'WooCommerce plugin can be uploaded and activated
|
||||||
|
|
||||||
afterAll( async () => {
|
afterAll( async () => {
|
||||||
await merchant.logout();
|
await merchant.logout();
|
||||||
|
await deleteDownloadedPluginFiles();
|
||||||
});
|
});
|
||||||
|
|
||||||
it( 'can upload and activate the WooCommerce plugin', async () => {
|
it( 'can upload and activate the WooCommerce plugin', async () => {
|
||||||
|
@ -46,4 +47,8 @@ utils.describeIf( UPDATE_WC )( 'WooCommerce plugin can be uploaded and activated
|
||||||
await merchant.runDatabaseUpdate();
|
await merchant.runDatabaseUpdate();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it( 'can remove downloaded plugin zip', async () => {
|
||||||
|
await deleteDownloadedPluginFiles();
|
||||||
|
} );
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
/**
|
||||||
|
* Internal dependencies
|
||||||
|
*/
|
||||||
|
const { merchant, utils } = require( '@woocommerce/e2e-utils' );
|
||||||
|
|
||||||
|
const { getRemotePluginZip, getLatestReleaseZipUrl, deleteDownloadedPluginFiles } = require( '@woocommerce/e2e-environment' );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* External dependencies
|
||||||
|
*/
|
||||||
|
const {
|
||||||
|
it,
|
||||||
|
beforeAll,
|
||||||
|
} = require( '@jest/globals' );
|
||||||
|
|
||||||
|
const { GITHUB_REPOSITORY, PLUGIN_NAME, GITHUB_TOKEN } = process.env;
|
||||||
|
|
||||||
|
let zipUrl;
|
||||||
|
let pluginPath;
|
||||||
|
|
||||||
|
utils.describeIf( GITHUB_REPOSITORY )( 'Upload and activate plugin', () => {
|
||||||
|
beforeAll( async () => {
|
||||||
|
zipUrl = await getLatestReleaseZipUrl( GITHUB_REPOSITORY, GITHUB_TOKEN );
|
||||||
|
|
||||||
|
pluginPath = await getRemotePluginZip( zipUrl, GITHUB_TOKEN );
|
||||||
|
|
||||||
|
await merchant.login();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll( async () => {
|
||||||
|
await merchant.logout();
|
||||||
|
});
|
||||||
|
|
||||||
|
it( 'can upload and activate the provided plugin', async () => {
|
||||||
|
await merchant.uploadAndActivatePlugin( pluginPath, PLUGIN_NAME );
|
||||||
|
});
|
||||||
|
|
||||||
|
it( 'can remove downloaded plugin zip', async () => {
|
||||||
|
await deleteDownloadedPluginFiles();
|
||||||
|
} );
|
||||||
|
|
||||||
|
});
|
|
@ -82,6 +82,7 @@ product_sku | SKU of product to be used in cart and checkout flow | yes `__ENV.P
|
||||||
product_url | the `product-name` portion of product permalink of the product to be used in cart and checkout flow | yes `__ENV.P_URL`
|
product_url | the `product-name` portion of product permalink of the product to be used in cart and checkout flow | yes `__ENV.P_URL`
|
||||||
product_id | the product ID of of product to be used in cart and checkout flow | yes `__ENV.P_ID`
|
product_id | the product ID of of product to be used in cart and checkout flow | yes `__ENV.P_ID`
|
||||||
product_search_term | search term to return product to be used in cart and checkout flow | yes `__ENV.P_TERM`
|
product_search_term | search term to return product to be used in cart and checkout flow | yes `__ENV.P_TERM`
|
||||||
|
product_category | category of product to be used for browsing category products | yes `__ENV.P_CAT`
|
||||||
coupon_code | coupon code to be used in applying coupon flow | yes `__ENV.P_COUPON`
|
coupon_code | coupon code to be used in applying coupon flow | yes `__ENV.P_COUPON`
|
||||||
add_product_title | title of product to be added in merchant add product flow | no
|
add_product_title | title of product to be added in merchant add product flow | no
|
||||||
add_product_regular_price | regular price of product to be added in merchant add product flow | no
|
add_product_regular_price | regular price of product to be added in merchant add product flow | no
|
||||||
|
@ -102,7 +103,7 @@ To execute an individual test file (for example `requests/shopper/shop-page.js`)
|
||||||
|
|
||||||
CLI `k6 run requests/shopper/shop-page.js`
|
CLI `k6 run requests/shopper/shop-page.js`
|
||||||
|
|
||||||
Docker `docker run --network="host" -v /[YOUR LOCAL WC DIRECTORY FULL PATH]/requests:/requests -it loadimpact/k6 run /requests/shopper/shop-page.js`
|
Docker `docker run --network="host" -v /[YOUR LOCAL WC DIRECTORY FULL PATH]/tests:/tests -it loadimpact/k6 run /tests/performance/requests/shopper/shop-page.js`
|
||||||
|
|
||||||
This will run the individual test for 1 iteration.
|
This will run the individual test for 1 iteration.
|
||||||
|
|
||||||
|
|
|
@ -37,6 +37,7 @@ export const product_sku = __ENV.P_SKU || 'woo-beanie';
|
||||||
export const product_url = __ENV.P_URL || 'beanie';
|
export const product_url = __ENV.P_URL || 'beanie';
|
||||||
export const product_id = __ENV.P_ID || '13';
|
export const product_id = __ENV.P_ID || '13';
|
||||||
export const product_search_term = __ENV.P_TERM || 'beanie';
|
export const product_search_term = __ENV.P_TERM || 'beanie';
|
||||||
|
export const product_category = __ENV.P_CAT || 'Accessories';
|
||||||
|
|
||||||
export const coupon_code = __ENV.P_COUPON || 'testing';
|
export const coupon_code = __ENV.P_COUPON || 'testing';
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
import { sleep, check, group } from "k6";
|
||||||
|
import http from "k6/http";
|
||||||
|
import { Trend } from "k6/metrics";
|
||||||
|
import { randomIntBetween } from "https://jslib.k6.io/k6-utils/1.1.0/index.js";
|
||||||
|
import { base_url, think_time_min, think_time_max, product_category } from "../../config.js";
|
||||||
|
import {
|
||||||
|
htmlRequestHeader,
|
||||||
|
commonRequestHeaders,
|
||||||
|
commonGetRequestHeaders,
|
||||||
|
commonNonStandardHeaders,
|
||||||
|
} from "../../headers.js";
|
||||||
|
|
||||||
|
// Custom metric to add to standard results output.
|
||||||
|
let categoryPageTrend = new Trend("wc_get_product_category_name");
|
||||||
|
|
||||||
|
export function categoryPage() {
|
||||||
|
let response;
|
||||||
|
|
||||||
|
group("Category Page", function () {
|
||||||
|
var requestHeaders = Object.assign({},
|
||||||
|
htmlRequestHeader,
|
||||||
|
commonRequestHeaders,
|
||||||
|
commonGetRequestHeaders,
|
||||||
|
commonNonStandardHeaders
|
||||||
|
);
|
||||||
|
|
||||||
|
response = http.get(`${base_url}/product-category/${product_category}/`, {
|
||||||
|
headers: requestHeaders,
|
||||||
|
});
|
||||||
|
categoryPageTrend.add(response.timings.duration);
|
||||||
|
check(response, {
|
||||||
|
"is status 200": (r) => r.status === 200,
|
||||||
|
"body contains: Category's title": (response) =>
|
||||||
|
response.body.includes(
|
||||||
|
`<h1 class="woocommerce-products-header__title page-title">${product_category}</h1>`
|
||||||
|
),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
sleep(randomIntBetween(`${think_time_min}`, `${think_time_max}`));
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function () {
|
||||||
|
categoryPage();
|
||||||
|
}
|
|
@ -15,6 +15,7 @@ import { orders } from '../requests/merchant/orders.js';
|
||||||
import { ordersHeartbeat } from '../requests/merchant/orders-heartbeat.js';
|
import { ordersHeartbeat } from '../requests/merchant/orders-heartbeat.js';
|
||||||
import { homeWCAdmin } from '../requests/merchant/home-wc-admin.js';
|
import { homeWCAdmin } from '../requests/merchant/home-wc-admin.js';
|
||||||
import { myAccountMerchantLogin } from '../requests/merchant/my-account-merchant.js';
|
import { myAccountMerchantLogin } from '../requests/merchant/my-account-merchant.js';
|
||||||
|
import { categoryPage } from '../requests/shopper/category-page.js';
|
||||||
|
|
||||||
export let options = {
|
export let options = {
|
||||||
scenarios: {
|
scenarios: {
|
||||||
|
@ -124,6 +125,7 @@ export function shopperBrowsingFlows() {
|
||||||
cartRemoveItem();
|
cartRemoveItem();
|
||||||
cartApplyCoupon();
|
cartApplyCoupon();
|
||||||
myAccount();
|
myAccount();
|
||||||
|
categoryPage();
|
||||||
}
|
}
|
||||||
export function checkoutGuestFlow() {
|
export function checkoutGuestFlow() {
|
||||||
cart();
|
cart();
|
||||||
|
|
|
@ -15,6 +15,7 @@ import { orders } from '../requests/merchant/orders.js';
|
||||||
import { ordersHeartbeat } from '../requests/merchant/orders-heartbeat.js';
|
import { ordersHeartbeat } from '../requests/merchant/orders-heartbeat.js';
|
||||||
import { homeWCAdmin } from '../requests/merchant/home-wc-admin.js';
|
import { homeWCAdmin } from '../requests/merchant/home-wc-admin.js';
|
||||||
import { myAccountMerchantLogin } from '../requests/merchant/my-account-merchant.js';
|
import { myAccountMerchantLogin } from '../requests/merchant/my-account-merchant.js';
|
||||||
|
import { categoryPage } from '../requests/shopper/category-page.js';
|
||||||
|
|
||||||
export let options = {
|
export let options = {
|
||||||
scenarios: {
|
scenarios: {
|
||||||
|
@ -114,6 +115,7 @@ export function shopperBrowsingFlows() {
|
||||||
cartRemoveItem();
|
cartRemoveItem();
|
||||||
cartApplyCoupon();
|
cartApplyCoupon();
|
||||||
myAccount();
|
myAccount();
|
||||||
|
categoryPage();
|
||||||
}
|
}
|
||||||
export function checkoutGuestFlow() {
|
export function checkoutGuestFlow() {
|
||||||
cart();
|
cart();
|
||||||
|
|
|
@ -8,6 +8,7 @@ import { cartApplyCoupon } from '../requests/shopper/cart-apply-coupon.js';
|
||||||
import { checkoutGuest } from '../requests/shopper/checkout-guest.js';
|
import { checkoutGuest } from '../requests/shopper/checkout-guest.js';
|
||||||
import { checkoutCustomerLogin } from '../requests/shopper/checkout-customer-login.js';
|
import { checkoutCustomerLogin } from '../requests/shopper/checkout-customer-login.js';
|
||||||
import { myAccount } from '../requests/shopper/my-account.js';
|
import { myAccount } from '../requests/shopper/my-account.js';
|
||||||
|
import { categoryPage } from '../requests/shopper/category-page.js';
|
||||||
|
|
||||||
export let options = {
|
export let options = {
|
||||||
scenarios: {
|
scenarios: {
|
||||||
|
@ -88,6 +89,7 @@ export function searchProductFlow() {
|
||||||
}
|
}
|
||||||
export function singleProductFlow() {
|
export function singleProductFlow() {
|
||||||
singleProduct();
|
singleProduct();
|
||||||
|
categoryPage();
|
||||||
}
|
}
|
||||||
export function checkoutGuestFlow() {
|
export function checkoutGuestFlow() {
|
||||||
cart();
|
cart();
|
||||||
|
|
2998
pnpm-lock.yaml
2998
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"executors": {
|
||||||
|
"changelog": {
|
||||||
|
"implementation": "./impl",
|
||||||
|
"schema": "./schema.json",
|
||||||
|
"description": "Executes Jetpack Changelogger scripts."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,115 @@
|
||||||
|
"use strict";
|
||||||
|
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||||
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||||
|
return new (P || (P = Promise))(function (resolve, reject) {
|
||||||
|
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||||
|
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||||
|
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||||
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||||
|
});
|
||||||
|
};
|
||||||
|
var __generator = (this && this.__generator) || function (thisArg, body) {
|
||||||
|
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
||||||
|
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
||||||
|
function verb(n) { return function (v) { return step([n, v]); }; }
|
||||||
|
function step(op) {
|
||||||
|
if (f) throw new TypeError("Generator is already executing.");
|
||||||
|
while (_) try {
|
||||||
|
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
||||||
|
if (y = 0, t) op = [op[0] & 2, t.value];
|
||||||
|
switch (op[0]) {
|
||||||
|
case 0: case 1: t = op; break;
|
||||||
|
case 4: _.label++; return { value: op[1], done: false };
|
||||||
|
case 5: _.label++; y = op[1]; op = [0]; continue;
|
||||||
|
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
||||||
|
default:
|
||||||
|
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
||||||
|
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
||||||
|
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
||||||
|
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
||||||
|
if (t[2]) _.ops.pop();
|
||||||
|
_.trys.pop(); continue;
|
||||||
|
}
|
||||||
|
op = body.call(thisArg, _);
|
||||||
|
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
||||||
|
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var __rest = (this && this.__rest) || function (s, e) {
|
||||||
|
var t = {};
|
||||||
|
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
||||||
|
t[p] = s[p];
|
||||||
|
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
||||||
|
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
||||||
|
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
||||||
|
t[p[i]] = s[p[i]];
|
||||||
|
}
|
||||||
|
return t;
|
||||||
|
};
|
||||||
|
exports.__esModule = true;
|
||||||
|
var child_process_1 = require("child_process");
|
||||||
|
var path_1 = require("path");
|
||||||
|
var fs_1 = require("fs");
|
||||||
|
var chalk = require("chalk");
|
||||||
|
var changeloggerScriptPath = 'vendor/bin/changelogger';
|
||||||
|
function runChangelogger(_a) {
|
||||||
|
var action = _a.action, cwd = _a.cwd, extraArgs = __rest(_a, ["action", "cwd"]);
|
||||||
|
return __awaiter(this, void 0, void 0, function () {
|
||||||
|
return __generator(this, function (_b) {
|
||||||
|
return [2 /*return*/, new Promise(function (resolve, reject) {
|
||||||
|
var args = [action].concat(
|
||||||
|
// Add any extra arguments supplied. NX camel cases and converts values to Numbers.
|
||||||
|
// Undo all that so arguments can be passed to Jetpack Changelogger unmodified.
|
||||||
|
Object.keys(extraArgs).map(function (key) {
|
||||||
|
return "--" + key.replace(/[A-Z]/g, function (m) { return '-' + m.toLowerCase(); }) + "=" + (Number(extraArgs[key]) && action === 'write'
|
||||||
|
? extraArgs[key].toFixed(1)
|
||||||
|
: extraArgs[key]);
|
||||||
|
}));
|
||||||
|
var changeloggerScript = (0, child_process_1.spawn)("./" + changeloggerScriptPath, args, {
|
||||||
|
stdio: 'inherit'
|
||||||
|
});
|
||||||
|
changeloggerScript.on('close', function (code) {
|
||||||
|
resolve({ code: code, error: undefined });
|
||||||
|
});
|
||||||
|
changeloggerScript.on('error', function (error) {
|
||||||
|
reject({ code: 1, error: error });
|
||||||
|
});
|
||||||
|
})];
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function changelogExecutor(options, context) {
|
||||||
|
return __awaiter(this, void 0, void 0, function () {
|
||||||
|
var cwd, projectPath, _a, code, error;
|
||||||
|
return __generator(this, function (_b) {
|
||||||
|
switch (_b.label) {
|
||||||
|
case 0:
|
||||||
|
cwd = options.cwd;
|
||||||
|
projectPath = (0, path_1.join)(__dirname, '../../../', cwd);
|
||||||
|
console.info(chalk.cyan("\nExecuting Changelogger...\n"));
|
||||||
|
try {
|
||||||
|
process.chdir(projectPath);
|
||||||
|
console.log(chalk.yellow('Executing from directory: ' + process.cwd() + '\n'));
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
console.error(chalk.bgRed('Unable to find project working directory'));
|
||||||
|
console.error(error);
|
||||||
|
return [2 /*return*/, { success: false }];
|
||||||
|
}
|
||||||
|
if (!(0, fs_1.existsSync)(changeloggerScriptPath)) {
|
||||||
|
console.error(chalk.bgRed('Changelogger scripts not found. Did you remember to `composer install` from project directory?'));
|
||||||
|
return [2 /*return*/, { success: false }];
|
||||||
|
}
|
||||||
|
return [4 /*yield*/, runChangelogger(options)];
|
||||||
|
case 1:
|
||||||
|
_a = _b.sent(), code = _a.code, error = _a.error;
|
||||||
|
if (error) {
|
||||||
|
console.error(chalk.bgRed(error));
|
||||||
|
return [2 /*return*/, { success: false }];
|
||||||
|
}
|
||||||
|
return [2 /*return*/, { success: code === 0 }];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
exports["default"] = changelogExecutor;
|
|
@ -0,0 +1,96 @@
|
||||||
|
/**
|
||||||
|
* External dependencies
|
||||||
|
*/
|
||||||
|
import { ExecutorContext } from '@nrwl/devkit';
|
||||||
|
import { spawn } from 'child_process';
|
||||||
|
import { join } from 'path';
|
||||||
|
import { existsSync } from 'fs';
|
||||||
|
import * as chalk from 'chalk';
|
||||||
|
|
||||||
|
export interface ChangelogExecutorOptions {
|
||||||
|
action: string;
|
||||||
|
cwd: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const changeloggerScriptPath = 'vendor/bin/changelogger';
|
||||||
|
|
||||||
|
async function runChangelogger( {
|
||||||
|
action,
|
||||||
|
cwd,
|
||||||
|
...extraArgs
|
||||||
|
}: ChangelogExecutorOptions ): Promise< { code: number; error: string } > {
|
||||||
|
return new Promise( ( resolve, reject ) => {
|
||||||
|
const args = [ action ].concat(
|
||||||
|
// Add any extra arguments supplied. NX camel cases and converts values to Numbers.
|
||||||
|
// Undo all that so arguments can be passed to Jetpack Changelogger unmodified.
|
||||||
|
Object.keys( extraArgs ).map(
|
||||||
|
( key ) =>
|
||||||
|
`--${ key.replace(
|
||||||
|
/[A-Z]/g,
|
||||||
|
( m ) => '-' + m.toLowerCase()
|
||||||
|
) }=${
|
||||||
|
Number( extraArgs[ key ] ) && action === 'write'
|
||||||
|
? extraArgs[ key ].toFixed( 1 )
|
||||||
|
: extraArgs[ key ]
|
||||||
|
}`
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
const changeloggerScript = spawn(
|
||||||
|
`./${ changeloggerScriptPath }`,
|
||||||
|
args,
|
||||||
|
{
|
||||||
|
stdio: 'inherit',
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
changeloggerScript.on( 'close', ( code ) => {
|
||||||
|
resolve( { code, error: undefined } );
|
||||||
|
} );
|
||||||
|
|
||||||
|
changeloggerScript.on( 'error', ( error ) => {
|
||||||
|
reject( { code: 1, error } );
|
||||||
|
} );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async function changelogExecutor(
|
||||||
|
options: ChangelogExecutorOptions,
|
||||||
|
context: ExecutorContext
|
||||||
|
) {
|
||||||
|
const { cwd } = options;
|
||||||
|
const projectPath = join( __dirname, '../../../', cwd );
|
||||||
|
|
||||||
|
console.info( chalk.cyan( `\nExecuting Changelogger...\n` ) );
|
||||||
|
|
||||||
|
try {
|
||||||
|
process.chdir( projectPath );
|
||||||
|
console.log(
|
||||||
|
chalk.yellow( 'Executing from directory: ' + process.cwd() + '\n' )
|
||||||
|
);
|
||||||
|
} catch ( error ) {
|
||||||
|
console.error(
|
||||||
|
chalk.bgRed( 'Unable to find project working directory' )
|
||||||
|
);
|
||||||
|
console.error( error );
|
||||||
|
return { success: false };
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( ! existsSync( changeloggerScriptPath ) ) {
|
||||||
|
console.error(
|
||||||
|
chalk.bgRed(
|
||||||
|
'Changelogger scripts not found. Did you remember to `composer install` from project directory?'
|
||||||
|
)
|
||||||
|
);
|
||||||
|
return { success: false };
|
||||||
|
}
|
||||||
|
|
||||||
|
const { code, error } = await runChangelogger( options );
|
||||||
|
|
||||||
|
if ( error ) {
|
||||||
|
console.error( chalk.bgRed( error ) );
|
||||||
|
return { success: false };
|
||||||
|
}
|
||||||
|
|
||||||
|
return { success: code === 0 };
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"executors": "./executor.json"
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
{
|
||||||
|
"cli": "nx",
|
||||||
|
"id": "Changelogger",
|
||||||
|
"description": "Executes Jetpack Changelogger scripts.",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"action": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Action to take"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [ "action" ]
|
||||||
|
}
|
Loading…
Reference in New Issue