How to Detect Visual Changes on a Page With a Cookie Banner
Make cookie-banner visual tests repeatable: choose the state to check, capture it consistently, and review screenshot differences against a baseline.
To detect visual changes reliably on a page with a cookie banner, decide whether the test is checking the banner, the page after consent, or both. Make that state repeatable, capture the same viewport or page region on every run, and compare the result with a saved baseline. If the banner is irrelevant to a particular comparison, dismiss it through its normal control or narrowly mask it for that screenshot. Keep a separate check for the banner whenever its appearance or behavior matters.
Visual regression testing compares screenshots captured at chosen checkpoints with stored baseline images. A reported difference is a signal to review, not an automatic reason to replace the baseline. Accept a new baseline only when the visual change is intended. Applitools describes this baseline and review workflow.
1. Decide which cookie-banner state the test should cover
Before writing capture code, state what the check must detect. A banner can obscure content, appear only for a fresh visitor, or vary because consent state persists between runs. Those are different test conditions.
| Test goal | Capture setup | What the screenshot can detect |
|---|---|---|
| Check the banner itself | Start from a consistent fresh-consent state and capture before interacting with the banner | Changes to its presence, position, dimensions, copy, and controls |
| Check the post-consent page | Wait for the banner and dismiss it using its ordinary control before capture | Changes to the page after consent; this checkpoint does not assess the banner |
| Check both | Use separate checkpoints: one before dismissal and one after | Both banner and post-consent page changes, with clear responsibility for each |
| Check underlying content despite an irrelevant variable banner | Use a narrowly scoped ignore region or screenshot-only CSS for this comparison | Changes outside the ignored area; changes inside it are not assessed by this comparison |
Playwright recommends handling a predictable overlay explicitly when that is the desired state: wait for it and dismiss it as part of the normal interaction flow. That makes the test setup clearer than relying on an overlay handler as the only preparation. See Playwright’s Page API documentation.
2. Make the consent state repeatable
Consent banners often persist a choice in cookies, local storage, or another site-managed state. A test that reuses browser state can therefore see the banner on its first run and miss it on the next. Choose one of these setups deliberately:
- For a banner-present checkpoint: start each run with a clean browser context or otherwise reset the site’s consent state, navigate to the page, and capture before clicking.
- For a post-consent checkpoint: locate the expected banner, wait for it to be visible, click its normal accept, reject, or dismiss control according to the scenario, then wait for it to disappear before capture.
- For both checkpoints: capture the initial banner state, interact with it, and capture the resulting page as a second named checkpoint.
Do not silently accept consent as generic test setup if the banner itself is under test. Likewise, do not leave the initial state to chance. Keep the chosen consent behavior consistent with what the test is meant to prove.
3. Choose screenshot scope and comparison behavior
Capture the area that matches the coverage goal. A viewport screenshot checks what a user sees without scrolling. A full-page screenshot covers content below the fold. A region capture focuses on a component or page section. Applitools documents viewport, full-page, and region capture options.
- Dismiss the banner: best when the checkpoint is explicitly the post-consent state. The banner needs another test if its appearance matters.
- Keep the banner visible: best when layout, wording, consent choices, or accessibility-related controls are part of the visual contract. Ensure every run starts with the same consent state.
- Mask the banner region: best when banner variation is genuinely irrelevant to one underlying-page comparison. Mask only the smallest necessary selector or coordinates. A masked region cannot reveal a regression in that region.
- Apply temporary screenshot CSS: useful when the capture tool supports screenshot-only styling and you need to hide a known variable element. Keep the styling scoped to the banner and do not let it alter the page areas being compared.
Percy supports selector-based and coordinate ignore regions, as well as temporary CSS for screenshots. Applitools also documents ignored regions, which are excluded from visual assessment. Use these mechanisms only for the comparison where the content is irrelevant; retain a banner-specific checkpoint if it matters. Percy’s Playwright documentation and Applitools’ ignored-region guidance describe these approaches.
4. Runnable Playwright example
The following Node.js example creates a fresh context for a repeatable banner-present capture, then demonstrates the separate post-consent checkpoint. Replace the URL and the example selectors with the site’s actual banner and button selectors. The code writes two PNG files; your visual testing service or comparison step can use them as checkpoints against stored baselines.
import { chromium } from 'playwright';
const url = 'https://example.com';
const browser = await chromium.launch({ headless: true });
const context = await browser.newContext({ viewport: { width: 1440, height: 900 } });
const page = await context.newPage();
try {
await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 30_000 });
// Banner-present checkpoint: wait for the expected banner and capture it.
const banner = page.locator('[data-testid="cookie-banner"]');
await banner.waitFor({ state: 'visible', timeout: 10_000 });
await page.screenshot({ path: 'baseline-banner-present.png', fullPage: false });
// Post-consent checkpoint: use the site's ordinary consent control.
await banner.getByRole('button', { name: 'Accept all' }).click();
await banner.waitFor({ state: 'hidden', timeout: 10_000 });
await page.screenshot({ path: 'baseline-after-consent.png', fullPage: true });
} finally {
await context.close();
await browser.close();
}
Playwright’s screenshot API supports viewport and full-page capture. Keep scope consistent between baseline and subsequent runs; changing viewport dimensions, device scale, or full-page behavior can create differences unrelated to the application change. Playwright screenshot documentation.
Mask a banner only for an underlying-page checkpoint
If the banner should remain on screen but does not belong in this comparison, Playwright’s screenshot API accepts locator masks. The masked area is intentionally not evaluated in that capture. Do not reuse this as the only screenshot when the banner matters.
await page.goto('https://example.com', { waitUntil: 'domcontentloaded' });
const banner = page.locator('[data-testid="cookie-banner"]');
await banner.waitFor({ state: 'visible', timeout: 10_000 });
await page.screenshot({
path: 'page-with-banner-masked.png',
fullPage: true,
mask: [banner],
maskColor: '#FF00FF'
});
Playwright’s screenshot options document masks and screenshot capture settings. If the banner’s dimensions or position change, the mask can move with its locator, which keeps the exclusion tied to the element rather than a guessed fixed rectangle.
5. Compare screenshots and review differences
- Save a baseline for each named checkpoint, such as
banner-presentandafter-consent. - Run the same setup in the same environment: browser version, viewport, device scale, fonts, locale, and relevant page data should be stable where possible.
- Compare each new screenshot with its matching baseline using your visual testing tool.
- Inspect the diff in context. Check whether a change is expected, whether it is confined to dynamic content, and whether a mask or state setup is hiding useful evidence.
- Update the baseline only after confirming the change is intentional. If the difference indicates a defect, keep the existing baseline and fix the page or test setup.
Visual comparison tools can report expected rendering variation as a difference. Stabilize the page state and capture conditions before increasing tolerance or ignoring more regions; broad ignore rules can make a broken page look clean.
6. Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| Banner appears only on some CI runs | Browser storage or cookies are reused inconsistently, or the test starts from different consent state | Create a fresh context for the banner-present check; for the post-consent check, set or establish consent explicitly and wait for the expected state. |
| Click on the consent button times out | The selector or accessible name differs, the banner has not appeared, or another layer intercepts the click | Inspect the rendered page and use the site’s actual role, name, or stable test ID. Wait for the banner to be visible before clicking. |
| Screenshot is captured before the banner is ready | Navigation readiness does not guarantee the banner has rendered | Wait for the banner locator to become visible for the banner checkpoint, or wait for it to become hidden after dismissal. |
| Underlying page diffs move around the banner | The banner varies in dimensions or location, or the comparison includes it unintentionally | Decide whether the variation is in scope. If not, mask the banner selector or use scoped screenshot CSS for that checkpoint only. |
| Changes to the banner are no longer reported | The banner was dismissed, masked, or hidden in every checkpoint | Restore a banner-present checkpoint with a consistent initial state. A region excluded from comparison cannot surface changes within that region. |
| Large diff after a browser or viewport change | The capture environment changed rather than the page alone | Align browser version, viewport, device scale, fonts, locale, and screenshot scope with the baseline before accepting changes. |
| Full-page capture differs near lazy content | Content loads on scroll or after the screenshot checkpoint | Use the browser flow to scroll or wait for the relevant content to render before capture, and keep the same full-page behavior on each run. |
7. Performance, reliability, and cost
For a small suite, two focused checkpoints can be more useful than one screenshot that mixes banner and page responsibilities. Full-page capture covers more content but can take longer to render and compare than a viewport or focused region; choose the smallest scope that proves the intended behavior. Reuse browser processes where your test runner supports it, while giving each test an isolated context when consent state must not leak between cases.
Reliability comes from controlling state and reviewing differences. Explicit waits for the banner or its dismissal make failures easier to diagnose than fixed sleeps. Stable data, consistent viewport and browser configuration, and narrowly scoped masks reduce unrelated changes without suppressing the behavior under test.
Cost depends on the comparison product, plan, capture volume, and CI setup; the sources cited here do not establish a common price or benchmark across tools. Estimate based on the number of pages, checkpoints, environments, and runs your suite performs, and check the current provider pricing before adopting a service.
Or skip the browser setup
ScreenshotNeo is a website screenshot API and MCP server from Yorker Media. A single request captures a URL as PNG, JPEG, WebP, or PDF; its options include full-page capture, element selection, custom waits, and custom CSS or JavaScript. For this visual workflow, capture the same URL and conditions consistently, then compare the returned image with your saved baseline. ScreenshotNeo does not replace baseline comparison or review.
Install the Python dependency with python -m pip install requests, set your API key, and run:
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)
See the ScreenshotNeo API documentation for request options. Cookie banners, popups, and chat widgets are removed before capture, and each step can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing; response headers identify the page verdict and whether it was billed. Its MCP server lets AI agents use take_screenshot, get_page_info, and capture_pdf. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 screenshots.
Sign up for ScreenshotNeo’s free plan: 1,000 screenshots a month, no card required.
FAQ
Should I accept or reject cookies in a visual test?
Use the choice that represents the intended state. For a post-consent page checkpoint, interact with the corresponding normal control and keep that choice consistent across runs. For a banner checkpoint, capture before making a choice.
Can I mask the cookie banner?
Yes, for a comparison where the banner is genuinely out of scope. A mask excludes that region from the comparison, so keep a separate banner check if its appearance or behavior matters.
Should I use a viewport or full-page screenshot?
Use a viewport for the visible viewport state, full-page capture for content below the fold, or a region for focused coverage. Match the baseline’s scope in every run.
Does taking a screenshot detect a visual change by itself?
No. Capture provides an image. A visual comparison step checks it against a baseline, and a person or review process decides whether to accept the difference.


