diff --git a/cypress/elements/catalog/products/variants-selectors.js b/cypress/elements/catalog/products/variants-selectors.js index 744127469..494a44742 100644 --- a/cypress/elements/catalog/products/variants-selectors.js +++ b/cypress/elements/catalog/products/variants-selectors.js @@ -3,7 +3,7 @@ export const VARIANTS_SELECTORS = { valueContainer: "[data-test-id='value-container']", nextButton: "[class*='MuiButton-containedPrimary']", warehouseCheckboxes: "[name*='warehouse:']", - skuInput: "input[class*='MuiInputBase'][type='text']", + skuInput: "[ data-test-id='sku-input']", attributeSelector: "[data-test='attribute-value']", attributeOption: "[data-test-type='option']", addWarehouseButton: "button[class*='MuiIconButton-colorPrimary']", diff --git a/cypress/fixtures/urlList.js b/cypress/fixtures/urlList.js index 5a2fbf661..e76e97aea 100644 --- a/cypress/fixtures/urlList.js +++ b/cypress/fixtures/urlList.js @@ -26,6 +26,7 @@ export const urlList = { staffMembers: "staff/", stripeApiPaymentMethods: "https://api.stripe.com/v1/payment_methods", translations: "translations/", + variants: "variant/", vouchers: "discounts/vouchers/", warehouses: "warehouses/", weightRete: "weight/" @@ -42,6 +43,9 @@ export const categoryDetailsUrl = categoryId => export const customerDetailsUrl = customerId => `${urlList.customers}${customerId}`; +export const giftCardDetailsUrl = giftCardId => + `${urlList.giftCards}${giftCardId}`; + export const menuDetailsUrl = menuId => `${urlList.navigation}${menuId}`; export const pageTypeDetailsUrl = pageTypeId => @@ -52,6 +56,12 @@ export const permissionGroupDetails = permissionGroupId => export const productDetailsUrl = productId => `${urlList.products}${productId}`; +export const productVariantDetailUrl = (productId, variantId) => + `${urlList.products}${productId}/${urlList.variants}${variantId}`; + +export const productTypeDetailsUrl = productTypeId => + `${urlList.productTypes}${productTypeId}`; + export const staffMemberDetailsUrl = staffMemberId => `${urlList.staffMembers}${staffMemberId}`; diff --git a/cypress/integration/products/productsWithoutSku/updatingProductsWithoutSku.js b/cypress/integration/products/productsWithoutSku/updatingProductsWithoutSku.js new file mode 100644 index 000000000..303e56dab --- /dev/null +++ b/cypress/integration/products/productsWithoutSku/updatingProductsWithoutSku.js @@ -0,0 +1,277 @@ +/// +/// + +import faker from "faker"; + +import { PRODUCT_DETAILS } from "../../../elements/catalog/products/product-details"; +import { VARIANTS_SELECTORS } from "../../../elements/catalog/products/variants-selectors"; +import { BUTTON_SELECTORS } from "../../../elements/shared/button-selectors"; +import { SHARED_ELEMENTS } from "../../../elements/shared/sharedElements"; +import { + productDetailsUrl, + productVariantDetailUrl +} from "../../../fixtures/urlList"; +import { + createVariant, + getVariant +} from "../../../support/api/requests/Product"; +import { + createTypeProduct, + productAttributeAssignmentUpdate +} from "../../../support/api/requests/ProductType"; +import { getDefaultChannel } from "../../../support/api/utils/channelsUtils"; +import { createWaitingForCaptureOrder } from "../../../support/api/utils/ordersUtils"; +import { + createProductInChannelWithoutVariants, + createTypeAttributeAndCategoryForProduct, + deleteProductsStartsWith +} from "../../../support/api/utils/products/productsUtils"; +import { + createShipping, + deleteShippingStartsWith +} from "../../../support/api/utils/shippingUtils"; +import filterTests from "../../../support/filterTests"; + +filterTests({ definedTags: ["all"], version: "3.1.0" }, () => { + describe("Updating products without sku", () => { + const startsWith = "UpdateProductsSku"; + + let defaultChannel; + let address; + let warehouse; + let shippingMethod; + let attribute; + let category; + let productTypeWithVariants; + let productTypeWithoutVariants; + let product; + + const name = `${startsWith}${faker.datatype.number()}`; + const productTypeWithoutVariantsName = `${startsWith}${faker.datatype.number()}`; + const email = "example@example.com"; + const attributeValues = ["value1", "value2"]; + + before(() => { + cy.clearSessionData().loginUserViaRequest(); + deleteProductsStartsWith(startsWith); + deleteShippingStartsWith(startsWith); + getDefaultChannel() + .then(channel => { + defaultChannel = channel; + cy.fixture("addresses"); + }) + .then(fixtureAddresses => { + address = fixtureAddresses.plAddress; + createShipping({ + channelId: defaultChannel.id, + name, + address + }); + }) + .then( + ({ + warehouse: warehouseResp, + shippingMethod: shippingMethodResp + }) => { + warehouse = warehouseResp; + shippingMethod = shippingMethodResp; + createTypeAttributeAndCategoryForProduct({ name, attributeValues }); + } + ) + .then( + ({ + attribute: attributeResp, + productType: productTypeResp, + category: categoryResp + }) => { + attribute = attributeResp; + productTypeWithVariants = productTypeResp; + category = categoryResp; + productAttributeAssignmentUpdate({ + productTypeId: productTypeWithVariants.id, + attributeId: attribute.id + }); + createTypeProduct({ + name: productTypeWithoutVariantsName, + attributeId: attribute.id, + hasVariants: false + }); + } + ) + .then(productTypeResp => { + productTypeWithoutVariants = productTypeResp; + productAttributeAssignmentUpdate({ + productTypeId: productTypeWithoutVariants.id, + attributeId: attribute.id + }); + createProductInChannelWithoutVariants({ + name, + channelId: defaultChannel.id, + attributeId: attribute.id, + productTypeId: productTypeWithVariants.id, + categoryId: category.id + }); + }) + .then(productResp => (product = productResp)); + }); + + beforeEach(() => { + cy.clearSessionData().loginUserViaRequest(); + }); + + it("should add sku to simple product", () => { + const sku = "NewSkuSimpleProd"; + const simpleProductName = `${startsWith}${faker.datatype.number()}`; + let simpleProduct; + createProductInChannelWithoutVariants({ + name: simpleProductName, + attributeId: attribute.id, + categoryId: category.id, + channelId: defaultChannel.id, + productTypeId: productTypeWithoutVariants.id + }) + .then(productResp => { + simpleProduct = productResp; + createVariant({ + productId: simpleProduct.id, + channelId: defaultChannel.id, + warehouseId: warehouse.id, + quantityInWarehouse: 10 + }); + }) + .then(variantsList => { + cy.visitAndWaitForProgressBarToDisappear( + productDetailsUrl(simpleProduct.id) + ) + .get(SHARED_ELEMENTS.skeleton) + .should("not.exist") + .get(PRODUCT_DETAILS.skuInput) + .type(sku) + .addAliasToGraphRequest("SimpleProductUpdate") + .get(BUTTON_SELECTORS.confirm) + .click() + .waitForRequestAndCheckIfNoErrors("@SimpleProductUpdate"); + getVariant(variantsList[0].id, defaultChannel.slug); + }) + .then(variantResp => { + expect(variantResp.sku).to.eq(sku); + }); + }); + + it("should add sku to variant", () => { + const sku = "NewSku"; + let variant; + createVariant({ + attributeId: attribute.id, + channelId: defaultChannel.id, + productId: product.id, + quantityInWarehouse: 10, + warehouseId: warehouse.id, + attributeName: attributeValues[0] + }) + .then(variantResp => { + variant = variantResp[0]; + cy.visitAndWaitForProgressBarToDisappear( + productVariantDetailUrl(product.id, variant.id) + ) + .get(SHARED_ELEMENTS.skeleton) + .should("not.exist") + .get(VARIANTS_SELECTORS.skuInput) + .type(sku) + .addAliasToGraphRequest("VariantUpdate") + .get(BUTTON_SELECTORS.confirm) + .click() + .waitForRequestAndCheckIfNoErrors("@VariantUpdate"); + getVariant(variant.id, defaultChannel.slug); + }) + .then(variantResp => { + expect(variantResp.sku).to.equal(sku); + }); + }); + + it("should remove sku from variant", () => { + let variant; + createVariant({ + attributeId: attribute.id, + channelId: defaultChannel.id, + productId: product.id, + quantityInWarehouse: 10, + warehouseId: warehouse.id, + sku: name, + attributeName: attributeValues[1] + }) + .then(variantResp => { + variant = variantResp[0]; + cy.visitAndWaitForProgressBarToDisappear( + productVariantDetailUrl(product.id, variant.id) + ) + .get(SHARED_ELEMENTS.skeleton) + .should("not.exist") + .get(VARIANTS_SELECTORS.skuInput) + .find("input") + .clear() + .addAliasToGraphRequest("VariantUpdate") + .get(BUTTON_SELECTORS.confirm) + .click() + .waitForRequestAndCheckIfNoErrors("@VariantUpdate"); + getVariant(variant.id, defaultChannel.slug); + }) + .then(variantResp => { + expect(variantResp.sku).to.be.null; + checkIfCheckoutForVariantCanBeCompleted(variantResp); + }); + }); + + it("should remove sku from simple product", () => { + const simpleProductName = `${startsWith}${faker.datatype.number()}`; + let simpleProduct; + createProductInChannelWithoutVariants({ + name: simpleProductName, + attributeId: attribute.id, + categoryId: category.id, + channelId: defaultChannel.id, + productTypeId: productTypeWithoutVariants.id + }) + .then(productResp => { + simpleProduct = productResp; + createVariant({ + productId: simpleProduct.id, + channelId: defaultChannel.id, + sku: simpleProductName, + quantityInWarehouse: 10, + warehouseId: warehouse.id + }); + }) + .then(variantsList => { + cy.visitAndWaitForProgressBarToDisappear( + productDetailsUrl(simpleProduct.id) + ) + .get(SHARED_ELEMENTS.skeleton) + .should("not.exist") + .get(PRODUCT_DETAILS.skuInput) + .clear() + .addAliasToGraphRequest("SimpleProductUpdate") + .get(BUTTON_SELECTORS.confirm) + .click() + .waitForRequestAndCheckIfNoErrors("@SimpleProductUpdate"); + getVariant(variantsList[0].id, defaultChannel.slug); + }) + .then(variantResp => { + expect(variantResp.sku).to.be.null; + checkIfCheckoutForVariantCanBeCompleted(variantResp); + }); + }); + + function checkIfCheckoutForVariantCanBeCompleted(variant) { + createWaitingForCaptureOrder({ + address, + channelSlug: defaultChannel.slug, + email, + shippingMethodId: shippingMethod.id, + variantsList: [variant] + }).then(({ order }) => { + expect(order.id).to.be.ok; + }); + } + }); +}); diff --git a/cypress/support/api/requests/Product.js b/cypress/support/api/requests/Product.js index 9f3ef3264..3467caa16 100644 --- a/cypress/support/api/requests/Product.js +++ b/cypress/support/api/requests/Product.js @@ -153,8 +153,20 @@ export function createVariant({ costPrice = 1, trackInventory = true, weight = 1, + attributeName = "value", attributeValues = ["value"] }) { + const skuLines = getValueWithDefault(sku, `sku: "${sku}"`); + + const attributeLines = getValueWithDefault( + attributeId, + `attributes: [{ + id:"${attributeId}" + values: ["${attributeName}"] + }]`, + "attributes:[]" + ); + const channelListings = getValueWithDefault( channelId, `channelListings:{ @@ -174,12 +186,9 @@ export function createVariant({ const mutation = `mutation{ productVariantBulkCreate(product: "${productId}", variants: { - attributes: [{ - id:"${attributeId}" - values: ${getValuesInArray(attributeValues)} - }] + ${attributeLines} weight: ${weight} - sku: "${sku}" + ${skuLines} ${channelListings} trackInventory:${trackInventory} ${stocks} @@ -236,6 +245,7 @@ export function getVariant(id, channel, auth = "auth") { productVariant(id:"${id}" channel:"${channel}"){ id name + sku pricing{ onSale discount{ diff --git a/cypress/support/api/utils/products/productsUtils.js b/cypress/support/api/utils/products/productsUtils.js index bf314fe4e..5cf160a1a 100644 --- a/cypress/support/api/utils/products/productsUtils.js +++ b/cypress/support/api/utils/products/productsUtils.js @@ -29,33 +29,28 @@ export function createProductInChannel({ collectionId = null, description = null, trackInventory = true, - weight = 1 + weight = 1, + sku = name }) { let product; let variantsList; - return productRequest - .createProduct({ - attributeId, - name, - productTypeId, - categoryId, - collectionId, - description - }) + return createProductInChannelWithoutVariants({ + name, + channelId, + productTypeId, + attributeId, + categoryId, + isPublished, + isAvailableForPurchase, + visibleInListings, + collectionId, + description + }) .then(productResp => { product = productResp; - productRequest.updateChannelInProduct({ - productId: product.id, - channelId, - isPublished, - isAvailableForPurchase, - visibleInListings - }); - }) - .then(() => { productRequest.createVariant({ productId: product.id, - sku: name, + sku, attributeId, warehouseId, quantityInWarehouse, @@ -207,3 +202,38 @@ export function createProductWithShipping({ address })); } + +export function createProductInChannelWithoutVariants({ + name, + channelId, + productTypeId, + attributeId, + categoryId, + isPublished = true, + isAvailableForPurchase = true, + visibleInListings = true, + collectionId = null, + description = null +}) { + let product; + return productRequest + .createProduct({ + attributeId, + name, + productTypeId, + categoryId, + collectionId, + description + }) + .then(productResp => { + product = productResp; + productRequest.updateChannelInProduct({ + productId: product.id, + channelId, + isPublished, + isAvailableForPurchase, + visibleInListings + }); + }) + .then(() => product); +} diff --git a/cypress/support/customCommands/sharedElementsOperations/progressBar.js b/cypress/support/customCommands/sharedElementsOperations/progressBar.js index f0ebc329e..e7674b55d 100644 --- a/cypress/support/customCommands/sharedElementsOperations/progressBar.js +++ b/cypress/support/customCommands/sharedElementsOperations/progressBar.js @@ -1,18 +1,5 @@ import { SHARED_ELEMENTS } from "../../../elements/shared/sharedElements"; -// export function visitAndWaitForProgressBarToDisappear(url) { -// cy.visit(url); -// return waitForProgressBarToNotBeVisible(); -// } - -// export function waitForProgressBarToNotBeVisible() { -// return cy.get(SHARED_ELEMENTS.progressBar).should("be.not.visible"); -// } - -// export function waitForProgressBarToNotExist() { -// return cy.get(SHARED_ELEMENTS.progressBar).should("not.exist"); -// } - Cypress.Commands.add("visitAndWaitForProgressBarToDisappear", url => { cy.visit(url).waitForProgressBarToNotBeVisible(); }); diff --git a/src/products/components/ProductStocks/ProductStocks.tsx b/src/products/components/ProductStocks/ProductStocks.tsx index 92669916e..c70ea5835 100644 --- a/src/products/components/ProductStocks/ProductStocks.tsx +++ b/src/products/components/ProductStocks/ProductStocks.tsx @@ -225,6 +225,7 @@ const ProductStocks: React.FC = ({