How to get value from the table? - java

<table id="usersForm:mainTable:n" class="extdt-table-layout res-table" cellspacing="0" cellpadding="0" border="0" width="100%;" style="">
<colgroup id="usersForm:mainTable:colgroup:body">
<tbody id="usersForm:mainTable:tb">
<tr id="usersForm:mainTable:n:0" class="extdt-firstrow rich-extdt-firstrow extdt-row-selected rich-sdt-row-selected res-table-selected" onmousedown="handleMouseClick(event)">
<td id="usersForm:mainTable:1:name" class="extdt-cell rich-extdt-cell">
<div class="extdt-cell-div">
<div onmouseup="if (showMenu(this)) {document.lkjasdf = event; selectContextMenu('ENTTUSER','MbUsersSearch');}">
<span title="ADMIN" onmouseup="if(!rightButton && isSelectedRow(this,rightButton) && false && !disableContext) {setDisableContext(true); beforePrepareDefaultAction('ENTTUSER','MbUsersSearch');}">ADMIN</span>
Using htmlelements pattern, i want get value from table, ie verify that table contains value "ADMIN".
In htmlelements can able work with tables "from the box". I wrote:
import ru.yandex.qatools.htmlelements.element.Table;
public class MainTableForm extends HtmlElement {
#FindBy(id = "usersForm:mainTable:n")
private Table table;
}
Then I do not know which method helps to get the value.

You can use List<List<String>> getRowsAsString() method to get all the values from your table. Then just check this collection with hamcrest matcher. So the final code will be pretty simple:
assertThat(table.getRowsAsString(), contains(contains(equalTo("ADMIN"))));

You can do this
WebElement adminSpan = driver.findElement(By.cssSelector("#usersForm:mainTable:n span[title='ADMIN']"));
System.out.println(adminSpan.getText().trim());
I added the TABLE id just in case there is more than one of these admin SPANs. The CSS selector reads as find an element with an id (#) of usersForm:mainTable:n that has a descendant () SPAN with the title of ADMIN.
You may or may not need to trim(). I've gotten to the point where I just always trim() because it's easy and it saves me time finding and adding it later when I find out I needed it but didn't realize it. :)

Try to find out by installing firepath
find out the path by firepath

Related

Selenium Find By Xpath returning the wrong element

I am trying to select a channel from a series of channles that are displayed in a HTML table. I'm using the following Selenium method to select the link
WebElement channel = driver.findElement(By.xpath("//span[contains(text(),Sales)]"));
channel.click();
However it's selecting the first channel in the list (Account Management) instead. I would expect that it would either select the correct channel or throw an error, rather than select the wrong one. The following is the full xpath of the channel I want:
/html/body/div[2]/div[2]/form/div/table/tbody[2]/tr/td/ul/li[2]/a/span
The list of channels is defined like this in the HTML code:
<form action="nextpage.do" method="post" name="selectChannelForm">
<div class="de">
<h2>Select channel</h2>
<table id="selectChannelForm">
<tbody id=""></tbody>
<tbody id="">
<tr rowtype="container">
<td class="desecond" colspan="3">
<ul>
<li>
<a id="selected_a" href="nextpage.do?selectedChannel=123">
<span>Account Management</span></a>
</li>
<li>
<a id="selected_a" href="nextpage.do?selectedChannel=456">
<span>Sales</span></a>
</li>
<li>
<a id="selected_a" href="nextpage.do?selectedChannel=789">
<span>Complaints</span></a>
</li>
</ul>
</td>
</tr>
</tbody>
</table>
<input type="hidden" value="selectChannelForm" name="formid">
</div>
First - your mistake!
You forgot the quotation marks around "Sales", just change your code a bit and it will work:
WebElement channel = driver.findElement(By.xpath("//span[contains(text(),'Sales')]"));
channel.click();
Second - xpath bug?
You're right that it is weird, that you are not getting an error message but instead the first element that is a span.
This might acutally be a bug in xpath. The contains functions realizes that your second argument is no a string, but instead of returning false, it returns true.
It actually hits all three of the span items. You only get the first as a result because you used the findElement function.
Try this and you will see the quirk:
System.out.println(driver.findElements(By.xpath("//span[contains(text(),Sales)]")).size());
Result will be:
3
Third - might be "as designed"
Having a look at the w3c definition you will find the following line:
If the value of $arg2 is the zero-length string, then the function
returns true.
Then on the xpath-site of microsoft you will find another interesting hint to the puzzle:
If an argument is not of type string, it is first converted to a
string and then evaluated.
Putting all this information together, I guess, xpath interprets your non-string/non-variable second parameter as an empty string and therefore returns true for all span elements since you were searching for //span.
UPDATE
From #MichaelKay in the comments we learn, that my "guess" was pretty close:
In XPath and XQuery, a bare name like "hello" means child::hello, and
if you're not using schema-awareness, then the system will just look
for children called hello, and if there aren't any, it will return an
empty node-set.
Conclusion: The behaviour the OP sees is as designed, even though it seems pretty non-intuitive.
The xpath that you are using is missing quotes "" around Sales text. text() function takes an argument that is a string and a string can be formed using quotes. Update your xpath in the following way -
driver.findElement(By.xpath("//span[contains(text(),'Sales')]")).click();
Or if you want to assign it to a WebElement, then do put in your quotes -
WebElement channel = driver.findElement(By.xpath("//span[contains(text(),'Sales')]"));
channel.click();
If at all you want to write nested double quotes or nested single quotes then use an escape character \ to write it. Here's how -
WebElement channel = driver.findElement(By.xpath("//span[contains(text(),\"Sales\")]"));
channel.click();
Hope this helps.
The xpath need to be modified as "//span[contains(text(),'Sales')]" .
As we can see below in the method definitions, contains method will return true only If second parameter is also a text.
Source: https://en.wikipedia.org/wiki/XPath
contains(s1, s2)
returns true if s1 contains s2.
text()
finds a node of type text

Sendkeys to a GXT integerSpinnerField in Selenium

I have been trying to "sendkeys" to a IntegerSpinnerField but it is not working. The IntegerSpinnerField has a id but I can't use the "sendkeys" on it. I opened up the html the ID is there but the IntegerSpinnerField is 3 widgets in 1. So is there anyway i can give an id to the 3 widgets that make up the IntegerSpinnerField.
html-code
<div __gwtcellbasedwidgetimpldispatchingfocus="true" __gwtcellbasedwidgetimpldispatchingblur="true" id="maximumfrequencySpinnerField" class="GJCAMDBBOPB" title="Enter an maximum frequency value within the allowable range. The allowed range is 776 to 787 MHz" style="width: 100px;"><div style="width: 100px;" class="GJCAMDBBAW"><table width="100%" cellpadding="0" cellspacing="0"><tbody><tr><td><input style="width: 75px;" type="text" value="" class="GJCAMDBBCV GJCAMDBBJV" id="x-auto-229-input" tabindex="0" disabled=""></td><td><div class="GJCAMDBBKV"></div><div class="GJCAMDBBNV"></div></td></tr></tbody></table></div></div>
Id of the textfield which is autogenerated
Don't go with id's for ExtJs Applications those were dynamically generated at runtime and prone to change everytime.So you have to write a relative xpath for every element.And ExtJS elements follow some pattern So you have to write xpaths for that and you can reuse them.
Refer any-suggestions-for-testing-extjs-code-in-a-browser-preferably-with-selenium
For your case and for most input element you can find it using label.The immediate input of the label might be the element you are looking for
I have tested the spinner field named Revenue %: in this demo site using the following xpath
//label[text()='Revenue %:']//following::input
The script for the above site.Hope it helps
WebDriver driver = new FirefoxDriver();
driver.get("https://www.sencha.com/examples/#ExamplePlace:dashboard");
WebElement element=driver.findElement(By.xpath("//label[text()='Revenue %:']//following::input"));
element.clear();
element.sendKeys("67.67");

Selenium div attributes keep changing, how can I find this element?

I am trying to find an element with Selenium and Java, the problem is that the element's id, class, and name always increment so I am not able to find it with selenium. Below is what I am currently trying:
WebElement field = driver.findElement(By.xpath("//input[contains(#linkText, 'Broadcast copy')]"));
In my html file these are the attributes that keeps changing:
id="files[%2Fopt%240%2Frules%2F%2F000102%2.xml][%2Fcluster%2Fname]"
name="files[%2Fopt%240%2Frules%2F%2F000102%2.xml][%2Fcluster%2Fname]"
value="copy (Cluster 102)"
Entire html
<tbody>
<tr class='rowOdd'>
<td><b>Name</b></td>
<td> <input type='text' data-validation='required validate-name-unique validate-name-not-empty' size='65' id='files[%2Fopt%240%2Frules%2F%2F000102%2Fcluster.xml][%2Fcluster%2Fname]' name='files[%2Fopt%240%2Frules%2F%2F000102%2Fcluster.xml][%2Fcluster%2Fname]' value='copy (Cluster 102)' /> </td>
These always increment and I have no access to the html file to change anything. So my question is how can I find this input element? Thanks in advance.
UPDATE
I get the error:
Unable to locate element:{"method":"id", "selector":"files[.*][.*]"}
I believe the xpath you are using is incorrect. Use
//input[contains(text(), 'Broadcast copy')]
instead of
//input[contains(#linkText, 'Broadcast copy')]
According to the html you have provide the following should work as well
//body[contains(.,'Name')]//input
Try this..
In case "copy (Cluster" text in value attribute is not changing, then you can try below xpath:-
//body[contains(.,'Name')]//input[contains(#value,'copy (Cluster')]
Since the attributes of id, class, and css were constantly changing, 'data-validation' was one that stayed the same all the time. So the code below worked for me.
driver.findElement(By.xpath("//input[#data-validation='required validate-name-unique validate-name-not-empty']"));

Java selenium xpath - getting all elements under a specific element

this is a simplified HTML structure i'm searching through:
<div class="main">
...other stuff...
<td class="child">44</td>
<td class="child">59</td>
<td class="child">11</td>
</div>
<div class="main">
...other stuff...
<td class="child">5</td>
<td class="child">14</td>
<td class="child">98</td>
</div>
...this kind of structure repeats with similar numbers a few more times but with identical class names
I need to extract all the numbers under the first found main class so I've made a query to search for the first main, and all td's with the specific class under it. Can somebody give me a hint what I'm doing wrong since this query gives me all the numbers from all td's with class "child" in all "main" div's:
List<WebElement> koefi = driver.findElements(By.xpath("//div[#class='main'][1]//td[#class='child']"));
What am I doing wrong or is my logic right but I'm missing some other parts of html which I haven't pasted here since the structure is too cumbersome..?
Thank You!!
p.s.:
I tried this also but again, I get contents of all td's with "child" class, and not only the first "main"..
List<WebElement> koefi = driver.findElements(By.xpath("//*[1][#class='main']//td[#class='child']"));
UPDATE:
I managed to solve my problem by first getting the first occurence of the "main" div which is by default found by the .findElement function:
WebElement element = driver.findElement(By.xpath("//*[1][#id='main']"));
And then extracting with .findElements function the "child" classes:
List<WebElement> kk = element.findElements(By.className("child"));
I am still unable to figure out why doesn't the .findElements with my xpath work, or it works too well, it extracts every "main" class and not only the first one. And the original HTML is too big to paste here, so I don't want to bother you guys!!
A much cleaner solution would be to first grab all the divs with class main, like so:
List<WebElement> allDivs = driver.findElements(By.className("main"));
Then, as you specified, find all the tds with class child, like so:
List<WebElement> tds = allDivs[0].findElements(By.className("child"));
After that, it is just a matter of iterating over all the "tds" and read out your values.
You say in a comment that
the "main"'s are not direct siblings
so I suspect you are falling foul of a common error related to the definition of // in XPath. The path
//div[#class='main'][1]
does not select the first "main" div in the document. The reason for this is that // is a shorthand for /descendant-or-self::node()/ (including the leading and trailing slashes), so what this path actually means is
/descendant-or-self::node()/child::div[#class='main'][1]
When you see it fully expanded you realise that the [1] relates to the child:: step and not the search for descendants, i.e. you'll get all the div elements in the document that have the class "main" and are the first div-with-class-main under their respective parent elements. If your actual HTML is
<div>
<div class="main">...</div>
</div>
<div>
<div class="main">...</div>
</div>
then that XPath would select both of them (they're both the first under their parents). If you do just want the first one in the document then you should use the descendant:: axis
/descendant::div[#class='main'][1]
which will give you the first matching descendant only.

dijit.form.Select re-appears unpopulated?

This is a continuation of an issue I was having yesterday so if it looks familiar, thats why :) It IS a different question tho!
So I have another dijit.form.Select initially created on the page like so;
<c:set var="clazzId" value="${verification.clazz.id}" />
<div id="clazzOptions">
<select id="clazz" name="clazz" style="width:22em" dojoType="dijit.form.Select" maxHeight="140">
<option value="-1" label=" " />
<c:forEach items="${requestScope.clazzes}" var="clazzItem">
<c:choose>
<c:when test="${clazzId eq clazzItem.id}">
<option value="${clazzItem.id}" selected = "true">${clazzItem.name}</option>
</c:when>
<c:otherwise>
<option value="${clazzItem.id}">${clazzItem.name}</option>
</c:otherwise>
</c:choose>
</c:forEach>
</select>
</div>
I then have some javascript that I'm trying to use to swap the contents of the div "clazzOptions" depending on the value chosen from a different drop down (not seen here). If its a certain value, replace the div with a text message, if its any other value, re-show the original dijit.form.Select;
<script type="text/javascript">
var classDropDown;
var classPhDMessage = "PhD's do not require a Class or Grade";
dojo.addOnLoad(function() {
classDropDown = dojo.byId('clazzOptions').innerHTML;
});
function checkForPHD() {
var awardOption = dijit.byId('qualification').attr('displayedValue');
if(awardOption == "PhD"){
dojo.byId('clazzOptions').innerHTML = classPhDMessage;
} else {
dojo.byId('clazzOptions').innerHTML = classDropDown;
}
}
</script>
As you can see I'm trying to capture the innerHTMLof the div as it is when the page loads and then depending on the value chosen in the other drop down (not seen) change between a predefine message and the captured div contents.
The issue is that after the original div contents have been replaced with the message and then the selection changes again away from "PhD" and the original div innerHTML is placed back into the div, the dijit.form.Select re-appears but is completely empty and in fact doesn't appear usable at all? If I remove the dijit.form.Select dojoType and just leave it as a normal select this whole operation works perfectly but I kinda need it to be a dijit.form.Select.
Why won't dijit.form.Select work in this case whereas a normal select does?
You shouldn't use innerHTML to add/remove dijits, especially if you're trying to re-use them.
A dijit is a combination of HTML and Javascript and can be thought of as 'more complex' than simply the contents of a specific node.
You have a number of options:
Disable your dijit.form.Select and add a small note under it (or set it's value) explaining why it's disabled (this is generally a better UI paradigm than removing controls). Depending on your UI this is what I'd try and do
Create a <span> as a sibling of your dijit.form.Select and use CSS to show/hide one of them at a time.
Obviously you'll still get the value of the dijit.form.Select when you submit your form, but because you do server side validation as well as client side validation (I hope :) this won't be a problem
Use the dojo.data API to create a datastore from your "${requestScope.clazzes}" iterator. Use this store when you (programatically) create the dijit.form.Select. Keep track of the current value of said Select somewhere. When you don't need it, call yourSelect.destroy() to properly destroy it, then when you need it again create a new one and set it's value to the saved value from before.
This seems unnecessarily expensive to me tho.

Categories