Skip to content

Commit 8d69508

Browse files
committed
De-flake popupTabNavigation, hopefully for good
1 parent 9361864 commit 8d69508

2 files changed

Lines changed: 105 additions & 154 deletions

File tree

test/e2e/core/popupTabNavigation.html

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@
99
</head>
1010

1111
<body>
12-
<mapml-viewer style="width: 500px;height: 500px;" is="web-map" projection="CBMTILE" zoom="2" lat="45.5052040"
12+
<mapml-viewer data-testid="viewer" style="width: 500px;height: 500px;" is="web-map" projection="CBMTILE" zoom="2" lat="45.5052040"
1313
lon="-75.2202344" controls>
14-
<layer- id="query" label="Fire" checked>
14+
<layer- data-testid="query" id="query" label="Fire" checked>
1515
<map-extent units="CBMTILE" checked hidden>
1616
<map-input name="z" type="zoom" value="18" min="0" max="18" ></map-input>
1717
<map-input name="txmin" type="location" units="tilematrix" position="top-left" axis="easting" min="-2.003750834E7"
@@ -46,10 +46,10 @@
4646
<map-meta name="zoom" content="min=1,max=5,value=0"></map-meta>
4747
<map-meta name="cs" content="gcrs"></map-meta>
4848
<map-meta name="extent" content="zoom=0,top-left-column=0,top-left-row=0,bottom-right-column=5,bottom-right-row=5"></map-meta>
49-
<map-feature zoom="2" class="refDiff">
49+
<map-feature data-testid="big-square" zoom="2" class="refDiff">
5050
<map-properties>
5151
<h1>Test</h1>
52-
<a href="www.example.com">test</a>
52+
<a data-testid="anchor" href="www.example.com">test</a>
5353
</map-properties>
5454
<map-geometry cs="tilematrix">
5555
<map-polygon>
@@ -69,7 +69,7 @@ <h1>Test</h1>
6969
</map-geometry>
7070
</map-feature>
7171

72-
<map-feature zoom="2" class="refDiff">
72+
<map-feature data-testid="small-trapezoid" zoom="2" class="refDiff">
7373
<map-properties>
7474
<h1>Test</h1>
7575
</map-properties>
@@ -80,7 +80,7 @@ <h1>Test</h1>
8080
</map-geometry>
8181
</map-feature>
8282
</layer->
83-
<layer- id="vector" label="vector states" src="data/us_pop_density.mapml"></layer->
83+
<layer- data-testid="vector" id="vector" label="vector states" src="data/us_pop_density.mapml"></layer->
8484
</mapml-viewer>
8585
</body>
8686

test/e2e/core/popupTabNavigation.test.js

Lines changed: 99 additions & 148 deletions
Original file line numberDiff line numberDiff line change
@@ -17,91 +17,74 @@ test.describe('Playwright Keyboard Navigation + Query Layer Tests', () => {
1717

1818
test.describe('Feature Popup Tab Navigation Tests', () => {
1919
test('Inline features popup focus order', async () => {
20-
await page.evaluateHandle(() =>
21-
document.getElementById('vector').removeAttribute('checked')
22-
);
23-
await page.evaluateHandle(() =>
24-
document.getElementById('query').removeAttribute('checked')
25-
);
26-
const body = page.locator('body');
27-
await body.click();
20+
const vectorLayer = page.getByTestId('vector');
21+
await vectorLayer.evaluate((l) => {
22+
l.removeAttribute('checked');
23+
});
24+
const queryLayer = page.getByTestId('query');
25+
await queryLayer.evaluate((l) => {
26+
l.removeAttribute('checked');
27+
});
28+
const viewer = page.locator('mapml-viewer');
29+
await viewer.focus();
2830
await page.keyboard.press('Tab'); // focus map
2931

3032
await page.keyboard.press('Tab'); // focus feature
3133
await page.keyboard.press('Enter'); // display popup with link in it
32-
const viewer = page.locator('mapml-viewer');
33-
let f = await viewer.evaluate(
34-
(viewer) => viewer.shadowRoot.activeElement.className
35-
);
36-
expect(f).toEqual('mapml-popup-content');
37-
34+
const popupContainer = page.locator('.leaflet-popup');
35+
const popup = popupContainer.locator('.mapml-popup-content');
36+
await expect(popup).toBeFocused();
3837
await page.keyboard.press('Tab'); // focus link
39-
let f2 = await viewer.evaluate(
40-
(viewer) => viewer.shadowRoot.activeElement.tagName
41-
);
42-
expect(f2.toUpperCase()).toEqual('A');
38+
// there are actually 2 copies of the testid, so we scope relative to the popup
39+
const anchor = popupContainer.getByTestId('anchor');
40+
await expect(anchor).toBeFocused();
4341

4442
await page.keyboard.press('Tab'); // focus on "zoom to here" link
45-
let f3 = await viewer.evaluate(
46-
(viewer) => viewer.shadowRoot.activeElement.tagName
47-
);
48-
expect(f3.toUpperCase()).toEqual('A');
49-
43+
const zoomToHereLink = popupContainer.getByText('Zoom to here');
44+
await expect(zoomToHereLink).toBeFocused();
45+
5046
await page.keyboard.press('Tab'); // focus on |< affordance
51-
let f4 = await viewer.evaluate(
52-
(viewer) => viewer.shadowRoot.activeElement.title
53-
);
54-
expect(f4).toEqual('Focus Map');
47+
const focusMapButton = popupContainer.getByRole('button', { name: 'Focus Map' });
48+
await expect(focusMapButton).toBeFocused();
5549

5650
await page.keyboard.press('Tab'); // focus on < affordance
57-
let f5 = await viewer.evaluate(
58-
(viewer) => viewer.shadowRoot.activeElement.title
59-
);
60-
expect(f5).toEqual('Previous Feature');
51+
const previousFeatureButton = popupContainer.getByRole('button', { name: 'Previous Feature' });
52+
await expect(previousFeatureButton).toBeFocused();
6153

6254
await page.keyboard.press('Tab'); // focus on > affordance
63-
let f6 = await viewer.evaluate(
64-
(viewer) => viewer.shadowRoot.activeElement.title
65-
);
66-
expect(f6).toEqual('Next Feature');
55+
const nextFeatureButton = popupContainer.getByRole('button', { name: 'Next Feature' });
56+
await expect(nextFeatureButton).toBeFocused();
6757

6858
await page.keyboard.press('Tab'); // focus on >| affordance
69-
let f7 = await viewer.evaluate(
70-
(viewer) => viewer.shadowRoot.activeElement.title
71-
);
72-
expect(f7).toEqual('Focus Controls');
59+
const focusControlsButton = popupContainer.getByRole('button', { name: 'Focus Controls' });
60+
await expect(focusControlsButton).toBeFocused();
7361

7462
await page.keyboard.press('Tab'); // focus on X dismiss popup affordance
75-
let f8 = await viewer.evaluate(
76-
(viewer) => viewer.shadowRoot.activeElement.className
77-
);
78-
expect(f8).toEqual('leaflet-popup-close-button');
63+
const dismissButton = popupContainer.getByRole('button', { name: 'Close popup' });
64+
await expect(dismissButton).toBeFocused();
7965
});
8066

8167
test('Tab to next feature after tabbing out of popup', async () => {
68+
const viewer = page.getByTestId('viewer');
8269
await page.keyboard.press('Escape'); // focus back on feature
83-
const h = await page.evaluateHandle(() =>
84-
document.querySelector('mapml-viewer')
85-
);
86-
const nh = await page.evaluateHandle((doc) => doc.shadowRoot, h);
87-
const rh = await page.evaluateHandle(
88-
(root) => root.activeElement.querySelector('.leaflet-interactive'),
89-
nh
70+
// according to https://github.com/microsoft/playwright/issues/15929
71+
// tests should locate the element and then check that it is focused
72+
const bigSquare = viewer.getByTestId('big-square');
73+
const bigSquarePathData = await bigSquare.evaluate((f) => {
74+
return f._groupEl.firstElementChild.getAttribute('d');
75+
});
76+
// no way to get a locator from another locator afaik, but this may work:
77+
const bigSquareGroupEl = viewer.locator(
78+
'g:has( > path[d="' + bigSquarePathData + '"])'
9079
);
91-
const f = await (
92-
await page.evaluateHandle((elem) => elem.getAttribute('d'), rh)
93-
).jsonValue();
94-
expect(f).toEqual('M330 83L586 83L586 339L330 339z');
95-
await page.waitForTimeout(500);
80+
await expect(bigSquareGroupEl).toBeFocused();
9681
// that we have to do this to get the tooltip back is a bug #681
9782
await page.keyboard.press('Shift+Tab');
9883
await page.keyboard.press('Tab');
99-
100-
let tooltipCount = await page.$eval(
101-
'mapml-viewer .leaflet-tooltip-pane',
84+
const toolTipPane = viewer.locator('.leaflet-tooltip-pane');
85+
let tooltipCount = await toolTipPane.evaluate(
10286
(div) => div.childElementCount
10387
);
104-
10588
expect(tooltipCount).toEqual(1);
10689
});
10790

@@ -110,26 +93,20 @@ test.describe('Playwright Keyboard Navigation + Query Layer Tests', () => {
11093
await page.waitForTimeout(500);
11194
await page.keyboard.press('Shift+Tab');
11295
await page.waitForTimeout(500);
113-
114-
const h = await page.evaluateHandle(() =>
115-
document.querySelector('mapml-viewer')
116-
);
117-
const nh = await page.evaluateHandle((doc) => doc.shadowRoot, h);
118-
const rh = await page.evaluateHandle(
119-
(root) => root.activeElement.querySelector('.leaflet-interactive'),
120-
nh
96+
const viewer = page.getByTestId('viewer');
97+
const bigSquare = viewer.getByTestId('big-square');
98+
const bigSquarePathData = await bigSquare.evaluate((f) => {
99+
return f._groupEl.firstElementChild.getAttribute('d');
100+
});
101+
const bigSquareGroupEl = viewer.locator(
102+
'g:has( > path[d="' + bigSquarePathData + '"])'
121103
);
122-
const f = await (
123-
await page.evaluateHandle((elem) => elem.getAttribute('d'), rh)
124-
).jsonValue();
125-
126-
let tooltipCount = await page.$eval(
127-
'mapml-viewer .leaflet-tooltip-pane',
104+
await expect(bigSquareGroupEl).toBeFocused();
105+
const toolTipPane = viewer.locator('.leaflet-tooltip-pane');
106+
let tooltipCount = await toolTipPane.evaluate(
128107
(div) => div.childElementCount
129108
);
130-
131109
expect(tooltipCount).toEqual(1);
132-
expect(f).toEqual('M330 83L586 83L586 339L330 339z');
133110
});
134111

135112
test('Previous feature button focuses previous feature', async () => {
@@ -145,25 +122,20 @@ test.describe('Playwright Keyboard Navigation + Query Layer Tests', () => {
145122
await page.waitForTimeout(500);
146123
await page.keyboard.press('Enter'); // focus should fall on previous feature
147124
await page.waitForTimeout(500);
148-
const h = await page.evaluateHandle(() =>
149-
document.querySelector('mapml-viewer')
150-
);
151-
const nh = await page.evaluateHandle((doc) => doc.shadowRoot, h);
152-
const rh = await page.evaluateHandle(
153-
(root) => root.activeElement.querySelector('.leaflet-interactive'),
154-
nh
125+
const viewer = page.getByTestId('viewer');
126+
const bigSquare = viewer.getByTestId('big-square');
127+
const bigSquarePathData = await bigSquare.evaluate((f) => {
128+
return f._groupEl.firstElementChild.getAttribute('d');
129+
});
130+
const bigSquareGroupEl = viewer.locator(
131+
'g:has( > path[d="' + bigSquarePathData + '"])'
155132
);
156-
const f = await (
157-
await page.evaluateHandle((elem) => elem.getAttribute('d'), rh)
158-
).jsonValue();
159-
160-
let tooltipCount = await page.$eval(
161-
'mapml-viewer .leaflet-tooltip-pane',
133+
await expect(bigSquareGroupEl).toBeFocused();
134+
const toolTipPane = viewer.locator('.leaflet-tooltip-pane');
135+
let tooltipCount = await toolTipPane.evaluate(
162136
(div) => div.childElementCount
163137
);
164-
165138
expect(tooltipCount).toEqual(1);
166-
expect(f).toEqual('M330 83L586 83L586 339L330 339z');
167139
});
168140

169141
test('Tooltip appears after pressing esc key', async () => {
@@ -172,25 +144,20 @@ test.describe('Playwright Keyboard Navigation + Query Layer Tests', () => {
172144
await page.keyboard.down('Escape'); // focus back on feature
173145
await page.keyboard.up('Escape');
174146
await page.waitForTimeout(500);
175-
176-
const h = await page.evaluateHandle(() =>
177-
document.querySelector('mapml-viewer')
147+
const viewer = page.getByTestId('viewer');
148+
const bigSquare = viewer.getByTestId('big-square');
149+
const bigSquarePathData = await bigSquare.evaluate((f) => {
150+
return f._groupEl.firstElementChild.getAttribute('d');
151+
});
152+
const bigSquareGroupEl = viewer.locator(
153+
'g:has( > path[d="' + bigSquarePathData + '"])'
178154
);
179-
const nh = await page.evaluateHandle((doc) => doc.shadowRoot, h);
180-
const rh = await page.evaluateHandle(
181-
(root) => root.activeElement.querySelector('.leaflet-interactive'),
182-
nh
183-
);
184-
const f = await (
185-
await page.evaluateHandle((elem) => elem.getAttribute('d'), rh)
186-
).jsonValue();
187-
188-
let tooltipCount = await page.$eval(
189-
'mapml-viewer .leaflet-tooltip-pane',
155+
await expect(bigSquareGroupEl).toBeFocused();
156+
const toolTipPane = viewer.locator('.leaflet-tooltip-pane');
157+
let tooltipCount = await toolTipPane.evaluate(
190158
(div) => div.childElementCount
191159
);
192160
expect(tooltipCount).toEqual(1);
193-
expect(f).toEqual('M330 83L586 83L586 339L330 339z');
194161
});
195162

196163
test('Tooltip appears after pressing enter on close button', async () => {
@@ -205,25 +172,20 @@ test.describe('Playwright Keyboard Navigation + Query Layer Tests', () => {
205172
await page.keyboard.down('Enter'); // press x button
206173
await page.keyboard.up('Enter');
207174
await page.waitForTimeout(500);
208-
209-
const h = await page.evaluateHandle(() =>
210-
document.querySelector('mapml-viewer')
211-
);
212-
const nh = await page.evaluateHandle((doc) => doc.shadowRoot, h);
213-
const rh = await page.evaluateHandle(
214-
(root) => root.activeElement.querySelector('.leaflet-interactive'),
215-
nh
175+
const viewer = page.getByTestId('viewer');
176+
const bigSquare = viewer.getByTestId('big-square');
177+
const bigSquarePathData = await bigSquare.evaluate((f) => {
178+
return f._groupEl.firstElementChild.getAttribute('d');
179+
});
180+
const bigSquareGroupEl = viewer.locator(
181+
'g:has( > path[d="' + bigSquarePathData + '"])'
216182
);
217-
const f = await (
218-
await page.evaluateHandle((elem) => elem.getAttribute('d'), rh)
219-
).jsonValue();
220-
221-
let tooltipCount = await page.$eval(
222-
'mapml-viewer .leaflet-tooltip-pane',
183+
await expect(bigSquareGroupEl).toBeFocused();
184+
const toolTipPane = viewer.locator('.leaflet-tooltip-pane');
185+
let tooltipCount = await toolTipPane.evaluate(
223186
(div) => div.childElementCount
224187
);
225188
expect(tooltipCount).toEqual(1);
226-
expect(f).toEqual('M330 83L586 83L586 339L330 339z');
227189
});
228190

229191
test('Next feature button focuses next feature', async () => {
@@ -240,25 +202,21 @@ test.describe('Playwright Keyboard Navigation + Query Layer Tests', () => {
240202
await page.keyboard.press('Tab'); // focus > affordance (next feature)
241203
await page.waitForTimeout(500);
242204
await page.keyboard.press('Enter'); // focus falls on next feature
243-
const h = await page.evaluateHandle(() =>
244-
document.querySelector('mapml-viewer')
205+
const viewer = page.getByTestId('viewer');
206+
const smallTrapezoid = viewer.getByTestId('small-trapezoid');
207+
const smallTrapezoidPathData = await smallTrapezoid.evaluate((f) => {
208+
return f._groupEl.firstElementChild.getAttribute('d');
209+
});
210+
// no way to get a locator from another locator afaik, but this may work:
211+
const smallTrapezoidGroupEl = viewer.locator(
212+
'g:has( > path[d="' + smallTrapezoidPathData + '"])'
245213
);
246-
const nh = await page.evaluateHandle((doc) => doc.shadowRoot, h);
247-
const rh = await page.evaluateHandle(
248-
(root) => root.activeElement.querySelector('.leaflet-interactive'),
249-
nh
250-
);
251-
const f = await (
252-
await page.evaluateHandle((elem) => elem.getAttribute('d'), rh)
253-
).jsonValue();
254-
255-
let tooltipCount = await page.$eval(
256-
'mapml-viewer .leaflet-tooltip-pane',
214+
await expect(smallTrapezoidGroupEl).toBeFocused();
215+
const toolTipPane = viewer.locator('.leaflet-tooltip-pane');
216+
let tooltipCount = await toolTipPane.evaluate(
257217
(div) => div.childElementCount
258218
);
259-
260219
expect(tooltipCount).toEqual(1);
261-
expect(f).toEqual('M285 373L460 380L468 477L329 459z');
262220
});
263221

264222
test('Focus Controls focuses the first <button> child in control div', async () => {
@@ -283,11 +241,8 @@ test.describe('Playwright Keyboard Navigation + Query Layer Tests', () => {
283241
await page.keyboard.press('Enter');
284242
for (let i = 0; i < 6; i++) await page.keyboard.press('Tab');
285243
await page.keyboard.press('Enter');
286-
let f = await page.$eval(
287-
'body > mapml-viewer',
288-
(viewer) => viewer.shadowRoot.activeElement.innerHTML
289-
);
290-
expect(f).toEqual('Maps for HTML Community Group');
244+
const focusedElement = page.getByText('Maps for HTML Community Group');
245+
await expect(focusedElement).toBeFocused();
291246
});
292247

293248
test('Zoom link zooms to the zoom level = zoom attribute', async () => {
@@ -300,10 +255,8 @@ test.describe('Playwright Keyboard Navigation + Query Layer Tests', () => {
300255
await page.keyboard.press('Tab'); // focus zoomto link
301256
await page.keyboard.press('Enter');
302257
await page.waitForTimeout(500);
303-
const zoom = await page.$eval(
304-
'body > mapml-viewer',
305-
(map) => +map.getAttribute('zoom')
306-
);
258+
const viewer = page.getByTestId('viewer');
259+
const zoom = await viewer.evaluate((map) => +map.getAttribute('zoom'));
307260
expect(zoom).toEqual(2);
308261
});
309262

@@ -324,10 +277,8 @@ test.describe('Playwright Keyboard Navigation + Query Layer Tests', () => {
324277
await page.keyboard.press('Tab'); // focus zoomto link
325278
await page.keyboard.press('Enter');
326279
await page.waitForTimeout(500);
327-
const zoom = await page.$eval(
328-
'body > mapml-viewer',
329-
(map) => +map.getAttribute('zoom')
330-
);
280+
const viewer = page.getByTestId('viewer');
281+
const zoom = await viewer.evaluate((map) => +map.getAttribute('zoom'));
331282
expect(zoom).toEqual(2);
332283
});
333284
});

0 commit comments

Comments
 (0)