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 = ({
-