JavaScript Execution, Browser Capabilities, Profiles, and Preferences: Core Concepts and Mental Model
Selenium becomes less portable when browser-specific power is used without a model. This lesson separates standard WebDriver commands, page-context JavaScript, standard capabilities, vendor options, and profile state before any browser configuration is changed.
Learning objectives
- Explain the standard WebDriver command path versus synchronous and asynchronous JavaScript execution inside the selected page context.
- Describe which JavaScript values can cross the WebDriver boundary and how element references are represented.
- Distinguish standard capabilities from browser-vendor option namespaces and profile/preferences state.
- Explain page-load strategy, headless mode, proxy/certificate configuration, and download preferences at the correct layer.
- Inspect requested and returned configuration before mutating browser or application state.
- Connect explicit browser configuration to reproducible CI, portability, and security review.
1. The practical problem: browser power can erase test meaning
Chapter 11 made browser state ownership explicit. Chapter 12 asks a harder question: when should a test use browser-specific configuration or JavaScript at all? A JavaScript click can make a test pass while hiding an overlay bug. A custom profile can make authentication “work” by importing old state. A permissive certificate capability can turn a trust failure into a green pipeline. Browser-specific options can silently make local and CI sessions different.
The objective is not “avoid JavaScript.” It is to put every escape hatch behind an explicit contract.
2. Mental model: two command paths, three configuration layers
The following diagram visualizes the relationships described in Mental model: two command paths, three configuration layers. 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 code] --> B[Selenium Python binding] B -->|standard command| W[WebDriver remote end] W --> BR[Browser] BR --> AUT[AUT document] B -->|executeScript / executeAsyncScript| JS[Page JavaScript realm] JS --> AUT B --> O[Options object] O --> S[Standard capabilities] O --> V[Vendor options: goog:chromeOptions] S --> W V --> W W -->|returned capabilities| B BR --> P[Disposable profile / preferences]
The normal path sends WebDriver commands such as find, click, send
keys, navigate, screenshot, or get window state through the remote
end. execute_script() and
execute_async_script() also travel through WebDriver,
but the supplied code then runs in the currently selected
page/window/frame JavaScript realm. Capabilities are negotiated when
the session starts; profile data/preferences live inside the browser
instance that session owns.
3. Terms to define before use
The following table organizes the key choices and evidence for Terms to define before use. Use it together with the surrounding prose so the rows serve as a comparison aid rather than standalone rules.
| Term | Meaning | Primary owner |
|---|---|---|
| WebDriver command | Standard protocol operation with WebDriver-defined semantics. | remote end + browser |
| executeScript | Run synchronous page-context JavaScript and return a WebDriver-serializable value. | selected browsing context |
| executeAsyncScript | Run page JavaScript that completes by invoking Selenium's callback before the script timeout. | selected browsing context + timeout policy |
| standard capability | Cross-browser session request/response key defined by WebDriver. | session negotiation |
| vendor capability/options |
Browser-specific configuration, usually namespaced such as
goog:chromeOptions.
|
browser vendor / driver |
| profile | Directory-backed browser state/configuration such as preferences, cache, cookies, extensions, history. | browser process |
| preference | Browser-specific setting stored/configured through the browser option/profile mechanism. | browser vendor/profile |
4. Values crossing the script boundary
Script arguments and return values are converted through WebDriver.
Safe mental-model values are null, booleans, numbers,
strings, arrays/lists, plain objects/dictionaries made from
serializable values, and DOM elements that Selenium converts to/from
WebElement references. Do not assume arbitrary JavaScript
objects—functions, symbols, cyclic objects, browser-internal
objects—can be serialized meaningfully.
driver.get("http://127.0.0.1:8777/")
summary = driver.execute_script("""
return {
title: document.title,
ready: document.querySelector('#ready').dataset.ready,
language: navigator.language,
input: document.querySelector('#nickname')
};
""")
print(summary["title"], summary["ready"], summary["language"])
print(type(summary["input"]).__name__) # WebElement
5. Asynchronous script completion is callback-based
execute_async_script() supplies a callback as the final
JavaScript argument. The script must call it exactly once. Selenium
waits only up to the configured script timeout. This is different
from an explicit WebDriver wait: the JavaScript itself owns the
completion signal.
driver.set_script_timeout(2)
state = driver.execute_async_script("""
const done = arguments[arguments.length - 1];
if (window.__academyReady) {
done(window.__academyReady);
return;
}
window.addEventListener('academy:ready', event => done(event.detail), {once:true});
""")
print(state)
6. Standard capabilities versus vendor options
The following table organizes the key choices and evidence for Standard capabilities versus vendor options. Use it together with the surrounding prose so the rows serve as a comparison aid rather than standalone rules.
| Layer | Examples | Portability |
|---|---|---|
| Standard WebDriver |
browserName, browserVersion,
platformName, pageLoadStrategy,
acceptInsecureCerts, proxy,
timeouts
|
protocol-defined, though browser support/behavior still matters |
| Chromium vendor options |
goog:chromeOptions: headless/window arguments,
binary, extensions, experimental preferences such as a
disposable download directory
|
Chromium-specific |
| Firefox vendor options | Firefox-specific profile/preferences under its vendor option namespace | Firefox-specific |
| CI/cloud metadata | vendor namespaces such as provider build/name options | provider-specific; never assume local portability |
7. Page-load strategy is not application readiness
The standard pageLoadStrategy capability can be
normal (default, waits for
document.readyState=complete),
eager (returns around interactive), or
none (does not block for document readiness). None of
these proves an SPA finished rendering application data. Keep
browser-navigation readiness separate from application readiness.
8. Proxy and certificate settings are trust controls
A proxy capability changes the network path.
acceptInsecureCerts changes how certificate errors are
treated for the entire session. These are not convenience toggles. A
realistic CI environment should normally validate the same trust
chain users rely on. If a dedicated lab must exercise invalid
certificates, isolate it and document why the capability exists.
9. Fresh profile versus custom profile
A fresh browser session is the portable default. A custom temporary profile is justified when the behavior under test depends on profile-level preferences. Reusing a personal profile is unsafe because it imports credentials, extensions, history, cookies, service workers, and policy state that the test did not declare.
10. Read-only configuration inspection first
The following example makes the Read-only configuration inspection first 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
options = webdriver.ChromeOptions()
options.page_load_strategy = "eager"
requested = options.to_capabilities()
print("requested pageLoadStrategy", requested.get("pageLoadStrategy"))
print("requested vendor key", "goog:chromeOptions" in requested)
driver = webdriver.Chrome(options=options)
try:
driver.get("http://127.0.0.1:8777/")
returned = driver.capabilities
print({
"selenium": selenium.__version__,
"session_id": driver.session_id,
"browser": returned.get("browserName"),
"browser_version": returned.get("browserVersion"),
"driver_version": returned.get("chrome", {}).get("chromedriverVersion"),
"page_load_strategy": returned.get("pageLoadStrategy"),
"url": driver.current_url,
"title": driver.title,
})
finally:
driver.quit()
Inspect the request and the returned capabilities separately. A vendor option may influence the browser without being echoed verbatim in the returned capability map; verify the observable browser behavior as well.
11. DevOps connection: configuration is evidence
Browser configuration belongs in code review and run evidence. A CI failure is easier to diagnose when the run records Selenium version, browser/version, headless/headed mode, standard capabilities, relevant vendor options, profile policy, and network/trust assumptions. Hidden workstation defaults turn browser automation into folklore.
12. Summary and next step
WebDriver commands, page JavaScript, capabilities, vendor options, and profile state are distinct control planes. Keep user behavior WebDriver-first, treat scripts as scoped helpers, make browser-specific configuration explicit, and inspect requested versus returned state.
Knowledge check
Why can a JavaScript click weaken a UI test?
It can bypass WebDriver interactability, scrolling, focus, and user-event semantics, so a real overlay or disabled-state bug may be hidden.
Does pageLoadStrategy=eager mean an SPA is
ready?
No. It concerns document readiness, not application-specific asynchronous rendering.
What should be compared when configuring a capability?
The requested options/capabilities, the returned session capabilities, and the actual observable browser behavior.
Why is a personal browser profile a poor test dependency?
It imports undeclared credentials, cookies, extensions, cache/history and policy state, breaking isolation and creating privacy/security risk.
When is execute_async_script() justified?
When the page exposes a deliberate asynchronous completion contract that is better represented by a callback/event; ordinary WebDriver-visible conditions should still use explicit waits.
Official references and version notes
- Selenium 4.47 release notes — stable baseline pinned for this chapter.
- Selenium downloads — stable bindings/Grid versus 4.48 snapshot artifacts.
- Python WebDriver API — synchronous/asynchronous JavaScript execution and session capabilities.
- Browser options — standard capabilities including page-load strategy and insecure-certificate behavior.
- Chrome-specific functionality — Chromium option/configuration boundary.
-
Python Chrome Options API
— arguments, experimental options, capabilities, and
to_capabilities(). - Python common Options API — page-load strategy and cross-browser option properties.
- Avoid sharing state — fresh-session isolation guidance.
Version-sensitive behavior was rechecked against current Selenium
primary documentation on 2026-08-28. Mandatory examples pin
Selenium Python 4.47.0 and Python 3.10+, use a supported locally
installed Chromium-family browser with Selenium Manager, and
target only
127.0.0.1. Selenium 4.48 material currently exposed
in generated API pages/download snapshots is treated as
development/nightly, not the stable lesson baseline.
Browser-specific preferences are labeled as such. JavaScript state
mutation is used only in explicit comparison/safety examples;
user-facing business interactions remain WebDriver-native. No
mandatory example enables insecure certificates or changes
system/enterprise browser policy.
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.