Merge branch 'trunk' into pr/31317
This commit is contained in:
commit
7b8f3be261
|
@ -1,3 +1,4 @@
|
|||
module.exports = {
|
||||
root: true,
|
||||
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.
|
|
@ -17,7 +17,7 @@ jobs:
|
|||
ref: ${{ github.event.inputs.ref || github.ref }}
|
||||
- name: Build the zip file
|
||||
id: build
|
||||
uses: woocommerce/action-build@v2
|
||||
uses: woocommerce/action-build@trunk
|
||||
- name: Unzip the file (prevents double zip problem)
|
||||
run: unzip ${{ steps.build.outputs.zip_path }} -d zipfile
|
||||
- name: Upload the zip file as an artifact
|
||||
|
|
|
@ -11,7 +11,7 @@ jobs:
|
|||
uses: actions/checkout@v2
|
||||
- name: Build
|
||||
id: build
|
||||
uses: woocommerce/action-build@v2
|
||||
uses: woocommerce/action-build@trunk
|
||||
- name: Upload release asset
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
|
|
|
@ -59,8 +59,13 @@ jobs:
|
|||
./vendor
|
||||
key: ${{ runner.os }}-${{ hashFiles('./composer.lock') }}
|
||||
|
||||
- name: Install PNPM and install dependencies
|
||||
run: |
|
||||
npm install -g pnpm
|
||||
pnpm install
|
||||
|
||||
- name: Setup and install composer
|
||||
run: composer install
|
||||
run: pnpm nx composer-install woocommerce
|
||||
|
||||
- name: Add PHP8 Compatibility.
|
||||
run: |
|
||||
|
@ -71,11 +76,11 @@ jobs:
|
|||
composer bin phpunit config repositories.0 '{"type": "path", "url": "/tmp/phpunit-7.5-fork/phpunit-add-compatibility-with-php8-to-phpunit-7", "options": {"symlink": false}}'
|
||||
composer bin phpunit require --dev -W phpunit/phpunit:@dev --ignore-platform-reqs
|
||||
rm -rf ./vendor/phpunit/
|
||||
composer dump-autoload
|
||||
pnpm nx composer-dump-autoload woocommerce
|
||||
fi
|
||||
|
||||
- name: Init DB and WP
|
||||
run: ./tests/bin/install.sh woo_test root root 127.0.0.1 ${{ matrix.wp }}
|
||||
|
||||
- name: Run tests
|
||||
run: ./vendor/bin/phpunit -c ./phpunit.xml
|
||||
run: pnpm nx test-unit woocommerce
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
name: Mirrors
|
||||
on:
|
||||
push:
|
||||
branches: ['trunk', 'release/**']
|
||||
jobs:
|
||||
build:
|
||||
name: Build WooCommerce zip
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Build
|
||||
id: build
|
||||
uses: woocommerce/action-build@trunk
|
||||
env:
|
||||
BUILD_ENV: mirrors
|
||||
|
||||
- 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
|
||||
|
||||
mirror:
|
||||
name: Push to Mirror
|
||||
runs-on: ubuntu-latest
|
||||
needs: [build]
|
||||
steps:
|
||||
- name: Create directories
|
||||
run: |
|
||||
mkdir -p tmp/woocommerce-build
|
||||
mkdir -p monorepo
|
||||
|
||||
- name: Checkout monorepo
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
path: monorepo
|
||||
|
||||
- name: Download WooCommerce ZIP
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: woocommerce
|
||||
path: tmp/woocommerce-build
|
||||
|
||||
- name: Extract and replace WooCommerce zip.
|
||||
working-directory: tmp/woocommerce-build
|
||||
run: |
|
||||
mkdir -p woocommerce/woocommerce-production
|
||||
unzip woocommerce.zip -d woocommerce/woocommerce-production
|
||||
mv woocommerce/woocommerce-production/woocommerce/* woocommerce/woocommerce-production
|
||||
rm -rf woocommerce/woocommerce-production/woocommerce
|
||||
|
||||
- name: Set up mirror
|
||||
working-directory: tmp/woocommerce-build
|
||||
run: |
|
||||
touch mirrors.txt
|
||||
echo "woocommerce/woocommerce-production" >> mirrors.txt
|
||||
|
||||
- name: Push to mirror
|
||||
uses: Automattic/action-push-to-mirrors@v1
|
||||
with:
|
||||
source-directory: ${{ github.workspace }}/monorepo
|
||||
token: ${{ secrets.API_TOKEN_GITHUB }}
|
||||
username: matticbot
|
||||
working-directory: ${{ github.workspace }}/tmp/woocommerce-build
|
||||
timeout-minutes: 5 # 2021-01-18: Successful runs seem to take about half a minute.
|
|
@ -11,6 +11,8 @@ jobs:
|
|||
- name: Build
|
||||
id: build
|
||||
uses: woocommerce/action-build@trunk
|
||||
env:
|
||||
BUILD_ENV: e2e
|
||||
|
||||
- name: Upload PR zip
|
||||
uses: actions/upload-artifact@v2
|
||||
|
@ -41,8 +43,8 @@ jobs:
|
|||
- name: Install PNPM and install dependencies
|
||||
working-directory: package/woocommerce
|
||||
run: |
|
||||
npm install -g pnpm
|
||||
pnpm install
|
||||
npm install -g pnpm
|
||||
pnpm install
|
||||
|
||||
- name: Load docker images and start containers.
|
||||
working-directory: package/woocommerce/plugins/woocommerce
|
||||
|
@ -66,8 +68,8 @@ jobs:
|
|||
- name: Install dependencies again
|
||||
working-directory: package/woocommerce
|
||||
run: |
|
||||
npm install -g pnpm
|
||||
pnpm install
|
||||
npm install -g pnpm
|
||||
pnpm install
|
||||
|
||||
- name: Run tests command.
|
||||
working-directory: package/woocommerce/plugins/woocommerce
|
||||
|
@ -75,9 +77,60 @@ jobs:
|
|||
WC_E2E_SCREENSHOTS: 1
|
||||
E2E_SLACK_TOKEN: ${{ secrets.E2E_SLACK_TOKEN }}
|
||||
E2E_SLACK_CHANNEL: ${{ secrets.E2E_SLACK_CHANNEL }}
|
||||
run: pnpx wc-e2e test:e2e
|
||||
|
||||
api-tests-run:
|
||||
name: Runs API tests.
|
||||
runs-on: ubuntu-18.04
|
||||
needs: [build]
|
||||
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:
|
||||
BASE_URL: ${{ secrets.PR_E2E_TEST_URL }}
|
||||
USER_KEY: ${{ secrets.PR_E2E_TEST_ADMIN_USER }}
|
||||
USER_SECRET: ${{ secrets.PR_E2E_TEST_ADMIN_PASSWORD }}
|
||||
run: |
|
||||
pnpx wc-e2e test:e2e
|
||||
pnpx wc-api-tests test api
|
||||
run: pnpx wc-api-tests test api
|
||||
|
|
|
@ -45,15 +45,20 @@ jobs:
|
|||
./vendor
|
||||
key: ${{ runner.os }}-${{ hashFiles('./composer.lock') }}
|
||||
|
||||
- name: Install PNPM and install dependencies
|
||||
run: |
|
||||
npm install -g pnpm
|
||||
pnpm install
|
||||
|
||||
- name: Setup and install composer
|
||||
run: composer install
|
||||
run: pnpm nx composer-install woocommerce
|
||||
|
||||
- name: Init DB and WP
|
||||
run: ./tests/bin/install.sh woo_test root root 127.0.0.1 latest
|
||||
run: pnpm nx install-unit-test-db woocommerce
|
||||
|
||||
- name: Run unit tests with code coverage. Allow to fail.
|
||||
run: |
|
||||
RUN_CODE_COVERAGE=1 bash ./tests/bin/phpunit.sh
|
||||
pnpm nx test-code-coverage woocommerce
|
||||
exit 0
|
||||
|
||||
- name: Send code coverage to Codecov.
|
||||
|
|
|
@ -35,8 +35,13 @@ jobs:
|
|||
./vendor
|
||||
key: ${{ runner.os }}-${{ hashFiles('./composer.lock') }}
|
||||
|
||||
- name: Install PNPM and install dependencies
|
||||
run: |
|
||||
npm install -g pnpm
|
||||
pnpm install
|
||||
|
||||
- name: Setup and install composer
|
||||
run: composer install
|
||||
run: pnpm nx composer-install woocommerce
|
||||
|
||||
- name: Run code sniff
|
||||
continue-on-error: true
|
||||
|
|
|
@ -13,10 +13,10 @@ jobs:
|
|||
steps:
|
||||
- name: Create dirs.
|
||||
run: |
|
||||
mkdir -p code/woocommerce
|
||||
mkdir -p package/woocommerce
|
||||
mkdir -p tmp/woocommerce
|
||||
mkdir -p node_modules
|
||||
mkdir -p code/woocommerce
|
||||
mkdir -p package/woocommerce
|
||||
mkdir -p tmp/woocommerce
|
||||
mkdir -p node_modules
|
||||
|
||||
- name: Checkout code.
|
||||
uses: actions/checkout@v2
|
||||
|
@ -25,15 +25,48 @@ jobs:
|
|||
|
||||
- name: Install prerequisites.
|
||||
working-directory: package/woocommerce/plugins/woocommerce
|
||||
id: installation
|
||||
run: |
|
||||
npm install -g pnpm
|
||||
pnpm install
|
||||
composer install --no-dev
|
||||
pnpm run build:assets
|
||||
pnpm nx composer-install-no-dev woocommerce
|
||||
pnpm nx build-assets woocommerce
|
||||
pnpm install jest
|
||||
|
||||
|
||||
- name: Run smoke test.
|
||||
working-directory: package/woocommerce/plugins/woocommerce
|
||||
if: always()
|
||||
env:
|
||||
SMOKE_TEST_URL: ${{ secrets.SMOKE_TEST_URL }}
|
||||
SMOKE_TEST_ADMIN_USER: ${{ secrets.SMOKE_TEST_ADMIN_USER }}
|
||||
SMOKE_TEST_ADMIN_PASSWORD: ${{ secrets.SMOKE_TEST_ADMIN_PASSWORD }}
|
||||
SMOKE_TEST_ADMIN_USER_EMAIL: ${{ secrets.SMOKE_TEST_ADMIN_USER_EMAIL }}
|
||||
SMOKE_TEST_CUSTOMER_USER: ${{ secrets.SMOKE_TEST_CUSTOMER_USER }}
|
||||
SMOKE_TEST_CUSTOMER_PASSWORD: ${{ secrets.SMOKE_TEST_CUSTOMER_PASSWORD }}
|
||||
WC_E2E_SCREENSHOTS: 1
|
||||
E2E_RETEST: 1
|
||||
E2E_SLACK_TOKEN: ${{ secrets.SMOKE_TEST_SLACK_TOKEN }}
|
||||
E2E_SLACK_CHANNEL: ${{ secrets.SMOKE_TEST_SLACK_CHANNEL }}
|
||||
UPDATE_WC: 1
|
||||
DEFAULT_TIMEOUT_OVERRIDE: 120000
|
||||
run: |
|
||||
pnpx wc-e2e test:e2e tests/e2e/specs/smoke-tests/update-woocommerce.js
|
||||
|
||||
- name: Post Smoke tests results comment on PR
|
||||
if: always()
|
||||
uses: actions/github-script@v5
|
||||
env:
|
||||
TITLE: 'Smoke Test Results'
|
||||
SMOKE_TEST_URL: ${{ secrets.SMOKE_TEST_URL }}
|
||||
with:
|
||||
github-token: ${{secrets.GITHUB_TOKEN}}
|
||||
script: |
|
||||
const script = require( './package/woocommerce/packages/js/e2e-environment/bin/post-results-to-github-pr.js' )
|
||||
await script({github, context})
|
||||
|
||||
- name: Run E2E tests.
|
||||
working-directory: package/woocommerce/plugins/woocommerce
|
||||
if: always()
|
||||
env:
|
||||
SMOKE_TEST_URL: ${{ secrets.SMOKE_TEST_URL }}
|
||||
SMOKE_TEST_ADMIN_USER: ${{ secrets.SMOKE_TEST_ADMIN_USER }}
|
||||
|
@ -48,11 +81,22 @@ jobs:
|
|||
UPDATE_WC: 1
|
||||
DEFAULT_TIMEOUT_OVERRIDE: 120000
|
||||
run: |
|
||||
pnpx wc-e2e test:e2e plugins/woocommerce/tests/e2e/specs/smoke-tests/update-woocommerce.js
|
||||
pnpx wc-e2e test:e2e
|
||||
|
||||
- name: Post E2E tests results comment on PR
|
||||
if: always()
|
||||
uses: actions/github-script@v5
|
||||
env:
|
||||
TITLE: 'E2E Test Results'
|
||||
SMOKE_TEST_URL: ${{ secrets.SMOKE_TEST_URL }}
|
||||
with:
|
||||
github-token: ${{secrets.GITHUB_TOKEN}}
|
||||
script: |
|
||||
const script = require( './package/woocommerce/packages/js/e2e-environment/bin/post-results-to-github-pr.js' )
|
||||
await script({github, context})
|
||||
|
||||
- name: Remove label from pull request.
|
||||
if: "${{ contains(github.event.pull_request.labels.*.name, 'run: smoke tests') }}"
|
||||
if: always() && ${{ contains( github.event.pull_request.labels.*.name, format('run{0} smoke tests', ':')) }}
|
||||
uses: actions-ecosystem/action-remove-labels@v1
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
|
|
@ -56,8 +56,11 @@ jobs:
|
|||
./vendor
|
||||
key: ${{ runner.os }}-${{ hashFiles('./composer.lock') }}
|
||||
|
||||
- name: Setup and install composer
|
||||
run: composer install
|
||||
- name: Install PNPM and install dependencies
|
||||
run: |
|
||||
npm install -g pnpm
|
||||
pnpm install
|
||||
pnpm nx composer-install woocommerce
|
||||
|
||||
- name: Add PHP8 Compatibility.
|
||||
run: |
|
||||
|
@ -75,4 +78,4 @@ jobs:
|
|||
run: ./tests/bin/install.sh woo_test root root 127.0.0.1 ${{ matrix.wp }}
|
||||
|
||||
- name: Run tests
|
||||
run: ./vendor/bin/phpunit -c ./phpunit.xml
|
||||
run: pnpm nx test-unit woocommerce
|
||||
|
|
|
@ -26,8 +26,8 @@ jobs:
|
|||
run: |
|
||||
npm install -g pnpm
|
||||
pnpm install
|
||||
composer install --no-dev
|
||||
pnpm run build:assets
|
||||
pnpm nx composer-install-no-dev woocommerce
|
||||
pnpm nx build-assets woocommerce
|
||||
pnpm install jest
|
||||
|
||||
- name: Run smoke test.
|
||||
|
@ -49,6 +49,106 @@ jobs:
|
|||
USER_KEY: ${{ secrets.SMOKE_TEST_ADMIN_USER }}
|
||||
USER_SECRET: ${{ secrets.SMOKE_TEST_ADMIN_PASSWORD }}
|
||||
run: |
|
||||
pnpx wc-e2e test:e2e plugins/woocommerce/tests/e2e/specs/smoke-tests/update-woocommerce.js
|
||||
pnpx wc-e2e test:e2e tests/e2e/specs/smoke-tests/update-woocommerce.js
|
||||
pnpx wc-e2e test:e2e
|
||||
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 tests/e2e/specs/smoke-tests/upload-plugin.js
|
||||
pnpm nx test-e2e woocommerce
|
||||
|
|
|
@ -26,8 +26,8 @@ jobs:
|
|||
run: |
|
||||
npm install -g pnpm
|
||||
pnpm install
|
||||
composer install --no-dev
|
||||
pnpm run build:assets
|
||||
pnpm nx composer-install-no-dev woocommerce
|
||||
pnpm nx build-assets woocommerce
|
||||
pnpm install jest
|
||||
|
||||
- name: Run smoke test.
|
||||
|
@ -50,7 +50,7 @@ jobs:
|
|||
USER_KEY: ${{ secrets.RELEASE_TEST_ADMIN_USER }}
|
||||
USER_SECRET: ${{ secrets.RELEASE_TEST_ADMIN_PASSWORD }}
|
||||
run: |
|
||||
pnpx wc-e2e test:e2e plugins/woocommerce/tests/e2e/specs/smoke-tests/update-woocommerce.js
|
||||
pnpx wc-e2e test:e2e tests/e2e/specs/smoke-tests/update-woocommerce.js
|
||||
pnpx wc-e2e test:e2e
|
||||
pnpx wc-api-tests test api
|
||||
test-wp-version:
|
||||
|
@ -73,16 +73,17 @@ jobs:
|
|||
with:
|
||||
path: package/woocommerce
|
||||
|
||||
- name: Run npm install.
|
||||
working-directory: package/woocommerce/plugins/woocommerce
|
||||
npm install -g pnpm
|
||||
pnpm install
|
||||
- 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: pnpx wc-e2e docker:up
|
||||
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
|
||||
|
@ -103,4 +104,75 @@ jobs:
|
|||
WC_E2E_SCREENSHOTS: 1
|
||||
E2E_SLACK_TOKEN: ${{ secrets.SMOKE_TEST_SLACK_TOKEN }}
|
||||
E2E_SLACK_CHANNEL: ${{ secrets.RELEASE_TEST_SLACK_CHANNEL }}
|
||||
run: pnpx wc-e2e test:e2e
|
||||
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 tests/e2e/specs/smoke-tests/upload-plugin.js
|
||||
pnpm nx test-e2e woocommerce
|
||||
|
|
|
@ -5,10 +5,28 @@ Thumbs.db
|
|||
# IDE files
|
||||
.idea
|
||||
.vscode/
|
||||
project.xml
|
||||
project.properties
|
||||
.project
|
||||
.settings*
|
||||
*.sublime-project
|
||||
*.sublime-workspace
|
||||
.sublimelinterrc
|
||||
|
||||
# Grunt
|
||||
none
|
||||
|
||||
# Sass
|
||||
.sass-cache/
|
||||
|
||||
# Logs
|
||||
logs/
|
||||
|
||||
# Eslint Cache
|
||||
.eslintcache
|
||||
|
||||
# Environment files
|
||||
wp-cli.local.yml
|
||||
.wp-env.override.json
|
||||
yarn-error.log
|
||||
npm-debug.log
|
||||
.pnpm-debug.log
|
||||
|
@ -22,6 +40,7 @@ npm-debug.log
|
|||
build/
|
||||
build-module/
|
||||
build-style/
|
||||
dist/
|
||||
|
||||
# Project files
|
||||
node_modules/
|
||||
|
@ -30,12 +49,27 @@ vendor/
|
|||
# TypeScript files
|
||||
tsconfig.tsbuildinfo
|
||||
|
||||
# Node Package Dependencies
|
||||
package-lock.json
|
||||
|
||||
# wp-env config
|
||||
.wp-env.override.json
|
||||
|
||||
# Unit tests
|
||||
/tmp
|
||||
packages/js/e2e-environment/config/default.json
|
||||
packages/js/e2e-environment/docker/wp-cli/initialize.sh
|
||||
packages/js/e2e-environment/build/
|
||||
packages/js/e2e-environment/build-module/
|
||||
tmp/
|
||||
|
||||
# Composer
|
||||
vendor/
|
||||
bin/composer/**/vendor/
|
||||
lib/vendor/
|
||||
contributors.md
|
||||
contributors.html
|
||||
|
||||
# Yarn
|
||||
yarn.lock
|
||||
|
||||
# Editors
|
||||
nbproject/private/
|
||||
|
||||
# Test Results
|
||||
test-results.json
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
{
|
||||
"extends": "stylelint-config-wordpress",
|
||||
"extends": "@wordpress/stylelint-config",
|
||||
}
|
||||
|
|
|
@ -34,9 +34,8 @@ The port # might be different depending on your `.wp-env.override.json` configur
|
|||
Once you have WP-ENV container up, we need to run a few commands to start developing.
|
||||
|
||||
1. Run `pnpm install` to install npm modules.
|
||||
2. Navigate to Core WooCommerce `cd plugins/woocommerce`.
|
||||
3. Run `pnpm run build:core`
|
||||
4. Run `composer install` to install PHP dependencies.
|
||||
2. Run `pnpm nx build woocommerce` to build core.
|
||||
3. Run `pnpm nx composer-install woocommerce` to install PHP dependencies.
|
||||
|
||||
If you don't have Composer available locally, run the following command. It runs the command in WP-ENV container.
|
||||
|
||||
|
|
105
changelog.txt
105
changelog.txt
|
@ -1,5 +1,110 @@
|
|||
== 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 =
|
||||
|
||||
**WooCommerce**
|
||||
|
|
37
nx.json
37
nx.json
|
@ -1,20 +1,17 @@
|
|||
{
|
||||
"npmScope": "woocommerce",
|
||||
"affected": {
|
||||
"defaultBase": "trunk"
|
||||
},
|
||||
"implicitDependencies": {
|
||||
"package.json": {
|
||||
"dependencies": "*",
|
||||
"devDependencies": "*"
|
||||
},
|
||||
".eslintrc.json": "*"
|
||||
},
|
||||
"extends": "@nrwl/workspace/presets/npm.json",
|
||||
"npmScope": "woocommerce-monorepo",
|
||||
"tasksRunnerOptions": {
|
||||
"default": {
|
||||
"runner": "@nrwl/workspace/tasks-runners/default",
|
||||
"options": {
|
||||
"cacheableOperations": ["build", "lint", "test", "e2e"]
|
||||
"cacheableOperations": [
|
||||
"build",
|
||||
"test",
|
||||
"lint",
|
||||
"package",
|
||||
"prepare"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -24,7 +21,21 @@
|
|||
"target": "build",
|
||||
"projects": "dependencies"
|
||||
}
|
||||
],
|
||||
"prepare": [
|
||||
{
|
||||
"target": "prepare",
|
||||
"projects": "dependencies"
|
||||
}
|
||||
],
|
||||
"package": [
|
||||
{
|
||||
"target": "package",
|
||||
"projects": "dependencies"
|
||||
}
|
||||
]
|
||||
},
|
||||
"projects": {}
|
||||
"affected": {
|
||||
"defaultBase": "trunk"
|
||||
}
|
||||
}
|
||||
|
|
81
package.json
81
package.json
|
@ -1,39 +1,46 @@
|
|||
{
|
||||
"name": "woocommerce-monorepo",
|
||||
"title": "WooCommerce Monorepo",
|
||||
"description": "Monorepo for the WooCommerce ecosystem",
|
||||
"homepage": "https://woocommerce.com/",
|
||||
"private": true,
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/woocommerce/woocommerce.git"
|
||||
},
|
||||
"author": "Automattic",
|
||||
"license": "GPL-3.0-or-later",
|
||||
"bugs": {
|
||||
"url": "https://github.com/woocommerce/woocommerce/issues"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nrwl/tao": "12.10.0",
|
||||
"@nrwl/cli": "12.10.0",
|
||||
"@nrwl/workspace": "12.10.0",
|
||||
"@types/node": "14.14.33",
|
||||
"@woocommerce/eslint-plugin": "^1.2.0",
|
||||
"@wordpress/prettier-config": "^1.0.5",
|
||||
"chalk": "^4.1.2",
|
||||
"glob": "^7.2.0",
|
||||
"jest": "^27.0.6",
|
||||
"mkdirp": "^1.0.4",
|
||||
"node-stream-zip": "^1.13.6",
|
||||
"prettier": "npm:wp-prettier@2.2.1-beta-1",
|
||||
"request": "^2.88.2",
|
||||
"typescript": "4.2.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/core": "7.12.9",
|
||||
"@wordpress/babel-plugin-import-jsx-pragma": "^3.1.0",
|
||||
"@wordpress/babel-preset-default": "^6.3.3",
|
||||
"lodash": "^4.17.21",
|
||||
"wp-textdomain": "1.0.1"
|
||||
}
|
||||
"name": "woocommerce-monorepo",
|
||||
"title": "WooCommerce Monorepo",
|
||||
"description": "Monorepo for the WooCommerce ecosystem",
|
||||
"homepage": "https://woocommerce.com/",
|
||||
"private": true,
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/woocommerce/woocommerce.git"
|
||||
},
|
||||
"author": "Automattic",
|
||||
"license": "GPL-3.0-or-later",
|
||||
"bugs": {
|
||||
"url": "https://github.com/woocommerce/woocommerce/issues"
|
||||
},
|
||||
"scripts": {
|
||||
"preinstall": "npx only-allow pnpm"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@automattic/nx-composer": "^0.1.0",
|
||||
"@nrwl/cli": "^13.3.4",
|
||||
"@nrwl/linter": "^13.3.4",
|
||||
"@nrwl/devkit": "^13.1.4",
|
||||
"@nrwl/tao": "13.3.4",
|
||||
"@nrwl/web": "^13.3.4",
|
||||
"@nrwl/workspace": "^13.3.4",
|
||||
"@types/node": "14.14.33",
|
||||
"@woocommerce/eslint-plugin": "^1.3.0",
|
||||
"@wordpress/prettier-config": "^1.1.1",
|
||||
"chalk": "^4.1.2",
|
||||
"glob": "^7.2.0",
|
||||
"jest": "^27.3.1",
|
||||
"mkdirp": "^1.0.4",
|
||||
"node-stream-zip": "^1.15.0",
|
||||
"prettier": "npm:wp-prettier@^2.2.1-beta-1",
|
||||
"request": "^2.88.2",
|
||||
"typescript": "4.2.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/core": "7.12.9",
|
||||
"@wordpress/babel-plugin-import-jsx-pragma": "^3.1.0",
|
||||
"@wordpress/babel-preset-default": "^6.4.1",
|
||||
"lodash": "^4.17.21",
|
||||
"wp-textdomain": "1.0.1"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,2 @@
|
|||
# Node modules
|
||||
node_modules/
|
||||
|
||||
# Environment
|
||||
.env
|
||||
|
||||
# Collection output
|
||||
collection.json
|
||||
|
|
|
@ -1,3 +1,10 @@
|
|||
# Unreleased
|
||||
|
||||
## Added
|
||||
- Shipping Zones API Tests
|
||||
- Shipping Methods API Tests
|
||||
- Complex Order API Tests
|
||||
|
||||
# 0.1.0
|
||||
|
||||
- Initial/beta release
|
||||
|
@ -6,3 +13,5 @@
|
|||
- Coupons API Tests
|
||||
- Refunds API Tests
|
||||
- Products API Tests
|
||||
- CRUD tests for the Orders API
|
||||
- Order Search API Tests
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
# Changelog
|
||||
|
||||
This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
---
|
||||
|
||||
[See legacy changelogs for previous versions](https://github.com/woocommerce/woocommerce/blob/<last-commit-hash-before-this-merge>/packages/js/api-core-tests/CHANGELOG.md).
|
|
@ -31,7 +31,7 @@ cd "$SCRIPTPATH/$(dirname "$REALPATH")/.."
|
|||
# Run scripts
|
||||
case $1 in
|
||||
'test')
|
||||
jest --group=$2
|
||||
jest --group=$2 --runInBand
|
||||
TESTRESULT=$?
|
||||
;;
|
||||
'make:collection')
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"name": "woocommerce/api-core-tests",
|
||||
"description": "WooCommerce API core test",
|
||||
"type": "library",
|
||||
"license": "GPL-3.0-or-later",
|
||||
"minimum-stability": "dev",
|
||||
"require-dev": {
|
||||
"automattic/jetpack-changelogger": "3.0.2"
|
||||
},
|
||||
"extra": {
|
||||
"changelogger": {
|
||||
"formatter": {
|
||||
"filename": "../../../tools/changelogger/PackageFormatter.php"
|
||||
},
|
||||
"types": {
|
||||
"fix": "Fixes an existing bug",
|
||||
"add": "Adds functionality",
|
||||
"update": "Update existing functionality",
|
||||
"dev": "Development related task",
|
||||
"tweak": "A minor adjustment to the codebase",
|
||||
"performance": "Address performance issues",
|
||||
"enhancement": "Improve existing functionality"
|
||||
},
|
||||
"changelog": "NEXT_CHANGELOG.md"
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,7 +1,19 @@
|
|||
const { order, getOrderExample } = require('./order');
|
||||
const { coupon } = require('./coupon');
|
||||
const { refund } = require('./refund');
|
||||
const shared = require('./shared');
|
||||
const { order, getOrderExample } = require( './order' );
|
||||
const { coupon } = require( './coupon' );
|
||||
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 { getShippingMethodExample } = require( './shipping-method' );
|
||||
const shared = require( './shared' );
|
||||
|
||||
module.exports = {
|
||||
order,
|
||||
|
@ -9,4 +21,14 @@ module.exports = {
|
|||
coupon,
|
||||
shared,
|
||||
refund,
|
||||
getTaxRateExamples,
|
||||
getVariationExample,
|
||||
simpleProduct,
|
||||
variableProduct,
|
||||
variation,
|
||||
virtualProduct,
|
||||
groupedProduct,
|
||||
externalProduct,
|
||||
getShippingZoneExample,
|
||||
getShippingMethodExample,
|
||||
};
|
||||
|
|
|
@ -0,0 +1,292 @@
|
|||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
const {
|
||||
postRequest,
|
||||
deleteRequest,
|
||||
getRequest,
|
||||
putRequest,
|
||||
} = require( '../utils/request' );
|
||||
const productsTestSetup = require( './product-list' );
|
||||
const { ordersApi } = require( '../endpoints/orders' );
|
||||
|
||||
const createCustomer = ( data ) => postRequest( 'customers', data );
|
||||
const deleteCustomer = ( id ) => deleteRequest( `customers/${ id }`, true );
|
||||
|
||||
const createSampleData = async () => {
|
||||
const testProductData = await productsTestSetup.createSampleData();
|
||||
|
||||
const orderedProducts = {
|
||||
pocketHoodie: testProductData.simpleProducts.find(
|
||||
( p ) => p.name === 'Hoodie with Pocket'
|
||||
),
|
||||
sunglasses: testProductData.simpleProducts.find(
|
||||
( p ) => p.name === 'Sunglasses'
|
||||
),
|
||||
beanie: testProductData.simpleProducts.find(
|
||||
( p ) => p.name === 'Beanie'
|
||||
),
|
||||
blueVneck: testProductData.variableProducts.vneckVariations.find(
|
||||
( p ) => p.sku === 'woo-vneck-tee-blue'
|
||||
),
|
||||
pennant: testProductData.externalProducts[ 0 ],
|
||||
};
|
||||
|
||||
const johnAddress = {
|
||||
first_name: 'John',
|
||||
last_name: 'Doe',
|
||||
company: 'Automattic',
|
||||
country: 'US',
|
||||
address_1: '60 29th Street',
|
||||
address_2: '#343',
|
||||
city: 'San Francisco',
|
||||
state: 'CA',
|
||||
postcode: '94110',
|
||||
phone: '123456789',
|
||||
};
|
||||
const tinaAddress = {
|
||||
first_name: 'Tina',
|
||||
last_name: 'Clark',
|
||||
company: 'Automattic',
|
||||
country: 'US',
|
||||
address_1: 'Oxford Ave',
|
||||
address_2: '',
|
||||
city: 'Buffalo',
|
||||
state: 'NY',
|
||||
postcode: '14201',
|
||||
phone: '123456789',
|
||||
};
|
||||
const guestShippingAddress = {
|
||||
first_name: 'Ano',
|
||||
last_name: 'Nymous',
|
||||
company: '',
|
||||
country: 'US',
|
||||
address_1: '0 Incognito St',
|
||||
address_2: '',
|
||||
city: 'Erie',
|
||||
state: 'PA',
|
||||
postcode: '16515',
|
||||
phone: '123456789',
|
||||
};
|
||||
const guestBillingAddress = {
|
||||
first_name: 'Ben',
|
||||
last_name: 'Efactor',
|
||||
company: '',
|
||||
country: 'US',
|
||||
address_1: '200 W University Avenue',
|
||||
address_2: '',
|
||||
city: 'Gainesville',
|
||||
state: 'FL',
|
||||
postcode: '32601',
|
||||
phone: '123456789',
|
||||
email: 'ben.efactor@email.net',
|
||||
};
|
||||
|
||||
const { body: john } = await createCustomer( {
|
||||
first_name: 'John',
|
||||
last_name: 'Doe',
|
||||
username: 'john.doe',
|
||||
email: 'john.doe@example.com',
|
||||
billing: {
|
||||
...johnAddress,
|
||||
email: 'john.doe@example.com',
|
||||
},
|
||||
shipping: johnAddress,
|
||||
} );
|
||||
|
||||
const { body: tina } = await createCustomer( {
|
||||
first_name: 'Tina',
|
||||
last_name: 'Clark',
|
||||
username: 'tina.clark',
|
||||
email: 'tina.clark@example.com',
|
||||
billing: {
|
||||
...tinaAddress,
|
||||
email: 'tina.clark@example.com',
|
||||
},
|
||||
shipping: tinaAddress,
|
||||
} );
|
||||
|
||||
const orderBaseData = {
|
||||
payment_method: 'cod',
|
||||
payment_method_title: 'Cash on Delivery',
|
||||
status: 'processing',
|
||||
set_paid: false,
|
||||
currency: 'USD',
|
||||
customer_id: 0,
|
||||
};
|
||||
|
||||
const orders = [];
|
||||
|
||||
// Have "John" order all products.
|
||||
Object.values( orderedProducts ).forEach( async ( product ) => {
|
||||
const { body: order } = await ordersApi.create.order( {
|
||||
...orderBaseData,
|
||||
customer_id: john.id,
|
||||
billing: {
|
||||
...johnAddress,
|
||||
email: 'john.doe@example.com',
|
||||
},
|
||||
shipping: johnAddress,
|
||||
line_items: [
|
||||
{
|
||||
product_id: product.id,
|
||||
quantity: 1,
|
||||
},
|
||||
],
|
||||
} );
|
||||
|
||||
orders.push( order );
|
||||
} );
|
||||
|
||||
// Have "Tina" order some sunglasses and make a child order.
|
||||
// This somewhat resembles a subscription renewal, but we're just testing the `parent` field.
|
||||
const { body: order2 } = await ordersApi.create.order( {
|
||||
...orderBaseData,
|
||||
status: 'completed',
|
||||
set_paid: true,
|
||||
customer_id: tina.id,
|
||||
billing: {
|
||||
...tinaAddress,
|
||||
email: 'tina.clark@example.com',
|
||||
},
|
||||
shipping: tinaAddress,
|
||||
line_items: [
|
||||
{
|
||||
product_id: orderedProducts.sunglasses.id,
|
||||
quantity: 1,
|
||||
},
|
||||
],
|
||||
} );
|
||||
orders.push( order2 );
|
||||
|
||||
const { body: order3 } = await ordersApi.create.order( {
|
||||
...orderBaseData,
|
||||
parent_id: order2.id,
|
||||
customer_id: tina.id,
|
||||
billing: {
|
||||
...tinaAddress,
|
||||
email: 'tina.clark@example.com',
|
||||
},
|
||||
shipping: tinaAddress,
|
||||
line_items: [
|
||||
{
|
||||
product_id: orderedProducts.sunglasses.id,
|
||||
quantity: 1,
|
||||
},
|
||||
],
|
||||
} );
|
||||
orders.push( order3 );
|
||||
|
||||
// Guest order.
|
||||
const { body: guestOrder } = await ordersApi.create.order( {
|
||||
...orderBaseData,
|
||||
billing: guestBillingAddress,
|
||||
shipping: guestShippingAddress,
|
||||
line_items: [
|
||||
{
|
||||
product_id: orderedProducts.pennant.id,
|
||||
quantity: 2,
|
||||
},
|
||||
{
|
||||
product_id: orderedProducts.beanie.id,
|
||||
quantity: 1,
|
||||
},
|
||||
],
|
||||
} );
|
||||
|
||||
// Create an order with all possible numerical fields (taxes, fees, refunds, etc).
|
||||
const { body: taxSetting } = await getRequest(
|
||||
'settings/general/woocommerce_calc_taxes'
|
||||
);
|
||||
await putRequest( 'settings/general/woocommerce_calc_taxes', {
|
||||
value: 'yes',
|
||||
} );
|
||||
|
||||
const { body: taxRate } = await postRequest( 'taxes', {
|
||||
country: '*',
|
||||
state: '*',
|
||||
postcode: '*',
|
||||
city: '*',
|
||||
rate: '5.5000',
|
||||
name: 'Tax',
|
||||
rate: '5.5',
|
||||
shipping: true,
|
||||
} );
|
||||
|
||||
const { body: coupon } = await postRequest( 'coupons', {
|
||||
code: 'save5',
|
||||
amount: '5',
|
||||
} );
|
||||
|
||||
const { body: order4 } = await ordersApi.create.order( {
|
||||
...orderBaseData,
|
||||
line_items: [
|
||||
{
|
||||
product_id: orderedProducts.blueVneck.id,
|
||||
quantity: 1,
|
||||
},
|
||||
],
|
||||
coupon_lines: [ { code: 'save5' } ],
|
||||
shipping_lines: [
|
||||
{
|
||||
method_id: 'flat_rate',
|
||||
total: '5.00',
|
||||
},
|
||||
],
|
||||
fee_lines: [
|
||||
{
|
||||
total: '1.00',
|
||||
name: 'Test Fee',
|
||||
},
|
||||
],
|
||||
} );
|
||||
|
||||
await postRequest( `orders/${ order4.id }/refunds`, {
|
||||
api_refund: false, // Prevent an actual refund request (fails with CoD),
|
||||
line_items: [
|
||||
{
|
||||
id: order4.line_items[ 0 ].id,
|
||||
quantity: 1,
|
||||
refund_total: order4.line_items[ 0 ].total,
|
||||
refund_tax: [
|
||||
{
|
||||
id: order4.line_items[ 0 ].taxes[ 0 ].id,
|
||||
refund_total: order4.line_items[ 0 ].total_tax,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
} );
|
||||
orders.push( order4 );
|
||||
|
||||
return {
|
||||
customers: { john, tina },
|
||||
orders,
|
||||
precisionOrder: order4,
|
||||
hierarchicalOrders: {
|
||||
parent: order2,
|
||||
child: order3,
|
||||
},
|
||||
guestOrder,
|
||||
testProductData,
|
||||
};
|
||||
};
|
||||
|
||||
const deleteSampleData = async ( sampleData ) => {
|
||||
await productsTestSetup.deleteSampleData( sampleData.testProductData );
|
||||
|
||||
sampleData.orders
|
||||
.concat( [ sampleData.guestOrder ] )
|
||||
.forEach( async ( { id } ) => {
|
||||
await ordersApi.delete.order( id, true );
|
||||
} );
|
||||
|
||||
Object.values( sampleData.customers ).forEach( async ( { id } ) => {
|
||||
await deleteCustomer( id );
|
||||
} );
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
createSampleData,
|
||||
deleteSampleData,
|
||||
};
|
|
@ -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 = {
|
||||
simpleProduct,
|
||||
virtualProduct,
|
||||
variableProduct,
|
||||
externalProduct,
|
||||
groupedProduct,
|
||||
};
|
File diff suppressed because it is too large
Load Diff
|
@ -1,4 +1,3 @@
|
|||
|
||||
/**
|
||||
* A basic refund.
|
||||
*
|
||||
|
@ -7,7 +6,7 @@
|
|||
* https://woocommerce.github.io/woocommerce-rest-api-docs/#order-refund-properties
|
||||
*
|
||||
*/
|
||||
const refund = {
|
||||
const refund = {
|
||||
api_refund: false,
|
||||
amount: '1.00',
|
||||
reason: 'Late delivery refund.',
|
||||
|
@ -15,5 +14,5 @@
|
|||
};
|
||||
|
||||
module.exports = {
|
||||
refund: refund,
|
||||
refund,
|
||||
};
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
/**
|
||||
* Constructs a shipping method based on the given `methodId` and `cost`.
|
||||
*
|
||||
* `methodId` should be one of the following:
|
||||
* - `free_shipping`
|
||||
* - `flat_rate`
|
||||
* - `local_pickup`
|
||||
*
|
||||
* @returns shipping method object that can serve as a request payload for adding a shipping method to a shipping zone.
|
||||
*/
|
||||
const getShippingMethodExample = ( methodId, cost ) => {
|
||||
const shippingMethodExample = {
|
||||
method_id: methodId,
|
||||
};
|
||||
|
||||
if ( cost !== undefined ) {
|
||||
shippingMethodExample.settings = {
|
||||
cost: cost,
|
||||
};
|
||||
}
|
||||
|
||||
return shippingMethodExample;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
getShippingMethodExample,
|
||||
};
|
|
@ -0,0 +1,24 @@
|
|||
/**
|
||||
* Default shipping zone object.
|
||||
*
|
||||
* For more details on shipping zone properties, see:
|
||||
*
|
||||
* https://woocommerce.github.io/woocommerce-rest-api-docs/#shipping-zone-properties
|
||||
*
|
||||
*/
|
||||
const shippingZone = {
|
||||
name: 'US Domestic',
|
||||
};
|
||||
|
||||
/**
|
||||
* Constructs a default shipping zone object.
|
||||
*
|
||||
* @returns default shipping zone
|
||||
*/
|
||||
const getShippingZoneExample = () => {
|
||||
return shippingZone;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
getShippingZoneExample,
|
||||
};
|
|
@ -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,
|
||||
};
|
|
@ -1,11 +1,19 @@
|
|||
const { ordersApi } = require('./orders');
|
||||
const { couponsApi } = require('./coupons');
|
||||
const { productsApi } = require('./products');
|
||||
const { refundsApi } = require('./refunds');
|
||||
const { ordersApi } = require( './orders' );
|
||||
const { couponsApi } = require( './coupons' );
|
||||
const { productsApi } = require( './products' );
|
||||
const { refundsApi } = require( './refunds' );
|
||||
const { taxRatesApi } = require( './tax-rates' );
|
||||
const { variationsApi } = require( './variations' );
|
||||
const { shippingZonesApi } = require( './shipping-zones' );
|
||||
const { shippingMethodsApi } = require( './shipping-methods' );
|
||||
|
||||
module.exports = {
|
||||
ordersApi,
|
||||
couponsApi,
|
||||
productsApi,
|
||||
refundsApi,
|
||||
taxRatesApi,
|
||||
variationsApi,
|
||||
shippingZonesApi,
|
||||
shippingMethodsApi,
|
||||
};
|
||||
|
|
|
@ -1,8 +1,13 @@
|
|||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
const { getRequest, postRequest, putRequest, deleteRequest } = require('../utils/request');
|
||||
const { getOrderExample, shared } = require('../data');
|
||||
const {
|
||||
getRequest,
|
||||
postRequest,
|
||||
putRequest,
|
||||
deleteRequest,
|
||||
} = require( '../utils/request' );
|
||||
const { getOrderExample, shared } = require( '../data' );
|
||||
|
||||
/**
|
||||
* WooCommerce Orders endpoints.
|
||||
|
@ -24,14 +29,16 @@ const ordersApi = {
|
|||
method: 'GET',
|
||||
path: 'orders/<id>',
|
||||
responseCode: 200,
|
||||
order: async ( orderId ) => getRequest( `orders/${orderId}` ),
|
||||
order: async ( orderId, ordersQuery = {} ) =>
|
||||
getRequest( `orders/${ orderId }`, ordersQuery ),
|
||||
},
|
||||
listAll: {
|
||||
name: 'List all orders',
|
||||
method: 'GET',
|
||||
path: 'orders',
|
||||
responseCode: 200,
|
||||
orders: async () => getRequest( 'orders' ),
|
||||
orders: async ( ordersQuery = {} ) =>
|
||||
getRequest( 'orders', ordersQuery ),
|
||||
},
|
||||
update: {
|
||||
name: 'Update an order',
|
||||
|
@ -39,7 +46,8 @@ const ordersApi = {
|
|||
path: 'orders/<id>',
|
||||
responseCode: 200,
|
||||
payload: getOrderExample(),
|
||||
order: async ( orderId, orderDetails ) => putRequest( `orders/${orderId}`, orderDetails ),
|
||||
order: async ( orderId, orderDetails ) =>
|
||||
putRequest( `orders/${ orderId }`, orderDetails ),
|
||||
},
|
||||
delete: {
|
||||
name: 'Delete an order',
|
||||
|
@ -47,9 +55,10 @@ const ordersApi = {
|
|||
path: 'orders/<id>',
|
||||
responseCode: 200,
|
||||
payload: {
|
||||
force: false
|
||||
force: false,
|
||||
},
|
||||
order: async ( orderId, deletePermanently ) => deleteRequest( `orders/${orderId}`, deletePermanently ),
|
||||
order: async ( orderId, deletePermanently ) =>
|
||||
deleteRequest( `orders/${ orderId }`, deletePermanently ),
|
||||
},
|
||||
batch: {
|
||||
name: 'Batch update orders',
|
||||
|
@ -57,7 +66,8 @@ const ordersApi = {
|
|||
path: 'orders/batch',
|
||||
responseCode: 200,
|
||||
payload: shared.getBatchPayloadExample( getOrderExample() ),
|
||||
orders: async ( batchUpdatePayload ) => postRequest( `orders/batch`, batchUpdatePayload ),
|
||||
orders: async ( batchUpdatePayload ) =>
|
||||
postRequest( `orders/batch`, batchUpdatePayload ),
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
const {
|
||||
const {
|
||||
getRequest,
|
||||
postRequest,
|
||||
deleteRequest,
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
const {
|
||||
getRequest,
|
||||
postRequest,
|
||||
putRequest,
|
||||
deleteRequest,
|
||||
} = require( '../utils/request' );
|
||||
|
||||
/**
|
||||
* WooCommerce Shipping method endpoints.
|
||||
*
|
||||
* https://woocommerce.github.io/woocommerce-rest-api-docs/#shipping-methods
|
||||
*/
|
||||
const shippingMethodsApi = {
|
||||
name: 'Shipping methods',
|
||||
create: {
|
||||
name: 'Include a shipping method to a shipping zone',
|
||||
method: 'POST',
|
||||
path: 'shipping/zones/<id>/methods',
|
||||
responseCode: 200,
|
||||
shippingMethod: async ( shippingZoneId, shippingMethod ) =>
|
||||
postRequest(
|
||||
`shipping/zones/${ shippingZoneId }/methods`,
|
||||
shippingMethod
|
||||
),
|
||||
},
|
||||
retrieve: {
|
||||
name: 'Retrieve a shipping method from a shipping zone',
|
||||
method: 'GET',
|
||||
path: 'shipping/zones/<zone_id>/methods/<id>',
|
||||
responseCode: 200,
|
||||
shippingMethod: async ( shippingZoneId, shippingMethodInstanceId ) =>
|
||||
getRequest(
|
||||
`shipping/zones/${ shippingZoneId }/methods/${ shippingMethodInstanceId }`
|
||||
),
|
||||
},
|
||||
listAll: {
|
||||
name: 'List all shipping methods from a shipping zone',
|
||||
method: 'GET',
|
||||
path: 'shipping/zones/<id>/methods',
|
||||
responseCode: 200,
|
||||
shippingMethods: async ( shippingZoneId, params = {} ) =>
|
||||
getRequest( `shipping/zones/${ shippingZoneId }/methods`, params ),
|
||||
},
|
||||
update: {
|
||||
name: 'Update a shipping method of a shipping zone',
|
||||
method: 'PUT',
|
||||
path: 'shipping/zones/<zone_id>/methods/<id>',
|
||||
responseCode: 200,
|
||||
shippingMethod: async (
|
||||
shippingZoneId,
|
||||
shippingMethodInstanceId,
|
||||
updatedShippingMethod
|
||||
) =>
|
||||
putRequest(
|
||||
`shipping/zones/${ shippingZoneId }/methods/${ shippingMethodInstanceId }`,
|
||||
updatedShippingMethod
|
||||
),
|
||||
},
|
||||
delete: {
|
||||
name: 'Delete a shipping method from a shipping zone',
|
||||
method: 'DELETE',
|
||||
path: 'shipping/zones/<zone_id>/methods/<id>>',
|
||||
responseCode: 200,
|
||||
payload: {
|
||||
force: false,
|
||||
},
|
||||
shippingMethod: async (
|
||||
shippingZoneId,
|
||||
shippingMethodInstanceId,
|
||||
deletePermanently
|
||||
) =>
|
||||
deleteRequest(
|
||||
`shipping/zones/${ shippingZoneId }/methods/${ shippingMethodInstanceId }`,
|
||||
deletePermanently
|
||||
),
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
shippingMethodsApi,
|
||||
};
|
|
@ -0,0 +1,71 @@
|
|||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
const {
|
||||
getRequest,
|
||||
postRequest,
|
||||
putRequest,
|
||||
deleteRequest,
|
||||
} = require( '../utils/request' );
|
||||
|
||||
/**
|
||||
* WooCommerce Shipping zone endpoints.
|
||||
*
|
||||
* https://woocommerce.github.io/woocommerce-rest-api-docs/#shipping-zones
|
||||
*/
|
||||
const shippingZonesApi = {
|
||||
name: 'Shipping zones',
|
||||
create: {
|
||||
name: 'Create a shipping zone',
|
||||
method: 'POST',
|
||||
path: 'shipping/zones',
|
||||
responseCode: 201,
|
||||
shippingZone: async ( shippingZone ) =>
|
||||
postRequest( `shipping/zones`, shippingZone ),
|
||||
},
|
||||
retrieve: {
|
||||
name: 'Retrieve a shipping zone',
|
||||
method: 'GET',
|
||||
path: 'shipping/zones/<id>',
|
||||
responseCode: 200,
|
||||
shippingZone: async ( shippingZoneId ) =>
|
||||
getRequest( `shipping/zones/${ shippingZoneId }` ),
|
||||
},
|
||||
listAll: {
|
||||
name: 'List all shipping zones',
|
||||
method: 'GET',
|
||||
path: 'shipping/zones',
|
||||
responseCode: 200,
|
||||
shippingZones: async ( params = {} ) =>
|
||||
getRequest( `shipping/zones`, params ),
|
||||
},
|
||||
update: {
|
||||
name: 'Update a shipping zone',
|
||||
method: 'PUT',
|
||||
path: 'shipping/zones/<id>',
|
||||
responseCode: 200,
|
||||
shippingZone: async ( shippingZoneId, updatedShippingZone ) =>
|
||||
putRequest(
|
||||
`shipping/zones/${ shippingZoneId }`,
|
||||
updatedShippingZone
|
||||
),
|
||||
},
|
||||
delete: {
|
||||
name: 'Delete a shipping zone',
|
||||
method: 'DELETE',
|
||||
path: 'shipping/zones/<id>',
|
||||
responseCode: 200,
|
||||
payload: {
|
||||
force: false,
|
||||
},
|
||||
shippingZone: async ( shippingZoneId, deletePermanently ) =>
|
||||
deleteRequest(
|
||||
`shipping/zones/${ shippingZoneId }`,
|
||||
deletePermanently
|
||||
),
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
shippingZonesApi,
|
||||
};
|
|
@ -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",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"preinstall": "npx only-allow pnpm",
|
||||
"test": "jest",
|
||||
"test:api": "jest --group=api",
|
||||
"test:hello": "jest --group=hello",
|
||||
|
@ -25,6 +26,9 @@
|
|||
"postman-collection": "^4.1.0",
|
||||
"supertest": "^6.1.4"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"bin": {
|
||||
"wc-api-tests": "bin/wc-api-tests.sh"
|
||||
}
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
{
|
||||
"root": "packages/js/api-core-tests/",
|
||||
"sourceRoot": "packages/js/api-core-tests",
|
||||
"projectType": "library",
|
||||
"targets": {
|
||||
"changelog": {
|
||||
"executor": "./tools/executors/changelogger:changelog",
|
||||
"options": {
|
||||
"action": "add",
|
||||
"cwd": "packages/js/api-core-tests"
|
||||
}
|
||||
},
|
||||
"test": {
|
||||
"executor": "@nrwl/workspace:run-script",
|
||||
"options": {
|
||||
"script": "test"
|
||||
}
|
||||
},
|
||||
"test-hello": {
|
||||
"executor": "@nrwl/workspace:run-script",
|
||||
"options": {
|
||||
"script": "test:hello"
|
||||
}
|
||||
},
|
||||
"make-collection": {
|
||||
"executor": "@nrwl/workspace:run-script",
|
||||
"options": {
|
||||
"script": "make:collection"
|
||||
}
|
||||
},
|
||||
"test-api": {
|
||||
"executor": "@nrwl/workspace:run-script",
|
||||
"options": {
|
||||
"script": "test:api"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,329 @@
|
|||
const { couponsApi } = require( '../../endpoints/coupons' );
|
||||
const { ordersApi } = require( '../../endpoints/orders' );
|
||||
const { coupon, order } = require( '../../data' );
|
||||
|
||||
/**
|
||||
* Tests for the WooCommerce Coupons API.
|
||||
*
|
||||
* @group api
|
||||
* @group coupons
|
||||
*
|
||||
*/
|
||||
describe( 'Coupons API tests', () => {
|
||||
let couponId;
|
||||
|
||||
it( 'can create a coupon', async () => {
|
||||
const testCoupon = {
|
||||
...coupon,
|
||||
code: `${ coupon.code }-${ Date.now() }`,
|
||||
};
|
||||
const response = await couponsApi.create.coupon( testCoupon );
|
||||
|
||||
expect( response.statusCode ).toEqual( couponsApi.create.responseCode );
|
||||
expect( response.body.id ).toBeDefined();
|
||||
couponId = response.body.id;
|
||||
|
||||
// Validate the created coupon object has the correct code, amount, and discount type
|
||||
expect( response.body ).toEqual(
|
||||
expect.objectContaining( {
|
||||
code: testCoupon.code,
|
||||
amount: Number( coupon.amount ).toFixed( 2 ),
|
||||
discount_type: coupon.discount_type,
|
||||
} )
|
||||
);
|
||||
} );
|
||||
|
||||
it( 'can retrieve a coupon', async () => {
|
||||
const response = await couponsApi.retrieve.coupon( couponId );
|
||||
expect( response.statusCode ).toEqual(
|
||||
couponsApi.retrieve.responseCode
|
||||
);
|
||||
expect( response.body.id ).toEqual( couponId );
|
||||
} );
|
||||
|
||||
it( 'can update a coupon', async () => {
|
||||
const updatedCouponDetails = {
|
||||
description: '10% off storewide',
|
||||
maximum_amount: '500.00',
|
||||
usage_limit_per_user: 1,
|
||||
free_shipping: true,
|
||||
};
|
||||
const response = await couponsApi.update.coupon(
|
||||
couponId,
|
||||
updatedCouponDetails
|
||||
);
|
||||
expect( response.statusCode ).toEqual( couponsApi.update.responseCode );
|
||||
expect( response.body ).toEqual(
|
||||
expect.objectContaining( updatedCouponDetails )
|
||||
);
|
||||
} );
|
||||
|
||||
it( 'can permanently delete a coupon', async () => {
|
||||
const response = await couponsApi.delete.coupon( couponId, true );
|
||||
|
||||
expect( response.statusCode ).toEqual( couponsApi.delete.responseCode );
|
||||
|
||||
const getCouponResponse = await couponsApi.retrieve.coupon( couponId );
|
||||
expect( getCouponResponse.statusCode ).toEqual( 404 );
|
||||
} );
|
||||
|
||||
describe( 'Batch update coupons', () => {
|
||||
/**
|
||||
* Coupons to be created, updated, and deleted.
|
||||
*/
|
||||
const expectedCoupons = [
|
||||
{
|
||||
code: `batchcoupon-${ Date.now() }`,
|
||||
discount_type: 'percent',
|
||||
amount: '10',
|
||||
free_shipping: false,
|
||||
},
|
||||
{
|
||||
code: `batchcoupon-${ Date.now() + 1 }`,
|
||||
discount_type: 'percent',
|
||||
amount: '20',
|
||||
},
|
||||
];
|
||||
|
||||
it( 'can batch create coupons', async () => {
|
||||
// Batch create 2 new coupons.
|
||||
const batchCreatePayload = {
|
||||
create: expectedCoupons,
|
||||
};
|
||||
const batchCreateResponse = await couponsApi.batch.coupons(
|
||||
batchCreatePayload
|
||||
);
|
||||
expect( batchCreateResponse.status ).toEqual(
|
||||
couponsApi.batch.responseCode
|
||||
);
|
||||
|
||||
// Verify that the 2 new coupons were created
|
||||
const actualCoupons = batchCreateResponse.body.create;
|
||||
expect( actualCoupons ).toHaveLength( expectedCoupons.length );
|
||||
for ( let i = 0; i < actualCoupons.length; i++ ) {
|
||||
const { id, code } = actualCoupons[ i ];
|
||||
const expectedCouponCode = expectedCoupons[ i ].code;
|
||||
|
||||
expect( id ).toBeDefined();
|
||||
expect( code ).toEqual( expectedCouponCode );
|
||||
|
||||
// Save the coupon id
|
||||
expectedCoupons[ i ].id = id;
|
||||
}
|
||||
} );
|
||||
|
||||
it( 'can batch update coupons', async () => {
|
||||
// Update the 1st coupon to free shipping.
|
||||
// Update the amount of the 2nd coupon to 25.
|
||||
const batchUpdatePayload = {
|
||||
update: [
|
||||
{
|
||||
id: expectedCoupons[ 0 ].id,
|
||||
free_shipping: true,
|
||||
},
|
||||
{
|
||||
id: expectedCoupons[ 1 ].id,
|
||||
amount: '25.00',
|
||||
},
|
||||
],
|
||||
};
|
||||
const batchUpdateResponse = await couponsApi.batch.coupons(
|
||||
batchUpdatePayload
|
||||
);
|
||||
|
||||
// Verify the response code and the number of coupons that were updated.
|
||||
const updatedCoupons = batchUpdateResponse.body.update;
|
||||
expect( batchUpdateResponse.status ).toEqual(
|
||||
couponsApi.batch.responseCode
|
||||
);
|
||||
expect( updatedCoupons ).toHaveLength( expectedCoupons.length );
|
||||
|
||||
// Verify that the 1st coupon was updated to free shipping.
|
||||
expect( updatedCoupons[ 0 ].id ).toEqual( expectedCoupons[ 0 ].id );
|
||||
expect( updatedCoupons[ 0 ].free_shipping ).toEqual( true );
|
||||
|
||||
// Verify that the amount of the 2nd coupon was updated to 25.
|
||||
expect( updatedCoupons[ 1 ].id ).toEqual( expectedCoupons[ 1 ].id );
|
||||
expect( updatedCoupons[ 1 ].amount ).toEqual( '25.00' );
|
||||
} );
|
||||
|
||||
it( 'can batch delete coupons', async () => {
|
||||
// Batch delete the 2 coupons.
|
||||
const couponIdsToDelete = expectedCoupons.map( ( { id } ) => id );
|
||||
const batchDeletePayload = {
|
||||
delete: couponIdsToDelete,
|
||||
};
|
||||
const batchDeleteResponse = await couponsApi.batch.coupons(
|
||||
batchDeletePayload
|
||||
);
|
||||
|
||||
// Verify that the response shows the 2 coupons.
|
||||
const deletedCouponIds = batchDeleteResponse.body.delete.map(
|
||||
( { id } ) => id
|
||||
);
|
||||
expect( batchDeleteResponse.status ).toEqual(
|
||||
couponsApi.batch.responseCode
|
||||
);
|
||||
expect( deletedCouponIds ).toEqual( couponIdsToDelete );
|
||||
|
||||
// Verify that the 2 deleted coupons cannot be retrieved.
|
||||
for ( const couponId of couponIdsToDelete ) {
|
||||
const { status } = await couponsApi.retrieve.coupon( couponId );
|
||||
|
||||
expect( status ).toEqual( 404 );
|
||||
}
|
||||
} );
|
||||
} );
|
||||
|
||||
describe( 'List coupons', () => {
|
||||
const allCoupons = [
|
||||
{
|
||||
...coupon,
|
||||
code: `listcoupons-01-${ Date.now() }`,
|
||||
description: `description-01-${ Date.now() }`,
|
||||
},
|
||||
{
|
||||
...coupon,
|
||||
code: `listcoupons-02-${ Date.now() }`,
|
||||
description: `description-02-${ Date.now() }`,
|
||||
},
|
||||
{
|
||||
...coupon,
|
||||
code: `listcoupons-03-${ Date.now() }`,
|
||||
description: `description-03-${ Date.now() }`,
|
||||
},
|
||||
];
|
||||
|
||||
beforeAll( async () => {
|
||||
// Create list of coupons for testing.
|
||||
const response = await couponsApi.batch.coupons( {
|
||||
create: allCoupons,
|
||||
} );
|
||||
const actualCreatedCoupons = response.body.create;
|
||||
|
||||
// Save their coupon ID's
|
||||
for ( const coupon of allCoupons ) {
|
||||
const { id } = actualCreatedCoupons.find(
|
||||
( { code } ) => coupon.code === code
|
||||
);
|
||||
|
||||
coupon.id = id;
|
||||
}
|
||||
} );
|
||||
|
||||
afterAll( async () => {
|
||||
// Clean up created coupons
|
||||
const batchDeletePayload = {
|
||||
delete: allCoupons.map( ( { id } ) => id ),
|
||||
};
|
||||
await couponsApi.batch.coupons( batchDeletePayload );
|
||||
} );
|
||||
|
||||
it( 'can list all coupons by default', async () => {
|
||||
const response = await couponsApi.listAll.coupons();
|
||||
const listedCoupons = response.body;
|
||||
const actualCouponIdsList = listedCoupons.map( ( { id } ) => id );
|
||||
const expectedCouponIdsList = allCoupons.map( ( { id } ) => id );
|
||||
|
||||
expect( response.status ).toEqual(
|
||||
couponsApi.listAll.responseCode
|
||||
);
|
||||
expect( actualCouponIdsList ).toEqual(
|
||||
expect.arrayContaining( expectedCouponIdsList )
|
||||
);
|
||||
} );
|
||||
|
||||
it( 'can limit result set to matching code', async () => {
|
||||
const matchingCoupon = allCoupons[ 1 ];
|
||||
const payload = { code: matchingCoupon.code };
|
||||
const { status, body } = await couponsApi.listAll.coupons(
|
||||
payload
|
||||
);
|
||||
|
||||
expect( status ).toEqual( couponsApi.listAll.responseCode );
|
||||
expect( body ).toHaveLength( 1 );
|
||||
expect( body[ 0 ].id ).toEqual( matchingCoupon.id );
|
||||
} );
|
||||
|
||||
it( 'can paginate results', async () => {
|
||||
const pageSize = 2;
|
||||
const payload = {
|
||||
page: 1,
|
||||
per_page: pageSize,
|
||||
};
|
||||
const { status, body } = await couponsApi.listAll.coupons(
|
||||
payload
|
||||
);
|
||||
|
||||
expect( status ).toEqual( couponsApi.listAll.responseCode );
|
||||
expect( body ).toHaveLength( pageSize );
|
||||
} );
|
||||
|
||||
it( 'can limit results to matching string', async () => {
|
||||
// Search by description
|
||||
const matchingCoupon = allCoupons[ 2 ];
|
||||
const matchingString = matchingCoupon.description;
|
||||
const payload = {
|
||||
search: matchingString,
|
||||
};
|
||||
|
||||
const { status, body } = await couponsApi.listAll.coupons(
|
||||
payload
|
||||
);
|
||||
|
||||
expect( status ).toEqual( couponsApi.listAll.responseCode );
|
||||
expect( body ).toHaveLength( 1 );
|
||||
expect( body[ 0 ].id ).toEqual( matchingCoupon.id );
|
||||
} );
|
||||
} );
|
||||
|
||||
describe( 'Add coupon to order', () => {
|
||||
const testCoupon = {
|
||||
code: `coupon-${ Date.now() }`,
|
||||
discount_type: 'percent',
|
||||
amount: '10',
|
||||
};
|
||||
let orderId;
|
||||
|
||||
beforeAll( async () => {
|
||||
// Create a coupon
|
||||
const createCouponResponse = await couponsApi.create.coupon(
|
||||
testCoupon
|
||||
);
|
||||
testCoupon.id = createCouponResponse.body.id;
|
||||
} );
|
||||
|
||||
// Clean up created coupon and order
|
||||
afterAll( async () => {
|
||||
await couponsApi.delete.coupon( testCoupon.id, true );
|
||||
await ordersApi.delete.order( orderId, true );
|
||||
} );
|
||||
|
||||
it( 'can add coupon to an order', async () => {
|
||||
const orderWithCoupon = {
|
||||
...order,
|
||||
coupon_lines: [ { code: testCoupon.code } ],
|
||||
};
|
||||
const { status, body } = await ordersApi.create.order(
|
||||
orderWithCoupon
|
||||
);
|
||||
orderId = body.id;
|
||||
|
||||
expect( status ).toEqual( ordersApi.create.responseCode );
|
||||
expect( body.coupon_lines ).toHaveLength( 1 );
|
||||
expect( body.coupon_lines[ 0 ].code ).toEqual( testCoupon.code );
|
||||
// Test that the coupon meta data exists.
|
||||
// See: https://github.com/woocommerce/woocommerce/issues/28166.
|
||||
expect( body.coupon_lines[ 0 ].meta_data ).toEqual(
|
||||
expect.arrayContaining( [
|
||||
expect.objectContaining( {
|
||||
key: 'coupon_data',
|
||||
value: expect.objectContaining( {
|
||||
code: testCoupon.code,
|
||||
} ),
|
||||
} ),
|
||||
] )
|
||||
);
|
||||
} );
|
||||
} );
|
||||
} );
|
|
@ -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 );
|
||||
}
|
||||
} );
|
||||
} );
|
|
@ -0,0 +1,86 @@
|
|||
const { ordersApi } = require( '../../endpoints' );
|
||||
const { getOrderExample, shared } = require( '../../data' );
|
||||
|
||||
/**
|
||||
* Order to be searched
|
||||
*/
|
||||
const order = {
|
||||
...getOrderExample(),
|
||||
shipping: {
|
||||
...shared.customerShipping,
|
||||
company: 'Murphy LLC',
|
||||
phone: '6146524353',
|
||||
},
|
||||
shipping_lines: [],
|
||||
fee_lines: [],
|
||||
coupon_lines: [],
|
||||
};
|
||||
|
||||
/**
|
||||
* Search parameters to be used.
|
||||
* The following scenarios are not covered in this test suite because they're already covered in the `List all orders > search` test in `orders.test.js`
|
||||
* ```
|
||||
* can search by billing address 1
|
||||
* can search by shipping address 1
|
||||
* can search by billing last name
|
||||
* can search by billing email
|
||||
* can search by item name
|
||||
* ```
|
||||
*/
|
||||
const searchParams = [
|
||||
[ 'orderId', 'orderId' ],
|
||||
[ 'billing first name', order.billing.first_name ],
|
||||
[ 'billing company name', order.billing.company ],
|
||||
[ 'billing address 2', order.billing.address_2 ],
|
||||
[ 'billing city name', order.billing.city ],
|
||||
[ 'billing post code', order.billing.postcode ],
|
||||
[ 'billing phone', order.billing.phone ],
|
||||
[ 'billing state', order.billing.state ],
|
||||
[ 'shipping first name', order.shipping.first_name ],
|
||||
[ 'shipping last name', order.shipping.last_name ],
|
||||
[ 'shipping address 2', order.shipping.address_2 ],
|
||||
[ 'shipping city', order.shipping.city ],
|
||||
[ 'shipping post code', order.shipping.postcode ],
|
||||
[ 'shipping state', order.shipping.state ],
|
||||
];
|
||||
|
||||
/**
|
||||
* Tests for the WooCommerce Order Search API.
|
||||
*
|
||||
* @group api
|
||||
* @group orders
|
||||
*
|
||||
*/
|
||||
describe( 'Order Search API tests', () => {
|
||||
beforeAll( async () => {
|
||||
// Create an order and save its ID
|
||||
const { body } = await ordersApi.create.order( order );
|
||||
order.id = body.id;
|
||||
} );
|
||||
|
||||
afterAll( async () => {
|
||||
// Cleanup: Delete the order
|
||||
await ordersApi.delete.order( order.id, true );
|
||||
} );
|
||||
|
||||
it.each( searchParams )( 'can search by %s', async ( title, param ) => {
|
||||
const searchValue = param === 'orderId' ? order.id : param;
|
||||
|
||||
const { status, body } = await ordersApi.listAll.orders( {
|
||||
search: searchValue,
|
||||
} );
|
||||
|
||||
expect( status ).toEqual( ordersApi.listAll.responseCode );
|
||||
expect( body ).toHaveLength( 1 );
|
||||
expect( body[ 0 ].id ).toEqual( order.id );
|
||||
} );
|
||||
|
||||
it( 'can return an empty result set when no matches were found', async () => {
|
||||
const { status, body } = await ordersApi.listAll.orders( {
|
||||
search: 'Chauncey Smith Kunde',
|
||||
} );
|
||||
|
||||
expect( status ).toEqual( ordersApi.listAll.responseCode );
|
||||
expect( body ).toEqual( [] );
|
||||
} );
|
||||
} );
|
|
@ -0,0 +1,215 @@
|
|||
const { ordersApi, productsApi } = require( '../../endpoints' );
|
||||
const { order } = require( '../../data' );
|
||||
|
||||
/**
|
||||
* Billing properties to update.
|
||||
*/
|
||||
const updatedCustomerBilling = {
|
||||
first_name: 'Jane',
|
||||
last_name: 'Doe',
|
||||
company: 'Automattic',
|
||||
country: 'US',
|
||||
address_1: '123 Market Street',
|
||||
address_2: 'Suite 500',
|
||||
city: 'Austin',
|
||||
state: 'TX',
|
||||
postcode: '73301',
|
||||
phone: '123456789',
|
||||
email: 'jane.doe@example.com',
|
||||
};
|
||||
|
||||
/**
|
||||
* Shipping properties to update.
|
||||
*/
|
||||
const updatedCustomerShipping = {
|
||||
first_name: 'Mike',
|
||||
last_name: 'Anderson',
|
||||
company: 'Automattic',
|
||||
country: 'US',
|
||||
address_1: '123 Ocean Ave',
|
||||
address_2: '',
|
||||
city: 'New York',
|
||||
state: 'NY',
|
||||
postcode: '10013',
|
||||
phone: '123456789',
|
||||
};
|
||||
|
||||
/**
|
||||
* Data tables to be used for testing the 'Create an order' API.
|
||||
*/
|
||||
const statusesDataTable = [
|
||||
'pending',
|
||||
'processing',
|
||||
'on-hold',
|
||||
'completed',
|
||||
'cancelled',
|
||||
'refunded',
|
||||
'failed',
|
||||
];
|
||||
|
||||
/**
|
||||
* A simple product that will be added to an order.
|
||||
*/
|
||||
const simpleProduct = {
|
||||
name: 'Incredible Plastic Table',
|
||||
regular_price: '48',
|
||||
};
|
||||
|
||||
/**
|
||||
* Tests for the WooCommerce Orders API.
|
||||
*
|
||||
* @group api
|
||||
* @group orders
|
||||
*
|
||||
*/
|
||||
describe( 'Orders API tests: CRUD', () => {
|
||||
let orderId;
|
||||
|
||||
describe( 'Create an order', () => {
|
||||
it( 'can create a pending order by default', async () => {
|
||||
// Create an order that has a null status
|
||||
const requestPayload = {
|
||||
...order,
|
||||
status: null,
|
||||
};
|
||||
const { body, status } = await ordersApi.create.order(
|
||||
requestPayload
|
||||
);
|
||||
// Save the order ID. It will be used by the retrieve, update, and delete tests.
|
||||
orderId = body.id;
|
||||
|
||||
// Verify that the order status is 'pending'
|
||||
expect( status ).toEqual( ordersApi.create.responseCode );
|
||||
expect( typeof body.id ).toEqual( 'number' );
|
||||
expect( body.status ).toEqual( 'pending' );
|
||||
} );
|
||||
|
||||
it.each( statusesDataTable )(
|
||||
"can create an order with status '%s'",
|
||||
async ( expectedStatus ) => {
|
||||
const requestPayload = {
|
||||
...order,
|
||||
status: expectedStatus,
|
||||
};
|
||||
const { status, body } = await ordersApi.create.order(
|
||||
requestPayload
|
||||
);
|
||||
|
||||
expect( status ).toEqual( ordersApi.create.responseCode );
|
||||
expect( typeof body.id ).toEqual( 'number' );
|
||||
expect( body.status ).toEqual( expectedStatus );
|
||||
|
||||
// Cleanup: Delete this order
|
||||
await ordersApi.delete.order( body.id, true );
|
||||
}
|
||||
);
|
||||
} );
|
||||
|
||||
describe( 'Retrieve an order', () => {
|
||||
it( 'can retrieve an order', async () => {
|
||||
const response = await ordersApi.retrieve.order( orderId );
|
||||
expect( response.status ).toEqual(
|
||||
ordersApi.retrieve.responseCode
|
||||
);
|
||||
expect( response.body.id ).toEqual( orderId );
|
||||
} );
|
||||
} );
|
||||
|
||||
describe( 'Update an order', () => {
|
||||
beforeAll( async () => {
|
||||
// Create the product and save its id
|
||||
const { body } = await productsApi.create.product( simpleProduct );
|
||||
simpleProduct.id = body.id;
|
||||
} );
|
||||
|
||||
afterAll( async () => {
|
||||
// Delete the created product
|
||||
await productsApi.delete.product( simpleProduct.id, true );
|
||||
} );
|
||||
|
||||
it.each( statusesDataTable )(
|
||||
"can update status of an order to '%s'",
|
||||
async ( expectedOrderStatus ) => {
|
||||
const requestPayload = {
|
||||
status: expectedOrderStatus,
|
||||
};
|
||||
const { status, body } = await ordersApi.update.order(
|
||||
orderId,
|
||||
requestPayload
|
||||
);
|
||||
|
||||
expect( status ).toEqual( ordersApi.update.responseCode );
|
||||
expect( body.id ).toEqual( orderId );
|
||||
expect( body.status ).toEqual( expectedOrderStatus );
|
||||
}
|
||||
);
|
||||
|
||||
it( 'can add shipping and billing contacts to an order', async () => {
|
||||
// Update the billing and shipping fields on the order
|
||||
order.billing = updatedCustomerBilling;
|
||||
order.shipping = updatedCustomerShipping;
|
||||
|
||||
const response = await ordersApi.update.order( orderId, order );
|
||||
expect( response.status ).toEqual( ordersApi.update.responseCode );
|
||||
|
||||
expect( response.body.billing ).toEqual( updatedCustomerBilling );
|
||||
expect( response.body.shipping ).toEqual( updatedCustomerShipping );
|
||||
} );
|
||||
|
||||
it( 'can add a product to an order', async () => {
|
||||
// Add the product to the order
|
||||
const requestPayload = {
|
||||
line_items: [ { product_id: simpleProduct.id } ],
|
||||
};
|
||||
const { body, status } = await ordersApi.update.order(
|
||||
orderId,
|
||||
requestPayload
|
||||
);
|
||||
|
||||
// Verify that the added product has the correct values
|
||||
expect( status ).toEqual( ordersApi.update.responseCode );
|
||||
expect( body.line_items ).toHaveLength( 1 );
|
||||
expect( body.line_items[ 0 ].product_id ).toEqual(
|
||||
simpleProduct.id
|
||||
);
|
||||
expect( body.line_items[ 0 ].name ).toEqual( simpleProduct.name );
|
||||
} );
|
||||
|
||||
it( 'can pay for an order', async () => {
|
||||
// Setup: Set order status to 'pending'
|
||||
await ordersApi.update.order( orderId, {
|
||||
status: 'pending',
|
||||
} );
|
||||
|
||||
// Pay for the order by setting `set_paid` to true
|
||||
const updateRequestPayload = {
|
||||
set_paid: true,
|
||||
};
|
||||
const { status, body } = await ordersApi.update.order(
|
||||
orderId,
|
||||
updateRequestPayload
|
||||
);
|
||||
expect( status ).toEqual( ordersApi.update.responseCode );
|
||||
expect( body.id ).toEqual( orderId );
|
||||
|
||||
// Validate that the status of the order was automatically set to 'processing'
|
||||
expect( body.status ).toEqual( 'processing' );
|
||||
|
||||
// Validate that the date_paid and date_paid_gmt properties are no longer null
|
||||
expect( body.date_paid ).not.toBeNull();
|
||||
expect( body.date_paid_gmt ).not.toBeNull();
|
||||
} );
|
||||
} );
|
||||
|
||||
describe( 'Delete an order', () => {
|
||||
it( 'can permanently delete an order', async () => {
|
||||
// Delete the order.
|
||||
const response = await ordersApi.delete.order( orderId, true );
|
||||
expect( response.status ).toEqual( ordersApi.delete.responseCode );
|
||||
|
||||
// Verify that the order can no longer be retrieved.
|
||||
const getOrderResponse = await ordersApi.retrieve.order( orderId );
|
||||
expect( getOrderResponse.status ).toEqual( 404 );
|
||||
} );
|
||||
} );
|
||||
} );
|
|
@ -1,10 +1,11 @@
|
|||
const { ordersApi } = require('../../endpoints/orders');
|
||||
const { order } = require('../../data');
|
||||
const { ordersApi } = require( '../../endpoints/orders' );
|
||||
const { order } = require( '../../data' );
|
||||
const { createSampleData, deleteSampleData } = require( '../../data/orders' );
|
||||
|
||||
/**
|
||||
* Billing properties to update.
|
||||
*/
|
||||
const updatedCustomerBilling = {
|
||||
const updatedCustomerBilling = {
|
||||
first_name: 'Jane',
|
||||
last_name: 'Doe',
|
||||
company: 'Automattic',
|
||||
|
@ -41,43 +42,523 @@ const updatedCustomerShipping = {
|
|||
* @group orders
|
||||
*
|
||||
*/
|
||||
describe('Orders API tests', () => {
|
||||
let orderId;
|
||||
describe( 'Orders API tests', () => {
|
||||
let orderId, sampleData;
|
||||
|
||||
it('can create an order', async () => {
|
||||
beforeAll( async () => {
|
||||
sampleData = await createSampleData();
|
||||
}, 100000 );
|
||||
|
||||
afterAll( async () => {
|
||||
await deleteSampleData( sampleData );
|
||||
}, 10000 );
|
||||
|
||||
it( 'can create an order', async () => {
|
||||
const response = await ordersApi.create.order( order );
|
||||
expect( response.status ).toEqual( ordersApi.create.responseCode );
|
||||
expect( response.body.id ).toBeDefined();
|
||||
orderId = response.body.id;
|
||||
|
||||
// Validate the data type and verify the order is in a pending state
|
||||
expect( typeof response.body.status ).toBe('string');
|
||||
expect( response.body.status ).toEqual('pending');
|
||||
});
|
||||
expect( typeof response.body.status ).toBe( 'string' );
|
||||
expect( response.body.status ).toEqual( 'pending' );
|
||||
} );
|
||||
|
||||
it('can retrieve an order', async () => {
|
||||
it( 'can retrieve an order', async () => {
|
||||
const response = await ordersApi.retrieve.order( orderId );
|
||||
expect( response.status ).toEqual( ordersApi.retrieve.responseCode );
|
||||
expect( response.body.id ).toEqual( orderId );
|
||||
});
|
||||
} );
|
||||
|
||||
it('can add shipping and billing contacts to an order', async () => {
|
||||
it( 'can add shipping and billing contacts to an order', async () => {
|
||||
// Update the billing and shipping fields on the order
|
||||
order.billing = updatedCustomerBilling;
|
||||
order.shipping = updatedCustomerShipping;
|
||||
|
||||
const response = await ordersApi.update.order( orderId, order );
|
||||
expect( response.status).toEqual( ordersApi.update.responseCode );
|
||||
expect( response.status ).toEqual( ordersApi.update.responseCode );
|
||||
|
||||
expect( response.body.billing ).toEqual( updatedCustomerBilling );
|
||||
expect( response.body.shipping ).toEqual( updatedCustomerShipping );
|
||||
});
|
||||
} );
|
||||
|
||||
it('can permanently delete an order', async () => {
|
||||
it( 'can permanently delete an order', async () => {
|
||||
const response = await ordersApi.delete.order( orderId, true );
|
||||
expect( response.status ).toEqual( ordersApi.delete.responseCode );
|
||||
|
||||
const getOrderResponse = await ordersApi.retrieve.order( orderId );
|
||||
expect( getOrderResponse.status ).toEqual( 404 );
|
||||
});
|
||||
});
|
||||
} );
|
||||
|
||||
describe( 'List all orders', () => {
|
||||
const ORDERS_COUNT = 10;
|
||||
|
||||
it( 'pagination', async () => {
|
||||
const pageSize = 4;
|
||||
const page1 = await ordersApi.listAll.orders( {
|
||||
per_page: pageSize,
|
||||
} );
|
||||
const page2 = await ordersApi.listAll.orders( {
|
||||
per_page: pageSize,
|
||||
page: 2,
|
||||
} );
|
||||
expect( page1.statusCode ).toEqual( 200 );
|
||||
expect( page2.statusCode ).toEqual( 200 );
|
||||
|
||||
// Verify total page count.
|
||||
expect( page1.headers[ 'x-wp-total' ] ).toEqual(
|
||||
ORDERS_COUNT.toString()
|
||||
);
|
||||
expect( page1.headers[ 'x-wp-totalpages' ] ).toEqual( '3' );
|
||||
|
||||
// Verify we get pageSize'd arrays.
|
||||
expect( Array.isArray( page1.body ) ).toBe( true );
|
||||
expect( Array.isArray( page2.body ) ).toBe( true );
|
||||
expect( page1.body ).toHaveLength( pageSize );
|
||||
expect( page2.body ).toHaveLength( pageSize );
|
||||
|
||||
// Ensure all of the order IDs are unique (no page overlap).
|
||||
const allOrderIds = page1.body
|
||||
.concat( page2.body )
|
||||
.reduce( ( acc, { id } ) => {
|
||||
acc[ id ] = 1;
|
||||
return acc;
|
||||
}, {} );
|
||||
expect( Object.keys( allOrderIds ) ).toHaveLength( pageSize * 2 );
|
||||
|
||||
// Verify that offset takes precedent over page number.
|
||||
const page2Offset = await ordersApi.listAll.orders( {
|
||||
per_page: pageSize,
|
||||
page: 2,
|
||||
offset: pageSize + 1,
|
||||
} );
|
||||
// The offset pushes the result set 1 order past the start of page 2.
|
||||
expect( page2Offset.body ).toEqual(
|
||||
expect.not.arrayContaining( [
|
||||
expect.objectContaining( { id: page2.body[ 0 ].id } ),
|
||||
] )
|
||||
);
|
||||
expect( page2Offset.body[ 0 ].id ).toEqual( page2.body[ 1 ].id );
|
||||
|
||||
// Verify the last page only has 1 order as we expect.
|
||||
const lastPage = await ordersApi.listAll.orders( {
|
||||
per_page: pageSize,
|
||||
page: 3,
|
||||
} );
|
||||
expect( Array.isArray( lastPage.body ) ).toBe( true );
|
||||
expect( lastPage.body ).toHaveLength( 2 );
|
||||
|
||||
// Verify a page outside the total page count is empty.
|
||||
const page6 = await ordersApi.listAll.orders( {
|
||||
per_page: pageSize,
|
||||
page: 6,
|
||||
} );
|
||||
expect( Array.isArray( page6.body ) ).toBe( true );
|
||||
expect( page6.body ).toHaveLength( 0 );
|
||||
} );
|
||||
|
||||
it( 'inclusion / exclusion', async () => {
|
||||
const allOrders = await ordersApi.listAll.orders( {
|
||||
per_page: 10,
|
||||
} );
|
||||
expect( allOrders.statusCode ).toEqual( 200 );
|
||||
const allOrdersIds = allOrders.body.map( ( order ) => order.id );
|
||||
expect( allOrdersIds ).toHaveLength( ORDERS_COUNT );
|
||||
|
||||
const ordersToFilter = [
|
||||
allOrdersIds[ 0 ],
|
||||
allOrdersIds[ 2 ],
|
||||
allOrdersIds[ 4 ],
|
||||
allOrdersIds[ 7 ],
|
||||
];
|
||||
|
||||
const included = await ordersApi.listAll.orders( {
|
||||
per_page: 20,
|
||||
include: ordersToFilter.join( ',' ),
|
||||
} );
|
||||
expect( included.statusCode ).toEqual( 200 );
|
||||
expect( included.body ).toHaveLength( ordersToFilter.length );
|
||||
expect( included.body ).toEqual(
|
||||
expect.arrayContaining(
|
||||
ordersToFilter.map( ( id ) =>
|
||||
expect.objectContaining( { id } )
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
const excluded = await ordersApi.listAll.orders( {
|
||||
per_page: 20,
|
||||
exclude: ordersToFilter.join( ',' ),
|
||||
} );
|
||||
expect( excluded.statusCode ).toEqual( 200 );
|
||||
expect( excluded.body ).toHaveLength(
|
||||
ORDERS_COUNT - ordersToFilter.length
|
||||
);
|
||||
expect( excluded.body ).toEqual(
|
||||
expect.not.arrayContaining(
|
||||
ordersToFilter.map( ( id ) =>
|
||||
expect.objectContaining( { id } )
|
||||
)
|
||||
)
|
||||
);
|
||||
} );
|
||||
|
||||
it( 'parent', async () => {
|
||||
const result1 = await ordersApi.listAll.orders( {
|
||||
parent: sampleData.hierarchicalOrders.parent.id,
|
||||
} );
|
||||
expect( result1.statusCode ).toEqual( 200 );
|
||||
expect( result1.body ).toHaveLength( 1 );
|
||||
expect( result1.body[ 0 ].id ).toBe(
|
||||
sampleData.hierarchicalOrders.child.id
|
||||
);
|
||||
|
||||
const result2 = await ordersApi.listAll.orders( {
|
||||
parent_exclude: sampleData.hierarchicalOrders.parent.id,
|
||||
} );
|
||||
expect( result2.statusCode ).toEqual( 200 );
|
||||
expect( result2.body ).toEqual(
|
||||
expect.not.arrayContaining( [
|
||||
expect.objectContaining( {
|
||||
id: sampleData.hierarchicalOrders.child.id,
|
||||
} ),
|
||||
] )
|
||||
);
|
||||
} );
|
||||
|
||||
it( 'status', async () => {
|
||||
const result1 = await ordersApi.listAll.orders( {
|
||||
status: 'completed',
|
||||
} );
|
||||
|
||||
expect( result1.statusCode ).toEqual( 200 );
|
||||
expect( result1.body ).toHaveLength( 2 );
|
||||
expect( result1.body ).toEqual(
|
||||
expect.arrayContaining( [
|
||||
expect.objectContaining( {
|
||||
status: 'completed',
|
||||
customer_id: 0,
|
||||
line_items: expect.arrayContaining( [
|
||||
expect.objectContaining( {
|
||||
name: 'Single',
|
||||
quantity: 2,
|
||||
} ),
|
||||
expect.objectContaining( {
|
||||
name: 'Beanie with Logo',
|
||||
quantity: 3,
|
||||
} ),
|
||||
expect.objectContaining( {
|
||||
name: 'T-Shirt',
|
||||
quantity: 1,
|
||||
} ),
|
||||
] ),
|
||||
} ),
|
||||
expect.objectContaining( {
|
||||
status: 'completed',
|
||||
customer_id: sampleData.customers.tina.id,
|
||||
line_items: expect.arrayContaining( [
|
||||
expect.objectContaining( {
|
||||
name: 'Sunglasses',
|
||||
quantity: 1,
|
||||
} ),
|
||||
] ),
|
||||
} ),
|
||||
] )
|
||||
);
|
||||
|
||||
const result2 = await ordersApi.listAll.orders( {
|
||||
status: 'processing',
|
||||
} );
|
||||
expect( result2.statusCode ).toEqual( 200 );
|
||||
expect( result2.body ).toHaveLength( 8 );
|
||||
expect( result2.body ).toEqual(
|
||||
expect.not.arrayContaining(
|
||||
result1.body.map( ( { id } ) =>
|
||||
expect.objectContaining( { id } )
|
||||
)
|
||||
)
|
||||
);
|
||||
} );
|
||||
|
||||
it( 'customer', async () => {
|
||||
const result1 = await ordersApi.listAll.orders( {
|
||||
customer: sampleData.customers.john.id,
|
||||
} );
|
||||
expect( result1.statusCode ).toEqual( 200 );
|
||||
expect( result1.body ).toHaveLength( 5 );
|
||||
result1.body.forEach( ( order ) =>
|
||||
expect( order ).toEqual(
|
||||
expect.objectContaining( {
|
||||
customer_id: sampleData.customers.john.id,
|
||||
} )
|
||||
)
|
||||
);
|
||||
|
||||
const result2 = await ordersApi.listAll.orders( {
|
||||
customer: 0,
|
||||
} );
|
||||
expect( result2.statusCode ).toEqual( 200 );
|
||||
expect( result2.body ).toHaveLength( 3 );
|
||||
result2.body.forEach( ( order ) =>
|
||||
expect( order ).toEqual(
|
||||
expect.objectContaining( {
|
||||
customer_id: 0,
|
||||
} )
|
||||
)
|
||||
);
|
||||
} );
|
||||
|
||||
it( 'product', async () => {
|
||||
const beanie = sampleData.testProductData.simpleProducts.find(
|
||||
( p ) => p.name === 'Beanie'
|
||||
);
|
||||
const result1 = await ordersApi.listAll.orders( {
|
||||
product: beanie.id,
|
||||
} );
|
||||
expect( result1.statusCode ).toEqual( 200 );
|
||||
expect( result1.body ).toHaveLength( 2 );
|
||||
result1.body.forEach( ( order ) =>
|
||||
expect( order ).toEqual(
|
||||
expect.objectContaining( {
|
||||
line_items: expect.arrayContaining( [
|
||||
expect.objectContaining( {
|
||||
name: 'Beanie',
|
||||
} ),
|
||||
] ),
|
||||
} )
|
||||
)
|
||||
);
|
||||
} );
|
||||
|
||||
// NOTE: This does not verify the `taxes` array nested in line items.
|
||||
// While the precision parameter doesn't affect those values, after some
|
||||
// discussion it seems `dp` may not be supported in v4 of the API.
|
||||
it( 'dp (precision)', async () => {
|
||||
const expectPrecisionToMatch = ( value, dp ) => {
|
||||
expect( value ).toEqual(
|
||||
Number.parseFloat( value ).toFixed( dp )
|
||||
);
|
||||
};
|
||||
|
||||
const verifyOrderPrecision = ( order, dp ) => {
|
||||
expectPrecisionToMatch( order[ 'discount_total' ], dp );
|
||||
expectPrecisionToMatch( order[ 'discount_tax' ], dp );
|
||||
expectPrecisionToMatch( order[ 'shipping_total' ], dp );
|
||||
expectPrecisionToMatch( order[ 'shipping_tax' ], dp );
|
||||
expectPrecisionToMatch( order[ 'cart_tax' ], dp );
|
||||
expectPrecisionToMatch( order[ 'total' ], dp );
|
||||
expectPrecisionToMatch( order[ 'total_tax' ], dp );
|
||||
|
||||
order[ 'line_items' ].forEach( ( lineItem ) => {
|
||||
expectPrecisionToMatch( lineItem[ 'total' ], dp );
|
||||
expectPrecisionToMatch( lineItem[ 'total_tax' ], dp );
|
||||
} );
|
||||
|
||||
order[ 'tax_lines' ].forEach( ( taxLine ) => {
|
||||
expectPrecisionToMatch( taxLine[ 'tax_total' ], dp );
|
||||
expectPrecisionToMatch(
|
||||
taxLine[ 'shipping_tax_total' ],
|
||||
dp
|
||||
);
|
||||
} );
|
||||
|
||||
order[ 'shipping_lines' ].forEach( ( shippingLine ) => {
|
||||
expectPrecisionToMatch( shippingLine[ 'total' ], dp );
|
||||
expectPrecisionToMatch( shippingLine[ 'total_tax' ], dp );
|
||||
} );
|
||||
|
||||
order[ 'fee_lines' ].forEach( ( feeLine ) => {
|
||||
expectPrecisionToMatch( feeLine[ 'total' ], dp );
|
||||
expectPrecisionToMatch( feeLine[ 'total_tax' ], dp );
|
||||
} );
|
||||
|
||||
order[ 'refunds' ].forEach( ( refund ) => {
|
||||
expectPrecisionToMatch( refund[ 'total' ], dp );
|
||||
} );
|
||||
};
|
||||
|
||||
const result1 = await ordersApi.retrieve.order(
|
||||
sampleData.precisionOrder.id,
|
||||
{
|
||||
dp: 1,
|
||||
}
|
||||
);
|
||||
expect( result1.statusCode ).toEqual( 200 );
|
||||
verifyOrderPrecision( result1.body, 1 );
|
||||
|
||||
const result2 = await ordersApi.retrieve.order(
|
||||
sampleData.precisionOrder.id,
|
||||
{
|
||||
dp: 3,
|
||||
}
|
||||
);
|
||||
expect( result2.statusCode ).toEqual( 200 );
|
||||
verifyOrderPrecision( result2.body, 3 );
|
||||
|
||||
const result3 = await ordersApi.retrieve.order(
|
||||
sampleData.precisionOrder.id
|
||||
);
|
||||
expect( result3.statusCode ).toEqual( 200 );
|
||||
verifyOrderPrecision( result3.body, 2 ); // The default value for 'dp' is 2.
|
||||
} );
|
||||
|
||||
it( 'search', async () => {
|
||||
// By default, 'search' looks in:
|
||||
// - _billing_address_index
|
||||
// - _shipping_address_index
|
||||
// - _billing_last_name
|
||||
// - _billing_email
|
||||
// - order_item_name
|
||||
|
||||
// Test billing email.
|
||||
const result1 = await ordersApi.listAll.orders( {
|
||||
search: 'example.com',
|
||||
} );
|
||||
expect( result1.statusCode ).toEqual( 200 );
|
||||
expect( result1.body ).toHaveLength( 7 );
|
||||
result1.body.forEach( ( order ) =>
|
||||
expect( order.billing.email ).toContain( 'example.com' )
|
||||
);
|
||||
|
||||
// Test billing address.
|
||||
const result2 = await ordersApi.listAll.orders( {
|
||||
search: 'gainesville', // Intentionally lowercase.
|
||||
} );
|
||||
expect( result2.statusCode ).toEqual( 200 );
|
||||
expect( result2.body ).toHaveLength( 1 );
|
||||
expect( result2.body[ 0 ].id ).toEqual( sampleData.guestOrder.id );
|
||||
|
||||
// Test shipping address.
|
||||
const result3 = await ordersApi.listAll.orders( {
|
||||
search: 'Incognito',
|
||||
} );
|
||||
expect( result3.statusCode ).toEqual( 200 );
|
||||
expect( result3.body ).toHaveLength( 1 );
|
||||
expect( result3.body[ 0 ].id ).toEqual( sampleData.guestOrder.id );
|
||||
|
||||
// Test billing last name.
|
||||
const result4 = await ordersApi.listAll.orders( {
|
||||
search: 'Doe',
|
||||
} );
|
||||
expect( result4.statusCode ).toEqual( 200 );
|
||||
expect( result4.body ).toHaveLength( 5 );
|
||||
result4.body.forEach( ( order ) =>
|
||||
expect( order.billing.last_name ).toEqual( 'Doe' )
|
||||
);
|
||||
|
||||
// Test order item name.
|
||||
const result5 = await ordersApi.listAll.orders( {
|
||||
search: 'Pennant',
|
||||
} );
|
||||
expect( result5.statusCode ).toEqual( 200 );
|
||||
expect( result5.body ).toHaveLength( 2 );
|
||||
result5.body.forEach( ( order ) =>
|
||||
expect( order ).toEqual(
|
||||
expect.objectContaining( {
|
||||
line_items: expect.arrayContaining( [
|
||||
expect.objectContaining( {
|
||||
name: 'WordPress Pennant',
|
||||
} ),
|
||||
] ),
|
||||
} )
|
||||
)
|
||||
);
|
||||
} );
|
||||
|
||||
describe( 'orderby', () => {
|
||||
// The orders endpoint `orderby` parameter uses WP_Query, so our tests won't
|
||||
// include slug and title, since they are programmatically generated.
|
||||
it( 'default', async () => {
|
||||
// Default = date desc.
|
||||
const result = await ordersApi.listAll.orders();
|
||||
expect( result.statusCode ).toEqual( 200 );
|
||||
|
||||
// Verify all dates are in descending order.
|
||||
let lastDate = Date.now();
|
||||
result.body.forEach( ( { date_created } ) => {
|
||||
const created = Date.parse( date_created + '.000Z' );
|
||||
expect( lastDate ).toBeGreaterThanOrEqual( created );
|
||||
lastDate = created;
|
||||
} );
|
||||
} );
|
||||
|
||||
it( 'date', async () => {
|
||||
const result = await ordersApi.listAll.orders( {
|
||||
order: 'asc',
|
||||
orderby: 'date',
|
||||
} );
|
||||
expect( result.statusCode ).toEqual( 200 );
|
||||
|
||||
// Verify all dates are in ascending order.
|
||||
let lastDate = 0;
|
||||
result.body.forEach( ( { date_created } ) => {
|
||||
const created = Date.parse( date_created + '.000Z' );
|
||||
expect( created ).toBeGreaterThanOrEqual( lastDate );
|
||||
lastDate = created;
|
||||
} );
|
||||
} );
|
||||
|
||||
it( 'id', async () => {
|
||||
const result1 = await ordersApi.listAll.orders( {
|
||||
order: 'asc',
|
||||
orderby: 'id',
|
||||
} );
|
||||
expect( result1.statusCode ).toEqual( 200 );
|
||||
|
||||
// Verify all results are in ascending order.
|
||||
let lastId = 0;
|
||||
result1.body.forEach( ( { id } ) => {
|
||||
expect( id ).toBeGreaterThan( lastId );
|
||||
lastId = id;
|
||||
} );
|
||||
|
||||
const result2 = await ordersApi.listAll.orders( {
|
||||
order: 'desc',
|
||||
orderby: 'id',
|
||||
} );
|
||||
expect( result2.statusCode ).toEqual( 200 );
|
||||
|
||||
// Verify all results are in descending order.
|
||||
lastId = Number.MAX_SAFE_INTEGER;
|
||||
result2.body.forEach( ( { id } ) => {
|
||||
expect( lastId ).toBeGreaterThan( id );
|
||||
lastId = id;
|
||||
} );
|
||||
} );
|
||||
|
||||
it( 'include', async () => {
|
||||
const includeIds = [
|
||||
sampleData.precisionOrder.id,
|
||||
sampleData.hierarchicalOrders.parent.id,
|
||||
sampleData.guestOrder.id,
|
||||
];
|
||||
|
||||
const result1 = await ordersApi.listAll.orders( {
|
||||
order: 'asc',
|
||||
orderby: 'include',
|
||||
include: includeIds.join( ',' ),
|
||||
} );
|
||||
expect( result1.statusCode ).toEqual( 200 );
|
||||
expect( result1.body ).toHaveLength( includeIds.length );
|
||||
|
||||
// Verify all results are in proper order.
|
||||
result1.body.forEach( ( { id }, idx ) => {
|
||||
expect( id ).toBe( includeIds[ idx ] );
|
||||
} );
|
||||
|
||||
const result2 = await ordersApi.listAll.orders( {
|
||||
order: 'desc',
|
||||
orderby: 'include',
|
||||
include: includeIds.join( ',' ),
|
||||
} );
|
||||
expect( result2.statusCode ).toEqual( 200 );
|
||||
expect( result2.body ).toHaveLength( includeIds.length );
|
||||
|
||||
// Verify all results are in proper order.
|
||||
result2.body.forEach( ( { id }, idx ) => {
|
||||
expect( id ).toBe( includeIds[ idx ] );
|
||||
} );
|
||||
} );
|
||||
} );
|
||||
} );
|
||||
} );
|
||||
|
|
|
@ -1,777 +0,0 @@
|
|||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
const { createSampleData, deleteSampleData } = require( '../../data/products' );
|
||||
const { productsApi } = require('../../endpoints/products');
|
||||
|
||||
/**
|
||||
* Tests for the WooCommerce Products API.
|
||||
*
|
||||
* @group api
|
||||
* @group products
|
||||
*
|
||||
*/
|
||||
describe( 'Products API tests', () => {
|
||||
|
||||
const PRODUCTS_COUNT = 20;
|
||||
let sampleData;
|
||||
|
||||
beforeAll( async () => {
|
||||
sampleData = await createSampleData();
|
||||
}, 10000 );
|
||||
|
||||
afterAll( async () => {
|
||||
await deleteSampleData( sampleData );
|
||||
}, 10000 );
|
||||
|
||||
describe( 'List all products', () => {
|
||||
|
||||
it( 'defaults', async () => {
|
||||
const result = await productsApi.listAll.products();
|
||||
expect( result.statusCode ).toEqual( 200 );
|
||||
expect( result.headers['x-wp-total'] ).toEqual( PRODUCTS_COUNT.toString() );
|
||||
expect( result.headers['x-wp-totalpages'] ).toEqual( '2' );
|
||||
} );
|
||||
|
||||
it( 'pagination', async () => {
|
||||
const pageSize = 6;
|
||||
const page1 = await productsApi.listAll.products( {
|
||||
per_page: pageSize,
|
||||
} );
|
||||
const page2 = await productsApi.listAll.products( {
|
||||
per_page: pageSize,
|
||||
page: 2,
|
||||
} );
|
||||
expect( page1.statusCode ).toEqual( 200 );
|
||||
expect( page2.statusCode ).toEqual( 200 );
|
||||
|
||||
// Verify total page count.
|
||||
expect( page1.headers['x-wp-total'] ).toEqual( PRODUCTS_COUNT.toString() );
|
||||
expect( page1.headers['x-wp-totalpages'] ).toEqual( '4' );
|
||||
|
||||
// Verify we get pageSize'd arrays.
|
||||
expect( Array.isArray( page1.body ) ).toBe( true );
|
||||
expect( Array.isArray( page2.body ) ).toBe( true );
|
||||
expect( page1.body ).toHaveLength( pageSize );
|
||||
expect( page2.body ).toHaveLength( pageSize );
|
||||
|
||||
// Ensure all of the product IDs are unique (no page overlap).
|
||||
const allProductIds = page1.body.concat( page2.body ).reduce( ( acc, product ) => {
|
||||
acc[ product.id ] = 1;
|
||||
return acc;
|
||||
}, {} );
|
||||
expect( Object.keys( allProductIds ) ).toHaveLength( pageSize * 2 );
|
||||
|
||||
// Verify that offset takes precedent over page number.
|
||||
const page2Offset = await productsApi.listAll.products( {
|
||||
per_page: pageSize,
|
||||
page: 2,
|
||||
offset: pageSize + 1,
|
||||
} );
|
||||
// The offset pushes the result set 1 product past the start of page 2.
|
||||
expect( page2Offset.body ).toEqual(
|
||||
expect.not.arrayContaining( [
|
||||
expect.objectContaining( { id: page2.body[0].id } )
|
||||
] )
|
||||
);
|
||||
expect( page2Offset.body[0].id ).toEqual( page2.body[1].id );
|
||||
|
||||
// Verify the last page only has 2 products as we expect.
|
||||
const lastPage = await productsApi.listAll.products( {
|
||||
per_page: pageSize,
|
||||
page: 4,
|
||||
} );
|
||||
expect( Array.isArray( lastPage.body ) ).toBe( true );
|
||||
expect( lastPage.body ).toHaveLength( 2 );
|
||||
|
||||
// Verify a page outside the total page count is empty.
|
||||
const page6 = await productsApi.listAll.products( {
|
||||
per_page: pageSize,
|
||||
page: 6,
|
||||
} );
|
||||
expect( Array.isArray( page6.body ) ).toBe( true );
|
||||
expect( page6.body ).toHaveLength( 0 );
|
||||
} );
|
||||
|
||||
it( 'search', async () => {
|
||||
// Match in the short description.
|
||||
const result1 = await productsApi.listAll.products( {
|
||||
search: 'external'
|
||||
} );
|
||||
expect( result1.statusCode ).toEqual( 200 );
|
||||
expect( result1.body ).toHaveLength( 1 );
|
||||
expect( result1.body[0].name ).toBe( 'WordPress Pennant' );
|
||||
|
||||
// Match in the product name.
|
||||
const result2 = await productsApi.listAll.products( {
|
||||
search: 'pocket'
|
||||
} );
|
||||
expect( result2.statusCode ).toEqual( 200 );
|
||||
expect( result2.body ).toHaveLength( 1 );
|
||||
expect( result2.body[0].name ).toBe( 'Hoodie with Pocket' );
|
||||
} );
|
||||
|
||||
it( 'inclusion / exclusion', async () => {
|
||||
const allProducts = await productsApi.listAll.products( {
|
||||
per_page: 20,
|
||||
} );
|
||||
expect( allProducts.statusCode ).toEqual( 200 );
|
||||
const allProductIds = allProducts.body.map( product => product.id );
|
||||
expect( allProductIds ).toHaveLength( PRODUCTS_COUNT );
|
||||
|
||||
const productsToFilter = [
|
||||
allProductIds[2],
|
||||
allProductIds[4],
|
||||
allProductIds[7],
|
||||
allProductIds[13],
|
||||
];
|
||||
|
||||
const included = await productsApi.listAll.products( {
|
||||
per_page: 20,
|
||||
include: productsToFilter.join( ',' ),
|
||||
} );
|
||||
expect( included.statusCode ).toEqual( 200 );
|
||||
expect( included.body ).toHaveLength( productsToFilter.length );
|
||||
expect( included.body ).toEqual(
|
||||
expect.arrayContaining(
|
||||
productsToFilter.map( id => expect.objectContaining( { id } ) )
|
||||
)
|
||||
);
|
||||
|
||||
const excluded = await productsApi.listAll.products( {
|
||||
per_page: 20,
|
||||
exclude: productsToFilter.join( ',' ),
|
||||
} );
|
||||
expect( excluded.statusCode ).toEqual( 200 );
|
||||
expect( excluded.body ).toHaveLength( PRODUCTS_COUNT - productsToFilter.length );
|
||||
expect( excluded.body ).toEqual(
|
||||
expect.not.arrayContaining(
|
||||
productsToFilter.map( id => expect.objectContaining( { id } ) )
|
||||
)
|
||||
);
|
||||
|
||||
} );
|
||||
|
||||
it( 'slug', async () => {
|
||||
// Match by slug.
|
||||
const result1 = await productsApi.listAll.products( {
|
||||
slug: 't-shirt-with-logo'
|
||||
} );
|
||||
expect( result1.statusCode ).toEqual( 200 );
|
||||
expect( result1.body ).toHaveLength( 1 );
|
||||
expect( result1.body[0].slug ).toBe( 't-shirt-with-logo' );
|
||||
|
||||
// No matches
|
||||
const result2 = await productsApi.listAll.products( {
|
||||
slug: 'no-product-with-this-slug'
|
||||
} );
|
||||
expect( result2.statusCode ).toEqual( 200 );
|
||||
expect( result2.body ).toHaveLength( 0 );
|
||||
} );
|
||||
|
||||
it( 'sku', async () => {
|
||||
// Match by SKU.
|
||||
const result1 = await productsApi.listAll.products( {
|
||||
sku: 'woo-sunglasses'
|
||||
} );
|
||||
expect( result1.statusCode ).toEqual( 200 );
|
||||
expect( result1.body ).toHaveLength( 1 );
|
||||
expect( result1.body[0].sku ).toBe( 'woo-sunglasses' );
|
||||
|
||||
// No matches
|
||||
const result2 = await productsApi.listAll.products( {
|
||||
sku: 'no-product-with-this-sku'
|
||||
} );
|
||||
expect( result2.statusCode ).toEqual( 200 );
|
||||
expect( result2.body ).toHaveLength( 0 );
|
||||
} );
|
||||
|
||||
it( 'type', async () => {
|
||||
const result1 = await productsApi.listAll.products( {
|
||||
type: 'simple'
|
||||
} );
|
||||
expect( result1.statusCode ).toEqual( 200 );
|
||||
expect( result1.headers['x-wp-total'] ).toEqual( '16' );
|
||||
|
||||
const result2 = await productsApi.listAll.products( {
|
||||
type: 'external'
|
||||
} );
|
||||
expect( result2.statusCode ).toEqual( 200 );
|
||||
expect( result2.body ).toHaveLength( 1 );
|
||||
expect( result2.body[0].name ).toBe( 'WordPress Pennant' );
|
||||
|
||||
const result3 = await productsApi.listAll.products( {
|
||||
type: 'variable'
|
||||
} );
|
||||
expect( result3.statusCode ).toEqual( 200 );
|
||||
expect( result3.body ).toHaveLength( 2 );
|
||||
|
||||
const result4 = await productsApi.listAll.products( {
|
||||
type: 'grouped'
|
||||
} );
|
||||
expect( result4.statusCode ).toEqual( 200 );
|
||||
expect( result4.body ).toHaveLength( 1 );
|
||||
expect( result4.body[0].name ).toBe( 'Logo Collection' );
|
||||
} );
|
||||
|
||||
it( 'featured', async () => {
|
||||
const featured = [
|
||||
expect.objectContaining( { name: 'Hoodie with Zipper' } ),
|
||||
expect.objectContaining( { name: 'Hoodie with Pocket' } ),
|
||||
expect.objectContaining( { name: 'Sunglasses' } ),
|
||||
expect.objectContaining( { name: 'Cap' } ),
|
||||
expect.objectContaining( { name: 'V-Neck T-Shirt' } ),
|
||||
];
|
||||
|
||||
const result1 = await productsApi.listAll.products( {
|
||||
featured: true,
|
||||
} );
|
||||
expect( result1.statusCode ).toEqual( 200 );
|
||||
expect( result1.body ).toHaveLength( featured.length );
|
||||
expect( result1.body ).toEqual( expect.arrayContaining( featured ) );
|
||||
|
||||
const result2 = await productsApi.listAll.products( {
|
||||
featured: false,
|
||||
} );
|
||||
expect( result2.statusCode ).toEqual( 200 );
|
||||
expect( result2.body ).toEqual( expect.not.arrayContaining( featured ) );
|
||||
} );
|
||||
|
||||
it( 'categories', async () => {
|
||||
const accessory = [
|
||||
expect.objectContaining( { name: 'Beanie' } ),
|
||||
]
|
||||
const hoodies = [
|
||||
expect.objectContaining( { name: 'Hoodie with Zipper' } ),
|
||||
expect.objectContaining( { name: 'Hoodie with Pocket' } ),
|
||||
expect.objectContaining( { name: 'Hoodie with Logo' } ),
|
||||
expect.objectContaining( { name: 'Hoodie' } ),
|
||||
];
|
||||
|
||||
// Verify that subcategories are included.
|
||||
const result1 = await productsApi.listAll.products( {
|
||||
per_page: 20,
|
||||
category: sampleData.categories.clothing.id,
|
||||
} );
|
||||
expect( result1.statusCode ).toEqual( 200 );
|
||||
expect( result1.body ).toEqual( expect.arrayContaining( accessory ) );
|
||||
expect( result1.body ).toEqual( expect.arrayContaining( hoodies ) );
|
||||
|
||||
// Verify sibling categories are not.
|
||||
const result2 = await productsApi.listAll.products( {
|
||||
category: sampleData.categories.hoodies.id,
|
||||
} );
|
||||
expect( result2.statusCode ).toEqual( 200 );
|
||||
expect( result2.body ).toEqual( expect.not.arrayContaining( accessory ) );
|
||||
expect( result2.body ).toEqual( expect.arrayContaining( hoodies ) );
|
||||
} );
|
||||
|
||||
it( 'on sale', async () => {
|
||||
const onSale = [
|
||||
expect.objectContaining( { name: 'Beanie with Logo' } ),
|
||||
expect.objectContaining( { name: 'Hoodie with Pocket' } ),
|
||||
expect.objectContaining( { name: 'Single' } ),
|
||||
expect.objectContaining( { name: 'Cap' } ),
|
||||
expect.objectContaining( { name: 'Belt' } ),
|
||||
expect.objectContaining( { name: 'Beanie' } ),
|
||||
expect.objectContaining( { name: 'Hoodie' } ),
|
||||
];
|
||||
|
||||
const result1 = await productsApi.listAll.products( {
|
||||
on_sale: true,
|
||||
} );
|
||||
expect( result1.statusCode ).toEqual( 200 );
|
||||
expect( result1.body ).toHaveLength( onSale.length );
|
||||
expect( result1.body ).toEqual( expect.arrayContaining( onSale ) );
|
||||
|
||||
const result2 = await productsApi.listAll.products( {
|
||||
on_sale: false,
|
||||
} );
|
||||
expect( result2.statusCode ).toEqual( 200 );
|
||||
expect( result2.body ).toEqual( expect.not.arrayContaining( onSale ) );
|
||||
} );
|
||||
|
||||
it( 'price', async () => {
|
||||
const result1 = await productsApi.listAll.products( {
|
||||
min_price: 21,
|
||||
max_price: 28,
|
||||
} );
|
||||
expect( result1.statusCode ).toEqual( 200 );
|
||||
expect( result1.body ).toHaveLength( 1 );
|
||||
expect( result1.body[0].name ).toBe( 'Long Sleeve Tee' );
|
||||
expect( result1.body[0].price ).toBe( '25' );
|
||||
|
||||
const result2 = await productsApi.listAll.products( {
|
||||
max_price: 5,
|
||||
} );
|
||||
expect( result2.statusCode ).toEqual( 200 );
|
||||
expect( result2.body ).toHaveLength( 1 );
|
||||
expect( result2.body[0].name ).toBe( 'Single' );
|
||||
expect( result2.body[0].price ).toBe( '2' );
|
||||
|
||||
const result3 = await productsApi.listAll.products( {
|
||||
min_price: 5,
|
||||
order: 'asc',
|
||||
orderby: 'price',
|
||||
} );
|
||||
expect( result3.statusCode ).toEqual( 200 );
|
||||
expect( result3.body ).toEqual(
|
||||
expect.not.arrayContaining( [
|
||||
expect.objectContaining( { name: 'Single' } )
|
||||
] )
|
||||
);
|
||||
} );
|
||||
|
||||
it( 'before / after', async () => {
|
||||
const before = [
|
||||
expect.objectContaining( { name: 'Album' } ),
|
||||
expect.objectContaining( { name: 'Single' } ),
|
||||
expect.objectContaining( { name: 'T-Shirt with Logo' } ),
|
||||
expect.objectContaining( { name: 'Beanie with Logo' } ),
|
||||
];
|
||||
const after = [
|
||||
expect.objectContaining( { name: 'Hoodie' } ),
|
||||
expect.objectContaining( { name: 'V-Neck T-Shirt' } ),
|
||||
expect.objectContaining( { name: 'Parent Product' } ),
|
||||
expect.objectContaining( { name: 'Child Product' } ),
|
||||
];
|
||||
|
||||
const result1 = await productsApi.listAll.products( {
|
||||
before: '2021-09-05T15:50:19',
|
||||
} );
|
||||
expect( result1.statusCode ).toEqual( 200 );
|
||||
expect( result1.body ).toHaveLength( before.length );
|
||||
expect( result1.body ).toEqual( expect.arrayContaining( before ) );
|
||||
|
||||
const result2 = await productsApi.listAll.products( {
|
||||
after: '2021-09-18T15:50:18',
|
||||
} );
|
||||
expect( result2.statusCode ).toEqual( 200 );
|
||||
expect( result2.body ).toEqual( expect.not.arrayContaining( before ) );
|
||||
expect( result2.body ).toHaveLength( after.length );
|
||||
expect( result2.body ).toEqual( expect.arrayContaining( after ) );
|
||||
} );
|
||||
|
||||
it( 'attributes', async () => {
|
||||
const red = sampleData.attributes.colors.find( term => term.name === 'Red' );
|
||||
|
||||
const redProducts = [
|
||||
expect.objectContaining( { name: 'V-Neck T-Shirt' } ),
|
||||
expect.objectContaining( { name: 'Hoodie' } ),
|
||||
expect.objectContaining( { name: 'Beanie' } ),
|
||||
expect.objectContaining( { name: 'Beanie with Logo' } ),
|
||||
];
|
||||
|
||||
const result = await productsApi.listAll.products( {
|
||||
attribute: 'pa_color',
|
||||
attribute_term: red.id,
|
||||
} );
|
||||
|
||||
expect( result.statusCode ).toEqual( 200 );
|
||||
expect( result.body ).toHaveLength( redProducts.length );
|
||||
expect( result.body ).toEqual( expect.arrayContaining( redProducts ) );
|
||||
} );
|
||||
|
||||
it( 'status', async () => {
|
||||
const result1 = await productsApi.listAll.products( {
|
||||
status: 'pending'
|
||||
} );
|
||||
expect( result1.statusCode ).toEqual( 200 );
|
||||
expect( result1.body ).toHaveLength( 1 );
|
||||
expect( result1.body[0].name ).toBe( 'Polo' );
|
||||
|
||||
const result2 = await productsApi.listAll.products( {
|
||||
status: 'draft'
|
||||
} );
|
||||
expect( result2.statusCode ).toEqual( 200 );
|
||||
expect( result2.body ).toHaveLength( 0 );
|
||||
} );
|
||||
|
||||
it( 'shipping class', async () => {
|
||||
const result = await productsApi.listAll.products( {
|
||||
shipping_class: sampleData.shippingClasses.freight.id,
|
||||
} );
|
||||
expect( result.statusCode ).toEqual( 200 );
|
||||
expect( result.body ).toHaveLength( 1 );
|
||||
expect( result.body[0].name ).toBe( 'Long Sleeve Tee' );
|
||||
} );
|
||||
|
||||
it( 'tax class', async () => {
|
||||
const result = await productsApi.listAll.products( {
|
||||
tax_class: 'reduced-rate',
|
||||
} );
|
||||
expect( result.statusCode ).toEqual( 200 );
|
||||
expect( result.body ).toHaveLength( 1 );
|
||||
expect( result.body[0].name ).toBe( 'Sunglasses' );
|
||||
} );
|
||||
|
||||
it( 'stock status', async () => {
|
||||
const result = await productsApi.listAll.products( {
|
||||
stock_status: 'onbackorder',
|
||||
} );
|
||||
expect( result.statusCode ).toEqual( 200 );
|
||||
expect( result.body ).toHaveLength( 1 );
|
||||
expect( result.body[0].name ).toBe( 'T-Shirt' );
|
||||
} );
|
||||
|
||||
it( 'tags', async () => {
|
||||
const coolProducts = [
|
||||
expect.objectContaining( { name: 'Sunglasses' } ),
|
||||
expect.objectContaining( { name: 'Hoodie with Pocket' } ),
|
||||
expect.objectContaining( { name: 'Beanie' } ),
|
||||
];
|
||||
|
||||
const result = await productsApi.listAll.products( {
|
||||
tag: sampleData.tags.cool.id,
|
||||
} );
|
||||
|
||||
expect( result.statusCode ).toEqual( 200 );
|
||||
expect( result.body ).toHaveLength( coolProducts.length );
|
||||
expect( result.body ).toEqual( expect.arrayContaining( coolProducts ) );
|
||||
} );
|
||||
|
||||
it( 'parent', async () => {
|
||||
const result1 = await productsApi.listAll.products( {
|
||||
parent: sampleData.hierarchicalProducts.parent.id,
|
||||
} );
|
||||
expect( result1.statusCode ).toEqual( 200 );
|
||||
expect( result1.body ).toHaveLength( 1 );
|
||||
expect( result1.body[0].name ).toBe( 'Child Product' );
|
||||
|
||||
const result2 = await productsApi.listAll.products( {
|
||||
parent_exclude: sampleData.hierarchicalProducts.parent.id,
|
||||
} );
|
||||
expect( result2.statusCode ).toEqual( 200 );
|
||||
expect( result2.body ).toEqual( expect.not.arrayContaining( [
|
||||
expect.objectContaining( { name: 'Child Product' } ),
|
||||
] ) );
|
||||
} );
|
||||
|
||||
describe( 'orderby', () => {
|
||||
const productNamesAsc = [
|
||||
'Album',
|
||||
'Beanie',
|
||||
'Beanie with Logo',
|
||||
'Belt',
|
||||
'Cap',
|
||||
'Child Product',
|
||||
'Hoodie',
|
||||
'Hoodie with Logo',
|
||||
'Hoodie with Pocket',
|
||||
'Hoodie with Zipper',
|
||||
'Logo Collection',
|
||||
'Long Sleeve Tee',
|
||||
'Parent Product',
|
||||
'Polo',
|
||||
'Single',
|
||||
'Sunglasses',
|
||||
'T-Shirt',
|
||||
'T-Shirt with Logo',
|
||||
'V-Neck T-Shirt',
|
||||
'WordPress Pennant',
|
||||
];
|
||||
const productNamesDesc = [ ...productNamesAsc ].reverse();
|
||||
const productNamesByRatingAsc = [
|
||||
'Sunglasses',
|
||||
'Cap',
|
||||
'T-Shirt',
|
||||
];
|
||||
const productNamesByRatingDesc = [ ...productNamesByRatingAsc ].reverse();
|
||||
const productNamesByPopularityDesc = [
|
||||
'Beanie with Logo',
|
||||
'Single',
|
||||
'T-Shirt',
|
||||
];
|
||||
const productNamesByPopularityAsc = [ ...productNamesByPopularityDesc ].reverse();
|
||||
|
||||
it( 'default', async () => {
|
||||
// Default = date desc.
|
||||
const result = await productsApi.listAll.products();
|
||||
expect( result.statusCode ).toEqual( 200 );
|
||||
|
||||
// Verify all dates are in descending order.
|
||||
let lastDate = Date.now();
|
||||
result.body.forEach( ( { date_created_gmt } ) => {
|
||||
const created = Date.parse( date_created_gmt + '.000Z' );
|
||||
expect( lastDate ).toBeGreaterThan( created );
|
||||
lastDate = created;
|
||||
} );
|
||||
} );
|
||||
|
||||
it( 'date', async () => {
|
||||
const result = await productsApi.listAll.products( {
|
||||
order: 'asc',
|
||||
orderby: 'date',
|
||||
} );
|
||||
expect( result.statusCode ).toEqual( 200 );
|
||||
|
||||
// Verify all dates are in ascending order.
|
||||
let lastDate = 0;
|
||||
result.body.forEach( ( { date_created_gmt } ) => {
|
||||
const created = Date.parse( date_created_gmt + '.000Z' );
|
||||
expect( created ).toBeGreaterThan( lastDate );
|
||||
lastDate = created;
|
||||
} );
|
||||
} );
|
||||
|
||||
it( 'id', async () => {
|
||||
const result1 = await productsApi.listAll.products( {
|
||||
order: 'asc',
|
||||
orderby: 'id',
|
||||
} );
|
||||
expect( result1.statusCode ).toEqual( 200 );
|
||||
|
||||
// Verify all results are in ascending order.
|
||||
let lastId = 0;
|
||||
result1.body.forEach( ( { id } ) => {
|
||||
expect( id ).toBeGreaterThan( lastId );
|
||||
lastId = id;
|
||||
} );
|
||||
|
||||
const result2 = await productsApi.listAll.products( {
|
||||
order: 'desc',
|
||||
orderby: 'id',
|
||||
} );
|
||||
expect( result2.statusCode ).toEqual( 200 );
|
||||
|
||||
// Verify all results are in descending order.
|
||||
lastId = Number.MAX_SAFE_INTEGER;
|
||||
result2.body.forEach( ( { id } ) => {
|
||||
expect( lastId ).toBeGreaterThan( id );
|
||||
lastId = id;
|
||||
} );
|
||||
} );
|
||||
|
||||
it( 'title', async () => {
|
||||
const result1 = await productsApi.listAll.products( {
|
||||
order: 'asc',
|
||||
orderby: 'title',
|
||||
per_page: productNamesAsc.length,
|
||||
} );
|
||||
expect( result1.statusCode ).toEqual( 200 );
|
||||
|
||||
// Verify all results are in ascending order.
|
||||
result1.body.forEach( ( { name }, idx ) => {
|
||||
expect( name ).toBe( productNamesAsc[ idx ] );
|
||||
} );
|
||||
|
||||
const result2 = await productsApi.listAll.products( {
|
||||
order: 'desc',
|
||||
orderby: 'title',
|
||||
per_page: productNamesDesc.length,
|
||||
} );
|
||||
expect( result2.statusCode ).toEqual( 200 );
|
||||
|
||||
// Verify all results are in descending order.
|
||||
result2.body.forEach( ( { name }, idx ) => {
|
||||
expect( name ).toBe( productNamesDesc[ idx ] );
|
||||
} );
|
||||
} );
|
||||
|
||||
it( 'slug', async () => {
|
||||
const productNamesBySlugAsc = [
|
||||
'Polo', // The Polo isn't published so it has an empty slug.
|
||||
...productNamesAsc.filter( p => p !== 'Polo' ),
|
||||
];
|
||||
const productNamesBySlugDesc = [ ...productNamesBySlugAsc ].reverse();
|
||||
|
||||
const result1 = await productsApi.listAll.products( {
|
||||
order: 'asc',
|
||||
orderby: 'slug',
|
||||
per_page: productNamesBySlugAsc.length,
|
||||
} );
|
||||
expect( result1.statusCode ).toEqual( 200 );
|
||||
|
||||
// Verify all results are in ascending order.
|
||||
result1.body.forEach( ( { name }, idx ) => {
|
||||
expect( name ).toBe( productNamesBySlugAsc[ idx ] );
|
||||
} );
|
||||
|
||||
const result2 = await productsApi.listAll.products( {
|
||||
order: 'desc',
|
||||
orderby: 'slug',
|
||||
per_page: productNamesBySlugDesc.length,
|
||||
} );
|
||||
expect( result2.statusCode ).toEqual( 200 );
|
||||
|
||||
// Verify all results are in descending order.
|
||||
result2.body.forEach( ( { name }, idx ) => {
|
||||
expect( name ).toBe( productNamesBySlugDesc[ idx ] );
|
||||
} );
|
||||
} );
|
||||
|
||||
it( 'price', async () => {
|
||||
const productNamesMinPriceAsc = [
|
||||
'Parent Product',
|
||||
'Child Product',
|
||||
'Single',
|
||||
'WordPress Pennant',
|
||||
'Album',
|
||||
'V-Neck T-Shirt',
|
||||
'Cap',
|
||||
'Beanie with Logo',
|
||||
'T-Shirt with Logo',
|
||||
'Beanie',
|
||||
'T-Shirt',
|
||||
'Logo Collection',
|
||||
'Polo',
|
||||
'Long Sleeve Tee',
|
||||
'Hoodie with Pocket',
|
||||
'Hoodie',
|
||||
'Hoodie with Zipper',
|
||||
'Hoodie with Logo',
|
||||
'Belt',
|
||||
'Sunglasses',
|
||||
];
|
||||
const result1 = await productsApi.listAll.products( {
|
||||
order: 'asc',
|
||||
orderby: 'price',
|
||||
per_page: productNamesMinPriceAsc.length
|
||||
} );
|
||||
expect( result1.statusCode ).toEqual( 200 );
|
||||
expect( result1.body ).toHaveLength( productNamesMinPriceAsc.length );
|
||||
|
||||
// Verify all results are in ascending order.
|
||||
// The query uses the min price calculated in the product meta lookup table,
|
||||
// so we can't just check the price property of the response.
|
||||
result1.body.forEach( ( { name }, idx ) => {
|
||||
expect( name ).toBe( productNamesMinPriceAsc[ idx ] );
|
||||
} );
|
||||
|
||||
const productNamesMaxPriceDesc = [
|
||||
'Sunglasses',
|
||||
'Belt',
|
||||
'Hoodie',
|
||||
'Logo Collection',
|
||||
'Hoodie with Logo',
|
||||
'Hoodie with Zipper',
|
||||
'Hoodie with Pocket',
|
||||
'Long Sleeve Tee',
|
||||
'V-Neck T-Shirt',
|
||||
'Polo',
|
||||
'T-Shirt',
|
||||
'Beanie',
|
||||
'T-Shirt with Logo',
|
||||
'Beanie with Logo',
|
||||
'Cap',
|
||||
'Album',
|
||||
'WordPress Pennant',
|
||||
'Single',
|
||||
'Child Product',
|
||||
'Parent Product',
|
||||
];
|
||||
|
||||
const result2 = await productsApi.listAll.products( {
|
||||
order: 'desc',
|
||||
orderby: 'price',
|
||||
per_page: productNamesMaxPriceDesc.length
|
||||
} );
|
||||
expect( result2.statusCode ).toEqual( 200 );
|
||||
expect( result2.body ).toHaveLength( productNamesMaxPriceDesc.length );
|
||||
|
||||
// Verify all results are in descending order.
|
||||
// The query uses the max price calculated in the product meta lookup table,
|
||||
// so we can't just check the price property of the response.
|
||||
result2.body.forEach( ( { name }, idx ) => {
|
||||
expect( name ).toBe( productNamesMaxPriceDesc[ idx ] );
|
||||
} );
|
||||
} );
|
||||
|
||||
// This case will remain skipped until orderby include is fixed.
|
||||
// See: https://github.com/woocommerce/woocommerce/issues/30354#issuecomment-925955099.
|
||||
it( 'include', async () => {
|
||||
const includeIds = [
|
||||
sampleData.groupedProducts[ 0 ].id,
|
||||
sampleData.simpleProducts[ 3 ].id,
|
||||
sampleData.hierarchicalProducts.parent.id,
|
||||
];
|
||||
|
||||
const result1 = await productsApi.listAll.products( {
|
||||
order: 'asc',
|
||||
orderby: 'include',
|
||||
include: includeIds.join( ',' ),
|
||||
} );
|
||||
expect( result1.statusCode ).toEqual( 200 );
|
||||
expect( result1.body ).toHaveLength( includeIds.length );
|
||||
|
||||
// Verify all results are in proper order.
|
||||
result1.body.forEach( ( { id }, idx ) => {
|
||||
expect( id ).toBe( includeIds[ idx ] );
|
||||
} );
|
||||
|
||||
const result2 = await productsApi.listAll.products( {
|
||||
order: 'desc',
|
||||
orderby: 'include',
|
||||
include: includeIds.join( ',' ),
|
||||
} );
|
||||
expect( result2.statusCode ).toEqual( 200 );
|
||||
expect( result2.body ).toHaveLength( includeIds.length );
|
||||
|
||||
// Verify all results are in proper order.
|
||||
result2.body.forEach( ( { id }, idx ) => {
|
||||
expect( id ).toBe( includeIds[ idx ] );
|
||||
} );
|
||||
} );
|
||||
|
||||
it( 'rating (desc)', async () => {
|
||||
const result2 = await productsApi.listAll.products( {
|
||||
order: 'desc',
|
||||
orderby: 'rating',
|
||||
per_page: productNamesByRatingDesc.length,
|
||||
} );
|
||||
expect( result2.statusCode ).toEqual( 200 );
|
||||
|
||||
// Verify all results are in descending order.
|
||||
result2.body.forEach( ( { name }, idx ) => {
|
||||
expect( name ).toBe( productNamesByRatingDesc[ idx ] );
|
||||
} );
|
||||
} );
|
||||
|
||||
// This case will remain skipped until ratings can be sorted ascending.
|
||||
// See: https://github.com/woocommerce/woocommerce/issues/30354#issuecomment-925955099.
|
||||
it.skip( 'rating (asc)', async () => {
|
||||
const result1 = await productsApi.listAll.products( {
|
||||
order: 'asc',
|
||||
orderby: 'rating',
|
||||
per_page: productNamesByRatingAsc.length,
|
||||
} );
|
||||
expect( result1.statusCode ).toEqual( 200 );
|
||||
|
||||
// Verify all results are in ascending order.
|
||||
result1.body.forEach( ( { name }, idx ) => {
|
||||
expect( name ).toBe( productNamesByRatingAsc[ idx ] );
|
||||
} );
|
||||
} );
|
||||
|
||||
it( 'popularity (desc)', async () => {
|
||||
const result2 = await productsApi.listAll.products( {
|
||||
order: 'desc',
|
||||
orderby: 'popularity',
|
||||
per_page: productNamesByPopularityDesc.length,
|
||||
} );
|
||||
expect( result2.statusCode ).toEqual( 200 );
|
||||
|
||||
// Verify all results are in descending order.
|
||||
result2.body.forEach( ( { name }, idx ) => {
|
||||
expect( name ).toBe( productNamesByPopularityDesc[ idx ] );
|
||||
} );
|
||||
} );
|
||||
|
||||
// This case will remain skipped until popularity can be sorted ascending.
|
||||
// See: https://github.com/woocommerce/woocommerce/issues/30354#issuecomment-925955099.
|
||||
it.skip( 'popularity (asc)', async () => {
|
||||
const result1 = await productsApi.listAll.products( {
|
||||
order: 'asc',
|
||||
orderby: 'popularity',
|
||||
per_page: productNamesByPopularityAsc.length,
|
||||
} );
|
||||
expect( result1.statusCode ).toEqual( 200 );
|
||||
|
||||
// Verify all results are in ascending order.
|
||||
result1.body.forEach( ( { name }, idx ) => {
|
||||
expect( name ).toBe( productNamesByPopularityAsc[ idx ] );
|
||||
} );
|
||||
} );
|
||||
} );
|
||||
} );
|
||||
} );
|
|
@ -0,0 +1,57 @@
|
|||
const { shippingMethodsApi } = require( '../../endpoints' );
|
||||
const { getShippingMethodExample } = require( '../../data' );
|
||||
|
||||
/**
|
||||
* Shipping zone id for "Locations not covered by your other zones".
|
||||
*/
|
||||
const shippingZoneId = 0;
|
||||
|
||||
/**
|
||||
* Data table for shipping methods.
|
||||
*/
|
||||
const shippingMethods = [
|
||||
[ 'Flat rate', 'flat_rate', '10' ],
|
||||
[ 'Free shipping', 'free_shipping', undefined ],
|
||||
[ 'Local pickup', 'local_pickup', '30' ],
|
||||
];
|
||||
|
||||
/**
|
||||
* Tests for the WooCommerce Shipping methods API.
|
||||
*
|
||||
* @group api
|
||||
* @group shipping-methods
|
||||
*
|
||||
*/
|
||||
describe( 'Shipping methods API tests', () => {
|
||||
it.each( shippingMethods )(
|
||||
"can add a '%s' shipping method",
|
||||
async ( methodTitle, methodId, cost ) => {
|
||||
const shippingMethod = getShippingMethodExample( methodId, cost );
|
||||
|
||||
const {
|
||||
status,
|
||||
body,
|
||||
} = await shippingMethodsApi.create.shippingMethod(
|
||||
shippingZoneId,
|
||||
shippingMethod
|
||||
);
|
||||
|
||||
expect( status ).toEqual( shippingMethodsApi.create.responseCode );
|
||||
expect( typeof body.id ).toEqual( 'number' );
|
||||
expect( body.method_id ).toEqual( methodId );
|
||||
expect( body.method_title ).toEqual( methodTitle );
|
||||
expect( body.enabled ).toEqual( true );
|
||||
|
||||
if ( [ 'flat_rate', 'local_pickup' ].includes( methodId ) ) {
|
||||
expect( body.settings.cost.value ).toEqual( cost );
|
||||
}
|
||||
|
||||
// Cleanup: Delete the shipping method
|
||||
await shippingMethodsApi.delete.shippingMethod(
|
||||
shippingZoneId,
|
||||
body.id,
|
||||
true
|
||||
);
|
||||
}
|
||||
);
|
||||
} );
|
|
@ -0,0 +1,124 @@
|
|||
const { shippingZonesApi } = require( '../../endpoints' );
|
||||
const { getShippingZoneExample } = require( '../../data' );
|
||||
|
||||
/**
|
||||
* Shipping zone to be created, retrieved, updated, and deleted by the tests.
|
||||
*/
|
||||
const shippingZone = getShippingZoneExample();
|
||||
|
||||
/**
|
||||
* Tests for the WooCommerce Shipping zones API.
|
||||
*
|
||||
* @group api
|
||||
* @group shipping-zones
|
||||
*
|
||||
*/
|
||||
describe( 'Shipping zones API tests', () => {
|
||||
it( 'cannot delete the default shipping zone "Locations not covered by your other zones"', async () => {
|
||||
// Delete all pre-existing shipping zones
|
||||
const { body } = await shippingZonesApi.listAll.shippingZones( {
|
||||
_fields: 'id',
|
||||
} );
|
||||
const ids = body.map( ( { id } ) => id );
|
||||
|
||||
for ( const id of ids ) {
|
||||
await shippingZonesApi.delete.shippingZone( id, true );
|
||||
}
|
||||
|
||||
// Verify that the default shipping zone remains
|
||||
const {
|
||||
body: remainingZones,
|
||||
} = await shippingZonesApi.listAll.shippingZones( {
|
||||
_fields: 'id',
|
||||
} );
|
||||
|
||||
expect( remainingZones ).toHaveLength( 1 );
|
||||
expect( remainingZones[ 0 ].id ).toEqual( 0 );
|
||||
} );
|
||||
|
||||
it( 'cannot update the default shipping zone', async () => {
|
||||
const newZoneDetails = {
|
||||
name: 'Default shipping zone',
|
||||
};
|
||||
const { status, body } = await shippingZonesApi.update.shippingZone(
|
||||
0,
|
||||
newZoneDetails
|
||||
);
|
||||
|
||||
expect( status ).toEqual( 403 );
|
||||
expect( body.code ).toEqual(
|
||||
'woocommerce_rest_shipping_zone_invalid_zone'
|
||||
);
|
||||
expect( body.message ).toEqual(
|
||||
'The "locations not covered by your other zones" zone cannot be updated.'
|
||||
);
|
||||
} );
|
||||
|
||||
it( 'can create a shipping zone', async () => {
|
||||
const { status, body } = await shippingZonesApi.create.shippingZone(
|
||||
shippingZone
|
||||
);
|
||||
|
||||
expect( status ).toEqual( shippingZonesApi.create.responseCode );
|
||||
expect( typeof body.id ).toEqual( 'number' );
|
||||
expect( body.name ).toEqual( shippingZone.name );
|
||||
|
||||
// Save the shipping zone ID. It will be used by other tests.
|
||||
shippingZone.id = body.id;
|
||||
} );
|
||||
|
||||
it( 'can retrieve a shipping zone', async () => {
|
||||
const { status, body } = await shippingZonesApi.retrieve.shippingZone(
|
||||
shippingZone.id
|
||||
);
|
||||
|
||||
expect( status ).toEqual( shippingZonesApi.retrieve.responseCode );
|
||||
expect( body.id ).toEqual( shippingZone.id );
|
||||
} );
|
||||
|
||||
it( 'can list all shipping zones', async () => {
|
||||
const param = {
|
||||
_fields: 'id',
|
||||
};
|
||||
const { status, body } = await shippingZonesApi.listAll.shippingZones(
|
||||
param
|
||||
);
|
||||
|
||||
expect( body ).toHaveLength( 2 ); // the test shipping zone, and the default 'Locations not covered by your other zones'
|
||||
expect( status ).toEqual( shippingZonesApi.listAll.responseCode );
|
||||
expect( body ).toEqual(
|
||||
expect.arrayContaining( [ { id: shippingZone.id } ] )
|
||||
);
|
||||
} );
|
||||
|
||||
it( 'can update a shipping zone', async () => {
|
||||
const updatedShippingZone = {
|
||||
name: 'United States (Domestic)',
|
||||
};
|
||||
|
||||
const { status, body } = await shippingZonesApi.update.shippingZone(
|
||||
shippingZone.id,
|
||||
updatedShippingZone
|
||||
);
|
||||
|
||||
expect( status ).toEqual( shippingZonesApi.retrieve.responseCode );
|
||||
expect( body.id ).toEqual( shippingZone.id );
|
||||
expect( body.name ).toEqual( updatedShippingZone.name );
|
||||
} );
|
||||
|
||||
it( 'can delete a shipping zone', async () => {
|
||||
const { status, body } = await shippingZonesApi.delete.shippingZone(
|
||||
shippingZone.id,
|
||||
true
|
||||
);
|
||||
|
||||
expect( status ).toEqual( shippingZonesApi.delete.responseCode );
|
||||
expect( body.id ).toEqual( shippingZone.id );
|
||||
|
||||
// Verify that the shipping zone can no longer be retrieved
|
||||
const {
|
||||
status: retrieveStatus,
|
||||
} = await shippingZonesApi.retrieve.shippingZone( shippingZone.id );
|
||||
expect( retrieveStatus ).toEqual( 404 );
|
||||
} );
|
||||
} );
|
|
@ -1,12 +1,9 @@
|
|||
module.exports = {
|
||||
parser: '@typescript-eslint/parser',
|
||||
env: {
|
||||
'jest/globals': true
|
||||
'jest/globals': true,
|
||||
},
|
||||
ignorePatterns: [
|
||||
'dist/',
|
||||
'node_modules/'
|
||||
],
|
||||
ignorePatterns: [ 'dist/', 'node_modules/' ],
|
||||
rules: {
|
||||
'no-unused-vars': 'off',
|
||||
'no-dupe-class-members': 'off',
|
||||
|
@ -14,18 +11,11 @@ module.exports = {
|
|||
'no-useless-constructor': 'off',
|
||||
'@typescript-eslint/no-useless-constructor': 2,
|
||||
},
|
||||
plugins: [
|
||||
'@typescript-eslint'
|
||||
],
|
||||
extends: [
|
||||
'plugin:@wordpress/eslint-plugin/recommended-with-formatting'
|
||||
],
|
||||
plugins: [ '@typescript-eslint/eslint-plugin' ],
|
||||
extends: [ 'plugin:@wordpress/eslint-plugin/recommended-with-formatting' ],
|
||||
overrides: [
|
||||
{
|
||||
files: [
|
||||
'**/*.js',
|
||||
'**/*.ts'
|
||||
],
|
||||
files: [ '**/*.js', '**/*.ts' ],
|
||||
settings: {
|
||||
jsdoc: {
|
||||
mode: 'typescript',
|
||||
|
@ -33,13 +23,10 @@ module.exports = {
|
|||
},
|
||||
},
|
||||
{
|
||||
files: [
|
||||
'**/*.spec.ts',
|
||||
'**/*.test.ts'
|
||||
],
|
||||
files: [ '**/*.spec.ts', '**/*.test.ts' ],
|
||||
rules: {
|
||||
'no-console': 'off',
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
|
||||
# Editors
|
||||
project.xml
|
||||
project.properties
|
||||
/nbproject/private/
|
||||
.buildpath
|
||||
.project
|
||||
.settings*
|
||||
.idea
|
||||
.vscode
|
||||
*.sublime-project
|
||||
*.sublime-workspace
|
||||
.sublimelinterrc
|
||||
|
||||
# Build Artifacts
|
||||
/node_modules/
|
||||
/dist/
|
||||
tsconfig.tsbuildinfo
|
|
@ -1,5 +1,9 @@
|
|||
# Unreleased
|
||||
|
||||
## Added
|
||||
|
||||
- Added low stock threshold field for products
|
||||
|
||||
# 0.2.0
|
||||
|
||||
## Added
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
# Changelog
|
||||
|
||||
This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
---
|
||||
|
||||
[See legacy changelogs for previous versions](https://github.com/woocommerce/woocommerce/blob/<last-commit-hash-before-this-merge>/packages/js/api/CHANGELOG.md).
|
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"name": "woocommerce/api",
|
||||
"description": "WooCommerce API",
|
||||
"type": "library",
|
||||
"license": "GPL-3.0-or-later",
|
||||
"minimum-stability": "dev",
|
||||
"require-dev": {
|
||||
"automattic/jetpack-changelogger": "3.0.2"
|
||||
},
|
||||
"extra": {
|
||||
"changelogger": {
|
||||
"formatter": {
|
||||
"filename": "../../../tools/changelogger/PackageFormatter.php"
|
||||
},
|
||||
"types": {
|
||||
"fix": "Fixes an existing bug",
|
||||
"add": "Adds functionality",
|
||||
"update": "Update existing functionality",
|
||||
"dev": "Development related task",
|
||||
"tweak": "A minor adjustment to the codebase",
|
||||
"performance": "Address performance issues",
|
||||
"enhancement": "Improve existing functionality"
|
||||
},
|
||||
"changelog": "NEXT_CHANGELOG.md"
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -26,6 +26,7 @@
|
|||
],
|
||||
"sideEffects": false,
|
||||
"scripts": {
|
||||
"preinstall": "npx only-allow pnpm",
|
||||
"clean": "rm -rf ./dist ./tsconfig.tsbuildinfo",
|
||||
"compile": "tsc -b",
|
||||
"build": "pnpm run clean && npm run compile",
|
||||
|
@ -34,20 +35,21 @@
|
|||
"test": "jest"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "0.21.2",
|
||||
"axios": "^0.24.0",
|
||||
"create-hmac": "1.1.7",
|
||||
"oauth-1.0a": "2.2.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/create-hmac": "1.1.0",
|
||||
"@types/jest": "25.2.1",
|
||||
"@types/moxios": "^0.4.9",
|
||||
"@types/jest": "^27.0.2",
|
||||
"@types/node": "13.13.5",
|
||||
"jest": "^25.1.0",
|
||||
"jest-mock-extended": "^1.0.10",
|
||||
"moxios": "0.4.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.3.1",
|
||||
"@typescript-eslint/parser": "^5.3.1",
|
||||
"axios-mock-adapter": "^1.20.0",
|
||||
"eslint": "^8.2.0",
|
||||
"jest": "^27.3.1",
|
||||
"ts-jest": "25.5.0",
|
||||
"typescript": "3.9.7"
|
||||
"typescript": "^4.4.4"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
{
|
||||
"root": "packages/js/api/",
|
||||
"sourceRoot": "packages/js/api/src",
|
||||
"projectType": "library",
|
||||
"targets": {
|
||||
"changelog": {
|
||||
"executor": "./tools/executors/changelogger:changelog",
|
||||
"options": {
|
||||
"action": "add",
|
||||
"cwd": "packages/js/api"
|
||||
}
|
||||
},
|
||||
"build": {
|
||||
"executor": "@nrwl/workspace:run-script",
|
||||
"options": {
|
||||
"script": "build"
|
||||
}
|
||||
},
|
||||
"clean": {
|
||||
"executor": "@nrwl/workspace:run-script",
|
||||
"options": {
|
||||
"script": "clean"
|
||||
}
|
||||
},
|
||||
"compile": {
|
||||
"executor": "@nrwl/workspace:run-script",
|
||||
"options": {
|
||||
"script": "compile"
|
||||
}
|
||||
},
|
||||
"prepare": {
|
||||
"executor": "@nrwl/workspace:run-script",
|
||||
"options": {
|
||||
"script": "prepare"
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"executor": "@nrwl/workspace:run-script",
|
||||
"options": {
|
||||
"script": "lint"
|
||||
}
|
||||
},
|
||||
"test": {
|
||||
"executor": "@nrwl/workspace:run-script",
|
||||
"options": {
|
||||
"script": "test"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -101,7 +101,7 @@ describe( 'ModelRepository', () => {
|
|||
|
||||
const created = await repository.create( { parent: 'yes' }, { childName: 'test' } );
|
||||
expect( created ).toBe( model );
|
||||
expect( callback ).toHaveBeenCalledWith( { childName: 'test' } );
|
||||
expect( callback ).toHaveBeenCalledWith( { parent: 'yes' }, { childName: 'test' } );
|
||||
} );
|
||||
|
||||
it( 'should throw error on create without callback', () => {
|
||||
|
|
|
@ -1,52 +1,54 @@
|
|||
import { mocked } from 'ts-jest/utils'
|
||||
import { ModelTransformerTransformation } from '../model-transformer-transformation';
|
||||
import { ModelTransformer } from '../../model-transformer';
|
||||
import { mock, MockProxy } from 'jest-mock-extended';
|
||||
import { DummyModel } from '../../../__test_data__/dummy-model';
|
||||
|
||||
jest.mock( '../../model-transformer' );
|
||||
|
||||
describe( 'ModelTransformerTransformation', () => {
|
||||
let mockTransformer: MockProxy< ModelTransformer< any > > & ModelTransformer< any >;
|
||||
let propertyTransformer: ModelTransformer< any >;
|
||||
let transformation: ModelTransformerTransformation< any >;
|
||||
|
||||
beforeEach( () => {
|
||||
mockTransformer = mock< ModelTransformer< any > >();
|
||||
propertyTransformer = new ModelTransformer( [] );
|
||||
transformation = new ModelTransformerTransformation< DummyModel >(
|
||||
'test',
|
||||
DummyModel,
|
||||
mockTransformer,
|
||||
propertyTransformer,
|
||||
);
|
||||
} );
|
||||
|
||||
it( 'should execute child transformer', () => {
|
||||
mockTransformer.toModel.mockReturnValue( { toModel: 'Test' } );
|
||||
mocked( propertyTransformer.toModel ).mockReturnValue( { toModel: 'Test' } );
|
||||
|
||||
let transformed = transformation.toModel( { test: 'Test' } );
|
||||
|
||||
expect( transformed ).toMatchObject( { test: { toModel: 'Test' } } );
|
||||
expect( mockTransformer.toModel ).toHaveBeenCalledWith( DummyModel, 'Test' );
|
||||
expect( propertyTransformer.toModel ).toHaveBeenCalledWith( DummyModel, 'Test' );
|
||||
|
||||
mockTransformer.fromModel.mockReturnValue( { fromModel: 'Test' } );
|
||||
mocked( propertyTransformer.fromModel ).mockReturnValue( { fromModel: 'Test' } );
|
||||
|
||||
transformed = transformation.fromModel( { test: 'Test' } );
|
||||
|
||||
expect( transformed ).toMatchObject( { test: { fromModel: 'Test' } } );
|
||||
expect( mockTransformer.fromModel ).toHaveBeenCalledWith( 'Test' );
|
||||
expect( propertyTransformer.fromModel ).toHaveBeenCalledWith( 'Test' );
|
||||
} );
|
||||
|
||||
it( 'should execute child transformer on array', () => {
|
||||
mockTransformer.toModel.mockReturnValue( { toModel: 'Test' } );
|
||||
mocked( propertyTransformer.toModel ).mockReturnValue( { toModel: 'Test' } );
|
||||
|
||||
let transformed = transformation.toModel( { test: [ 'Test', 'Test2' ] } );
|
||||
|
||||
expect( transformed ).toMatchObject( { test: [ { toModel: 'Test' }, { toModel: 'Test' } ] } );
|
||||
expect( mockTransformer.toModel ).toHaveBeenCalledWith( DummyModel, 'Test' );
|
||||
expect( mockTransformer.toModel ).toHaveBeenCalledWith( DummyModel, 'Test2' );
|
||||
expect( propertyTransformer.toModel ).toHaveBeenCalledWith( DummyModel, 'Test' );
|
||||
expect( propertyTransformer.toModel ).toHaveBeenCalledWith( DummyModel, 'Test2' );
|
||||
|
||||
mockTransformer.fromModel.mockReturnValue( { fromModel: 'Test' } );
|
||||
mocked( propertyTransformer.fromModel ).mockReturnValue( { fromModel: 'Test' } );
|
||||
|
||||
transformed = transformation.fromModel( { test: [ 'Test', 'Test2' ] } );
|
||||
|
||||
expect( transformed ).toMatchObject( { test: [ { fromModel: 'Test' }, { fromModel: 'Test' } ] } );
|
||||
expect( mockTransformer.fromModel ).toHaveBeenCalledWith( 'Test' );
|
||||
expect( mockTransformer.fromModel ).toHaveBeenCalledWith( 'Test2' );
|
||||
expect( propertyTransformer.fromModel ).toHaveBeenCalledWith( 'Test' );
|
||||
expect( propertyTransformer.fromModel ).toHaveBeenCalledWith( 'Test2' );
|
||||
} );
|
||||
} );
|
||||
|
|
|
@ -1,56 +1,57 @@
|
|||
import * as moxios from 'moxios';
|
||||
import MockAdapter from 'axios-mock-adapter';
|
||||
import { AxiosClient } from '../axios-client';
|
||||
import { HTTPResponse } from '../../http-client';
|
||||
import { AxiosInterceptor } from '../axios-interceptor';
|
||||
import { mock } from 'jest-mock-extended';
|
||||
import axios from 'axios';
|
||||
|
||||
class DummyInterceptor extends AxiosInterceptor {
|
||||
public start = jest.fn();
|
||||
public stop = jest.fn();
|
||||
}
|
||||
|
||||
describe( 'AxiosClient', () => {
|
||||
let httpClient: AxiosClient;
|
||||
|
||||
beforeEach( () => {
|
||||
moxios.install();
|
||||
} );
|
||||
|
||||
afterEach( () => {
|
||||
moxios.uninstall();
|
||||
} );
|
||||
|
||||
it( 'should transform to HTTPResponse', async () => {
|
||||
const adapter = new MockAdapter( axios );
|
||||
|
||||
httpClient = new AxiosClient( { baseURL: 'http://test.test' } );
|
||||
|
||||
moxios.stubRequest( '/test', {
|
||||
status: 200,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
responseText: JSON.stringify( { test: 'value' } ),
|
||||
} );
|
||||
adapter
|
||||
.onGet( '/test' )
|
||||
.reply(
|
||||
200,
|
||||
{ test: 'value' },
|
||||
{ 'content-type': 'application/json' }
|
||||
);
|
||||
|
||||
const response = await httpClient.get( '/test' );
|
||||
|
||||
adapter.restore();
|
||||
|
||||
expect( response ).toBeInstanceOf( HTTPResponse );
|
||||
expect( response ).toHaveProperty( 'statusCode', 200 );
|
||||
expect( response ).toHaveProperty( 'headers', { 'content-type': 'application/json' } );
|
||||
expect( response ).toHaveProperty( 'headers', {
|
||||
'content-type': 'application/json',
|
||||
} );
|
||||
expect( response ).toHaveProperty( 'data', { test: 'value' } );
|
||||
} );
|
||||
|
||||
it( 'should start extra interceptors', async () => {
|
||||
const interceptor = mock< AxiosInterceptor >();
|
||||
const interceptor = new DummyInterceptor();
|
||||
|
||||
httpClient = new AxiosClient(
|
||||
{ baseURL: 'http://test.test' },
|
||||
[ interceptor ],
|
||||
);
|
||||
const adapter = new MockAdapter( axios );
|
||||
|
||||
moxios.stubRequest( '/test', {
|
||||
status: 200,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
responseText: JSON.stringify( { test: 'value' } ),
|
||||
} );
|
||||
httpClient = new AxiosClient( { baseURL: 'http://test.test' }, [
|
||||
interceptor,
|
||||
] );
|
||||
|
||||
adapter.onGet( '/test' ).reply( 200, { test: 'value' } );
|
||||
|
||||
await httpClient.get( '/test' );
|
||||
|
||||
adapter.restore();
|
||||
|
||||
expect( interceptor.start ).toHaveBeenCalled();
|
||||
} );
|
||||
} );
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import axios, { AxiosInstance } from 'axios';
|
||||
import * as moxios from 'moxios';
|
||||
import MockAdapter from 'axios-mock-adapter';
|
||||
import { AxiosInterceptor } from '../axios-interceptor';
|
||||
|
||||
class TestInterceptor extends AxiosInterceptor {}
|
||||
|
@ -7,10 +7,11 @@ class TestInterceptor extends AxiosInterceptor {}
|
|||
describe( 'AxiosInterceptor', () => {
|
||||
let interceptors: TestInterceptor[];
|
||||
let axiosInstance: AxiosInstance;
|
||||
let adapter: MockAdapter;
|
||||
|
||||
beforeEach( () => {
|
||||
axiosInstance = axios.create();
|
||||
moxios.install( axiosInstance );
|
||||
adapter = new MockAdapter( axiosInstance );
|
||||
interceptors = [];
|
||||
} );
|
||||
|
||||
|
@ -18,11 +19,11 @@ describe( 'AxiosInterceptor', () => {
|
|||
for ( const interceptor of interceptors ) {
|
||||
interceptor.stop( axiosInstance );
|
||||
}
|
||||
moxios.uninstall( axiosInstance );
|
||||
adapter.restore();
|
||||
} );
|
||||
|
||||
it( 'should not break interceptor chaining for success', async () => {
|
||||
moxios.stubRequest( 'http://test.test', { status: 200 } );
|
||||
adapter.onGet( 'http://test.test' ).reply( 200 );
|
||||
|
||||
interceptors.push( new TestInterceptor() );
|
||||
interceptors.push( new TestInterceptor() );
|
||||
|
@ -37,7 +38,7 @@ describe( 'AxiosInterceptor', () => {
|
|||
} );
|
||||
|
||||
it( 'should not break interceptor chaining for errors', async () => {
|
||||
moxios.stubRequest( 'http://test.test', { status: 401 } );
|
||||
adapter.onGet( 'http://test.test' ).reply( 401 );
|
||||
|
||||
interceptors.push( new TestInterceptor() );
|
||||
interceptors.push( new TestInterceptor() );
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
import axios, { AxiosInstance } from 'axios';
|
||||
import * as moxios from 'moxios';
|
||||
import MockAdapter from 'axios-mock-adapter';
|
||||
import { AxiosOAuthInterceptor } from '../axios-oauth-interceptor';
|
||||
|
||||
describe( 'AxiosOAuthInterceptor', () => {
|
||||
let apiAuthInterceptor: AxiosOAuthInterceptor;
|
||||
let axiosInstance: AxiosInstance;
|
||||
let adapter: MockAdapter;
|
||||
|
||||
beforeEach( () => {
|
||||
axiosInstance = axios.create();
|
||||
moxios.install( axiosInstance );
|
||||
adapter = new MockAdapter( axiosInstance );
|
||||
apiAuthInterceptor = new AxiosOAuthInterceptor(
|
||||
'consumer_key',
|
||||
'consumer_secret',
|
||||
|
@ -18,66 +19,40 @@ describe( 'AxiosOAuthInterceptor', () => {
|
|||
|
||||
afterEach( () => {
|
||||
apiAuthInterceptor.stop( axiosInstance );
|
||||
moxios.uninstall( axiosInstance );
|
||||
} );
|
||||
|
||||
it( 'should not run unless started', async () => {
|
||||
moxios.stubRequest( 'https://api.test', { status: 200 } );
|
||||
|
||||
apiAuthInterceptor.stop( axiosInstance );
|
||||
await axiosInstance.get( 'https://api.test' );
|
||||
|
||||
let request = moxios.requests.mostRecent();
|
||||
expect( request.headers ).not.toHaveProperty( 'Authorization' );
|
||||
|
||||
apiAuthInterceptor.start( axiosInstance );
|
||||
await axiosInstance.get( 'https://api.test' );
|
||||
|
||||
request = moxios.requests.mostRecent();
|
||||
expect( request.headers ).toHaveProperty( 'Authorization' );
|
||||
adapter.restore();
|
||||
} );
|
||||
|
||||
it( 'should use basic auth for HTTPS', async () => {
|
||||
moxios.stubRequest( 'https://api.test', { status: 200 } );
|
||||
await axiosInstance.get( 'https://api.test' );
|
||||
adapter.onGet( 'https://api.test' ).reply( 200 );
|
||||
const response = await axiosInstance.get( 'https://api.test' );
|
||||
|
||||
const request = moxios.requests.mostRecent();
|
||||
|
||||
expect( request.headers ).toHaveProperty( 'Authorization' );
|
||||
expect( request.headers.Authorization ).toBe(
|
||||
'Basic ' +
|
||||
Buffer.from( 'consumer_key:consumer_secret' ).toString( 'base64' ),
|
||||
);
|
||||
expect( response.config.auth ).not.toBeNull();
|
||||
expect( response.config.auth!.username ).toBe( 'consumer_key' );
|
||||
expect( response.config.auth!.password ).toBe( 'consumer_secret' );
|
||||
} );
|
||||
|
||||
it( 'should use OAuth 1.0a for HTTP', async () => {
|
||||
moxios.stubRequest( 'http://api.test', { status: 200 } );
|
||||
await axiosInstance.get( 'http://api.test' );
|
||||
|
||||
const request = moxios.requests.mostRecent();
|
||||
adapter.onGet( 'http://api.test' ).reply( 200 );
|
||||
const response = await axiosInstance.get( 'http://api.test' );
|
||||
|
||||
// We're going to assume that the oauth-1.0a package added the signature data correctly so we will
|
||||
// focus on ensuring that the header looks roughly correct given what we readily know.
|
||||
expect( request.headers ).toHaveProperty( 'Authorization' );
|
||||
expect( request.headers.Authorization ).toMatch(
|
||||
expect( response.config.headers! ).toHaveProperty( 'Authorization' );
|
||||
expect( response.config.headers!.Authorization ).toMatch(
|
||||
/^OAuth oauth_consumer_key="consumer_key".*oauth_signature_method="HMAC-SHA256".*oauth_version="1.0"/,
|
||||
);
|
||||
} );
|
||||
|
||||
it( 'should work with base URL', async () => {
|
||||
moxios.stubRequest( '/test', { status: 200 } );
|
||||
await axiosInstance.request( {
|
||||
adapter.onGet( 'https://api.test/test' ).reply( 200 );
|
||||
const response = await axiosInstance.request( {
|
||||
method: 'GET',
|
||||
baseURL: 'https://api.test/',
|
||||
url: '/test',
|
||||
} );
|
||||
|
||||
const request = moxios.requests.mostRecent();
|
||||
|
||||
expect( request.headers ).toHaveProperty( 'Authorization' );
|
||||
expect( request.headers.Authorization ).toBe(
|
||||
'Basic ' +
|
||||
Buffer.from( 'consumer_key:consumer_secret' ).toString( 'base64' ),
|
||||
);
|
||||
expect( response.config.auth ).not.toBeNull();
|
||||
expect( response.config.auth!.username ).toBe( 'consumer_key' );
|
||||
expect( response.config.auth!.password ).toBe( 'consumer_secret' );
|
||||
} );
|
||||
} );
|
||||
|
|
|
@ -1,31 +1,30 @@
|
|||
import axios, { AxiosInstance } from 'axios';
|
||||
import * as moxios from 'moxios';
|
||||
import MockAdapter from 'axios-mock-adapter';
|
||||
import { AxiosResponseInterceptor } from '../axios-response-interceptor';
|
||||
|
||||
describe( 'AxiosResponseInterceptor', () => {
|
||||
let apiResponseInterceptor: AxiosResponseInterceptor;
|
||||
let axiosInstance: AxiosInstance;
|
||||
let adapter: MockAdapter;
|
||||
|
||||
beforeEach( () => {
|
||||
axiosInstance = axios.create();
|
||||
moxios.install( axiosInstance );
|
||||
adapter = new MockAdapter( axiosInstance );
|
||||
apiResponseInterceptor = new AxiosResponseInterceptor();
|
||||
apiResponseInterceptor.start( axiosInstance );
|
||||
} );
|
||||
|
||||
afterEach( () => {
|
||||
apiResponseInterceptor.stop( axiosInstance );
|
||||
moxios.uninstall();
|
||||
adapter.restore();
|
||||
} );
|
||||
|
||||
it( 'should transform responses into an HTTPResponse', async () => {
|
||||
moxios.stubRequest( 'http://test.test', {
|
||||
status: 200,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
responseText: JSON.stringify( { test: 'value' } ),
|
||||
} );
|
||||
adapter.onGet( 'http://test.test' ).reply(
|
||||
200,
|
||||
{ test: 'value' },
|
||||
{ 'content-type': 'application/json' }
|
||||
);
|
||||
|
||||
const response = await axiosInstance.get( 'http://test.test' );
|
||||
|
||||
|
@ -41,13 +40,11 @@ describe( 'AxiosResponseInterceptor', () => {
|
|||
} );
|
||||
|
||||
it( 'should transform error responses into an HTTPResponse', async () => {
|
||||
moxios.stubRequest( 'http://test.test', {
|
||||
status: 404,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
responseText: JSON.stringify( { code: 'error_code', message: 'value' } ),
|
||||
} );
|
||||
adapter.onGet( 'http://test.test' ).reply(
|
||||
404,
|
||||
{ code: 'error_code', message: 'value' },
|
||||
{ 'content-type': 'application/json' }
|
||||
);
|
||||
|
||||
await expect( axiosInstance.get( 'http://test.test' ) ).rejects.toMatchObject( {
|
||||
statusCode: 404,
|
||||
|
@ -62,7 +59,7 @@ describe( 'AxiosResponseInterceptor', () => {
|
|||
} );
|
||||
|
||||
it( 'should bubble non-response errors', async () => {
|
||||
moxios.stubTimeout( 'http://test.test' );
|
||||
adapter.onGet( 'http://test.test' ).timeout();
|
||||
|
||||
await expect( axiosInstance.get( 'http://test.test' ) ).rejects.toMatchObject(
|
||||
new Error( 'timeout of 0ms exceeded' ),
|
||||
|
|
|
@ -1,31 +1,33 @@
|
|||
import axios, { AxiosInstance } from 'axios';
|
||||
import * as moxios from 'moxios';
|
||||
import MockAdapter from 'axios-mock-adapter';
|
||||
import { AxiosURLToQueryInterceptor } from '../axios-url-to-query-interceptor';
|
||||
|
||||
describe( 'AxiosURLToQueryInterceptor', () => {
|
||||
let urlToQueryInterceptor: AxiosURLToQueryInterceptor;
|
||||
let axiosInstance: AxiosInstance;
|
||||
let adapter: MockAdapter;
|
||||
|
||||
beforeEach( () => {
|
||||
axiosInstance = axios.create();
|
||||
moxios.install( axiosInstance );
|
||||
adapter = new MockAdapter( axiosInstance );
|
||||
urlToQueryInterceptor = new AxiosURLToQueryInterceptor( 'test' );
|
||||
urlToQueryInterceptor.start( axiosInstance );
|
||||
} );
|
||||
|
||||
afterEach( () => {
|
||||
urlToQueryInterceptor.stop( axiosInstance );
|
||||
moxios.uninstall();
|
||||
adapter.restore();
|
||||
} );
|
||||
|
||||
it( 'should put path in query string', async () => {
|
||||
moxios.stubRequest( 'http://test.test/?test=%2Ftest%2Froute', {
|
||||
status: 200,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
responseText: JSON.stringify( { test: 'value' } ),
|
||||
} );
|
||||
adapter.onGet(
|
||||
'http://test.test/',
|
||||
{ params: { test: '/test/route' } }
|
||||
).reply(
|
||||
200,
|
||||
{ test: 'value' },
|
||||
{ 'content-type': 'application/json' }
|
||||
);
|
||||
|
||||
const response = await axiosInstance.get( 'http://test.test/test/route' );
|
||||
|
||||
|
|
|
@ -50,7 +50,7 @@ export class AxiosOAuthInterceptor extends AxiosInterceptor {
|
|||
username: this.oauth.consumer.key,
|
||||
password: this.oauth.consumer.secret,
|
||||
};
|
||||
} else {
|
||||
} else if ( request.headers ) {
|
||||
request.headers.Authorization = this.oauth.toHeader(
|
||||
this.oauth.authorize( {
|
||||
url,
|
||||
|
|
|
@ -34,7 +34,7 @@ abstract class AbstractProductInventory extends Model {
|
|||
*
|
||||
* @type {StockStatus}
|
||||
*/
|
||||
public readonly stockStatus: StockStatus = ''
|
||||
public readonly stockStatus: StockStatus = '';
|
||||
|
||||
/**
|
||||
* The status of backordering for a product.
|
||||
|
@ -56,6 +56,13 @@ abstract class AbstractProductInventory extends Model {
|
|||
* @type {boolean}
|
||||
*/
|
||||
public readonly isOnBackorder: boolean = false;
|
||||
|
||||
/**
|
||||
* Indicates the threshold for when the low stock notification will be sent to the merchant.
|
||||
*
|
||||
* @type {number}
|
||||
*/
|
||||
public readonly lowStockThreshold: number = -1;
|
||||
}
|
||||
|
||||
export interface IProductInventory extends AbstractProductInventory {}
|
||||
|
|
|
@ -53,7 +53,7 @@ export type ProductGroupedUpdateParams = 'groupedProducts';
|
|||
* Properties related to tracking inventory.
|
||||
*/
|
||||
export type ProductInventoryUpdateParams = 'backorderStatus' | 'canBackorder' | 'trackInventory'
|
||||
| 'onePerOrder' | 'remainingStock';
|
||||
| 'onePerOrder' | 'remainingStock' | 'lowStockThreshold';
|
||||
|
||||
/**
|
||||
* Properties related to sales tax.
|
||||
|
|
|
@ -130,10 +130,11 @@ export class SimpleProduct extends AbstractProduct implements
|
|||
public readonly onePerOrder: boolean = false;
|
||||
public readonly trackInventory: boolean = false;
|
||||
public readonly remainingStock: number = -1;
|
||||
public readonly stockStatus: StockStatus = ''
|
||||
public readonly stockStatus: StockStatus = '';
|
||||
public readonly backorderStatus: BackorderStatus = BackorderStatus.Allowed;
|
||||
public readonly canBackorder: boolean = false;
|
||||
public readonly isOnBackorder: boolean = false;
|
||||
public readonly lowStockThreshold: number = -1;
|
||||
|
||||
/**
|
||||
* @see ./abstracts/price.ts
|
||||
|
|
|
@ -115,10 +115,11 @@ export class VariableProduct extends AbstractProduct implements
|
|||
public readonly onePerOrder: boolean = false;
|
||||
public readonly trackInventory: boolean = false;
|
||||
public readonly remainingStock: number = -1;
|
||||
public readonly stockStatus: StockStatus = ''
|
||||
public readonly stockStatus: StockStatus = '';
|
||||
public readonly backorderStatus: BackorderStatus = BackorderStatus.Allowed;
|
||||
public readonly canBackorder: boolean = false;
|
||||
public readonly isOnBackorder: boolean = false;
|
||||
public readonly lowStockThreshold: number = -1;
|
||||
|
||||
/**
|
||||
* @see ./abstracts/sales-tax.ts
|
||||
|
|
|
@ -118,6 +118,7 @@ export class ProductVariation extends AbstractProductData implements
|
|||
public readonly backorderStatus: BackorderStatus = BackorderStatus.Allowed;
|
||||
public readonly canBackorder: boolean = false;
|
||||
public readonly isOnBackorder: boolean = false;
|
||||
public readonly lowStockThreshold: number = -1;
|
||||
|
||||
/**
|
||||
* @see ./abstracts/price.ts
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { mock, MockProxy } from 'jest-mock-extended';
|
||||
import { mocked } from 'ts-jest/utils';
|
||||
import { HTTPClient, HTTPResponse } from '../../../http';
|
||||
import { ModelTransformer, ModelRepositoryParams } from '../../../framework';
|
||||
import { DummyModel } from '../../../__test_data__/dummy-model';
|
||||
|
@ -26,17 +26,25 @@ class DummyChildModel extends Model {
|
|||
}
|
||||
type DummyChildParams = ModelRepositoryParams< DummyChildModel, { parent: string }, { childSearch: string }, 'childName' >
|
||||
|
||||
jest.mock( '../../../framework/model-transformer' );
|
||||
|
||||
describe( 'Shared REST Functions', () => {
|
||||
let mockClient: MockProxy< HTTPClient >;
|
||||
let mockTransformer: MockProxy< ModelTransformer< any > > & ModelTransformer< any >;
|
||||
let mockClient: HTTPClient;
|
||||
let mockTransformer: ModelTransformer< any >;
|
||||
|
||||
beforeEach( () => {
|
||||
mockClient = mock< HTTPClient >();
|
||||
mockTransformer = mock< ModelTransformer< any > >();
|
||||
mockClient = {
|
||||
get: jest.fn(),
|
||||
post: jest.fn(),
|
||||
patch: jest.fn(),
|
||||
put: jest.fn(),
|
||||
delete: jest.fn(),
|
||||
};
|
||||
mockTransformer = new ModelTransformer( [] );
|
||||
} );
|
||||
|
||||
it( 'restList', async () => {
|
||||
mockClient.get.mockResolvedValue( new HTTPResponse(
|
||||
mocked( mockClient.get ).mockResolvedValue( new HTTPResponse(
|
||||
200,
|
||||
{},
|
||||
[
|
||||
|
@ -50,7 +58,7 @@ describe( 'Shared REST Functions', () => {
|
|||
},
|
||||
],
|
||||
) );
|
||||
mockTransformer.toModel.mockReturnValue( new DummyModel( { name: 'Test' } ) );
|
||||
mocked( mockTransformer.toModel ).mockReturnValue( new DummyModel( { name: 'Test' } ) );
|
||||
|
||||
const fn = restList< DummyModelParams >( () => 'test-url', DummyModel, mockClient, mockTransformer );
|
||||
|
||||
|
@ -65,7 +73,7 @@ describe( 'Shared REST Functions', () => {
|
|||
} );
|
||||
|
||||
it( 'restListChildren', async () => {
|
||||
mockClient.get.mockResolvedValue( new HTTPResponse(
|
||||
mocked( mockClient.get ).mockResolvedValue( new HTTPResponse(
|
||||
200,
|
||||
{},
|
||||
[
|
||||
|
@ -79,7 +87,7 @@ describe( 'Shared REST Functions', () => {
|
|||
},
|
||||
],
|
||||
) );
|
||||
mockTransformer.toModel.mockReturnValue( new DummyChildModel( { name: 'Test' } ) );
|
||||
mocked( mockTransformer.toModel ).mockReturnValue( new DummyChildModel( { name: 'Test' } ) );
|
||||
|
||||
const fn = restListChild< DummyChildParams >(
|
||||
( parent ) => 'test-url-' + parent.parent,
|
||||
|
@ -99,7 +107,7 @@ describe( 'Shared REST Functions', () => {
|
|||
} );
|
||||
|
||||
it( 'restCreate', async () => {
|
||||
mockClient.post.mockResolvedValue( new HTTPResponse(
|
||||
mocked( mockClient.post ).mockResolvedValue( new HTTPResponse(
|
||||
200,
|
||||
{},
|
||||
{
|
||||
|
@ -107,8 +115,8 @@ describe( 'Shared REST Functions', () => {
|
|||
label: 'Test 1',
|
||||
},
|
||||
) );
|
||||
mockTransformer.fromModel.mockReturnValue( { name: 'From-Test' } );
|
||||
mockTransformer.toModel.mockReturnValue( new DummyModel( { name: 'Test' } ) );
|
||||
mocked( mockTransformer.fromModel ).mockReturnValue( { name: 'From-Test' } );
|
||||
mocked( mockTransformer.toModel ).mockReturnValue( new DummyModel( { name: 'Test' } ) );
|
||||
|
||||
const fn = restCreate< DummyModelParams >(
|
||||
( properties ) => 'test-url-' + properties.name,
|
||||
|
@ -126,7 +134,7 @@ describe( 'Shared REST Functions', () => {
|
|||
} );
|
||||
|
||||
it( 'restRead', async () => {
|
||||
mockClient.get.mockResolvedValue( new HTTPResponse(
|
||||
mocked( mockClient.get ).mockResolvedValue( new HTTPResponse(
|
||||
200,
|
||||
{},
|
||||
{
|
||||
|
@ -134,7 +142,7 @@ describe( 'Shared REST Functions', () => {
|
|||
label: 'Test 1',
|
||||
},
|
||||
) );
|
||||
mockTransformer.toModel.mockReturnValue( new DummyModel( { name: 'Test' } ) );
|
||||
mocked( mockTransformer.toModel ).mockReturnValue( new DummyModel( { name: 'Test' } ) );
|
||||
|
||||
const fn = restRead< DummyModelParams >( ( id ) => 'test-url-' + id, DummyModel, mockClient, mockTransformer );
|
||||
|
||||
|
@ -146,7 +154,7 @@ describe( 'Shared REST Functions', () => {
|
|||
} );
|
||||
|
||||
it( 'restReadChildren', async () => {
|
||||
mockClient.get.mockResolvedValue( new HTTPResponse(
|
||||
mocked( mockClient.get ).mockResolvedValue( new HTTPResponse(
|
||||
200,
|
||||
{},
|
||||
{
|
||||
|
@ -154,7 +162,7 @@ describe( 'Shared REST Functions', () => {
|
|||
label: 'Test 1',
|
||||
},
|
||||
) );
|
||||
mockTransformer.toModel.mockReturnValue( new DummyChildModel( { name: 'Test' } ) );
|
||||
mocked( mockTransformer.toModel ).mockReturnValue( new DummyChildModel( { name: 'Test' } ) );
|
||||
|
||||
const fn = restReadChild< DummyChildParams >(
|
||||
( parent, id ) => 'test-url-' + parent.parent + '-' + id,
|
||||
|
@ -171,7 +179,7 @@ describe( 'Shared REST Functions', () => {
|
|||
} );
|
||||
|
||||
it( 'restUpdate', async () => {
|
||||
mockClient.patch.mockResolvedValue( new HTTPResponse(
|
||||
mocked( mockClient.patch ).mockResolvedValue( new HTTPResponse(
|
||||
200,
|
||||
{},
|
||||
{
|
||||
|
@ -179,8 +187,8 @@ describe( 'Shared REST Functions', () => {
|
|||
label: 'Test 1',
|
||||
},
|
||||
) );
|
||||
mockTransformer.fromModel.mockReturnValue( { name: 'From-Test' } );
|
||||
mockTransformer.toModel.mockReturnValue( new DummyModel( { name: 'Test' } ) );
|
||||
mocked( mockTransformer.fromModel ).mockReturnValue( { name: 'From-Test' } );
|
||||
mocked( mockTransformer.toModel ).mockReturnValue( new DummyModel( { name: 'Test' } ) );
|
||||
|
||||
const fn = restUpdate< DummyModelParams >( ( id ) => 'test-url-' + id, DummyModel, mockClient, mockTransformer );
|
||||
|
||||
|
@ -193,7 +201,7 @@ describe( 'Shared REST Functions', () => {
|
|||
} );
|
||||
|
||||
it( 'restUpdateChildren', async () => {
|
||||
mockClient.patch.mockResolvedValue( new HTTPResponse(
|
||||
mocked( mockClient.patch ).mockResolvedValue( new HTTPResponse(
|
||||
200,
|
||||
{},
|
||||
{
|
||||
|
@ -201,8 +209,8 @@ describe( 'Shared REST Functions', () => {
|
|||
label: 'Test 1',
|
||||
},
|
||||
) );
|
||||
mockTransformer.fromModel.mockReturnValue( { name: 'From-Test' } );
|
||||
mockTransformer.toModel.mockReturnValue( new DummyChildModel( { name: 'Test' } ) );
|
||||
mocked( mockTransformer.fromModel ).mockReturnValue( { name: 'From-Test' } );
|
||||
mocked( mockTransformer.toModel ).mockReturnValue( new DummyChildModel( { name: 'Test' } ) );
|
||||
|
||||
const fn = restUpdateChild< DummyChildParams >(
|
||||
( parent, id ) => 'test-url-' + parent.parent + '-' + id,
|
||||
|
@ -220,7 +228,7 @@ describe( 'Shared REST Functions', () => {
|
|||
} );
|
||||
|
||||
it( 'restDelete', async () => {
|
||||
mockClient.delete.mockResolvedValue( new HTTPResponse( 200, {}, {} ) );
|
||||
mocked( mockClient.delete ).mockResolvedValue( new HTTPResponse( 200, {}, {} ) );
|
||||
|
||||
const fn = restDelete< DummyModelParams >( ( id ) => 'test-url-' + id, mockClient );
|
||||
|
||||
|
@ -231,7 +239,7 @@ describe( 'Shared REST Functions', () => {
|
|||
} );
|
||||
|
||||
it( 'restDeleteChildren', async () => {
|
||||
mockClient.delete.mockResolvedValue( new HTTPResponse( 200, {}, {} ) );
|
||||
mocked( mockClient.delete ).mockResolvedValue( new HTTPResponse( 200, {}, {} ) );
|
||||
|
||||
const fn = restDeleteChild< DummyChildParams >(
|
||||
( parent, id ) => 'test-url-' + parent.parent + '-' + id,
|
||||
|
|
|
@ -345,6 +345,7 @@ export function createProductInventoryTransformation(): ModelTransformation[] {
|
|||
onePerOrder: PropertyType.Boolean,
|
||||
stockStatus: PropertyType.String,
|
||||
backOrderStatus: PropertyType.String,
|
||||
lowStockThreshold: PropertyType.Integer,
|
||||
},
|
||||
),
|
||||
new KeyChangeTransformation< IProductInventory >(
|
||||
|
@ -356,6 +357,7 @@ export function createProductInventoryTransformation(): ModelTransformation[] {
|
|||
backorderStatus: 'backorders',
|
||||
canBackorder: 'backorders_allowed',
|
||||
isOnBackorder: 'backordered',
|
||||
lowStockThreshold: 'low_stock_amount',
|
||||
},
|
||||
),
|
||||
];
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
import { mock, MockProxy } from 'jest-mock-extended';
|
||||
import { UpdatesSettings } from '../../models';
|
||||
import { SettingService } from '../setting-service';
|
||||
|
||||
describe( 'SettingService', () => {
|
||||
let repository: MockProxy< UpdatesSettings >;
|
||||
let repository: UpdatesSettings;
|
||||
let service: SettingService;
|
||||
|
||||
beforeEach( () => {
|
||||
repository = mock< UpdatesSettings >();
|
||||
repository = {
|
||||
update: jest.fn(),
|
||||
};
|
||||
service = new SettingService( repository );
|
||||
} );
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
{
|
||||
"extends": "../../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"types": [ "node", "jest", "axios", "moxios", "create-hmac" ],
|
||||
"rootDir": "src",
|
||||
"types": [ "node", "jest", "axios", "create-hmac" ],
|
||||
"rootDir": "src",
|
||||
"outDir": "dist",
|
||||
"target": "es5"
|
||||
},
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
/test-data/sample_products.csv
|
|
@ -1,8 +1,19 @@
|
|||
# Unreleased
|
||||
|
||||
## Fixed
|
||||
- Moved `merchant.login()` out of `beforeAll()` block and into test body for retried runs.
|
||||
|
||||
## Added
|
||||
|
||||
- This package is now transpiled with Babel, which allows the usage of modern, yet compatible JS code.
|
||||
- A `specs/data` folder to store page element data.
|
||||
- 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
|
||||
|
||||
- New coupon test deletes the coupon instead of trashing it.
|
||||
- A copy of sample_data.csv is included in the package.
|
||||
|
||||
# 0.1.6
|
||||
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
# Changelog
|
||||
|
||||
This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
---
|
||||
|
||||
[See legacy changelogs for previous versions](https://github.com/woocommerce/woocommerce/blob/<last-commit-hash-before-this-merge>/packages/js/e2e-core-tests/CHANGELOG.md).
|
|
@ -20,6 +20,18 @@ Follow [E2E setup instructions](https://github.com/woocommerce/woocommerce/blob/
|
|||
|
||||
### 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.
|
||||
- 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
|
||||
|
@ -72,6 +84,7 @@ The functions to access the core tests are:
|
|||
- `runAnalyticsPageLoadsTest` - Merchant can load and see all pages in Analytics
|
||||
- `runImportProductsTest` - Merchant can import products via CSV file
|
||||
- `runInitiateWccomConnectionTest` - Merchant can initiate connection to WooCommerce.com
|
||||
- `runAdminPageLoadTests` - Merchant can load pages from the WP Admin sidebar
|
||||
|
||||
### Shopper
|
||||
|
||||
|
@ -103,7 +116,7 @@ The functions to access the core tests are:
|
|||
|
||||
## 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
|
||||
```js
|
||||
const {
|
||||
|
@ -129,7 +142,7 @@ const 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
|
||||
const runExampleTestName = require( './grouping/example-test-name.test' );
|
||||
// ...
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
#!/usr/bin/env bash
|
||||
#
|
||||
# Copy the WooCommerce sample data file to the package
|
||||
#
|
||||
|
||||
PACKAGEPATH=$(dirname $(dirname "$0"))
|
||||
|
||||
cp -v $PACKAGEPATH/../../../plugins/woocommerce/sample-data/sample_products.csv $PACKAGEPATH/test-data
|
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"name": "woocommerce/e2e-core-tests",
|
||||
"description": "WooCommerce end to end core tests",
|
||||
"type": "library",
|
||||
"license": "GPL-3.0-or-later",
|
||||
"minimum-stability": "dev",
|
||||
"require-dev": {
|
||||
"automattic/jetpack-changelogger": "3.0.2"
|
||||
},
|
||||
"extra": {
|
||||
"changelogger": {
|
||||
"formatter": {
|
||||
"filename": "../../../tools/changelogger/PackageFormatter.php"
|
||||
},
|
||||
"types": {
|
||||
"fix": "Fixes an existing bug",
|
||||
"add": "Adds functionality",
|
||||
"update": "Update existing functionality",
|
||||
"dev": "Development related task",
|
||||
"tweak": "A minor adjustment to the codebase",
|
||||
"performance": "Address performance issues",
|
||||
"enhancement": "Improve existing functionality"
|
||||
},
|
||||
"changelog": "NEXT_CHANGELOG.md"
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -21,6 +21,7 @@ const getCoreTestsRoot = () => {
|
|||
return {
|
||||
appRoot: coreTestsPath,
|
||||
packageRoot: moduleDir,
|
||||
coreTestsRoot: __dirname
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -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',
|
||||
};
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue