diff --git a/cypress/components/tooltip/tooltip.cy.tsx b/cypress/components/tooltip/tooltip.cy.tsx deleted file mode 100644 index b4c876db14..0000000000 --- a/cypress/components/tooltip/tooltip.cy.tsx +++ /dev/null @@ -1,274 +0,0 @@ -import React from "react"; -import CypressMountWithProviders from "../../support/component-helper/cypress-mount"; -import { TooltipProps } from "../../../src/components/tooltip"; -import * as testStories from "../../../src/components/tooltip/tooltip-test.stories"; -import * as stories from "../../../src/components/tooltip/tooltip.stories"; -import { - tooltipPreview, - tooltipTrigger, - tooltipTriggerToggle, -} from "../../locators/tooltip/index"; -import { - SIZE, - COLOR, - CHARACTERS, -} from "../../support/component-helper/constants"; -import { assertCssValueIsApproximately } from "../../support/component-helper/common-steps"; -import { getDataElementByValue } from "../../locators"; -import { TooltipPositions } from "../../../src/components/tooltip/tooltip.config"; - -const testData = [CHARACTERS.DIACRITICS, CHARACTERS.SPECIALCHARACTERS]; -const backgroundColors = [COLOR.ORANGE, COLOR.RED, COLOR.BLACK, COLOR.BROWN]; - -context("Tests for Tooltip component", () => { - describe("should check Tooltip component properties", () => { - it.each(testData)( - "should check %s as message for Tooltip component", - (message) => { - CypressMountWithProviders( - - ); - tooltipPreview().should("have.text", message); - } - ); - - it.each(testData)("should check %s as Id for Tooltip component", (id) => { - CypressMountWithProviders(); - tooltipPreview().should("have.id", id); - }); - - it.each([ - [true, "be.visible"], - [false, "not.exist"], - ])( - "should check when tooltip visibility is %s for Tooltip component", - (bool, state) => { - CypressMountWithProviders( - - ); - tooltipPreview().should(state); - } - ); - - it.each(["bottom", "left", "right", "top"] as TooltipProps["position"][])( - "should check %s position of tooltip for Tooltip component", - (position) => { - CypressMountWithProviders( - - <>{`This tooltip is positioned ${position}`} - - ); - tooltipPreview().should("be.visible").and("have.css", position); - } - ); - - it.each(["undefined", "error"])( - "should check %s type for Tooltip component", - (type) => { - CypressMountWithProviders(); - tooltipPreview().should("have.attr", "type", type); - } - ); - - it.each(backgroundColors)( - "should check tooltip background-color as %s for Tooltip component", - (color) => { - CypressMountWithProviders( - - ); - tooltipPreview().should("have.css", "background-color", color); - } - ); - - it.each(backgroundColors)( - "should check tooltip font color as %s for Tooltip component", - (color) => { - CypressMountWithProviders( - - ); - tooltipPreview().should("have.css", "color", color); - } - ); - - it.each([ - [SIZE.MEDIUM, 14], - [SIZE.LARGE, 16], - ] as [TooltipProps["size"], number][])( - "should check %s size for Tooltip component", - (size, fontSize) => { - CypressMountWithProviders(); - tooltipPreview().should("have.css", "font-size", `${fontSize}px`); - } - ); - - it.each([ - ["left", "bottom", "top"], - ["top", "bottom", "top"], - ["left", "top", "bottom"], - ["bottom", "top", "bottom"], - ["bottom", "left", "bottom"], - ["bottom", "right", "bottom"], - ["top", "left", "top"], - ["top", "right", "top"], - ["right", "bottom", "right"], - ["right", "top", "right"], - ] as [TooltipPositions, TooltipProps["position"], Cypress.PositionType][])( - "should check flip position to the %s when tooltip position is %s and scrolling to the %s side for Tooltip component", - (flipPosition, tooltipPosition, scrollPosition) => { - CypressMountWithProviders( -
- -
- ); - cy.viewport(700, 120); - cy.scrollTo(scrollPosition); - tooltipPreview().should("have.attr", "data-placement", flipPosition); - } - ); - - describe.each(["top", "bottom", "right", "left"] as NonNullable< - TooltipProps["position"] - >[])("when tooltip has %s position and", (position) => { - it.each([ - [SIZE.SMALL, { top: 15, bottom: 631, left: 47, right: 1040 }], - [SIZE.MEDIUM, { top: 14, bottom: 630, left: 44, right: 1037 }], - [SIZE.LARGE, { top: 10, bottom: 626, left: 40, right: 1033 }], - ] as [TooltipProps["inputSize"], { top: number; bottom: number; left: number; right: number }][])( - "when inputSize is %s should have correct styles applied", - (inputSize, offset) => { - CypressMountWithProviders( - - ); - tooltipPreview().then(($el) => { - assertCssValueIsApproximately($el, position, offset[position]); - Cypress.dom.isVisible($el); - }); - } - ); - }); - - it("should show tooltip when target is hovered", () => { - CypressMountWithProviders(); - tooltipPreview().should("not.exist"); - tooltipTrigger().trigger("mouseenter"); - tooltipPreview().should("be.visible"); - }); - - it("should hide tooltip when mouse leaves target", () => { - CypressMountWithProviders(); - tooltipPreview().should("not.exist"); - tooltipTrigger().trigger("mouseenter"); - tooltipPreview().should("be.visible"); - tooltipTrigger().trigger("mouseleave"); - tooltipPreview().should("not.exist"); - }); - - it("should show tooltip when target is focused", () => { - CypressMountWithProviders(); - tooltipPreview().should("not.exist"); - tooltipTrigger().focus(); - tooltipPreview().should("be.visible"); - }); - - it("should hide tooltip when target is blurred", () => { - CypressMountWithProviders(); - tooltipPreview().should("not.exist"); - tooltipTrigger().focus(); - tooltipPreview().should("be.visible"); - tooltipTrigger().blur(); - tooltipPreview().should("not.exist"); - }); - - it("new tooltip target should still trigger tooltip visibility", () => { - CypressMountWithProviders( - - ); - tooltipTrigger().should("have.text", "Target"); - - tooltipTrigger().trigger("mouseenter"); - tooltipPreview().should("be.visible"); - tooltipTrigger().trigger("mouseleave"); - tooltipPreview().should("not.exist"); - - tooltipTriggerToggle().click(); - tooltipTrigger().should("have.text", "Secondary target"); - - tooltipTrigger().trigger("mouseenter"); - tooltipPreview().should("be.visible"); - tooltipTrigger().trigger("mouseleave"); - tooltipPreview().should("not.exist"); - }); - }); - - describe("Accessibility tests for Tooltip component", () => { - it("should pass accessibility tests for Tooltip Default story", () => { - CypressMountWithProviders(); - - getDataElementByValue("main-text").click(); - cy.checkAccessibility(); - }); - - it("should pass accessibility tests for Tooltip Controlled story", () => { - CypressMountWithProviders(); - - getDataElementByValue("main-text").eq(0).click(); - cy.checkAccessibility(); - }); - - it.each([ - ["top", 0], - ["bottom", 1], - ["left", 2], - ["right", 3], - ])( - "should pass accessibility tests for Tooltip Positioning story %s position", - (position, button) => { - CypressMountWithProviders(); - - getDataElementByValue("main-text").eq(button).click(); - cy.checkAccessibility(); - } - ); - - it("should pass accessibility tests for Tooltip FlipBehaviourOverrides story", () => { - CypressMountWithProviders(); - - cy.checkAccessibility(); - }); - - it("should pass accessibility tests for Tooltip LargeTooltip story", () => { - CypressMountWithProviders(); - - getDataElementByValue("main-text").click(); - cy.checkAccessibility(); - }); - - it("should pass accessibility tests for Tooltip Types story", () => { - CypressMountWithProviders(); - - getDataElementByValue("main-text").eq(1).click(); - - getDataElementByValue("main-text").eq(2).click(); - cy.checkAccessibility(); - }); - - it("should pass accessibility tests for Tooltip ColorOverrides story", () => { - CypressMountWithProviders(); - - getDataElementByValue("main-text").click(); - cy.checkAccessibility(); - }); - - it("should render the Tooltip with the expected border radius styling", () => { - CypressMountWithProviders(); - tooltipPreview().should("have.css", "border-radius", "4px"); - }); - }); -}); diff --git a/playwright/components/tooltip/index.ts b/playwright/components/tooltip/index.ts new file mode 100644 index 0000000000..8bf3f0df85 --- /dev/null +++ b/playwright/components/tooltip/index.ts @@ -0,0 +1,9 @@ +import type { Page } from "@playwright/test"; +import { TOOLTIP_PREVIEW } from "./locators"; + +// component preview locators +const tooltipPreview = (page: Page) => { + return page.locator(TOOLTIP_PREVIEW); +}; + +export default tooltipPreview; diff --git a/playwright/components/tooltip/locators.ts b/playwright/components/tooltip/locators.ts new file mode 100644 index 0000000000..fd44fb8309 --- /dev/null +++ b/playwright/components/tooltip/locators.ts @@ -0,0 +1,4 @@ +// component preview locators +const TOOLTIP_PREVIEW = '[data-element="tooltip"]'; + +export default TOOLTIP_PREVIEW; diff --git a/src/components/tooltip/components.test-pw.tsx b/src/components/tooltip/components.test-pw.tsx new file mode 100644 index 0000000000..0c4d983c01 --- /dev/null +++ b/src/components/tooltip/components.test-pw.tsx @@ -0,0 +1,213 @@ +import React, { forwardRef, useState } from "react"; +import Tooltip, { TooltipProps } from "."; +import Button, { ButtonProps } from "../button"; +import { SecondaryButton } from "../button/button.stories"; +import Box from "../box"; +import { TooltipPositions } from "./tooltip.config"; + +export const TooltipComponent = ({ + message, + ...props +}: Partial) => ( +
+ + + +
+); + +export const UncontrolledTooltipComponent = () => ( +
+ + + +
+); + +export const TooltipWithChangingTargetComponent = () => { + const [displayOther, setDisplayOther] = useState(false); + + return ( + <> + + + {displayOther ? ( + Secondary target + ) : ( + + )} + + + ); +}; + +export const Default = () => { + const Component = forwardRef( + ({ children }: ButtonProps, ref) => ( + + ) + ); + Component.displayName = "Example Button"; + return ( + + + target + + + ); +}; + +export const Controlled = () => { + const [isVisible, setIsVisible] = useState(false); + const Component = forwardRef( + ({ children }: ButtonProps, ref) => ( + + ) + ); + Component.displayName = "Example Button"; + return ( + <> + + + + + + target + + + + ); +}; + +export const Positioning = () => { + const [position, setPosition] = useState("top"); + const Component = forwardRef( + ({ children }: ButtonProps, ref) => ( + + ) + ); + Component.displayName = "Example Button"; + return ( + <> + + + + + + + + + target + + + + ); +}; + +export const FlipBehaviourOverrides = () => { + const Component = forwardRef( + ({ children }: ButtonProps, ref) => ( + + ) + ); + Component.displayName = "Example Button"; + return ( + + + target + + + ); +}; + +export const LargeTooltip = () => { + const Component = forwardRef( + ({ children }: ButtonProps, ref) => ( + + ) + ); + Component.displayName = "Example Button"; + return ( + + + target + + + ); +}; + +export const Types = () => { + const [type, setType] = useState(undefined); + const Component = forwardRef( + ({ children }: ButtonProps, ref) => ( + + ) + ); + Component.displayName = "Example Button"; + return ( + <> + + + + + + + target + + + + ); +}; + +export const ColorOverrides = () => { + const Component = forwardRef( + ({ children }: ButtonProps, ref) => ( + + ) + ); + Component.displayName = "Example Button"; + return ( + + + target + + + ); +}; diff --git a/src/components/tooltip/tooltip-test.stories.tsx b/src/components/tooltip/tooltip-test.stories.tsx index 75857beeec..a8897dd3b9 100644 --- a/src/components/tooltip/tooltip-test.stories.tsx +++ b/src/components/tooltip/tooltip-test.stories.tsx @@ -149,53 +149,3 @@ const SecondaryButton = forwardRef( ) ); SecondaryButton.displayName = "Tooltip"; - -export const TooltipComponent = ({ - message, - ...props -}: Partial) => ( -
- - - -
-); - -export const UncontrolledTooltipComponent = () => ( -
- - - -
-); - -export const TooltipWithChangingTargetComponent = () => { - const [displayOther, setDisplayOther] = useState(false); - - return ( - <> - - - {displayOther ? ( - Secondary target - ) : ( - - )} - - - ); -}; diff --git a/src/components/tooltip/tooltip.pw.tsx b/src/components/tooltip/tooltip.pw.tsx new file mode 100644 index 0000000000..a81aa2cdfd --- /dev/null +++ b/src/components/tooltip/tooltip.pw.tsx @@ -0,0 +1,365 @@ +import React from "react"; +import { test, expect } from "@playwright/experimental-ct-react17"; +import { + TooltipComponent, + UncontrolledTooltipComponent, + Default, + Controlled, + Positioning, + FlipBehaviourOverrides, + LargeTooltip, + Types, + ColorOverrides, +} from "./components.test-pw"; +import Box from "../../../src/components/box"; +import { tooltipPreview } from "../../../playwright/components/tooltip"; +import { TooltipPositions } from "../../../src/components/tooltip/tooltip.config"; +import { + checkAccessibility, + assertCssValueIsApproximately, +} from "../../../playwright/support/helper"; +import { SIZE, COLOR, CHARACTERS } from "../../../playwright/support/constants"; +import { TooltipProps } from "../../../src/components/tooltip/tooltip.component"; +import { getDataElementByValue } from "../../../playwright/components"; + +const backgroundColors = [COLOR.ORANGE, COLOR.RED, COLOR.BLACK, COLOR.BROWN]; +const testData = [CHARACTERS.DIACRITICS, CHARACTERS.SPECIALCHARACTERS]; + +test.describe("Tooltip component", () => { + testData.forEach((message) => { + test(`check when message prop is set as ${message}`, async ({ + mount, + page, + }) => { + await mount(); + + await expect(tooltipPreview(page)).toHaveText(message); + }); + }); + + testData.forEach((id) => { + test(`check when id prop is set as ${id}`, async ({ mount, page }) => { + await mount(); + + await expect(tooltipPreview(page)).toHaveId(id); + }); + }); + + [true, false].forEach((boolVal) => { + test(`check when tooltip visibility is set as ${boolVal}`, async ({ + mount, + page, + }) => { + await mount(); + + if (boolVal === true) { + await expect(tooltipPreview(page)).toBeVisible(); + } else { + await expect(tooltipPreview(page)).not.toBeVisible(); + } + }); + }); + + (["bottom", "left", "right", "top"] as [ + "bottom", + "left", + "right", + "top" + ]).forEach((position) => { + test(`check when tooltip position is set as ${position}`, async ({ + mount, + page, + }) => { + await mount( + + <>{`This tooltip is positioned ${position}`} + + ); + + await expect(tooltipPreview(page)).toBeVisible(); + await expect(tooltipPreview(page)).toHaveAttribute( + "data-placement", + position + ); + }); + }); + + ["undefined", "error"].forEach((type) => { + test(`check when type prop is set as ${type}`, async ({ mount, page }) => { + await mount(); + + await expect(tooltipPreview(page)).toHaveAttribute("type", type); + }); + }); + + backgroundColors.forEach((color) => { + test(`check when tooltip background color is set as ${color}`, async ({ + mount, + page, + }) => { + await mount(); + + await expect(tooltipPreview(page)).toHaveCSS("background-color", color); + }); + }); + + backgroundColors.forEach((color) => { + test(`check when tooltip font color is set as ${color}`, async ({ + mount, + page, + }) => { + await mount(); + + await expect(tooltipPreview(page)).toHaveCSS("color", color); + }); + }); + + ([ + [SIZE.MEDIUM, 14], + [SIZE.LARGE, 16], + ] as [TooltipProps["size"], number][]).forEach(([size, fontSize]) => { + test(`check when size prop is set as ${size}`, async ({ mount, page }) => { + await mount(); + + await expect(tooltipPreview(page)).toHaveCSS( + "font-size", + `${fontSize}px` + ); + }); + }); + + ([ + ["left", "bottom"], + ["top", "bottom"], + ["left", "top"], + ["bottom", "top"], + ["bottom", "left"], + ["bottom", "right"], + ["top", "left"], + ["top", "right"], + ["right", "bottom"], + ["right", "top"], + ] as [TooltipPositions, TooltipProps["position"]][]).forEach( + ([flipPosition, tooltipPosition]) => { + test(`check flip position to the ${flipPosition} when tooltip position is set as ${tooltipPosition}`, async ({ + mount, + page, + }) => { + await page.setViewportSize({ width: 700, height: 120 }); + await mount( + + + + ); + + await expect(tooltipPreview(page)).toHaveAttribute( + "data-placement", + flipPosition + ); + }); + } + ); + + ([ + [SIZE.SMALL, 15], + [SIZE.MEDIUM, 14], + [SIZE.LARGE, 10], + ] as [TooltipProps["inputSize"], number][]).forEach(([inputSize, offset]) => { + test(`should have correct styles applied when inputSize is ${inputSize} and tooltip position is set as top`, async ({ + mount, + page, + }) => { + await mount( + + ); + + const element = tooltipPreview(page); + await assertCssValueIsApproximately(element, "top", offset); + }); + }); + + ([ + [SIZE.SMALL, 5], + [SIZE.MEDIUM, 6], + [SIZE.LARGE, 10], + ] as [TooltipProps["inputSize"], number][]).forEach(([inputSize, offset]) => { + test(`should have correct styles applied when inputSize is ${inputSize} and tooltip position is set as bottom`, async ({ + mount, + page, + }) => { + await mount( + + ); + + const buttonRect = await page + .getByRole("button") + .evaluate((element) => element.getBoundingClientRect()); + const element = tooltipPreview(page); + + await assertCssValueIsApproximately( + element, + "top", + buttonRect.top + buttonRect.height + offset + ); + }); + }); + + ([ + [SIZE.SMALL, 47], + [SIZE.MEDIUM, 44], + [SIZE.LARGE, 40], + ] as [TooltipProps["inputSize"], number][]).forEach(([inputSize, offset]) => { + test(`should have correct styles applied when inputSize is ${inputSize} and tooltip position is set as left`, async ({ + mount, + page, + }) => { + await mount( + + ); + + const element = tooltipPreview(page); + + await assertCssValueIsApproximately(element, "left", offset); + }); + }); + + ([ + [SIZE.SMALL, 5], + [SIZE.MEDIUM, 8], + [SIZE.LARGE, 12], + ] as [TooltipProps["inputSize"], number][]).forEach(([inputSize, offset]) => { + test(`should have correct styles applied when inputSize is ${inputSize} and tooltip position is set as right`, async ({ + mount, + page, + }) => { + await mount( + + ); + + const buttonRect = await page + .getByRole("button") + .evaluate((element) => element.getBoundingClientRect()); + const element = tooltipPreview(page); + + await assertCssValueIsApproximately( + element, + "left", + buttonRect.left + buttonRect.width + offset + ); + }); + }); + + test(`should show tooltip when target is hovered`, async ({ + mount, + page, + }) => { + await mount(); + + await expect(tooltipPreview(page)).not.toBeVisible(); + const tooltipElement = page.getByRole("button"); + await tooltipElement.hover(); + await expect(tooltipPreview(page)).toBeVisible(); + }); + + test(`should have the expected border radius styling`, async ({ + mount, + page, + }) => { + await mount(); + + await expect(tooltipPreview(page)).toHaveCSS("border-radius", "4px"); + }); +}); + +test.describe("Accessibility tests for Tooltip component", () => { + test(`check accessibility tests for Default story`, async ({ + mount, + page, + }) => { + await mount(); + + await checkAccessibility(page); + }); + + test(`check accessibility tests for Controlled story`, async ({ + mount, + page, + }) => { + await mount(); + + expect(getDataElementByValue(page, "main-text").nth(0).click()); + await checkAccessibility(page); + }); + + test(`check accessibility tests for FlipBehaviourOverrides story`, async ({ + mount, + page, + }) => { + await mount(); + + await checkAccessibility(page); + }); + + ([ + ["top", 0], + ["bottom", 1], + ["left", 2], + ["right", 3], + ] as [string, number][]).forEach(([position, button]) => { + test(`check accessibility tests for Positioning story when position is set as ${position}`, async ({ + mount, + page, + }) => { + await mount(); + + expect(getDataElementByValue(page, "main-text").nth(button).click()); + await checkAccessibility(page); + }); + }); + + test(`check accessibility tests for LargeTooltip story`, async ({ + mount, + page, + }) => { + await mount(); + + expect(getDataElementByValue(page, "main-text").click()); + await checkAccessibility(page); + }); + + ([ + ["undefined", 0], + ["error", 1], + ] as [string, number][]).forEach(([type, button]) => { + test(`check accessibility tests when type is set as ${type}`, async ({ + mount, + page, + }) => { + await mount(); + + expect(getDataElementByValue(page, "main-text").nth(button).click()); + await checkAccessibility(page); + }); + }); + + test(`check accessibility tests for ColorOverrides story`, async ({ + mount, + page, + }) => { + await mount(); + + expect(getDataElementByValue(page, "main-text").click()); + await checkAccessibility(page); + }); +});