diff --git a/cypress/components/menu/menu.cy.tsx b/cypress/components/menu/menu.cy.tsx deleted file mode 100644 index a0ee77f55d..0000000000 --- a/cypress/components/menu/menu.cy.tsx +++ /dev/null @@ -1,2024 +0,0 @@ -import React from "react"; -import { - Menu, - MenuProps, - MenuItem, - MenuWithChildren, - MenuDivider, - MenuDividerProps, - MenuSegmentTitle, - MenuFullscreenProps, - ScrollableBlockProps, -} from "../../../src/components/menu"; -import Box from "../../../src/components/box"; -import { - submenuBlock, - innerMenu, - submenu, - scrollBlock, - lastSubmenuElement, - menuDivider, - segmentTitle, - menuComponent, - submenuItem, - fullscreenMenu, - fullScreenMenuWrapper, - menu, - menuItem, - fullScreenMenuItem, - searchDefaultInput, - searchCrossIcon, - searchButton, -} from "../../locators/menu"; -import { getComponent, closeIconButton, icon } from "../../locators"; -import { - keyCode, - positionOfElement, - pressTABKey, - continuePressingTABKey, -} from "../../support/helper"; -import { CHARACTERS } from "../../support/component-helper/constants"; -import { - checkGoldenOutline, - assertCssValueIsApproximately, -} from "../../support/component-helper/common-steps"; -import CypressMountWithProviders from "../../support/component-helper/cypress-mount"; -import { - MenuComponent, - MenuComponentScrollable, - MenuComponentSearch, - MenuWithChildrenUpdating, - MenuComponentFullScreen, - MenuFullScreenBackgroundScrollTest, - MenuFullScreenWithFalsyValues, - MenuFullScreenKeysTest, - MenuComponentItems, - MenuFullScreenWithSearchButton, - MenuComponentScrollableParent, - MenuComponentWithIcon, - MenuComponentButtonIcon, - MenuSegmentTitleComponent, - MenuItems, - ClosedMenuFullScreenWithButtons, - MenuDividerComponent, - InGlobalHeaderStory, -} from "../../../src/components/menu/menu-test.stories"; -import { NavigationBarWithSubmenuAndChangingHeight } from "../../../src/components/navigation-bar/navigation-bar-test.stories"; - -const span = "span"; -const div = "div"; - -context("Testing Menu component", () => { - describe("check props for Menu component", () => { - it("should verify scroll block within a submenu is scrollable", () => { - CypressMountWithProviders(); - - submenu().eq(positionOfElement("first")).trigger("mouseover"); - scrollBlock().scrollTo("bottom"); - lastSubmenuElement("li").should("be.visible"); - }); - - it("should verify a submenu can be navigated using keyboard tabbing after an item was clicked", () => { - CypressMountWithProviders(); - - submenu().eq(positionOfElement("first")).trigger("mouseover"); - innerMenu(positionOfElement("third"), span).click({ multiple: true }); - cy.focused().tab(); - cy.focused().should("contain", "Item Submenu Three"); - cy.focused().parent().should("have.css", "box-shadow", "none"); - cy.focused().tab(); - cy.focused().should("contain", "Item Submenu Four"); - cy.focused().parent().should("have.css", "box-shadow", "none"); - }); - - it("should verify a submenu can be navigated using keyboard down arrow after an item was clicked", () => { - CypressMountWithProviders(); - - submenu().eq(positionOfElement("first")).trigger("mouseover"); - innerMenu(positionOfElement("third"), span).click({ multiple: true }); - cy.focused().trigger("keydown", keyCode("downarrow")); - cy.focused().should("contain", "Item Submenu Three"); - cy.focused().trigger("keydown", keyCode("downarrow")); - cy.focused().should("contain", "Item Submenu Four"); - }); - - it("should verify a submenu can be navigated using keyboard shift + tabbing after an item was clicked", () => { - CypressMountWithProviders(); - - submenu().eq(positionOfElement("first")).trigger("mouseover"); - innerMenu(positionOfElement("fifth"), span).click({ multiple: true }); - cy.focused().tab({ shift: true }); - cy.focused().should("contain", "Item Submenu Two"); - cy.focused().tab({ shift: true }); - cy.focused().should("contain", "Item Submenu One"); - }); - - it("should verify a submenu can be navigated using keyboard up arrow after an item was clicked", () => { - CypressMountWithProviders(); - - submenu().eq(positionOfElement("first")).trigger("mouseover"); - innerMenu(positionOfElement("fifth"), span).click({ multiple: true }); - cy.focused().trigger("keydown", keyCode("uparrow")); - cy.focused().should("contain", "Item Submenu Two"); - cy.focused().trigger("keydown", keyCode("uparrow")); - cy.focused().should("contain", "Item Submenu One"); - }); - - it("should verify a the first submenu item is focused using keyboard tabbing after the parent item was clicked", () => { - CypressMountWithProviders(); - - submenu().eq(positionOfElement("first")).click(); - cy.focused().tab(); - cy.focused().should("contain", "Item Submenu One"); - }); - - it("should verify a the first submenu item is focused using keyboard down arrow after the parent item was clicked", () => { - CypressMountWithProviders(); - - submenu().eq(positionOfElement("first")).click(); - cy.focused().trigger("keydown", keyCode("downarrow")); - cy.focused().should("contain", "Item Submenu One"); - }); - - it("should verify number and type of elements in submenu", () => { - const position = ["second", "third", "fifth", "sixth"] as const; - - CypressMountWithProviders(); - - submenu().eq(positionOfElement("first")).trigger("mouseover"); - submenuBlock().children().should("have.length", 5); - for (let i = 0; i < position.length; i++) { - innerMenu(positionOfElement(position[i]), span).should( - "have.attr", - "data-component", - "link" - ); - } - innerMenu(positionOfElement("fourth"), div).should( - "have.attr", - "data-component", - "menu-divider" - ); - }); - - it.each([ - ["white", "rgb(230, 235, 237)", "first"], - ["light", "rgb(255, 255, 255)", "third"], - ["dark", "rgb(0, 25, 38)", "fifth"], - ["black", "rgb(38, 38, 38)", "seventh"], - ] as [string, string, "first" | "third" | "fifth" | "seventh"][])( - "should verify submenu background color is %s", - (colorName, color, menuNumber) => { - CypressMountWithProviders(); - - submenu().eq(positionOfElement(menuNumber)).trigger("mouseover"); - innerMenu(positionOfElement("second"), span).should( - "have.css", - "background-color", - color - ); - } - ); - - it.each([ - ["white", "rgb(255, 255, 255)", "first"], - ["light", "rgb(230, 235, 237)", "fifth"], - ["dark", "rgb(0, 50, 76)", "ninth"], - ["black", "rgb(0, 0, 0)", "thirteenth"], - ] as [string, string, "first" | "fifth" | "ninth" | "thirteenth"][])( - "should verify Menu background color is %s", - (colorName, color, menuNumber) => { - CypressMountWithProviders(); - menuItem() - .eq(positionOfElement(menuNumber)) - .children() - .should("have.css", "background-color", color); - } - ); - - it.each([ - ["white", "rgba(0, 0, 0, 0.9)"], - ["light", "rgba(0, 0, 0, 0.9)"], - ["dark", "rgb(255, 255, 255)"], - ["black", "rgb(255, 255, 255)"], - ] as [MenuProps["menuType"], string][])( - "should verify icons have the correct color when menuType is %s", - (menuType, color) => { - CypressMountWithProviders( - - {}} icon="home"> - Foo - - - ); - pressTABKey(1); - icon().should("have.css", "color", color); - } - ); - - it.each([ - ["default", 1], - ["large", 4], - ] as [MenuDividerProps["size"], number][])( - "should verify Menu Divider size is %s", - (size, height) => { - CypressMountWithProviders(); - - submenu().eq(positionOfElement("first")).trigger("mouseover"); - menuDivider().then(($el) => { - assertCssValueIsApproximately($el, "height", height); - }); - } - ); - - it("should verify Menu Segment Title is visible within a submenu", () => { - CypressMountWithProviders(); - - submenu().eq(positionOfElement("second")).trigger("mouseover"); - segmentTitle() - .should("have.text", "segment title") - .and("be.visible") - .and("have.css", "color", "rgba(0, 0, 0, 0.65)"); - }); - - it("should verify default menu clickToOpen element does not open on hover", () => { - CypressMountWithProviders(); - - menuComponent(positionOfElement("fourth")).trigger("mouseover", { - force: true, - }); - submenuItem(positionOfElement("fourth")) - .should("have.length", 0) - .and("not.exist"); - }); - - it("should verify default menu clickToOpen element opens on click", () => { - CypressMountWithProviders(); - - menuComponent(positionOfElement("sixth")).click(); - submenuItem(positionOfElement("sixth")).should("have.length", 2); - innerMenu(positionOfElement("second"), span) - .should("have.attr", "data-component", "link") - .and("be.visible"); - innerMenu(positionOfElement("third"), span) - .should("have.attr", "data-component", "link") - .and("be.visible"); - }); - - it.each(["Enter", "Space", "downarrow", "uparrow"] as ( - | "Enter" - | "Space" - | "downarrow" - | "uparrow" - )[])( - "should verify default menu clickToOpen element opens using %s key", - (key) => { - CypressMountWithProviders(); - menuComponent(positionOfElement("sixth")).trigger( - "keydown", - keyCode(key) - ); - submenuItem(positionOfElement("sixth")).should("have.length", 2); - innerMenu(positionOfElement("second"), span) - .should("have.attr", "data-component", "link") - .and("be.visible"); - innerMenu(positionOfElement("third"), span) - .should("have.attr", "data-component", "link") - .and("be.visible"); - } - ); - - it.each([ - ["downarrow", 0], - ["uparrow", 2], - ] as ["downarrow" | "uparrow", number][])( - "should verify the Search component is focusable by using the %s key", - (key, tabs) => { - CypressMountWithProviders(); - - pressTABKey(1); - - cy.focused().trigger("keydown", keyCode("Enter")); - pressTABKey(tabs); - cy.focused().trigger("keydown", keyCode(key)); - searchDefaultInput().should("have.focus"); - } - ); - - it("should verify the Search component close icon is focusable when using keyboard to navigate down the list of items", () => { - CypressMountWithProviders(); - - pressTABKey(1); - cy.focused().trigger("keydown", keyCode("Enter")); - cy.focused().trigger("keydown", keyCode("downarrow")); - searchDefaultInput().clear().type("FooBar"); - searchDefaultInput().tab(); - searchCrossIcon().parent().should("have.focus"); - }); - - it("should verify the Search component close icon is centred when focused", () => { - CypressMountWithProviders(); - const bottomLess = 201; - const topLess = 181; - const leftLess = 134; - // additionVal is to compensate for the outline. - const additionVal = 2; - - pressTABKey(1); - cy.focused().trigger("keydown", keyCode("Enter")); - - cy.focused().trigger("keydown", keyCode("downarrow")); - - searchDefaultInput().clear().type("FooBar"); - - searchDefaultInput().tab(); - searchCrossIcon().parent().should("have.focus"); - - const bounding = (element: JQuery) => { - return element[0].getBoundingClientRect(); - }; - - searchCrossIcon() - .then(($el) => bounding($el)) - .as("position"); - - cy.get("@position") - .its("bottom") - .should("be.lessThan", bottomLess + additionVal); - cy.get("@position").its("bottom").should("be.greaterThan", bottomLess); - cy.get("@position") - .its("top") - .should("be.lessThan", topLess + additionVal); - cy.get("@position").its("top").should("be.greaterThan", topLess); - cy.get("@position") - .its("left") - .should("be.lessThan", leftLess + additionVal); - cy.get("@position").its("left").should("be.greaterThan", leftLess); - }); - - it("should verify the Search component close icon is focusable when using keyboard to navigate up the list of items", () => { - CypressMountWithProviders(); - pressTABKey(1); - cy.focused().trigger("keydown", keyCode("Enter")); - searchDefaultInput().clear().type("FooBar"); - cy.focused().trigger("keydown", keyCode("End")); - cy.focused().tab({ shift: true }); - cy.focused().tab({ shift: true }); - searchCrossIcon().parent().should("have.focus"); - cy.focused().tab({ shift: true }); - searchDefaultInput().should("have.focus"); - }); - - it("should verify that the Search component is focusable by using the downarrow key when rendered as the parent of a scrollable submenu", () => { - CypressMountWithProviders(); - - pressTABKey(3); - cy.focused().trigger("keydown", keyCode("Enter")); - cy.focused().trigger("keydown", keyCode("downarrow")); - searchDefaultInput().should("have.focus"); - }); - - it("should verify scroll Menu search has an alternate background color", () => { - CypressMountWithProviders(); - submenu().eq(positionOfElement("third")).trigger("mouseover"); - menuItem() - .eq(4) - .children() - .should("have.css", "background-color", "rgb(0, 50, 76)"); - }); - - it("should not close submenu when enter is pressed on search component", () => { - CypressMountWithProviders(); - - pressTABKey(1); - cy.focused().trigger("keydown", keyCode("Enter")); - cy.focused().trigger("keydown", keyCode("downarrow")); - searchDefaultInput().clear().type("FooBar"); - cy.focused().tab(); - cy.focused().trigger("keydown", keyCode("Enter")); - submenuBlock().should("be.visible"); - }); - - it("should verify if there is a Menu Item with a very long label inside a submenu, check that the width of the whole submenu is determined by this submenu item", () => { - CypressMountWithProviders( - - - - - Item Submenu One Is A Very Long Submenu Item Indeed - - - Item Submenu Two - - - - - ); - - submenu().eq(positionOfElement("first")).trigger("mouseover"); - innerMenu(positionOfElement("second"), span).as("submenuBlock"); - cy.get("@submenuBlock").its("0").should("have.css", "width").as("width"); - cy.get("@width") - .then(($el) => parseInt($el.toString())) - .should("be.within", 385, 395); - }); - - it("should verify if there is a Menu Item with a submenu that has a very long label, check the width of the whole submenu is determined by this parent item", () => { - CypressMountWithProviders( - - - - Item Submenu One - - Item Submenu Two - - - - - ); - - submenu().eq(positionOfElement("first")).trigger("mouseover"); - submenu().eq(positionOfElement("first")).as("submenuBlock"); - cy.get("@submenuBlock").its("0").should("have.css", "width").as("width"); - cy.get("@width") - .then(($el) => parseInt($el.toString())) - .should("be.within", 490, 499); - }); - - it.each([ - ["float", 0.3, 409], - ["float", 0.6, 819], - ["float", 1.0, 1366], - ["number", 350, 350], - ["number", 900, 900], - ["number", 1350, 1350], - ["string", "450px", 450], - ["string", "675px", 675], - ["string", "1200px", 1200], - ])( - "should verify Menu width is set by width prop when passed as %s", - (type, width, pixels) => { - CypressMountWithProviders(); - - menu().then(($el) => { - assertCssValueIsApproximately($el, "width", pixels); - }); - } - ); - - it.each([ - ["number", 15, 15], - ["number", 27, 27], - ["number", 41, 41], - ["string", "10px", 10], - ["string", "30px", 30], - ["string", "50px", 50], - ])( - "should verify Menu height is set by height prop when passed as %s", - (type, propValue, pixels) => { - CypressMountWithProviders(); - - menu().then(($el) => { - assertCssValueIsApproximately($el, "height", pixels); - }); - } - ); - - it.each([ - ["number", 810, 350, 810], - ["number", 810, 1350, 1350], - ["string", "700px", "300px", 700], - ["string", "700px", "1200px", 1200], - ] as [string, string | number, string | number, number][])( - "should verify Menu minimum width is set by minWidth prop when passed as %s", - (type, minWidth, width, pixels) => { - CypressMountWithProviders( - - ); - menu().then(($el) => { - assertCssValueIsApproximately($el, "width", pixels); - }); - } - ); - - it.each([ - ["number", 810, 350, 350], - ["number", 810, 1350, 810], - ["string", "700px", "300px", 300], - ["string", "700px", "1200px", 700], - ] as [string, string | number, string | number, number][])( - "should verify Menu maximum width is set by minWidth prop when passed as %s", - (type, maxWidth, width, pixels) => { - CypressMountWithProviders( - - ); - menu().then(($el) => { - assertCssValueIsApproximately($el, "width", pixels); - }); - } - ); - - it.each([ - ["number", 30, 20, 30], - ["number", 30, 40, 40], - ["string", "35px", "25px", 35], - ["string", "35px", "40px", 40], - ] as [string, string | number, string | number, number][])( - "should verify Menu minimum height is set by minWidth prop when passed as %s", - (type, minHeight, height, pixels) => { - CypressMountWithProviders( - - ); - - menu().then(($el) => { - assertCssValueIsApproximately($el, "height", pixels); - }); - } - ); - - it.each([ - ["number", 30, 20, 20], - ["number", 30, 40, 30], - ["string", "35px", "25px", 25], - ["string", "35px", "40px", 35], - ] as [string, string | number, string | number, number][])( - "should verify Menu maximum height is set by minWidth prop when passed as %s", - (type, maxHeight, height, pixels) => { - CypressMountWithProviders( - - ); - - menu().then(($el) => { - assertCssValueIsApproximately($el, "height", pixels); - }); - } - ); - - it.each(["block", "inline-block", "flex", "contents", "list-item", "none"])( - "should verify Menu display is %s", - (display) => { - CypressMountWithProviders(); - - menu() - .should("have.attr", "display", display) - .and("have.css", "display", display); - } - ); - - it.each([ - "baseline", - "bottom", - "middle", - "sub", - "super", - "text-bottom", - "text-top", - "top", - ])("should verify Menu verticalAlign is %s", (alignment) => { - CypressMountWithProviders(); - - menu().should("have.css", "vertical-align", alignment); - }); - - it.each(["auto", "clip", "hidden", "scroll", "visible"])( - "should verify Menu overflow is %s", - (overflow) => { - CypressMountWithProviders(); - - menu() - .should("have.attr", "overflow", overflow) - .and("have.css", "overflow", overflow); - } - ); - - it.each([ - "auto", - "clip", - "hidden", - "scroll", - "visible", - ] as MenuProps["overflowX"][])( - "should verify Menu overflowX is %s", - (overflow) => { - CypressMountWithProviders(); - - menu().should("have.css", "overflow-x", overflow); - } - ); - - it.each([ - "auto", - "clip", - "hidden", - "scroll", - "visible", - ] as MenuProps["overflowY"][])( - "should verify Menu overflowY is %s", - (overflow) => { - CypressMountWithProviders(); - - menu().should("have.css", "overflow-y", overflow); - } - ); - - it.each([ - "normal", - "stretch", - "baseline", - "center", - "flex-start", - "flex-end", - ])("should verify Menu alignItems is %s", (alignment) => { - CypressMountWithProviders(); - - menu().should("have.css", "align-items", alignment); - }); - - it.each([ - "normal", - "baseline", - "center", - "flex-start", - "flex-end", - "space-between", - "space-around", - "stretch", - ])("should verify Menu alignContent is %s", (alignment) => { - CypressMountWithProviders(); - - menu().should("have.css", "align-content", alignment); - }); - - it.each([ - "left", - "center", - "right", - "flex-start", - "flex-end", - "normal", - "stretch", - ])("should verify Menu justifyItems is %s", (justified) => { - CypressMountWithProviders(); - - menu().should("have.css", "justify-items", justified); - }); - - it.each([ - "left", - "center", - "right", - "flex-start", - "flex-end", - "normal", - "space-between", - "space-around", - "stretch", - ])("should verify Menu justifyContent is %s", (justified) => { - CypressMountWithProviders(); - - menu().should("have.css", "justify-content", justified); - }); - - it.each(["nowrap", "wrap", "wrap-reverse"] as MenuProps["flexWrap"][])( - "should verify Menu flex wrap is %s", - (wrap) => { - CypressMountWithProviders(); - - menu().should("have.css", "flex-wrap", wrap); - } - ); - - it.each([ - "column", - "column-reverse", - "row", - "row-reverse", - ] as MenuProps["flexDirection"][])( - "should verify Menu flex direction is %s", - (direction) => { - CypressMountWithProviders(); - - menu().should("have.css", "flex-direction", direction); - } - ); - - it.each(["auto", "content", "fit-content", "max-content", "min-content"])( - "should verify Menu flex is %s", - (flex) => { - CypressMountWithProviders(); - - menu().should("have.css", "flex-basis", flex); - } - ); - - it.each([ - [10, "10"], - [50, "50"], - [100, "100"], - ])("should verify Menu flex grow is %s", (value, growText) => { - CypressMountWithProviders(); - - menu().should("have.css", "flex-grow", growText); - }); - - it.each([ - [10, "10"], - [50, "50"], - [100, "100"], - ])("should verify Menu flex shrink is %s", (value, shrinkText) => { - CypressMountWithProviders( - - ); - - menu().should("have.css", "flex-shrink", shrinkText); - }); - - it.each(["auto", "content", "fit-content", "max-content", "min-content"])( - "should verify Menu flex basis is %s", - (basis) => { - CypressMountWithProviders(); - - menu().should("have.css", "flex-basis", basis); - } - ); - - it.each([ - "auto", - "baseline", - "left", - "normal", - "right", - "stretch", - "center", - "flex-start", - "flex-end", - ])("should verify Menu justifySelf is %s", (justify) => { - CypressMountWithProviders(); - - menu().should("have.css", "justify-self", justify); - }); - - it.each([ - "auto", - "baseline", - "normal", - "stretch", - "center", - "flex-start", - "flex-end", - ])("should verify Menu alignSelf is %s", (align) => { - CypressMountWithProviders(); - - menu().should("have.css", "align-self", align); - }); - - it.each([ - [10, "10"], - [50, "50"], - [100, "100"], - ])("should verify Menu order is %s", (value, orderText) => { - CypressMountWithProviders(); - - menu().should("have.css", "order", orderText); - }); - - it("should verify Menu Items className is customisable", () => { - CypressMountWithProviders( - - ); - menuItem().eq(0).should("have.attr", "class").as("item"); - cy.get("@item").should("contain", CHARACTERS.STANDARD); - }); - - it.each([ - ["selected", true, "rgb(230, 235, 237)"], - ["not selected", false, "rgb(255, 255, 255)"], - ])("should verify first Menu Item is %s", (state, boolVal, color) => { - CypressMountWithProviders(); - - submenu().eq(0).children().should("have.css", "background-color", color); - }); - - it.each([ - CHARACTERS.STANDARD, - CHARACTERS.DIACRITICS, - CHARACTERS.SPECIALCHARACTERS, - ])("should verify Menu Item text is %s", (text) => { - CypressMountWithProviders(); - - menuItem().eq(0).should("have.text", text); - }); - - it("should verify Menu Item target is %s", () => { - CypressMountWithProviders( - - ); - menuItem() - .eq(0) - .children() - .children() - .children() - .should("have.attr", "target", CHARACTERS.STANDARD); - }); - - it.each([ - [true, 32], - [false, 16], - ])( - "should verify dropdown arrow is displayed on Menu Item", - (boolVal, padding) => { - CypressMountWithProviders( - - ); - - submenu() - .eq(0) - .children() - .children() - .should("have.css", "padding-right", `${padding}px`); - } - ); - - it.each([ - ["default", "rgb(255, 255, 255)"], - ["alternate", "rgb(217, 224, 228)"], - ] as [MenuWithChildren["variant"], string][])( - "should verify Menu Item has %s variant", - (variant, color) => { - CypressMountWithProviders(); - - submenu() - .eq(0) - .children() - .should("have.css", "background-color", color); - } - ); - - it("should verify Menu Item ariaLabel is set to cypress_data", () => { - CypressMountWithProviders( - - ); - - submenu() - .eq(0) - .children() - .children() - .should("have.attr", "aria-label", CHARACTERS.STANDARD); - }); - - it.each([ - ["default", 1, 16, 121], - ["large", 4, 0, 153], - ] as [MenuDividerProps["size"], number, number, number][])( - "should verify that a Menu size is %s", - (size, height, margin, width) => { - CypressMountWithProviders( - - - - Submenu Item One - - Submenu Item Two - - - - ); - - submenu().eq(positionOfElement("first")).trigger("mouseover"); - menuDivider().then(($el) => { - assertCssValueIsApproximately($el, "height", height); - assertCssValueIsApproximately($el, "margin-left", margin); - assertCssValueIsApproximately($el, "width", width); - }); - } - ); - - it.each([ - [100, 100], - [200, 200], - ["150px", 150], - ["250px", 250], - ] as [ScrollableBlockProps["height"], number][])( - "should verify Menu scroll block height prop can be entered as a number or string", - (height, pixels) => { - CypressMountWithProviders(); - - submenu().eq(positionOfElement("first")).trigger("mouseover"); - scrollBlock().then(($el) => { - assertCssValueIsApproximately($el, "height", pixels); - }); - } - ); - - it.each([ - ["default", "rgb(230, 235, 237)"], - ["alternate", "rgb(217, 224, 228)"], - ] as [ScrollableBlockProps["variant"], string][])( - "should verify Menu scroll block has %s background color using variant prop", - (variant, color) => { - CypressMountWithProviders( - - ); - - submenu().eq(positionOfElement("first")).trigger("mouseover"); - scrollBlock() - .children() - .children() - .should("have.css", "background-color", color); - } - ); - - it.each([ - ["default", "rgba(0, 0, 0, 0)"], - ["alternate", "rgb(217, 224, 228)"], - ] as [ScrollableBlockProps["variant"], string][])( - "should verify that a Menu Segment Title has a variant background color", - (variant, color) => { - CypressMountWithProviders( - - - - - Item Submenu One Is A Very Long Submenu Item Indeed - - - Item With Segment Title - - - - - ); - submenu().eq(positionOfElement("first")).trigger("mouseover"); - segmentTitle().should("have.css", "background-color", color); - } - ); - - it("should verify that inner Menu without link is NOT available with tabbing in Fullscreen Menu", () => { - CypressMountWithProviders(); - - cy.viewport(1200, 800); - menuItem().eq(positionOfElement("first")).click(); - continuePressingTABKey(8); - menuItem().should("not.be.focused"); - }); - - it("should verify Menu Item href prop", () => { - CypressMountWithProviders( - - ); - - submenu() - .eq(0) - .children() - .children() - .should("have.attr", "href", CHARACTERS.STANDARD); - }); - - it.each([["noopener"], ["noreferrer"], ["opener"]])( - "should verify Menu Item with rel prop set to %s", - (rel) => { - CypressMountWithProviders(); - - submenu().eq(0).children().children().should("have.attr", "rel", rel); - } - ); - - it("should verify Scrollable Block parent prop", () => { - CypressMountWithProviders( - Parent} /> - ); - - submenu().eq(positionOfElement("first")).trigger("mouseover"); - scrollBlock() - .eq(0) - .parent() - .children() - .eq(0) - .should("have.text", "Parent"); - }); - - it.each([ - ["default", "rgb(230, 235, 237)"], - ["alternate", "rgb(217, 224, 228)"], - ] as [ScrollableBlockProps["parentVariant"], string][])( - "should verify Scrollable Block parent variant prop", - (variant, color) => { - CypressMountWithProviders( - Parent} - parentVariant={variant} - /> - ); - - submenu().eq(positionOfElement("first")).trigger("mouseover"); - scrollBlock() - .eq(0) - .parent() - .children() - .eq(0) - .children() - .should("have.css", "background-color", color); - } - ); - }); - - describe("check props for Menu Fullscreen component", () => { - beforeEach(() => { - cy.viewport(1200, 800); - CypressMountWithProviders(); - menuItem().eq(positionOfElement("first")).click(); - }); - - it("should verify that the Menu Fullscreen is rendered properly", () => { - fullscreenMenu(positionOfElement("first")) - .find("span") - .should("have.attr", "data-element", "close") - .and("be.visible"); - fullscreenMenu(positionOfElement("second")) - .find("ul") - .should("have.attr", "data-component", "menu") - .should("be.visible"); - fullscreenMenu(positionOfElement("second")) - .find("ul > li") - .should("have.length", 15); - }); - - it("should verify that the Menu Fullscreen is closed when close icon is clicked", () => { - closeIconButton().eq(0).click(); - - fullscreenMenu(positionOfElement("first")).should("not.be.visible"); - fullscreenMenu(positionOfElement("second")).should("not.be.visible"); - menu().should("be.visible"); - }); - - it("should verify that close icon is focused in Menu Fullscreen, focusRedesignOptOut false", () => { - fullScreenMenuWrapper().tab(); - closeIconButton() - .should( - "have.css", - "box-shadow", - "rgb(255, 188, 25) 0px 0px 0px 3px, rgba(0, 0, 0, 0.9) 0px 0px 0px 6px" - ) - .and("have.css", "outline", "rgba(0, 0, 0, 0) solid 3px"); - closeIconButton().tab(); - cy.focused() - .should( - "have.css", - "box-shadow", - "rgba(0, 0, 0, 0.9) 0px 0px 0px 3px inset, rgb(255, 188, 25) 0px 0px 0px 6px inset" - ) - .and("have.css", "outline", "rgba(0, 0, 0, 0) solid 3px"); - }); - - it("should verify that close icon is focused in Menu Fullscreen, focusRedesignOptOut true", () => { - cy.viewport(1200, 800); - CypressMountWithProviders( - , - undefined, - undefined, - { - focusRedesignOptOut: true, - } - ); - menuItem().eq(positionOfElement("first")).click(); - - fullScreenMenuWrapper().tab(); - closeIconButton().then(($el) => { - checkGoldenOutline($el); - }); - - closeIconButton().tab(); - cy.focused().should( - "have.css", - "box-shadow", - "rgb(255, 188, 25) 0px 0px 0px 3px inset" - ); - }); - - it("should verify that inner Menu is available with tabbing and styles are correct", () => { - cy.viewport(1200, 800); - CypressMountWithProviders(); - menuItem().eq(positionOfElement("first")).click(); - - fullScreenMenuWrapper().tab(); - for (let i = 0; i < 4; i++) { - cy.focused().tab(); - } - fullScreenMenuItem(positionOfElement("fourth")) - .find("ul > li") - .eq(1) - .children() - .children() - .should("have.css", "box-shadow") - .and("contain", "rgb(255, 188, 25)"); - fullScreenMenuItem(positionOfElement("fourth")) - .find("ul > li") - .eq(1) - .children() - .children() - .should("have.css", "background-color") - .and("contain", "rgb(0, 126, 69)"); - fullScreenMenuItem(positionOfElement("fourth")) - .find("ul > li") - .eq(1) - .children() - .children() - .should("have.css", "color") - .and("contain", "rgb(255, 255, 255)"); - fullScreenMenuItem(positionOfElement("fourth")) - .find("ul > li") - .eq(1) - .children() - .children() - .should("be.focused"); - }); - - it("should verify that inner Menu is available with shift-tabbing and styles are correct", () => { - cy.viewport(1200, 800); - CypressMountWithProviders(); - menuItem().eq(positionOfElement("first")).click(); - - fullScreenMenuWrapper().tab(); - for (let i = 0; i < 5; i++) { - cy.focused().tab(); - } - cy.focused().tab({ shift: true }); - fullScreenMenuItem(positionOfElement("fourth")) - .find("ul > li") - .eq(1) - .children() - .children() - .should("have.css", "box-shadow") - .and("contain", "rgb(255, 188, 25)"); - fullScreenMenuItem(positionOfElement("fourth")) - .find("ul > li") - .eq(1) - .children() - .children() - .should("have.css", "background-color") - .and("contain", "rgb(0, 126, 69)"); - fullScreenMenuItem(positionOfElement("fourth")) - .find("ul > li") - .eq(1) - .children() - .children() - .should("have.css", "color") - .and("contain", "rgb(255, 255, 255)"); - fullScreenMenuItem(positionOfElement("fourth")) - .find("ul > li") - .eq(1) - .children() - .children() - .should("be.focused"); - }); - - it("should verify that inner Menu without link is NOT available with tabbing in Fullscreen Menu", () => { - continuePressingTABKey(8); - menuItem().should("not.be.focused"); - }); - - it.each([ - ["open", true, "be.visible"], - ["closed", false, "not.be.visible"], - ])( - "should verify that Menu Fullscreen is %s when isOpen prop is %s", - (value, boolVal, state) => { - CypressMountWithProviders(); - - getComponent("menu-fullscreen").should(state); - } - ); - - it("should verify that Menu Fullscreen has no effect on the tab order when isOpen prop is false", () => { - CypressMountWithProviders(); - - cy.tab(); - cy.get("#button-1").should("be.focused"); - cy.tab(); - cy.get("#button-2").should("be.focused"); - }); - - it.each([ - ["left", -1200, 1200], - ["right", 1200, -1200], - ] as [MenuFullscreenProps["startPosition"], number, number][])( - "should verify that Menu Fullscreen start position is %s", - (side, left, right) => { - CypressMountWithProviders( - - ); - getComponent("menu-fullscreen").should("have.css", "left", `${left}px`); - getComponent("menu-fullscreen").should( - "have.css", - "right", - `${right}px` - ); - } - ); - - it("should focus the next menu item on tab press when the current item has a Search input with searchButton but no value", () => { - CypressMountWithProviders( - - ); - - menuItem().first().find("a").focus(); - cy.tab(); - searchDefaultInput().should("have.focus"); - cy.tab(); - menuItem().last().find("a").should("have.focus"); - }); - - it("should focus the search icon and button on tab press when the current item has a Search input with searchButton and has a value, focusRedesignOptOut flag not set", () => { - CypressMountWithProviders( - - ); - - menuItem().first().find("a").focus(); - cy.tab(); - searchDefaultInput().should("have.focus"); - cy.tab(); - searchCrossIcon().parent().should("have.focus"); - cy.tab(); - searchButton().should("have.focus"); - searchButton().should( - "have.css", - "box-shadow", - "rgb(255, 188, 25) 0px 0px 0px 3px, rgba(0, 0, 0, 0.9) 0px 0px 0px 6px" - ); - cy.tab(); - menuItem().last().find("a").should("have.focus"); - }); - - it("should focus the search icon and button on tab press when the current item has a Search input with searchButton and has a value, focusRedesignOptOut flag set", () => { - CypressMountWithProviders( - , - undefined, - undefined, - { focusRedesignOptOut: true } - ); - - menuItem().first().find("a").focus(); - cy.tab(); - searchDefaultInput().should("have.focus"); - cy.tab(); - searchCrossIcon().parent().should("have.focus"); - cy.tab(); - searchButton().should("have.focus"); - searchButton().then(($el) => { - checkGoldenOutline($el); - }); - cy.tab(); - menuItem().last().find("a").should("have.focus"); - }); - }); - - describe("check events for Menu component", () => { - it("should call onClick callback when a click event is triggered", () => { - const callback: MenuWithChildren["onClick"] = cy.stub().as("onClick"); - CypressMountWithProviders(); - menuComponent(positionOfElement("second")).click(); - cy.get("@onClick").should("have.been.calledOnce"); - }); - - it("should call onSubmenuOpen callback when mouseover event is triggered", () => { - const callback: MenuWithChildren["onSubmenuOpen"] = cy - .stub() - .as("onSubmenuOpen"); - CypressMountWithProviders( - - ); - - submenu().eq(positionOfElement("first")).trigger("mouseover"); - cy.get("@onSubmenuOpen").should("have.been.calledOnce"); - }); - - it("should call onSubmenuOpen callback when a click event is triggered", () => { - const callback: MenuWithChildren["onSubmenuOpen"] = cy - .stub() - .as("onSubmenuOpen"); - CypressMountWithProviders( - - ); - menuComponent(positionOfElement("second")).click(); - cy.get("@onSubmenuOpen").should("have.been.calledOnce"); - }); - - it.each(["Space", "Enter", "downarrow", "uparrow"] as ( - | "Space" - | "Enter" - | "downarrow" - | "uparrow" - )[])( - "should call onSubmenuOpen callback when a %s keyboard event is triggered", - (key) => { - const callback: MenuWithChildren["onSubmenuOpen"] = cy - .stub() - .as("onSubmenuOpen"); - CypressMountWithProviders( - - ); - menuComponent(positionOfElement("second")).trigger( - "keydown", - keyCode(key) - ); - cy.get("@onSubmenuOpen").should("have.been.calledOnce"); - } - ); - - it("should call onSubmenuClose callback when menu is closed", () => { - const callback: MenuWithChildren["onSubmenuOpen"] = cy - .stub() - .as("onSubmenuOpen"); - CypressMountWithProviders( - - ); - submenu().eq(positionOfElement("first")).trigger("mouseover"); - submenu().eq(positionOfElement("second")).trigger("mouseover"); - - cy.get("@onSubmenuOpen").should("have.been.calledOnce"); - }); - - it("should call onClose callback when Menu Fullscreen is closed", () => { - const callback: MenuFullscreenProps["onClose"] = cy.stub().as("onClose"); - CypressMountWithProviders(); - cy.viewport(1200, 800); - menuItem().eq(positionOfElement("first")).click(); - closeIconButton().eq(0).click(); - cy.get("@onClose").should("have.been.calledOnce"); - }); - - it("should have correct keyboard navigation order when children of submenu update", () => { - CypressMountWithProviders(); - submenu().eq(positionOfElement("first")).trigger("mouseover"); - submenuBlock().children().should("have.length", 4); - - pressTABKey(1); - cy.focused().trigger("keydown", keyCode("downarrow")); - cy.focused().should("contain", "Apple"); - cy.focused().trigger("keydown", keyCode("downarrow")); - cy.focused().should("contain", "Banana"); - cy.focused().trigger("keydown", keyCode("downarrow")); - cy.focused().should("contain", "Carrot"); - cy.focused().trigger("keydown", keyCode("downarrow")); - cy.focused().should("contain", "Broccoli"); - - cy.focused().trigger("keydown", keyCode("uparrow")); - cy.focused().should("contain", "Carrot"); - cy.focused().trigger("keydown", keyCode("uparrow")); - cy.focused().should("contain", "Banana"); - cy.focused().trigger("keydown", keyCode("uparrow")); - cy.focused().should("contain", "Apple"); - }); - }); - - describe("Accessibility tests for Menu component", () => { - it("should pass accessibility tests for Menu default", () => { - CypressMountWithProviders(); - - cy.checkAccessibility(); - }); - - it("should pass accessibility tests for Menu expanded", () => { - CypressMountWithProviders(); - - submenu().eq(positionOfElement("first")).trigger("mouseover"); - - cy.checkAccessibility(); - }); - - it.each(["default", "large"] as MenuDividerProps["size"][])( - "should pass accessibility tests for Menu when divider size is %s", - (divider) => { - CypressMountWithProviders(); - - submenu().eq(positionOfElement("first")).trigger("mouseover"); - - cy.checkAccessibility(); - } - ); - - it("should pass accessibility tests for Menu when search component is focused", () => { - CypressMountWithProviders(); - - pressTABKey(1); - cy.focused().trigger("keydown", keyCode("Enter")); - pressTABKey(0); - cy.focused().trigger("keydown", keyCode("downarrow")); - - cy.checkAccessibility(); - }); - - it("should pass accessibility tests for Menu when a submenu has a long label", () => { - CypressMountWithProviders( - - - - - Item Submenu One Is A Very Long Submenu Item Indeed - - - Item Submenu Two - - - - - ); - - submenu().eq(positionOfElement("first")).trigger("mouseover"); - - cy.checkAccessibility(); - }); - - it("should pass accessibility tests for Menu when a menu item has a long label", () => { - CypressMountWithProviders( - - - - Item Submenu One - - Item Submenu Two - - - - - ); - - submenu().eq(positionOfElement("first")).trigger("mouseover"); - - cy.checkAccessibility(); - }); - - it.each(["450px", "675px", "1200px"])( - "should pass accessibility tests for Menu when width is %s", - (width) => { - CypressMountWithProviders(); - - cy.checkAccessibility(); - } - ); - - it.each(["10px", "30px", "50px"])( - "should pass accessibility tests for Menu when height is s", - (propValue) => { - CypressMountWithProviders(); - - cy.checkAccessibility(); - } - ); - - it.each(["default", "large"] as MenuDividerProps["size"][])( - "should pass accessibility tests for Menu when size is %spx", - (size) => { - CypressMountWithProviders(); - - cy.checkAccessibility(); - } - ); - - it.each(["block", "inline-block", "flex", "contents", "list-item", "none"])( - "should pass accessibility tests for Menu when display is %s", - (display) => { - CypressMountWithProviders(); - - cy.checkAccessibility(); - } - ); - - it.each([ - "baseline", - "bottom", - "middle", - "sub", - "super", - "text-bottom", - "text-top", - "top", - ])( - "should pass accessibility tests for Menu when alignItems is %s", - (alignment) => { - CypressMountWithProviders(); - - cy.checkAccessibility(); - } - ); - - it.each(["auto", "clip", "hidden", "scroll", "visible"])( - "should pass accessibility tests for Menu when overflow is %s", - (overflow) => { - CypressMountWithProviders(); - - cy.checkAccessibility(); - } - ); - - it.each([ - "auto", - "clip", - "hidden", - "scroll", - "visible", - ] as MenuProps["overflowX"][])( - "should pass accessibility tests for Menu when overflowX is %s", - (overflow) => { - CypressMountWithProviders(); - - cy.checkAccessibility(); - } - ); - - it.each([ - "auto", - "clip", - "hidden", - "scroll", - "visible", - ] as MenuProps["overflowY"][])( - "should pass accessibility tests for Menu when overflowY is %s", - (overflow) => { - CypressMountWithProviders(); - - cy.checkAccessibility(); - } - ); - - it.each([ - "normal", - "stretch", - "baseline", - "center", - "flex-start", - "flex-end", - ])( - "should pass accessibility tests for Menu when alignItems is %s", - (alignment) => { - CypressMountWithProviders(); - - cy.checkAccessibility(); - } - ); - - it.each([ - "normal", - "baseline", - "center", - "flex-start", - "flex-end", - "space-between", - "space-around", - "stretch", - ])( - "should pass accessibility tests for Menu when alignContent is %s", - (alignment) => { - CypressMountWithProviders(); - - cy.checkAccessibility(); - } - ); - - it.each([ - "left", - "center", - "right", - "flex-start", - "flex-end", - "normal", - "stretch", - ])( - "should pass accessibility tests for Menu when justifyItems is %s", - (justified) => { - CypressMountWithProviders(); - - cy.checkAccessibility(); - } - ); - - it.each([ - "left", - "center", - "right", - "flex-start", - "flex-end", - "normal", - "space-between", - "space-around", - "stretch", - ])( - "should pass accessibility tests for Menu when justifyContent is %s", - (justified) => { - CypressMountWithProviders(); - - cy.checkAccessibility(); - } - ); - - it.each(["nowrap", "wrap", "wrap-reverse"] as MenuProps["flexWrap"][])( - "should pass accessibility tests for Menu when flexWrap is %s", - (wrap) => { - CypressMountWithProviders(); - - cy.checkAccessibility(); - } - ); - - it.each([ - "column", - "column-reverse", - "row", - "row-reverse", - ] as MenuProps["flexDirection"][])( - "should pass accessibility tests for Menu when flexDirection is %s", - (direction) => { - CypressMountWithProviders(); - - cy.checkAccessibility(); - } - ); - - it.each(["auto", "content", "fit-content", "max-content", "min-content"])( - "should pass accessibility tests for Menu when flex is %s", - (flex) => { - CypressMountWithProviders(); - - cy.checkAccessibility(); - } - ); - - it.each([10, 50, 100])( - "should pass accessibility tests for Menu when flexGrow is %s", - (value) => { - CypressMountWithProviders( - - ); - - cy.checkAccessibility(); - } - ); - - it.each([10, 50, 100])( - "should pass accessibility tests for Menu when flexShrink is %s", - (value) => { - CypressMountWithProviders( - - ); - - cy.checkAccessibility(); - } - ); - - it.each(["auto", "content", "fit-content", "max-content", "min-content"])( - "should pass accessibility tests for Menu when flexBasis is %s", - (basis) => { - CypressMountWithProviders(); - - cy.checkAccessibility(); - } - ); - - it.each([ - "auto", - "baseline", - "left", - "normal", - "right", - "stretch", - "center", - "flex-start", - "flex-end", - ])( - "should pass accessibility tests for Menu when justifySelf is %s", - (justify) => { - CypressMountWithProviders(); - - cy.checkAccessibility(); - } - ); - - it.each([ - "auto", - "baseline", - "normal", - "stretch", - "center", - "flex-start", - "flex-end", - ])( - "should pass accessibility tests for Menu when alignSelf is %s", - (align) => { - CypressMountWithProviders(); - - cy.checkAccessibility(); - } - ); - - it.each([ - CHARACTERS.STANDARD, - CHARACTERS.DIACRITICS, - CHARACTERS.SPECIALCHARACTERS, - ])( - "should pass accessibility tests for Menu when item text is %s", - (text) => { - CypressMountWithProviders(); - - cy.checkAccessibility(); - } - ); - - it.each(["default", "alternate"] as ScrollableBlockProps["variant"][])( - "should pass accessibility tests for Menu when scroll block has a variant background color", - (variant) => { - CypressMountWithProviders( - - ); - - submenu().eq(positionOfElement("first")).trigger("mouseover"); - cy.checkAccessibility(); - } - ); - - it.each(["default", "alternate"] as ScrollableBlockProps["variant"][])( - "should pass accessibility tests for Menu when Segment Title has a variant background color", - (variant) => { - CypressMountWithProviders( - - ); - - submenu().eq(positionOfElement("first")).trigger("mouseover"); - cy.checkAccessibility(); - } - ); - - it("should pass accessibility tests for Menu with parent item", () => { - CypressMountWithProviders(); - - submenu().eq(positionOfElement("first")).trigger("mouseover"); - cy.checkAccessibility(); - }); - - it("should pass accessibility tests for Menu with button icon", () => { - CypressMountWithProviders(); - - submenu().eq(positionOfElement("first")).trigger("mouseover"); - cy.checkAccessibility(); - }); - - // Test skipped because of issue FE-5731 - it.skip("should pass accessibility tests for Menu with icon", () => { - CypressMountWithProviders(); - - cy.checkAccessibility(); - }); - }); - - describe("Accessibility tests for Menu Fullscreen component", () => { - beforeEach(() => { - cy.viewport(1200, 800); - CypressMountWithProviders(); - menuItem().eq(positionOfElement("first")).click(); - }); - - it("should pass accessibility tests for Menu Fullscreen", () => { - cy.checkAccessibility(); - }); - - it("should pass accessibility tests for Menu Fullscreen when close icon is clicked", () => { - closeIconButton().eq(0).click(); - cy.checkAccessibility(); - }); - - it("should pass accessibility tests for Menu Fullscreen when menu item is highlighted", () => { - pressTABKey(5); - cy.checkAccessibility(); - }); - - it.each(["left", "right"] as MenuFullscreenProps["startPosition"][])( - "should pass accessibility tests for Menu Fullscreen when start position is %s", - (side) => { - CypressMountWithProviders( - - ); - cy.checkAccessibility(); - } - ); - }); - - describe("when focused", () => { - it("menu items should have the expected focus styling, focusRedesignOptOut true", () => { - cy.viewport(1200, 800); - CypressMountWithProviders(, undefined, undefined, { - focusRedesignOptOut: true, - }); - - menuItem() - .first() - .find("a") - .focus() - .should( - "have.css", - "box-shadow", - "rgb(255, 188, 25) 0px 0px 0px 3px inset" - ); - - menuItem() - .last() - .find("button") - .focus() - .should( - "have.css", - "box-shadow", - "rgb(255, 188, 25) 0px 0px 0px 3px inset" - ); - - menuItem().last().find("button").click(); - - submenu() - .last() - .find("button") - .first() - .focus() - .should( - "have.css", - "box-shadow", - "rgb(255, 188, 25) 0px 0px 0px 3px inset" - ); - - submenu() - .last() - .find("a") - .first() - .focus() - .should( - "have.css", - "box-shadow", - "rgb(255, 188, 25) 0px 0px 0px 3px inset" - ); - }); - - it("menu items should have the expected focus styling, focusRedesignOptOut false", () => { - cy.viewport(1200, 800); - CypressMountWithProviders(); - - menuItem() - .first() - .find("a") - .focus() - .should( - "have.css", - "box-shadow", - "rgba(0, 0, 0, 0.9) 0px 0px 0px 3px inset, rgb(255, 188, 25) 0px 0px 0px 6px inset" - ) - .and("have.css", "outline", "rgba(0, 0, 0, 0) solid 3px"); - - menuItem() - .last() - .find("button") - .focus() - .should( - "have.css", - "box-shadow", - "rgba(0, 0, 0, 0.9) 0px 0px 0px 3px inset, rgb(255, 188, 25) 0px 0px 0px 6px inset" - ) - .and("have.css", "outline", "rgba(0, 0, 0, 0) solid 3px"); - - menuItem().last().find("button").click(); - - submenu() - .last() - .find("button") - .first() - .focus() - .should( - "have.css", - "box-shadow", - "rgba(0, 0, 0, 0.9) 0px 0px 0px 3px inset, rgb(255, 188, 25) 0px 0px 0px 6px inset" - ) - .and("have.css", "outline", "rgba(0, 0, 0, 0) solid 3px"); - - submenu() - .last() - .find("a") - .first() - .focus() - .should( - "have.css", - "box-shadow", - "rgba(0, 0, 0, 0.9) 0px 0px 0px 3px inset, rgb(255, 188, 25) 0px 0px 0px 6px inset" - ) - .and("have.css", "outline", "rgba(0, 0, 0, 0) solid 3px"); - }); - }); - - describe("rounded corners", () => { - it("has the expected border radius styling on the Submenu", () => { - CypressMountWithProviders(); - - submenu().eq(positionOfElement("first")).trigger("mouseover"); - submenuBlock().should("have.css", "border-radius", "0px 0px 8px 8px"); - submenu() - .find("a") - .last() - .should("have.css", "border-radius", "0px 0px 8px 8px"); - }); - - it("has the expected border radius styling on the Submenu Scrollable Block", () => { - CypressMountWithProviders(); - - submenu().eq(positionOfElement("first")).trigger("mouseover"); - scrollBlock().should("have.css", "border-radius", "0px 0px 0px 8px"); - scrollBlock() - .find("a") - .last() - .should("have.css", "border-radius", "0px 0px 0px 8px"); - }); - }); - - describe("edge case tests for MenuFullScreen", () => { - it("tabbing forward through the menu and back to the start should not make the background scroll to the bottom", () => { - CypressMountWithProviders(); - - continuePressingTABKey(4); - - closeIconButton().should("be.focused"); - - cy.checkNotInViewport("#bottom-box"); - }); - - it("tabbing backward through the menu and back to the start should not make the background scroll to the bottom", () => { - CypressMountWithProviders(); - - continuePressingTABKey(3, true); - - closeIconButton().should("be.focused"); - - cy.checkNotInViewport("#bottom-box"); - }); - - it("should not render a MenuDivider when falsy values are rendered", () => { - CypressMountWithProviders(); - - menuDivider().should("not.exist"); - }); - - it("should maintain the state of child items when a new item is added", () => { - cy.clock(); - CypressMountWithProviders(); - - cy.tick(5000); - fullScreenMenuItem(5).should("contain.text", "count 2"); - }); - }); - - describe("when inside a Navigation Bar", () => { - it("all the content of a long submenu can be accessed with the keyboard while remaining visible", () => { - CypressMountWithProviders(); - - cy.viewport(1000, 500); - - menuComponent(1).trigger("keydown", keyCode("downarrow")); - submenuItem(1).should("have.length", 20); - - for (let i = 0; i < 20; i++) { - cy.focused().trigger("keydown", keyCode("downarrow")); - } - - cy.focused().should("contain", "Foo 20"); - cy.checkInViewport( - '[data-component="submenu-wrapper"] ul > li:nth-child(20)' - ); - }); - - it("all the content of a long submenu can be accessed with the keyboard while remaining visible if the navbar height changes", () => { - CypressMountWithProviders(); - - cy.viewport(1000, 500); - - menuComponent(1).trigger("keydown", keyCode("downarrow")); - submenuItem(1).should("have.length", 21); - - // navigate to "change height" item and press it - for (let i = 0; i < 3; i++) { - cy.focused().trigger("keydown", keyCode("downarrow")); - } - cy.focused().trigger("keydown", keyCode("Enter")); - - // reopen menu and scroll to bottom with keyboard - cy.wait(100); - menuComponent(1).trigger("keydown", keyCode("downarrow")); - - for (let i = 0; i < 21; i++) { - cy.focused().trigger("keydown", keyCode("downarrow")); - } - - cy.checkInViewport( - '[data-component="submenu-wrapper"] ul > li:nth-child(21)' - ); - }); - }); -}); diff --git a/cypress/locators/menu/index.js b/cypress/locators/menu/index.js deleted file mode 100644 index ac8fb1cf03..0000000000 --- a/cypress/locators/menu/index.js +++ /dev/null @@ -1,50 +0,0 @@ -import { DLS_ROOT } from "../locators"; -import { - SUBMENU, - SCROLL_BLOCK, - MENU_DIVIDER, - SEGMENT_TITLE, - MENU, - FULLSCREEN_MENU, - MENU_ITEM, - SEARCH_COMPONENT, - CROSS_ICON, -} from "./locators"; - -import { BUTTON_DATA_COMPONENT_PREVIEW } from "../button/locators"; - -// component preview locators -export const submenu = () => cy.get(SUBMENU); -export const submenuBlock = () => cy.get(SUBMENU).find("ul"); -export const innerMenu = (index, htmlProp) => - submenuBlock().find(`li:nth-child(${index})`).find(htmlProp); -export const scrollBlock = () => cy.get(SUBMENU).find(SCROLL_BLOCK); -export const lastSubmenuElement = (htmlProp) => - submenuBlock().find(htmlProp).last(); -export const menuDivider = () => cy.get(MENU_DIVIDER); -export const segmentTitle = () => cy.get(SEGMENT_TITLE); -export const segmentTitleItem = (index) => - segmentTitle() - .parent() - .find("ul") - .find(`li:nth-child(${index})`) - .find("span"); -export const menuComponent = (index) => - cy.get(MENU).first().find(`li:nth-child(${index})`); -export const submenuItem = (index) => - menuComponent(index).find(SUBMENU).find("ul > li"); -export const menuCanvas = () => cy.get(DLS_ROOT); -export const fullScreenMenuWrapper = () => cy.get(FULLSCREEN_MENU); -export const fullscreenMenu = (index) => - cy.get(FULLSCREEN_MENU).find("div").eq(index); -export const fullScreenMenuItem = (index) => - cy.get(`${FULLSCREEN_MENU} ${MENU}`).find(`li:nth-child(${index})`); -export const menu = () => cy.get(MENU); -export const menuItem = () => cy.get(MENU_ITEM); - -// component preview locators for Search -export const searchDefault = () => cy.get(SEARCH_COMPONENT); -export const searchDefaultInput = () => searchDefault().find("input"); -export const searchCrossIcon = () => searchDefault().find(CROSS_ICON); -export const searchButton = () => - searchDefault().find(BUTTON_DATA_COMPONENT_PREVIEW); diff --git a/playwright/components/menu/index.ts b/playwright/components/menu/index.ts new file mode 100644 index 0000000000..0eafa17a9a --- /dev/null +++ b/playwright/components/menu/index.ts @@ -0,0 +1,36 @@ +import type { Page } from "@playwright/test"; +import { DLS_ROOT } from "../locators"; +import { + SUBMENU, + SCROLL_BLOCK, + MENU_DIVIDER, + SEGMENT_TITLE, + MENU, + FULLSCREEN_MENU, + MENU_ITEM, +} from "./locators"; + +// component preview locators +export const submenu = (page: Page) => page.locator(SUBMENU); +export const submenuBlock = (page: Page) => page.locator(SUBMENU).locator("ul"); +export const innerMenu = (page: Page, index: number, htmlProp: string) => + submenuBlock(page).locator(`li:nth-child(${index})`).locator(htmlProp); +export const scrollBlock = (page: Page) => + page.locator(SUBMENU).locator(SCROLL_BLOCK); +export const lastSubmenuElement = (page: Page, htmlProp: string) => + submenuBlock(page).locator(htmlProp).last(); +export const menuDivider = (page: Page) => page.locator(MENU_DIVIDER); +export const segmentTitle = (page: Page) => page.locator(SEGMENT_TITLE); +export const menuComponent = (page: Page, index: number) => + page.locator(MENU).first().locator(`li:nth-child(${index})`); +export const submenuItem = (page: Page, index: number) => + menuComponent(page, index).locator(SUBMENU).locator("ul > li"); +export const menuCanvas = (page: Page) => page.locator(DLS_ROOT); +export const fullScreenMenuWrapper = (page: Page) => + page.locator(FULLSCREEN_MENU); +export const fullscreenMenu = (page: Page, index: number) => + page.locator(FULLSCREEN_MENU).locator("div").nth(index); +export const fullScreenMenuItem = (page: Page, index: number) => + page.locator(`${FULLSCREEN_MENU} ${MENU}`).locator(`li:nth-child(${index})`); +export const menu = (page: Page) => page.locator(MENU); +export const menuItem = (page: Page) => page.locator(MENU_ITEM); diff --git a/cypress/locators/menu/locators.js b/playwright/components/menu/locators.ts similarity index 75% rename from cypress/locators/menu/locators.js rename to playwright/components/menu/locators.ts index be87757d5f..7894eddbd0 100644 --- a/cypress/locators/menu/locators.js +++ b/playwright/components/menu/locators.ts @@ -6,7 +6,3 @@ export const SEGMENT_TITLE = '[data-component="menu-segment-title"]'; export const MENU = '[data-component="menu"]'; export const FULLSCREEN_MENU = '[data-component="menu-fullscreen"]'; export const MENU_ITEM = '[data-component="menu-item"]'; - -export const SEARCH_COMPONENT = '[data-component="search"]'; -export const CROSS_ICON = '[data-element="cross"]'; -export const SEARCH_ICON = "search"; diff --git a/src/components/menu/component.test-pw.tsx b/src/components/menu/component.test-pw.tsx new file mode 100644 index 0000000000..63d3999cff --- /dev/null +++ b/src/components/menu/component.test-pw.tsx @@ -0,0 +1,586 @@ +import React, { useState } from "react"; +import { + Menu, + MenuDivider, + MenuSegmentTitle, + MenuTitleProps, + ScrollableBlock, + MenuItem, + MenuWithChildren, + MenuFullscreen, + MenuFullscreenProps, + MenuProps, + MenuDividerProps, + ScrollableBlockProps, +} from "."; +import { MenuType } from "./menu.context"; +import Search from "../search"; +import GlobalHeader from "../global-header"; +import Box from "../box/box.component"; +import Typography from "../typography/typography.component"; +import useMediaQuery from "../../hooks/useMediaQuery"; + +const menuTypes: MenuType[] = ["white", "light", "dark", "black"]; + +export const MenuComponent = (props: Partial & MenuDividerProps) => { + return ( + + {menuTypes.map((menuType) => ( +
+ + {menuType} + + + Menu Item One + Menu Item Two + + Item Submenu One + Item Submenu Two + + + Item Submenu Three + + Item Submenu Four + + {}}> + {}}>Item Submenu One + + Item Submenu Two + + +
+ ))} +
+ ); +}; + +export const MenuComponentScrollable = ( + props: Partial +) => { + return ( + + {menuTypes.map((menuType) => ( +
+ + {menuType} + + + {}}>Menu Item One + Menu Item Two + + + Item Submenu One + Item Submenu Two + Item Submenu Three + Item Submenu Four + Item Submenu Five + Item Submenu Six + Item Submenu Seven + Item Submenu Eight + Item Submenu Nine + Item Submenu Ten + Item Submenu Eleven + Item Submenu Twelve + + + + Item Submenu One + Item Submenu Two + + Item Submenu Three + Item Submenu Four + Item Submenu Five + Item Submenu Six + Item Submenu Seven + Item Submenu Eight + Item Submenu Nine + Item Submenu Ten + Item Submenu Eleven + Item Submenu Twelve + + + +
+ ))} +
+ ); +}; + +export const MenuComponentSearch = () => { + return ( + + {menuTypes.map((menuType) => ( +
+ + {menuType} + + + + Item Submenu One + + + + + + Item Submenu Two + Item Submenu Three + + +
+ ))} +
+ ); +}; + +export const MenuWithChildrenUpdating = () => { + const [show, setShow] = React.useState(false); + return ( +
{}} + onFocus={() => {}} + onBlur={() => {}} + onMouseOver={() => setTimeout(() => setShow(true), 500)} + > + + + Apple + {show && ( + <> + Banana + Carrot + + )} + Broccoli + + +
+ ); +}; + +export const MenuComponentFullScreen = ( + props: Partial +) => { + const [menuOpen, setMenuOpen] = useState({ + light: false, + dark: false, + white: false, + black: false, + }); + const fullscreenViewBreakPoint = useMediaQuery("(max-width: 1200px)"); + const responsiveMenuItems = ( + startPosition: "left" | "right", + menu: MenuType + ) => { + if (fullscreenViewBreakPoint) { + return [ + setMenuOpen((state) => ({ ...state, [menu]: true }))} + > + Menu + , + setMenuOpen((state) => ({ ...state, [menu]: false }))} + {...props} + > + Menu Item One + {}} submenu="Menu Item Two"> + Submenu Item One + Submenu Item Two + + Menu Item Three + Menu Item Four + + Submenu Item One + Submenu Item Two + + Menu Item Six + , + ]; + } + return [ + + Menu Item One + , + + Submenu Item One + Submenu Item Two + , + + Menu Item Three + , + + Menu Item Four + , + + Submenu Item One + Submenu Item Two + , + + Menu Item Six + , + ]; + }; + return ( + + {menuTypes.map((menuType) => ( +
+ + {menuType} + + + {React.Children.map( + responsiveMenuItems("left", menuType), + (items) => items + )} + +
+ ))} +
+ ); +}; + +export const MenuFullScreenBackgroundScrollTest = () => { + return ( + + + I should not be scrolled into view + + {}}> + Menu Item One + Menu Item Two + + + ); +}; + +export const MenuComponentItems = ( + props: MenuWithChildren & MenuDividerProps +) => { + return ( + + + + + Item Submenu One + Item Submenu Two + + + Item Submenu Three + + Item Submenu Four + + Menu Item Two + {}} + {...props} + > + {}}>Item Submenu One + Item Submenu Two + + + + ); +}; + +export const MenuItems = (props: MenuWithChildren) => { + return ( + + {menuTypes.map((menuType) => ( + + + {menuType} + + + Menu Item One + Menu Item Two + + Item Submenu One + Item Submenu Two + + + Item Submenu Three + + Item Submenu Four + + + {}}>Item Submenu One + Item Submenu Two + + + {}}>Item Submenu One + Item Submenu Two + + + + ))} + + ); +}; + +export const MenuFullScreenWithSearchButton = ({ + searchValue, +}: { + searchValue?: string; +}) => ( + {}}> + Menu Item before Search + + + + + Menu Item after Search + + +); + +export const MenuComponentScrollableParent = ( + props: Partial +) => { + const items = ["apple", "banana", "carrot", "grapefruit", "melon", "orange"]; + const [itemSearch, setItemSearch] = React.useState(items); + const [searchString, setSearchString] = React.useState(""); + + const handleTextChange = (e: { target: { value: any } }) => { + const searchStr = e.target.value; + setSearchString(searchStr); + let found; + + if (searchStr.length > 0) { + found = items.filter((item) => item.includes(searchStr)); + } else { + found = items; + } + + setItemSearch(found); + }; + + return ( + + + {}}>Menu Item One + Menu Item Two + + Item Submenu One + + } + {...props} + > + {itemSearch.map((item) => ( + + {item} + + ))} + + + + + ); +}; + +export const MenuComponentWithIcon = () => { + return ( + + {menuTypes.map((menuType) => ( +
+ + {menuType} + + + + Home + + + + Item Submenu One + Item Submenu Two + + + Item Submenu Four + + + Item Submenu One + Item Submenu Two + + +
+ ))} +
+ ); +}; + +export const MenuComponentButtonIcon = () => { + return ( +
+ + + {}}> + onClick and Icon + + {}}> + onClick + + + + href and Icon + + + href + + + +
+ ); +}; + +export const MenuSegmentTitleComponent = (props: Partial) => { + return ( + + {menuTypes.map((menuType) => ( +
+ + {menuType} + + + Menu Item One + Menu Item Two + + Item Submenu One + Item Submenu Two + + Item Submenu Three + + Item Submenu Four + + {}}> + {}}>Item Submenu One + + Item Submenu Two + + +
+ ))} +
+ ); +}; + +export const ClosedMenuFullScreenWithButtons = () => { + return ( + <> + + {}}> + Menu Item One + Menu Item Two + + + + ); +}; + +export const MenuDividerComponent = (props: MenuDividerProps) => { + return ( + + {menuTypes.map((menuType) => ( +
+ + {menuType} + + + Menu Item One + Menu Item Two + + Item Submenu One + Item Submenu Two + + + Item Submenu Three + + Item Submenu Four + + {}}> + {}}>Item Submenu One + + Item Submenu Two + + +
+ ))} +
+ ); +}; + +export const InGlobalHeaderStory = () => { + return ( + + + + {}}>Foo 1 + {}}>Foo 2 + {}}>Foo 3 + {}}>Foo 4 + {}}>Foo 5 + {}}>Foo 6 + {}}>Foo 7 + {}}>Foo 8 + {}}>Foo 9 + {}}>Foo 10 + {}}>Foo 11 + {}}>Foo 12 + {}}>Foo 13 + {}}>Foo 14 + {}}>Foo 15 + {}}>Foo 16 + {}}>Foo 17 + {}}>Foo 18 + {}}>Foo 19 + {}}>Foo 20 + + + + ); +}; + +export const SubMenuWithVeryLongLabel = () => { + return ( + + + + Item Submenu One + + Item Submenu Two + + + + + ); +}; diff --git a/src/components/menu/menu-test.stories.tsx b/src/components/menu/menu-test.stories.tsx index 2df950a16e..3f3ec786ec 100644 --- a/src/components/menu/menu-test.stories.tsx +++ b/src/components/menu/menu-test.stories.tsx @@ -1,28 +1,10 @@ import React, { useState, useEffect } from "react"; import { action } from "@storybook/addon-actions"; -import { - Menu, - MenuDivider, - MenuSegmentTitle, - MenuTitleProps, - ScrollableBlock, - MenuItem, - MenuWithChildren, - MenuFullscreen, - MenuFullscreenProps, - MenuProps, - MenuDividerProps, - ScrollableBlockProps, -} from "."; +import { Menu, MenuItem, MenuFullscreen, MenuFullscreenProps } from "."; import { MenuType } from "./menu.context"; import Search from "../search"; import NavigationBar, { NavigationBarProps } from "../navigation-bar"; import GlobalHeader from "../global-header"; -import Box from "../box/box.component"; -import Typography from "../typography/typography.component"; -import useMediaQuery from "../../../src/hooks/useMediaQuery"; - -const menuTypes: MenuType[] = ["white", "light", "dark", "black"]; export default { title: "Menu/Test", @@ -232,538 +214,6 @@ InNavigationBarStory.story = { }, }; -export const MenuComponent = (props: Partial & MenuDividerProps) => { - return ( - - {menuTypes.map((menuType) => ( -
- - {menuType} - - - Menu Item One - Menu Item Two - - Item Submenu One - Item Submenu Two - - - Item Submenu Three - - Item Submenu Four - - {}}> - {}}>Item Submenu One - - Item Submenu Two - - - -
- ))} -
- ); -}; - -export const MenuComponentScrollable = ( - props: Partial -) => { - return ( - - {menuTypes.map((menuType) => ( -
- - {menuType} - - - {}}>Menu Item One - Menu Item Two - - - Item Submenu One - Item Submenu Two - Item Submenu Three - Item Submenu Four - Item Submenu Five - Item Submenu Six - Item Submenu Seven - Item Submenu Eight - Item Submenu Nine - Item Submenu Ten - Item Submenu Eleven - Item Submenu Twelve - - - - Item Submenu One - Item Submenu Two - - Item Submenu Three - Item Submenu Four - Item Submenu Five - Item Submenu Six - Item Submenu Seven - Item Submenu Eight - Item Submenu Nine - Item Submenu Ten - Item Submenu Eleven - Item Submenu Twelve - - - -
- ))} -
- ); -}; - -export const MenuComponentSearch = () => { - return ( - - {menuTypes.map((menuType) => ( -
- - {menuType} - - - - Item Submenu One - - - - - - Item Submenu Two - Item Submenu Three - - - -
- ))} -
- ); -}; - -export const MenuWithChildrenUpdating = () => { - const [show, setShow] = React.useState(false); - return ( -
{}} - onFocus={() => {}} - onBlur={() => {}} - onMouseOver={() => setTimeout(() => setShow(true), 500)} - > - - - Apple - {show && ( - <> - Banana - Carrot - - )} - Broccoli - - -
- ); -}; - -export const MenuComponentFullScreen = ( - props: Partial -) => { - const [menuOpen, setMenuOpen] = useState({ - light: false, - dark: false, - white: false, - black: false, - }); - const fullscreenViewBreakPoint = useMediaQuery("(max-width: 1200px)"); - const responsiveMenuItems = ( - startPosition: "left" | "right", - menu: MenuType - ) => { - if (fullscreenViewBreakPoint) { - return [ - setMenuOpen((state) => ({ ...state, [menu]: true }))} - > - Menu - , - setMenuOpen((state) => ({ ...state, [menu]: false }))} - {...props} - > - Menu Item One - {}} submenu="Menu Item Two"> - Submenu Item One - Submenu Item Two - - Menu Item Three - Menu Item Four - - Submenu Item One - Submenu Item Two - - Menu Item Six - , - ]; - } - return [ - - Menu Item One - , - - Submenu Item One - Submenu Item Two - , - - Menu Item Three - , - - Menu Item Four - , - - Submenu Item One - Submenu Item Two - , - - Menu Item Six - , - ]; - }; - return ( - - {menuTypes.map((menuType) => ( -
- - {menuType} - - - {React.Children.map( - responsiveMenuItems("left", menuType), - (items) => items - )} - -
- ))} -
- ); -}; - -export const MenuFullScreenBackgroundScrollTest = () => { - return ( - - - I should not be scrolled into view - - {}}> - Menu Item One - Menu Item Two - - - ); -}; - -export const MenuComponentItems = ( - props: MenuWithChildren & MenuDividerProps -) => { - return ( - - - - - Item Submenu One - Item Submenu Two - - - Item Submenu Three - - Item Submenu Four - - Menu Item Two - {}} - > - {}}>Item Submenu One - - Item Submenu Two - - - - - ); -}; - -export const MenuFullScreenWithFalsyValues = ({ - ...props -}: Partial) => { - const showMenuItem = false; - return ( - {}} {...props}> - Submenu Item One - {false && Product Item One} - {showMenuItem ? Product Item Two : null} - - ); -}; - -export const MenuItems = (props: MenuWithChildren) => { - return ( - - {menuTypes.map((menuType) => ( - - - {menuType} - - - Menu Item One - Menu Item Two - - Item Submenu One - Item Submenu Two - - - Item Submenu Three - - Item Submenu Four - - - {}}>Item Submenu One - Item Submenu Two - - - {}}>Item Submenu One - Item Submenu Two - - - - ))} - - ); -}; - -export const MenuFullScreenWithSearchButton = ({ - searchValue, -}: { - searchValue?: string; -}) => ( - {}}> - Menu Item before Search - - - - - Menu Item after Search - - -); - -export const MenuComponentScrollableParent = () => { - const items = ["apple", "banana", "carrot", "grapefruit", "melon", "orange"]; - const [itemSearch, setItemSearch] = React.useState(items); - const [searchString, setSearchString] = React.useState(""); - - const handleTextChange = (e: { target: { value: string } }) => { - const searchStr = e.target.value; - setSearchString(searchStr); - let found; - - if (searchStr.length > 0) { - found = items.filter((item) => item.includes(searchStr)); - } else { - found = items; - } - - setItemSearch(found); - }; - - return ( - - - {}}>Menu Item One - Menu Item Two - - Item Submenu One - - } - > - {itemSearch.map((item) => ( - - {item} - - ))} - - - - - ); -}; - -export const MenuComponentWithIcon = () => { - return ( - - {menuTypes.map((menuType) => ( -
- - {menuType} - - - - Home - - - - Item Submenu One - Item Submenu Two - - - Item Submenu Four - - - Item Submenu One - Item Submenu Two - - -
- ))} -
- ); -}; - -export const MenuComponentButtonIcon = () => { - return ( -
- - - {}}> - onClick and Icon - - {}}> - onClick - - - - href and Icon - - - href - - - -
- ); -}; - -export const MenuSegmentTitleComponent = (props: Partial) => { - return ( - - {menuTypes.map((menuType) => ( -
- - {menuType} - - - Menu Item One - Menu Item Two - - Item Submenu One - Item Submenu Two - - Item Submenu Three - - Item Submenu Four - - {}}> - {}}>Item Submenu One - - Item Submenu Two - - - -
- ))} -
- ); -}; - -export const ClosedMenuFullScreenWithButtons = () => { - return ( - <> - - {}}> - Menu Item One - Menu Item Two - - - - ); -}; - -export const MenuDividerComponent = (props: MenuDividerProps) => { - return ( - - {menuTypes.map((menuType) => ( -
- - {menuType} - - - Menu Item One - Menu Item Two - - Item Submenu One - Item Submenu Two - - - Item Submenu Three - - Item Submenu Four - - {}}> - {}}>Item Submenu One - - Item Submenu Two - - - -
- ))} -
- ); -}; - const UpdatingSubmenu = () => { const [counter, setCounter] = useState(0); useEffect(() => { diff --git a/src/components/menu/menu.pw.tsx b/src/components/menu/menu.pw.tsx new file mode 100644 index 0000000000..d190b54aef --- /dev/null +++ b/src/components/menu/menu.pw.tsx @@ -0,0 +1,2439 @@ +/* eslint-disable no-await-in-loop */ +import React from "react"; +import { test, expect } from "@playwright/experimental-ct-react17"; +import { + Menu, + MenuProps, + MenuItem, + MenuWithChildren, + MenuDivider, + MenuDividerProps, + MenuSegmentTitle, + MenuFullscreenProps, + ScrollableBlockProps, +} from "."; +import Box from "../box"; +import { + submenuBlock, + innerMenu, + submenu, + scrollBlock, + lastSubmenuElement, + menuDivider, + segmentTitle, + menuComponent, + submenuItem, + fullscreenMenu, + menu, + menuItem, +} from "../../../playwright/components/menu/index"; +import { + searchDefaultInput, + searchCrossIcon, + searchButton, +} from "../../../playwright/components/search/index"; +import { + getComponent, + closeIconButton, + icon, +} from "../../../playwright/components/index"; +import { + continuePressingTAB, + continuePressingSHIFTTAB, + checkGoldenOutline, + assertCssValueIsApproximately, + checkAccessibility, + waitForAnimationEnd, +} from "../../../playwright/support/helper"; +import { CHARACTERS } from "../../../playwright/support/constants"; +import { + MenuComponent, + MenuComponentScrollable, + MenuComponentSearch, + MenuWithChildrenUpdating, + MenuComponentFullScreen, + MenuFullScreenBackgroundScrollTest, + MenuComponentItems, + MenuFullScreenWithSearchButton, + MenuComponentScrollableParent, + MenuComponentWithIcon, + MenuComponentButtonIcon, + MenuSegmentTitleComponent, + MenuItems, + ClosedMenuFullScreenWithButtons, + MenuDividerComponent, + InGlobalHeaderStory, + SubMenuWithVeryLongLabel, +} from "./component.test-pw"; +import { NavigationBarWithSubmenuAndChangingHeight } from "../navigation-bar/navigation-bar-test.stories"; +import { HooksConfig } from "../../../playwright"; + +const span = "span"; +const div = "div"; + +test.describe("Prop tests for Menu component", () => { + test(`should verify scroll block within a submenu is scrollable`, async ({ + mount, + page, + }) => { + await mount(); + + const subMenu = submenu(page).first(); + await subMenu.hover(); + const lastElement = lastSubmenuElement(page, "li"); + await lastElement.scrollIntoViewIfNeeded(); + await expect(lastElement).toBeInViewport(); + }); + + test(`should verify a submenu can be navigated using keyboard tabbing after an item was clicked`, async ({ + mount, + page, + }) => { + await mount(); + + const subMenu = submenu(page).first(); + await subMenu.hover(); + const menuItemThree = innerMenu(page, 4, span) + .filter({ hasText: "Item Submenu Three" }) + .first(); + await menuItemThree.click(); + await expect(menuItemThree).toHaveText("Item Submenu Three"); + await expect(menuItemThree).toHaveCSS("box-shadow", "none"); + await page.keyboard.press("Tab"); + const menuItemFour = innerMenu(page, 5, span) + .filter({ hasText: "Item Submenu Four" }) + .first(); + await expect(menuItemFour).toHaveText("Item Submenu Four"); + await expect(menuItemFour).toHaveCSS("box-shadow", "none"); + }); + + test(`should verify a submenu can be navigated using keyboard down arrow after an item was clicked`, async ({ + mount, + page, + }) => { + await mount(); + + const subMenu = submenu(page).first(); + await subMenu.hover(); + const menuItemThree = innerMenu(page, 2, span).first(); + await menuItemThree.click(); + await page.keyboard.press("ArrowDown"); + const focusedElement1 = page.locator("*:focus"); + await expect(focusedElement1).toContainText("Item Submenu Three"); + await page.keyboard.press("ArrowDown"); + const focusedElement2 = page.locator("*:focus"); + await expect(focusedElement2).toContainText("Item Submenu Four"); + }); + + test(`should verify a submenu can be navigated using keyboard shift + tabbing after an item was clicked`, async ({ + mount, + page, + }) => { + await mount(); + + const subMenu = submenu(page).first(); + await subMenu.hover(); + const menuItemThree = innerMenu(page, 4, span).first(); + await menuItemThree.click(); + await page.keyboard.press("Shift+Tab"); + const focusedElement1 = page.locator("*:focus"); + await expect(focusedElement1).toContainText("Item Submenu Two"); + await page.keyboard.press("Shift+Tab"); + const focusedElement2 = page.locator("*:focus"); + await expect(focusedElement2).toContainText("Item Submenu One"); + }); + + test(`should verify a submenu can be navigated using keyboard up arrow after an item was clicked`, async ({ + mount, + page, + }) => { + await mount(); + + const subMenu = submenu(page).first(); + await subMenu.hover(); + const menuItemThree = innerMenu(page, 4, span).first(); + await menuItemThree.click(); + await page.keyboard.press("ArrowUp"); + const focusedElement1 = page.locator("*:focus"); + await expect(focusedElement1).toContainText("Item Submenu Two"); + await page.keyboard.press("ArrowUp"); + const focusedElement2 = page.locator("*:focus"); + await expect(focusedElement2).toContainText("Item Submenu One"); + }); + + test(`should verify the first submenu item is focused using keyboard tabbing after the parent item was clicked`, async ({ + mount, + page, + }) => { + await mount(); + + const subMenu = submenu(page).first(); + await subMenu.click(); + await page.keyboard.press("Tab"); + const focusedElement = page.locator("*:focus"); + await expect(focusedElement).toContainText("Item Submenu One"); + }); + + test(`should verify the first submenu item is focused using keyboard down arrow after the parent item was clicked`, async ({ + mount, + page, + }) => { + await mount(); + + const subMenu = submenu(page).first(); + await subMenu.click(); + await page.keyboard.press("ArrowDown"); + const focusedElement = page.locator("*:focus"); + await expect(focusedElement).toContainText("Item Submenu One"); + }); + + test(`should verify number and type of elements in submenu`, async ({ + mount, + page, + }) => { + const position = [1, 2, 4, 5] as const; + await mount(); + + const subMenu = submenu(page).first(); + await subMenu.hover(); + const subMenuBlock = submenuBlock(page).first().locator("li"); + await expect(subMenuBlock).toHaveCount(5); + for (let i = 0; i < position.length; i++) { + await expect(innerMenu(page, position[i], span).first()).toHaveAttribute( + "data-component", + "link" + ); + } + const menuItemDivider = innerMenu(page, 3, div).first(); + await expect(menuItemDivider).toHaveAttribute( + "data-component", + "menu-divider" + ); + }); + + ([ + ["white", "rgb(230, 235, 237)", 0], + ["light", "rgb(255, 255, 255)", 2], + ["dark", "rgb(0, 25, 38)", 4], + ["black", "rgb(38, 38, 38)", 6], + ] as [string, string, number][]).forEach(([colorName, color, menuNumber]) => { + test(`should verify the ${menuNumber}th submenu background color is ${colorName}`, async ({ + mount, + page, + }) => { + await mount(); + + const subMenu = submenu(page).nth(menuNumber); + await subMenu.hover(); + const menuItemThree = innerMenu(page, 2, span).first(); + await expect(menuItemThree).toHaveCSS("background-color", color); + }); + }); + + ([ + ["white", "rgb(255, 255, 255)", 0], + ["light", "rgb(230, 235, 237)", 4], + ["dark", "rgb(0, 50, 76)", 8], + ["black", "rgb(0, 0, 0)", 12], + ] as [string, string, number][]).forEach(([colorName, color, menuNumber]) => { + test(`should verify the ${menuNumber}th menu background color is ${colorName}`, async ({ + mount, + page, + }) => { + await mount(); + + const item = menuItem(page).nth(menuNumber).locator("span").first(); + await expect(item).toHaveCSS("background-color", color); + }); + }); + + ([ + ["white", "rgba(0, 0, 0, 0.9)"], + ["light", "rgba(0, 0, 0, 0.9)"], + ["dark", "rgb(255, 255, 255)"], + ["black", "rgb(255, 255, 255)"], + ] as [MenuProps["menuType"], string][]).forEach(([menuType, color]) => { + test(`should verify icon color is ${color} when menuType prop is ${menuType}`, async ({ + mount, + page, + }) => { + await mount( + + {}} icon="home"> + Foo + + + ); + + await page.keyboard.press("Tab"); + await expect(icon(page)).toHaveCSS("color", color); + }); + }); + + ([ + ["default", 1], + ["large", 4], + ] as [MenuDividerProps["size"], number][]).forEach(([size, height]) => { + test(`should verify Menu Divider has the proper height when the size is ${size}`, async ({ + mount, + page, + }) => { + await mount(); + + const subMenu = submenu(page).first(); + await subMenu.hover(); + const divider = menuDivider(page).first(); + await assertCssValueIsApproximately(divider, "height", height); + }); + }); + + test(`should verify a segment title is visible within a submenu`, async ({ + mount, + page, + }) => { + await mount(); + + const subMenu = submenu(page).nth(1); + await subMenu.hover(); + const menuSegmentTitle = segmentTitle(page).first(); + await expect(menuSegmentTitle).toHaveText("segment title"); + await expect(menuSegmentTitle).toBeVisible(); + await expect(menuSegmentTitle).toHaveCSS("color", "rgba(0, 0, 0, 0.65)"); + }); + + test(`should verify menu does not open on hover when clickToOpen prop is true`, async ({ + mount, + page, + }) => { + await mount(); + + const fourthMenu = menuComponent(page, 3); + await fourthMenu.hover(); + const subItem = submenuItem(page, 3); + await expect(subItem).toHaveCount(0); + }); + + test(`should verify menu opens on click when clickToOpen prop is true`, async ({ + mount, + page, + }) => { + await mount(); + + const sixthMenu = menuComponent(page, 5); + await sixthMenu.click(); + const subMenuBlock = submenuBlock(page).first().locator("li"); + await expect(subMenuBlock).toHaveCount(2); + await expect(innerMenu(page, 1, span).first()).toHaveAttribute( + "data-component", + "link" + ); + await expect(innerMenu(page, 2, span).first()).toHaveAttribute( + "data-component", + "link" + ); + }); + + ["Enter", "Space", "ArrowDown", "ArrowUp"].forEach((key) => { + test(`should verify menu opens using ${key} key when clickToOpen prop is true`, async ({ + mount, + page, + }) => { + await mount(); + + const sixthMenu = menuComponent(page, 5); + await sixthMenu.click(); + await page.keyboard.press("Escape"); + await page.keyboard.press(key); + const subMenuBlock = submenuBlock(page).first().locator("li"); + await expect(subMenuBlock).toHaveCount(2); + await expect(innerMenu(page, 1, span).first()).toHaveAttribute( + "data-component", + "link" + ); + await expect(innerMenu(page, 2, span).first()).toHaveAttribute( + "data-component", + "link" + ); + }); + }); + + ([ + ["ArrowDown", 0], + ["ArrowUp", 2], + ] as [string, number][]).forEach(([key, tabs]) => { + test(`should verify the Search component is focusable by pressing the ${key} key`, async ({ + mount, + page, + }) => { + await mount(); + + await page.keyboard.press("Tab"); + await page.keyboard.press("Enter"); + await continuePressingTAB(page, tabs); + await page.keyboard.press(key); + await expect(searchDefaultInput(page)).toBeFocused(); + }); + }); + + test(`should verify the Search component close icon is focusable when using keyboard to navigate down the list of items`, async ({ + mount, + page, + }) => { + await mount(); + + await page.keyboard.press("Tab"); + await page.keyboard.press("Enter"); + await page.keyboard.press("ArrowDown"); + await searchDefaultInput(page).fill("FooBar"); + await page.keyboard.press("Tab"); + const cross = searchCrossIcon(page).locator(".."); + await expect(cross).toBeFocused(); + }); + + test(`should verify the Search component close icon is centred when focused`, async ({ + mount, + page, + }) => { + await mount(); + const bottomLess = 210; + const topLess = 174; + const leftLess = 124; + // additionVal is to compensate for the outline. + const additionVal = 2; + + await page.keyboard.press("Tab"); + await page.keyboard.press("Enter"); + await page.keyboard.press("ArrowDown"); + await searchDefaultInput(page).fill("FooBar"); + await page.keyboard.press("Tab"); + const cross = searchCrossIcon(page).locator(".."); + await expect(cross).toBeFocused(); + + const boundBottom = await cross.evaluate((element) => { + return element.getBoundingClientRect().bottom; + }); + expect(boundBottom).toBeLessThan(bottomLess + additionVal); + expect(boundBottom).toBeGreaterThan(bottomLess); + + const boundTop = await cross.evaluate((element) => { + return element.getBoundingClientRect().top; + }); + expect(boundTop).toBeLessThan(topLess + additionVal); + expect(boundTop).toBeGreaterThan(topLess); + + const boundLeft = await cross.evaluate((element) => { + return element.getBoundingClientRect().left; + }); + expect(boundLeft).toBeLessThan(leftLess + additionVal); + expect(boundLeft).toBeGreaterThan(leftLess); + }); + + test(`should verify the Search component close icon is focusable when using keyboard to navigate up the list of items`, async ({ + mount, + page, + }) => { + await mount(); + + await page.keyboard.press("Tab"); + await page.keyboard.press("Enter"); + await page.keyboard.press("ArrowDown"); + await searchDefaultInput(page).fill("FooBar"); + await page.keyboard.press("End"); + await continuePressingSHIFTTAB(page, 2); + await page.waitForTimeout(2000); + const cross = searchCrossIcon(page).locator(".."); + await expect(cross).toBeFocused(); + await page.keyboard.press("Shift+Tab"); + await expect(searchDefaultInput(page)).toBeFocused(); + }); + + test(`should verify that the Search component is focusable by using the downarrow key when rendered as the parent of a scrollable submenu`, async ({ + mount, + page, + }) => { + await mount(); + + await continuePressingSHIFTTAB(page, 3); + await page.keyboard.press("Enter"); + await page.keyboard.press("ArrowDown"); + await expect(searchDefaultInput(page)).toBeFocused(); + }); + + test(`should verify scroll Menu search has an alternate background color`, async ({ + mount, + page, + }) => { + await mount(); + + const subMenu = submenu(page).nth(2); + await subMenu.hover(); + const item = menuItem(page).nth(4).locator("span").first(); + await expect(item).toHaveCSS("background-color", "rgb(0, 50, 76)"); + }); + + test(`should verify submenu is not closed when Enter key is pressed on search component`, async ({ + mount, + page, + }) => { + await mount(); + + await page.keyboard.press("Tab"); + await page.keyboard.press("Enter"); + await page.keyboard.press("ArrowDown"); + await searchDefaultInput(page).fill("FooBar"); + await page.keyboard.press("Tab"); + await page.keyboard.press("Enter"); + const subMenuBlock = submenuBlock(page).first().locator("li").first(); + await expect(subMenuBlock).toBeVisible(); + }); + + test(`should render with a menu item that has a very long label and verify the width of the whole submenu is determined by this item`, async ({ + mount, + page, + }) => { + await mount( + + + + + Item Submenu One Is A Very Long Submenu Item Indeed + + + Item Submenu Two + + + + + ); + + const subMenu = submenu(page).first(); + await subMenu.hover(); + const subMenuBlock = innerMenu(page, 2, span).first(); + const cssWidth = await subMenuBlock.evaluate((el) => + window.getComputedStyle(el).getPropertyValue("width") + ); + expect(parseInt(cssWidth)).toBeLessThanOrEqual(395); + expect(parseInt(cssWidth)).toBeGreaterThanOrEqual(385); + }); + + test(`should render with a submenu that has a very long label and verify the width of the whole submenu is determined by this item`, async ({ + mount, + page, + }) => { + await mount(); + + const subMenu = submenu(page).first(); + await subMenu.hover(); + const subMenuBlock = innerMenu(page, 2, span).first(); + const cssWidth = await subMenuBlock.evaluate((el) => + window.getComputedStyle(el).getPropertyValue("width") + ); + expect(parseInt(cssWidth)).toBeLessThanOrEqual(500); + expect(parseInt(cssWidth)).toBeGreaterThanOrEqual(490); + }); + + ([ + ["float", 0.3, 409], + ["float", 0.6, 819], + ["float", 1.0, 1366], + ["number", 350, 350], + ["number", 900, 900], + ["number", 1350, 1350], + ["string", "450px", 450], + ["string", "675px", 675], + ["string", "1200px", 1200], + ] as [string, number | string, number][]).forEach(([type, width, pixels]) => { + test(`should render with width set to ${pixels}px when prop is passed as a ${type}`, async ({ + mount, + page, + }) => { + await mount(); + + const thisMenu = menu(page).first(); + await assertCssValueIsApproximately(thisMenu, "width", pixels); + }); + }); + + ([ + ["number", 15, 15], + ["number", 27, 27], + ["number", 41, 41], + ["string", "10px", 10], + ["string", "30px", 30], + ["string", "50px", 50], + ] as [string, number | string, number][]).forEach( + ([type, propValue, pixels]) => { + test(`should render with height set to ${pixels}px when prop is passed as a ${type}`, async ({ + mount, + page, + }) => { + await mount(); + + const thisMenu = menu(page).first(); + await assertCssValueIsApproximately(thisMenu, "height", pixels); + }); + } + ); + + ([ + ["number", 810, 350, 810], + ["number", 810, 1350, 1350], + ["string", "700px", "300px", 700], + ["string", "700px", "1200px", 1200], + ] as [string, string | number, string | number, number][]).forEach( + ([type, minWidth, width, pixels]) => { + test(`should render with minimum width of ${pixels}px when minWidth prop is passed as a ${type}`, async ({ + mount, + page, + }) => { + await mount(); + + const thisMenu = menu(page).first(); + await assertCssValueIsApproximately(thisMenu, "width", pixels); + }); + } + ); + + ([ + ["number", 810, 350, 350], + ["number", 810, 1350, 810], + ["string", "700px", "300px", 300], + ["string", "700px", "1200px", 700], + ] as [string, string | number, string | number, number][]).forEach( + ([type, maxWidth, width, pixels]) => { + test(`should render with maximum width of ${pixels}px when maxWidth prop is passed as a ${type}`, async ({ + mount, + page, + }) => { + await mount(); + + const thisMenu = menu(page).first(); + await assertCssValueIsApproximately(thisMenu, "width", pixels); + }); + } + ); + + ([ + ["number", 30, 20, 30], + ["number", 30, 40, 40], + ["string", "35px", "25px", 35], + ["string", "35px", "40px", 40], + ] as [string, string | number, string | number, number][]).forEach( + ([type, minHeight, height, pixels]) => { + test(`should render with minimum height of ${pixels}px when minHeight prop is passed as a ${type}`, async ({ + mount, + page, + }) => { + await mount(); + + const thisMenu = menu(page).first(); + await assertCssValueIsApproximately(thisMenu, "height", pixels); + }); + } + ); + + ([ + ["number", 30, 20, 20], + ["number", 30, 40, 30], + ["string", "35px", "25px", 25], + ["string", "35px", "40px", 35], + ] as [string, string | number, string | number, number][]).forEach( + ([type, maxHeight, height, pixels]) => { + test(`should render with maximum height of ${pixels}px when maxHeight prop is passed as a ${type}`, async ({ + mount, + page, + }) => { + await mount(); + + const thisMenu = menu(page).first(); + await assertCssValueIsApproximately(thisMenu, "height", pixels); + }); + } + ); + + ["block", "inline-block", "flex", "contents", "list-item", "none"].forEach( + (display) => { + test(`should render with display as ${display}`, async ({ + mount, + page, + }) => { + await mount(); + + const thisMenu = menu(page).first(); + await expect(thisMenu).toHaveAttribute("display", display); + await expect(thisMenu).toHaveCSS("display", display); + }); + } + ); + + [ + "baseline", + "bottom", + "middle", + "sub", + "super", + "text-bottom", + "text-top", + "top", + ].forEach((alignment) => { + test(`should render with verticalAlign as ${alignment}`, async ({ + mount, + page, + }) => { + await mount(); + + const thisMenu = menu(page).first(); + await expect(thisMenu).toHaveCSS("vertical-align", alignment); + }); + }); + + ["auto", "clip", "hidden", "scroll", "visible"].forEach((overflow) => { + test(`should render with overflow as ${overflow}`, async ({ + mount, + page, + }) => { + await mount(); + + const thisMenu = menu(page).first(); + await expect(thisMenu).toHaveAttribute("overflow", overflow); + await expect(thisMenu).toHaveCSS("overflow", overflow); + }); + }); + + ([ + "auto", + "clip", + "hidden", + "scroll", + "visible", + ] as MenuProps["overflowX"][]).forEach((overflow) => { + test(`should render with overflowX as ${overflow}`, async ({ + mount, + page, + }) => { + await mount(); + + const thisMenu = menu(page).first(); + await expect(thisMenu).toHaveCSS("overflow-x", overflow as string); + }); + }); + + ([ + "auto", + "clip", + "hidden", + "scroll", + "visible", + ] as MenuProps["overflowY"][]).forEach((overflow) => { + test(`should render with overflowY as ${overflow}`, async ({ + mount, + page, + }) => { + await mount(); + + const thisMenu = menu(page).first(); + await expect(thisMenu).toHaveCSS("overflow-y", overflow as string); + }); + }); + + ["normal", "stretch", "baseline", "center", "flex-start", "flex-end"].forEach( + (alignment) => { + test(`should render with alignItems as ${alignment}`, async ({ + mount, + page, + }) => { + await mount(); + + const thisMenu = menu(page).first(); + await expect(thisMenu).toHaveCSS("align-items", alignment); + }); + } + ); + + [ + "normal", + "baseline", + "center", + "flex-start", + "flex-end", + "space-between", + "space-around", + "stretch", + ].forEach((alignment) => { + test(`should render with alignContent as ${alignment}`, async ({ + mount, + page, + }) => { + await mount(); + + const thisMenu = menu(page).first(); + await expect(thisMenu).toHaveCSS("align-content", alignment); + }); + }); + + [ + "left", + "center", + "right", + "flex-start", + "flex-end", + "normal", + "stretch", + ].forEach((justified) => { + test(`should render with justifyItems as ${justified}`, async ({ + mount, + page, + }) => { + await mount(); + + const thisMenu = menu(page).first(); + await expect(thisMenu).toHaveCSS("justify-items", justified); + }); + }); + + [ + "left", + "center", + "right", + "flex-start", + "flex-end", + "normal", + "space-between", + "space-around", + "stretch", + ].forEach((justified) => { + test(`should render with justifyContent as ${justified}`, async ({ + mount, + page, + }) => { + await mount(); + + const thisMenu = menu(page).first(); + await expect(thisMenu).toHaveCSS("justify-content", justified); + }); + }); + + (["nowrap", "wrap", "wrap-reverse"] as MenuProps["flexWrap"][]).forEach( + (wrap) => { + test(`should render with flexWrap as ${wrap}`, async ({ + mount, + page, + }) => { + await mount(); + + const thisMenu = menu(page).first(); + await expect(thisMenu).toHaveCSS("flex-wrap", wrap as string); + }); + } + ); + + ([ + "column", + "column-reverse", + "row", + "row-reverse", + ] as MenuProps["flexDirection"][]).forEach((direction) => { + test(`should render with flexDirection as ${direction}`, async ({ + mount, + page, + }) => { + await mount(); + + const thisMenu = menu(page).first(); + await expect(thisMenu).toHaveCSS("flex-direction", direction as string); + }); + }); + + ["auto", "content", "fit-content", "max-content", "min-content"].forEach( + (flex) => { + test(`should render with flex as ${flex}`, async ({ mount, page }) => { + await mount(); + + const thisMenu = menu(page).first(); + await expect(thisMenu).toHaveCSS("flex-basis", flex); + }); + } + ); + + ([ + [10, "10"], + [50, "50"], + [100, "100"], + ] as [MenuProps["flexGrow"], string][]).forEach(([value, growText]) => { + test(`should render with flexGrow as ${value}`, async ({ mount, page }) => { + await mount(); + + const thisMenu = menu(page).first(); + await expect(thisMenu).toHaveCSS("flex-grow", growText); + }); + }); + + ([ + [10, "10"], + [50, "50"], + [100, "100"], + ] as [MenuProps["flexShrink"], string][]).forEach(([value, shrinkText]) => { + test(`should render with flexShrink as ${value}`, async ({ + mount, + page, + }) => { + await mount(); + + const thisMenu = menu(page).first(); + await expect(thisMenu).toHaveCSS("flex-shrink", shrinkText); + }); + }); + + ["auto", "content", "fit-content", "max-content", "min-content"].forEach( + (basis) => { + test(`should render with flexBasis as ${basis}`, async ({ + mount, + page, + }) => { + await mount(); + + const thisMenu = menu(page).first(); + await expect(thisMenu).toHaveCSS("flex-basis", basis); + }); + } + ); + + [ + "auto", + "baseline", + "left", + "normal", + "right", + "stretch", + "center", + "flex-start", + "flex-end", + ].forEach((justify) => { + test(`should render with justifySelf as ${justify}`, async ({ + mount, + page, + }) => { + await mount(); + + const thisMenu = menu(page).first(); + await expect(thisMenu).toHaveCSS("justify-self", justify); + }); + }); + + [ + "auto", + "baseline", + "normal", + "stretch", + "center", + "flex-start", + "flex-end", + ].forEach((align) => { + test(`should render with alignSelf as ${align}`, async ({ + mount, + page, + }) => { + await mount(); + + const thisMenu = menu(page).first(); + await expect(thisMenu).toHaveCSS("align-self", align); + }); + }); + + ([ + [10, "10"], + [50, "50"], + [100, "100"], + ] as [MenuProps["order"], string][]).forEach(([value, orderText]) => { + test(`should render with order as ${value}`, async ({ mount, page }) => { + await mount(); + + const thisMenu = menu(page).first(); + await expect(thisMenu).toHaveCSS("order", orderText); + }); + }); + + test(`should render with className as ${CHARACTERS.STANDARD}`, async ({ + mount, + page, + }) => { + await mount(); + + const item = menuItem(page).nth(0); + const itemClass = await item.evaluate((element) => + element.getAttribute("class") + ); + expect(itemClass).toContain(CHARACTERS.STANDARD); + }); + + ([ + ["selected", true, "rgb(230, 235, 237)"], + ["not selected", false, "rgb(255, 255, 255)"], + ] as [string, MenuWithChildren["selected"], string][]).forEach( + ([state, boolVal, color]) => { + test(`should render with first Menu Item ${state}`, async ({ + mount, + page, + }) => { + await mount(); + + const subMenu = submenu(page).first().locator("span").first(); + await expect(subMenu).toHaveCSS("background-color", color); + }); + } + ); + + [ + CHARACTERS.STANDARD, + CHARACTERS.DIACRITICS, + CHARACTERS.SPECIALCHARACTERS, + ].forEach((text) => { + test(`should render with submenu text set to ${text}`, async ({ + mount, + page, + }) => { + await mount(); + + const item = menuItem(page).first(); + await expect(item).toHaveText(text); + }); + }); + + test(`should render with Item target ${CHARACTERS.STANDARD}`, async ({ + mount, + page, + }) => { + await mount(); + + const item = menuItem(page).first().locator("button"); + await expect(item).toHaveAttribute("target", CHARACTERS.STANDARD); + }); + + ([ + [true, 32], + [false, 16], + ] as [MenuWithChildren["showDropdownArrow"], number][]).forEach( + ([boolVal, padding]) => { + test(`should render with padding of ${padding}px on menu item when showDropdownArrow prop is ${boolVal}`, async ({ + mount, + page, + }) => { + await mount(); + + const subMenu = submenu(page).first().locator("button"); + await expect(subMenu).toHaveCSS("padding-right", `${padding}px`); + }); + } + ); + + ([ + ["default", "rgb(255, 255, 255)"], + ["alternate", "rgb(217, 224, 228)"], + ] as [MenuWithChildren["variant"], string][]).forEach(([variant, color]) => { + test(`should render with ${variant} Menu Item variant`, async ({ + mount, + page, + }) => { + await mount(); + + const subMenu = submenu(page).first().locator("span").first(); + await expect(subMenu).toHaveCSS("background-color", color); + }); + }); + + test(`should render with Menu Item ariaLabel set to ${CHARACTERS.STANDARD}`, async ({ + mount, + page, + }) => { + await mount(); + + const subMenu = submenu(page).first().locator("button"); + await expect(subMenu).toHaveAttribute("aria-label", CHARACTERS.STANDARD); + }); + + ([ + ["default", 1, 16, 121], + ["large", 4, 0, 153], + ] as [MenuDividerProps["size"], number, number, number][]).forEach( + ([size, height, margin, width]) => { + test(`should render with Menu size ${size}`, async ({ mount, page }) => { + await mount( + + + + Submenu Item One + + Submenu Item Two + + + + ); + + const subMenu = submenu(page).first(); + await subMenu.hover(); + const divider = menuDivider(page); + await assertCssValueIsApproximately(divider, "height", height); + await assertCssValueIsApproximately(divider, "margin-left", margin); + await assertCssValueIsApproximately(divider, "width", width); + }); + } + ); + + ([ + [100, 100], + [200, 200], + ["150px", 150], + ["250px", 250], + ] as [ScrollableBlockProps["height"], number][]).forEach( + ([height, pixels]) => { + test(`should render with scroll block height of ${pixels}px when height prop is passed as a number or string`, async ({ + mount, + page, + }) => { + await mount(); + + const subMenu = submenu(page).first(); + await subMenu.hover(); + const block = scrollBlock(page); + await assertCssValueIsApproximately(block, "height", pixels); + }); + } + ); + + ([ + ["default", "rgb(230, 235, 237)"], + ["alternate", "rgb(217, 224, 228)"], + ] as [ScrollableBlockProps["variant"], string][]).forEach( + ([variant, color]) => { + test(`should render Menu scroll block with ${variant} background color using variant prop`, async ({ + mount, + page, + }) => { + await mount(); + + const subMenu = submenu(page).first(); + await subMenu.hover(); + const block = scrollBlock(page).locator("span").first(); + await expect(block).toHaveCSS("background-color", color); + }); + } + ); + + ([ + ["default", "rgba(0, 0, 0, 0)"], + ["alternate", "rgb(217, 224, 228)"], + ] as [ScrollableBlockProps["variant"], string][]).forEach( + ([variant, color]) => { + test(`should render segment title with ${variant} variant background color`, async ({ + mount, + page, + }) => { + await mount( + + + + + Item Submenu One Is A Very Long Submenu Item Indeed + + + + + + ); + + const subMenu = submenu(page).first(); + await subMenu.hover(); + const title = segmentTitle(page); + await expect(title).toHaveCSS("background-color", color); + }); + } + ); + + test(`should verify that inner Menu without link is NOT available with tabbing in Fullscreen Menu`, async ({ + mount, + page, + }) => { + await mount(); + + await page.setViewportSize({ width: 1200, height: 800 }); + const item = menuItem(page).first(); + await item.click(); + const fullscreen = getComponent(page, "menu-fullscreen").first(); + await waitForAnimationEnd(fullscreen); + await continuePressingTAB(page, 8); + await expect(item).not.toBeFocused(); + }); + + test(`should render Menu Item with href prop set to ${CHARACTERS.STANDARD}`, async ({ + mount, + page, + }) => { + await mount(); + + const subMenu = submenu(page).first().locator("a"); + await expect(subMenu).toHaveAttribute("href", CHARACTERS.STANDARD); + }); + + ([["noopener"], ["noreferrer"], ["opener"]] as [ + MenuWithChildren["rel"] + ][]).forEach(([rel]) => { + test(`should render Menu Item with rel prop set to ${rel}`, async ({ + mount, + page, + }) => { + await mount(); + + const subMenu = submenu(page).first().locator("button"); + await expect(subMenu).toHaveAttribute("rel", rel as string); + }); + }); + + test(`should render Scrollable Block with parent`, async ({ + mount, + page, + }) => { + await mount(); + + const subMenu = submenu(page).first(); + await subMenu.hover(); + const scrollBlockParent = getComponent(page, "scrollable-block-parent"); + await expect(scrollBlockParent).toHaveCount(1); + }); + + ([ + ["default", "rgb(255, 255, 255)"], + ["alternate", "rgb(230, 235, 237)"], + ] as [ScrollableBlockProps["parentVariant"], string][]).forEach( + ([variant, color]) => { + test(`should render scrollable block with ${variant} variant background color`, async ({ + mount, + page, + }) => { + await mount(); + + const subMenu = submenu(page).first(); + await subMenu.hover(); + const parentBackground = getComponent(page, "scrollable-block-parent") + .locator("span") + .nth(0); + await expect(parentBackground).toHaveAttribute( + "data-component", + "link" + ); + await expect(parentBackground).toHaveCSS("background-color", color); + }); + } + ); +}); + +test.describe("Prop tests for Menu Fullscreen component", () => { + test(`should render Menu Fullscreen`, async ({ mount, page }) => { + await page.setViewportSize({ width: 1200, height: 800 }); + await mount(); + + const item = menuItem(page).first(); + await item.click(); + const fullscreen = getComponent(page, "menu-fullscreen").first(); + await waitForAnimationEnd(fullscreen); + const fullScreenMenu1 = fullscreenMenu(page, 0).locator("span"); + await expect(fullScreenMenu1).toHaveAttribute("data-element", "close"); + await expect(fullScreenMenu1).toBeVisible(); + const fullScreenMenu2 = fullscreenMenu(page, 1).locator("ul").first(); + await expect(fullScreenMenu2).toHaveAttribute("data-component", "menu"); + await expect(fullScreenMenu2).toBeVisible(); + const fullScreenMenu3 = fullscreenMenu(page, 1).locator("li"); + await expect(fullScreenMenu3).toHaveCount(15); + }); + + test(`should verify that the Menu Fullscreen is closed when close icon is clicked`, async ({ + mount, + page, + }) => { + await page.setViewportSize({ width: 1200, height: 800 }); + await mount(); + + const item = menuItem(page).first(); + await item.click(); + const fullscreen = getComponent(page, "menu-fullscreen").first(); + await waitForAnimationEnd(fullscreen); + const closeIcon = closeIconButton(page).first(); + await closeIcon.click(); + const fullScreenMenu1 = fullscreenMenu(page, 0).locator("span"); + await expect(fullScreenMenu1).not.toBeVisible(); + const fullScreenMenu2 = fullscreenMenu(page, 1).locator("ul").first(); + await expect(fullScreenMenu2).not.toBeVisible(); + const thisMenu = menu(page).first(); + await expect(thisMenu).toBeVisible(); + }); + + test(`should verify that close icon is focused in Menu Fullscreen when focusRedesignOptOut is false`, async ({ + mount, + page, + }) => { + await page.setViewportSize({ width: 1200, height: 800 }); + await mount(); + + const item = menuItem(page).first(); + await item.click(); + const fullscreen = getComponent(page, "menu-fullscreen").first(); + await waitForAnimationEnd(fullscreen); + await page.keyboard.press("Tab"); + const closeIcon = closeIconButton(page).first(); + await expect(closeIcon).toHaveCSS( + "box-shadow", + "rgb(255, 188, 25) 0px 0px 0px 3px, rgba(0, 0, 0, 0.9) 0px 0px 0px 6px" + ); + await expect(closeIcon).toHaveCSS("outline", "rgba(0, 0, 0, 0) solid 3px"); + const focusedElement = page.locator("*:focus"); + await expect(focusedElement).toHaveCSS( + "box-shadow", + "rgb(255, 188, 25) 0px 0px 0px 3px, rgba(0, 0, 0, 0.9) 0px 0px 0px 6px" + ); + await expect(focusedElement).toHaveCSS( + "outline", + "rgba(0, 0, 0, 0) solid 3px" + ); + }); + + test(`should verify that close icon is focused in Menu Fullscreen when focusRedesignOptOut is true`, async ({ + mount, + page, + }) => { + await page.setViewportSize({ width: 1200, height: 800 }); + await mount(, { + hooksConfig: { focusRedesignOptOut: true }, + }); + + const item = menuItem(page).first(); + await item.click(); + const fullscreen = getComponent(page, "menu-fullscreen").first(); + await waitForAnimationEnd(fullscreen); + await page.keyboard.press("Tab"); + const closeIcon = closeIconButton(page).first(); + await checkGoldenOutline(closeIcon); + await page.keyboard.press("Tab"); + const focusedElement = page.locator("*:focus"); + await expect(focusedElement).toHaveCSS( + "box-shadow", + "rgb(255, 188, 25) 0px 0px 0px 3px inset" + ); + }); + + test(`should verify that inner Menu is available with tabbing and styles are correct`, async ({ + mount, + page, + }) => { + await page.setViewportSize({ width: 1200, height: 800 }); + await mount(); + + const item = menuItem(page).first(); + await item.click(); + const fullscreen = getComponent(page, "menu-fullscreen").first(); + await waitForAnimationEnd(fullscreen); + await continuePressingTAB(page, 4); + const fullMenuItem = fullscreenMenu(page, 3) + .locator("li") + .locator("a") + .first(); + + const fullMenuItemBoxShadow = await fullMenuItem.evaluate((element) => { + const style = window.getComputedStyle(element); + return style.getPropertyValue("box-shadow"); + }); + expect(fullMenuItemBoxShadow).toContain("rgb(255, 188, 25)"); + + const fullMenuItemBackgroundColor = await fullMenuItem.evaluate( + (element) => { + const style = window.getComputedStyle(element); + return style.getPropertyValue("background-color"); + } + ); + expect(fullMenuItemBackgroundColor).toContain("rgb(0, 126, 69)"); + + const fullMenuItemColor = await fullMenuItem.evaluate((element) => { + const style = window.getComputedStyle(element); + return style.getPropertyValue("color"); + }); + expect(fullMenuItemColor).toContain("rgb(255, 255, 255)"); + await expect(fullMenuItem).toBeFocused(); + }); + + test(`should verify that inner Menu is available with shift-tabbing and styles are correct`, async ({ + mount, + page, + }) => { + await page.setViewportSize({ width: 1200, height: 800 }); + await mount(); + + const item = menuItem(page).first(); + await item.click(); + const fullscreen = getComponent(page, "menu-fullscreen").first(); + await waitForAnimationEnd(fullscreen); + await continuePressingTAB(page, 5); + await page.keyboard.press("Shift+Tab"); + const fullMenuItem = fullscreenMenu(page, 3) + .locator("li") + .locator("a") + .first(); + + const fullMenuItemBoxShadow = await fullMenuItem.evaluate((element) => { + const style = window.getComputedStyle(element); + return style.getPropertyValue("box-shadow"); + }); + expect(fullMenuItemBoxShadow).toContain("rgb(255, 188, 25)"); + + const fullMenuItemBackgroundColor = await fullMenuItem.evaluate( + (element) => { + const style = window.getComputedStyle(element); + return style.getPropertyValue("background-color"); + } + ); + expect(fullMenuItemBackgroundColor).toContain("rgb(0, 126, 69)"); + + const fullMenuItemColor = await fullMenuItem.evaluate((element) => { + const style = window.getComputedStyle(element); + return style.getPropertyValue("color"); + }); + expect(fullMenuItemColor).toContain("rgb(255, 255, 255)"); + + await expect(fullMenuItem).toBeFocused(); + }); + + test(`should verify that inner Menu without link is NOT available when tabbing in Fullscreen Menu`, async ({ + mount, + page, + }) => { + await page.setViewportSize({ width: 1200, height: 800 }); + await mount(); + + const item = menuItem(page).first(); + await item.click(); + const fullscreen = getComponent(page, "menu-fullscreen").first(); + await waitForAnimationEnd(fullscreen); + await continuePressingTAB(page, 5); + await expect(item).not.toBeFocused(); + }); + + ([ + ["open", true], + ["closed", false], + ] as [string, MenuFullscreenProps["isOpen"]][]).forEach( + ([value, boolVal]) => { + test(`should render with Menu Fullscreen ${value} when isOpen prop is ${boolVal}`, async ({ + mount, + page, + }) => { + await page.setViewportSize({ width: 1200, height: 800 }); + await mount(); + + const fullScreenMenu = getComponent(page, "menu-fullscreen").nth(3); + if (boolVal) { + await expect(fullScreenMenu).toBeVisible(); + } else { + await expect(fullScreenMenu).not.toBeVisible(); + } + }); + } + ); + + test(`should verify that Menu Fullscreen has no effect on the tab order when isOpen prop is false`, async ({ + mount, + page, + }) => { + await mount(); + + await page.keyboard.press("Tab"); + const button1 = page.getByRole("button").nth(0); + await expect(button1).toBeFocused(); + await page.keyboard.press("Tab"); + const button2 = page.getByRole("button").nth(1); + await expect(button2).toBeFocused(); + }); + + ([ + ["left", -1200, 1200], + ["right", 1200, -1200], + ] as [MenuFullscreenProps["startPosition"], number, number][]).forEach( + ([side, left, right]) => { + test(`should render with Menu Fullscreen start position ${side}`, async ({ + mount, + page, + }) => { + await page.setViewportSize({ width: 1200, height: 800 }); + await mount(); + + const menuFullScreen = getComponent(page, "menu-fullscreen").first(); + await expect(menuFullScreen).toHaveCSS("left", `${left}px`); + await expect(menuFullScreen).toHaveCSS("right", `${right}px`); + }); + } + ); + + test(`should focus the next menu item on tab press when the current item has a Search input with searchButton but no value`, async ({ + mount, + page, + }) => { + await page.setViewportSize({ width: 1200, height: 800 }); + await mount(); + + const item1 = menuItem(page).first().locator("a"); + await item1.focus(); + await page.keyboard.press("Tab"); + const searchInput = searchDefaultInput(page); + await expect(searchInput).toBeFocused(); + await page.keyboard.press("Tab"); + const item2 = menuItem(page).last().locator("a"); + await expect(item2).toBeFocused(); + }); + + test(`should focus the search icon and button on tab press when the current item has a Search input with searchButton and has a value, focusRedesignOptOut flag not set`, async ({ + mount, + page, + }) => { + await page.setViewportSize({ width: 1200, height: 800 }); + await mount(); + + const item1 = menuItem(page).first().locator("a"); + await item1.focus(); + await page.keyboard.press("Tab"); + const searchInput = searchDefaultInput(page); + await expect(searchInput).toBeFocused(); + await page.keyboard.press("Tab"); + const crossIcon = searchCrossIcon(page).locator(".."); + await expect(crossIcon).toBeFocused(); + await page.keyboard.press("Tab"); + const button = searchButton(page); + await expect(button).toBeFocused(); + await expect(button).toHaveCSS( + "box-shadow", + "rgb(255, 188, 25) 0px 0px 0px 3px, rgba(0, 0, 0, 0.9) 0px 0px 0px 6px" + ); + await page.keyboard.press("Tab"); + const item2 = menuItem(page).last().locator("a"); + await expect(item2).toBeFocused(); + }); + + test(`should focus the search icon and button on tab press when the current item has a Search input with searchButton and has a value, focusRedesignOptOut flag set`, async ({ + mount, + page, + }) => { + await mount( + , + { + hooksConfig: { focusRedesignOptOut: true }, + } + ); + + const item = menuItem(page).first().locator("a"); + await item.focus(); + await page.keyboard.press("Tab"); + const searchInput = searchDefaultInput(page); + await expect(searchInput).toBeFocused(); + await page.keyboard.press("Tab"); + const crossIcon = searchCrossIcon(page).locator(".."); + await expect(crossIcon).toBeFocused(); + await page.keyboard.press("Tab"); + const button = searchButton(page); + await expect(button).toBeFocused(); + await checkGoldenOutline(button); + await page.keyboard.press("Tab"); + const item2 = menuItem(page).last().locator("a"); + await expect(item2).toBeFocused(); + }); +}); + +test.describe("Event tests for Menu component", () => { + test(`should call onClick callback when a click event is triggered`, async ({ + mount, + page, + }) => { + let callbackCount = 0; + await mount( + { + callbackCount += 1; + }} + /> + ); + + const menuComp = menuComponent(page, 1); + await menuComp.click(); + expect(callbackCount).toBe(1); + }); + + test(`should call onSubmenuOpen callback when mouseover event is triggered`, async ({ + mount, + page, + }) => { + let callbackCount = 0; + await mount( + { + callbackCount += 1; + }} + submenu="Menu Item One" + /> + ); + + const subMenu = submenu(page).first(); + await subMenu.hover(); + expect(callbackCount).toBe(1); + }); + + test(`should call onSubmenuOpen callback when a click event is triggered`, async ({ + mount, + page, + }) => { + let callbackCount = 0; + await mount( + { + callbackCount += 1; + }} + submenu="Menu Item One" + /> + ); + + const menuComp = menuComponent(page, 1); + await menuComp.click(); + expect(callbackCount).toBe(1); + }); + + ["Space", "Enter", "ArrowDown", "ArrowUp"].forEach((key) => { + test(`should call onSubmenuOpen callback when a ${key} keyboard event is triggered`, async ({ + mount, + page, + }) => { + let callbackCount = 0; + await mount( + { + callbackCount += 1; + }} + submenu="Menu Item One" + /> + ); + + await page.keyboard.press("Tab"); + await page.keyboard.press(key); + expect(callbackCount).toBe(1); + }); + }); + + test(`should call onSubmenuClose callback when menu is closed`, async ({ + mount, + page, + }) => { + let callbackCount = 0; + await mount( + { + callbackCount += 1; + }} + submenu="Menu Item Three" + /> + ); + + await page.keyboard.press("Tab"); + await page.keyboard.press("ArrowDown"); + expect(callbackCount).toBe(1); + }); + + test(`should call onClose callback when Menu Fullscreen is closed`, async ({ + mount, + page, + }) => { + let callbackCount = 0; + await page.setViewportSize({ width: 1200, height: 800 }); + await mount( + { + callbackCount += 1; + }} + /> + ); + + const item = menuItem(page).first(); + await item.click(); + const fullscreen = getComponent(page, "menu-fullscreen").first(); + await waitForAnimationEnd(fullscreen); + const closeIcon = closeIconButton(page).first(); + await closeIcon.click(); + expect(callbackCount).toBe(1); + }); + + test(`should have correct keyboard navigation order when children of submenu update`, async ({ + mount, + page, + }) => { + await mount(); + + const subMenu = submenu(page).first(); + await subMenu.hover(); + const subMenuBlock = submenuBlock(page).locator("li"); + await expect(subMenuBlock).toHaveCount(4); + await page.keyboard.press("Tab"); + await page.keyboard.press("ArrowDown"); + const focusedElement1 = page.locator("*:focus"); + await expect(focusedElement1).toHaveText("Apple"); + await page.keyboard.press("ArrowDown"); + const focusedElement2 = page.locator("*:focus"); + await expect(focusedElement2).toHaveText("Banana"); + await page.keyboard.press("ArrowDown"); + const focusedElement3 = page.locator("*:focus"); + await expect(focusedElement3).toHaveText("Carrot"); + await page.keyboard.press("ArrowDown"); + const focusedElement4 = page.locator("*:focus"); + await expect(focusedElement4).toHaveText("Broccoli"); + await page.keyboard.press("ArrowUp"); + const focusedElement5 = page.locator("*:focus"); + await expect(focusedElement5).toHaveText("Carrot"); + await page.keyboard.press("ArrowUp"); + const focusedElement6 = page.locator("*:focus"); + await expect(focusedElement6).toHaveText("Banana"); + await page.keyboard.press("ArrowUp"); + const focusedElement7 = page.locator("*:focus"); + await expect(focusedElement7).toHaveText("Apple"); + }); +}); + +test.describe("Accessibility tests for Menu component", () => { + test(`should pass accessibility tests for default Menu`, async ({ + mount, + page, + }) => { + await mount(); + + await checkAccessibility(page); + }); + + test(`should pass accessibility tests when expanded`, async ({ + mount, + page, + }) => { + await mount(); + + const subMenu = submenu(page).first(); + await subMenu.hover(); + await checkAccessibility(page); + }); + + (["default", "large"] as MenuDividerProps["size"][]).forEach((divider) => { + test(`should pass accessibility tests when divider size is ${divider}`, async ({ + mount, + page, + }) => { + await mount(); + + const subMenu = submenu(page).first(); + await subMenu.hover(); + await checkAccessibility(page); + }); + }); + + // Skipped because of FE-6287 + test.skip(`should pass accessibility tests when search component is focused`, async ({ + mount, + page, + }) => { + await mount(); + + await page.keyboard.press("Tab"); + await page.keyboard.press("Enter"); + await page.keyboard.press("Tab"); + const subMenu = getComponent(page, "submenu").first(); + await waitForAnimationEnd(subMenu); + await checkAccessibility(page); + }); + + test(`should pass accessibility tests when a submenu has a long label`, async ({ + mount, + page, + }) => { + await mount( + + + + + Item Submenu One Is A Very Long Submenu Item Indeed + + + Item Submenu Two + + + + + ); + + const subMenu = submenu(page).first(); + await subMenu.hover(); + await checkAccessibility(page); + }); + + test(`should pass accessibility tests when a menu item has a long label`, async ({ + mount, + page, + }) => { + await mount( + + + + Item Submenu One + + Item Submenu Two + + + + + ); + + const subMenu = submenu(page).first(); + await subMenu.hover(); + await checkAccessibility(page); + }); + + ["450px", "675px", "1200px"].forEach((width) => { + test(`should pass accessibility tests when width is ${width}`, async ({ + mount, + page, + }) => { + await mount(); + + await checkAccessibility(page); + }); + }); + + ["10px", "30px", "50px"].forEach((propValue) => { + test(`should pass accessibility tests when height is ${propValue}`, async ({ + mount, + page, + }) => { + await mount(); + + await checkAccessibility(page); + }); + }); + + (["default", "large"] as MenuDividerProps["size"][]).forEach((size) => { + test(`should pass accessibility tests when size is ${size}px`, async ({ + mount, + page, + }) => { + await mount(); + + await checkAccessibility(page); + }); + }); + + ["block", "inline-block", "flex", "contents", "list-item", "none"].forEach( + (display) => { + test(`should pass accessibility tests when display is ${display}`, async ({ + mount, + page, + }) => { + await mount(); + + await checkAccessibility(page); + }); + } + ); + + [ + "baseline", + "bottom", + "middle", + "sub", + "super", + "text-bottom", + "text-top", + "top", + ].forEach((alignment) => { + test(`should pass accessibility tests when alignItems is ${alignment}`, async ({ + mount, + page, + }) => { + await mount(); + + await checkAccessibility(page); + }); + }); + + ["auto", "clip", "hidden", "scroll", "visible"].forEach((overflow) => { + test(`should pass accessibility tests when overflow is ${overflow}`, async ({ + mount, + page, + }) => { + await mount(); + + await checkAccessibility(page); + }); + }); + + ([ + "auto", + "clip", + "hidden", + "scroll", + "visible", + ] as MenuProps["overflowX"][]).forEach((overflow) => { + test(`should pass accessibility tests when overflowX is ${overflow}`, async ({ + mount, + page, + }) => { + await mount(); + + await checkAccessibility(page); + }); + }); + + ([ + "auto", + "clip", + "hidden", + "scroll", + "visible", + ] as MenuProps["overflowY"][]).forEach((overflow) => { + test(`should pass accessibility tests when overflowY is ${overflow}`, async ({ + mount, + page, + }) => { + await mount(); + + await checkAccessibility(page); + }); + }); + + ["normal", "stretch", "baseline", "center", "flex-start", "flex-end"].forEach( + (alignment) => { + test(`should pass accessibility tests for when alignItems is ${alignment}`, async ({ + mount, + page, + }) => { + await mount(); + + await checkAccessibility(page); + }); + } + ); + + [ + "normal", + "baseline", + "center", + "flex-start", + "flex-end", + "space-between", + "space-around", + "stretch", + ].forEach((alignment) => { + test(`should pass accessibility tests when alignContent is ${alignment}`, async ({ + mount, + page, + }) => { + await mount(); + + await checkAccessibility(page); + }); + }); + + [ + "left", + "center", + "right", + "flex-start", + "flex-end", + "normal", + "stretch", + ].forEach((justified) => { + test(`should pass accessibility tests when justifyItems is ${justified}`, async ({ + mount, + page, + }) => { + await mount(); + + await checkAccessibility(page); + }); + }); + + [ + "left", + "center", + "right", + "flex-start", + "flex-end", + "normal", + "space-between", + "space-around", + "stretch", + ].forEach((justified) => { + test(`should pass accessibility tests when justifyContent is ${justified}`, async ({ + mount, + page, + }) => { + await mount(); + + await checkAccessibility(page); + }); + }); + + (["nowrap", "wrap", "wrap-reverse"] as MenuProps["flexWrap"][]).forEach( + (wrap) => { + test(`should pass accessibility tests when flexWrap is ${wrap}`, async ({ + mount, + page, + }) => { + await mount(); + + await checkAccessibility(page); + }); + } + ); + + ([ + "column", + "column-reverse", + "row", + "row-reverse", + ] as MenuProps["flexDirection"][]).forEach((direction) => { + test(`should pass accessibility tests when flexDirection is ${direction}`, async ({ + mount, + page, + }) => { + await mount(); + + await checkAccessibility(page); + }); + }); + + ["auto", "content", "fit-content", "max-content", "min-content"].forEach( + (flex) => { + test(`should pass accessibility tests when flex is ${flex}`, async ({ + mount, + page, + }) => { + await mount(); + + await checkAccessibility(page); + }); + } + ); + + [10, 50, 100].forEach((value) => { + test(`should pass accessibility tests when flexGrow is ${value}`, async ({ + mount, + page, + }) => { + await mount(); + + await checkAccessibility(page); + }); + }); + + [10, 50, 100].forEach((value) => { + test(`should pass accessibility tests when flexShrink is ${value}`, async ({ + mount, + page, + }) => { + await mount(); + + await checkAccessibility(page); + }); + }); + + ["auto", "content", "fit-content", "max-content", "min-content"].forEach( + (basis) => { + test(`should pass accessibility tests when flexBasis is ${basis}`, async ({ + mount, + page, + }) => { + await mount(); + + await checkAccessibility(page); + }); + } + ); + + [ + "auto", + "baseline", + "left", + "normal", + "right", + "stretch", + "center", + "flex-start", + "flex-end", + ].forEach((justify) => { + test(`should pass accessibility tests when justifySelf is ${justify}`, async ({ + mount, + page, + }) => { + await mount(); + + await checkAccessibility(page); + }); + }); + + [ + "auto", + "baseline", + "normal", + "stretch", + "center", + "flex-start", + "flex-end", + ].forEach((align) => { + test(`should pass accessibility tests when alignSelf is ${align}`, async ({ + mount, + page, + }) => { + await mount(); + + await checkAccessibility(page); + }); + }); + + [ + CHARACTERS.STANDARD, + CHARACTERS.DIACRITICS, + CHARACTERS.SPECIALCHARACTERS, + ].forEach((text) => { + test(`should pass accessibility tests when item text is ${text}`, async ({ + mount, + page, + }) => { + await mount(); + + await checkAccessibility(page); + }); + }); + + (["default", "alternate"] as ScrollableBlockProps["variant"][]).forEach( + (variant) => { + test(`should pass accessibility tests when scroll block has ${variant} variant background color`, async ({ + mount, + page, + }) => { + await mount(); + + const subMenu = submenu(page).first(); + await subMenu.hover(); + + await checkAccessibility(page); + }); + } + ); + + (["default", "alternate"] as ScrollableBlockProps["variant"][]).forEach( + (variant) => { + test(`should pass accessibility tests when Segment Title has a ${variant} variant background color`, async ({ + mount, + page, + }) => { + await mount(); + + const subMenu = submenu(page).first(); + await subMenu.hover(); + + await checkAccessibility(page); + }); + } + ); + + test(`should pass accessibility tests for Menu with parent item`, async ({ + mount, + page, + }) => { + await mount(); + + const subMenu = submenu(page).first(); + await subMenu.hover(); + await checkAccessibility(page); + }); + + test(`should pass accessibility tests for Menu with button icon`, async ({ + mount, + page, + }) => { + await mount(); + + const subMenu = submenu(page).first(); + await subMenu.hover(); + await checkAccessibility(page); + }); + + // Test skipped because of issue FE-5731 + test.skip(`should pass accessibility tests for Menu with icon`, async ({ + mount, + page, + }) => { + await mount(); + + await checkAccessibility(page); + }); +}); + +test.describe("Accessibility tests for Menu Fullscreen component", () => { + test(`should pass accessibility tests for Menu Fullscreen`, async ({ + mount, + page, + }) => { + await page.setViewportSize({ width: 1200, height: 800 }); + await mount(); + + const item = menuItem(page).first(); + await item.click(); + const fullscreen = getComponent(page, "menu-fullscreen").first(); + await waitForAnimationEnd(fullscreen); + await checkAccessibility(page); + }); + + test(`should pass accessibility tests when close icon is clicked`, async ({ + mount, + page, + }) => { + await page.setViewportSize({ width: 1200, height: 800 }); + await mount(); + + const item = menuItem(page).first(); + await item.click(); + await expect( + getComponent(page, "menu-fullscreen") + .first() + .locator("a") + .first() + .locator("span") + ).toHaveText("Menu Item One"); + const closeIcon = closeIconButton(page).first(); + await closeIcon.click(); + await checkAccessibility(page); + }); + + test(`should pass accessibility tests when menu item is highlighted`, async ({ + mount, + page, + }) => { + await page.setViewportSize({ width: 1200, height: 800 }); + await mount(); + + const item = menuItem(page).first(); + await item.click(); + const fullscreen = getComponent(page, "menu-fullscreen").first(); + await waitForAnimationEnd(fullscreen); + await continuePressingTAB(page, 5); + await checkAccessibility(page); + }); + + (["left", "right"] as MenuFullscreenProps["startPosition"][]).forEach( + (side) => { + test(`should pass accessibility tests when start position is ${side}`, async ({ + mount, + page, + }) => { + await page.setViewportSize({ width: 1200, height: 800 }); + await mount(); + + const item = menuItem(page).first(); + await item.click(); + const fullscreen = getComponent(page, "menu-fullscreen").first(); + await waitForAnimationEnd(fullscreen); + await checkAccessibility(page); + }); + } + ); +}); + +test.describe( + "Styling, Scrolling & Navigation Bar Tests for Menu Component", + () => { + test(`should render menu items with the expected focus styling when focusRedesignOptOut is true`, async ({ + mount, + page, + }) => { + await page.setViewportSize({ width: 1200, height: 800 }); + await mount(, { + hooksConfig: { focusRedesignOptOut: true }, + }); + + const item1 = menuItem(page).first().locator("a"); + await item1.focus(); + await expect(item1).toHaveCSS( + "box-shadow", + "rgb(255, 188, 25) 0px 0px 0px 3px inset" + ); + + const item2 = menuItem(page).last().locator("button"); + await item2.focus(); + await expect(item2).toHaveCSS( + "box-shadow", + "rgb(255, 188, 25) 0px 0px 0px 3px inset" + ); + await item2.click(); + + const subMenu1 = submenu(page).last().locator("button").first(); + await subMenu1.focus(); + await expect(subMenu1).toHaveCSS( + "box-shadow", + "rgb(255, 188, 25) 0px 0px 0px 3px inset" + ); + + const subMenu2 = submenu(page).last().locator("a").first(); + await subMenu2.focus(); + await expect(subMenu2).toHaveCSS( + "box-shadow", + "rgb(255, 188, 25) 0px 0px 0px 3px inset" + ); + }); + + test(`should render menu items with the expected focus styling when focusRedesignOptOut is false`, async ({ + mount, + page, + }) => { + await page.setViewportSize({ width: 1200, height: 800 }); + await mount(); + + const item1 = menuItem(page).first().locator("a"); + await item1.focus(); + await expect(item1).toHaveCSS( + "box-shadow", + "rgba(0, 0, 0, 0.9) 0px 0px 0px 3px inset, rgb(255, 188, 25) 0px 0px 0px 6px inset" + ); + await expect(item1).toHaveCSS("outline", "rgba(0, 0, 0, 0) solid 3px"); + + const item2 = menuItem(page).last().locator("button"); + await item2.focus(); + await expect(item2).toHaveCSS( + "box-shadow", + "rgba(0, 0, 0, 0.9) 0px 0px 0px 3px inset, rgb(255, 188, 25) 0px 0px 0px 6px inset" + ); + await expect(item2).toHaveCSS("outline", "rgba(0, 0, 0, 0) solid 3px"); + await item2.click(); + + const subMenu1 = submenu(page).last().locator("button").first(); + await subMenu1.focus(); + await expect(subMenu1).toHaveCSS( + "box-shadow", + "rgba(0, 0, 0, 0.9) 0px 0px 0px 3px inset, rgb(255, 188, 25) 0px 0px 0px 6px inset" + ); + await expect(subMenu1).toHaveCSS("outline", "rgba(0, 0, 0, 0) solid 3px"); + + const subMenu2 = submenu(page).last().locator("a").first(); + await subMenu2.focus(); + await expect(subMenu2).toHaveCSS( + "box-shadow", + "rgba(0, 0, 0, 0.9) 0px 0px 0px 3px inset, rgb(255, 188, 25) 0px 0px 0px 6px inset" + ); + await expect(subMenu2).toHaveCSS("outline", "rgba(0, 0, 0, 0) solid 3px"); + }); + + test(`should render with the expected border radius styling on the Submenu`, async ({ + mount, + page, + }) => { + await mount(); + + const subMenu = submenu(page).first(); + await subMenu.hover(); + const subMenuBlock = submenuBlock(page).first(); + await expect(subMenuBlock).toHaveCSS("border-radius", "0px 0px 8px 8px"); + const subMenu2 = submenu(page).locator("a").last(); + await expect(subMenu2).toHaveCSS("border-radius", "0px 0px 8px 8px"); + }); + + test(`should render with the expected border radius styling on the Submenu Scrollable Block`, async ({ + mount, + page, + }) => { + await mount(); + + const subMenu = submenu(page).first(); + await subMenu.hover(); + const scrollableBlock = scrollBlock(page).first(); + await expect(scrollableBlock).toHaveCSS( + "border-radius", + "0px 0px 0px 8px" + ); + const scrollableItem = scrollBlock(page).locator("a").last(); + await expect(scrollableItem).toHaveCSS( + "border-radius", + "0px 0px 0px 8px" + ); + }); + + test(`should verify that tabbing forward through the menu and back to the start should not make the background scroll to the bottom`, async ({ + mount, + page, + }) => { + await mount(); + + await continuePressingTAB(page, 4); + const closeIcon = closeIconButton(page); + await expect(closeIcon).toBeFocused(); + await expect(page.getByTestId("#bottom-box")).not.toBeInViewport(); + }); + + test(`should verify that tabbing backward through the menu and back to the start should not make the background scroll to the bottom`, async ({ + mount, + page, + }) => { + await mount(); + + await continuePressingSHIFTTAB(page, 3); + const closeIcon = closeIconButton(page); + await expect(closeIcon).toBeFocused(); + await expect(page.getByTestId("#bottom-box")).not.toBeInViewport(); + }); + + test(`should render with all the content of a long submenu accessible with the keyboard while remaining visible`, async ({ + mount, + page, + }) => { + await mount(); + + await page.setViewportSize({ width: 1000, height: 500 }); + await page.keyboard.press("Tab"); + await page.keyboard.press("ArrowDown"); + const subMenuItem = submenuItem(page, 1); + await expect(subMenuItem).toHaveCount(20); + for (let i = 0; i < 20; i++) { + await page.keyboard.press("ArrowDown"); + } + const focusedElement = page.locator("*:focus"); + await expect(focusedElement).toHaveText("Foo 20"); + const subMenu = page.locator( + '[data-component="submenu-wrapper"] ul > li:nth-child(20)' + ); + await expect(subMenu).toBeInViewport(); + }); + + test(`should render with all the content of a long submenu accessible with the keyboard while remaining visible if the navbar height changes`, async ({ + mount, + page, + }) => { + await mount(); + + await page.setViewportSize({ width: 1000, height: 500 }); + await page.keyboard.press("Tab"); + await page.keyboard.press("ArrowDown"); + const subMenuItem = submenuItem(page, 1); + await expect(subMenuItem).toHaveCount(21); + for (let i = 0; i < 3; i++) { + await page.keyboard.press("ArrowDown"); + } + await page.keyboard.press("Enter"); + + await page.waitForTimeout(100); + await page.keyboard.press("Tab"); + await page.keyboard.press("ArrowDown"); + for (let i = 0; i < 21; i++) { + await page.keyboard.press("ArrowDown"); + } + const subMenu = page.locator( + '[data-component="submenu-wrapper"] ul > li:nth-child(21)' + ); + await expect(subMenu).toBeInViewport(); + }); + } +);