From 8d7de0f3d57e1cc9278a5edba34e10d9a1bc08e9 Mon Sep 17 00:00:00 2001 From: Peter Rushforth Date: Fri, 30 Jun 2023 17:29:40 -0400 Subject: [PATCH 01/14] WIP on getting featureIndexOverlay to behave --- src/mapml/layers/FeatureIndexOverlay.js | 10 +++------- src/mapml/layers/MapMLLayer.js | 1 + test/e2e/core/featureIndexOverlay.test.js | 11 ++++++----- 3 files changed, 10 insertions(+), 12 deletions(-) diff --git a/src/mapml/layers/FeatureIndexOverlay.js b/src/mapml/layers/FeatureIndexOverlay.js index 813f67a5a..c6acece94 100644 --- a/src/mapml/layers/FeatureIndexOverlay.js +++ b/src/mapml/layers/FeatureIndexOverlay.js @@ -190,11 +190,7 @@ export var FeatureIndexOverlay = L.Layer.extend({ }, _toggleEvents: function () { - this._map.on( - 'viewreset move moveend focus blur popupclose', - this._addOrRemoveFeatureIndex, - this - ); + this._map.on('focus blur popupclose', this._addOrRemoveFeatureIndex, this); }, _addOrRemoveFeatureIndex: function (e) { @@ -214,8 +210,8 @@ 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'); + // 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'); 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/test/e2e/core/featureIndexOverlay.test.js b/test/e2e/core/featureIndexOverlay.test.js index 8657fd396..5fc79e12e 100644 --- a/test/e2e/core/featureIndexOverlay.test.js +++ b/test/e2e/core/featureIndexOverlay.test.js @@ -109,21 +109,22 @@ 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 overlay is visible when empty, reticle still visible', async () => { + await page.pause(); 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') ); - const reticle = await page.$eval( + const reticleVisible = await page.$eval( 'div > div.mapml-feature-index-box', (div) => div.hasAttribute('hidden') ); - expect(overlay).toEqual(true); - expect(reticle).toEqual(false); + expect(overlayVisible).toEqual(true); + expect(reticleVisible).toEqual(true); }); test('Popup test with templated features', async () => { From a409e66943ff915fc758d634c6b66876cc85f84e Mon Sep 17 00:00:00 2001 From: Peter Rushforth Date: Wed, 5 Jul 2023 16:13:52 -0400 Subject: [PATCH 02/14] Add localized message to feature index output for 'No features found' --- src/mapml/layers/FeatureIndexOverlay.js | 5 +++++ src/mapml/options.js | 3 ++- test/e2e/core/featureIndexOverlay.test.js | 11 ++++++++--- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/mapml/layers/FeatureIndexOverlay.js b/src/mapml/layers/FeatureIndexOverlay.js index c6acece94..7c6239a33 100644 --- a/src/mapml/layers/FeatureIndexOverlay.js +++ b/src/mapml/layers/FeatureIndexOverlay.js @@ -77,6 +77,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 +125,9 @@ export var FeatureIndexOverlay = L.Layer.extend({ } }); this._addToggleKeys(); + if (index === 1) { + body.appendChild(noFeaturesMessage); + } }, _updateOutput: function (label, index, key) { 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/test/e2e/core/featureIndexOverlay.test.js b/test/e2e/core/featureIndexOverlay.test.js index 5fc79e12e..030d19ff8 100644 --- a/test/e2e/core/featureIndexOverlay.test.js +++ b/test/e2e/core/featureIndexOverlay.test.js @@ -109,22 +109,27 @@ test.describe('Feature Index Overlay test', () => { expect(firstFeature).toContain('1 Maine'); }); - test('Feature index overlay is visible when empty, reticle still visible', async () => { + test('Feature index message for "No features found", reticle still visible', async () => { await page.pause(); await page.keyboard.press('ArrowUp'); await page.waitForTimeout(1000); 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 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(overlayVisible).toEqual(true); expect(reticleVisible).toEqual(true); + expect(message).toEqual('No features found'); }); test('Popup test with templated features', async () => { From ab95f20caa2d5ea7d1f539f34f79510eb6dda384 Mon Sep 17 00:00:00 2001 From: Peter Rushforth Date: Wed, 5 Jul 2023 17:11:41 -0400 Subject: [PATCH 03/14] Remove commented code --- src/mapml/layers/FeatureIndexOverlay.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/mapml/layers/FeatureIndexOverlay.js b/src/mapml/layers/FeatureIndexOverlay.js index 7c6239a33..d44105cad 100644 --- a/src/mapml/layers/FeatureIndexOverlay.js +++ b/src/mapml/layers/FeatureIndexOverlay.js @@ -215,7 +215,6 @@ 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', ''); From fb0e1bce76c3acd8cca81c2e3393d66ad568f0a1 Mon Sep 17 00:00:00 2001 From: prushfor Date: Thu, 6 Jul 2023 14:24:14 -0400 Subject: [PATCH 04/14] Remove unused / uncovered code, add comments. Update _handleLink to focus map when finished handling, so that the feature index shows up again (if enabled). --- src/mapml/layers/FeatureIndexOverlay.js | 20 +++++++++----------- src/mapml/utils/Util.js | 1 + 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/mapml/layers/FeatureIndexOverlay.js b/src/mapml/layers/FeatureIndexOverlay.js index d44105cad..32d021841 100644 --- a/src/mapml/layers/FeatureIndexOverlay.js +++ b/src/mapml/layers/FeatureIndexOverlay.js @@ -199,7 +199,6 @@ export var FeatureIndexOverlay = L.Layer.extend({ }, _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'); @@ -216,9 +215,14 @@ export var FeatureIndexOverlay = L.Layer.extend({ } else if (e && e.type === 'focus') { this._container.removeAttribute('hidden'); 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 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 +230,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/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 From ec8b7101ad2540a5b192cc31ee16d0c6131d6911 Mon Sep 17 00:00:00 2001 From: prushfor Date: Thu, 6 Jul 2023 15:29:59 -0400 Subject: [PATCH 05/14] Ensure feature index and reticle displayed after reload Ensure feature index and reticle displayed before and after fullscreen --- src/mapml-viewer.js | 1 + src/mapml/control/FullscreenButton.js | 1 + src/web-map.js | 1 + 3 files changed, 3 insertions(+) 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/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/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() { From 8daed41831cc333b29aad967f750aef6d3ae2b9b Mon Sep 17 00:00:00 2001 From: prushfor Date: Thu, 6 Jul 2023 17:21:18 -0400 Subject: [PATCH 06/14] Upgrade playwright Add tests for feature index and reticle display after fullscreen, history navigation, link activation. --- package-lock.json | 105 ++++++++++++++++------ package.json | 4 +- test/e2e/core/featureIndexOverlay.html | 26 +++++- test/e2e/core/featureIndexOverlay.test.js | 54 ++++++++++- 4 files changed, 153 insertions(+), 36 deletions(-) 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/test/e2e/core/featureIndexOverlay.html b/test/e2e/core/featureIndexOverlay.html index 417f9ab87..c51302005 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/featureIndexOverlay.test.js b/test/e2e/core/featureIndexOverlay.test.js index 030d19ff8..c0a5f8d72 100644 --- a/test/e2e/core/featureIndexOverlay.test.js +++ b/test/e2e/core/featureIndexOverlay.test.js @@ -4,7 +4,10 @@ test.describe('Feature Index Overlay test', () => { let page; let context; test.beforeAll(async () => { - context = await chromium.launchPersistentContext(''); + context = await chromium.launchPersistentContext('', { + headless: false, + slowMo: 250 + }); page = context.pages().find((page) => page.url() === 'about:blank') || (await context.newPage()); @@ -41,7 +44,53 @@ test.describe('Feature Index Overlay test', () => { 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.only('Feature index overlay and reticle show after following a link', async () => { + await page.locator('#map3').scrollIntoViewIfNeeded(); + + //const feature = page.locator('#linkedfeature').click({force: true}); + + }); test('Feature index content is correct', async () => { const spanCount = await page.$eval( 'div > output.mapml-feature-index > span', @@ -110,7 +159,6 @@ test.describe('Feature Index Overlay test', () => { }); test('Feature index message for "No features found", reticle still visible', async () => { - await page.pause(); await page.keyboard.press('ArrowUp'); await page.waitForTimeout(1000); From ab0cf4a5b94691c81049747ef8d1c4fce3256d0e Mon Sep 17 00:00:00 2001 From: Peter Rushforth Date: Fri, 7 Jul 2023 15:37:34 -0400 Subject: [PATCH 07/14] De-flake a test that got really bad post-upgrade of playwright --- test/e2e/layers/multipleQueryExtents.test.js | 81 +++++++------------- 1 file changed, 28 insertions(+), 53 deletions(-) diff --git a/test/e2e/layers/multipleQueryExtents.test.js b/test/e2e/layers/multipleQueryExtents.test.js index 5a57ae6f7..b65e4c965 100644 --- a/test/e2e/layers/multipleQueryExtents.test.js +++ b/test/e2e/layers/multipleQueryExtents.test.js @@ -52,21 +52,13 @@ 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') .locator('h1') @@ -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,8 @@ 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 +113,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 +137,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 +150,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 +159,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 +168,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 From a1469706f82fc177e6248a8037cdac745bbd4ecf Mon Sep 17 00:00:00 2001 From: Peter Rushforth Date: Fri, 7 Jul 2023 15:41:16 -0400 Subject: [PATCH 08/14] Remove layerchange layeradd layerremove overlayremove events from feature index handlers; the index and reticle display on map focus, disappear when focus moves off map. This seems a bit odd when adding or removing layers, but it is simple, and reticle returns with focus. --- src/mapml/layers/FeatureIndexOverlay.js | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/mapml/layers/FeatureIndexOverlay.js b/src/mapml/layers/FeatureIndexOverlay.js index 32d021841..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(); @@ -194,10 +190,6 @@ export var FeatureIndexOverlay = L.Layer.extend({ } }, - _toggleEvents: function () { - this._map.on('focus blur popupclose', this._addOrRemoveFeatureIndex, this); - }, - _addOrRemoveFeatureIndex: function (e) { //Toggle aria-hidden attribute so screen reader rereads the feature index on focus if (!this._output.initialFocus) { From a55c9e9c769fc7f2f48f45d2c75b70a0bc7984df Mon Sep 17 00:00:00 2001 From: Peter Rushforth Date: Fri, 7 Jul 2023 15:42:48 -0400 Subject: [PATCH 09/14] Update GeolocationButton to focus the map after it locates the user. Update geolocation tests to adapt to changes. --- src/mapml/control/GeolocationButton.js | 34 ++++++++++++++-------- test/e2e/api/locateApi.test.js | 8 +---- test/e2e/mapml-viewer/locateButton.test.js | 20 ++----------- 3 files changed, 26 insertions(+), 36 deletions(-) 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/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/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', From 3368adb2035a947e0b5819670abd5e516b95fbf5 Mon Sep 17 00:00:00 2001 From: Peter Rushforth Date: Fri, 7 Jul 2023 15:44:25 -0400 Subject: [PATCH 10/14] Add feature index tests for after reload, fullscreen, history nav, geolocation activation, --- test/e2e/core/featureIndexOverlay.test.js | 53 ++++++++++++++--------- 1 file changed, 33 insertions(+), 20 deletions(-) diff --git a/test/e2e/core/featureIndexOverlay.test.js b/test/e2e/core/featureIndexOverlay.test.js index c0a5f8d72..82b8fb99d 100644 --- a/test/e2e/core/featureIndexOverlay.test.js +++ b/test/e2e/core/featureIndexOverlay.test.js @@ -46,13 +46,19 @@ test.describe('Feature Index Overlay test', () => { }); 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'); + 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); + + 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 () => { @@ -60,12 +66,13 @@ test.describe('Feature Index Overlay test', () => { 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); - + 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'); @@ -76,20 +83,26 @@ test.describe('Feature Index Overlay test', () => { 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'); + 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); + + 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.only('Feature index overlay and reticle show after following a link', async () => { + test('Feature index overlay and reticle show on geolocation activation, deactivation', async () => {}); + test('Feature index overlay and reticle show after following a link', async () => { await page.locator('#map3').scrollIntoViewIfNeeded(); - + //const feature = page.locator('#linkedfeature').click({force: true}); - }); test('Feature index content is correct', async () => { const spanCount = await page.$eval( From 176cc372c2ad48a3d0cae2e8297a88e3ac44b6fc Mon Sep 17 00:00:00 2001 From: Peter Rushforth Date: Fri, 7 Jul 2023 16:45:24 -0400 Subject: [PATCH 11/14] Add test feature index presence post-geolocation activation, deactivation, and link following actions --- test/e2e/core/featureIndexOverlay.html | 2 +- test/e2e/core/featureIndexOverlay.test.js | 68 ++++++++++++++++++-- test/e2e/layers/multipleQueryExtents.test.js | 34 +++++----- 3 files changed, 82 insertions(+), 22 deletions(-) diff --git a/test/e2e/core/featureIndexOverlay.html b/test/e2e/core/featureIndexOverlay.html index c51302005..880d83fcc 100644 --- a/test/e2e/core/featureIndexOverlay.html +++ b/test/e2e/core/featureIndexOverlay.html @@ -35,7 +35,7 @@ - + diff --git a/test/e2e/core/featureIndexOverlay.test.js b/test/e2e/core/featureIndexOverlay.test.js index 82b8fb99d..05c065b52 100644 --- a/test/e2e/core/featureIndexOverlay.test.js +++ b/test/e2e/core/featureIndexOverlay.test.js @@ -1,5 +1,10 @@ import { test, expect, chromium } from '@playwright/test'; +test.use({ + geolocation: { longitude: -75.705278, latitude: 45.397778 }, + permissions: ['geolocation'] +}); + test.describe('Feature Index Overlay test', () => { let page; let context; @@ -98,12 +103,6 @@ test.describe('Feature Index Overlay test', () => { ).toBe(false); await page.locator('#map1').getByTitle('Reload').click(); }); - test('Feature index overlay and reticle show on geolocation activation, deactivation', async () => {}); - test('Feature index overlay and reticle show after following a link', async () => { - await page.locator('#map3').scrollIntoViewIfNeeded(); - - //const feature = page.locator('#linkedfeature').click({force: true}); - }); test('Feature index content is correct', async () => { const spanCount = await page.$eval( 'div > output.mapml-feature-index > span', @@ -241,4 +240,61 @@ test.describe('Feature Index Overlay test', () => { expect(popupName).toContain('Banditos'); expect(overlay).toEqual(false); }); + 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); + + // had to make the map3 800 px wide because the geolocation button was + // underneath the output for the feature index, and playwright logged + // that the output was intercepting pointer events (i.e. click(), below). + // this means we should make the output responsive, so that it always fits + // between the controls on the left and right bottom of the map, without + // over/underlap. + 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/layers/multipleQueryExtents.test.js b/test/e2e/layers/multipleQueryExtents.test.js index b65e4c965..c5c0b58f5 100644 --- a/test/e2e/layers/multipleQueryExtents.test.js +++ b/test/e2e/layers/multipleQueryExtents.test.js @@ -52,13 +52,13 @@ 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; - 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(); - + 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') .locator('h1') @@ -79,7 +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 () => { - await page.getByTitle('Previous Feature', {exact: true}).click(); + 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) @@ -103,8 +103,12 @@ test.describe('Multiple Extent Query Tests', () => { await page.evaluateHandle(() => document.querySelector('mapml-viewer').zoomTo(10, 5, 0) ); - 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(); + 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 @@ -114,7 +118,7 @@ test.describe('Multiple Extent Query Tests', () => { test('Only features from one extent are returned for queries inside its (non overlapping) bounds', async () => { await page.getByRole('button', { name: 'Close popup' }).click(); - await page.locator('mapml-viewer').click({ position: { x: 450, y: 150} }); + 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(); @@ -141,7 +145,7 @@ test.describe('Multiple Extent Query Tests', () => { await page.evaluateHandle(() => document.querySelector('mapml-viewer').zoomTo(-18, 5, 0) ); - await page.locator('mapml-viewer').click({ position: { x: 400, y: 250} }); + 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 @@ -150,7 +154,7 @@ test.describe('Multiple Extent Query Tests', () => { await page.evaluateHandle(() => document.querySelector('mapml-viewer').zoomTo(-16, -40, 0) ); - await page.locator('mapml-viewer').click({ position: { x: 250, y: 400} }); + 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 @@ -159,7 +163,7 @@ test.describe('Multiple Extent Query Tests', () => { await page.evaluateHandle(() => document.querySelector('mapml-viewer').zoomTo(33, -170, 0) ); - await page.locator('mapml-viewer').click({ position: { x: 50, y: 250} }); + 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 @@ -168,7 +172,7 @@ test.describe('Multiple Extent Query Tests', () => { await page.evaluateHandle(() => document.querySelector('mapml-viewer').zoomTo(30, 98, 0) ); - await page.locator('mapml-viewer').click({ position: { x: 250, y: 50} }); + 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 From ba4911682b98b020e1c14fc0eae868e71a6f3338 Mon Sep 17 00:00:00 2001 From: Peter Rushforth Date: Fri, 7 Jul 2023 17:05:40 -0400 Subject: [PATCH 12/14] Formatting by prettier --- test/e2e/core/featureIndexOverlay.test.js | 83 ++++++++++++----------- 1 file changed, 44 insertions(+), 39 deletions(-) diff --git a/test/e2e/core/featureIndexOverlay.test.js b/test/e2e/core/featureIndexOverlay.test.js index 05c065b52..84749b673 100644 --- a/test/e2e/core/featureIndexOverlay.test.js +++ b/test/e2e/core/featureIndexOverlay.test.js @@ -1,7 +1,7 @@ import { test, expect, chromium } from '@playwright/test'; test.use({ - geolocation: { longitude: -75.705278, latitude: 45.397778 }, + geolocation: { longitude: -75.705278, latitude: 45.397778 }, permissions: ['geolocation'] }); @@ -240,43 +240,48 @@ test.describe('Feature Index Overlay test', () => { expect(popupName).toContain('Banditos'); expect(overlay).toEqual(false); }); - 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); - - // had to make the map3 800 px wide because the geolocation button was - // underneath the output for the feature index, and playwright logged - // that the output was intercepting pointer events (i.e. click(), below). - // this means we should make the output responsive, so that it always fits - // between the controls on the left and right bottom of the map, without - // over/underlap. - 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); + 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); + + // had to make the map3 800 px wide because the geolocation button was + // underneath the output for the feature index, and playwright logged + // that the output was intercepting pointer events (i.e. click(), below). + // this means we should make the output responsive, so that it always fits + // between the controls on the left and right bottom of the map, without + // over/underlap. + 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 () => { @@ -295,6 +300,6 @@ test.describe('Feature Index Overlay test', () => { o.classList.contains('mapml-screen-reader-output') ) ).toBe(false); - await page.locator('#map3').getByTitle('Reload').click(); + await page.locator('#map3').getByTitle('Reload').click(); }); }); From 183a6ee8dbbbe7e9a59073def54970e7ba531446 Mon Sep 17 00:00:00 2001 From: Peter Rushforth Date: Fri, 7 Jul 2023 17:27:06 -0400 Subject: [PATCH 13/14] Split up slow test --- ...st.js => featureIndexOverlayFocus.test.js} | 139 +--------------- .../core/featureIndexOverlayResults.test.js | 156 ++++++++++++++++++ 2 files changed, 157 insertions(+), 138 deletions(-) rename test/e2e/core/{featureIndexOverlay.test.js => featureIndexOverlayFocus.test.js} (56%) create mode 100644 test/e2e/core/featureIndexOverlayResults.test.js diff --git a/test/e2e/core/featureIndexOverlay.test.js b/test/e2e/core/featureIndexOverlayFocus.test.js similarity index 56% rename from test/e2e/core/featureIndexOverlay.test.js rename to test/e2e/core/featureIndexOverlayFocus.test.js index 84749b673..a6bdfafed 100644 --- a/test/e2e/core/featureIndexOverlay.test.js +++ b/test/e2e/core/featureIndexOverlayFocus.test.js @@ -5,7 +5,7 @@ test.use({ permissions: ['geolocation'] }); -test.describe('Feature Index Overlay test', () => { +test.describe('Feature Index Overlay Focus tests', () => { let page; let context; test.beforeAll(async () => { @@ -103,143 +103,6 @@ test.describe('Feature Index Overlay test', () => { ).toBe(false); await page.locator('#map1').getByTitle('Reload').click(); }); - test('Feature index content is correct', async () => { - const spanCount = await page.$eval( - 'div > output.mapml-feature-index > span', - (span) => span.childElementCount - ); - const firstFeature = await page.$eval( - 'div > output.mapml-feature-index > span > span:nth-child(1)', - (span) => span.innerText - ); - const moreResults = await page.$eval( - 'div > output.mapml-feature-index > span > span:nth-child(8)', - (span) => span.innerText - ); - - expect(spanCount).toEqual(8); - expect(firstFeature).toContain('1 Vermont'); - expect(moreResults).toContain('9 More results'); - }); - - test('Feature index more results are correct', async () => { - await page.keyboard.press('9'); - await page.waitForTimeout(500); - - const spanCount = await page.$eval( - 'div > output.mapml-feature-index > span', - (span) => span.childElementCount - ); - const firstFeature = await page.$eval( - 'div > output.mapml-feature-index > span > span:nth-child(1)', - (span) => span.innerText - ); - const prevResults = await page.$eval( - 'div > output.mapml-feature-index > span > span:nth-child(5)', - (span) => span.innerText - ); - - expect(spanCount).toEqual(5); - expect(firstFeature).toContain('1 Pennsylvania'); - expect(prevResults).toContain('8 Previous results'); - }); - - test('Feature index previous results are correct', async () => { - await page.keyboard.press('8'); - const spanCount = await page.$eval( - 'div > output.mapml-feature-index > span', - (span) => span.childElementCount - ); - - expect(spanCount).toEqual(8); - }); - - test('Feature index content is correct on moveend', async () => { - await page.keyboard.press('ArrowRight'); - await page.waitForTimeout(1000); - const spanCount = await page.$eval( - 'div > output.mapml-feature-index > span', - (span) => span.childElementCount - ); - const firstFeature = await page.$eval( - 'div > output.mapml-feature-index > span > span:nth-child(1)', - (span) => span.innerText - ); - - expect(spanCount).toEqual(2); - expect(firstFeature).toContain('1 Maine'); - }); - - test('Feature index message for "No features found", reticle still visible', async () => { - await page.keyboard.press('ArrowUp'); - await page.waitForTimeout(1000); - - const overlayVisible = await page.$eval( - 'div > output.mapml-feature-index', - (output) => !output.classList.contains('mapml-screen-reader-output') - ); - const reticleVisible = await page.$eval( - 'div > div.mapml-feature-index-box', - (div) => !div.hasAttribute('hidden') - ); - const message = await page.$eval( - '.mapml-feature-index-content > span', - (message) => message.textContent - ); - - expect(overlayVisible).toEqual(true); - expect(reticleVisible).toEqual(true); - expect(message).toEqual('No features found'); - }); - - test('Popup test with templated features', async () => { - await page.mouse.click(10, 600); - await page.waitForTimeout(500); - await page.focus('#map2 > div'); - await page.waitForTimeout(500); - - await page.keyboard.press('ArrowRight'); - await page.waitForTimeout(1000); - await page.keyboard.press('Control+ArrowUp'); - await page.waitForTimeout(1000); - - await page.keyboard.press('1'); - await page.waitForTimeout(500); - - const popupCount = await page.$eval( - '#map2 > div > div.leaflet-pane.leaflet-map-pane > div.leaflet-pane.leaflet-popup-pane', - (popup) => popup.childElementCount - ); - const popupName = await page.$eval( - '#map2 > div > div.leaflet-pane.leaflet-map-pane > div.leaflet-pane.leaflet-popup-pane', - (popup) => popup.children[0].innerText - ); - - expect(popupCount).toEqual(1); - expect(popupName).toContain('Hareg Cafe & Variety'); - }); - - test('Opening another popup with index keys closes already open popup', async () => { - await page.keyboard.press('2'); - await page.waitForTimeout(500); - - const popupCount = await page.$eval( - '#map2 > div > div.leaflet-pane.leaflet-map-pane > div.leaflet-pane.leaflet-popup-pane', - (popup) => popup.childElementCount - ); - const popupName = await page.$eval( - '#map2 > div > div.leaflet-pane.leaflet-map-pane > div.leaflet-pane.leaflet-popup-pane', - (popup) => popup.children[0].innerText - ); - const overlay = await page.$eval( - '#map2 > div > output.mapml-feature-index', - (output) => output.classList.contains('mapml-screen-reader-output') - ); - - expect(popupCount).toEqual(1); - expect(popupName).toContain('Banditos'); - expect(overlay).toEqual(false); - }); test('Feature index overlay and reticle show on geolocation activation, deactivation', async () => { await page .locator('#map3') diff --git a/test/e2e/core/featureIndexOverlayResults.test.js b/test/e2e/core/featureIndexOverlayResults.test.js new file mode 100644 index 000000000..2a5911a5b --- /dev/null +++ b/test/e2e/core/featureIndexOverlayResults.test.js @@ -0,0 +1,156 @@ +import { test, expect, chromium } from '@playwright/test'; + +test.describe('Feature Index Overlay results test', () => { + let page; + let context; + test.beforeAll(async () => { + context = await chromium.launchPersistentContext(''); + 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 content is correct', async () => { + await page.keyboard.press('Tab'); + const spanCount = await page.$eval( + 'div > output.mapml-feature-index > span', + (span) => span.childElementCount + ); + const firstFeature = await page.$eval( + 'div > output.mapml-feature-index > span > span:nth-child(1)', + (span) => span.innerText + ); + const moreResults = await page.$eval( + 'div > output.mapml-feature-index > span > span:nth-child(8)', + (span) => span.innerText + ); + + expect(spanCount).toEqual(8); + expect(firstFeature).toContain('1 Vermont'); + expect(moreResults).toContain('9 More results'); + }); + + test('Feature index more results are correct', async () => { + await page.keyboard.press('9'); + await page.waitForTimeout(500); + + const spanCount = await page.$eval( + 'div > output.mapml-feature-index > span', + (span) => span.childElementCount + ); + const firstFeature = await page.$eval( + 'div > output.mapml-feature-index > span > span:nth-child(1)', + (span) => span.innerText + ); + const prevResults = await page.$eval( + 'div > output.mapml-feature-index > span > span:nth-child(5)', + (span) => span.innerText + ); + + expect(spanCount).toEqual(5); + expect(firstFeature).toContain('1 Pennsylvania'); + expect(prevResults).toContain('8 Previous results'); + }); + + test('Feature index previous results are correct', async () => { + await page.keyboard.press('8'); + const spanCount = await page.$eval( + 'div > output.mapml-feature-index > span', + (span) => span.childElementCount + ); + + expect(spanCount).toEqual(8); + }); + + test('Feature index content is correct on moveend', async () => { + await page.keyboard.press('ArrowRight'); + await page.waitForTimeout(1000); + const spanCount = await page.$eval( + 'div > output.mapml-feature-index > span', + (span) => span.childElementCount + ); + const firstFeature = await page.$eval( + 'div > output.mapml-feature-index > span > span:nth-child(1)', + (span) => span.innerText + ); + + expect(spanCount).toEqual(2); + expect(firstFeature).toContain('1 Maine'); + }); + + test('Feature index message for "No features found", reticle still visible', async () => { + await page.keyboard.press('ArrowUp'); + await page.waitForTimeout(1000); + + const overlayVisible = await page.$eval( + 'div > output.mapml-feature-index', + (output) => !output.classList.contains('mapml-screen-reader-output') + ); + const reticleVisible = await page.$eval( + 'div > div.mapml-feature-index-box', + (div) => !div.hasAttribute('hidden') + ); + const message = await page.$eval( + '.mapml-feature-index-content > span', + (message) => message.textContent + ); + + expect(overlayVisible).toEqual(true); + expect(reticleVisible).toEqual(true); + expect(message).toEqual('No features found'); + }); + + test('Popup test with templated features', async () => { + await page.mouse.click(10, 600); + await page.waitForTimeout(500); + await page.focus('#map2 > div'); + await page.waitForTimeout(500); + + await page.keyboard.press('ArrowRight'); + await page.waitForTimeout(1000); + await page.keyboard.press('Control+ArrowUp'); + await page.waitForTimeout(1000); + + await page.keyboard.press('1'); + await page.waitForTimeout(500); + + const popupCount = await page.$eval( + '#map2 > div > div.leaflet-pane.leaflet-map-pane > div.leaflet-pane.leaflet-popup-pane', + (popup) => popup.childElementCount + ); + const popupName = await page.$eval( + '#map2 > div > div.leaflet-pane.leaflet-map-pane > div.leaflet-pane.leaflet-popup-pane', + (popup) => popup.children[0].innerText + ); + + expect(popupCount).toEqual(1); + expect(popupName).toContain('Hareg Cafe & Variety'); + }); + + test('Opening another popup with index keys closes already open popup', async () => { + await page.keyboard.press('2'); + await page.waitForTimeout(500); + + const popupCount = await page.$eval( + '#map2 > div > div.leaflet-pane.leaflet-map-pane > div.leaflet-pane.leaflet-popup-pane', + (popup) => popup.childElementCount + ); + const popupName = await page.$eval( + '#map2 > div > div.leaflet-pane.leaflet-map-pane > div.leaflet-pane.leaflet-popup-pane', + (popup) => popup.children[0].innerText + ); + const overlay = await page.$eval( + '#map2 > div > output.mapml-feature-index', + (output) => output.classList.contains('mapml-screen-reader-output') + ); + + expect(popupCount).toEqual(1); + expect(popupName).toContain('Banditos'); + expect(overlay).toEqual(false); + }); +}); From f9b1f72dc19eafe1d828cf85d1f39aefaa0070e3 Mon Sep 17 00:00:00 2001 From: Peter Rushforth Date: Sat, 8 Jul 2023 15:43:49 -0400 Subject: [PATCH 14/14] Make feature index overlay responsive to map width --- src/mapml.css | 13 +++++++++++-- test/e2e/core/featureIndexOverlay.html | 2 +- test/e2e/core/featureIndexOverlayFocus.test.js | 6 ------ 3 files changed, 12 insertions(+), 9 deletions(-) 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/test/e2e/core/featureIndexOverlay.html b/test/e2e/core/featureIndexOverlay.html index 880d83fcc..f6226545e 100644 --- a/test/e2e/core/featureIndexOverlay.html +++ b/test/e2e/core/featureIndexOverlay.html @@ -35,7 +35,7 @@ - + diff --git a/test/e2e/core/featureIndexOverlayFocus.test.js b/test/e2e/core/featureIndexOverlayFocus.test.js index a6bdfafed..6b4d8051a 100644 --- a/test/e2e/core/featureIndexOverlayFocus.test.js +++ b/test/e2e/core/featureIndexOverlayFocus.test.js @@ -122,12 +122,6 @@ test.describe('Feature Index Overlay Focus tests', () => { ) ).toBe(false); - // had to make the map3 800 px wide because the geolocation button was - // underneath the output for the feature index, and playwright logged - // that the output was intercepting pointer events (i.e. click(), below). - // this means we should make the output responsive, so that it always fits - // between the controls on the left and right bottom of the map, without - // over/underlap. await page .locator('#map3') .getByTitle('Show my location - location tracking on')