Getting StaleElementReferenceException after performing checkbox.click() - webdriver

Getting org.openqa.selenium.StaleElementReferenceException: Element is no longer attached to the DOM
list = driver.findElements(By.cssSelector(listLocator));
for (WebElement listItem : list) {
checkbox = listItem.findElement(By.cssSelector(checkboxLocator));
checkbox.click();
String path = checkbox.getCssValue("background-image"));
}
After performing checkbox.click(); I am not able to call any method on checkbox element
corresponding image :
My Locators are
listLocator = ul[class="planList"] > li[class="conditionsTextWrapper"]
checkboxLocator = label[role="button"] > span[class="ui-button-text"]
My HTML source before peforming checkbox.click() :
<ul class="planList">
<li class="conditionsTextWrapper" >
<input name="chkSubOpt" type="checkbox">
<label class="check ui-button ui-widget ui-state-default ui-corner-all ui-button-text-only" for="CAT_5844" aria-pressed="false" role="button">
<span class="ui-button-text"></span>
</label>
<label class="conditionsText">Eat at least 5 fruits and vegetables every day</label>
</li>
</ul>
after performing checkbox.click() :
<ul class="planList">
<li class="conditionsTextWrapper" >
<input name="chkSubOpt" type="checkbox">
<label class="check ui-button ui-widget ui-state-default ui-corner-all ui-state-active ui-button-text-only" for="CAT_5844" aria-pressed="true" role="button" aria-disabled="false">
<label class="conditionsText">Eat at least 5 fruits and vegetables every day</label>
</li>
</ul>

As mentioned above, the reason of these errors is that the DOM structure has been changed after clicking on the checkbox. The following code works for me.
string checkboxXPath = "//input[contains(#id, 'chblRqstState')]";
var allCheckboxes = driver.FindElements(By.XPath(checkboxXPath));
for (int i = 0; i != allCheckboxes.Count; i++)
{
allCheckboxes[i].Click();
System.Threading.Thread.Sleep(2000);
allCheckboxes = driver.FindElements(By.XPath(checkboxXPath));
}

Your DOM is changing following the .click(), as such the reference Webdriver formed to relate to that element (as in the next in your list) is no longer valid. As such you're going to need to rebuild the list within your loop.
list = driver.findElements(By.cssSelector(listLocator));
for (i=0; list.length(); i++) {
list = driver.findElements(By.cssSelector(listLocator));
checkbox = list[i].findElement(By.cssSelector(checkboxLocator));
checkbox.click();
String path = checkbox.getCssValue("background-image"));
}

This Happens because your DOM structure has changed since you have referred your check box.
This is a very common Exception which people get.
WorkAround can be to catch the Exception and try locating and clicking the same element again.
Example
WebElement date = driver.findElement(By.linkText("date"));
date.click();
}
catch(org.openqa.selenium.StaleElementReferenceException ex)
{
log.debug("Exception in finding date");
log.debug(e);
WebElement date = driver.findElement(By.linkText("date"));
date.click();
}
This can solve most of your problems !
Will work for your checkbox problem as well. However i suggest you use #Mark Rowlands solution. His code is cleaner.

Related

Keep Filter from HttpPost on next page of PaginatedList in ASP.NET Core MVC

I am creating a program in which I have a Database with records I want to filter.
The filter has 4 input type="text" and returns the right records based on the filters I typed.
The problem is, when I click Next Page link (of PaginatedList), the second page has no filters adapted.
It lose the filters I gave. I know I must give asp-route on Next Page link some variable with the data given in filter like Microsoft Tutorial gives currentFilter, but I have 4 filters like this :
Name=x&Phone=y&...
Here is my Controller Code simplyfied :
public async Task<IActionResult> Index( some variables ){
var appusers = from a in _context.AppUsers
select a;
return(return View(await app.PaginatedList<AppUser>.CreateAsync(appusers.AsNoTracking(), pageNumber ?? 1, pageSize));)
}
AND
[HttpPost]
public async Task<IActionResult> Index(string Name, string Phone, string Calls, string Date){
var ap = from c in _context.AppUsers
select c;
Filter The database based on variable values Name,Phone,Calls,Date
return View(await PushNotificationApp.PaginatedList<AppUser>.CreateAsync(ap.AsNoTracking(),
pageNumber?? 1, pageSize));}
Here is the part of Index :
#using (Html.BeginForm("Index", "AppUsers", new { id = "filterForm" }))
{
name="SearchTrips" value="#ViewData["CurrentTrips"]" />
<input id="NameFilterField" type="text" name="Name" />
<input id="PhoneFilterField" type="text" name="Phone" />
<input id="CallsFilterField" type="text" name="Calls" />
<input id="DateFilterField" type="text" pattern="\d{1,2}/\d{1,2}/\d{4}" name="Date" />
<button class="m-3" type="submit" value="submit">Search</button>
}
AND PAGELIST LINK PART :
<div class="p-2 bd-highlight">
<nav aria-label="Page navigation example">
<ul class="pagination">
<li class="page-item">
<a class="page-link MainButton" asp-action="Index"
asp-route-pageNumber="#(Model.PageIndex - 1)"
asp-route-currentFilter="#ViewData["CurrentFilter"]"
class="page-link #prevDisabled">
Previous
</a>
</li>
<li class="page-item">
<a class="page-link MainButton" asp-action="Index"
asp-route-pageNumber="#(Model.PageIndex + 1)"
asp-route-currentFilter="#ViewData["CurrentFilter"]"
Next
</a>
</li>
</ul>
</nav>
</div>
The problem is, when I click Next Page link (of PaginatedList), the second page has no filters adapted. It lose the filters I gave.
You are doing the filter in the Post action, but when you click the Next link, it will go to the Get action which not filter. So, I think you can make the form do a get request and just do the filter in the Get method.
I know I must give asp-route on Next Page link some variable with the data given in filter like Microsoft Tutorial gives currentFilter, but I have 4 filters like this : Name=x&Phone=y&...
4 filters is the same, just give the other filter parameters to the Next link
<a class="page-link MainButton" asp-action="Index"
asp-route-pageNumber="#(Model.PageIndex + 1)"
asp-route-Name="#ViewData["Name"]"
asp-route-Phone="#ViewData["Phone"]"
asp-route-Calls="#ViewData["Calls"]"
asp-route-Date="#ViewData["Date"]"
Next
</a>
why not use a single box to search everything or four search boxes using the method below in the tutorial?
if (!String.IsNullOrEmpty(searchString))
{
students = students.Where(s => s.LastName.Contains(searchString)
|| s.FirstMidName.Contains(searchString));
}

Toggling CSS active classes on click events in Blazor

I am trying to find the best way to toggle css classes on and off with Blazor click events. This functionality involves clicking on a column of list boxes generated by a #foreach and changing the style for only the active box and then disabling it with subsequent clicks on other boxes. In short, only the active box changes style on click and all other boxes do not have the style change.
I have started with the code below, which creates the boxes correctly, but it applies 'active' to all of the list boxes rather than to just the currently active box (this is still in POC, so some of the code still needs refactoring):
#foreach (var msg in Message)
{
<a class="list-group-item list-group-item-action #(ActiveClass ? "active" : "")" data-toggle="list" href="#home" role="tab" #onclick="() => popIt(msg.MsgId)">
<span id="msgSubject1">#msg.Subject</span><br /><span class="datetimeCls" id="datetime1">#msg.DateCreated</span>
</a>
}
private bool ActiveClass { get; set; }
public void popIt(int num)
{
var text = Message[num].MessageText;
var subject = Message[num].Subject;
Subject = subject;
MessageText = text;
DateCreated = Message[num].DateCreated;
ActiveClass = true;
}
Angular 8 has the built-in "ngClass." The following code works for this scenario - does Blazor have anything similar? I also noticed that the Angular click can handle two inputs, which I haven't seen in Blazor yet:
<div *ngFor="let msg of messages; let i = index" >
<a class="list-group-item list-group-item-action" data-toggle="list" href="#home" role="tab" [ngClass]="{ 'active' : msg == activeMessage}"(click)="activeMessage = msg;popIt(i)">
<span id="msgSubject1">{{msg.Subject}}</span><br /><span class="datetimeCls" id="datetime1">{{msg.DateCreated | date : 'short' }}</span>
</a>
</div>
</div>
The below code seems to work. It creates a new variable called "ActiveMessageId" and assigns the clicked message id to that message.
#foreach (var msg in Message)
{
<a class="list-group-item list-group-item-action #(ActiveMessageId == msg.MsgId ? "active" : "")" data-toggle="list" href="#home" role="tab" #onclick="() => popIt(msg.MsgId)">
<span id="msgSubject1">#msg.Subject</span><br /><span class="datetimeCls" id="datetime1">#msg.DateCreated</span>
</a>
}
private int ActiveMessageId { get; set; }
public void popIt(int num)
{
var text = Message[num].MessageText;
var subject = Message[num].Subject;
Subject = subject;
MessageText = text;
DateCreated = Message[num].DateCreated;
ActiveMessageId = num;
}

How do you limit the number of items on show in the bootstrap btn-dropdowns

I am making a search bar with bootstrap and one of the elements is a category list. The problem is at the moment when I press the category button it shows every single category in the list at the same time - making the UI awful! How can I limit the number of concurrent options on show?
This is an example of the problem:
For example I might just want to show the first 5 options and then have a scroll bar for the rest?
This is the markup used to create what I have so far:
<form class="navbar-form" role="search" action="/search" method="get">
<div class="input-group">
<input type="text" class="form-control" placeholder="Search for an event" name="q" id="q">
<div class="input-group-btn">
<div class="btn-group" role="group">
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-expanded="false">
Dropdown
<span class="caret"></span>
</button>
<ul class="dropdown-menu" role="menu">
<% #categories.each do |category| %>
<% category.sub_categories.each do |sub_category| %>
<li><%= sub_category.name %></li>
<% end %>
<% end %>
</ul>
A fiddle we are working with to try to show the problem can be found here: http://jsfiddle.net/c7vcuLsa/2/
Why not just add an overflow to the UL?
http://jsfiddle.net/0Lmjppsz/1/
.menu-scroll {
overflow-y: scroll;
max-height: 200px;
}
Man--if you can figure out a way to keep the dropdown from closing, I have a pretty neat solution here:
It's from an old fiddle.
Here is my fiddle.
$(document).ready(function () {
var $lis = $(".dropdown-menu li").hide();
$lis.slice(0, 5).show();
var size_li = $lis.length;
var x = 5,
start = 0;
$('#next').click(function () {
if (start + x < size_li) {
$lis.slice(start, start + x).hide();
start += x;
$lis.slice(start, start + x).show();
}
});
$('#prev').click(function () {
if (start - x >= 0) {
$lis.slice(start, start + x).hide();
start -= x;
$lis.slice(start, start + x).show();
}
});
});
It scrolls through the list when you use the Prev and Next buttons, but right now the dropdown closes and I am admittedly a little too tired to play with it right now.
Not sure how good you are with js but this will solve it. Your situation is a little different because you have input-group.
http://jsfiddle.net/KyleMit/ZS4L7/

differences between css and equivalent xpath in selenium 2.0

Still new to selenium and css/xpath locators. I stumbled across a problem where CSS works but the equivalent XPath doesn't, and I'd really like to know why. I'm using Scala in the examples but it's still the normal Java Selenium2 library. I also use the FirefoxDriver
Here's the interesting part of the HTML:
...
<li class="k-item k-filter-item k-state-default k-last" role="menuitem" style="z-index: auto;">
...
<form class="k-filter-menu k-secondary">
<div>
<div class="k-filter-help-text">Show items with value that:</div>
<span class="k-widget k-dropdown k-header" style="" unselectable="on" role="listbox" aria-haspopup="true" aria-expanded="false" tabindex="0" aria-owns="" aria-disabled="false" aria-readonly="false" aria-busy="false">
<span class="k-widget k-datetimepicker k-header" style="">
<div>
<button class="k-button" type="submit">Filter</button>
<button class="k-button" type="reset">Clear</button>
</div>
</div>
</form>
</li>
...
I'm getting the li with
val filter = driver.findElement(By.cssSelector("li.k-filter-item"))
which works for me.
Then, I want to find the button. It's a dynamic menu thingy sliding out and what not, so I need to wait for it to appear:
new WebDriverWait(driver, selectorTimeout).until(
new ExpectedCondition[Boolean] {
override def apply(d: WebDriver) = {
filter.findElement(By.cssSelector("button[type=submit]")).isDisplayed
}
})
And that works nicely, too. My question is, why doesn't the xpath equivalent work:
new WebDriverWait(driver, selectorTimeout).until(
new ExpectedCondition[Boolean] {
override def apply(d: WebDriver) = {
filter.findElement(By.xpath("//button[#type='submit']")).isDisplayed
}
})
Anybody?
[EDIT]
Selenium version: 2.35.0
FireFox driver: 2.35.0
I will try it with Opera now.
You'll need the . in front of the XPath selector anyway, so that it'll search the current element's descendants/children:
.//button[#type='submit']
Sometimes, a more elaborate XPath can also help:
.//descendant::button[#type='submit']

Multiple 'foreach' loops in one knockout JS

I'll start by saying that I am working within the context of DotNetNuke7, which is essentially ASP.net based framework, and that i am fairly new to KO.
I am trying to have one ko viewmodel and have two foreach loops in it. Each loop renders an array which is part of the view model definition like so:
//We build two arrays: one for the users that are in the group
//and one for the users that are not in the group
var nonGroupMembers = $.map(initialData.NonGroupUsers, function (item) { return new Member(item); });
var groupMembers = $.map(initialData.GroupUsers, function (item) { return new Member(item); });
//The members we start with before we added new members
self.SearchTerm = ko.observable('');
self.CircleMembers = ko.observableArray(groupMembers);
self.NonCircleMembers = ko.observableArray(nonGroupMembers);
In the html context (or the asp user control) i placed the following code
<div id="socialDirectory" class="dnnForm dnnMemberDirectory">
<ul id="mdMemberList" class="mdMemberList dnnClear" style="display:none"
data-bind="foreach: { data: NonCircleMembers, afterRender: handleAfterRender },
css: { mdMemberListVisible : Visible }, visible: HasMembers()">
<li class="memberItem">
<div data-bind="visible: $parent.isEven($data)">
<%=MemberItemTemplate %>
</div>
<div data-bind="visible: !$parent.isEven($data)">
<%=MemberAlternateItemTemplate %>
</div>
</li>
</ul>
</div>
<div class="circleDirectory" id="circleDirectory" >
<ul id="cdMembersList" data-bind =" foreach: {data: CircleMembers, afterRender: handleAfterRender}">
<li class="memberItem">
<div class="mdMemberDetails">
<a href="" class="mdMemberImg" data-bind="attr: { title: DisplayName, href: ProfileUrl }">
<span><img data-bind="attr: { src: getProfilePicture(50,50), title: DisplayName }" /></span>
</a>
<ul class="MdMemberInfo">
<li class="mdDisplayName" >
<a href="" title="" class="mdMemberTitle"
data-bind="attr: { title: DisplayName, href: ProfileUrl },
event: { mouseover: $parent.showPopUp }">
<span data-bind="text: DisplayName"></span>
</a>
</li>
<li class="mdTitle"><p><span data-bind="text: Title"></span></p></li>
<li class="mdLocation"><p><span data-bind="text: Location()"></span></p></li>
</ul>
</div>
</li>
</ul>
</div>
Each one of the DIVs which contain the foreach binding loop in them works perfectly well without the other. For instance, the bottom div (id= cdMembersList) will work fine but when I add the upper div with the binding markups it will stop working. The same thing happens vise verse.
Does anybody have a clue why it might happen? Can i not have 2 loops in one view model?
looking forward to solving this mystery.
thanks,
David
Ok, I hate to say it but the answer is very simple as always. I didn't add to my view model the Visible property for
css: { mdMemberListVisible : Visible }
When I created a new script file I simply skipped this property. A few lessons:
You can run more than one loop in one view model.
Always check that you have all the properties defined in the view model.
Also, apparently it helps creating a question on this board since it makes you think clearly about the problem and revisit your actions. I had spent 2 hours chasing this problem before i posted my question, and then it took me 15 minutes to solve it after I posted it.

Resources