From ff4683af1fdaeb12b56b3020648b2133310e9660 Mon Sep 17 00:00:00 2001 From: Tim Chamberlain Date: Tue, 8 Jul 2025 13:53:02 -0500 Subject: [PATCH] added tests around filter criteria paster tool --- .../components/query/FilterCriteriaPaster.tsx | 3 +- .../selenium/lib/QueryScreenLib.java | 24 ++ .../selenium/tests/query/QueryScreenTest.java | 215 ++++++++++++++++++ 3 files changed, 241 insertions(+), 1 deletion(-) diff --git a/src/qqq/components/query/FilterCriteriaPaster.tsx b/src/qqq/components/query/FilterCriteriaPaster.tsx index 79655f9..84fc6ad 100644 --- a/src/qqq/components/query/FilterCriteriaPaster.tsx +++ b/src/qqq/components/query/FilterCriteriaPaster.tsx @@ -363,7 +363,7 @@ function FilterCriteriaPaster({table, field, type, onSave}: Props): JSX.Element return ( - paste_content + paste_content { pasteModalIsOpen && @@ -391,6 +391,7 @@ function FilterCriteriaPaster({table, field, type, onSave}: Props): JSX.Element values) + { + ///////////////////////////////////////////////////////////////////////////// + // open the is any of criteria for given field and click the paster button // + ///////////////////////////////////////////////////////////////////////////// + qSeleniumLib.waitForSelectorContaining("BUTTON", fieldName).click(); + qSeleniumLib.waitForSelector("#criteriaOperator").click(); + qSeleniumLib.waitForSelectorContaining("LI", "is any of").click(); + qSeleniumLib.waitForMillis(250); + qSeleniumLib.waitForSelector(".criteriaPasterButton").click(); + + //////////////////////////////////////// + // paste the values into the textarea // + //////////////////////////////////////// + qSeleniumLib + .waitForSelector(".criteriaPasterTextArea textarea#outlined-multiline-static") + .sendKeys(String.join("\n", values)); + } + + + /******************************************************************************* ** *******************************************************************************/ diff --git a/src/test/java/com/kingsrook/qqq/frontend/materialdashboard/selenium/tests/query/QueryScreenTest.java b/src/test/java/com/kingsrook/qqq/frontend/materialdashboard/selenium/tests/query/QueryScreenTest.java index 0cb7ce4..569202b 100755 --- a/src/test/java/com/kingsrook/qqq/frontend/materialdashboard/selenium/tests/query/QueryScreenTest.java +++ b/src/test/java/com/kingsrook/qqq/frontend/materialdashboard/selenium/tests/query/QueryScreenTest.java @@ -22,13 +22,16 @@ package com.kingsrook.qqq.frontend.materialdashboard.selenium.tests.query; +import java.util.HashSet; import java.util.List; +import java.util.stream.Collectors; import com.kingsrook.qqq.frontend.materialdashboard.selenium.lib.QBaseSeleniumTest; import com.kingsrook.qqq.frontend.materialdashboard.selenium.lib.QQQMaterialDashboardSelectors; import com.kingsrook.qqq.frontend.materialdashboard.selenium.lib.QueryScreenLib; import com.kingsrook.qqq.frontend.materialdashboard.selenium.lib.javalin.CapturedContext; import com.kingsrook.qqq.frontend.materialdashboard.selenium.lib.javalin.QSeleniumJavalin; import org.junit.jupiter.api.Test; +import org.openqa.selenium.WebElement; import static org.assertj.core.api.Assertions.assertThat; @@ -200,6 +203,204 @@ public class QueryScreenTest extends QBaseSeleniumTest + /******************************************************************************* + ** + *******************************************************************************/ + @Test + void testCriteriaPasterHappyPath() + { + QueryScreenLib queryScreenLib = new QueryScreenLib(qSeleniumLib); + + //////////////////////////// + // go to the person page // + //////////////////////////// + qSeleniumLib.gotoAndWaitForBreadcrumbHeaderToContain("/peopleApp/greetingsApp/person", "Person"); + queryScreenLib.waitForQueryToHaveRan(); + + ////////////////////////////////////// + // open the paste values dialog UI // + ////////////////////////////////////// + queryScreenLib.openCriteriaPasterAndPasteValues("id", List.of("1", "2", "3")); + + /////////////////////////////////////////////////////////////// + // wait for chips to appear in the filter values review box // + /////////////////////////////////////////////////////////////// + assertFilterPasterChipCounts(3, 0); + + /////////////////////////////////////////////// + // confirm each chip has the blue color class // + /////////////////////////////////////////////// + qSeleniumLib.waitForSelectorAll(".MuiChip-root", 3).forEach(chip -> + { + String classAttr = chip.getAttribute("class"); + assertThat(classAttr).contains("MuiChip-colorInfo"); + }); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + @Test + void testCriteriaPasterInvalidValueValidation() + { + QueryScreenLib queryScreenLib = new QueryScreenLib(qSeleniumLib); + + //////////////////////////// + // go to the person page // + //////////////////////////// + qSeleniumLib.gotoAndWaitForBreadcrumbHeaderToContain("/peopleApp/greetingsApp/person", "Person"); + queryScreenLib.waitForQueryToHaveRan(); + + ////////////////////////////////////// + // open the paste values dialog UI // + ////////////////////////////////////// + queryScreenLib.openCriteriaPasterAndPasteValues("id", List.of("1", "a", "3")); + + ////////////////////////////////////////////////////// + // check that chips match values and are classified // + ////////////////////////////////////////////////////// + assertFilterPasterChipCounts(2, 1); + + //////////////////////////////////////////////////////////////////// + // confirm that an appropriate validation error message is shown // + //////////////////////////////////////////////////////////////////// + WebElement errorMessage = qSeleniumLib.waitForSelectorContaining("span", "value is not a number"); + assertThat(errorMessage.getText()).contains("value is not a number"); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + @Test + void testCriteriaPasterDuplicateValueValidation() + { + QueryScreenLib queryScreenLib = new QueryScreenLib(qSeleniumLib); + + //////////////////////////// + // go to the person page // + //////////////////////////// + qSeleniumLib.gotoAndWaitForBreadcrumbHeaderToContain("/peopleApp/greetingsApp/person", "Person"); + queryScreenLib.waitForQueryToHaveRan(); + + ////////////////////////////////////// + // open the paste values dialog UI // + ////////////////////////////////////// + List pastedValues = List.of("1", "1", "1", "2", "2"); + queryScreenLib.openCriteriaPasterAndPasteValues("id", pastedValues); + + /////////////////////////////////////////////// + // expected chip & uniqueness calculations // + /////////////////////////////////////////////// + int totalCount = pastedValues.size(); // 5 + int uniqueCount = new HashSet<>(pastedValues).size(); // 2 + + ///////////////////////////// + // chips should show dupes // + ///////////////////////////// + assertFilterPasterChipCounts(pastedValues.size(), 0); + + //////////////////////////////////////////////////////////////// + // counter text should match “5 values (2 unique)” (or alike) // + //////////////////////////////////////////////////////////////// + String expectedCounter = totalCount + " values (" + uniqueCount + " unique)"; + WebElement counterLabel = qSeleniumLib.waitForSelectorContaining("span", "unique"); + assertThat(counterLabel.getText()).contains(expectedCounter); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + @Test + void testCriteriaPasterWithPVSHappyPath() + { + QueryScreenLib queryScreenLib = new QueryScreenLib(qSeleniumLib); + + //////////////////////////// + // go to the person page // + //////////////////////////// + qSeleniumLib.gotoAndWaitForBreadcrumbHeaderToContain("/peopleApp/greetingsApp/person", "Person"); + queryScreenLib.waitForQueryToHaveRan(); + + ////////////////////////////////////// + // open the paste values dialog UI // + ////////////////////////////////////// + queryScreenLib.addBasicFilter("home city"); + queryScreenLib.openCriteriaPasterAndPasteValues("home city", List.of("St. Louis", "chesterfield")); + qSeleniumLib.waitForSeconds(1); + + /////////////////////////////////////////////////////////////// + // wait for chips to appear in the filter values review box // + /////////////////////////////////////////////////////////////// + assertFilterPasterChipCounts(2, 0); + + /////////////////////////////////////////////// + // confirm each chip has the blue color class // + /////////////////////////////////////////////// + qSeleniumLib.waitForSelectorAll(".MuiChip-root", 2).forEach(chip -> + { + String classAttr = chip.getAttribute("class"); + assertThat(classAttr).contains("MuiChip-colorInfo"); + }); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + @Test + void testCriteriaPasterWithPVSTwoGoodOneBadAndDupes() + { + QueryScreenLib queryScreenLib = new QueryScreenLib(qSeleniumLib); + + //////////////////////////// + // go to the person page // + //////////////////////////// + qSeleniumLib.gotoAndWaitForBreadcrumbHeaderToContain("/peopleApp/greetingsApp/person", "Person"); + queryScreenLib.waitForQueryToHaveRan(); + + ////////////////////////////////////// + // open the paste values dialog UI // + ////////////////////////////////////// + List cities = List.of("St. Louis", "chesterfield", "Maryville", "st. louis", "st. louis", "chesterfield"); + queryScreenLib.addBasicFilter("home city"); + queryScreenLib.openCriteriaPasterAndPasteValues("home city", cities); + qSeleniumLib.waitForSeconds(1); + + /////////////////////////////////////////////// + // expected chip & uniqueness calculations // + /////////////////////////////////////////////// + int totalCount = cities.size(); + int uniqueCount = cities.stream().map(String::toLowerCase).collect(Collectors.toSet()).size(); + + /////////////////////////////////////////// + // chips should show dupes and bad chips // + /////////////////////////////////////////// + assertFilterPasterChipCounts(5, 1); + + //////////////////////////////////////////////////////////////// + // counter text should match “5 values (2 unique)” (or alike) // + //////////////////////////////////////////////////////////////// + String expectedCounter = totalCount + " values (" + uniqueCount + " unique)"; + WebElement counterLabel = qSeleniumLib.waitForSelectorContaining("span", "unique"); + assertThat(counterLabel.getText()).contains(expectedCounter); + + ////////////////////////////////////////// + // assert the "value not found" warning // + ////////////////////////////////////////// + WebElement warning = qSeleniumLib.waitForSelectorContaining("span", "was not found"); + assertThat(warning.getText()).contains("1 value was not found and will not be added to the filter"); + + } + + + /******************************************************************************* ** *******************************************************************************/ @@ -273,4 +474,18 @@ public class QueryScreenTest extends QBaseSeleniumTest queryScreenLib.clickAdvancedFilterClearIcon(); } + + + /******************************************************************************* + ** + *******************************************************************************/ + private void assertFilterPasterChipCounts(int expectedValid, int expectedInvalid) + { + List chips = qSeleniumLib.waitForSelectorAll(".MuiChip-root", expectedValid + expectedInvalid); + long validCount = chips.stream().filter(c -> c.getAttribute("class").contains("MuiChip-colorInfo")).count(); + long errorCount = chips.stream().filter(c -> c.getAttribute("class").contains("MuiChip-colorError")).count(); + + assertThat(validCount).isEqualTo(expectedValid); + assertThat(errorCount).isEqualTo(expectedInvalid); + } }