How to fix Playwright geolocation/permissions not applying in tests?

Playwright

Geolocation and browser permissions fail to apply in Playwright tests when they are set at the wrong stage of the browser context lifecycle. The browser only evaluates permissions in the context of a specific origin, and geolocation values must be set before the page makes a getCurrentPosition() call. Setting permissions after page.goto() or after the JavaScript that reads geolocation has already run produces either a permission denied error or stale coordinates.

Common mistake

test('shows nearby stores', async ({ page, context }) => {
  await page.goto('https://app.example.com/stores');

  // Too late — the geolocation request already fired during page load
  await context.setGeolocation({ latitude: 37.7749, longitude: -122.4194 });
  await context.grantPermissions(['geolocation']);

  await page.getByRole('button', { name: 'Find nearby' }).click();
  // Shows no stores because coords weren't set when requested
});

The fix

Set geolocation and permissions before navigating, using test.use() for project-wide defaults or context.setGeolocation() for per-test overrides:

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

// Project-level default — applies to all tests in this file
test.use({
  geolocation: { latitude: 37.7749, longitude: -122.4194 }, // San Francisco
  permissions: ['geolocation'],
});

test('shows nearby stores in San Francisco', async ({ page }) => {
  await page.goto('https://app.example.com/stores');
  await page.getByRole('button', { name: 'Find nearby' }).click();

  await expect(page.getByText('Stores near you')).toBeVisible();
  await expect(page.getByRole('listitem').first()).toBeVisible();
});

For tests that need different locations:

test('shows Tokyo stores', async ({ page, context }) => {
  // Override before any navigation
  await context.setGeolocation({ latitude: 35.6762, longitude: 139.6503 });
  await context.grantPermissions(['geolocation'], {
    origin: 'https://app.example.com',
  });

  await page.goto('https://app.example.com/stores');
  await page.getByRole('button', { name: 'Find nearby' }).click();

  await expect(page.getByText('Tokyo')).toBeVisible();
});

For testing permission denied behavior:

test('shows permission prompt fallback', async ({ page, context }) => {
  // Explicitly deny geolocation permission
  await context.grantPermissions([], { origin: 'https://app.example.com' });

  await page.goto('https://app.example.com/stores');
  await page.getByRole('button', { name: 'Find nearby' }).click();

  await expect(page.getByText('Enable location access')).toBeVisible();
});

Why it works

Browser permissions are evaluated at the context level, and the grantPermissions call configures the permission registry for a specific origin before any page interaction. When the application calls navigator.geolocation.getCurrentPosition(), the browser checks this registry synchronously and returns the configured values without prompting. Playwright's test.use({ geolocation, permissions }) applies settings during context creation, which happens before any page navigation in the test, ensuring the permission state is correct from the first navigation.

Tips

  • Always pair permissions: ['geolocation'] with a geolocation value — granting the permission without setting coordinates will return a default location (or zero coordinates) that may confuse the application.
  • For notifications, camera, and microphone permissions, the same pattern applies — grant before navigation.
  • If geolocation works locally but not in CI, verify the CI container doesn't restrict system calls that Chromium uses to emulate geolocation — running Chromium with --no-sandbox can be necessary on some restricted kernels.
  • Use page.evaluate(() => navigator.permissions.query({ name: 'geolocation' })) to verify the permission state from within the page if debugging.