diff --git a/package-lock.json b/package-lock.json
index 1f3486729..5ff4cf506 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9,7 +9,7 @@
"version": "0.11.0",
"license": "W3C",
"devDependencies": {
- "@playwright/test": "^1.24.2",
+ "@playwright/test": "^1.27.0",
"diff": "^5.1.0",
"express": "^4.17.1",
"grunt": "^1.4.0",
@@ -30,7 +30,7 @@
"leaflet": "^1.9.4",
"leaflet.locatecontrol": "^0.79.0",
"path": "^0.12.7",
- "playwright": "^1.24.2",
+ "playwright": "^1.27.0",
"proj4": "^2.6.2",
"proj4leaflet": "^1.0.2",
"rollup": "^2.23.1"
@@ -1513,19 +1513,22 @@
}
},
"node_modules/@playwright/test": {
- "version": "1.24.2",
- "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.24.2.tgz",
- "integrity": "sha512-Q4X224pRHw4Dtkk5PoNJplZCokLNvVbXD9wDQEMrHcEuvWpJWEQDeJ9gEwkZ3iCWSFSWBshIX177B231XW4wOQ==",
+ "version": "1.35.1",
+ "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.35.1.tgz",
+ "integrity": "sha512-b5YoFe6J9exsMYg0pQAobNDR85T1nLumUYgUTtKm4d21iX2L7WqKq9dW8NGJ+2vX0etZd+Y7UeuqsxDXm9+5ZA==",
"dev": true,
"dependencies": {
"@types/node": "*",
- "playwright-core": "1.24.2"
+ "playwright-core": "1.35.1"
},
"bin": {
"playwright": "cli.js"
},
"engines": {
- "node": ">=14"
+ "node": ">=16"
+ },
+ "optionalDependencies": {
+ "fsevents": "2.3.2"
}
},
"node_modules/@sideway/address": {
@@ -4192,6 +4195,20 @@
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
"dev": true
},
+ "node_modules/fsevents": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
+ "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
+ "dev": true,
+ "hasInstallScript": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
"node_modules/function-bind": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
@@ -9723,31 +9740,31 @@
}
},
"node_modules/playwright": {
- "version": "1.24.2",
- "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.24.2.tgz",
- "integrity": "sha512-iMWDLgaFRT+7dXsNeYwgl8nhLHsUrzFyaRVC+ftr++P1dVs70mPrFKBZrGp1fOKigHV9d1syC03IpPbqLKlPsg==",
+ "version": "1.35.1",
+ "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.35.1.tgz",
+ "integrity": "sha512-NbwBeGJLu5m7VGM0+xtlmLAH9VUfWwYOhUi/lSEDyGg46r1CA9RWlvoc5yywxR9AzQb0mOCm7bWtOXV7/w43ZA==",
"dev": true,
"hasInstallScript": true,
"dependencies": {
- "playwright-core": "1.24.2"
+ "playwright-core": "1.35.1"
},
"bin": {
"playwright": "cli.js"
},
"engines": {
- "node": ">=14"
+ "node": ">=16"
}
},
"node_modules/playwright-core": {
- "version": "1.24.2",
- "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.24.2.tgz",
- "integrity": "sha512-zfAoDoPY/0sDLsgSgLZwWmSCevIg1ym7CppBwllguVBNiHeixZkc1AdMuYUPZC6AdEYc4CxWEyLMBTw2YcmRrA==",
+ "version": "1.35.1",
+ "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.35.1.tgz",
+ "integrity": "sha512-pNXb6CQ7OqmGDRspEjlxE49w+4YtR6a3X6mT1hZXeJHWmsEz7SunmvZeiG/+y1yyMZdHnnn73WKYdtV1er0Xyg==",
"dev": true,
"bin": {
- "playwright": "cli.js"
+ "playwright-core": "cli.js"
},
"engines": {
- "node": ">=14"
+ "node": ">=16"
}
},
"node_modules/posix-character-classes": {
@@ -10225,6 +10242,21 @@
"fsevents": "~2.1.2"
}
},
+ "node_modules/rollup/node_modules/fsevents": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz",
+ "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==",
+ "deprecated": "\"Please update to latest v2.3 or v2.2\"",
+ "dev": true,
+ "hasInstallScript": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
"node_modules/rsvp": {
"version": "4.8.5",
"resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz",
@@ -13391,13 +13423,14 @@
}
},
"@playwright/test": {
- "version": "1.24.2",
- "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.24.2.tgz",
- "integrity": "sha512-Q4X224pRHw4Dtkk5PoNJplZCokLNvVbXD9wDQEMrHcEuvWpJWEQDeJ9gEwkZ3iCWSFSWBshIX177B231XW4wOQ==",
+ "version": "1.35.1",
+ "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.35.1.tgz",
+ "integrity": "sha512-b5YoFe6J9exsMYg0pQAobNDR85T1nLumUYgUTtKm4d21iX2L7WqKq9dW8NGJ+2vX0etZd+Y7UeuqsxDXm9+5ZA==",
"dev": true,
"requires": {
"@types/node": "*",
- "playwright-core": "1.24.2"
+ "fsevents": "2.3.2",
+ "playwright-core": "1.35.1"
}
},
"@sideway/address": {
@@ -15513,6 +15546,13 @@
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
"dev": true
},
+ "fsevents": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
+ "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
+ "dev": true,
+ "optional": true
+ },
"function-bind": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
@@ -19747,18 +19787,18 @@
}
},
"playwright": {
- "version": "1.24.2",
- "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.24.2.tgz",
- "integrity": "sha512-iMWDLgaFRT+7dXsNeYwgl8nhLHsUrzFyaRVC+ftr++P1dVs70mPrFKBZrGp1fOKigHV9d1syC03IpPbqLKlPsg==",
+ "version": "1.35.1",
+ "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.35.1.tgz",
+ "integrity": "sha512-NbwBeGJLu5m7VGM0+xtlmLAH9VUfWwYOhUi/lSEDyGg46r1CA9RWlvoc5yywxR9AzQb0mOCm7bWtOXV7/w43ZA==",
"dev": true,
"requires": {
- "playwright-core": "1.24.2"
+ "playwright-core": "1.35.1"
}
},
"playwright-core": {
- "version": "1.24.2",
- "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.24.2.tgz",
- "integrity": "sha512-zfAoDoPY/0sDLsgSgLZwWmSCevIg1ym7CppBwllguVBNiHeixZkc1AdMuYUPZC6AdEYc4CxWEyLMBTw2YcmRrA==",
+ "version": "1.35.1",
+ "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.35.1.tgz",
+ "integrity": "sha512-pNXb6CQ7OqmGDRspEjlxE49w+4YtR6a3X6mT1hZXeJHWmsEz7SunmvZeiG/+y1yyMZdHnnn73WKYdtV1er0Xyg==",
"dev": true
},
"posix-character-classes": {
@@ -20116,6 +20156,15 @@
"dev": true,
"requires": {
"fsevents": "~2.1.2"
+ },
+ "dependencies": {
+ "fsevents": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz",
+ "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==",
+ "dev": true,
+ "optional": true
+ }
}
},
"rsvp": {
diff --git a/package.json b/package.json
index d9aca5692..e48a7057a 100644
--- a/package.json
+++ b/package.json
@@ -56,8 +56,8 @@
"leaflet": "^1.9.4",
"leaflet.locatecontrol": "^0.79.0",
"path": "^0.12.7",
- "@playwright/test": "^1.24.2",
- "playwright": "^1.24.2",
+ "@playwright/test": "^1.27.0",
+ "playwright": "^1.27.0",
"proj4": "^2.6.2",
"proj4leaflet": "^1.0.2",
"rollup": "^2.23.1"
diff --git a/src/mapml-viewer.js b/src/mapml-viewer.js
index 1210f4b72..4132f1eb8 100644
--- a/src/mapml-viewer.js
+++ b/src/mapml-viewer.js
@@ -1087,6 +1087,7 @@ export class MapViewer extends HTMLElement {
this._traversalCall = 1;
this._map.panBy([initialLocation.x - curr.x, initialLocation.y - curr.y]);
}
+ this._map.getContainer().focus();
}
_toggleFullScreen() {
diff --git a/src/mapml.css b/src/mapml.css
index e6eb37a4c..eaa5990e3 100644
--- a/src/mapml.css
+++ b/src/mapml.css
@@ -51,7 +51,7 @@
left: 45px;
}
}
-
+
/* Generic class for seamless buttons */
.mapml-button {
background-color: transparent;
@@ -909,7 +909,16 @@ label.mapml-layer-item-toggle {
width: 450px;
font-size: 16px;
}
-
+@container leafletmap (max-width: 650px ) {
+ .mapml-feature-index {
+ width: 70cqw;
+ }
+}
+@container leafletmap (max-width: 390px ) {
+ .mapml-feature-index {
+ bottom: 100px;
+ }
+}
.mapml-feature-index-content > span{
width: 140px;
white-space: nowrap;
diff --git a/src/mapml/control/FullscreenButton.js b/src/mapml/control/FullscreenButton.js
index 2ade90123..9b0448e93 100644
--- a/src/mapml/control/FullscreenButton.js
+++ b/src/mapml/control/FullscreenButton.js
@@ -84,6 +84,7 @@ L.Map.include({
this._enablePseudoFullscreen(container);
}
}
+ this.getContainer().focus();
},
_enablePseudoFullscreen: function (container) {
diff --git a/src/mapml/control/GeolocationButton.js b/src/mapml/control/GeolocationButton.js
index 56be01a3c..a69311735 100644
--- a/src/mapml/control/GeolocationButton.js
+++ b/src/mapml/control/GeolocationButton.js
@@ -4,18 +4,28 @@ export var GeolocationButton = L.Control.extend({
},
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);
+ // customize locate control to focus map after start/stop, so that
+ // featureIndexOverlay is correctly displayed
+ L.Control.CustomLocate = L.Control.Locate.extend({
+ start: function () {
+ L.Control.Locate.prototype.start.call(this);
+ map.getContainer().focus();
+ },
+ stop: function () {
+ L.Control.Locate.prototype.stop.call(this);
+ map.getContainer().focus();
+ }
+ });
+ this.locateControl = new L.Control.CustomLocate({
+ 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;
diff --git a/src/mapml/layers/FeatureIndexOverlay.js b/src/mapml/layers/FeatureIndexOverlay.js
index 813f67a5a..677cd39e7 100644
--- a/src/mapml/layers/FeatureIndexOverlay.js
+++ b/src/mapml/layers/FeatureIndexOverlay.js
@@ -24,11 +24,7 @@ export var FeatureIndexOverlay = L.Layer.extend({
);
this._body.index = 0;
this._output.initialFocus = false;
- map.on(
- 'layerchange layeradd layerremove overlayremove',
- this._toggleEvents,
- this
- );
+ map.on('focus blur popupclose', this._addOrRemoveFeatureIndex, this);
map.on('moveend focus templatedfeatureslayeradd', this._checkOverlap, this);
map.on('keydown', this._onKeyDown, this);
this._addOrRemoveFeatureIndex();
@@ -77,6 +73,8 @@ export var FeatureIndexOverlay = L.Layer.extend({
let index = 1;
let keys = Object.keys(features);
let body = this._body;
+ let noFeaturesMessage = document.createElement('span');
+ noFeaturesMessage.innerHTML = M.options.locale.fIndexNoFeatures;
body.innerHTML = '';
body.index = 0;
@@ -123,6 +121,9 @@ export var FeatureIndexOverlay = L.Layer.extend({
}
});
this._addToggleKeys();
+ if (index === 1) {
+ body.appendChild(noFeaturesMessage);
+ }
},
_updateOutput: function (label, index, key) {
@@ -189,16 +190,7 @@ export var FeatureIndexOverlay = L.Layer.extend({
}
},
- _toggleEvents: function () {
- this._map.on(
- 'viewreset move moveend focus blur popupclose',
- this._addOrRemoveFeatureIndex,
- this
- );
- },
-
_addOrRemoveFeatureIndex: function (e) {
- let features = this._body.allFeatures ? this._body.allFeatures.length : 0;
//Toggle aria-hidden attribute so screen reader rereads the feature index on focus
if (!this._output.initialFocus) {
this._output.setAttribute('aria-hidden', 'true');
@@ -214,11 +206,15 @@ export var FeatureIndexOverlay = L.Layer.extend({
this._output.popupClosed = true;
} else if (e && e.type === 'focus') {
this._container.removeAttribute('hidden');
- if (features !== 0)
- this._output.classList.remove('mapml-screen-reader-output');
- } else if (e && e.originalEvent && e.originalEvent.type === 'pointermove') {
- this._container.setAttribute('hidden', '');
- this._output.classList.add('mapml-screen-reader-output');
+ this._output.classList.remove('mapml-screen-reader-output');
+ // this is a very subtle branch. The event that gets handled below is a blur
+ // event, which happens to have the e.target._popup property
+ // when there will be a popup. Because blur gets handled here, it doesn't
+ // get handled in the next else if block, which would hide both the reticle
+ // and the index menu, and then recursively call this method with no event
+ // argument, which manipulates the aria-hidden attribute on the output
+ // in order to have the screenreader read its contents when the focus returns
+ // to (what exactly???).
} else if (e && e.target._popup) {
this._container.setAttribute('hidden', '');
} else if (e && e.type === 'blur') {
@@ -226,14 +222,8 @@ export var FeatureIndexOverlay = L.Layer.extend({
this._output.classList.add('mapml-screen-reader-output');
this._output.initialFocus = false;
this._addOrRemoveFeatureIndex();
- } else if (this._map.isFocused && e) {
- this._container.removeAttribute('hidden');
- if (features !== 0) {
- this._output.classList.remove('mapml-screen-reader-output');
- } else {
- this._output.classList.add('mapml-screen-reader-output');
- }
} else {
+ // this is the default block, called when no event is passed (recursive call)
this._container.setAttribute('hidden', '');
this._output.classList.add('mapml-screen-reader-output');
}
diff --git a/src/mapml/layers/MapMLLayer.js b/src/mapml/layers/MapMLLayer.js
index 2d49c14b8..da8216fb5 100644
--- a/src/mapml/layers/MapMLLayer.js
+++ b/src/mapml/layers/MapMLLayer.js
@@ -2057,6 +2057,7 @@ export var MapMLLayer = L.Layer.extend({
e.preventDefault();
featureEl.zoomTo();
featureEl._map.closePopup();
+ featureEl._map.getContainer().focus();
};
content.insertBefore(
zoomLink,
diff --git a/src/mapml/options.js b/src/mapml/options.js
index 73995b365..008ff476f 100644
--- a/src/mapml/options.js
+++ b/src/mapml/options.js
@@ -53,6 +53,7 @@ export var Options = {
kbdNextFeature: 'Next feature',
dfLayer: 'Layer',
popupZoom: 'Zoom to here',
- dfPastedLayer: 'Pasted layer'
+ dfPastedLayer: 'Pasted layer',
+ fIndexNoFeatures: 'No features found'
}
};
diff --git a/src/mapml/utils/Util.js b/src/mapml/utils/Util.js
index e00feb307..f01368ae4 100644
--- a/src/mapml/utils/Util.js
+++ b/src/mapml/utils/Util.js
@@ -558,6 +558,7 @@ export var Util = {
);
if (opacity) layer.opacity = opacity;
}
+ map.getContainer().focus();
},
// _gcrsToTileMatrix returns the [column, row] of the tiles at map center. Used for Announce movement for screen readers
diff --git a/src/web-map.js b/src/web-map.js
index ff03f80ae..a8268ea73 100644
--- a/src/web-map.js
+++ b/src/web-map.js
@@ -1132,6 +1132,7 @@ export class WebMap extends HTMLMapElement {
this._traversalCall = 1;
this._map.panBy([initialLocation.x - curr.x, initialLocation.y - curr.y]);
}
+ this._map.getContainer().focus();
}
_toggleFullScreen() {
diff --git a/test/e2e/api/locateApi.test.js b/test/e2e/api/locateApi.test.js
index eb76d56b0..a2af63e66 100644
--- a/test/e2e/api/locateApi.test.js
+++ b/test/e2e/api/locateApi.test.js
@@ -81,13 +81,7 @@ test.describe('Locate API Test', () => {
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.getByTitle('Show my location - location tracking off').click();
await page.mouse.move(600, 300);
await page.mouse.down();
diff --git a/test/e2e/core/featureIndexOverlay.html b/test/e2e/core/featureIndexOverlay.html
index 417f9ab87..f6226545e 100644
--- a/test/e2e/core/featureIndexOverlay.html
+++ b/test/e2e/core/featureIndexOverlay.html
@@ -14,11 +14,10 @@
-
+
-
+
@@ -36,6 +35,27 @@
+
+
+
+
+
+
+
+
+
+
+ Test link
+
+
+
+ -75.705278 45.397778
+
+
+
+
+
+
diff --git a/test/e2e/core/featureIndexOverlayFocus.test.js b/test/e2e/core/featureIndexOverlayFocus.test.js
new file mode 100644
index 000000000..6b4d8051a
--- /dev/null
+++ b/test/e2e/core/featureIndexOverlayFocus.test.js
@@ -0,0 +1,162 @@
+import { test, expect, chromium } from '@playwright/test';
+
+test.use({
+ geolocation: { longitude: -75.705278, latitude: 45.397778 },
+ permissions: ['geolocation']
+});
+
+test.describe('Feature Index Overlay Focus tests', () => {
+ let page;
+ let context;
+ test.beforeAll(async () => {
+ context = await chromium.launchPersistentContext('', {
+ headless: false,
+ slowMo: 250
+ });
+ page =
+ context.pages().find((page) => page.url() === 'about:blank') ||
+ (await context.newPage());
+ await page.goto('featureIndexOverlay.html');
+ });
+
+ test.afterAll(async function () {
+ await context.close();
+ });
+
+ test('Feature index overlay and reticle shows on focus', async () => {
+ const hiddenOverlay = await page.$eval(
+ 'div > output.mapml-feature-index',
+ (output) => output.classList.contains('mapml-screen-reader-output')
+ );
+ const hiddenReticle = await page.$eval(
+ 'div > div.mapml-feature-index-box',
+ (div) => div.hasAttribute('hidden')
+ );
+
+ await page.keyboard.press('Tab');
+ await page.waitForTimeout(500);
+ const afterTabOverlay = await page.$eval(
+ 'div > output.mapml-feature-index',
+ (output) => output.classList.contains('mapml-screen-reader-output')
+ );
+ const afterTabReticle = await page.$eval(
+ 'div > div.mapml-feature-index-box',
+ (div) => div.hasAttribute('hidden')
+ );
+
+ expect(hiddenOverlay).toEqual(true);
+ expect(hiddenReticle).toEqual(true);
+ expect(afterTabOverlay).toEqual(false);
+ expect(afterTabReticle).toEqual(false);
+ });
+ test('Feature index overlay and reticle show on fullscreen', async () => {
+ await page.locator('#map1').getByTitle('View Fullscreen').click();
+ const afterFullscreenReticle = page.locator(
+ '#map1 .mapml-feature-index-box'
+ );
+ expect(await afterFullscreenReticle.isHidden()).toBe(false);
+
+ const afterFullscreenOutput = page.locator(
+ '#map1 output.mapml-feature-index'
+ );
+ expect(
+ await afterFullscreenOutput.evaluate((o) =>
+ o.classList.contains('mapml-screen-reader-output')
+ )
+ ).toBe(false);
+ await page.locator('#map1').getByTitle('Exit Fullscreen').click();
+ });
+ test('Feature index overlay and reticle show on reload', async () => {
+ await page.keyboard.press('ArrowRight');
+ await page.locator('#map1').getByTitle('Reload').click();
+ const afterReloadReticle = page.locator('#map1 .mapml-feature-index-box');
+ expect(await afterReloadReticle.isHidden()).toBe(false);
+
+ const afterReloadOutput = page.locator('#map1 output.mapml-feature-index');
+ expect(
+ await afterReloadOutput.evaluate((o) =>
+ o.classList.contains('mapml-screen-reader-output')
+ )
+ ).toBe(false);
+ });
+ test('Feature index overlay and reticle show on history-based navigation', async () => {
+ await page.keyboard.press('ArrowRight');
+ await page.keyboard.press('ArrowUp');
+ await page.keyboard.press('ArrowRight');
+ await page.keyboard.press('ArrowLeft');
+ await page.keyboard.press('Shift+F10');
+ await page.locator('#map1').getByText('Back').click();
+ await page.keyboard.press('Shift+F10');
+ await page.locator('#map1').getByText('Back').click();
+ const afterHistoryNavReticle = page.locator(
+ '#map1 .mapml-feature-index-box'
+ );
+ expect(await afterHistoryNavReticle.isHidden()).toBe(false);
+
+ const afterHistoryNavOutput = page.locator(
+ '#map1 output.mapml-feature-index'
+ );
+ expect(
+ await afterHistoryNavOutput.evaluate((o) =>
+ o.classList.contains('mapml-screen-reader-output')
+ )
+ ).toBe(false);
+ await page.locator('#map1').getByTitle('Reload').click();
+ });
+ test('Feature index overlay and reticle show on geolocation activation, deactivation', async () => {
+ await page
+ .locator('#map3')
+ .getByTitle('Show my location - location tracking off')
+ .click();
+ const afterGeolocationStartReticle = page.locator(
+ '#map3 .mapml-feature-index-box'
+ );
+ expect(await afterGeolocationStartReticle.isHidden()).toBe(false);
+
+ const afterGeolocationStartOutput = page.locator(
+ '#map3 output.mapml-feature-index'
+ );
+ expect(
+ await afterGeolocationStartOutput.evaluate((o) =>
+ o.classList.contains('mapml-screen-reader-output')
+ )
+ ).toBe(false);
+
+ await page
+ .locator('#map3')
+ .getByTitle('Show my location - location tracking on')
+ .click();
+ const afterGeolocationStopReticle = page.locator(
+ '#map3 .mapml-feature-index-box'
+ );
+ expect(await afterGeolocationStopReticle.isHidden()).toBe(false);
+
+ const afterGeolocationStopOutput = page.locator(
+ '#map3 output.mapml-feature-index'
+ );
+ expect(
+ await afterGeolocationStopOutput.evaluate((o) =>
+ o.classList.contains('mapml-screen-reader-output')
+ )
+ ).toBe(false);
+ await page.locator('#map3').getByTitle('Reload').click();
+ });
+ test('Feature index overlay and reticle show after following a link', async () => {
+ await page.locator('#map3').scrollIntoViewIfNeeded();
+ await page.locator('#map3 .leaflet-interactive.map-a').click();
+ const afterFollowingLinkReticle = page.locator(
+ '#map3 .mapml-feature-index-box'
+ );
+ expect(await afterFollowingLinkReticle.isHidden()).toBe(false);
+
+ const afterFollowingLinkOutput = page.locator(
+ '#map3 output.mapml-feature-index'
+ );
+ expect(
+ await afterFollowingLinkOutput.evaluate((o) =>
+ o.classList.contains('mapml-screen-reader-output')
+ )
+ ).toBe(false);
+ await page.locator('#map3').getByTitle('Reload').click();
+ });
+});
diff --git a/test/e2e/core/featureIndexOverlay.test.js b/test/e2e/core/featureIndexOverlayResults.test.js
similarity index 77%
rename from test/e2e/core/featureIndexOverlay.test.js
rename to test/e2e/core/featureIndexOverlayResults.test.js
index 8657fd396..2a5911a5b 100644
--- a/test/e2e/core/featureIndexOverlay.test.js
+++ b/test/e2e/core/featureIndexOverlayResults.test.js
@@ -1,6 +1,6 @@
import { test, expect, chromium } from '@playwright/test';
-test.describe('Feature Index Overlay test', () => {
+test.describe('Feature Index Overlay results test', () => {
let page;
let context;
test.beforeAll(async () => {
@@ -15,34 +15,8 @@ test.describe('Feature Index Overlay test', () => {
await context.close();
});
- test('Feature index overlay and reticle shows on focus', async () => {
- const hiddenOverlay = await page.$eval(
- 'div > output.mapml-feature-index',
- (output) => output.classList.contains('mapml-screen-reader-output')
- );
- const hiddenReticle = await page.$eval(
- 'div > div.mapml-feature-index-box',
- (div) => div.hasAttribute('hidden')
- );
-
- await page.keyboard.press('Tab');
- await page.waitForTimeout(500);
- const afterTabOverlay = await page.$eval(
- 'div > output.mapml-feature-index',
- (output) => output.classList.contains('mapml-screen-reader-output')
- );
- const afterTabReticle = await page.$eval(
- 'div > div.mapml-feature-index-box',
- (div) => div.hasAttribute('hidden')
- );
-
- expect(hiddenOverlay).toEqual(true);
- expect(hiddenReticle).toEqual(true);
- expect(afterTabOverlay).toEqual(false);
- expect(afterTabReticle).toEqual(false);
- });
-
test('Feature index content is correct', async () => {
+ await page.keyboard.press('Tab');
const spanCount = await page.$eval(
'div > output.mapml-feature-index > span',
(span) => span.childElementCount
@@ -109,21 +83,26 @@ test.describe('Feature Index Overlay test', () => {
expect(firstFeature).toContain('1 Maine');
});
- test('Feature index overlay is hidden when empty, reticle still visible', async () => {
+ test('Feature index message for "No features found", reticle still visible', async () => {
await page.keyboard.press('ArrowUp');
await page.waitForTimeout(1000);
- const overlay = await page.$eval(
+ const overlayVisible = await page.$eval(
'div > output.mapml-feature-index',
- (output) => output.classList.contains('mapml-screen-reader-output')
+ (output) => !output.classList.contains('mapml-screen-reader-output')
);
- const reticle = await page.$eval(
+ const reticleVisible = await page.$eval(
'div > div.mapml-feature-index-box',
- (div) => div.hasAttribute('hidden')
+ (div) => !div.hasAttribute('hidden')
+ );
+ const message = await page.$eval(
+ '.mapml-feature-index-content > span',
+ (message) => message.textContent
);
- expect(overlay).toEqual(true);
- expect(reticle).toEqual(false);
+ expect(overlayVisible).toEqual(true);
+ expect(reticleVisible).toEqual(true);
+ expect(message).toEqual('No features found');
});
test('Popup test with templated features', async () => {
diff --git a/test/e2e/layers/multipleQueryExtents.test.js b/test/e2e/layers/multipleQueryExtents.test.js
index 5a57ae6f7..c5c0b58f5 100644
--- a/test/e2e/layers/multipleQueryExtents.test.js
+++ b/test/e2e/layers/multipleQueryExtents.test.js
@@ -52,20 +52,12 @@ test.describe('Multiple Extent Query Tests', () => {
test('Querying overlapping extents, user is able to navigate into second set of query results using popup controls', async () => {
let feature;
- let nextFeatureButton =
- 'div > div.leaflet-pane.leaflet-map-pane > div.leaflet-pane.leaflet-popup-pane > div > div.leaflet-popup-content-wrapper > div > div > nav > button:nth-child(4)';
- await page.click(nextFeatureButton);
- // await page.waitForTimeout(500);
- await page.click(nextFeatureButton);
- // await page.waitForTimeout(500);
- await page.click(nextFeatureButton);
- // await page.waitForTimeout(500);
- await page.click(nextFeatureButton);
- // await page.waitForTimeout(500);
- await page.click(nextFeatureButton);
- // await page.waitForTimeout(500);
- await page.click(nextFeatureButton);
- // await page.waitForTimeout(500);
+ await page.getByTitle('Next Feature', { exact: true }).click();
+ await page.getByTitle('Next Feature', { exact: true }).click();
+ await page.getByTitle('Next Feature', { exact: true }).click();
+ await page.getByTitle('Next Feature', { exact: true }).click();
+ await page.getByTitle('Next Feature', { exact: true }).click();
+ await page.getByTitle('Next Feature', { exact: true }).click();
const name = await page
.frameLocator('iframe')
@@ -87,10 +79,7 @@ test.describe('Multiple Extent Query Tests', () => {
});
test("Navigate back from second query result set to end of first query result set by clicking '< / Previous'", async () => {
- // click the '<' (previous) button in the popup.
- await page.click(
- 'div > div.leaflet-pane.leaflet-map-pane > div.leaflet-pane.leaflet-popup-pane > div > div.leaflet-popup-content-wrapper > div > div > nav > button:nth-child(2)'
- );
+ await page.getByTitle('Previous Feature', { exact: true }).click();
const feature = await page.$eval(
'div > div.leaflet-pane.leaflet-map-pane > div.leaflet-pane.leaflet-overlay-pane > div > div.mapml-vector-container > svg > g',
(g) => (g.firstElementChild ? g.firstElementChild : false)
@@ -114,10 +103,12 @@ test.describe('Multiple Extent Query Tests', () => {
await page.evaluateHandle(() =>
document.querySelector('mapml-viewer').zoomTo(10, 5, 0)
);
- await page.click('div');
- await page.waitForSelector(
- 'div > div.leaflet-pane.leaflet-map-pane > div.leaflet-pane.leaflet-popup-pane > div'
- );
+ await page.locator('mapml-viewer').click({ position: { x: 250, y: 250 } });
+ await page
+ .locator(
+ 'div > div.leaflet-pane.leaflet-map-pane > div.leaflet-pane.leaflet-popup-pane > div'
+ )
+ .waitFor();
const popupNum = await page.$eval(
'div > div.leaflet-pane.leaflet-map-pane > div.leaflet-pane.leaflet-popup-pane',
(div) => div.childElementCount
@@ -126,32 +117,23 @@ test.describe('Multiple Extent Query Tests', () => {
});
test('Only features from one extent are returned for queries inside its (non overlapping) bounds', async () => {
- var numFeatures = await page.$eval(
- 'div > div.leaflet-pane.leaflet-map-pane > div.leaflet-pane.leaflet-popup-pane > div > div.leaflet-popup-content-wrapper > div > div > nav > p',
- (p) => p.innerText
- );
- expect(numFeatures).toEqual('1/6');
- for (let i = 0; i < 6; i++) {
- await page.click(
- 'div > div.leaflet-pane.leaflet-map-pane > div.leaflet-pane.leaflet-popup-pane > div > div.leaflet-popup-content-wrapper > div > div > nav > button:nth-child(4)'
- );
- await page.waitForSelector(
- 'div > div.leaflet-pane.leaflet-map-pane > div.leaflet-pane.leaflet-popup-pane > div > div.leaflet-popup-content-wrapper > div > div > iframe'
- );
- }
- let feature = await page.$eval(
- 'div > div.leaflet-pane.leaflet-map-pane > div.leaflet-pane.leaflet-overlay-pane > div > div.mapml-vector-container > svg > g',
- (g) => (g.firstElementChild ? g.firstElementChild : false)
- );
- expect(feature).toBeFalsy();
-
- const popup = await page.$eval(
- 'div > div.leaflet-pane.leaflet-map-pane > div.leaflet-pane.leaflet-popup-pane > div > div.leaflet-popup-content-wrapper > div > div > iframe',
+ await page.getByRole('button', { name: 'Close popup' }).click();
+ await page.locator('mapml-viewer').click({ position: { x: 450, y: 150 } });
+ await page.getByTitle('Next Feature').click();
+ await page.getByTitle('Next Feature').click();
+ await page.getByTitle('Next Feature').click();
+ await page.getByTitle('Next Feature').click();
+ await page.getByTitle('Next Feature').click();
+ let feature = page.locator('.mapml-vector-container > svg > g');
+ await expect(feature).toBeEmpty();
+
+ const frame = page.locator('iframe');
+ const popup = await frame.evaluate(
(iframe) => iframe.contentWindow.document.querySelector('h1').innerText
);
expect(popup).toEqual('No Geometry');
- numFeatures = await page.$eval(
+ let numFeatures = await page.$eval(
'div > div.leaflet-pane.leaflet-map-pane > div.leaflet-pane.leaflet-popup-pane > div > div.leaflet-popup-content-wrapper > div > div > nav > p',
(p) => p.innerText
);
@@ -159,14 +141,11 @@ test.describe('Multiple Extent Query Tests', () => {
});
test('No features returned when queried outside of bounds of all extents', async () => {
+ await page.keyboard.press('Escape');
await page.evaluateHandle(() =>
document.querySelector('mapml-viewer').zoomTo(-18, 5, 0)
);
- await page.click('div');
- await page.waitForSelector(
- 'div > div.leaflet-pane.leaflet-map-pane > div.leaflet-pane.leaflet-popup-pane > div',
- { state: 'hidden' }
- );
+ await page.locator('mapml-viewer').click({ position: { x: 400, y: 250 } });
const popupNumRight = await page.$eval(
'div > div.leaflet-pane.leaflet-map-pane > div.leaflet-pane.leaflet-popup-pane',
(div) => div.childElementCount
@@ -175,7 +154,7 @@ test.describe('Multiple Extent Query Tests', () => {
await page.evaluateHandle(() =>
document.querySelector('mapml-viewer').zoomTo(-16, -40, 0)
);
- await page.click('div');
+ await page.locator('mapml-viewer').click({ position: { x: 250, y: 400 } });
const popupNumBottom = await page.$eval(
'div > div.leaflet-pane.leaflet-map-pane > div.leaflet-pane.leaflet-popup-pane',
(div) => div.childElementCount
@@ -184,7 +163,7 @@ test.describe('Multiple Extent Query Tests', () => {
await page.evaluateHandle(() =>
document.querySelector('mapml-viewer').zoomTo(33, -170, 0)
);
- await page.click('div');
+ await page.locator('mapml-viewer').click({ position: { x: 50, y: 250 } });
const popupNumLeft = await page.$eval(
'div > div.leaflet-pane.leaflet-map-pane > div.leaflet-pane.leaflet-popup-pane',
(div) => div.childElementCount
@@ -193,7 +172,7 @@ test.describe('Multiple Extent Query Tests', () => {
await page.evaluateHandle(() =>
document.querySelector('mapml-viewer').zoomTo(30, 98, 0)
);
- await page.click('div');
+ await page.locator('mapml-viewer').click({ position: { x: 250, y: 50 } });
const popupNumTop = await page.$eval(
'div > div.leaflet-pane.leaflet-map-pane > div.leaflet-pane.leaflet-popup-pane',
(div) => div.childElementCount
diff --git a/test/e2e/mapml-viewer/locateButton.test.js b/test/e2e/mapml-viewer/locateButton.test.js
index 6d7a8c0e6..f541f3225 100644
--- a/test/e2e/mapml-viewer/locateButton.test.js
+++ b/test/e2e/mapml-viewer/locateButton.test.js
@@ -22,14 +22,7 @@ test.describe('Geolocation control tests', () => {
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('Tab');
- await page.keyboard.press('Enter');
+ await page.getByTitle('Show my location - location tracking off').click();
let locateButton_lat = await page.$eval(
'body > mapml-viewer',
@@ -54,14 +47,7 @@ test.describe('Geolocation control tests', () => {
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('Tab');
- await page.keyboard.press('Enter');
+ await page.getByTitle('Show my location - location tracking on').click();
let locationOnText = await page.evaluate(
() => M.options.locale.btnLocTrackOn
@@ -79,7 +65,7 @@ test.describe('Geolocation control tests', () => {
);
expect(locateButton_title1).toEqual(locationOffText);
- await page.keyboard.press('Enter');
+ await page.getByTitle('Show my location - location tracking off').click();
let locateButton_title2 = await page.$eval(
'div > div.leaflet-control-container > div.leaflet-bottom.leaflet-right > div > a',