Terminology: "Mixed content nodes" is used in its meaning defined by XML spec 1.0 5th edition, "DOM text nodes" has the meaning defined by DOM Core spec
Problem dexcription
The entire WebDriver specification addresses retrieving, the visibility, accessing and interaction with 'WebElement's (concept/representation of DOM element nodes), while any of these traits remain undefined to DOM text nodes in mixed content.
Some examples where the accessing text node in mixed content may arise: problem - get the text of <div id="one">
without including the content of the children blockquotes
:
This will result in plenty of white-space Text nodes, highly probable
without special meaning for testing
<div id='one'>
<blockquote id='two'>dolore ipsum</blockquote>
Ah, the pain itself
<blockquote id='three'>Ah, the pain</blockquote>
</div>
or
The non-breakable spaces may carry meaning relevant for testing
<div id='one'>
<blockquote id='two'>dolore ipsum</blockquote> Ah, the pain itself
</div>
The fact that retrieving/accessing the context of text nodes in mixed content is conflated into the WebElement.innerText
as the sole solution makes hard the separation of the text nodes content from the content of children elements. Various coping strategies may be found, ranging from:
- easy to implement but not exact - e.g. iterate and subtract the inner text of children WebElements from the inner text of the parent (may lead to a great number of whitespace-only text nodes with content that cannot be separated by spaces with relevance for testing - see second example),
- more effective but harder to implement and maybe not supported by all browsers - e.g. via scripts able to access the actual Web Page DOM, "injected" via
Execute/Execure Async
, keeping in mind that XPath document.evaluate
is not supported by a wide range of Internet Explorer versions.
Furthermore, the inability to reference individual text nodes in mixed content also restrict the applicable XPath expression that a WebDriver may accept only to those able to return an WebElement.
The current specification also fails to address the expected reaction of the WebDriver when presented with a valid XPath expression that does not result in a reference to a WebElement (e.g Element attribute or Text node).
In the context of the examples above, the following is the reaction of using the Selenium WebDriver (java API):
driver.findElement(By.xpath("//*[@id='one']/text()"));
org.openqa.selenium.InvalidSelectorException: invalid selector:
The result of the xpath expression "//*[@id='one']/text()" is: [object Text].
It should be an element.
The need to access text nodes in mixed content seems to exist in the industry with indications that it is not an uncommon need and all work-around approaches mentioned above are taken (including the Script execution - which extends the range of cross-browser support of document.evaluate
by using a TreeWalker).
An enhancement request raised against the Selenium WebDriver got a response indicating that the same request would need to be lodged with all the WebDriver providers, the missing functionality being traced to the lack of clarity in the WebDriver specification for handling such cases.
A minimal-change suggestion
The following is thought to be possible solution to the need without introducing new concepts/interfaces into the specification, but only by enhancing the behaviour of existing ones.
- ~~~WebElement should implement a way of accessing *text values* of any of the children nodes individually, no matter if text-type or element-type children (maybe supported by `GET /session/{session id}/element/{element id}/child-text/{child-index}` ???) - at least with this functionality in place, applying set differences operations between the texts of all children and the inner-text of element children may yield the set of text nodes' content on individual basis (unpleasant as it may be to do it a every time one needs to individualize text nodes content).~~~
It is somehow hard to find a solution to accessing the content of the text nodes in mixed content without introducing new concepts/interfaces, especially because- the interleaving order of text nodes with element nodes (or indeed, other type of nodes) may be significant
- there's no guarantees that text content and element content can be distinguishable by their textual content only
As the author of the present issue doesn't have deep enough knowledge of the WebDriver specification, the suggested approach is formulated in terms of Java method specs (assuming the Selenium WebDriver as a reference implementation):interface WebElement {
// Already specified by Selenium WebDrive.
// Allows obtaining all the WebElement children by, for example, using By.xpath("./*")
java.util.List<WebElement> findElements(By by);
/** Proposed extension: if the parent WebElement is of a mixed-content type and
* there are sibling nodes of text type preceding this node, the method will return
* the content of these text type nodes in the natural order of appearance in the
* document (that is, the last element in this list is the closest to this WebElement).
* Otherwise returns an empty list.
* The method should have the same effect as applying an XPath selection of
* 'preceding-sibling::text()' except for the lack of preceding-sibling::
* axis inversion.
*/
java.util.List<String> getPrecidingTextNodes();
/** Proposed extension: if the parent WebElement is of a mixed-content type and
* there are sibling nodes of text type following this node, the method will return
* the content of these text type nodes. Otherwise returns an empty list.
* The method should have the same effect as applying an XPath selection of
* 'following-sibling::text()'
java.util.List<String> getFollowingTextNodes();
/** Proposed extension: if the (assumed common) WebElement parent of the two
* parameters is of a mixed-content type, the method will return the content of
* the text nodes occurring between the two, in the in the natural order of
* appearance in the document.
* The value in the first position of the returned list will be the content of
* the closest text node to the element represented by the first parameter, the last
* value of the returned list is the closest to the second one.
* If the two nodes are presented in the reversed order from the order established
* by their natural position in the context of their parent, the return of the method
* is an empty list.
* If the first parameter is null, the result of this method is the same as calling
* the getPrecidingTextNodes
method for the second parameter.
* If the second parameter is null, the result of this method is the same as calling
* the getFollowingTextNodes
method for the first parameter.
* If the two nodes represented by the parameters are not children of this WebElement,
* the method throws.
*
* Note: except for testing of direct parent-ship of this, the result of this method should
* be equivalent with
* child1.getFollowingTextNodes().retainAll(child2.getPrecidingTextNodes())
*/
java.util.List<String> getTextNodesBetween(WebElement child1, WebElement child2);
}
Note: even if two consecutive sibling WebElement nodes are presented to
the getTextNodesBetween
method, the method can return a list with a size of more
than one for cases in which other node types that are present in the document break
the flow of the text (comment and processing-instruction nodes).
Note: of course, a WebDriver API which would introduce specific representations
for text()
, comment()
and processing-instruction()
nodes would be an exact DOM model of the represented Web page and thus open the opportunities
for a richer automation logic (not based solely on artefacts producing a visual representation on
the screen). But this would make the present proposal go beyond the *minimal-change suggestion*
scope announced in this section.
- the behaviour of WebDriver when referencing non-WebElements through XPath/XPointer should be changed to return the closest parent WebElement rather than signalling an error. Examples of such XPath selectors where the parent element is to be returned instead
//*[@id=]/@name
or //*[@id=]/text()
or //*[@id=]/text()[]
.