How to Test Map Interactions and Geolocation
Short answer
Google Maps and map SDKs load tiles asynchronously, depend on geolocation permission, and resist coordinate clicks in headless CI. Mock location with context.setGeolocation, grant permissions before navigation, use frameLocator when maps sit in iframes, ai.act for pan/zoom semantics, and probe Assert on nearest-location API results—not pixel-perfect marker clicks.
Part of Testing Guides by integrations.
Who this is for
Logistics, delivery, real estate, field service, and travel apps with Google Maps JavaScript API, Mapbox, or embedded map widgets with search autocomplete and marker selection.
Why testing maps matters
- Wrong dispatch — geolocation denied → empty map; default center wrong city
- Search flake — Places autocomplete debounce + network latency
- Billing — unmocked Maps API calls in CI exhaust quotas
- Regional bugs — EU restrictions or locale formatting untested
Complexity map
| Scenario | Edge case | Why tests break | Approach |
|---|---|---|---|
| Geolocation | Permission denied | Empty results | setGeolocation + grant |
| Autocomplete | Debounced typing | Flake on fill | Keyboard select first item |
| Marker click | Coordinate drift | Miss target | Probe selected place id |
| iframe embed | Cross-origin map | Wrong context | frameLocator |
| API quota | Live Maps in CI | 403/quota | Stub Places or seed results |
| Pan/zoom | Tile load delay | Assert too early | Wait map idle / probe |
| Mobile web | touch gestures | dragTo fails | ai.act pan map |
| Static map fallback | JS blocked | Untested | Separate scenario |
| Multiple markers | Overlapping pins | Wrong selection | Probe marker id from click handler |
| Route directions | Async route | Timeout | Poll route probe |
Geolocation mock (Playwright)
From Playwright geolocation docs:
test.use({
geolocation: { latitude: 37.7749, longitude: -122.4194 },
permissions: ['geolocation'],
});
test('shows nearby stores', async ({ page, request }) => {
await page.goto('/stores');
await expect.poll(async () => {
const res = await request.get('/api/test/probe-nearest-store?runId=' + runId);
return (await res.json()).storeId;
}).toBe('store-sf-001');
});
Grant permissions before page.goto if app reads location on load.
Denied permission path
await context.clearPermissions();
await page.goto('/stores');
await expect(page.getByText(/enable location/i)).toBeVisible();
await expect.poll(() => probeLocationQuery(runId)).toBeNull();
Places autocomplete
Typing fast into autocomplete flakes—prefer deliberate keyboard flow:
await page.getByLabel('Address').fill('1600 Amphitheatre');
await page.waitForTimeout(300); // debounce only—prefer wait for suggestions container
await page.getByRole('option').first().click();
await expect.poll(() => probeSelectedPlace(runId)).toMatchObject({ placeId: expect.any(String) });
Optional: stub Places Autocomplete API in test env with fixed suggestions JSON.
frameLocator for embedded maps
When map is iframe (common with some embed patterns):
const mapFrame = page.frameLocator('iframe[title*="map" i]');
await mapFrame.getByRole('button', { name: /zoom in/i }).click();
See third-party embeds for nested frames.
ai.act for pan and marker selection
await ai.act('Pan map north and select the marker labeled Store #42');
await ai.verify('Store detail panel shows Store #42 address');
await expect.poll(() => probeSelectedMarker(runId)).toBe('store-42');
Use when coordinates unreliable; probe confirms selection.
Seed stable POI data
// POST /api/test/seed-locations
// Body: { runId, places: [{ id, lat, lng, label }] }
Avoid depending on live Google POI data changing over time.
Anti-patterns
| Anti-pattern | Why it fails | Better approach |
|---|---|---|
| Live Maps API every CI run | Quota + flake | Stub or seed |
| Click lat/lng pixels | Tile load miss | ai.act or probe |
| No permission denied test | Empty prod map | Negative spec |
| Assert map tile images | CDN variance | Probe business outcome |
| Single global geolocation | Regional logic untested | Parameterized coords |
Example scenario
Situation: Delivery app finds nearest driver using browser geolocation.
Expected outcome: Nearest driver id matches seeded fleet for mocked San Francisco coordinates.
Why UI-only automation breaks: Map centers correctly but dispatch API uses default NYC fallback.
- Arrange: setGeolocation SF; seed drivers with known nearest id.
- Act: Open dispatch map and request nearest driver.
- Assert: Probe nearestDriverId matches seed; optional ai.verify map shows driver marker.
TestChimp workflow: Track map_interaction × geo_region when EU searches untested.
Same Arrange/Act/Assert pattern as expired-coupon checkout.
Connect scenarios to your QA workflow
Capture business rules in markdown test plans and enforce them with seed routes and probe Assert. Link SmartTests with // @Scenario: for requirement traceability. Use /testchimp test on PRs; /testchimp explore on SmartTest paths for non-functional gaps (ExploreChimp).
Related scenarios
- Canvas visual widgets — chart/map overlap
- Third-party embeds — iframe patterns
- Real estate listings — map search UX
- Search filters — location filters in grids
External references
- Playwright geolocation
- Playwright permissions
- Google Maps JavaScript API
- Places Autocomplete
- SmartTests intro — ai.act for map interactions
Frequently asked questions
How do I mock geolocation in Playwright?
Use test.use or context.setGeolocation with latitude/longitude and grant geolocation permission before page.goto. Assert via probe nearest-location API—not map pixels.
How do I test Places autocomplete without flake?
Fill input, wait for suggestion list, select first option via keyboard. Stub autocomplete API in test env for fully deterministic CI.
Maps load in an iframe—how do I interact?
Use page.frameLocator('iframe[...]') then chain locators inside. Wait for inner controls visible before click.
Should CI call live Google Maps API?
Avoid for every PR—stub Places responses or seed backend location data. Optional nightly job with real API and quota alerts.
How do I test geolocation denied?
Clear permissions, load app, assert prompt or fallback UI and probe that location-based API was not called with coords.
Coordinate clicks miss markers—what now?
Use ai.act semantic selection, app test hooks on marker click handlers, or probe selected place id after interaction.
Users in different regions use map differently—how do we prioritize tests?
Compare map_interaction × geo_region in TrueCoverage prod vs test-run. /testchimp evolve when high-traffic regions lack scenarios.
Apply these patterns in your repo
Run `/testchimp init` to connect TestChimp to your repo, then `/testchimp test` on PRs to turn these patterns into maintained SmartTests. Use `/testchimp evolve` when you want to expand coverage as your app grows.