Improvement With Xpath Search? - selenium

I am a student learning selenium and trying to set a checkbox(by placing a tick) that resides on the same row as a target document. Here is how the row looks:
<span><input>Checkbox</input></span> - <span>_lblDocumentCategoryDescription</span> - <span>_lblDocumentdescription</span>
Each row consists of a checkbox for that row, a category and a description. I am provided the description and need to set the corresponding checkbox at the beginning of the row.
Although I have a solution that works I am looking for a better method as my method is very slow and I can call it multiple times, compounding the slowness. The page in question has 315 fixed different documents that might possibly be added (to a merge and print feature). I usually add at least 3 rows so need to run the search
I am currently using xpath to search since my knowledge of CSS is limited and it seems like traversing up the DOM (no :parent?) is not easy unless using some javascript, which I am trying to avoid. I have read that executing JS doesn't emulate the user correctly so I guess xpath is the solution?
As I know there must be a quicker/better/more efficient solution I am hoping others might have found better?
Here is what I am currently using:
Method Call
CPTools,setStupidCheckbox("Sublease Addendum", true);
Method:
public CPTools setStupidCheckbox(String documentToPrint, Boolean onIfTrue) {
this.log.trace("Starting: setStupidCheckbox; parameter: documentToPrint " + documentToPrint);
String xpathForCheckboxes = "/ancestor::tr//input";
try {
String xpathForDocumentDescriptions = String.format("//span[contains(text(), '%s')]", documentToPrint);
tools.waitForElement(By.xpath(xpathForDocumentDescriptions), 2);
if (driver.findElement(By.xpath(xpathForDocumentDescriptions)).isDisplayed()) {
driver.findElementByXPath(xpathForDocumentDescriptions + xpathForCheckboxes).click();
}
} catch (org.openqa.selenium.NoSuchElementException | ElementNotVisibleException e) {
}
this.log.trace("Ending: setStupidCheckbox");
return this;
}
Here is the full xpath for a table row (the dynamic part of the id is the _ctl316):
<tr class="DataGridCellData">
<td class="DataGridCellDataCenterAligned">
<span class="DataLabel"><input id="ctl01_PageBody_grdDocument_ctl316_chkSelect" name="ctl01$PageBody$grdDocument$ctl316$chkSelect" onclick="javascript:uiHelper.GridCellCheck(this, 'chkAllSelect');" type="checkbox"></span>
</td><td>
<span id="ctl01_PageBody_grdDocument_ctl316_lblDocumentCategoryDescription">Approval Form</span>
</td><td align="left">
<span id="ctl01_PageBody_grdDocument_ctl316_lblDocumentdescription">Sublease Addendum</span>
</td><td align="center">
</td>
</tr>
Thanks in advance for any possible help.

Try below XPath if span text (Sublease Addendum) is unique:
//td[.= 'Sublease Addendum']

Related

Clicking all elements with matching class

I'm new to Selinium. There is a table (inside another table) that I want to click each <td>click here</td> with a matching class value (note the text between opening and closing td will change but is irrelevant for matching purposes). The class value I'm trying to match is open. From here I learn the right way is with //*[contains(concat(" ", normalize-space(#class), " "), " open ")]
This seems to work but just for one random cell. How do I make it click all? I was planning on accomplishing this first and repeating the step, but it may be worth noting that the script should also do the same for class value available and not just open Is there away to do logical or?
TL;DR I want to click everything with <td class="open">...</td>
and
<td class="available">...</td> where ... is example text that will vary but should be ignored.
Get all elements and store them in a List, then iterate over them and click the buttons in sequence.
List<WebElement> list = driver.findElements(By.xpath("//*[contains(concat(" ", normalize-space(#class), " "), " open ")]"));
for(WebElement webelement : list) {
// webelement Click the button here
}
To get all elements, you should use driver.FindElements. Make sure it's what you used, not driver.FindElement?
Use following approach may help you -
List<WebElement> allElements = driver.findElements(By.tagName("td"));
for(WebElement element:allElements)
{
if(element.getAttribute("class").contains("open"))
{
// manipulate with <td> tag data
}
}
Please use following XPath:
//td[normalize-space(#class)="open" or normalize-space(#class)="available"]
This should match both <td class=" open ">...</td> and
<td class=" available ">...</td>

Aurelia customAttribute to toggle input edit

I need some help in figuring out the best way to create a customAttribute that will allow for an easy edit-toggle. Here is what I'm looking for:
<tr toggle-edit>
<td edit-hide>${model.name}</td>
<td edit-show><input type="text" value.bind="model.name"></td>
<td><button edit-trigger>Edit</button></td>
</tr>
So basically I want a customAttribute named toggleEdit that will look for edit-trigger attribute and add an event listener to it that will toogle a variable true/false and depending on it will either show or hide the elements that have edit-hide / edit-show.
I'd prefer to not travers the DOM inside the element to find these attributes as it feels jQuerish, is this achievable?
I want to have a customAttribute like this because I have at least 10 elements that will use an edit button and having a variable for each one of them and then use if.bind seems like a bad idea. I could always do inside of the template itself through click.delegate="myShowVar = !myShowVar" but as far as I know puting logic inside html is a bad practice (coming from an angular background).
I would add a plunker/codepen but because of the whole compilation and libraries dependencies this does not seem like an easy task.
Many thanks for any ideas.
Use the contenteditable attribute
I recommend against trying to have a custom attribute automagically handling this for you. You'll probably run into more problems than you'll solve this way. Instead, I recommend that you create an editable property in your view model and bind to it.
The contenteditable attribute is a standard HTML attribute that allows for editing the content of HTML elements, such as DIVs, and is supported out of the box with Aurelia. I recommend leveraging it if it will meet your needs. Here's how:
table.html
<td contenteditable.bind="editable"></td>
<td><button click.delegate="editable = !editable"></td>
Full running gist here: https://gist.run/?id=c4e716f21f4f9c15a9346cfacbdae74b
Since my <tr></tr> turned out to be a 14 line code (with some animation on toggling) I decided it was best to create a customElement out of it. The problem I ran on was that customElements don't really work as table elements (similar as in other frameworks). The soultion to this is to use a as-element attribute.
Inside of the customElement I used contenteditable which is actually a better solution than using if.bind since swapping between input and a div produces a jumping effect because of the difference in styles (which of course could be circumnavigated by applying certain style to them but contenteditable works out-of-the-box).
This is more or less what I created (parent.html):
<tr as-element="my-custom-row"
title="My first row"
is-editable="true"
model.two-way="myModel.name"
value-changed-callback.call="updateModel(myModel)">
</tr>
And inside my-custom-row.html:
<template>
<td>${title & oneTime}</td>
<td>
<div contenteditable.one-way="editingEnabled ? 'true' : 'false'"
blur.trigger="valueChanged(model)"
textcontent.two-way="model">
</div>
<div class="my-class" class.one-way="editingEnabled ? 'my-class--active' : ''"></div>
</td>
<td>
<span class="edit-icon"
click.delegate="editingEnabled = !editingEnabled"
if.one-time="isEditable">
</span>
</td>
</template>
This way I don't have to create a variable for each edit since editingEnabled is unique to each customElement.
As I side note, I think it's better to be more explicit and to use one-way/two-way/one-time instead of bind since it's clear what is happening.
I will also provide the corresponding js for a full answer(my-custom-row.js):
import { bindable } from 'aurelia-framework';
export class MyCustomRow {
#bindable model;
#bindable title;
#bindable isEditable;
#bindable valueChangedCallback;
constructor() {}
valueChanged(modelValue) {
this.valueChangedCallback({ modelValue })
}
}

How to retrieve the value of an attribute using Seleium WebDriver?

The HTML code is given attached, I do not want to use hard code xpath, the requirement is to make it generic:
<td bgcolor="#FFFFFF">
<input name="hotel_name_0" id="hotel_name_0" type="text" value="Hotel Creek" class="select_text" onfocus="disable_ctrlV()" onkeypress="return Nothingonly(event)">
</td>
Code:
public static boolean fncVerifyTextInColumn(WebElement gridObjWebElement,
String stringToValidate, int columnNumber,String colName) {
boolean flagTextinColumn=false;
ArrayList<WebElement> objRows;
ArrayList<WebElement> objCols;
ArrayList<WebElement> childElement;
objRows=(ArrayList<WebElement>)gridObjWebElement.findElements(By.tagName("tr"));
objCols=(ArrayList<WebElement>)objRows.get(0).findElements(By.tagName("td"));
if(objCols.get(columnNumber).getText().equalsIgnoreCase(colName)){
for(int index=1;index<objRows.size();index++){
objCols=(ArrayList<WebElement>)objRows.get(index).findElements(By.tagName("td"));
childElement=(ArrayList<WebElement>)objCols.get(columnNumber).findElements(By.xpath("//input"));
System.out.println(childElement.get(0).getAttribute("value"));
if(stringToValidate.trim().equalsIgnoreCase(childElement.get(0).getAttribute("value").trim())){
flagTextinColumn=true;
}
}
}
return flagTextinColumn;
}
Method Calling:
fncVerifyTextInColumn(objGrid,hotels,1,"Hotel Name");
I would use cssSelector [id^='hotel_name_'] to locate the element and then getAttribute() retrieve the attribute value
By css = By.cssSelector("[id^='hotel_name_']");
WebElement myDynamicElement = (new WebDriverWait(driver, 10))
.until(ExpectedConditions.presenceOfElementLocated(css));
System.out.println(myDynamicElement.getAttribute("value"));
Notice the regex search of cssSelector here. With ^ I am skipping any dynamic number. Hoping that's the only element with hotel_name_someNumber on the page.
Just do
String attValue = driver.findElement(byAnyMethod).getAttribute("AttributeName");
Hope it helps
I think what you are looking for is this. (I'm assuming you know how to code, you just need a general direction so I'm going to leave out specific code.)
First, find the table the td is in. You might need to use an xPath for this or you'll need to assign an ID to the table so you can locate it.
Then once you have the table, do a FindElements to get the list of TRs under it.
Once you have the TRs, you can loop through them, grab the TDs under that and grab the TD at the index that has the INPUT you want the value of, get the INPUT and then get it's value.
Yep, lots of steps.
A shortcut may be to class all of the inputs you want the value for with a unique class and do a FindElements By className and loop through that list.

Get classname from tag in LESS

I want to use a classname to set a background on a element, but i'm not sure if it's possible with LESS js at the moment.
The case is:
I have a table with a list of athletes, on these lists i want to show a country flag as a background of a p tag.
In HTML it looks like:
<table>
<tr>
<td class="athlete"><p class="us">Athlete 1</p></td>
</tr>
</table>
I want to set the background image through a mixin, if i can get the classname from the P tag i would be there.
.athlete {
p {
.setBackgroundImage(#ClassName);
}
}
is there anyway in LESS to achieve this?
The problem is that i need a whole new rule for every possible country. My loop implementation didn't cut it either.
I went for the javascript implementation, i add a background image to a iso coded classname in a certain parent class.
<div class="nationality">
<p class="athlete">Athlete X<span class="nl"></span></p>
</div>
jQuery(".nationality span").css('background-image', 'url(../img/flags/60/'+jQuery(".nationality span").attr("class")+'.png);
Thanks guys.

How can I select checkboxes using the Selenium Java WebDriver?

How can I check the checkboxes using an id or XPath expression? Is there a method similar to select by visibletext for a dropdown?
Going through the examples given for all other related questions, I could not find a proper solution that works in a concise way that by few line or method I can check a chekbox or radio button.
A sample HTML section is below:
<tbody>
<tr>
<td>
<span class="120927">
<input id="ctl00_CM_ctl01_chkOptions_0" type="checkbox" name="ctl00$CM$ctl01$chkOptions$0"/>
<label for="ctl00_CM_ctl01_chkOptions_0">housingmoves</label>
</span>
</td>
</tr>
<tr>
<td>
<span class="120928">
<input id="ctl00_CM_ctl01_chkOptions_1" type="checkbox" name="ctl00$CM$ctl01$chkOptions$1"/>
<label for="ctl00_CM_ctl01_chkOptions_1">Seaside & Country Homes</label>
</span>
</td>
</tr>
</tbody>
Selecting a checkbox is similar to clicking a button.
driver.findElement(By.id("idOfTheElement")).click();
will do.
However, you can also see whether the checkbox is already checked. The following snippet checks whether the checkbox is selected or not. If it is not selected, then it selects.
if ( !driver.findElement(By.id("idOfTheElement")).isSelected() )
{
driver.findElement(By.id("idOfTheElement")).click();
}
It appears that the Internet Explorer driver does not interact with everything in the same way the other drivers do and checkboxes is one of those cases.
The trick with checkboxes is to send the Space key instead of using a click (only needed on Internet Explorer), like so in C#:
if (driver.Capabilities.BrowserName.Equals(“internet explorer"))
driver.findElement(By.id("idOfTheElement").SendKeys(Keys.Space);
else
driver.findElement(By.id("idOfTheElement").Click();
If you want to click on all checkboxes at once, a method like this will do:
private void ClickAllCheckboxes()
{
foreach (IWebElement e in driver.FindElements(By.xpath("//input[#type='checkbox']")))
{
if(!e.Selected)
e.Click();
}
}
Solution for C#
try
{
IWebElement TargetElement = driver.FindElement(By.XPath(xPathVal));
if (!TargetElement.Selected)
{
TargetElement.SendKeys(Keys.Space);
}
}
catch (Exception e)
{
}
You can use the following code:
List<WebElement> checkbox = driver.findElements(By.name("vehicle"));
((WebElement) checkbox.get(0)).click();
My HTML code was as follows:
<.input type="checkbox" name="vehicle" value="Bike">I have a bike<br/>
<.input type="checkbox" name="vehicle" value="Car">I have a car<br/>
To get the checkbox for 'Seaside & Country Homes', use this XPath:
//label[text()='Seaside & Country Homes']/preceding-sibling::input[#type='checkbox']
To get the checkbox for 'housingmoves', use this XPath:
//label[text()='housingmoves']/preceding-sibling::input[#type='checkbox']
The principle here is to get the label with the text you want, then get the checkbox that is before the label, since that seems to be how your HTML is laid out.
To get all checkboxes, you would start a little higher up and then work down, so that is to say get the table, and then get any checkbox within a span:
//table/descendant::span/input[#type='checkbox']
I found that sometimes JavaScript doesn't allow me to click the checkbox because was working with the element by onchange event.
And that sentence helps me to allow the problem:
driver.findElement(By.xpath(".//*[#id='theID']")).sendKeys(Keys.SPACE);
This should help -
IWebElement elementToClick = driver.findElement(By.xpath(""//input[contains(#id, 'lstCategory_0')]"));
elementToClick.Click();
You can also pass an id.
If you want something like visible text you can "find element" by name if they have names.
The below code will first get all the checkboxes present on the page, and then deselect all the checked boxes.
List<WebElement> allCheckbox = driver.findElements(By
.xpath("//input[#type='checkbox']"));
for (WebElement ele : allCheckbox) {
if (ele.isSelected()) {
ele.click();
}
}
A solution using WebDriver and C# is below. The key idea is to get the ID of the checkbox from the labels' 'for' attribute, and use that to identify the checkbox.
The code will also set the checkbox state only if it needs to be changed.
public void SetCheckboxStatus(string value, bool toCheck)
{
// Get the label containing the checkbox state
IWebElement labelElement = this.Driver.FindElement(By.XPath(string.Format("//label[.='{0}']",value)));
string checkboxId = labelElement.GetAttribute("for");
IWebElement checkbox = this.Driver.FindElement(By.Id(checkboxId));
if (toCheck != checkbox.Selected)
{
checkbox.Click();
}
}
Maybe a good starting point:
isChecked = driver.findElement((By.id("idOftheElement"))).getAttribute("name");
if(!isChecked.contains("chkOptions$1"))
{
driver.FindElement(By.Id("idOfTheElement")).Click();
}
Running this approach will in fact toggle the checkbox; .isSelected() in Java/Selenium 2 apparently always returns false (at least with the Java, Selenium, and Firefox versions I tested it with).
The selection of the proper checkbox isn't where the problem lies -- rather, it is in distinguishing correctly the initial state to needlessly avoid reclicking an already-checked box.
To select a checkbox, use the "WebElement" class.
To operate on a drop-down list, use the "Select" class.
Step 1:
The object locator supposed to be used here is XPath. So derive the XPath for those two checkboxes.
String housingmoves="//label[contains(text(),'housingmoves')]/preceding-sibling::input";
String season_country_homes="//label[contains(text(),'Seaside & Country Homes')]/preceding-sibling::input";
Step 2:
Perform a click on the checkboxes
driver.findElement(By.xpath(housingmoves)).click();
driver.findElement(By.xpath(season_country_homes)).click();
For a partial match, do the following:
getDriver().findElement(By.cssSelector("<tag name>[id*='id pattern to look for']")).click();
Here is the C# version of Scott Crowe's answer. I found that both IEDriver and ChromeDriver responded to sending a Key.Space instead of clicking on the checkbox.
if (((RemoteWebDriver)driver).Capabilities.BrowserName == "firefox")
{
// Firefox
driver.FindElement(By.Id("idOfTheElement")).Click();
}
else
{
// Chrome and Internet Explorer
driver.FindElement(By.Id("idOfTheElement")).SendKeys(Keys.Space);
}
I tried with various approaches, but nothing worked. I kept getting "Cannot click element" or ElementNotVisibleException.
I was able to find the input, but I couldn't check it. Now, I'm clicking on the div that contains the checkbox and it works with following HTML (CSS based on Bootstrap).
#foreach (var item in Model)
{
<tr>
<td>
<div id="#item.Id" class="checkbox">
<label><input type="checkbox" class="selectone" value="#item.Id"></label>
</div>
</td>
<td val="#item.Id">
#item.Detail
</td>
<td>
<div>#item.Desc
</div>
</td>
<td>
#Html.ActionLink("Edit", "Create", new { EditId = item.Id })
</td>
</tr>
}
This is the code for WebDriver:
var table = driver.FindElement(By.TagName("table"));
var tds = table.FindElements(By.TagName("td"));
var itemTds = tds.Where(t => t.Text == itemtocheck);
foreach (var td in itemTds)
{
var CheckBoxTd = tds[tds.IndexOf(td) - 1];
var val = td.GetAttribute("val");
CheckBoxTd.FindElement(By.Id(val)).Click();
}
In this approach, I give the item id as id for the div and also add an attribute for td with that id. Once I find the td of the item that needs to be checked, I can find the div before that td and click it. We can also use the XPath query that supports before (here is the example http://learn-automation.com/how-to-write-dynamic-xpath-in-selenium/).