diff --git a/cypress/elements/productTypes/productTypeDetails.js b/cypress/elements/productTypes/productTypeDetails.js index 31ba5df39..e1568e3e4 100644 --- a/cypress/elements/productTypes/productTypeDetails.js +++ b/cypress/elements/productTypes/productTypeDetails.js @@ -5,5 +5,6 @@ export const PRODUCT_TYPE_DETAILS = { assignVariantAttributeButton: '[data-test-id="assignVariantsAttributes"]', hasVariantsButton: '[name="hasVariants"]', shippingWeightInput: '[name="weight"]', - giftCardKindCheckbox: '[data-test-id="product-type-kind-option-GIFT_CARD"]' + giftCardKindCheckbox: '[data-test-id="product-type-kind-option-GIFT_CARD"]', + variantSelectionCheckbox: '[data-test-id = "variant-selection-checkbox"]' }; diff --git a/cypress/elements/shared/button-selectors.js b/cypress/elements/shared/button-selectors.js index fc07d91cd..d896fc499 100644 --- a/cypress/elements/shared/button-selectors.js +++ b/cypress/elements/shared/button-selectors.js @@ -10,7 +10,8 @@ export const BUTTON_SELECTORS = { deleteButton: '[data-test="button-bar-delete"]', expandIcon: '[data-test-id="expand-icon"]', nextPaginationButton: '[data-test="button-pagination-next"]', - deleteIcon: '[data-test-id="deleteIcon"]', + deleteIcon: '[data-test-id="delete-icon"]', showMoreButton: '[data-test-id="showMoreButton"]', - button: "button" + button: "button", + deleteAssignedItemsConsentCheckbox: '[name="delete-assigned-items-consent"]' }; diff --git a/cypress/elements/shared/sharedElements.js b/cypress/elements/shared/sharedElements.js index 080beceac..dfde657cb 100644 --- a/cypress/elements/shared/sharedElements.js +++ b/cypress/elements/shared/sharedElements.js @@ -21,7 +21,8 @@ export const SHARED_ELEMENTS = { filters: { filterGroupActivateCheckbox: '[data-test="filterGroupActive"]', filterRow: '[data-test="channel-availability-item"]' - } + }, + warningDialog: '[data-test-id="warning-dialog"]' }; export const selectorWithDataValue = value => `[data-value="${value}"]`; diff --git a/cypress/integration/checkout/purchaseWithProductTypes.js b/cypress/integration/checkout/purchaseWithProductTypes.js index ca1b0941b..46fb1ca25 100644 --- a/cypress/integration/checkout/purchaseWithProductTypes.js +++ b/cypress/integration/checkout/purchaseWithProductTypes.js @@ -98,7 +98,7 @@ filterTests({ definedTags: ["all", "critical"] }, () => { }; }); - xit("should purchase digital product", () => { + it("should purchase digital product", () => { const digitalName = `${startsWith}${faker.datatype.number()}`; let variants; @@ -141,7 +141,7 @@ filterTests({ definedTags: ["all", "critical"] }, () => { }); }); - xit("should purchase physical product", () => { + it("should purchase physical product", () => { const physicalName = `${startsWith}${faker.datatype.number()}`; createTypeProduct({ name: physicalName, diff --git a/cypress/integration/configuration/productTypes.js b/cypress/integration/configuration/productTypes.js deleted file mode 100644 index 8df407835..000000000 --- a/cypress/integration/configuration/productTypes.js +++ /dev/null @@ -1,122 +0,0 @@ -/// -/// - -import faker from "faker"; - -import { PRODUCT_TYPE_DETAILS } from "../../elements/productTypes/productTypeDetails"; -import { productTypeDetailsUrl, urlList } from "../../fixtures/urlList"; -import { createAttribute } from "../../support/api/requests/Attribute"; -import { - createTypeProduct, - getProductType -} from "../../support/api/requests/ProductType"; -import { deleteProductsStartsWith } from "../../support/api/utils/products/productsUtils"; -import filterTests from "../../support/filterTests"; -import { createProductType } from "../../support/pages/productTypePage"; - -filterTests({ definedTags: ["all"] }, () => { - describe("As an admin I want to manage product types", () => { - const startsWith = "ProductType"; - - before(() => { - cy.clearSessionData().loginUserViaRequest(); - deleteProductsStartsWith(startsWith); - createAttribute({ name: startsWith }); - }); - - beforeEach(() => { - cy.clearSessionData() - .loginUserViaRequest() - .visit(urlList.productTypes) - .softExpectSkeletonIsVisible(); - }); - - it("As an admin I should be able to create product type without shipping required", () => { - const name = `${startsWith}${faker.datatype.number()}`; - - createProductType({ name }) - .then(productType => { - getProductType(productType.id); - }) - .then(productType => { - expect(productType.name).to.be.eq(name); - expect(productType.isShippingRequired).to.be.false; - expect(productType.kind).to.be.eq("NORMAL"); - }); - }); - - it("As an admin I should be able to create product type with shipping required", () => { - const name = `${startsWith}${faker.datatype.number()}`; - const shippingWeight = 10; - - createProductType({ name, shippingWeight }) - .then(productType => { - getProductType(productType.id); - }) - .then(productType => { - expect(productType.name).to.be.eq(name); - expect(productType.isShippingRequired).to.be.true; - expect(productType.weight.value).to.eq(shippingWeight); - expect(productType.kind).to.be.eq("NORMAL"); - }); - }); - - it("As an admin I should be able to create product type with gift card kind", () => { - const name = `${startsWith}${faker.datatype.number()}`; - - createProductType({ name, giftCard: true }) - .then(productType => { - getProductType(productType.id); - }) - .then(productType => { - expect(productType.name).to.be.eq(name); - expect(productType.isShippingRequired).to.be.false; - expect(productType.kind).to.be.eq("GIFT_CARD"); - }); - }); - - it("As an admin I should be able to update product type with product attribute", () => { - const name = `${startsWith}${faker.datatype.number()}`; - - createTypeProduct({ name }) - .then(productType => { - cy.visitAndWaitForProgressBarToDisappear( - productTypeDetailsUrl(productType.id) - ) - .get(PRODUCT_TYPE_DETAILS.assignProductAttributeButton) - .click() - .addAliasToGraphRequest("AssignProductAttribute") - .assignElements(startsWith, false) - .confirmationMessageShouldDisappear() - .waitForRequestAndCheckIfNoErrors("@AssignProductAttribute"); - getProductType(productType.id); - }) - .then(productType => { - expect(productType.productAttributes[0].name).to.eq(startsWith); - }); - }); - - it("As an admin I should be able to update product type with variant attribute", () => { - const name = `${startsWith}${faker.datatype.number()}`; - - createTypeProduct({ name, hasVariants: false }) - .then(productType => { - cy.visitAndWaitForProgressBarToDisappear( - productTypeDetailsUrl(productType.id) - ) - .get(PRODUCT_TYPE_DETAILS.hasVariantsButton) - .click({ force: true }) - .get(PRODUCT_TYPE_DETAILS.assignVariantAttributeButton) - .click() - .addAliasToGraphRequest("AssignProductAttribute") - .assignElements(startsWith, false) - .confirmationMessageShouldDisappear() - .wait("@AssignProductAttribute"); - getProductType(productType.id); - }) - .then(productType => { - expect(productType.variantAttributes[0].name).to.eq(startsWith); - }); - }); - }); -}); diff --git a/cypress/integration/configuration/productTypes/attributesInProduductTypes.js b/cypress/integration/configuration/productTypes/attributesInProduductTypes.js new file mode 100644 index 000000000..f8d57d06a --- /dev/null +++ b/cypress/integration/configuration/productTypes/attributesInProduductTypes.js @@ -0,0 +1,168 @@ +/// +/// + +import faker from "faker"; + +import { PRODUCT_TYPE_DETAILS } from "../../../elements/productTypes/productTypeDetails"; +import { BUTTON_SELECTORS } from "../../../elements/shared/button-selectors"; +import { productTypeDetailsUrl } from "../../../fixtures/urlList"; +import { createAttribute } from "../../../support/api/requests/Attribute"; +import { createCategory } from "../../../support/api/requests/Category"; +import { + assignAttribute, + createTypeProduct, + getProductType +} from "../../../support/api/requests/ProductType"; +import { getDefaultChannel } from "../../../support/api/utils/channelsUtils"; +import { deleteProductsStartsWith } from "../../../support/api/utils/products/productsUtils"; +import filterTests from "../../../support/filterTests"; + +filterTests({ definedTags: ["all"] }, () => { + describe("As an admin I want to manage product types", () => { + const startsWith = "productType"; + let category; + let channel; + let attribute; + + before(() => { + cy.clearSessionData().loginUserViaRequest(); + deleteProductsStartsWith(startsWith); + createAttribute({ name: startsWith }).then(resp => (attribute = resp)); + createCategory(startsWith).then(resp => (category = resp)); + getDefaultChannel().then(resp => (channel = resp)); + }); + + beforeEach(() => { + cy.clearSessionData().loginUserViaRequest(); + }); + + it("should be able to update product type with product attribute. TC: SALEOR_1503", () => { + const name = `${startsWith}${faker.datatype.number()}`; + + createTypeProduct({ name }) + .then(productType => { + cy.visitAndWaitForProgressBarToDisappear( + productTypeDetailsUrl(productType.id) + ) + .get(PRODUCT_TYPE_DETAILS.assignProductAttributeButton) + .click() + .addAliasToGraphRequest("AssignProductAttribute") + .assignElements(startsWith, false) + .confirmationMessageShouldDisappear() + .waitForRequestAndCheckIfNoErrors("@AssignProductAttribute"); + getProductType(productType.id); + }) + .then(productType => { + expect(productType.productAttributes[0].name).to.eq(startsWith); + }); + }); + + it("should be able to update product type with variant attribute. TC: SALEOR_1504", () => { + const name = `${startsWith}${faker.datatype.number()}`; + + createTypeProduct({ name, hasVariants: false }) + .then(productType => { + cy.visitAndWaitForProgressBarToDisappear( + productTypeDetailsUrl(productType.id) + ) + .get(PRODUCT_TYPE_DETAILS.hasVariantsButton) + .click({ force: true }) + .get(PRODUCT_TYPE_DETAILS.assignVariantAttributeButton) + .click() + .addAliasToGraphRequest("AssignProductAttribute") + .assignElements(startsWith, false) + .confirmationMessageShouldDisappear() + .wait("@AssignProductAttribute"); + getProductType(productType.id); + }) + .then(productType => { + expect(productType.assignedVariantAttributes[0].attribute.name).to.eq( + startsWith + ); + }); + }); + + it("should be able to remove variant attribute from product type. TC: SALEOR_1506", () => { + const name = `${startsWith}${faker.datatype.number()}`; + let productType; + + createTypeProduct({ name, hasVariants: true }) + .then(productTypeResp => { + productType = productTypeResp; + assignAttribute(productType.id, attribute.id); + }) + .then(() => { + cy.visitAndWaitForProgressBarToDisappear( + productTypeDetailsUrl(productType.id) + ) + .get(BUTTON_SELECTORS.deleteIcon) + .click() + .addAliasToGraphRequest("UnassignProductAttribute") + .get(BUTTON_SELECTORS.submit) + .click() + .wait("@UnassignProductAttribute"); + getProductType(productType.id); + }) + .then(productType => { + expect(productType.assignedVariantAttributes).to.be.empty; + }); + }); + + it("should be able to remove product attribute from product type. TC: SALEOR_1507", () => { + const name = `${startsWith}${faker.datatype.number()}`; + let productType; + + createTypeProduct({ name, hasVariants: false }) + .then(productTypeResp => { + productType = productTypeResp; + assignAttribute(productType.id, attribute.id, "PRODUCT"); + }) + .then(() => { + cy.visitAndWaitForProgressBarToDisappear( + productTypeDetailsUrl(productType.id) + ) + .get(BUTTON_SELECTORS.deleteIcon) + .click() + .addAliasToGraphRequest("UnassignProductAttribute") + .get(BUTTON_SELECTORS.submit) + .click() + .wait("@UnassignProductAttribute"); + getProductType(productType.id); + }) + .then(productType => { + expect(productType.assignedVariantAttributes).to.be.empty; + }); + }); + + it("should be able to select attribute as variant selection. TC: SALEOR_1508", () => { + const name = `${startsWith}${faker.datatype.number()}`; + let productType; + + createTypeProduct({ name, hasVariants: true }) + .then(productTypeResp => { + productType = productTypeResp; + assignAttribute(productType.id, attribute.id); + }) + .then(() => { + cy.visitAndWaitForProgressBarToDisappear( + productTypeDetailsUrl(productType.id) + ) + .get(PRODUCT_TYPE_DETAILS.variantSelectionCheckbox) + .click() + .addAliasToGraphRequest("ProductAttributeAssignmentUpdate") + .get(BUTTON_SELECTORS.confirm) + .click() + .wait("@ProductAttributeAssignmentUpdate"); + getProductType(productType.id); + }) + .then(productType => { + expect(productType.assignedVariantAttributes[0].attribute.name).to.eq( + startsWith + ); + expect( + productType.assignedVariantAttributes[0].variantSelection + ).to.eq(true); + }); + }); + }); +}); diff --git a/cypress/integration/configuration/productTypes/createProductType.js b/cypress/integration/configuration/productTypes/createProductType.js new file mode 100644 index 000000000..7a79e1994 --- /dev/null +++ b/cypress/integration/configuration/productTypes/createProductType.js @@ -0,0 +1,72 @@ +/// +/// + +import faker from "faker"; + +import { urlList } from "../../../fixtures/urlList"; +import { getProductType } from "../../../support/api/requests/ProductType"; +import { deleteProductsStartsWith } from "../../../support/api/utils/products/productsUtils"; +import filterTests from "../../../support/filterTests"; +import { createProductType } from "../../../support/pages/productTypePage"; + +filterTests({ definedTags: ["all"] }, () => { + describe("As an admin I want to create product types", () => { + const startsWith = "productType"; + + before(() => { + cy.clearSessionData().loginUserViaRequest(); + deleteProductsStartsWith(startsWith); + }); + + beforeEach(() => { + cy.clearSessionData() + .loginUserViaRequest() + .visit(urlList.productTypes) + .softExpectSkeletonIsVisible(); + }); + + it("should be able to create product type without shipping required. TC: SALEOR_1501", () => { + const name = `${startsWith}${faker.datatype.number()}`; + + createProductType({ name }) + .then(productType => { + getProductType(productType.id); + }) + .then(productType => { + expect(productType.name).to.be.eq(name); + expect(productType.isShippingRequired).to.be.false; + expect(productType.kind).to.be.eq("NORMAL"); + }); + }); + + it("should be able to create product type with shipping required. TC: SALEOR_1502", () => { + const name = `${startsWith}${faker.datatype.number()}`; + const shippingWeight = 10; + + createProductType({ name, shippingWeight }) + .then(productType => { + getProductType(productType.id); + }) + .then(productType => { + expect(productType.name).to.be.eq(name); + expect(productType.isShippingRequired).to.be.true; + expect(productType.weight.value).to.eq(shippingWeight); + expect(productType.kind).to.be.eq("NORMAL"); + }); + }); + + it("should be able to create product type with gift card kind. TC: SALEOR_1510", () => { + const name = `${startsWith}${faker.datatype.number()}`; + + createProductType({ name, giftCard: true }) + .then(productType => { + getProductType(productType.id); + }) + .then(productType => { + expect(productType.name).to.be.eq(name); + expect(productType.isShippingRequired).to.be.false; + expect(productType.kind).to.be.eq("GIFT_CARD"); + }); + }); + }); +}); diff --git a/cypress/integration/configuration/productTypes/deleteProductType.js b/cypress/integration/configuration/productTypes/deleteProductType.js new file mode 100644 index 000000000..53f109a7b --- /dev/null +++ b/cypress/integration/configuration/productTypes/deleteProductType.js @@ -0,0 +1,97 @@ +/// +/// + +import faker from "faker"; + +import { BUTTON_SELECTORS } from "../../../elements/shared/button-selectors"; +import { SHARED_ELEMENTS } from "../../../elements/shared/sharedElements"; +import { productTypeDetailsUrl } from "../../../fixtures/urlList"; +import { createAttribute } from "../../../support/api/requests/Attribute"; +import { createCategory } from "../../../support/api/requests/Category"; +import { + createTypeProduct, + getProductType +} from "../../../support/api/requests/ProductType"; +import { getProductDetails } from "../../../support/api/requests/storeFront/ProductDetails"; +import { getDefaultChannel } from "../../../support/api/utils/channelsUtils"; +import { + createProductInChannel, + deleteProductsStartsWith +} from "../../../support/api/utils/products/productsUtils"; +import filterTests from "../../../support/filterTests"; + +filterTests({ definedTags: ["all"] }, () => { + describe("As an admin I want to manage product types", () => { + const startsWith = "productType"; + let category; + let channel; + let attribute; + + before(() => { + cy.clearSessionData().loginUserViaRequest(); + deleteProductsStartsWith(startsWith); + createAttribute({ name: startsWith }).then(resp => (attribute = resp)); + createCategory(startsWith).then(resp => (category = resp)); + getDefaultChannel().then(resp => (channel = resp)); + }); + + beforeEach(() => { + cy.clearSessionData().loginUserViaRequest(); + }); + + it("should be able to delete product type. TC: SALEOR_1505", () => { + const name = `${startsWith}${faker.datatype.number()}`; + + createTypeProduct({ name, hasVariants: false }).then(productType => { + cy.visitAndWaitForProgressBarToDisappear( + productTypeDetailsUrl(productType.id) + ) + .get(BUTTON_SELECTORS.deleteButton) + .click() + .addAliasToGraphRequest("ProductTypeDelete") + .get(SHARED_ELEMENTS.warningDialog) + .find(BUTTON_SELECTORS.deleteButton) + .click() + .waitForRequestAndCheckIfNoErrors("@ProductTypeDelete"); + getProductType(productType.id).should("be.null"); + }); + }); + + it("should be able to delete product type with assigned product. TC: SALEOR_1509", () => { + const name = `${startsWith}${faker.datatype.number()}`; + let productType; + + createTypeProduct({ name, hasVariants: false }) + .then(productTypeResp => { + productType = productTypeResp; + createProductInChannel({ + name, + channelId: channel.id, + categoryId: category.id, + productTypeId: productType.id + }); + }) + .then(({ product }) => { + cy.visitAndWaitForProgressBarToDisappear( + productTypeDetailsUrl(productType.id) + ) + .get(BUTTON_SELECTORS.deleteButton) + .click() + .addAliasToGraphRequest("ProductTypeDelete") + .get(SHARED_ELEMENTS.warningDialog) + .find(BUTTON_SELECTORS.deleteButton) + .should("not.be.enabled") + .get(BUTTON_SELECTORS.deleteAssignedItemsConsentCheckbox) + .click() + .get(SHARED_ELEMENTS.warningDialog) + .find(BUTTON_SELECTORS.deleteButton) + .click() + .waitForRequestAndCheckIfNoErrors("@ProductTypeDelete"); + getProductType(productType.id).should("be.null"); + getProductDetails(product.id) + .its("body.data.product") + .should("be.null"); + }); + }); + }); +}); diff --git a/cypress/support/api/requests/Product.js b/cypress/support/api/requests/Product.js index c05367b85..982bd1472 100644 --- a/cypress/support/api/requests/Product.js +++ b/cypress/support/api/requests/Product.js @@ -110,17 +110,16 @@ export function createProduct({ attributeValue, `values:["${attributeValue}"]` ); - const attributesLines = getValueWithDefault( + const attributes = getValueWithDefault( attributeId, `attributes:[{ - id:"${attributeId}" - ${attributeValuesLine} - }]` + id:"${attributeId}" + ${attributeValuesLine} + }]` ); - const mutation = `mutation{ productCreate(input:{ - ${attributesLines} + ${attributes} name:"${name}" slug:"${name}" seo:{title:"${name}" description:""} diff --git a/cypress/support/api/requests/ProductType.js b/cypress/support/api/requests/ProductType.js index 327c38183..6bab393fe 100644 --- a/cypress/support/api/requests/ProductType.js +++ b/cypress/support/api/requests/ProductType.js @@ -105,8 +105,11 @@ export function getProductType(productTypeId) { productAttributes{ name } - variantAttributes{ - name + assignedVariantAttributes{ + attribute{ + name + } + variantSelection } } }`; @@ -145,3 +148,22 @@ export function setProductTypeAsDigital(productTypeId, isDigital = true) { }`; return cy.sendRequestWithQuery(mutation); } + +export function assignAttribute( + productTypeId, + attributeId, + attributeType = "VARIANT" +) { + const mutation = `mutation{ + productAttributeAssign(productTypeId:"${productTypeId}", operations:{ + id:"${attributeId}" + type: ${attributeType} + }){ + errors{ + field + message + } + } + }`; + return cy.sendRequestWithQuery(mutation); +} diff --git a/src/categories/views/CategoryDetails.tsx b/src/categories/views/CategoryDetails.tsx index fa6d1bde9..c774218ad 100644 --- a/src/categories/views/CategoryDetails.tsx +++ b/src/categories/views/CategoryDetails.tsx @@ -265,7 +265,7 @@ export const CategoryDetails: React.FC = ({ } productListToolbar={ openModal("delete-products", { diff --git a/src/components/TypeDeleteWarningDialog/TypeDeleteWarningDialog.tsx b/src/components/TypeDeleteWarningDialog/TypeDeleteWarningDialog.tsx index d40650a69..94e900721 100644 --- a/src/components/TypeDeleteWarningDialog/TypeDeleteWarningDialog.tsx +++ b/src/components/TypeDeleteWarningDialog/TypeDeleteWarningDialog.tsx @@ -92,7 +92,7 @@ function TypeDeleteWarningDialog({ return ( -
+
= props => { {permissionGroup.userCanManage && ( onDelete(permissionGroup.id) diff --git a/src/productTypes/components/ProductTypeAttributes/ProductTypeAttributes.tsx b/src/productTypes/components/ProductTypeAttributes/ProductTypeAttributes.tsx index 1737cdf0d..8829b581b 100644 --- a/src/productTypes/components/ProductTypeAttributes/ProductTypeAttributes.tsx +++ b/src/productTypes/components/ProductTypeAttributes/ProductTypeAttributes.tsx @@ -177,6 +177,7 @@ const ProductTypeAttributes: React.FC = props => { diff --git a/src/productTypes/components/ProductTypeVariantAttributes/ProductTypeVariantAttributes.tsx b/src/productTypes/components/ProductTypeVariantAttributes/ProductTypeVariantAttributes.tsx index 8979184f9..991eb6907 100644 --- a/src/productTypes/components/ProductTypeVariantAttributes/ProductTypeVariantAttributes.tsx +++ b/src/productTypes/components/ProductTypeVariantAttributes/ProductTypeVariantAttributes.tsx @@ -243,10 +243,11 @@ const ProductTypeVariantAttributes: React.FC
onAttributeUnassign(attribute.id) )} diff --git a/src/storybook/__snapshots__/Stories.test.ts.snap b/src/storybook/__snapshots__/Stories.test.ts.snap index 89badf2f1..9e6621002 100644 --- a/src/storybook/__snapshots__/Stories.test.ts.snap +++ b/src/storybook/__snapshots__/Stories.test.ts.snap @@ -162738,7 +162738,7 @@ exports[`Storyshots Views / Permission Groups / Permission Group List default 1`