Trying to get the text from the div with xPath. Finds information in the browser good, but when i try to run with an idea than i get error:"is: [object Text]. It should be an element."
List<WebElement> priceGameWebElement = webDriver.findElements(By.xpath("//div[contains(#class,'search_price')]" +
"/text()[normalize-space()][1]"));
What do I need to do to make everything work?
You can "interrupt" your query before the /text()... part like this:
List<WebElement> priceGameWebElement = webDriver.findElements(By.xpath("//div[contains(#class,'search_price')]"));
Then you should get a List<WebElement> which contains the elements with the text() nodes for further distinction. They could probably be queried with the .getText() function.
If for some reason you cannot exclude text() from XPath and you need just extract the text of the element, then there is a workaround: use method document.evaluate() from JavaScript.
Code example:
String textObjectXpath = "\"//*[local-name() = 'text'][#x='8']/text()\"";
String script = "var element = document.evaluate("
+ textObjectXpath
+ ", document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;"
+ "if (element) {return element.nodeValue;}";
String extractedText = (String) ((JavascriptExecutor)driver).executeScript(script);
The if statement "if (element) {return element.nodeValue;}" could be omitted if you don't care about the scenario when the element will be without text. In a such case, you may want to replace the line with "return element.nodeValue;"
Related
I'm trying to save just a number from a string I get from a paragraph but when I try to asign an alias to it and then check the value it returns undefined. I've tried a few solutions I found but none of those seem to work for me. These are two ways I tried (I tried another one similar to the second one but using split, had same result). The console.log inside of the 'then' doesn't show in the console, and when I try the alias after the code is when I get undefined.
cy.get('p')
.eq(1)
.should('have.text', '/[0-9]+/g')
.as('solNumber')
cy.get('p')
.eq(1)
.invoke('text')
.then((text)=>{
var fullText = text;
var pattern = /[0-9]+/g;
var number = fullText.match(pattern);
console.log(number);
})
.as('solNumber')
Please convert with + operator and return the numeric value if you want numeric type to be stored.
cy.get('p').eq(1)
.invoke('text')
.then(fullText => {
const number = fullText.match(/[0-9]+/);
return +number // text to numeric
})
.as('solNumber')
cy.get('#solNumber')
.should('eq', 42) // numeric type
});
Running your 2nd code on this,
<p>21</p>
<p>42</p>
gives the correct outcome
cy.get('p')
.eq(1)
.invoke('text')
.then((text)=>{
var fullText = text;
var pattern = /[0-9]+/g;
var number = fullText.match(pattern);
console.log(number); // logs 42
})
.as('solNumber')
cy.get('#solNumber')
.should('eq', '42') // passes
So, you need to inspect the DOM, it looks like it's not what you expect.
The first attempt you were passing a jquery element to the .should() and although some chainers change the subject yours did not so it saved the jquery element as solNumber.
The second attempt invokes the .text() which was passed to the .then() it logs the number correctly. However, you did not return anything at the end of the .then() block, therefore, solNumber should hold the entire paragraph.
This should help you out to extract the specific number and save it as an alias.
cy.get('p')
.invoke('text')
.invoke('trim')
.then(paragraph => {
const matcher = /some/
expect(paragraph).to.match(matcher) // check number is there
const indexOfText = paragraph.match(matcher) // get index of match text
return paragraph.substring(indexOfText.index, indexOfText.index + indexOfText[0].length) // return substring
})
.as('savedText')
cy.get('#savedText')
.then(cy.log) // will print out the number you seek
Scenario with Example:
10 Different Row in Table (e.g. //a[#class='*******'] ).
Need to Retrieve different Value from each Row
Retrieving it using List
In given example, Temp1 getting executed and iterate as required, As information need to retrieve from the same and got it through getAttribute(" ").
Need to retrieve Temp2 value which is dependent on object findList. findSubInfo is having Following Siblings from mainList. Its only addition on following siblings in to List exa.(findList).
Issue is :
If I execute 1st provision, as independent node, It always retrieve first row's value. (Which is Obvious)
If I execute 2nd provision, with xpath + List object using con-cat it throws xpath Syntax issue.
Question :
How can retrieve all information from object's siblings, where object List<WebElement> findList is pointing ?
Trial :
By mainList = By.xpath("//a[#class='*******']");
By findSubInfo = By.xpath("//a[#class='*****']//following::div[#class='****']");
List<WebElement> findList = driver.findElements(mainList);
for (WebElement webElement : findList ) {
if (webElement.isDisplayed()) {
String temp1= "Info1:" + webElement.getAttribute("ng-href");
String temp2= "Info2: " + driver.findElement(findSubInfo).getText();
}
}
OR
String mainList "//a[#class='*******']";
String findSubInfo = "//following::div[#class='****']";
List<WebElement> findList = driver.findElements(By.xpath(mainList));
for (WebElement webElement : findList ) {
if (webElement.isDisplayed()) {
String temp1= "Info1:" + webElement.getAttribute("ng-href");
String temp2= "Info2: " + driver.findElement(By.xpath(webElement + findSubInfo )).getText();
}
}
Exception Details:
SyntaxError: Failed to execute 'evaluate' on 'Document': The string
'[[ChromeDriver: chrome on XP (3208ef0a2ffd32812c33e159291eebe4)] ->
xpath: //a[#class='noDecoration
addPointer']]//following::div[#class='assignedAccountMasterAddress']'
is not a valid XPath expression.
If you want to search for element with related XPath starting from current element (webelement), try
# Note that it should start with the dot
String findSubInfo = "./following::div[#class='****']";
# use webElement.findElement instead of driver.findElement. No XPath concatenations needed
String temp2= "Info2: " + webElement.findElement(By.xpath(findSubInfo)).getText();
I have an ArrayList ArrayList<String> externalDataList = new ArrayList<>(1600);and I would like to display in a textArea first 3 strings, but I can't succed:
Here is my code
textareaShowPreview.setPrefRowCount(3);
Iterator<String> it = externalDataList.iterator();
int tot = 0;
while(it.hasNext() && tot<3){
String element = it.next();
textareaShowPreview.setText(element + "\n");
System.out.println("elements are: " + element);
tot++;
}
The sout correctly print first 3 strings
element are: 23/05/2007 ,30.9455,31.2545,30.9091,30.9545,7518142
element are: 24/05/2007 ,30.6545,31.0909,30.5364,30.6909,12851606
element are: 25/05/2007 ,30.6636,30.8545,30.4818,30.8091,9392088
but in textArea I have only first one
How do I have to modify my code to show in textArea all three strings, one string per row?
Use appendText instead of setText here is a link.
The setText, delete the previous text and set the text you are giving to it. The append keep the current text in your text area.
Hope it helps!
I have a piece of HTML like this:
<a href="/something">
Title
<span>Author</span>
</a>
I got a WebElement that matches this HTML. How can I extract only "Title" from it? Method .getText() returns "Title\nAuthor"...
You can't do this in the WebDriver API, you have to do it in your code. For example:
var textOfA = theAElement.getText();
var textOfSpan = theSpanElement.getText();
var text = textOfA.substr(0, textOfA.length - textOfSpan.length).trim('\n');
Note that the trailing newline is actually part of the text of the <a> element, so if you don't want it, you need to strip it.
Here is the method developed in python.
def get_text_exclude_children(element):
return driver.execute_script(
"""
var parent = arguments[0];
var child = parent.firstChild;
var textValue = "";
while(child) {
if (child.nodeType === Node.TEXT_NODE)
textValue += child.textContent;
child = child.nextSibling;
}
return textValue;""",
element).strip()
How to use in this:
liElement = driver.find_element_by_xpath("//a[#href='your_href_goes_here']")
liOnlyText = get_text_exclude_children(liElement)
print(liOnlyText)
Please use your possible strategy to get the element, this method need an element from which you need the text (without children text).
If using Python:
[x['textContent'].strip() for x in element.get_property('childNodes') if isinstance(x, dict)]
Where element is your element.
This will return ['Title', ''] (because there are spaces after span).
you can use jsexecutor to iterate the child nodes, trap the textNode 'Title' and then return its content like below
WebElement link = driver.findElement(By.xpath("//a[#href='something']"));
JavascriptExecutor js = ((JavascriptExecutor)driver);
String authorText = (String) js.executeScript("for(var i = 0; i < arguments[0].childNodes.length; i++) {
if(arguments[0].childNodes[i].nodeName == \"#text\") { return arguments[0].childNodes[i].textContent; } }", link);
The javascript code block above iterates both textNode ('Title') and SPAN ('Author') but returns only the text content of textNode.
Note: Previous to this, I have tried including text node in xpath like below, but webdriver throws invalidselector exception as it requires element not textnode
WebElement link = driver.findElement(By.xpath("//a[#href='something']/text()"));
Verify the element present for "//a[normalize-space(text())=Title]". It will return true if the text present inside 'a' tag is 'Title'.
Is there a way in Selenium RC to get the id from the xpath?
If I have the xpath
/html/body/div/div//input
I want to get the id of all the nodes associated to the xpath
You can use getAttribute in combination with getXpathCount.
A Selenium 1 example in Java would be:
int inputs = selenium.getXpathCount("/html/body/div/div/descendant::input").intValue();
for (int i=1; i<=inputs; i++) {
System.out.println(selenium.getAttribute("/html/body/div/div/descendant::input[" + i + "]#id"));
}
A Selenium 2 example in Java would be:
List<WebElement> inputs = driver.findElements(By.xpath("/html/body/div/div/descendant::input"));
for (WebElement input : inputs) {
System.out.println(input.getAttribute("id"));
}
You can get that by running a javascript, using this.browserbot.findElement('/html/body/div/div//input'):
Of course, this depends on the source language, but it would be something like this (in perl, untested):
#first count the number of inputs with ids
my $count = $selObj->get_xpath_count('/html/body/div/div//input[#id]');
#build a javascript that iterates through the inputs and saves their IDs
my $javascript;
$javascript .= 'var elements = [];';
$javascript .= "for (i=1;i<=$count;i++)";
$javascript .= " elements.push(this.browserbot.findElement('/html/body/div/div/input['+i+']').id);";
#the last thing it should do is output a string, which Selenium will return to you
$javascript .= "elements.join(',');";
my $idString = $selObj->get_eval($javascript);
I always thought there should be a more direct way to do this, but I haven't found it yet!
EDITED based on the comments, the for loop count should start from 1 and include $count, also the findElement line only needs one forward-slash before input.
EDIT2 Adding a completely different idea based on further comments:
Selenium's javascripts that get attached to every page include a function called eval_xpath that returns an array of DOM elements for a given query. Sounds like what you want?
Here's what I think the javascript would look like (again, untested):
var elements = eval_xpath('/html/body/div/div//input',this.browserbot.getCurrentWindow().document);
var results = [];
for (i=0;i<elements.length;i++){
results.push(elements[i].id);
}
results.join(',');