Skip to main content

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

ScenarioEdge caseWhy tests breakApproach
GeolocationPermission deniedEmpty resultssetGeolocation + grant
AutocompleteDebounced typingFlake on fillKeyboard select first item
Marker clickCoordinate driftMiss targetProbe selected place id
iframe embedCross-origin mapWrong contextframeLocator
API quotaLive Maps in CI403/quotaStub Places or seed results
Pan/zoomTile load delayAssert too earlyWait map idle / probe
Mobile webtouch gesturesdragTo failsai.act pan map
Static map fallbackJS blockedUntestedSeparate scenario
Multiple markersOverlapping pinsWrong selectionProbe marker id from click handler
Route directionsAsync routeTimeoutPoll 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-patternWhy it failsBetter approach
Live Maps API every CI runQuota + flakeStub or seed
Click lat/lng pixelsTile load missai.act or probe
No permission denied testEmpty prod mapNegative spec
Assert map tile imagesCDN varianceProbe business outcome
Single global geolocationRegional logic untestedParameterized 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.

  1. Arrange: setGeolocation SF; seed drivers with known nearest id.
  2. Act: Open dispatch map and request nearest driver.
  3. 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).

External references

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.

Start free on TestChimp · Book a demo