How do I handle geolocation and permissions in Playwright?
Handling geolocation and permissions in Playwright is essential for testing location-based features and applications that require specific browser permissions. Playwright provides robust APIs to manage these settings, allowing you to simulate different geographic locations and control browser permissions for comprehensive testing scenarios.
Understanding Geolocation in Playwright
Geolocation handling in Playwright involves setting specific coordinates that the browser will report when a web application requests the user's location. This is particularly useful for testing location-based services, maps applications, and features that depend on geographic positioning.
Setting Up Geolocation
Basic Geolocation Configuration
You can set geolocation at the browser context level, which applies to all pages within that context:
// JavaScript/Node.js
const { chromium } = require('playwright');
async function setupGeolocation() {
const browser = await chromium.launch();
const context = await browser.newContext({
geolocation: { longitude: -122.4194, latitude: 37.7749 }, // San Francisco
permissions: ['geolocation']
});
const page = await context.newPage();
await page.goto('https://example.com');
// The page will now report San Francisco coordinates
await browser.close();
}
# Python
import asyncio
from playwright.async_api import async_playwright
async def setup_geolocation():
async with async_playwright() as p:
browser = await p.chromium.launch()
context = await browser.new_context(
geolocation={"longitude": -122.4194, "latitude": 37.7749}, # San Francisco
permissions=["geolocation"]
)
page = await context.new_page()
await page.goto("https://example.com")
# The page will now report San Francisco coordinates
await browser.close()
asyncio.run(setup_geolocation())
Dynamic Geolocation Updates
You can also update geolocation settings dynamically during test execution:
// JavaScript
async function updateGeolocation() {
const browser = await chromium.launch();
const context = await browser.newContext({
permissions: ['geolocation']
});
const page = await context.newPage();
// Set initial location (New York)
await context.setGeolocation({ longitude: -74.0060, latitude: 40.7128 });
await page.goto('https://maps.google.com');
// Later, update to different location (London)
await context.setGeolocation({ longitude: -0.1276, latitude: 51.5074 });
await page.reload();
await browser.close();
}
# Python
async def update_geolocation():
async with async_playwright() as p:
browser = await p.chromium.launch()
context = await browser.new_context(permissions=["geolocation"])
page = await context.new_page()
# Set initial location (New York)
await context.set_geolocation(longitude=-74.0060, latitude=40.7128)
await page.goto("https://maps.google.com")
# Later, update to different location (London)
await context.set_geolocation(longitude=-0.1276, latitude=51.5074)
await page.reload()
await browser.close()
Managing Browser Permissions
Common Permission Types
Playwright supports various browser permissions that you can grant or deny:
// JavaScript - Comprehensive permissions setup
const context = await browser.newContext({
permissions: [
'geolocation',
'camera',
'microphone',
'notifications',
'clipboard-read',
'clipboard-write',
'persistent-storage',
'background-sync'
]
});
# Python - Comprehensive permissions setup
context = await browser.new_context(
permissions=[
"geolocation",
"camera",
"microphone",
"notifications",
"clipboard-read",
"clipboard-write",
"persistent-storage",
"background-sync"
]
)
Dynamic Permission Management
You can grant or revoke permissions dynamically during test execution:
// JavaScript
async function managePermissions() {
const browser = await chromium.launch();
const context = await browser.newContext();
const page = await context.newPage();
// Grant geolocation permission
await context.grantPermissions(['geolocation']);
// Set geolocation
await context.setGeolocation({ longitude: -122.4194, latitude: 37.7749 });
await page.goto('https://example.com');
// Later, revoke geolocation permission
await context.clearPermissions();
// Or grant different permissions
await context.grantPermissions(['camera', 'microphone']);
await browser.close();
}
# Python
async def manage_permissions():
async with async_playwright() as p:
browser = await p.chromium.launch()
context = await browser.new_context()
page = await context.new_page()
# Grant geolocation permission
await context.grant_permissions(["geolocation"])
# Set geolocation
await context.set_geolocation(longitude=-122.4194, latitude=37.7749)
await page.goto("https://example.com")
# Later, revoke geolocation permission
await context.clear_permissions()
# Or grant different permissions
await context.grant_permissions(["camera", "microphone"])
await browser.close()
Testing Geolocation Features
Verifying Location Detection
Here's how to test that your application correctly handles geolocation:
// JavaScript
async function testGeolocation() {
const browser = await chromium.launch();
const context = await browser.newContext({
geolocation: { longitude: 2.3522, latitude: 48.8566 }, // Paris
permissions: ['geolocation']
});
const page = await context.newPage();
await page.goto('https://your-app.com');
// Wait for location to be detected
await page.waitForSelector('.location-display');
// Verify the location is correctly displayed
const locationText = await page.textContent('.location-display');
expect(locationText).toContain('Paris');
// Test location accuracy
const coordinates = await page.evaluate(() => {
return new Promise((resolve) => {
navigator.geolocation.getCurrentPosition((position) => {
resolve({
latitude: position.coords.latitude,
longitude: position.coords.longitude
});
});
});
});
expect(coordinates.latitude).toBeCloseTo(48.8566, 4);
expect(coordinates.longitude).toBeCloseTo(2.3522, 4);
await browser.close();
}
Testing Location-Based Features
When testing applications that depend on user location, you can simulate different scenarios:
// JavaScript - Testing location-based search
async function testLocationBasedSearch() {
const browser = await chromium.launch();
const context = await browser.newContext({
geolocation: { longitude: -73.935242, latitude: 40.730610 }, // New York
permissions: ['geolocation']
});
const page = await context.newPage();
await page.goto('https://restaurant-finder.com');
// Click "Find restaurants near me"
await page.click('[data-testid="find-nearby"]');
// Wait for results
await page.waitForSelector('.restaurant-list');
// Verify results are location-appropriate
const restaurants = await page.$$eval('.restaurant-item', items =>
items.map(item => item.textContent)
);
// Test moving to different location
await context.setGeolocation({ longitude: -122.4194, latitude: 37.7749 }); // San Francisco
await page.click('[data-testid="refresh-location"]');
await page.waitForSelector('.restaurant-list');
const newRestaurants = await page.$$eval('.restaurant-item', items =>
items.map(item => item.textContent)
);
// Verify results changed
expect(newRestaurants).not.toEqual(restaurants);
await browser.close();
}
Advanced Permission Scenarios
Testing Permission Requests
Test how your application handles permission requests and denials:
// JavaScript
async function testPermissionRequests() {
const browser = await chromium.launch();
const context = await browser.newContext(); // No permissions granted initially
const page = await context.newPage();
// Listen for permission requests
page.on('dialog', async dialog => {
console.log('Permission dialog:', dialog.message());
await dialog.accept();
});
await page.goto('https://your-app.com');
// Trigger geolocation request
await page.click('[data-testid="get-location"]');
// Grant permission when requested
await context.grantPermissions(['geolocation']);
await context.setGeolocation({ longitude: -74.0060, latitude: 40.7128 });
// Verify location was obtained
await page.waitForSelector('.location-success');
await browser.close();
}
Testing Multiple Devices and Locations
Simulate different devices in various locations:
# Python
async def test_multiple_locations():
async with async_playwright() as p:
browser = await p.chromium.launch()
# Test locations
locations = [
{"name": "New York", "longitude": -74.0060, "latitude": 40.7128},
{"name": "London", "longitude": -0.1276, "latitude": 51.5074},
{"name": "Tokyo", "longitude": 139.6503, "latitude": 35.6762}
]
for location in locations:
context = await browser.new_context(
geolocation={"longitude": location["longitude"], "latitude": location["latitude"]},
permissions=["geolocation"]
)
page = await context.new_page()
await page.goto("https://weather-app.com")
# Wait for location-based content
await page.wait_for_selector(".weather-location")
# Verify location-specific content
location_text = await page.text_content(".weather-location")
print(f"Testing from {location['name']}: {location_text}")
await context.close()
await browser.close()
Best Practices for Geolocation Testing
1. Test Edge Cases
Always test scenarios where geolocation might fail or be unavailable:
// JavaScript
async function testGeolocationEdgeCases() {
const browser = await chromium.launch();
// Test with no geolocation permission
const contextNoPermission = await browser.newContext({
permissions: [] // No geolocation permission
});
const pageNoPermission = await contextNoPermission.newPage();
await pageNoPermission.goto('https://your-app.com');
// Test how app handles geolocation denial
await pageNoPermission.click('[data-testid="get-location"]');
await pageNoPermission.waitForSelector('.location-error');
await contextNoPermission.close();
// Test with invalid coordinates
const contextInvalid = await browser.newContext({
geolocation: { longitude: 999, latitude: 999 }, // Invalid coordinates
permissions: ['geolocation']
});
const pageInvalid = await contextInvalid.newPage();
await pageInvalid.goto('https://your-app.com');
await contextInvalid.close();
await browser.close();
}
2. Verify Accuracy Requirements
Test that your application handles location accuracy appropriately:
// JavaScript
async function testLocationAccuracy() {
const browser = await chromium.launch();
const context = await browser.newContext({
geolocation: { longitude: -122.4194, latitude: 37.7749, accuracy: 100 },
permissions: ['geolocation']
});
const page = await context.newPage();
await page.goto('https://your-app.com');
const accuracy = await page.evaluate(() => {
return new Promise((resolve) => {
navigator.geolocation.getCurrentPosition((position) => {
resolve(position.coords.accuracy);
});
});
});
expect(accuracy).toBe(100);
await browser.close();
}
CLI Commands for Geolocation Testing
When running Playwright tests with geolocation, you can use command-line options:
# Run tests with specific geolocation
npx playwright test --project=chromium --use-device="iPhone 12" --geolocation="40.7128,-74.0060"
# Run tests with geolocation permissions
npx playwright test --project=chromium --permissions=geolocation
Troubleshooting Common Issues
Issue 1: Geolocation Not Working
If geolocation isn't working as expected, ensure you're granting the proper permissions:
// Correct approach
const context = await browser.newContext({
geolocation: { longitude: -122.4194, latitude: 37.7749 },
permissions: ['geolocation'] // Don't forget this!
});
Issue 2: Permission Dialogs Not Appearing
When testing permission flows, make sure you're not pre-granting permissions if you want to test the dialog:
// To test permission dialog
const context = await browser.newContext(); // No permissions granted
page.on('dialog', async dialog => {
// Handle permission dialog
await dialog.accept();
});
Issue 3: Coordinates Not Updating
If coordinates aren't updating after calling setGeolocation()
, try reloading the page or triggering a fresh geolocation request:
// Update location and refresh
await context.setGeolocation({ longitude: -0.1276, latitude: 51.5074 });
await page.reload(); // or trigger a new geolocation request
Integration with Testing Frameworks
Jest Example
// Jest test example
describe('Geolocation Features', () => {
test('should display nearby restaurants', async () => {
const context = await browser.newContext({
geolocation: { longitude: -73.935242, latitude: 40.730610 },
permissions: ['geolocation']
});
const page = await context.newPage();
await page.goto('https://restaurant-app.com');
await page.click('[data-testid="find-nearby"]');
await expect(page.locator('.restaurant-list')).toBeVisible();
await context.close();
});
});
Mocha Example
// Mocha test example
describe('Location Services', function() {
it('should handle location permission denied', async function() {
const context = await browser.newContext({
permissions: [] // No geolocation permission
});
const page = await context.newPage();
await page.goto('https://your-app.com');
await page.click('[data-testid="get-location"]');
const errorMessage = await page.textContent('.location-error');
expect(errorMessage).to.contain('Location access denied');
await context.close();
});
});
Configuration Files
You can also configure geolocation settings in your Playwright configuration file:
// playwright.config.js
module.exports = {
use: {
geolocation: { longitude: -122.4194, latitude: 37.7749 },
permissions: ['geolocation']
},
projects: [
{
name: 'chromium-nyc',
use: {
...devices['Desktop Chrome'],
geolocation: { longitude: -74.0060, latitude: 40.7128 }, // New York
permissions: ['geolocation']
}
},
{
name: 'chromium-london',
use: {
...devices['Desktop Chrome'],
geolocation: { longitude: -0.1276, latitude: 51.5074 }, // London
permissions: ['geolocation']
}
}
]
};
Managing geolocation and permissions in Playwright enables comprehensive testing of location-based features and permission-dependent functionality. By understanding how to handle browser sessions in Puppeteer and applying similar concepts to Playwright, you can create robust test suites that cover various geographic scenarios and permission states.
For applications that heavily rely on location services, proper geolocation testing ensures your users have a consistent experience regardless of their geographic location. Similarly, when working with authentication in Puppeteer, understanding permission flows becomes crucial for comprehensive testing strategies.
Remember to test both the happy path where permissions are granted and locations are accurate, as well as edge cases where permissions are denied or geolocation services are unavailable. This comprehensive approach will help you build more resilient location-aware applications that gracefully handle various user scenarios and browser restrictions.