From f189083a5ac86bc2fad892ad27c553f2cbf76265 Mon Sep 17 00:00:00 2001 From: Darin Kelkhoff Date: Wed, 26 Jul 2023 12:39:58 -0500 Subject: [PATCH] Fix bug w/ filter in URL not having any values not being respected. Add selenium test for it!! --- src/qqq/utils/qqq/FilterUtils.ts | 11 +- .../tests/QueryScreenFilterInUrlTest.java | 185 ++++++++++++++++++ .../person/possibleValues/homeCityId.json | 12 ++ 3 files changed, 204 insertions(+), 4 deletions(-) create mode 100755 src/test/java/com/kingsrook/qqq/materialdashboard/tests/QueryScreenFilterInUrlTest.java create mode 100644 src/test/resources/fixtures/data/person/possibleValues/homeCityId.json diff --git a/src/qqq/utils/qqq/FilterUtils.ts b/src/qqq/utils/qqq/FilterUtils.ts index 8ce5706..eeb86b9 100644 --- a/src/qqq/utils/qqq/FilterUtils.ts +++ b/src/qqq/utils/qqq/FilterUtils.ts @@ -449,12 +449,15 @@ class FilterUtils ////////////////////////////////////////////////////////////////////////// // replace objects that look like expressions with expression instances // ////////////////////////////////////////////////////////////////////////// - for(let i = 0; i < values.length; i++) + if(values && values.length) { - const expression = this.gridCriteriaValueToExpression(values[i]) - if(expression) + for (let i = 0; i < values.length; i++) { - values[i] = expression; + const expression = this.gridCriteriaValueToExpression(values[i]) + if (expression) + { + values[i] = expression; + } } } diff --git a/src/test/java/com/kingsrook/qqq/materialdashboard/tests/QueryScreenFilterInUrlTest.java b/src/test/java/com/kingsrook/qqq/materialdashboard/tests/QueryScreenFilterInUrlTest.java new file mode 100755 index 0000000..77e8b0e --- /dev/null +++ b/src/test/java/com/kingsrook/qqq/materialdashboard/tests/QueryScreenFilterInUrlTest.java @@ -0,0 +1,185 @@ +/* + * QQQ - Low-code Application Framework for Engineers. + * Copyright (C) 2021-2022. Kingsrook, LLC + * 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States + * contact@kingsrook.com + * https://github.com/Kingsrook/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.kingsrook.qqq.materialdashboard.tests; + + +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.time.temporal.ChronoUnit; +import com.kingsrook.qqq.backend.core.model.actions.tables.query.QCriteriaOperator; +import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterCriteria; +import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter; +import com.kingsrook.qqq.backend.core.model.actions.tables.query.expressions.NowWithOffset; +import com.kingsrook.qqq.backend.core.model.actions.tables.query.expressions.ThisOrLastPeriod; +import com.kingsrook.qqq.backend.core.utils.JsonUtils; +import com.kingsrook.qqq.materialdashboard.lib.QBaseSeleniumTest; +import com.kingsrook.qqq.materialdashboard.lib.QQQMaterialDashboardSelectors; +import com.kingsrook.qqq.materialdashboard.lib.javalin.QSeleniumJavalin; +import org.junit.jupiter.api.Test; +import org.openqa.selenium.WebElement; + + +/******************************************************************************* + ** Test for the record query screen when a filter is given in the URL + *******************************************************************************/ +public class QueryScreenFilterInUrlTest extends QBaseSeleniumTest +{ + + /******************************************************************************* + ** + *******************************************************************************/ + @Override + protected void addJavalinRoutes(QSeleniumJavalin qSeleniumJavalin) + { + super.addJavalinRoutes(qSeleniumJavalin); + qSeleniumJavalin + .withRouteToFile("/data/person/count", "data/person/count.json") + .withRouteToFile("/data/person/query", "data/person/index.json") + .withRouteToFile("/data/person/possibleValues/homeCityId", "data/person/possibleValues/homeCityId.json") + .withRouteToFile("/data/person/variants", "data/person/variants.json") + .withRouteToFile("/processes/querySavedFilter/init", "processes/querySavedFilter/init.json"); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + @Test + void testUrlWithFilter() + { + //////////////////////////////////////// + // not-blank -- criteria w/ no values // + //////////////////////////////////////// + String filterJSON = JsonUtils.toJson(new QQueryFilter() + .withCriteria(new QFilterCriteria("annualSalary", QCriteriaOperator.IS_NOT_BLANK))); + qSeleniumLib.gotoAndWaitForBreadcrumbHeader("/peopleApp/greetingsApp/person?filter=" + URLEncoder.encode(filterJSON, StandardCharsets.UTF_8), "Person"); + waitForQueryToHaveRan(); + assertFilterButtonBadge(1); + clickFilterButton(); + qSeleniumLib.waitForSelector("input[value=\"is not empty\"]"); + + /////////////////////////////// + // between on a number field // + /////////////////////////////// + filterJSON = JsonUtils.toJson(new QQueryFilter() + .withCriteria(new QFilterCriteria("annualSalary", QCriteriaOperator.BETWEEN, 1701, 74656))); + qSeleniumLib.gotoAndWaitForBreadcrumbHeader("/peopleApp/greetingsApp/person?filter=" + URLEncoder.encode(filterJSON, StandardCharsets.UTF_8), "Person"); + waitForQueryToHaveRan(); + assertFilterButtonBadge(1); + clickFilterButton(); + qSeleniumLib.waitForSelector("input[value=\"is between\"]"); + qSeleniumLib.waitForSelector("input[value=\"1701\"]"); + qSeleniumLib.waitForSelector("input[value=\"74656\"]"); + + ////////////////////////////////////////// + // not-equals on a possible-value field // + ////////////////////////////////////////// + filterJSON = JsonUtils.toJson(new QQueryFilter() + .withCriteria(new QFilterCriteria("homeCityId", QCriteriaOperator.NOT_EQUALS, 1))); + qSeleniumLib.gotoAndWaitForBreadcrumbHeader("/peopleApp/greetingsApp/person?filter=" + URLEncoder.encode(filterJSON, StandardCharsets.UTF_8), "Person"); + waitForQueryToHaveRan(); + assertFilterButtonBadge(1); + clickFilterButton(); + qSeleniumLib.waitForSelector("input[value=\"does not equal\"]"); + qSeleniumLib.waitForSelector("input[value=\"St. Louis\"]"); + + ////////////////////////////////////// + // an IN for a possible-value field // + ////////////////////////////////////// + filterJSON = JsonUtils.toJson(new QQueryFilter() + .withCriteria(new QFilterCriteria("homeCityId", QCriteriaOperator.IN, 1, 2))); + qSeleniumLib.gotoAndWaitForBreadcrumbHeader("/peopleApp/greetingsApp/person?filter=" + URLEncoder.encode(filterJSON, StandardCharsets.UTF_8), "Person"); + waitForQueryToHaveRan(); + assertFilterButtonBadge(1); + clickFilterButton(); + qSeleniumLib.waitForSelector("input[value=\"is any of\"]"); + qSeleniumLib.waitForSelectorContaining(".MuiChip-label", "St. Louis"); + qSeleniumLib.waitForSelectorContaining(".MuiChip-label", "Chesterfield"); + + ///////////////////////////////////////// + // greater than a date-time expression // + ///////////////////////////////////////// + filterJSON = JsonUtils.toJson(new QQueryFilter() + .withCriteria(new QFilterCriteria("createDate", QCriteriaOperator.GREATER_THAN, NowWithOffset.minus(5, ChronoUnit.DAYS)))); + qSeleniumLib.gotoAndWaitForBreadcrumbHeader("/peopleApp/greetingsApp/person?filter=" + URLEncoder.encode(filterJSON, StandardCharsets.UTF_8), "Person"); + waitForQueryToHaveRan(); + assertFilterButtonBadge(1); + clickFilterButton(); + qSeleniumLib.waitForSelector("input[value=\"is after\"]"); + qSeleniumLib.waitForSelector("input[value=\"5 days ago\"]"); + + /////////////////////// + // multiple criteria // + /////////////////////// + filterJSON = JsonUtils.toJson(new QQueryFilter() + .withCriteria(new QFilterCriteria("firstName", QCriteriaOperator.STARTS_WITH, "Dar")) + .withCriteria(new QFilterCriteria("createDate", QCriteriaOperator.LESS_THAN_OR_EQUALS, ThisOrLastPeriod.this_(ChronoUnit.YEARS)))); + qSeleniumLib.gotoAndWaitForBreadcrumbHeader("/peopleApp/greetingsApp/person?filter=" + URLEncoder.encode(filterJSON, StandardCharsets.UTF_8), "Person"); + waitForQueryToHaveRan(); + assertFilterButtonBadge(2); + clickFilterButton(); + qSeleniumLib.waitForSelector("input[value=\"is at or before\"]"); + qSeleniumLib.waitForSelector("input[value=\"start of this year\"]"); + qSeleniumLib.waitForSelector("input[value=\"starts with\"]"); + qSeleniumLib.waitForSelector("input[value=\"Dar\"]"); + + //////////////// + // remove one // + //////////////// + qSeleniumLib.waitForSelectorContaining(".MuiIcon-root", "close").click(); + assertFilterButtonBadge(1); + + qSeleniumLib.waitForever(); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + private WebElement assertFilterButtonBadge(int valueInBadge) + { + return qSeleniumLib.waitForSelectorContaining(".MuiBadge-root", String.valueOf(valueInBadge)); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + private WebElement waitForQueryToHaveRan() + { + return qSeleniumLib.waitForSelector(QQQMaterialDashboardSelectors.QUERY_GRID_CELL); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + private void clickFilterButton() + { + qSeleniumLib.waitForSelectorContaining(".MuiDataGrid-toolbarContainer BUTTON", "Filter").click(); + } + +} diff --git a/src/test/resources/fixtures/data/person/possibleValues/homeCityId.json b/src/test/resources/fixtures/data/person/possibleValues/homeCityId.json new file mode 100644 index 0000000..380b357 --- /dev/null +++ b/src/test/resources/fixtures/data/person/possibleValues/homeCityId.json @@ -0,0 +1,12 @@ +{ + "options": [ + { + "id": 1, + "label": "St. Louis" + }, + { + "id": 2, + "label": "Chesterfield" + } + ] +} \ No newline at end of file