Two things to know before you start

The six interactions

Every example below implements the same six steps. They are defined once here rather than repeated in each code sample:

  1. Visit — navigate to https://www.google.com/maps.
  2. Search — locate the search box (has aria-label="Search Google Maps" in current Google Maps markup — note this may drift over time like any third-party site) and type a query, e.g. "Cardiff Castle", then submit (Enter, or the search button, which typically has aria-label="Search").
  3. Click a link/result — click the first search result in the results panel (results are normally DOM list items, not canvas — findable by role or text even though the map itself is canvas).
  4. Scroll — simulate a scroll/pan over the map canvas: for tools with a real mouse-wheel/scroll API, dispatch a wheel event or scroll gesture over the canvas element's bounding box, not over the whole page.
  5. Zoom — click the zoom-in button (aria-label="Zoom in") or zoom-out button (aria-label="Zoom out") — a much more reliable approach than trying to pinch or scroll-zoom the canvas programmatically, and worth calling out as the pragmatic choice.
  6. Activate a layer — open the Layers panel (button aria-label="Layers"), then choose a layer such as "Traffic" (typically a labelled option or button once the panel is open — described below as "the traffic layer toggle" without overclaiming an exact stable selector).

Selenium

All three languages below use aria-label-based locators throughout, per the lesson above. The Rust example uses thirtyfour, the de facto Selenium/WebDriver client for Rust — there is no official one, and its name nods to selenium's atomic number, 34.

Selenium + JavaScript
const { Builder, By, Key, until } = require('selenium-webdriver');

(async function googleMapsWalkthrough() {
  const driver = await new Builder().forBrowser('chrome').build();

  try {
    // 1. Visit
    await driver.get('https://www.google.com/maps');

    // 2. Search
    const searchBox = await driver.findElement(By.css('[aria-label="Search Google Maps"]'));
    await searchBox.sendKeys('Cardiff Castle', Key.RETURN);

    // 3. Click the first result
    const firstResult = await driver.wait(
      until.elementLocated(By.css('[role="feed"] a')),
      10000
    );
    await firstResult.click();

    // 4. Scroll: dispatch a synthetic wheel event over the map canvas.
    // (selenium-webdriver's newer driver.actions().scroll() API works
    // too; this executeScript fallback is shown because it works across
    // more selenium-webdriver versions.)
    const canvas = await driver.findElement(By.css('canvas'));
    await driver.executeScript((el) => {
      const rect = el.getBoundingClientRect();
      el.dispatchEvent(
        new WheelEvent('wheel', {
          deltaY: 200,
          clientX: rect.x + rect.width / 2,
          clientY: rect.y + rect.height / 2,
          bubbles: true
        })
      );
    }, canvas);

    // 5. Zoom in
    const zoomIn = await driver.findElement(By.css('[aria-label="Zoom in"]'));
    await zoomIn.click();

    // 6. Toggle the traffic layer
    const layersButton = await driver.findElement(By.css('[aria-label="Layers"]'));
    await layersButton.click();
    const trafficOption = await driver.wait(
      until.elementLocated(By.xpath("//*[contains(text(), 'Traffic')]")),
      5000
    );
    await trafficOption.click();
  } finally {
    await driver.quit();
  }
})();
Selenium + Python
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

driver = webdriver.Chrome()

try:
    # 1. Visit
    driver.get('https://www.google.com/maps')

    # 2. Search
    search_box = driver.find_element(By.CSS_SELECTOR, '[aria-label="Search Google Maps"]')
    search_box.send_keys('Cardiff Castle')
    search_box.send_keys(Keys.RETURN)

    # 3. Click the first result
    first_result = WebDriverWait(driver, 10).until(
        EC.presence_of_element_located((By.CSS_SELECTOR, '[role="feed"] a'))
    )
    first_result.click()

    # 4. Scroll: dispatch a synthetic wheel event over the map canvas,
    # mirroring the JavaScript example's executeScript fallback above.
    # (ActionChains.scroll_to_element / scroll_by_amount is a real
    # alternative on recent selenium versions.)
    canvas = driver.find_element(By.CSS_SELECTOR, 'canvas')
    driver.execute_script(
        """
        const rect = arguments[0].getBoundingClientRect();
        arguments[0].dispatchEvent(new WheelEvent('wheel', {
            deltaY: 200,
            clientX: rect.x + rect.width / 2,
            clientY: rect.y + rect.height / 2,
            bubbles: true
        }));
        """,
        canvas
    )

    # 5. Zoom in
    driver.find_element(By.CSS_SELECTOR, '[aria-label="Zoom in"]').click()

    # 6. Toggle the traffic layer
    driver.find_element(By.CSS_SELECTOR, '[aria-label="Layers"]').click()
    traffic_option = WebDriverWait(driver, 5).until(
        EC.presence_of_element_located((By.XPATH, "//*[contains(text(), 'Traffic')]"))
    )
    traffic_option.click()
finally:
    driver.quit()
Selenium + Rust
use thirtyfour::prelude::*;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let caps = DesiredCapabilities::chrome();
    let driver = WebDriver::new("http://localhost:9515", caps).await?;

    // 1. Visit
    driver.goto("https://www.google.com/maps").await?;

    // 2. Search
    let search_box = driver
        .query(By::Css("[aria-label='Search Google Maps']"))
        .desc("Maps search box")
        .single()
        .await?;
    search_box.send_keys("Cardiff Castle\u{E007}").await?;

    // 3. Click the first result. Results are ordinary DOM list items, not
    // canvas, so they are findable like any other element.
    let first_result = driver
        .query(By::Css("[role='feed'] a"))
        .desc("first search result")
        .single()
        .await?;
    first_result.click().await?;

    // 4. Scroll: dispatch a synthetic wheel event over the map canvas.
    // thirtyfour has no dedicated scroll gesture, so this mirrors the
    // execute-script fallback used in the JavaScript and Python examples.
    driver
        .execute(
            r#"const el = document.querySelector('canvas');
               const rect = el.getBoundingClientRect();
               el.dispatchEvent(new WheelEvent('wheel', {
                   deltaY: 200,
                   clientX: rect.x + rect.width / 2,
                   clientY: rect.y + rect.height / 2,
                   bubbles: true
               }));"#,
            Vec::new(),
        )
        .await?;

    // 5. Zoom in
    let zoom_in = driver
        .query(By::Css("[aria-label='Zoom in']"))
        .desc("Zoom in button")
        .single()
        .await?;
    zoom_in.click().await?;

    // 6. Toggle the traffic layer
    let layers_button = driver
        .query(By::Css("[aria-label='Layers']"))
        .desc("Layers button")
        .single()
        .await?;
    layers_button.click().await?;

    let traffic_option = driver
        .query(By::XPath("//*[contains(text(), 'Traffic')]"))
        .desc("traffic layer toggle")
        .single()
        .await?;
    traffic_option.click().await?;

    driver.quit().await?;
    Ok(())
}

Playwright

Playwright ships official bindings for JavaScript, Python, .NET, and Java. Rust is community-maintained: playwright-rs (actively developed by padamson/playwright-rust, pre-1.0) is the example below. The older octaltree/playwright-rust crate, published on crates.io as playwright, has been abandoned since 2022.

Playwright + JavaScript
const { chromium } = require('playwright');

(async () => {
  const browser = await chromium.launch();
  const page = await browser.newPage();

  // 1. Visit
  await page.goto('https://www.google.com/maps');

  // 2. Search
  await page.fill('[aria-label="Search Google Maps"]', 'Cardiff Castle');
  await page.keyboard.press('Enter');

  // 3. Click the first result
  await page.locator('[role="feed"] a').first().click();

  // 4. Scroll: Playwright has a real, clean API for this — hover the
  // canvas, then dispatch a mouse-wheel gesture over it. Far more
  // ergonomic than the executeScript-wheel-event fallback the other
  // tools needed.
  await page.hover('canvas');
  await page.mouse.wheel(0, 200);

  // 5. Zoom in
  await page.click('[aria-label="Zoom in"]');

  // 6. Toggle the traffic layer
  await page.click('[aria-label="Layers"]');
  await page.click('text=Traffic');

  await browser.close();
})();
Playwright + Python
from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch()
    page = browser.new_page()

    # 1. Visit
    page.goto('https://www.google.com/maps')

    # 2. Search
    page.fill('[aria-label="Search Google Maps"]', 'Cardiff Castle')
    page.keyboard.press('Enter')

    # 3. Click the first result
    page.locator('[role="feed"] a').first.click()

    # 4. Scroll: hover the canvas, then dispatch a mouse-wheel gesture —
    # Playwright's real advantage here over the other tools' fallbacks.
    page.hover('canvas')
    page.mouse.wheel(0, 200)

    # 5. Zoom in
    page.click('[aria-label="Zoom in"]')

    # 6. Toggle the traffic layer
    page.click('[aria-label="Layers"]')
    page.click('text=Traffic')

    browser.close()
Playwright + Rust
use playwright_rs::Playwright;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let pw = Playwright::launch().await?;
    let browser = pw.chromium().launch().await?;
    let page = browser.new_page().await?;

    // 1. Visit
    page.goto("https://www.google.com/maps", None).await?;

    // 2. Search, then submit
    let search_box = page.locator("[aria-label='Search Google Maps']");
    search_box.fill("Cardiff Castle", None).await?;
    search_box.press("Enter", None).await?;

    // 3. Click the first result
    let first_result = page.locator("[role='feed'] a");
    first_result.click(None).await?;

    // 4. Scroll: omitted here. playwright-rs is pre-1.0 and, unlike the
    // JavaScript and Python bindings above, does not yet expose an
    // ergonomic mouse-wheel API — see the JavaScript example for the
    // pattern (hover the canvas, then page.mouse.wheel) this crate is
    // expected to grow into.

    // 5. Zoom in
    let zoom_in = page.locator("[aria-label='Zoom in']");
    zoom_in.click(None).await?;

    // 6. Toggle the traffic layer
    let layers_button = page.locator("[aria-label='Layers']");
    layers_button.click(None).await?;
    let traffic_option = page.locator("text=Traffic");
    traffic_option.click(None).await?;

    browser.close().await?;
    Ok(())
}

Back to Examples Google Search Examples