Java XPath question - java

I wrote some codes to parse the html using xpath and Java. The html file is something like:
<div class="field_row">
<label for="names">Names *</label>
<input id="address.A" type="text" maxlength="15" size="32" value="12345" name="address.work">
<span class="additional_info"> Information 1 </span>
</div>
<div class="field_row">
<label for="names">Names *</label>
<input id="address.B" type="text" maxlength="15" size="32" value="12345" name="address.work">
<span class="additional_info"> Information 2 </span>
</div>
And Java codes:
public static final Element INFOFIELD= Element.findXPath(".//*[#class='additional_info'");
will let me get 'Information 1'; however, I need to retrieve 'Information 2'. Therefore, I use:
public static final Element INFOFIELD= Element.findXPath(".//*[#class='additional_info' and #id='address.B']");
But got errors. Could you give me some hint please? Thanks. A.

You can create an XPath based on your input field (address.B), and then specify you want to access one of its sibling nodes and thus retrieve its data...
XPath:
//input[#id='address.B']/following-sibling::span[#class='additional_info']
as you can see after we find the input node with the id attribute 'address.b', we specify 'following-sibling'. This indicates that we want to select one of the siblings after the current node('address.B's input field). Then we specify which node that is followed by the attribute details: span[#class='additional_info']
some working code implementing the above XPath:
WebElement element = driver.findElement(By.xpath("//input[#id='address.B']/following-sibling::span[#class='additional_info']"));
System.out.println(element.getText());
will print 'Information 2'
You can use XPath axes in other related ways to access other nodes in the DOM (parents, children, siblings,etc).
http://www.w3schools.com/xpath/xpath_axes.asp
An axis defines a node-set relative to the current node.

Related

extracting attribute value of parent element using Selenium

Experienced with Java, pretty new to Selenium, locators, etc.
Buried deep in some HTML is several similar divisions:
<div tabgroup="topTabs__County Summary" sectiongroup class="field TextDescription tab">
<label for="request_48543">
<span class="label">Monument</span>
</label>
</div>
<div tabgroup="topTabs__County Summary" sectiongroup class="field DropDownList readonly tab">
<label for="request_48543">
<span class="label">Geolocation</span>
</label>
</div>
<div tabgroup="topTabs__County Summary" sectiongroup class="field SingleLineText tab">
<label for="request_48543">
<span class="label">Intersection</span>
</label>
</div
I need some Selenium magic to find a label with a specific value then backtrack to find that label's division and from that division extract the value of a given attribute. Drilling down seems fairly easy but how does one "back up" ?
For example, given "Geolocation" I'd like to extract "field DropDownList readonly tab"
I've tried things like
WebElement chill = m.findElement(By.xpath("../..//span[text='Geolocation']"));
to no avail
You reversed the order of going to the parent element, and you need () in text. The xpath should be
"//span[text()='Geolocation']/../.."
Another option is to look for an element that has a chilled with "Geolocation" text
"//div[.//span[text()='Geolocation']]"
this might give you more results, depends on the html structure that is not in the question. In that case you can add unique attribute, for example tabgroup
"//div[.//span[text()='Geolocation']][#tabgroup]"
this will return only <div> tag that has tabgroup attribute.
To extract the data use getAttribute("class") on chill WebElement

selecting input-field via placeholder - Element is not visible

Having a hard time entering text into the credentials page on:
Pixiv
<div class="input-field-group">
<div class="input-field">
<input placeholder="E-mail address / pixiv ID" autocapitalize="off" value="" type="text">
</div>
<div class="input-field">
<input placeholder="Password" autocapitalize="off" value="" type="password">
</div>
</div>
I am selecting the field using the following code as id attribute is not available:
driver.findElement(By.xpath("//input[#placeholder='Password']"));
but I am unable to manipulate the element via sendKeys nor clear.
it throws the following exceptions respectively:
org.openqa.selenium.ElementNotInteractableException: Element is not visible
org.openqa.selenium.InvalidElementStateException: Element is not currently interactable and may not be manipulated
There are actually two elements on this page that use that XPath. The webdriver will choose the first element that meets the requirements set by the developer. Unfortunately the element you want is the second element in the DOM. However updating your XPath to be more specific will help:
driver.findElement(By.xpath("//div[#id='container-login']//input[#placeholder='Password']"));

Find element's siblings with selenium webdriver

I have a structure like this:
<div>
<div>
<span class="">TextA</span>
</div>
<div>
<span class="">TextB</span>
</div>
</div>
I can find element span with TextA, and from there, I want to find span with textB, then click on it (which is not available to find it alone). So I used xpath like this:
webDriver.findElement(By.xpath("//span[contains(.,'TextA')]/following-sibling::span")).click();
I got exception Element is not found. I assumed these spans are siblings ?!. Can anyone help me in this case. Thanks
I got exception Element is not found. I assumed these spans are siblings ?
These two spans are not siblings - they are children of different elements and cannot be siblings.
Instead of following-sibling, you may use the following axis:
//span[contains(.,'TextA')]/following::span
Or, you may get the div element that contains the span with TextA text and then get it's following sibling:
//div[contains(span, 'TextA')]/following-sibling::div/span
The <span>'s aren't siblings. if the <span>'s were siblings, they'd have to be adjacent.
for example:
<div>
<div>
<span class="">TextA</span>
<span class="">TextB</span>
</div>
</div>
In this case, they are siblings and your selector would work.
The elements that are actually siblings, are the <div>s.
The xpath that could work for you, would be:
//span[contains(.,'TextA')]/../following-sibling::div/span

Selenium Java Xpath complex element determination of child of one sibling

I am trying to locate a specific element on a page but cannot figure out the proper Xpath to use.
Here is the HTML (note that the location of each div can vary):
<div>
<label>First Name</label>
<span class="metadataField metadataFieldReadonly">
<input type="text" name="some-random-value" value="John">
</span>
</div>
<div>
<label>Last Name</label>
<span class="metadataField metadataFieldReadonly">
<input type="text" name="some-random-value" value="Smith">
</span>
</div>
So what I am trying to locate the INPUT element that is the //div/span[#class='metadataField metadataFieldReadonly']/input in the same div that has a //div/label[text()='Last Name']
I can successfully locate the label with this (using JAVA):
driver.findElement(By.xpath("//div/label[text()='Last Name']")).click();
And I can successfully locate the first input under the first element (but I may not always want the first element) with this:
driver.findElement(By.xpath("//div/span[#class='metadataField metadataFieldReadonly']/input")).click();
So the problems are that (i) the name tag and value of the INPUT are always different so they cannot be used to pick the element, and (ii) the div with the last name label may not always be the second one, and (iii) the label and span are the same level (siblings) so I cannot figure out how to properly create the Xpath statement.
So in words, I need to find the input of the span in the same div that has a label with 'Last Name' in it.
So I need to know how to combine these two XPath statements into one complex statement (assuming they are in the same div and that the label and span are siblings):
//div/label[text()='Last Name']
//div/span[#class='metadataField metadataFieldReadonly']/input
Thanks
That's downright easy: //div[label[text()='Last Name']]/span[#class='metadataField metadataFieldReadonly']/input - literally, "the input contained in the span with the metadataField and metadataFieldReadonly classes contained in the div that contains the label with the text 'Last Name'". So you're using the label to locate the div and then building from it to the input you want.
To be a more robust, you shouldn't count on the ordering of the class names - that isn't guaranteed by the specs. So something like this would be stronger: //div[label[text()='Last Name']]/span[contains(concat(' ', #class,' '),' metadataField ') and contains(concat(' ',#class,' '),' metadataFieldReadonly ')]/input.

How to add a component to a label?

I have the following html:
<label wicket:id="drugSearchResult.row.item.label" for="drug_1">[Drug XYZ]
<span wicket:id="drugSearchResult.row.item.info">[Information, Price, Other]</span>
</label>
But label element are not allowed to add a child component.
Is there any way to achieve this html requirement?
This is the designer's requirement:
Drug XYZ // label
Information, Price, Other // span
Make sure you're using FormComponentLabel for the <label> element instead of Label.
Label's purpose is to output text inside the associated element (it can be a <span>, <div> or almost any other tag).
FormComponentLabel's purpose is to model <label> tags. They receive the FormComponent they're related to and automatically output the for attribute with the proper value for the dom id attribute.
Take a look at the Wicket wiki page on Form control labels. They're adding components to FormComponentLabel there.
If you'd like to avoid using FormComponentLabel at all, you shouldn't be giving it a wicket:id attribute, and manually set the DOM id attribute of the element the <label> is going to refer to. Then just use it in the for attribute of the <label>.
For instance:
HTML
<input wicket:id="drug">
<label for="drug_1">[Drug XYZ]
<span wicket:id="drugSearchResult.row.item.info">[Information, Price, Other]</span>
</label>
Java
TextField drug = new TextField("drug");
drug.setMarkupId("drug_1"); // Make sure this ID is unique in the page!
drug.setOutputMarkupId(true);
add(drug);
Label drugDescription = new Label("drugSearchResult.row.item.label", aModel);
add(drugDescription);
Using properties and <wicket:message>
For me, the approach below is useful.
In my project, I have only one location per page where the text for the <label>s and validation messages is defined. It's the properties file of the web page.
The additional <div>s and their class attributes are from Bootstrap.
<div class="form-group required">
<label wicket:for="customer.name1">
<wicket:message key="customer.name1"/>
</label>
<div class="controls">
<input type="text" wicket:id="customer.name1" required class="form-control">
</div>
</div>
Java
add(new RequiredTextField<String>("customer.name1")
.setLabel(new StringResourceModel("customer.name1")));
customerPage.properties
# siehe wicket-core-7.9.0-sources.jar!/org/apache/wicket/Application_de.properties
Required='${label}' ist erforderlich
customer.name1=Name 1
customer.name2=Name 2
customer.department=Abteilung
customer.phone=Telefon
customer.active=aktiv

Categories