From d24a60671163a3b5757328eae4b19d76174d402c Mon Sep 17 00:00:00 2001 From: Jacky0299 <83443908+Jacky0299@users.noreply.github.com> Date: Wed, 5 Apr 2023 16:17:31 -0400 Subject: [PATCH] geolocation button closes issue#780 (#800) * geolocation button * Delete index.js * Delete multipleExtents.html * some changes to the file * button name change * minor edits * geolocation sync with controlslist api * tests fixed and syncing web-map * Peter suggested changes * locate api * locateapi and locate button test * tests * locate button title changes when its state changes * locatebutton states test * web-map syncing * Remove the word "control" from button title * Remove the word "control" from button title * Fix test * grunt file change * Finalize text strings prior to localization * Localize text strings for location control button states * custom marker * small changes and tests * locate APi bug fix * minor styling fix * marker image made to svg * Alliyan suggested changes * locateapi test bug fix * marker title changes depends on statr * Localize text strings for my current or cached location marker * Update test to use variables for geolocation button states * tooltip focus * tooltip test added * geolocationbutton code refactored * fixed tests and small fixes * Formatting --------- Co-authored-by: Peter Rushforth Co-authored-by: AliyanH --- Gruntfile.js | 15 +++- package-lock.json | 13 ++++ package.json | 1 + src/mapml-viewer.js | 44 ++++++++++- src/mapml/control/GeolocationButton.js | 44 +++++++++++ src/mapml/index.js | 5 +- src/mapml/options.js | 5 ++ src/web-map.js | 44 ++++++++++- test/e2e/api/domApi-mapml-viewer.test.js | 35 +++++++++ test/e2e/api/domApi-web-map.test.js | 35 +++++++++ test/e2e/api/locateApi.html | 79 +++++++++++++++++++ test/e2e/api/locateApi.test.js | 91 ++++++++++++++++++++++ test/e2e/mapml-viewer/locateButton.html | 79 +++++++++++++++++++ test/e2e/mapml-viewer/locateButton.test.js | 78 +++++++++++++++++++ 14 files changed, 562 insertions(+), 6 deletions(-) create mode 100644 src/mapml/control/GeolocationButton.js create mode 100644 test/e2e/api/locateApi.html create mode 100644 test/e2e/api/locateApi.test.js create mode 100644 test/e2e/mapml-viewer/locateButton.html create mode 100644 test/e2e/mapml-viewer/locateButton.test.js diff --git a/Gruntfile.js b/Gruntfile.js index 811b5fade..d3c492929 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -9,7 +9,7 @@ module.exports = function(grunt) { }, combine: { files: { - 'dist/mapml.css': ['node_modules/leaflet/dist/leaflet.css', 'src/mapml.css'] + 'dist/mapml.css': ['node_modules/leaflet/dist/leaflet.css', 'node_modules/leaflet.locatecontrol/dist/L.Control.Locate.css', 'node_modules/leaflet.locatecontrol/dist/L.Control.Locate.mapbox.css', 'src/mapml.css'] } } }, @@ -31,7 +31,8 @@ module.exports = function(grunt) { 'dist/layer.js': ['src/layer.js'], 'dist/leaflet.js': ['dist/leaflet-src.js', 'dist/proj4-src.js', - 'dist/proj4leaflet.js'] + 'dist/proj4leaflet.js', + 'dist/L.Control.Locate.js'] } } }, @@ -87,6 +88,14 @@ module.exports = function(grunt) { filter: 'isFile', src: ['index.html'], dest: 'dist/' + }, + { + expand: true, + cwd: 'node_modules/leaflet.locatecontrol/src/', + flatten: true, + filter: 'isFile', + src: ['L.Control.Locate.js'], + dest: 'dist/' } ], options: { @@ -155,7 +164,7 @@ module.exports = function(grunt) { }, clean: { dist: ['dist'], - tidyup: ['dist/leaflet-src.js','dist/proj4-src.js','dist/proj4leaflet.js'], + tidyup: ['dist/leaflet-src.js','dist/proj4-src.js','dist/proj4leaflet.js','dist/L.Control.Locate.js'], experiments: { options: {force: true}, src: ['../experiments/dist'] diff --git a/package-lock.json b/package-lock.json index c1aac4526..71079f776 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27,6 +27,7 @@ "jest-environment-node": "^26.6.1", "jest-playwright-preset": "^1.7.0", "leaflet": "^1.9.3", + "leaflet.locatecontrol": "^0.79.0", "path": "^0.12.7", "playwright": "^1.24.2", "proj4": "^2.6.2", @@ -8642,6 +8643,12 @@ "integrity": "sha512-iB2cR9vAkDOu5l3HAay2obcUHZ7xwUBBjph8+PGtmW/2lYhbLizWtG7nTeYht36WfOslixQF9D/uSIzhZgGMfQ==", "dev": true }, + "node_modules/leaflet.locatecontrol": { + "version": "0.79.0", + "resolved": "https://registry.npmjs.org/leaflet.locatecontrol/-/leaflet.locatecontrol-0.79.0.tgz", + "integrity": "sha512-h64QIHFkypYdr90lkSfjKvPvvk8/b8UnP3m9WuoWdp5p2AaCWC0T1NVwyuj4rd5U4fBW3tQt4ppmZ2LceHMIDg==", + "dev": true + }, "node_modules/leven": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", @@ -18887,6 +18894,12 @@ "integrity": "sha512-iB2cR9vAkDOu5l3HAay2obcUHZ7xwUBBjph8+PGtmW/2lYhbLizWtG7nTeYht36WfOslixQF9D/uSIzhZgGMfQ==", "dev": true }, + "leaflet.locatecontrol": { + "version": "0.79.0", + "resolved": "https://registry.npmjs.org/leaflet.locatecontrol/-/leaflet.locatecontrol-0.79.0.tgz", + "integrity": "sha512-h64QIHFkypYdr90lkSfjKvPvvk8/b8UnP3m9WuoWdp5p2AaCWC0T1NVwyuj4rd5U4fBW3tQt4ppmZ2LceHMIDg==", + "dev": true + }, "leven": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", diff --git a/package.json b/package.json index 3b437dcce..012eb29ac 100644 --- a/package.json +++ b/package.json @@ -53,6 +53,7 @@ "jest-environment-node": "^26.6.1", "jest-playwright-preset": "^1.7.0", "leaflet": "^1.9.3", + "leaflet.locatecontrol": "^0.79.0", "path": "^0.12.7", "@playwright/test": "^1.24.2", "playwright": "^1.24.2", diff --git a/src/mapml-viewer.js b/src/mapml-viewer.js index 51644ad8d..05b3090e2 100644 --- a/src/mapml-viewer.js +++ b/src/mapml-viewer.js @@ -134,7 +134,7 @@ export class MapViewer extends HTMLElement { this._controlsList = new DOMTokenList( this.getAttribute("controlslist"), this, "controlslist", - ["noreload","nofullscreen","nozoom","nolayer"] + ["noreload","nofullscreen","nozoom","nolayer","geolocation"] ); // the dimension attributes win, if they're there. A map does not @@ -354,6 +354,9 @@ export class MapViewer extends HTMLElement { totalSize += 49; this._fullScreenControl = M.fullscreenButton().addTo(this._map); } + if (!this._geolocationButton) { + this._geolocationButton = M.geolocationButton().addTo(this._map); + } } // Sets controls by hiding/unhiding them based on the map attribute @@ -372,12 +375,14 @@ export class MapViewer extends HTMLElement { this._setControlsVisibility("layercontrol",true); this._setControlsVisibility("reload",true); this._setControlsVisibility("zoom",true); + this._setControlsVisibility("geolocation",true); } _showControls() { this._setControlsVisibility("fullscreen",false); this._setControlsVisibility("layercontrol",false); this._setControlsVisibility("reload",false); this._setControlsVisibility("zoom",false); + this._setControlsVisibility("geolocation",true); // prune the controls shown if necessary // this logic could be embedded in _showControls @@ -398,6 +403,9 @@ export class MapViewer extends HTMLElement { case 'nozoom': this._setControlsVisibility("zoom",true); break; + case 'geolocation': + this._setControlsVisibility("geolocation",false); + break; } }); } @@ -431,6 +439,11 @@ export class MapViewer extends HTMLElement { container = this._layerControl._container; } break; + case "geolocation": + if (this._geolocationButton) { + container = this._geolocationButton._container; + } + break; } if (container) { if (hide) { @@ -525,6 +538,19 @@ export class MapViewer extends HTMLElement { {target: this}})); } }); + + this._map.on('locationfound', + function (e) { + this.dispatchEvent(new CustomEvent('maplocationfound', {detail: + {latlng: e.latlng, accuracy: e.accuracy} + })); + },this); + this._map.on('locationerror', + function (e) { + this.dispatchEvent(new CustomEvent('locationerror', {detail: + {error:e.message} + })); + },this); this._map.on('load', function () { this.dispatchEvent(new CustomEvent('load', {detail: {target: this}})); @@ -656,6 +682,22 @@ export class MapViewer extends HTMLElement { } }); } + + locate(options) { //options: https://leafletjs.com/reference.html#locate-options + if (this._geolocationButton) { + this._geolocationButton.stop(); + } + if (options) { + if (options.zoomTo) { + options.setView = options.zoomTo; + delete options.zoomTo; + } + this._map.locate(options); + } else { + this._map.locate({setView: true, maxZoom: 16}); + } + } + toggleDebug(){ if(this._debug){ this._debug.remove(); diff --git a/src/mapml/control/GeolocationButton.js b/src/mapml/control/GeolocationButton.js new file mode 100644 index 000000000..1ba633aa8 --- /dev/null +++ b/src/mapml/control/GeolocationButton.js @@ -0,0 +1,44 @@ +export var GeolocationButton = L.Control.extend({ + options: { + position: 'bottomright' + }, + + onAdd: function (map) { + this.locateControl = L.control.locate({ + showPopup: false, + strings: { + title: M.options.locale.btnLocTrackOff + }, + position: this.options.position, + locateOptions: { + maxZoom: 16 + } + }).addTo(map); + + var container = this.locateControl._container; + var button = this.locateControl; + var observer = new MutationObserver(function(mutations) { + if (container.classList.contains('active') && container.classList.contains('following')) { + container.firstChild.title = M.options.locale.btnLocTrackOn; + button._marker.bindTooltip(M.options.locale.btnMyLocTrackOn,{permanent:true}); + } else if (container.classList.contains('active')) { + container.firstChild.title = M.options.locale.btnLocTrackLastKnown; + button._marker.bindTooltip(M.options.locale.btnMyLastKnownLocTrackOn); + } else { + container.firstChild.title = M.options.locale.btnLocTrackOff; + } + }); + var observerConfig = { attributes: true, attributeFilter: ['class'] }; + observer.observe(container, observerConfig); + + return container; + }, + + stop: function () { + return this.locateControl.stop(); + } +}); + +export var geolocationButton = function (options) { + return new GeolocationButton(options); +}; diff --git a/src/mapml/index.js b/src/mapml/index.js index 938a47deb..df601a660 100644 --- a/src/mapml/index.js +++ b/src/mapml/index.js @@ -53,7 +53,8 @@ import { ContextMenu } from './handlers/ContextMenu'; import { Util } from './utils/Util'; import { ReloadButton, reloadButton } from './control/ReloadButton'; import { FullscreenButton, fullscreenButton } from './control/FullscreenButton'; -import {attributionControl} from "./control/AttributionControl"; +import {attributionControl} from "./control/AttributionControl"; +import {geolocationButton} from "./control/GeolocationButton"; import { Crosshair, crosshair } from "./layers/Crosshair"; import { Feature, feature } from "./features/feature"; import { FeatureRenderer, featureRenderer } from './features/featureRenderer'; @@ -659,6 +660,8 @@ M.fullscreenButton = fullscreenButton; M.attributionControl = attributionControl; +M.geolocationButton = geolocationButton; + M.StaticTileLayer = StaticTileLayer; M.staticTileLayer = staticTileLayer; diff --git a/src/mapml/options.js b/src/mapml/options.js index 2bcc4326f..e5590430e 100644 --- a/src/mapml/options.js +++ b/src/mapml/options.js @@ -22,6 +22,11 @@ export var Options = { btnZoomOut: "Zoom out", btnFullScreen: "View Fullscreen", btnExitFullScreen: "Exit Fullscreen", + btnLocTrackOn: "Show my location - location tracking on", + btnMyLocTrackOn: "My current location, shown on map", + btnLocTrackOff: "Show my location - location tracking off", + btnLocTrackLastKnown: "Show my location - last known location shown", + btnMyLastKnownLocTrackOn: "My last known location, shown on map", amZoom: "zoom level", amColumn: "column", amRow: "row", diff --git a/src/web-map.js b/src/web-map.js index f987ff0a4..56da275a3 100644 --- a/src/web-map.js +++ b/src/web-map.js @@ -138,7 +138,7 @@ export class WebMap extends HTMLMapElement { this._controlsList = new DOMTokenList( this.getAttribute("controlslist"), this, "controlslist", - ["noreload","nofullscreen","nozoom","nolayer"] + ["noreload","nofullscreen","nozoom","nolayer","geolocation"] ); // the dimension attributes win, if they're there. A map does not @@ -397,6 +397,9 @@ export class WebMap extends HTMLMapElement { totalSize += 49; this._fullScreenControl = M.fullscreenButton().addTo(this._map); } + if (!this._geolocationButton) { + this._geolocationButton = M.geolocationButton().addTo(this._map); + } } // Sets controls by hiding/unhiding them based on the map attribute @@ -415,12 +418,14 @@ export class WebMap extends HTMLMapElement { this._setControlsVisibility("layercontrol",true); this._setControlsVisibility("reload",true); this._setControlsVisibility("zoom",true); + this._setControlsVisibility("geolocation",true); } _showControls() { this._setControlsVisibility("fullscreen",false); this._setControlsVisibility("layercontrol",false); this._setControlsVisibility("reload",false); this._setControlsVisibility("zoom",false); + this._setControlsVisibility("geolocation",true); // prune the controls shown if necessary // this logic could be embedded in _showControls @@ -441,6 +446,9 @@ export class WebMap extends HTMLMapElement { case 'nozoom': this._setControlsVisibility("zoom",true); break; + case 'geolocation': + this._setControlsVisibility("geolocation",false); + break; } }); } @@ -474,6 +482,11 @@ export class WebMap extends HTMLMapElement { container = this._layerControl._container; } break; + case "geolocation": + if (this._geolocationButton) { + container = this._geolocationButton._container; + } + break; } if (container) { if (hide) { @@ -568,6 +581,19 @@ export class WebMap extends HTMLMapElement { {target: this}})); } }); + + this._map.on('locationfound', + function (e) { + this.dispatchEvent(new CustomEvent('maplocationfound', {detail: + {latlng: e.latlng, accuracy: e.accuracy} + })); + },this); + this._map.on('locationerror', + function (e) { + this.dispatchEvent(new CustomEvent('locationerror', {detail: + {error:e.message} + })); + },this); this._map.on('load', function () { this.dispatchEvent(new CustomEvent('load', {detail: {target: this}})); @@ -699,6 +725,22 @@ export class WebMap extends HTMLMapElement { } }); } + + locate(options) { //options: https://leafletjs.com/reference.html#locate-options + if (this._geolocationButton) { + this._geolocationButton.stop(); + } + if (options) { + if (options.zoomTo) { + options.setView = options.zoomTo; + delete options.zoomTo; + } + this._map.locate(options); + } else { + this._map.locate({setView: true, maxZoom: 16}); + } + } + toggleDebug(){ if(this._debug){ this._debug.remove(); diff --git a/test/e2e/api/domApi-mapml-viewer.test.js b/test/e2e/api/domApi-mapml-viewer.test.js index b5547b91f..a0bfca99c 100644 --- a/test/e2e/api/domApi-mapml-viewer.test.js +++ b/test/e2e/api/domApi-mapml-viewer.test.js @@ -96,6 +96,8 @@ test.describe("mapml-viewer DOM API Tests", () => { let reloadHidden = await page.$eval(".leaflet-top.leaflet-left > .mapml-reload-button", (div) => div.hidden); let fullscreenHidden = await page.$eval(".leaflet-top.leaflet-left > .leaflet-control-fullscreen", (div) => div.hidden); let layerControlHidden = await page.$eval(".leaflet-top.leaflet-right > .leaflet-control-layers", (div) => div.hidden); + let geolocation = await page.$eval(".leaflet-bottom.leaflet-right > .leaflet-control-locate", (div) => div.hidden); + expect(geolocation).toEqual(true); expect(zoomHidden).toEqual(true); expect(reloadHidden).toEqual(true); expect(fullscreenHidden).toEqual(true); @@ -135,6 +137,8 @@ test.describe("mapml-viewer DOM API Tests", () => { let reloadHidden = await page.$eval(".leaflet-top.leaflet-left > .mapml-reload-button", (div) => div.hidden); let fullscreenHidden = await page.$eval(".leaflet-top.leaflet-left > .leaflet-control-fullscreen", (div) => div.hidden); let layerControlHidden = await page.$eval(".leaflet-top.leaflet-right > .leaflet-control-layers", (div) => div.hidden); + let geolocation = await page.$eval(".leaflet-bottom.leaflet-right > .leaflet-control-locate", (div) => div.hidden); + expect(geolocation).toEqual(true); expect(zoomHidden).toEqual(true); expect(reloadHidden).toEqual(true); expect(fullscreenHidden).toEqual(true); @@ -153,6 +157,8 @@ test.describe("mapml-viewer DOM API Tests", () => { let reloadHidden = await page.$eval(".leaflet-top.leaflet-left > .mapml-reload-button", (div) => div.hidden); let fullscreenHidden = await page.$eval(".leaflet-top.leaflet-left > .leaflet-control-fullscreen", (div) => div.hidden); let layerControlHidden = await page.$eval(".leaflet-top.leaflet-right > .leaflet-control-layers", (div) => div.hidden); + let geolocation = await page.$eval(".leaflet-bottom.leaflet-right > .leaflet-control-locate", (div) => div.hidden); + expect(geolocation).toEqual(true); expect(zoomHidden).toEqual(true); expect(reloadHidden).toEqual(true); expect(fullscreenHidden).toEqual(true); @@ -168,6 +174,8 @@ test.describe("mapml-viewer DOM API Tests", () => { let reloadHidden = await page.$eval(".leaflet-top.leaflet-left > .mapml-reload-button", (div) => div.hidden); let fullscreenHidden = await page.$eval(".leaflet-top.leaflet-left > .leaflet-control-fullscreen", (div) => div.hidden); let layerControlHidden = await page.$eval(".leaflet-top.leaflet-right > .leaflet-control-layers", (div) => div.hidden); + let geolocation = await page.$eval(".leaflet-bottom.leaflet-right > .leaflet-control-locate", (div) => div.hidden); + expect(geolocation).toEqual(true); expect(zoomHidden).toEqual(false); expect(reloadHidden).toEqual(false); expect(fullscreenHidden).toEqual(false); @@ -231,6 +239,8 @@ test.describe("mapml-viewer DOM API Tests", () => { let reloadHidden = await page.$eval(".leaflet-top.leaflet-left > .mapml-reload-button", (div) => div.hidden); let fullscreenHidden = await page.$eval(".leaflet-top.leaflet-left > .leaflet-control-fullscreen", (div) => div.hidden); let layerControlHidden = await page.$eval(".leaflet-top.leaflet-right > .leaflet-control-layers", (div) => div.hidden); + let geolocation = await page.$eval(".leaflet-bottom.leaflet-right > .leaflet-control-locate", (div) => div.hidden); + expect(geolocation).toEqual(true); expect(zoomHidden).toEqual(true); expect(reloadHidden).toEqual(false); expect(fullscreenHidden).toEqual(true); @@ -250,6 +260,8 @@ test.describe("mapml-viewer DOM API Tests", () => { let reloadHidden = await page.$eval(".leaflet-top.leaflet-left > .mapml-reload-button", (div) => div.hidden); let fullscreenHidden = await page.$eval(".leaflet-top.leaflet-left > .leaflet-control-fullscreen", (div) => div.hidden); let layerControlHidden = await page.$eval(".leaflet-top.leaflet-right > .leaflet-control-layers", (div) => div.hidden); + let geolocation = await page.$eval(".leaflet-bottom.leaflet-right > .leaflet-control-locate", (div) => div.hidden); + expect(geolocation).toEqual(true); expect(zoomHidden).toEqual(false); expect(reloadHidden).toEqual(true); expect(fullscreenHidden).toEqual(false); @@ -264,6 +276,8 @@ test.describe("mapml-viewer DOM API Tests", () => { reloadHidden = await page.$eval(".leaflet-top.leaflet-left > .mapml-reload-button", (div) => div.hidden); fullscreenHidden = await page.$eval(".leaflet-top.leaflet-left > .leaflet-control-fullscreen", (div) => div.hidden); layerControlHidden = await page.$eval(".leaflet-top.leaflet-right > .leaflet-control-layers", (div) => div.hidden); + geolocation = await page.$eval(".leaflet-bottom.leaflet-right > .leaflet-control-locate", (div) => div.hidden); + expect(geolocation).toEqual(true); expect(zoomHidden).toEqual(true); expect(reloadHidden).toEqual(true); expect(fullscreenHidden).toEqual(false); @@ -281,6 +295,8 @@ test.describe("mapml-viewer DOM API Tests", () => { let reloadHidden = await page.$eval(".leaflet-top.leaflet-left > .mapml-reload-button", (div) => div.hidden); let fullscreenHidden = await page.$eval(".leaflet-top.leaflet-left > .leaflet-control-fullscreen", (div) => div.hidden); let layerControlHidden = await page.$eval(".leaflet-top.leaflet-right > .leaflet-control-layers", (div) => div.hidden); + let geolocation = await page.$eval(".leaflet-bottom.leaflet-right > .leaflet-control-locate", (div) => div.hidden); + expect(geolocation).toEqual(true); expect(zoomHidden).toEqual(true); expect(reloadHidden).toEqual(true); expect(fullscreenHidden).toEqual(true); @@ -295,12 +311,31 @@ test.describe("mapml-viewer DOM API Tests", () => { reloadHidden = await page.$eval(".leaflet-top.leaflet-left > .mapml-reload-button", (div) => div.hidden); fullscreenHidden = await page.$eval(".leaflet-top.leaflet-left > .leaflet-control-fullscreen", (div) => div.hidden); layerControlHidden = await page.$eval(".leaflet-top.leaflet-right > .leaflet-control-layers", (div) => div.hidden); + geolocation = await page.$eval(".leaflet-bottom.leaflet-right > .leaflet-control-locate", (div) => div.hidden); + expect(geolocation).toEqual(true); expect(zoomHidden).toEqual(true); expect(reloadHidden).toEqual(true); expect(fullscreenHidden).toEqual(false); expect(layerControlHidden).toEqual(true); }); + test("controlslist geolocations test", async () => { + const viewerHandle = await page.evaluateHandle(() => document.querySelector('mapml-viewer')); + await page.evaluate( viewer => viewer.setAttribute("controlslist","geolocation"), viewerHandle); + let hascontrolslist = await page.evaluate( viewer => viewer.getAttribute("controlslist"), viewerHandle); + expect(hascontrolslist).toEqual('geolocation'); + + let geolocation = await page.$eval(".leaflet-bottom.leaflet-right > .leaflet-control-locate", (div) => div.hidden); + expect(geolocation).toEqual(false); + //remove geolocation + await page.evaluate( viewer => viewer.controlsList = "noreload", viewerHandle); + hascontrolslist = await page.evaluate( viewer => viewer.getAttribute("controlslist"), viewerHandle); + expect(hascontrolslist).toEqual('noreload'); + let reloadHidden = await page.$eval(".leaflet-top.leaflet-left > .mapml-reload-button", (div) => div.hidden); + geolocation = await page.$eval(".leaflet-bottom.leaflet-right > .leaflet-control-locate", (div) => div.hidden); + expect(geolocation).toEqual(true); + expect(reloadHidden).toEqual(true); + }); test("controlslist removeAttribute", async () => { const viewerHandle = await page.evaluateHandle(() => document.querySelector('mapml-viewer')); // removeAttribute diff --git a/test/e2e/api/domApi-web-map.test.js b/test/e2e/api/domApi-web-map.test.js index f7ed8385a..a0791a3fe 100644 --- a/test/e2e/api/domApi-web-map.test.js +++ b/test/e2e/api/domApi-web-map.test.js @@ -97,6 +97,8 @@ test.describe("web-map DOM API Tests", () => { let reloadHidden = await page.$eval(".leaflet-top.leaflet-left > .mapml-reload-button", (div) => div.hidden); let fullscreenHidden = await page.$eval(".leaflet-top.leaflet-left > .leaflet-control-fullscreen", (div) => div.hidden); let layerControlHidden = await page.$eval(".leaflet-top.leaflet-right > .leaflet-control-layers", (div) => div.hidden); + let geolocation = await page.$eval(".leaflet-bottom.leaflet-right > .leaflet-control-locate", (div) => div.hidden); + expect(geolocation).toEqual(true); expect(zoomHidden).toEqual(true); expect(reloadHidden).toEqual(true); expect(fullscreenHidden).toEqual(true); @@ -137,6 +139,8 @@ test.describe("web-map DOM API Tests", () => { let reloadHidden = await page.$eval(".leaflet-top.leaflet-left > .mapml-reload-button", (div) => div.hidden); let fullscreenHidden = await page.$eval(".leaflet-top.leaflet-left > .leaflet-control-fullscreen", (div) => div.hidden); let layerControlHidden = await page.$eval(".leaflet-top.leaflet-right > .leaflet-control-layers", (div) => div.hidden); + let geolocation = await page.$eval(".leaflet-bottom.leaflet-right > .leaflet-control-locate", (div) => div.hidden); + expect(geolocation).toEqual(true); expect(zoomHidden).toEqual(true); expect(reloadHidden).toEqual(true); expect(fullscreenHidden).toEqual(true); @@ -155,6 +159,8 @@ test.describe("web-map DOM API Tests", () => { let reloadHidden = await page.$eval(".leaflet-top.leaflet-left > .mapml-reload-button", (div) => div.hidden); let fullscreenHidden = await page.$eval(".leaflet-top.leaflet-left > .leaflet-control-fullscreen", (div) => div.hidden); let layerControlHidden = await page.$eval(".leaflet-top.leaflet-right > .leaflet-control-layers", (div) => div.hidden); + let geolocation = await page.$eval(".leaflet-bottom.leaflet-right > .leaflet-control-locate", (div) => div.hidden); + expect(geolocation).toEqual(true); expect(zoomHidden).toEqual(true); expect(reloadHidden).toEqual(true); expect(fullscreenHidden).toEqual(true); @@ -170,6 +176,8 @@ test.describe("web-map DOM API Tests", () => { let reloadHidden = await page.$eval(".leaflet-top.leaflet-left > .mapml-reload-button", (div) => div.hidden); let fullscreenHidden = await page.$eval(".leaflet-top.leaflet-left > .leaflet-control-fullscreen", (div) => div.hidden); let layerControlHidden = await page.$eval(".leaflet-top.leaflet-right > .leaflet-control-layers", (div) => div.hidden); + let geolocation = await page.$eval(".leaflet-bottom.leaflet-right > .leaflet-control-locate", (div) => div.hidden); + expect(geolocation).toEqual(true); expect(zoomHidden).toEqual(false); expect(reloadHidden).toEqual(false); expect(fullscreenHidden).toEqual(false); @@ -235,6 +243,8 @@ test.describe("web-map DOM API Tests", () => { let reloadHidden = await page.$eval(".leaflet-top.leaflet-left > .mapml-reload-button", (div) => div.hidden); let fullscreenHidden = await page.$eval(".leaflet-top.leaflet-left > .leaflet-control-fullscreen", (div) => div.hidden); let layerControlHidden = await page.$eval(".leaflet-top.leaflet-right > .leaflet-control-layers", (div) => div.hidden); + let geolocation = await page.$eval(".leaflet-bottom.leaflet-right > .leaflet-control-locate", (div) => div.hidden); + expect(geolocation).toEqual(true); expect(zoomHidden).toEqual(true); expect(reloadHidden).toEqual(false); expect(fullscreenHidden).toEqual(true); @@ -254,6 +264,8 @@ test.describe("web-map DOM API Tests", () => { let reloadHidden = await page.$eval(".leaflet-top.leaflet-left > .mapml-reload-button", (div) => div.hidden); let fullscreenHidden = await page.$eval(".leaflet-top.leaflet-left > .leaflet-control-fullscreen", (div) => div.hidden); let layerControlHidden = await page.$eval(".leaflet-top.leaflet-right > .leaflet-control-layers", (div) => div.hidden); + let geolocation = await page.$eval(".leaflet-bottom.leaflet-right > .leaflet-control-locate", (div) => div.hidden); + expect(geolocation).toEqual(true); expect(zoomHidden).toEqual(false); expect(reloadHidden).toEqual(true); expect(fullscreenHidden).toEqual(false); @@ -268,6 +280,8 @@ test.describe("web-map DOM API Tests", () => { reloadHidden = await page.$eval(".leaflet-top.leaflet-left > .mapml-reload-button", (div) => div.hidden); fullscreenHidden = await page.$eval(".leaflet-top.leaflet-left > .leaflet-control-fullscreen", (div) => div.hidden); layerControlHidden = await page.$eval(".leaflet-top.leaflet-right > .leaflet-control-layers", (div) => div.hidden); + geolocation = await page.$eval(".leaflet-bottom.leaflet-right > .leaflet-control-locate", (div) => div.hidden); + expect(geolocation).toEqual(true); expect(zoomHidden).toEqual(true); expect(reloadHidden).toEqual(true); expect(fullscreenHidden).toEqual(false); @@ -285,6 +299,8 @@ test.describe("web-map DOM API Tests", () => { let reloadHidden = await page.$eval(".leaflet-top.leaflet-left > .mapml-reload-button", (div) => div.hidden); let fullscreenHidden = await page.$eval(".leaflet-top.leaflet-left > .leaflet-control-fullscreen", (div) => div.hidden); let layerControlHidden = await page.$eval(".leaflet-top.leaflet-right > .leaflet-control-layers", (div) => div.hidden); + let geolocation = await page.$eval(".leaflet-bottom.leaflet-right > .leaflet-control-locate", (div) => div.hidden); + expect(geolocation).toEqual(true); expect(zoomHidden).toEqual(true); expect(reloadHidden).toEqual(true); expect(fullscreenHidden).toEqual(true); @@ -299,12 +315,31 @@ test.describe("web-map DOM API Tests", () => { reloadHidden = await page.$eval(".leaflet-top.leaflet-left > .mapml-reload-button", (div) => div.hidden); fullscreenHidden = await page.$eval(".leaflet-top.leaflet-left > .leaflet-control-fullscreen", (div) => div.hidden); layerControlHidden = await page.$eval(".leaflet-top.leaflet-right > .leaflet-control-layers", (div) => div.hidden); + geolocation = await page.$eval(".leaflet-bottom.leaflet-right > .leaflet-control-locate", (div) => div.hidden); + expect(geolocation).toEqual(true); expect(zoomHidden).toEqual(true); expect(reloadHidden).toEqual(true); expect(fullscreenHidden).toEqual(false); expect(layerControlHidden).toEqual(true); }); + test("controlslist geolocations test", async () => { + const viewerHandle = await page.evaluateHandle(() => document.querySelector('map')); + await page.evaluate( map => map.setAttribute("controlslist","geolocation"), viewerHandle); + let hascontrolslist = await page.evaluate( map => map.getAttribute("controlslist"), viewerHandle); + expect(hascontrolslist).toEqual('geolocation'); + + let geolocation = await page.$eval(".leaflet-bottom.leaflet-right > .leaflet-control-locate", (div) => div.hidden); + expect(geolocation).toEqual(false); + //remove geolocation + await page.evaluate( map => map.controlsList = "noreload", viewerHandle); + hascontrolslist = await page.evaluate( viewer => viewer.getAttribute("controlslist"), viewerHandle); + expect(hascontrolslist).toEqual('noreload'); + let reloadHidden = await page.$eval(".leaflet-top.leaflet-left > .mapml-reload-button", (div) => div.hidden); + geolocation = await page.$eval(".leaflet-bottom.leaflet-right > .leaflet-control-locate", (div) => div.hidden); + expect(geolocation).toEqual(true); + expect(reloadHidden).toEqual(true); + }); test("controlslist removeAttribute", async () => { const mapHandle = await page.evaluateHandle(() => document.querySelector('map')); // removeAttribute diff --git a/test/e2e/api/locateApi.html b/test/e2e/api/locateApi.html new file mode 100644 index 000000000..5a256a095 --- /dev/null +++ b/test/e2e/api/locateApi.html @@ -0,0 +1,79 @@ + + + + + + locateApi.html + + + + + + + + + + diff --git a/test/e2e/api/locateApi.test.js b/test/e2e/api/locateApi.test.js new file mode 100644 index 000000000..887b5dddb --- /dev/null +++ b/test/e2e/api/locateApi.test.js @@ -0,0 +1,91 @@ +import { test, expect, chromium } from '@playwright/test'; + +test.use({ + geolocation: { longitude: -73.56766530667056, latitude: 45.5027789304487 }, + permissions: ['geolocation'], +}); + +test.describe("Locate API Test", () => { + let page; + let context; + test.beforeAll(async function() { + context = await chromium.launchPersistentContext(''); + page = context.pages().find((page) => page.url() === 'about:blank') || await context.newPage(); + await context.grantPermissions(['geolocation']); + await page.goto("locateApi.html"); + }); + test.afterAll(async function () { + await context.close(); + }); + + test("Using locate API to find myself", async () => { + await page.$eval("body > mapml-viewer",(viewer) => viewer.locate()); + + let locateAPI_lat = await page.$eval("body > mapml-viewer", (viewer) => viewer.lat); + let locateAPI_lng = await page.$eval("body > mapml-viewer", (viewer) => viewer.lon); + //rounding to three decimal places + locateAPI_lat = parseFloat(locateAPI_lat).toFixed(3); + locateAPI_lng = parseFloat(locateAPI_lng).toFixed(3); + + expect(locateAPI_lat).toEqual("45.503"); + expect(locateAPI_lng).toEqual("-73.568"); + }); + + test("Testing maplocationfound event", async () => { + const latlng = await page.evaluate(() => { + const viewer = document.querySelector('body > mapml-viewer'); + return new Promise((resolve) => { + viewer.addEventListener('maplocationfound', (e) => { + resolve(e.detail.latlng); + }, { once: true }); + viewer.locate(); + }); + }); + expect(latlng.lat).toEqual(45.5027789304487); + expect(latlng.lng).toEqual(-73.56766530667056); + }); + + test("Testing locationerror event", async () => { + const error = await page.evaluate(() => { + const viewer = document.querySelector('body > mapml-viewer'); + return new Promise((resolve) => { + viewer.addEventListener('locationerror', (e) => { + resolve(e.detail.error); + }, { once: true }); + const errorEvent = new CustomEvent('locationerror', { + detail: { error: 'Your location could not be determined.' } + }); + viewer.dispatchEvent(errorEvent); + viewer.locate(); + }); + }); + expect(error).toEqual("Your location could not be determined."); + }); + + test("Testing API when the button is used", async () => { + await page.reload(); + await page.click("body > mapml-viewer"); + await page.keyboard.press("Tab"); + await page.keyboard.press("Tab"); + await page.keyboard.press("Tab"); + await page.keyboard.press("Tab"); + await page.keyboard.press("Tab"); + await page.keyboard.press("Tab"); + await page.keyboard.press("Enter"); + + await page.mouse.move(600, 300); + await page.mouse.down(); + await page.mouse.move(1200, 450, {steps: 5}); + await page.mouse.up(); + await page.$eval("body > mapml-viewer",(viewer) => viewer.locate()); + + let locateAPI_lat = await page.$eval("body > mapml-viewer", (viewer) => viewer.lat); + let locateAPI_lng = await page.$eval("body > mapml-viewer", (viewer) => viewer.lon); + + locateAPI_lat = parseFloat(locateAPI_lat).toFixed(1); + locateAPI_lng = parseFloat(locateAPI_lng).toFixed(1); + + expect(locateAPI_lat).toEqual("45.5"); + expect(locateAPI_lng).toEqual("-73.6"); + }); +}); diff --git a/test/e2e/mapml-viewer/locateButton.html b/test/e2e/mapml-viewer/locateButton.html new file mode 100644 index 000000000..69a8d1406 --- /dev/null +++ b/test/e2e/mapml-viewer/locateButton.html @@ -0,0 +1,79 @@ + + + + + + locateApi.html + + + + + + + + + + diff --git a/test/e2e/mapml-viewer/locateButton.test.js b/test/e2e/mapml-viewer/locateButton.test.js new file mode 100644 index 000000000..c8949d4a5 --- /dev/null +++ b/test/e2e/mapml-viewer/locateButton.test.js @@ -0,0 +1,78 @@ +import { test, expect, chromium } from '@playwright/test'; + +test.use({ + geolocation: { longitude: -73.56766530667056, latitude: 45.5027789304487 }, + permissions: ['geolocation'] +}); + +test.describe("Geolocation control tests", () => { + let page; + let context; + test.beforeAll(async function() { + context = await chromium.launchPersistentContext(''); + page = context.pages().find((page) => page.url() === 'about:blank') || await context.newPage(); + await context.grantPermissions(['geolocation']); + await page.goto("locateButton.html"); + }); + test.afterAll(async function () { + await context.close(); + }); + + test("Using geolocation control to control map", async () => { + await page.click("body > mapml-viewer"); + await page.keyboard.press("Tab"); + await page.keyboard.press("Tab"); + await page.keyboard.press("Tab"); + await page.keyboard.press("Tab"); + await page.keyboard.press("Tab"); + await page.keyboard.press("Tab"); + await page.keyboard.press("Enter"); + + let locateButton_lat = await page.$eval("body > mapml-viewer", (viewer) => viewer.lat); + let locateButton_lng = await page.$eval("body > mapml-viewer", (viewer) => viewer.lon); + locateButton_lat = parseFloat(locateButton_lat).toFixed(3); + locateButton_lng = parseFloat(locateButton_lng).toFixed(3); + let tooltip = await page.$eval("body > mapml-viewer", (viewer) => viewer._geolocationButton.locateControl._marker._tooltip._content); + + expect(locateButton_lat).toEqual("45.503"); + expect(locateButton_lng).toEqual("-73.568"); + expect(tooltip).toEqual("My current location, shown on map"); + + }); + + test("Geolocation control state changes when pressed", async () => { + await page.click("body > mapml-viewer"); + await page.keyboard.press("Tab"); + await page.keyboard.press("Tab"); + await page.keyboard.press("Tab"); + await page.keyboard.press("Tab"); + await page.keyboard.press("Tab"); + await page.keyboard.press("Tab"); + await page.keyboard.press("Enter"); + + let locationOnText = await page.evaluate(()=>M.options.locale.btnLocTrackOn); + let locationOffText = await page.evaluate(()=>M.options.locale.btnLocTrackOff); + let lastKnownLocationText = await page.evaluate(()=>M.options.locale.btnLocTrackLastKnown); + + let locateButton_title1 = await page.$eval("div > div.leaflet-control-container > div.leaflet-bottom.leaflet-right > div > a", (button) => button.title); + + expect(locateButton_title1).toEqual(locationOffText); + await page.keyboard.press("Enter"); + + let locateButton_title2 = await page.$eval("div > div.leaflet-control-container > div.leaflet-bottom.leaflet-right > div > a", (button) => button.title); + expect(locateButton_title2).toEqual(locationOnText); + + await page.click("body > mapml-viewer"); + + await page.mouse.move(600, 300); + await page.mouse.down(); + await page.mouse.move(1200, 450, {steps: 5}); + await page.mouse.up(); + let tooltip = await page.$eval("body > mapml-viewer", (viewer) => viewer._geolocationButton.locateControl._marker._tooltip._content); + expect(tooltip).toEqual("My last known location, shown on map"); + await page.click("body > mapml-viewer"); + + let locateButton_title3 = await page.$eval("div > div.leaflet-control-container > div.leaflet-bottom.leaflet-right > div > a", (button) => button.title); + expect(locateButton_title3).toEqual(lastKnownLocationText); + }); +});