Alerts, Prompts, Modal Dialogs, and Browser Context Changes: Core Concepts and Mental Model
Learn to separate browser-native user prompts from application-owned DOM modals. They can look equally “modal” to a human, but WebDriver reaches them through different protocols, state stores, and failure semantics.
Learning objectives
- Explain why JavaScript alert/confirm/prompt dialogs are browser-native user prompts rather than DOM elements.
-
Distinguish explicit prompt handling from the session-wide
unhandledPromptBehaviorpolicy. - Describe alert text, prompt input, accept/dismiss semantics, and post-dialog application state.
- Contrast browser-native prompts with HTML/CSS/ARIA modals that remain ordinary DOM content.
- Inspect session capabilities, prompt presence, URL/title, active element, and modal DOM state before mutation.
- Connect prompt/modal classification to reliable CI diagnosis and evidence.
1. The practical problem: “modal” describes appearance, not ownership
Chapter 08 made browsing context explicit. Chapter 09 adds another source of apparent context change: a dialog can block normal browser commands even though no new tab, window, or iframe was created. The crucial question is who owns the dialog?
A JavaScript alert(), confirm(), or
prompt() is a browser-native
user prompt. It is outside the document DOM and is
controlled through WebDriver's Alert commands. A typical application
modal is ordinary HTML/CSS—often a
<div role="dialog"> or
<dialog>—and is controlled with normal locators,
waits, element state, and interactions.
switch_to.alert when there is no
browser-native prompt.
2. Mental model: two modal systems, two APIs
The following diagram visualizes the relationships described in Mental model: two modal systems, two APIs. Read the nodes in sequence and use the arrows to connect the conceptual state changes to the explanation around the diagram.
flowchart TD T[Test runner] --> W[WebDriver session] W --> B[Current top-level browsing context] B --> D[Document DOM] B --> P[Browser-native user prompt] D --> M[DOM modal element] P --> A[Alert API: text / send keys / accept / dismiss] M --> E[Ordinary locators / waits / element interactions] A --> S[Post-dialog browser + AUT state] E --> S W --> V[Evidence: capabilities prompt text DOM state URL title]
The WebDriver session owns the current top-level browsing context. A browser-native prompt can block that context's event loop until the prompt is handled. WebDriver exposes prompt-specific commands for reading text, entering prompt text, accepting, and dismissing.
A DOM modal remains part of the document. It can alter focus, hide or disable background controls, and drive application state, but WebDriver still sees it through the normal DOM search context. The browser does not expose it through the Alert API merely because it visually overlays the page.
3. Terms and state stores to keep separate
The following table organizes the key choices and evidence for Terms and state stores to keep separate. Use it together with the surrounding prose so the rows serve as a comparison aid rather than standalone rules.
| Term / state | Owner | Observable examples | Correct control |
|---|---|---|---|
| User prompt | browser / WebDriver remote end | alert text, prompt input field, accept/dismiss outcome | driver.switch_to.alert / Alert API |
| DOM modal | AUT document | role=dialog, hidden/display state, focus, buttons, form values | locators + waits + WebElement interactions |
| Unhandled-prompt policy | WebDriver session capability |
unhandledPromptBehavior returned capability
|
browser options before session creation |
| AUT state | application JavaScript/server | result label, submitted data, route, transaction state | business assertion after dialog resolution |
| Evidence | test runner / CI workspace | prompt text, exception, screenshot after closure, DOM state | capture before destructive recovery where possible |
4. Browser-native alert, confirm, and prompt semantics
An alert presents text and one effective closure
path. A confirm returns a boolean-like business
decision: accepting and dismissing normally drive different
application branches. A prompt adds text input;
Alert.send_keys() targets the prompt's input, not a
page element.
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
# A click has just triggered window.prompt(...)
alert = WebDriverWait(driver, 5).until(EC.alert_is_present())
print("prompt text:", alert.text)
assert alert.text == "Synthetic release label?"
alert.send_keys("rc-42")
alert.accept()
# Only now assert the AUT outcome produced after prompt() returned.
Reading and asserting prompt text before accepting/dismissing is a valuable guardrail: it proves the test is resolving the expected decision point rather than silently closing an unrelated prompt.
5. What unhandledPromptBehavior actually governs
The session capability unhandledPromptBehavior tells
the remote end what to do when an unrelated WebDriver command
encounters a user prompt the test did not explicitly handle.
Selenium Python 4.47.0 documents these string values:
dismiss, accept,
dismiss and notify, accept and notify, and
ignore. Its documented default is
dismiss and notify.
| Policy | Prompt action | Does command receive an error? | Use in this chapter |
|---|---|---|---|
dismiss |
dismiss | normally no notify error | rare; can hide missing explicit assertions |
accept |
accept | normally no notify error | rare; can accidentally approve a business path |
dismiss and notify |
dismiss | yes | documented Selenium Python 4.47 default |
accept and notify |
accept | yes | explicit policy when acceptance is intentional |
ignore |
leave open | yes | controlled failure injection so the prompt can still be inspected |
6. DOM modals remain ordinary application state
A DOM modal is located like any other element. Synchronize on an observable condition such as visibility, inspect its accessible name/text and control state, interact through normal WebElement methods, then verify the application result and modal closure.
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
wait = WebDriverWait(driver, 5)
driver.find_element(By.ID, "dom-modal-btn").click()
modal = wait.until(EC.visibility_of_element_located((By.ID, "dom-modal")))
assert modal.get_attribute("role") == "dialog"
assert modal.find_element(By.ID, "modal-title").text == "Review synthetic change"
modal.find_element(By.ID, "modal-approve").click()
wait.until(EC.invisibility_of_element_located((By.ID, "dom-modal")))
There is no call to switch_to.alert because the modal
never left the DOM control plane.
7. Read-only inspection before changing dialog state
The following example makes the Read-only inspection before changing dialog state behavior concrete. Read it with the stated assumptions, then compare its observable output or state changes with the explanation that follows.
import selenium
from selenium import webdriver
from selenium.common.exceptions import NoAlertPresentException
from selenium.webdriver.common.by import By
driver = webdriver.Chrome()
try:
driver.get("http://127.0.0.1:8774/index.html")
caps = driver.capabilities
print("selenium", selenium.__version__)
print("session", driver.session_id)
print("browser", caps.get("browserName"), caps.get("browserVersion"))
print("prompt_policy", caps.get("unhandledPromptBehavior"))
print("url/title", driver.current_url, driver.title)
print("dom_modals", len(driver.find_elements(By.CSS_SELECTOR, '[role="dialog"]')))
print("active_element", driver.switch_to.active_element.tag_name)
try:
print("native_prompt", driver.switch_to.alert.text)
except NoAlertPresentException:
print("native_prompt", "none")
finally:
driver.quit()
This inspection deliberately does not open or close anything. It records provenance and demonstrates that DOM-dialog existence and browser-prompt presence are independent observations.
8. Focus and page state after closure
Prompt closure returns control to the current top-level browsing
context, but do not assume the application returned to the exact
semantic state that existed before the prompt. JavaScript after
alert()/confirm()/prompt()
may run immediately and mutate the DOM, navigate, or update
application data.
Likewise, closing a DOM modal may intentionally restore focus to its opener—or the application may implement a different focus rule. Verify the state your test actually needs: result text, URL/title, focused element, enabled controls, or backend-visible outcome.
9. Security, identity, and evidence boundaries
Prompt text can contain account identifiers, server messages, or other sensitive content. DOM modals can contain credentials or privileged actions. Mandatory labs use only synthetic text and loopback pages. In real CI, preserve first-failure evidence with redaction/retention rules and never “solve” unexpected authentication/MFA/CAPTCHA prompts by auto-accepting or bypassing them.
10. DevOps connection: classify the blocker before retrying
When a pipeline says “unexpected alert open,” the useful question is not “how much longer should Selenium wait?” It is whether the browser-native prompt was expected, what text it carried, what policy was negotiated, and which application action triggered it. When a DOM modal obscures a button, the issue belongs to DOM state/interactability instead.
This classification turns a flaky-looking browser failure into a specific layer: browser chrome/user prompt, document modal state, or application logic.
11. Summary and next step
Browser-native user prompts are outside the DOM and use the Alert
control plane; DOM modals remain application elements. Explicit
prompt assertions should carry business intent, while
unhandledPromptBehavior is a session policy for truly
unhandled prompts.
Knowledge check
Why will find_element(By.CSS_SELECTOR, ...) never
locate a JavaScript alert()?
A browser-native user prompt is outside the document DOM; WebDriver exposes it through the Alert commands instead of element locators.
What is the documented Selenium Python 4.47.0 default
unhandled_prompt_behavior?
dismiss and notify: the prompt is dismissed and the
blocked command is notified with an unexpected-alert error.
Why assert prompt text before accepting or dismissing?
It proves the test is resolving the intended decision point rather than silently handling an unrelated prompt.
Does closing a browser-native prompt guarantee the AUT is unchanged?
No. JavaScript resumes after the prompt returns and can mutate DOM/application state or navigate; verify the required post-dialog state explicitly.
How should a DOM modal be automated?
With ordinary locator, wait, element-state, and interaction semantics because it remains part of the document DOM.
Official references and version notes
- Selenium 4.47 release notes — stable baseline pinned for this chapter.
- Selenium downloads — current stable client and Selenium Server/Grid versions.
- JavaScript alerts, prompts, and confirmations — current Selenium interaction examples.
- Selenium Python Alert API — text, accept, dismiss, and prompt input.
-
Selenium Python Options API
—
unhandled_prompt_behaviorand its 4.47.0 default. -
Expected Conditions
—
alert_is_present()for bounded prompt synchronization. -
W3C WebDriver Working Draft — User prompts
— prompt handling and
unexpected alert opensemantics.
Version-sensitive behavior was rechecked against current primary
documentation on 2026-08-28. Mandatory examples pin Selenium
Python 4.47.0, require Python 3.10+, use a supported local
Chromium-family browser with Selenium Manager for normal local
driver resolution, and target only loopback fixtures. Selenium
Python 4.47.0 documents
dismiss and notify as the default
unhandled_prompt_behavior; the chapter uses
ignore only in a controlled failure-injection session
so the learner can observe and then explicitly resolve the prompt.
Classic WebDriver is sufficient for the mandatory chapter path;
BiDi/CDP are not required to handle these dialog semantics.
Keep the academy open
Support free, practical DevOps education.
Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0Send only Ethereum/ERC-20 compatible assets to this
address.