From c354a672923ccc51d4a12ecb11eb86480d12f1a0 Mon Sep 17 00:00:00 2001 From: Greg Date: Mon, 3 May 2021 20:21:00 -0600 Subject: [PATCH] Finish work on order; removed unused variable from test --- tests/e2e/api/src/models/index.ts | 1 + tests/e2e/api/src/models/orders/index.ts | 2 +- tests/e2e/api/src/models/orders/orders.ts | 59 ++-- .../api/src/models/orders/shared/classes.ts | 4 +- tests/e2e/api/src/repositories/rest/index.ts | 5 +- .../api/src/repositories/rest/orders/index.ts | 3 + .../api/src/repositories/rest/orders/order.ts | 47 +++ .../repositories/rest/orders/transformer.ts | 267 +++++++++++++++++- tests/e2e/config/default.json | 11 + tests/e2e/core-tests/specs/api/order.test.js | 82 ++++++ tests/e2e/core-tests/specs/index.js | 3 + .../merchant/wp-admin-order-emails.test.js | 1 - tests/e2e/specs/rest-api/order.js | 6 + 13 files changed, 451 insertions(+), 40 deletions(-) create mode 100644 tests/e2e/core-tests/specs/api/order.test.js create mode 100644 tests/e2e/specs/rest-api/order.js diff --git a/tests/e2e/api/src/models/index.ts b/tests/e2e/api/src/models/index.ts index 13ce569f196..902fdec58e3 100644 --- a/tests/e2e/api/src/models/index.ts +++ b/tests/e2e/api/src/models/index.ts @@ -3,3 +3,4 @@ export * from './model'; export * from './settings'; export * from './shared-types'; export * from './coupons'; +export * from './orders'; diff --git a/tests/e2e/api/src/models/orders/index.ts b/tests/e2e/api/src/models/orders/index.ts index de526a0af64..d4df1e6ca90 100644 --- a/tests/e2e/api/src/models/orders/index.ts +++ b/tests/e2e/api/src/models/orders/index.ts @@ -1,2 +1,2 @@ -export * from './shared'; export * from './orders'; +export * from './shared'; diff --git a/tests/e2e/api/src/models/orders/orders.ts b/tests/e2e/api/src/models/orders/orders.ts index 04fd8a93282..d23c4ca7611 100644 --- a/tests/e2e/api/src/models/orders/orders.ts +++ b/tests/e2e/api/src/models/orders/orders.ts @@ -1,5 +1,5 @@ -//import { HTTPClient } from '../../http'; -//import { orderRESTRepository } from '../../repositories'; +import { HTTPClient } from '../../http'; +import { orderRESTRepository } from '../../repositories'; import { ModelRepositoryParams, CreatesModels, @@ -50,47 +50,47 @@ type OrderUpdateParams = OrderAddressUpdateParams export type OrderRepositoryParams = ModelRepositoryParams< Order, never, never, OrderUpdateParams >; /** - * An interface for creating coupons using the repository. + * An interface for creating orders using the repository. * - * @typedef CreatesCoupons - * @alias CreatesModels. + * @typedef CreatesOrders + * @alias CreatesModels. */ -export type CreatesCoupons = CreatesModels< OrderRepositoryParams >; +export type CreatesOrders = CreatesModels< OrderRepositoryParams >; /** - * An interface for reading coupons using the repository. + * An interface for reading orders using the repository. * - * @typedef ReadsCoupons - * @alias ReadsModels. + * @typedef ReadsOrders + * @alias ReadsModels. */ -export type ReadsCoupons = ReadsModels< OrderRepositoryParams >; +export type ReadsOrders = ReadsModels< OrderRepositoryParams >; /** - * An interface for updating coupons using the repository. + * An interface for updating orders using the repository. * - * @typedef UpdatesCoupons - * @alias UpdatesModels. + * @typedef UpdatesOrders + * @alias UpdatesModels. */ -export type UpdatesCoupons = UpdatesModels< OrderRepositoryParams >; +export type UpdatesOrders = UpdatesModels< OrderRepositoryParams >; /** - * An interface for listing coupons using the repository. + * An interface for listing orders using the repository. * - * @typedef ListsCoupons - * @alias ListsModels. + * @typedef ListsOrders + * @alias ListsModels. */ -export type ListsCoupons = ListsModels< OrderRepositoryParams >; +export type ListsOrders = ListsModels< OrderRepositoryParams >; /** - * An interface for deleting coupons using the repository. + * An interface for deleting orders using the repository. * - * @typedef DeletesCoupons - * @alias DeletesModels. + * @typedef DeletesOrders + * @alias DeletesModels. */ -export type DeletesCoupons = DeletesModels< OrderRepositoryParams >; +export type DeletesOrders = DeletesModels< OrderRepositoryParams >; /** - * A coupon object. + * An order object. */ export class Order extends OrderItemMeta { /** @@ -289,6 +289,13 @@ export class Order extends OrderItemMeta { */ public readonly currencySymbol: string = ''; + /** + * The order's paid state. + * + * @type {boolean} + */ + public readonly setPaid: boolean = false; + /** * The order's line items. * @@ -342,7 +349,7 @@ export class Order extends OrderItemMeta { }; /** - * Creates a new coupon instance with the given properties + * Creates a new order instance with the given properties * * @param {Object} properties The properties to set in the object. */ @@ -350,15 +357,13 @@ export class Order extends OrderItemMeta { super(); Object.assign( this, properties ); } - /* + /** * Returns the repository for interacting with this type of model. * * @param {HTTPClient} httpClient The client for communicating via HTTP. */ - /* public static restRepository( httpClient: HTTPClient ): ReturnType< typeof orderRESTRepository > { return orderRESTRepository( httpClient ); } - */ } diff --git a/tests/e2e/api/src/models/orders/shared/classes.ts b/tests/e2e/api/src/models/orders/shared/classes.ts index f26923edcd5..540c543d4ae 100644 --- a/tests/e2e/api/src/models/orders/shared/classes.ts +++ b/tests/e2e/api/src/models/orders/shared/classes.ts @@ -36,7 +36,7 @@ export class OrderItemTax extends Model { /** * An order address. */ -export class OrderAddress { +export class OrderAddress extends Model { /** * The first name of the person in the address. * @@ -131,7 +131,7 @@ export class OrderLineItem extends OrderItemMeta { * * @type {number} */ - public readonly ProductId: number = -1; + public readonly productId: number = -1; /** * The ID of the product variation. diff --git a/tests/e2e/api/src/repositories/rest/index.ts b/tests/e2e/api/src/repositories/rest/index.ts index 15f18f10ee0..7ae5c9a38a2 100644 --- a/tests/e2e/api/src/repositories/rest/index.ts +++ b/tests/e2e/api/src/repositories/rest/index.ts @@ -1,3 +1,4 @@ -export * from './products'; -export * from './settings'; export * from './coupons'; +export * from './products'; +export * from './orders'; +export * from './settings'; diff --git a/tests/e2e/api/src/repositories/rest/orders/index.ts b/tests/e2e/api/src/repositories/rest/orders/index.ts index e69de29bb2d..fa4dd50d134 100644 --- a/tests/e2e/api/src/repositories/rest/orders/index.ts +++ b/tests/e2e/api/src/repositories/rest/orders/index.ts @@ -0,0 +1,3 @@ +import orderRESTRepository from './order'; + +export { orderRESTRepository }; diff --git a/tests/e2e/api/src/repositories/rest/orders/order.ts b/tests/e2e/api/src/repositories/rest/orders/order.ts index e69de29bb2d..76e29525982 100644 --- a/tests/e2e/api/src/repositories/rest/orders/order.ts +++ b/tests/e2e/api/src/repositories/rest/orders/order.ts @@ -0,0 +1,47 @@ +import { HTTPClient } from '../../../http'; +import { + ModelRepository, +} from '../../../framework'; +import { + ModelID, + Order, + OrderRepositoryParams, + ListsOrders, + ReadsOrders, + UpdatesOrders, + CreatesOrders, + DeletesOrders, +} from '../../../models'; + +import { + restList, + restCreate, + restRead, + restUpdate, + restDelete, +} from '../shared'; + +import { createOrderTransformer } from './transformer'; +/** + * + * @param {HTTPClient} httpClient The HTTP client for the REST requests to be made using. + */ +export default function orderRESTRepository( httpClient: HTTPClient ): CreatesOrders +& ListsOrders +& ReadsOrders +& UpdatesOrders +& DeletesOrders { + const buildURL = ( id: ModelID ) => '/wc/v3/orders/' + id; + // Using `?force=true` permanently deletes the order + const buildDeleteUrl = ( id: ModelID ) => `/wc/v3/orders/${ id }?force=true`; + + const transformer = createOrderTransformer(); + + return new ModelRepository( + restList< OrderRepositoryParams >( () => '/wc/v3/orders', Order, httpClient, transformer ), + restCreate< OrderRepositoryParams >( () => '/wc/v3/orders', Order, httpClient, transformer ), + restRead< OrderRepositoryParams >( buildURL, Order, httpClient, transformer ), + restUpdate< OrderRepositoryParams >( buildURL, Order, httpClient, transformer ), + restDelete< OrderRepositoryParams >( buildDeleteUrl, httpClient ), + ); +} diff --git a/tests/e2e/api/src/repositories/rest/orders/transformer.ts b/tests/e2e/api/src/repositories/rest/orders/transformer.ts index 41452a2ce26..5cd3c0e2b6f 100644 --- a/tests/e2e/api/src/repositories/rest/orders/transformer.ts +++ b/tests/e2e/api/src/repositories/rest/orders/transformer.ts @@ -1,24 +1,277 @@ import { - AddPropertyTransformation, - CustomTransformation, IgnorePropertyTransformation, KeyChangeTransformation, - ModelTransformation, ModelTransformer, ModelTransformerTransformation, PropertyType, PropertyTypeTransformation, - TransformationOrder, } from '../../../framework'; import { + Order, OrderAddress, OrderCouponLine, OrderFeeLine, - OrderItemTax, OrderLineItem, OrderRefundLine, OrderShippingLine, - OrderStatus, OrderTaxRate, } from '../../../models'; -import { createMetaDataTransformer } from '../shared'; + +/** + * Creates a transformer for an order object. + * + * @return {ModelTransformer} The created transformer. + */ +export function createOrderTransformer(): ModelTransformer< Order > { + return new ModelTransformer( + [ + new IgnorePropertyTransformation( [ 'date_created', 'date_modified' ] ), + new ModelTransformerTransformation( 'billing', OrderAddress, createOrderAddressTransformer() ), + new ModelTransformerTransformation( 'tax_lines', OrderTaxRate, createOrderTaxRateTransformer() ), + new ModelTransformerTransformation( 'refunds', OrderRefundLine, createOrderRefundLineTransformer() ), + new ModelTransformerTransformation( 'coupon_lines', OrderCouponLine, createOrdeCouponLineTransformer() ), + new ModelTransformerTransformation( 'fee_lines', OrderFeeLine, createOrderFeeLineTransformer() ), + new ModelTransformerTransformation( 'line_items', OrderLineItem, createOrderLineItemTransformer() ), + new ModelTransformerTransformation( 'shipping_lines', OrderShippingLine, createOrderShippingItemTransformer() ), + + new PropertyTypeTransformation( + { + status: PropertyType.String, + currency: PropertyType.String, + discountTotal: PropertyType.String, + discountTax: PropertyType.String, + shippingTotal: PropertyType.String, + shippingTax: PropertyType.String, + cartTax: PropertyType.String, + total: PropertyType.String, + totalTax: PropertyType.String, + pricesIncludeTax: PropertyType.Boolean, + customerId: PropertyType.Integer, + customerNote: PropertyType.String, + paymentMethod: PropertyType.String, + transactionId: PropertyType.String, + setPaid: PropertyType.Boolean, + }, + ), + new KeyChangeTransformation< Order >( + { + discountTotal: 'discount_total', + discountTax: 'discount_tax', + shippingTotal: 'shipping_total', + shippingTax: 'shipping_tax', + cartTax: 'cart_tax', + totalTax: 'total_tax', + pricesIncludeTax: 'prices_include_tax', + customerId: 'customer_id', + customerNote: 'customer_note', + paymentMethod: 'payment_method', + transactionId: 'transaction_id', + setPaid: 'set_paid', + }, + ), + ], + ); +} + +/** + * Creates a transformer for an order address object. + * + * @return {ModelTransformer} The created transformer. + */ +export function createOrderAddressTransformer(): ModelTransformer< OrderAddress > { + return new ModelTransformer( + [ + new PropertyTypeTransformation( + { + firstName: PropertyType.String, + lastName: PropertyType.String, + company: PropertyType.String, + address1: PropertyType.String, + address2: PropertyType.String, + city: PropertyType.String, + state: PropertyType.String, + postCode: PropertyType.String, + country: PropertyType.String, + }, + ), + new KeyChangeTransformation< OrderAddress >( + { + firstName: 'first_name', + lastName: 'last_name', + address1: 'address_1', + address2: 'address_2', + postCode: 'postcode', + }, + ), + ], + ); +} + +/** + * Creates a transformer for an order tax rate object. + * + * @return {ModelTransformer} The created transformer. + */ +function createOrderTaxRateTransformer(): ModelTransformer< OrderTaxRate > { + return new ModelTransformer( + [ + new PropertyTypeTransformation( + { + rateCode: PropertyType.String, + rateId: PropertyType.Integer, + label: PropertyType.String, + compoundRate: PropertyType.Boolean, + taxTotal: PropertyType.String, + shippingTaxTotal: PropertyType.String, + ratePercent: PropertyType.Integer, + }, + ), + new KeyChangeTransformation< OrderTaxRate >( + { + rateCode: 'rate_code', + rateId: 'rate_id', + compoundRate: 'compound', + taxTotal: 'tax_total', + shippingTaxTotal: 'shipping_tax_total', + }, + ), + ], + ); +} + +/** + * Creates a transformer for an order refund line object. + * + * @return {ModelTransformer} The created transformer. + */ +function createOrderRefundLineTransformer(): ModelTransformer< OrderRefundLine > { + return new ModelTransformer( + [ + new PropertyTypeTransformation( + { + reason: PropertyType.String, + total: PropertyType.String, + }, + ), + ], + ); +} + +/** + * Creates a transformer for an order coupon line object. + * + * @return {ModelTransformer} The created transformer. + */ +function createOrdeCouponLineTransformer(): ModelTransformer< OrderCouponLine > { + return new ModelTransformer( + [ + new PropertyTypeTransformation( + { + code: PropertyType.String, + discount: PropertyType.Integer, + discountTax: PropertyType.String, + }, + ), + new KeyChangeTransformation< OrderCouponLine >( + { + discountTax: 'discount_tax', + }, + ), + ], + ); +} + +/** + * Creates a transformer for an order fee line object. + * + * @return {ModelTransformer} The created transformer. + */ +function createOrderFeeLineTransformer(): ModelTransformer< OrderFeeLine > { + return new ModelTransformer( + [ + new ModelTransformerTransformation( 'taxes', OrderTaxRate, createOrderTaxRateTransformer() ), + new PropertyTypeTransformation( + { + name: PropertyType.String, + taxClass: PropertyType.String, + taxStatus: PropertyType.String, + total: PropertyType.String, + totalTax: PropertyType.String, + }, + ), + new KeyChangeTransformation< OrderFeeLine >( + { + taxClass: 'tax_class', + taxStatus: 'tax_status', + totalTax: 'total_tax', + }, + ), + ], + ); +} + +/** + * Creates a transformer for an order line item object. + * + * @return {ModelTransformer} The created transformer. + */ +function createOrderLineItemTransformer(): ModelTransformer< OrderLineItem > { + return new ModelTransformer( + [ + new ModelTransformerTransformation( 'taxes', OrderTaxRate, createOrderTaxRateTransformer() ), + new PropertyTypeTransformation( + { + name: PropertyType.String, + productId: PropertyType.Integer, + variationId: PropertyType.Integer, + quantity: PropertyType.Integer, + taxClass: PropertyType.String, + subtotal: PropertyType.String, + subtotalTax: PropertyType.String, + total: PropertyType.String, + totalTax: PropertyType.String, + sku: PropertyType.String, + price: PropertyType.Integer, + parentName: PropertyType.String, + }, + ), + new KeyChangeTransformation< OrderLineItem >( + { + productId: 'product_id', + variationId: 'variation_id', + taxClass: 'tax_class', + subtotalTax: 'subtotal_tax', + totalTax: 'total_tax', + }, + ), + ], + ); +} + +/** + * Creates a transformer for an order shipping line item object. + * + * @return {ModelTransformer} The created transformer. + */ +function createOrderShippingItemTransformer(): ModelTransformer< OrderShippingLine > { + return new ModelTransformer( + [ + new ModelTransformerTransformation( 'taxes', OrderTaxRate, createOrderTaxRateTransformer() ), + new PropertyTypeTransformation( + { + methodTitle: PropertyType.String, + methodId: PropertyType.String, + total: PropertyType.String, + totalTax: PropertyType.String, + }, + ), + new KeyChangeTransformation< OrderShippingLine >( + { + methodTitle: 'method_title', + methodId: 'method_id', + totalTax: 'total_tax', + }, + ), + ], + ); +} diff --git a/tests/e2e/config/default.json b/tests/e2e/config/default.json index 4d6b397aac2..adb8538915e 100644 --- a/tests/e2e/config/default.json +++ b/tests/e2e/config/default.json @@ -180,6 +180,17 @@ } } }, + "orders": { + "basicPaidOrder": { + "paymentMethod": "cod", + "status": "processing", + "billing": { + "firstName": "John", + "lastName": "Doe", + "email": "john.doe@example.com" + } + } + }, "onboardingwizard": { "industry": "Test industry", "numberofproducts": "1 - 10", diff --git a/tests/e2e/core-tests/specs/api/order.test.js b/tests/e2e/core-tests/specs/api/order.test.js new file mode 100644 index 00000000000..cdfc0b7a5c1 --- /dev/null +++ b/tests/e2e/core-tests/specs/api/order.test.js @@ -0,0 +1,82 @@ +/* eslint-disable jest/no-export, jest/no-disabled-tests */ +/** + * Internal dependencies + */ + const { HTTPClientFactory, Order } = require( '@woocommerce/api' ); + + /** + * 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', () => { + let client; + let order; + let repository; + + beforeAll(async () => { + order = config.get( 'orders.basicPaidOrder' ); + const admin = config.get( 'users.admin' ); + const url = config.get( 'url' ); + + client = HTTPClientFactory.build( url ) + .withBasicAuth( admin.username, admin.password ) + .withIndexPermalinks() + .create(); + } ); + + 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 () => { + const orderProperties = { + id: order.id, + payment_method: order.paymentMethod, + status: order.status, + }; + + // Read order directly from API to compare. + 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 () => { + const updatedOrderProperties = { + payment_method: 'bacs', + status: 'completed', + }; + + 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.statusCode ).toBe( 200 ); + expect( response.data ).toEqual( expect.objectContaining( updatedOrderProperties ) ); + }); + + 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; diff --git a/tests/e2e/core-tests/specs/index.js b/tests/e2e/core-tests/specs/index.js index a0eca5a730f..12ecebb3c24 100644 --- a/tests/e2e/core-tests/specs/index.js +++ b/tests/e2e/core-tests/specs/index.js @@ -51,6 +51,7 @@ const runExternalProductAPITest = require( './api/external-product.test' ); const runCouponApiTest = require( './api/coupon.test' ); const runGroupedProductAPITest = require( './api/grouped-product.test' ); const runVariableProductAPITest = require( './api/variable-product.test' ); +const runOrderApiTest = require( './api/order.test' ); const runSetupOnboardingTests = () => { runActivationTest(); @@ -104,6 +105,7 @@ const runApiTests = () => { runGroupedProductAPITest(); runVariableProductAPITest(); runCouponApiTest(); + runOrderApiTest(); } module.exports = { @@ -133,6 +135,7 @@ module.exports = { runUpdateGeneralSettingsTest, runProductSettingsTest, runTaxSettingsTest, + runOrderApiTest, runOrderStatusFiltersTest, runOrderRefundTest, runOrderApplyCouponTest, diff --git a/tests/e2e/core-tests/specs/merchant/wp-admin-order-emails.test.js b/tests/e2e/core-tests/specs/merchant/wp-admin-order-emails.test.js index ad18a8de6d1..3b69ba28beb 100644 --- a/tests/e2e/core-tests/specs/merchant/wp-admin-order-emails.test.js +++ b/tests/e2e/core-tests/specs/merchant/wp-admin-order-emails.test.js @@ -11,7 +11,6 @@ const { } = require( '@woocommerce/e2e-utils' ); const config = require( 'config' ); -const simpleProductName = config.get( 'products.simple.name' ); const customerEmail = config.get( 'addresses.customer.billing.email' ); const adminEmail = 'admin@woocommercecoree2etestsuite.com'; const storeName = 'WooCommerce Core E2E Test Suite'; diff --git a/tests/e2e/specs/rest-api/order.js b/tests/e2e/specs/rest-api/order.js new file mode 100644 index 00000000000..d73a8be5f28 --- /dev/null +++ b/tests/e2e/specs/rest-api/order.js @@ -0,0 +1,6 @@ +/* + * Internal dependencies + */ +const { runOrderApiTest } = require( '@woocommerce/e2e-core-tests' ); + +runOrderApiTest();