diff --git a/package.json b/package.json index 3e81a6b9cc8..86982425a65 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "docker:down": "npx wc-e2e docker:down", "docker:ssh": "npx wc-e2e docker:ssh", "docker:up": "npx wc-e2e docker:up", + "test:api": "cd ./tests/e2e/api-core-tests && jest --group=api", "test:e2e": "npx wc-e2e test:e2e", "test:e2e-debug": "npx wc-e2e test:e2e-debug", "test:e2e-dev": "npx wc-e2e test:e2e-dev", @@ -47,6 +48,7 @@ "@typescript-eslint/experimental-utils": "3.10.1", "@typescript-eslint/parser": "3.10.1", "@woocommerce/api": "file:tests/e2e/api", + "@woocommerce/api-core-tests": "file:tests/e2e/api-core-tests", "@woocommerce/e2e-core-tests": "file:tests/e2e/core-tests", "@woocommerce/e2e-environment": "file:tests/e2e/env", "@woocommerce/e2e-utils": "file:tests/e2e/utils", @@ -132,6 +134,7 @@ "no-e2e": { "exclude": [ "@woocommerce/api", + "@woocommerce/api-core-tests", "@woocommerce/e2e-core-tests", "@woocommerce/e2e-environment", "@woocommerce/e2e-utils", diff --git a/tests/e2e/api-core-tests/.env.example b/tests/e2e/api-core-tests/.env.example index 4b916049ed5..7f70ce7a99f 100644 --- a/tests/e2e/api-core-tests/.env.example +++ b/tests/e2e/api-core-tests/.env.example @@ -5,14 +5,14 @@ BASE_URL="" # The admin user's username or generated consumer key -# USERNAME="admin" -# USERNAME="ck_1234" -USERNAME="" +# USER_KEY="admin" +# USER_KEY="ck_1234" +USER_KEY="" # The admin user's password or generated consumer secret -# PASSWORD="password" -# PASSWORD="cs_1234" -PASSWORD="" +# USER_SECRET="password" +# USER_SECRET="cs_1234" +USER_SECRET="" # Optional setting to output verbose logs from Jest # VERBOSE=true diff --git a/tests/e2e/api-core-tests/.gitignore b/tests/e2e/api-core-tests/.gitignore index 242bb19030a..e0031849ea1 100644 --- a/tests/e2e/api-core-tests/.gitignore +++ b/tests/e2e/api-core-tests/.gitignore @@ -1,22 +1,3 @@ -# Editors -project.xml -project.properties -/nbproject/private/ -.buildpath -.project -.settings* -.idea -.vscode -*.sublime-project -*.sublime-workspace -.sublimelinterrc - -# OS X metadata -.DS_Store - -# Windows thumbnail cache -Thumbs.db - # Node modules node_modules/ diff --git a/tests/e2e/api-core-tests/README.md b/tests/e2e/api-core-tests/README.md index 3cbab98c0d4..694e7e9a134 100644 --- a/tests/e2e/api-core-tests/README.md +++ b/tests/e2e/api-core-tests/README.md @@ -11,14 +11,20 @@ Before running the tests, the following environment variables need to be configu BASE_URL="https://mysite.com" # The admin user's username or generated consumer key -USERNAME="" +USER_KEY="" # The admin user's password or generated consumer secret -PASSWORD="" +USER_SECRET="" ``` For local setup, create a `.env` file in this folder with the three required values described above. +Alternatively, these values can be passed in via the command line. For example: + +```shell +BASE_URL=http://localhost:8084 USER_KEY=admin USER_SECRET=password npm run test:api +``` + When using a username and password combination instead of a consumer secret and consumer key, make sure to have the [JSON Basic Authentication plugin](https://github.com/WP-API/Basic-Auth) installed and activated on the test site. For more information about authentication with the WooCommerce API, please see the [Authentication](https://woocommerce.github.io/woocommerce-rest-api-docs/?javascript#authentication) section in the WooCommerce REST API documentation. diff --git a/tests/e2e/api-core-tests/data/coupon.js b/tests/e2e/api-core-tests/data/coupon.js index c7f9e3c20b9..6104d22448b 100644 --- a/tests/e2e/api-core-tests/data/coupon.js +++ b/tests/e2e/api-core-tests/data/coupon.js @@ -25,9 +25,9 @@ const coupon = { exclude_sale_items: false, minimum_amount: '100.00', maximum_amount: '', - email_restrictions: [] -} + email_restrictions: [], +}; module.exports = { - coupon -} + coupon, +}; diff --git a/tests/e2e/api-core-tests/data/index.js b/tests/e2e/api-core-tests/data/index.js index edee2939e8b..d56e40746de 100644 --- a/tests/e2e/api-core-tests/data/index.js +++ b/tests/e2e/api-core-tests/data/index.js @@ -1,9 +1,9 @@ -const order = require('./order'); -const coupon = require('./coupon'); +const { order } = require('./order'); +const { coupon } = require('./coupon'); const shared = require('./shared'); module.exports = { order, coupon, shared, -} +}; diff --git a/tests/e2e/api-core-tests/data/order.js b/tests/e2e/api-core-tests/data/order.js index 286abccf19a..812c1975320 100644 --- a/tests/e2e/api-core-tests/data/order.js +++ b/tests/e2e/api-core-tests/data/order.js @@ -7,34 +7,6 @@ const { customerBilling, customerShipping } = require('./shared'); * * https://woocommerce.github.io/woocommerce-rest-api-docs/#order-properties */ - -const productLineItems = { - name: '', - product_id: '', - variation_id: 0, - quantity: 0, - tax_class: '', - subtotal: '', - total: '', -} - -const shippingLines = { - method_title: '', - method_id: '', - total: '', -} - -const feeLines = { - name: '', - tax_class: '', - tax_status: '', - total: '', -} - -const couponLines = { - code: '' -} - const order = { payment_method: '', payment_method_title: '', @@ -45,19 +17,38 @@ const order = { customer_id: 0, billing: customerBilling, shipping: customerShipping, - line_items: [ - productLineItems - ], - shipping_lines: [ - shippingLines - ], - fee_lines: [ - feeLines - ], - coupon_lines: [ - couponLines - ] -} + line_items: [], + shipping_lines: [], + fee_lines: [], + coupon_lines: [], +}; + +const productLineItems = { + name: '', + product_id: '', + variation_id: 0, + quantity: 0, + tax_class: '', + subtotal: '', + total: '', +}; + +const shippingLines = { + method_title: '', + method_id: 'flat_rate', + total: '', +}; + +const feeLines = { + name: 'Fee', + tax_class: '', + tax_status: 'none', + total: '', +}; + +const couponLines = { + code: '10off', +}; module.exports = { order, @@ -65,4 +56,4 @@ module.exports = { shippingLines, feeLines, couponLines, -} +}; diff --git a/tests/e2e/api-core-tests/data/shared/batch-update.js b/tests/e2e/api-core-tests/data/shared/batch-update.js index 8c85e0e6213..bb44e62f0a6 100644 --- a/tests/e2e/api-core-tests/data/shared/batch-update.js +++ b/tests/e2e/api-core-tests/data/shared/batch-update.js @@ -1,87 +1,46 @@ /** * Shared model for batch updates for a resource. * - * Note that by default the update is limited to 100 objects to be created, updated, or deleted. - */ -const batchUpdatePayload = { - create: [], - update: [], - delete: [], -} - -/** - * Batch create a resource. + * Note that by default the update endpoint is limited to 100 objects to be created, updated, or deleted. * - * @param {Array} resourcesToCreate A list of resource objects to create. - * @returns + * @param {string} action Batch action. Must be one of: create, update, or delete. + * @param {Array} resources A list of resource objects. For the delete action, this will be a list of IDs. + * @param {Object} payload The batch payload object. Defaults to an empty object. + * @returns {Object} The payload to send to the batch endpoint. */ -const batchCreate = ( resourcesToCreate = [] ) => { - if ( resourcesToCreate.length === 0 ) { + const batch = ( action, resources = [], payload = {} ) => { + if ( ! [ 'create', 'update', 'delete' ].includes( action ) ) { return; } - // Build array of resources to create - const createArray = []; - resourcesToCreate.forEach( ( resource ) => { - createArray.push( resource ); - }); - - batchUpdatePayload.create = createArray - - return createArray; -} - -/** - * Batch update resources. - * - * @param {Array} resourcesToUpdate A list of resource objects to update. - * @returns - */ -const batchUpdate = ( resourcesToUpdate = [] ) => { - if ( resourcesToUpdate.length === 0 ) { - return - } - - // Build array of resources to update - const updateArray = []; - resourcesToUpdate.forEach( ( resource ) => { - updateArray.push( resource ); - }); - - return updateArray; -} - -/** - * Batch delete resources. - * - * @param {Array} resourceIds A list of IDs of resource objects to delete. - * @returns - */ -const batchDelete = ( resourceIds = [] ) => { - if ( resourceIds.length === 0 ) { + if ( resources.length === 0 ) { return; } - // Build array of resources to delete - const deleteArray = []; - resourceIds.forEach( ( id ) => { - deleteArray.push( id ); - }); + if ( action === 'create' ) { + payload.create = [ ...resources ]; + } - return deleteArray; -} + if ( action === 'update' ) { + payload.update = [ ...resources ]; + } + + if ( action === 'delete' ) { + payload.delete = [ ...resources ]; + } + + return payload; +}; const getBatchPayloadExample = ( resource ) => { - batchUpdatePayload.create = batchCreate( [ resource ] ); - batchUpdatePayload.update = batchUpdate( [ resource ] ); - batchUpdatePayload.delete = batchDelete( [ 1, 2, 3 ] ); + let batchUpdatePayload = {}; + batchUpdatePayload = batch( 'create', [ resource ], batchUpdatePayload ); + batchUpdatePayload = batch( 'update', [ resource ], batchUpdatePayload ); + batchUpdatePayload = batch( 'delete', [ 1, 2, 3 ], batchUpdatePayload ); return batchUpdatePayload; -} +}; module.exports = { - batchUpdatePayload, - batchCreate, - batchUpdate, - batchDelete, - getBatchPayloadExample -} + batch, + getBatchPayloadExample, +}; diff --git a/tests/e2e/api-core-tests/data/shared/customer.js b/tests/e2e/api-core-tests/data/shared/customer.js index 366e3cc3acd..1c49bd797d7 100644 --- a/tests/e2e/api-core-tests/data/shared/customer.js +++ b/tests/e2e/api-core-tests/data/shared/customer.js @@ -17,7 +17,7 @@ const customerBilling = { postcode: '94107', phone: '123456789', email: 'john.doe@example.com', -} +}; /** * Customer shipping object. @@ -37,9 +37,9 @@ const customerShipping = { state: 'NY', postcode: '14201', phone: '123456789', -} +}; module.exports = { customerBilling, - customerShipping -} + customerShipping, +}; diff --git a/tests/e2e/api-core-tests/data/shared/error-response.js b/tests/e2e/api-core-tests/data/shared/error-response.js index 370925deca6..e83836eab1b 100644 --- a/tests/e2e/api-core-tests/data/shared/error-response.js +++ b/tests/e2e/api-core-tests/data/shared/error-response.js @@ -8,5 +8,7 @@ const errorResponse = { message: '', data: { status: 400 - } -} + }, +}; + +module.exports = { errorResponse }; diff --git a/tests/e2e/api-core-tests/data/shared/index.js b/tests/e2e/api-core-tests/data/shared/index.js index 3e571c25b1e..9947340a9e1 100644 --- a/tests/e2e/api-core-tests/data/shared/index.js +++ b/tests/e2e/api-core-tests/data/shared/index.js @@ -1,20 +1,14 @@ const { customerBilling, customerShipping } = require('./customer'); const { - batchUpdatePayload, - batchCreate, - batchUpdate, - batchDelete, + batch, getBatchPayloadExample } = require('./batch-update'); -const errorRessponse = require('./customer'); +const { errorResponse } = require('./error-response'); module.exports = { customerBilling, customerShipping, - batchUpdatePayload, - batchCreate, - batchUpdate, - batchDelete, + batch, getBatchPayloadExample, - errorRessponse -} + errorResponse, +}; diff --git a/tests/e2e/api-core-tests/endpoints/coupons.js b/tests/e2e/api-core-tests/endpoints/coupons.js index 587f5047e02..db66d3e989e 100644 --- a/tests/e2e/api-core-tests/endpoints/coupons.js +++ b/tests/e2e/api-core-tests/endpoints/coupons.js @@ -17,21 +17,21 @@ const couponsApi = { path: 'coupons', responseCode: 201, payload: coupon, - coupon: async ( couponDetails ) => postRequest( 'coupons', couponDetails ) + coupon: async ( couponDetails ) => postRequest( 'coupons', couponDetails ), }, retrieve: { name: 'Retrieve a coupon', method: 'GET', path: 'coupons/', responseCode: 200, - coupon: async ( couponId ) => getRequest( `coupons/${couponId}` ) + coupon: async ( couponId ) => getRequest( `coupons/${couponId}` ), }, listAll: { name: 'List all coupons', method: 'GET', path: 'coupons', responseCode: 200, - coupons: async () => { getRequest( 'coupons' ) } + coupons: async () => { getRequest( 'coupons' ) }, }, update: { name: 'Update a coupon', @@ -39,7 +39,7 @@ const couponsApi = { path: 'coupons/', responseCode: 200, payload: coupon, - coupon: async ( couponId, couponDetails ) => putRequest( `coupons/${couponId}`, couponDetails ) + coupon: async ( couponId, couponDetails ) => putRequest( `coupons/${couponId}`, couponDetails ), }, delete: { name: 'Delete a coupon', @@ -49,7 +49,7 @@ const couponsApi = { payload: { force: false }, - coupon: async ( couponId, deletePermanently ) => deleteRequest( `coupons/${couponId}`, deletePermanently ) + coupon: async ( couponId, deletePermanently ) => deleteRequest( `coupons/${couponId}`, deletePermanently ), }, batch: { name: 'Batch update coupons', @@ -57,8 +57,8 @@ const couponsApi = { path: 'coupons/batch', responseCode: 200, payload: shared.getBatchPayloadExample( coupon ), - coupons: async ( batchUpdatePayload ) => postRequest( `coupons/batch`, batchUpdatePayload ) + coupons: async ( batchUpdatePayload ) => postRequest( `coupons/batch`, batchUpdatePayload ), }, -} +}; - module.exports = { couponsApi } + module.exports = { couponsApi }; diff --git a/tests/e2e/api-core-tests/endpoints/index.js b/tests/e2e/api-core-tests/endpoints/index.js index 93d8a86bf55..859f9042e43 100644 --- a/tests/e2e/api-core-tests/endpoints/index.js +++ b/tests/e2e/api-core-tests/endpoints/index.js @@ -3,5 +3,5 @@ const { couponsApi } = require('./coupons'); module.exports = { ordersApi, - couponsApi -} + couponsApi, +}; diff --git a/tests/e2e/api-core-tests/endpoints/orders.js b/tests/e2e/api-core-tests/endpoints/orders.js index 0a7c23dace8..84e3663475c 100644 --- a/tests/e2e/api-core-tests/endpoints/orders.js +++ b/tests/e2e/api-core-tests/endpoints/orders.js @@ -17,29 +17,29 @@ const ordersApi = { path: 'orders', responseCode: 201, payload: order, - order: async ( orderDetails ) => postRequest( 'orders', orderDetails ) + order: async ( orderDetails ) => postRequest( 'orders', orderDetails ), }, retrieve: { name: 'Retrieve an order', method: 'GET', path: 'orders/', responseCode: 200, - order: async ( orderId ) => getRequest( `orders/${orderId}` ) + order: async ( orderId ) => getRequest( `orders/${orderId}` ), }, listAll: { name: 'List all orders', method: 'GET', path: 'orders', responseCode: 200, - orders: async () => { getRequest( 'orders' ) } + orders: async () => { getRequest( 'orders' ) }, }, update: { name: 'Update an order', method: 'PUT', path: 'orders/', responseCode: 200, - payload: order, - order: async ( orderId, orderDetails ) => putRequest( `orders/${orderId}`, orderDetails ) + payload: order.order, + order: async ( orderId, orderDetails ) => putRequest( `orders/${orderId}`, orderDetails ), }, delete: { name: 'Delete an order', @@ -49,18 +49,20 @@ const ordersApi = { payload: { force: false }, - order: async ( orderId, deletePermanently ) => deleteRequest( `orders/${orderId}`, deletePermanently ) + order: async ( orderId, deletePermanently ) => deleteRequest( `orders/${orderId}`, deletePermanently ), }, batch: { name: 'Batch update orders', method: 'POST', path: 'orders/batch', responseCode: 200, - payload: shared.getBatchPayloadExample( order ), - orders: async ( batchUpdatePayload ) => postRequest( `orders/batch`, batchUpdatePayload ) + payload: shared.getBatchPayloadExample( order.order ), + orders: async ( batchUpdatePayload ) => postRequest( `orders/batch`, batchUpdatePayload ), }, -} +}; + +// TODO -- build example payload here? Remove ID etc from the example and just add here? module.exports = { ordersApi, -} +}; diff --git a/tests/e2e/api-core-tests/jest.config.js b/tests/e2e/api-core-tests/jest.config.js index ab9626e82fd..704edcbb8e2 100644 --- a/tests/e2e/api-core-tests/jest.config.js +++ b/tests/e2e/api-core-tests/jest.config.js @@ -4,9 +4,9 @@ const verboseOutput = VERBOSE === 'true'; // Update the API path if the `USE_INDEX_PERMALINKS` flag is set const useIndexPermalinks = USE_INDEX_PERMALINKS === 'true'; -let apiPath = `${BASE_URL}/wp-json/wc/v3/`; -if (useIndexPermalinks) { - apiPath = `${BASE_URL}/?rest_route=/wc/v3/`; +let apiPath = `${BASE_URL}/?rest_route=/wc/v3/`; +if ( useIndexPermalinks ) { + apiPath = `${BASE_URL}/wp-json/wc/v3/`; } module.exports = { diff --git a/tests/e2e/api-core-tests/utils/api-collection/build-collection.js b/tests/e2e/api-core-tests/utils/api-collection/build-collection.js index 6fbcaf6d449..7cd90da228b 100644 --- a/tests/e2e/api-core-tests/utils/api-collection/build-collection.js +++ b/tests/e2e/api-core-tests/utils/api-collection/build-collection.js @@ -3,8 +3,8 @@ const { Collection, ItemGroup, Item } = require('postman-collection'); require('dotenv').config(); const { BASE_URL, - USERNAME, - PASSWORD, + USER_KEY, + USER_SECRET, USE_INDEX_PERMALINKS } = process.env; @@ -16,18 +16,25 @@ const { */ // Set up our empty collection +if ( typeof USER_KEY === 'undefined' ) { + console.log('No USER_KEY was defined.'); +} +if ( typeof USER_SECRET === 'undefined' ) { + console.log('No USER_SECRET was defined.'); +} + const postmanCollection = new Collection( { auth: { type: 'basic', basic: [ { key: 'username', - value: USERNAME, + value: USER_KEY, type: 'string' }, { key: 'password', - value: PASSWORD, + value: USER_SECRET, type: 'string' }, ] @@ -38,11 +45,15 @@ const postmanCollection = new Collection( { } ); // Get the API url +if ( typeof BASE_URL === 'undefined' ) { + console.log('No BASE_URL was defined.'); +} + // Update the API path if the `USE_INDEX_PERMALINKS` flag is set const useIndexPermalinks = ( USE_INDEX_PERMALINKS === 'true' ); -let apiPath = `${BASE_URL}/wp-json/wc/v3`; +let apiPath = `${BASE_URL}/?rest_route=/wc/v3/`; if ( useIndexPermalinks ) { - apiPath = `${BASE_URL}/?rest_route=/wc/v3`; + apiPath = `${BASE_URL}/wp-json/wc/v3/`; } // Set this here for use in `request.js` global.API_PATH = `${apiPath}/`; @@ -84,9 +95,7 @@ for ( const key in resources ) { mode: 'raw', raw: JSON.stringify( api.payload ), options: { - raw: { - language: 'json' - } + raw: { language: 'json' } } }, }, @@ -94,7 +103,7 @@ for ( const key in resources ) { folder.items.add( request ); } - postmanCollection.items.add( folder ) ; + postmanCollection.items.add( folder ); } // We need to convert the collection to JSON so that it can be exported to a file diff --git a/tests/e2e/api-core-tests/utils/request.js b/tests/e2e/api-core-tests/utils/request.js index 89f59785ba7..6dbbdd104d6 100644 --- a/tests/e2e/api-core-tests/utils/request.js +++ b/tests/e2e/api-core-tests/utils/request.js @@ -1,5 +1,5 @@ require('dotenv').config(); -const { USERNAME, PASSWORD } = process.env; +const { USER_KEY, USER_SECRET } = process.env; const request = require('supertest')( API_PATH ); /** @@ -14,9 +14,9 @@ const getRequest = async ( requestPath, queryString = {} ) => { .get( requestPath ) .set( 'Accept', 'application/json' ) .query( queryString ) - .auth( USERNAME, PASSWORD ); + .auth( USER_KEY, USER_SECRET ); return response; -} +}; /** * Make a POST request. @@ -30,9 +30,9 @@ const postRequest = async ( requestPath, requestBody ) => { .post( requestPath ) .send( requestBody ) .set( 'Accept', 'application/json' ) - .auth( USERNAME, PASSWORD ); + .auth( USER_KEY, USER_SECRET ); return response; -} +}; /** * Make a PUT request. @@ -46,9 +46,9 @@ const putRequest = async ( requestPath, requestBody ) => { .put( requestPath ) .send( requestBody ) .set( 'Accept', 'application/json' ) - .auth( USERNAME, PASSWORD ); + .auth( USER_KEY, USER_SECRET ); return response; -} +}; /** * Make a DELETE request, optionally deleting the resource permanently. @@ -63,8 +63,8 @@ const deleteRequest = async ( requestPath, deletePermanently = false ) => { .delete( requestPath ) .set( 'Accept', 'application/json' ) .send( requestBody ) - .auth( USERNAME, PASSWORD ); + .auth( USER_KEY, USER_SECRET ); return response; -} +}; module.exports = { getRequest, postRequest, putRequest, deleteRequest }