Merge branch 'trunk' into add/api-tests-orders-crud
This commit is contained in:
commit
3c31414c80
|
@ -17,7 +17,7 @@ jobs:
|
|||
ref: ${{ github.event.inputs.ref || github.ref }}
|
||||
- name: Build the zip file
|
||||
id: build
|
||||
uses: woocommerce/action-build@v2
|
||||
uses: woocommerce/action-build@trunk
|
||||
- name: Unzip the file (prevents double zip problem)
|
||||
run: unzip ${{ steps.build.outputs.zip_path }} -d zipfile
|
||||
- name: Upload the zip file as an artifact
|
||||
|
|
|
@ -11,7 +11,7 @@ jobs:
|
|||
uses: actions/checkout@v2
|
||||
- name: Build
|
||||
id: build
|
||||
uses: woocommerce/action-build@v2
|
||||
uses: woocommerce/action-build@trunk
|
||||
- name: Upload release asset
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
|
|
|
@ -59,8 +59,13 @@ jobs:
|
|||
./vendor
|
||||
key: ${{ runner.os }}-${{ hashFiles('./composer.lock') }}
|
||||
|
||||
- name: Install PNPM and install dependencies
|
||||
run: |
|
||||
npm install -g pnpm
|
||||
pnpm install
|
||||
|
||||
- name: Setup and install composer
|
||||
run: composer install
|
||||
run: pnpm nx composer-install woocommerce
|
||||
|
||||
- name: Add PHP8 Compatibility.
|
||||
run: |
|
||||
|
@ -71,11 +76,11 @@ jobs:
|
|||
composer bin phpunit config repositories.0 '{"type": "path", "url": "/tmp/phpunit-7.5-fork/phpunit-add-compatibility-with-php8-to-phpunit-7", "options": {"symlink": false}}'
|
||||
composer bin phpunit require --dev -W phpunit/phpunit:@dev --ignore-platform-reqs
|
||||
rm -rf ./vendor/phpunit/
|
||||
composer dump-autoload
|
||||
pnpm nx composer-dump-autoload woocommerce
|
||||
fi
|
||||
|
||||
- name: Init DB and WP
|
||||
run: ./tests/bin/install.sh woo_test root root 127.0.0.1 ${{ matrix.wp }}
|
||||
|
||||
- name: Run tests
|
||||
run: ./vendor/bin/phpunit -c ./phpunit.xml
|
||||
run: pnpm nx test-unit woocommerce
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
name: Mirrors
|
||||
on:
|
||||
push:
|
||||
branches: ['trunk', 'release/**']
|
||||
jobs:
|
||||
build:
|
||||
name: Build WooCommerce zip
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Build
|
||||
id: build
|
||||
uses: woocommerce/action-build@trunk
|
||||
env:
|
||||
BUILD_ENV: mirrors
|
||||
|
||||
- name: Upload PR zip
|
||||
uses: actions/upload-artifact@v2
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
name: woocommerce
|
||||
path: ${{ steps.build.outputs.zip_path }}
|
||||
retention-days: 7
|
||||
|
||||
mirror:
|
||||
name: Push to Mirror
|
||||
runs-on: ubuntu-latest
|
||||
needs: [build]
|
||||
steps:
|
||||
- name: Create directories
|
||||
run: |
|
||||
mkdir -p tmp/woocommerce-build
|
||||
mkdir -p monorepo
|
||||
|
||||
- name: Checkout monorepo
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
path: monorepo
|
||||
|
||||
- name: Download WooCommerce ZIP
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: woocommerce
|
||||
path: tmp/woocommerce-build
|
||||
|
||||
- name: Extract and replace WooCommerce zip.
|
||||
working-directory: tmp/woocommerce-build
|
||||
run: |
|
||||
mkdir -p woocommerce/woocommerce-production
|
||||
unzip woocommerce.zip -d woocommerce/woocommerce-production
|
||||
mv woocommerce/woocommerce-production/woocommerce/* woocommerce/woocommerce-production
|
||||
rm -rf woocommerce/woocommerce-production/woocommerce
|
||||
|
||||
- name: Set up mirror
|
||||
working-directory: tmp/woocommerce-build
|
||||
run: |
|
||||
touch mirrors.txt
|
||||
echo "woocommerce/woocommerce-production" >> mirrors.txt
|
||||
|
||||
- name: Push to mirror
|
||||
uses: Automattic/action-push-to-mirrors@v1
|
||||
with:
|
||||
source-directory: ${{ github.workspace }}/monorepo
|
||||
token: ${{ secrets.API_TOKEN_GITHUB }}
|
||||
username: matticbot
|
||||
working-directory: ${{ github.workspace }}/tmp/woocommerce-build
|
||||
timeout-minutes: 5 # 2021-01-18: Successful runs seem to take about half a minute.
|
|
@ -11,6 +11,8 @@ jobs:
|
|||
- name: Build
|
||||
id: build
|
||||
uses: woocommerce/action-build@trunk
|
||||
env:
|
||||
BUILD_ENV: e2e
|
||||
|
||||
- name: Upload PR zip
|
||||
uses: actions/upload-artifact@v2
|
||||
|
@ -41,8 +43,8 @@ jobs:
|
|||
- name: Install PNPM and install dependencies
|
||||
working-directory: package/woocommerce
|
||||
run: |
|
||||
npm install -g pnpm
|
||||
pnpm install
|
||||
npm install -g pnpm
|
||||
pnpm install
|
||||
|
||||
- name: Load docker images and start containers.
|
||||
working-directory: package/woocommerce/plugins/woocommerce
|
||||
|
@ -66,8 +68,8 @@ jobs:
|
|||
- name: Install dependencies again
|
||||
working-directory: package/woocommerce
|
||||
run: |
|
||||
npm install -g pnpm
|
||||
pnpm install
|
||||
npm install -g pnpm
|
||||
pnpm install
|
||||
|
||||
- name: Run tests command.
|
||||
working-directory: package/woocommerce/plugins/woocommerce
|
||||
|
@ -75,9 +77,60 @@ jobs:
|
|||
WC_E2E_SCREENSHOTS: 1
|
||||
E2E_SLACK_TOKEN: ${{ secrets.E2E_SLACK_TOKEN }}
|
||||
E2E_SLACK_CHANNEL: ${{ secrets.E2E_SLACK_CHANNEL }}
|
||||
run: pnpx wc-e2e test:e2e
|
||||
|
||||
api-tests-run:
|
||||
name: Runs API tests.
|
||||
runs-on: ubuntu-18.04
|
||||
needs: [build]
|
||||
steps:
|
||||
- name: Create dirs.
|
||||
run: |
|
||||
mkdir -p code/woocommerce
|
||||
mkdir -p package/woocommerce
|
||||
mkdir -p tmp/woocommerce
|
||||
mkdir -p node_modules
|
||||
|
||||
- name: Checkout code.
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
path: package/woocommerce
|
||||
|
||||
- name: Install PNPM and install dependencies
|
||||
working-directory: package/woocommerce
|
||||
run: |
|
||||
npm install -g pnpm
|
||||
pnpm install
|
||||
|
||||
- name: Load docker images and start containers.
|
||||
working-directory: package/woocommerce/plugins/woocommerce
|
||||
run: pnpx wc-e2e docker:up
|
||||
|
||||
- name: Move current directory to code. We will install zip file in this dir later.
|
||||
run: mv ./package/woocommerce/plugins/woocommerce/* ./code/woocommerce
|
||||
|
||||
- name: Download WooCommerce ZIP.
|
||||
uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: woocommerce
|
||||
path: tmp
|
||||
|
||||
- name: Extract and replace WooCommerce zip.
|
||||
working-directory: tmp
|
||||
run: |
|
||||
unzip woocommerce.zip -d woocommerce
|
||||
mv woocommerce/woocommerce/* ../package/woocommerce/plugins/woocommerce/
|
||||
|
||||
- name: Install dependencies again
|
||||
working-directory: package/woocommerce
|
||||
run: |
|
||||
npm install -g pnpm
|
||||
pnpm install
|
||||
|
||||
- name: Run tests command.
|
||||
working-directory: package/woocommerce/plugins/woocommerce
|
||||
env:
|
||||
BASE_URL: ${{ secrets.PR_E2E_TEST_URL }}
|
||||
USER_KEY: ${{ secrets.PR_E2E_TEST_ADMIN_USER }}
|
||||
USER_SECRET: ${{ secrets.PR_E2E_TEST_ADMIN_PASSWORD }}
|
||||
run: |
|
||||
pnpx wc-e2e test:e2e
|
||||
pnpx wc-api-tests test api
|
||||
run: pnpx wc-api-tests test api
|
||||
|
|
|
@ -45,15 +45,20 @@ jobs:
|
|||
./vendor
|
||||
key: ${{ runner.os }}-${{ hashFiles('./composer.lock') }}
|
||||
|
||||
- name: Install PNPM and install dependencies
|
||||
run: |
|
||||
npm install -g pnpm
|
||||
pnpm install
|
||||
|
||||
- name: Setup and install composer
|
||||
run: composer install
|
||||
run: pnpm nx composer-install woocommerce
|
||||
|
||||
- name: Init DB and WP
|
||||
run: ./tests/bin/install.sh woo_test root root 127.0.0.1 latest
|
||||
run: pnpm nx install-unit-test-db woocommerce
|
||||
|
||||
- name: Run unit tests with code coverage. Allow to fail.
|
||||
run: |
|
||||
RUN_CODE_COVERAGE=1 bash ./tests/bin/phpunit.sh
|
||||
pnpm nx test-code-coverage woocommerce
|
||||
exit 0
|
||||
|
||||
- name: Send code coverage to Codecov.
|
||||
|
|
|
@ -35,8 +35,13 @@ jobs:
|
|||
./vendor
|
||||
key: ${{ runner.os }}-${{ hashFiles('./composer.lock') }}
|
||||
|
||||
- name: Install PNPM and install dependencies
|
||||
run: |
|
||||
npm install -g pnpm
|
||||
pnpm install
|
||||
|
||||
- name: Setup and install composer
|
||||
run: composer install
|
||||
run: pnpm nx composer-install woocommerce
|
||||
|
||||
- name: Run code sniff
|
||||
continue-on-error: true
|
||||
|
|
|
@ -28,8 +28,8 @@ jobs:
|
|||
run: |
|
||||
npm install -g pnpm
|
||||
pnpm install
|
||||
composer install --no-dev
|
||||
pnpm run build:assets
|
||||
pnpm nx composer-install-no-dev" woocommerce
|
||||
pnpm nx build-assets woocommerce
|
||||
pnpm install jest
|
||||
|
||||
- name: Run smoke test.
|
||||
|
|
|
@ -56,8 +56,11 @@ jobs:
|
|||
./vendor
|
||||
key: ${{ runner.os }}-${{ hashFiles('./composer.lock') }}
|
||||
|
||||
- name: Setup and install composer
|
||||
run: composer install
|
||||
- name: Install PNPM and install dependencies
|
||||
run: |
|
||||
npm install -g pnpm
|
||||
pnpm install
|
||||
pnpm nx composer-install woocommerce
|
||||
|
||||
- name: Add PHP8 Compatibility.
|
||||
run: |
|
||||
|
@ -75,4 +78,4 @@ jobs:
|
|||
run: ./tests/bin/install.sh woo_test root root 127.0.0.1 ${{ matrix.wp }}
|
||||
|
||||
- name: Run tests
|
||||
run: ./vendor/bin/phpunit -c ./phpunit.xml
|
||||
run: pnpm nx test-unit woocommerce
|
||||
|
|
|
@ -26,8 +26,8 @@ jobs:
|
|||
run: |
|
||||
npm install -g pnpm
|
||||
pnpm install
|
||||
composer install --no-dev
|
||||
pnpm run build:assets
|
||||
pnpm nx composer-install-no-dev woocommerce
|
||||
pnpm nx build-assets woocommerce
|
||||
pnpm install jest
|
||||
|
||||
- name: Run smoke test.
|
||||
|
|
|
@ -26,8 +26,8 @@ jobs:
|
|||
run: |
|
||||
npm install -g pnpm
|
||||
pnpm install
|
||||
composer install --no-dev
|
||||
pnpm run build:assets
|
||||
pnpm nx composer-install-no-dev woocommerce
|
||||
pnpm nx build-assets woocommerce
|
||||
pnpm install jest
|
||||
|
||||
- name: Run smoke test.
|
||||
|
@ -82,7 +82,7 @@ jobs:
|
|||
working-directory: package/woocommerce/plugins/woocommerce
|
||||
env:
|
||||
LATEST_WP_VERSION_MINUS: ${{ matrix.wp }}
|
||||
run: pnpx wc-e2e docker:up
|
||||
run: pnpm nx docker-up woocommerce
|
||||
|
||||
- name: Move current directory to code. We will install zip file in this dir later.
|
||||
run: mv ./package/woocommerce/plugins/woocommerce/* ./code/woocommerce
|
||||
|
@ -103,4 +103,4 @@ jobs:
|
|||
WC_E2E_SCREENSHOTS: 1
|
||||
E2E_SLACK_TOKEN: ${{ secrets.SMOKE_TEST_SLACK_TOKEN }}
|
||||
E2E_SLACK_CHANNEL: ${{ secrets.RELEASE_TEST_SLACK_CHANNEL }}
|
||||
run: pnpx wc-e2e test:e2e
|
||||
run: pnpm nx test-e2e woocommerce
|
||||
|
|
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"
|
||||
}
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
# Changelog
|
||||
|
||||
This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
---
|
||||
|
||||
[See legacy changelogs for previous versions](https://github.com/woocommerce/woocommerce/blob/<last-commit-hash-before-this-merge>/packages/js/api-core-tests/CHANGELOG.md).
|
|
@ -31,7 +31,7 @@ cd "$SCRIPTPATH/$(dirname "$REALPATH")/.."
|
|||
# Run scripts
|
||||
case $1 in
|
||||
'test')
|
||||
jest --group=$2
|
||||
jest --group=$2 --runInBand
|
||||
TESTRESULT=$?
|
||||
;;
|
||||
'make:collection')
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"name": "woocommerce/api-core-tests",
|
||||
"description": "WooCommerce API core test",
|
||||
"type": "library",
|
||||
"license": "GPL-3.0-or-later",
|
||||
"minimum-stability": "dev",
|
||||
"require-dev": {
|
||||
"automattic/jetpack-changelogger": "3.0.2"
|
||||
},
|
||||
"extra": {
|
||||
"changelogger": {
|
||||
"formatter": {
|
||||
"filename": "../../../tools/changelogger/PackageFormatter.php"
|
||||
},
|
||||
"types": [
|
||||
"Fix",
|
||||
"Add",
|
||||
"Update",
|
||||
"Dev",
|
||||
"Tweak",
|
||||
"Performance",
|
||||
"Enhancement"
|
||||
],
|
||||
"changelog": "NEXT_CHANGELOG.md"
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,292 @@
|
|||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
const {
|
||||
postRequest,
|
||||
deleteRequest,
|
||||
getRequest,
|
||||
putRequest,
|
||||
} = require( '../utils/request' );
|
||||
const productsTestSetup = require( './product-list' );
|
||||
const { ordersApi } = require( '../endpoints/orders' );
|
||||
|
||||
const createCustomer = ( data ) => postRequest( 'customers', data );
|
||||
const deleteCustomer = ( id ) => deleteRequest( `customers/${ id }`, true );
|
||||
|
||||
const createSampleData = async () => {
|
||||
const testProductData = await productsTestSetup.createSampleData();
|
||||
|
||||
const orderedProducts = {
|
||||
pocketHoodie: testProductData.simpleProducts.find(
|
||||
( p ) => p.name === 'Hoodie with Pocket'
|
||||
),
|
||||
sunglasses: testProductData.simpleProducts.find(
|
||||
( p ) => p.name === 'Sunglasses'
|
||||
),
|
||||
beanie: testProductData.simpleProducts.find(
|
||||
( p ) => p.name === 'Beanie'
|
||||
),
|
||||
blueVneck: testProductData.variableProducts.vneckVariations.find(
|
||||
( p ) => p.sku === 'woo-vneck-tee-blue'
|
||||
),
|
||||
pennant: testProductData.externalProducts[ 0 ],
|
||||
};
|
||||
|
||||
const johnAddress = {
|
||||
first_name: 'John',
|
||||
last_name: 'Doe',
|
||||
company: 'Automattic',
|
||||
country: 'US',
|
||||
address_1: '60 29th Street',
|
||||
address_2: '#343',
|
||||
city: 'San Francisco',
|
||||
state: 'CA',
|
||||
postcode: '94110',
|
||||
phone: '123456789',
|
||||
};
|
||||
const tinaAddress = {
|
||||
first_name: 'Tina',
|
||||
last_name: 'Clark',
|
||||
company: 'Automattic',
|
||||
country: 'US',
|
||||
address_1: 'Oxford Ave',
|
||||
address_2: '',
|
||||
city: 'Buffalo',
|
||||
state: 'NY',
|
||||
postcode: '14201',
|
||||
phone: '123456789',
|
||||
};
|
||||
const guestShippingAddress = {
|
||||
first_name: 'Ano',
|
||||
last_name: 'Nymous',
|
||||
company: '',
|
||||
country: 'US',
|
||||
address_1: '0 Incognito St',
|
||||
address_2: '',
|
||||
city: 'Erie',
|
||||
state: 'PA',
|
||||
postcode: '16515',
|
||||
phone: '123456789',
|
||||
};
|
||||
const guestBillingAddress = {
|
||||
first_name: 'Ben',
|
||||
last_name: 'Efactor',
|
||||
company: '',
|
||||
country: 'US',
|
||||
address_1: '200 W University Avenue',
|
||||
address_2: '',
|
||||
city: 'Gainesville',
|
||||
state: 'FL',
|
||||
postcode: '32601',
|
||||
phone: '123456789',
|
||||
email: 'ben.efactor@email.net',
|
||||
};
|
||||
|
||||
const { body: john } = await createCustomer( {
|
||||
first_name: 'John',
|
||||
last_name: 'Doe',
|
||||
username: 'john.doe',
|
||||
email: 'john.doe@example.com',
|
||||
billing: {
|
||||
...johnAddress,
|
||||
email: 'john.doe@example.com',
|
||||
},
|
||||
shipping: johnAddress,
|
||||
} );
|
||||
|
||||
const { body: tina } = await createCustomer( {
|
||||
first_name: 'Tina',
|
||||
last_name: 'Clark',
|
||||
username: 'tina.clark',
|
||||
email: 'tina.clark@example.com',
|
||||
billing: {
|
||||
...tinaAddress,
|
||||
email: 'tina.clark@example.com',
|
||||
},
|
||||
shipping: tinaAddress,
|
||||
} );
|
||||
|
||||
const orderBaseData = {
|
||||
payment_method: 'cod',
|
||||
payment_method_title: 'Cash on Delivery',
|
||||
status: 'processing',
|
||||
set_paid: false,
|
||||
currency: 'USD',
|
||||
customer_id: 0,
|
||||
};
|
||||
|
||||
const orders = [];
|
||||
|
||||
// Have "John" order all products.
|
||||
Object.values( orderedProducts ).forEach( async ( product ) => {
|
||||
const { body: order } = await ordersApi.create.order( {
|
||||
...orderBaseData,
|
||||
customer_id: john.id,
|
||||
billing: {
|
||||
...johnAddress,
|
||||
email: 'john.doe@example.com',
|
||||
},
|
||||
shipping: johnAddress,
|
||||
line_items: [
|
||||
{
|
||||
product_id: product.id,
|
||||
quantity: 1,
|
||||
},
|
||||
],
|
||||
} );
|
||||
|
||||
orders.push( order );
|
||||
} );
|
||||
|
||||
// Have "Tina" order some sunglasses and make a child order.
|
||||
// This somewhat resembles a subscription renewal, but we're just testing the `parent` field.
|
||||
const { body: order2 } = await ordersApi.create.order( {
|
||||
...orderBaseData,
|
||||
status: 'completed',
|
||||
set_paid: true,
|
||||
customer_id: tina.id,
|
||||
billing: {
|
||||
...tinaAddress,
|
||||
email: 'tina.clark@example.com',
|
||||
},
|
||||
shipping: tinaAddress,
|
||||
line_items: [
|
||||
{
|
||||
product_id: orderedProducts.sunglasses.id,
|
||||
quantity: 1,
|
||||
},
|
||||
],
|
||||
} );
|
||||
orders.push( order2 );
|
||||
|
||||
const { body: order3 } = await ordersApi.create.order( {
|
||||
...orderBaseData,
|
||||
parent_id: order2.id,
|
||||
customer_id: tina.id,
|
||||
billing: {
|
||||
...tinaAddress,
|
||||
email: 'tina.clark@example.com',
|
||||
},
|
||||
shipping: tinaAddress,
|
||||
line_items: [
|
||||
{
|
||||
product_id: orderedProducts.sunglasses.id,
|
||||
quantity: 1,
|
||||
},
|
||||
],
|
||||
} );
|
||||
orders.push( order3 );
|
||||
|
||||
// Guest order.
|
||||
const { body: guestOrder } = await ordersApi.create.order( {
|
||||
...orderBaseData,
|
||||
billing: guestBillingAddress,
|
||||
shipping: guestShippingAddress,
|
||||
line_items: [
|
||||
{
|
||||
product_id: orderedProducts.pennant.id,
|
||||
quantity: 2,
|
||||
},
|
||||
{
|
||||
product_id: orderedProducts.beanie.id,
|
||||
quantity: 1,
|
||||
},
|
||||
],
|
||||
} );
|
||||
|
||||
// Create an order with all possible numerical fields (taxes, fees, refunds, etc).
|
||||
const { body: taxSetting } = await getRequest(
|
||||
'settings/general/woocommerce_calc_taxes'
|
||||
);
|
||||
await putRequest( 'settings/general/woocommerce_calc_taxes', {
|
||||
value: 'yes',
|
||||
} );
|
||||
|
||||
const { body: taxRate } = await postRequest( 'taxes', {
|
||||
country: '*',
|
||||
state: '*',
|
||||
postcode: '*',
|
||||
city: '*',
|
||||
rate: '5.5000',
|
||||
name: 'Tax',
|
||||
rate: '5.5',
|
||||
shipping: true,
|
||||
} );
|
||||
|
||||
const { body: coupon } = await postRequest( 'coupons', {
|
||||
code: 'save5',
|
||||
amount: '5',
|
||||
} );
|
||||
|
||||
const { body: order4 } = await ordersApi.create.order( {
|
||||
...orderBaseData,
|
||||
line_items: [
|
||||
{
|
||||
product_id: orderedProducts.blueVneck.id,
|
||||
quantity: 1,
|
||||
},
|
||||
],
|
||||
coupon_lines: [ { code: 'save5' } ],
|
||||
shipping_lines: [
|
||||
{
|
||||
method_id: 'flat_rate',
|
||||
total: '5.00',
|
||||
},
|
||||
],
|
||||
fee_lines: [
|
||||
{
|
||||
total: '1.00',
|
||||
name: 'Test Fee',
|
||||
},
|
||||
],
|
||||
} );
|
||||
|
||||
await postRequest( `orders/${ order4.id }/refunds`, {
|
||||
api_refund: false, // Prevent an actual refund request (fails with CoD),
|
||||
line_items: [
|
||||
{
|
||||
id: order4.line_items[ 0 ].id,
|
||||
quantity: 1,
|
||||
refund_total: order4.line_items[ 0 ].total,
|
||||
refund_tax: [
|
||||
{
|
||||
id: order4.line_items[ 0 ].taxes[ 0 ].id,
|
||||
refund_total: order4.line_items[ 0 ].total_tax,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
} );
|
||||
orders.push( order4 );
|
||||
|
||||
return {
|
||||
customers: { john, tina },
|
||||
orders,
|
||||
precisionOrder: order4,
|
||||
hierarchicalOrders: {
|
||||
parent: order2,
|
||||
child: order3,
|
||||
},
|
||||
guestOrder,
|
||||
testProductData,
|
||||
};
|
||||
};
|
||||
|
||||
const deleteSampleData = async ( sampleData ) => {
|
||||
await productsTestSetup.deleteSampleData( sampleData.testProductData );
|
||||
|
||||
sampleData.orders
|
||||
.concat( [ sampleData.guestOrder ] )
|
||||
.forEach( async ( { id } ) => {
|
||||
await ordersApi.delete.order( id, true );
|
||||
} );
|
||||
|
||||
Object.values( sampleData.customers ).forEach( async ( { id } ) => {
|
||||
await deleteCustomer( id );
|
||||
} );
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
createSampleData,
|
||||
deleteSampleData,
|
||||
};
|
File diff suppressed because it is too large
Load Diff
|
@ -1,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,
|
||||
|
|
|
@ -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",
|
||||
"Add",
|
||||
"Update",
|
||||
"Dev",
|
||||
"Tweak",
|
||||
"Performance",
|
||||
"Enhancement"
|
||||
],
|
||||
"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,7 @@
|
|||
# Changelog
|
||||
|
||||
This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
---
|
||||
|
||||
[See legacy changelogs for previous versions](https://github.com/woocommerce/woocommerce/blob/<last-commit-hash-before-this-merge>/packages/js/e2e-core-tests/CHANGELOG.md).
|
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"name": "woocommerce/e2e-core-tests",
|
||||
"description": "WooCommerce end to end core tests",
|
||||
"type": "library",
|
||||
"license": "GPL-3.0-or-later",
|
||||
"minimum-stability": "dev",
|
||||
"require-dev": {
|
||||
"automattic/jetpack-changelogger": "3.0.2"
|
||||
},
|
||||
"extra": {
|
||||
"changelogger": {
|
||||
"formatter": {
|
||||
"filename": "../../../tools/changelogger/PackageFormatter.php"
|
||||
},
|
||||
"types": [
|
||||
"Fix",
|
||||
"Add",
|
||||
"Update",
|
||||
"Dev",
|
||||
"Tweak",
|
||||
"Performance",
|
||||
"Enhancement"
|
||||
],
|
||||
"changelog": "NEXT_CHANGELOG.md"
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -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 );
|
||||
} );
|
||||
} );
|
||||
};
|
||||
|
|
|
@ -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' );
|
||||
|
@ -95,6 +96,7 @@ const runMerchantTests = () => {
|
|||
runTaxSettingsTest();
|
||||
runOrderStatusFiltersTest();
|
||||
runOrderRefundTest();
|
||||
runOrderRefundRestockTest();
|
||||
runOrderApplyCouponTest();
|
||||
runProductEditDetailsTest();
|
||||
runProductSearchTest();
|
||||
|
@ -142,6 +144,7 @@ module.exports = {
|
|||
runOrderApiTest,
|
||||
runOrderStatusFiltersTest,
|
||||
runOrderRefundTest,
|
||||
runOrderRefundRestockTest,
|
||||
runOrderApplyCouponTest,
|
||||
runProductEditDetailsTest,
|
||||
runProductSearchTest,
|
||||
|
|
|
@ -1,83 +0,0 @@
|
|||
/* eslint-disable jest/no-export, jest/no-disabled-tests */
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
const {
|
||||
merchant,
|
||||
completeOnboardingWizard,
|
||||
withRestApi,
|
||||
addShippingZoneAndMethod,
|
||||
IS_RETEST_MODE,
|
||||
} = require( '@woocommerce/e2e-utils' );
|
||||
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
const config = require( 'config' );
|
||||
const {
|
||||
it,
|
||||
describe,
|
||||
} = require( '@jest/globals' );
|
||||
|
||||
const shippingZoneNameUS = config.get( 'addresses.customer.shipping.country' );
|
||||
|
||||
const runOnboardingFlowTest = () => {
|
||||
describe('Store owner can go through store Onboarding', () => {
|
||||
if ( IS_RETEST_MODE ) {
|
||||
it('can reset onboarding to default settings', async () => {
|
||||
await withRestApi.resetOnboarding();
|
||||
});
|
||||
|
||||
it('can reset shipping zones to default settings', async () => {
|
||||
await withRestApi.deleteAllShippingZones();
|
||||
});
|
||||
|
||||
it('can reset to default settings', async () => {
|
||||
await withRestApi.resetSettingsGroupToDefault('general');
|
||||
await withRestApi.resetSettingsGroupToDefault('products');
|
||||
await withRestApi.resetSettingsGroupToDefault('tax');
|
||||
});
|
||||
}
|
||||
|
||||
it('can start and complete onboarding when visiting the site for the first time.', async () => {
|
||||
await completeOnboardingWizard();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const runTaskListTest = () => {
|
||||
describe('Store owner can go through setup Task List', () => {
|
||||
it('can setup shipping', async () => {
|
||||
await page.evaluate(() => {
|
||||
document.querySelector('.woocommerce-list__item-title').scrollIntoView();
|
||||
});
|
||||
// Query for all tasks on the list
|
||||
const taskListItems = await page.$$('.woocommerce-list__item-title');
|
||||
expect(taskListItems.length).toBeInRange( 5, 6 );
|
||||
|
||||
// Work around for https://github.com/woocommerce/woocommerce-admin/issues/6761
|
||||
if ( taskListItems.length == 6 ) {
|
||||
// Click on "Set up shipping" task to move to the next step
|
||||
const [ setupTaskListItem ] = await page.$x( '//div[contains(text(),"Set up shipping")]' );
|
||||
await setupTaskListItem.click();
|
||||
|
||||
// Wait for "Proceed" button to become active
|
||||
await page.waitForSelector('button.is-primary:not(:disabled)');
|
||||
await page.waitFor(3000);
|
||||
|
||||
// Click on "Proceed" button to save shipping settings
|
||||
await page.click('button.is-primary');
|
||||
await page.waitFor(3000);
|
||||
} else {
|
||||
await merchant.openNewShipping();
|
||||
await addShippingZoneAndMethod(shippingZoneNameUS);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
runOnboardingFlowTest,
|
||||
runTaskListTest,
|
||||
};
|
|
@ -5,9 +5,8 @@ const {
|
|||
merchant,
|
||||
clickTab,
|
||||
AdminEdit,
|
||||
factories,
|
||||
withRestApi,
|
||||
} = require( '@woocommerce/e2e-utils' );
|
||||
const { Coupon } = require( '@woocommerce/api' );
|
||||
|
||||
/**
|
||||
* External dependencies
|
||||
|
@ -50,8 +49,7 @@ const runCreateCouponTest = () => {
|
|||
// Delete the coupon
|
||||
const couponId = await adminEdit.getId();
|
||||
if ( couponId ) {
|
||||
const repository = Coupon.restRepository( factories.api.withDefaultPermalinks );
|
||||
await repository.delete( couponId );
|
||||
await withRestApi.deleteCoupon( couponId );
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
@ -21,7 +21,13 @@ const runInitiateWccomConnectionTest = () => {
|
|||
});
|
||||
|
||||
it.skip('can initiate WCCOM connection', async () => {
|
||||
await merchant.openHelper();
|
||||
await merchant.openExtensions();
|
||||
|
||||
// Click on a tab to choose WooCommerce Subscriptions extension
|
||||
await Promise.all( [
|
||||
expect( page ).toClick( 'a.nav-tab', { text: "WooCommerce.com Subscriptions" } ),
|
||||
page.waitForNavigation( { waitUntil: 'networkidle0' } ),
|
||||
] );
|
||||
|
||||
// Click on Connect button to initiate a WCCOM connection
|
||||
await Promise.all([
|
||||
|
|
|
@ -4,9 +4,10 @@
|
|||
const {
|
||||
merchant,
|
||||
uiUnblocked,
|
||||
withRestApi,
|
||||
AdminEdit,
|
||||
} = require('@woocommerce/e2e-utils');
|
||||
const config = require('config');
|
||||
} = require( '@woocommerce/e2e-utils' );
|
||||
const config = require( 'config' );
|
||||
const {
|
||||
HTTPClientFactory,
|
||||
VariableProduct,
|
||||
|
@ -14,22 +15,34 @@ const {
|
|||
SimpleProduct,
|
||||
ProductVariation,
|
||||
ExternalProduct
|
||||
} = require('@woocommerce/api');
|
||||
} = require( '@woocommerce/api' );
|
||||
|
||||
const taxClasses = [
|
||||
{
|
||||
name: 'Tax Class Simple',
|
||||
},
|
||||
{
|
||||
name: 'Tax Class Variable',
|
||||
},
|
||||
{
|
||||
name: 'Tax Class External',
|
||||
},
|
||||
];
|
||||
|
||||
const taxRates = [
|
||||
{
|
||||
name: 'Tax Rate Simple',
|
||||
rate: '10',
|
||||
rate: '10.0000',
|
||||
class: 'tax-class-simple'
|
||||
},
|
||||
{
|
||||
name: 'Tax Rate Variable',
|
||||
rate: '20',
|
||||
rate: '20.0000',
|
||||
class: 'tax-class-variable'
|
||||
},
|
||||
{
|
||||
name: 'Tax Rate External',
|
||||
rate: '30',
|
||||
rate: '30.0000',
|
||||
class: 'tax-class-external'
|
||||
}
|
||||
];
|
||||
|
@ -43,60 +56,22 @@ const initProducts = async () => {
|
|||
const httpClient = HTTPClientFactory.build(apiUrl)
|
||||
.withBasicAuth(adminUsername, adminPassword)
|
||||
.create();
|
||||
const taxClassesPath = '/wc/v3/taxes/classes';
|
||||
const taxClasses = [
|
||||
{
|
||||
name: 'Tax Class Simple',
|
||||
slug: 'tax-class-simple-698962'
|
||||
},
|
||||
{
|
||||
name: 'Tax Class Variable',
|
||||
slug: 'tax-class-variable-790238'
|
||||
},
|
||||
{
|
||||
name: 'Tax Class External',
|
||||
slug: 'tax-class-external-991321'
|
||||
}
|
||||
];
|
||||
|
||||
// Enable taxes in settings
|
||||
const enableTaxes = async () => {
|
||||
const path = '/wc/v3/settings/general/woocommerce_calc_taxes';
|
||||
const data = {
|
||||
value: 'yes'
|
||||
};
|
||||
await httpClient.put(path, data);
|
||||
};
|
||||
await enableTaxes();
|
||||
|
||||
// Initialize tax classes
|
||||
const initTaxClasses = async () => {
|
||||
for (const classToBeAdded of taxClasses) {
|
||||
await httpClient.post(taxClassesPath, classToBeAdded);
|
||||
}
|
||||
};
|
||||
await initTaxClasses();
|
||||
|
||||
// Initialize tax rates
|
||||
const initTaxRates = async () => {
|
||||
const path = '/wc/v3/taxes';
|
||||
|
||||
for (const rateToBeAdded of taxRates) {
|
||||
await httpClient.post(path, rateToBeAdded);
|
||||
}
|
||||
};
|
||||
await initTaxRates();
|
||||
await withRestApi.updateSettingOption( 'general', 'woocommerce_calc_taxes', { value: 'yes' } );
|
||||
await withRestApi.addTaxClasses( taxClasses );
|
||||
await withRestApi.addTaxRates( taxRates );
|
||||
|
||||
// Initialization functions per product type
|
||||
const initSimpleProduct = async () => {
|
||||
const repo = SimpleProduct.restRepository(httpClient);
|
||||
const repo = SimpleProduct.restRepository( httpClient );
|
||||
const simpleProduct = {
|
||||
name: 'Simple Product 273722',
|
||||
regularPrice: '100',
|
||||
taxClass: 'Tax Class Simple'
|
||||
};
|
||||
return await repo.create(simpleProduct);
|
||||
return await repo.create( simpleProduct );
|
||||
};
|
||||
|
||||
const initVariableProduct = async () => {
|
||||
const variations = [
|
||||
{
|
||||
|
@ -264,12 +239,12 @@ const runCreateOrderTest = () => {
|
|||
}
|
||||
|
||||
// Verify that the names of each tax class were shown
|
||||
for (const { name } of taxRates) {
|
||||
for (const taxRate of taxRates) {
|
||||
await expect(page).toMatchElement('th.line_tax', {
|
||||
text: name
|
||||
text: taxRate.name
|
||||
});
|
||||
await expect(page).toMatchElement('.wc-order-totals td.label', {
|
||||
text: name
|
||||
text: taxRate.name
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
|
@ -9,32 +9,12 @@ const {
|
|||
uiUnblocked,
|
||||
evalAndClick,
|
||||
createOrder,
|
||||
clickAndWaitForSelector,
|
||||
} = require( '@woocommerce/e2e-utils' );
|
||||
|
||||
const { waitForSelector } = require( '@woocommerce/e2e-environment' );
|
||||
|
||||
const config = require( 'config' );
|
||||
const simpleProductPrice = config.has('products.simple.price') ? config.get('products.simple.price') : '9.99';
|
||||
|
||||
/**
|
||||
* Evaluate and click a button selector then wait for a result selector.
|
||||
* This is a work around for what appears to be intermittent delays in handling confirm dialogs.
|
||||
*
|
||||
* @param buttonSelector
|
||||
* @param resultSelector
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
const clickAndWaitForSelector = async ( buttonSelector, resultSelector ) => {
|
||||
await evalAndClick( buttonSelector );
|
||||
await waitForSelector(
|
||||
page,
|
||||
resultSelector,
|
||||
{
|
||||
timeout: 5000
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
const runRefundOrderTest = () => {
|
||||
describe('WooCommerce Orders > Refund an order', () => {
|
||||
let productId;
|
||||
|
|
|
@ -51,7 +51,7 @@ const updateCustomerBilling = async () => {
|
|||
search: 'Jane',
|
||||
role: 'all',
|
||||
} );
|
||||
if ( ! customers.data | ! customers.data.length ) {
|
||||
if ( ! customers.data || ! customers.data.length ) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -45,7 +45,7 @@ const runOrderStatusFiltersTest = () => {
|
|||
});
|
||||
|
||||
// Create the orders using the API
|
||||
await withRestApi.batchCreateOrders(orders);
|
||||
await withRestApi.batchCreateOrders( orders, false );
|
||||
|
||||
// Next, let's login and navigate to the Orders page
|
||||
await merchant.login();
|
||||
|
|
|
@ -107,9 +107,9 @@ const runImportProductsTest = () => {
|
|||
afterAll(async () => {
|
||||
// Delete imported products
|
||||
await withRestApi.deleteAllProducts();
|
||||
await withRestApi.deleteAllProductAttributes();
|
||||
await withRestApi.deleteAllProductCategories();
|
||||
await withRestApi.deleteAllProductTags();
|
||||
await withRestApi.deleteAllProductAttributes( false );
|
||||
await withRestApi.deleteAllProductCategories( false );
|
||||
await withRestApi.deleteAllProductTags( false );
|
||||
});
|
||||
|
||||
it( 'should show error message if you go without providing CSV file', async () => {
|
||||
|
|
|
@ -14,7 +14,7 @@ const runAddShippingClassesTest = () => {
|
|||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await withRestApi.deleteAllShippingClasses();
|
||||
await withRestApi.deleteAllShippingClasses( false );
|
||||
});
|
||||
|
||||
it('can add shipping classes', async () => {
|
||||
|
|
|
@ -35,7 +35,7 @@ const runAddNewShippingZoneTest = () => {
|
|||
|
||||
beforeAll(async () => {
|
||||
productId = await createSimpleProduct();
|
||||
await withRestApi.deleteAllShippingZones();
|
||||
await withRestApi.deleteAllShippingZones( false );
|
||||
await merchant.login();
|
||||
});
|
||||
|
||||
|
|
|
@ -42,19 +42,19 @@ const runCartCalculateShippingTest = () => {
|
|||
firstProductId = await createSimpleProduct(firstProductName);
|
||||
secondProductId = await createSimpleProduct(secondProductName, secondProductPrice);
|
||||
|
||||
await withRestApi.resetSettingsGroupToDefault( 'general' );
|
||||
await withRestApi.resetSettingsGroupToDefault( 'general', false );
|
||||
|
||||
// Add a new shipping zone Germany with Free shipping
|
||||
await withRestApi.addShippingZoneAndMethod(shippingZoneNameDE, shippingCountryDE, ' ', 'free_shipping');
|
||||
await withRestApi.addShippingZoneAndMethod(shippingZoneNameDE, shippingCountryDE, ' ', 'free_shipping', '', [], false );
|
||||
|
||||
// Add a new shipping zone for France with Flat rate & Local pickup
|
||||
await withRestApi.addShippingZoneAndMethod(shippingZoneNameFR, shippingCountryFR, ' ', 'flat_rate', '5', ['local_pickup']);
|
||||
await withRestApi.addShippingZoneAndMethod(shippingZoneNameFR, shippingCountryFR, ' ', 'flat_rate', '5', ['local_pickup'], false );
|
||||
|
||||
await shopper.emptyCart();
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await withRestApi.deleteAllShippingZones();
|
||||
await withRestApi.deleteAllShippingZones( false );
|
||||
});
|
||||
|
||||
it('allows customer to calculate Free Shipping if in Germany', async () => {
|
||||
|
|
|
@ -9,6 +9,7 @@ const {
|
|||
applyCoupon,
|
||||
removeCoupon,
|
||||
} = require( '@woocommerce/e2e-utils' );
|
||||
const { getCouponId, getCouponsTable } = require( '../utils/coupons' );
|
||||
|
||||
/**
|
||||
* External dependencies
|
||||
|
@ -19,36 +20,12 @@ const {
|
|||
beforeAll,
|
||||
} = require( '@jest/globals' );
|
||||
|
||||
const couponsTable = [
|
||||
['fixed cart', { text: '$5.00' }, { text: '$4.99' }],
|
||||
['percentage', { text: '$4.99' }, { text: '$5.00' }],
|
||||
['fixed product', { text: '$5.00' }, { text: '$4.99' }]
|
||||
];
|
||||
|
||||
let couponFixedCart;
|
||||
let couponPercentage;
|
||||
let couponFixedProduct;
|
||||
|
||||
const getCoupon = (couponType) => {
|
||||
switch (couponType) {
|
||||
case 'fixed cart':
|
||||
return couponFixedCart;
|
||||
case 'percentage':
|
||||
return couponPercentage;
|
||||
case 'fixed product':
|
||||
return couponFixedProduct;
|
||||
}
|
||||
};
|
||||
|
||||
const runCartApplyCouponsTest = () => {
|
||||
describe('Cart applying coupons', () => {
|
||||
let productId;
|
||||
|
||||
beforeAll(async () => {
|
||||
productId = await createSimpleProduct();
|
||||
couponFixedCart = await createCoupon();
|
||||
couponPercentage = await createCoupon('50', 'Percentage discount');
|
||||
couponFixedProduct = await createCoupon('5', 'Fixed product discount');
|
||||
await shopper.emptyCart();
|
||||
await shopper.goToShop();
|
||||
await shopper.addToCartFromShopPage( productId );
|
||||
|
@ -56,8 +33,8 @@ const runCartApplyCouponsTest = () => {
|
|||
await shopper.goToCart();
|
||||
});
|
||||
|
||||
it.each(couponsTable)('allows cart to apply %s coupon', async (couponType, cartDiscount, orderTotal) => {
|
||||
const coupon = getCoupon(couponType);
|
||||
it.each( getCouponsTable() )( 'allows cart to apply %s coupon', async ( couponType, cartDiscount, orderTotal ) => {
|
||||
const coupon = await getCouponId( couponType );
|
||||
await applyCoupon(coupon);
|
||||
await expect(page).toMatchElement('.woocommerce-message', { text: 'Coupon code applied successfully.' });
|
||||
|
||||
|
@ -69,9 +46,10 @@ const runCartApplyCouponsTest = () => {
|
|||
});
|
||||
|
||||
it('prevents cart applying same coupon twice', async () => {
|
||||
await applyCoupon(couponFixedCart);
|
||||
const couponId = await getCouponId( 'fixed cart' );
|
||||
await applyCoupon( couponId );
|
||||
await expect(page).toMatchElement('.woocommerce-message', {text: 'Coupon code applied successfully.'});
|
||||
await applyCoupon(couponFixedCart);
|
||||
await applyCoupon( couponId );
|
||||
// Verify only one discount applied
|
||||
// This is a work around for Puppeteer inconsistently finding 'Coupon code already applied'
|
||||
await expect(page).toMatchElement('.cart-discount .amount', {text: '$5.00'});
|
||||
|
@ -79,7 +57,7 @@ const runCartApplyCouponsTest = () => {
|
|||
});
|
||||
|
||||
it('allows cart to apply multiple coupons', async () => {
|
||||
await applyCoupon(couponFixedProduct);
|
||||
await applyCoupon( await getCouponId( 'fixed product' ) );
|
||||
await expect(page).toMatchElement('.woocommerce-message', {text: 'Coupon code applied successfully.'});
|
||||
|
||||
// Verify discount applied and order total
|
||||
|
@ -88,8 +66,8 @@ const runCartApplyCouponsTest = () => {
|
|||
});
|
||||
|
||||
it('restores cart total when coupons are removed', async () => {
|
||||
await removeCoupon(couponFixedCart);
|
||||
await removeCoupon(couponFixedProduct);
|
||||
await removeCoupon( await getCouponId( 'fixed cart' ) );
|
||||
await removeCoupon( await getCouponId( 'fixed product' ) );
|
||||
await expect(page).toMatchElement('.order-total .amount', {text: '$9.99'});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -28,9 +28,9 @@ const runCartPageTest = () => {
|
|||
|
||||
beforeAll(async () => {
|
||||
productId = await createSimpleProduct();
|
||||
await withRestApi.resetSettingsGroupToDefault('general');
|
||||
await withRestApi.resetSettingsGroupToDefault('products');
|
||||
await withRestApi.resetSettingsGroupToDefault('tax');
|
||||
await withRestApi.resetSettingsGroupToDefault( 'general', false );
|
||||
await withRestApi.resetSettingsGroupToDefault( 'products', false );
|
||||
await withRestApi.resetSettingsGroupToDefault( 'tax', false );
|
||||
});
|
||||
|
||||
it('should display no item in the cart', async () => {
|
||||
|
|
|
@ -10,6 +10,7 @@ const {
|
|||
removeCoupon,
|
||||
waitForSelectorWithoutThrow,
|
||||
} = require( '@woocommerce/e2e-utils' );
|
||||
const { getCouponId, getCouponsTable } = require( '../utils/coupons' );
|
||||
|
||||
/**
|
||||
* External dependencies
|
||||
|
@ -20,26 +21,6 @@ const {
|
|||
beforeAll,
|
||||
} = require( '@jest/globals' );
|
||||
|
||||
const couponsTable = [
|
||||
['fixed cart', { text: '$5.00' }, { text: '$4.99' }],
|
||||
['percentage', { text: '$4.99' }, { text: '$5.00' }],
|
||||
['fixed product', { text: '$5.00' }, { text: '$4.99' }]
|
||||
];
|
||||
|
||||
let couponFixedCart;
|
||||
let couponPercentage;
|
||||
let couponFixedProduct;
|
||||
|
||||
const getCoupon = (couponType) => {
|
||||
switch (couponType) {
|
||||
case 'fixed cart':
|
||||
return couponFixedCart;
|
||||
case 'percentage':
|
||||
return couponPercentage;
|
||||
case 'fixed product':
|
||||
return couponFixedProduct;
|
||||
}
|
||||
};
|
||||
|
||||
const runCheckoutApplyCouponsTest = () => {
|
||||
describe('Checkout coupons', () => {
|
||||
|
@ -47,9 +28,6 @@ const runCheckoutApplyCouponsTest = () => {
|
|||
|
||||
beforeAll(async () => {
|
||||
productId = await createSimpleProduct();
|
||||
couponFixedCart = await createCoupon();
|
||||
couponPercentage = await createCoupon('50', 'Percentage discount');
|
||||
couponFixedProduct = await createCoupon('5', 'Fixed product discount');
|
||||
await shopper.emptyCart();
|
||||
await shopper.goToShop();
|
||||
await waitForSelectorWithoutThrow( '.add_to_cart_button' );
|
||||
|
@ -58,8 +36,8 @@ const runCheckoutApplyCouponsTest = () => {
|
|||
await shopper.goToCheckout();
|
||||
});
|
||||
|
||||
it.each(couponsTable)('allows checkout to apply %s coupon', async (couponType, cartDiscount, orderTotal) => {
|
||||
const coupon = getCoupon(couponType);
|
||||
it.each( getCouponsTable() )( 'allows checkout to apply %s coupon', async ( couponType, cartDiscount, orderTotal ) => {
|
||||
const coupon = await getCouponId( couponType );
|
||||
await applyCoupon(coupon);
|
||||
await expect(page).toMatchElement('.woocommerce-message', { text: 'Coupon code applied successfully.' });
|
||||
|
||||
|
@ -73,9 +51,10 @@ const runCheckoutApplyCouponsTest = () => {
|
|||
});
|
||||
|
||||
it('prevents checkout applying same coupon twice', async () => {
|
||||
await applyCoupon(couponFixedCart);
|
||||
const couponId = await getCouponId( 'fixed cart' );
|
||||
await applyCoupon( couponId );
|
||||
await expect(page).toMatchElement('.woocommerce-message', {text: 'Coupon code applied successfully.'});
|
||||
await applyCoupon(couponFixedCart);
|
||||
await applyCoupon( couponId );
|
||||
// Verify only one discount applied
|
||||
// This is a work around for Puppeteer inconsistently finding 'Coupon code already applied'
|
||||
await expect(page).toMatchElement('.cart-discount .amount', {text: '$5.00'});
|
||||
|
@ -83,7 +62,7 @@ const runCheckoutApplyCouponsTest = () => {
|
|||
});
|
||||
|
||||
it('allows checkout to apply multiple coupons', async () => {
|
||||
await applyCoupon(couponFixedProduct);
|
||||
await applyCoupon( await getCouponId( 'fixed product' ) );
|
||||
await expect(page).toMatchElement('.woocommerce-message', {text: 'Coupon code applied successfully.'});
|
||||
|
||||
// Verify discount applied and order total
|
||||
|
@ -92,8 +71,8 @@ const runCheckoutApplyCouponsTest = () => {
|
|||
});
|
||||
|
||||
it('restores checkout total when coupons are removed', async () => {
|
||||
await removeCoupon(couponFixedCart);
|
||||
await removeCoupon(couponFixedProduct);
|
||||
await removeCoupon( await getCouponId( 'fixed cart' ) );
|
||||
await removeCoupon( await getCouponId( 'fixed product' ) );
|
||||
await expect(page).toMatchElement('.order-total .amount', {text: '$9.99'});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -39,7 +39,7 @@ const runCheckoutCreateAccountTest = () => {
|
|||
await settingsPageSaveChanges();
|
||||
|
||||
// Set free shipping within California
|
||||
await addShippingZoneAndMethod('Free Shipping CA', 'state:US:CA', ' ', 'free_shipping');
|
||||
await addShippingZoneAndMethod('Free Shipping CA', 'state:US:CA', ' ', 'free_shipping' );
|
||||
|
||||
await merchant.logout();
|
||||
|
||||
|
|
|
@ -26,12 +26,12 @@ const runCheckoutPageTest = () => {
|
|||
describe('Checkout page', () => {
|
||||
beforeAll(async () => {
|
||||
productId = await createSimpleProduct();
|
||||
await withRestApi.resetSettingsGroupToDefault('general');
|
||||
await withRestApi.resetSettingsGroupToDefault('products');
|
||||
await withRestApi.resetSettingsGroupToDefault('tax');
|
||||
await withRestApi.resetSettingsGroupToDefault( 'general', false );
|
||||
await withRestApi.resetSettingsGroupToDefault( 'products', false );
|
||||
await withRestApi.resetSettingsGroupToDefault( 'tax', false );
|
||||
|
||||
// Set free shipping within California
|
||||
await withRestApi.addShippingZoneAndMethod('Free Shipping CA', 'state:US:CA', '', 'free_shipping');
|
||||
await withRestApi.addShippingZoneAndMethod('Free Shipping CA', 'state:US:CA', '', 'free_shipping', '', [], false );
|
||||
|
||||
// Set base location with state CA.
|
||||
await withRestApi.updateSettingOption( 'general', 'woocommerce_default_country', { value: 'US:CA' } );
|
||||
|
@ -44,14 +44,14 @@ const runCheckoutPageTest = () => {
|
|||
// Tax calculation should have been enabled by another test - no-op
|
||||
|
||||
// Enable BACS payment method
|
||||
await withRestApi.updatePaymentGateway( 'bacs', { enabled: true } );
|
||||
await withRestApi.updatePaymentGateway( 'bacs', { enabled: true }, false );
|
||||
|
||||
// Enable COD payment method
|
||||
await withRestApi.updatePaymentGateway( 'cod', { enabled: true } );
|
||||
await withRestApi.updatePaymentGateway( 'cod', { enabled: true }, false );
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await withRestApi.deleteAllShippingZones();
|
||||
await withRestApi.deleteAllShippingZones( false );
|
||||
});
|
||||
|
||||
it('should display cart items in order review', async () => {
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
const { createCoupon } = require( '@woocommerce/e2e-utils' );
|
||||
|
||||
const couponsTable = [
|
||||
[ 'fixed cart', { text: '$5.00' }, { text: '$4.99' } ],
|
||||
[ 'percentage', { text: '$4.99' }, { text: '$5.00' } ],
|
||||
[ 'fixed product', { text: '$5.00' }, { text: '$4.99' } ]
|
||||
];
|
||||
|
||||
let couponFixedCart;
|
||||
let couponPercentage;
|
||||
let couponFixedProduct;
|
||||
|
||||
/**
|
||||
* Get a test coupon Id. Create the coupon if it does not exist.
|
||||
*
|
||||
* @param {string} couponType Coupon type.
|
||||
* @return {string} Coupon code.
|
||||
*/
|
||||
const getCouponId = async ( couponType ) => {
|
||||
switch ( couponType ) {
|
||||
case 'fixed cart':
|
||||
if ( ! couponFixedCart ) {
|
||||
couponFixedCart = await createCoupon();
|
||||
}
|
||||
return couponFixedCart;
|
||||
case 'percentage':
|
||||
if ( ! couponPercentage ) {
|
||||
couponPercentage = await createCoupon( '50', 'Percentage discount' );
|
||||
}
|
||||
return couponPercentage;
|
||||
case 'fixed product':
|
||||
if ( ! couponFixedProduct ) {
|
||||
couponFixedProduct = await createCoupon( '5', 'Fixed product discount' );
|
||||
}
|
||||
return couponFixedProduct;
|
||||
}
|
||||
};
|
||||
|
||||
const getCouponsTable = () => {
|
||||
return couponsTable;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
getCouponsTable,
|
||||
getCouponId,
|
||||
};
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
- Added quotes around `WORDPRESS_TITLE` value in .env file to address issue with docker compose 2 "key cannot contain a space" error.
|
||||
- Added `LATEST_WP_VERSION_MINUS` that allows setting a number to subtract from the current WordPress version for the WordPress Docker image.
|
||||
- Support for PHP_VERSION, MARIADB_VERSION environment variables for built in container initialization
|
||||
|
||||
## Fixed
|
||||
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
# Changelog
|
||||
|
||||
This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
---
|
||||
|
||||
[See legacy changelogs for previous versions](https://github.com/woocommerce/woocommerce/blob/<last-commit-hash-before-this-merge>/packages/js/e2e-environment/CHANGELOG.md).
|
|
@ -17,22 +17,30 @@ if [[ $1 ]]; then
|
|||
export WORDPRESS_VERSION=$(./bin/get-previous-version.js $WORDPRESS_VERSION $LATEST_WP_VERSION_MINUS 2> /dev/null)
|
||||
fi
|
||||
|
||||
if ! [[ $TRAVIS_PHP_VERSION =~ ^[0-9]+\.[0-9]+ ]]; then
|
||||
TRAVIS_PHP_VERSION=$(./bin/get-latest-docker-tag.js php 7 2> /dev/null)
|
||||
fi
|
||||
if [[ $TRAVIS_PHP_VERSION =~ ^[0-9]+\.[0-9]+ ]]; then
|
||||
export DC_PHP_VERSION=$TRAVIS_PHP_VERSION
|
||||
if [[ $PHP_VERSION =~ ^[0-9]+\.[0-9]+ ]]; then
|
||||
export DC_PHP_VERSION=$PHP_VERSION
|
||||
else
|
||||
export DC_PHP_VERSION="7.4.22"
|
||||
if ! [[ $TRAVIS_PHP_VERSION =~ ^[0-9]+\.[0-9]+ ]]; then
|
||||
TRAVIS_PHP_VERSION=$(./bin/get-latest-docker-tag.js php 7 2> /dev/null)
|
||||
fi
|
||||
if [[ $TRAVIS_PHP_VERSION =~ ^[0-9]+\.[0-9]+ ]]; then
|
||||
export DC_PHP_VERSION=$TRAVIS_PHP_VERSION
|
||||
else
|
||||
export DC_PHP_VERSION="7.4.25"
|
||||
fi
|
||||
fi
|
||||
|
||||
if ! [[ $TRAVIS_MARIADB_VERSION =~ ^[0-9]+\.[0-9]+ ]]; then
|
||||
TRAVIS_MARIADB_VERSION=$(./bin/get-latest-docker-tag.js mariadb 10 2> /dev/null)
|
||||
fi
|
||||
if [[ $TRAVIS_MARIADB_VERSION =~ ^[0-9]+\.[0-9]+ ]]; then
|
||||
export DC_MARIADB_VERSION=$TRAVIS_MARIADB_VERSION
|
||||
if [[ $MARIADB_VERSION =~ ^[0-9]+\.[0-9]+ ]]; then
|
||||
export DC_MARIADB_VERSION=$MARIADB_VERSION
|
||||
else
|
||||
export DC_MARIADB_VERSION="10.6.4"
|
||||
if ! [[ $TRAVIS_MARIADB_VERSION =~ ^[0-9]+\.[0-9]+ ]]; then
|
||||
TRAVIS_MARIADB_VERSION=$(./bin/get-latest-docker-tag.js mariadb 10 2> /dev/null)
|
||||
fi
|
||||
if [[ $TRAVIS_MARIADB_VERSION =~ ^[0-9]+\.[0-9]+ ]]; then
|
||||
export DC_MARIADB_VERSION=$TRAVIS_MARIADB_VERSION
|
||||
else
|
||||
export DC_MARIADB_VERSION="10.6.5"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ $1 == 'up' ]]; then
|
||||
|
|
|
@ -126,9 +126,17 @@ The built in container defaults to mapping the root folder of the repository to
|
|||
|
||||
Since the introduction of the WooCommerce Monorepo, a `WC_CORE_PATH` environment variable maps to Core WooCommerce at `plugins/woocommerce`. It can also be overriden in a similar fashion.
|
||||
|
||||
### Specifying Server Software versions
|
||||
|
||||
The built-in container supports these variables for use locally and in CI environments:
|
||||
|
||||
- `WP_VERSION` - WordPress (default `latest`)
|
||||
- `PHP_VERSION` - PHP (default `latest`)
|
||||
- `MARIADB_VERSION` - MariaDB (default `latest`)
|
||||
|
||||
### Travis CI Supported Versions
|
||||
|
||||
Travis CI uses environment variables to allow control of some software versions in the testing environment. The built in container supports these variables:
|
||||
Travis CI uses environment variables to allow control of some software versions in the testing environment. The built-in container supports these variables:
|
||||
|
||||
- `WP_VERSION` - WordPress (default `latest`)
|
||||
- `TRAVIS_PHP_VERSION` - PHP (default `latest`)
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"name": "woocommerce/e2e-environment",
|
||||
"description": "WooCommerce end to end testing environment",
|
||||
"type": "library",
|
||||
"license": "GPL-3.0-or-later",
|
||||
"minimum-stability": "dev",
|
||||
"require-dev": {
|
||||
"automattic/jetpack-changelogger": "3.0.2"
|
||||
},
|
||||
"extra": {
|
||||
"changelogger": {
|
||||
"formatter": {
|
||||
"filename": "../../../tools/changelogger/PackageFormatter.php"
|
||||
},
|
||||
"types": [
|
||||
"Fix",
|
||||
"Add",
|
||||
"Update",
|
||||
"Dev",
|
||||
"Tweak",
|
||||
"Performance",
|
||||
"Enhancement"
|
||||
],
|
||||
"changelog": "NEXT_CHANGELOG.md"
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -7,6 +7,13 @@
|
|||
- Update `shopper.addToCartFromShopPage()` and `.removeFromCart()` to accept product Id or Title
|
||||
- Added `deleteAllProductAttributes()`, `deleteAllProductCategories()`, and `deleteAllProductTags()` to clean up meta data added when products are imported
|
||||
- Added `withRestApi.createProductCategory()` that creates a product category and returns the ID
|
||||
- `deleteAllProductAttributes()`, `deleteAllProductCategories()`, and `deleteAllProductTags()` to clean up meta data added when products are imported
|
||||
- `withRestApi.createProductCategory()` that creates a product category and returns the ID
|
||||
- `withRestApi.deleteCoupon()` that deletes a single coupon
|
||||
- `withRestApi.addTaxClasses()` that adds an array of tax classes if they do not exist
|
||||
- `withRestApi.addTaxRates()` that adds an array of tax rates if they do not exist
|
||||
- `clickAndWaitForSelector( buttonSelector, resultSelector, timeout )` to click a button and wait for response
|
||||
- Optional parameter `testResponse` to `withRestApi` functions that contain an `expect()`
|
||||
|
||||
# 0.1.6
|
||||
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
# Changelog
|
||||
|
||||
This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
---
|
||||
|
||||
[See legacy changelogs for previous versions](https://github.com/woocommerce/woocommerce/blob/<last-commit-hash-before-this-merge>/packages/js/e2e-utils/CHANGELOG.md).
|
|
@ -142,22 +142,26 @@ Please note: if you're using a non-SSL environment (such as a Docker container f
|
|||
|
||||
| Function | Parameters | Description |
|
||||
|----------|------------|-------------|
|
||||
| `resetOnboarding` | | Reset onboarding settings |
|
||||
| `addShippingZoneAndMethod` | `zoneName`, `zoneLocation`, `zipCode`, `zoneMethod`, `cost`, `additionalZoneMethods`, `testResponse` | Adds a shipping zone along with a shipping method |
|
||||
| `batchCreateOrders` | `orders`, `testResponse` | Create a batch of orders using the "Batch Create Order" API endpoint |
|
||||
| `addTaxClasses` | `taxClasses` | Add an array of tax classes if they do not exist |
|
||||
| `addTaxRates` | `taxRates` | Add an array of tax rates if they do not exist |
|
||||
| `createProductCategory` | `categoryName` | Create a product category with the provided name |
|
||||
| `deleteAllCoupons` | | Permanently delete all coupons |
|
||||
| `deleteAllProducts` | | Permanently delete all products |
|
||||
| `deleteAllShippingZones` | | Permanently delete all shipping zones except the default |
|
||||
| `deleteAllShippingClasses` | Permanently delete all shipping classes |
|
||||
| `deleteCustomerByEmail` | `emailAddress` | Delete customer user account. Posts are reassigned to user ID 1 |
|
||||
| `resetSettingsGroupToDefault` | `settingsGroup` | Reset settings in settings group to default except `select` fields |
|
||||
| `batchCreateOrders` | `orders` | Create a batch of orders using the "Batch Create Order" API endpoint |
|
||||
| `deleteAllOrders` | | Permanently delete all orders |
|
||||
| `updateSettingOption` | `settingsGroup`, `settingID`, `payload` | Update a settings group |
|
||||
| `updatePaymentGateway`| `paymentGatewayId`, `payload` | Update a payment gateway |
|
||||
| `deleteAllProductAttributes` | `testResponse` | Permanently delete all product attributes |
|
||||
| `deleteAllProductCategories` | `testResponse` | Permanently delete all product categories |
|
||||
| `deleteAllProducts` | | Permanently delete all products |
|
||||
| `deleteAllProductTags` | `testResponse` | Permanently delete all product tags |
|
||||
| `deleteAllShippingClasses` | `testResponse` | Permanently delete all shipping classes |
|
||||
| `deleteAllShippingZones` | `testResponse` | Permanently delete all shipping zones except the default |
|
||||
| `deleteCoupon` | `couponId` | Permanently delete a coupon |
|
||||
| `deleteCustomerByEmail` | `emailAddress` | Delete customer user account. Posts are reassigned to user ID 1 |
|
||||
| `getSystemEnvironment` | | Get the current environment from the WooCommerce system status API. |
|
||||
| `deleteAllProductAttributes` | | Permanently delete all product attributes. |
|
||||
| `deleteAllProductCategories` | | Permanently delete all product categories. |
|
||||
| `deleteAllProductTags` | | Permanently delete all product tags. |
|
||||
| `createProductCategory` | `categoryName` | Create a product category with the provided name. |
|
||||
| `resetOnboarding` | | Reset onboarding settings |
|
||||
| `resetSettingsGroupToDefault` | `settingsGroup`, `testResponse` | Reset settings in settings group to default except `select` fields |
|
||||
| `updateSettingOption` | `settingsGroup`, `settingID`, `payload` | Update a settings group |
|
||||
| `updatePaymentGateway`| `paymentGatewayId`, `payload`, `testResponse` | Update a payment gateway |
|
||||
|
||||
### Classes
|
||||
|
||||
|
@ -229,6 +233,7 @@ There is a general utilities object `utils` with the following functions:
|
|||
| `deleteAllShippingZones` | | Delete all the existing shipping zones |
|
||||
| `waitForSelectorWithoutThrow` | `selector`, `timeoutInSeconds` | conditionally wait for a selector without throwing an error. Default timeout is 5 seconds |
|
||||
| `createOrder` | `orderOptions` | Creates an order using the API with the passed in details |
|
||||
| `clickAndWaitForSelector` | `buttonSelector`, `resultSelector`, `timeout` | Click a button and wait for response |
|
||||
|
||||
### Test Utilities
|
||||
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"name": "woocommerce/e2e-utiles",
|
||||
"description": "WooCommerce end to end testing utilities",
|
||||
"type": "library",
|
||||
"license": "GPL-3.0-or-later",
|
||||
"minimum-stability": "dev",
|
||||
"require-dev": {
|
||||
"automattic/jetpack-changelogger": "3.0.2"
|
||||
},
|
||||
"extra": {
|
||||
"changelogger": {
|
||||
"formatter": {
|
||||
"filename": "../../../tools/changelogger/PackageFormatter.php"
|
||||
},
|
||||
"types": [
|
||||
"Fix",
|
||||
"Add",
|
||||
"Update",
|
||||
"Dev",
|
||||
"Tweak",
|
||||
"Performance",
|
||||
"Enhancement"
|
||||
],
|
||||
"changelog": "NEXT_CHANGELOG.md"
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -11,6 +11,7 @@
|
|||
"main": "build/index.js",
|
||||
"module": "build-module/index.js",
|
||||
"dependencies": {
|
||||
"@automattic/puppeteer-utils": "github:Automattic/puppeteer-utils#0f3ec50",
|
||||
"@wordpress/deprecated": "^2.10.0",
|
||||
"@wordpress/e2e-test-utils": "^4.16.1",
|
||||
"config": "3.3.3",
|
||||
|
|
|
@ -190,16 +190,23 @@ const completeOnboardingWizard = async () => {
|
|||
/**
|
||||
* Create simple product.
|
||||
*
|
||||
* @param productTitle - Defaults to Simple Product. Customizable title.
|
||||
* @param productPrice - Defaults to $9.99. Customizable pricing.
|
||||
* @param {string} productTitle Defaults to Simple Product. Customizable title.
|
||||
* @param {string} productPrice Defaults to $9.99. Customizable pricing.
|
||||
* @param {Object} additionalProps Defaults to nothing. Additional product properties.
|
||||
*/
|
||||
const createSimpleProduct = async ( productTitle = simpleProductName, productPrice = simpleProductPrice ) => {
|
||||
const product = await factories.products.simple.create( {
|
||||
const createSimpleProduct = async (
|
||||
productTitle = simpleProductName,
|
||||
productPrice = simpleProductPrice,
|
||||
additionalProps = {}
|
||||
) => {
|
||||
const newProduct = {
|
||||
name: productTitle,
|
||||
regularPrice: productPrice
|
||||
} );
|
||||
regularPrice: productPrice,
|
||||
...additionalProps,
|
||||
};
|
||||
const product = await factories.products.simple.create( newProduct );
|
||||
return product.id;
|
||||
} ;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create simple product with categories
|
||||
|
@ -335,21 +342,29 @@ const createGroupedProduct = async (groupedProduct = defaultGroupedProduct) => {
|
|||
*/
|
||||
const createOrder = async ( orderOptions = {} ) => {
|
||||
const newOrder = {
|
||||
...( orderOptions.status ) && { status: orderOptions.status },
|
||||
...( orderOptions.customerId ) && { customer_id: orderOptions.customerId },
|
||||
...( orderOptions.customerBilling ) && { billing: orderOptions.customerBilling },
|
||||
...( orderOptions.customerShipping ) && { shipping: orderOptions.customerShipping },
|
||||
...( orderOptions.productId ) && { line_items: [
|
||||
{ product_id: orderOptions.productId },
|
||||
]
|
||||
},
|
||||
...( orderOptions.status && { status: orderOptions.status } ),
|
||||
...( orderOptions.customerId && {
|
||||
customer_id: orderOptions.customerId,
|
||||
} ),
|
||||
...( orderOptions.customerBilling && {
|
||||
billing: orderOptions.customerBilling,
|
||||
} ),
|
||||
...( orderOptions.customerShipping && {
|
||||
shipping: orderOptions.customerShipping,
|
||||
} ),
|
||||
...( orderOptions.productId && {
|
||||
line_items: [ { product_id: orderOptions.productId } ],
|
||||
} ),
|
||||
...( orderOptions.lineItems && {
|
||||
line_items: orderOptions.lineItems,
|
||||
} ),
|
||||
};
|
||||
|
||||
const repository = Order.restRepository( client );
|
||||
const order = await repository.create( newOrder );
|
||||
|
||||
return order.id;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a basic order with the provided order status.
|
||||
|
|
|
@ -4,12 +4,14 @@ import {Coupon, Setting, SimpleProduct, Order} from '@woocommerce/api';
|
|||
|
||||
const client = factories.api.withDefaultPermalinks;
|
||||
const onboardingProfileEndpoint = '/wc-admin/onboarding/profile';
|
||||
const shippingZoneEndpoint = '/wc/v3/shipping/zones';
|
||||
const shippingClassesEndpoint = '/wc/v3/products/shipping_classes';
|
||||
const userEndpoint = '/wp/v2/users';
|
||||
const systemStatusEndpoint = '/wc/v3/system_status';
|
||||
const productsEndpoint = '/wc/v3/products';
|
||||
const productCategoriesEndpoint = '/wc/v3/products/categories';
|
||||
const shippingClassesEndpoint = '/wc/v3/products/shipping_classes';
|
||||
const shippingZoneEndpoint = '/wc/v3/shipping/zones';
|
||||
const systemStatusEndpoint = '/wc/v3/system_status';
|
||||
const taxClassesEndpoint = '/wc/v3/taxes/classes';
|
||||
const taxRatesEndpoint = '/wc/v3/taxes';
|
||||
const userEndpoint = '/wp/v2/users';
|
||||
|
||||
/**
|
||||
* Utility function to delete all merchant created data store objects.
|
||||
|
@ -42,6 +44,16 @@ const deleteAllRepositoryObjects = async ( repository, defaultObjectId = null, s
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Utility to flatten a tax rate.
|
||||
*
|
||||
* @param {object} taxRate Tax rate to be flattened.
|
||||
* @return {string}
|
||||
*/
|
||||
const flattenTaxRate = ( taxRate ) => {
|
||||
return taxRate.rate + '/' + taxRate.class + '/' + taxRate.name;
|
||||
};
|
||||
|
||||
/**
|
||||
* Utility functions that use the REST API to process the requested function.
|
||||
*/
|
||||
|
@ -77,6 +89,16 @@ export const withRestApi = {
|
|||
const repository = Coupon.restRepository( client );
|
||||
await deleteAllRepositoryObjects( repository );
|
||||
},
|
||||
/**
|
||||
* Use api package to delete a coupon.
|
||||
*
|
||||
* @param {number} couponId Coupon ID.
|
||||
* @return {Promise} Promise resolving once coupon has been deleted.
|
||||
*/
|
||||
deleteCoupon: async ( couponId ) => {
|
||||
const repository = Coupon.restRepository( client );
|
||||
await repository.delete( couponId );
|
||||
},
|
||||
/**
|
||||
* Use api package to delete products.
|
||||
*
|
||||
|
@ -89,24 +111,28 @@ export const withRestApi = {
|
|||
/**
|
||||
* Use the API to delete all product attributes.
|
||||
*
|
||||
* @param {boolean} testResponse Test the response status code.
|
||||
* @return {Promise} Promise resolving once attributes have been deleted.
|
||||
*/
|
||||
deleteAllProductAttributes: async () => {
|
||||
deleteAllProductAttributes: async ( testResponse = true ) => {
|
||||
const productAttributesPath = productsEndpoint + '/attributes';
|
||||
const productAttributes = await client.get( productAttributesPath );
|
||||
if ( productAttributes.data && productAttributes.data.length ) {
|
||||
for ( let a = 0; a < productAttributes.data.length; a++ ) {
|
||||
const response = await client.delete( productAttributesPath + `/${productAttributes.data[a].id}?force=true` );
|
||||
expect( response.status ).toBe( 200 );
|
||||
if ( testResponse ) {
|
||||
expect( response.status ).toBe( 200 );
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Use the API to delete all product categories.
|
||||
*
|
||||
* @param {boolean} testResponse Test the response status code.
|
||||
* @return {Promise} Promise resolving once categories have been deleted.
|
||||
*/
|
||||
deleteAllProductCategories: async () => {
|
||||
deleteAllProductCategories: async ( testResponse = true ) => {
|
||||
const productCategoriesPath = productsEndpoint + '/categories';
|
||||
const productCategories = await client.get( productCategoriesPath );
|
||||
if ( productCategories.data && productCategories.data.length ) {
|
||||
|
@ -116,22 +142,27 @@ export const withRestApi = {
|
|||
continue;
|
||||
}
|
||||
const response = await client.delete( productCategoriesPath + `/${productCategories.data[c].id}?force=true` );
|
||||
expect( response.status ).toBe( 200 );
|
||||
if ( testResponse ) {
|
||||
expect( response.status ).toBe( 200 );
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Use the API to delete all product tags.
|
||||
*
|
||||
* @param {boolean} testResponse Test the response status code.
|
||||
* @return {Promise} Promise resolving once tags have been deleted.
|
||||
*/
|
||||
deleteAllProductTags: async () => {
|
||||
deleteAllProductTags: async ( testResponse = true ) => {
|
||||
const productTagsPath = productsEndpoint + '/tags';
|
||||
const productTags = await client.get( productTagsPath );
|
||||
if ( productTags.data && productTags.data.length ) {
|
||||
for ( let t = 0; t < productTags.data.length; t++ ) {
|
||||
const response = await client.delete( productTagsPath + `/${productTags.data[t].id}?force=true` );
|
||||
expect( response.status ).toBe( 200 );
|
||||
if ( testResponse ) {
|
||||
expect( response.status ).toBe( 200 );
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -155,6 +186,7 @@ export const withRestApi = {
|
|||
* @param zoneMethod Shipping method type. Defaults to flat_rate (use also: free_shipping or local_pickup).
|
||||
* @param cost Shipping method cost. Default is no cost.
|
||||
* @param additionalZoneMethods Array of additional zone methods to add to the shipping zone.
|
||||
* @param {boolean} testResponse Test the response status code.
|
||||
*/
|
||||
addShippingZoneAndMethod: async (
|
||||
zoneName,
|
||||
|
@ -162,33 +194,38 @@ export const withRestApi = {
|
|||
zipCode = '',
|
||||
zoneMethod = 'flat_rate',
|
||||
cost = '',
|
||||
additionalZoneMethods = [] ) => {
|
||||
additionalZoneMethods = [],
|
||||
testResponse = true ) => {
|
||||
|
||||
const path = 'wc/v3/shipping/zones';
|
||||
const path = 'wc/v3/shipping/zones';
|
||||
|
||||
const response = await client.post( path, { name: zoneName } );
|
||||
expect(response.status).toEqual(201);
|
||||
let zoneId = response.data.id;
|
||||
const response = await client.post( path, { name: zoneName } );
|
||||
if ( testResponse ) {
|
||||
expect( response.status ).toEqual( 201 );
|
||||
}
|
||||
let zoneId = response.data.id;
|
||||
|
||||
// Select shipping zone location
|
||||
let [ zoneType, zoneCode ] = zoneLocation.split(/:(.+)/);
|
||||
let zoneLocationPayload = [
|
||||
// Select shipping zone location
|
||||
let [ zoneType, zoneCode ] = zoneLocation.split(/:(.+)/);
|
||||
let zoneLocationPayload = [
|
||||
{
|
||||
code: zoneCode,
|
||||
type: zoneType,
|
||||
}
|
||||
];
|
||||
];
|
||||
|
||||
// Fill shipping zone postcode if provided
|
||||
if ( zipCode ) {
|
||||
// Fill shipping zone postcode if provided
|
||||
if ( zipCode ) {
|
||||
zoneLocationPayload.push( {
|
||||
code: zipCode,
|
||||
type: "postcode",
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
||||
const locationResponse = await client.put( path + `/${zoneId}/locations`, zoneLocationPayload );
|
||||
expect(locationResponse.status).toEqual(200);
|
||||
const locationResponse = await client.put( path + `/${zoneId}/locations`, zoneLocationPayload );
|
||||
if ( testResponse ) {
|
||||
expect( locationResponse.status ).toEqual( 200 );
|
||||
}
|
||||
|
||||
// Add shipping zone method
|
||||
let methodPayload = {
|
||||
|
@ -196,7 +233,9 @@ export const withRestApi = {
|
|||
}
|
||||
|
||||
const methodsResponse = await client.post( path + `/${zoneId}/methods`, methodPayload );
|
||||
expect(methodsResponse.status).toEqual(200);
|
||||
if ( testResponse ) {
|
||||
expect( methodsResponse.status ).toEqual( 200 );
|
||||
}
|
||||
let methodId = methodsResponse.data.id;
|
||||
|
||||
// Add in cost, if provided
|
||||
|
@ -208,23 +247,28 @@ export const withRestApi = {
|
|||
}
|
||||
|
||||
const costResponse = await client.put( path + `/${zoneId}/methods/${methodId}`, costPayload );
|
||||
expect(costResponse.status).toEqual(200);
|
||||
if ( testResponse ) {
|
||||
expect( costResponse.status ).toEqual( 200 );
|
||||
}
|
||||
}
|
||||
|
||||
// Add any additional zones, if provided
|
||||
if (additionalZoneMethods.length > 0) {
|
||||
for ( let z = 0; z < additionalZoneMethods.length; z++ ) {
|
||||
let response = await client.post( path + `/${zoneId}/methods`, { method_id: additionalZoneMethods[z] } );
|
||||
expect(response.status).toEqual(200);
|
||||
if ( testResponse ) {
|
||||
expect( response.status ).toBe( 200 );
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Use api package to delete shipping zones.
|
||||
*
|
||||
* @param {boolean} testResponse Test the response status code.
|
||||
* @return {Promise} Promise resolving once shipping zones have been deleted.
|
||||
*/
|
||||
deleteAllShippingZones: async () => {
|
||||
deleteAllShippingZones: async ( testResponse = true ) => {
|
||||
const shippingZones = await client.get( shippingZoneEndpoint );
|
||||
if ( shippingZones.data && shippingZones.data.length ) {
|
||||
for ( let z = 0; z < shippingZones.data.length; z++ ) {
|
||||
|
@ -233,21 +277,26 @@ export const withRestApi = {
|
|||
continue;
|
||||
}
|
||||
const response = await client.delete( shippingZoneEndpoint + `/${shippingZones.data[z].id}?force=true` );
|
||||
expect( response.status ).toBe( 200 );
|
||||
if ( testResponse ) {
|
||||
expect( response.status ).toBe( 200 );
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Use api package to delete shipping classes.
|
||||
*
|
||||
* @param {boolean} testResponse Test the response status code.
|
||||
* @return {Promise} Promise resolving once shipping classes have been deleted.
|
||||
*/
|
||||
deleteAllShippingClasses: async () => {
|
||||
deleteAllShippingClasses: async ( testResponse = true ) => {
|
||||
const shippingClasses = await client.get( shippingClassesEndpoint );
|
||||
if ( shippingClasses.data && shippingClasses.data.length ) {
|
||||
for ( let c = 0; c < shippingClasses.data.length; c++ ) {
|
||||
const response = await client.delete( shippingClassesEndpoint + `/${shippingClasses.data[c].id}?force=true` );
|
||||
expect( response.status ).toBe( 200 );
|
||||
if ( testResponse ) {
|
||||
expect( response.status ).toBe( 200 );
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -278,9 +327,10 @@ export const withRestApi = {
|
|||
/**
|
||||
* Reset a settings group to default values except selects.
|
||||
* @param settingsGroup
|
||||
* @param {boolean} testResponse Test the response status code.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
resetSettingsGroupToDefault: async ( settingsGroup ) => {
|
||||
resetSettingsGroupToDefault: async ( settingsGroup, testResponse = true ) => {
|
||||
const settingsClient = Setting.restRepository( client );
|
||||
const settings = await settingsClient.list( settingsGroup );
|
||||
if ( ! settings.length ) {
|
||||
|
@ -300,7 +350,7 @@ export const withRestApi = {
|
|||
|
||||
const response = await settingsClient.update( settingsGroup, defaultSetting.id, defaultSetting );
|
||||
// Multi-selects have a default '' but return an empty [].
|
||||
if ( settings[s].type != 'multiselect' ) {
|
||||
if ( testResponse && settings[s].type != 'multiselect' ) {
|
||||
expect( response.value ).toBe( defaultSetting.value );
|
||||
}
|
||||
}
|
||||
|
@ -321,22 +371,61 @@ export const withRestApi = {
|
|||
*
|
||||
* @param {string} paymentGatewayId The ID of the payment gateway to update.
|
||||
* @param {object} payload An object with the key/value pair to update.
|
||||
* @param {boolean} testResponse Test the response status code.
|
||||
*/
|
||||
updatePaymentGateway: async ( paymentGatewayId, payload = {} ) => {
|
||||
updatePaymentGateway: async ( paymentGatewayId, payload = {}, testResponse = true ) => {
|
||||
const response = await client.put( `/wc/v3/payment_gateways/${paymentGatewayId}`, payload );
|
||||
expect( response.status ).toBe( 200 );
|
||||
if ( testResponse ) {
|
||||
expect( response.status ).toEqual( 200 );
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Create a batch of orders using the "Batch Create Order" API endpoint.
|
||||
*
|
||||
* @param orders Array of orders to be created
|
||||
* @param {boolean} testResponse Test the response status code.
|
||||
*/
|
||||
batchCreateOrders: async (orders) => {
|
||||
batchCreateOrders: async ( orders, testResponse = true ) => {
|
||||
const path = '/wc/v3/orders/batch';
|
||||
const payload = { create: orders };
|
||||
|
||||
const response = await client.post(path, payload);
|
||||
expect( response.status ).toEqual(200);
|
||||
if ( testResponse ) {
|
||||
expect( response.status ).toBe( 200 );
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Add tax classes.
|
||||
*
|
||||
* @param {<Array<Object>>} taxClasses Array of tax class objects.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
addTaxClasses: async ( taxClasses ) => {
|
||||
// Only add tax classes which don't already exist.
|
||||
const existingTaxClasses = await client.get( taxClassesEndpoint );
|
||||
const existingTaxNames = existingTaxClasses.data.map( taxClass => taxClass.name );
|
||||
const newTaxClasses = taxClasses.filter( taxClass => ! existingTaxNames.includes( taxClass.name ) );
|
||||
|
||||
for ( const taxClass of newTaxClasses ) {
|
||||
await client.post( taxClassesEndpoint, taxClass );
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Add tax rates.
|
||||
*
|
||||
* @param {<Array<Object>>} taxRates Array of tax rate objects.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
addTaxRates: async ( taxRates ) => {
|
||||
// Only add rates which don't already exist
|
||||
const existingTaxRates = await client.get( taxRatesEndpoint );
|
||||
const existingRates = existingTaxRates.data.map( taxRate => flattenTaxRate( taxRate ) );
|
||||
|
||||
for ( const taxRate of taxRates ) {
|
||||
if ( ! existingRates.includes( flattenTaxRate( taxRate ) ) ) {
|
||||
await client.post( taxRatesEndpoint, taxRate );
|
||||
}
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Get the current environment from the WooCommerce system status API.
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
* External dependencies
|
||||
*/
|
||||
import { pressKeyWithModifier } from '@wordpress/e2e-test-utils';
|
||||
import { waitForSelector } from '@automattic/puppeteer-utils';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
|
@ -220,11 +221,11 @@ export const evalAndClick = async ( selector ) => {
|
|||
* @param {string} selector Selector of the select2 search field
|
||||
*/
|
||||
export const selectOptionInSelect2 = async ( value, selector = 'input.select2-search__field' ) => {
|
||||
await page.waitForSelector(selector);
|
||||
await page.click(selector);
|
||||
await page.type(selector, value);
|
||||
await page.waitForSelector( selector );
|
||||
await page.click( selector );
|
||||
await page.type( selector, value );
|
||||
await waitForTimeout( 2000 ); // to avoid flakyness, must wait before pressing Enter
|
||||
await page.keyboard.press('Enter');
|
||||
await page.keyboard.press( 'Enter' );
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -234,12 +235,12 @@ export const selectOptionInSelect2 = async ( value, selector = 'input.select2-se
|
|||
* @param {string} orderId Order ID
|
||||
* @param {string} customerName Customer's full name attached to order ID.
|
||||
*/
|
||||
export const searchForOrder = async (value, orderId, customerName) => {
|
||||
await clearAndFillInput('#post-search-input', value);
|
||||
await expect(page).toMatchElement('#post-search-input', value);
|
||||
await expect(page).toClick('#search-submit' );
|
||||
await page.waitForSelector('#the-list', { timeout: 10000 } );
|
||||
await expect(page).toMatchElement('.order_number > a.order-view', {text: `#${orderId} ${customerName}`});
|
||||
export const searchForOrder = async ( value, orderId, customerName) => {
|
||||
await clearAndFillInput( '#post-search-input', value );
|
||||
await expect( page ).toMatchElement( '#post-search-input', value );
|
||||
await expect( page ).toClick( '#search-submit' );
|
||||
await page.waitForSelector( '#the-list', { timeout: 10000 } );
|
||||
await expect( page ).toMatchElement( '.order_number > a.order-view', { text: `#${orderId} ${customerName}` } );
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -255,16 +256,16 @@ export const applyCoupon = async ( couponCode ) => {
|
|||
page.reload(),
|
||||
page.waitForNavigation( { waitUntil: 'networkidle0' } ),
|
||||
]);
|
||||
await expect(page).toClick('a', {text: 'Click here to enter your code'});
|
||||
await expect( page ).toClick( 'a', { text: 'Click here to enter your code' } );
|
||||
await uiUnblocked();
|
||||
await clearAndFillInput('#coupon_code', couponCode);
|
||||
await expect(page).toClick('button', {text: 'Apply coupon'});
|
||||
await clearAndFillInput( '#coupon_code', couponCode );
|
||||
await expect( page ).toClick( 'button', {text: 'Apply coupon' } );
|
||||
await uiUnblocked();
|
||||
} catch (error) {
|
||||
await clearAndFillInput('#coupon_code', couponCode);
|
||||
await expect(page).toClick('button', {text: 'Apply coupon'});
|
||||
} catch ( error ) {
|
||||
await clearAndFillInput( '#coupon_code', couponCode );
|
||||
await expect( page ).toClick( 'button', { text: 'Apply coupon' } );
|
||||
await uiUnblocked();
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -278,9 +279,9 @@ export const removeCoupon = async ( couponCode ) => {
|
|||
page.reload(),
|
||||
page.waitForNavigation( { waitUntil: 'networkidle0' } ),
|
||||
]);
|
||||
await expect(page).toClick('[data-coupon="'+couponCode.toLowerCase()+'"]', {text: '[Remove]'});
|
||||
await expect( page ).toClick( '[data-coupon="'+couponCode.toLowerCase()+'"]', {text: '[Remove]' } );
|
||||
await uiUnblocked();
|
||||
await expect(page).toMatchElement('.woocommerce-message', {text: 'Coupon has been removed.'});
|
||||
await expect( page ).toMatchElement( '.woocommerce-message', {text: 'Coupon has been removed.' } );
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -296,3 +297,24 @@ export const selectOrderAction = async ( action ) => {
|
|||
page.waitForNavigation( { waitUntil: 'networkidle0' } ),
|
||||
] );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Evaluate and click a button selector then wait for a result selector.
|
||||
* This is a work around for what appears to be intermittent delays in handling confirm dialogs.
|
||||
*
|
||||
* @param {string} buttonSelector Selector of button to click
|
||||
* @param {string} resultSelector Selector to wait for after click
|
||||
* @param {number} timeout Timeout length in milliseconds. Default 5000.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
export const clickAndWaitForSelector = async ( buttonSelector, resultSelector, timeout = 5000 ) => {
|
||||
await evalAndClick( buttonSelector );
|
||||
await waitForSelector(
|
||||
page,
|
||||
resultSelector,
|
||||
{
|
||||
timeout
|
||||
}
|
||||
);
|
||||
};
|
||||
|
|
|
@ -16,6 +16,7 @@ docker-compose.yaml
|
|||
Dockerfile
|
||||
Gruntfile.js
|
||||
none
|
||||
project.json
|
||||
package-lock.json
|
||||
package.json
|
||||
packages/woocommerce-admin/docs
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
# Changelog
|
||||
|
||||
---
|
||||
|
||||
[See changelogs for previous versions](https://github.com/woocommerce/woocommerce/blob/77ccfc56ca5680f3bc1496d8b2f93befa28e1483/changelog.txt).
|
|
@ -90,36 +90,17 @@
|
|||
@media only screen and ( min-width: 768px ) {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
.current-section-dropdown {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
.current-section-dropdown {
|
||||
background: #fff;
|
||||
border: 1px solid #a7aaad;
|
||||
margin-bottom: 20px;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
|
||||
@media only screen and ( min-width: 600px ) {
|
||||
margin-left: 70px;
|
||||
width: 288px;
|
||||
}
|
||||
}
|
||||
|
||||
.current-section-name {
|
||||
cursor: pointer;
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
padding: 20px 16px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.current-section-name::after {
|
||||
background-image: url(../images/icons/gridicons-chevron-down.svg);
|
||||
background-size: contain;
|
||||
content: "";
|
||||
display: block;
|
||||
height: 20px;
|
||||
position: absolute;
|
||||
right: 20px;
|
||||
top: 20px;
|
||||
width: 20px;
|
||||
@media only screen and (min-width: 600px) {
|
||||
width: 288px;
|
||||
}
|
||||
|
||||
ul {
|
||||
|
@ -136,11 +117,13 @@
|
|||
width: 100%;
|
||||
z-index: 10;
|
||||
|
||||
@media only screen and ( min-width: 600px ) {
|
||||
@media only screen and (min-width: 600px) {
|
||||
border: 1px solid #1e1e1e;
|
||||
left: -1px;
|
||||
top: 48px;
|
||||
}
|
||||
|
||||
@media only screen and ( min-width: 1100px ) {
|
||||
@media only screen and (min-width: 1100px) {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
|
@ -148,6 +131,17 @@
|
|||
font-size: 13px;
|
||||
line-height: 16px;
|
||||
margin: 0;
|
||||
|
||||
&.current a::after {
|
||||
background-image: url(../images/icons/gridicons-checkmark.svg);
|
||||
content: "";
|
||||
display: block;
|
||||
height: 20px;
|
||||
position: absolute;
|
||||
right: 20px;
|
||||
top: 7px;
|
||||
width: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
a,
|
||||
|
@ -165,32 +159,41 @@
|
|||
position: relative;
|
||||
width: 100%;
|
||||
|
||||
@media only screen and ( min-width: 600px ) {
|
||||
@media only screen and (min-width: 600px) {
|
||||
padding: 10px 18px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
a.current::after {
|
||||
background-image: url(../images/icons/gridicons-checkmark.svg);
|
||||
content: "";
|
||||
display: block;
|
||||
height: 20px;
|
||||
position: absolute;
|
||||
right: 20px;
|
||||
top: 7px;
|
||||
width: 20px;
|
||||
}
|
||||
.current-section-name {
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
line-height: 24px;
|
||||
padding: 12px 16px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.current-section-name::after {
|
||||
background-image: url(../images/icons/gridicons-chevron-down.svg);
|
||||
background-size: contain;
|
||||
content: "";
|
||||
display: block;
|
||||
height: 20px;
|
||||
position: absolute;
|
||||
right: 20px;
|
||||
top: 16px;
|
||||
width: 20px;
|
||||
}
|
||||
|
||||
.current-section-dropdown.is-open {
|
||||
|
||||
ul {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.current-section-dropdown.is-open {
|
||||
|
||||
ul {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.current-section-name::after {
|
||||
transform: rotate(0.5turn);
|
||||
}
|
||||
.current-section-name::after {
|
||||
transform: rotate(0.5turn);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -202,7 +205,7 @@
|
|||
font-size: 9px;
|
||||
font-weight: 600;
|
||||
line-height: 17px;
|
||||
margin: 1px 0 0 2px;
|
||||
margin: 1px 0 0 4px;
|
||||
padding: 0 6px;
|
||||
vertical-align: text-top;
|
||||
}
|
||||
|
@ -223,7 +226,7 @@
|
|||
font-size: 20px;
|
||||
font-family: $font-sf-pro-display;
|
||||
line-height: 1.2;
|
||||
margin: 48px 0 16px;
|
||||
margin: 48px 0 12px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
|
@ -430,10 +433,6 @@
|
|||
|
||||
@media only screen and (max-width: 400px) {
|
||||
|
||||
.addons-featured {
|
||||
margin: -1% -5%;
|
||||
}
|
||||
|
||||
.addons-button {
|
||||
width: 100%;
|
||||
}
|
||||
|
@ -450,9 +449,21 @@
|
|||
width: 100%;
|
||||
}
|
||||
|
||||
.addon-product-group {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.addon-product-group-title {
|
||||
font-family: $font-sf-pro-display;
|
||||
letter-spacing: 0.38px;
|
||||
font-size: 20px;
|
||||
font-weight: 400;
|
||||
line-height: 24px;
|
||||
margin: 0 0 4px;
|
||||
}
|
||||
|
||||
.current-section-dropdown__title {
|
||||
display: none;
|
||||
font-family: $font-sf-pro-display;
|
||||
}
|
||||
|
||||
.addon-product-group-description-container {
|
||||
|
@ -503,6 +514,7 @@
|
|||
background: #fff;
|
||||
border: 1px solid #dcdcde;
|
||||
border-radius: 2px;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex: 1 0 auto;
|
||||
flex-direction: column;
|
||||
|
@ -793,6 +805,36 @@
|
|||
}
|
||||
}
|
||||
|
||||
.marketplace-header__tabs {
|
||||
display: flex;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.marketplace-header__tab {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.marketplace-header__tab-link {
|
||||
align-items: center;
|
||||
border-bottom: 2px solid transparent;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
font-size: 14px;
|
||||
height: 60px;
|
||||
justify-content: center;
|
||||
line-height: 20px;
|
||||
padding: 0 24px;
|
||||
text-decoration: none;
|
||||
width: 100%;
|
||||
|
||||
&.is-current {
|
||||
border-bottom: 2px solid #1e1e1e;
|
||||
color: #1e1e1e;
|
||||
}
|
||||
}
|
||||
|
||||
.no-touch,
|
||||
.no-js {
|
||||
|
||||
|
@ -813,6 +855,19 @@
|
|||
|
||||
.wc-subscriptions-wrap {
|
||||
max-width: 1200px;
|
||||
|
||||
.update-plugins .update-count {
|
||||
background-color: #d54e21;
|
||||
border-radius: 10px;
|
||||
color: #fff;
|
||||
display: inline-block;
|
||||
font-size: 9px;
|
||||
font-weight: 600;
|
||||
line-height: 17px;
|
||||
margin: 1px 0 0 2px;
|
||||
padding: 0 6px;
|
||||
vertical-align: text-top;
|
||||
}
|
||||
}
|
||||
|
||||
.woocommerce-page-wc-marketplace {
|
||||
|
@ -7699,4 +7754,87 @@ table.bar_chart {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
.marketplace-header__tab {
|
||||
flex: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 961px) {
|
||||
|
||||
.marketplace-header__tabs {
|
||||
margin-left: 84px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-width: 1024px) {
|
||||
|
||||
.current-section-name {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.wc-addons-wrap {
|
||||
.current-section-dropdown__title {
|
||||
display: block;
|
||||
font-size: 20px;
|
||||
font-weight: 400;
|
||||
line-height: 24px;
|
||||
margin: 0 0 16px;
|
||||
}
|
||||
|
||||
.current-section-dropdown {
|
||||
background: none;
|
||||
border: none;
|
||||
margin-bottom: 32px;
|
||||
width: 100%;
|
||||
|
||||
ul {
|
||||
background: none;
|
||||
border: none;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-start;
|
||||
margin-top: -12px;
|
||||
padding: 0;
|
||||
position: static;
|
||||
|
||||
li {
|
||||
background: #fff;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 32px;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
margin: 12px 12px 0 0;
|
||||
|
||||
&.current {
|
||||
background: #007cba;
|
||||
border: 1px solid #007cba;
|
||||
|
||||
a {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
a::after {
|
||||
background: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
a,
|
||||
a:visited,
|
||||
a:hover,
|
||||
a:active {
|
||||
color: #2c3338;
|
||||
padding: 10px 16px !important;
|
||||
}
|
||||
}
|
||||
|
||||
li:last-child {
|
||||
a::after {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ rm -rf "$BUILD_PATH"
|
|||
mkdir -p "$DEST_PATH"
|
||||
|
||||
echo "Installing PHP and JS dependencies..."
|
||||
pnpm run install:no-e2e
|
||||
pnpm install
|
||||
composer install || exit "$?"
|
||||
echo "Running JS Build..."
|
||||
pnpm run build:core || exit "$?"
|
||||
|
|
|
@ -266,16 +266,16 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/console",
|
||||
"version": "v5.3.7",
|
||||
"version": "v5.3.10",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/console.git",
|
||||
"reference": "8b1008344647462ae6ec57559da166c2bfa5e16a"
|
||||
"reference": "d4e409d9fbcfbf71af0e5a940abb7b0b4bad0bd3"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/console/zipball/8b1008344647462ae6ec57559da166c2bfa5e16a",
|
||||
"reference": "8b1008344647462ae6ec57559da166c2bfa5e16a",
|
||||
"url": "https://api.github.com/repos/symfony/console/zipball/d4e409d9fbcfbf71af0e5a940abb7b0b4bad0bd3",
|
||||
"reference": "d4e409d9fbcfbf71af0e5a940abb7b0b4bad0bd3",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -345,7 +345,7 @@
|
|||
"terminal"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/console/tree/v5.3.7"
|
||||
"source": "https://github.com/symfony/console/tree/v5.3.10"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
@ -361,7 +361,7 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2021-08-25T20:02:16+00:00"
|
||||
"time": "2021-10-26T09:30:15+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/deprecation-contracts",
|
||||
|
@ -1059,16 +1059,16 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/string",
|
||||
"version": "v5.3.7",
|
||||
"version": "v5.3.10",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/string.git",
|
||||
"reference": "8d224396e28d30f81969f083a58763b8b9ceb0a5"
|
||||
"reference": "d70c35bb20bbca71fc4ab7921e3c6bda1a82a60c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/string/zipball/8d224396e28d30f81969f083a58763b8b9ceb0a5",
|
||||
"reference": "8d224396e28d30f81969f083a58763b8b9ceb0a5",
|
||||
"url": "https://api.github.com/repos/symfony/string/zipball/d70c35bb20bbca71fc4ab7921e3c6bda1a82a60c",
|
||||
"reference": "d70c35bb20bbca71fc4ab7921e3c6bda1a82a60c",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -1122,7 +1122,7 @@
|
|||
"utf8"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/string/tree/v5.3.7"
|
||||
"source": "https://github.com/symfony/string/tree/v5.3.10"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
@ -1138,7 +1138,7 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2021-08-26T08:00:08+00:00"
|
||||
"time": "2021-10-27T18:21:46+00:00"
|
||||
}
|
||||
],
|
||||
"aliases": [],
|
||||
|
|
|
@ -1112,16 +1112,16 @@
|
|||
},
|
||||
{
|
||||
"name": "sebastian/exporter",
|
||||
"version": "3.1.3",
|
||||
"version": "3.1.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/exporter.git",
|
||||
"reference": "6b853149eab67d4da22291d36f5b0631c0fd856e"
|
||||
"reference": "0c32ea2e40dbf59de29f3b49bf375176ce7dd8db"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/6b853149eab67d4da22291d36f5b0631c0fd856e",
|
||||
"reference": "6b853149eab67d4da22291d36f5b0631c0fd856e",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/0c32ea2e40dbf59de29f3b49bf375176ce7dd8db",
|
||||
"reference": "0c32ea2e40dbf59de29f3b49bf375176ce7dd8db",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -1130,7 +1130,7 @@
|
|||
},
|
||||
"require-dev": {
|
||||
"ext-mbstring": "*",
|
||||
"phpunit/phpunit": "^6.0"
|
||||
"phpunit/phpunit": "^8.5"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
|
@ -1177,7 +1177,7 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/exporter/issues",
|
||||
"source": "https://github.com/sebastianbergmann/exporter/tree/3.1.3"
|
||||
"source": "https://github.com/sebastianbergmann/exporter/tree/3.1.4"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
@ -1185,7 +1185,7 @@
|
|||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2020-11-30T07:47:53+00:00"
|
||||
"time": "2021-11-11T13:51:24+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sebastian/global-state",
|
||||
|
|
|
@ -9,16 +9,16 @@
|
|||
"packages-dev": [
|
||||
{
|
||||
"name": "gettext/gettext",
|
||||
"version": "v4.8.5",
|
||||
"version": "v4.8.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/php-gettext/Gettext.git",
|
||||
"reference": "ef2e312dff383fc0e4cd62dd39042e1157f137d4"
|
||||
"reference": "bbeb8f4d3077663739aecb4551b22e720c0e9efe"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/php-gettext/Gettext/zipball/ef2e312dff383fc0e4cd62dd39042e1157f137d4",
|
||||
"reference": "ef2e312dff383fc0e4cd62dd39042e1157f137d4",
|
||||
"url": "https://api.github.com/repos/php-gettext/Gettext/zipball/bbeb8f4d3077663739aecb4551b22e720c0e9efe",
|
||||
"reference": "bbeb8f4d3077663739aecb4551b22e720c0e9efe",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -70,7 +70,7 @@
|
|||
"support": {
|
||||
"email": "oom@oscarotero.com",
|
||||
"issues": "https://github.com/oscarotero/Gettext/issues",
|
||||
"source": "https://github.com/php-gettext/Gettext/tree/v4.8.5"
|
||||
"source": "https://github.com/php-gettext/Gettext/tree/v4.8.6"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
@ -86,20 +86,20 @@
|
|||
"type": "patreon"
|
||||
}
|
||||
],
|
||||
"time": "2021-07-13T16:45:53+00:00"
|
||||
"time": "2021-10-19T10:44:53+00:00"
|
||||
},
|
||||
{
|
||||
"name": "gettext/languages",
|
||||
"version": "2.8.1",
|
||||
"version": "2.9.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/php-gettext/Languages.git",
|
||||
"reference": "4ad818b6341e177b7c508ec4c37e18932a7b788a"
|
||||
"reference": "ed56dd2c7f4024cc953ed180d25f02f2640e3ffa"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/php-gettext/Languages/zipball/4ad818b6341e177b7c508ec4c37e18932a7b788a",
|
||||
"reference": "4ad818b6341e177b7c508ec4c37e18932a7b788a",
|
||||
"url": "https://api.github.com/repos/php-gettext/Languages/zipball/ed56dd2c7f4024cc953ed180d25f02f2640e3ffa",
|
||||
"reference": "ed56dd2c7f4024cc953ed180d25f02f2640e3ffa",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -148,7 +148,7 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/php-gettext/Languages/issues",
|
||||
"source": "https://github.com/php-gettext/Languages/tree/2.8.1"
|
||||
"source": "https://github.com/php-gettext/Languages/tree/2.9.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
@ -160,20 +160,20 @@
|
|||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2021-07-14T15:03:58+00:00"
|
||||
"time": "2021-11-11T17:30:39+00:00"
|
||||
},
|
||||
{
|
||||
"name": "mck89/peast",
|
||||
"version": "v1.13.8",
|
||||
"version": "v1.13.9",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/mck89/peast.git",
|
||||
"reference": "4f0423441ec557f3935b056d10987f2e1c7a3e76"
|
||||
"reference": "5329e997fb50e0b82ca8f6e4164f92941f689b47"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/mck89/peast/zipball/4f0423441ec557f3935b056d10987f2e1c7a3e76",
|
||||
"reference": "4f0423441ec557f3935b056d10987f2e1c7a3e76",
|
||||
"url": "https://api.github.com/repos/mck89/peast/zipball/5329e997fb50e0b82ca8f6e4164f92941f689b47",
|
||||
"reference": "5329e997fb50e0b82ca8f6e4164f92941f689b47",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -185,7 +185,7 @@
|
|||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.13.8-dev"
|
||||
"dev-master": "1.13.9-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
|
@ -207,9 +207,9 @@
|
|||
"description": "Peast is PHP library that generates AST for JavaScript code",
|
||||
"support": {
|
||||
"issues": "https://github.com/mck89/peast/issues",
|
||||
"source": "https://github.com/mck89/peast/tree/v1.13.8"
|
||||
"source": "https://github.com/mck89/peast/tree/v1.13.9"
|
||||
},
|
||||
"time": "2021-09-11T10:28:18+00:00"
|
||||
"time": "2021-11-12T13:44:49+00:00"
|
||||
},
|
||||
{
|
||||
"name": "mustache/mustache",
|
||||
|
|
|
@ -9,5 +9,5 @@ runOnChange() {
|
|||
fi
|
||||
}
|
||||
|
||||
runOnChange "package-lock.json" "pnpm run install:no-e2e"
|
||||
runOnChange "package-lock.json" "pnpm install"
|
||||
runOnChange "composer.lock" "SKIP_UPDATE_TEXTDOMAINS=true composer install"
|
||||
|
|
|
@ -1,121 +1,138 @@
|
|||
{
|
||||
"name": "woocommerce/woocommerce",
|
||||
"description": "An eCommerce toolkit that helps you sell anything. Beautifully.",
|
||||
"homepage": "https://woocommerce.com/",
|
||||
"type": "wordpress-plugin",
|
||||
"license": "GPL-3.0-or-later",
|
||||
"prefer-stable": true,
|
||||
"minimum-stability": "dev",
|
||||
"repositories": [
|
||||
{
|
||||
"type": "path",
|
||||
"url": "lib"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=7.0",
|
||||
"automattic/jetpack-autoloader": "2.10.1",
|
||||
"automattic/jetpack-constants": "1.5.1",
|
||||
"composer/installers": "~1.7",
|
||||
"maxmind-db/reader": "1.6.0",
|
||||
"pelago/emogrifier": "3.1.0",
|
||||
"psr/container": "1.0.0",
|
||||
"woocommerce/action-scheduler": "3.3.0",
|
||||
"woocommerce/woocommerce-admin": "2.8.0",
|
||||
"woocommerce/woocommerce-blocks": "6.1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"bamarni/composer-bin-plugin": "^1.4",
|
||||
"yoast/phpunit-polyfills": "^1.0"
|
||||
},
|
||||
"config": {
|
||||
"optimize-autoloader": true,
|
||||
"platform": {
|
||||
"php": "7.0"
|
||||
},
|
||||
"preferred-install": {
|
||||
"woocommerce/action-scheduler": "dist",
|
||||
"woocommerce/woocommerce-rest-api": "dist",
|
||||
"woocommerce/woocommerce-blocks": "dist"
|
||||
},
|
||||
"sort-packages": true
|
||||
},
|
||||
"autoload": {
|
||||
"exclude-from-classmap": [
|
||||
"includes/legacy",
|
||||
"includes/libraries"
|
||||
],
|
||||
"classmap": [
|
||||
"includes/rest-api"
|
||||
],
|
||||
"psr-4": {
|
||||
"Automattic\\WooCommerce\\": "src/",
|
||||
"Automattic\\WooCommerce\\Vendor\\": "lib/packages/"
|
||||
},
|
||||
"psr-0": {
|
||||
"Automattic\\WooCommerce\\Vendor\\": "lib/packages/"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Automattic\\WooCommerce\\Tests\\": "tests/php/src/",
|
||||
"Automattic\\WooCommerce\\Testing\\Tools\\": "tests/Tools/"
|
||||
},
|
||||
"classmap": [
|
||||
"tests/legacy/unit-tests/rest-api/Helpers"
|
||||
]
|
||||
},
|
||||
"scripts": {
|
||||
"post-install-cmd": [
|
||||
"@composer bin all install --ansi",
|
||||
"sh ./bin/package-update.sh"
|
||||
],
|
||||
"post-update-cmd": [
|
||||
"@composer bin all update --ansi",
|
||||
"sh ./bin/package-update.sh"
|
||||
],
|
||||
"test": [
|
||||
"phpunit"
|
||||
],
|
||||
"phpcs": [
|
||||
"phpcs -s -p"
|
||||
],
|
||||
"phpcs-pre-commit": [
|
||||
"phpcs -s -p -n"
|
||||
],
|
||||
"phpcbf": [
|
||||
"phpcbf -p"
|
||||
],
|
||||
"makepot-audit": [
|
||||
"wp --allow-root i18n make-pot . --exclude=\".github,.wordpress-org,bin,sample-data,node_modules,tests\" --slug=woocommerce"
|
||||
],
|
||||
"makepot": [
|
||||
"@makepot-audit --skip-audit"
|
||||
],
|
||||
"bin": [
|
||||
"echo 'bin not installed'"
|
||||
],
|
||||
"build-lib": [
|
||||
"sh ./bin/build-lib.sh"
|
||||
]
|
||||
},
|
||||
"extra": {
|
||||
"installer-paths": {
|
||||
"packages/{$name}": [
|
||||
"woocommerce/action-scheduler",
|
||||
"woocommerce/woocommerce-blocks",
|
||||
"woocommerce/woocommerce-admin"
|
||||
]
|
||||
},
|
||||
"scripts-description": {
|
||||
"test": "Run unit tests",
|
||||
"phpcs": "Analyze code against the WordPress coding standards with PHP_CodeSniffer",
|
||||
"phpcbf": "Fix coding standards warnings/errors automatically with PHP Code Beautifier",
|
||||
"makepot-audit": "Generate i18n/languages/woocommerce.pot file and run audit",
|
||||
"makepot": "Generate i18n/languages/woocommerce.pot file"
|
||||
},
|
||||
"bamarni-bin": {
|
||||
"target-directory": "bin/composer"
|
||||
}
|
||||
}
|
||||
"name": "woocommerce/woocommerce",
|
||||
"description": "An eCommerce toolkit that helps you sell anything. Beautifully.",
|
||||
"homepage": "https://woocommerce.com/",
|
||||
"type": "wordpress-plugin",
|
||||
"license": "GPL-3.0-or-later",
|
||||
"prefer-stable": true,
|
||||
"minimum-stability": "dev",
|
||||
"repositories": [
|
||||
{
|
||||
"type": "path",
|
||||
"url": "lib"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=7.0",
|
||||
"automattic/jetpack-autoloader": "2.10.1",
|
||||
"automattic/jetpack-constants": "1.5.1",
|
||||
"composer/installers": "~1.7",
|
||||
"maxmind-db/reader": "1.6.0",
|
||||
"pelago/emogrifier": "3.1.0",
|
||||
"psr/container": "1.0.0",
|
||||
"woocommerce/action-scheduler": "3.3.0",
|
||||
"woocommerce/woocommerce-admin": "2.9.0-rc.2",
|
||||
"woocommerce/woocommerce-blocks": "6.3.2"
|
||||
},
|
||||
"require-dev": {
|
||||
"bamarni/composer-bin-plugin": "^1.4",
|
||||
"yoast/phpunit-polyfills": "^1.0",
|
||||
"automattic/jetpack-changelogger": "3.0.2"
|
||||
},
|
||||
"config": {
|
||||
"optimize-autoloader": true,
|
||||
"preferred-install": {
|
||||
"woocommerce/action-scheduler": "dist",
|
||||
"woocommerce/woocommerce-rest-api": "dist",
|
||||
"woocommerce/woocommerce-blocks": "dist"
|
||||
},
|
||||
"sort-packages": true,
|
||||
"platform": {
|
||||
"php": "7.0.33"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"exclude-from-classmap": [
|
||||
"includes/legacy",
|
||||
"includes/libraries"
|
||||
],
|
||||
"classmap": [
|
||||
"includes/rest-api"
|
||||
],
|
||||
"psr-4": {
|
||||
"Automattic\\WooCommerce\\": "src/",
|
||||
"Automattic\\WooCommerce\\Vendor\\": "lib/packages/"
|
||||
},
|
||||
"psr-0": {
|
||||
"Automattic\\WooCommerce\\Vendor\\": "lib/packages/"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Automattic\\WooCommerce\\Tests\\": "tests/php/src/",
|
||||
"Automattic\\WooCommerce\\Testing\\Tools\\": "tests/Tools/"
|
||||
},
|
||||
"classmap": [
|
||||
"tests/legacy/unit-tests/rest-api/Helpers"
|
||||
]
|
||||
},
|
||||
"scripts": {
|
||||
"post-install-cmd": [
|
||||
"@composer bin all install --ansi",
|
||||
"sh ./bin/package-update.sh"
|
||||
],
|
||||
"post-update-cmd": [
|
||||
"@composer bin all update --ansi",
|
||||
"sh ./bin/package-update.sh"
|
||||
],
|
||||
"test": [
|
||||
"phpunit"
|
||||
],
|
||||
"phpcs": [
|
||||
"phpcs -s -p"
|
||||
],
|
||||
"phpcs-pre-commit": [
|
||||
"phpcs -s -p -n"
|
||||
],
|
||||
"phpcbf": [
|
||||
"phpcbf -p"
|
||||
],
|
||||
"makepot-audit": [
|
||||
"wp --allow-root i18n make-pot . --exclude=\".github,.wordpress-org,bin,sample-data,node_modules,tests\" --slug=woocommerce"
|
||||
],
|
||||
"makepot": [
|
||||
"@makepot-audit --skip-audit"
|
||||
],
|
||||
"bin": [
|
||||
"echo 'bin not installed'"
|
||||
],
|
||||
"build-lib": [
|
||||
"sh ./bin/build-lib.sh"
|
||||
]
|
||||
},
|
||||
"extra": {
|
||||
"installer-paths": {
|
||||
"packages/{$name}": [
|
||||
"woocommerce/action-scheduler",
|
||||
"woocommerce/woocommerce-blocks",
|
||||
"woocommerce/woocommerce-admin"
|
||||
]
|
||||
},
|
||||
"scripts-description": {
|
||||
"test": "Run unit tests",
|
||||
"phpcs": "Analyze code against the WordPress coding standards with PHP_CodeSniffer",
|
||||
"phpcbf": "Fix coding standards warnings/errors automatically with PHP Code Beautifier",
|
||||
"makepot-audit": "Generate i18n/languages/woocommerce.pot file and run audit",
|
||||
"makepot": "Generate i18n/languages/woocommerce.pot file"
|
||||
},
|
||||
"bamarni-bin": {
|
||||
"target-directory": "bin/composer"
|
||||
},
|
||||
"changelogger": {
|
||||
"formatter": {
|
||||
"filename": "../../tools/changelogger/PluginFormatter.php"
|
||||
},
|
||||
"types": [
|
||||
"Fix",
|
||||
"Add",
|
||||
"Update",
|
||||
"Dev",
|
||||
"Tweak",
|
||||
"Performance",
|
||||
"Enhancement"
|
||||
],
|
||||
"versioning": "wordpress",
|
||||
"changelog": "NEXT_CHANGELOG.md"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "2850714f99d072cbc80188fd4b56630f",
|
||||
"content-hash": "83379234b653f45a89537a0bb470eb69",
|
||||
"packages": [
|
||||
{
|
||||
"name": "automattic/jetpack-autoloader",
|
||||
|
@ -438,27 +438,22 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/css-selector",
|
||||
"version": "v3.3.6",
|
||||
"version": "v3.4.47",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/css-selector.git",
|
||||
"reference": "4d882dced7b995d5274293039370148e291808f2"
|
||||
"reference": "da3d9da2ce0026771f5fe64cb332158f1bd2bc33"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/css-selector/zipball/4d882dced7b995d5274293039370148e291808f2",
|
||||
"reference": "4d882dced7b995d5274293039370148e291808f2",
|
||||
"url": "https://api.github.com/repos/symfony/css-selector/zipball/da3d9da2ce0026771f5fe64cb332158f1bd2bc33",
|
||||
"reference": "da3d9da2ce0026771f5fe64cb332158f1bd2bc33",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.5.9"
|
||||
"php": "^5.5.9|>=7.0.8"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "3.3-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Symfony\\Component\\CssSelector\\": ""
|
||||
|
@ -472,14 +467,14 @@
|
|||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Jean-François Simon",
|
||||
"email": "jeanfrancois.simon@sensiolabs.com"
|
||||
},
|
||||
{
|
||||
"name": "Fabien Potencier",
|
||||
"email": "fabien@symfony.com"
|
||||
},
|
||||
{
|
||||
"name": "Jean-François Simon",
|
||||
"email": "jeanfrancois.simon@sensiolabs.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
|
@ -488,9 +483,23 @@
|
|||
"description": "Symfony CssSelector Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/css-selector/tree/master"
|
||||
"source": "https://github.com/symfony/css-selector/tree/v3.4.47"
|
||||
},
|
||||
"time": "2017-05-01T15:01:29+00:00"
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://symfony.com/sponsor",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2020-10-24T10:57:07+00:00"
|
||||
},
|
||||
{
|
||||
"name": "woocommerce/action-scheduler",
|
||||
|
@ -533,16 +542,16 @@
|
|||
},
|
||||
{
|
||||
"name": "woocommerce/woocommerce-admin",
|
||||
"version": "2.8.0",
|
||||
"version": "2.9.0-rc.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/woocommerce/woocommerce-admin.git",
|
||||
"reference": "63b93a95db4bf788f42587a41f2378128a2adfdf"
|
||||
"reference": "6c81e761d9a19e6ed4484db82d6710331f375a3a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/woocommerce/woocommerce-admin/zipball/63b93a95db4bf788f42587a41f2378128a2adfdf",
|
||||
"reference": "63b93a95db4bf788f42587a41f2378128a2adfdf",
|
||||
"url": "https://api.github.com/repos/woocommerce/woocommerce-admin/zipball/6c81e761d9a19e6ed4484db82d6710331f375a3a",
|
||||
"reference": "6c81e761d9a19e6ed4484db82d6710331f375a3a",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -598,22 +607,22 @@
|
|||
"homepage": "https://github.com/woocommerce/woocommerce-admin",
|
||||
"support": {
|
||||
"issues": "https://github.com/woocommerce/woocommerce-admin/issues",
|
||||
"source": "https://github.com/woocommerce/woocommerce-admin/tree/v2.8.0"
|
||||
"source": "https://github.com/woocommerce/woocommerce-admin/tree/v2.9.0-rc.2"
|
||||
},
|
||||
"time": "2021-11-02T19:28:38+00:00"
|
||||
"time": "2021-11-18T08:35:56+00:00"
|
||||
},
|
||||
{
|
||||
"name": "woocommerce/woocommerce-blocks",
|
||||
"version": "v6.1.0",
|
||||
"version": "v6.3.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/woocommerce/woocommerce-gutenberg-products-block.git",
|
||||
"reference": "8556efd69e85c01f5571d39e6581d9b8486b682f"
|
||||
"reference": "5b3aea9adb718a1f78525881e4f0c33c72ba175d"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/woocommerce/woocommerce-gutenberg-products-block/zipball/8556efd69e85c01f5571d39e6581d9b8486b682f",
|
||||
"reference": "8556efd69e85c01f5571d39e6581d9b8486b682f",
|
||||
"url": "https://api.github.com/repos/woocommerce/woocommerce-gutenberg-products-block/zipball/5b3aea9adb718a1f78525881e4f0c33c72ba175d",
|
||||
"reference": "5b3aea9adb718a1f78525881e4f0c33c72ba175d",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -621,6 +630,8 @@
|
|||
"composer/installers": "^1.7.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"johnbillion/wp-hooks-generator": "0.6.1",
|
||||
"mockery/mockery": "^1.4",
|
||||
"woocommerce/woocommerce-sniffs": "0.1.0",
|
||||
"wp-phpunit/wp-phpunit": "^5.4",
|
||||
"yoast/phpunit-polyfills": "^1.0"
|
||||
|
@ -650,12 +661,69 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/woocommerce/woocommerce-gutenberg-products-block/issues",
|
||||
"source": "https://github.com/woocommerce/woocommerce-gutenberg-products-block/tree/v6.1.0"
|
||||
"source": "https://github.com/woocommerce/woocommerce-gutenberg-products-block/tree/v6.3.2"
|
||||
},
|
||||
"time": "2021-10-12T13:07:11+00:00"
|
||||
"time": "2021-11-18T03:27:03+00:00"
|
||||
}
|
||||
],
|
||||
"packages-dev": [
|
||||
{
|
||||
"name": "automattic/jetpack-changelogger",
|
||||
"version": "v3.0.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Automattic/jetpack-changelogger.git",
|
||||
"reference": "b76f9cb4c22ec08490eff91a2e0e5aa586ee04b3"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/Automattic/jetpack-changelogger/zipball/b76f9cb4c22ec08490eff91a2e0e5aa586ee04b3",
|
||||
"reference": "b76f9cb4c22ec08490eff91a2e0e5aa586ee04b3",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.6",
|
||||
"symfony/console": "^3.4 | ^5.2",
|
||||
"symfony/process": "^3.4 | ^5.2",
|
||||
"wikimedia/at-ease": "^1.2 | ^2.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"wikimedia/testing-access-wrapper": "^1.0 | ^2.0",
|
||||
"yoast/phpunit-polyfills": "1.0.2"
|
||||
},
|
||||
"bin": [
|
||||
"bin/changelogger"
|
||||
],
|
||||
"type": "project",
|
||||
"extra": {
|
||||
"autotagger": true,
|
||||
"branch-alias": {
|
||||
"dev-master": "3.0.x-dev"
|
||||
},
|
||||
"mirror-repo": "Automattic/jetpack-changelogger",
|
||||
"version-constants": {
|
||||
"::VERSION": "src/Application.php"
|
||||
},
|
||||
"changelogger": {
|
||||
"link-template": "https://github.com/Automattic/jetpack-changelogger/compare/${old}...${new}"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Automattic\\Jetpack\\Changelogger\\": "src",
|
||||
"Automattic\\Jetpack\\Changelog\\": "lib"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"GPL-2.0-or-later"
|
||||
],
|
||||
"description": "Jetpack Changelogger tool. Allows for managing changelogs by dropping change files into a changelog directory with each PR.",
|
||||
"support": {
|
||||
"source": "https://github.com/Automattic/jetpack-changelogger/tree/v3.0.2"
|
||||
},
|
||||
"time": "2021-11-02T14:06:49+00:00"
|
||||
},
|
||||
{
|
||||
"name": "bamarni/composer-bin-plugin",
|
||||
"version": "1.4.1",
|
||||
|
@ -1576,6 +1644,56 @@
|
|||
"abandoned": true,
|
||||
"time": "2018-08-09T05:50:03+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/log",
|
||||
"version": "1.1.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/php-fig/log.git",
|
||||
"reference": "d49695b909c3b7628b6289db5479a1c204601f11"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11",
|
||||
"reference": "d49695b909c3b7628b6289db5479a1c204601f11",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.1.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Psr\\Log\\": "Psr/Log/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "PHP-FIG",
|
||||
"homepage": "https://www.php-fig.org/"
|
||||
}
|
||||
],
|
||||
"description": "Common interface for logging libraries",
|
||||
"homepage": "https://github.com/php-fig/log",
|
||||
"keywords": [
|
||||
"log",
|
||||
"psr",
|
||||
"psr-3"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/php-fig/log/tree/1.1.4"
|
||||
},
|
||||
"time": "2021-05-03T11:20:27+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sebastian/code-unit-reverse-lookup",
|
||||
"version": "1.0.2",
|
||||
|
@ -1811,16 +1929,16 @@
|
|||
},
|
||||
{
|
||||
"name": "sebastian/exporter",
|
||||
"version": "3.1.3",
|
||||
"version": "3.1.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/exporter.git",
|
||||
"reference": "6b853149eab67d4da22291d36f5b0631c0fd856e"
|
||||
"reference": "0c32ea2e40dbf59de29f3b49bf375176ce7dd8db"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/6b853149eab67d4da22291d36f5b0631c0fd856e",
|
||||
"reference": "6b853149eab67d4da22291d36f5b0631c0fd856e",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/0c32ea2e40dbf59de29f3b49bf375176ce7dd8db",
|
||||
"reference": "0c32ea2e40dbf59de29f3b49bf375176ce7dd8db",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -1829,7 +1947,7 @@
|
|||
},
|
||||
"require-dev": {
|
||||
"ext-mbstring": "*",
|
||||
"phpunit/phpunit": "^6.0"
|
||||
"phpunit/phpunit": "^8.5"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
|
@ -1876,7 +1994,7 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/exporter/issues",
|
||||
"source": "https://github.com/sebastianbergmann/exporter/tree/3.1.3"
|
||||
"source": "https://github.com/sebastianbergmann/exporter/tree/3.1.4"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
@ -1884,7 +2002,7 @@
|
|||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2020-11-30T07:47:53+00:00"
|
||||
"time": "2021-11-11T13:51:24+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sebastian/global-state",
|
||||
|
@ -2209,6 +2327,158 @@
|
|||
},
|
||||
"time": "2016-10-03T07:35:21+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/console",
|
||||
"version": "v3.4.47",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/console.git",
|
||||
"reference": "a10b1da6fc93080c180bba7219b5ff5b7518fe81"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/console/zipball/a10b1da6fc93080c180bba7219b5ff5b7518fe81",
|
||||
"reference": "a10b1da6fc93080c180bba7219b5ff5b7518fe81",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^5.5.9|>=7.0.8",
|
||||
"symfony/debug": "~2.8|~3.0|~4.0",
|
||||
"symfony/polyfill-mbstring": "~1.0"
|
||||
},
|
||||
"conflict": {
|
||||
"symfony/dependency-injection": "<3.4",
|
||||
"symfony/process": "<3.3"
|
||||
},
|
||||
"provide": {
|
||||
"psr/log-implementation": "1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"psr/log": "~1.0",
|
||||
"symfony/config": "~3.3|~4.0",
|
||||
"symfony/dependency-injection": "~3.4|~4.0",
|
||||
"symfony/event-dispatcher": "~2.8|~3.0|~4.0",
|
||||
"symfony/lock": "~3.4|~4.0",
|
||||
"symfony/process": "~3.3|~4.0"
|
||||
},
|
||||
"suggest": {
|
||||
"psr/log": "For using the console logger",
|
||||
"symfony/event-dispatcher": "",
|
||||
"symfony/lock": "",
|
||||
"symfony/process": ""
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Symfony\\Component\\Console\\": ""
|
||||
},
|
||||
"exclude-from-classmap": [
|
||||
"/Tests/"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Fabien Potencier",
|
||||
"email": "fabien@symfony.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Symfony Console Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/console/tree/v3.4.47"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://symfony.com/sponsor",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2020-10-24T10:57:07+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/debug",
|
||||
"version": "v3.4.47",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/debug.git",
|
||||
"reference": "ab42889de57fdfcfcc0759ab102e2fd4ea72dcae"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/debug/zipball/ab42889de57fdfcfcc0759ab102e2fd4ea72dcae",
|
||||
"reference": "ab42889de57fdfcfcc0759ab102e2fd4ea72dcae",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^5.5.9|>=7.0.8",
|
||||
"psr/log": "~1.0"
|
||||
},
|
||||
"conflict": {
|
||||
"symfony/http-kernel": ">=2.3,<2.3.24|~2.4.0|>=2.5,<2.5.9|>=2.6,<2.6.2"
|
||||
},
|
||||
"require-dev": {
|
||||
"symfony/http-kernel": "~2.8|~3.0|~4.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Symfony\\Component\\Debug\\": ""
|
||||
},
|
||||
"exclude-from-classmap": [
|
||||
"/Tests/"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Fabien Potencier",
|
||||
"email": "fabien@symfony.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Symfony Debug Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/debug/tree/v3.4.47"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://symfony.com/sponsor",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2020-10-24T10:57:07+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-ctype",
|
||||
"version": "v1.19.0",
|
||||
|
@ -2288,6 +2558,147 @@
|
|||
],
|
||||
"time": "2020-10-23T09:01:57+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-mbstring",
|
||||
"version": "v1.19.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/polyfill-mbstring.git",
|
||||
"reference": "b5f7b932ee6fa802fc792eabd77c4c88084517ce"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/b5f7b932ee6fa802fc792eabd77c4c88084517ce",
|
||||
"reference": "b5f7b932ee6fa802fc792eabd77c4c88084517ce",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.3"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-mbstring": "For best performance"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "1.19-dev"
|
||||
},
|
||||
"thanks": {
|
||||
"name": "symfony/polyfill",
|
||||
"url": "https://github.com/symfony/polyfill"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Symfony\\Polyfill\\Mbstring\\": ""
|
||||
},
|
||||
"files": [
|
||||
"bootstrap.php"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Nicolas Grekas",
|
||||
"email": "p@tchwork.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Symfony polyfill for the Mbstring extension",
|
||||
"homepage": "https://symfony.com",
|
||||
"keywords": [
|
||||
"compatibility",
|
||||
"mbstring",
|
||||
"polyfill",
|
||||
"portable",
|
||||
"shim"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.19.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://symfony.com/sponsor",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2020-10-23T09:01:57+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/process",
|
||||
"version": "v3.4.47",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/process.git",
|
||||
"reference": "b8648cf1d5af12a44a51d07ef9bf980921f15fca"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/process/zipball/b8648cf1d5af12a44a51d07ef9bf980921f15fca",
|
||||
"reference": "b8648cf1d5af12a44a51d07ef9bf980921f15fca",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^5.5.9|>=7.0.8"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Symfony\\Component\\Process\\": ""
|
||||
},
|
||||
"exclude-from-classmap": [
|
||||
"/Tests/"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Fabien Potencier",
|
||||
"email": "fabien@symfony.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Symfony Process Component",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/process/tree/v3.4.47"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://symfony.com/sponsor",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2020-10-24T10:57:07+00:00"
|
||||
},
|
||||
{
|
||||
"name": "theseer/tokenizer",
|
||||
"version": "1.1.3",
|
||||
|
@ -2385,6 +2796,61 @@
|
|||
},
|
||||
"time": "2020-07-08T17:02:28+00:00"
|
||||
},
|
||||
{
|
||||
"name": "wikimedia/at-ease",
|
||||
"version": "v2.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/wikimedia/at-ease.git",
|
||||
"reference": "013ac61929797839c80a111a3f1a4710d8248e7a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/wikimedia/at-ease/zipball/013ac61929797839c80a111a3f1a4710d8248e7a",
|
||||
"reference": "013ac61929797839c80a111a3f1a4710d8248e7a",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.6.99"
|
||||
},
|
||||
"require-dev": {
|
||||
"jakub-onderka/php-console-highlighter": "0.3.2",
|
||||
"jakub-onderka/php-parallel-lint": "1.0.0",
|
||||
"mediawiki/mediawiki-codesniffer": "22.0.0",
|
||||
"mediawiki/minus-x": "0.3.1",
|
||||
"ockcyp/covers-validator": "0.5.1 || 0.6.1",
|
||||
"phpunit/phpunit": "4.8.36 || ^6.5"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Wikimedia\\AtEase\\": "src/Wikimedia/AtEase/"
|
||||
},
|
||||
"files": [
|
||||
"src/Wikimedia/Functions.php"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"GPL-2.0-or-later"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Tim Starling",
|
||||
"email": "tstarling@wikimedia.org"
|
||||
},
|
||||
{
|
||||
"name": "MediaWiki developers",
|
||||
"email": "wikitech-l@lists.wikimedia.org"
|
||||
}
|
||||
],
|
||||
"description": "Safe replacement to @ for suppressing warnings.",
|
||||
"homepage": "https://www.mediawiki.org/wiki/at-ease",
|
||||
"support": {
|
||||
"source": "https://github.com/wikimedia/at-ease/tree/master"
|
||||
},
|
||||
"time": "2018-10-10T15:39:06+00:00"
|
||||
},
|
||||
{
|
||||
"name": "yoast/phpunit-polyfills",
|
||||
"version": "1.0.2",
|
||||
|
@ -2457,7 +2923,7 @@
|
|||
},
|
||||
"platform-dev": [],
|
||||
"platform-overrides": {
|
||||
"php": "7.0"
|
||||
"php": "7.0.33"
|
||||
},
|
||||
"plugin-api-version": "2.0.0"
|
||||
"plugin-api-version": "2.1.0"
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue