Skip to content

Commit 0dbdc10

Browse files
committed
minor #3038 [Map] Add E2E tests, close #3022 (Kocal)
This PR was squashed before being merged into the 2.x branch. Discussion ---------- [Map] Add E2E tests, close #3022 | Q | A | ------------- | --- | Bug fix? | no | New feature? | no <!-- please update src/**/CHANGELOG.md files --> | Docs? | no <!-- required for new features --> | Issues | Fix #3022 <!-- prefix each issue number with "Fix #", no need to create an issue if none exist, explain below instead --> | License | MIT <!-- Replace this notice by a description of your feature/bugfix. This will help reviewers and should be a good start for the documentation. Additionally (see https://symfony.com/releases): - Always add tests and ensure they pass. - For new features, provide some code snippets to help understand usage. - Features and deprecations must be submitted against branch main. - Update/add documentation as required (we can help!) - Changelog entry should follow https://symfony.com/doc/current/contributing/code/conventions.html#writing-a-changelog-entry - Never break backward compatibility (see https://symfony.com/bc). --> I only tested Leaflet map with various features, including custom icons. Google Maps won't be tested as it requires a **paid** API Key. Commits ------- 174b837 [Map] Add E2E tests 5067140 [E2E] Hide React/Vue/Svelte components, until we start to add E2E tests for them
2 parents a28ad51 + 174b837 commit 0dbdc10

File tree

17 files changed

+478
-154
lines changed

17 files changed

+478
-154
lines changed
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { expect, test } from '@playwright/test';
2+
3+
test('Can render basic map', async ({ page }) => {
4+
await page.goto('/ux-map/basic?renderer=google');
5+
6+
await expect(await page.getByTestId('map')).toBeVisible();
7+
8+
// Since we can't test Google Maps rendering due to API costs, we only assert that Google Maps API is (wrongly) loaded
9+
await expect(await page.getByTestId('map')).toContainText('Oops! Something went wrong.');
10+
await expect(await page.getByTestId('map')).toContainText(
11+
"This page didn't load Google Maps correctly. See the JavaScript console for technical details."
12+
);
13+
});

src/Map/src/Bridge/Google/assets/test/browser/map_controller.test.ts

Lines changed: 0 additions & 71 deletions
This file was deleted.
Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
import { expect, type Page, test } from '@playwright/test';
2+
import { getSymfonyKernelVersionId } from '../../../../../../../../test/playwright-helpers';
3+
4+
async function expectMapToBeVisible(page: Page) {
5+
await expect(await page.getByTestId('map')).toBeVisible();
6+
await expect(await page.getByTestId('map').locator('.leaflet-pane').first()).toBeAttached();
7+
await expect(await page.getByTestId('map').locator('.leaflet-control-container')).toBeAttached();
8+
}
9+
10+
async function expectOneInfoWindowToBeOpenedAndContainText(page: Page, text: string) {
11+
const popups = page.locator('.leaflet-popup');
12+
await expect(popups).toHaveCount(1);
13+
await expect(popups.first()).toBeInViewport();
14+
await expect(popups.first()).toContainText(text);
15+
}
16+
17+
test('Can render basic map', async ({ page }) => {
18+
await page.goto('/ux-map/basic?renderer=leaflet');
19+
await expectMapToBeVisible(page);
20+
});
21+
22+
test('Can render markers and fit bounds to marker', async ({ page }) => {
23+
await page.goto('/ux-map/with-markers-and-fit-bounds-to-markers?renderer=leaflet');
24+
await expectMapToBeVisible(page);
25+
26+
const markers = page.getByTestId('map').locator('.leaflet-marker-icon');
27+
await expect(markers, '2 markers should be present').toHaveCount(2);
28+
for (const marker of await markers.all()) {
29+
await expect(marker).toBeInViewport();
30+
}
31+
32+
await expect(markers.nth(0)).toHaveAttribute('title', 'Paris');
33+
await expect(markers.nth(1)).toHaveAttribute('title', 'Lyon');
34+
});
35+
36+
test('Can render markers, zoomed on Paris, Lyon marker should be hidden', async ({ page }) => {
37+
await page.goto('/ux-map/with-markers-and-zoomed-on-paris?renderer=leaflet');
38+
await expectMapToBeVisible(page);
39+
40+
// Ensure the two markers are rendered, but only the Paris marker should be visible in the viewport
41+
const markers = page.getByTestId('map').locator('.leaflet-marker-icon');
42+
await expect(markers).toHaveCount(2);
43+
await expect(markers.nth(0)).toHaveAttribute('title', 'Paris');
44+
await expect(markers.nth(0)).toBeInViewport();
45+
await expect(markers.nth(1)).toHaveAttribute('title', 'Lyon');
46+
await expect(markers.nth(1), 'The "Lyon" marker should not be visible').not.toBeInViewport();
47+
});
48+
49+
test('Can render markers and info windows', async ({ page }) => {
50+
await page.goto('/ux-map/with-markers-and-info-windows?renderer=leaflet');
51+
await expectMapToBeVisible(page);
52+
53+
const markers = page.getByTestId('map').locator('.leaflet-marker-icon');
54+
await expect(markers, '2 markers should be present').toHaveCount(2);
55+
for (const marker of await markers.all()) {
56+
await expect(marker).toBeInViewport();
57+
}
58+
59+
await expect(markers.nth(0)).toHaveAttribute('title', 'Paris');
60+
await expect(markers.nth(1)).toHaveAttribute('title', 'Lyon');
61+
62+
// Ensure only one popup is visible at a time, the popup for Paris should be opened by default
63+
await expectOneInfoWindowToBeOpenedAndContainText(page, 'Capital of France');
64+
65+
// Click on the Lyon marker to open its popup, the Paris popup should close
66+
await markers.nth(1).click();
67+
await expectOneInfoWindowToBeOpenedAndContainText(page, 'Famous for its gastronomy');
68+
});
69+
70+
test('Can render markers with custom icons', async ({ page }) => {
71+
await page.goto('/ux-map/with-markers-and-custom-icons?renderer=leaflet');
72+
await expectMapToBeVisible(page);
73+
74+
const markers = page.getByTestId('map').locator('.leaflet-marker-icon');
75+
await expect(markers, '3 markers should be present').toHaveCount(3);
76+
for (const marker of await markers.all()) {
77+
await expect(marker).toBeInViewport();
78+
}
79+
80+
await expect(markers.nth(0)).toHaveAttribute('title', 'Paris');
81+
expect(await markers.nth(0).innerHTML()).toEqual(
82+
'<svg viewBox="0 0 24 24" width="24" height="24" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"><path fill="currentColor" d="M8.21 17c.44-.85.85-1.84 1.23-3H9v-2h1c.61-2.6 1-5.87 1-10h2c0 4.13.4 7.4 1 10h1v2h-.44c.38 1.16.79 2.15 1.23 3H17v2l2 3h-2.42c-.77-1.76-2.53-3-4.58-3s-3.81 1.24-4.58 3H5l2-3l-.03-2zm4.38-3h-1.18a22 22 0 0 1-1.13 3h3.44c-.4-.87-.79-1.87-1.13-3"></path></svg>'
83+
);
84+
85+
await expect(markers.nth(1)).toHaveAttribute('title', 'Lyon');
86+
expect(await markers.nth(1).innerHTML()).toEqual(
87+
'<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 24 24"><path fill="currentColor" d="M11 9H9V2H7v7H5V2H3v7c0 2.12 1.66 3.84 3.75 3.97V22h2.5v-9.03C11.34 12.84 13 11.12 13 9V2h-2zm5-3v8h2.5v8H21V2c-2.76 0-5 2.24-5 4"></path></svg>'
88+
);
89+
90+
await expect(markers.nth(2)).toHaveAttribute('title', 'Bordeaux');
91+
await expect(markers.nth(2)).toHaveAttribute(
92+
'src',
93+
getSymfonyKernelVersionId() >= 70200
94+
? '/assets/icons/mdi/glass-wine-SOLVwOG.svg'
95+
: '/assets/icons/mdi/glass-wine-48e2d5c0e18f9b07dab82e113ec7490e.svg'
96+
);
97+
});
98+
99+
test('Can render polygons', async ({ page }) => {
100+
await page.goto('/ux-map/with-polygons?renderer=leaflet');
101+
await expectMapToBeVisible(page);
102+
103+
const paths = page.getByTestId('map').locator('path.leaflet-interactive');
104+
await expect(paths, '2 polygons must be present').toHaveCount(2);
105+
await expect(paths.nth(0)).toHaveAttribute(
106+
'd',
107+
'M548 276L656 188L762 260L708 433L573 384zM615 352L696 354L678 236L640 250z'
108+
);
109+
await expect(paths.nth(1)).toHaveAttribute('d', 'M870 476L795 364L844 395L911 508z');
110+
111+
// Workaround for `paths.nth(0).click({ relative: { ... } })` which does not work, it tries to click the center of the polygon,
112+
// but since it's empty, the popup can't be opened.
113+
const firstPathBoundingBox = await paths.nth(0).boundingBox();
114+
await page.mouse.click(firstPathBoundingBox.x + 40, firstPathBoundingBox.y + 100);
115+
await expectOneInfoWindowToBeOpenedAndContainText(page, 'A weird shape on the France');
116+
117+
const secondPathBoundingBox = await paths.nth(1).boundingBox();
118+
await page.mouse.click(secondPathBoundingBox.x + 50, secondPathBoundingBox.y + 40);
119+
await expectOneInfoWindowToBeOpenedAndContainText(page, 'A polygon covering some of the main cities in Italy');
120+
});
121+
122+
test('Can render polylines', async ({ page }) => {
123+
await page.goto('/ux-map/with-polylines?renderer=leaflet');
124+
await expectMapToBeVisible(page);
125+
126+
const paths = page.getByTestId('map').locator('path.leaflet-interactive');
127+
await expect(paths, '2 polylines must be present').toHaveCount(2);
128+
await expect(paths.nth(0)).toHaveAttribute('d', 'M640 250L696 354L708 433L573 384');
129+
await expect(paths.nth(1)).toHaveAttribute('d', 'M548 276L551 306L603 302');
130+
131+
// Workaround for `paths.nth(0).click({ relative: { ... } })` which does not work, it tries to click the center of the polygon,
132+
// but since it's empty, the popup can't be opened.
133+
const firstPathBoundingBox = await paths.nth(0).boundingBox();
134+
await page.mouse.click(firstPathBoundingBox.x + 95, firstPathBoundingBox.y + 50);
135+
await expectOneInfoWindowToBeOpenedAndContainText(page, 'A line passing through Paris, Lyon, Marseille, Bordeaux');
136+
137+
const secondPathBoundingBox = await paths.nth(1).boundingBox();
138+
await page.mouse.click(secondPathBoundingBox.x + 5, secondPathBoundingBox.y + 25);
139+
await expectOneInfoWindowToBeOpenedAndContainText(page, 'A line passing through Rennes, Nantes and Tours');
140+
});
141+
142+
test('Can render circles', async ({ page }) => {
143+
await page.goto('/ux-map/with-circles?renderer=leaflet');
144+
await expectMapToBeVisible(page);
145+
146+
const paths = page.getByTestId('map').locator('path.leaflet-interactive');
147+
await expect(paths, '2 circles must be present').toHaveCount(2);
148+
await expect(paths.nth(0)).toHaveAttribute(
149+
'd',
150+
'M623.5256177777774,250.21082480695986a16,16 0 1,0 32,0 a16,16 0 1,0 -32,0 '
151+
);
152+
await expect(paths.nth(1)).toHaveAttribute(
153+
'd',
154+
'M687.0390399999997,354.0936387274919a9,9 0 1,0 18,0 a9,9 0 1,0 -18,0 '
155+
);
156+
157+
await paths.nth(0).click();
158+
await expectOneInfoWindowToBeOpenedAndContainText(page, 'A 50km radius circle centered on Paris');
159+
160+
await paths.nth(1).click();
161+
await expectOneInfoWindowToBeOpenedAndContainText(page, 'A 30km radius circle centered on Lyon');
162+
});
163+
164+
test('Can render rectangles', async ({ page }) => {
165+
await page.goto('/ux-map/with-rectangles?renderer=leaflet');
166+
await expectMapToBeVisible(page);
167+
168+
const paths = page.getByTestId('map').locator('path.leaflet-interactive');
169+
await expect(paths, '2 rectangles must be present').toHaveCount(2);
170+
await expect(paths.nth(0)).toHaveAttribute('d', 'M640 250L640 188L656 188L656 250z');
171+
await expect(paths.nth(1)).toHaveAttribute('d', 'M573 384L573 354L696 354L696 384z');
172+
173+
await paths.nth(0).click();
174+
await expectOneInfoWindowToBeOpenedAndContainText(page, 'A rectangle from Paris to Lille');
175+
176+
await paths.nth(1).click();
177+
await expectOneInfoWindowToBeOpenedAndContainText(page, 'A rectangle from Bordeaux to Lyon');
178+
});

src/Map/src/Bridge/Leaflet/assets/test/browser/map_controller.test.ts

Lines changed: 0 additions & 72 deletions
This file was deleted.

test/playwright-helpers.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import * as path from 'node:path';
2+
import * as fs from 'fs';
3+
4+
export function getSymfonyKernelVersionId(): number {
5+
const kernelPath = path.join(import.meta.dirname, '../test_apps/e2e-app/vendor/symfony/http-kernel/Kernel.php');
6+
if (!fs.existsSync(kernelPath)) {
7+
throw new Error(`Unable to read Symfony Kernel version ID, the file "${kernelPath}" does not exist.`)
8+
}
9+
10+
const match = fs.readFileSync(kernelPath, 'utf8').match((/VERSION_ID = (\d+)/));
11+
if (match === null) {
12+
throw new Error(`Unable to extract Symfony Kernel version ID.`)
13+
}
14+
15+
return Number(match[1]);
16+
}
Lines changed: 1 addition & 0 deletions
Loading
Lines changed: 1 addition & 0 deletions
Loading
File renamed without changes.
File renamed without changes.
File renamed without changes.

0 commit comments

Comments
 (0)