From 81471681c0824db703114c0d3594e27aaf227dad Mon Sep 17 00:00:00 2001 From: temi Date: Mon, 26 Aug 2024 11:58:40 +1000 Subject: [PATCH] #3291 - start and end date behaviour modification - added functional and unit test cases --- .../assets/javascripts/projectExplorer.js | 24 ++++--- .../au/org/ala/merit/SearchService.groovy | 34 ++++++---- grails-app/views/home/_projectFinder.gsp | 12 +++- package-lock.json | 34 +++++----- package.json | 2 +- .../fieldcapture/ProjectExplorerSpec.groovy | 65 ++++++++++++++++++- .../groovy/pages/ProjectExplorer.groovy | 11 +++- .../groovy/pages/ReloadablePage.groovy | 4 ++ .../resources/dataset2/loadDataSet.js | 4 +- src/test/js/spec/DatePickerModelSpec.js | 5 +- 10 files changed, 151 insertions(+), 44 deletions(-) diff --git a/grails-app/assets/javascripts/projectExplorer.js b/grails-app/assets/javascripts/projectExplorer.js index 86fbf7be0..68c293f70 100644 --- a/grails-app/assets/javascripts/projectExplorer.js +++ b/grails-app/assets/javascripts/projectExplorer.js @@ -4,7 +4,7 @@ //= require reef2050Report.js //= require components.js -var DatePickerModel = function(fromDate, toDate, urlWithoutDates, $location) { +var DatePickerModel = function(fromDate, toDate, isFilterByCompletedProjects, urlWithoutDates, $location) { var formatString = 'YYYY-MM-DD'; var self = this; var date = moment('2011-07-01T00:00:00+10:00'); @@ -18,6 +18,7 @@ var DatePickerModel = function(fromDate, toDate, urlWithoutDates, $location) { date = rangeEnd; } self.selectedRange = ko.observable(); + self.isFilterByCompletedProjects = ko.observable(isFilterByCompletedProjects || false); self.fromDate = ko.observable().extend({simpleDate:false}); if (fromDate) { self.fromDate(moment(fromDate).format()); @@ -34,23 +35,23 @@ var DatePickerModel = function(fromDate, toDate, urlWithoutDates, $location) { $location.href = urlWithoutDates; }; - var validateAndReload = function(newFromDate, newToDate) { + var validateAndReloadAndOption = function(newFromDate, newToDate, newIsFilterByCompletedProjects) { var formattedFromDate = moment(fromDate).format(fromDate); var formattedToDate = moment(toDate).format(toDate); var formattedNewFromDate = moment(newFromDate).format(formatString); var formattedNewToDate = moment(newToDate).format(formatString); - if (formattedNewFromDate == formattedFromDate && formattedNewToDate == formattedToDate) { + if (formattedNewFromDate == formattedFromDate && formattedNewToDate == formattedToDate && isFilterByCompletedProjects == newIsFilterByCompletedProjects) { return; } if ($('#facet-dates').validationEngine('validate')) { - reloadWithDates(newFromDate, newToDate); + reloadWithDatesAndOption(newFromDate, newToDate, newIsFilterByCompletedProjects); } } - var reloadWithDates = function(newFromDate, newToDate) { + var reloadWithDatesAndOption = function(newFromDate, newToDate, isFilterByCompletedProjects) { var parsedNewFromDate = moment(newFromDate); var parsedNewToDate = moment(newToDate); if (newFromDate && parsedNewFromDate.isValid()) { @@ -61,23 +62,30 @@ var DatePickerModel = function(fromDate, toDate, urlWithoutDates, $location) { urlWithoutDates += urlWithoutDates?'&':'?'; urlWithoutDates += 'toDate='+moment(newToDate).format(formatString); } + if (isFilterByCompletedProjects != undefined) { + urlWithoutDates += urlWithoutDates?'&':'?'; + urlWithoutDates += 'isFilterByCompletedProjects='+isFilterByCompletedProjects; + } $location.href = urlWithoutDates; } self.fromDate.subscribe(function(a, b) { - validateAndReload(self.fromDate(), self.toDate()); + validateAndReloadAndOption(self.fromDate(), self.toDate(), self.isFilterByCompletedProjects()); }); self.toDate.subscribe(function(toDate) { - validateAndReload(self.fromDate(), self.toDate()); + validateAndReloadAndOption(self.fromDate(), self.toDate(), self.isFilterByCompletedProjects()); }); self.selectedRange.subscribe(function(value) { if (value.from) { - reloadWithDates(value.from, value.to); + reloadWithDatesAndOption(value.from, value.to, self.isFilterByCompletedProjects()); } }); + self.isFilterByCompletedProjects.subscribe(function() { + validateAndReloadAndOption(self.fromDate(), self.toDate(), self.isFilterByCompletedProjects()); + }); }; var DownloadViewModel = function(options) { diff --git a/grails-app/services/au/org/ala/merit/SearchService.groovy b/grails-app/services/au/org/ala/merit/SearchService.groovy index b87fa4abd..0b4eb8f99 100644 --- a/grails-app/services/au/org/ala/merit/SearchService.groovy +++ b/grails-app/services/au/org/ala/merit/SearchService.groovy @@ -5,12 +5,10 @@ import grails.core.GrailsApplication import groovy.json.JsonSlurper import groovy.util.logging.Slf4j import org.apache.commons.lang.StringUtils -import org.apache.http.HttpStatus import org.springframework.beans.factory.annotation.Autowired import javax.annotation.PostConstruct import java.math.RoundingMode - /** * Service for ElasticSearch running on ecodata */ @@ -159,16 +157,30 @@ class SearchService { } private void handleDateFilters(params) { - if (params.fromDate || params.toDate) { - List fq = params.getList('fq') - if (!params.fromDate) { - fq += "_query:(plannedStartDate:[* TO ${params.toDate}])" - } else if (!params.toDate) { - fq += "_query:(plannedEndDate:[${params.fromDate} TO *])" - } else { - fq += "_query:(plannedEndDate:[${params.fromDate} TO *] AND plannedStartDate:[* TO ${params.toDate}])" + if (params.getBoolean('isFilterByCompletedProjects', false)) { + if (params.fromDate || params.toDate) { + List fq = params.getList('fq') + if (!params.fromDate) { + fq += "_query:(plannedStartDate:[* TO ${params.toDate}) AND plannedEndDate:[* TO ${params.toDate}))" + } else if (!params.toDate) { + fq += "_query:(plannedStartDate:[${params.fromDate} TO *] AND plannedEndDate:[${params.fromDate} TO *])" + } else { + fq += "_query:(plannedStartDate:[${params.fromDate} TO ${params.toDate}] AND plannedEndDate:[${params.fromDate} TO ${params.toDate}])" + } + params.fq = fq + } + } else { + if (params.fromDate || params.toDate) { + List fq = params.getList('fq') + if (!params.fromDate) { + fq += "_query:(plannedStartDate:[* TO ${params.toDate}])" + } else if (!params.toDate) { + fq += "_query:(plannedEndDate:[${params.fromDate} TO *])" + } else { + fq += "_query:(plannedEndDate:[${params.fromDate} TO *] AND plannedStartDate:[* TO ${params.toDate}])" + } + params.fq = fq } - params.fq = fq } } diff --git a/grails-app/views/home/_projectFinder.gsp b/grails-app/views/home/_projectFinder.gsp index 7fe44b6c0..042156802 100644 --- a/grails-app/views/home/_projectFinder.gsp +++ b/grails-app/views/home/_projectFinder.gsp @@ -68,6 +68,12 @@
+
+ + +
@@ -472,6 +478,9 @@ params += '&toDate='+'${params.toDate.encodeAsURL()}'; + + params += '&isFilterByCompletedProjects=' + ${params.isFilterByCompletedProjects.encodeAsJavaScript()}; + $.post(url, params).done(function(data1) { //console.log("getJSON data", data); @@ -545,11 +554,12 @@ var urlWithoutDates = ''; var fromDate = '${params.fromDate?.encodeAsJavaScript()?:''}'; var toDate = '${params.toDate?.encodeAsJavaScript()?:''}'; + var isFilterByCompletedProjects = ${params.isFilterByCompletedProjects?.encodeAsJavaScript() ?: false }; var error = "${error?.encodeAsJavaScript()}"; if(!error){ - ko.applyBindings(new DatePickerModel(fromDate, toDate, urlWithoutDates, window.location), document.getElementById('facet-dates')); + ko.applyBindings(new DatePickerModel(fromDate, toDate, isFilterByCompletedProjects, urlWithoutDates, window.location), document.getElementById('facet-dates')); } function FacetFilterViewModel (params) { diff --git a/package-lock.json b/package-lock.json index ad7654df0..13097f216 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,7 @@ }, "devDependencies": { "@metahub/karma-jasmine-jquery": "^2.0.1", - "chromedriver": "^126.0.4", + "chromedriver": "^128.0.0", "jasmine-core": "^3.5.0", "jasmine-jquery": "^2.0.0", "jquery": "3.6.2", @@ -664,12 +664,12 @@ "dev": true }, "node_modules/axios": { - "version": "1.6.7", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.7.tgz", - "integrity": "sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA==", + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.5.tgz", + "integrity": "sha512-fZu86yCo+svH3uqJ/yTdQ0QHpQu5oL+/QE+QPSv6BZSkDAoky9vytxp7u5qk83OJFS3kEBcesWni9WTZAv3tSw==", "dev": true, "dependencies": { - "follow-redirects": "^1.15.4", + "follow-redirects": "^1.15.6", "form-data": "^4.0.0", "proxy-from-env": "^1.1.0" } @@ -1087,14 +1087,14 @@ } }, "node_modules/chromedriver": { - "version": "126.0.4", - "resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-126.0.4.tgz", - "integrity": "sha512-mIdJqdocfN/y9fl5BymIzM9WQLy64x078i5tS1jGFzbFAwXwXrj3zmA86Wf3R/hywPYpWqwXxFGBJHgqZTuGCA==", + "version": "128.0.0", + "resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-128.0.0.tgz", + "integrity": "sha512-Ggo21z/dFQxTOTgU0vm0V59Mi79yyR+9AUk/KiVAsRfbDRdVZQYQWfgxnIvD/x8KOKn0oB7haRzDO/KfrKyvOA==", "dev": true, "hasInstallScript": true, "dependencies": { "@testim/chrome-version": "^1.1.4", - "axios": "^1.6.7", + "axios": "^1.7.4", "compare-versions": "^6.1.0", "extract-zip": "^2.0.1", "proxy-agent": "^6.4.0", @@ -4960,12 +4960,12 @@ "dev": true }, "axios": { - "version": "1.6.7", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.7.tgz", - "integrity": "sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA==", + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.5.tgz", + "integrity": "sha512-fZu86yCo+svH3uqJ/yTdQ0QHpQu5oL+/QE+QPSv6BZSkDAoky9vytxp7u5qk83OJFS3kEBcesWni9WTZAv3tSw==", "dev": true, "requires": { - "follow-redirects": "^1.15.4", + "follow-redirects": "^1.15.6", "form-data": "^4.0.0", "proxy-from-env": "^1.1.0" } @@ -5301,13 +5301,13 @@ } }, "chromedriver": { - "version": "126.0.4", - "resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-126.0.4.tgz", - "integrity": "sha512-mIdJqdocfN/y9fl5BymIzM9WQLy64x078i5tS1jGFzbFAwXwXrj3zmA86Wf3R/hywPYpWqwXxFGBJHgqZTuGCA==", + "version": "128.0.0", + "resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-128.0.0.tgz", + "integrity": "sha512-Ggo21z/dFQxTOTgU0vm0V59Mi79yyR+9AUk/KiVAsRfbDRdVZQYQWfgxnIvD/x8KOKn0oB7haRzDO/KfrKyvOA==", "dev": true, "requires": { "@testim/chrome-version": "^1.1.4", - "axios": "^1.6.7", + "axios": "^1.7.4", "compare-versions": "^6.1.0", "extract-zip": "^2.0.1", "proxy-agent": "^6.4.0", diff --git a/package.json b/package.json index 7b54a1815..9ccde6792 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ }, "devDependencies": { "@metahub/karma-jasmine-jquery": "^2.0.1", - "chromedriver": "^126.0.4", + "chromedriver": "^128.0.0", "jasmine-core": "^3.5.0", "jasmine-jquery": "^2.0.0", "jquery": "3.6.2", diff --git a/src/integration-test/groovy/au/org/ala/fieldcapture/ProjectExplorerSpec.groovy b/src/integration-test/groovy/au/org/ala/fieldcapture/ProjectExplorerSpec.groovy index 6da8fb0c1..19aa83250 100644 --- a/src/integration-test/groovy/au/org/ala/fieldcapture/ProjectExplorerSpec.groovy +++ b/src/integration-test/groovy/au/org/ala/fieldcapture/ProjectExplorerSpec.groovy @@ -1,10 +1,11 @@ package au.org.ala.fieldcapture -import pages.AdminTools +import groovy.util.logging.Slf4j import pages.Facet import pages.ProjectExplorer import spock.lang.Stepwise +@Slf4j @Stepwise class ProjectExplorerSpec extends StubbedCasSpec { @@ -60,6 +61,68 @@ class ProjectExplorerSpec extends StubbedCasSpec { new HashSet(projects.collect{it.name}) == expectedProjects + when: "We filter project by dates" + dateFacet.click() + + then: + waitFor 10, { + clearDatesBtn.displayed + } + + when: + at ProjectExplorer // reset timer + setFromDate("01/06/2015") + + then: + waitFor {hasBeenReloaded()} + waitFor 10, { + clearDatesBtn.displayed + } + waitFor 20, { + projects.size() == 15 + } + + when: "We filter project by dates" + at ProjectExplorer // reset timer + setToDate("02/07/2018") + + then: + waitFor {hasBeenReloaded()} + waitFor 10, { + clearDatesBtn.displayed + } + waitFor 20, { + projects.size() == 14 + } + + when: "We filter project by dates" + at ProjectExplorer + dateOption.click() + + then: + waitFor {hasBeenReloaded()} + waitFor 20, { + clearDatesBtn.displayed + } + waitFor 20, { + projects.size() == 1 + } + + when: "We clear dates" + at ProjectExplorer + clearDatesBtn.click() + + then: + waitFor {hasBeenReloaded()} + waitFor 10, { + clearDatesBtn.displayed + } + waitFor 20, { + projects.size() == 15 + facets.size() == 12 + chooseMoreFacetTerms.size() == 0 + } + when: Facet projectStatus = findFacetByName("Project Status") projectStatus.expand() diff --git a/src/integration-test/groovy/pages/ProjectExplorer.groovy b/src/integration-test/groovy/pages/ProjectExplorer.groovy index a05a33ce1..cfc58e424 100644 --- a/src/integration-test/groovy/pages/ProjectExplorer.groovy +++ b/src/integration-test/groovy/pages/ProjectExplorer.groovy @@ -3,6 +3,7 @@ package pages import geb.Module import geb.module.Checkbox import pages.modules.ViewReef2050PlanReport +import org.openqa.selenium.Keys class FacetItem extends Module { static content = { @@ -59,7 +60,9 @@ class ProjectExplorer extends ReloadablePage { chooseMoreFacetTerms(required: false) { $('#facetsContent .moreFacets') } facetTerms(required: false) { $("#facetsContent .accordion .card-header a") } facetAccordion(required: false) { $("#facetsContent .accordion") } - + dateOption(required: false, wait: true) { $("#isFilterByCompletedProjectsOption", dynamic: true) } + dateFacet(required: false) { $("#projectDates", dynamic: true) } + clearDatesBtn(required: false) { $("#facet-dates .clearDates", dynamic: true) } inputText{ $("#keywords")} dashboardContent (required: false) {$("div#dashboard-content")} @@ -110,7 +113,13 @@ class ProjectExplorer extends ReloadablePage { waitFor { dashboardContent.displayed } } + void setFromDate (String date) { + $("input#fromDate", dynamic: true) << date << Keys.TAB + } + void setToDate (String date) { + $("input#toDate", dynamic: true) << date << Keys.TAB + } } class ProjectsList extends Module { diff --git a/src/integration-test/groovy/pages/ReloadablePage.groovy b/src/integration-test/groovy/pages/ReloadablePage.groovy index 2c218ad21..0b50aae1f 100644 --- a/src/integration-test/groovy/pages/ReloadablePage.groovy +++ b/src/integration-test/groovy/pages/ReloadablePage.groovy @@ -1,7 +1,9 @@ package pages import geb.Page +import groovy.util.logging.Slf4j +@Slf4j class ReloadablePage extends Page { private long atCheckTime = 0l @@ -48,5 +50,7 @@ class ReloadablePage extends Page { if (result) { saveAtCheckTime() } + + result } } diff --git a/src/integration-test/resources/dataset2/loadDataSet.js b/src/integration-test/resources/dataset2/loadDataSet.js index e68e9d63e..c6b1398b8 100644 --- a/src/integration-test/resources/dataset2/loadDataSet.js +++ b/src/integration-test/resources/dataset2/loadDataSet.js @@ -84,7 +84,7 @@ for (var i = 1; i < 10; i++) { } } -createProject({projectId: "meri1", name: "Configurable MERI plan project", programId: "configurable_meri_plan"}); +createProject({projectId: "meri1", name: "Configurable MERI plan project", programId: "configurable_meri_plan", plannedStartDate: ISODate("2015-06-30T14:00:00Z"), plannedEndDate: ISODate("2016-06-30T14:00:00Z")}); db.userPermission.insert({ entityType: 'au.org.ala.ecodata.Project', entityId: "meri1", @@ -115,7 +115,7 @@ createOrganisation({ acronym:'TSTORG', description:'THE TRUSTEE FOR PSS FUND Test' }) -createProject({name:'project active', projectId:"project_active", status:"active", planStatus:'submitted', externalIds:[{idType:'INTERNAL_ORDER_NUMBER', externalId:'12345'}], programId:'default_outcome' }) +createProject({name:'project active', projectId:"project_active", status:"active", planStatus:'submitted', externalIds:[{idType:'INTERNAL_ORDER_NUMBER', externalId:'12345'}], programId:'default_outcome', plannedStartDate: ISODate("2023-12-01T14:00:00Z"), plannedEndDate: ISODate("2024-08-01T14:00:00Z") }) createProject({name:'project application', projectId:"project_application", status:"application", planStatus:'submitted', programId:'default_outcome', externalIds:[]}) createProject({name:'project completed', projectId:"project_completed", status:"completed", planStatus:'submitted', externalIds:[{idType:'INTERNAL_ORDER_NUMBER', externalId:'12345'}], programId:'default_outcome'}) diff --git a/src/test/js/spec/DatePickerModelSpec.js b/src/test/js/spec/DatePickerModelSpec.js index 6729f5e13..0cd0c5448 100644 --- a/src/test/js/spec/DatePickerModelSpec.js +++ b/src/test/js/spec/DatePickerModelSpec.js @@ -3,14 +3,15 @@ describe("The documents contains view models for working with documents", functi it("Can decide whether document roles are public based on metadata", function() { var fromDate = '2022-01-01'; var toDate = '2022-06-30'; + var isFilterByCompletedProjects = true; var urlWithoutDates = '?query=Test' var $location = {}; - var model = new DatePickerModel(fromDate, toDate, urlWithoutDates, $location); + var model = new DatePickerModel(fromDate, toDate, isFilterByCompletedProjects, urlWithoutDates, $location); // Ensure the page doesn't automatically reload on initialisation (this is a regression test for a bug) expect($location.href).toBeUndefined(); model.fromDate('2022-02-01'); - expect($location.href).toEqual('?query=Test&fromDate=2022-02-01&toDate=2022-06-30'); + expect($location.href).toEqual('?query=Test&fromDate=2022-02-01&toDate=2022-06-30&isFilterByCompletedProjects=true'); }); }); \ No newline at end of file