From 1f4be5f3f5acab79d8be0f1043dc4c9851fbc616 Mon Sep 17 00:00:00 2001 From: nboisteault Date: Mon, 2 Dec 2024 12:08:28 +0100 Subject: [PATCH 1/8] Form filter: filter autocomplete list based on previous applied filters --- assets/src/legacy/filter.js | 38 +++++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/assets/src/legacy/filter.js b/assets/src/legacy/filter.js index 760d742cf5..9fafe9f45a 100644 --- a/assets/src/legacy/filter.js +++ b/assets/src/legacy/filter.js @@ -400,18 +400,6 @@ var lizLayerFilterTool = function () { fieldname: field, filter: '' }; - $.get(globalThis['filterConfigData'].url, sdata, function (result) { - if (!checkResult(result)) { - return false; - } - - var autocompleteData = []; - for (var a in result) { - var feat = result[a]; - if (feat['v'] === null || !feat['v'] || (typeof feat['v'] === 'string' && feat['v'].trim() === '')) - continue; - autocompleteData.push(feat['v']); - } var html = ''; html += getFormFieldHeader(field_item); @@ -424,7 +412,30 @@ var lizLayerFilterTool = function () { addFieldEvents(field_item); $("#liz-filter-field-text" + lizMap.cleanName(field_item.title)).autocomplete({ - source: autocompleteData, + source: function (request, response) { + const autocompleteFilter = `"${field}" ILIKE '%${request.term}%' `; + if (!globalThis['filterConfigData'].filter) { + sdata.filter = autocompleteFilter; + } else { + sdata.filter = globalThis['filterConfigData'].filter + ' AND ' + autocompleteFilter; + } + fetch(globalThis['filterConfigData'].url, { + method: "POST", + body: new URLSearchParams(sdata) + }).then(response => { + return response.json(); + }).then(result => { + var autocompleteData = []; + for (var a in result) { + var feat = result[a]; + if (feat['v'] === null || !feat['v'] || (typeof feat['v'] === 'string' && feat['v'].trim() === '')) { + continue; + } + autocompleteData.push(feat['v']); + } + response(autocompleteData); + }); + }, autoFocus: false, // do not autofocus, because this prevents from searching with LIKE delay: 200, minLength: 2, @@ -433,7 +444,6 @@ var lizLayerFilterTool = function () { $(this).change(); } }); - }, 'json'); } // Get the HTML form element for the uniqueValues field type From 4f81670b69631161c9dd270a32fe1e160ee62f4d Mon Sep 17 00:00:00 2001 From: nboisteault Date: Mon, 2 Dec 2024 12:28:09 +0100 Subject: [PATCH 2/8] e2e: test dynamic autocomplete in form filter --- tests/qgis-projects/tests/form_filter.qgs | 800 +++++++++++------- tests/qgis-projects/tests/form_filter.qgs.cfg | 39 +- 2 files changed, 518 insertions(+), 321 deletions(-) diff --git a/tests/qgis-projects/tests/form_filter.qgs b/tests/qgis-projects/tests/form_filter.qgs index 1d5b6c850f..46dcbae370 100644 --- a/tests/qgis-projects/tests/form_filter.qgs +++ b/tests/qgis-projects/tests/form_filter.qgs @@ -1,11 +1,10 @@ - + - - - + + - + PROJCRS["RGF93 v1 / Lambert-93",BASEGEOGCRS["RGF93 v1",DATUM["Reseau Geodesique Francais 1993 v1",ELLIPSOID["GRS 1980",6378137,298.257222101,LENGTHUNIT["metre",1]]],PRIMEM["Greenwich",0,ANGLEUNIT["degree",0.0174532925199433]],ID["EPSG",4171]],CONVERSION["Lambert-93",METHOD["Lambert Conic Conformal (2SP)",ID["EPSG",9802]],PARAMETER["Latitude of false origin",46.5,ANGLEUNIT["degree",0.0174532925199433],ID["EPSG",8821]],PARAMETER["Longitude of false origin",3,ANGLEUNIT["degree",0.0174532925199433],ID["EPSG",8822]],PARAMETER["Latitude of 1st standard parallel",49,ANGLEUNIT["degree",0.0174532925199433],ID["EPSG",8823]],PARAMETER["Latitude of 2nd standard parallel",44,ANGLEUNIT["degree",0.0174532925199433],ID["EPSG",8824]],PARAMETER["Easting at false origin",700000,LENGTHUNIT["metre",1],ID["EPSG",8826]],PARAMETER["Northing at false origin",6600000,LENGTHUNIT["metre",1],ID["EPSG",8827]]],CS[Cartesian,2],AXIS["easting (X)",east,ORDER[1],LENGTHUNIT["metre",1]],AXIS["northing (Y)",north,ORDER[2],LENGTHUNIT["metre",1]],USAGE[SCOPE["Engineering survey, topographic mapping."],AREA["France - onshore and offshore, mainland and Corsica."],BBOX[41.15,-9.86,51.56,10.38]],ID["EPSG",2154]] +proj=lcc +lat_0=46.5 +lon_0=3 +lat_1=49 +lat_2=44 +x_0=700000 +y_0=6600000 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs 145 @@ -17,6 +16,7 @@ false + @@ -38,8 +38,8 @@ - + @@ -58,7 +58,7 @@ 0 - + PROJCRS["RGF93 v1 / Lambert-93",BASEGEOGCRS["RGF93 v1",DATUM["Reseau Geodesique Francais 1993 v1",ELLIPSOID["GRS 1980",6378137,298.257222101,LENGTHUNIT["metre",1]]],PRIMEM["Greenwich",0,ANGLEUNIT["degree",0.0174532925199433]],ID["EPSG",4171]],CONVERSION["Lambert-93",METHOD["Lambert Conic Conformal (2SP)",ID["EPSG",9802]],PARAMETER["Latitude of false origin",46.5,ANGLEUNIT["degree",0.0174532925199433],ID["EPSG",8821]],PARAMETER["Longitude of false origin",3,ANGLEUNIT["degree",0.0174532925199433],ID["EPSG",8822]],PARAMETER["Latitude of 1st standard parallel",49,ANGLEUNIT["degree",0.0174532925199433],ID["EPSG",8823]],PARAMETER["Latitude of 2nd standard parallel",44,ANGLEUNIT["degree",0.0174532925199433],ID["EPSG",8824]],PARAMETER["Easting at false origin",700000,LENGTHUNIT["metre",1],ID["EPSG",8826]],PARAMETER["Northing at false origin",6600000,LENGTHUNIT["metre",1],ID["EPSG",8827]]],CS[Cartesian,2],AXIS["easting (X)",east,ORDER[1],LENGTHUNIT["metre",1]],AXIS["northing (Y)",north,ORDER[2],LENGTHUNIT["metre",1]],USAGE[SCOPE["Engineering survey, topographic mapping."],AREA["France - onshore and offshore, mainland and Corsica."],BBOX[41.15,-9.86,51.56,10.38]],ID["EPSG",2154]] +proj=lcc +lat_0=46.5 +lon_0=3 +lat_1=49 +lat_2=44 +x_0=700000 +y_0=6600000 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs 145 @@ -87,8 +87,7 @@ - - + Annotations_1b82beaa_5a0a_445e_b2ad_d7288106e20f @@ -96,7 +95,7 @@ - + 0 @@ -116,10 +115,11 @@ + - + 0 @@ -134,23 +134,32 @@ + + 1 + 1 + 1 + 0 + + + + 1 0 - + 639.26738227050134356 -681 - 662 + 1062.67599971713116247 -291.40733550341667524 -1.3590046014910826 -5.98787712656267779 - -1.35872929593759695 - -5.98544315188782594 + -1.35623708791804964 + -5.98530531082845574 form_filter_8bfd580f_2848_4bd4_80cf_facb270a9af5 service='lizmapdb' sslmode=disable key='id' estimatedmetadata=true srid=2154 type=Point checkPrimaryKeyUnicity='1' table="tests_projects"."form_filter" (geom) @@ -160,7 +169,7 @@ form filter (à) - + PROJCRS["RGF93 v1 / Lambert-93",BASEGEOGCRS["RGF93 v1",DATUM["Reseau Geodesique Francais 1993 v1",ELLIPSOID["GRS 1980",6378137,298.257222101,LENGTHUNIT["metre",1]]],PRIMEM["Greenwich",0,ANGLEUNIT["degree",0.0174532925199433]],ID["EPSG",4171]],CONVERSION["Lambert-93",METHOD["Lambert Conic Conformal (2SP)",ID["EPSG",9802]],PARAMETER["Latitude of false origin",46.5,ANGLEUNIT["degree",0.0174532925199433],ID["EPSG",8821]],PARAMETER["Longitude of false origin",3,ANGLEUNIT["degree",0.0174532925199433],ID["EPSG",8822]],PARAMETER["Latitude of 1st standard parallel",49,ANGLEUNIT["degree",0.0174532925199433],ID["EPSG",8823]],PARAMETER["Latitude of 2nd standard parallel",44,ANGLEUNIT["degree",0.0174532925199433],ID["EPSG",8824]],PARAMETER["Easting at false origin",700000,LENGTHUNIT["metre",1],ID["EPSG",8826]],PARAMETER["Northing at false origin",6600000,LENGTHUNIT["metre",1],ID["EPSG",8827]]],CS[Cartesian,2],AXIS["easting (X)",east,ORDER[1],LENGTHUNIT["metre",1]],AXIS["northing (Y)",north,ORDER[2],LENGTHUNIT["metre",1]],USAGE[SCOPE["Engineering survey, topographic mapping."],AREA["France - onshore and offshore, mainland and Corsica."],BBOX[41.15,-9.86,51.56,10.38]],ID["EPSG",2154]] +proj=lcc +lat_0=46.5 +lon_0=3 +lat_1=49 +lat_2=44 +x_0=700000 +y_0=6600000 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs 145 @@ -189,10 +198,11 @@ + - + PROJCRS["RGF93 v1 / Lambert-93",BASEGEOGCRS["RGF93 v1",DATUM["Reseau Geodesique Francais 1993 v1",ELLIPSOID["GRS 1980",6378137,298.257222101,LENGTHUNIT["metre",1]]],PRIMEM["Greenwich",0,ANGLEUNIT["degree",0.0174532925199433]],ID["EPSG",4171]],CONVERSION["Lambert-93",METHOD["Lambert Conic Conformal (2SP)",ID["EPSG",9802]],PARAMETER["Latitude of false origin",46.5,ANGLEUNIT["degree",0.0174532925199433],ID["EPSG",8821]],PARAMETER["Longitude of false origin",3,ANGLEUNIT["degree",0.0174532925199433],ID["EPSG",8822]],PARAMETER["Latitude of 1st standard parallel",49,ANGLEUNIT["degree",0.0174532925199433],ID["EPSG",8823]],PARAMETER["Latitude of 2nd standard parallel",44,ANGLEUNIT["degree",0.0174532925199433],ID["EPSG",8824]],PARAMETER["Easting at false origin",700000,LENGTHUNIT["metre",1],ID["EPSG",8826]],PARAMETER["Northing at false origin",6600000,LENGTHUNIT["metre",1],ID["EPSG",8827]]],CS[Cartesian,2],AXIS["easting (X)",east,ORDER[1],LENGTHUNIT["metre",1]],AXIS["northing (Y)",north,ORDER[2],LENGTHUNIT["metre",1]],USAGE[SCOPE["Engineering survey, topographic mapping."],AREA["France - onshore and offshore, mainland and Corsica."],BBOX[41.15,-9.86,51.56,10.38]],ID["EPSG",2154]] +proj=lcc +lat_0=46.5 +lon_0=3 +lat_1=49 +lat_2=44 +x_0=700000 +y_0=6600000 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs 145 @@ -236,13 +246,146 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + - + @@ -250,7 +393,7 @@ - + @@ -272,25 +415,6 @@ - - - - - - - - - - - - - - - - - - - @@ -300,7 +424,7 @@ - + @@ -308,7 +432,7 @@ - + @@ -330,25 +454,45 @@ - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -360,7 +504,7 @@ - + @@ -368,7 +512,7 @@ - + @@ -390,25 +534,6 @@ - - - - - - - - - - - - - - - - - - - @@ -422,14 +547,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + @@ -437,7 +606,7 @@ - + @@ -459,25 +628,6 @@ - - - - - - - - - - - - - - - - - - - @@ -487,7 +637,7 @@ - + @@ -495,7 +645,7 @@ - + @@ -509,17 +659,6 @@ - - - - - - - - - - - @@ -541,8 +680,8 @@ - - + + @@ -562,7 +701,7 @@ - + @@ -584,6 +723,7 @@ + @@ -595,10 +735,10 @@ 1 - + - + @@ -606,7 +746,7 @@ - + @@ -636,33 +776,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -691,14 +804,14 @@ - + - + @@ -710,6 +823,10 @@ + + + + @@ -775,9 +892,9 @@ def my_form_open(dialog, layer, feature): "id" - + - + 515.92221776402072919 -833.24502244259929284 @@ -798,7 +915,7 @@ def my_form_open(dialog, layer, feature): form_filter_child_bus_stops - + PROJCRS["RGF93 v1 / Lambert-93",BASEGEOGCRS["RGF93 v1",DATUM["Reseau Geodesique Francais 1993 v1",ELLIPSOID["GRS 1980",6378137,298.257222101,LENGTHUNIT["metre",1]]],PRIMEM["Greenwich",0,ANGLEUNIT["degree",0.0174532925199433]],ID["EPSG",4171]],CONVERSION["Lambert-93",METHOD["Lambert Conic Conformal (2SP)",ID["EPSG",9802]],PARAMETER["Latitude of false origin",46.5,ANGLEUNIT["degree",0.0174532925199433],ID["EPSG",8821]],PARAMETER["Longitude of false origin",3,ANGLEUNIT["degree",0.0174532925199433],ID["EPSG",8822]],PARAMETER["Latitude of 1st standard parallel",49,ANGLEUNIT["degree",0.0174532925199433],ID["EPSG",8823]],PARAMETER["Latitude of 2nd standard parallel",44,ANGLEUNIT["degree",0.0174532925199433],ID["EPSG",8824]],PARAMETER["Easting at false origin",700000,LENGTHUNIT["metre",1],ID["EPSG",8826]],PARAMETER["Northing at false origin",6600000,LENGTHUNIT["metre",1],ID["EPSG",8827]]],CS[Cartesian,2],AXIS["easting (X)",east,ORDER[1],LENGTHUNIT["metre",1]],AXIS["northing (Y)",north,ORDER[2],LENGTHUNIT["metre",1]],USAGE[SCOPE["Engineering survey, topographic mapping."],AREA["France - onshore and offshore, mainland and Corsica."],BBOX[41.15,-9.86,51.56,10.38]],ID["EPSG",2154]] +proj=lcc +lat_0=46.5 +lon_0=3 +lat_1=49 +lat_2=44 +x_0=700000 +y_0=6600000 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs 145 @@ -827,10 +944,11 @@ def my_form_open(dialog, layer, feature): + - + PROJCRS["RGF93 v1 / Lambert-93",BASEGEOGCRS["RGF93 v1",DATUM["Reseau Geodesique Francais 1993 v1",ELLIPSOID["GRS 1980",6378137,298.257222101,LENGTHUNIT["metre",1]]],PRIMEM["Greenwich",0,ANGLEUNIT["degree",0.0174532925199433]],ID["EPSG",4171]],CONVERSION["Lambert-93",METHOD["Lambert Conic Conformal (2SP)",ID["EPSG",9802]],PARAMETER["Latitude of false origin",46.5,ANGLEUNIT["degree",0.0174532925199433],ID["EPSG",8821]],PARAMETER["Longitude of false origin",3,ANGLEUNIT["degree",0.0174532925199433],ID["EPSG",8822]],PARAMETER["Latitude of 1st standard parallel",49,ANGLEUNIT["degree",0.0174532925199433],ID["EPSG",8823]],PARAMETER["Latitude of 2nd standard parallel",44,ANGLEUNIT["degree",0.0174532925199433],ID["EPSG",8824]],PARAMETER["Easting at false origin",700000,LENGTHUNIT["metre",1],ID["EPSG",8826]],PARAMETER["Northing at false origin",6600000,LENGTHUNIT["metre",1],ID["EPSG",8827]]],CS[Cartesian,2],AXIS["easting (X)",east,ORDER[1],LENGTHUNIT["metre",1]],AXIS["northing (Y)",north,ORDER[2],LENGTHUNIT["metre",1]],USAGE[SCOPE["Engineering survey, topographic mapping."],AREA["France - onshore and offshore, mainland and Corsica."],BBOX[41.15,-9.86,51.56,10.38]],ID["EPSG",2154]] +proj=lcc +lat_0=46.5 +lon_0=3 +lat_1=49 +lat_2=44 +x_0=700000 +y_0=6600000 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs 145 @@ -874,13 +992,145 @@ def my_form_open(dialog, layer, feature): + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - + @@ -888,7 +1138,7 @@ def my_form_open(dialog, layer, feature): - + @@ -910,25 +1160,6 @@ def my_form_open(dialog, layer, feature): - - - - - - - - - - - - - - - - - - - @@ -938,7 +1169,7 @@ def my_form_open(dialog, layer, feature): - + @@ -946,7 +1177,7 @@ def my_form_open(dialog, layer, feature): - + @@ -968,25 +1199,6 @@ def my_form_open(dialog, layer, feature): - - - - - - - - - - - - - - - - - - - @@ -998,7 +1210,7 @@ def my_form_open(dialog, layer, feature): - + @@ -1006,7 +1218,7 @@ def my_form_open(dialog, layer, feature): - + @@ -1028,25 +1240,6 @@ def my_form_open(dialog, layer, feature): - - - - - - - - - - - - - - - - - - - @@ -1060,14 +1253,17 @@ def my_form_open(dialog, layer, feature): + + + - + - + - + @@ -1075,7 +1271,7 @@ def my_form_open(dialog, layer, feature): - + @@ -1097,25 +1293,6 @@ def my_form_open(dialog, layer, feature): - - - - - - - - - - - - - - - - - - - @@ -1125,7 +1302,7 @@ def my_form_open(dialog, layer, feature): - + @@ -1133,7 +1310,7 @@ def my_form_open(dialog, layer, feature): - + @@ -1147,17 +1324,6 @@ def my_form_open(dialog, layer, feature): - - - - - - - - - - - @@ -1179,8 +1345,8 @@ def my_form_open(dialog, layer, feature): - - + + @@ -1200,7 +1366,7 @@ def my_form_open(dialog, layer, feature): - + @@ -1232,10 +1398,10 @@ def my_form_open(dialog, layer, feature): 1 - + - + @@ -1243,7 +1409,7 @@ def my_form_open(dialog, layer, feature): - + @@ -1273,33 +1439,6 @@ def my_form_open(dialog, layer, feature): - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -1332,21 +1471,21 @@ def my_form_open(dialog, layer, feature): - + - + - + @@ -1359,6 +1498,11 @@ def my_form_open(dialog, layer, feature): + + + + + @@ -1431,7 +1575,7 @@ def my_form_open(dialog, layer, feature): "id" - + @@ -1439,14 +1583,7 @@ def my_form_open(dialog, layer, feature): - - - - - - 1 - true - + 2 @@ -1478,6 +1615,7 @@ def my_form_open(dialog, layer, feature): 16 30 2.5 + false false false 0 @@ -1621,16 +1759,21 @@ def my_form_open(dialog, layer, feature): + + + 2021-04-12T08:10:15 + + - - + + PROJCRS["RGF93 v1 / Lambert-93",BASEGEOGCRS["RGF93 v1",DATUM["Reseau Geodesique Francais 1993 v1",ELLIPSOID["GRS 1980",6378137,298.257222101,LENGTHUNIT["metre",1]]],PRIMEM["Greenwich",0,ANGLEUNIT["degree",0.0174532925199433]],ID["EPSG",4171]],CONVERSION["Lambert-93",METHOD["Lambert Conic Conformal (2SP)",ID["EPSG",9802]],PARAMETER["Latitude of false origin",46.5,ANGLEUNIT["degree",0.0174532925199433],ID["EPSG",8821]],PARAMETER["Longitude of false origin",3,ANGLEUNIT["degree",0.0174532925199433],ID["EPSG",8822]],PARAMETER["Latitude of 1st standard parallel",49,ANGLEUNIT["degree",0.0174532925199433],ID["EPSG",8823]],PARAMETER["Latitude of 2nd standard parallel",44,ANGLEUNIT["degree",0.0174532925199433],ID["EPSG",8824]],PARAMETER["Easting at false origin",700000,LENGTHUNIT["metre",1],ID["EPSG",8826]],PARAMETER["Northing at false origin",6600000,LENGTHUNIT["metre",1],ID["EPSG",8827]]],CS[Cartesian,2],AXIS["easting (X)",east,ORDER[1],LENGTHUNIT["metre",1]],AXIS["northing (Y)",north,ORDER[2],LENGTHUNIT["metre",1]],USAGE[SCOPE["Engineering survey, topographic mapping."],AREA["France - onshore and offshore, mainland and Corsica."],BBOX[41.15,-9.86,51.56,10.38]],ID["EPSG",2154]] +proj=lcc +lat_0=46.5 +lon_0=3 +lat_1=49 +lat_2=44 +x_0=700000 +y_0=6600000 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs 145 @@ -1643,19 +1786,58 @@ def my_form_open(dialog, layer, feature): + + + - + + + + + + + - + + + + + + + + + + + + + + + + + + GEOGCRS["WGS 84",ENSEMBLE["World Geodetic System 1984 ensemble",MEMBER["World Geodetic System 1984 (Transit)"],MEMBER["World Geodetic System 1984 (G730)"],MEMBER["World Geodetic System 1984 (G873)"],MEMBER["World Geodetic System 1984 (G1150)"],MEMBER["World Geodetic System 1984 (G1674)"],MEMBER["World Geodetic System 1984 (G1762)"],MEMBER["World Geodetic System 1984 (G2139)"],ELLIPSOID["WGS 84",6378137,298.257223563,LENGTHUNIT["metre",1]],ENSEMBLEACCURACY[2.0]],PRIMEM["Greenwich",0,ANGLEUNIT["degree",0.0174532925199433]],CS[ellipsoidal,2],AXIS["geodetic latitude (Lat)",north,ORDER[1],ANGLEUNIT["degree",0.0174532925199433]],AXIS["geodetic longitude (Lon)",east,ORDER[2],ANGLEUNIT["degree",0.0174532925199433]],USAGE[SCOPE["Horizontal component of 3D system."],AREA["World."],BBOX[-90,-180,90,180]],ID["EPSG",4326]] + +proj=longlat +datum=WGS84 +no_defs + 3452 + 4326 + EPSG:4326 + WGS 84 + longlat + EPSG:7030 + true + + - + + + + \ No newline at end of file diff --git a/tests/qgis-projects/tests/form_filter.qgs.cfg b/tests/qgis-projects/tests/form_filter.qgs.cfg index 5dc6b4901a..661985e41d 100644 --- a/tests/qgis-projects/tests/form_filter.qgs.cfg +++ b/tests/qgis-projects/tests/form_filter.qgs.cfg @@ -1,15 +1,17 @@ { "metadata": { - "qgis_desktop_version": 32216, - "lizmap_plugin_version_str": "3.17.1-alpha", - "lizmap_plugin_version": 31701, - "lizmap_web_client_target_version": 30700, + "qgis_desktop_version": 33413, + "lizmap_plugin_version_str": "4.4.6-alpha", + "lizmap_plugin_version": 40406, + "lizmap_web_client_target_version": 31000, "lizmap_web_client_target_status": "Dev", "instance_target_url": "http://localhost:8130/", - "instance_target_repository": "intranet", - "project_valid": true + "instance_target_repository": "intranet" + }, + "warnings": {}, + "debug": { + "total_time": 0.49300000000000005 }, - "warnings": [], "options": { "projection": { "proj4": "+proj=lcc +lat_0=46.5 +lon_0=3 +lat_1=49 +lat_2=44 +x_0=700000 +y_0=6600000 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs", @@ -31,16 +33,19 @@ ], "minScale": 10000, "maxScale": 500000, + "use_native_zoom_levels": false, + "hide_numeric_scale_value": false, "initialExtent": [ - -1027.6760398981332, - -1652.927886247878, - 2893.747729202038, - 556.2721137521228 + -1027.676, + -1652.9279, + 2893.7477, + 556.2721 ], "popupLocation": "dock", "pointTolerance": 25, "lineTolerance": 10, "polygonTolerance": 5, + "automatic_permalink": true, "tmTimeFrameSize": 10, "tmTimeFrameType": "seconds", "tmAnimationFrameLength": 1000, @@ -58,7 +63,7 @@ "extent": [ 639.2673822705013, -681.0, - 662.0, + 1062.6759997171312, -291.4073355034167 ], "crs": "EPSG:2154", @@ -171,6 +176,16 @@ "format": "select", "order": 0, "provider": "postgres" + }, + "1": { + "layerId": "form_filter_8bfd580f_2848_4bd4_80cf_facb270a9af5", + "title": "autocomplete", + "type": "text", + "field": "label", + "start_field": "id", + "end_field": "id", + "order": 1, + "provider": "postgres" } } } From a19a81d0743466782ed1af452f495e066a5fe65b Mon Sep 17 00:00:00 2001 From: nboisteault Date: Mon, 2 Dec 2024 17:50:19 +0100 Subject: [PATCH 3/8] Form filter: adjust autocomplete parameters to avoid too much requests --- assets/src/legacy/filter.js | 35 +++++++++++++++++------------------ 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/assets/src/legacy/filter.js b/assets/src/legacy/filter.js index 9fafe9f45a..8c7d60bf3b 100644 --- a/assets/src/legacy/filter.js +++ b/assets/src/legacy/filter.js @@ -392,7 +392,6 @@ var lizLayerFilterTool = function () { * @param field_item */ function textFormInput(field_item) { - // Ajout des données d'autocompletion var field = field_item['field']; var sdata = { request: 'getUniqueValues', @@ -401,17 +400,17 @@ var lizLayerFilterTool = function () { filter: '' }; - var html = ''; - html += getFormFieldHeader(field_item); - html += '
' - html += ''; - html += '
' - html += getFormFieldFooter(field_item); + var html = ''; + html += getFormFieldHeader(field_item); + html += '
' + html += ''; + html += '
' + html += getFormFieldFooter(field_item); - $("#filter-field-order" + String(field_item.order)).append(html); - addFieldEvents(field_item); + $("#filter-field-order" + String(field_item.order)).append(html); + addFieldEvents(field_item); - $("#liz-filter-field-text" + lizMap.cleanName(field_item.title)).autocomplete({ + $("#liz-filter-field-text" + lizMap.cleanName(field_item.title)).autocomplete({ source: function (request, response) { const autocompleteFilter = `"${field}" ILIKE '%${request.term}%' `; if (!globalThis['filterConfigData'].filter) { @@ -436,14 +435,14 @@ var lizLayerFilterTool = function () { response(autocompleteData); }); }, - autoFocus: false, // do not autofocus, because this prevents from searching with LIKE - delay: 200, - minLength: 2, - select: function (event, ui) { - $(this).val(ui.item.value); - $(this).change(); - } - }); + autoFocus: false, // do not autofocus, because this prevents from searching with LIKE + delay: 500, + minLength: 3, + select: function (event, ui) { + $(this).val(ui.item.value); + $(this).blur(); + } + }); } // Get the HTML form element for the uniqueValues field type From 6f17feb1a7803716394c551031051ea18c3637a4 Mon Sep 17 00:00:00 2001 From: nboisteault Date: Tue, 3 Dec 2024 09:58:22 +0100 Subject: [PATCH 4/8] e2e: migrate form-filter test from cypress to playwright --- .../integration/form-filter-ghaction.js | 70 ------------------- tests/end2end/playwright/form-filter.spec.js | 66 +++++++++++++++++ tests/qgis-projects/tests/form_filter.qgs | 56 ++++++++++++--- tests/qgis-projects/tests/form_filter.qgs.cfg | 19 +++-- tests/qgis-projects/tests/tests_dataset.sql | 5 +- 5 files changed, 131 insertions(+), 85 deletions(-) delete mode 100644 tests/end2end/cypress/integration/form-filter-ghaction.js create mode 100644 tests/end2end/playwright/form-filter.spec.js diff --git a/tests/end2end/cypress/integration/form-filter-ghaction.js b/tests/end2end/cypress/integration/form-filter-ghaction.js deleted file mode 100644 index fe9fe6955e..0000000000 --- a/tests/end2end/cypress/integration/form-filter-ghaction.js +++ /dev/null @@ -1,70 +0,0 @@ -describe('Form filter', () => { - it('Test the form filter with checkboxes', function () { - // There is randomly an error in the console when attribute table is resized - // This avoid test to fail - Cypress.on('uncaught:exception', (err, runnable) => { - // returning false here prevents Cypress from - // failing the test - return false - }) - - cy.visit('/index.php/view/map/?repository=testsrepository&project=form_filter') - cy.get('#button-filter').click() - - cy.intercept('*REQUEST=GetMap*', - { middleware: true }, - (req) => { - req.on('before:response', (res) => { - // force all API responses to not be cached - // It is needed when launching tests multiple time in headed mode - res.headers['cache-control'] = 'no-store' - }) - }).as('getMap') - - const combo = '#liz-filter-field-test_filter' - const countFeature = '#liz-filter-item-layer-total-count' - - // Default - cy.get('#liz-filter-item-layer-total-count').should('have.text', '2') - cy.get(combo + ' > option:nth-child(1)').should('have.text', ' --- ') - - // Open the attribute tables for the 2 layers - cy.get('button[value="form_filter__a_"].btn-open-attribute-layer').click({ force: true }) - cy.get('button[value="form_filter_child_bus_stops"].btn-open-attribute-layer').click({ force: true }) - - // Select the first one - cy.get(combo).select('_uvres_d_art_et_monuments_de_l_espace_urbain') - cy.get(countFeature).should('have.text', '1') - - // Wait for the cascading filter to happen - cy.wait(300) - cy.wait('@getMap') - - // Check the attribute table shows only one line - cy.get('#attribute-layer-table-form_filter__a_ tbody tr').should('have.length', 1) - - // Check the child features are filtered too (3 children) - cy.get('#attribute-layer-table-form_filter_child_bus_stops tbody tr').should('have.length', 3) - - // Reset - cy.get('#liz-filter-unfilter').click() - cy.get(countFeature).should('have.text', '2') - - cy.wait('@getMap') - - // Select the second one - cy.get(combo).select('simple_label') - cy.get(countFeature).should('have.text', '1') - - // Check the attribute table shows only one line - cy.wait('@getMap') - cy.get('#attribute-layer-table-form_filter__a_ tbody tr').should('have.length', 1) - - // Check the child features are filtered too (2 children) - cy.get('#attribute-layer-table-form_filter_child_bus_stops tbody tr').should('have.length', 2) - - // Disable combobox - cy.get('div#liz-filter-box-test_filter button.btn-primary:nth-child(2)').click() - cy.get(countFeature).should('have.text', '2') - }) -}) diff --git a/tests/end2end/playwright/form-filter.spec.js b/tests/end2end/playwright/form-filter.spec.js new file mode 100644 index 0000000000..94d5e47180 --- /dev/null +++ b/tests/end2end/playwright/form-filter.spec.js @@ -0,0 +1,66 @@ +// @ts-check +import { test, expect } from '@playwright/test'; +import { gotoMap } from './globals'; + +test.describe('Attribute table', () => { + test.beforeEach(async ({ page }) => { + const url = '/index.php/view/map/?repository=testsrepository&project=form_filter'; + await gotoMap(url, page); + await page.locator('#button-filter').click(); + }); + + test('Form filter with combobox', async ({ page }) => { + const getMapPromise = page.waitForRequest(/GetMap/); + + const combo = '#liz-filter-field-test_filter'; + const countFeature = '#liz-filter-item-layer-total-count'; + + // Default + await expect(page.locator('#liz-filter-item-layer-total-count')).toHaveText('4'); + await expect(page.locator(combo + ' > option:nth-child(1)')).toHaveText(' --- '); + + // Open the attribute tables for the 2 layers + await page.locator('#button-attributeLayers').click(); + await page.locator('button[value="form_filter__a_"].btn-open-attribute-layer').click({ force: true }); + await page.locator('#nav-tab-attribute-summary').click(); + await page.locator('button[value="form_filter_child_bus_stops"].btn-open-attribute-layer').click({ force: true }); + + // Select the first one + await page.locator(combo).selectOption('_uvres_d_art_et_monuments_de_l_espace_urbain'); + await expect(page.locator(countFeature)).toHaveText('1'); + + await getMapPromise; + + // Check the attribute table shows only one line + await page.locator('#nav-tab-attribute-layer-form_filter__a_').click({ force: true }); + await expect(page.locator('#attribute-layer-table-form_filter__a_ tbody tr')).toHaveCount(1); + + // Check the child features are filtered too (3 children) + await page.locator('#nav-tab-attribute-layer-form_filter_child_bus_stops').click({ force: true }); + await expect(page.locator('#attribute-layer-table-form_filter_child_bus_stops tbody tr')).toHaveCount(3); + + // Reset + page.locator('#liz-filter-unfilter').click(); + await expect(page.locator(countFeature)).toHaveText('4'); + + await getMapPromise; + + // Select the second one + await page.locator(combo).selectOption('simple_label'); + await expect(page.locator(countFeature)).toHaveText('1'); + + // Check the attribute table shows only one line + await getMapPromise; + await page.locator('#nav-tab-attribute-layer-form_filter__a_').click({ force: true }); + await expect(page.locator('#attribute-layer-table-form_filter__a_ tbody tr')).toHaveCount(1); + + // Check the child features are filtered too (2 children) + await page.locator('#nav-tab-attribute-layer-form_filter_child_bus_stops').click({ force: true }); + await expect(page.locator('#attribute-layer-table-form_filter_child_bus_stops tbody tr')).toHaveCount(2); + + // Disable combobox + await page.locator('div#liz-filter-box-test_filter button.btn-primary:nth-child(2)').click(); + await expect(page.locator(countFeature)).toHaveText('4'); + }); +}); + diff --git a/tests/qgis-projects/tests/form_filter.qgs b/tests/qgis-projects/tests/form_filter.qgs index 46dcbae370..3a11dfe20a 100644 --- a/tests/qgis-projects/tests/form_filter.qgs +++ b/tests/qgis-projects/tests/form_filter.qgs @@ -1,4 +1,4 @@ - + @@ -38,8 +38,8 @@ - + @@ -382,7 +382,8 @@ - + + @@ -502,6 +503,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -701,7 +741,7 @@ - + @@ -722,8 +762,6 @@ - - @@ -846,7 +884,7 @@ - + @@ -1366,7 +1404,7 @@ def my_form_open(dialog, layer, feature): - + @@ -1786,7 +1824,7 @@ def my_form_open(dialog, layer, feature): - + diff --git a/tests/qgis-projects/tests/form_filter.qgs.cfg b/tests/qgis-projects/tests/form_filter.qgs.cfg index 661985e41d..4745549fc6 100644 --- a/tests/qgis-projects/tests/form_filter.qgs.cfg +++ b/tests/qgis-projects/tests/form_filter.qgs.cfg @@ -10,7 +10,7 @@ }, "warnings": {}, "debug": { - "total_time": 0.49300000000000005 + "total_time": 0.657 }, "options": { "projection": { @@ -45,7 +45,7 @@ "pointTolerance": 25, "lineTolerance": 10, "polygonTolerance": 5, - "automatic_permalink": true, + "automatic_permalink": false, "tmTimeFrameSize": 10, "tmTimeFrameType": "seconds", "tmAnimationFrameLength": 1000, @@ -169,22 +169,31 @@ }, "formFilterLayers": { "0": { + "layerId": "form_filter_8bfd580f_2848_4bd4_80cf_facb270a9af5", + "title": "IDs", + "type": "numeric", + "start_field": "id", + "end_field": "id", + "order": 0, + "provider": "postgres" + }, + "1": { "layerId": "form_filter_8bfd580f_2848_4bd4_80cf_facb270a9af5", "title": "test_filter", "type": "uniquevalues", "field": "label", "format": "select", - "order": 0, + "order": 1, "provider": "postgres" }, - "1": { + "2": { "layerId": "form_filter_8bfd580f_2848_4bd4_80cf_facb270a9af5", "title": "autocomplete", "type": "text", "field": "label", "start_field": "id", "end_field": "id", - "order": 1, + "order": 2, "provider": "postgres" } } diff --git a/tests/qgis-projects/tests/tests_dataset.sql b/tests/qgis-projects/tests/tests_dataset.sql index d9c7f2872c..03b7821e0c 100644 --- a/tests/qgis-projects/tests/tests_dataset.sql +++ b/tests/qgis-projects/tests/tests_dataset.sql @@ -3408,6 +3408,8 @@ COPY tests_projects.form_edition_vr_point (id, code_without_exp, code_with_simpl COPY tests_projects.form_filter (id, label, geom) FROM stdin; 1 simple label 01010000206A080000ABDA509923FA8340CA9A3B72843672C0 2 Œuvres d'art et monuments de l'espace urbain 01010000206A0800000000000000B0844000000000004885C0 +3 monuments 01010000206A080000BC144539B49A9040D88E7FA8F0C072C0 +4 monuments 2 01010000206A080000BA9926EC46DC9140DD5B3AA4A0AD80C0 \. @@ -4123,7 +4125,7 @@ SELECT pg_catalog.setval('tests_projects.form_filter_child_bus_stops_id_seq', 5, -- Name: form_filter_id_seq; Type: SEQUENCE SET; Schema: tests_projects; Owner: - -- -SELECT pg_catalog.setval('tests_projects.form_filter_id_seq', 2, true); +SELECT pg_catalog.setval('tests_projects.form_filter_id_seq', 4, true); -- @@ -5050,3 +5052,4 @@ ALTER TABLE ONLY tests_projects.tramway_pivot -- -- PostgreSQL database dump complete -- + From 19d793f1ab96514a865c849c11960de2b8f7caa9 Mon Sep 17 00:00:00 2001 From: nboisteault Date: Tue, 3 Dec 2024 10:49:42 +0100 Subject: [PATCH 5/8] e2e: form filter with autocomplete list based on previous filters --- tests/end2end/playwright/form-filter.spec.js | 26 ++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/tests/end2end/playwright/form-filter.spec.js b/tests/end2end/playwright/form-filter.spec.js index 94d5e47180..984d284260 100644 --- a/tests/end2end/playwright/form-filter.spec.js +++ b/tests/end2end/playwright/form-filter.spec.js @@ -62,5 +62,31 @@ test.describe('Attribute table', () => { await page.locator('div#liz-filter-box-test_filter button.btn-primary:nth-child(2)').click(); await expect(page.locator(countFeature)).toHaveText('4'); }); + + test('Form filter with autocomplete', async ({ page }) => { + await page.locator('#liz-filter-field-textautocomplete').fill('mon'); + + // Assert autocomplete list has 3 values + await expect(page.locator('#ui-id-2 .ui-menu-item')).toHaveCount(3); + + await page.locator('#liz-filter-field-textautocomplete').fill(''); + + // Filter by ID then assert autocomplete list has now 2 values + // when filling 'mon' in the autocomplete field + await page.locator('#liz-filter-field-max-numericIDs').fill('3'); + await page.locator('#liz-filter-field-textautocomplete').fill('mon'); + await expect(page.locator('#ui-id-2 .ui-menu-item')).toHaveCount(2); + + // Reset + await page.locator('#liz-filter-field-textautocomplete').fill(''); + await page.locator('#liz-filter-unfilter').click(); + + // Filter by combobox then assert autocomplete list has now 1 value + // when filling 'mon' in the autocomplete field + await page.locator('#liz-filter-field-test_filter').selectOption('monuments'); + await page.locator('#liz-filter-field-textautocomplete').fill('mon'); + await expect(page.locator('#ui-id-2 .ui-menu-item')).toHaveCount(1); + await expect(page.locator('#ui-id-2 .ui-menu-item div')).toHaveText('monuments'); + }); }); From bae34404e31314cd1ae6bd3f7a118921710359e7 Mon Sep 17 00:00:00 2001 From: nboisteault Date: Tue, 3 Dec 2024 12:45:02 +0100 Subject: [PATCH 6/8] e2e: form filter, migrate manuel test to playwright --- tests/end2end/playwright/form-filter.spec.js | 20 +++++++++++++++++--- tests/qgis-projects/tests/form_filter.md | 12 ------------ 2 files changed, 17 insertions(+), 15 deletions(-) delete mode 100644 tests/qgis-projects/tests/form_filter.md diff --git a/tests/end2end/playwright/form-filter.spec.js b/tests/end2end/playwright/form-filter.spec.js index 984d284260..6f1a2692b4 100644 --- a/tests/end2end/playwright/form-filter.spec.js +++ b/tests/end2end/playwright/form-filter.spec.js @@ -10,7 +10,7 @@ test.describe('Attribute table', () => { }); test('Form filter with combobox', async ({ page }) => { - const getMapPromise = page.waitForRequest(/GetMap/); + let getMapPromise = page.waitForRequest(/GetMap/); const combo = '#liz-filter-field-test_filter'; const countFeature = '#liz-filter-item-layer-total-count'; @@ -29,7 +29,13 @@ test.describe('Attribute table', () => { await page.locator(combo).selectOption('_uvres_d_art_et_monuments_de_l_espace_urbain'); await expect(page.locator(countFeature)).toHaveText('1'); - await getMapPromise; + let getMapRequest = await getMapPromise; + // Re-send the request with additionnal echo param to retrieve the WMS Request + let echoGetMap = await page.request.get(getMapRequest.url() + '&__echo__'); + let originalUrl = decodeURIComponent(await echoGetMap.text()); + let urlObj = new URLSearchParams((new URL(originalUrl).search)); + + expect(urlObj.get('filter')).toBe('form_filter_layer:"id" IN ( 2 ) '); // Check the attribute table shows only one line await page.locator('#nav-tab-attribute-layer-form_filter__a_').click({ force: true }); @@ -40,10 +46,18 @@ test.describe('Attribute table', () => { await expect(page.locator('#attribute-layer-table-form_filter_child_bus_stops tbody tr')).toHaveCount(3); // Reset + getMapPromise = page.waitForRequest(/GetMap/); page.locator('#liz-filter-unfilter').click(); await expect(page.locator(countFeature)).toHaveText('4'); - await getMapPromise; + getMapRequest = await getMapPromise; + + // Re-send the request with additionnal echo param to retrieve the WMS Request + echoGetMap = await page.request.get(getMapRequest.url() + '&__echo__'); + originalUrl = decodeURIComponent(await echoGetMap.text()); + urlObj = new URLSearchParams((new URL(originalUrl).search)); + + expect(urlObj.get('filter')).toBeNull(); // Select the second one await page.locator(combo).selectOption('simple_label'); diff --git a/tests/qgis-projects/tests/form_filter.md b/tests/qgis-projects/tests/form_filter.md deleted file mode 100644 index 206adbf332..0000000000 --- a/tests/qgis-projects/tests/form_filter.md +++ /dev/null @@ -1,12 +0,0 @@ -# Test form filter - -**Project : form_filter** - -**Note :** This test is highly covered by cypress, but GetMap requests are not checked in Cypress - -## Procedure - -* [ ] Display form filter tool then: -* [ ] Selecting "simple label" in `test_filter` should only display one point with "simple label" label on map -* [ ] Selecting "Œuvres d'art et monuments de l'espace urbain" in `test_filter` should only display one point with "Œuvres d'art et monuments de l'espace urbain" label on map -* [ ] Clicking cross button should display the two initial points on map From d67ad3de922cfa0f31fccda332e07f7c5ea7d8b0 Mon Sep 17 00:00:00 2001 From: Nicolas Boisteault Date: Tue, 3 Dec 2024 15:22:16 +0100 Subject: [PATCH 7/8] Update tests/end2end/playwright/form-filter.spec.js MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Étienne Trimaille --- tests/end2end/playwright/form-filter.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/end2end/playwright/form-filter.spec.js b/tests/end2end/playwright/form-filter.spec.js index 6f1a2692b4..c2b0813fd1 100644 --- a/tests/end2end/playwright/form-filter.spec.js +++ b/tests/end2end/playwright/form-filter.spec.js @@ -2,7 +2,7 @@ import { test, expect } from '@playwright/test'; import { gotoMap } from './globals'; -test.describe('Attribute table', () => { +test.describe('Form filter', () => { test.beforeEach(async ({ page }) => { const url = '/index.php/view/map/?repository=testsrepository&project=form_filter'; await gotoMap(url, page); From 44dfb4d6445c9c171e3e20f7d628ea92e7b20b75 Mon Sep 17 00:00:00 2001 From: Nicolas Boisteault Date: Thu, 5 Dec 2024 11:18:49 +0100 Subject: [PATCH 8/8] The generated filter can contain OR terms. We should use parenthesis to ensure the correct logic will be used. Spaces are used around them to please QGIS Co-authored-by: mdouchin --- assets/src/legacy/filter.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/src/legacy/filter.js b/assets/src/legacy/filter.js index 8c7d60bf3b..6623f5fe4b 100644 --- a/assets/src/legacy/filter.js +++ b/assets/src/legacy/filter.js @@ -416,7 +416,7 @@ var lizLayerFilterTool = function () { if (!globalThis['filterConfigData'].filter) { sdata.filter = autocompleteFilter; } else { - sdata.filter = globalThis['filterConfigData'].filter + ' AND ' + autocompleteFilter; + sdata.filter = ' ( ' + globalThis['filterConfigData'].filter + ' ) AND ' + autocompleteFilter; } fetch(globalThis['filterConfigData'].url, { method: "POST",