| title | Testing viewports |
|---|---|
| description | Run a suite at several sizes, resize inside a test, and know what the agent sees at each. |
Set a viewport on the target to run a suite at that size. Use
browser.setViewport() to resize during a test. The agent uses the current size
and cannot resize it.
viewport sets the initial size of each attempt, in pixels. The default is
1280 by 720. Add one target for each size you want to test:
import type { E2EConfig } from 'e2e';
import { web } from '@e2e-dev/web';
const app = { url: 'http://localhost:3000' };
export default {
targets: [
{ name: 'desktop', engine: web(), app },
{ name: 'tablet', engine: web({ viewport: { width: 820, height: 1180 } }), app },
{ name: 'phone', engine: web({ browser: 'webkit', viewport: { width: 390, height: 844 } }), app },
],
} satisfies E2EConfig;Each test runs once per target, with a separate result labeled desktop,
tablet, or phone. Run one size with npx e2e run --target phone, or list
the selected test-target pairs with npx e2e list.
To have the runner start one dev server for every size, give every target
the same app.command: targets declaring one command share one process; see
several targets. Every retry starts at
the target's configured size.
viewport: null sets no size, so the page fills the browser window. The
agent, screenshots, and videos use that size. Use it in a headed run, or
with a hosted browser whose live view would show a fixed 1280 by 720 page
in one corner:
{ name: 'window', engine: web({ viewport: null }), app }The page takes the window's size, minus the toolbar in a headed browser. A
local headed launch opens a small window; a hosted browser uses the window
size you requested from the service. browser.setViewport still works.
browser.setViewport resizes the page for the rest of the attempt, through
app.restart() and app.clearState():
import { test } from '@e2e-dev/web';
import { expect } from 'e2e';
test('the menu collapses on a narrow screen', async ({ app, screen, browser }) => {
await app.open('/');
await expect(screen.getByRole('navigation')).toBeVisible();
await browser.setViewport({ width: 390, height: 844 });
await expect(screen.getByRole('button', 'Menu')).toBeVisible();
await expect(screen.getByRole('navigation')).toBeHidden();
});Use this when one test needs to check a layout before and after resizing. For a whole suite, configure separate targets instead.
Call it before app.open to load the first page at that size, so the app
never renders its desktop layout first:
await browser.setViewport({ width: 390, height: 844 });
await app.open('/');The resize appears as a step in the report. To check the exact width, read it from the page:
const width = await browser.evaluate(() => window.innerWidth);
expect(width).toBe(390);The agent receives the current viewport size with each observation. Screenshots cover that viewport. Off-screen elements can still appear in the tree, and tapping them scrolls them into view. The agent can scroll to reveal lazy-loaded content.
Resize before calling agent.act:
test('the mobile menu reaches pricing', async ({ app, agent, browser }) => {
await browser.setViewport({ width: 390, height: 844 });
await app.open('/');
await agent.act('open the navigation menu and go to Pricing');
await expect(browser).toHaveURL('/pricing');
});platforms and requires cannot select a viewport; all Playwright targets
use platform web. To skip a test at some sizes, check the width in the
test:
test('the sidebar stays open', async ({ app, screen, browser }) => {
await app.open('/');
test.skip((await browser.evaluate(() => window.innerWidth)) < 768, 'desktop layout only');
await expect(screen.getByRole('complementary')).toBeVisible();
});To keep a whole group of size-specific tests apart, give them their own
config and run it with --config.
A narrow viewport tests responsive layout. It still uses a desktop browser with a scale factor of 1, desktop pointer behavior, and its usual user agent. It does not emulate touch, color scheme, or reduced-motion preferences. Use the device engine for tests on a simulator or emulator.
A video keeps the size the page had when it started recording; set
web({ screencast: { size } }) for one fixed size. A browser provider that
records, such as Kernel, records the whole
screen instead.