Live DOM reads and script inventory
Use the DOM property annotations for current control values and state. Use DOM attribute annotations when the attribute itself is the contract. Both read the current DOM, not the original HTTP response or an archived HTML file. No name-prefix heuristic or property/attribute fallback is applied.
| Intent | Read | Store | Assert |
|---|---|---|---|
| Live attribute | @GetDomAttribute | @StoreDomAttribute | @AssertDomAttribute |
| Live property | @GetDomProperty | @StoreDomProperty | @AssertDomProperty |
@GetDomProperty(value = "#name", by = Lookup.CSS, propertyName = "value")
String currentName();
@AssertDomProperty(value = "#subscribe", by = Lookup.CSS, propertyName = "checked")
void checkSubscription(String expected); // "true" or "false"
@GetDomAttribute(value = "#offering", by = Lookup.CSS, attributeName = "data-offering-id")
String offeringId();
@StoreDomProperty(value = "#name", by = Lookup.CSS, propertyName = "value", key = "name")
void rememberName();
@AssertDomAttribute(value = "#offering", by = Lookup.CSS, attributeName = "data-offering-id")
void checkOffering(String expected);
All six annotations support the existing selector templates, by, and timeout options. Store actions support key and globalStore; assertions consume the final argument as the expected String or null. Agent callers can use getDomAttribute(selector, by, name, timeout) and getDomProperty(...). These APIs are implemented by the web agent; other agents reject them explicitly rather than silently approximating browser semantics.
Each value read uses one dedicated WebDriver GET command. The existing visibility condition is retained. New DOM reads reuse the element that passed that condition for the first read attempt, eliminating the second lookup. The reuse lasts for one attempt only: on a stale read, Selenide resolves the locator again using its normal retry loop. The original visibility timeout and subsequent configured read timeout remain separate. A successful action therefore still needs lookup and visibility commands; it is not a single round trip or entirely script-free. Assertion timeout waits for a visible element, as before; it does not introduce polling until the expected value appears. Assertions remain exact comparisons.
Compatibility and migration
@GetAttributeValue, @StoreAttributeValue, @AssertAttributeValue, and the agent's getAttribute(...) are deprecated for guidance only. They remain supported indefinitely, with no planned removal. Their Selenium property-first behavior and null-to-empty-string conversion remain unchanged. Existing mobile/native consumers also retain their behavior.
Migrate intentionally rather than replacing every call mechanically:
| Example | DOM attribute | DOM property | Migration |
|---|---|---|---|
Input initially value="initial", then typed into | Usually "initial" until the attribute is mutated | Current typed value | DomProperty, name value |
Checkbox has checked attribute, then unchecked | "true" (attribute still present) | "false" | Property for current state |
| Option initially selected, then deselected | "true" if attribute remains | "false" | Property for current selection |
disabled="false" | "true": boolean attribute presence | "true": control is disabled | Property for state; attribute for presence |
| Missing boolean attribute | null | Usually "false" for a defined boolean property | Compare null versus "false" deliberately |
Custom data-*, ARIA or other attribute | Current attribute value, including script mutations | Usually null unless a property with that exact name exists | Attribute |
| Missing attribute/property | null | null for absent/undefined property | New APIs preserve null; legacy returns "" |
| Empty string | "" | "" | Distinct from null in new APIs |
Relative href | Literal attribute | Resolved URL | Choose intended contract |
| CSS class | Attribute class | Property className | Property names are case sensitive; aliases are not applied |
Selenium's legacy helper has additional special handling for booleans, style, URLs and property aliases. The new DOM APIs do not emulate that helper. A disabled property describes the control's own property; it is not a replacement for WebDriver enabledness or interactability checks (for example, disabled fieldsets). Properties return Selenium's string representation; prefer scalar properties. A boolean property assertion takes "false", not Java boolean false. Missing get results now return null through the Pumpo proxy rather than being mistaken for an absent action; stores pass null through to their backing store.
Annotation and primitive inventory
Audited against Pumpo's pinned Selenium 4.10.0 and Selenide 6.16.0. “Direct” describes the client protocol command, not a guarantee that the browser driver's internals never use JavaScript.
| Pumpo path | Script or semantic issue | Decision |
|---|---|---|
| Legacy attribute get/store/assert | Selenium rewrites getAttribute into its JavaScript helper; property-first semantics | Retain deprecated compatibility path; new DOM annotations use /attribute/{name} or /property/{name} |
| New DOM annotations and other visible-element actions | getElement waits on Condition.visible; Selenium rewrites displayedness to its atom | Retain. Presence, CSS display, or element dimensions are not equivalent to displayedness |
@GetElementContent, @StoreElementContent, @AssertElementContent | Selenide getText checks tag name; ordinary elements use rendered WebDriver text, while selects run a script joining the selected options' .text values | Retain. Neither textContent, innerHTML, nor input value is equivalent; use DOM property value for inputs |
@Wait, @AssertElementPresent, @AssertElementAbsent | Presence/wait paths vary; web waits can involve visibility | Retain their existing waiting/absence behavior; do not replace visibility with existence |
@ScrollIntoView and @SetValue pre-scroll | Selenide runs scrollIntoView(true) | Retain alignment behavior. Pointer movement or wheel actions are not a drop-in equivalent |
@SetValue default/typing | Selenide editable checks; clear/empty-value path can execute blur(); optional fastSetValue uses JS | Retain events and editable checks. A direct property read does not reproduce an interaction |
@SetValue(strategy = DOM) | Native setter plus bubbling input/change events, returning the assigned value in the same script | Retain. Removing the returned value adds a round trip; sendKeys has different event semantics |
@Select (text, index, value) | Selenide uses scripts for selection, disabled/not-found checks and events | Retain. Selenium Select/click is a possible future experiment, requiring event and multi-select equivalence tests |
@Click, @Clicks, @DoubleClick, @SendKeys, @ClickAndDownload | Lookup/interactability checks can execute displayedness; optional click diagnostics and pointer hit-testing explicitly run scripts; download behavior depends on configured mode | Retain visibility, interception, events and download checks; no assertion/interaction weakening |
| Page-source capture on failures, including JUnit reporting | Selenium 4.10 rewrites getPageSource() to outerHTML with XML serialization fallback | Retain in this change. Standard /source exists but client codec aliases the command; serialization (including doctype/XML) needs independent equivalence tests before changing reporting |
| URL, refresh, maximize, screenshots | Dedicated protocol commands; not HTML attribute reads | No replacement needed |
Page source is diagnostic serialization of current DOM, not the original network response and not a reliable representation of a typed input's current property value. A stalled source capture after a ScriptTimeoutException is evidence of browser unresponsiveness, not proof that the attribute helper caused it. Direct reads can also stall. This change does not increase timeouts or claim to cure browser hangs.
Sources and measurement
The Selenium 4.10 command codec supplies the exact script aliases and DOM endpoints. The WebDriver specification distinguishes attributes, properties, rendered text, source and displayedness. Selenide's 6.16.0 source artifact supplies the command implementations reviewed here (GetText, SetValue, Clear, ScrollIntoView, SelectOptionByTextOrIndex, SelectOptionByValue).
See measurement results for the reproducible comparison and its limits.
Playwright comparison
Playwright also uses browser-side JavaScript for DOM reads. Its frame implementation distinguishes getAttribute() (DOM attribute) from inputValue() (the current property), and combines selector resolution with the read. Its DOM implementation caches injected helper infrastructure per execution context. These sources were reviewed on 2026-10-07; the linked main branch can evolve.
Playwright web assertions re-resolve locators and retry the expected condition. Its ordinary attribute/value reads do not require visibility. Those are different contracts from Pumpo's visible-element lookup followed by an immediate equality assertion, so this change does not adopt them silently. Playwright's visibility definition also considers opacity-zero elements visible; it is not a drop-in replacement for Selenium displayedness.
The applicable design lessons are explicit read semantics, reducing redundant lookup/transport work, and preserving retries. The engineering implication is that a cached helper or isolated execution context should not be treated as protection against a hung renderer. No Playwright performance comparison or browser-hang recovery experiment was performed here.