From 4faafd2489a507ff03ba2b3c663063a463950a31 Mon Sep 17 00:00:00 2001 From: Karolina Rakoczy Date: Fri, 9 Jul 2021 11:43:45 +0200 Subject: [PATCH] Saleor 3626 tests for attributes (#1198) * tests for attributes * add tests for content attribute --- cypress/apiRequests/Attribute.js | 18 +++ .../elements/attribute/attributes_details.js | 39 ++++++ cypress/elements/attribute/attributes_list.js | 3 + .../configuration/attributes/attributes.js | 120 ++++++++++++++++++ .../attributes/contentAttribute.js | 120 ++++++++++++++++++ cypress/steps/attributesSteps.js | 82 ++++++++++++ cypress/url/urlList.js | 3 +- cypress/utils/attributes.js/attributeUtils.js | 5 + .../utils/attributes.js/checkAttributeData.js | 19 +++ cypress/utils/products/productsUtils.js | 2 + .../AttributeListPage/AttributeListPage.tsx | 7 +- .../AttributeValueEditDialog.tsx | 2 + .../AttributeValues/AttributeValues.tsx | 7 +- .../__snapshots__/Stories.test.ts.snap | 9 ++ 14 files changed, 433 insertions(+), 3 deletions(-) create mode 100644 cypress/elements/attribute/attributes_details.js create mode 100644 cypress/elements/attribute/attributes_list.js create mode 100644 cypress/integration/allEnv/configuration/attributes/attributes.js create mode 100644 cypress/integration/allEnv/configuration/attributes/contentAttribute.js create mode 100644 cypress/steps/attributesSteps.js create mode 100644 cypress/utils/attributes.js/attributeUtils.js create mode 100644 cypress/utils/attributes.js/checkAttributeData.js diff --git a/cypress/apiRequests/Attribute.js b/cypress/apiRequests/Attribute.js index b25cd9380..f889a0f08 100644 --- a/cypress/apiRequests/Attribute.js +++ b/cypress/apiRequests/Attribute.js @@ -58,3 +58,21 @@ export function deleteAttribute(attributeId) { }`; return cy.sendRequestWithQuery(mutation); } + +export function getAttribute(attributeId) { + const query = `query{ + attribute(id:"${attributeId}"){ + id + inputType + name + slug + type + entityType + valueRequired + visibleInStorefront + availableInGrid + unit + } + }`; + return cy.sendRequestWithQuery(query).its("body.data.attribute"); +} diff --git a/cypress/elements/attribute/attributes_details.js b/cypress/elements/attribute/attributes_details.js new file mode 100644 index 000000000..4edfdbcad --- /dev/null +++ b/cypress/elements/attribute/attributes_details.js @@ -0,0 +1,39 @@ +export const ATTRIBUTES_DETAILS = { + nameInput: '[name="name"]', + codeInput: '[name="slug"]', + inputTypeSelect: '[id="mui-component-select-inputType"]', + assignValuesButton: '[data-test-id="assignValueButton"]', + valueRequired: '[name="valueRequired"]', + valueNameInput: '[data-test-id="valueName"]', + attributesInputTypes: { + DROPDOWN: '[data-test-id="DROPDOWN"]', + MULTISELECT: '[data-test-id="MULTISELECT"]', + FILE: '[data-test-id="FILE"]', + REFERENCE: '[data-test-id="REFERENCE"]', + RICH_TEXT: '[data-test-id="RICH_TEXT"]', + NUMERIC: '[data-test-id="NUMERIC"]', + BOOLEAN: '[data-test-id="BOOLEAN"]' + }, + entityTypeSelect: '[id="mui-component-select-entityType"]', + entityTypeOptions: { + PRODUCT: '[data-test-id="PRODUCT"]', + PAGE: '[data-test-id="PAGE"]' + }, + selectUnitCheckbox: '[name="selectUnit"]', + unitSystemSelect: '[data-test-id="unit-system"]', + unitSystemsOptions: { + IMPERIAL: '[data-test-id="imperial"]', + METRIC: '[data-test-id="metric"]' + }, + unitOfSelect: '[data-test-id="unit-of"]', + unitsOfOptions: { + VOLUME: '[data-test-id="volume"]', + DISTANCE: '[data-test-id="distance"]' + }, + unitSelect: '[data-test-id="unit"]', + unitsOptions: { + CUBIC_CENTIMETER: '[data-test-id="CUBIC_CENTIMETER"]', + FT: '[data-test-id="FT"]' + }, + pageTypeAttributeCheckbox: '[value="PAGE_TYPE"]' +}; diff --git a/cypress/elements/attribute/attributes_list.js b/cypress/elements/attribute/attributes_list.js new file mode 100644 index 000000000..c6c0e559a --- /dev/null +++ b/cypress/elements/attribute/attributes_list.js @@ -0,0 +1,3 @@ +export const ATTRIBUTES_LIST = { + createAttributeButton: '[data-test-id="createAttributeButton"]' +}; diff --git a/cypress/integration/allEnv/configuration/attributes/attributes.js b/cypress/integration/allEnv/configuration/attributes/attributes.js new file mode 100644 index 000000000..62ea080bf --- /dev/null +++ b/cypress/integration/allEnv/configuration/attributes/attributes.js @@ -0,0 +1,120 @@ +// + +import faker from "faker"; + +import { getAttribute } from "../../../../apiRequests/Attribute"; +import { ATTRIBUTES_LIST } from "../../../../elements/attribute/attributes_list"; +import { createAttributeWithInputType } from "../../../../steps/attributesSteps"; +import { urlList } from "../../../../url/urlList"; +import { deleteAttributesStartsWith } from "../../../../utils/attributes.js/attributeUtils"; +import { expectCorrectDataInAttribute } from "../../../../utils/attributes.js/checkAttributeData"; + +describe("Create attribute with type", () => { + const startsWith = "AttrCreate"; + const attributesTypes = [ + "DROPDOWN", + "MULTISELECT", + "FILE", + "RICH_TEXT", + "BOOLEAN" + ]; + const attributeReferenceType = ["PRODUCT", "PAGE"]; + const attributeNumericType = [ + { unitSystem: "IMPERIAL", unitsOf: "DISTANCE", unit: "FT" }, + { unitSystem: "METRIC", unitsOf: "VOLUME", unit: "CUBIC_CENTIMETER" }, + { unitSystem: "without selecting unit" } + ]; + + before(() => { + cy.clearSessionData().loginUserViaRequest(); + deleteAttributesStartsWith(startsWith); + }); + + beforeEach(() => { + cy.clearSessionData() + .loginUserViaRequest() + .visit(urlList.attributes) + .get(ATTRIBUTES_LIST.createAttributeButton) + .click(); + }); + + attributesTypes.forEach(attributeType => { + it(`should create ${attributeType} attribute`, () => { + const attributeName = `${startsWith}${faker.datatype.number()}`; + createAttributeWithInputType({ name: attributeName, attributeType }) + .then(({ attribute }) => { + getAttribute(attribute.id); + }) + .then(attribute => { + expectCorrectDataInAttribute(attribute, { + attributeName, + attributeType + }); + }); + }); + }); + + attributeReferenceType.forEach(entityType => { + it(`should create reference ${entityType} attribute`, () => { + const attributeType = "REFERENCE"; + const attributeName = `${startsWith}${faker.datatype.number()}`; + createAttributeWithInputType({ + name: attributeName, + attributeType, + entityType + }) + .then(({ attribute }) => { + getAttribute(attribute.id); + }) + .then(attribute => { + expectCorrectDataInAttribute(attribute, { + attributeName, + attributeType, + entityType + }); + }); + }); + }); + + attributeNumericType.forEach(numericSystemType => { + it(`should create numeric attribute - ${numericSystemType.unitSystem}`, () => { + const attributeType = "NUMERIC"; + const attributeName = `${startsWith}${faker.datatype.number()}`; + createAttributeWithInputType({ + name: attributeName, + attributeType, + numericSystemType + }) + .then(({ attribute }) => { + getAttribute(attribute.id); + }) + .then(attribute => { + expectCorrectDataInAttribute(attribute, { + attributeName, + attributeType, + unit: numericSystemType.unit + }); + }); + }); + }); + + it("should create attribute without required value", () => { + const attributeType = "BOOLEAN"; + const attributeName = `${startsWith}${faker.datatype.number()}`; + createAttributeWithInputType({ + name: attributeName, + attributeType, + valueRequired: false + }) + .then(({ attribute }) => { + getAttribute(attribute.id); + }) + .then(attribute => { + expectCorrectDataInAttribute(attribute, { + attributeName, + attributeType, + valueRequired: false + }); + }); + }); +}); diff --git a/cypress/integration/allEnv/configuration/attributes/contentAttribute.js b/cypress/integration/allEnv/configuration/attributes/contentAttribute.js new file mode 100644 index 000000000..e4deae53b --- /dev/null +++ b/cypress/integration/allEnv/configuration/attributes/contentAttribute.js @@ -0,0 +1,120 @@ +import faker from "faker"; + +import { getAttribute } from "../../../../apiRequests/Attribute"; +import { ATTRIBUTES_DETAILS } from "../../../../elements/attribute/attributes_details"; +import { ATTRIBUTES_LIST } from "../../../../elements/attribute/attributes_list"; +import { createAttributeWithInputType } from "../../../../steps/attributesSteps"; +import { urlList } from "../../../../url/urlList"; +import { deleteAttributesStartsWith } from "../../../../utils/attributes.js/attributeUtils"; +import { expectCorrectDataInAttribute } from "../../../../utils/attributes.js/checkAttributeData"; + +describe("Create content attribute", () => { + const startsWith = "AttrCont"; + const attributesTypes = [ + "DROPDOWN", + "MULTISELECT", + "FILE", + "RICH_TEXT", + "BOOLEAN" + ]; + const attributeReferenceType = ["PRODUCT", "PAGE"]; + const attributeNumericType = [ + { unitSystem: "IMPERIAL", unitsOf: "DISTANCE", unit: "FT" }, + { unitSystem: "METRIC", unitsOf: "VOLUME", unit: "CUBIC_CENTIMETER" }, + { unitSystem: "without selecting unit" } + ]; + + before(() => { + cy.clearSessionData().loginUserViaRequest(); + deleteAttributesStartsWith(startsWith); + }); + + beforeEach(() => { + cy.clearSessionData() + .loginUserViaRequest() + .visit(urlList.attributes) + .get(ATTRIBUTES_LIST.createAttributeButton) + .click() + .get(ATTRIBUTES_DETAILS.pageTypeAttributeCheckbox) + .click(); + }); + attributesTypes.forEach(attributeType => { + it(`should create ${attributeType} attribute`, () => { + const attributeName = `${startsWith}${faker.datatype.number()}`; + createAttributeWithInputType({ name: attributeName, attributeType }) + .then(({ attribute }) => { + getAttribute(attribute.id); + }) + .then(attribute => { + expectCorrectDataInAttribute(attribute, { + attributeName, + attributeType + }); + }); + }); + }); + + attributeReferenceType.forEach(entityType => { + it(`should create reference ${entityType} attribute`, () => { + const attributeType = "REFERENCE"; + const attributeName = `${startsWith}${faker.datatype.number()}`; + createAttributeWithInputType({ + name: attributeName, + attributeType, + entityType + }) + .then(({ attribute }) => { + getAttribute(attribute.id); + }) + .then(attribute => { + expectCorrectDataInAttribute(attribute, { + attributeName, + attributeType, + entityType + }); + }); + }); + }); + + attributeNumericType.forEach(numericSystemType => { + it(`should create numeric attribute - ${numericSystemType.unitSystem}`, () => { + const attributeType = "NUMERIC"; + const attributeName = `${startsWith}${faker.datatype.number()}`; + createAttributeWithInputType({ + name: attributeName, + attributeType, + numericSystemType + }) + .then(({ attribute }) => { + getAttribute(attribute.id); + }) + .then(attribute => { + expectCorrectDataInAttribute(attribute, { + attributeName, + attributeType, + unit: numericSystemType.unit + }); + }); + }); + }); + + it("should create attribute without required value", () => { + const attributeType = "BOOLEAN"; + const attributeName = `${startsWith}${faker.datatype.number()}`; + createAttributeWithInputType({ + name: attributeName, + attributeType, + valueRequired: false + }) + .then(({ attribute }) => { + getAttribute(attribute.id); + }) + .then(attribute => { + expectCorrectDataInAttribute(attribute, { + attributeName, + attributeType, + valueRequired: false + }); + }); + }); +}); diff --git a/cypress/steps/attributesSteps.js b/cypress/steps/attributesSteps.js new file mode 100644 index 000000000..0a388936d --- /dev/null +++ b/cypress/steps/attributesSteps.js @@ -0,0 +1,82 @@ +import { ATTRIBUTES_DETAILS } from "../elements/attribute/attributes_details"; +import { BUTTON_SELECTORS } from "../elements/shared/button-selectors"; + +export function createAttributeWithInputType({ + name, + attributeType, + entityType, + numericSystemType, + valueRequired = true +}) { + fillUpAttributeCreateFields({ name, attributeType, valueRequired }); + if (attributeType === "DROPDOWN" || attributeType === "MULTISELECT") { + addSingleValue(name); + } + if (attributeType === "REFERENCE") { + selectEntityType(entityType); + } + if (attributeType === "NUMERIC" && numericSystemType.unitsOf) { + selectNumericSystem(numericSystemType); + } + return saveAttribute(); +} + +export function fillUpAttributeCreateFields({ + name, + attributeType, + valueRequired +}) { + cy.get(ATTRIBUTES_DETAILS.nameInput) + .type(name) + .get(ATTRIBUTES_DETAILS.codeInput) + .type(name) + .get(ATTRIBUTES_DETAILS.inputTypeSelect) + .click() + .get(ATTRIBUTES_DETAILS.attributesInputTypes[attributeType]) + .click(); + if (!valueRequired) { + cy.get(ATTRIBUTES_DETAILS.valueRequired).click(); + } +} + +export function saveAttribute() { + return cy + .addAliasToGraphRequest("AttributeCreate") + .get(BUTTON_SELECTORS.confirm) + .click() + .wait("@AttributeCreate") + .its("response.body.data.attributeCreate"); +} + +export function addSingleValue(valueName) { + cy.get(ATTRIBUTES_DETAILS.assignValuesButton) + .click() + .get(ATTRIBUTES_DETAILS.valueNameInput) + .type(valueName) + .get(BUTTON_SELECTORS.submit) + .click(); +} + +export function selectEntityType(entityType) { + cy.get(ATTRIBUTES_DETAILS.entityTypeSelect) + .click() + .get(ATTRIBUTES_DETAILS.entityTypeOptions[entityType]) + .click(); +} + +export function selectNumericSystem({ unitSystem, unitsOf, unit }) { + cy.get(ATTRIBUTES_DETAILS.selectUnitCheckbox) + .click() + .get(ATTRIBUTES_DETAILS.unitSystemSelect) + .click() + .get(ATTRIBUTES_DETAILS.unitSystemsOptions[unitSystem]) + .click() + .get(ATTRIBUTES_DETAILS.unitOfSelect) + .click() + .get(ATTRIBUTES_DETAILS.unitsOfOptions[unitsOf]) + .click() + .get(ATTRIBUTES_DETAILS.unitSelect) + .click() + .get(ATTRIBUTES_DETAILS.unitsOptions[unit]) + .click(); +} diff --git a/cypress/url/urlList.js b/cypress/url/urlList.js index 084f4e312..b1cde33dc 100644 --- a/cypress/url/urlList.js +++ b/cypress/url/urlList.js @@ -14,6 +14,7 @@ export const urlList = { staffMembers: "staff/", newPassword: "new-password/", permissionsGroups: "permission-groups/", + attributes: "attributes/", productTypes: "product-types/" }; export const productDetailsUrl = productId => `${urlList.products}${productId}`; @@ -33,4 +34,4 @@ export const warehouseDetailsUrl = warehouseId => `${urlList.warehouses}${warehouseId}`; export const productTypeDetailsUrl = productTypeId => - `${urlList.productTypes}${productTypeId}`; \ No newline at end of file + `${urlList.productTypes}${productTypeId}`; diff --git a/cypress/utils/attributes.js/attributeUtils.js b/cypress/utils/attributes.js/attributeUtils.js new file mode 100644 index 000000000..442717b14 --- /dev/null +++ b/cypress/utils/attributes.js/attributeUtils.js @@ -0,0 +1,5 @@ +import { deleteAttribute, getAttributes } from "../../apiRequests/Attribute"; + +export function deleteAttributesStartsWith(startsWith) { + cy.deleteElementsStartsWith(deleteAttribute, getAttributes, startsWith); +} diff --git a/cypress/utils/attributes.js/checkAttributeData.js b/cypress/utils/attributes.js/checkAttributeData.js new file mode 100644 index 000000000..31b286f7d --- /dev/null +++ b/cypress/utils/attributes.js/checkAttributeData.js @@ -0,0 +1,19 @@ +const { softExpect } = chai; + +export function expectCorrectDataInAttribute( + attribute, + { + attributeName, + attributeType, + entityType = null, + unit = null, + valueRequired = true + } +) { + softExpect(attribute.name).to.eq(attributeName); + softExpect(attribute.slug).to.eq(attributeName); + softExpect(attribute.inputType).to.eq(attributeType); + softExpect(attribute.entityType).to.eq(entityType); + softExpect(attribute.unit).to.eq(unit); + softExpect(attribute.valueRequired).to.eq(valueRequired); +} diff --git a/cypress/utils/products/productsUtils.js b/cypress/utils/products/productsUtils.js index 87453c332..2f2893257 100644 --- a/cypress/utils/products/productsUtils.js +++ b/cypress/utils/products/productsUtils.js @@ -6,6 +6,7 @@ import { deleteProductType, getProductTypes } from "../../apiRequests/productType"; +import { deleteAttributesStartsWith } from "../attributes.js/attributeUtils"; export function createProductInChannel({ name, @@ -85,6 +86,7 @@ export function createTypeAttributeAndCategoryForProduct( }); } export function deleteProductsStartsWith(startsWith) { + deleteAttributesStartsWith(startsWith); cy.deleteElementsStartsWith(deleteProductType, getProductTypes, startsWith); cy.deleteElementsStartsWith( attributeRequest.deleteAttribute, diff --git a/src/attributes/components/AttributeListPage/AttributeListPage.tsx b/src/attributes/components/AttributeListPage/AttributeListPage.tsx index 09b588644..009fb2721 100644 --- a/src/attributes/components/AttributeListPage/AttributeListPage.tsx +++ b/src/attributes/components/AttributeListPage/AttributeListPage.tsx @@ -58,7 +58,12 @@ const AttributeListPage: React.FC = ({ - = ({ description: "section header" })} toolbar={ -