How to Handle Mobile Device Emulation in Puppeteer?
Mobile device emulation is a crucial feature in Puppeteer that allows developers to test how their web applications behave on different mobile devices without actually owning those devices. This comprehensive guide will walk you through various techniques to effectively emulate mobile devices using Puppeteer.
Understanding Mobile Device Emulation
Mobile device emulation in Puppeteer involves simulating several key characteristics of mobile devices:
- Viewport dimensions (screen width and height)
- Device pixel ratio (for high-DPI screens)
- User agent strings (to identify as mobile browsers)
- Touch events (instead of mouse events)
- Geolocation (for location-based testing)
- Network conditions (to simulate mobile networks)
Basic Mobile Device Emulation
Setting Up Basic Mobile Emulation
The simplest way to emulate a mobile device is by using Puppeteer's built-in device descriptors:
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch({ headless: false });
const page = await browser.newPage();
// Emulate iPhone X
await page.emulate(puppeteer.devices['iPhone X']);
await page.goto('https://example.com');
await page.screenshot({ path: 'mobile-view.png' });
await browser.close();
})();
Available Device Presets
Puppeteer comes with numerous pre-configured device profiles:
const puppeteer = require('puppeteer');
// List all available devices
console.log(Object.keys(puppeteer.devices));
// Common mobile devices
const commonDevices = [
'iPhone 6',
'iPhone 6 Plus',
'iPhone 7',
'iPhone 8',
'iPhone X',
'iPhone XR',
'iPhone 11',
'iPhone 12',
'Samsung Galaxy S9+',
'Samsung Galaxy Tab S4',
'iPad',
'iPad Pro',
'Pixel 2',
'Pixel 2 XL'
];
// Use a specific device
const page = await browser.newPage();
await page.emulate(puppeteer.devices['iPhone 11']);
Custom Mobile Device Configuration
Manual Viewport and User Agent Setup
For more control, you can manually configure mobile device properties:
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
// Set viewport for mobile device
await page.setViewport({
width: 375,
height: 667,
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true,
isLandscape: false
});
// Set mobile user agent
await page.setUserAgent('Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0.3 Mobile/15E148 Safari/604.1');
await page.goto('https://example.com');
await browser.close();
})();
Creating Custom Device Profiles
You can create your own device profiles for specific testing needs:
const customDevice = {
name: 'Custom Mobile Device',
userAgent: 'Mozilla/5.0 (Linux; Android 10; SM-G975F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.120 Mobile Safari/537.36',
viewport: {
width: 414,
height: 896,
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true,
isLandscape: false
}
};
const page = await browser.newPage();
await page.emulate(customDevice);
Advanced Mobile Emulation Techniques
Handling Touch Events
Mobile devices use touch events instead of mouse events. Here's how to simulate touch interactions:
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.emulate(puppeteer.devices['iPhone X']);
await page.goto('https://example.com');
// Simulate touch tap
await page.touchscreen.tap(100, 200);
// Simulate swipe gesture
await page.touchscreen.swipe(100, 300, 300, 300);
// Simulate pinch gesture (zoom)
await page.touchscreen.pinch(200, 200, 1.5);
await browser.close();
})();
Geolocation Emulation
Many mobile applications rely on geolocation. Here's how to emulate GPS coordinates:
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.emulate(puppeteer.devices['iPhone X']);
// Set geolocation to New York City
await page.setGeolocation({
latitude: 40.7128,
longitude: -74.0060,
accuracy: 100
});
// Grant geolocation permissions
const context = browser.defaultBrowserContext();
await context.overridePermissions('https://example.com', ['geolocation']);
await page.goto('https://example.com');
await browser.close();
})();
Network Throttling for Mobile
Mobile devices often have slower network connections. Simulate this with network throttling:
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.emulate(puppeteer.devices['iPhone X']);
// Simulate slow 3G network
await page.emulateNetworkConditions({
offline: false,
downloadThroughput: 1.6 * 1024 * 1024 / 8, // 1.6 Mbps
uploadThroughput: 750 * 1024 / 8, // 750 Kbps
latency: 40 // 40ms latency
});
await page.goto('https://example.com');
await browser.close();
})();
Testing Responsive Design
Viewport Testing Across Multiple Devices
Test your responsive design across multiple device sizes:
const puppeteer = require('puppeteer');
const devices = ['iPhone 6', 'iPhone X', 'iPad', 'Samsung Galaxy S9+'];
(async () => {
const browser = await puppeteer.launch();
for (const deviceName of devices) {
const page = await browser.newPage();
await page.emulate(puppeteer.devices[deviceName]);
await page.goto('https://example.com');
await page.screenshot({
path: `screenshot-${deviceName.replace(/\s+/g, '-')}.png`
});
await page.close();
}
await browser.close();
})();
Orientation Testing
Test both portrait and landscape orientations:
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
// Test portrait orientation
await page.setViewport({
width: 375,
height: 667,
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true,
isLandscape: false
});
await page.goto('https://example.com');
await page.screenshot({ path: 'portrait.png' });
// Test landscape orientation
await page.setViewport({
width: 667,
height: 375,
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true,
isLandscape: true
});
await page.goto('https://example.com');
await page.screenshot({ path: 'landscape.png' });
await browser.close();
})();
Best Practices for Mobile Device Emulation
1. Use Realistic Device Profiles
Always use realistic device profiles that match actual devices your users might have:
// Good: Using actual device specifications
await page.emulate(puppeteer.devices['iPhone 12']);
// Better: Custom profile matching your target audience
const targetDevice = {
userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X)...',
viewport: {
width: 390,
height: 844,
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true
}
};
2. Test Performance on Mobile
Mobile devices have limited resources. Monitor performance during testing:
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.emulate(puppeteer.devices['iPhone X']);
// Start performance monitoring
await page.tracing.start({ path: 'trace.json' });
await page.goto('https://example.com');
// Get performance metrics
const metrics = await page.metrics();
console.log('Performance metrics:', metrics);
await page.tracing.stop();
await browser.close();
})();
3. Handle Mobile-Specific Events
Remember that mobile devices behave differently from desktop browsers:
// Wait for mobile-specific events
await page.waitForSelector('.mobile-menu', { visible: true });
// Handle touch-specific interactions
await page.touchscreen.tap(100, 200);
// Check for mobile-specific CSS classes
const isMobile = await page.evaluate(() => {
return window.getComputedStyle(document.body).getPropertyValue('content').includes('mobile');
});
Python Implementation with Pyppeteer
For Python developers, you can achieve similar mobile device emulation using Pyppeteer:
import asyncio
from pyppeteer import launch
async def mobile_emulation():
browser = await launch(headless=False)
page = await browser.newPage()
# Set mobile viewport
await page.setViewport({
'width': 375,
'height': 667,
'deviceScaleFactor': 2,
'isMobile': True,
'hasTouch': True,
'isLandscape': False
})
# Set mobile user agent
await page.setUserAgent('Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0.3 Mobile/15E148 Safari/604.1')
await page.goto('https://example.com')
await page.screenshot({'path': 'mobile-view.png'})
await browser.close()
asyncio.run(mobile_emulation())
Integration with Testing Frameworks
Jest Integration
Integrate mobile device emulation with Jest for automated testing:
const puppeteer = require('puppeteer');
describe('Mobile Device Tests', () => {
let browser;
let page;
beforeAll(async () => {
browser = await puppeteer.launch();
});
beforeEach(async () => {
page = await browser.newPage();
await page.emulate(puppeteer.devices['iPhone X']);
});
afterEach(async () => {
await page.close();
});
afterAll(async () => {
await browser.close();
});
test('should display mobile navigation', async () => {
await page.goto('https://example.com');
const mobileNav = await page.$('.mobile-nav');
expect(mobileNav).toBeTruthy();
});
});
Comparison with Other Tools
While Puppeteer excels at mobile device emulation, you might also consider Playwright for device emulation, which offers similar capabilities with additional browser support. For comprehensive testing across multiple browsers, setting up Playwright for multiple browsers provides broader coverage.
Troubleshooting Common Issues
Device Detection Problems
If websites don't recognize your emulated device:
// Ensure all mobile indicators are set
await page.setViewport({
width: 375,
height: 667,
deviceScaleFactor: 2,
isMobile: true, // Critical for mobile detection
hasTouch: true, // Enable touch events
isLandscape: false
});
// Set comprehensive mobile user agent
await page.setUserAgent('Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0.3 Mobile/15E148 Safari/604.1');
Performance Issues
Large viewport sizes can cause performance problems:
// Optimize for performance
await page.setViewport({
width: 375,
height: 667,
deviceScaleFactor: 1, // Reduce for better performance
isMobile: true,
hasTouch: true
});
// Disable images for faster loading
await page.setRequestInterception(true);
page.on('request', (req) => {
if (req.resourceType() === 'image') {
req.abort();
} else {
req.continue();
}
});
Advanced Configuration Options
Memory Management for Mobile Testing
Mobile devices have limited memory. Configure Puppeteer to simulate these constraints:
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch({
args: [
'--memory-pressure-off',
'--max-old-space-size=512' // Limit memory usage
]
});
const page = await browser.newPage();
await page.emulate(puppeteer.devices['iPhone X']);
// Monitor memory usage
const client = await page.target().createCDPSession();
await client.send('Runtime.enable');
const memoryUsage = await client.send('Runtime.getHeapUsage');
console.log('Memory usage:', memoryUsage);
await browser.close();
})();
Battery and Power Management
Some mobile-specific features require battery status emulation:
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.emulate(puppeteer.devices['iPhone X']);
// Override battery API
await page.evaluateOnNewDocument(() => {
Object.defineProperty(navigator, 'getBattery', {
value: () => Promise.resolve({
charging: false,
chargingTime: Infinity,
dischargingTime: 3600,
level: 0.5
})
});
});
await page.goto('https://example.com');
await browser.close();
})();
Conclusion
Mobile device emulation in Puppeteer is a powerful feature that enables comprehensive testing of responsive web applications. By combining viewport settings, user agent strings, touch events, and network throttling, you can create realistic mobile testing environments. Remember to use actual device specifications, test across multiple devices and orientations, and integrate mobile testing into your continuous integration pipeline for the best results.
The key to successful mobile device emulation is understanding that it's not just about changing screen size—it's about creating a comprehensive mobile environment that accurately reflects how your users will experience your application on their devices.