diff --git a/cypress/components/decimal/decimal.cy.tsx b/cypress/components/decimal/decimal.cy.tsx deleted file mode 100644 index 5351ae61d7..0000000000 --- a/cypress/components/decimal/decimal.cy.tsx +++ /dev/null @@ -1,570 +0,0 @@ -/* eslint-disable array-callback-return, no-unused-expressions, jest/valid-expect-in-promise, jest/valid-expect */ -import React from "react"; -import Decimal, { - CustomEvent, - DecimalProps, -} from "../../../src/components/decimal"; -import * as stories from "../../../src/components/decimal/decimal.stories"; -import CypressMountWithProviders from "../../support/component-helper/cypress-mount"; - -import { - fieldHelpPreview, - getDataElementByValue, - tooltipPreview, - commonDataElementInputPreview, - cyRoot, - getElement, -} from "../../locators/index"; - -import { textboxPrefix } from "../../locators/textbox"; -import { CHARACTERS } from "../../support/component-helper/constants"; - -const eventOutput = (formattedVal: string, rawVal: string) => { - return { - target: { - id: undefined, - name: undefined, - value: { - formattedValue: formattedVal, - rawValue: rawVal, - }, - }, - }; -}; - -context("Tests for Decimal component", () => { - describe("check props for Decimal component", () => { - const input = [ - [0, "1", "1"], - [0, "134^", "134^"], - [0, "1234567789", "1,234,567,789"], - [0, ",,,,,", ",,,,,"], - [0, ".....", "....."], - [0, "abc,.123,.!@.00", "abc,.123,.!@.00"], - [0, "1,234$", "1,234$"], - - [1, "1", "1.0"], - [1, "1.2", "1.2"], - [1, "1.23", "1.23"], - - [2, "2", "2.00"], - [2, "2.1", "2.10"], - [2, "2.12", "2.12"], - [2, "2.123", "2.123"], - - [3, "2.1", "2.100"], - [3, "2.12", "2.120"], - [3, "2.123", "2.123"], - [3, "2.1234", "2.1234"], - - [4, "1.1234", "1.1234"], - [4, "2", "2.0000"], - [4, "234556654", "234,556,654.0000"], - [4, "%^%^%<<,,,", "%^%^%<<,,,"], - - [5, "1", "1.00000"], - [5, "1.12345", "1.12345"], - [5, "1a.23", "1a.23"], - - [6, "2.123456", "2.123456"], - [6, "2.1", "2.100000"], - [6, "2a.12", "2a.12"], - [6, "1,232.123", "1,232.123000"], - - [7, "1", "1.0000000"], - [7, "2344.1234567", "2,344.1234567"], - [7, "88652344.1234567", "88,652,344.1234567"], - - [8, "1", "1.00000000"], - [8, "1.2", "1.20000000"], - [8, "1.23", "1.23000000"], - - [9, "2", "2.000000000"], - [9, "2.1", "2.100000000"], - [9, "1222.12", "1,222.120000000"], - [9, "2.123000000", "2.123000000"], - - [10, "2.1", "2.1000000000"], - [10, "2.12", "2.1200000000"], - [10, "1222.123", "1,222.1230000000"], - [10, "2.12345", "2.1234500000"], - - [11, "1", "1.00000000000"], - [11, "2345", "2,345.00000000000"], - [11, "17899536472345", "17,899,536,472,345.00000000000"], - - [12, "1", "1.000000000000"], - [12, "1.12345", "1.123450000000"], - [12, "1a.23", "1a.23"], - - [13, "2", "2.0000000000000"], - [13, "2.1", "2.1000000000000"], - [13, "2a.12", "2a.12"], - [13, "1232.123", "1,232.1230000000000"], - - [14, "2.1", "2.10000000000000"], - [14, "2.12", "2.12000000000000"], - [14, "2.123", "2.12300000000000"], - [14, "1222.1234", "1,222.12340000000000"], - - [15, "1", "1.000000000000000"], - [15, "2332.78", "2,332.780000000000000"], - [15, "1a3.55", "1a3.55"], - [15, "1.12345", "1.123450000000000"], - [15, "1a.23", "1a.23"], - ] as [DecimalProps["precision"], string, string][]; - - it.each(input)( - "should use %s as precision and %s as input value to produce %s output value", - (precision, inputValue, outputValue) => { - CypressMountWithProviders(); - - commonDataElementInputPreview() - .type(inputValue, { delay: 0 }) - .blur({ force: true }); - commonDataElementInputPreview().should("have.value", outputValue); - } - ); - - const inputLocale = [ - ["en", "1,1,1,1,1.1", "11,111.100"], - ["en", "1,1,1222,12,1.1", "111,222,121.100"], - ["en", "1,,1,,1", "1,,1,,1"], - - ["es-ES", "1.1.1.1.1.1,1", "111.111,100"], - ["es-ES", "2.123", "2123,000"], - ["es-ES", "21.21.111.1,013", "21.211.111,013"], - ["es-ES", "2.,12.,1", "2.,12.,1"], - - ["fr", "11111,25", "11 111,250"], - ["fr", "1 1 1 1 1,25", "1 1 1 1 1,25"], - - ["pt-PT", "1111,2", "1111,200"], - ["pt-PT", "111 11,25", "11 111,250"], - - ["no-NO", "1 1 11,21", "1 111,210"], - ["no-No", "111 1 1,25", "11 111,250"], - ["no-NO", "1 1 1 1 1,25", "1 1 1 1 1,25"], - ] as [DecimalProps["locale"], string, string][]; - - it.each(inputLocale)( - "should use %s locale and %s input value to produce %s output value", - (locale, inputValue, outputValue) => { - CypressMountWithProviders(); - - commonDataElementInputPreview() - .type(inputValue, { delay: 0 }) - .blur({ force: true }); - commonDataElementInputPreview() - .invoke("val") - .then(($el) => { - for (let number = 0; number < String($el).length; number++) { - expect( - String($el) - .replace(/(\s)|( )|(\u00a0)/g, " ") - .charCodeAt(number) - ).to.equals(outputValue.charCodeAt(number)); - } - }); - } - ); - - it("should render Decimal with readOnly prop", () => { - CypressMountWithProviders(); - - const inputValue = "test"; - - commonDataElementInputPreview() - .type(inputValue, { force: true }) - .blur({ force: true }); - commonDataElementInputPreview() - .parent() - .should("not.have.value", inputValue) - .and("have.attr", "readOnly"); - }); - - it.each(["10%", "30%", "50%", "80%", "100%"])( - "should check maxWidth as %s for Decimal component", - (maxWidth) => { - CypressMountWithProviders(); - - getDataElementByValue("input") - .parent() - .parent() - .should("have.css", "max-width", maxWidth); - } - ); - - it("when maxWidth has no value it should render as 100%", () => { - CypressMountWithProviders(); - - getDataElementByValue("input") - .parent() - .parent() - .should("have.css", "max-width", "100%"); - }); - }); - - describe("check Decimal input", () => { - const testData = [CHARACTERS.DIACRITICS, CHARACTERS.SPECIALCHARACTERS]; - - it.each(testData)( - "check label renders properly with %s as specific value", - (specificValue) => { - CypressMountWithProviders(); - - fieldHelpPreview().should("have.text", specificValue); - } - ); - - it.each(testData)( - "check fieldHelp renders properly with %s specific value", - (specificValue) => { - CypressMountWithProviders(); - - getDataElementByValue("label").should("have.text", specificValue); - } - ); - - it.each(testData)( - "check tooltip renders properly with %s specific values", - (specificValue) => { - CypressMountWithProviders( - - ); - - getDataElementByValue("question").trigger("mouseover"); - tooltipPreview().should("have.text", specificValue); - cyRoot().realHover({ position: "topLeft" }); - } - ); - - it.each(testData)( - "should render Decimal with prefix prop set to %s", - (prefix) => { - CypressMountWithProviders(); - - textboxPrefix() - .should("have.text", prefix) - .and("have.css", "font-size", "14px") - .and("have.css", "font-weight", "900") - .and("have.css", "margin-left", "12px"); - } - ); - - it("when prefix prop is set, 'flex-direction' should be 'row'", () => { - CypressMountWithProviders(); - - getDataElementByValue("input") - .parent() - .should("have.css", "flex-direction", "row"); - }); - - it.each(testData)( - "check Decimal component accepts %s as specific value", - (specificValue) => { - CypressMountWithProviders(); - - commonDataElementInputPreview() - .type(specificValue) - .blur({ force: true }); - commonDataElementInputPreview() - .invoke("val") - .then(($el) => { - for (let number = 0; number < String($el).length; number++) { - expect( - String($el) - .replace(/(\s)|( )|(\u00a0)/g, " ") - .charCodeAt(number) - ).to.equals(specificValue.charCodeAt(number)); - } - }); - } - ); - - it("check Decimal component accepts white spaces", () => { - CypressMountWithProviders(); - - commonDataElementInputPreview().type(" ").blur({ force: true }); - commonDataElementInputPreview().should("have.attr", "value", " "); - }); - }); - - describe("allowEmptyValue", () => { - it.each([ - [0, "en", "0", "0"], - [1, "en", "0.0", "0.0"], - [2, "en", "0.00", "0.00"], - [0, "es-ES", "0", "0"], - [1, "es-ES", "0.0", "0,0"], - [2, "es-ES", "0.00", "0,00"], - [0, "fr", "0", "0"], - [1, "fr", "0.0", "0,0"], - [2, "fr", "0.00", "0,00"], - [0, "pt-PT", "0", "0"], - [1, "pt-PT", "0.0", "0,0"], - [2, "pt-PT", "0.00", "0,00"], - ] as [DecimalProps["precision"], string, string, string][])( - "should format an empty value correctly when precision is %s, locale is %s and allowEmptyValue is false", - (precisionValue, localeValue, rawValue, expectedValue) => { - const callback: DecimalProps["onBlur"] = cy.stub().as("onBlur"); - - CypressMountWithProviders( - - ); - - commonDataElementInputPreview().type("100"); - commonDataElementInputPreview() - .clear() - .blur({ force: true }) - .then(() => { - cy.get("@onBlur").should( - "have.been.calledWith", - eventOutput(expectedValue, rawValue) - ); - }); - expect( - commonDataElementInputPreview().should("have.value", expectedValue) - ); - } - ); - - it.each([ - [0, "en"], - [1, "en"], - [2, "en"], - [0, "es-ES"], - [1, "es-ES"], - [2, "es-ES"], - [0, "fr"], - [1, "fr"], - [2, "fr"], - [0, "pt-PT"], - [1, "pt-PT"], - [2, "pt-PT"], - ] as [DecimalProps["precision"], string][])( - "should format an empty value correctly when precision is %s, locale is %s and allowEmptyValue is true", - (precisionValue, localeValue) => { - const callback: DecimalProps["onBlur"] = cy.stub().as("onBlur"); - - CypressMountWithProviders( - - ); - - commonDataElementInputPreview().type("100"); - commonDataElementInputPreview() - .clear() - .blur({ force: true }) - .then(() => { - cy.get("@onBlur").should( - "have.been.calledWith", - eventOutput("", "") - ); - }); - expect(commonDataElementInputPreview().should("have.value", "")); - } - ); - }); - - describe("check events for Decimal component", () => { - const inputValue = "123"; - const iterable = [ - ["1", "1.00"], - ["12", "12.00"], - ["123", "123.00"], - ] as [string, string][]; - - it.each(iterable)( - "should call onChange callback when a type event is triggered with %s value", - (rawValueTest, formattedValueTest) => { - const callback: DecimalProps["onChange"] = cy.stub().as("onChange"); - - CypressMountWithProviders(); - - commonDataElementInputPreview() - .type(rawValueTest) - .blur({ force: true }) - - .then(() => { - cy.get("@onChange").should( - "have.been.calledWith", - eventOutput(formattedValueTest, rawValueTest) - ); - }); - } - ); - - it("can have a custom onChange handler", () => { - const CustomDecimalComponent = (props: DecimalProps) => { - const [state, setState] = React.useState("0.01"); - const handleChange = ({ target }: CustomEvent) => { - let newValue = target.value.rawValue; - if (newValue.startsWith("22.22")) newValue = "22.22"; - setState(newValue); - }; - - return ; - }; - - CypressMountWithProviders(); - - commonDataElementInputPreview().type("22.222"); - expect( - commonDataElementInputPreview().should("have.attr", "value", "22.22") - ); - }); - - it("should call onBlur callback when a blur event is triggered", () => { - const callback: DecimalProps["onBlur"] = cy.stub().as("onBlur"); - - CypressMountWithProviders(); - - commonDataElementInputPreview() - .type(inputValue) - .blur({ force: true }) - .then(() => { - cy.get("@onBlur").should( - "have.been.calledWith", - eventOutput("123.00", inputValue) - ); - expect(callback).to.have.been.calledOnce; - }); - }); - }); - - describe("Accessibility tests for Decimal component", () => { - it.each(["small", "medium", "large"] as DecimalProps["size"][])( - "should pass accessibility tests for Decimal with %s input size", - (size) => { - CypressMountWithProviders(); - - cy.checkAccessibility(); - } - ); - - it.each(["left", "right"] as DecimalProps["labelAlign"][])( - "should pass accessibility tests for Decimal with label aligned %s", - (labelAlign) => { - CypressMountWithProviders( - - ); - - cy.checkAccessibility(); - } - ); - - it("should pass accessibility tests for Decimal with custom precision", () => { - CypressMountWithProviders(); - - cy.checkAccessibility(); - }); - - it("should pass accessibility tests for Decimal with custom label width and input width", () => { - CypressMountWithProviders(); - - cy.checkAccessibility(); - }); - - it("should pass accessibility tests for Decimal with custom max width", () => { - CypressMountWithProviders(); - - cy.checkAccessibility(); - }); - - it("should pass accessibility tests for Decimal with field help", () => { - CypressMountWithProviders(); - - cy.checkAccessibility(); - }); - - it("should pass accessibility tests for Decimal with label help", () => { - CypressMountWithProviders(); - - getDataElementByValue("question") - .trigger("mouseover") - .then(() => { - cy.checkAccessibility(); - }); - }); - - it("should pass accessibility tests for Decimal required", () => { - CypressMountWithProviders(); - - cy.checkAccessibility(); - }); - - it("should pass accessibility tests for Decimal left aligned", () => { - CypressMountWithProviders(); - - cy.checkAccessibility(); - }); - - it("should pass accessibility tests for Decimal with validation", () => { - CypressMountWithProviders(); - - cy.checkAccessibility(); - }); - - it("should pass accessibility tests for Decimal validations redesigned", () => { - CypressMountWithProviders(); - - cy.checkAccessibility(); - }); - - it("should pass accessibility tests for Decimal default", () => { - CypressMountWithProviders( - - ); - - cy.checkAccessibility(); - }); - - it("should pass accessibility tests for Decimal with readOnly prop", () => { - CypressMountWithProviders( - - ); - - cy.checkAccessibility(); - }); - - it("should pass accessibility tests for Decimal with tooltip", () => { - CypressMountWithProviders(); - - cy.checkAccessibility(); - }); - - it("should pass accessibility tests for Decimal with tooltip label", () => { - CypressMountWithProviders(); - - cy.checkAccessibility(); - }); - }); - - it("should have the expected border radius styling", () => { - CypressMountWithProviders(); - getElement("input").should("have.css", "border-radius", "4px"); - }); -}); diff --git a/playwright/components/index.ts b/playwright/components/index.ts index 0cc250fe63..bd57a4605e 100644 --- a/playwright/components/index.ts +++ b/playwright/components/index.ts @@ -9,6 +9,7 @@ import { FIELD_HELP_PREVIEW, LABEL, STICKY_FOOTER, + COMMMON_DATA_ELEMENT_INPUT, } from "./locators"; export const icon = (page: Page) => { @@ -19,6 +20,10 @@ export const getDataElementByValue = (page: Page, element: string) => { return page.locator(`[data-element="${element}"]`); }; +export const commonDataElementInputPreview = (page: Page) => { + return page.locator(COMMMON_DATA_ELEMENT_INPUT); +}; + export const closeIconButton = (page: Page) => { return page.locator(CLOSE_ICON_BUTTON); }; @@ -51,6 +56,10 @@ export const getComponent = (page: Page, component: string) => { return page.locator(`[data-component="${component}"]`); }; +export const getElement = (page: Page, element: string) => { + return page.locator(`[data-element="${element}"]`).first(); +}; + export const label = (page: Page) => { return page.locator(LABEL); }; diff --git a/playwright/components/textbox/index.ts b/playwright/components/textbox/index.ts new file mode 100644 index 0000000000..1e22797c51 --- /dev/null +++ b/playwright/components/textbox/index.ts @@ -0,0 +1,20 @@ +import type { Page } from "@playwright/test"; + +import { TEXTBOX, TEXTBOX_DATA_COMPONENT, TEXTBOX_PREFIX } from "./locators"; + +// component preview locators +export const textbox = (page: Page) => { + return page.locator(TEXTBOX); +}; + +export const textboxDataComponent = (page: Page) => { + return page.locator(TEXTBOX_DATA_COMPONENT); +}; + +export const textboxPrefix = (page: Page) => { + return page.locator(TEXTBOX_PREFIX); +}; + +export const textboxInput = (page: Page) => { + return page.locator(TEXTBOX).locator("div").first(); +}; diff --git a/playwright/components/textbox/locators.ts b/playwright/components/textbox/locators.ts new file mode 100644 index 0000000000..cc8bf17768 --- /dev/null +++ b/playwright/components/textbox/locators.ts @@ -0,0 +1,4 @@ +// component preview locators +export const TEXTBOX = 'div[role="presentation"]'; +export const TEXTBOX_DATA_COMPONENT = '[data-component="textbox"]'; +export const TEXTBOX_PREFIX = '[data-element="textbox-prefix"]'; diff --git a/src/components/decimal/components.test-pw.tsx b/src/components/decimal/components.test-pw.tsx new file mode 100644 index 0000000000..8af9b55142 --- /dev/null +++ b/src/components/decimal/components.test-pw.tsx @@ -0,0 +1,217 @@ +import React, { useState } from "react"; + +import Decimal, { DecimalProps, CustomEvent } from "."; +import CarbonProvider from "../carbon-provider/carbon-provider.component"; + +export const DefaultStory = ( + args: Partial & { message?: string | boolean } +) => { + const [state, setState] = useState("0.01"); + const setValue = ({ target }: CustomEvent) => { + setState(target.value.rawValue); + }; + + return ( + + ); +}; + +export const Sizes = () => { + const [state, setState] = useState({ + small: "0.01", + medium: "0.01", + large: "0.01", + }); + + const handleChange = (size: DecimalProps["size"]) => (e: CustomEvent) => { + setState({ ...state, [size || "small"]: e.target.value.rawValue }); + }; + + return (["small", "medium", "large"] as const).map((size) => ( + + )); +}; + +export const Disabled = () => ; + +export const Prefix = () => ; + +export const LabelAlign = () => { + const [state, setState] = useState({ + right: "0.01", + left: "0.01", + }); + const handleChange = (alignment: DecimalProps["labelAlign"]) => ( + e: CustomEvent + ) => { + setState({ ...state, [alignment || "left"]: e.target.value.rawValue }); + }; + return (["right", "left"] as const).map((alignment) => ( + + )); +}; + +export const ReadOnly = () => ; + +export const Empty = () => ; + +export const WithCustomPrecision = () => { + const [state, setState] = useState("0.0001"); + const setValue = ({ target }: CustomEvent) => { + setState(target.value.rawValue); + }; + return ( + + ); +}; + +export const LabelInline = () => ; + +export const WithCustomLabelWidthAndInputWidth = ( + props: Partial +) => ; + +export const WithCustomMaxWidth = () => ; + +export const WithFieldHelp = (props: Partial) => ( + +); + +export const WithLabelHelp = (props: Partial) => ( + +); + +export const Required = () => ; + +export const LeftAligned = () => ; + +type Validation = "error" | "warning" | "info"; + +export const Validations = ( + args: Partial & { message?: string | boolean } +) => { + const [state, setState] = useState({ + error: "0.01", + warning: "0.01", + info: "0.01", + }); + + const handleChange = (validation: Validation) => (e: CustomEvent) => { + setState({ ...state, [validation]: e.target.value.rawValue }); + }; + + return ( + <> + {(["error", "warning", "info"] as const).map((validationType) => ( +
+ + +
+ ))} + + ); +}; + +export const ValidationsStringComponent = () => ( + +); + +export const ValidationsStringLabel = () => ( + +); + +export const ValidationsBoolean = () => ; + +export const ValidationsRedesign = () => { + const [state, setState] = useState({ + error: "0.01", + warning: "0.01", + }); + const handleChange = (validation: Validation) => (e: CustomEvent) => { + setState({ ...state, [validation]: e.target.value.rawValue }); + }; + return ( + + {(["error", "warning"] as const).map((validationType) => + (["small", "medium", "large"] as const).map((size) => ( +
+ + +
+ )) + )} +
+ ); +}; + +export const ValidationsTooltip = () => { + const [state, setState] = useState({ + error: "0.01", + warning: "0.01", + info: "0.01", + }); + const handleChange = (validation: Validation) => (e: CustomEvent) => { + setState({ ...state, [validation]: e.target.value.rawValue }); + }; + return ( + <> + {(["error", "warning", "info"] as const).map((validationType) => ( +
+ +
+ ))} + + ); +}; + +export const ValidationsTooltipLabel = () => ; diff --git a/src/components/decimal/decimal.pw.tsx b/src/components/decimal/decimal.pw.tsx new file mode 100644 index 0000000000..c365877861 --- /dev/null +++ b/src/components/decimal/decimal.pw.tsx @@ -0,0 +1,629 @@ +import React from "react"; +import { expect, test } from "@playwright/experimental-ct-react17"; +import { Locator } from "@playwright/test"; + +import { + commonDataElementInputPreview, + fieldHelpPreview, + getDataElementByValue, + getElement, + tooltipPreview, +} from "../../../playwright/components/index"; +import { textboxPrefix, textbox } from "../../../playwright/components/textbox"; + +import { CHARACTERS } from "../../../playwright/support/constants"; +import { + checkAccessibility, + getStyle, +} from "../../../playwright/support/helper"; + +import Decimal, { DecimalProps } from "../../../src/components/decimal"; +import { + DefaultStory, + WithCustomPrecision, + WithCustomLabelWidthAndInputWidth, + WithCustomMaxWidth, + WithFieldHelp, + WithLabelHelp, + Required, + LeftAligned, + Validations, + ValidationsRedesign, + ValidationsTooltip, + ValidationsTooltipLabel, +} from "./components.test-pw"; + +test.describe("check props for Decimal component", () => { + ([ + [0, "1", "1"], + [0, "134^", "134^"], + [0, "1234567789", "1,234,567,789"], + [0, ",,,,,", ",,,,,"], + [0, ".....", "....."], + [0, "abc,.123,.!@.00", "abc,.123,.!@.00"], + [0, "1,234$", "1,234$"], + [1, "1", "1.0"], + [1, "1.2", "1.2"], + [1, "1.23", "1.23"], + [2, "2", "2.00"], + [2, "2.1", "2.10"], + [2, "2.12", "2.12"], + [2, "2.123", "2.123"], + [3, "2.1", "2.100"], + [3, "2.12", "2.120"], + [3, "2.123", "2.123"], + [3, "2.1234", "2.1234"], + [4, "1.1234", "1.1234"], + [4, "2", "2.0000"], + [4, "234556654", "234,556,654.0000"], + [4, "%^%^%<<,,,", "%^%^%<<,,,"], + [5, "1", "1.00000"], + [5, "1.12345", "1.12345"], + [5, "1a.23", "1a.23"], + [6, "2.123456", "2.123456"], + [6, "2.1", "2.100000"], + [6, "2a.12", "2a.12"], + [6, "1,232.123", "1,232.123000"], + [7, "1", "1.0000000"], + [7, "2344.1234567", "2,344.1234567"], + [7, "88652344.1234567", "88,652,344.1234567"], + [8, "1", "1.00000000"], + [8, "1.2", "1.20000000"], + [8, "1.23", "1.23000000"], + [9, "2", "2.000000000"], + [9, "2.1", "2.100000000"], + [9, "1222.12", "1,222.120000000"], + [9, "2.123000000", "2.123000000"], + [10, "2.1", "2.1000000000"], + [10, "2.12", "2.1200000000"], + [10, "1222.123", "1,222.1230000000"], + [10, "2.12345", "2.1234500000"], + [11, "1", "1.00000000000"], + [11, "2345", "2,345.00000000000"], + [11, "17899536472345", "17,899,536,472,345.00000000000"], + [12, "1", "1.000000000000"], + [12, "1.12345", "1.123450000000"], + [12, "1a.23", "1a.23"], + [13, "2", "2.0000000000000"], + [13, "2.1", "2.1000000000000"], + [13, "2a.12", "2a.12"], + [13, "1232.123", "1,232.1230000000000"], + [14, "2.1", "2.10000000000000"], + [14, "2.12", "2.12000000000000"], + [14, "2.123", "2.12300000000000"], + [14, "1222.1234", "1,222.12340000000000"], + [15, "1", "1.000000000000000"], + [15, "2332.78", "2,332.780000000000000"], + [15, "1a3.55", "1a3.55"], + [15, "1.12345", "1.123450000000000"], + [15, "1a.23", "1a.23"], + ] as const).forEach(([precision, inputValue, outputValue]) => { + test(`should use ${precision} as precision and ${inputValue} as input value to produce ${outputValue} output value`, async ({ + mount, + page, + }) => { + await mount(); + + const commonDataElementInputPreviewElement = commonDataElementInputPreview( + page + ); + await commonDataElementInputPreviewElement.fill(inputValue); + await commonDataElementInputPreviewElement.blur(); + await expect(commonDataElementInputPreviewElement).toHaveAttribute( + "value", + outputValue + ); + }); + }); + + ([ + ["en", "1,1,1,1,1.1", "11,111.100"], + ["en", "1,1,1222,12,1.1", "111,222,121.100"], + ["en", "1,,1,,1", "1,,1,,1"], + ["es-ES", "1.1.1.1.1.1,1", "111.111,100"], + ["es-ES", "2.123", "2123,000"], + ["es-ES", "21.21.111.1,013", "21.211.111,013"], + ["es-ES", "2.,12.,1", "2.,12.,1"], + ["fr", "11111,25", "11 111,250"], + ["fr", "1 1 1 1 1,25", "1 1 1 1 1,25"], + ["pt-PT", "1111,2", "1111,200"], + ["pt-PT", "111 11,25", "11 111,250"], + ["no-NO", "1 1 11,21", "1 111,210"], + ["no-No", "111 1 1,25", "11 111,250"], + ["no-NO", "1 1 1 1 1,25", "1 1 1 1 1,25"], + ] as const).forEach(([locale, inputValue, outputValue]) => { + test(`should use ${locale} locale and ${inputValue} input value to produce ${outputValue} output value`, async ({ + mount, + page, + }) => { + await mount(); + + const commonDataElementInputPreviewElement = commonDataElementInputPreview( + page + ); + await commonDataElementInputPreviewElement.fill(inputValue); + await commonDataElementInputPreviewElement.blur(); + const value = await commonDataElementInputPreviewElement.evaluate( + (element) => + (element as HTMLInputElement).value.replace( + /(\s)|( )|(\u00a0)/g, + " " + ) + ); + expect(value).toBe(outputValue); + }); + }); + + test("should render Decimal with readOnly prop", async ({ mount, page }) => { + await mount(); + + const inputValue = "test"; + const commonDataElementInputPreviewElement = commonDataElementInputPreview( + page + ); + await commonDataElementInputPreviewElement.type(inputValue); + await commonDataElementInputPreviewElement.blur(); + await expect(commonDataElementInputPreviewElement).not.toHaveAttribute( + "value", + inputValue + ); + await expect(commonDataElementInputPreviewElement).not.toBeEditable(); + }); + + (["10%", "30%", "50%", "80%", "100%"] as const).forEach((maxWidth) => { + test(`should check maxWidth as ${maxWidth} for Decimal component`, async ({ + mount, + page, + }) => { + await mount(); + + const textboxParent = await textbox(page).evaluateHandle( + (element: Element) => { + return element.parentElement; + } + ); + const maxWidthValue = await getStyle( + (textboxParent as unknown) as Locator, + "max-width" + ); + expect(maxWidthValue).toContain(maxWidth); + }); + }); + + test("when maxWidth has no value it should render as 100%", async ({ + mount, + page, + }) => { + await mount(); + + const textboxParent = await textbox(page).evaluateHandle( + (element: Element) => { + return element.parentElement; + } + ); + const maxWidthValue = await getStyle( + (textboxParent as unknown) as Locator, + "max-width" + ); + expect(maxWidthValue).toContain("100%"); + }); +}); + +test.describe("check Decimal input", () => { + const testData = [CHARACTERS.DIACRITICS, CHARACTERS.SPECIALCHARACTERS]; + testData.forEach((specificValue) => { + test(`check label renders properly with ${specificValue} as specific value`, async ({ + mount, + page, + }) => { + await mount(); + + const fieldHelpPreviewElement = fieldHelpPreview(page); + await expect(fieldHelpPreviewElement).toHaveText(specificValue); + }); + }); + + testData.forEach((specificValue) => { + test(`check fieldHelp renders properly with ${specificValue} specific value`, async ({ + mount, + page, + }) => { + await mount(); + + const getDataElementByValueElementLabel = getDataElementByValue( + page, + "label" + ); + await expect(getDataElementByValueElementLabel).toHaveText(specificValue); + }); + }); + + testData.forEach((specificValue) => { + test(`check tooltip renders properly with ${specificValue} specific values`, async ({ + mount, + page, + }) => { + await mount(); + + const getDataElementByValueElementQuestion = getDataElementByValue( + page, + "question" + ); + await getDataElementByValueElementQuestion.hover(); + const tooltipPreviewElement = tooltipPreview(page); + await expect(tooltipPreviewElement).toHaveText(specificValue); + }); + }); + + testData.forEach((prefix) => { + test(`should render Decimal with prefix prop set to ${prefix}`, async ({ + mount, + page, + }) => { + await mount(); + + const textboxPrefixElement = textboxPrefix(page); + await expect(textboxPrefixElement).toHaveText(prefix); + await expect(textboxPrefixElement).toHaveCSS("font-size", "14px"); + await expect(textboxPrefixElement).toHaveCSS("font-weight", "900"); + await expect(textboxPrefixElement).toHaveCSS("margin-left", "12px"); + }); + }); + + test("when prefix prop is set, 'flex-direction' should be 'row'", async ({ + mount, + page, + }) => { + await mount(); + + const textboxInput = textbox(page); + await expect(textboxInput).toHaveCSS("flex-direction", "row"); + }); + + testData.forEach((specificValue) => { + test(`check Decimal component accepts ${specificValue} as specific value`, async ({ + mount, + page, + }) => { + await mount(); + + const commonDataElementInputPreviewElement = commonDataElementInputPreview( + page + ); + await commonDataElementInputPreviewElement.fill(specificValue); + await commonDataElementInputPreviewElement.blur(); + const value = await commonDataElementInputPreviewElement.evaluate( + (element) => + (element as HTMLInputElement).value.replace( + /(\s)|( )|(\u00a0)/g, + " " + ) + ); + expect(value).toBe(specificValue); + }); + }); + + test("check Decimal component accepts white spaces", async ({ + mount, + page, + }) => { + await mount(); + + const commonDataElementInputPreviewElement = commonDataElementInputPreview( + page + ); + await commonDataElementInputPreviewElement.fill(" "); + await commonDataElementInputPreviewElement.blur(); + await expect(commonDataElementInputPreviewElement).toHaveAttribute( + "value", + " " + ); + }); +}); + +test.describe("allowEmptyValue", () => { + ([ + [0, "en", "0"], + [1, "en", "0.0"], + [2, "en", "0.00"], + [0, "es-ES", "0"], + [1, "es-ES", "0,0"], + [2, "es-ES", "0,00"], + [0, "fr", "0"], + [1, "fr", "0,0"], + [2, "fr", "0,00"], + [0, "pt-PT", "0"], + [1, "pt-PT", "0,0"], + [2, "pt-PT", "0,00"], + ] as [DecimalProps["precision"], string, string][]).forEach( + ([precisionValue, localeValue, expectedValue]) => { + test(`should format an empty value correctly when precision is ${precisionValue}, locale is ${localeValue} and allowEmptyValue is false`, async ({ + mount, + page, + }) => { + await mount( + + ); + + const commonDataElementInputPreviewElement = commonDataElementInputPreview( + page + ); + await commonDataElementInputPreviewElement.fill("100"); + await commonDataElementInputPreviewElement.clear(); + await commonDataElementInputPreviewElement.blur(); + await expect(commonDataElementInputPreviewElement).toHaveAttribute( + "value", + expectedValue + ); + }); + } + ); + + ([ + [0, "en"], + [1, "en"], + [2, "en"], + [0, "es-ES"], + [1, "es-ES"], + [2, "es-ES"], + [0, "fr"], + [1, "fr"], + [2, "fr"], + [0, "pt-PT"], + [1, "pt-PT"], + [2, "pt-PT"], + ] as [DecimalProps["precision"], string][]).forEach( + ([precisionValue, localeValue]) => { + test(`should format an empty value correctly when precision is ${precisionValue}, locale is ${localeValue} and allowEmptyValue is true`, async ({ + mount, + page, + }) => { + await mount( + + ); + + const commonDataElementInputPreviewElement = commonDataElementInputPreview( + page + ); + await commonDataElementInputPreviewElement.fill("100"); + await commonDataElementInputPreviewElement.clear(); + await commonDataElementInputPreviewElement.blur(); + + await expect(commonDataElementInputPreviewElement).toHaveAttribute( + "value", + "" + ); + }); + } + ); +}); + +test.describe("check events for Decimal component", () => { + ([ + ["1", "1.00"], + ["12", "12.00"], + ["123", "123.00"], + ] as [string, string][]).forEach(([rawValueTest, formattedValueTest]) => { + test(`should call onChange callback when a type event is triggered with ${rawValueTest} value`, async ({ + mount, + page, + }) => { + let callbackCount = 0; + const callback: DecimalProps["onChange"] = () => { + callbackCount += 1; + }; + await mount(); + + const commonDataElementInputPreviewElement = commonDataElementInputPreview( + page + ); + await commonDataElementInputPreviewElement.fill(rawValueTest); + expect(callbackCount).toBe(1); + await commonDataElementInputPreviewElement.blur(); + await expect(commonDataElementInputPreviewElement).toHaveAttribute( + "value", + formattedValueTest + ); + }); + }); + + test("should call onBlur callback when a blur event is triggered", async ({ + mount, + page, + }) => { + let callbackCount = 0; + const callback: DecimalProps["onBlur"] = () => { + callbackCount += 1; + }; + await mount(); + + const inputValue = "123"; + const commonDataElementInputPreviewElement = commonDataElementInputPreview( + page + ); + await commonDataElementInputPreviewElement.fill(inputValue); + await commonDataElementInputPreviewElement.blur(); + expect(callbackCount).toBe(1); + }); +}); + +test.describe("Accessibility tests for Decimal component", () => { + (["small", "medium", "large"] as DecimalProps["size"][]).forEach((size) => { + test(`should pass accessibility tests for Decimal with ${size} input size`, async ({ + mount, + page, + }) => { + await mount(); + + await checkAccessibility(page); + }); + }); + + (["left", "right"] as DecimalProps["labelAlign"][]).forEach((labelAlign) => { + test(`should pass accessibility tests for Decimal with label aligned ${labelAlign}`, async ({ + mount, + page, + }) => { + await mount(); + + await checkAccessibility(page); + }); + }); + + test("should pass accessibility tests for Decimal with custom precision", async ({ + mount, + page, + }) => { + await mount(); + + await checkAccessibility(page); + }); + + test("should pass accessibility tests for Decimal with custom label width and input width", async ({ + mount, + page, + }) => { + await mount(); + + await checkAccessibility(page); + }); + + test("should pass accessibility tests for Decimal with custom max width", async ({ + mount, + page, + }) => { + await mount(); + + await checkAccessibility(page); + }); + + test("should pass accessibility tests for Decimal with field help", async ({ + mount, + page, + }) => { + await mount(); + + await checkAccessibility(page); + }); + + test("should pass accessibility tests for Decimal with label help", async ({ + mount, + page, + }) => { + await mount(); + + const getDataElementByValueElementQuestion = getDataElementByValue( + page, + "question" + ); + await getDataElementByValueElementQuestion.hover(); + await checkAccessibility(page); + }); + + test("should pass accessibility tests for Decimal required", async ({ + mount, + page, + }) => { + await mount(); + + await checkAccessibility(page); + }); + + test("should pass accessibility tests for Decimal left aligned", async ({ + mount, + page, + }) => { + await mount(); + + await checkAccessibility(page); + }); + + test("should pass accessibility tests for Decimal with validation", async ({ + mount, + page, + }) => { + await mount(); + + await checkAccessibility(page); + }); + + test("should pass accessibility tests for Decimal validations redesigned", async ({ + mount, + page, + }) => { + await mount(); + + await checkAccessibility(page); + }); + + test("should pass accessibility tests for Decimal default", async ({ + mount, + page, + }) => { + await mount( + + ); + + await checkAccessibility(page); + }); + + test("should pass accessibility tests for Decimal with readOnly prop", async ({ + mount, + page, + }) => { + await mount( + + ); + + await checkAccessibility(page); + }); + + test("should pass accessibility tests for Decimal with tooltip", async ({ + mount, + page, + }) => { + await mount(); + + await checkAccessibility(page); + }); + + test("should pass accessibility tests for Decimal with tooltip label", async ({ + mount, + page, + }) => { + await mount(); + + await checkAccessibility(page); + }); +}); + +test("should have the expected border radius styling", async ({ + mount, + page, +}) => { + await mount(); + + const getElementElementInput = getElement(page, "input"); + await expect(getElementElementInput).toHaveCSS("border-radius", "4px"); +});