Chapter 09Lesson 01~155 minutes

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.

User promptsAlert APIDOM modalsPrompt policyCI diagnosis

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 unhandledPromptBehavior policy.
  • 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.

Classification before action: if you choose the wrong control plane, the test can wait for a DOM element that can never exist or call switch_to.alert when there is no browser-native prompt.

2. Mental model: two modal systems, two APIs

Two modal systems with different control planes

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
Policy is not a test oracle. A global prompt policy should not replace scenario-specific assertions about prompt text and intended accept/dismiss business behavior.

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()?

What is the documented Selenium Python 4.47.0 default unhandled_prompt_behavior?

Why assert prompt text before accepting or dismissing?

Does closing a browser-native prompt guarantee the AUT is unchanged?

How should a DOM modal be automated?

Next lesson

Operate both dialog systems in one local fixture

Lesson 2 builds alert, confirm, prompt, unexpected-prompt, and DOM-modal flows with before/after state evidence.

Official references and version notes

Version and compatibility note

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.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0Send only Ethereum/ERC-20 compatible assets to this address.