|
1 | 1 | import { test, expect } from "@playwright/test" |
2 | 2 |
|
3 | | -test("community events page map loads and Zurich meetup link works", async ({ |
4 | | - page, |
5 | | -}) => { |
| 3 | +test("map loads and Zurich meetup link works", async ({ page }) => { |
6 | 4 | await page.goto("/community/events") |
7 | 5 |
|
8 | 6 | // Wait for the map canvas to be visible |
@@ -42,3 +40,85 @@ test("community events page map loads and Zurich meetup link works", async ({ |
42 | 40 | await newPage.waitForLoadState("domcontentloaded", { timeout: 10000 }) |
43 | 41 | expect(newPage.url()).toContain("meetup.com/graphql-zurich") |
44 | 42 | }) |
| 43 | + |
| 44 | +test("map tooltip appears on marker hover", async ({ page }) => { |
| 45 | + await page.goto("/community/events") |
| 46 | + const mapCanvas = page.locator("canvas").first() |
| 47 | + await expect(mapCanvas).toBeVisible({ timeout: 10000 }) |
| 48 | + await expect |
| 49 | + .poll(async () => { |
| 50 | + const box = await mapCanvas.boundingBox() |
| 51 | + return Boolean(box && box.width > 100 && box.height > 100) |
| 52 | + }) |
| 53 | + .toBe(true) |
| 54 | + const tooltip = page.getByRole("tooltip") |
| 55 | + await expect(tooltip).toHaveCount(0) |
| 56 | + await mapCanvas.hover() |
| 57 | + const { clientX, clientY } = await page.evaluate(() => { |
| 58 | + const canvas = document.querySelector("canvas") as HTMLCanvasElement | null |
| 59 | + if (!canvas) throw new Error("Canvas not found") |
| 60 | + const targetLat = 51.51 |
| 61 | + const targetLon = -0.12 |
| 62 | + const aspectRatio = 1.65 |
| 63 | + const cellSize = 8 |
| 64 | + const mercatorLimit = 85.05112878 |
| 65 | + const minDisplayedLatitude = -60 |
| 66 | + const baseLatitudeOffset = 4 |
| 67 | + const baseLongitudeOffset = 0.1 |
| 68 | + const clamp01 = (value: number) => { |
| 69 | + if (value <= 0) return 0 |
| 70 | + if (value >= 1) return 1 |
| 71 | + return value |
| 72 | + } |
| 73 | + const normalizeLongitude = (value: number) => { |
| 74 | + let lon = value |
| 75 | + while (lon <= -180) lon += 360 |
| 76 | + while (lon > 180) lon -= 360 |
| 77 | + return lon |
| 78 | + } |
| 79 | + const latToRawV = (lat: number) => { |
| 80 | + const clampedLat = Math.max(-mercatorLimit, Math.min(mercatorLimit, lat)) |
| 81 | + const rad = (clampedLat * Math.PI) / 180 |
| 82 | + return ( |
| 83 | + 0.5 - Math.log(Math.tan(Math.PI * 0.25 + rad * 0.5)) / (2 * Math.PI) |
| 84 | + ) |
| 85 | + } |
| 86 | + const maxProjectedV = latToRawV(mercatorLimit) |
| 87 | + const minProjectedV = latToRawV(minDisplayedLatitude) |
| 88 | + const lonLatToUV = (lon: number, lat: number) => { |
| 89 | + const adjustedLon = normalizeLongitude(lon + baseLongitudeOffset) |
| 90 | + const u = (adjustedLon + 180) / 360 |
| 91 | + const adjustedLat = Math.max( |
| 92 | + minDisplayedLatitude, |
| 93 | + Math.min(mercatorLimit, lat + baseLatitudeOffset), |
| 94 | + ) |
| 95 | + const rawV = latToRawV(adjustedLat) |
| 96 | + const normalizedV = clamp01( |
| 97 | + (rawV - maxProjectedV) / (minProjectedV - maxProjectedV), |
| 98 | + ) |
| 99 | + return [u, normalizedV] as const |
| 100 | + } |
| 101 | + const { width, height } = canvas |
| 102 | + const pixelRatio = window.devicePixelRatio || 1 |
| 103 | + const worldHeight = Math.min(width / aspectRatio, height) |
| 104 | + const worldWidth = worldHeight * aspectRatio |
| 105 | + const panX = width * 0.5 - worldWidth * 0.5 |
| 106 | + const panY = height * 0.5 - worldHeight * 0.5 |
| 107 | + const [u, v] = lonLatToUV(targetLon, targetLat) |
| 108 | + const markerY = 1 - v |
| 109 | + const screenX = panX + u * worldWidth |
| 110 | + const screenY = panY + markerY * worldHeight |
| 111 | + const deviceCell = cellSize * pixelRatio |
| 112 | + const cellX = Math.floor(screenX / deviceCell) |
| 113 | + const cellY = Math.floor(screenY / deviceCell) |
| 114 | + const centerX = (cellX + 0.5) * deviceCell |
| 115 | + const centerY = (cellY + 0.5) * deviceCell |
| 116 | + const rect = canvas.getBoundingClientRect() |
| 117 | + const clientX = rect.left + centerX / pixelRatio |
| 118 | + const clientY = rect.bottom - centerY / pixelRatio |
| 119 | + return { clientX, clientY } |
| 120 | + }) |
| 121 | + await page.mouse.move(clientX, clientY) |
| 122 | + await expect(tooltip).toHaveText("London GraphQL", { timeout: 5000 }) |
| 123 | + await expect(tooltip).toBeVisible() |
| 124 | +}) |
0 commit comments