From 8b7ace53b99c7abc12df7b0b0200f35930193dcd Mon Sep 17 00:00:00 2001 From: mikieyx <103902194+mikieyx@users.noreply.github.com> Date: Fri, 28 Jun 2024 09:16:18 -0700 Subject: [PATCH] #2014 Use React Testing Library for common-properties tests - pt1 Actions (#2015) --- .../__tests__/_utils_/property-utils.js | 4 +- .../__tests__/_utils_/property-utilsRTL.js | 120 ++++++++++++++++++ .../actions/button-action-test.js | 106 ++++++++++------ .../actions/image-action-test.js | 90 ++++++++----- 4 files changed, 243 insertions(+), 77 deletions(-) create mode 100644 canvas_modules/common-canvas/__tests__/_utils_/property-utilsRTL.js diff --git a/canvas_modules/common-canvas/__tests__/_utils_/property-utils.js b/canvas_modules/common-canvas/__tests__/_utils_/property-utils.js index ddb46ac18d..ead9175bbe 100644 --- a/canvas_modules/common-canvas/__tests__/_utils_/property-utils.js +++ b/canvas_modules/common-canvas/__tests__/_utils_/property-utils.js @@ -20,12 +20,11 @@ import * as UiConditionsParser from "../../src/common-properties/ui-conditions/u import { mountWithIntl, mountWithIntlMessages } from "./intl-utils"; import { expect } from "chai"; import cloneDeep from "lodash/cloneDeep"; - import CustomTableControl from "./custom-controls/CustomTableControl"; import CustomToggleControl from "./custom-controls/CustomToggleControl"; import CustomOpMax from "./custom-condition-ops/customMax"; - import sinon from "sinon"; + var renderedController; function controllerHandler(propertyController) { renderedController = propertyController; @@ -60,7 +59,6 @@ function flyoutEditorForm(paramDef, propertiesConfigOverrides, callbacksOverride if (propertiesConfigOverrides) { propertiesConfig = Object.assign(propertiesConfig, propertiesConfigOverrides); } - const wrapper = mountWithIntl(
+ +
+ ); + return { wrapper: wrapper, controller: renderedController, callbacks: callbacks }; + +} + +function setControls(controller, controls) { + const parsedControls = []; + for (const control of controls) { + UiConditionsParser.parseControl(parsedControls, control); + } + controller.saveControls(parsedControls); +} + +function genLongString(length) { + let str = ""; + while (length > str.length) { + str += Math.random().toString(36) + .substr(2, 1); + } + return str; +} + +function openSummaryPanel(wrapper, panelId) { + const { container } = wrapper; + const summaryPanel = container.querySelector(`div[data-id='properties-${panelId}']`); + expect(summaryPanel).to.exist; + fireEvent.click(summaryPanel.querySelector("button.properties-summary-link-button")); + return container.querySelector("div.properties-wf-content.show"); +} + +function getParameterFromParamDef(parameterId, paramDef) { + const parameters = paramDef.parameters; + let parameterFound = null; + parameters.forEach((parameter) => { + if (parameter.id === parameterId) { + parameterFound = parameter; + } + }); + return parameterFound; +} + +module.exports = { + flyoutEditorForm: flyoutEditorForm, + setControls: setControls, + genLongString: genLongString, + openSummaryPanel: openSummaryPanel, + getParameterFromParamDef: getParameterFromParamDef +}; diff --git a/canvas_modules/common-canvas/__tests__/common-properties/actions/button-action-test.js b/canvas_modules/common-canvas/__tests__/common-properties/actions/button-action-test.js index 98478045ab..40ccb760b1 100644 --- a/canvas_modules/common-canvas/__tests__/common-properties/actions/button-action-test.js +++ b/canvas_modules/common-canvas/__tests__/common-properties/actions/button-action-test.js @@ -17,13 +17,15 @@ import React from "react"; import { Provider } from "react-redux"; import ActionButton from "./../../../src/common-properties/actions/button"; -import { mount } from "../../_utils_/mount-utils.js"; +import { render } from "../../_utils_/mount-utils.js"; import { expect } from "chai"; +import { expect as expectJest } from "@jest/globals"; import sinon from "sinon"; import Controller from "./../../../src/common-properties/properties-controller"; - import ACTION_PARAMDEF from "../../test_resources/paramDefs/action_paramDef.json"; -import propertyUtils from "../../_utils_/property-utils"; +import propertyUtilsRTL from "../../_utils_/property-utilsRTL"; +import { fireEvent, within } from "@testing-library/react"; + const actionHandler = sinon.spy(); const controller = new Controller(); @@ -51,10 +53,22 @@ const action = { "class_name": "custom-class-for-action-button" }; +const mockActionButton = jest.fn(); +jest.mock("./../../../src/common-properties/actions/button", + () => (props) => mockActionButton(props) +); + +mockActionButton.mockImplementation((props) => { + const ActionButtonComp = jest.requireActual( + "./../../../src/common-properties/actions/button", + ).default; + return ; +}); + describe("action-button renders correctly", () => { it("props should have been defined", () => { - const wrapper = mount( + const wrapper = render( { /> ); - - const button = wrapper.find("ButtonAction"); + const button = wrapper.queryAllByRole("button"); expect(button).to.have.length(1); - expect(button.prop("action")).to.equal(action); - expect(button.prop("controller")).to.equal(controller); + + expectJest(mockActionButton).toHaveBeenCalledWith({ + "action": action, + "controller": controller + }); }); it("should render a `ActionButton`", () => { - const wrapper = mount( + const wrapper = render( { /> ); - const button = wrapper.find("button"); + const button = wrapper.getAllByRole("button"); expect(button).to.have.length(1); }); it("should fire action when button clicked", (done) => { @@ -89,7 +105,7 @@ describe("action-button renders correctly", () => { done(); } controller.setHandlers({ actionHandler: callback }); - const wrapper = mount( + const wrapper = render( { /> ); - const button = wrapper.find("button"); - button.simulate("click"); + const button = wrapper.getByRole("button"); + fireEvent.click(button); }); it("action button renders when disabled", () => { controller.updateActionState(actionStateId, "disabled"); - const wrapper = mount( + const wrapper = render( { /> ); - const buttonWrapper = wrapper.find("div[data-id='increment']"); - expect(buttonWrapper.find("button").prop("disabled")).to.equal(true); + const { container } = wrapper; + const buttonWrapper = container.querySelector("div[data-id='increment']"); + const button = within(buttonWrapper).getByRole("button"); + expect(button.disabled).to.equal(true); }); it("action button renders when hidden", () => { controller.updateActionState(actionStateId, "hidden"); - const wrapper = mount( + const wrapper = render( { /> ); - const buttonWrapper = wrapper.find("div[data-id='increment']"); - expect(buttonWrapper.hasClass("hide")).to.equal(true); + const { container } = wrapper; + const buttonWrapper = container.querySelector("div[data-id='increment']"); + expect(buttonWrapper.className.includes("hide")).to.equal(true); }); it("action button renders tooltip", () => { - const wrapper = mount( + const wrapper = render( { /> ); - const tooltip = wrapper.find("div.tooltipContainer"); - expect(tooltip).to.have.length(1); - expect(tooltip.text()).to.equal("Increment number by 1."); + const tooltips = wrapper.getAllByText("Increment number by 1."); + const parentTooltip = tooltips[0].parentElement; + expect(tooltips.length).equal(1); + expect(parentTooltip.className).equal("tooltipContainer"); }); it("action button kind and size", () => { - const wrapper = mount( + const wrapper = render( { /> ); - const button = wrapper.find("button"); + const { container } = wrapper; + const contButton = container.getElementsByClassName("cds--btn")[0]; + const button = wrapper.getAllByRole("button"); expect(button).to.have.length(1); // verify button kind is secondary - expect(button.prop("className").includes("cds--btn--secondary")).to.equal(true); + expect(contButton.className.includes("cds--btn--secondary")).to.equal(true); // verify button size is extra large - expect(button.prop("className").includes("cds--btn--xl")).to.equal(true); + expect(contButton.className.includes("cds--btn--xl")).to.equal(true); }); it("action button default kind is tertiary and size is small", () => { const actionWithoutButtonObject = { @@ -170,7 +192,7 @@ describe("action-button renders correctly", () => { "parameter_ref": "number" } }; - const wrapper = mount( + const wrapper = render( { /> ); - const button = wrapper.find("button"); + const { container } = wrapper; + const contButton = container.getElementsByClassName("cds--btn")[0]; + const button = wrapper.getAllByRole("button"); expect(button).to.have.length(1); // verify default button kind is tertiary - expect(button.prop("className").includes("cds--btn--tertiary")).to.equal(true); + expect(contButton.className.includes("cds--btn--tertiary")).to.equal(true); // verify default button size is small - expect(button.prop("className").includes("cds--btn--sm")).to.equal(true); + expect(contButton.className.includes("cds--btn--sm")).to.equal(true); }); }); @@ -191,12 +215,12 @@ describe("actions using paramDef", () => { let wrapper; let renderedObject; beforeEach(() => { - renderedObject = propertyUtils.flyoutEditorForm(ACTION_PARAMDEF); + renderedObject = propertyUtilsRTL.flyoutEditorForm(ACTION_PARAMDEF); wrapper = renderedObject.wrapper; }); it("should fire action when button clicked", (done) => { - renderedObject = propertyUtils.flyoutEditorForm(ACTION_PARAMDEF, null, { actionHandler: callback }, { appData: appData }); + renderedObject = propertyUtilsRTL.flyoutEditorForm(ACTION_PARAMDEF, null, { actionHandler: callback }, { appData: appData }); wrapper = renderedObject.wrapper; function callback(id, inAppData, data) { expect(id).to.equal("increment"); @@ -204,17 +228,21 @@ describe("actions using paramDef", () => { expect(data.parameter_ref).to.equal("number"); done(); } - const button = wrapper.find("div[data-id='increment'] button"); - button.simulate("click"); + const { container } = wrapper; + const div = container.querySelector("div[data-id='increment']"); + const button = within(div).getByRole("button"); + fireEvent.click(button); }); it("action button should have custom classname defined", () => { + const { container } = wrapper; // class_name defined in uiHints action_info - const incrementButton = wrapper.find("div[data-id='increment']"); - expect(incrementButton.prop("className")).to.equal("properties-action-button custom-class-for-action-button"); + // const incrementButton = wrapper.find("div[data-id='increment']"); + const incrementButton = container.querySelector("div[data-id='increment']"); + expect(incrementButton.className).to.equal("properties-action-button custom-class-for-action-button"); // class_name not defined in uiHints action_info - const decrementButton = wrapper.find("div[data-id='decrement']"); - expect(decrementButton.prop("className")).to.equal("properties-action-button"); + const decrementButton = container.querySelector("div[data-id='decrement']"); + expect(decrementButton.className).to.equal("properties-action-button"); }); }); diff --git a/canvas_modules/common-canvas/__tests__/common-properties/actions/image-action-test.js b/canvas_modules/common-canvas/__tests__/common-properties/actions/image-action-test.js index 0a41eeafd6..e60f048b95 100644 --- a/canvas_modules/common-canvas/__tests__/common-properties/actions/image-action-test.js +++ b/canvas_modules/common-canvas/__tests__/common-properties/actions/image-action-test.js @@ -17,13 +17,14 @@ import React from "react"; import { Provider } from "react-redux"; import ActionImage from ".../../../src/common-properties/actions/image"; -import { mount } from "../../_utils_/mount-utils.js"; +import { render } from "../../_utils_/mount-utils.js"; import { expect } from "chai"; import sinon from "sinon"; import Controller from "../../../src/common-properties/properties-controller"; - +import { expect as expectJest } from "@jest/globals"; import ACTION_PARAMDEF from "../../test_resources/paramDefs/action_paramDef.json"; -import propertyUtils from "../../_utils_/property-utils"; +import propertyUtilsRTL from "../../_utils_/property-utilsRTL"; +import { fireEvent, within } from "@testing-library/react"; const actionHandler = sinon.spy(); const controller = new Controller(); @@ -56,10 +57,22 @@ const action = { "class_name": "custom-class-for-action-image" }; +const mockActionImage = jest.fn(); +jest.mock(".../../../src/common-properties/actions/image", + () => (props) => mockActionImage(props) +); + +mockActionImage.mockImplementation((props) => { + const ActionImageComp = jest.requireActual( + ".../../../src/common-properties/actions/image", + ).default; + return ; +}); + describe("action-image renders correctly", () => { it("props should have been defined", () => { - const wrapper = mount( + render( { /> ); - - const image = wrapper.find("ImageAction"); - expect(image).to.have.length(1); - expect(image.prop("action")).to.equal(action); - expect(image.prop("controller")).to.equal(controller); + expectJest(mockActionImage).toHaveBeenCalledWith({ + "action": action, + "controller": controller + }); }); it("should render a `ActionImage`", () => { - const wrapper = mount( + const wrapper = render( { /> ); - const image = wrapper.find("img"); - expect(image).to.have.length(1); - expect(image.prop("height")).to.equal(20); - expect(image.prop("width")).to.equal(25); - expect(wrapper.find(".right")).to.have.length(1); + const { container } = wrapper; + const image = container.querySelectorAll("img"); + expect(image[0].height).to.equal(20); + expect(image[0].width).to.equal(25); + expect(container.querySelectorAll(".right")).to.have.length(1); }); it("should fire action when image clicked", (done) => { function callback(id, inAppData, data) { @@ -96,7 +108,7 @@ describe("action-image renders correctly", () => { done(); } controller.setHandlers({ actionHandler: callback }); - const wrapper = mount( + const wrapper = render( { /> ); - const image = wrapper.find("img"); - image.simulate("click"); + const image = wrapper.getByRole("img"); + fireEvent.click(image); }); it("action image renders when disabled", () => { controller.updateActionState(actionStateId, "disabled"); - const wrapper = mount( + const wrapper = render( { /> ); - const imageWrapper = wrapper.find(".properties-action-image"); - expect(imageWrapper.hasClass("disabled")).to.equal(true); + const { container } = wrapper; + const imageWrapper = container.querySelector(".properties-action-image"); + expect(imageWrapper.className.includes("disabled")).to.equal(true); }); it("action image renders when hidden", () => { controller.updateActionState(actionStateId, "hidden"); - const wrapper = mount( + const wrapper = render( { /> ); - const imageWrapper = wrapper.find(".properties-action-image"); - expect(imageWrapper.hasClass("hide")).to.equal(true); + const { container } = wrapper; + const imageWrapper = container.querySelector(".properties-action-image"); + expect(imageWrapper.className.includes("hide")).to.equal(true); }); it("action image renders tooltip", () => { - const wrapper = mount( + const wrapper = render( { /> ); - const tooltip = wrapper.find("div.tooltipContainer"); + const tooltip = wrapper.getAllByText("Click to rotate through moon phases."); + const tooltipWrapper = tooltip[0].parentElement; expect(tooltip).to.have.length(1); - expect(tooltip.text()).to.equal("Click to rotate through moon phases."); + expect(tooltipWrapper.className).to.equal("tooltipContainer"); + expect(tooltip[0].textContent).to.equal("Click to rotate through moon phases."); }); }); @@ -152,12 +168,12 @@ describe("actions using paramDef", () => { let wrapper; let renderedObject; beforeEach(() => { - renderedObject = propertyUtils.flyoutEditorForm(ACTION_PARAMDEF); + renderedObject = propertyUtilsRTL.flyoutEditorForm(ACTION_PARAMDEF); wrapper = renderedObject.wrapper; }); it("should fire action when image clicked", (done) => { - renderedObject = propertyUtils.flyoutEditorForm(ACTION_PARAMDEF, null, { actionHandler: callback }, { appData: appData }); + renderedObject = propertyUtilsRTL.flyoutEditorForm(ACTION_PARAMDEF, null, { actionHandler: callback }, { appData: appData }); wrapper = renderedObject.wrapper; function callback(id, inAppData, data) { expect(id).to.equal("moon"); @@ -165,17 +181,21 @@ describe("actions using paramDef", () => { expect(data.parameter_ref).to.equal("moon_phase"); done(); } - const image = wrapper.find("div[data-id='properties-ctrl-moon_phase']").find("div[data-id='moon'] img"); - image.simulate("click"); + const { container } = wrapper; + const imageWrapper = container.querySelector("div[data-id='properties-ctrl-moon_phase']"); + const image = within(imageWrapper).getByRole("img"); + fireEvent.click(image); }); it("action image should have custom classname defined", () => { // class_name defined in uiHints action_info - const fallImage = wrapper.find(".properties-action-image").at(3); - expect(fallImage.prop("className")).to.equal("properties-action-image right custom-class-for-action-image"); + const { container } = wrapper; + const images = container.querySelectorAll(".properties-action-image"); + const fallImage = images[3]; + expect(fallImage.className).to.equal("properties-action-image right custom-class-for-action-image"); // class_name not defined in uiHints action_info - const winterImage = wrapper.find(".properties-action-image").at(0); - expect(winterImage.prop("className")).to.equal("properties-action-image"); + const winterImage = images[0]; + expect(winterImage.className).to.equal("properties-action-image"); }); });