diff --git a/cypress/apiRequests/Checkout.js b/cypress/apiRequests/Checkout.js index b3b219721..c131aa354 100644 --- a/cypress/apiRequests/Checkout.js +++ b/cypress/apiRequests/Checkout.js @@ -171,3 +171,23 @@ export function checkoutShippingAddressUpdate(checkoutId, address) { }`; return cy.sendRequestWithQuery(mutation); } + +export function addProductsToCheckout( + checkoutId, + variantsList, + productQuantity +) { + const lines = getVariantsLines(variantsList, productQuantity); + const mutation = `mutation{ + checkoutLinesUpdate(checkoutId:"${checkoutId}" lines:[${lines.join()}]){ + checkout{ + id + } + errors{ + field + message + } + } + }`; + return cy.sendRequestWithQuery(mutation).its("body.data.checkoutLinesUpdate"); +} diff --git a/cypress/apiRequests/Product.js b/cypress/apiRequests/Product.js index 68da6345c..0f0858021 100644 --- a/cypress/apiRequests/Product.js +++ b/cypress/apiRequests/Product.js @@ -137,7 +137,8 @@ export function createVariant({ channelId, attributeId, price = 1, - costPrice = 1 + costPrice = 1, + trackInventory = true }) { const channelListings = getValueWithDefault( channelId, @@ -164,6 +165,7 @@ export function createVariant({ }] sku: "${sku}" ${channelListings} + trackInventory:${trackInventory} ${stocks} }) { productVariants{ diff --git a/cypress/elements/shared/button-selectors.js b/cypress/elements/shared/button-selectors.js index f29392a8f..5553459d8 100644 --- a/cypress/elements/shared/button-selectors.js +++ b/cypress/elements/shared/button-selectors.js @@ -9,6 +9,7 @@ export const BUTTON_SELECTORS = { notSelectedOption: ":not([aria-selected])", deleteButton: '[data-test="button-bar-delete"]', expandIcon: '[data-test-id="expand-icon"]', + nextPaginationButton: '[data-test="button-pagination-next"]', deleteIcon: '[data-test-id="deleteIcon"]', showMoreButton: '[data-test-id="showMoreButton"]' }; diff --git a/cypress/integration/allEnv/checkout/stocksInCheckout.js b/cypress/integration/allEnv/checkout/stocksInCheckout.js new file mode 100644 index 000000000..5920dd558 --- /dev/null +++ b/cypress/integration/allEnv/checkout/stocksInCheckout.js @@ -0,0 +1,136 @@ +import faker from "faker"; + +import { + addProductsToCheckout, + createCheckout +} from "../../../apiRequests/Checkout"; +import { getDefaultChannel } from "../../../utils/channelsUtils"; +import { createOrderWithNewProduct } from "../../../utils/ordersUtils"; +import { + createProductInChannel, + createTypeAttributeAndCategoryForProduct, + deleteProductsStartsWith +} from "../../../utils/products/productsUtils"; +import { + createShipping, + deleteShippingStartsWith +} from "../../../utils/shippingUtils"; + +describe("Products stocks in checkout", () => { + const startsWith = "CyStocksCheckout-"; + const name = `${startsWith}${faker.datatype.number()}`; + + let defaultChannel; + let address; + let warehouse; + let attribute; + let category; + let productType; + let shippingMethod; + + before(() => { + cy.clearSessionData().loginUserViaRequest(); + deleteProductsStartsWith(startsWith); + deleteShippingStartsWith(startsWith); + cy.fixture("addresses") + .then(addresses => { + address = addresses.usAddress; + getDefaultChannel(); + }) + .then(channel => { + defaultChannel = channel; + createShipping({ + channelId: defaultChannel.id, + name, + address + }); + }) + .then( + ({ warehouse: warehouseResp, shippingMethod: shippingMethodResp }) => { + warehouse = warehouseResp; + shippingMethod = shippingMethodResp; + createTypeAttributeAndCategoryForProduct(name); + } + ) + .then( + ({ + attribute: attributeResp, + category: categoryResp, + productType: productTypeResp + }) => { + attribute = attributeResp; + category = categoryResp; + productType = productTypeResp; + } + ); + }); + it("should create checkout with last product in stock", () => { + const productName = `${startsWith}${faker.datatype.number()}`; + + createOrderWithNewProduct({ + attributeId: attribute.id, + categoryId: category.id, + productTypeId: productType.id, + channel: defaultChannel, + name: productName, + warehouseId: warehouse.id, + shippingMethodId: shippingMethod.id, + address + }).then(({ order }) => { + expect(order, "order should be created").to.be.ok; + }); + }); + it("should not be possible to add product with quantity greater than stock", () => { + const productName = `${startsWith}${faker.datatype.number()}`; + let variants; + + createProductInChannel({ + attributeId: attribute.id, + categoryId: category.id, + productTypeId: productType.id, + channelId: defaultChannel.id, + name: productName, + warehouseId: warehouse.id, + quantityInWarehouse: 1 + }) + .then(({ variantsList }) => { + variants = variantsList; + createCheckout({ + channelSlug: defaultChannel.slug, + address, + billingAddress: address, + email: "email@example.com", + variantsList, + auth: "token" + }); + }) + .then(({ checkout: checkout }) => { + addProductsToCheckout(checkout.id, variants, 2); + }) + .then(({ errors }) => { + expect( + errors[0], + "should return error on field quantity" + ).to.have.property("field", "quantity"); + }); + }); + + it("should buy product with no quantity if tracking is not set", () => { + const productName = `${startsWith}${faker.datatype.number()}`; + + createOrderWithNewProduct({ + attributeId: attribute.id, + categoryId: category.id, + productTypeId: productType.id, + channel: defaultChannel, + name: productName, + warehouseId: warehouse.id, + quantityInWarehouse: 0, + trackInventory: false, + shippingMethodId: shippingMethod.id, + address + }).then(({ order }) => { + expect(order, "order should be created").to.be.ok; + }); + }); +}); diff --git a/cypress/integration/allEnv/configuration/shippingMethod.js b/cypress/integration/allEnv/configuration/shippingMethod.js index 79cf2af39..2f0178280 100644 --- a/cypress/integration/allEnv/configuration/shippingMethod.js +++ b/cypress/integration/allEnv/configuration/shippingMethod.js @@ -11,6 +11,7 @@ import { createWarehouse } from "../../../apiRequests/Warehouse"; import { BUTTON_SELECTORS } from "../../../elements/shared/button-selectors"; import { SHARED_ELEMENTS } from "../../../elements/shared/sharedElements"; import { SHIPPING_ZONE_DETAILS } from "../../../elements/shipping/shipping-zone-details"; +import { SHIPPING_ZONES_LIST } from "../../../elements/shipping/shipping-zones-list"; import { selectChannelInHeader } from "../../../steps/channelsSteps"; import { createShippingRate, diff --git a/cypress/integration/allEnv/products/updatingProducts.js b/cypress/integration/allEnv/products/updatingProducts.js index 318afd3bb..16e4b4266 100644 --- a/cypress/integration/allEnv/products/updatingProducts.js +++ b/cypress/integration/allEnv/products/updatingProducts.js @@ -18,8 +18,8 @@ import { } from "../../../utils/products/productsUtils"; describe("Update products", () => { - const startsWith = "Cy-"; - const name = `${startsWith}${faker.random.number()}`; + const startsWith = "CyUpdateProducts-"; + const name = `${startsWith}${faker.datatype.number()}`; const description = faker.lorem.sentences(2); let defaultChannel; diff --git a/cypress/utils/ordersUtils.js b/cypress/utils/ordersUtils.js index 9d06ba7cd..0a32b365e 100644 --- a/cypress/utils/ordersUtils.js +++ b/cypress/utils/ordersUtils.js @@ -1,5 +1,6 @@ import * as checkoutRequest from "../apiRequests/Checkout"; import * as orderRequest from "../apiRequests/Order"; +import { createProductInChannel } from "./products/productsUtils"; export function createWaitingForCaptureOrder( channelSlug, @@ -12,7 +13,14 @@ export function createWaitingForCaptureOrder( const auth = "token"; cy.loginInShop(); return checkoutRequest - .createCheckout({ channelSlug, email, variantsList, address, auth }) + .createCheckout({ + channelSlug, + email, + variantsList, + address, + billingAddress: address, + auth + }) .then(({ checkout: checkoutResp }) => { checkout = checkoutResp; checkoutRequest.addShippingMethod(checkout.id, shippingMethodId); @@ -144,3 +152,35 @@ export function createAndCompleteCheckoutWithoutShipping({ .then(() => checkoutRequest.completeCheckout(checkout.id)) .then(({ order }) => ({ checkout, order })); } + +export function createOrderWithNewProduct({ + attributeId, + categoryId, + productTypeId, + channel, + name, + warehouseId, + quantityInWarehouse = 1, + trackInventory = true, + shippingMethodId, + address +}) { + return createProductInChannel({ + attributeId, + categoryId, + productTypeId, + channelId: channel.id, + name, + warehouseId, + quantityInWarehouse, + trackInventory + }).then(({ variantsList }) => + createWaitingForCaptureOrder( + channel.slug, + "email@example.com", + variantsList, + shippingMethodId, + address + ) + ); +} diff --git a/cypress/utils/products/productsUtils.js b/cypress/utils/products/productsUtils.js index 847f1aabe..d7944d8b9 100644 --- a/cypress/utils/products/productsUtils.js +++ b/cypress/utils/products/productsUtils.js @@ -15,7 +15,8 @@ export function createProductInChannel({ isAvailableForPurchase = true, visibleInListings = true, collectionId = null, - description = null + description = null, + trackInventory = true }) { let product; let variantsList; @@ -46,7 +47,8 @@ export function createProductInChannel({ warehouseId, quantityInWarehouse, channelId, - price + price, + trackInventory }); }) .then(variantsResp => {