Emulation API
The emulation API in Lupa allows you to customize the browser context's environment. This is useful for testing responsive viewport layouts, media queries (such as dark mode or print styles), geolocation features, API permissions, and offline application behavior.
The Emulation Singleton
To configure emulation settings, import the pre-instantiated emulation singleton:
import { emulation } from '@pawel-up/lupa/commands'Emulation Actions
All emulation commands are asynchronous and return a promise.
Viewport Emulation
setViewport(viewport): Dynamically resizes the browser window viewport.tsawait emulation.setViewport({ width: 375, height: 812 })
NOTE
Dynamic Viewport Limitations: Playwright's setViewportSize() only supports changing the viewport width and height dynamically on a running page. Context-level device emulation settings such as deviceScaleFactor (pixel ratio), isMobile, and hasTouch are configured when the browser context is initialized and cannot be updated dynamically during a test.
Media Features Emulation
emulateMedia(media): Simulates page media types and CSS features (e.g. print layout or dark mode).ts// Emulate dark mode color scheme await emulation.emulateMedia({ colorScheme: 'dark' }) // Emulate print CSS styles await emulation.emulateMedia({ media: 'print' }) // Emulate accessibility preferences await emulation.emulateMedia({ reducedMotion: 'reduce' })
Geolocation Emulation
setGeolocation(geolocation): Emulates a specific geographical location. Passnullto clear the geolocation override.ts// Emulate Tokyo, Japan await emulation.setGeolocation({ latitude: 35.6762, longitude: 139.6503, accuracy: 100 }) // Clear geolocation override await emulation.setGeolocation(null)
Permissions Emulation
grantPermissions(permissions, options?): Grants specific browser permissions to the current context.tsawait emulation.grantPermissions(['geolocation', 'notifications']) // Limit to a specific origin await emulation.grantPermissions(['clipboard-read'], { origin: 'https://example.com' })
WARNING
Browser Compatibility: Supported permissions differ significantly between browsers (e.g. Chromium vs. WebKit/Safari vs. Firefox), and even between different versions of the same browser. Overrides for unsupported permissions may fail silently or throw errors depending on the browser engine.
Common permission types supported by various browsers:
'geolocation''notifications''clipboard-read''clipboard-write''camera''microphone''accelerometer''gyroscope''magnetometer''background-sync''midi''midi-sysex'(system-exclusive MIDI)'ambient-light-sensor''screen-wake-lock''storage-access''local-fonts''local-network-access''payment-handler'
clearPermissions(): Clears all granted permission overrides and restores default browser settings.tsawait emulation.clearPermissions()
Network Offline Emulation
setOffline(offline): Simulates network disconnection by going offline context-wide.ts// Disconnect network await emulation.setOffline(true) // Restore network await emulation.setOffline(false)
Persistence Behavior
IMPORTANT
Context-Wide State: Emulation configurations apply to the entire active browser context. Unlike mock utilities that automatically reset after a test completes, emulation changes (such as viewports, permissions, or offline status) remain active until explicitly reverted.
If you set the browser offline or change the viewport in a test, make sure to reset it in the test teardown or afterEach hook:
test.group('Offline Workflow', (group) => {
group.teardown(async () => {
// Ensure the browser goes back online for subsequent tests
await emulation.setOffline(false)
})
test('should show offline banner', async ({ assert }) => {
await emulation.setOffline(true)
// assert offline banner UI...
})
})Deprecation Notice
WARNING
The legacy global functions setViewport() and emulateMedia() are deprecated and will be removed:
import { setViewport, emulateMedia } from '@pawel-up/lupa/commands' // DEPRECATEDUse the equivalent methods on the emulation singleton:
import { emulation } from '@pawel-up/lupa/commands'
await emulation.setViewport({ width: 1280, height: 720 })
await emulation.emulateMedia({ colorScheme: 'light' })Best Practices (Dos and Don'ts)
Dos
- Do remember to clean up permissions, geolocations, and network overrides in the group teardown (
teardownorafterEach). - Do test responsive layouts by dynamically changing viewports using
emulation.setViewport.
Don'ts
- Don't assume that calling
emulation.setGeolocationwill work without granting geolocation permission first. You must grant the permissions needed:tsawait emulation.grantPermissions(['geolocation']) await emulation.setGeolocation({ latitude: 35.6762, longitude: 139.6503 }) - Don't use
emulation.setOffline(true)if you only want to mock a specific failing API endpoint. Use the Network Mocking API instead, as it is scoped to individual requests and doesn't cut off all internet/localhost traffic.