Resource library

QA How-To

How to Use Playwright geolocation emulation (2026)

Learn playwright geolocation emulation with permissions, secure origins, dynamic location changes, regional projects, fallback testing, and debugging for 2026.

22 min read | 2,636 words

TL;DR

Set `geolocation` and `permissions: ["geolocation"]` on the Playwright browser context, then assert the application outcome. Use `context.setGeolocation()` to move and `context.setGeolocation(null)` when position should be unavailable.

Key Takeaways

  • Control both context geolocation and geolocation permission for positive location tests.
  • Grant permission to the exact application origin when a scenario crosses trust boundaries.
  • Use context.setGeolocation() for movement and pass null to emulate position unavailable.
  • Configure locale, timezone, and IP-based region independently from browser coordinates.
  • Assert store, delivery, jurisdiction, or fallback behavior rather than only raw coordinates.
  • Keep contexts isolated and coordinate fixtures synthetic, named, stable, and privacy-safe.

Playwright can emulate a browser location by setting coordinates on a browser context and granting the geolocation permission. A dependable playwright geolocation emulation test controls both pieces, then verifies the application's observable behavior instead of assuming coordinates alone are enough.

For Playwright Test, configure geolocation and permissions in use, in test.use(), or in a project. For a location change during a test, call await context.setGeolocation(...). This guide builds a secure-origin, self-contained example and covers permission scope, unavailable position, locale, timezone, test design, and CI diagnosis.

TL;DR

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

test.use({
  geolocation: { latitude: 37.7749, longitude: -122.4194 },
  permissions: ['geolocation'],
});

test('uses the emulated location', async ({ page }) => {
  await page.goto('/stores');
  await expect(page.getByTestId('nearest-city')).toHaveText('San Francisco');
});
Goal Playwright API Scope
Give tests a fixed position use.geolocation or test.use() New browser context
Allow location access permissions: ['geolocation'] Browser context
Limit permission to one site context.grantPermissions(..., { origin }) Specified origin
Move during a test context.setGeolocation(position) Existing context
Make position unavailable context.setGeolocation(null) Existing context
Remove granted permissions context.clearPermissions() Existing context

Latitude must be between -90 and 90, longitude between -180 and 180, and optional accuracy must be non-negative.

1. What playwright geolocation emulation controls

Geolocation emulation changes what pages in a Playwright browser context can receive through the web Geolocation API. A position contains latitude, longitude, and optionally accuracy. The context isolates that state from other contexts, which makes parallel tests deterministic when each test uses its own standard Playwright context fixture.

Location and permission are separate controls. Setting coordinates does not automatically approve a site's request. Granting the permission without setting coordinates does not define the intended position. Most positive tests need both:

test.use({
  geolocation: {
    latitude: 51.5074,
    longitude: -0.1278,
    accuracy: 25,
  },
  permissions: ['geolocation'],
});

The browser supplies the emulated position to page code that calls navigator.geolocation.getCurrentPosition() or observes location. Playwright is not changing your test runner's physical location, public IP address, locale, timezone, currency, map database, or backend region. Those are independent signals.

It also does not validate whether a coordinate belongs to the city name used in a fixture. Playwright accepts valid numeric ranges, while geographic meaning belongs to your test data and services. Review named fixtures carefully and keep a trusted source for boundary coordinates. A syntactically valid but mislabeled point can make an application look wrong when the test data is wrong.

This boundary matters. A product may determine region from IP on the server and use browser geolocation only after consent. Emulating coordinates will test the latter, not the former. State the source of truth in the scenario before choosing an assertion.

Use precise coordinates only as test fixtures, not as claims about a real user's exact address. For most business tests, a representative point inside a service region is better than a boundary point unless the boundary itself is under test.

2. Configure geolocation in Playwright Test

Global configuration is suitable when most tests use the same location. Place both options under use in playwright.config.ts:

import { defineConfig } from '@playwright/test';

export default defineConfig({
  use: {
    baseURL: 'https://app.example.test',
    geolocation: {
      latitude: 40.7128,
      longitude: -74.0060,
      accuracy: 50,
    },
    permissions: ['geolocation'],
  },
});

The page and context fixtures for each test inherit those values. When location is relevant only to one group, keep it local:

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

test.describe('New York store discovery', () => {
  test.use({
    geolocation: { latitude: 40.7128, longitude: -74.0060 },
    permissions: ['geolocation'],
  });

  test('suggests the local store', async ({ page }) => {
    await page.goto('/stores');
    await expect(page.getByRole('heading', { name: /new york/i }))
      .toBeVisible();
  });
});

Declare test.use() at file scope or inside a test.describe() block, not midway through a running test. These options configure the context created for the test.

For a suite with distinct regions, projects provide clearer reporting than conditionals inside tests. Name each project for the scenario, spread an appropriate device if needed, and set location, permission, locale, and timezone intentionally. Section 7 shows that matrix.

3. Run a self-contained secure-origin location test

Geolocation is a powerful browser feature and is normally associated with secure contexts. A robust isolated example should therefore load a synthetic HTTPS origin instead of relying on about:blank. Playwright routing can fulfill that origin without a web server.

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

test('renders coordinates from the browser Geolocation API', async ({
  context,
  page,
}) => {
  const origin = 'https://geo.example.test';
  await context.grantPermissions(['geolocation'], { origin });
  await context.setGeolocation({
    latitude: 37.7749,
    longitude: -122.4194,
    accuracy: 20,
  });

  await page.route(origin + '/', route => route.fulfill({
    contentType: 'text/html',
    body: `
      <button id="locate">Use my location</button>
      <output id="result">Not requested</output>
      <script>
        document.querySelector('#locate').addEventListener('click', () => {
          navigator.geolocation.getCurrentPosition(
            position => {
              const { latitude, longitude } = position.coords;
              document.querySelector('#result').textContent =
                latitude.toFixed(4) + ',' + longitude.toFixed(4);
            },
            error => {
              document.querySelector('#result').textContent =
                'ERROR:' + error.code;
            }
          );
        });
      </script>
    `,
  }));

  await page.goto(origin + '/');
  await page.getByRole('button', { name: 'Use my location' }).click();
  await expect(page.getByRole('status'))
    .toHaveText('37.7749,-122.4194');
});

The <output> element has an implicit status role, so the assertion uses getByRole('status'). The test grants permission only to the synthetic origin, sets the position, triggers the real browser API, and asserts page output. It does not call application internals.

In a production suite, route only when testing UI logic in isolation. Keep separate integration coverage for the real reverse-geocoding or store-search service.

4. Grant geolocation permission with the right scope

Playwright supports permission configuration at context creation and dynamic grants with browserContext.grantPermissions(). Choose based on the scenario's trust boundary.

await context.grantPermissions(['geolocation'], {
  origin: 'https://app.example.test',
});

An origin is scheme, host, and port. https://app.example.test and https://admin.example.test are different origins. Granting to the application origin makes intent clearer and reduces accidental access by third-party pages opened during a test.

Configuration such as permissions: ['geolocation'] grants the listed permission to pages in that context according to Playwright's browser support. It is convenient for dedicated contexts and project matrices. Origin-scoped grants are preferable when the test navigates across trust boundaries or specifically verifies that one site receives access.

Permissions are browser-context state. A new standard Playwright Test context starts isolated, so one test's grant should not leak into another. If you manually share a context, you take responsibility for cleanup and concurrency. Prefer the built-in per-test context unless setup cost and isolation have been carefully designed.

Use await context.clearPermissions() to clear permission overrides. Do not assume that clearing always produces the same visible prompt behavior in every browser and headless environment. A denial scenario should assert your application's defined fallback and may need browser-specific expectations.

The permission prompt itself is browser chrome, not a normal DOM dialog. Do not use page.on('dialog') for geolocation permission prompts. Playwright's permission APIs are the correct control surface.

5. Change location during a running test

Call context.setGeolocation() when a scenario needs movement or a new position after the context already exists. Pages in that context receive the updated emulated state. Applications using watchPosition() can react to changes.

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

test('updates a location watcher after movement', async ({ context, page }) => {
  const origin = 'https://tracker.example.test';
  await context.grantPermissions(['geolocation'], { origin });
  await context.setGeolocation({ latitude: 34.0522, longitude: -118.2437 });
  await page.route(origin + '/', route => route.fulfill({
    contentType: 'text/html',
    body: `
      <output id="latitude">Waiting</output>
      <script>
        navigator.geolocation.watchPosition(position => {
          document.querySelector('#latitude').textContent =
            position.coords.latitude.toFixed(4);
        });
      </script>
    `,
  }));

  await page.goto(origin + '/');

  await expect(page.getByRole('status')).toHaveText('34.0522');

  await context.setGeolocation({ latitude: 47.6062, longitude: -122.3321 });
  await expect(page.getByRole('status')).toHaveText('47.6062');
});

The watcher starts on a secure synthetic origin and receives both the initial and updated positions. A real application may debounce, smooth, or ignore small changes. Choose two points that cross a meaningful product threshold rather than assuming every coordinate update must repaint immediately.

Define why movement matters. Delivery availability might update after a significant location change, while a route tracker might record a sequence. Avoid loops that simulate hundreds of GPS points in an end-to-end test. Test interpolation and geometry at a lower layer, then use a few browser-level checkpoints to prove integration.

After each move, assert an observable application outcome. Merely calling setGeolocation() proves only that the test requested a new emulated position.

6. Test unavailable location and fallback behavior

Passing null to context.setGeolocation() emulates position unavailable. This is different from withholding permission. It models a situation where access is allowed but a position cannot be supplied.

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

test('offers manual entry when position is unavailable', async ({
  context,
  page,
}) => {
  const origin = 'https://delivery.example.test';
  await context.grantPermissions(['geolocation'], { origin });
  await context.setGeolocation(null);

  await page.route(origin + '/', route => route.fulfill({
    contentType: 'text/html',
    body: `
      <button>Find nearby delivery</button>
      <p role="alert"></p>
      <label hidden>Postal code <input></label>
      <script>
        document.querySelector('button').addEventListener('click', () => {
          navigator.geolocation.getCurrentPosition(
            () => {},
            () => {
              document.querySelector('[role=alert]').textContent =
                'Location unavailable. Enter a postal code.';
              document.querySelector('label').hidden = false;
            }
          );
        });
      </script>
    `,
  }));

  await page.goto(origin + '/');
  await page.getByRole('button', { name: 'Find nearby delivery' }).click();
  await expect(page.getByRole('alert'))
    .toHaveText('Location unavailable. Enter a postal code.');
  await expect(page.getByLabel('Postal code')).toBeVisible();
});

A mature product should provide a recovery path: manual address entry, retained last-known region, or a clear retry. Assert the path, not a browser-specific error string.

Record useful operational evidence without storing unnecessarily precise personal coordinates. A failure category such as position unavailable and the affected feature may be enough. Privacy requirements can limit logging precision, retention, and access even in test environments. Use synthetic coordinates and test accounts whenever the scenario allows it.

Separate tests can cover user denial, unavailable position, service failure after coordinates are received, and coordinates outside the service area. These mechanisms may lead to similar UI, but they require different logging and remediation.

7. Combine coordinates, locale, and timezone intentionally

Coordinates do not automatically change navigator.language, Intl formatting, or the browser timezone. Configure those independently when testing a coherent regional experience.

import { defineConfig } from '@playwright/test';

export default defineConfig({
  projects: [
    {
      name: 'london-en-gb',
      use: {
        geolocation: { latitude: 51.5074, longitude: -0.1278 },
        permissions: ['geolocation'],
        locale: 'en-GB',
        timezoneId: 'Europe/London',
      },
    },
    {
      name: 'tokyo-ja-jp',
      use: {
        geolocation: { latitude: 35.6762, longitude: 139.6503 },
        permissions: ['geolocation'],
        locale: 'ja-JP',
        timezoneId: 'Asia/Tokyo',
      },
    },
  ],
});
Signal Controls Does not automatically control
Browser geolocation geolocation IP-based country
Permission permissions or grantPermissions() Coordinates
Language and formatting locale Physical position
Browser clock zone timezoneId Test runner timezone
Network origin URL, DNS, proxy, deployment Browser coordinate

Do not bundle all signals if the requirement needs mixed cases. A traveler may have a London location, US English locale, and a device timezone not yet updated. Explicit independent options let you cover those risks.

The browser timezone setting affects the page, not necessarily the Node test runner. Build expected page output from controlled business inputs rather than relying on the host machine's local date.

8. Assert location-aware application behavior

Coordinates are an input, not the final business assertion. Decide which product outcome they should cause: nearest location, delivery eligibility, jurisdiction notice, map center, units, or content availability.

A useful scenario has three layers:

  1. Browser precondition: permission and emulated position are controlled.
  2. Application behavior: the page requests location and sends or processes it.
  3. User outcome: the correct region, service, or fallback is displayed.

For a store finder, assert the named store and distance unit, not only that a latitude appears in a debug field. For regulated content, assert the applicable notice and permitted action. For a map, prefer an accessible address or region label over pixel coordinates when possible.

Stub reverse geocoding for deterministic UI tests, but maintain a smaller integration test against the real contract. Your stub should validate the request enough to catch swapped latitude and longitude. A permissive route that returns London for every request can make an incorrect coordinate payload pass.

Boundary testing deserves controlled points just inside and just outside a service polygon. Keep geometric calculations in unit tests with broad data coverage. Use the browser layer to prove that location enters the system and the resulting decision reaches the user.

When the backend is authoritative, the UI test should not reimplement distance or polygon logic. Send controlled coordinates, return a controlled decision or use a stable test tenant, and assert the decision's presentation. Duplicating geographic algorithms in test code creates two implementations to maintain and can make the same defect appear correct on both sides.

If application behavior includes a confirmation dialog after region selection, the Playwright dialog handling guide covers event registration and dismissal without race conditions.

9. Design cross-browser and parallel coverage

Geolocation and permission behavior can vary across browser engines, operating systems, and headless modes. Run critical location workflows in the Playwright projects your product officially supports. Keep expectations focused on product behavior rather than browser prompt wording.

Each Playwright Test test normally receives an isolated browser context. That isolation is essential for parallel region scenarios. Do not store the current test location in a shared global variable or mutate one worker-wide context. A London test and a Tokyo test running concurrently should have independent contexts and backend data.

Tag or name regional projects clearly so failures show the coordinate set and environment. Keep fixture data in a typed constant:

export const locations = {
  london: { latitude: 51.5074, longitude: -0.1278 },
  tokyo: { latitude: 35.6762, longitude: 139.6503 },
} as const;

Avoid implying that a named coordinate remains permanently appropriate for a changing store boundary. Business-owned service zones should come from controlled test fixtures or contract data, not copied production records.

Retries create new test attempts and usually new contexts. Setup must be repeatable, and backend records should use per-test identifiers. Trace the first failure before treating a retry as resolution.

For reusable context and data setup, typed Playwright fixture patterns explain test scope, worker scope, and teardown.

10. Debug playwright geolocation emulation failures

Start by identifying the failing boundary. Did page code call the Geolocation API? Was permission granted to the exact origin? Did the context have a valid position? Did the application send coordinates correctly? Did a downstream service map them to the expected result?

Inspect the current page origin with page.url(). Permission granted to https://app.example.test will not automatically explain behavior on another subdomain. Confirm latitude and longitude order, because many business APIs and geographic formats use different conventions. Playwright objects name both properties, so avoid unlabeled tuples.

Add a temporary page probe when necessary:

const observed = await page.evaluate(() => new Promise((resolve, reject) => {
  navigator.geolocation.getCurrentPosition(
    position => resolve({
      latitude: position.coords.latitude,
      longitude: position.coords.longitude,
      accuracy: position.coords.accuracy,
    }),
    error => reject(new Error('Geolocation error code: ' + error.code)),
  );
}));
console.log(observed);

Use this as diagnosis, not the only product assertion. If the probe succeeds but the UI fails, move to application code and network evidence. If it fails, inspect secure context, permission, origin, and browser project.

Playwright traces can show page actions, console output, and network activity. The Playwright timeout guide helps when a location flow waits indefinitely. Do not lengthen timeouts until you know whether the app is waiting for permission, position, or a downstream response.

11. Plan geolocation coverage by testing layer

Keep coordinate validation, distance formulas, polygon rules, and formatting permutations in unit or component tests. Use contract tests for the request schema between the browser application and regional service. Reserve Playwright browser tests for permission wiring, browser API integration, important fallbacks, and a few representative user outcomes.

This layered strategy prevents a slow city-by-city UI matrix while retaining confidence. A positive browser case should prove that an approved position reaches the application and produces the right local result. A recovery case should prove that unavailable location leaves the user with a usable alternative. Add movement, denial, or cross-browser cases only when the product behavior differs.

Assign ownership to location fixtures and service zones. Coordinates should be synthetic, documented, and stable within a controlled test environment. Review artifact privacy, because traces and requests can contain precise positions. Coverage is strongest when each layer proves a distinct fact and failures identify the responsible boundary.

Interview Questions and Answers

Q: What two settings are normally required for Playwright geolocation emulation?

Set a geolocation object with latitude and longitude, and grant the geolocation permission. Coordinates define the position, while permission lets the page read it. Treat them as separate browser-context concerns.

Q: How do you change location after a test has started?

Call await context.setGeolocation({ latitude, longitude, accuracy? }). Applications using a location watcher can observe the change. Assert the resulting product state rather than only calling the API.

Q: How do you emulate an unavailable position?

Call await context.setGeolocation(null). This models position unavailable and is distinct from permission denial. Verify the application's manual-entry or retry fallback.

Q: Does geolocation emulation change the browser locale or timezone?

No. Configure locale and timezoneId separately. It also does not change IP-based region, so identify which location signal the product actually uses.

Q: Why should permission sometimes be granted with an origin?

Origin scope makes the trust boundary explicit and avoids unintentionally granting access to other sites visited in the context. The scheme, host, and port must match the page that requests location.

Q: How would you test a delivery-zone boundary?

Use controlled points just inside and outside the zone, stub or control downstream mapping for deterministic browser tests, and assert eligibility plus user messaging. Put broad polygon mathematics in lower-level tests and retain a smaller end-to-end integration path.

Q: What is a common cause of correct emulated coordinates producing the wrong city?

Latitude and longitude may be swapped, the application may use IP location instead of browser geolocation, or a downstream stub may return incorrect mapping. Trace each boundary and validate named coordinate fields rather than using ambiguous arrays.

Common Mistakes

  • Setting coordinates but forgetting to grant geolocation permission.
  • Granting permission while leaving the position undefined.
  • Granting permission to an origin different from the page's scheme, host, or port.
  • Swapping latitude and longitude in application payloads or fixtures.
  • Expecting coordinates to change locale, timezone, currency, or IP region automatically.
  • Testing browser chrome prompts with page.on('dialog').
  • Using about:blank or an unsuitable origin for a realistic permission scenario.
  • Asserting only raw coordinates instead of the location-aware user outcome.
  • Sharing a mutable context or location variable across parallel tests.
  • Using production store boundaries as unstable test data without ownership.
  • Treating unavailable position, denied permission, and backend lookup failure as one mechanism.
  • Increasing timeouts without finding whether permission, position, or service response is missing.

Conclusion

Reliable playwright geolocation emulation controls a browser context's position and permission, uses a secure and correctly scoped origin, and asserts the business result caused by that location. Configure fixed regions through use, change position with context.setGeolocation(), and use null for unavailable-position coverage.

Begin with one positive nearby-location scenario and one recovery scenario. Then add only the regional matrix, boundary points, browser projects, locale combinations, and service integrations justified by product risk.

Interview Questions and Answers

What settings are normally required for positive Playwright geolocation emulation?

Configure a `geolocation` object with latitude and longitude, and grant the `geolocation` permission. The position defines what the browser can return, while permission authorizes the page to read it. Both are scoped to a browser context.

How do you change location after the page starts?

Call `await context.setGeolocation()` with the next position. If the application uses `watchPosition()`, assert the resulting updated state. Avoid assuming the API call alone proves the product handled movement.

How do you emulate position unavailable?

Pass `null` to `context.setGeolocation()`. This keeps the scenario separate from permission denial and lets the test verify the application fallback for a position that cannot be supplied.

Does geolocation emulation change locale, timezone, or IP region?

No. Those are independent signals with separate configuration or infrastructure. A strong test states which source the product uses and can intentionally combine or separate them.

Why grant permission to a specific origin?

Origin scope documents the trust boundary and avoids giving unrelated pages access in a multi-origin flow. The scheme, host, and port must match the page that calls the Geolocation API.

How would you test a service-area boundary?

Use controlled points just inside and outside the boundary, control the service-zone data, and assert eligibility plus user messaging. Test polygon mathematics broadly below the browser layer and keep a small browser integration sample.

What causes valid coordinates to map to the wrong city?

Latitude and longitude may be swapped, the product may use IP location, the geographic fixture may be mislabeled, or a downstream mock may ignore its request. Trace named fields across each boundary and validate the stub input.

Frequently Asked Questions

How do I set geolocation in Playwright Test?

Set `geolocation: { latitude, longitude }` and `permissions: ["geolocation"]` under config `use` or `test.use()`. Playwright creates the test context with that position and permission.

Does setting geolocation automatically grant permission?

No. Position and permission are separate context controls. Most positive tests must set coordinates and grant the `geolocation` permission.

How do I change location during a Playwright test?

Call `await context.setGeolocation({ latitude, longitude, accuracy? })`. A page using `watchPosition()` can react to the change, after which the test should assert a user-visible result.

How do I emulate unavailable location?

Call `await context.setGeolocation(null)`. This is distinct from permission denial and is useful for verifying manual address entry, retry, or another product fallback.

Does Playwright geolocation change locale and timezone?

No. Configure `locale` and `timezoneId` independently. Browser geolocation also does not change public IP or a server-side IP region.

Can I grant geolocation to one origin only?

Yes. Use `context.grantPermissions(["geolocation"], { origin })`. Match the exact scheme, host, and port used by the page requesting location.

Why does a geolocation test time out?

Common causes include missing permission, wrong origin, invalid or swapped coordinates, a page that never requests location, or a stalled downstream service. Inspect each boundary before changing timeouts.

Related Guides