Merge branch 'trunk' into add/api-tests-shipping-zones-and-methods
This commit is contained in:
commit
eee9a56d71
|
@ -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
|
||||
|
|
|
@ -77,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.
|
||||
|
@ -73,16 +73,17 @@ jobs:
|
|||
with:
|
||||
path: package/woocommerce
|
||||
|
||||
- name: Run npm install.
|
||||
working-directory: package/woocommerce/plugins/woocommerce
|
||||
npm install -g pnpm
|
||||
pnpm install
|
||||
- name: Install PNPM and install dependencies
|
||||
working-directory: package/woocommerce
|
||||
run: |
|
||||
npm install -g pnpm
|
||||
pnpm install
|
||||
|
||||
- name: Load docker images and start containers.
|
||||
working-directory: package/woocommerce/plugins/woocommerce
|
||||
env:
|
||||
LATEST_WP_VERSION_MINUS: ${{ matrix.wp }}
|
||||
run: pnpx wc-e2e docker:up
|
||||
run: pnpm nx docker-up woocommerce
|
||||
|
||||
- name: Move current directory to code. We will install zip file in this dir later.
|
||||
run: mv ./package/woocommerce/plugins/woocommerce/* ./code/woocommerce
|
||||
|
@ -103,4 +104,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
|
||||
|
|
|
@ -34,9 +34,8 @@ The port # might be different depending on your `.wp-env.override.json` configur
|
|||
Once you have WP-ENV container up, we need to run a few commands to start developing.
|
||||
|
||||
1. Run `pnpm install` to install npm modules.
|
||||
2. Navigate to Core WooCommerce `cd plugins/woocommerce`.
|
||||
3. Run `pnpm run build:core`
|
||||
4. Run `composer install` to install PHP dependencies.
|
||||
2. Run `pnpm nx build woocommerce` to build core.
|
||||
3. Run `pnpm nx composer-install woocommerce` to install PHP dependencies.
|
||||
|
||||
If you don't have Composer available locally, run the following command. It runs the command in WP-ENV container.
|
||||
|
||||
|
|
16
package.json
16
package.json
|
@ -19,26 +19,26 @@
|
|||
"devDependencies": {
|
||||
"@automattic/nx-composer": "^0.1.0",
|
||||
"@nrwl/cli": "latest",
|
||||
"@nrwl/linter": "^13.1.3",
|
||||
"@nrwl/linter": "^13.1.4",
|
||||
"@nrwl/tao": "latest",
|
||||
"@nrwl/web": "^13.1.3",
|
||||
"@nrwl/web": "^13.1.4",
|
||||
"@nrwl/workspace": "latest",
|
||||
"@types/node": "14.14.33",
|
||||
"@woocommerce/eslint-plugin": "^1.2.0",
|
||||
"@wordpress/prettier-config": "^1.0.5",
|
||||
"@woocommerce/eslint-plugin": "^1.3.0",
|
||||
"@wordpress/prettier-config": "^1.1.1",
|
||||
"chalk": "^4.1.2",
|
||||
"glob": "^7.2.0",
|
||||
"jest": "^27.0.6",
|
||||
"jest": "^27.3.1",
|
||||
"mkdirp": "^1.0.4",
|
||||
"node-stream-zip": "^1.13.6",
|
||||
"prettier": "npm:wp-prettier@2.2.1-beta-1",
|
||||
"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.3.3",
|
||||
"@wordpress/babel-preset-default": "^6.4.1",
|
||||
"lodash": "^4.17.21",
|
||||
"wp-textdomain": "1.0.1"
|
||||
}
|
||||
|
|
|
@ -6,5 +6,7 @@
|
|||
- Coupons API Tests
|
||||
- Refunds API Tests
|
||||
- Products API Tests
|
||||
- CRUD tests for the Orders API
|
||||
- Order Search API Tests
|
||||
- Shipping Zones API Tests
|
||||
- Shipping Methods API Tests
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
# Changelog
|
||||
|
||||
This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
---
|
||||
|
||||
[See legacy changelogs for previous versions](https://github.com/woocommerce/woocommerce/blob/<last-commit-hash-before-this-merge>/packages/js/api-core-tests/CHANGELOG.md).
|
|
@ -31,7 +31,7 @@ cd "$SCRIPTPATH/$(dirname "$REALPATH")/.."
|
|||
# Run scripts
|
||||
case $1 in
|
||||
'test')
|
||||
jest --group=$2
|
||||
jest --group=$2 --runInBand
|
||||
TESTRESULT=$?
|
||||
;;
|
||||
'make:collection')
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"name": "woocommerce/api-core-tests",
|
||||
"description": "WooCommerce API core test",
|
||||
"type": "library",
|
||||
"license": "GPL-3.0-or-later",
|
||||
"minimum-stability": "dev",
|
||||
"require-dev": {
|
||||
"automattic/jetpack-changelogger": "3.0.2"
|
||||
},
|
||||
"extra": {
|
||||
"changelogger": {
|
||||
"formatter": {
|
||||
"filename": "../../../tools/changelogger/PackageFormatter.php"
|
||||
},
|
||||
"types": {
|
||||
"fix": "Fixes an existing bug",
|
||||
"add": "Adds functionality",
|
||||
"update": "Update existing functionality",
|
||||
"dev": "Development related task",
|
||||
"tweak": "A minor adjustment to the codebase",
|
||||
"performance": "Address performance issues",
|
||||
"enhancement": "Improve existing functionality"
|
||||
},
|
||||
"changelog": "NEXT_CHANGELOG.md"
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -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.',
|
||||
|
@ -15,5 +14,5 @@
|
|||
};
|
||||
|
||||
module.exports = {
|
||||
refund: refund,
|
||||
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,14 +29,16 @@ const ordersApi = {
|
|||
method: 'GET',
|
||||
path: 'orders/<id>',
|
||||
responseCode: 200,
|
||||
order: async ( orderId ) => getRequest( `orders/${orderId}` ),
|
||||
order: async ( orderId, ordersQuery = {} ) =>
|
||||
getRequest( `orders/${ orderId }`, ordersQuery ),
|
||||
},
|
||||
listAll: {
|
||||
name: 'List all orders',
|
||||
method: 'GET',
|
||||
path: 'orders',
|
||||
responseCode: 200,
|
||||
orders: async () => getRequest( 'orders' ),
|
||||
orders: async ( ordersQuery = {} ) =>
|
||||
getRequest( 'orders', ordersQuery ),
|
||||
},
|
||||
update: {
|
||||
name: 'Update an order',
|
||||
|
@ -39,7 +46,8 @@ const ordersApi = {
|
|||
path: 'orders/<id>',
|
||||
responseCode: 200,
|
||||
payload: getOrderExample(),
|
||||
order: async ( orderId, orderDetails ) => putRequest( `orders/${orderId}`, orderDetails ),
|
||||
order: async ( orderId, orderDetails ) =>
|
||||
putRequest( `orders/${ orderId }`, orderDetails ),
|
||||
},
|
||||
delete: {
|
||||
name: 'Delete an order',
|
||||
|
@ -47,9 +55,10 @@ const ordersApi = {
|
|||
path: 'orders/<id>',
|
||||
responseCode: 200,
|
||||
payload: {
|
||||
force: false
|
||||
force: false,
|
||||
},
|
||||
order: async ( orderId, deletePermanently ) => deleteRequest( `orders/${orderId}`, deletePermanently ),
|
||||
order: async ( orderId, deletePermanently ) =>
|
||||
deleteRequest( `orders/${ orderId }`, deletePermanently ),
|
||||
},
|
||||
batch: {
|
||||
name: 'Batch update orders',
|
||||
|
@ -57,7 +66,8 @@ const ordersApi = {
|
|||
path: 'orders/batch',
|
||||
responseCode: 200,
|
||||
payload: shared.getBatchPayloadExample( getOrderExample() ),
|
||||
orders: async ( batchUpdatePayload ) => postRequest( `orders/batch`, batchUpdatePayload ),
|
||||
orders: async ( batchUpdatePayload ) =>
|
||||
postRequest( `orders/batch`, batchUpdatePayload ),
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
const {
|
||||
const {
|
||||
getRequest,
|
||||
postRequest,
|
||||
deleteRequest,
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
const { ordersApi } = require( '../../endpoints' );
|
||||
const { getOrderExample, shared } = require( '../../data' );
|
||||
|
||||
/**
|
||||
* Order to be searched
|
||||
*/
|
||||
const order = {
|
||||
...getOrderExample(),
|
||||
shipping: {
|
||||
...shared.customerShipping,
|
||||
company: 'Murphy LLC',
|
||||
phone: '6146524353',
|
||||
},
|
||||
shipping_lines: [],
|
||||
fee_lines: [],
|
||||
coupon_lines: [],
|
||||
};
|
||||
|
||||
/**
|
||||
* Search parameters to be used.
|
||||
* The following scenarios are not covered in this test suite because they're already covered in the `List all orders > search` test in `orders.test.js`
|
||||
* ```
|
||||
* can search by billing address 1
|
||||
* can search by shipping address 1
|
||||
* can search by billing last name
|
||||
* can search by billing email
|
||||
* can search by item name
|
||||
* ```
|
||||
*/
|
||||
const searchParams = [
|
||||
[ 'orderId', 'orderId' ],
|
||||
[ 'billing first name', order.billing.first_name ],
|
||||
[ 'billing company name', order.billing.company ],
|
||||
[ 'billing address 2', order.billing.address_2 ],
|
||||
[ 'billing city name', order.billing.city ],
|
||||
[ 'billing post code', order.billing.postcode ],
|
||||
[ 'billing phone', order.billing.phone ],
|
||||
[ 'billing state', order.billing.state ],
|
||||
[ 'shipping first name', order.shipping.first_name ],
|
||||
[ 'shipping last name', order.shipping.last_name ],
|
||||
[ 'shipping address 2', order.shipping.address_2 ],
|
||||
[ 'shipping city', order.shipping.city ],
|
||||
[ 'shipping post code', order.shipping.postcode ],
|
||||
[ 'shipping state', order.shipping.state ],
|
||||
];
|
||||
|
||||
/**
|
||||
* Tests for the WooCommerce Order Search API.
|
||||
*
|
||||
* @group api
|
||||
* @group orders
|
||||
*
|
||||
*/
|
||||
describe( 'Order Search API tests', () => {
|
||||
beforeAll( async () => {
|
||||
// Create an order and save its ID
|
||||
const { body } = await ordersApi.create.order( order );
|
||||
order.id = body.id;
|
||||
} );
|
||||
|
||||
afterAll( async () => {
|
||||
// Cleanup: Delete the order
|
||||
await ordersApi.delete.order( order.id, true );
|
||||
} );
|
||||
|
||||
it.each( searchParams )( 'can search by %s', async ( title, param ) => {
|
||||
const searchValue = param === 'orderId' ? order.id : param;
|
||||
|
||||
const { status, body } = await ordersApi.listAll.orders( {
|
||||
search: searchValue,
|
||||
} );
|
||||
|
||||
expect( status ).toEqual( ordersApi.listAll.responseCode );
|
||||
expect( body ).toHaveLength( 1 );
|
||||
expect( body[ 0 ].id ).toEqual( order.id );
|
||||
} );
|
||||
|
||||
it( 'can return an empty result set when no matches were found', async () => {
|
||||
const { status, body } = await ordersApi.listAll.orders( {
|
||||
search: 'Chauncey Smith Kunde',
|
||||
} );
|
||||
|
||||
expect( status ).toEqual( ordersApi.listAll.responseCode );
|
||||
expect( body ).toEqual( [] );
|
||||
} );
|
||||
} );
|
|
@ -0,0 +1,215 @@
|
|||
const { ordersApi, productsApi } = require( '../../endpoints' );
|
||||
const { order } = require( '../../data' );
|
||||
|
||||
/**
|
||||
* Billing properties to update.
|
||||
*/
|
||||
const updatedCustomerBilling = {
|
||||
first_name: 'Jane',
|
||||
last_name: 'Doe',
|
||||
company: 'Automattic',
|
||||
country: 'US',
|
||||
address_1: '123 Market Street',
|
||||
address_2: 'Suite 500',
|
||||
city: 'Austin',
|
||||
state: 'TX',
|
||||
postcode: '73301',
|
||||
phone: '123456789',
|
||||
email: 'jane.doe@example.com',
|
||||
};
|
||||
|
||||
/**
|
||||
* Shipping properties to update.
|
||||
*/
|
||||
const updatedCustomerShipping = {
|
||||
first_name: 'Mike',
|
||||
last_name: 'Anderson',
|
||||
company: 'Automattic',
|
||||
country: 'US',
|
||||
address_1: '123 Ocean Ave',
|
||||
address_2: '',
|
||||
city: 'New York',
|
||||
state: 'NY',
|
||||
postcode: '10013',
|
||||
phone: '123456789',
|
||||
};
|
||||
|
||||
/**
|
||||
* Data tables to be used for testing the 'Create an order' API.
|
||||
*/
|
||||
const statusesDataTable = [
|
||||
'pending',
|
||||
'processing',
|
||||
'on-hold',
|
||||
'completed',
|
||||
'cancelled',
|
||||
'refunded',
|
||||
'failed',
|
||||
];
|
||||
|
||||
/**
|
||||
* A simple product that will be added to an order.
|
||||
*/
|
||||
const simpleProduct = {
|
||||
name: 'Incredible Plastic Table',
|
||||
regular_price: '48',
|
||||
};
|
||||
|
||||
/**
|
||||
* Tests for the WooCommerce Orders API.
|
||||
*
|
||||
* @group api
|
||||
* @group orders
|
||||
*
|
||||
*/
|
||||
describe( 'Orders API tests: CRUD', () => {
|
||||
let orderId;
|
||||
|
||||
describe( 'Create an order', () => {
|
||||
it( 'can create a pending order by default', async () => {
|
||||
// Create an order that has a null status
|
||||
const requestPayload = {
|
||||
...order,
|
||||
status: null,
|
||||
};
|
||||
const { body, status } = await ordersApi.create.order(
|
||||
requestPayload
|
||||
);
|
||||
// Save the order ID. It will be used by the retrieve, update, and delete tests.
|
||||
orderId = body.id;
|
||||
|
||||
// Verify that the order status is 'pending'
|
||||
expect( status ).toEqual( ordersApi.create.responseCode );
|
||||
expect( typeof body.id ).toEqual( 'number' );
|
||||
expect( body.status ).toEqual( 'pending' );
|
||||
} );
|
||||
|
||||
it.each( statusesDataTable )(
|
||||
"can create an order with status '%s'",
|
||||
async ( expectedStatus ) => {
|
||||
const requestPayload = {
|
||||
...order,
|
||||
status: expectedStatus,
|
||||
};
|
||||
const { status, body } = await ordersApi.create.order(
|
||||
requestPayload
|
||||
);
|
||||
|
||||
expect( status ).toEqual( ordersApi.create.responseCode );
|
||||
expect( typeof body.id ).toEqual( 'number' );
|
||||
expect( body.status ).toEqual( expectedStatus );
|
||||
|
||||
// Cleanup: Delete this order
|
||||
await ordersApi.delete.order( body.id, true );
|
||||
}
|
||||
);
|
||||
} );
|
||||
|
||||
describe( 'Retrieve an order', () => {
|
||||
it( 'can retrieve an order', async () => {
|
||||
const response = await ordersApi.retrieve.order( orderId );
|
||||
expect( response.status ).toEqual(
|
||||
ordersApi.retrieve.responseCode
|
||||
);
|
||||
expect( response.body.id ).toEqual( orderId );
|
||||
} );
|
||||
} );
|
||||
|
||||
describe( 'Update an order', () => {
|
||||
beforeAll( async () => {
|
||||
// Create the product and save its id
|
||||
const { body } = await productsApi.create.product( simpleProduct );
|
||||
simpleProduct.id = body.id;
|
||||
} );
|
||||
|
||||
afterAll( async () => {
|
||||
// Delete the created product
|
||||
await productsApi.delete.product( simpleProduct.id, true );
|
||||
} );
|
||||
|
||||
it.each( statusesDataTable )(
|
||||
"can update status of an order to '%s'",
|
||||
async ( expectedOrderStatus ) => {
|
||||
const requestPayload = {
|
||||
status: expectedOrderStatus,
|
||||
};
|
||||
const { status, body } = await ordersApi.update.order(
|
||||
orderId,
|
||||
requestPayload
|
||||
);
|
||||
|
||||
expect( status ).toEqual( ordersApi.update.responseCode );
|
||||
expect( body.id ).toEqual( orderId );
|
||||
expect( body.status ).toEqual( expectedOrderStatus );
|
||||
}
|
||||
);
|
||||
|
||||
it( 'can add shipping and billing contacts to an order', async () => {
|
||||
// Update the billing and shipping fields on the order
|
||||
order.billing = updatedCustomerBilling;
|
||||
order.shipping = updatedCustomerShipping;
|
||||
|
||||
const response = await ordersApi.update.order( orderId, order );
|
||||
expect( response.status ).toEqual( ordersApi.update.responseCode );
|
||||
|
||||
expect( response.body.billing ).toEqual( updatedCustomerBilling );
|
||||
expect( response.body.shipping ).toEqual( updatedCustomerShipping );
|
||||
} );
|
||||
|
||||
it( 'can add a product to an order', async () => {
|
||||
// Add the product to the order
|
||||
const requestPayload = {
|
||||
line_items: [ { product_id: simpleProduct.id } ],
|
||||
};
|
||||
const { body, status } = await ordersApi.update.order(
|
||||
orderId,
|
||||
requestPayload
|
||||
);
|
||||
|
||||
// Verify that the added product has the correct values
|
||||
expect( status ).toEqual( ordersApi.update.responseCode );
|
||||
expect( body.line_items ).toHaveLength( 1 );
|
||||
expect( body.line_items[ 0 ].product_id ).toEqual(
|
||||
simpleProduct.id
|
||||
);
|
||||
expect( body.line_items[ 0 ].name ).toEqual( simpleProduct.name );
|
||||
} );
|
||||
|
||||
it( 'can pay for an order', async () => {
|
||||
// Setup: Set order status to 'pending'
|
||||
await ordersApi.update.order( orderId, {
|
||||
status: 'pending',
|
||||
} );
|
||||
|
||||
// Pay for the order by setting `set_paid` to true
|
||||
const updateRequestPayload = {
|
||||
set_paid: true,
|
||||
};
|
||||
const { status, body } = await ordersApi.update.order(
|
||||
orderId,
|
||||
updateRequestPayload
|
||||
);
|
||||
expect( status ).toEqual( ordersApi.update.responseCode );
|
||||
expect( body.id ).toEqual( orderId );
|
||||
|
||||
// Validate that the status of the order was automatically set to 'processing'
|
||||
expect( body.status ).toEqual( 'processing' );
|
||||
|
||||
// Validate that the date_paid and date_paid_gmt properties are no longer null
|
||||
expect( body.date_paid ).not.toBeNull();
|
||||
expect( body.date_paid_gmt ).not.toBeNull();
|
||||
} );
|
||||
} );
|
||||
|
||||
describe( 'Delete an order', () => {
|
||||
it( 'can permanently delete an order', async () => {
|
||||
// Delete the order.
|
||||
const response = await ordersApi.delete.order( orderId, true );
|
||||
expect( response.status ).toEqual( ordersApi.delete.responseCode );
|
||||
|
||||
// Verify that the order can no longer be retrieved.
|
||||
const getOrderResponse = await ordersApi.retrieve.order( orderId );
|
||||
expect( getOrderResponse.status ).toEqual( 404 );
|
||||
} );
|
||||
} );
|
||||
} );
|
|
@ -1,10 +1,11 @@
|
|||
const { ordersApi } = require('../../endpoints/orders');
|
||||
const { order } = require('../../data');
|
||||
const { ordersApi } = require( '../../endpoints/orders' );
|
||||
const { order } = require( '../../data' );
|
||||
const { createSampleData, deleteSampleData } = require( '../../data/orders' );
|
||||
|
||||
/**
|
||||
* Billing properties to update.
|
||||
*/
|
||||
const updatedCustomerBilling = {
|
||||
const updatedCustomerBilling = {
|
||||
first_name: 'Jane',
|
||||
last_name: 'Doe',
|
||||
company: 'Automattic',
|
||||
|
@ -41,43 +42,523 @@ const updatedCustomerShipping = {
|
|||
* @group orders
|
||||
*
|
||||
*/
|
||||
describe('Orders API tests', () => {
|
||||
let orderId;
|
||||
describe( 'Orders API tests', () => {
|
||||
let orderId, sampleData;
|
||||
|
||||
it('can create an order', async () => {
|
||||
beforeAll( async () => {
|
||||
sampleData = await createSampleData();
|
||||
}, 100000 );
|
||||
|
||||
afterAll( async () => {
|
||||
await deleteSampleData( sampleData );
|
||||
}, 10000 );
|
||||
|
||||
it( 'can create an order', async () => {
|
||||
const response = await ordersApi.create.order( order );
|
||||
expect( response.status ).toEqual( ordersApi.create.responseCode );
|
||||
expect( response.body.id ).toBeDefined();
|
||||
orderId = response.body.id;
|
||||
|
||||
// Validate the data type and verify the order is in a pending state
|
||||
expect( typeof response.body.status ).toBe('string');
|
||||
expect( response.body.status ).toEqual('pending');
|
||||
});
|
||||
expect( typeof response.body.status ).toBe( 'string' );
|
||||
expect( response.body.status ).toEqual( 'pending' );
|
||||
} );
|
||||
|
||||
it('can retrieve an order', async () => {
|
||||
it( 'can retrieve an order', async () => {
|
||||
const response = await ordersApi.retrieve.order( orderId );
|
||||
expect( response.status ).toEqual( ordersApi.retrieve.responseCode );
|
||||
expect( response.body.id ).toEqual( orderId );
|
||||
});
|
||||
} );
|
||||
|
||||
it('can add shipping and billing contacts to an order', async () => {
|
||||
it( 'can add shipping and billing contacts to an order', async () => {
|
||||
// Update the billing and shipping fields on the order
|
||||
order.billing = updatedCustomerBilling;
|
||||
order.shipping = updatedCustomerShipping;
|
||||
|
||||
const response = await ordersApi.update.order( orderId, order );
|
||||
expect( response.status).toEqual( ordersApi.update.responseCode );
|
||||
expect( response.status ).toEqual( ordersApi.update.responseCode );
|
||||
|
||||
expect( response.body.billing ).toEqual( updatedCustomerBilling );
|
||||
expect( response.body.shipping ).toEqual( updatedCustomerShipping );
|
||||
});
|
||||
} );
|
||||
|
||||
it('can permanently delete an order', async () => {
|
||||
it( 'can permanently delete an order', async () => {
|
||||
const response = await ordersApi.delete.order( orderId, true );
|
||||
expect( response.status ).toEqual( ordersApi.delete.responseCode );
|
||||
|
||||
const getOrderResponse = await ordersApi.retrieve.order( orderId );
|
||||
expect( getOrderResponse.status ).toEqual( 404 );
|
||||
});
|
||||
});
|
||||
} );
|
||||
|
||||
describe( 'List all orders', () => {
|
||||
const ORDERS_COUNT = 10;
|
||||
|
||||
it( 'pagination', async () => {
|
||||
const pageSize = 4;
|
||||
const page1 = await ordersApi.listAll.orders( {
|
||||
per_page: pageSize,
|
||||
} );
|
||||
const page2 = await ordersApi.listAll.orders( {
|
||||
per_page: pageSize,
|
||||
page: 2,
|
||||
} );
|
||||
expect( page1.statusCode ).toEqual( 200 );
|
||||
expect( page2.statusCode ).toEqual( 200 );
|
||||
|
||||
// Verify total page count.
|
||||
expect( page1.headers[ 'x-wp-total' ] ).toEqual(
|
||||
ORDERS_COUNT.toString()
|
||||
);
|
||||
expect( page1.headers[ 'x-wp-totalpages' ] ).toEqual( '3' );
|
||||
|
||||
// Verify we get pageSize'd arrays.
|
||||
expect( Array.isArray( page1.body ) ).toBe( true );
|
||||
expect( Array.isArray( page2.body ) ).toBe( true );
|
||||
expect( page1.body ).toHaveLength( pageSize );
|
||||
expect( page2.body ).toHaveLength( pageSize );
|
||||
|
||||
// Ensure all of the order IDs are unique (no page overlap).
|
||||
const allOrderIds = page1.body
|
||||
.concat( page2.body )
|
||||
.reduce( ( acc, { id } ) => {
|
||||
acc[ id ] = 1;
|
||||
return acc;
|
||||
}, {} );
|
||||
expect( Object.keys( allOrderIds ) ).toHaveLength( pageSize * 2 );
|
||||
|
||||
// Verify that offset takes precedent over page number.
|
||||
const page2Offset = await ordersApi.listAll.orders( {
|
||||
per_page: pageSize,
|
||||
page: 2,
|
||||
offset: pageSize + 1,
|
||||
} );
|
||||
// The offset pushes the result set 1 order past the start of page 2.
|
||||
expect( page2Offset.body ).toEqual(
|
||||
expect.not.arrayContaining( [
|
||||
expect.objectContaining( { id: page2.body[ 0 ].id } ),
|
||||
] )
|
||||
);
|
||||
expect( page2Offset.body[ 0 ].id ).toEqual( page2.body[ 1 ].id );
|
||||
|
||||
// Verify the last page only has 1 order as we expect.
|
||||
const lastPage = await ordersApi.listAll.orders( {
|
||||
per_page: pageSize,
|
||||
page: 3,
|
||||
} );
|
||||
expect( Array.isArray( lastPage.body ) ).toBe( true );
|
||||
expect( lastPage.body ).toHaveLength( 2 );
|
||||
|
||||
// Verify a page outside the total page count is empty.
|
||||
const page6 = await ordersApi.listAll.orders( {
|
||||
per_page: pageSize,
|
||||
page: 6,
|
||||
} );
|
||||
expect( Array.isArray( page6.body ) ).toBe( true );
|
||||
expect( page6.body ).toHaveLength( 0 );
|
||||
} );
|
||||
|
||||
it( 'inclusion / exclusion', async () => {
|
||||
const allOrders = await ordersApi.listAll.orders( {
|
||||
per_page: 10,
|
||||
} );
|
||||
expect( allOrders.statusCode ).toEqual( 200 );
|
||||
const allOrdersIds = allOrders.body.map( ( order ) => order.id );
|
||||
expect( allOrdersIds ).toHaveLength( ORDERS_COUNT );
|
||||
|
||||
const ordersToFilter = [
|
||||
allOrdersIds[ 0 ],
|
||||
allOrdersIds[ 2 ],
|
||||
allOrdersIds[ 4 ],
|
||||
allOrdersIds[ 7 ],
|
||||
];
|
||||
|
||||
const included = await ordersApi.listAll.orders( {
|
||||
per_page: 20,
|
||||
include: ordersToFilter.join( ',' ),
|
||||
} );
|
||||
expect( included.statusCode ).toEqual( 200 );
|
||||
expect( included.body ).toHaveLength( ordersToFilter.length );
|
||||
expect( included.body ).toEqual(
|
||||
expect.arrayContaining(
|
||||
ordersToFilter.map( ( id ) =>
|
||||
expect.objectContaining( { id } )
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
const excluded = await ordersApi.listAll.orders( {
|
||||
per_page: 20,
|
||||
exclude: ordersToFilter.join( ',' ),
|
||||
} );
|
||||
expect( excluded.statusCode ).toEqual( 200 );
|
||||
expect( excluded.body ).toHaveLength(
|
||||
ORDERS_COUNT - ordersToFilter.length
|
||||
);
|
||||
expect( excluded.body ).toEqual(
|
||||
expect.not.arrayContaining(
|
||||
ordersToFilter.map( ( id ) =>
|
||||
expect.objectContaining( { id } )
|
||||
)
|
||||
)
|
||||
);
|
||||
} );
|
||||
|
||||
it( 'parent', async () => {
|
||||
const result1 = await ordersApi.listAll.orders( {
|
||||
parent: sampleData.hierarchicalOrders.parent.id,
|
||||
} );
|
||||
expect( result1.statusCode ).toEqual( 200 );
|
||||
expect( result1.body ).toHaveLength( 1 );
|
||||
expect( result1.body[ 0 ].id ).toBe(
|
||||
sampleData.hierarchicalOrders.child.id
|
||||
);
|
||||
|
||||
const result2 = await ordersApi.listAll.orders( {
|
||||
parent_exclude: sampleData.hierarchicalOrders.parent.id,
|
||||
} );
|
||||
expect( result2.statusCode ).toEqual( 200 );
|
||||
expect( result2.body ).toEqual(
|
||||
expect.not.arrayContaining( [
|
||||
expect.objectContaining( {
|
||||
id: sampleData.hierarchicalOrders.child.id,
|
||||
} ),
|
||||
] )
|
||||
);
|
||||
} );
|
||||
|
||||
it( 'status', async () => {
|
||||
const result1 = await ordersApi.listAll.orders( {
|
||||
status: 'completed',
|
||||
} );
|
||||
|
||||
expect( result1.statusCode ).toEqual( 200 );
|
||||
expect( result1.body ).toHaveLength( 2 );
|
||||
expect( result1.body ).toEqual(
|
||||
expect.arrayContaining( [
|
||||
expect.objectContaining( {
|
||||
status: 'completed',
|
||||
customer_id: 0,
|
||||
line_items: expect.arrayContaining( [
|
||||
expect.objectContaining( {
|
||||
name: 'Single',
|
||||
quantity: 2,
|
||||
} ),
|
||||
expect.objectContaining( {
|
||||
name: 'Beanie with Logo',
|
||||
quantity: 3,
|
||||
} ),
|
||||
expect.objectContaining( {
|
||||
name: 'T-Shirt',
|
||||
quantity: 1,
|
||||
} ),
|
||||
] ),
|
||||
} ),
|
||||
expect.objectContaining( {
|
||||
status: 'completed',
|
||||
customer_id: sampleData.customers.tina.id,
|
||||
line_items: expect.arrayContaining( [
|
||||
expect.objectContaining( {
|
||||
name: 'Sunglasses',
|
||||
quantity: 1,
|
||||
} ),
|
||||
] ),
|
||||
} ),
|
||||
] )
|
||||
);
|
||||
|
||||
const result2 = await ordersApi.listAll.orders( {
|
||||
status: 'processing',
|
||||
} );
|
||||
expect( result2.statusCode ).toEqual( 200 );
|
||||
expect( result2.body ).toHaveLength( 8 );
|
||||
expect( result2.body ).toEqual(
|
||||
expect.not.arrayContaining(
|
||||
result1.body.map( ( { id } ) =>
|
||||
expect.objectContaining( { id } )
|
||||
)
|
||||
)
|
||||
);
|
||||
} );
|
||||
|
||||
it( 'customer', async () => {
|
||||
const result1 = await ordersApi.listAll.orders( {
|
||||
customer: sampleData.customers.john.id,
|
||||
} );
|
||||
expect( result1.statusCode ).toEqual( 200 );
|
||||
expect( result1.body ).toHaveLength( 5 );
|
||||
result1.body.forEach( ( order ) =>
|
||||
expect( order ).toEqual(
|
||||
expect.objectContaining( {
|
||||
customer_id: sampleData.customers.john.id,
|
||||
} )
|
||||
)
|
||||
);
|
||||
|
||||
const result2 = await ordersApi.listAll.orders( {
|
||||
customer: 0,
|
||||
} );
|
||||
expect( result2.statusCode ).toEqual( 200 );
|
||||
expect( result2.body ).toHaveLength( 3 );
|
||||
result2.body.forEach( ( order ) =>
|
||||
expect( order ).toEqual(
|
||||
expect.objectContaining( {
|
||||
customer_id: 0,
|
||||
} )
|
||||
)
|
||||
);
|
||||
} );
|
||||
|
||||
it( 'product', async () => {
|
||||
const beanie = sampleData.testProductData.simpleProducts.find(
|
||||
( p ) => p.name === 'Beanie'
|
||||
);
|
||||
const result1 = await ordersApi.listAll.orders( {
|
||||
product: beanie.id,
|
||||
} );
|
||||
expect( result1.statusCode ).toEqual( 200 );
|
||||
expect( result1.body ).toHaveLength( 2 );
|
||||
result1.body.forEach( ( order ) =>
|
||||
expect( order ).toEqual(
|
||||
expect.objectContaining( {
|
||||
line_items: expect.arrayContaining( [
|
||||
expect.objectContaining( {
|
||||
name: 'Beanie',
|
||||
} ),
|
||||
] ),
|
||||
} )
|
||||
)
|
||||
);
|
||||
} );
|
||||
|
||||
// NOTE: This does not verify the `taxes` array nested in line items.
|
||||
// While the precision parameter doesn't affect those values, after some
|
||||
// discussion it seems `dp` may not be supported in v4 of the API.
|
||||
it( 'dp (precision)', async () => {
|
||||
const expectPrecisionToMatch = ( value, dp ) => {
|
||||
expect( value ).toEqual(
|
||||
Number.parseFloat( value ).toFixed( dp )
|
||||
);
|
||||
};
|
||||
|
||||
const verifyOrderPrecision = ( order, dp ) => {
|
||||
expectPrecisionToMatch( order[ 'discount_total' ], dp );
|
||||
expectPrecisionToMatch( order[ 'discount_tax' ], dp );
|
||||
expectPrecisionToMatch( order[ 'shipping_total' ], dp );
|
||||
expectPrecisionToMatch( order[ 'shipping_tax' ], dp );
|
||||
expectPrecisionToMatch( order[ 'cart_tax' ], dp );
|
||||
expectPrecisionToMatch( order[ 'total' ], dp );
|
||||
expectPrecisionToMatch( order[ 'total_tax' ], dp );
|
||||
|
||||
order[ 'line_items' ].forEach( ( lineItem ) => {
|
||||
expectPrecisionToMatch( lineItem[ 'total' ], dp );
|
||||
expectPrecisionToMatch( lineItem[ 'total_tax' ], dp );
|
||||
} );
|
||||
|
||||
order[ 'tax_lines' ].forEach( ( taxLine ) => {
|
||||
expectPrecisionToMatch( taxLine[ 'tax_total' ], dp );
|
||||
expectPrecisionToMatch(
|
||||
taxLine[ 'shipping_tax_total' ],
|
||||
dp
|
||||
);
|
||||
} );
|
||||
|
||||
order[ 'shipping_lines' ].forEach( ( shippingLine ) => {
|
||||
expectPrecisionToMatch( shippingLine[ 'total' ], dp );
|
||||
expectPrecisionToMatch( shippingLine[ 'total_tax' ], dp );
|
||||
} );
|
||||
|
||||
order[ 'fee_lines' ].forEach( ( feeLine ) => {
|
||||
expectPrecisionToMatch( feeLine[ 'total' ], dp );
|
||||
expectPrecisionToMatch( feeLine[ 'total_tax' ], dp );
|
||||
} );
|
||||
|
||||
order[ 'refunds' ].forEach( ( refund ) => {
|
||||
expectPrecisionToMatch( refund[ 'total' ], dp );
|
||||
} );
|
||||
};
|
||||
|
||||
const result1 = await ordersApi.retrieve.order(
|
||||
sampleData.precisionOrder.id,
|
||||
{
|
||||
dp: 1,
|
||||
}
|
||||
);
|
||||
expect( result1.statusCode ).toEqual( 200 );
|
||||
verifyOrderPrecision( result1.body, 1 );
|
||||
|
||||
const result2 = await ordersApi.retrieve.order(
|
||||
sampleData.precisionOrder.id,
|
||||
{
|
||||
dp: 3,
|
||||
}
|
||||
);
|
||||
expect( result2.statusCode ).toEqual( 200 );
|
||||
verifyOrderPrecision( result2.body, 3 );
|
||||
|
||||
const result3 = await ordersApi.retrieve.order(
|
||||
sampleData.precisionOrder.id
|
||||
);
|
||||
expect( result3.statusCode ).toEqual( 200 );
|
||||
verifyOrderPrecision( result3.body, 2 ); // The default value for 'dp' is 2.
|
||||
} );
|
||||
|
||||
it( 'search', async () => {
|
||||
// By default, 'search' looks in:
|
||||
// - _billing_address_index
|
||||
// - _shipping_address_index
|
||||
// - _billing_last_name
|
||||
// - _billing_email
|
||||
// - order_item_name
|
||||
|
||||
// Test billing email.
|
||||
const result1 = await ordersApi.listAll.orders( {
|
||||
search: 'example.com',
|
||||
} );
|
||||
expect( result1.statusCode ).toEqual( 200 );
|
||||
expect( result1.body ).toHaveLength( 7 );
|
||||
result1.body.forEach( ( order ) =>
|
||||
expect( order.billing.email ).toContain( 'example.com' )
|
||||
);
|
||||
|
||||
// Test billing address.
|
||||
const result2 = await ordersApi.listAll.orders( {
|
||||
search: 'gainesville', // Intentionally lowercase.
|
||||
} );
|
||||
expect( result2.statusCode ).toEqual( 200 );
|
||||
expect( result2.body ).toHaveLength( 1 );
|
||||
expect( result2.body[ 0 ].id ).toEqual( sampleData.guestOrder.id );
|
||||
|
||||
// Test shipping address.
|
||||
const result3 = await ordersApi.listAll.orders( {
|
||||
search: 'Incognito',
|
||||
} );
|
||||
expect( result3.statusCode ).toEqual( 200 );
|
||||
expect( result3.body ).toHaveLength( 1 );
|
||||
expect( result3.body[ 0 ].id ).toEqual( sampleData.guestOrder.id );
|
||||
|
||||
// Test billing last name.
|
||||
const result4 = await ordersApi.listAll.orders( {
|
||||
search: 'Doe',
|
||||
} );
|
||||
expect( result4.statusCode ).toEqual( 200 );
|
||||
expect( result4.body ).toHaveLength( 5 );
|
||||
result4.body.forEach( ( order ) =>
|
||||
expect( order.billing.last_name ).toEqual( 'Doe' )
|
||||
);
|
||||
|
||||
// Test order item name.
|
||||
const result5 = await ordersApi.listAll.orders( {
|
||||
search: 'Pennant',
|
||||
} );
|
||||
expect( result5.statusCode ).toEqual( 200 );
|
||||
expect( result5.body ).toHaveLength( 2 );
|
||||
result5.body.forEach( ( order ) =>
|
||||
expect( order ).toEqual(
|
||||
expect.objectContaining( {
|
||||
line_items: expect.arrayContaining( [
|
||||
expect.objectContaining( {
|
||||
name: 'WordPress Pennant',
|
||||
} ),
|
||||
] ),
|
||||
} )
|
||||
)
|
||||
);
|
||||
} );
|
||||
|
||||
describe( 'orderby', () => {
|
||||
// The orders endpoint `orderby` parameter uses WP_Query, so our tests won't
|
||||
// include slug and title, since they are programmatically generated.
|
||||
it( 'default', async () => {
|
||||
// Default = date desc.
|
||||
const result = await ordersApi.listAll.orders();
|
||||
expect( result.statusCode ).toEqual( 200 );
|
||||
|
||||
// Verify all dates are in descending order.
|
||||
let lastDate = Date.now();
|
||||
result.body.forEach( ( { date_created } ) => {
|
||||
const created = Date.parse( date_created + '.000Z' );
|
||||
expect( lastDate ).toBeGreaterThanOrEqual( created );
|
||||
lastDate = created;
|
||||
} );
|
||||
} );
|
||||
|
||||
it( 'date', async () => {
|
||||
const result = await ordersApi.listAll.orders( {
|
||||
order: 'asc',
|
||||
orderby: 'date',
|
||||
} );
|
||||
expect( result.statusCode ).toEqual( 200 );
|
||||
|
||||
// Verify all dates are in ascending order.
|
||||
let lastDate = 0;
|
||||
result.body.forEach( ( { date_created } ) => {
|
||||
const created = Date.parse( date_created + '.000Z' );
|
||||
expect( created ).toBeGreaterThanOrEqual( lastDate );
|
||||
lastDate = created;
|
||||
} );
|
||||
} );
|
||||
|
||||
it( 'id', async () => {
|
||||
const result1 = await ordersApi.listAll.orders( {
|
||||
order: 'asc',
|
||||
orderby: 'id',
|
||||
} );
|
||||
expect( result1.statusCode ).toEqual( 200 );
|
||||
|
||||
// Verify all results are in ascending order.
|
||||
let lastId = 0;
|
||||
result1.body.forEach( ( { id } ) => {
|
||||
expect( id ).toBeGreaterThan( lastId );
|
||||
lastId = id;
|
||||
} );
|
||||
|
||||
const result2 = await ordersApi.listAll.orders( {
|
||||
order: 'desc',
|
||||
orderby: 'id',
|
||||
} );
|
||||
expect( result2.statusCode ).toEqual( 200 );
|
||||
|
||||
// Verify all results are in descending order.
|
||||
lastId = Number.MAX_SAFE_INTEGER;
|
||||
result2.body.forEach( ( { id } ) => {
|
||||
expect( lastId ).toBeGreaterThan( id );
|
||||
lastId = id;
|
||||
} );
|
||||
} );
|
||||
|
||||
it( 'include', async () => {
|
||||
const includeIds = [
|
||||
sampleData.precisionOrder.id,
|
||||
sampleData.hierarchicalOrders.parent.id,
|
||||
sampleData.guestOrder.id,
|
||||
];
|
||||
|
||||
const result1 = await ordersApi.listAll.orders( {
|
||||
order: 'asc',
|
||||
orderby: 'include',
|
||||
include: includeIds.join( ',' ),
|
||||
} );
|
||||
expect( result1.statusCode ).toEqual( 200 );
|
||||
expect( result1.body ).toHaveLength( includeIds.length );
|
||||
|
||||
// Verify all results are in proper order.
|
||||
result1.body.forEach( ( { id }, idx ) => {
|
||||
expect( id ).toBe( includeIds[ idx ] );
|
||||
} );
|
||||
|
||||
const result2 = await ordersApi.listAll.orders( {
|
||||
order: 'desc',
|
||||
orderby: 'include',
|
||||
include: includeIds.join( ',' ),
|
||||
} );
|
||||
expect( result2.statusCode ).toEqual( 200 );
|
||||
expect( result2.body ).toHaveLength( includeIds.length );
|
||||
|
||||
// Verify all results are in proper order.
|
||||
result2.body.forEach( ( { id }, idx ) => {
|
||||
expect( id ).toBe( includeIds[ idx ] );
|
||||
} );
|
||||
} );
|
||||
} );
|
||||
} );
|
||||
} );
|
||||
|
|
|
@ -1,777 +0,0 @@
|
|||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
const { createSampleData, deleteSampleData } = require( '../../data/products' );
|
||||
const { productsApi } = require('../../endpoints/products');
|
||||
|
||||
/**
|
||||
* Tests for the WooCommerce Products API.
|
||||
*
|
||||
* @group api
|
||||
* @group products
|
||||
*
|
||||
*/
|
||||
describe( 'Products API tests', () => {
|
||||
|
||||
const PRODUCTS_COUNT = 20;
|
||||
let sampleData;
|
||||
|
||||
beforeAll( async () => {
|
||||
sampleData = await createSampleData();
|
||||
}, 10000 );
|
||||
|
||||
afterAll( async () => {
|
||||
await deleteSampleData( sampleData );
|
||||
}, 10000 );
|
||||
|
||||
describe( 'List all products', () => {
|
||||
|
||||
it( 'defaults', async () => {
|
||||
const result = await productsApi.listAll.products();
|
||||
expect( result.statusCode ).toEqual( 200 );
|
||||
expect( result.headers['x-wp-total'] ).toEqual( PRODUCTS_COUNT.toString() );
|
||||
expect( result.headers['x-wp-totalpages'] ).toEqual( '2' );
|
||||
} );
|
||||
|
||||
it( 'pagination', async () => {
|
||||
const pageSize = 6;
|
||||
const page1 = await productsApi.listAll.products( {
|
||||
per_page: pageSize,
|
||||
} );
|
||||
const page2 = await productsApi.listAll.products( {
|
||||
per_page: pageSize,
|
||||
page: 2,
|
||||
} );
|
||||
expect( page1.statusCode ).toEqual( 200 );
|
||||
expect( page2.statusCode ).toEqual( 200 );
|
||||
|
||||
// Verify total page count.
|
||||
expect( page1.headers['x-wp-total'] ).toEqual( PRODUCTS_COUNT.toString() );
|
||||
expect( page1.headers['x-wp-totalpages'] ).toEqual( '4' );
|
||||
|
||||
// Verify we get pageSize'd arrays.
|
||||
expect( Array.isArray( page1.body ) ).toBe( true );
|
||||
expect( Array.isArray( page2.body ) ).toBe( true );
|
||||
expect( page1.body ).toHaveLength( pageSize );
|
||||
expect( page2.body ).toHaveLength( pageSize );
|
||||
|
||||
// Ensure all of the product IDs are unique (no page overlap).
|
||||
const allProductIds = page1.body.concat( page2.body ).reduce( ( acc, product ) => {
|
||||
acc[ product.id ] = 1;
|
||||
return acc;
|
||||
}, {} );
|
||||
expect( Object.keys( allProductIds ) ).toHaveLength( pageSize * 2 );
|
||||
|
||||
// Verify that offset takes precedent over page number.
|
||||
const page2Offset = await productsApi.listAll.products( {
|
||||
per_page: pageSize,
|
||||
page: 2,
|
||||
offset: pageSize + 1,
|
||||
} );
|
||||
// The offset pushes the result set 1 product past the start of page 2.
|
||||
expect( page2Offset.body ).toEqual(
|
||||
expect.not.arrayContaining( [
|
||||
expect.objectContaining( { id: page2.body[0].id } )
|
||||
] )
|
||||
);
|
||||
expect( page2Offset.body[0].id ).toEqual( page2.body[1].id );
|
||||
|
||||
// Verify the last page only has 2 products as we expect.
|
||||
const lastPage = await productsApi.listAll.products( {
|
||||
per_page: pageSize,
|
||||
page: 4,
|
||||
} );
|
||||
expect( Array.isArray( lastPage.body ) ).toBe( true );
|
||||
expect( lastPage.body ).toHaveLength( 2 );
|
||||
|
||||
// Verify a page outside the total page count is empty.
|
||||
const page6 = await productsApi.listAll.products( {
|
||||
per_page: pageSize,
|
||||
page: 6,
|
||||
} );
|
||||
expect( Array.isArray( page6.body ) ).toBe( true );
|
||||
expect( page6.body ).toHaveLength( 0 );
|
||||
} );
|
||||
|
||||
it( 'search', async () => {
|
||||
// Match in the short description.
|
||||
const result1 = await productsApi.listAll.products( {
|
||||
search: 'external'
|
||||
} );
|
||||
expect( result1.statusCode ).toEqual( 200 );
|
||||
expect( result1.body ).toHaveLength( 1 );
|
||||
expect( result1.body[0].name ).toBe( 'WordPress Pennant' );
|
||||
|
||||
// Match in the product name.
|
||||
const result2 = await productsApi.listAll.products( {
|
||||
search: 'pocket'
|
||||
} );
|
||||
expect( result2.statusCode ).toEqual( 200 );
|
||||
expect( result2.body ).toHaveLength( 1 );
|
||||
expect( result2.body[0].name ).toBe( 'Hoodie with Pocket' );
|
||||
} );
|
||||
|
||||
it( 'inclusion / exclusion', async () => {
|
||||
const allProducts = await productsApi.listAll.products( {
|
||||
per_page: 20,
|
||||
} );
|
||||
expect( allProducts.statusCode ).toEqual( 200 );
|
||||
const allProductIds = allProducts.body.map( product => product.id );
|
||||
expect( allProductIds ).toHaveLength( PRODUCTS_COUNT );
|
||||
|
||||
const productsToFilter = [
|
||||
allProductIds[2],
|
||||
allProductIds[4],
|
||||
allProductIds[7],
|
||||
allProductIds[13],
|
||||
];
|
||||
|
||||
const included = await productsApi.listAll.products( {
|
||||
per_page: 20,
|
||||
include: productsToFilter.join( ',' ),
|
||||
} );
|
||||
expect( included.statusCode ).toEqual( 200 );
|
||||
expect( included.body ).toHaveLength( productsToFilter.length );
|
||||
expect( included.body ).toEqual(
|
||||
expect.arrayContaining(
|
||||
productsToFilter.map( id => expect.objectContaining( { id } ) )
|
||||
)
|
||||
);
|
||||
|
||||
const excluded = await productsApi.listAll.products( {
|
||||
per_page: 20,
|
||||
exclude: productsToFilter.join( ',' ),
|
||||
} );
|
||||
expect( excluded.statusCode ).toEqual( 200 );
|
||||
expect( excluded.body ).toHaveLength( PRODUCTS_COUNT - productsToFilter.length );
|
||||
expect( excluded.body ).toEqual(
|
||||
expect.not.arrayContaining(
|
||||
productsToFilter.map( id => expect.objectContaining( { id } ) )
|
||||
)
|
||||
);
|
||||
|
||||
} );
|
||||
|
||||
it( 'slug', async () => {
|
||||
// Match by slug.
|
||||
const result1 = await productsApi.listAll.products( {
|
||||
slug: 't-shirt-with-logo'
|
||||
} );
|
||||
expect( result1.statusCode ).toEqual( 200 );
|
||||
expect( result1.body ).toHaveLength( 1 );
|
||||
expect( result1.body[0].slug ).toBe( 't-shirt-with-logo' );
|
||||
|
||||
// No matches
|
||||
const result2 = await productsApi.listAll.products( {
|
||||
slug: 'no-product-with-this-slug'
|
||||
} );
|
||||
expect( result2.statusCode ).toEqual( 200 );
|
||||
expect( result2.body ).toHaveLength( 0 );
|
||||
} );
|
||||
|
||||
it( 'sku', async () => {
|
||||
// Match by SKU.
|
||||
const result1 = await productsApi.listAll.products( {
|
||||
sku: 'woo-sunglasses'
|
||||
} );
|
||||
expect( result1.statusCode ).toEqual( 200 );
|
||||
expect( result1.body ).toHaveLength( 1 );
|
||||
expect( result1.body[0].sku ).toBe( 'woo-sunglasses' );
|
||||
|
||||
// No matches
|
||||
const result2 = await productsApi.listAll.products( {
|
||||
sku: 'no-product-with-this-sku'
|
||||
} );
|
||||
expect( result2.statusCode ).toEqual( 200 );
|
||||
expect( result2.body ).toHaveLength( 0 );
|
||||
} );
|
||||
|
||||
it( 'type', async () => {
|
||||
const result1 = await productsApi.listAll.products( {
|
||||
type: 'simple'
|
||||
} );
|
||||
expect( result1.statusCode ).toEqual( 200 );
|
||||
expect( result1.headers['x-wp-total'] ).toEqual( '16' );
|
||||
|
||||
const result2 = await productsApi.listAll.products( {
|
||||
type: 'external'
|
||||
} );
|
||||
expect( result2.statusCode ).toEqual( 200 );
|
||||
expect( result2.body ).toHaveLength( 1 );
|
||||
expect( result2.body[0].name ).toBe( 'WordPress Pennant' );
|
||||
|
||||
const result3 = await productsApi.listAll.products( {
|
||||
type: 'variable'
|
||||
} );
|
||||
expect( result3.statusCode ).toEqual( 200 );
|
||||
expect( result3.body ).toHaveLength( 2 );
|
||||
|
||||
const result4 = await productsApi.listAll.products( {
|
||||
type: 'grouped'
|
||||
} );
|
||||
expect( result4.statusCode ).toEqual( 200 );
|
||||
expect( result4.body ).toHaveLength( 1 );
|
||||
expect( result4.body[0].name ).toBe( 'Logo Collection' );
|
||||
} );
|
||||
|
||||
it( 'featured', async () => {
|
||||
const featured = [
|
||||
expect.objectContaining( { name: 'Hoodie with Zipper' } ),
|
||||
expect.objectContaining( { name: 'Hoodie with Pocket' } ),
|
||||
expect.objectContaining( { name: 'Sunglasses' } ),
|
||||
expect.objectContaining( { name: 'Cap' } ),
|
||||
expect.objectContaining( { name: 'V-Neck T-Shirt' } ),
|
||||
];
|
||||
|
||||
const result1 = await productsApi.listAll.products( {
|
||||
featured: true,
|
||||
} );
|
||||
expect( result1.statusCode ).toEqual( 200 );
|
||||
expect( result1.body ).toHaveLength( featured.length );
|
||||
expect( result1.body ).toEqual( expect.arrayContaining( featured ) );
|
||||
|
||||
const result2 = await productsApi.listAll.products( {
|
||||
featured: false,
|
||||
} );
|
||||
expect( result2.statusCode ).toEqual( 200 );
|
||||
expect( result2.body ).toEqual( expect.not.arrayContaining( featured ) );
|
||||
} );
|
||||
|
||||
it( 'categories', async () => {
|
||||
const accessory = [
|
||||
expect.objectContaining( { name: 'Beanie' } ),
|
||||
]
|
||||
const hoodies = [
|
||||
expect.objectContaining( { name: 'Hoodie with Zipper' } ),
|
||||
expect.objectContaining( { name: 'Hoodie with Pocket' } ),
|
||||
expect.objectContaining( { name: 'Hoodie with Logo' } ),
|
||||
expect.objectContaining( { name: 'Hoodie' } ),
|
||||
];
|
||||
|
||||
// Verify that subcategories are included.
|
||||
const result1 = await productsApi.listAll.products( {
|
||||
per_page: 20,
|
||||
category: sampleData.categories.clothing.id,
|
||||
} );
|
||||
expect( result1.statusCode ).toEqual( 200 );
|
||||
expect( result1.body ).toEqual( expect.arrayContaining( accessory ) );
|
||||
expect( result1.body ).toEqual( expect.arrayContaining( hoodies ) );
|
||||
|
||||
// Verify sibling categories are not.
|
||||
const result2 = await productsApi.listAll.products( {
|
||||
category: sampleData.categories.hoodies.id,
|
||||
} );
|
||||
expect( result2.statusCode ).toEqual( 200 );
|
||||
expect( result2.body ).toEqual( expect.not.arrayContaining( accessory ) );
|
||||
expect( result2.body ).toEqual( expect.arrayContaining( hoodies ) );
|
||||
} );
|
||||
|
||||
it( 'on sale', async () => {
|
||||
const onSale = [
|
||||
expect.objectContaining( { name: 'Beanie with Logo' } ),
|
||||
expect.objectContaining( { name: 'Hoodie with Pocket' } ),
|
||||
expect.objectContaining( { name: 'Single' } ),
|
||||
expect.objectContaining( { name: 'Cap' } ),
|
||||
expect.objectContaining( { name: 'Belt' } ),
|
||||
expect.objectContaining( { name: 'Beanie' } ),
|
||||
expect.objectContaining( { name: 'Hoodie' } ),
|
||||
];
|
||||
|
||||
const result1 = await productsApi.listAll.products( {
|
||||
on_sale: true,
|
||||
} );
|
||||
expect( result1.statusCode ).toEqual( 200 );
|
||||
expect( result1.body ).toHaveLength( onSale.length );
|
||||
expect( result1.body ).toEqual( expect.arrayContaining( onSale ) );
|
||||
|
||||
const result2 = await productsApi.listAll.products( {
|
||||
on_sale: false,
|
||||
} );
|
||||
expect( result2.statusCode ).toEqual( 200 );
|
||||
expect( result2.body ).toEqual( expect.not.arrayContaining( onSale ) );
|
||||
} );
|
||||
|
||||
it( 'price', async () => {
|
||||
const result1 = await productsApi.listAll.products( {
|
||||
min_price: 21,
|
||||
max_price: 28,
|
||||
} );
|
||||
expect( result1.statusCode ).toEqual( 200 );
|
||||
expect( result1.body ).toHaveLength( 1 );
|
||||
expect( result1.body[0].name ).toBe( 'Long Sleeve Tee' );
|
||||
expect( result1.body[0].price ).toBe( '25' );
|
||||
|
||||
const result2 = await productsApi.listAll.products( {
|
||||
max_price: 5,
|
||||
} );
|
||||
expect( result2.statusCode ).toEqual( 200 );
|
||||
expect( result2.body ).toHaveLength( 1 );
|
||||
expect( result2.body[0].name ).toBe( 'Single' );
|
||||
expect( result2.body[0].price ).toBe( '2' );
|
||||
|
||||
const result3 = await productsApi.listAll.products( {
|
||||
min_price: 5,
|
||||
order: 'asc',
|
||||
orderby: 'price',
|
||||
} );
|
||||
expect( result3.statusCode ).toEqual( 200 );
|
||||
expect( result3.body ).toEqual(
|
||||
expect.not.arrayContaining( [
|
||||
expect.objectContaining( { name: 'Single' } )
|
||||
] )
|
||||
);
|
||||
} );
|
||||
|
||||
it( 'before / after', async () => {
|
||||
const before = [
|
||||
expect.objectContaining( { name: 'Album' } ),
|
||||
expect.objectContaining( { name: 'Single' } ),
|
||||
expect.objectContaining( { name: 'T-Shirt with Logo' } ),
|
||||
expect.objectContaining( { name: 'Beanie with Logo' } ),
|
||||
];
|
||||
const after = [
|
||||
expect.objectContaining( { name: 'Hoodie' } ),
|
||||
expect.objectContaining( { name: 'V-Neck T-Shirt' } ),
|
||||
expect.objectContaining( { name: 'Parent Product' } ),
|
||||
expect.objectContaining( { name: 'Child Product' } ),
|
||||
];
|
||||
|
||||
const result1 = await productsApi.listAll.products( {
|
||||
before: '2021-09-05T15:50:19',
|
||||
} );
|
||||
expect( result1.statusCode ).toEqual( 200 );
|
||||
expect( result1.body ).toHaveLength( before.length );
|
||||
expect( result1.body ).toEqual( expect.arrayContaining( before ) );
|
||||
|
||||
const result2 = await productsApi.listAll.products( {
|
||||
after: '2021-09-18T15:50:18',
|
||||
} );
|
||||
expect( result2.statusCode ).toEqual( 200 );
|
||||
expect( result2.body ).toEqual( expect.not.arrayContaining( before ) );
|
||||
expect( result2.body ).toHaveLength( after.length );
|
||||
expect( result2.body ).toEqual( expect.arrayContaining( after ) );
|
||||
} );
|
||||
|
||||
it( 'attributes', async () => {
|
||||
const red = sampleData.attributes.colors.find( term => term.name === 'Red' );
|
||||
|
||||
const redProducts = [
|
||||
expect.objectContaining( { name: 'V-Neck T-Shirt' } ),
|
||||
expect.objectContaining( { name: 'Hoodie' } ),
|
||||
expect.objectContaining( { name: 'Beanie' } ),
|
||||
expect.objectContaining( { name: 'Beanie with Logo' } ),
|
||||
];
|
||||
|
||||
const result = await productsApi.listAll.products( {
|
||||
attribute: 'pa_color',
|
||||
attribute_term: red.id,
|
||||
} );
|
||||
|
||||
expect( result.statusCode ).toEqual( 200 );
|
||||
expect( result.body ).toHaveLength( redProducts.length );
|
||||
expect( result.body ).toEqual( expect.arrayContaining( redProducts ) );
|
||||
} );
|
||||
|
||||
it( 'status', async () => {
|
||||
const result1 = await productsApi.listAll.products( {
|
||||
status: 'pending'
|
||||
} );
|
||||
expect( result1.statusCode ).toEqual( 200 );
|
||||
expect( result1.body ).toHaveLength( 1 );
|
||||
expect( result1.body[0].name ).toBe( 'Polo' );
|
||||
|
||||
const result2 = await productsApi.listAll.products( {
|
||||
status: 'draft'
|
||||
} );
|
||||
expect( result2.statusCode ).toEqual( 200 );
|
||||
expect( result2.body ).toHaveLength( 0 );
|
||||
} );
|
||||
|
||||
it( 'shipping class', async () => {
|
||||
const result = await productsApi.listAll.products( {
|
||||
shipping_class: sampleData.shippingClasses.freight.id,
|
||||
} );
|
||||
expect( result.statusCode ).toEqual( 200 );
|
||||
expect( result.body ).toHaveLength( 1 );
|
||||
expect( result.body[0].name ).toBe( 'Long Sleeve Tee' );
|
||||
} );
|
||||
|
||||
it( 'tax class', async () => {
|
||||
const result = await productsApi.listAll.products( {
|
||||
tax_class: 'reduced-rate',
|
||||
} );
|
||||
expect( result.statusCode ).toEqual( 200 );
|
||||
expect( result.body ).toHaveLength( 1 );
|
||||
expect( result.body[0].name ).toBe( 'Sunglasses' );
|
||||
} );
|
||||
|
||||
it( 'stock status', async () => {
|
||||
const result = await productsApi.listAll.products( {
|
||||
stock_status: 'onbackorder',
|
||||
} );
|
||||
expect( result.statusCode ).toEqual( 200 );
|
||||
expect( result.body ).toHaveLength( 1 );
|
||||
expect( result.body[0].name ).toBe( 'T-Shirt' );
|
||||
} );
|
||||
|
||||
it( 'tags', async () => {
|
||||
const coolProducts = [
|
||||
expect.objectContaining( { name: 'Sunglasses' } ),
|
||||
expect.objectContaining( { name: 'Hoodie with Pocket' } ),
|
||||
expect.objectContaining( { name: 'Beanie' } ),
|
||||
];
|
||||
|
||||
const result = await productsApi.listAll.products( {
|
||||
tag: sampleData.tags.cool.id,
|
||||
} );
|
||||
|
||||
expect( result.statusCode ).toEqual( 200 );
|
||||
expect( result.body ).toHaveLength( coolProducts.length );
|
||||
expect( result.body ).toEqual( expect.arrayContaining( coolProducts ) );
|
||||
} );
|
||||
|
||||
it( 'parent', async () => {
|
||||
const result1 = await productsApi.listAll.products( {
|
||||
parent: sampleData.hierarchicalProducts.parent.id,
|
||||
} );
|
||||
expect( result1.statusCode ).toEqual( 200 );
|
||||
expect( result1.body ).toHaveLength( 1 );
|
||||
expect( result1.body[0].name ).toBe( 'Child Product' );
|
||||
|
||||
const result2 = await productsApi.listAll.products( {
|
||||
parent_exclude: sampleData.hierarchicalProducts.parent.id,
|
||||
} );
|
||||
expect( result2.statusCode ).toEqual( 200 );
|
||||
expect( result2.body ).toEqual( expect.not.arrayContaining( [
|
||||
expect.objectContaining( { name: 'Child Product' } ),
|
||||
] ) );
|
||||
} );
|
||||
|
||||
describe( 'orderby', () => {
|
||||
const productNamesAsc = [
|
||||
'Album',
|
||||
'Beanie',
|
||||
'Beanie with Logo',
|
||||
'Belt',
|
||||
'Cap',
|
||||
'Child Product',
|
||||
'Hoodie',
|
||||
'Hoodie with Logo',
|
||||
'Hoodie with Pocket',
|
||||
'Hoodie with Zipper',
|
||||
'Logo Collection',
|
||||
'Long Sleeve Tee',
|
||||
'Parent Product',
|
||||
'Polo',
|
||||
'Single',
|
||||
'Sunglasses',
|
||||
'T-Shirt',
|
||||
'T-Shirt with Logo',
|
||||
'V-Neck T-Shirt',
|
||||
'WordPress Pennant',
|
||||
];
|
||||
const productNamesDesc = [ ...productNamesAsc ].reverse();
|
||||
const productNamesByRatingAsc = [
|
||||
'Sunglasses',
|
||||
'Cap',
|
||||
'T-Shirt',
|
||||
];
|
||||
const productNamesByRatingDesc = [ ...productNamesByRatingAsc ].reverse();
|
||||
const productNamesByPopularityDesc = [
|
||||
'Beanie with Logo',
|
||||
'Single',
|
||||
'T-Shirt',
|
||||
];
|
||||
const productNamesByPopularityAsc = [ ...productNamesByPopularityDesc ].reverse();
|
||||
|
||||
it( 'default', async () => {
|
||||
// Default = date desc.
|
||||
const result = await productsApi.listAll.products();
|
||||
expect( result.statusCode ).toEqual( 200 );
|
||||
|
||||
// Verify all dates are in descending order.
|
||||
let lastDate = Date.now();
|
||||
result.body.forEach( ( { date_created_gmt } ) => {
|
||||
const created = Date.parse( date_created_gmt + '.000Z' );
|
||||
expect( lastDate ).toBeGreaterThan( created );
|
||||
lastDate = created;
|
||||
} );
|
||||
} );
|
||||
|
||||
it( 'date', async () => {
|
||||
const result = await productsApi.listAll.products( {
|
||||
order: 'asc',
|
||||
orderby: 'date',
|
||||
} );
|
||||
expect( result.statusCode ).toEqual( 200 );
|
||||
|
||||
// Verify all dates are in ascending order.
|
||||
let lastDate = 0;
|
||||
result.body.forEach( ( { date_created_gmt } ) => {
|
||||
const created = Date.parse( date_created_gmt + '.000Z' );
|
||||
expect( created ).toBeGreaterThan( lastDate );
|
||||
lastDate = created;
|
||||
} );
|
||||
} );
|
||||
|
||||
it( 'id', async () => {
|
||||
const result1 = await productsApi.listAll.products( {
|
||||
order: 'asc',
|
||||
orderby: 'id',
|
||||
} );
|
||||
expect( result1.statusCode ).toEqual( 200 );
|
||||
|
||||
// Verify all results are in ascending order.
|
||||
let lastId = 0;
|
||||
result1.body.forEach( ( { id } ) => {
|
||||
expect( id ).toBeGreaterThan( lastId );
|
||||
lastId = id;
|
||||
} );
|
||||
|
||||
const result2 = await productsApi.listAll.products( {
|
||||
order: 'desc',
|
||||
orderby: 'id',
|
||||
} );
|
||||
expect( result2.statusCode ).toEqual( 200 );
|
||||
|
||||
// Verify all results are in descending order.
|
||||
lastId = Number.MAX_SAFE_INTEGER;
|
||||
result2.body.forEach( ( { id } ) => {
|
||||
expect( lastId ).toBeGreaterThan( id );
|
||||
lastId = id;
|
||||
} );
|
||||
} );
|
||||
|
||||
it( 'title', async () => {
|
||||
const result1 = await productsApi.listAll.products( {
|
||||
order: 'asc',
|
||||
orderby: 'title',
|
||||
per_page: productNamesAsc.length,
|
||||
} );
|
||||
expect( result1.statusCode ).toEqual( 200 );
|
||||
|
||||
// Verify all results are in ascending order.
|
||||
result1.body.forEach( ( { name }, idx ) => {
|
||||
expect( name ).toBe( productNamesAsc[ idx ] );
|
||||
} );
|
||||
|
||||
const result2 = await productsApi.listAll.products( {
|
||||
order: 'desc',
|
||||
orderby: 'title',
|
||||
per_page: productNamesDesc.length,
|
||||
} );
|
||||
expect( result2.statusCode ).toEqual( 200 );
|
||||
|
||||
// Verify all results are in descending order.
|
||||
result2.body.forEach( ( { name }, idx ) => {
|
||||
expect( name ).toBe( productNamesDesc[ idx ] );
|
||||
} );
|
||||
} );
|
||||
|
||||
it( 'slug', async () => {
|
||||
const productNamesBySlugAsc = [
|
||||
'Polo', // The Polo isn't published so it has an empty slug.
|
||||
...productNamesAsc.filter( p => p !== 'Polo' ),
|
||||
];
|
||||
const productNamesBySlugDesc = [ ...productNamesBySlugAsc ].reverse();
|
||||
|
||||
const result1 = await productsApi.listAll.products( {
|
||||
order: 'asc',
|
||||
orderby: 'slug',
|
||||
per_page: productNamesBySlugAsc.length,
|
||||
} );
|
||||
expect( result1.statusCode ).toEqual( 200 );
|
||||
|
||||
// Verify all results are in ascending order.
|
||||
result1.body.forEach( ( { name }, idx ) => {
|
||||
expect( name ).toBe( productNamesBySlugAsc[ idx ] );
|
||||
} );
|
||||
|
||||
const result2 = await productsApi.listAll.products( {
|
||||
order: 'desc',
|
||||
orderby: 'slug',
|
||||
per_page: productNamesBySlugDesc.length,
|
||||
} );
|
||||
expect( result2.statusCode ).toEqual( 200 );
|
||||
|
||||
// Verify all results are in descending order.
|
||||
result2.body.forEach( ( { name }, idx ) => {
|
||||
expect( name ).toBe( productNamesBySlugDesc[ idx ] );
|
||||
} );
|
||||
} );
|
||||
|
||||
it( 'price', async () => {
|
||||
const productNamesMinPriceAsc = [
|
||||
'Parent Product',
|
||||
'Child Product',
|
||||
'Single',
|
||||
'WordPress Pennant',
|
||||
'Album',
|
||||
'V-Neck T-Shirt',
|
||||
'Cap',
|
||||
'Beanie with Logo',
|
||||
'T-Shirt with Logo',
|
||||
'Beanie',
|
||||
'T-Shirt',
|
||||
'Logo Collection',
|
||||
'Polo',
|
||||
'Long Sleeve Tee',
|
||||
'Hoodie with Pocket',
|
||||
'Hoodie',
|
||||
'Hoodie with Zipper',
|
||||
'Hoodie with Logo',
|
||||
'Belt',
|
||||
'Sunglasses',
|
||||
];
|
||||
const result1 = await productsApi.listAll.products( {
|
||||
order: 'asc',
|
||||
orderby: 'price',
|
||||
per_page: productNamesMinPriceAsc.length
|
||||
} );
|
||||
expect( result1.statusCode ).toEqual( 200 );
|
||||
expect( result1.body ).toHaveLength( productNamesMinPriceAsc.length );
|
||||
|
||||
// Verify all results are in ascending order.
|
||||
// The query uses the min price calculated in the product meta lookup table,
|
||||
// so we can't just check the price property of the response.
|
||||
result1.body.forEach( ( { name }, idx ) => {
|
||||
expect( name ).toBe( productNamesMinPriceAsc[ idx ] );
|
||||
} );
|
||||
|
||||
const productNamesMaxPriceDesc = [
|
||||
'Sunglasses',
|
||||
'Belt',
|
||||
'Hoodie',
|
||||
'Logo Collection',
|
||||
'Hoodie with Logo',
|
||||
'Hoodie with Zipper',
|
||||
'Hoodie with Pocket',
|
||||
'Long Sleeve Tee',
|
||||
'V-Neck T-Shirt',
|
||||
'Polo',
|
||||
'T-Shirt',
|
||||
'Beanie',
|
||||
'T-Shirt with Logo',
|
||||
'Beanie with Logo',
|
||||
'Cap',
|
||||
'Album',
|
||||
'WordPress Pennant',
|
||||
'Single',
|
||||
'Child Product',
|
||||
'Parent Product',
|
||||
];
|
||||
|
||||
const result2 = await productsApi.listAll.products( {
|
||||
order: 'desc',
|
||||
orderby: 'price',
|
||||
per_page: productNamesMaxPriceDesc.length
|
||||
} );
|
||||
expect( result2.statusCode ).toEqual( 200 );
|
||||
expect( result2.body ).toHaveLength( productNamesMaxPriceDesc.length );
|
||||
|
||||
// Verify all results are in descending order.
|
||||
// The query uses the max price calculated in the product meta lookup table,
|
||||
// so we can't just check the price property of the response.
|
||||
result2.body.forEach( ( { name }, idx ) => {
|
||||
expect( name ).toBe( productNamesMaxPriceDesc[ idx ] );
|
||||
} );
|
||||
} );
|
||||
|
||||
// This case will remain skipped until orderby include is fixed.
|
||||
// See: https://github.com/woocommerce/woocommerce/issues/30354#issuecomment-925955099.
|
||||
it( 'include', async () => {
|
||||
const includeIds = [
|
||||
sampleData.groupedProducts[ 0 ].id,
|
||||
sampleData.simpleProducts[ 3 ].id,
|
||||
sampleData.hierarchicalProducts.parent.id,
|
||||
];
|
||||
|
||||
const result1 = await productsApi.listAll.products( {
|
||||
order: 'asc',
|
||||
orderby: 'include',
|
||||
include: includeIds.join( ',' ),
|
||||
} );
|
||||
expect( result1.statusCode ).toEqual( 200 );
|
||||
expect( result1.body ).toHaveLength( includeIds.length );
|
||||
|
||||
// Verify all results are in proper order.
|
||||
result1.body.forEach( ( { id }, idx ) => {
|
||||
expect( id ).toBe( includeIds[ idx ] );
|
||||
} );
|
||||
|
||||
const result2 = await productsApi.listAll.products( {
|
||||
order: 'desc',
|
||||
orderby: 'include',
|
||||
include: includeIds.join( ',' ),
|
||||
} );
|
||||
expect( result2.statusCode ).toEqual( 200 );
|
||||
expect( result2.body ).toHaveLength( includeIds.length );
|
||||
|
||||
// Verify all results are in proper order.
|
||||
result2.body.forEach( ( { id }, idx ) => {
|
||||
expect( id ).toBe( includeIds[ idx ] );
|
||||
} );
|
||||
} );
|
||||
|
||||
it( 'rating (desc)', async () => {
|
||||
const result2 = await productsApi.listAll.products( {
|
||||
order: 'desc',
|
||||
orderby: 'rating',
|
||||
per_page: productNamesByRatingDesc.length,
|
||||
} );
|
||||
expect( result2.statusCode ).toEqual( 200 );
|
||||
|
||||
// Verify all results are in descending order.
|
||||
result2.body.forEach( ( { name }, idx ) => {
|
||||
expect( name ).toBe( productNamesByRatingDesc[ idx ] );
|
||||
} );
|
||||
} );
|
||||
|
||||
// This case will remain skipped until ratings can be sorted ascending.
|
||||
// See: https://github.com/woocommerce/woocommerce/issues/30354#issuecomment-925955099.
|
||||
it.skip( 'rating (asc)', async () => {
|
||||
const result1 = await productsApi.listAll.products( {
|
||||
order: 'asc',
|
||||
orderby: 'rating',
|
||||
per_page: productNamesByRatingAsc.length,
|
||||
} );
|
||||
expect( result1.statusCode ).toEqual( 200 );
|
||||
|
||||
// Verify all results are in ascending order.
|
||||
result1.body.forEach( ( { name }, idx ) => {
|
||||
expect( name ).toBe( productNamesByRatingAsc[ idx ] );
|
||||
} );
|
||||
} );
|
||||
|
||||
it( 'popularity (desc)', async () => {
|
||||
const result2 = await productsApi.listAll.products( {
|
||||
order: 'desc',
|
||||
orderby: 'popularity',
|
||||
per_page: productNamesByPopularityDesc.length,
|
||||
} );
|
||||
expect( result2.statusCode ).toEqual( 200 );
|
||||
|
||||
// Verify all results are in descending order.
|
||||
result2.body.forEach( ( { name }, idx ) => {
|
||||
expect( name ).toBe( productNamesByPopularityDesc[ idx ] );
|
||||
} );
|
||||
} );
|
||||
|
||||
// This case will remain skipped until popularity can be sorted ascending.
|
||||
// See: https://github.com/woocommerce/woocommerce/issues/30354#issuecomment-925955099.
|
||||
it.skip( 'popularity (asc)', async () => {
|
||||
const result1 = await productsApi.listAll.products( {
|
||||
order: 'asc',
|
||||
orderby: 'popularity',
|
||||
per_page: productNamesByPopularityAsc.length,
|
||||
} );
|
||||
expect( result1.statusCode ).toEqual( 200 );
|
||||
|
||||
// Verify all results are in ascending order.
|
||||
result1.body.forEach( ( { name }, idx ) => {
|
||||
expect( name ).toBe( productNamesByPopularityAsc[ idx ] );
|
||||
} );
|
||||
} );
|
||||
} );
|
||||
} );
|
||||
} );
|
|
@ -0,0 +1,7 @@
|
|||
# Changelog
|
||||
|
||||
This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
---
|
||||
|
||||
[See legacy changelogs for previous versions](https://github.com/woocommerce/woocommerce/blob/<last-commit-hash-before-this-merge>/packages/js/api/CHANGELOG.md).
|
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"name": "woocommerce/api",
|
||||
"description": "WooCommerce API",
|
||||
"type": "library",
|
||||
"license": "GPL-3.0-or-later",
|
||||
"minimum-stability": "dev",
|
||||
"require-dev": {
|
||||
"automattic/jetpack-changelogger": "3.0.2"
|
||||
},
|
||||
"extra": {
|
||||
"changelogger": {
|
||||
"formatter": {
|
||||
"filename": "../../../tools/changelogger/PackageFormatter.php"
|
||||
},
|
||||
"types": {
|
||||
"fix": "Fixes an existing bug",
|
||||
"add": "Adds functionality",
|
||||
"update": "Update existing functionality",
|
||||
"dev": "Development related task",
|
||||
"tweak": "A minor adjustment to the codebase",
|
||||
"performance": "Address performance issues",
|
||||
"enhancement": "Improve existing functionality"
|
||||
},
|
||||
"changelog": "NEXT_CHANGELOG.md"
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -34,23 +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",
|
||||
"@typescript-eslint/eslint-plugin": "^5.3.0",
|
||||
"@typescript-eslint/parser": "^5.3.0",
|
||||
"eslint": "^8.1.0",
|
||||
"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"
|
||||
|
|
|
@ -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 @@
|
|||
/test-data/sample_products.csv
|
|
@ -1,8 +1,14 @@
|
|||
# Unreleased
|
||||
|
||||
## Added
|
||||
|
||||
- A `specs/data` folder to store page element data.
|
||||
- Tests to verify that different top-level menu and their associated sub-menus load successfully.
|
||||
|
||||
## Changed
|
||||
|
||||
- New coupon test deletes the coupon instead of trashing it.
|
||||
- A copy of sample_data.csv is included in the package.
|
||||
|
||||
# 0.1.6
|
||||
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
# Changelog
|
||||
|
||||
This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
---
|
||||
|
||||
[See legacy changelogs for previous versions](https://github.com/woocommerce/woocommerce/blob/<last-commit-hash-before-this-merge>/packages/js/e2e-core-tests/CHANGELOG.md).
|
|
@ -72,6 +72,7 @@ The functions to access the core tests are:
|
|||
- `runAnalyticsPageLoadsTest` - Merchant can load and see all pages in Analytics
|
||||
- `runImportProductsTest` - Merchant can import products via CSV file
|
||||
- `runInitiateWccomConnectionTest` - Merchant can initiate connection to WooCommerce.com
|
||||
- `runAdminPageLoadTests` - Merchant can load pages from the WP Admin sidebar
|
||||
|
||||
### Shopper
|
||||
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
#!/usr/bin/env bash
|
||||
#
|
||||
# Copy the WooCommerce sample data file to the package
|
||||
#
|
||||
|
||||
PACKAGEPATH=$(dirname $(dirname "$0"))
|
||||
|
||||
cp -v $PACKAGEPATH/../../../plugins/woocommerce/sample-data/sample_products.csv $PACKAGEPATH/test-data
|
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"name": "woocommerce/e2e-core-tests",
|
||||
"description": "WooCommerce end to end core tests",
|
||||
"type": "library",
|
||||
"license": "GPL-3.0-or-later",
|
||||
"minimum-stability": "dev",
|
||||
"require-dev": {
|
||||
"automattic/jetpack-changelogger": "3.0.2"
|
||||
},
|
||||
"extra": {
|
||||
"changelogger": {
|
||||
"formatter": {
|
||||
"filename": "../../../tools/changelogger/PackageFormatter.php"
|
||||
},
|
||||
"types": {
|
||||
"fix": "Fixes an existing bug",
|
||||
"add": "Adds functionality",
|
||||
"update": "Update existing functionality",
|
||||
"dev": "Development related task",
|
||||
"tweak": "A minor adjustment to the codebase",
|
||||
"performance": "Address performance issues",
|
||||
"enhancement": "Improve existing functionality"
|
||||
},
|
||||
"changelog": "NEXT_CHANGELOG.md"
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -11,6 +11,7 @@
|
|||
"main": "index.js",
|
||||
"dependencies": {
|
||||
"@jest/globals": "^26.4.2",
|
||||
"@wordpress/deprecated": "^3.2.3",
|
||||
"config": "3.3.3",
|
||||
"faker": "^5.1.0"
|
||||
},
|
||||
|
@ -20,5 +21,9 @@
|
|||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "./bin/build.sh",
|
||||
"prepare": "pnpm run build"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ const {
|
|||
describe,
|
||||
beforeAll,
|
||||
} = require( '@jest/globals' );
|
||||
import deprecated from '@wordpress/deprecated';
|
||||
|
||||
const runActivationTest = () => {
|
||||
describe('Store owner can login and make sure WooCommerce is activated', () => {
|
||||
|
@ -19,6 +20,10 @@ const runActivationTest = () => {
|
|||
});
|
||||
|
||||
it('can make sure WooCommerce is activated. If not, activate it', async () => {
|
||||
deprecated( 'runActivationTest', {
|
||||
alternative: '@woocommerce/admin-e2e-tests `testAdminBasicSetup()`',
|
||||
});
|
||||
|
||||
const slug = 'woocommerce';
|
||||
await merchant.openPlugins();
|
||||
const disableLink = await page.$(`tr[data-slug="${slug}"] .deactivate a`);
|
||||
|
|
|
@ -13,6 +13,7 @@ const {
|
|||
* External dependencies
|
||||
*/
|
||||
const config = require( 'config' );
|
||||
import deprecated from '@wordpress/deprecated';
|
||||
const {
|
||||
it,
|
||||
describe,
|
||||
|
@ -47,6 +48,9 @@ const runOnboardingFlowTest = () => {
|
|||
}
|
||||
|
||||
it('can start and complete onboarding when visiting the site for the first time.', async () => {
|
||||
deprecated( 'runOnboardingFlowTest', {
|
||||
alternative: '@woocommerce/admin-e2e-tests `testAdminOnboardingWizard()`',
|
||||
});
|
||||
await completeOnboardingWizard();
|
||||
});
|
||||
});
|
||||
|
@ -59,6 +63,9 @@ const runTaskListTest = () => {
|
|||
});
|
||||
|
||||
it('can setup shipping', async () => {
|
||||
deprecated( 'runTaskListTest', {
|
||||
alternative: '@woocommerce/admin-e2e-tests `testAdminHomescreenTasklist()`',
|
||||
});
|
||||
await page.evaluate(() => {
|
||||
document.querySelector('.woocommerce-list__item-title').scrollIntoView();
|
||||
});
|
||||
|
|
|
@ -6,6 +6,7 @@ const {
|
|||
it,
|
||||
describe,
|
||||
} = require( '@jest/globals' );
|
||||
import deprecated from '@wordpress/deprecated';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
|
@ -31,6 +32,9 @@ const runInitialStoreSettingsTest = () => {
|
|||
});
|
||||
|
||||
it('can enable tax rates and calculations', async () => {
|
||||
deprecated( 'runInitialStoreSettingsTest', {
|
||||
alternative: '@woocommerce/admin-e2e-tests `testAdminBasicSetup()`',
|
||||
});
|
||||
// Go to general settings page
|
||||
await merchant.openSettings('general');
|
||||
|
||||
|
|
|
@ -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 );
|
||||
} );
|
||||
} );
|
||||
};
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
/**
|
||||
* WP top-level menu items and their associated sub-menus
|
||||
*/
|
||||
|
||||
export const MENUS = [
|
||||
[
|
||||
'WooCommerce',
|
||||
'#adminmenu > li:nth-child(8) > a',
|
||||
[
|
||||
[
|
||||
'Home',
|
||||
'#toplevel_page_woocommerce > ul > li:nth-child(2) > a',
|
||||
'Home',
|
||||
],
|
||||
[
|
||||
'Orders',
|
||||
'#toplevel_page_woocommerce > ul > li:nth-child(3) > a',
|
||||
'Orders',
|
||||
],
|
||||
[
|
||||
'Reports',
|
||||
'#toplevel_page_woocommerce > ul > li:nth-child(6) > a',
|
||||
'Orders',
|
||||
],
|
||||
[
|
||||
'Settings',
|
||||
'#toplevel_page_woocommerce > ul > li:nth-child(7) > a',
|
||||
'General',
|
||||
],
|
||||
[
|
||||
'Status',
|
||||
'#toplevel_page_woocommerce > ul > li:nth-child(8) > a',
|
||||
'System status',
|
||||
],
|
||||
// [ 'Extensions', '#toplevel_page_woocommerce > ul > li:nth-child(9)', 'Extensions' ],
|
||||
],
|
||||
],
|
||||
[
|
||||
'Products',
|
||||
'#adminmenu > li:nth-child(9) > a',
|
||||
[
|
||||
[
|
||||
'All Products',
|
||||
'#menu-posts-product > ul > li:nth-child(2) > a',
|
||||
'Products',
|
||||
],
|
||||
[
|
||||
'Add New',
|
||||
'#menu-posts-product > ul > li:nth-child(3) > a',
|
||||
'Add New',
|
||||
],
|
||||
[
|
||||
'Categories',
|
||||
'#menu-posts-product > ul > li:nth-child(4) > a',
|
||||
'Product categories',
|
||||
],
|
||||
[
|
||||
'Product tags',
|
||||
'#menu-posts-product > ul > li:nth-child(5) > a',
|
||||
'Product tags',
|
||||
],
|
||||
[
|
||||
'Attributes',
|
||||
'#menu-posts-product > ul > li:nth-child(6) > a',
|
||||
'Attributes',
|
||||
],
|
||||
],
|
||||
],
|
||||
[
|
||||
'Marketing',
|
||||
'#adminmenu > li:nth-child(11) > a',
|
||||
[
|
||||
[
|
||||
'Overview',
|
||||
'#toplevel_page_woocommerce-marketing > ul > li:nth-child(2) > a',
|
||||
'Overview',
|
||||
],
|
||||
[
|
||||
'Coupons',
|
||||
'#toplevel_page_woocommerce-marketing > ul > li:nth-child(3) > a',
|
||||
'Coupons',
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
|
@ -37,6 +37,7 @@ const runProductSettingsTest = require( './merchant/wp-admin-settings-product.te
|
|||
const runTaxSettingsTest = require( './merchant/wp-admin-settings-tax.test' );
|
||||
const runOrderStatusFiltersTest = require( './merchant/wp-admin-order-status-filters.test' );
|
||||
const runOrderRefundTest = require( './merchant/wp-admin-order-refund.test' );
|
||||
const runOrderRefundRestockTest = require( './merchant/wp-admin-order-refund-restock.test' );
|
||||
const runOrderApplyCouponTest = require( './merchant/wp-admin-order-apply-coupon.test' );
|
||||
const runProductEditDetailsTest = require( './merchant/wp-admin-product-edit-details.test' );
|
||||
const runProductSearchTest = require( './merchant/wp-admin-product-search.test' );
|
||||
|
@ -46,6 +47,7 @@ const runOrderSearchingTest = require( './merchant/wp-admin-order-searching.test
|
|||
const runAnalyticsPageLoadsTest = require( './merchant/wp-admin-analytics-page-loads.test' );
|
||||
const runImportProductsTest = require( './merchant/wp-admin-product-import-csv.test' );
|
||||
const runInitiateWccomConnectionTest = require( './merchant/wp-admin-extensions-connect-wccom.test' );
|
||||
const runAdminPageLoadTests = require( './merchant/wp-admin-page-loads.test.js' );
|
||||
|
||||
// REST API tests
|
||||
const runExternalProductAPITest = require( './api/external-product.test' );
|
||||
|
@ -95,12 +97,14 @@ const runMerchantTests = () => {
|
|||
runTaxSettingsTest();
|
||||
runOrderStatusFiltersTest();
|
||||
runOrderRefundTest();
|
||||
runOrderRefundRestockTest();
|
||||
runOrderApplyCouponTest();
|
||||
runProductEditDetailsTest();
|
||||
runProductSearchTest();
|
||||
runMerchantOrdersCustomerPaymentPage();
|
||||
runAnalyticsPageLoadsTest();
|
||||
runInitiateWccomConnectionTest();
|
||||
runAdminPageLoadTests();
|
||||
}
|
||||
|
||||
const runApiTests = () => {
|
||||
|
@ -142,6 +146,7 @@ module.exports = {
|
|||
runOrderApiTest,
|
||||
runOrderStatusFiltersTest,
|
||||
runOrderRefundTest,
|
||||
runOrderRefundRestockTest,
|
||||
runOrderApplyCouponTest,
|
||||
runProductEditDetailsTest,
|
||||
runProductSearchTest,
|
||||
|
@ -163,4 +168,5 @@ module.exports = {
|
|||
runOrderEmailReceivingTest,
|
||||
runInitiateWccomConnectionTest,
|
||||
runTelemetryAPITest,
|
||||
runAdminPageLoadTests,
|
||||
};
|
||||
|
|
|
@ -15,6 +15,7 @@ const {
|
|||
describe,
|
||||
beforeAll,
|
||||
} = require( '@jest/globals' );
|
||||
import deprecated from '@wordpress/deprecated';
|
||||
|
||||
/**
|
||||
* Quick check for page title and no data message.
|
||||
|
@ -57,6 +58,10 @@ const runAnalyticsPageLoadsTest = () => {
|
|||
await merchant.login();
|
||||
});
|
||||
|
||||
deprecated( 'runAnalyticsPageLoadsTest', {
|
||||
alternative: '@woocommerce/admin-e2e-tests `testAdminAnalyticsPages()`',
|
||||
});
|
||||
|
||||
it.each(pages)(
|
||||
'can see %s page properly',
|
||||
async (pageTitle, element, elementText) => {
|
||||
|
|
|
@ -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([
|
||||
|
|
|
@ -0,0 +1,117 @@
|
|||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
const {
|
||||
merchant,
|
||||
createOrder,
|
||||
createSimpleProduct,
|
||||
verifyCheckboxIsSet,
|
||||
uiUnblocked,
|
||||
evalAndClick,
|
||||
clickUpdateOrder,
|
||||
} = require( '@woocommerce/e2e-utils' );
|
||||
const { waitForSelector } = require( '@woocommerce/e2e-environment' );
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @param {string} resultSelector
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
const clickAndWaitForSelector = async ( buttonSelector, resultSelector ) => {
|
||||
await evalAndClick( buttonSelector );
|
||||
await waitForSelector( page, resultSelector, {
|
||||
timeout: 5000,
|
||||
} );
|
||||
};
|
||||
|
||||
const getRefundQuantityInputSelector = ( productName ) =>
|
||||
`td.name[data-sort-value="${ productName }"] ~ td.quantity input.refund_order_item_qty`;
|
||||
|
||||
const runOrderRefundRestockTest = () => {
|
||||
describe( 'WooCommerce Orders > Refund and restock an order item', () => {
|
||||
// See: https://github.com/woocommerce/woocommerce/issues/30618
|
||||
it( 'Can update order after refunding item without automatic stock adjustment', async () => {
|
||||
const noInventoryProductId = await createSimpleProduct();
|
||||
const productToRestockId = await createSimpleProduct(
|
||||
'Product with Stock',
|
||||
'10',
|
||||
{
|
||||
trackInventory: true,
|
||||
remainingStock: 10,
|
||||
}
|
||||
);
|
||||
|
||||
const orderId = await createOrder( {
|
||||
status: 'completed',
|
||||
lineItems: [
|
||||
{
|
||||
product_id: noInventoryProductId,
|
||||
},
|
||||
{
|
||||
product_id: productToRestockId,
|
||||
quantity: 2,
|
||||
},
|
||||
],
|
||||
} );
|
||||
|
||||
await merchant.login();
|
||||
await merchant.goToOrder( orderId );
|
||||
|
||||
// Get the currency symbol for the store's selected currency
|
||||
await page.waitForSelector( '.woocommerce-Price-currencySymbol' );
|
||||
|
||||
// Verify stock reduction system note was added
|
||||
await expect( page ).toMatchElement( '.system-note', {
|
||||
text: `Stock levels reduced: Product with Stock (#${ productToRestockId }) 10→8`,
|
||||
} );
|
||||
|
||||
// Click the Refund button
|
||||
await expect( page ).toClick( 'button.refund-items' );
|
||||
|
||||
// Verify the refund section shows
|
||||
await page.waitForSelector( 'div.wc-order-refund-items', {
|
||||
visible: true,
|
||||
} );
|
||||
await verifyCheckboxIsSet( '#restock_refunded_items' );
|
||||
|
||||
// Initiate a refund
|
||||
await expect( page ).toFill(
|
||||
getRefundQuantityInputSelector( 'Product with Stock' ),
|
||||
'2'
|
||||
);
|
||||
await expect( page ).toFill( '#refund_reason', 'No longer wanted' );
|
||||
|
||||
await clickAndWaitForSelector(
|
||||
'.do-manual-refund',
|
||||
'.quantity .refunded'
|
||||
);
|
||||
await uiUnblocked();
|
||||
|
||||
// Verify restock system note was added
|
||||
await expect( page ).toMatchElement( '.system-note', {
|
||||
text: `Item #${ productToRestockId } stock increased from 8 to 10.`,
|
||||
} );
|
||||
|
||||
// Update the order.
|
||||
await clickUpdateOrder( 'Order updated.' );
|
||||
|
||||
// Verify that inventory wasn't modified.
|
||||
// For some reason using expect().not.toMatchElement() did not work for this case.
|
||||
expect(
|
||||
await page.$$eval( '.note', ( notes ) =>
|
||||
notes.every(
|
||||
( note ) =>
|
||||
! note.textContent.match(
|
||||
/Adjusted stock: Product with Stock \(10→8\)/
|
||||
)
|
||||
)
|
||||
)
|
||||
).toEqual( true );
|
||||
} );
|
||||
} );
|
||||
};
|
||||
|
||||
module.exports = runOrderRefundRestockTest;
|
|
@ -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();
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
const { merchant } = require( '@woocommerce/e2e-utils' );
|
||||
const { MENUS } = require( '../data/elements' );
|
||||
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
const { it, describe, beforeAll } = require( '@jest/globals' );
|
||||
|
||||
const runPageLoadTest = () => {
|
||||
describe.each( MENUS )(
|
||||
' %s > Opening Top Level Pages',
|
||||
( menuTitle, menuElement, subMenus ) => {
|
||||
beforeAll( async () => {
|
||||
await merchant.login();
|
||||
} );
|
||||
|
||||
afterAll( async () => {
|
||||
await merchant.logout();
|
||||
} );
|
||||
|
||||
it.each( subMenus )(
|
||||
'can see %s page properly',
|
||||
async ( subMenuTitle, subMenuElement, subMenuText ) => {
|
||||
// Go to Top Level Menu
|
||||
await Promise.all( [
|
||||
page.click( menuElement ),
|
||||
page.waitForNavigation( { waitUntil: 'networkidle0' } ),
|
||||
] );
|
||||
|
||||
// Click sub-menu item and wait for the page to finish loading
|
||||
await Promise.all( [
|
||||
page.click( subMenuElement ),
|
||||
page.waitForNavigation( { waitUntil: 'networkidle0' } ),
|
||||
] );
|
||||
|
||||
await expect( page ).toMatchElement( 'h1', {
|
||||
text: subMenuText,
|
||||
} );
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
// eslint-disable-next-line jest/no-export
|
||||
module.exports = runPageLoadTest;
|
|
@ -17,8 +17,8 @@ const { it, describe, beforeAll, afterAll } = require( '@jest/globals' );
|
|||
const path = require( 'path' );
|
||||
const coreTestsPath = getCoreTestsRoot();
|
||||
const filePath = path.resolve(
|
||||
coreTestsPath.appRoot,
|
||||
'plugins/woocommerce/sample-data/sample_products.csv'
|
||||
coreTestsPath.packageRoot,
|
||||
'test-data/sample_products.csv'
|
||||
);
|
||||
const filePathOverride = path.resolve(
|
||||
coreTestsPath.packageRoot,
|
||||
|
@ -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 () => {
|
||||
|
|
|
@ -8,12 +8,13 @@ const {
|
|||
uiUnblocked,
|
||||
evalAndClick,
|
||||
setCheckbox,
|
||||
setBrowserViewport,
|
||||
verifyAndPublish,
|
||||
waitForSelector,
|
||||
waitForSelectorWithoutThrow
|
||||
} = require( '@woocommerce/e2e-utils' );
|
||||
const {
|
||||
waitAndClick,
|
||||
waitForSelector,
|
||||
} = require( '@woocommerce/e2e-environment' );
|
||||
|
||||
/**
|
||||
|
@ -45,6 +46,13 @@ const runAddSimpleProductTest = () => {
|
|||
});
|
||||
|
||||
it('can create simple virtual product and add it to the cart', async () => {
|
||||
|
||||
// @todo: remove this once https://github.com/woocommerce/woocommerce/issues/31337 has been addressed
|
||||
await setBrowserViewport( {
|
||||
width: 970,
|
||||
height: 700,
|
||||
} );
|
||||
|
||||
await openNewProductAndVerify();
|
||||
|
||||
// Set product data and publish the product
|
||||
|
@ -72,6 +80,12 @@ const runAddSimpleProductTest = () => {
|
|||
});
|
||||
|
||||
it('can create simple non-virtual product and add it to the cart', async () => {
|
||||
// @todo: remove this once https://github.com/woocommerce/woocommerce/issues/31337 has been addressed
|
||||
await setBrowserViewport( {
|
||||
width: 960,
|
||||
height: 700,
|
||||
} );
|
||||
|
||||
await merchant.login();
|
||||
await openNewProductAndVerify();
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ const runAddShippingClassesTest = () => {
|
|||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await withRestApi.deleteAllShippingClasses();
|
||||
await withRestApi.deleteAllShippingClasses( false );
|
||||
});
|
||||
|
||||
it('can add shipping classes', async () => {
|
||||
|
|
|
@ -35,10 +35,14 @@ const runAddNewShippingZoneTest = () => {
|
|||
|
||||
beforeAll(async () => {
|
||||
productId = await createSimpleProduct();
|
||||
await withRestApi.deleteAllShippingZones();
|
||||
await withRestApi.deleteAllShippingZones( false );
|
||||
await merchant.login();
|
||||
});
|
||||
|
||||
afterAll( async () => {
|
||||
shopper.logout();
|
||||
} );
|
||||
|
||||
it('add shipping zone for San Francisco with free Local pickup', async () => {
|
||||
// Add a new shipping zone for San Francisco 94107, CA, US with Local pickup
|
||||
await addShippingZoneAndMethod(shippingZoneNameSF, california, sanFranciscoZIP, 'local_pickup');
|
||||
|
|
|
@ -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 () => {
|
||||
|
|
|
@ -25,6 +25,10 @@ const runMyAccountCreateAccountTest = () => {
|
|||
await merchant.logout();
|
||||
});
|
||||
|
||||
afterAll( async () => {
|
||||
shopper.logout();
|
||||
} );
|
||||
|
||||
it('can create a new account via my account', async () => {
|
||||
await shopper.gotoMyAccount();
|
||||
await page.waitForSelector('.woocommerce-form-register');
|
||||
|
|
|
@ -34,6 +34,10 @@ const runMyAccountPayOrderTest = () => {
|
|||
await merchant.logout();
|
||||
});
|
||||
|
||||
afterAll( async () => {
|
||||
shopper.logout();
|
||||
} );
|
||||
|
||||
it('allows customer to pay for their order in My Account', async () => {
|
||||
await shopper.login();
|
||||
await shopper.goToOrders();
|
||||
|
|
|
@ -15,6 +15,10 @@ const pages = [
|
|||
|
||||
const runMyAccountPageTest = () => {
|
||||
describe('My account page', () => {
|
||||
afterAll( async () => {
|
||||
shopper.logout();
|
||||
} );
|
||||
|
||||
it('allows customer to login', async () => {
|
||||
await shopper.login();
|
||||
expect(page).toMatch('Hello');
|
||||
|
|
|
@ -30,12 +30,16 @@ const runOrderEmailReceivingTest = () => {
|
|||
describe('Shopper Order Email Receiving', () => {
|
||||
beforeAll(async () => {
|
||||
simplePostIdValue = await createSimpleProduct();
|
||||
|
||||
|
||||
await merchant.login();
|
||||
await deleteAllEmailLogs();
|
||||
await merchant.logout();
|
||||
});
|
||||
|
||||
afterAll( async () => {
|
||||
shopper.logout();
|
||||
} );
|
||||
|
||||
it('should receive order email after purchasing an item', async () => {
|
||||
await shopper.login();
|
||||
|
||||
|
|
|
@ -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,
|
||||
};
|
|
@ -1,10 +1,15 @@
|
|||
# Unreleased
|
||||
|
||||
## Fixed
|
||||
|
||||
- Updated the browserViewport in `jest.setup.js` to match the `defaultViewport` dimensions defined in `jest-puppeteer.config.js`
|
||||
## Added
|
||||
|
||||
- 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
|
||||
- `resolveLocalE2ePath` to resolve path to local E2E file
|
||||
- `WC_E2E_FOLDER` for mapping plugin root to path within repo
|
||||
|
||||
## 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).
|
|
@ -10,6 +10,7 @@ const {
|
|||
getAppRoot,
|
||||
getAppName,
|
||||
getTestConfig,
|
||||
resolveLocalE2ePath,
|
||||
} = require( '../utils' );
|
||||
|
||||
const dockerArgs = [];
|
||||
|
@ -56,10 +57,7 @@ if ( appPath ) {
|
|||
|
||||
const appInitFile = customInitFile
|
||||
? customInitFile
|
||||
: path.resolve(
|
||||
appPath,
|
||||
'plugins/woocommerce/tests/e2e/docker/initialize.sh'
|
||||
);
|
||||
: resolveLocalE2ePath( 'docker/initialize.sh' );
|
||||
// If found, copy it into the wp-cli Docker context so
|
||||
// it gets picked up by the entrypoint script.
|
||||
if ( fs.existsSync( appInitFile ) ) {
|
||||
|
@ -84,11 +82,6 @@ if ( ! process.env.WC_E2E_FOLDER_MAPPING ) {
|
|||
'/var/www/html/wp-content/plugins/' + getAppBase();
|
||||
}
|
||||
|
||||
// Set some environment variables
|
||||
if ( ! process.env.WC_CORE_PATH ) {
|
||||
envVars.WC_CORE_PATH = 'plugins/woocommerce';
|
||||
}
|
||||
|
||||
if ( ! process.env.WORDPRESS_PORT ) {
|
||||
process.env.WORDPRESS_PORT = testConfig.port;
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ const { spawnSync } = require( 'child_process' );
|
|||
const program = require( 'commander' );
|
||||
const path = require( 'path' );
|
||||
const fs = require( 'fs' );
|
||||
const { getAppRoot } = require( '../utils' );
|
||||
const { getAppRoot, resolveLocalE2ePath } = require( '../utils' );
|
||||
const {
|
||||
WC_E2E_SCREENSHOTS,
|
||||
JEST_PUPPETEER_CONFIG,
|
||||
|
@ -21,10 +21,7 @@ const appPath = getAppRoot();
|
|||
|
||||
// clear the screenshots folder before running tests.
|
||||
if ( WC_E2E_SCREENSHOTS ) {
|
||||
const screenshotPath = path.resolve(
|
||||
appPath,
|
||||
'plugins/woocommerce/tests/e2e/screenshots'
|
||||
);
|
||||
const screenshotPath = resolveLocalE2ePath( 'screenshots' );
|
||||
if ( fs.existsSync( screenshotPath ) ) {
|
||||
fs.readdirSync( screenshotPath ).forEach( ( file, index ) => {
|
||||
const filename = path.join( screenshotPath, file );
|
||||
|
@ -36,9 +33,7 @@ if ( WC_E2E_SCREENSHOTS ) {
|
|||
const nodeConfigDirs = [ path.resolve( __dirname, '../config' ) ];
|
||||
|
||||
if ( appPath ) {
|
||||
nodeConfigDirs.unshift(
|
||||
path.resolve( appPath, 'plugins/woocommerce/tests/e2e/config' )
|
||||
);
|
||||
nodeConfigDirs.unshift( resolveLocalE2ePath( 'config' ) );
|
||||
}
|
||||
|
||||
const testEnvVars = {
|
||||
|
@ -55,10 +50,7 @@ if ( DEFAULT_TIMEOUT_OVERRIDE ) {
|
|||
if ( ! JEST_PUPPETEER_CONFIG ) {
|
||||
// Use local Puppeteer config if there is one.
|
||||
// Load test configuration file into an object.
|
||||
const localJestConfigFile = path.resolve(
|
||||
appPath,
|
||||
'tests/e2e/config/jest-puppeteer.config.js'
|
||||
);
|
||||
const localJestConfigFile = resolveLocalE2ePath( 'config/jest-puppeteer.config.js' );
|
||||
const jestConfigFile = path.resolve(
|
||||
__dirname,
|
||||
'../config/jest-puppeteer.config.js'
|
||||
|
@ -100,10 +92,7 @@ let configPath = path.resolve( __dirname, '../config/jest.config.js' );
|
|||
|
||||
// Look for a Jest config in the dependent app's path.
|
||||
if ( appPath ) {
|
||||
const appConfig = path.resolve(
|
||||
appPath,
|
||||
'plugins/woocommerce/tests/e2e/config/jest.config.js'
|
||||
);
|
||||
const appConfig = resolveLocalE2ePath( 'config/jest.config.js' );
|
||||
|
||||
if ( fs.existsSync( appConfig ) ) {
|
||||
configPath = appConfig;
|
||||
|
|
|
@ -11,7 +11,8 @@ count=0
|
|||
WP_BASE_URL=$(node utils/get-base-url.js)
|
||||
printf "Testing URL: $WP_BASE_URL\n\n"
|
||||
|
||||
while [[ "$(curl -s -o /dev/null -w ''%{http_code}'' ${WP_BASE_URL}/?pagename=ready)" != "200" ]]
|
||||
RAWDATA=$(curl -s -o /dev/null -w '%{http_code}' ${WP_BASE_URL}/?pagename=ready)
|
||||
while [[ "${RAWDATA: -3}" != "200" ]]
|
||||
|
||||
do
|
||||
echo "$(date) - Waiting for testing environment"
|
||||
|
@ -23,6 +24,7 @@ do
|
|||
echo "$(date) - Testing environment couldn't be found"
|
||||
exit 1
|
||||
fi
|
||||
RAWDATA=$(curl -s -o /dev/null -w '%{http_code}' ${WP_BASE_URL}/?pagename=ready)
|
||||
done
|
||||
|
||||
if [[ $count -gt 0 ]]; then
|
||||
|
|
|
@ -33,6 +33,18 @@ SCRIPTPATH=$(dirname "$0")
|
|||
REALPATH=$(readlink "$0")
|
||||
cd "$SCRIPTPATH/$(dirname "$REALPATH")/.."
|
||||
|
||||
# Set a flag to distinguish between the development repo and npm package
|
||||
DEV_PATH=$(echo $0 | rev | cut -f4 -d/ | rev)
|
||||
if [ "$DEV_PATH" != "node_modules" ]; then
|
||||
export WC_E2E_WOOCOMMERCE_DEV='true'
|
||||
export WC_E2E_FOLDER='plugins/woocommerce'
|
||||
else
|
||||
export WC_E2E_WOOCOMMERCE_DEV=''
|
||||
if [ -z $WC_E2E_FOLDER ]; then
|
||||
export WC_E2E_FOLDER=''
|
||||
fi
|
||||
fi
|
||||
|
||||
# Run scripts
|
||||
case $1 in
|
||||
'docker:up')
|
||||
|
|
|
@ -119,7 +119,12 @@ You can override these in `/tests/e2e/config/default.json`.
|
|||
|
||||
### Folder Mapping
|
||||
|
||||
The built in container defaults to mapping the root folder of the repository to a folder in the `plugins` folder. For example `woocommerce` is mapped to `/var/www/html/wp-content/plugins/woocommerce`. Use the `WC_E2E_FOLDER_MAPPING` environment variable to override this mapping.
|
||||
The built in container defaults to mapping the root folder of the repository to a folder in the `plugins` folder. Use the environment variables `WC_E2E_FOLDER` and `WC_E2E_FOLDER_MAPPING` to override this mapping. The `WC_E2E_FOLDER` is a path relative to the root of the project. For example, in the `woocommerce` repository this mapping is:
|
||||
|
||||
- `WC_E2E_FOLDER=plugins/woocommerce`
|
||||
- `WC_E2E_FOLDER_MAPPING=/var/www/html/wp-content/plugins/woocommerce`
|
||||
|
||||
Other repository examples:
|
||||
|
||||
- Storefront Theme - ```WC_E2E_FOLDER_MAPPING=/var/www/html/wp-content/themes/storefront npx wc-e2e docker:up```
|
||||
- Site Project - ```WC_E2E_FOLDER_MAPPING=/var/www/html/wp-content npx wc-e2e docker:up```
|
||||
|
|
|
@ -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": "Fixes an existing bug",
|
||||
"add": "Adds functionality",
|
||||
"update": "Update existing functionality",
|
||||
"dev": "Development related task",
|
||||
"tweak": "A minor adjustment to the codebase",
|
||||
"performance": "Address performance issues",
|
||||
"enhancement": "Improve existing functionality"
|
||||
},
|
||||
"changelog": "NEXT_CHANGELOG.md"
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -1,32 +1,46 @@
|
|||
/** @format */
|
||||
const { jestPuppeteerConfig } = require( '@automattic/puppeteer-utils' );
|
||||
/**
|
||||
* For a detailed explanation of configuration properties, visit:
|
||||
* https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#puppeteerlaunchoptions
|
||||
*/
|
||||
|
||||
const { CI, E2E_DEBUG, PUPPETEER_SLOWMO, E2E_EXE_PATH } = process.env;
|
||||
let executablePath = '';
|
||||
let dumpio = false;
|
||||
let puppeteerConfig;
|
||||
|
||||
|
||||
if ( ! CI && E2E_EXE_PATH !== '' ) {
|
||||
executablePath = E2E_EXE_PATH;
|
||||
}
|
||||
|
||||
if ( E2E_DEBUG ) {
|
||||
dumpio = true;
|
||||
}
|
||||
const jestPuppeteerLaunch = {
|
||||
// Required for the logged out and logged in tests so they don't share app state/token.
|
||||
browserContext: 'incognito',
|
||||
defaultViewport: {
|
||||
width: 1280,
|
||||
height: 800,
|
||||
},
|
||||
};
|
||||
|
||||
if ( 'no' == global.process.env.node_config_dev ) {
|
||||
puppeteerConfig = {
|
||||
launch: {
|
||||
// Required for the logged out and logged in tests so they don't share app state/token.
|
||||
browserContext: 'incognito',
|
||||
defaultViewport: {
|
||||
width: 1280,
|
||||
height: 800,
|
||||
},
|
||||
},
|
||||
launch: jestPuppeteerLaunch,
|
||||
};
|
||||
} else {
|
||||
puppeteerConfig = {
|
||||
launch: {
|
||||
...jestPuppeteerConfig.launch,
|
||||
slowMo: process.env.PUPPETEER_SLOWMO ? process.env.PUPPETEER_SLOWMO : 50,
|
||||
...jestPuppeteerLaunch,
|
||||
executablePath,
|
||||
dumpio,
|
||||
slowMo: PUPPETEER_SLOWMO ? PUPPETEER_SLOWMO : 50,
|
||||
headless: false,
|
||||
ignoreHTTPSErrors: true,
|
||||
args: [ '--window-size=1920,1080', '--user-agent=chrome' ],
|
||||
devtools: true,
|
||||
defaultViewport: {
|
||||
width: 1280,
|
||||
height: 800,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
/**
|
||||
* External Dependencies
|
||||
*/
|
||||
const { jestConfig } = require( '@automattic/puppeteer-utils' );
|
||||
const { WC_E2E_SCREENSHOTS } = process.env;
|
||||
const path = require( 'path' );
|
||||
const fs = require( 'fs' );
|
||||
|
@ -9,7 +8,7 @@ const fs = require( 'fs' );
|
|||
/**
|
||||
* Internal Dependencies
|
||||
*/
|
||||
const { getAppRoot } = require( '../utils' );
|
||||
const { resolveLocalE2ePath } = require( '../utils' );
|
||||
|
||||
const failureSetup = [];
|
||||
if ( WC_E2E_SCREENSHOTS ) {
|
||||
|
@ -23,19 +22,24 @@ const setupFilesAfterEnv = [
|
|||
'expect-puppeteer',
|
||||
];
|
||||
|
||||
const appPath = getAppRoot();
|
||||
const localJestSetupFile = path.resolve(
|
||||
appPath,
|
||||
'plugins/woocommerce/tests/e2e/config/jest.setup.js'
|
||||
);
|
||||
const localJestSetupFile = resolveLocalE2ePath( 'config/jest.setup.js' );
|
||||
const moduleNameMap = resolveLocalE2ePath( '$1' );
|
||||
const testSpecs = resolveLocalE2ePath( 'specs' );
|
||||
|
||||
if ( fs.existsSync( localJestSetupFile ) ) {
|
||||
setupFilesAfterEnv.push( localJestSetupFile );
|
||||
}
|
||||
|
||||
const combinedConfig = {
|
||||
...jestConfig,
|
||||
preset: 'jest-puppeteer',
|
||||
clearMocks: true,
|
||||
moduleFileExtensions: [ 'js' ],
|
||||
testMatch: [
|
||||
'**/*.(test|spec).js',
|
||||
'*.(test|spec).js'
|
||||
],
|
||||
moduleNameMapper: {
|
||||
'@woocommerce/e2e/tests/(.*)': appPath + 'tests/e2e/$1',
|
||||
'@woocommerce/e2e/tests/(.*)': moduleNameMap,
|
||||
},
|
||||
|
||||
setupFiles: [ '<rootDir>/config/env.setup.js' ],
|
||||
|
@ -49,10 +53,9 @@ const combinedConfig = {
|
|||
testTimeout: parseInt( global.process.env.jest_test_timeout ),
|
||||
|
||||
transformIgnorePatterns: [
|
||||
...jestConfig.transformIgnorePatterns,
|
||||
'node_modules/(?!(woocommerce)/)',
|
||||
],
|
||||
roots: [ appPath + 'tests/e2e/specs' ],
|
||||
roots: [ testSpecs ],
|
||||
};
|
||||
|
||||
if ( process.env.jest_test_spec ) {
|
||||
|
|
|
@ -35,7 +35,7 @@ services:
|
|||
WORDPRESS_DEBUG: 1
|
||||
volumes:
|
||||
- wordpress:/var/www/html
|
||||
- "../../../${WC_CORE_PATH}:${WC_E2E_FOLDER_MAPPING}"
|
||||
- "../../../${WC_E2E_FOLDER}:${WC_E2E_FOLDER_MAPPING}"
|
||||
|
||||
wordpress-cli:
|
||||
container_name: "${APP_NAME}_wordpress-cli"
|
||||
|
@ -60,7 +60,7 @@ services:
|
|||
|
||||
volumes:
|
||||
- wordpress:/var/www/html
|
||||
- "../../../plugins/woocommerce:${WC_E2E_FOLDER_MAPPING}"
|
||||
- "../../../${WC_E2E_FOLDER}:${WC_E2E_FOLDER_MAPPING}"
|
||||
|
||||
volumes:
|
||||
db:
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue