Location & Maps Testing Guide

How to Test Google Places Autocomplete with Playwright: Complete 2026 Guide

A scenario-by-scenario walkthrough of testing Google Places Autocomplete with Playwright. Prediction list timing, session tokens, Place Details fetch, address component parsing, map pin updates, and the pitfalls that break real autocomplete test suites.

10B+

Google Maps Platform serves over 10 billion requests per week across Places, Directions, and Maps APIs, with Places Autocomplete accounting for a significant share of that traffic.

Google Cloud blog

0+Async network calls per selection
0msTypical prediction debounce
0Test scenarios covered
0%Fewer lines with Assrt

Google Places Autocomplete End-to-End Flow

UserBrowser InputPlaces JS SDKAutocomplete APIPlace Details APIMap ComponentTypes address textDebounced input eventgetPlacePredictions()Prediction listRender dropdown itemsClicks a predictiongetDetails(placeId)Address components + geometrypanTo(lat, lng)

1. Why Testing Google Places Autocomplete Is Harder Than It Looks

Google Places Autocomplete looks simple on the surface: a user types into an input, a dropdown of predictions appears, and they click one. Under the hood, though, there are several layers of asynchronous behavior that make reliable end-to-end testing genuinely difficult. The autocomplete widget debounces keystrokes (typically 200 to 300 milliseconds), fires a network request to the Autocomplete API, waits for the response, renders a prediction list, and then waits again for the user to select an item before firing a completely separate Place Details request to fetch the full address components and geometry.

The prediction list itself is the first trap. It is rendered by the Google Maps JavaScript SDK in a detached container that lives outside your application's DOM tree. The .pac-container element is appended directly to document.body and positioned absolutely, which means your usual component-scoped selectors will not find it. The items inside use .pac-item class names, not semantic roles or labels, so accessible selectors like getByRole do not work out of the box.

There are five structural reasons this flow resists automation. First, the prediction list timing depends on network latency and the debounce interval, so any fixed waitForTimeout will either be too slow in CI or too fast on a congested network. Second, session tokens tie a sequence of autocomplete requests to a single Place Details call for billing purposes, and if your test inadvertently creates multiple sessions, your billing tests and your functional tests will give different results. Third, the Place Details response contains structured address components (street number, route, locality, administrative area, postal code, country) that your app must parse correctly; a test that only checks the formatted address string misses parsing bugs. Fourth, many implementations update a map marker or viewport when a place is selected, and verifying that the map actually panned requires reading the Google Map instance state. Fifth, Google enforces per-key quotas and rate limits; a test suite that runs 50 autocomplete queries in rapid succession can exhaust the daily quota for a test API key.

Places Autocomplete Interaction Flow

🌐

User Types

Keystrokes in input

⚙️

Debounce

200-300ms wait

↪️

API Request

getPlacePredictions()

🌐

Predictions

Dropdown renders

User Selects

Clicks pac-item

⚙️

Details Fetch

getDetails(placeId)

UI Update

Address + map pin

Session Token Billing Lifecycle

🔒

New Session

Token created

⚙️

Predictions

Multiple requests share token

Selection

getDetails() with same token

💳

Session Ends

Billed as one session

A reliable autocomplete test suite must handle all of these layers. The sections below walk through each scenario you need, with runnable Playwright TypeScript you can paste into a real project.

2. Setting Up a Reliable Test Environment

Before writing test scenarios, you need a stable environment that isolates your tests from production billing and quota limits. Google Maps Platform requires an API key with the Places API (New) or Places API enabled. Create a separate project in the Google Cloud Console dedicated to testing. Restrict the key to localhost and your CI domain so it cannot be abused, and set a daily quota cap that is generous enough for your full test suite but low enough to prevent runaway costs.

Google Places Test Environment Checklist

  • Create a dedicated Google Cloud project for testing
  • Enable the Places API and Maps JavaScript API
  • Generate a browser-restricted API key for localhost and CI domains
  • Set a daily quota cap (e.g. 1,000 requests per day)
  • Configure billing alerts at $5 and $20 thresholds
  • Store the API key in environment variables, never in source code
  • Install @googlemaps/js-api-loader or load the script tag dynamically
  • Configure Playwright to allow requests to maps.googleapis.com

Environment Variables

.env.test

Playwright Configuration for Places Autocomplete

The Google Maps JavaScript SDK makes requests to maps.googleapis.com from the browser. Playwright needs to allow these cross-origin requests and handle the asynchronous script loading. Set a generous action timeout because prediction responses depend on network latency, and configure a custom fixture to intercept Places API responses when you want deterministic results.

playwright.config.ts

Route Interception for Deterministic Tests

For CI environments where you cannot rely on live Google API responses (or want to avoid quota usage), intercept the Autocomplete and Place Details endpoints and return canned responses. This gives you full control over prediction timing, response content, and error simulation.

test/fixtures/places-mock.ts
Install Dependencies

3. Scenario: Basic Prediction List Appears

The most fundamental scenario for any Places Autocomplete integration is confirming that the prediction dropdown appears when a user types into the address input. This is your smoke test. If predictions do not appear, every downstream feature (address selection, form population, map updates) is broken. The challenge is that the prediction list is asynchronous and depends on the Google Maps SDK loading, the API key being valid, and the debounce timer completing before any predictions appear.

1

Basic Prediction List Appears

Straightforward

Goal

Type a partial address into the autocomplete input and verify that the .pac-container dropdown appears with at least one prediction item.

Preconditions

  • App running at APP_BASE_URL with the Places script loaded
  • Valid Google Maps API key with Places API enabled
  • Autocomplete input is visible on the page

Playwright Implementation

places-predictions.spec.ts

What to Assert Beyond the UI

  • The .pac-container element is appended to document.body, not inside your component
  • Prediction items have both main text and secondary text spans
  • The Google logo/attribution is visible (required by Google ToS)

Prediction List: Playwright vs Assrt

import { test, expect } from '@playwright/test';

test('prediction list appears', async ({ page }) => {
  await page.goto('/address-form');
  await page.waitForFunction(() =>
    typeof google !== 'undefined'
    && typeof google.maps.places !== 'undefined'
  );

  const input = page.getByPlaceholder('Enter your address');
  await input.click();
  await input.pressSequentially('1600 Amphitheatre', { delay: 50 });

  const pacContainer = page.locator('.pac-container');
  await expect(pacContainer).toBeVisible({ timeout: 5_000 });

  const predictions = pacContainer.locator('.pac-item');
  await expect(predictions.first()).toBeVisible();
  expect(await predictions.count()).toBeGreaterThanOrEqual(1);
});
45% fewer lines

4. Scenario: Selecting a Prediction and Parsing Address Components

Clicking a prediction item triggers the most important part of the flow: the Place Details API call. When a user selects a prediction, the Google Maps SDK calls getDetails() with the place_id from the selected prediction and returns structured address components. Your application must parse these components correctly to populate form fields like street, city, state, and ZIP code. This scenario tests both the selection interaction and the downstream parsing logic.

2

Select Prediction and Parse Address

Moderate

Goal

Select a prediction from the dropdown and verify that the application correctly parses the address components into separate form fields (street, city, state, ZIP, country).

Preconditions

  • Prediction list is visible with at least one item
  • Form has separate fields for street, city, state, ZIP code, and country
  • Application uses place.address_components to populate form fields

Playwright Implementation

places-select-parse.spec.ts

Address Component Mapping Reference

Google returns address components as an array of objects, each with long_name, short_name, and a types array. The types determine which form field each component maps to. Getting this mapping wrong is the most common autocomplete bug in production applications.

utils/parse-address-components.ts

Address Parsing: Playwright vs Assrt

import { test, expect } from '@playwright/test';

test('selecting prediction populates fields', async ({ page }) => {
  await page.goto('/address-form');
  await page.waitForFunction(
    () => typeof google?.maps?.places !== 'undefined'
  );

  const input = page.getByPlaceholder('Enter your address');
  await input.click();
  await input.pressSequentially('1600 Amphitheatre Parkway', { delay: 50 });

  const pacContainer = page.locator('.pac-container');
  await expect(pacContainer).toBeVisible({ timeout: 5_000 });
  await pacContainer.locator('.pac-item').first().click();

  await expect(page.getByLabel('Street')).toHaveValue(/1600 Amphitheatre/);
  await expect(page.getByLabel('City')).toHaveValue('Mountain View');
  await expect(page.getByLabel('State')).toHaveValue('CA');
  await expect(page.getByLabel('ZIP Code')).toHaveValue('94043');
  await expect(page.getByLabel('Country')).toHaveValue('US');
});
46% fewer lines

Try Assrt for free

Open-source AI testing framework. No signup required.

Get Started

5. Scenario: Session Token Lifecycle

Google Places Autocomplete uses session tokens to group a series of autocomplete requests with a final Place Details request into a single billing session. When the AutocompleteSessionToken is used correctly, all prediction requests within the session are free and you only pay for the Place Details call. If your application creates a new session token on every keystroke or forgets to pass the token to getDetails(), you get billed per prediction request instead of per session. This can multiply your Places API costs by 10x or more.

3

Session Token Lifecycle Verification

Complex

Goal

Verify that a single session token is created when the user starts typing, reused across all prediction requests, passed to the Place Details call, and invalidated after selection.

Playwright Implementation

places-session-token.spec.ts

What to Assert Beyond the UI

  • All autocomplete requests within a single typing sequence share one session token
  • The Place Details request uses the same session token as the predictions
  • A new session token is generated when the user clears the input and starts typing again
  • No prediction requests are fired after the Place Details call until a new session begins

6. Scenario: Map Pin Update After Selection

Many applications display a Google Map alongside the autocomplete input. When the user selects a prediction, the map should pan to the selected location and place a marker at the correct coordinates. Testing this requires reading the Google Map instance's internal state (center position and zoom level) through the browser's JavaScript context. You cannot rely solely on visual inspection because a map that looks correct at a glance might be off by several hundred meters.

4

Map Pin Update After Selection

Moderate

Goal

After selecting a prediction, verify that the map pans to the correct coordinates and a marker is placed at the selected location.

Playwright Implementation

places-map-pin.spec.ts

Exposing the Map Instance for Testing

The most reliable way to verify map state is to expose the google.maps.Map instance and any markers on window in your application code, gated behind an environment variable. This avoids fragile DOM scraping of the map canvas.

components/AddressMap.tsx

7. Scenario: Error States and Rate Limiting

Your application must handle Places API errors gracefully. Common failure modes include an invalid or expired API key, exceeding the daily quota, network timeouts, and the ZERO_RESULTS response when the user types a nonsensical address. A robust test suite covers these error paths to ensure the user sees a helpful message instead of a broken, empty dropdown or a silent failure.

5

Error States and Rate Limiting

Complex

Goal

Simulate API errors and verify the application displays appropriate error states without crashing.

Playwright Implementation

places-errors.spec.ts
Error Scenario Test Run

8. Scenario: Mobile Viewport and Touch Interactions

The Places Autocomplete dropdown behaves differently on mobile viewports. The .pac-container is positioned absolutely and can overflow the viewport on narrow screens. Touch interactions may require different handling than mouse clicks, and the virtual keyboard on mobile devices pushes the viewport up, potentially hiding the prediction list. Testing with a mobile viewport ensures your autocomplete experience works for the majority of users who are on phones.

6

Mobile Viewport Autocomplete

Moderate

Goal

Verify that the prediction dropdown is visible and usable on a mobile viewport (375x667), and that selecting a prediction works with tap interactions.

Playwright Implementation

places-mobile.spec.ts

Mobile Autocomplete: Playwright vs Assrt

import { test, expect, devices } from '@playwright/test';
test.use(devices['iPhone 13']);

test('autocomplete works on mobile', async ({ page }) => {
  await page.goto('/address-form');
  await page.waitForFunction(
    () => typeof google?.maps?.places !== 'undefined'
  );

  const input = page.getByPlaceholder('Enter your address');
  await input.tap();
  await input.pressSequentially('1600 Amphitheatre', { delay: 80 });

  const pacContainer = page.locator('.pac-container');
  await expect(pacContainer).toBeVisible({ timeout: 5_000 });

  const containerBox = await pacContainer.boundingBox();
  const viewport = page.viewportSize();
  expect(containerBox!.x + containerBox!.width)
    .toBeLessThanOrEqual(viewport!.width);

  await pacContainer.locator('.pac-item').first().tap();
  await expect(page.getByLabel('City')).not.toHaveValue('');
});
46% fewer lines

9. Common Pitfalls That Break Autocomplete Test Suites

These are real problems sourced from Google Maps Platform GitHub issues, Stack Overflow threads, and production post-mortems. Every one of them has broken a real team's test suite.

Autocomplete Testing Anti-Patterns

  • Using waitForTimeout instead of waitForSelector for the .pac-container. The prediction list timing varies by 100ms to 2 seconds depending on network conditions. A fixed timeout that works locally will fail in CI.
  • Using page.fill() instead of pressSequentially(). The fill() method sets the input value programmatically without dispatching individual keydown/keyup events. The Google Maps SDK listens for real keyboard events to trigger predictions. Using fill() results in no predictions appearing.
  • Forgetting that .pac-container is appended to document.body. If your test scopes locators to a component wrapper (e.g. page.locator('#address-form .pac-container')), the prediction dropdown will never be found because it lives outside your component tree.
  • Not waiting for the Place Details response after clicking a prediction. The selection triggers an async API call. If your test immediately checks form field values after clicking, the fields will still be empty because the details response has not arrived yet.
  • Running tests against the live API without quota guards. A test suite with 50 autocomplete scenarios can burn through hundreds of API calls per run. Without a daily quota cap, a CI loop that retries on failure can generate an unexpected bill.
  • Hardcoding expected prediction results. Google's autocomplete ranking algorithm changes frequently. A test that asserts the first prediction is exactly '1600 Amphitheatre Parkway, Mountain View, CA, USA' will break when Google rewords or reorders results. Assert on the presence of relevant content, not exact strings.
  • Ignoring the Google attribution requirement. The .pac-container includes a 'Powered by Google' logo that must remain visible per the Terms of Service. CSS that hides overflow on the autocomplete container can accidentally hide this attribution, which violates the ToS.
  • Creating a new AutocompleteSessionToken on every keystroke. Each token starts a new billing session. If your application creates tokens inside the input onChange handler instead of once per user interaction session, you pay per prediction request instead of per session.
Common Failure: fill() vs pressSequentially()

The pressSequentially() versus fill()distinction is the single most common reason autocomplete tests fail. Playwright's fill() method dispatches an input event directly on the element, which works for standard form fields. The Google Maps SDK, however, attaches event listeners to keydown, keyup, and keypress events. Without those events, the SDK never knows the user is typing and never fetches predictions.

Use pressSequentially() with a delayof 50 to 100 milliseconds to simulate real typing. This fires individual key events for each character and gives the SDK's debounce logic time to trigger the API call.

10. Writing These Scenarios in Plain English with Assrt

Every scenario above required careful handling of the .pac-container selector, pressSequentially() instead of fill(), explicit waits for async API responses, and knowledge of Google Maps SDK internals. Assrt lets you write the same scenarios in plain English. The Assrt compiler generates the correct Playwright TypeScript with all the SDK-specific patterns built in.

Here is the address selection and parsing scenario from Section 4, rewritten as an Assrt scenario file alongside the session token and error handling tests.

scenarios/places-autocomplete.assrt

Assrt compiles each scenario block into the same Playwright TypeScript you saw in the preceding sections, committed to your repo as real tests you can read, run, and modify. When Google updates the Maps JavaScript SDK and changes the .pac-container structure or the autocomplete API response format, Assrt detects the failure, analyzes the new DOM, and opens a pull request with the updated selectors and assertions. Your scenario files stay untouched.

Start with the basic prediction scenario. Once it is green in your CI, add the address parsing test, then the session token verification, then the error handling scenarios, then the mobile viewport test. In a single afternoon you can have complete Google Places Autocomplete coverage that most production applications never manage to achieve by hand.

Related Guides

Ready to automate your testing?

Assrt discovers test scenarios, writes Playwright tests from plain English, and self-heals when your UI changes.

$npm install @assrt/sdk