Merge branch 'trunk' into add/api-t-order-search
This commit is contained in:
commit
ca5124e1d1
|
@ -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
|
||||
|
|
|
@ -28,8 +28,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.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
@ -82,7 +82,7 @@ jobs:
|
|||
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 +103,4 @@ 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
|
||||
|
|
|
@ -5,10 +5,19 @@ Thumbs.db
|
|||
# IDE files
|
||||
.idea
|
||||
.vscode/
|
||||
project.xml
|
||||
project.properties
|
||||
.project
|
||||
.settings*
|
||||
*.sublime-project
|
||||
*.sublime-workspace
|
||||
.sublimelinterrc
|
||||
|
||||
# Eslint Cache
|
||||
.eslintcache
|
||||
|
||||
# Environment files
|
||||
wp-cli.local.yml
|
||||
.wp-env.override.json
|
||||
yarn-error.log
|
||||
npm-debug.log
|
||||
.pnpm-debug.log
|
||||
|
@ -22,6 +31,7 @@ npm-debug.log
|
|||
build/
|
||||
build-module/
|
||||
build-style/
|
||||
dist/
|
||||
|
||||
# Project files
|
||||
node_modules/
|
||||
|
@ -37,5 +47,3 @@ tsconfig.tsbuildinfo
|
|||
/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/
|
||||
|
|
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"
|
||||
}
|
||||
}
|
||||
|
|
80
package.json
80
package.json
|
@ -1,39 +1,45 @@
|
|||
{
|
||||
"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": "latest",
|
||||
"@nrwl/linter": "^13.1.4",
|
||||
"@nrwl/tao": "latest",
|
||||
"@nrwl/web": "^13.1.4",
|
||||
"@nrwl/workspace": "latest",
|
||||
"@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
|
||||
|
|
|
@ -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",
|
||||
"Add",
|
||||
"Update",
|
||||
"Dev",
|
||||
"Tweak",
|
||||
"Performance",
|
||||
"Enhancement"
|
||||
],
|
||||
"changelog": "NEXT_CHANGELOG.md"
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -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,
|
||||
};
|
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.',
|
||||
|
|
|
@ -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,7 +29,8 @@ 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',
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
const {
|
||||
const {
|
||||
getRequest,
|
||||
postRequest,
|
||||
deleteRequest,
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
{
|
||||
"root": "packages/js/api-core-tests/",
|
||||
"sourceRoot": "packages/js/api-core-tests",
|
||||
"projectType": "library",
|
||||
"targets": {
|
||||
"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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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 ] );
|
||||
} );
|
||||
} );
|
||||
} );
|
||||
} );
|
||||
} );
|
|
@ -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 +1,2 @@
|
|||
|
||||
# Editors
|
||||
project.xml
|
||||
project.properties
|
||||
/nbproject/private/
|
||||
.buildpath
|
||||
.project
|
||||
.settings*
|
||||
.idea
|
||||
.vscode
|
||||
*.sublime-project
|
||||
*.sublime-workspace
|
||||
.sublimelinterrc
|
||||
|
||||
# Build Artifacts
|
||||
/node_modules/
|
||||
/dist/
|
||||
tsconfig.tsbuildinfo
|
||||
|
|
|
@ -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",
|
||||
"Add",
|
||||
"Update",
|
||||
"Dev",
|
||||
"Tweak",
|
||||
"Performance",
|
||||
"Enhancement"
|
||||
],
|
||||
"changelog": "NEXT_CHANGELOG.md"
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -34,20 +34,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,43 @@
|
|||
{
|
||||
"root": "packages/js/api/",
|
||||
"sourceRoot": "packages/js/api/src",
|
||||
"projectType": "library",
|
||||
"targets": {
|
||||
"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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,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).
|
|
@ -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",
|
||||
"Add",
|
||||
"Update",
|
||||
"Dev",
|
||||
"Tweak",
|
||||
"Performance",
|
||||
"Enhancement"
|
||||
],
|
||||
"changelog": "NEXT_CHANGELOG.md"
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"root": "packages/js/e2e-core-tests/",
|
||||
"sourceRoot": "packages/js/e2e-core-tests",
|
||||
"projectType": "library"
|
||||
}
|
|
@ -7,23 +7,19 @@ const { HTTPClientFactory, Coupon } = require( '@woocommerce/api' );
|
|||
* External dependencies
|
||||
*/
|
||||
const config = require( 'config' );
|
||||
const {
|
||||
it,
|
||||
describe,
|
||||
beforeAll,
|
||||
} = require( '@jest/globals' );
|
||||
const { it, describe, beforeAll } = require( '@jest/globals' );
|
||||
|
||||
/**
|
||||
* Create the default coupon and tests interactions with it via the API.
|
||||
*/
|
||||
const runCouponApiTest = () => {
|
||||
describe('REST API > Coupon', () => {
|
||||
describe( 'REST API > Coupon', () => {
|
||||
let client;
|
||||
let percentageCoupon;
|
||||
let coupon;
|
||||
let repository;
|
||||
|
||||
beforeAll(async () => {
|
||||
beforeAll( async () => {
|
||||
percentageCoupon = config.get( 'coupons.percentage' );
|
||||
const admin = config.get( 'users.admin' );
|
||||
const url = config.get( 'url' );
|
||||
|
@ -34,15 +30,17 @@ const runCouponApiTest = () => {
|
|||
.create();
|
||||
} );
|
||||
|
||||
it('can create a coupon', async () => {
|
||||
it( 'can create a coupon', async () => {
|
||||
repository = Coupon.restRepository( client );
|
||||
|
||||
// Check properties of the coupon in the create coupon response.
|
||||
coupon = await repository.create( percentageCoupon );
|
||||
expect( coupon ).toEqual( expect.objectContaining( percentageCoupon ) );
|
||||
});
|
||||
expect( coupon ).toEqual(
|
||||
expect.objectContaining( percentageCoupon )
|
||||
);
|
||||
} );
|
||||
|
||||
it('can retrieve a coupon', async () => {
|
||||
it( 'can retrieve a coupon', async () => {
|
||||
const couponProperties = {
|
||||
id: coupon.id,
|
||||
code: percentageCoupon.code,
|
||||
|
@ -51,12 +49,16 @@ const runCouponApiTest = () => {
|
|||
};
|
||||
|
||||
// Read coupon directly from API to compare.
|
||||
const response = await client.get( `/wc/v3/coupons/${coupon.id}` );
|
||||
expect( response.status ).toBe( 200 );
|
||||
expect( response.data ).toEqual( expect.objectContaining( couponProperties ) );
|
||||
});
|
||||
const response = await client.get(
|
||||
`/wc/v3/coupons/${ coupon.id }`
|
||||
);
|
||||
expect( response.statusCode ).toBe( 200 );
|
||||
expect( response.data ).toEqual(
|
||||
expect.objectContaining( couponProperties )
|
||||
);
|
||||
} );
|
||||
|
||||
it('can update a coupon', async () => {
|
||||
it( 'can update a coupon', async () => {
|
||||
const updatedCouponProperties = {
|
||||
amount: '75.00',
|
||||
discount_type: 'fixed_cart',
|
||||
|
@ -66,19 +68,23 @@ const runCouponApiTest = () => {
|
|||
await repository.update( coupon.id, updatedCouponProperties );
|
||||
|
||||
// Check the coupon response for the updated values.
|
||||
const response = await client.get( `/wc/v3/coupons/${coupon.id}` );
|
||||
expect( response.status ).toBe( 200 );
|
||||
expect( response.data ).toEqual( expect.objectContaining( updatedCouponProperties ) );
|
||||
});
|
||||
const response = await client.get(
|
||||
`/wc/v3/coupons/${ coupon.id }`
|
||||
);
|
||||
expect( response.statusCode ).toBe( 200 );
|
||||
expect( response.data ).toEqual(
|
||||
expect.objectContaining( updatedCouponProperties )
|
||||
);
|
||||
} );
|
||||
|
||||
it('can delete a coupon', async () => {
|
||||
it( 'can delete a coupon', async () => {
|
||||
// Delete the coupon
|
||||
const status = await repository.delete( coupon.id );
|
||||
|
||||
// If the delete is successful, the response comes back truthy
|
||||
expect( status ).toBeTruthy();
|
||||
});
|
||||
});
|
||||
} );
|
||||
} );
|
||||
};
|
||||
|
||||
module.exports = runCouponApiTest;
|
||||
|
|
|
@ -7,24 +7,20 @@ const { HTTPClientFactory, ExternalProduct } = require( '@woocommerce/api' );
|
|||
* External dependencies
|
||||
*/
|
||||
const config = require( 'config' );
|
||||
const {
|
||||
it,
|
||||
describe,
|
||||
beforeAll,
|
||||
} = require( '@jest/globals' );
|
||||
const { it, describe, beforeAll } = require( '@jest/globals' );
|
||||
|
||||
/**
|
||||
* Create an external product and retrieve via the API.
|
||||
*/
|
||||
const runExternalProductAPITest = () => {
|
||||
// @todo: add a call to ensure pretty permalinks are enabled once settings api is in use.
|
||||
describe('REST API > External Product', () => {
|
||||
describe( 'REST API > External Product', () => {
|
||||
let client;
|
||||
let defaultExternalProduct;
|
||||
let product;
|
||||
let repository;
|
||||
|
||||
beforeAll(async () => {
|
||||
beforeAll( async () => {
|
||||
defaultExternalProduct = config.get( 'products.external' );
|
||||
const admin = config.get( 'users.admin' );
|
||||
const url = config.get( 'url' );
|
||||
|
@ -35,15 +31,17 @@ const runExternalProductAPITest = () => {
|
|||
.create();
|
||||
} );
|
||||
|
||||
it('can create an external product', async () => {
|
||||
it( 'can create an external product', async () => {
|
||||
repository = ExternalProduct.restRepository( client );
|
||||
|
||||
// Check properties of product in the create product response.
|
||||
product = await repository.create( defaultExternalProduct );
|
||||
expect( product ).toEqual( expect.objectContaining( defaultExternalProduct ) );
|
||||
});
|
||||
expect( product ).toEqual(
|
||||
expect.objectContaining( defaultExternalProduct )
|
||||
);
|
||||
} );
|
||||
|
||||
it('can retrieve a raw external product', async () => {
|
||||
it( 'can retrieve a raw external product', async () => {
|
||||
const rawProperties = {
|
||||
id: product.id,
|
||||
button_text: defaultExternalProduct.buttonText,
|
||||
|
@ -52,12 +50,16 @@ const runExternalProductAPITest = () => {
|
|||
};
|
||||
|
||||
// Read product directly from api.
|
||||
const response = await client.get( `/wc/v3/products/${product.id}` );
|
||||
expect( response.status ).toBe( 200 );
|
||||
expect( response.data ).toEqual( expect.objectContaining( rawProperties ) );
|
||||
});
|
||||
const response = await client.get(
|
||||
`/wc/v3/products/${ product.id }`
|
||||
);
|
||||
expect( response.statusCode ).toBe( 200 );
|
||||
expect( response.data ).toEqual(
|
||||
expect.objectContaining( rawProperties )
|
||||
);
|
||||
} );
|
||||
|
||||
it('can retrieve a transformed external product', async () => {
|
||||
it( 'can retrieve a transformed external product', async () => {
|
||||
const transformedProperties = {
|
||||
...defaultExternalProduct,
|
||||
id: product.id,
|
||||
|
@ -66,14 +68,16 @@ const runExternalProductAPITest = () => {
|
|||
|
||||
// Read product via the repository.
|
||||
const transformed = await repository.read( product.id );
|
||||
expect( transformed ).toEqual( expect.objectContaining( transformedProperties ) );
|
||||
});
|
||||
expect( transformed ).toEqual(
|
||||
expect.objectContaining( transformedProperties )
|
||||
);
|
||||
} );
|
||||
|
||||
it('can delete an external product', async () => {
|
||||
it( 'can delete an external product', async () => {
|
||||
const status = repository.delete( product.id );
|
||||
expect( status ).toBeTruthy();
|
||||
});
|
||||
});
|
||||
} );
|
||||
} );
|
||||
};
|
||||
|
||||
module.exports = runExternalProductAPITest;
|
||||
|
|
|
@ -1,32 +1,32 @@
|
|||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
const { HTTPClientFactory, GroupedProduct, SimpleProduct } = require( '@woocommerce/api' );
|
||||
const {
|
||||
HTTPClientFactory,
|
||||
GroupedProduct,
|
||||
SimpleProduct,
|
||||
} = require( '@woocommerce/api' );
|
||||
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
const config = require( 'config' );
|
||||
const {
|
||||
it,
|
||||
describe,
|
||||
beforeAll,
|
||||
} = require( '@jest/globals' );
|
||||
const { it, describe, beforeAll } = require( '@jest/globals' );
|
||||
|
||||
/**
|
||||
* Create an external product and retrieve via the API.
|
||||
*/
|
||||
const runGroupedProductAPITest = () => {
|
||||
// @todo: add a call to ensure pretty permalinks are enabled once settings api is in use.
|
||||
describe('REST API > Grouped Product', () => {
|
||||
describe( 'REST API > Grouped Product', () => {
|
||||
let client;
|
||||
let defaultGroupedProduct;
|
||||
let baseGroupedProduct;
|
||||
let product;
|
||||
let groupedProducts = [];
|
||||
const groupedProducts = [];
|
||||
let repository;
|
||||
|
||||
beforeAll(async () => {
|
||||
beforeAll( async () => {
|
||||
defaultGroupedProduct = config.get( 'products.grouped' );
|
||||
const admin = config.get( 'users.admin' );
|
||||
const url = config.get( 'url' );
|
||||
|
@ -38,13 +38,19 @@ const runGroupedProductAPITest = () => {
|
|||
|
||||
// Create the simple products to be grouped first.
|
||||
repository = SimpleProduct.restRepository( client );
|
||||
for ( let c = 0; c < defaultGroupedProduct.groupedProducts.length; c++ ) {
|
||||
product = await repository.create( defaultGroupedProduct.groupedProducts[ c ] );
|
||||
for (
|
||||
let c = 0;
|
||||
c < defaultGroupedProduct.groupedProducts.length;
|
||||
c++
|
||||
) {
|
||||
product = await repository.create(
|
||||
defaultGroupedProduct.groupedProducts[ c ]
|
||||
);
|
||||
groupedProducts.push( product.id );
|
||||
}
|
||||
});
|
||||
} );
|
||||
|
||||
it('can create a grouped product', async () => {
|
||||
it( 'can create a grouped product', async () => {
|
||||
baseGroupedProduct = {
|
||||
...defaultGroupedProduct,
|
||||
groupedProducts,
|
||||
|
@ -53,38 +59,46 @@ const runGroupedProductAPITest = () => {
|
|||
|
||||
// Check properties of product in the create product response.
|
||||
product = await repository.create( baseGroupedProduct );
|
||||
expect( product ).toEqual( expect.objectContaining( baseGroupedProduct ) );
|
||||
});
|
||||
expect( product ).toEqual(
|
||||
expect.objectContaining( baseGroupedProduct )
|
||||
);
|
||||
} );
|
||||
|
||||
it('can retrieve a raw grouped product', async () => {
|
||||
let rawProperties = {
|
||||
it( 'can retrieve a raw grouped product', async () => {
|
||||
const rawProperties = {
|
||||
id: product.id,
|
||||
grouped_products: baseGroupedProduct.groupedProducts,
|
||||
...defaultGroupedProduct,
|
||||
};
|
||||
delete rawProperties['groupedProducts'];
|
||||
delete rawProperties.groupedProducts;
|
||||
|
||||
// Read product directly from api.
|
||||
const response = await client.get( `/wc/v3/products/${product.id}` );
|
||||
expect( response.status ).toBe( 200 );
|
||||
expect( response.data ).toEqual( expect.objectContaining( rawProperties ) );
|
||||
});
|
||||
const response = await client.get(
|
||||
`/wc/v3/products/${ product.id }`
|
||||
);
|
||||
expect( response.statusCode ).toBe( 200 );
|
||||
expect( response.data ).toEqual(
|
||||
expect.objectContaining( rawProperties )
|
||||
);
|
||||
} );
|
||||
|
||||
it('can retrieve a transformed grouped product', async () => {
|
||||
it( 'can retrieve a transformed grouped product', async () => {
|
||||
// Read product via the repository.
|
||||
const transformed = await repository.read( product.id );
|
||||
expect( transformed ).toEqual( expect.objectContaining( baseGroupedProduct ) );
|
||||
});
|
||||
expect( transformed ).toEqual(
|
||||
expect.objectContaining( baseGroupedProduct )
|
||||
);
|
||||
} );
|
||||
|
||||
it('can delete a grouped product', async () => {
|
||||
it( 'can delete a grouped product', async () => {
|
||||
const status = repository.delete( product.id );
|
||||
expect( status ).toBeTruthy();
|
||||
// Delete the simple "child" products.
|
||||
groupedProducts.forEach( ( productId ) => {
|
||||
repository.delete( productId );
|
||||
});
|
||||
});
|
||||
});
|
||||
} );
|
||||
} );
|
||||
} );
|
||||
};
|
||||
|
||||
module.exports = runGroupedProductAPITest;
|
||||
|
|
|
@ -1,28 +1,24 @@
|
|||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
const { HTTPClientFactory, Order } = require( '@woocommerce/api' );
|
||||
const { HTTPClientFactory, Order } = require( '@woocommerce/api' );
|
||||
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
const config = require( 'config' );
|
||||
const {
|
||||
it,
|
||||
describe,
|
||||
beforeAll,
|
||||
} = require( '@jest/globals' );
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
const config = require( 'config' );
|
||||
const { it, describe, beforeAll } = require( '@jest/globals' );
|
||||
|
||||
/**
|
||||
/**
|
||||
* Creates an order and tests interactions with it via the API.
|
||||
*/
|
||||
const runOrderApiTest = () => {
|
||||
describe('REST API > Order', () => {
|
||||
describe( 'REST API > Order', () => {
|
||||
let client;
|
||||
let order;
|
||||
let repository;
|
||||
|
||||
beforeAll(async () => {
|
||||
beforeAll( async () => {
|
||||
order = config.get( 'orders.basicPaidOrder' );
|
||||
const admin = config.get( 'users.admin' );
|
||||
const url = config.get( 'url' );
|
||||
|
@ -33,15 +29,15 @@ const runOrderApiTest = () => {
|
|||
.create();
|
||||
} );
|
||||
|
||||
it('can create an order', async () => {
|
||||
it( 'can create an order', async () => {
|
||||
repository = Order.restRepository( client );
|
||||
|
||||
// Check properties of the order in the create order response.
|
||||
order = await repository.create( order );
|
||||
expect( order ).toEqual( expect.objectContaining( order ) );
|
||||
});
|
||||
} );
|
||||
|
||||
it('can retrieve an order', async () => {
|
||||
it( 'can retrieve an order', async () => {
|
||||
const orderProperties = {
|
||||
id: order.id,
|
||||
payment_method: order.paymentMethod,
|
||||
|
@ -49,12 +45,14 @@ const runOrderApiTest = () => {
|
|||
};
|
||||
|
||||
// Read order directly from API to compare.
|
||||
const response = await client.get( `/wc/v3/orders/${order.id}` );
|
||||
expect( response.status ).toBe( 200 );
|
||||
expect( response.data ).toEqual( expect.objectContaining( orderProperties ) );
|
||||
});
|
||||
const response = await client.get( `/wc/v3/orders/${ order.id }` );
|
||||
expect( response.statusCode ).toBe( 200 );
|
||||
expect( response.data ).toEqual(
|
||||
expect.objectContaining( orderProperties )
|
||||
);
|
||||
} );
|
||||
|
||||
it('can update an order', async () => {
|
||||
it( 'can update an order', async () => {
|
||||
const updatedOrderProperties = {
|
||||
payment_method: 'bacs',
|
||||
status: 'completed',
|
||||
|
@ -63,19 +61,21 @@ const runOrderApiTest = () => {
|
|||
await repository.update( order.id, updatedOrderProperties );
|
||||
|
||||
// Check the order response for the updated values.
|
||||
const response = await client.get( `/wc/v3/orders/${order.id}` );
|
||||
expect( response.status ).toBe( 200 );
|
||||
expect( response.data ).toEqual( expect.objectContaining( updatedOrderProperties ) );
|
||||
});
|
||||
const response = await client.get( `/wc/v3/orders/${ order.id }` );
|
||||
expect( response.statusCode ).toBe( 200 );
|
||||
expect( response.data ).toEqual(
|
||||
expect.objectContaining( updatedOrderProperties )
|
||||
);
|
||||
} );
|
||||
|
||||
it('can delete an order', async () => {
|
||||
it( 'can delete an order', async () => {
|
||||
// Delete the order
|
||||
const status = await repository.delete( order.id );
|
||||
|
||||
// If the delete is successful, the response comes back truthy
|
||||
expect( status ).toBeTruthy();
|
||||
});
|
||||
});
|
||||
} );
|
||||
} );
|
||||
};
|
||||
|
||||
module.exports = runOrderApiTest;
|
||||
|
|
|
@ -7,11 +7,7 @@ const { HTTPClientFactory } = require( '@woocommerce/api' );
|
|||
* External dependencies
|
||||
*/
|
||||
const config = require( 'config' );
|
||||
const {
|
||||
it,
|
||||
describe,
|
||||
beforeAll,
|
||||
} = require( '@jest/globals' );
|
||||
const { it, describe, beforeAll } = require( '@jest/globals' );
|
||||
|
||||
/**
|
||||
* Create the default coupon and tests interactions with it via the API.
|
||||
|
@ -20,7 +16,7 @@ const runTelemetryAPITest = () => {
|
|||
describe( 'REST API > Telemetry', () => {
|
||||
let client;
|
||||
|
||||
beforeAll(async () => {
|
||||
beforeAll( async () => {
|
||||
const admin = config.get( 'users.admin' );
|
||||
const url = config.get( 'url' );
|
||||
|
||||
|
@ -30,29 +26,26 @@ const runTelemetryAPITest = () => {
|
|||
.create();
|
||||
} );
|
||||
|
||||
it.each([
|
||||
null,
|
||||
{},
|
||||
{ platform: 'ios' },
|
||||
{ version: '1.1' },
|
||||
])( 'errors for invalid request body - %p', async data => {
|
||||
const response = await client
|
||||
.post( `/wc-telemetry/tracker`, data )
|
||||
.catch( err => {
|
||||
expect( err.response.status ).toBe( 400 );
|
||||
} );
|
||||
it.each( [ null, {}, { platform: 'ios' }, { version: '1.1' } ] )(
|
||||
'errors for invalid request body - %p',
|
||||
async ( data ) => {
|
||||
const response = await client
|
||||
.post( `/wc-telemetry/tracker`, data )
|
||||
.catch( ( err ) => {
|
||||
expect( err.statusCode ).toBe( 400 );
|
||||
} );
|
||||
|
||||
expect( response ).toBeUndefined();
|
||||
} );
|
||||
expect( response ).toBeUndefined();
|
||||
}
|
||||
);
|
||||
|
||||
it( 'returns 200 with correct fields', async () => {
|
||||
const response = await client
|
||||
.post( `/wc-telemetry/tracker`, {
|
||||
platform: 'ios',
|
||||
version: '1.0',
|
||||
})
|
||||
const response = await client.post( `/wc-telemetry/tracker`, {
|
||||
platform: 'ios',
|
||||
version: '1.0',
|
||||
} );
|
||||
|
||||
expect( response.status ).toBe( 200 );
|
||||
expect( response.statusCode ).toBe( 200 );
|
||||
} );
|
||||
} );
|
||||
};
|
||||
|
|
|
@ -1,83 +0,0 @@
|
|||
/* eslint-disable jest/no-export, jest/no-disabled-tests */
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
const {
|
||||
merchant,
|
||||
completeOnboardingWizard,
|
||||
withRestApi,
|
||||
addShippingZoneAndMethod,
|
||||
IS_RETEST_MODE,
|
||||
} = require( '@woocommerce/e2e-utils' );
|
||||
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
const config = require( 'config' );
|
||||
const {
|
||||
it,
|
||||
describe,
|
||||
} = require( '@jest/globals' );
|
||||
|
||||
const shippingZoneNameUS = config.get( 'addresses.customer.shipping.country' );
|
||||
|
||||
const runOnboardingFlowTest = () => {
|
||||
describe('Store owner can go through store Onboarding', () => {
|
||||
if ( IS_RETEST_MODE ) {
|
||||
it('can reset onboarding to default settings', async () => {
|
||||
await withRestApi.resetOnboarding();
|
||||
});
|
||||
|
||||
it('can reset shipping zones to default settings', async () => {
|
||||
await withRestApi.deleteAllShippingZones();
|
||||
});
|
||||
|
||||
it('can reset to default settings', async () => {
|
||||
await withRestApi.resetSettingsGroupToDefault('general');
|
||||
await withRestApi.resetSettingsGroupToDefault('products');
|
||||
await withRestApi.resetSettingsGroupToDefault('tax');
|
||||
});
|
||||
}
|
||||
|
||||
it('can start and complete onboarding when visiting the site for the first time.', async () => {
|
||||
await completeOnboardingWizard();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const runTaskListTest = () => {
|
||||
describe('Store owner can go through setup Task List', () => {
|
||||
it('can setup shipping', async () => {
|
||||
await page.evaluate(() => {
|
||||
document.querySelector('.woocommerce-list__item-title').scrollIntoView();
|
||||
});
|
||||
// Query for all tasks on the list
|
||||
const taskListItems = await page.$$('.woocommerce-list__item-title');
|
||||
expect(taskListItems.length).toBeInRange( 5, 6 );
|
||||
|
||||
// Work around for https://github.com/woocommerce/woocommerce-admin/issues/6761
|
||||
if ( taskListItems.length == 6 ) {
|
||||
// Click on "Set up shipping" task to move to the next step
|
||||
const [ setupTaskListItem ] = await page.$x( '//div[contains(text(),"Set up shipping")]' );
|
||||
await setupTaskListItem.click();
|
||||
|
||||
// Wait for "Proceed" button to become active
|
||||
await page.waitForSelector('button.is-primary:not(:disabled)');
|
||||
await page.waitFor(3000);
|
||||
|
||||
// Click on "Proceed" button to save shipping settings
|
||||
await page.click('button.is-primary');
|
||||
await page.waitFor(3000);
|
||||
} else {
|
||||
await merchant.openNewShipping();
|
||||
await addShippingZoneAndMethod(shippingZoneNameUS);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
runOnboardingFlowTest,
|
||||
runTaskListTest,
|
||||
};
|
|
@ -5,9 +5,8 @@ const {
|
|||
merchant,
|
||||
clickTab,
|
||||
AdminEdit,
|
||||
factories,
|
||||
withRestApi,
|
||||
} = require( '@woocommerce/e2e-utils' );
|
||||
const { Coupon } = require( '@woocommerce/api' );
|
||||
|
||||
/**
|
||||
* External dependencies
|
||||
|
@ -50,8 +49,7 @@ const runCreateCouponTest = () => {
|
|||
// Delete the coupon
|
||||
const couponId = await adminEdit.getId();
|
||||
if ( couponId ) {
|
||||
const repository = Coupon.restRepository( factories.api.withDefaultPermalinks );
|
||||
await repository.delete( couponId );
|
||||
await withRestApi.deleteCoupon( couponId );
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
@ -21,7 +21,13 @@ const runInitiateWccomConnectionTest = () => {
|
|||
});
|
||||
|
||||
it.skip('can initiate WCCOM connection', async () => {
|
||||
await merchant.openHelper();
|
||||
await merchant.openExtensions();
|
||||
|
||||
// Click on a tab to choose WooCommerce Subscriptions extension
|
||||
await Promise.all( [
|
||||
expect( page ).toClick( 'a.nav-tab', { text: "WooCommerce.com Subscriptions" } ),
|
||||
page.waitForNavigation( { waitUntil: 'networkidle0' } ),
|
||||
] );
|
||||
|
||||
// Click on Connect button to initiate a WCCOM connection
|
||||
await Promise.all([
|
||||
|
|
|
@ -4,9 +4,10 @@
|
|||
const {
|
||||
merchant,
|
||||
uiUnblocked,
|
||||
withRestApi,
|
||||
AdminEdit,
|
||||
} = require('@woocommerce/e2e-utils');
|
||||
const config = require('config');
|
||||
} = require( '@woocommerce/e2e-utils' );
|
||||
const config = require( 'config' );
|
||||
const {
|
||||
HTTPClientFactory,
|
||||
VariableProduct,
|
||||
|
@ -14,22 +15,34 @@ const {
|
|||
SimpleProduct,
|
||||
ProductVariation,
|
||||
ExternalProduct
|
||||
} = require('@woocommerce/api');
|
||||
} = require( '@woocommerce/api' );
|
||||
|
||||
const taxClasses = [
|
||||
{
|
||||
name: 'Tax Class Simple',
|
||||
},
|
||||
{
|
||||
name: 'Tax Class Variable',
|
||||
},
|
||||
{
|
||||
name: 'Tax Class External',
|
||||
},
|
||||
];
|
||||
|
||||
const taxRates = [
|
||||
{
|
||||
name: 'Tax Rate Simple',
|
||||
rate: '10',
|
||||
rate: '10.0000',
|
||||
class: 'tax-class-simple'
|
||||
},
|
||||
{
|
||||
name: 'Tax Rate Variable',
|
||||
rate: '20',
|
||||
rate: '20.0000',
|
||||
class: 'tax-class-variable'
|
||||
},
|
||||
{
|
||||
name: 'Tax Rate External',
|
||||
rate: '30',
|
||||
rate: '30.0000',
|
||||
class: 'tax-class-external'
|
||||
}
|
||||
];
|
||||
|
@ -43,60 +56,22 @@ const initProducts = async () => {
|
|||
const httpClient = HTTPClientFactory.build(apiUrl)
|
||||
.withBasicAuth(adminUsername, adminPassword)
|
||||
.create();
|
||||
const taxClassesPath = '/wc/v3/taxes/classes';
|
||||
const taxClasses = [
|
||||
{
|
||||
name: 'Tax Class Simple',
|
||||
slug: 'tax-class-simple-698962'
|
||||
},
|
||||
{
|
||||
name: 'Tax Class Variable',
|
||||
slug: 'tax-class-variable-790238'
|
||||
},
|
||||
{
|
||||
name: 'Tax Class External',
|
||||
slug: 'tax-class-external-991321'
|
||||
}
|
||||
];
|
||||
|
||||
// Enable taxes in settings
|
||||
const enableTaxes = async () => {
|
||||
const path = '/wc/v3/settings/general/woocommerce_calc_taxes';
|
||||
const data = {
|
||||
value: 'yes'
|
||||
};
|
||||
await httpClient.put(path, data);
|
||||
};
|
||||
await enableTaxes();
|
||||
|
||||
// Initialize tax classes
|
||||
const initTaxClasses = async () => {
|
||||
for (const classToBeAdded of taxClasses) {
|
||||
await httpClient.post(taxClassesPath, classToBeAdded);
|
||||
}
|
||||
};
|
||||
await initTaxClasses();
|
||||
|
||||
// Initialize tax rates
|
||||
const initTaxRates = async () => {
|
||||
const path = '/wc/v3/taxes';
|
||||
|
||||
for (const rateToBeAdded of taxRates) {
|
||||
await httpClient.post(path, rateToBeAdded);
|
||||
}
|
||||
};
|
||||
await initTaxRates();
|
||||
await withRestApi.updateSettingOption( 'general', 'woocommerce_calc_taxes', { value: 'yes' } );
|
||||
await withRestApi.addTaxClasses( taxClasses );
|
||||
await withRestApi.addTaxRates( taxRates );
|
||||
|
||||
// Initialization functions per product type
|
||||
const initSimpleProduct = async () => {
|
||||
const repo = SimpleProduct.restRepository(httpClient);
|
||||
const repo = SimpleProduct.restRepository( httpClient );
|
||||
const simpleProduct = {
|
||||
name: 'Simple Product 273722',
|
||||
regularPrice: '100',
|
||||
taxClass: 'Tax Class Simple'
|
||||
};
|
||||
return await repo.create(simpleProduct);
|
||||
return await repo.create( simpleProduct );
|
||||
};
|
||||
|
||||
const initVariableProduct = async () => {
|
||||
const variations = [
|
||||
{
|
||||
|
@ -264,12 +239,12 @@ const runCreateOrderTest = () => {
|
|||
}
|
||||
|
||||
// Verify that the names of each tax class were shown
|
||||
for (const { name } of taxRates) {
|
||||
for (const taxRate of taxRates) {
|
||||
await expect(page).toMatchElement('th.line_tax', {
|
||||
text: name
|
||||
text: taxRate.name
|
||||
});
|
||||
await expect(page).toMatchElement('.wc-order-totals td.label', {
|
||||
text: name
|
||||
text: taxRate.name
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -9,32 +9,12 @@ const {
|
|||
uiUnblocked,
|
||||
evalAndClick,
|
||||
createOrder,
|
||||
clickAndWaitForSelector,
|
||||
} = require( '@woocommerce/e2e-utils' );
|
||||
|
||||
const { waitForSelector } = require( '@woocommerce/e2e-environment' );
|
||||
|
||||
const config = require( 'config' );
|
||||
const simpleProductPrice = config.has('products.simple.price') ? config.get('products.simple.price') : '9.99';
|
||||
|
||||
/**
|
||||
* Evaluate and click a button selector then wait for a result selector.
|
||||
* This is a work around for what appears to be intermittent delays in handling confirm dialogs.
|
||||
*
|
||||
* @param buttonSelector
|
||||
* @param resultSelector
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
const clickAndWaitForSelector = async ( buttonSelector, resultSelector ) => {
|
||||
await evalAndClick( buttonSelector );
|
||||
await waitForSelector(
|
||||
page,
|
||||
resultSelector,
|
||||
{
|
||||
timeout: 5000
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
const runRefundOrderTest = () => {
|
||||
describe('WooCommerce Orders > Refund an order', () => {
|
||||
let productId;
|
||||
|
|
|
@ -51,7 +51,7 @@ const updateCustomerBilling = async () => {
|
|||
search: 'Jane',
|
||||
role: 'all',
|
||||
} );
|
||||
if ( ! customers.data | ! customers.data.length ) {
|
||||
if ( ! customers.data || ! customers.data.length ) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -45,7 +45,7 @@ const runOrderStatusFiltersTest = () => {
|
|||
});
|
||||
|
||||
// Create the orders using the API
|
||||
await withRestApi.batchCreateOrders(orders);
|
||||
await withRestApi.batchCreateOrders( orders, false );
|
||||
|
||||
// Next, let's login and navigate to the Orders page
|
||||
await merchant.login();
|
||||
|
|
|
@ -107,9 +107,9 @@ const runImportProductsTest = () => {
|
|||
afterAll(async () => {
|
||||
// Delete imported products
|
||||
await withRestApi.deleteAllProducts();
|
||||
await withRestApi.deleteAllProductAttributes();
|
||||
await withRestApi.deleteAllProductCategories();
|
||||
await withRestApi.deleteAllProductTags();
|
||||
await withRestApi.deleteAllProductAttributes( false );
|
||||
await withRestApi.deleteAllProductCategories( false );
|
||||
await withRestApi.deleteAllProductTags( false );
|
||||
});
|
||||
|
||||
it( 'should show error message if you go without providing CSV file', async () => {
|
||||
|
|
|
@ -14,7 +14,7 @@ const runAddShippingClassesTest = () => {
|
|||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await withRestApi.deleteAllShippingClasses();
|
||||
await withRestApi.deleteAllShippingClasses( false );
|
||||
});
|
||||
|
||||
it('can add shipping classes', async () => {
|
||||
|
|
|
@ -35,7 +35,7 @@ const runAddNewShippingZoneTest = () => {
|
|||
|
||||
beforeAll(async () => {
|
||||
productId = await createSimpleProduct();
|
||||
await withRestApi.deleteAllShippingZones();
|
||||
await withRestApi.deleteAllShippingZones( false );
|
||||
await merchant.login();
|
||||
});
|
||||
|
||||
|
|
|
@ -42,19 +42,19 @@ const runCartCalculateShippingTest = () => {
|
|||
firstProductId = await createSimpleProduct(firstProductName);
|
||||
secondProductId = await createSimpleProduct(secondProductName, secondProductPrice);
|
||||
|
||||
await withRestApi.resetSettingsGroupToDefault( 'general' );
|
||||
await withRestApi.resetSettingsGroupToDefault( 'general', false );
|
||||
|
||||
// Add a new shipping zone Germany with Free shipping
|
||||
await withRestApi.addShippingZoneAndMethod(shippingZoneNameDE, shippingCountryDE, ' ', 'free_shipping');
|
||||
await withRestApi.addShippingZoneAndMethod(shippingZoneNameDE, shippingCountryDE, ' ', 'free_shipping', '', [], false );
|
||||
|
||||
// Add a new shipping zone for France with Flat rate & Local pickup
|
||||
await withRestApi.addShippingZoneAndMethod(shippingZoneNameFR, shippingCountryFR, ' ', 'flat_rate', '5', ['local_pickup']);
|
||||
await withRestApi.addShippingZoneAndMethod(shippingZoneNameFR, shippingCountryFR, ' ', 'flat_rate', '5', ['local_pickup'], false );
|
||||
|
||||
await shopper.emptyCart();
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await withRestApi.deleteAllShippingZones();
|
||||
await withRestApi.deleteAllShippingZones( false );
|
||||
});
|
||||
|
||||
it('allows customer to calculate Free Shipping if in Germany', async () => {
|
||||
|
|
|
@ -9,6 +9,7 @@ const {
|
|||
applyCoupon,
|
||||
removeCoupon,
|
||||
} = require( '@woocommerce/e2e-utils' );
|
||||
const { getCouponId, getCouponsTable } = require( '../utils/coupons' );
|
||||
|
||||
/**
|
||||
* External dependencies
|
||||
|
@ -19,36 +20,12 @@ const {
|
|||
beforeAll,
|
||||
} = require( '@jest/globals' );
|
||||
|
||||
const couponsTable = [
|
||||
['fixed cart', { text: '$5.00' }, { text: '$4.99' }],
|
||||
['percentage', { text: '$4.99' }, { text: '$5.00' }],
|
||||
['fixed product', { text: '$5.00' }, { text: '$4.99' }]
|
||||
];
|
||||
|
||||
let couponFixedCart;
|
||||
let couponPercentage;
|
||||
let couponFixedProduct;
|
||||
|
||||
const getCoupon = (couponType) => {
|
||||
switch (couponType) {
|
||||
case 'fixed cart':
|
||||
return couponFixedCart;
|
||||
case 'percentage':
|
||||
return couponPercentage;
|
||||
case 'fixed product':
|
||||
return couponFixedProduct;
|
||||
}
|
||||
};
|
||||
|
||||
const runCartApplyCouponsTest = () => {
|
||||
describe('Cart applying coupons', () => {
|
||||
let productId;
|
||||
|
||||
beforeAll(async () => {
|
||||
productId = await createSimpleProduct();
|
||||
couponFixedCart = await createCoupon();
|
||||
couponPercentage = await createCoupon('50', 'Percentage discount');
|
||||
couponFixedProduct = await createCoupon('5', 'Fixed product discount');
|
||||
await shopper.emptyCart();
|
||||
await shopper.goToShop();
|
||||
await shopper.addToCartFromShopPage( productId );
|
||||
|
@ -56,8 +33,8 @@ const runCartApplyCouponsTest = () => {
|
|||
await shopper.goToCart();
|
||||
});
|
||||
|
||||
it.each(couponsTable)('allows cart to apply %s coupon', async (couponType, cartDiscount, orderTotal) => {
|
||||
const coupon = getCoupon(couponType);
|
||||
it.each( getCouponsTable() )( 'allows cart to apply %s coupon', async ( couponType, cartDiscount, orderTotal ) => {
|
||||
const coupon = await getCouponId( couponType );
|
||||
await applyCoupon(coupon);
|
||||
await expect(page).toMatchElement('.woocommerce-message', { text: 'Coupon code applied successfully.' });
|
||||
|
||||
|
@ -69,9 +46,10 @@ const runCartApplyCouponsTest = () => {
|
|||
});
|
||||
|
||||
it('prevents cart applying same coupon twice', async () => {
|
||||
await applyCoupon(couponFixedCart);
|
||||
const couponId = await getCouponId( 'fixed cart' );
|
||||
await applyCoupon( couponId );
|
||||
await expect(page).toMatchElement('.woocommerce-message', {text: 'Coupon code applied successfully.'});
|
||||
await applyCoupon(couponFixedCart);
|
||||
await applyCoupon( couponId );
|
||||
// Verify only one discount applied
|
||||
// This is a work around for Puppeteer inconsistently finding 'Coupon code already applied'
|
||||
await expect(page).toMatchElement('.cart-discount .amount', {text: '$5.00'});
|
||||
|
@ -79,7 +57,7 @@ const runCartApplyCouponsTest = () => {
|
|||
});
|
||||
|
||||
it('allows cart to apply multiple coupons', async () => {
|
||||
await applyCoupon(couponFixedProduct);
|
||||
await applyCoupon( await getCouponId( 'fixed product' ) );
|
||||
await expect(page).toMatchElement('.woocommerce-message', {text: 'Coupon code applied successfully.'});
|
||||
|
||||
// Verify discount applied and order total
|
||||
|
@ -88,8 +66,8 @@ const runCartApplyCouponsTest = () => {
|
|||
});
|
||||
|
||||
it('restores cart total when coupons are removed', async () => {
|
||||
await removeCoupon(couponFixedCart);
|
||||
await removeCoupon(couponFixedProduct);
|
||||
await removeCoupon( await getCouponId( 'fixed cart' ) );
|
||||
await removeCoupon( await getCouponId( 'fixed product' ) );
|
||||
await expect(page).toMatchElement('.order-total .amount', {text: '$9.99'});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -28,9 +28,9 @@ const runCartPageTest = () => {
|
|||
|
||||
beforeAll(async () => {
|
||||
productId = await createSimpleProduct();
|
||||
await withRestApi.resetSettingsGroupToDefault('general');
|
||||
await withRestApi.resetSettingsGroupToDefault('products');
|
||||
await withRestApi.resetSettingsGroupToDefault('tax');
|
||||
await withRestApi.resetSettingsGroupToDefault( 'general', false );
|
||||
await withRestApi.resetSettingsGroupToDefault( 'products', false );
|
||||
await withRestApi.resetSettingsGroupToDefault( 'tax', false );
|
||||
});
|
||||
|
||||
it('should display no item in the cart', async () => {
|
||||
|
|
|
@ -10,6 +10,7 @@ const {
|
|||
removeCoupon,
|
||||
waitForSelectorWithoutThrow,
|
||||
} = require( '@woocommerce/e2e-utils' );
|
||||
const { getCouponId, getCouponsTable } = require( '../utils/coupons' );
|
||||
|
||||
/**
|
||||
* External dependencies
|
||||
|
@ -20,26 +21,6 @@ const {
|
|||
beforeAll,
|
||||
} = require( '@jest/globals' );
|
||||
|
||||
const couponsTable = [
|
||||
['fixed cart', { text: '$5.00' }, { text: '$4.99' }],
|
||||
['percentage', { text: '$4.99' }, { text: '$5.00' }],
|
||||
['fixed product', { text: '$5.00' }, { text: '$4.99' }]
|
||||
];
|
||||
|
||||
let couponFixedCart;
|
||||
let couponPercentage;
|
||||
let couponFixedProduct;
|
||||
|
||||
const getCoupon = (couponType) => {
|
||||
switch (couponType) {
|
||||
case 'fixed cart':
|
||||
return couponFixedCart;
|
||||
case 'percentage':
|
||||
return couponPercentage;
|
||||
case 'fixed product':
|
||||
return couponFixedProduct;
|
||||
}
|
||||
};
|
||||
|
||||
const runCheckoutApplyCouponsTest = () => {
|
||||
describe('Checkout coupons', () => {
|
||||
|
@ -47,9 +28,6 @@ const runCheckoutApplyCouponsTest = () => {
|
|||
|
||||
beforeAll(async () => {
|
||||
productId = await createSimpleProduct();
|
||||
couponFixedCart = await createCoupon();
|
||||
couponPercentage = await createCoupon('50', 'Percentage discount');
|
||||
couponFixedProduct = await createCoupon('5', 'Fixed product discount');
|
||||
await shopper.emptyCart();
|
||||
await shopper.goToShop();
|
||||
await waitForSelectorWithoutThrow( '.add_to_cart_button' );
|
||||
|
@ -58,8 +36,8 @@ const runCheckoutApplyCouponsTest = () => {
|
|||
await shopper.goToCheckout();
|
||||
});
|
||||
|
||||
it.each(couponsTable)('allows checkout to apply %s coupon', async (couponType, cartDiscount, orderTotal) => {
|
||||
const coupon = getCoupon(couponType);
|
||||
it.each( getCouponsTable() )( 'allows checkout to apply %s coupon', async ( couponType, cartDiscount, orderTotal ) => {
|
||||
const coupon = await getCouponId( couponType );
|
||||
await applyCoupon(coupon);
|
||||
await expect(page).toMatchElement('.woocommerce-message', { text: 'Coupon code applied successfully.' });
|
||||
|
||||
|
@ -73,9 +51,10 @@ const runCheckoutApplyCouponsTest = () => {
|
|||
});
|
||||
|
||||
it('prevents checkout applying same coupon twice', async () => {
|
||||
await applyCoupon(couponFixedCart);
|
||||
const couponId = await getCouponId( 'fixed cart' );
|
||||
await applyCoupon( couponId );
|
||||
await expect(page).toMatchElement('.woocommerce-message', {text: 'Coupon code applied successfully.'});
|
||||
await applyCoupon(couponFixedCart);
|
||||
await applyCoupon( couponId );
|
||||
// Verify only one discount applied
|
||||
// This is a work around for Puppeteer inconsistently finding 'Coupon code already applied'
|
||||
await expect(page).toMatchElement('.cart-discount .amount', {text: '$5.00'});
|
||||
|
@ -83,7 +62,7 @@ const runCheckoutApplyCouponsTest = () => {
|
|||
});
|
||||
|
||||
it('allows checkout to apply multiple coupons', async () => {
|
||||
await applyCoupon(couponFixedProduct);
|
||||
await applyCoupon( await getCouponId( 'fixed product' ) );
|
||||
await expect(page).toMatchElement('.woocommerce-message', {text: 'Coupon code applied successfully.'});
|
||||
|
||||
// Verify discount applied and order total
|
||||
|
@ -92,8 +71,8 @@ const runCheckoutApplyCouponsTest = () => {
|
|||
});
|
||||
|
||||
it('restores checkout total when coupons are removed', async () => {
|
||||
await removeCoupon(couponFixedCart);
|
||||
await removeCoupon(couponFixedProduct);
|
||||
await removeCoupon( await getCouponId( 'fixed cart' ) );
|
||||
await removeCoupon( await getCouponId( 'fixed product' ) );
|
||||
await expect(page).toMatchElement('.order-total .amount', {text: '$9.99'});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -39,7 +39,7 @@ const runCheckoutCreateAccountTest = () => {
|
|||
await settingsPageSaveChanges();
|
||||
|
||||
// Set free shipping within California
|
||||
await addShippingZoneAndMethod('Free Shipping CA', 'state:US:CA', ' ', 'free_shipping');
|
||||
await addShippingZoneAndMethod('Free Shipping CA', 'state:US:CA', ' ', 'free_shipping' );
|
||||
|
||||
await merchant.logout();
|
||||
|
||||
|
|
|
@ -26,12 +26,12 @@ const runCheckoutPageTest = () => {
|
|||
describe('Checkout page', () => {
|
||||
beforeAll(async () => {
|
||||
productId = await createSimpleProduct();
|
||||
await withRestApi.resetSettingsGroupToDefault('general');
|
||||
await withRestApi.resetSettingsGroupToDefault('products');
|
||||
await withRestApi.resetSettingsGroupToDefault('tax');
|
||||
await withRestApi.resetSettingsGroupToDefault( 'general', false );
|
||||
await withRestApi.resetSettingsGroupToDefault( 'products', false );
|
||||
await withRestApi.resetSettingsGroupToDefault( 'tax', false );
|
||||
|
||||
// Set free shipping within California
|
||||
await withRestApi.addShippingZoneAndMethod('Free Shipping CA', 'state:US:CA', '', 'free_shipping');
|
||||
await withRestApi.addShippingZoneAndMethod('Free Shipping CA', 'state:US:CA', '', 'free_shipping', '', [], false );
|
||||
|
||||
// Set base location with state CA.
|
||||
await withRestApi.updateSettingOption( 'general', 'woocommerce_default_country', { value: 'US:CA' } );
|
||||
|
@ -44,14 +44,14 @@ const runCheckoutPageTest = () => {
|
|||
// Tax calculation should have been enabled by another test - no-op
|
||||
|
||||
// Enable BACS payment method
|
||||
await withRestApi.updatePaymentGateway( 'bacs', { enabled: true } );
|
||||
await withRestApi.updatePaymentGateway( 'bacs', { enabled: true }, false );
|
||||
|
||||
// Enable COD payment method
|
||||
await withRestApi.updatePaymentGateway( 'cod', { enabled: true } );
|
||||
await withRestApi.updatePaymentGateway( 'cod', { enabled: true }, false );
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await withRestApi.deleteAllShippingZones();
|
||||
await withRestApi.deleteAllShippingZones( false );
|
||||
});
|
||||
|
||||
it('should display cart items in order review', async () => {
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
const { createCoupon } = require( '@woocommerce/e2e-utils' );
|
||||
|
||||
const couponsTable = [
|
||||
[ 'fixed cart', { text: '$5.00' }, { text: '$4.99' } ],
|
||||
[ 'percentage', { text: '$4.99' }, { text: '$5.00' } ],
|
||||
[ 'fixed product', { text: '$5.00' }, { text: '$4.99' } ]
|
||||
];
|
||||
|
||||
let couponFixedCart;
|
||||
let couponPercentage;
|
||||
let couponFixedProduct;
|
||||
|
||||
/**
|
||||
* Get a test coupon Id. Create the coupon if it does not exist.
|
||||
*
|
||||
* @param {string} couponType Coupon type.
|
||||
* @return {string} Coupon code.
|
||||
*/
|
||||
const getCouponId = async ( couponType ) => {
|
||||
switch ( couponType ) {
|
||||
case 'fixed cart':
|
||||
if ( ! couponFixedCart ) {
|
||||
couponFixedCart = await createCoupon();
|
||||
}
|
||||
return couponFixedCart;
|
||||
case 'percentage':
|
||||
if ( ! couponPercentage ) {
|
||||
couponPercentage = await createCoupon( '50', 'Percentage discount' );
|
||||
}
|
||||
return couponPercentage;
|
||||
case 'fixed product':
|
||||
if ( ! couponFixedProduct ) {
|
||||
couponFixedProduct = await createCoupon( '5', 'Fixed product discount' );
|
||||
}
|
||||
return couponFixedProduct;
|
||||
}
|
||||
};
|
||||
|
||||
const getCouponsTable = () => {
|
||||
return couponsTable;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
getCouponsTable,
|
||||
getCouponId,
|
||||
};
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
- Added quotes around `WORDPRESS_TITLE` value in .env file to address issue with docker compose 2 "key cannot contain a space" error.
|
||||
- Added `LATEST_WP_VERSION_MINUS` that allows setting a number to subtract from the current WordPress version for the WordPress Docker image.
|
||||
- Support for PHP_VERSION, MARIADB_VERSION environment variables for built in container initialization
|
||||
|
||||
## Fixed
|
||||
|
||||
|
|
|
@ -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-environment/CHANGELOG.md).
|
|
@ -17,22 +17,30 @@ if [[ $1 ]]; then
|
|||
export WORDPRESS_VERSION=$(./bin/get-previous-version.js $WORDPRESS_VERSION $LATEST_WP_VERSION_MINUS 2> /dev/null)
|
||||
fi
|
||||
|
||||
if ! [[ $TRAVIS_PHP_VERSION =~ ^[0-9]+\.[0-9]+ ]]; then
|
||||
TRAVIS_PHP_VERSION=$(./bin/get-latest-docker-tag.js php 7 2> /dev/null)
|
||||
fi
|
||||
if [[ $TRAVIS_PHP_VERSION =~ ^[0-9]+\.[0-9]+ ]]; then
|
||||
export DC_PHP_VERSION=$TRAVIS_PHP_VERSION
|
||||
if [[ $PHP_VERSION =~ ^[0-9]+\.[0-9]+ ]]; then
|
||||
export DC_PHP_VERSION=$PHP_VERSION
|
||||
else
|
||||
export DC_PHP_VERSION="7.4.22"
|
||||
if ! [[ $TRAVIS_PHP_VERSION =~ ^[0-9]+\.[0-9]+ ]]; then
|
||||
TRAVIS_PHP_VERSION=$(./bin/get-latest-docker-tag.js php 7 2> /dev/null)
|
||||
fi
|
||||
if [[ $TRAVIS_PHP_VERSION =~ ^[0-9]+\.[0-9]+ ]]; then
|
||||
export DC_PHP_VERSION=$TRAVIS_PHP_VERSION
|
||||
else
|
||||
export DC_PHP_VERSION="7.4.25"
|
||||
fi
|
||||
fi
|
||||
|
||||
if ! [[ $TRAVIS_MARIADB_VERSION =~ ^[0-9]+\.[0-9]+ ]]; then
|
||||
TRAVIS_MARIADB_VERSION=$(./bin/get-latest-docker-tag.js mariadb 10 2> /dev/null)
|
||||
fi
|
||||
if [[ $TRAVIS_MARIADB_VERSION =~ ^[0-9]+\.[0-9]+ ]]; then
|
||||
export DC_MARIADB_VERSION=$TRAVIS_MARIADB_VERSION
|
||||
if [[ $MARIADB_VERSION =~ ^[0-9]+\.[0-9]+ ]]; then
|
||||
export DC_MARIADB_VERSION=$MARIADB_VERSION
|
||||
else
|
||||
export DC_MARIADB_VERSION="10.6.4"
|
||||
if ! [[ $TRAVIS_MARIADB_VERSION =~ ^[0-9]+\.[0-9]+ ]]; then
|
||||
TRAVIS_MARIADB_VERSION=$(./bin/get-latest-docker-tag.js mariadb 10 2> /dev/null)
|
||||
fi
|
||||
if [[ $TRAVIS_MARIADB_VERSION =~ ^[0-9]+\.[0-9]+ ]]; then
|
||||
export DC_MARIADB_VERSION=$TRAVIS_MARIADB_VERSION
|
||||
else
|
||||
export DC_MARIADB_VERSION="10.6.5"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ $1 == 'up' ]]; then
|
||||
|
|
|
@ -126,9 +126,17 @@ The built in container defaults to mapping the root folder of the repository to
|
|||
|
||||
Since the introduction of the WooCommerce Monorepo, a `WC_CORE_PATH` environment variable maps to Core WooCommerce at `plugins/woocommerce`. It can also be overriden in a similar fashion.
|
||||
|
||||
### Specifying Server Software versions
|
||||
|
||||
The built-in container supports these variables for use locally and in CI environments:
|
||||
|
||||
- `WP_VERSION` - WordPress (default `latest`)
|
||||
- `PHP_VERSION` - PHP (default `latest`)
|
||||
- `MARIADB_VERSION` - MariaDB (default `latest`)
|
||||
|
||||
### Travis CI Supported Versions
|
||||
|
||||
Travis CI uses environment variables to allow control of some software versions in the testing environment. The built in container supports these variables:
|
||||
Travis CI uses environment variables to allow control of some software versions in the testing environment. The built-in container supports these variables:
|
||||
|
||||
- `WP_VERSION` - WordPress (default `latest`)
|
||||
- `TRAVIS_PHP_VERSION` - PHP (default `latest`)
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"name": "woocommerce/e2e-environment",
|
||||
"description": "WooCommerce end to end testing environment",
|
||||
"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",
|
||||
"Add",
|
||||
"Update",
|
||||
"Dev",
|
||||
"Tweak",
|
||||
"Performance",
|
||||
"Enhancement"
|
||||
],
|
||||
"changelog": "NEXT_CHANGELOG.md"
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -2,7 +2,11 @@
|
|||
|
||||
echo "Initializing WooCommerce E2E"
|
||||
|
||||
wp plugin activate woocommerce
|
||||
# This is a workaround to accommodate different directory names.
|
||||
wp plugin activate --all
|
||||
wp plugin deactivate akismet
|
||||
wp plugin deactivate hello
|
||||
|
||||
wp theme install twentynineteen --activate
|
||||
wp user create customer customer@woocommercecoree2etestsuite.com \
|
||||
--user_pass=password \
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
"@babel/polyfill": "7.12.1",
|
||||
"@babel/preset-env": "7.12.7",
|
||||
"@wordpress/eslint-plugin": "7.3.0",
|
||||
"eslint": "^8.1.0",
|
||||
"ndb": "^1.1.5",
|
||||
"semver": "^7.3.2"
|
||||
},
|
||||
|
@ -58,7 +59,8 @@
|
|||
"docker:ssh": "docker exec -it $(node utils/get-app-name.js)_wordpress-www /bin/bash",
|
||||
"test:e2e": "bash ./bin/wait-for-build.sh && ./bin/e2e-test-integration.js",
|
||||
"test:e2e-debug": "bash ./bin/wait-for-build.sh && ./bin/e2e-test-integration.js --dev --debug",
|
||||
"test:e2e-dev": "bash ./bin/wait-for-build.sh && ./bin/e2e-test-integration.js --dev"
|
||||
"test:e2e-dev": "bash ./bin/wait-for-build.sh && ./bin/e2e-test-integration.js --dev",
|
||||
"lint": "eslint src"
|
||||
},
|
||||
"bin": {
|
||||
"wc-e2e": "bin/wc-e2e.sh"
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
{
|
||||
"root": "packages/js/e2e-environment/",
|
||||
"sourceRoot": "packages/js/e2e-environment/src",
|
||||
"projectType": "library",
|
||||
"targets": {
|
||||
"build": {
|
||||
"executor": "@nrwl/workspace:run-script",
|
||||
"options": {
|
||||
"script": "build"
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"executor": "@nrwl/workspace:run-script",
|
||||
"options": {
|
||||
"script": "lint"
|
||||
}
|
||||
},
|
||||
"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"
|
||||
}
|
||||
},
|
||||
"docker-up": {
|
||||
"executor": "@nrwl/workspace:run-script",
|
||||
"options": {
|
||||
"script": "docker:up"
|
||||
}
|
||||
},
|
||||
"docker-down": {
|
||||
"executor": "@nrwl/workspace:run-script",
|
||||
"options": {
|
||||
"script": "docker:down"
|
||||
}
|
||||
},
|
||||
"docker-clear-all": {
|
||||
"executor": "@nrwl/workspace:run-script",
|
||||
"options": {
|
||||
"script": "docker:clear-all"
|
||||
}
|
||||
},
|
||||
"docker-ssh": {
|
||||
"executor": "@nrwl/workspace:run-script",
|
||||
"options": {
|
||||
"script": "docker:ssh"
|
||||
}
|
||||
},
|
||||
"test-e2e": {
|
||||
"executor": "@nrwl/workspace:run-script",
|
||||
"options": {
|
||||
"script": "test:e2e"
|
||||
}
|
||||
},
|
||||
"test-e2e-debug": {
|
||||
"executor": "@nrwl/workspace:run-script",
|
||||
"options": {
|
||||
"script": "test:e2e-debug"
|
||||
}
|
||||
},
|
||||
"test-e2e-dev": {
|
||||
"executor": "@nrwl/workspace:run-script",
|
||||
"options": {
|
||||
"script": "test:e2e-dev"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
module.exports = {
|
||||
parser: '@typescript-eslint/parser',
|
||||
env: {
|
||||
'jest/globals': true,
|
||||
},
|
||||
ignorePatterns: [ 'dist/', 'node_modules/' ],
|
||||
rules: {
|
||||
'no-unused-vars': 'off',
|
||||
'no-dupe-class-members': 'off',
|
||||
|
||||
'no-useless-constructor': 'off',
|
||||
'@typescript-eslint/no-useless-constructor': 2,
|
||||
},
|
||||
plugins: [ '@typescript-eslint/eslint-plugin' ],
|
||||
extends: [ 'plugin:@wordpress/eslint-plugin/recommended-with-formatting' ],
|
||||
overrides: [
|
||||
{
|
||||
files: [ '**/*.js', '**/*.ts' ],
|
||||
settings: {
|
||||
jsdoc: {
|
||||
mode: 'typescript',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
files: [ '**/*.spec.ts', '**/*.test.ts' ],
|
||||
rules: {
|
||||
'no-console': 'off',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
|
@ -7,6 +7,13 @@
|
|||
- Update `shopper.addToCartFromShopPage()` and `.removeFromCart()` to accept product Id or Title
|
||||
- Added `deleteAllProductAttributes()`, `deleteAllProductCategories()`, and `deleteAllProductTags()` to clean up meta data added when products are imported
|
||||
- Added `withRestApi.createProductCategory()` that creates a product category and returns the ID
|
||||
- `deleteAllProductAttributes()`, `deleteAllProductCategories()`, and `deleteAllProductTags()` to clean up meta data added when products are imported
|
||||
- `withRestApi.createProductCategory()` that creates a product category and returns the ID
|
||||
- `withRestApi.deleteCoupon()` that deletes a single coupon
|
||||
- `withRestApi.addTaxClasses()` that adds an array of tax classes if they do not exist
|
||||
- `withRestApi.addTaxRates()` that adds an array of tax rates if they do not exist
|
||||
- `clickAndWaitForSelector( buttonSelector, resultSelector, timeout )` to click a button and wait for response
|
||||
- Optional parameter `testResponse` to `withRestApi` functions that contain an `expect()`
|
||||
|
||||
# 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-utils/CHANGELOG.md).
|
|
@ -142,22 +142,26 @@ Please note: if you're using a non-SSL environment (such as a Docker container f
|
|||
|
||||
| Function | Parameters | Description |
|
||||
|----------|------------|-------------|
|
||||
| `resetOnboarding` | | Reset onboarding settings |
|
||||
| `addShippingZoneAndMethod` | `zoneName`, `zoneLocation`, `zipCode`, `zoneMethod`, `cost`, `additionalZoneMethods`, `testResponse` | Adds a shipping zone along with a shipping method |
|
||||
| `batchCreateOrders` | `orders`, `testResponse` | Create a batch of orders using the "Batch Create Order" API endpoint |
|
||||
| `addTaxClasses` | `taxClasses` | Add an array of tax classes if they do not exist |
|
||||
| `addTaxRates` | `taxRates` | Add an array of tax rates if they do not exist |
|
||||
| `createProductCategory` | `categoryName` | Create a product category with the provided name |
|
||||
| `deleteAllCoupons` | | Permanently delete all coupons |
|
||||
| `deleteAllProducts` | | Permanently delete all products |
|
||||
| `deleteAllShippingZones` | | Permanently delete all shipping zones except the default |
|
||||
| `deleteAllShippingClasses` | Permanently delete all shipping classes |
|
||||
| `deleteCustomerByEmail` | `emailAddress` | Delete customer user account. Posts are reassigned to user ID 1 |
|
||||
| `resetSettingsGroupToDefault` | `settingsGroup` | Reset settings in settings group to default except `select` fields |
|
||||
| `batchCreateOrders` | `orders` | Create a batch of orders using the "Batch Create Order" API endpoint |
|
||||
| `deleteAllOrders` | | Permanently delete all orders |
|
||||
| `updateSettingOption` | `settingsGroup`, `settingID`, `payload` | Update a settings group |
|
||||
| `updatePaymentGateway`| `paymentGatewayId`, `payload` | Update a payment gateway |
|
||||
| `deleteAllProductAttributes` | `testResponse` | Permanently delete all product attributes |
|
||||
| `deleteAllProductCategories` | `testResponse` | Permanently delete all product categories |
|
||||
| `deleteAllProducts` | | Permanently delete all products |
|
||||
| `deleteAllProductTags` | `testResponse` | Permanently delete all product tags |
|
||||
| `deleteAllShippingClasses` | `testResponse` | Permanently delete all shipping classes |
|
||||
| `deleteAllShippingZones` | `testResponse` | Permanently delete all shipping zones except the default |
|
||||
| `deleteCoupon` | `couponId` | Permanently delete a coupon |
|
||||
| `deleteCustomerByEmail` | `emailAddress` | Delete customer user account. Posts are reassigned to user ID 1 |
|
||||
| `getSystemEnvironment` | | Get the current environment from the WooCommerce system status API. |
|
||||
| `deleteAllProductAttributes` | | Permanently delete all product attributes. |
|
||||
| `deleteAllProductCategories` | | Permanently delete all product categories. |
|
||||
| `deleteAllProductTags` | | Permanently delete all product tags. |
|
||||
| `createProductCategory` | `categoryName` | Create a product category with the provided name. |
|
||||
| `resetOnboarding` | | Reset onboarding settings |
|
||||
| `resetSettingsGroupToDefault` | `settingsGroup`, `testResponse` | Reset settings in settings group to default except `select` fields |
|
||||
| `updateSettingOption` | `settingsGroup`, `settingID`, `payload` | Update a settings group |
|
||||
| `updatePaymentGateway`| `paymentGatewayId`, `payload`, `testResponse` | Update a payment gateway |
|
||||
|
||||
### Classes
|
||||
|
||||
|
@ -229,6 +233,7 @@ There is a general utilities object `utils` with the following functions:
|
|||
| `deleteAllShippingZones` | | Delete all the existing shipping zones |
|
||||
| `waitForSelectorWithoutThrow` | `selector`, `timeoutInSeconds` | conditionally wait for a selector without throwing an error. Default timeout is 5 seconds |
|
||||
| `createOrder` | `orderOptions` | Creates an order using the API with the passed in details |
|
||||
| `clickAndWaitForSelector` | `buttonSelector`, `resultSelector`, `timeout` | Click a button and wait for response |
|
||||
|
||||
### Test Utilities
|
||||
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"name": "woocommerce/e2e-utiles",
|
||||
"description": "WooCommerce end to end testing utilities",
|
||||
"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",
|
||||
"Add",
|
||||
"Update",
|
||||
"Dev",
|
||||
"Tweak",
|
||||
"Performance",
|
||||
"Enhancement"
|
||||
],
|
||||
"changelog": "NEXT_CHANGELOG.md"
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -11,12 +11,18 @@
|
|||
"main": "build/index.js",
|
||||
"module": "build-module/index.js",
|
||||
"dependencies": {
|
||||
"@automattic/puppeteer-utils": "github:Automattic/puppeteer-utils#0f3ec50",
|
||||
"@wordpress/deprecated": "^2.10.0",
|
||||
"@wordpress/e2e-test-utils": "^4.16.1",
|
||||
"config": "3.3.3",
|
||||
"faker": "^5.1.0",
|
||||
"fishery": "^1.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@typescript-eslint/eslint-plugin": "^5.3.0",
|
||||
"@typescript-eslint/parser": "^5.3.0",
|
||||
"eslint": "^8.1.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@woocommerce/api": "^0.2.0"
|
||||
},
|
||||
|
@ -27,6 +33,7 @@
|
|||
"clean": "rm -rf ./build ./build-module",
|
||||
"compile": "node ./../bin/build.js",
|
||||
"build": "pnpm run clean && pnpm run compile",
|
||||
"prepare": "pnpm run build"
|
||||
"prepare": "pnpm run build",
|
||||
"lint": "eslint src"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
{
|
||||
"root": "packages/js/e2e-utils/",
|
||||
"sourceRoot": "packages/js/e2e-utils/src",
|
||||
"projectType": "library",
|
||||
"targets": {
|
||||
"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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -190,13 +190,13 @@ const completeOnboardingWizard = async () => {
|
|||
/**
|
||||
* Create simple product.
|
||||
*
|
||||
* @param productTitle - Defaults to Simple Product. Customizable title.
|
||||
* @param productPrice - Defaults to $9.99. Customizable pricing.
|
||||
* @param productTitle Defaults to Simple Product. Customizable title.
|
||||
* @param productPrice Defaults to $9.99. Customizable pricing.
|
||||
*/
|
||||
const createSimpleProduct = async ( productTitle = simpleProductName, productPrice = simpleProductPrice ) => {
|
||||
const product = await factories.products.simple.create( {
|
||||
name: productTitle,
|
||||
regularPrice: productPrice
|
||||
regularPrice: productPrice,
|
||||
} );
|
||||
return product.id;
|
||||
} ;
|
||||
|
|
|
@ -4,12 +4,14 @@ import {Coupon, Setting, SimpleProduct, Order} from '@woocommerce/api';
|
|||
|
||||
const client = factories.api.withDefaultPermalinks;
|
||||
const onboardingProfileEndpoint = '/wc-admin/onboarding/profile';
|
||||
const shippingZoneEndpoint = '/wc/v3/shipping/zones';
|
||||
const shippingClassesEndpoint = '/wc/v3/products/shipping_classes';
|
||||
const userEndpoint = '/wp/v2/users';
|
||||
const systemStatusEndpoint = '/wc/v3/system_status';
|
||||
const productsEndpoint = '/wc/v3/products';
|
||||
const productCategoriesEndpoint = '/wc/v3/products/categories';
|
||||
const shippingClassesEndpoint = '/wc/v3/products/shipping_classes';
|
||||
const shippingZoneEndpoint = '/wc/v3/shipping/zones';
|
||||
const systemStatusEndpoint = '/wc/v3/system_status';
|
||||
const taxClassesEndpoint = '/wc/v3/taxes/classes';
|
||||
const taxRatesEndpoint = '/wc/v3/taxes';
|
||||
const userEndpoint = '/wp/v2/users';
|
||||
|
||||
/**
|
||||
* Utility function to delete all merchant created data store objects.
|
||||
|
@ -42,6 +44,16 @@ const deleteAllRepositoryObjects = async ( repository, defaultObjectId = null, s
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Utility to flatten a tax rate.
|
||||
*
|
||||
* @param {object} taxRate Tax rate to be flattened.
|
||||
* @return {string}
|
||||
*/
|
||||
const flattenTaxRate = ( taxRate ) => {
|
||||
return taxRate.rate + '/' + taxRate.class + '/' + taxRate.name;
|
||||
};
|
||||
|
||||
/**
|
||||
* Utility functions that use the REST API to process the requested function.
|
||||
*/
|
||||
|
@ -77,6 +89,16 @@ export const withRestApi = {
|
|||
const repository = Coupon.restRepository( client );
|
||||
await deleteAllRepositoryObjects( repository );
|
||||
},
|
||||
/**
|
||||
* Use api package to delete a coupon.
|
||||
*
|
||||
* @param {number} couponId Coupon ID.
|
||||
* @return {Promise} Promise resolving once coupon has been deleted.
|
||||
*/
|
||||
deleteCoupon: async ( couponId ) => {
|
||||
const repository = Coupon.restRepository( client );
|
||||
await repository.delete( couponId );
|
||||
},
|
||||
/**
|
||||
* Use api package to delete products.
|
||||
*
|
||||
|
@ -89,24 +111,28 @@ export const withRestApi = {
|
|||
/**
|
||||
* Use the API to delete all product attributes.
|
||||
*
|
||||
* @param {boolean} testResponse Test the response status code.
|
||||
* @return {Promise} Promise resolving once attributes have been deleted.
|
||||
*/
|
||||
deleteAllProductAttributes: async () => {
|
||||
deleteAllProductAttributes: async ( testResponse = true ) => {
|
||||
const productAttributesPath = productsEndpoint + '/attributes';
|
||||
const productAttributes = await client.get( productAttributesPath );
|
||||
if ( productAttributes.data && productAttributes.data.length ) {
|
||||
for ( let a = 0; a < productAttributes.data.length; a++ ) {
|
||||
const response = await client.delete( productAttributesPath + `/${productAttributes.data[a].id}?force=true` );
|
||||
expect( response.status ).toBe( 200 );
|
||||
if ( testResponse ) {
|
||||
expect( response.status ).toBe( 200 );
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Use the API to delete all product categories.
|
||||
*
|
||||
* @param {boolean} testResponse Test the response status code.
|
||||
* @return {Promise} Promise resolving once categories have been deleted.
|
||||
*/
|
||||
deleteAllProductCategories: async () => {
|
||||
deleteAllProductCategories: async ( testResponse = true ) => {
|
||||
const productCategoriesPath = productsEndpoint + '/categories';
|
||||
const productCategories = await client.get( productCategoriesPath );
|
||||
if ( productCategories.data && productCategories.data.length ) {
|
||||
|
@ -116,22 +142,27 @@ export const withRestApi = {
|
|||
continue;
|
||||
}
|
||||
const response = await client.delete( productCategoriesPath + `/${productCategories.data[c].id}?force=true` );
|
||||
expect( response.status ).toBe( 200 );
|
||||
if ( testResponse ) {
|
||||
expect( response.status ).toBe( 200 );
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Use the API to delete all product tags.
|
||||
*
|
||||
* @param {boolean} testResponse Test the response status code.
|
||||
* @return {Promise} Promise resolving once tags have been deleted.
|
||||
*/
|
||||
deleteAllProductTags: async () => {
|
||||
deleteAllProductTags: async ( testResponse = true ) => {
|
||||
const productTagsPath = productsEndpoint + '/tags';
|
||||
const productTags = await client.get( productTagsPath );
|
||||
if ( productTags.data && productTags.data.length ) {
|
||||
for ( let t = 0; t < productTags.data.length; t++ ) {
|
||||
const response = await client.delete( productTagsPath + `/${productTags.data[t].id}?force=true` );
|
||||
expect( response.status ).toBe( 200 );
|
||||
if ( testResponse ) {
|
||||
expect( response.status ).toBe( 200 );
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -155,6 +186,7 @@ export const withRestApi = {
|
|||
* @param zoneMethod Shipping method type. Defaults to flat_rate (use also: free_shipping or local_pickup).
|
||||
* @param cost Shipping method cost. Default is no cost.
|
||||
* @param additionalZoneMethods Array of additional zone methods to add to the shipping zone.
|
||||
* @param {boolean} testResponse Test the response status code.
|
||||
*/
|
||||
addShippingZoneAndMethod: async (
|
||||
zoneName,
|
||||
|
@ -162,33 +194,38 @@ export const withRestApi = {
|
|||
zipCode = '',
|
||||
zoneMethod = 'flat_rate',
|
||||
cost = '',
|
||||
additionalZoneMethods = [] ) => {
|
||||
additionalZoneMethods = [],
|
||||
testResponse = true ) => {
|
||||
|
||||
const path = 'wc/v3/shipping/zones';
|
||||
const path = 'wc/v3/shipping/zones';
|
||||
|
||||
const response = await client.post( path, { name: zoneName } );
|
||||
expect(response.status).toEqual(201);
|
||||
let zoneId = response.data.id;
|
||||
const response = await client.post( path, { name: zoneName } );
|
||||
if ( testResponse ) {
|
||||
expect( response.status ).toEqual( 201 );
|
||||
}
|
||||
let zoneId = response.data.id;
|
||||
|
||||
// Select shipping zone location
|
||||
let [ zoneType, zoneCode ] = zoneLocation.split(/:(.+)/);
|
||||
let zoneLocationPayload = [
|
||||
// Select shipping zone location
|
||||
let [ zoneType, zoneCode ] = zoneLocation.split(/:(.+)/);
|
||||
let zoneLocationPayload = [
|
||||
{
|
||||
code: zoneCode,
|
||||
type: zoneType,
|
||||
}
|
||||
];
|
||||
];
|
||||
|
||||
// Fill shipping zone postcode if provided
|
||||
if ( zipCode ) {
|
||||
// Fill shipping zone postcode if provided
|
||||
if ( zipCode ) {
|
||||
zoneLocationPayload.push( {
|
||||
code: zipCode,
|
||||
type: "postcode",
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
||||
const locationResponse = await client.put( path + `/${zoneId}/locations`, zoneLocationPayload );
|
||||
expect(locationResponse.status).toEqual(200);
|
||||
const locationResponse = await client.put( path + `/${zoneId}/locations`, zoneLocationPayload );
|
||||
if ( testResponse ) {
|
||||
expect( locationResponse.status ).toEqual( 200 );
|
||||
}
|
||||
|
||||
// Add shipping zone method
|
||||
let methodPayload = {
|
||||
|
@ -196,7 +233,9 @@ export const withRestApi = {
|
|||
}
|
||||
|
||||
const methodsResponse = await client.post( path + `/${zoneId}/methods`, methodPayload );
|
||||
expect(methodsResponse.status).toEqual(200);
|
||||
if ( testResponse ) {
|
||||
expect( methodsResponse.status ).toEqual( 200 );
|
||||
}
|
||||
let methodId = methodsResponse.data.id;
|
||||
|
||||
// Add in cost, if provided
|
||||
|
@ -208,23 +247,28 @@ export const withRestApi = {
|
|||
}
|
||||
|
||||
const costResponse = await client.put( path + `/${zoneId}/methods/${methodId}`, costPayload );
|
||||
expect(costResponse.status).toEqual(200);
|
||||
if ( testResponse ) {
|
||||
expect( costResponse.status ).toEqual( 200 );
|
||||
}
|
||||
}
|
||||
|
||||
// Add any additional zones, if provided
|
||||
if (additionalZoneMethods.length > 0) {
|
||||
for ( let z = 0; z < additionalZoneMethods.length; z++ ) {
|
||||
let response = await client.post( path + `/${zoneId}/methods`, { method_id: additionalZoneMethods[z] } );
|
||||
expect(response.status).toEqual(200);
|
||||
if ( testResponse ) {
|
||||
expect( response.status ).toBe( 200 );
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Use api package to delete shipping zones.
|
||||
*
|
||||
* @param {boolean} testResponse Test the response status code.
|
||||
* @return {Promise} Promise resolving once shipping zones have been deleted.
|
||||
*/
|
||||
deleteAllShippingZones: async () => {
|
||||
deleteAllShippingZones: async ( testResponse = true ) => {
|
||||
const shippingZones = await client.get( shippingZoneEndpoint );
|
||||
if ( shippingZones.data && shippingZones.data.length ) {
|
||||
for ( let z = 0; z < shippingZones.data.length; z++ ) {
|
||||
|
@ -233,21 +277,26 @@ export const withRestApi = {
|
|||
continue;
|
||||
}
|
||||
const response = await client.delete( shippingZoneEndpoint + `/${shippingZones.data[z].id}?force=true` );
|
||||
expect( response.status ).toBe( 200 );
|
||||
if ( testResponse ) {
|
||||
expect( response.status ).toBe( 200 );
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Use api package to delete shipping classes.
|
||||
*
|
||||
* @param {boolean} testResponse Test the response status code.
|
||||
* @return {Promise} Promise resolving once shipping classes have been deleted.
|
||||
*/
|
||||
deleteAllShippingClasses: async () => {
|
||||
deleteAllShippingClasses: async ( testResponse = true ) => {
|
||||
const shippingClasses = await client.get( shippingClassesEndpoint );
|
||||
if ( shippingClasses.data && shippingClasses.data.length ) {
|
||||
for ( let c = 0; c < shippingClasses.data.length; c++ ) {
|
||||
const response = await client.delete( shippingClassesEndpoint + `/${shippingClasses.data[c].id}?force=true` );
|
||||
expect( response.status ).toBe( 200 );
|
||||
if ( testResponse ) {
|
||||
expect( response.status ).toBe( 200 );
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -278,9 +327,10 @@ export const withRestApi = {
|
|||
/**
|
||||
* Reset a settings group to default values except selects.
|
||||
* @param settingsGroup
|
||||
* @param {boolean} testResponse Test the response status code.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
resetSettingsGroupToDefault: async ( settingsGroup ) => {
|
||||
resetSettingsGroupToDefault: async ( settingsGroup, testResponse = true ) => {
|
||||
const settingsClient = Setting.restRepository( client );
|
||||
const settings = await settingsClient.list( settingsGroup );
|
||||
if ( ! settings.length ) {
|
||||
|
@ -300,7 +350,7 @@ export const withRestApi = {
|
|||
|
||||
const response = await settingsClient.update( settingsGroup, defaultSetting.id, defaultSetting );
|
||||
// Multi-selects have a default '' but return an empty [].
|
||||
if ( settings[s].type != 'multiselect' ) {
|
||||
if ( testResponse && settings[s].type != 'multiselect' ) {
|
||||
expect( response.value ).toBe( defaultSetting.value );
|
||||
}
|
||||
}
|
||||
|
@ -321,22 +371,61 @@ export const withRestApi = {
|
|||
*
|
||||
* @param {string} paymentGatewayId The ID of the payment gateway to update.
|
||||
* @param {object} payload An object with the key/value pair to update.
|
||||
* @param {boolean} testResponse Test the response status code.
|
||||
*/
|
||||
updatePaymentGateway: async ( paymentGatewayId, payload = {} ) => {
|
||||
updatePaymentGateway: async ( paymentGatewayId, payload = {}, testResponse = true ) => {
|
||||
const response = await client.put( `/wc/v3/payment_gateways/${paymentGatewayId}`, payload );
|
||||
expect( response.status ).toBe( 200 );
|
||||
if ( testResponse ) {
|
||||
expect( response.status ).toEqual( 200 );
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Create a batch of orders using the "Batch Create Order" API endpoint.
|
||||
*
|
||||
* @param orders Array of orders to be created
|
||||
* @param {boolean} testResponse Test the response status code.
|
||||
*/
|
||||
batchCreateOrders: async (orders) => {
|
||||
batchCreateOrders: async ( orders, testResponse = true ) => {
|
||||
const path = '/wc/v3/orders/batch';
|
||||
const payload = { create: orders };
|
||||
|
||||
const response = await client.post(path, payload);
|
||||
expect( response.status ).toEqual(200);
|
||||
if ( testResponse ) {
|
||||
expect( response.status ).toBe( 200 );
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Add tax classes.
|
||||
*
|
||||
* @param {<Array<Object>>} taxClasses Array of tax class objects.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
addTaxClasses: async ( taxClasses ) => {
|
||||
// Only add tax classes which don't already exist.
|
||||
const existingTaxClasses = await client.get( taxClassesEndpoint );
|
||||
const existingTaxNames = existingTaxClasses.data.map( taxClass => taxClass.name );
|
||||
const newTaxClasses = taxClasses.filter( taxClass => ! existingTaxNames.includes( taxClass.name ) );
|
||||
|
||||
for ( const taxClass of newTaxClasses ) {
|
||||
await client.post( taxClassesEndpoint, taxClass );
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Add tax rates.
|
||||
*
|
||||
* @param {<Array<Object>>} taxRates Array of tax rate objects.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
addTaxRates: async ( taxRates ) => {
|
||||
// Only add rates which don't already exist
|
||||
const existingTaxRates = await client.get( taxRatesEndpoint );
|
||||
const existingRates = existingTaxRates.data.map( taxRate => flattenTaxRate( taxRate ) );
|
||||
|
||||
for ( const taxRate of taxRates ) {
|
||||
if ( ! existingRates.includes( flattenTaxRate( taxRate ) ) ) {
|
||||
await client.post( taxRatesEndpoint, taxRate );
|
||||
}
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Get the current environment from the WooCommerce system status API.
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
* External dependencies
|
||||
*/
|
||||
import { pressKeyWithModifier } from '@wordpress/e2e-test-utils';
|
||||
import { waitForSelector } from '@automattic/puppeteer-utils';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
|
@ -220,11 +221,11 @@ export const evalAndClick = async ( selector ) => {
|
|||
* @param {string} selector Selector of the select2 search field
|
||||
*/
|
||||
export const selectOptionInSelect2 = async ( value, selector = 'input.select2-search__field' ) => {
|
||||
await page.waitForSelector(selector);
|
||||
await page.click(selector);
|
||||
await page.type(selector, value);
|
||||
await page.waitForSelector( selector );
|
||||
await page.click( selector );
|
||||
await page.type( selector, value );
|
||||
await waitForTimeout( 2000 ); // to avoid flakyness, must wait before pressing Enter
|
||||
await page.keyboard.press('Enter');
|
||||
await page.keyboard.press( 'Enter' );
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -234,12 +235,12 @@ export const selectOptionInSelect2 = async ( value, selector = 'input.select2-se
|
|||
* @param {string} orderId Order ID
|
||||
* @param {string} customerName Customer's full name attached to order ID.
|
||||
*/
|
||||
export const searchForOrder = async (value, orderId, customerName) => {
|
||||
await clearAndFillInput('#post-search-input', value);
|
||||
await expect(page).toMatchElement('#post-search-input', value);
|
||||
await expect(page).toClick('#search-submit' );
|
||||
await page.waitForSelector('#the-list', { timeout: 10000 } );
|
||||
await expect(page).toMatchElement('.order_number > a.order-view', {text: `#${orderId} ${customerName}`});
|
||||
export const searchForOrder = async ( value, orderId, customerName) => {
|
||||
await clearAndFillInput( '#post-search-input', value );
|
||||
await expect( page ).toMatchElement( '#post-search-input', value );
|
||||
await expect( page ).toClick( '#search-submit' );
|
||||
await page.waitForSelector( '#the-list', { timeout: 10000 } );
|
||||
await expect( page ).toMatchElement( '.order_number > a.order-view', { text: `#${orderId} ${customerName}` } );
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -255,16 +256,16 @@ export const applyCoupon = async ( couponCode ) => {
|
|||
page.reload(),
|
||||
page.waitForNavigation( { waitUntil: 'networkidle0' } ),
|
||||
]);
|
||||
await expect(page).toClick('a', {text: 'Click here to enter your code'});
|
||||
await expect( page ).toClick( 'a', { text: 'Click here to enter your code' } );
|
||||
await uiUnblocked();
|
||||
await clearAndFillInput('#coupon_code', couponCode);
|
||||
await expect(page).toClick('button', {text: 'Apply coupon'});
|
||||
await clearAndFillInput( '#coupon_code', couponCode );
|
||||
await expect( page ).toClick( 'button', {text: 'Apply coupon' } );
|
||||
await uiUnblocked();
|
||||
} catch (error) {
|
||||
await clearAndFillInput('#coupon_code', couponCode);
|
||||
await expect(page).toClick('button', {text: 'Apply coupon'});
|
||||
} catch ( error ) {
|
||||
await clearAndFillInput( '#coupon_code', couponCode );
|
||||
await expect( page ).toClick( 'button', { text: 'Apply coupon' } );
|
||||
await uiUnblocked();
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -278,9 +279,9 @@ export const removeCoupon = async ( couponCode ) => {
|
|||
page.reload(),
|
||||
page.waitForNavigation( { waitUntil: 'networkidle0' } ),
|
||||
]);
|
||||
await expect(page).toClick('[data-coupon="'+couponCode.toLowerCase()+'"]', {text: '[Remove]'});
|
||||
await expect( page ).toClick( '[data-coupon="'+couponCode.toLowerCase()+'"]', {text: '[Remove]' } );
|
||||
await uiUnblocked();
|
||||
await expect(page).toMatchElement('.woocommerce-message', {text: 'Coupon has been removed.'});
|
||||
await expect( page ).toMatchElement( '.woocommerce-message', {text: 'Coupon has been removed.' } );
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -296,3 +297,24 @@ export const selectOrderAction = async ( action ) => {
|
|||
page.waitForNavigation( { waitUntil: 'networkidle0' } ),
|
||||
] );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Evaluate and click a button selector then wait for a result selector.
|
||||
* This is a work around for what appears to be intermittent delays in handling confirm dialogs.
|
||||
*
|
||||
* @param {string} buttonSelector Selector of button to click
|
||||
* @param {string} resultSelector Selector to wait for after click
|
||||
* @param {number} timeout Timeout length in milliseconds. Default 5000.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
export const clickAndWaitForSelector = async ( buttonSelector, resultSelector, timeout = 5000 ) => {
|
||||
await evalAndClick( buttonSelector );
|
||||
await waitForSelector(
|
||||
page,
|
||||
resultSelector,
|
||||
{
|
||||
timeout
|
||||
}
|
||||
);
|
||||
};
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue