Fix Selenium Screenshots That Show a Cookie Settings Modal Instead of the Webpage
Selenium captures the page as it appears at that moment. Learn how to handle consent deliberately, wait for the right state, and capture the page reliably.
Short answer: Selenium screenshots show the browser’s current state. If a cookie settings modal is visible when the screenshot command runs, Selenium captures the modal. Decide whether the test should show the first-visit consent prompt or the page after a consent choice. For a post-consent screenshot, interact with the site’s actual consent control, wait until the resulting state is visible, and then take the screenshot.
There is no universal consent button selector or cookie name: consent interfaces and storage vary by site. The examples below use placeholders that you must replace with controls and state conditions from the page under test. Selenium documents screenshots of the current browsing context and individual elements, but it does not choose or record consent for you. See the official Selenium guidance on cookies, waits, and browser interactions.
1. Decide what the screenshot should prove
Before changing browser state, make the expected behavior explicit:
- First visit: Keep the modal visible if the test is checking that consent is requested. Its appearance is the expected screenshot.
- After consent: Use the site’s visible consent controls, verify the dialog closes or the page reaches a known post-consent state, then capture.
- Specific content: If the test concerns one component, capture that element rather than the full browser context.
This distinction keeps a test from accidentally hiding the very consent experience it is meant to validate.
2. Use the site’s consent controls and wait for the result
Here is a runnable Python example using Selenium 4. Replace the URL, button selector, and post-consent selector with values inspected on your target site. Install Selenium with python -m pip install selenium; Selenium Manager can obtain a compatible browser driver for supported setups.
from pathlib import Path
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait
URL = "https://example.com"
CONSENT_BUTTON = "button[data-testid='accept-cookies']" # Replace for this site
CONSENT_DIALOG = "[role='dialog'][aria-label='Cookie settings']" # Replace
options = webdriver.ChromeOptions()
# options.add_argument("--headless=new") # Enable for headless runs if desired
driver = webdriver.Chrome(options=options)
try:
driver.get(URL)
wait = WebDriverWait(driver, 15)
# If the dialog is part of this scenario, wait until its real control is clickable.
button = wait.until(
EC.element_to_be_clickable((By.CSS_SELECTOR, CONSENT_BUTTON))
)
button.click()
# Wait for a meaningful result; do not assume click() means the UI is gone.
wait.until(
EC.invisibility_of_element_located((By.CSS_SELECTOR, CONSENT_DIALOG))
)
# Optionally also wait for a page-specific landmark to become visible.
wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, "main")))
Path("page.png").write_bytes(driver.get_screenshot_as_png())
finally:
driver.quit()
The sample expects a dialog and a button to exist. Some sites show consent only on first visit, render it inside an iframe, use a shadow root, or expose separate accept, reject, and customize choices. Inspect the actual page and use the control that matches the test’s intended consent choice. Do not silently choose “accept” in a test whose purpose is to verify another option.
3. Wait for state, not elapsed time
driver.get() returning at its configured document readiness state does not guarantee that JavaScript has finished rendering or updating the consent interface. Selenium’s waiting strategies documentation explains that scripts can change the page after the HTML assets have loaded. Prefer an explicit wait for a condition such as a button becoming clickable, a dialog becoming invisible, or a known page landmark becoming visible.
A fixed delay such as time.sleep(5) is a poor default: it can waste time when the page is fast and still take the screenshot too early when it is slow. If a site provides no stable state signal, a short delay can be a last-resort fallback, but it is less reliable than checking the UI condition.
Use one wait strategy consistently. Selenium specifically warns against mixing implicit and explicit waits because the resulting wait times can be unpredictable. A typical explicit-wait setup leaves the implicit wait at its default and uses WebDriverWait for the relevant state.
4. Complete runnable alternatives
Java
With Selenium Java and a compatible Chrome setup, replace the example selectors with the target site’s selectors:
import java.time.Duration;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
public class ConsentScreenshot {
public static void main(String[] args) {
WebDriver driver = new ChromeDriver();
try {
driver.get("https://example.com");
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(15));
By button = By.cssSelector("button[data-testid='accept-cookies']"); // Replace
By dialog = By.cssSelector("[role='dialog'][aria-label='Cookie settings']"); // Replace
wait.until(ExpectedConditions.elementToBeClickable(button)).click();
wait.until(ExpectedConditions.invisibilityOfElementLocated(dialog));
wait.until(ExpectedConditions.visibilityOfElementLocated(By.cssSelector("main")));
driver.getScreenshotAs(org.openqa.selenium.OutputType.FILE)
.renameTo(new java.io.File("page.png"));
} finally {
driver.quit();
}
}
}
JavaScript
This example uses the Selenium WebDriver JavaScript package. Install it with npm install selenium-webdriver and configure a browser driver as required by your environment:
const { Builder, By, until } = require('selenium-webdriver');
const chrome = require('selenium-webdriver/chrome');
const fs = require('node:fs/promises');
(async () => {
const options = new chrome.Options();
// options.addArguments('--headless=new');
const driver = await new Builder().forBrowser('chrome').setChromeOptions(options).build();
try {
await driver.get('https://example.com');
const button = By.css("button[data-testid='accept-cookies']"); // Replace
const dialog = By.css("[role='dialog'][aria-label='Cookie settings']"); // Replace
await driver.wait(until.elementIsVisible(await driver.findElement(button)), 15000);
await driver.findElement(button).click();
await driver.wait(async () => {
const dialogs = await driver.findElements(dialog);
return dialogs.length === 0 || !(await dialogs[0].isDisplayed());
}, 15000);
await driver.wait(until.elementLocated(By.css('main')), 15000);
await fs.writeFile('page.png', await driver.takeScreenshot(), 'base64');
} finally {
await driver.quit();
}
})().catch(error => { console.error(error); process.exitCode = 1; });
In the JavaScript snippet, if the button appears asynchronously, use a condition that repeatedly locates it and checks clickability rather than locating it once before the wait. The same principle applies in any language: wait on a condition that can be reevaluated as the page changes.
5. Handle frames, elements, and existing consent state
Consent UI inside an iframe
If the consent interface lives in an iframe, locate that frame and switch into it before finding the control. After dismissing the interface, switch back to the top-level document before waiting on page content or capturing the page:
frame = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, "iframe")))
driver.switch_to.frame(frame)
wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, "button.accept"))).click()
driver.switch_to.default_content()
wait.until(EC.invisibility_of_element_located((By.CSS_SELECTOR, "iframe")))
Use the iframe selector and the post-action condition that match the page. Some consent tools keep the iframe in the DOM after closing, so waiting for the frame itself to disappear may be wrong; wait for the visible dialog or overlay to become hidden instead.
Capture a specific element
If only the main content matters, Selenium can capture an element after it is visible. This does not resolve a modal that covers the content, so dismiss or account for the dialog first if it obscures the target.
main = wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, "main")))
main.screenshot("main.png")
Restore a known consent choice
For repeatable tests, you may restore a consent state only when you know the site’s real storage mechanism and the intended choice. Selenium cookie operations apply to the current browsing context, and a cookie can be added only while the browser is on a domain for which that cookie is valid. Visit the domain first, then add a cookie with the name, value, path, domain, and other relevant attributes documented or observed for that site.
driver.get("https://example.com") # Must be on the relevant domain first
# Use the real site-specific cookie and attributes; these values are examples only.
driver.add_cookie({
"name": "site_consent",
"value": "documented-consent-value",
"path": "/",
})
driver.refresh()
wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, "main")))
The cookie name and value above are placeholders, not a universal consent format. Some sites store choices in local storage, session storage, or another site-specific mechanism instead. Avoid copying state from one environment into another without checking its scope and expiration. If your test is about the first visit, do not preload a choice: preserve the initial state.
6. Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| The modal is still in the screenshot | The screenshot ran before the choice finished updating the page, or the click did not reach the intended control. | Wait for the dialog to become invisible and verify a page landmark before capture. Check that the click selected the intended option. |
NoSuchElementException |
The selector is incorrect, the UI has not rendered, or it is inside a frame or shadow root. | Inspect the live DOM, wait for the element, and switch into the correct frame or use the site’s actual rendering context. |
ElementClickInterceptedException |
An overlay, animation, or another element is covering the control. | Wait for the overlay or animation state to settle, then click the real control. Confirm the button is visible and enabled. |
TimeoutException waiting for the dialog to disappear |
The click failed, the chosen option keeps settings open, or the condition assumes the wrong post-click behavior. | Inspect the resulting UI. Wait for the actual expected state, such as a confirmation, a hidden overlay, or a page landmark. |
| Consent reappears on every run | The test uses a fresh profile, the state was not persisted, or the site requires a different storage mechanism. | Use the visible controls in the test or restore the site’s real consent state in the correct profile and domain. Do not assume one cookie works everywhere. |
| Page content is missing even though navigation completed | Client-side rendering or later network activity has not completed. | Wait for a stable content selector or a relevant state change rather than relying on navigation readiness or a guessed sleep. |
| Screenshot contains only part of the page | The capture call targets the viewport or an element rather than a full-page image. | Use the capture behavior supported by your browser and Selenium setup, and confirm whether the test needs the viewport, an element, or a full-page result. |
7. Reliability, runtime, and cost considerations
- Reliability: Use a fresh browser profile when testing first-visit behavior. For post-consent scenarios, make the choice explicit and assert the resulting state so a changed site interface does not silently alter the screenshot.
- Runtime: Wait only for meaningful conditions and use reasonable timeouts. A long fixed sleep adds time to every run, while a state-based wait can proceed as soon as the page is ready.
- Reproducibility: Keep browser, viewport, locale, and test data consistent when comparing screenshots. Dynamic content and consent state can change independently of your code.
- Cost: Selenium is open source, but running browsers consumes your machine or CI resources. The practical cost is runtime and infrastructure; this dossier provides no benchmark or fixed cost figure.
8. Or skip the browser setup
If you need a clean webpage capture without maintaining Selenium browser setup, ScreenshotNeo is a website screenshot API and MCP server. One GET request returns an image or PDF. See the ScreenshotNeo API documentation for 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}`);
const fs = await import('node:fs/promises');
await fs.writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));
ScreenshotNeo accepts cookie and consent banners and removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each of those steps can be turned off. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and response headers report the page verdict and billing status. Its MCP server offers screenshot tools for AI agents. The free plan includes 1,000 shots per month with no card; paid plans start at $5 for 3,000 shots.
Sign up free for 1,000 screenshots a month, with no card required.
9. Frequently asked questions
Should my screenshot test accept or reject cookies?
Choose the action that matches the behavior under test. A first-visit test should preserve the prompt; a post-consent test should make and verify its intended choice.
Can I use the same consent cookie on every website?
No. Cookie names, values, scope, and even the storage mechanism are site-specific. Identify the target site’s actual consent state before restoring it.
Does Selenium wait until a page is visually finished?
Navigation readiness covers document loading conditions, not every later JavaScript-driven change. Wait for the particular UI or content state your screenshot requires.
Can I screenshot only the webpage content?
Yes, Selenium supports element screenshots. First ensure the relevant element is visible and the consent overlay does not obscure it.


