diff --git a/cypress/Data/permissions.js b/cypress/Data/permissions.js new file mode 100644 index 000000000..063faade2 --- /dev/null +++ b/cypress/Data/permissions.js @@ -0,0 +1,90 @@ +import * as menuSelectors from "../elements/account/left-menu/left-menu-selectors"; +import { CONFIGURATION_SELECTORS } from "../elements/configuration/configuration-selectors"; + +const configurationAsParent = { + parentMenuSelector: menuSelectors.LEFT_MENU_SELECTORS.configuration, + parentSelectors: CONFIGURATION_SELECTORS +}; + +export const PERMISSIONS = { + app: { + permissionSelectors: [menuSelectors.LEFT_MENU_SELECTORS.app] + }, + customer: { + permissionSelectors: [menuSelectors.LEFT_MENU_SELECTORS.customers] + }, + discounts: { + parent: { + parentMenuSelector: menuSelectors.LEFT_MENU_SELECTORS.discounts, + parentSelectors: [menuSelectors.DISCOUNTS_MENU_SELECTORS] + }, + permissionSelectors: [ + menuSelectors.DISCOUNTS_MENU_SELECTORS.sales, + menuSelectors.DISCOUNTS_MENU_SELECTORS.sales + ] + }, + order: { + parent: { + parentMenuSelector: menuSelectors.LEFT_MENU_SELECTORS.orders, + parentSelectors: menuSelectors.ORDERS + }, + permissionSelectors: [ + menuSelectors.ORDERS.orders, + menuSelectors.ORDERS.draftOrders + ] + }, + page: { + parent: configurationAsParent, + permissionSelectors: [ + CONFIGURATION_SELECTORS.pageTypes, + CONFIGURATION_SELECTORS.pages + ] + }, + plugin: { + parent: configurationAsParent, + permissionSelectors: [CONFIGURATION_SELECTORS.plugin] + }, + product: { + parent: { + parentMenuSelector: menuSelectors.LEFT_MENU_SELECTORS.catalog, + parentSelectors: menuSelectors.CATALOG + }, + permissionSelectors: [ + menuSelectors.CATALOG.categories, + menuSelectors.CATALOG.collections, + menuSelectors.CATALOG.products + ] + }, + productTypeAndAttribute: { + parent: configurationAsParent, + permissionSelectors: [ + CONFIGURATION_SELECTORS.attributes, + CONFIGURATION_SELECTORS.productTypes + ] + }, + settings: { + parent: configurationAsParent, + permissionSelectors: [ + CONFIGURATION_SELECTORS.taxes, + CONFIGURATION_SELECTORS.settings + ] + }, + shipping: { + parent: configurationAsParent, + permissionSelectors: [CONFIGURATION_SELECTORS.shipping] + }, + staff: { + parent: configurationAsParent, + permissionSelectors: [ + CONFIGURATION_SELECTORS.staffMembers, + CONFIGURATION_SELECTORS.permissionGroups + ] + }, + translations: { + permissionSelectors: [menuSelectors.LEFT_MENU_SELECTORS.translations] + }, + warehouse: { + parent: configurationAsParent, + permissionSelectors: [CONFIGURATION_SELECTORS.warehouse] + } +}; diff --git a/cypress/Data/permissionsUsers.js b/cypress/Data/permissionsUsers.js new file mode 100644 index 000000000..736d8d458 --- /dev/null +++ b/cypress/Data/permissionsUsers.js @@ -0,0 +1,63 @@ +import { PERMISSIONS } from "./permissions"; +import { ONE_PERMISSION_USERS, TEST_ADMIN_USER } from "./users"; + +export const PERMISSIONS_OPTIONS = { + all: { + user: TEST_ADMIN_USER, + permissions: Object.values(PERMISSIONS) + }, + app: { + user: ONE_PERMISSION_USERS.app, + permissions: [PERMISSIONS.app] + }, + customer: { + user: ONE_PERMISSION_USERS.user, + permissions: [PERMISSIONS.customer] + }, + discount: { + user: ONE_PERMISSION_USERS.discount, + permissions: [PERMISSIONS.discounts] + }, + giftCard: { + user: ONE_PERMISSION_USERS.giftCard + }, + order: { + user: ONE_PERMISSION_USERS.order, + permissions: [PERMISSIONS.order] + }, + page: { + user: ONE_PERMISSION_USERS.page, + permissions: [PERMISSIONS.page] + }, + pageTypeAndAttribute: { + user: ONE_PERMISSION_USERS.pageTypeAndAttribute + }, + plugin: { + user: ONE_PERMISSION_USERS.plugin, + permissions: [PERMISSIONS.plugin] + }, + product: { + user: ONE_PERMISSION_USERS.product, + permissions: [PERMISSIONS.product, PERMISSIONS.warehouse] + }, + productTypeAndAttribute: { + user: ONE_PERMISSION_USERS.productTypeAndAttribute, + permissions: [PERMISSIONS.productTypeAndAttribute] + }, + settings: { + user: ONE_PERMISSION_USERS.settings, + permissions: [PERMISSIONS.settings] + }, + shipping: { + user: ONE_PERMISSION_USERS.shipping, + permissions: [PERMISSIONS.shipping] + }, + staff: { + user: ONE_PERMISSION_USERS.staff, + permissions: [PERMISSIONS.staff] + }, + translations: { + user: ONE_PERMISSION_USERS.translations, + permissions: [PERMISSIONS.translations] + } +}; diff --git a/cypress/Data/users.js b/cypress/Data/users.js index 7b6cfda6e..ec73ef704 100644 --- a/cypress/Data/users.js +++ b/cypress/Data/users.js @@ -8,3 +8,27 @@ export const USER_WITHOUT_NAME = { email: Cypress.env("SECOND_USER_NAME"), password: Cypress.env("USER_PASSWORD") }; +export const ONE_PERMISSION_USERS = { + shipping: getOnePermissionUser("shipping.manager@example.com"), + giftCard: getOnePermissionUser("gift.card.manager@example.com"), + app: getOnePermissionUser("app.manager@example.com"), + settings: getOnePermissionUser("setting.manager@example.com"), + page: getOnePermissionUser("page.manager@example.com"), + order: getOnePermissionUser("order.manager@example.com"), + translations: getOnePermissionUser("translation.manager@example.com"), + menu: getOnePermissionUser("menu.manager@example.com"), + staff: getOnePermissionUser("staff.manager@example.com"), + user: getOnePermissionUser("user.manager@example.com"), + pageTypeAndAttribute: getOnePermissionUser( + "page.type.and.attribute.manager@example.com" + ), + productTypeAndAttribute: getOnePermissionUser( + "product.type.and.attribute.manager@example.com" + ), + discount: getOnePermissionUser("discount.manager@example.com"), + plugin: getOnePermissionUser("plugin.manager@example.com"), + product: getOnePermissionUser("product.manager@example.com") +}; +function getOnePermissionUser(email) { + return { email, password: Cypress.env("PERMISSIONS_USERS_PASSWORD") }; +} diff --git a/cypress/elements/account/left-menu/left-menu-selectors.js b/cypress/elements/account/left-menu/left-menu-selectors.js index 6181a5ab0..6d9b928a2 100644 --- a/cypress/elements/account/left-menu/left-menu-selectors.js +++ b/cypress/elements/account/left-menu/left-menu-selectors.js @@ -2,6 +2,22 @@ export const LEFT_MENU_SELECTORS = { catalog: "[data-test='menu-item-label'][data-test-id='catalogue']", configuration: "[data-test='menu-item-label'][data-test-id='configure']", home: "[data-test='menu-item-label'][data-test-id='home']", - orders: "[data-test='menu-item-label'][data-test-id=orders']", - products: "[data-test='submenu-item-label'][data-test-id='products']" + orders: "[data-test='menu-item-label'][data-test-id='orders']", + discounts: "[data-test='menu-item-label'][data-test-id='discounts']", + app: "[data-test='menu-item-label'][data-test-id='apps']", + translations: "[data-test='menu-item-label'][data-test-id='translations']", + customers: "[data-test='menu-item-label'][data-test-id='customers']" +}; +export const DISCOUNTS_MENU_SELECTORS = { + sales: "[data-test='submenu-item-label'][data-test-id='sales']", + vouchers: "[data-test='submenu-item-label'][data-test-id='vouchers']" +}; +export const ORDERS = { + orders: "[data-test='submenu-item-label'][data-test-id='orders']", + draftOrders: "[data-test='submenu-item-label'][data-test-id='order drafts']" +}; +export const CATALOG = { + products: "[data-test='submenu-item-label'][data-test-id='products']", + categories: "[data-test='submenu-item-label'][data-test-id='categories']", + collections: "[data-test='submenu-item-label'][data-test-id='collections']" }; diff --git a/cypress/elements/configuration/configuration-selectors.js b/cypress/elements/configuration/configuration-selectors.js index a158e2cd8..8cb5ddbca 100644 --- a/cypress/elements/configuration/configuration-selectors.js +++ b/cypress/elements/configuration/configuration-selectors.js @@ -1,3 +1,15 @@ export const CONFIGURATION_SELECTORS = { - channels: "[data-testid='channels']" + channels: "[data-testid='channels']", + shipping: '[data-testid="shipping methods"]', + taxes: '[data-test-id="configurationMenuTaxes"]', + settings: '[data-test-id="configurationMenuSiteSettings"]', + pageTypes: '[data-test-id="configurationMenuPageTypes"]', + pages: '[data-test-id="configurationMenuPages"]', + navigation: '[data-test-id="configurationMenuNavigation"]', + staffMembers: '[data-test-id="configurationMenuStaff"]', + permissionGroups: '[data-test-id="configurationMenuPermissionGroups"]', + attributes: '[data-test-id="configurationMenuAttributes"]', + productTypes: '[data-test-id="configurationMenuProductTypes"]', + plugin: '[data-test-id="configurationPluginsPages"]', + warehouse: '[data-test-id="configurationMenuWarehouses"]' }; diff --git a/cypress/elements/shared/sharedElements.js b/cypress/elements/shared/sharedElements.js new file mode 100644 index 000000000..17b4858b0 --- /dev/null +++ b/cypress/elements/shared/sharedElements.js @@ -0,0 +1,3 @@ +export const SHARED_ELEMENTS = { + header: "[data-test-id='page-header']" +}; diff --git a/cypress/integration/navigation.js b/cypress/integration/navigation.js new file mode 100644 index 000000000..1da549182 --- /dev/null +++ b/cypress/integration/navigation.js @@ -0,0 +1,45 @@ +import { PERMISSIONS_OPTIONS } from "../Data/permissionsUsers"; +import * as permissionsSteps from "../steps/permissions"; + +describe("Navigation for users with different permissions", () => { + Object.keys(PERMISSIONS_OPTIONS).forEach(key => { + it(`should navigate as an user with ${key} permission`, () => { + const permissionOption = PERMISSIONS_OPTIONS[key]; + const permissions = permissionOption.permissions; + cy.clearSessionData(); + permissionsSteps.navigateToAllAvailablePageAndCheckIfDisplayed( + permissionOption + ); + if (key === "all") { + return; + } + permissionsSteps + .getDisplayedSelectors() + .then(selectors => { + permissionsSteps.expectAllSelectorsPermitted(permissions, selectors); + }) + .then(() => { + if (!permissions) { + return; + } + permissions.forEach(permission => { + if (permission.parent) { + cy.get(permission.parent.parentMenuSelector) + .click() + .then(() => { + permissionsSteps.getDisplayedSelectors( + permission.parent.parentSelectors + ); + }) + .then(parentSelectors => { + permissionsSteps.expectAllSelectorsPermitted( + permissions, + parentSelectors + ); + }); + } + }); + }); + }); + }); +}); diff --git a/cypress/integration/products/products.js b/cypress/integration/products/products.js index f594d32d5..7988137f8 100644 --- a/cypress/integration/products/products.js +++ b/cypress/integration/products/products.js @@ -1,5 +1,4 @@ // -import { LEFT_MENU_SELECTORS } from "../../elements/account/left-menu/left-menu-selectors"; import { PRODUCTS_SELECTORS } from "../../elements/catalog/products/product-selectors"; import { urlList } from "../../url/urlList"; @@ -8,16 +7,6 @@ describe("Products", () => { cy.clearSessionData().loginUserViaRequest(); }); - it("should navigate to channels page", () => { - cy.visit(urlList.homePage) - .get(LEFT_MENU_SELECTORS.catalog) - .click() - .get(LEFT_MENU_SELECTORS.products) - .click() - .location("pathname") - .should("contain", "/products"); - }); - it("should add new visible product", () => { cy.visit(urlList.products) .get(PRODUCTS_SELECTORS.createProductBtn) diff --git a/cypress/steps/permissions.js b/cypress/steps/permissions.js new file mode 100644 index 000000000..e8de98b9c --- /dev/null +++ b/cypress/steps/permissions.js @@ -0,0 +1,77 @@ +import { LEFT_MENU_SELECTORS } from "../elements/account/left-menu/left-menu-selectors"; +import { SHARED_ELEMENTS } from "../elements/shared/sharedElements"; +import { urlList } from "../url/urlList"; + +/* eslint-disable no-unused-expressions */ + +export function navigateToAllAvailablePageAndCheckIfDisplayed({ + user, + permissions +}) { + cy.loginUserViaRequest("auth", user); + cy.visit(urlList.homePage); + if (!permissions) { + return; + } + return permissions.forEach(permission => + permission.permissionSelectors.forEach(permissionSelector => { + navigateToAvailablePageAndCheckIfDisplayed( + permission.parent, + permissionSelector + ); + }) + ); +} +function navigateToAvailablePageAndCheckIfDisplayed( + parent, + permissionSelector +) { + if (parent) { + cy.get(parent.parentMenuSelector).click(); + } + return cy + .get(permissionSelector) + .click() + .then(() => { + isElementDisplayed(); + }) + .then(isDisplayed => { + expect(isDisplayed).to.be.true; + }); +} +export function isElementDisplayed(element = SHARED_ELEMENTS.header) { + return cy.get("body").then(body => body.find(element).length > 0); +} +export function getDisplayedSelectors(selectors = LEFT_MENU_SELECTORS) { + const displayedSelectors = {}; + cy.wrap(displayedSelectors).as("displayedSelectors"); + + Object.values(selectors).forEach((value, i) => + isElementDisplayed(value).then(isDisplayed => { + if (isDisplayed) { + cy.wrap(value); + displayedSelectors["link" + i] = value; + cy.wrap(displayedSelectors).as("displayedSelectors"); + } + }) + ); + return cy.get("@displayedSelectors"); +} +export function expectAllSelectorsPermitted(permissions, selectors) { + Object.values(selectors).forEach(selector => { + const isSelectorPermitted = isPermitted(permissions, selector); + expect(isSelectorPermitted).to.be.true; + }); +} +function isPermitted(permissions, selector) { + let permittedSelectors = [LEFT_MENU_SELECTORS.home]; + permissions.forEach(permission => { + if (permission.parent) { + permittedSelectors.push(permission.parent.parentMenuSelector); + } + permittedSelectors = permittedSelectors.concat( + permission.permissionSelectors + ); + }); + return permittedSelectors.includes(selector); +} diff --git a/src/configuration/ConfigurationPage.tsx b/src/configuration/ConfigurationPage.tsx index 502b9568c..38b8563f6 100644 --- a/src/configuration/ConfigurationPage.tsx +++ b/src/configuration/ConfigurationPage.tsx @@ -19,6 +19,7 @@ export interface MenuItem { permission: PermissionEnum; title: string; url?: string; + testId?: string; } export interface MenuSection { @@ -128,6 +129,7 @@ export const ConfigurationPage: React.FC = props => { key={itemIndex} data-test="settingsSubsection" data-testid={item.title.toLowerCase()} + data-test-id={item.testId} >
{item.icon}
diff --git a/src/configuration/index.tsx b/src/configuration/index.tsx index 60bc1582f..8eb051fc5 100644 --- a/src/configuration/index.tsx +++ b/src/configuration/index.tsx @@ -50,7 +50,8 @@ export function createConfigurationMenu(intl: IntlShape): MenuSection[] { icon: , permission: PermissionEnum.MANAGE_PRODUCT_TYPES_AND_ATTRIBUTES, title: intl.formatMessage(sectionNames.attributes), - url: attributeListUrl() + url: attributeListUrl(), + testId: "configurationMenuAttributes" }, { description: intl.formatMessage({ @@ -60,7 +61,8 @@ export function createConfigurationMenu(intl: IntlShape): MenuSection[] { icon: , permission: PermissionEnum.MANAGE_PRODUCT_TYPES_AND_ATTRIBUTES, title: intl.formatMessage(sectionNames.productTypes), - url: productTypeListUrl() + url: productTypeListUrl(), + testId: "configurationMenuProductTypes" } ] }, @@ -77,7 +79,8 @@ export function createConfigurationMenu(intl: IntlShape): MenuSection[] { icon: , permission: PermissionEnum.MANAGE_SETTINGS, title: intl.formatMessage(sectionNames.taxes), - url: taxSection + url: taxSection, + testId: "configurationMenuTaxes" } ] }, @@ -94,7 +97,8 @@ export function createConfigurationMenu(intl: IntlShape): MenuSection[] { icon: , permission: PermissionEnum.MANAGE_STAFF, title: intl.formatMessage(sectionNames.staff), - url: staffListUrl() + url: staffListUrl(), + testId: "configurationMenuStaff" }, { description: intl.formatMessage({ @@ -105,7 +109,8 @@ export function createConfigurationMenu(intl: IntlShape): MenuSection[] { icon: , permission: PermissionEnum.MANAGE_STAFF, title: intl.formatMessage(sectionNames.permissionGroups), - url: permissionGroupListUrl() + url: permissionGroupListUrl(), + testId: "configurationMenuPermissionGroups" } ] }, @@ -122,7 +127,8 @@ export function createConfigurationMenu(intl: IntlShape): MenuSection[] { icon: , permission: PermissionEnum.MANAGE_SHIPPING, title: intl.formatMessage(sectionNames.shipping), - url: shippingZonesListUrl() + url: shippingZonesListUrl(), + testId: "configurationMenuShipping" }, { description: intl.formatMessage({ @@ -132,7 +138,8 @@ export function createConfigurationMenu(intl: IntlShape): MenuSection[] { icon: , permission: PermissionEnum.MANAGE_PRODUCTS, title: intl.formatMessage(sectionNames.warehouses), - url: warehouseSection + url: warehouseSection, + testId: "configurationMenuWarehouses" } ] }, @@ -149,7 +156,8 @@ export function createConfigurationMenu(intl: IntlShape): MenuSection[] { icon: , permission: PermissionEnum.MANAGE_CHANNELS, title: intl.formatMessage(sectionNames.channels), - url: channelsListUrl() + url: channelsListUrl(), + testId: "configurationMenuChannels" } ] }, @@ -166,7 +174,8 @@ export function createConfigurationMenu(intl: IntlShape): MenuSection[] { icon: , permission: PermissionEnum.MANAGE_PAGES, title: intl.formatMessage(sectionNames.pageTypes), - url: pageTypeListUrl() + url: pageTypeListUrl(), + testId: "configurationMenuPageTypes" }, { description: intl.formatMessage({ @@ -176,7 +185,8 @@ export function createConfigurationMenu(intl: IntlShape): MenuSection[] { icon: , permission: PermissionEnum.MANAGE_PAGES, title: intl.formatMessage(sectionNames.pages), - url: pageListUrl() + url: pageListUrl(), + testId: "configurationMenuPages" } ] }, @@ -193,7 +203,8 @@ export function createConfigurationMenu(intl: IntlShape): MenuSection[] { icon: , permission: PermissionEnum.MANAGE_MENUS, title: intl.formatMessage(sectionNames.navigation), - url: menuListUrl() + url: menuListUrl(), + testId: "configurationMenuNavigation" }, { description: intl.formatMessage({ @@ -203,7 +214,8 @@ export function createConfigurationMenu(intl: IntlShape): MenuSection[] { icon: , permission: PermissionEnum.MANAGE_SETTINGS, title: intl.formatMessage(sectionNames.siteSettings), - url: siteSettingsUrl() + url: siteSettingsUrl(), + testId: "configurationMenuSiteSettings" }, { description: intl.formatMessage({ @@ -219,7 +231,8 @@ export function createConfigurationMenu(intl: IntlShape): MenuSection[] { ), permission: PermissionEnum.MANAGE_PLUGINS, title: intl.formatMessage(sectionNames.plugins), - url: pluginListUrl() + url: pluginListUrl(), + testId: "configurationPluginsPages" } ] } diff --git a/src/storybook/__snapshots__/Stories.test.ts.snap b/src/storybook/__snapshots__/Stories.test.ts.snap index d3f988554..0de87082e 100644 --- a/src/storybook/__snapshots__/Stories.test.ts.snap +++ b/src/storybook/__snapshots__/Stories.test.ts.snap @@ -29,7 +29,7 @@ exports[`Storyshots Attributes / Attributes default 1`] = ` style="padding:24px" >