diff --git a/cypress/apiRequests/Menu.js b/cypress/apiRequests/Menu.js new file mode 100644 index 000000000..0c2b63afb --- /dev/null +++ b/cypress/apiRequests/Menu.js @@ -0,0 +1,68 @@ +export function getMenu(menuId) { + const query = `query{ + menu(id:"${menuId}"){ + id + name + items{ + name + category{ + name + } + collection{ + name + } + page{ + title + } + } + } + }`; + return cy.sendRequestWithQuery(query).its("body.data.menu"); +} + +export function getMenus(first, search) { + const mutation = `query{ + menus(first:${first}, filter:{ + search:"${search}" + }){ + edges{ + node{ + id + name + } + } + } + }`; + return cy + .sendRequestWithQuery(mutation) + .then(resp => resp.body.data.menus.edges); +} + +export function deleteMenu(menuId) { + const mutation = `mutation{ + menuDelete(id:"${menuId}"){ + errors{ + field + message + } + } + }`; + return cy.sendRequestWithQuery(mutation); +} + +export function createMenu(name) { + const mutation = `mutation{ + menuCreate(input:{ + name:"${name}" + }){ + errors{ + field + message + } + menu{ + id + } + } + }`; + return cy.sendRequestWithQuery(mutation).its("body.data.menuCreate"); +} diff --git a/cypress/apiRequests/Page.js b/cypress/apiRequests/Page.js new file mode 100644 index 000000000..60ff84908 --- /dev/null +++ b/cypress/apiRequests/Page.js @@ -0,0 +1,18 @@ +export function createPage({ title, pageTypeId }) { + const mutation = `mutation{ + pageCreate(input:{ + title:"${title}" + pageType:"${pageTypeId}" + }){ + errors{ + field + message + } + page{ + title + id + } + } + }`; + return cy.sendRequestWithQuery(mutation).its("body.data.pageCreate"); +} diff --git a/cypress/apiRequests/PageTypes.js b/cypress/apiRequests/PageTypes.js index 74572708a..acb40cdad 100644 --- a/cypress/apiRequests/PageTypes.js +++ b/cypress/apiRequests/PageTypes.js @@ -1,3 +1,5 @@ +import { getValueWithDefault } from "./utils/Utils"; + export function getPageType(pageTypeId) { const query = `query{ pageType(id:"${pageTypeId}"){ @@ -11,9 +13,14 @@ export function getPageType(pageTypeId) { return cy.sendRequestWithQuery(query).its("body.data.pageType"); } -export function createPageType(name) { +export function createPageType({ name, attributeId }) { + const attributeLine = getValueWithDefault( + attributeId, + `addAttributes:["${attributeId}"]` + ); + const mutation = `mutation{ - pageTypeCreate(input:{ name: "${name}"}){ + pageTypeCreate(input:{ name: "${name}" ${attributeLine}}){ pageType{ name id @@ -26,3 +33,33 @@ export function createPageType(name) { }`; return cy.sendRequestWithQuery(mutation).its("body.data.pageTypeCreate"); } + +export function getPageTypes(first, search) { + const query = `query{ + pageTypes(first: ${first}, filter:{ + search:"${search}" + }){ + edges{ + node{ + id + name + } + } + } + }`; + return cy + .sendRequestWithQuery(query) + .then(resp => resp.body.data.pageTypes.edges); +} + +export function deletePageType(pageTypeId) { + const mutation = `mutation{ + pageTypeDelete(id:"${pageTypeId}"){ + errors{ + field + message + } + } + }`; + return cy.sendRequestWithQuery(mutation); +} diff --git a/cypress/elements/navigation/menu-details.js b/cypress/elements/navigation/menu-details.js new file mode 100644 index 000000000..94b637d4a --- /dev/null +++ b/cypress/elements/navigation/menu-details.js @@ -0,0 +1,10 @@ +export const MENU_DETAILS = { + createNewMenuItemButton: '[data-test-id="createNewMenuItem"]', + newMenuItemForm: { + nameInput: '[name="name"]', + autocompleteSelectReference: '[data-test-id="containerAutocompleteSelect"]', + categoryItem: '[data-test-id="category"]', + collectionItem: '[data-test-id="collection"]', + pageItem: '[data-test-id="page"]' + } +}; diff --git a/cypress/elements/navigation/menu-list.js b/cypress/elements/navigation/menu-list.js new file mode 100644 index 000000000..003ffae4f --- /dev/null +++ b/cypress/elements/navigation/menu-list.js @@ -0,0 +1,6 @@ +export const MENU_LIST = { + addMenuButton: '[data-test-id="addMenu"]', + createMenuForm: { + nameInput: '[name="name"]' + } +}; diff --git a/cypress/elements/shared/sharedElements.js b/cypress/elements/shared/sharedElements.js index a935ae025..80abfba1d 100644 --- a/cypress/elements/shared/sharedElements.js +++ b/cypress/elements/shared/sharedElements.js @@ -6,6 +6,8 @@ export const SHARED_ELEMENTS = { table: 'table[class*="Table"]', tableRow: '[data-test="id"]', confirmationMsg: "[data-test='notification-success']", + notificationSuccess: '[data-test="notification-success"]', + dialog: '[role="dialog"]', searchInput: '[data-test-id="searchInput"]', selectOption: '[data-test="selectFieldOption"]', richTextEditor: { diff --git a/cypress/integration/allEnv/configuration/attributes/attributes.js b/cypress/integration/allEnv/configuration/attributes/attributes.js index 62ea080bf..695aed870 100644 --- a/cypress/integration/allEnv/configuration/attributes/attributes.js +++ b/cypress/integration/allEnv/configuration/attributes/attributes.js @@ -6,8 +6,8 @@ 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"; +import { deleteAttributesStartsWith } from "../../../../utils/attributes/attributeUtils"; +import { expectCorrectDataInAttribute } from "../../../../utils/attributes/checkAttributeData"; describe("Create attribute with type", () => { const startsWith = "AttrCreate"; diff --git a/cypress/integration/allEnv/configuration/attributes/contentAttribute.js b/cypress/integration/allEnv/configuration/attributes/contentAttribute.js index e4deae53b..de6faace1 100644 --- a/cypress/integration/allEnv/configuration/attributes/contentAttribute.js +++ b/cypress/integration/allEnv/configuration/attributes/contentAttribute.js @@ -5,8 +5,8 @@ import { ATTRIBUTES_DETAILS } from "../../../../elements/attribute/attributes_de 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"; +import { deleteAttributesStartsWith } from "../../../../utils/attributes/attributeUtils"; +import { expectCorrectDataInAttribute } from "../../../../utils/attributes/checkAttributeData"; describe("Create content attribute", () => { const startsWith = "AttrCont"; diff --git a/cypress/integration/allEnv/configuration/navigation.js b/cypress/integration/allEnv/configuration/navigation.js new file mode 100644 index 000000000..3f219a895 --- /dev/null +++ b/cypress/integration/allEnv/configuration/navigation.js @@ -0,0 +1,66 @@ +import faker from "faker"; + +import { + createMenu as createMenuViaApi, + getMenu +} from "../../../apiRequests/Menu"; +import { + createMenu, + createNewMenuItem, + MENU_ITEM_TYPES +} from "../../../steps/navigationSteps"; +import { deleteMenusStartsWith } from "../../../utils/navigationUtils"; + +describe("Tests for menu navigation", () => { + const startsWith = "Navigation"; + const randomName = `${startsWith}${faker.datatype.number()}`; + + let menu; + + before(() => { + cy.clearSessionData().loginUserViaRequest(); + deleteMenusStartsWith(startsWith); + createMenuViaApi(randomName).then( + ({ menu: menuResp }) => (menu = menuResp) + ); + }); + + beforeEach(() => { + cy.clearSessionData().loginUserViaRequest(); + }); + + it("should create a menu", () => { + const name = `${startsWith}${faker.datatype.number()}`; + + createMenu(name) + .then(menuResp => { + getMenu(menuResp.id); + }) + .then(menuResp => { + expect(menuResp.name).to.eq(name); + }); + }); + + ["category", "collection", "page"].forEach(itemType => { + it(`should add new ${itemType} item to menu`, () => { + const itemName = `${startsWith}${faker.datatype.number()}`; + let selectedItem; + + createNewMenuItem({ + menuId: menu.id, + name: itemName, + menuItemType: MENU_ITEM_TYPES[itemType] + }) + .then(selectedItemResp => { + selectedItem = selectedItemResp; + getMenu(menu.id); + }) + .then(({ items }) => { + const item = items.find(element => element.name === itemName); + const itemOfType = item[itemType]; + const name = itemType !== "page" ? "name" : "title"; + expect(itemOfType[name]).to.eq(selectedItem); + }); + }); + }); +}); diff --git a/cypress/steps/navigationSteps.js b/cypress/steps/navigationSteps.js new file mode 100644 index 000000000..55bd68d39 --- /dev/null +++ b/cypress/steps/navigationSteps.js @@ -0,0 +1,56 @@ +import { MENU_DETAILS } from "../elements/navigation/menu-details"; +import { MENU_LIST } from "../elements/navigation/menu-list"; +import { BUTTON_SELECTORS } from "../elements/shared/button-selectors"; +import { SHARED_ELEMENTS } from "../elements/shared/sharedElements"; +import { menuDetailsUrl, urlList } from "../url/urlList"; +import { confirmationMessageShouldDisappear } from "./shared/confirmationMessage"; + +export function createMenu(name) { + cy.visit(urlList.navigation) + .get(MENU_LIST.addMenuButton) + .click() + .get(MENU_LIST.createMenuForm.nameInput) + .type(name) + .addAliasToGraphRequest("MenuCreate") + .get(BUTTON_SELECTORS.submit) + .click(); + confirmationMessageShouldDisappear(); + return cy.wait("@MenuCreate").its("response.body.data.menuCreate.menu"); +} + +export function createNewMenuItem({ menuId, name, menuItemType }) { + let selectedItem; + + return cy + .visit(menuDetailsUrl(menuId)) + .get(MENU_DETAILS.createNewMenuItemButton) + .click() + .get(SHARED_ELEMENTS.dialog) + .find(MENU_DETAILS.newMenuItemForm.nameInput) + .type(name) + .get(MENU_DETAILS.newMenuItemForm.autocompleteSelectReference) + .click() + .get(SHARED_ELEMENTS.progressBar) + .should("be.not.visible") + .get(MENU_DETAILS.newMenuItemForm[menuItemType]) + .click() + .get(MENU_DETAILS.newMenuItemForm[menuItemType]) + .first() + .click() + .invoke("text") + .then(text => { + selectedItem = text; + cy.addAliasToGraphRequest("MenuItemCreate") + .get(BUTTON_SELECTORS.submit) + .click(); + confirmationMessageShouldDisappear(); + cy.wait("@MenuItemCreate"); + }) + .then(() => selectedItem); +} + +export const MENU_ITEM_TYPES = { + category: "categoryItem", + collection: "collectionItem", + page: "pageItem" +}; diff --git a/cypress/url/urlList.js b/cypress/url/urlList.js index 731a6a575..a9c03bd5d 100644 --- a/cypress/url/urlList.js +++ b/cypress/url/urlList.js @@ -11,6 +11,7 @@ export const urlList = { draftOrders: "orders/drafts/", homePage: "/", newPassword: "new-password/", + navigation: "navigation/", orders: "orders/", pageTypes: "page-types/", permissionsGroups: "permission-groups/", @@ -50,6 +51,8 @@ export const warehouseDetailsUrl = warehouseId => export const productTypeDetailsUrl = productTypeId => `${urlList.productTypes}${productTypeId}`; +export const menuDetailsUrl = menuId => `${urlList.navigation}${menuId}`; + export const customerDetailsUrl = customerId => `${urlList.customers}${customerId}`; diff --git a/cypress/utils/attributes.js/attributeUtils.js b/cypress/utils/attributes/attributeUtils.js similarity index 100% rename from cypress/utils/attributes.js/attributeUtils.js rename to cypress/utils/attributes/attributeUtils.js diff --git a/cypress/utils/attributes.js/checkAttributeData.js b/cypress/utils/attributes/checkAttributeData.js similarity index 100% rename from cypress/utils/attributes.js/checkAttributeData.js rename to cypress/utils/attributes/checkAttributeData.js diff --git a/cypress/utils/navigationUtils.js b/cypress/utils/navigationUtils.js new file mode 100644 index 000000000..5334053cf --- /dev/null +++ b/cypress/utils/navigationUtils.js @@ -0,0 +1,5 @@ +import { deleteMenu, getMenus } from "../apiRequests/Menu"; + +export function deleteMenusStartsWith(startsWith) { + cy.deleteElementsStartsWith(deleteMenu, getMenus, startsWith); +} diff --git a/cypress/utils/products/productsUtils.js b/cypress/utils/products/productsUtils.js index 064842cda..eb3f499e5 100644 --- a/cypress/utils/products/productsUtils.js +++ b/cypress/utils/products/productsUtils.js @@ -6,7 +6,7 @@ import { deleteProductType, getProductTypes } from "../../apiRequests/productType"; -import { deleteAttributesStartsWith } from "../attributes.js/attributeUtils"; +import { deleteAttributesStartsWith } from "../attributes/attributeUtils"; export function createProductInChannel({ name, diff --git a/src/components/AutocompleteSelectMenu/AutocompleteSelectMenu.tsx b/src/components/AutocompleteSelectMenu/AutocompleteSelectMenu.tsx index 24d3dbb8c..a51849070 100644 --- a/src/components/AutocompleteSelectMenu/AutocompleteSelectMenu.tsx +++ b/src/components/AutocompleteSelectMenu/AutocompleteSelectMenu.tsx @@ -27,6 +27,7 @@ export interface AutocompleteSelectMenuProps { loading: boolean; name: string; options: IMenu; + testIds?: string[]; placeholder: string; onChange: (event: React.ChangeEvent) => void; onInputChange?: (value: string) => void; @@ -72,6 +73,7 @@ const AutocompleteSelectMenu: React.FC = props => { loading, name, options, + testIds, placeholder, onChange, onInputChange @@ -110,7 +112,10 @@ const AutocompleteSelectMenu: React.FC = props => { onSelect={handleChange} > {({ getItemProps, isOpen, openMenu, closeMenu, selectItem }) => ( -
+
, @@ -157,6 +162,7 @@ const AutocompleteSelectMenu: React.FC = props => { : options ).map((suggestion, index) => ( = ({ color="primary" variant="contained" onClick={submit} + data-test="submit" > diff --git a/src/navigation/components/MenuItemDialog/MenuItemDialog.tsx b/src/navigation/components/MenuItemDialog/MenuItemDialog.tsx index 4eaebcb67..f4e82023d 100644 --- a/src/navigation/components/MenuItemDialog/MenuItemDialog.tsx +++ b/src/navigation/components/MenuItemDialog/MenuItemDialog.tsx @@ -116,6 +116,7 @@ const MenuItemDialog: React.FC = ({ const mutationErrors = errors.filter(err => err.field === null); const formErrors = getFormErrors(["name"], errors); + const testIds = ["category", "collection", "page", "url"]; const idError = ["category", "collection", "page", "url"] .map(field => getFieldError(errors, field)) .reduce((acc, err) => acc || err); @@ -270,6 +271,7 @@ const MenuItemDialog: React.FC = ({ displayValue={displayValue} loading={loading} options={options} + testIds={testIds} error={!!idError} helperText={getMenuErrorMessage(idError, intl)} placeholder={intl.formatMessage({ @@ -294,6 +296,7 @@ const MenuItemDialog: React.FC = ({ = props => { )}
-