From fc45b5bed8e8505e410998ec23667ec2b27e3756 Mon Sep 17 00:00:00 2001 From: Darin Kelkhoff Date: Tue, 13 Feb 2024 11:51:31 -0600 Subject: [PATCH] ES-84, ES-85 - fix boolean operators, trimming away values (don't trim if implicit values) w/ tests; fix to pre-filter-for-backend before launching processes; --- .../components/query/FilterCriteriaRow.tsx | 2 +- src/qqq/components/query/QuickFilter.tsx | 2 +- src/qqq/pages/records/query/RecordQuery.tsx | 14 +- .../selenium/lib/QSeleniumLib.java | 35 ++++- .../selenium/lib/QueryScreenLib.java | 97 +++++++++++- .../lib/javalin/QSeleniumJavalin.java | 3 +- ...ueryScreenFilterInUrlAdvancedModeTest.java | 12 +- .../selenium/tests/query/QueryScreenTest.java | 144 +++++++++++++++++- 8 files changed, 281 insertions(+), 28 deletions(-) diff --git a/src/qqq/components/query/FilterCriteriaRow.tsx b/src/qqq/components/query/FilterCriteriaRow.tsx index e170d00..c792b38 100644 --- a/src/qqq/components/query/FilterCriteriaRow.tsx +++ b/src/qqq/components/query/FilterCriteriaRow.tsx @@ -398,7 +398,7 @@ export function FilterCriteriaRow({id, index, tableMetaData, metaData, criteria, criteria.values = []; } - if(newValue.valueMode) + if(newValue.valueMode && !newValue.implicitValues) { const requiredValueCount = getValueModeRequiredCount(newValue.valueMode); if(requiredValueCount != null && criteria.values.length > requiredValueCount) diff --git a/src/qqq/components/query/QuickFilter.tsx b/src/qqq/components/query/QuickFilter.tsx index d73ded5..46c7b90 100644 --- a/src/qqq/components/query/QuickFilter.tsx +++ b/src/qqq/components/query/QuickFilter.tsx @@ -271,7 +271,7 @@ export default function QuickFilter({tableMetaData, fullFieldName, fieldMetaData criteria.values = []; } - if(newValue.valueMode) + if(newValue.valueMode && !newValue.implicitValues) { const requiredValueCount = getValueModeRequiredCount(newValue.valueMode); if(requiredValueCount != null && criteria.values.length > requiredValueCount) diff --git a/src/qqq/pages/records/query/RecordQuery.tsx b/src/qqq/pages/records/query/RecordQuery.tsx index 9d4f79a..a1819fe 100644 --- a/src/qqq/pages/records/query/RecordQuery.tsx +++ b/src/qqq/pages/records/query/RecordQuery.tsx @@ -1380,12 +1380,15 @@ function RecordQuery({table, launchProcess}: Props): JSX.Element { if (selectFullFilterState === "filter") { - return `?recordsParam=filterJSON&filterJSON=${encodeURIComponent(JSON.stringify(queryFilter))}`; + return `?recordsParam=filterJSON&filterJSON=${encodeURIComponent(JSON.stringify(prepQueryFilterForBackend(queryFilter)))}`; } if (selectFullFilterState === "filterSubset") { - return `?recordsParam=filterJSON&filterJSON=${encodeURIComponent(JSON.stringify(queryFilter))}`; + const filterForBackend = prepQueryFilterForBackend(queryFilter); + filterForBackend.skip = 0; + filterForBackend.limit = selectionSubsetSize; + return `?recordsParam=filterJSON&filterJSON=${encodeURIComponent(JSON.stringify(filterForBackend))}`; } if (selectedIds.length > 0) @@ -1405,11 +1408,14 @@ function RecordQuery({table, launchProcess}: Props): JSX.Element { if (selectFullFilterState === "filter") { - setRecordIdsForProcess(queryFilter); + setRecordIdsForProcess(prepQueryFilterForBackend(queryFilter)); } else if (selectFullFilterState === "filterSubset") { - setRecordIdsForProcess(new QQueryFilter(queryFilter.criteria, queryFilter.orderBys, queryFilter.booleanOperator, 0, selectionSubsetSize)); + const filterForBackend = prepQueryFilterForBackend(queryFilter); + filterForBackend.skip = 0; + filterForBackend.limit = selectionSubsetSize; + setRecordIdsForProcess(filterForBackend); } else if (selectedIds.length > 0) { diff --git a/src/test/java/com/kingsrook/qqq/frontend/materialdashboard/selenium/lib/QSeleniumLib.java b/src/test/java/com/kingsrook/qqq/frontend/materialdashboard/selenium/lib/QSeleniumLib.java index afdda0b..18a4f98 100755 --- a/src/test/java/com/kingsrook/qqq/frontend/materialdashboard/selenium/lib/QSeleniumLib.java +++ b/src/test/java/com/kingsrook/qqq/frontend/materialdashboard/selenium/lib/QSeleniumLib.java @@ -29,6 +29,7 @@ import java.util.Objects; import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; +import java.util.function.Function; import com.kingsrook.qqq.backend.core.utils.SleepUtils; import org.apache.commons.io.FileUtils; import org.apache.logging.log4j.LogManager; @@ -504,7 +505,33 @@ public class QSeleniumLib *******************************************************************************/ public WebElement waitForSelectorContaining(String cssSelector, String textContains) { - LOG.debug("Waiting for element matching selector [" + cssSelector + "] containing text [" + textContains + "]."); + return (waitForSelectorMatchingPredicate(cssSelector, "containing text [" + textContains + "]", (WebElement element) -> + { + return (element.getText() != null && element.getText().toLowerCase().contains(textContains.toLowerCase())); + })); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + public WebElement waitForSelectorContainingTextMatchingRegex(String cssSelector, String regExp) + { + return (waitForSelectorMatchingPredicate(cssSelector, "matching regexp [" + regExp + "]", (WebElement element) -> + { + return (element.getText() != null && element.getText().matches(regExp)); + })); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + private WebElement waitForSelectorMatchingPredicate(String cssSelector, String description, Function predicate) + { + LOG.debug("Waiting for element matching selector [" + cssSelector + "] " + description); long start = System.currentTimeMillis(); do @@ -514,9 +541,9 @@ public class QSeleniumLib { try { - if(element.getText() != null && element.getText().toLowerCase().contains(textContains.toLowerCase())) + if(predicate.apply(element)) { - LOG.debug("Found element matching selector [" + cssSelector + "] containing text [" + textContains + "]."); + LOG.debug("Found element matching selector [" + cssSelector + "] " + description); Actions actions = new Actions(driver); actions.moveToElement(element); conditionallyAutoHighlight(element); @@ -537,7 +564,7 @@ public class QSeleniumLib } while(start + (1000 * WAIT_SECONDS) > System.currentTimeMillis()); - fail("Failed to find element matching selector [" + cssSelector + "] containing text [" + textContains + "] after [" + WAIT_SECONDS + "] seconds."); + fail("Failed to find element matching selector [" + cssSelector + "] " + description + " after [" + WAIT_SECONDS + "] seconds."); return (null); } diff --git a/src/test/java/com/kingsrook/qqq/frontend/materialdashboard/selenium/lib/QueryScreenLib.java b/src/test/java/com/kingsrook/qqq/frontend/materialdashboard/selenium/lib/QueryScreenLib.java index c69b883..d355f38 100644 --- a/src/test/java/com/kingsrook/qqq/frontend/materialdashboard/selenium/lib/QueryScreenLib.java +++ b/src/test/java/com/kingsrook/qqq/frontend/materialdashboard/selenium/lib/QueryScreenLib.java @@ -22,6 +22,9 @@ package com.kingsrook.qqq.frontend.materialdashboard.selenium.lib; +import java.util.List; +import com.kingsrook.qqq.backend.core.utils.CollectionUtils; +import com.kingsrook.qqq.backend.core.utils.StringUtils; import org.openqa.selenium.By; import org.openqa.selenium.Keys; import org.openqa.selenium.WebElement; @@ -103,7 +106,7 @@ public class QueryScreenLib /******************************************************************************* ** *******************************************************************************/ - public void clickFilterButton() + public void clickFilterBuilderButton() { qSeleniumLib.waitForSelectorContaining("BUTTON", "FILTER BUILDER").click(); } @@ -181,10 +184,11 @@ public class QueryScreenLib } + /******************************************************************************* ** *******************************************************************************/ - public void addAdvancedQueryFilterInput(QSeleniumLib qSeleniumLib, int index, String fieldLabel, String operator, String value, String booleanOperator) + public void addAdvancedQueryFilterInput(int index, String fieldLabel, String operator, String value, String booleanOperator) { if(index > 0) { @@ -221,10 +225,91 @@ public class QueryScreenLib operatorInput.sendKeys("\n"); qSeleniumLib.waitForMillis(100); - WebElement valueInput = subFormForField.findElement(By.cssSelector(".filterValuesColumn INPUT")); - valueInput.click(); - valueInput.sendKeys(value); - qSeleniumLib.waitForMillis(100); + if(StringUtils.hasContent(value)) + { + WebElement valueInput = subFormForField.findElement(By.cssSelector(".filterValuesColumn INPUT")); + valueInput.click(); + valueInput.sendKeys(value); + qSeleniumLib.waitForMillis(100); + } } + + + /******************************************************************************* + ** + *******************************************************************************/ + public void addBasicFilter(String fieldLabel) + { + qSeleniumLib.waitForSelectorContaining("BUTTON", "Add Filter").click(); + qSeleniumLib.waitForSelectorContaining(".fieldListMenuBody-addQuickFilter LI", fieldLabel).click(); + qSeleniumLib.clickBackdrop(); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + public void setBasicFilter(String fieldLabel, String operatorLabel, String value) + { + qSeleniumLib.waitForSelectorContaining("BUTTON", fieldLabel).click(); + qSeleniumLib.waitForMillis(250); + qSeleniumLib.waitForSelector("#criteriaOperator").click(); + qSeleniumLib.waitForSelectorContaining("LI", operatorLabel).click(); + + if(StringUtils.hasContent(value)) + { + qSeleniumLib.waitForSelector(".filterValuesColumn INPUT").click(); + // todo - no, not in a listbox/LI here... + qSeleniumLib.waitForSelectorContaining(".MuiAutocomplete-listbox LI", value).click(); + System.out.println(value); + } + + qSeleniumLib.clickBackdrop(); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + public void setBasicFilterPossibleValues(String fieldLabel, String operatorLabel, List values) + { + qSeleniumLib.waitForSelectorContaining("BUTTON", fieldLabel).click(); + qSeleniumLib.waitForMillis(250); + qSeleniumLib.waitForSelector("#criteriaOperator").click(); + qSeleniumLib.waitForSelectorContaining("LI", operatorLabel).click(); + + if(CollectionUtils.nullSafeHasContents(values)) + { + qSeleniumLib.waitForSelector(".filterValuesColumn INPUT").click(); + for(String value : values) + { + qSeleniumLib.waitForSelectorContaining(".MuiAutocomplete-listbox LI", value).click(); + } + } + + qSeleniumLib.clickBackdrop(); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + public void waitForAdvancedQueryStringMatchingRegex(String regEx) + { + qSeleniumLib.waitForSelectorContainingTextMatchingRegex(".advancedQueryString", regEx); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + public void waitForBasicFilterButtonMatchingRegex(String regEx) + { + qSeleniumLib.waitForSelectorContainingTextMatchingRegex("BUTTON", regEx); + } } diff --git a/src/test/java/com/kingsrook/qqq/frontend/materialdashboard/selenium/lib/javalin/QSeleniumJavalin.java b/src/test/java/com/kingsrook/qqq/frontend/materialdashboard/selenium/lib/javalin/QSeleniumJavalin.java index 864b2db..6edf03b 100755 --- a/src/test/java/com/kingsrook/qqq/frontend/materialdashboard/selenium/lib/javalin/QSeleniumJavalin.java +++ b/src/test/java/com/kingsrook/qqq/frontend/materialdashboard/selenium/lib/javalin/QSeleniumJavalin.java @@ -27,6 +27,7 @@ import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; import com.kingsrook.qqq.frontend.materialdashboard.selenium.lib.QSeleniumLib; import io.javalin.Javalin; import org.apache.logging.log4j.LogManager; @@ -284,7 +285,6 @@ public class QSeleniumJavalin do { - // LOG.debug(" captured paths: " + captured.stream().map(CapturedContext::getPath).collect(Collectors.joining(","))); for(CapturedContext context : captured) { if(context.getPath().equals(path)) @@ -301,6 +301,7 @@ public class QSeleniumJavalin } while(start + (1000 * WAIT_SECONDS) > System.currentTimeMillis()); + LOG.debug(" captured paths: \n " + captured.stream().map(cc -> cc.getPath() + "[" + cc.getBody() + "]").collect(Collectors.joining("\n "))); fail("Failed to capture a request for path [" + path + "] with body containing [" + bodyContaining + "] after [" + WAIT_SECONDS + "] seconds."); return (null); } diff --git a/src/test/java/com/kingsrook/qqq/frontend/materialdashboard/selenium/tests/query/QueryScreenFilterInUrlAdvancedModeTest.java b/src/test/java/com/kingsrook/qqq/frontend/materialdashboard/selenium/tests/query/QueryScreenFilterInUrlAdvancedModeTest.java index 0c42219..5cbeea6 100755 --- a/src/test/java/com/kingsrook/qqq/frontend/materialdashboard/selenium/tests/query/QueryScreenFilterInUrlAdvancedModeTest.java +++ b/src/test/java/com/kingsrook/qqq/frontend/materialdashboard/selenium/tests/query/QueryScreenFilterInUrlAdvancedModeTest.java @@ -82,7 +82,7 @@ public class QueryScreenFilterInUrlAdvancedModeTest extends QBaseSeleniumTest qSeleniumLib.gotoAndWaitForBreadcrumbHeaderToContain("/peopleApp/greetingsApp/person?filter=" + URLEncoder.encode(filterJSON, StandardCharsets.UTF_8), "Person"); queryScreenLib.waitForQueryToHaveRan(); queryScreenLib.assertFilterButtonBadge(1); - queryScreenLib.clickFilterButton(); + queryScreenLib.clickFilterBuilderButton(); qSeleniumLib.waitForSelector("input[value=\"is not empty\"]"); /////////////////////////////// @@ -93,7 +93,7 @@ public class QueryScreenFilterInUrlAdvancedModeTest extends QBaseSeleniumTest qSeleniumLib.gotoAndWaitForBreadcrumbHeaderToContain("/peopleApp/greetingsApp/person?filter=" + URLEncoder.encode(filterJSON, StandardCharsets.UTF_8), "Person"); queryScreenLib.waitForQueryToHaveRan(); queryScreenLib.assertFilterButtonBadge(1); - queryScreenLib.clickFilterButton(); + queryScreenLib.clickFilterBuilderButton(); qSeleniumLib.waitForSelector("input[value=\"is between\"]"); qSeleniumLib.waitForSelector("input[value=\"1701\"]"); qSeleniumLib.waitForSelector("input[value=\"74656\"]"); @@ -116,7 +116,7 @@ public class QueryScreenFilterInUrlAdvancedModeTest extends QBaseSeleniumTest qSeleniumLib.gotoAndWaitForBreadcrumbHeaderToContain("/peopleApp/greetingsApp/person?filter=" + URLEncoder.encode(filterJSON, StandardCharsets.UTF_8), "Person"); queryScreenLib.waitForQueryToHaveRan(); queryScreenLib.assertFilterButtonBadge(1); - queryScreenLib.clickFilterButton(); + queryScreenLib.clickFilterBuilderButton(); qSeleniumLib.waitForSelector("input[value=\"is any of\"]"); qSeleniumLib.waitForSelectorContaining(".MuiChip-label", "St. Louis"); qSeleniumLib.waitForSelectorContaining(".MuiChip-label", "Chesterfield"); @@ -129,7 +129,7 @@ public class QueryScreenFilterInUrlAdvancedModeTest extends QBaseSeleniumTest qSeleniumLib.gotoAndWaitForBreadcrumbHeaderToContain("/peopleApp/greetingsApp/person?filter=" + URLEncoder.encode(filterJSON, StandardCharsets.UTF_8), "Person"); queryScreenLib.waitForQueryToHaveRan(); queryScreenLib.assertFilterButtonBadge(1); - queryScreenLib.clickFilterButton(); + queryScreenLib.clickFilterBuilderButton(); qSeleniumLib.waitForSelector("input[value=\"is after\"]"); qSeleniumLib.waitForSelector("input[value=\"5 days ago\"]"); @@ -142,7 +142,7 @@ public class QueryScreenFilterInUrlAdvancedModeTest extends QBaseSeleniumTest qSeleniumLib.gotoAndWaitForBreadcrumbHeaderToContain("/peopleApp/greetingsApp/person?filter=" + URLEncoder.encode(filterJSON, StandardCharsets.UTF_8), "Person"); queryScreenLib.waitForQueryToHaveRan(); queryScreenLib.assertFilterButtonBadge(2); - queryScreenLib.clickFilterButton(); + queryScreenLib.clickFilterBuilderButton(); qSeleniumLib.waitForSelector("input[value=\"is at or before\"]"); qSeleniumLib.waitForSelector("input[value=\"start of this year\"]"); qSeleniumLib.waitForSelector("input[value=\"starts with\"]"); @@ -165,7 +165,7 @@ public class QueryScreenFilterInUrlAdvancedModeTest extends QBaseSeleniumTest qSeleniumLib.gotoAndWaitForBreadcrumbHeaderToContain("/peopleApp/greetingsApp/person?filter=" + URLEncoder.encode(filterJSON, StandardCharsets.UTF_8), "Person"); queryScreenLib.waitForQueryToHaveRan(); queryScreenLib.assertFilterButtonBadge(1); - queryScreenLib.clickFilterButton(); + queryScreenLib.clickFilterBuilderButton(); qSeleniumLib.waitForSelector("input[value=\"does not equal\"]"); qSeleniumLib.waitForSelector("input[value=\"St. Louis\"]"); 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 9a458f0..3dbf608 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,6 +22,7 @@ package com.kingsrook.qqq.frontend.materialdashboard.selenium.tests.query; +import java.util.List; 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; @@ -48,6 +49,7 @@ public class QueryScreenTest extends QBaseSeleniumTest .withRouteToFile("/data/person/count", "data/person/count.json") .withRouteToFile("/data/person/query", "data/person/index.json") .withRouteToFile("/data/person/variants", "data/person/variants.json") + .withRouteToFile("/data/person/possibleValues/homeCityId", "data/person/possibleValues/homeCityId.json") .withRouteToFile("/processes/querySavedView/init", "processes/querySavedView/init.json"); } @@ -64,13 +66,13 @@ public class QueryScreenTest extends QBaseSeleniumTest qSeleniumLib.gotoAndWaitForBreadcrumbHeaderToContain("/peopleApp/greetingsApp/person", "Person"); queryScreenLib.waitForQueryToHaveRan(); queryScreenLib.gotoAdvancedMode(); - queryScreenLib.clickFilterButton(); + queryScreenLib.clickFilterBuilderButton(); ///////////////////////////////////////////////////////////////////// // open the filter window, enter a value, wait for query to re-run // ///////////////////////////////////////////////////////////////////// qSeleniumJavalin.beginCapture(); - queryScreenLib.addAdvancedQueryFilterInput(qSeleniumLib, 0, "Id", "equals", "1", null); + queryScreenLib.addAdvancedQueryFilterInput(0, "Id", "equals", "1", null); /////////////////////////////////////////////////////////////////// // assert that query & count both have the expected filter value // @@ -117,11 +119,11 @@ public class QueryScreenTest extends QBaseSeleniumTest qSeleniumLib.gotoAndWaitForBreadcrumbHeaderToContain("/peopleApp/greetingsApp/person", "Person"); queryScreenLib.waitForQueryToHaveRan(); queryScreenLib.gotoAdvancedMode(); - queryScreenLib.clickFilterButton(); + queryScreenLib.clickFilterBuilderButton(); qSeleniumJavalin.beginCapture(); - queryScreenLib.addAdvancedQueryFilterInput(qSeleniumLib, 0, "First Name", "contains", "Dar", "Or"); - queryScreenLib.addAdvancedQueryFilterInput(qSeleniumLib, 1, "First Name", "contains", "Jam", "Or"); + queryScreenLib.addAdvancedQueryFilterInput(0, "First Name", "contains", "Dar", "Or"); + queryScreenLib.addAdvancedQueryFilterInput(1, "First Name", "contains", "Jam", "Or"); String expectedFilterContents0 = """ {"fieldName":"firstName","operator":"CONTAINS","values":["Dar"]}"""; @@ -137,6 +139,138 @@ public class QueryScreenTest extends QBaseSeleniumTest } + + /******************************************************************************* + ** + *******************************************************************************/ + @Test + void testBasicBooleanOperators() + { + QueryScreenLib queryScreenLib = new QueryScreenLib(qSeleniumLib); + + qSeleniumLib.gotoAndWaitForBreadcrumbHeaderToContain("/peopleApp/greetingsApp/person", "Person"); + queryScreenLib.waitForQueryToHaveRan(); + + queryScreenLib.addBasicFilter("Is Employed"); + + testBasicCriteria(queryScreenLib, "Is Employed", "equals yes", null, "(?s).*Is Employed:.*yes.*", """ + {"fieldName":"isEmployed","operator":"EQUALS","values":[true]}"""); + + testBasicCriteria(queryScreenLib, "Is Employed", "equals no", null, "(?s).*Is Employed:.*no.*", """ + {"fieldName":"isEmployed","operator":"EQUALS","values":[false]}"""); + + testBasicCriteria(queryScreenLib, "Is Employed", "is empty", null, "(?s).*Is Employed:.*is empty.*", """ + {"fieldName":"isEmployed","operator":"IS_BLANK","values":[]}"""); + + testBasicCriteria(queryScreenLib, "Is Employed", "is not empty", null, "(?s).*Is Employed:.*is not empty.*", """ + {"fieldName":"isEmployed","operator":"IS_NOT_BLANK","values":[]}"""); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + @Test + void testBasicPossibleValues() + { + QueryScreenLib queryScreenLib = new QueryScreenLib(qSeleniumLib); + + qSeleniumLib.gotoAndWaitForBreadcrumbHeaderToContain("/peopleApp/greetingsApp/person", "Person"); + queryScreenLib.waitForQueryToHaveRan(); + + String field = "Home City"; + queryScreenLib.addBasicFilter(field); + + testBasicCriteriaPossibleValues(queryScreenLib, field, "is any of", List.of("St. Louis", "Chesterfield"), "(?s).*" + field + ":.*St. Louis.*\\+1.*", """ + {"fieldName":"homeCityId","operator":"IN","values":[1,2]}"""); + + testBasicCriteriaPossibleValues(queryScreenLib, field, "equals", List.of("Chesterfield"), "(?s).*" + field + ":.*Chesterfield.*", """ + {"fieldName":"homeCityId","operator":"EQUALS","values":[2]}"""); + + testBasicCriteriaPossibleValues(queryScreenLib, field, "is empty", null, "(?s).*" + field + ":.*is empty.*", """ + {"fieldName":"homeCityId","operator":"IS_BLANK","values":[]}"""); + + testBasicCriteriaPossibleValues(queryScreenLib, field, "does not equal", List.of("St. Louis"), "(?s).*" + field + ":.*does not equal.*St. Louis.*", """ + {"fieldName":"homeCityId","operator":"NOT_EQUALS_OR_IS_NULL","values":[1]}"""); + + testBasicCriteriaPossibleValues(queryScreenLib, field, "is none of", List.of("Chesterfield"), "(?s).*" + field + ":.*is none of.*St. Louis.*\\+1", """ + {"fieldName":"homeCityId","operator":"NOT_IN","values":[1,2]}"""); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + private void testBasicCriteria(QueryScreenLib queryScreenLib, String fieldLabel, String operatorLabel, String value, String expectButtonStringRegex, String expectFilterJsonContains) + { + qSeleniumJavalin.beginCapture(); + queryScreenLib.setBasicFilter(fieldLabel, operatorLabel, value); + queryScreenLib.waitForBasicFilterButtonMatchingRegex(expectButtonStringRegex); + qSeleniumJavalin.waitForCapturedPathWithBodyContaining("/data/person/query", expectFilterJsonContains); + qSeleniumJavalin.endCapture(); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + private void testBasicCriteriaPossibleValues(QueryScreenLib queryScreenLib, String fieldLabel, String operatorLabel, List values, String expectButtonStringRegex, String expectFilterJsonContains) + { + qSeleniumJavalin.beginCapture(); + queryScreenLib.setBasicFilterPossibleValues(fieldLabel, operatorLabel, values); + queryScreenLib.waitForBasicFilterButtonMatchingRegex(expectButtonStringRegex); + qSeleniumJavalin.waitForCapturedPathWithBodyContaining("/data/person/query", expectFilterJsonContains); + qSeleniumJavalin.endCapture(); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + @Test + void testAdvancedBooleanOperators() + { + QueryScreenLib queryScreenLib = new QueryScreenLib(qSeleniumLib); + + qSeleniumLib.gotoAndWaitForBreadcrumbHeaderToContain("/peopleApp/greetingsApp/person", "Person"); + queryScreenLib.waitForQueryToHaveRan(); + + queryScreenLib.gotoAdvancedMode(); + + testAdvancedCriteria(queryScreenLib, "Is Employed", "equals yes", null, "(?s).*Is Employed.*equals yes.*", """ + {"fieldName":"isEmployed","operator":"EQUALS","values":[true]}"""); + + testAdvancedCriteria(queryScreenLib, "Is Employed", "equals no", null, "(?s).*Is Employed.*equals no.*", """ + {"fieldName":"isEmployed","operator":"EQUALS","values":[false]}"""); + + testAdvancedCriteria(queryScreenLib, "Is Employed", "is empty", null, "(?s).*Is Employed.*is empty.*", """ + {"fieldName":"isEmployed","operator":"IS_BLANK","values":[]}"""); + + testAdvancedCriteria(queryScreenLib, "Is Employed", "is not empty", null, "(?s).*Is Employed.*is not empty.*", """ + {"fieldName":"isEmployed","operator":"IS_NOT_BLANK","values":[]}"""); + } + // todo - table requires variant - prompt for it, choose it, see query; change variant, change on-screen, re-query + + + /******************************************************************************* + ** + *******************************************************************************/ + private void testAdvancedCriteria(QueryScreenLib queryScreenLib, String fieldLabel, String operatorLabel, String value, String expectQueryStringRegex, String expectFilterJsonContains) + { + qSeleniumJavalin.beginCapture(); + queryScreenLib.clickFilterBuilderButton(); + queryScreenLib.addAdvancedQueryFilterInput(0, fieldLabel, operatorLabel, value, null); + qSeleniumLib.clickBackdrop(); + queryScreenLib.waitForAdvancedQueryStringMatchingRegex(expectQueryStringRegex); + qSeleniumJavalin.waitForCapturedPathWithBodyContaining("/data/person/query", expectFilterJsonContains); + qSeleniumJavalin.endCapture(); + queryScreenLib.clickAdvancedFilterClearIcon(); + } + }