Fix Cookie Banner Overlays in Full-Page Playwright Screenshots on Mobile Viewport
Dismiss predictable consent banners through the test flow, or mask them at screenshot time when consent behavior is out of scope.
To remove a predictable cookie banner from a full-page Playwright screenshot on mobile, set up the mobile viewport before navigation, wait for the real consent controls, and take the appropriate consent action before calling page.screenshot({ fullPage: true }). If the banner is irrelevant to a visual artifact, use the screenshot mask or style option instead. Those options alter the captured image; they do not complete consent or change the page’s consent state.
fullPage: true captures the full scrollable page. It does not dismiss overlays. Choose the approach based on what the test must prove: test the consent interface with the banner present, exercise consent for a post-consent screenshot, or mask/style the banner only when its appearance is outside the test’s scope.
1. Set up a mobile viewport before navigating
Use a Playwright device profile when its browser, viewport, and device settings match the scenario. Alternatively, set a viewport explicitly. Configure it before navigation so the page loads at the intended dimensions; some sites respond differently to resizing after load.
import { test, expect, devices } from '@playwright/test';
// Use a device profile for a mobile browser context.
test.use({ ...devices['iPhone 13'] });
test('captures the post-consent mobile page', async ({ page }) => {
await page.goto('https://example.com');
// The consent interaction and screenshot follow below.
});
If you prefer explicit dimensions, create a context with a viewport before opening the page:
import { chromium } from 'playwright';
const browser = await chromium.launch();
const context = await browser.newContext({
viewport: { width: 390, height: 844 },
isMobile: true,
hasTouch: true
});
const page = await context.newPage();
await page.goto('https://example.com');
// Perform the consent flow and capture here.
await browser.close();
Device emulation settings can include viewport and other device characteristics. Keep the browser, viewport, locale, and storage setup consistent across visual runs when they affect the page. See the official Playwright emulation guide.
2. Dismiss the banner through the intended consent flow
When the screenshot is meant to show the page after a visitor’s consent choice, locate the actual banner and perform the choice the test is supposed to represent. Do not silently accept consent just to make the image clean: accepting, rejecting, and opening preferences are different behaviors. Use accessible role and name locators where the site exposes them, and wait for the UI rather than relying on a fixed sleep.
import { test, expect, devices } from '@playwright/test';
test.use({ ...devices['iPhone 13'] });
test('captures the page after rejecting optional cookies', async ({ page }) => {
await page.goto('https://example.com');
const banner = page.getByRole('dialog', { name: /cookie|consent/i });
await expect(banner).toBeVisible();
// Replace this accessible name with the real label used by the site.
await banner.getByRole('button', { name: /reject optional|reject all/i }).click();
await expect(banner).toBeHidden();
await page.screenshot({ path: 'mobile-after-consent.png', fullPage: true });
});
Locator names are examples, not universal selectors. Inspect the site’s accessible roles and labels and choose the action required by the test. If the consent choice is stored, start from a deliberate storage state or clear the relevant cookies/local storage between runs so a previous run does not suppress the banner.
When to use an overlay handler
For an overlay that appears unexpectedly while normal actions or auto-waiting assertions are running, page.addLocatorHandler() can respond to it. It is not a general pre-screenshot hook: it runs around actions and auto-waiting assertions. A predictable cookie banner that must be handled before a screenshot should be explicitly awaited and handled in the test flow.
3. Capture the full page
Once the intended state is ready, capture the scrollable page:
await page.screenshot({ path: 'mobile-full-page.png', fullPage: true });
This captures beyond the current viewport. It does not itself scroll through a user flow, dismiss the banner, or guarantee that every lazy-loaded element has finished rendering. If the page loads content as it scrolls, make the test wait for the content it needs before capture and verify the resulting artifact.
4. Mask or hide the banner only in the screenshot
If the consent UI is not part of the behavior under test and you only need a clean image, a mask covers the matched element’s bounding box in the output. A screenshot style can hide or alter an element for capture. Neither option clicks a consent control, records a consent decision, or proves that the page is in a post-consent state.
import { test, devices } from '@playwright/test';
test.use({ ...devices['iPhone 13'] });
test('captures content while masking the consent panel', async ({ page }) => {
await page.goto('https://example.com');
const banner = page.getByRole('dialog', { name: /cookie|consent/i });
await banner.waitFor({ state: 'visible' });
await page.screenshot({
path: 'mobile-masked.png',
fullPage: true,
mask: [banner]
});
});
To hide the banner with a capture-only stylesheet, use a selector that matches the site’s actual banner:
await page.screenshot({
path: 'mobile-style-hidden.png',
fullPage: true,
style: `
/* Replace with the consent component's verified selector. */
.cookie-consent-banner { visibility: hidden !important; }
`
});
The screenshot style option applies through Shadow DOM and inner frames according to the Playwright Page API. Be careful with broad selectors: hiding a shared container can remove page content as well as the banner. A mask may also conceal a banner regression, so do not apply either technique to a test whose purpose is to verify consent UI.
5. Choose the method that matches the test
| Method | Use it when | What the result means | Trade-off |
|---|---|---|---|
| Exercise the consent UI, then capture | The screenshot should represent a post-consent visitor state | The test covers the chosen consent action and resulting page state | Needs the site’s actual controls and deliberate state setup |
| Screenshot-time mask or style | The banner is irrelevant noise for this visual artifact | The output omits or covers the matched visual region | Does not test consent or change consent state; may hide a UI regression |
| Leave the banner visible | The consent experience itself is under test | The screenshot includes the prompt and its layout | The banner may obscure content below it |
6. Stabilize screenshot assertions
For visual regression tests, use Playwright Test’s screenshot assertions and decide which parts of the image should be stable. Mask only volatile UI that is outside the behavior under test. If consent behavior is under test, keep the banner visible and assert or compare it instead of masking it away.
import { test, expect, devices } from '@playwright/test';
test.use({ ...devices['iPhone 13'] });
test('checks the post-consent page visually', async ({ page }) => {
await page.goto('https://example.com');
const banner = page.getByRole('dialog', { name: /cookie|consent/i });
await expect(banner).toBeVisible();
await banner.getByRole('button', { name: /reject optional|reject all/i }).click();
await expect(banner).toBeHidden();
await expect(page).toHaveScreenshot('mobile-after-consent.png', {
fullPage: true
});
});
Playwright offers screenshot masking and pixel-difference controls, but there is no universal tolerance suitable for every site. Set comparison options for the page’s rendering variability and the test’s purpose. Consult the Playwright screenshot assertion documentation.
7. Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| The banner still covers the page | fullPage controls capture extent, not page state |
Wait for the banner and take the intended consent action before capture, or mask/style it if it is outside scope. |
| The locator times out | The role, accessible name, or timing does not match the site’s UI; the banner may not appear in this storage state | Inspect the rendered accessibility tree, use the actual role/name or a verified selector, and start with deliberate consent storage. |
| The banner appears only on some runs | Cookies or local storage from an earlier run may retain a consent choice, or the UI loads conditionally | Use a clean or explicitly seeded browser context and wait for the expected state; avoid depending on a prior test. |
| The mobile layout is actually desktop-sized | Viewport/device configuration was applied after navigation or was not applied to the context used by the page | Configure the device or viewport before navigation and use that context consistently. |
| The screenshot assertion is flaky | Fonts, animations, dynamic content, or late loading can change pixels; a masked region may also vary | Wait for the relevant content and stable page state, keep environment settings consistent, and mask only nonessential volatile regions. |
| Masking hides too much or misses the banner | The locator selects a large ancestor or does not match the actual component | Use a locator scoped to the banner itself and check its match and bounds before relying on the screenshot. |
| An overlay handler does not run before capture | Locator handlers are invoked around actions and auto-waiting assertions, not as a general screenshot callback | Handle predictable consent UI explicitly before calling screenshot. |
| Lower-page content is blank in the capture | Content may render lazily or only after scrolling | Wait for the content needed by the artifact and, if required by the page, scroll through relevant sections before capture. |
8. Reliability, performance, and cost
Keep the test deterministic by starting with a known browser context and storage state, setting mobile emulation before navigation, waiting on locators or assertions rather than arbitrary delays, and capturing only after the intended page state is ready. A fixed sleep can waste time and still miss a slow banner; an explicit condition states what must be true.
Full-page capture covers more content than a viewport screenshot and may take longer on very long or dynamic pages. Limit the screenshot to the assertion’s real purpose where practical. Avoid adding broad masks simply to stabilize a test: they reduce visual coverage and can conceal failures. No universal performance figure or pixel tolerance applies to this problem; the page, browser, and test goal determine the useful settings.
With Playwright, cost depends on the compute environment running the browser and tests. The cited Playwright documentation does not establish a per-screenshot service price. If you run your own browser automation, account for the runtime and maintenance of that environment.
9. Or skip the browser setup
ScreenshotNeo is a website screenshot API and MCP server. For a one-off screenshot, call its API with the page URL; see the API documentation for parameters and options.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
timeout=90,
)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await Bun.write('shot.webp', res);
- Cookie banners, newsletter popups, and chat widgets are removed before the shot; each cleanup step can be turned off.
- Bot checks and CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are never billed. Response headers identify the page verdict and billing status.
- An MCP server lets AI agents use
take_screenshot,get_page_info, andcapture_pdf. - The free plan includes 1,000 screenshots a month with no card. Paid plans start at $5 for 3,000 screenshots; every feature is available on every plan.
Sign up free for 1,000 screenshots a month with no card.
10. FAQ
Does fullPage: true remove a fixed banner?
No. It changes how much of the scrollable page is captured. Handle the banner through the page flow or change the screenshot artifact with a mask or style.
Should I accept cookies to make a clean screenshot?
Only if acceptance is the behavior the test is meant to exercise. Otherwise use the intended consent choice, or use screenshot-only masking when consent state is outside scope.
Can a screenshot mask test consent behavior?
No. It covers pixels in the output and does not perform a consent action or change application state.
Is a locator handler enough for a predictable banner?
Use an explicit wait and action in the ordinary test flow. Locator handlers are for overlays encountered around actions or auto-waiting assertions, not as a general pre-screenshot hook.


