I have to click a certain button on a page. However, when I retrieve all of the elements that have a particular class name. All of the retrieved elements throw a stale reference exception when I try to perform each one or click. I can not double click on any of them. It find's the right elements but throws the exception for all of them. The commented out code is where I actually am trying to select and click the appropriate button. I attached a picture of the form. Note that the pages are changed each time a button is clicked or performed. The Select Upload BOM button is what you need to pay particular attention to.
Website
// Switch to correct frame
IWebElement editorFrame = driver.FindElement(By.ClassName("frame-banner"));
driver.SwitchTo().Frame(editorFrame);
var action = new OpenQA.Selenium.Interactions.Actions(driver);
// Select Project File
IList<IWebElement> projectFileButtonList= driver.FindElements(By.ClassName("data-cell"));
foreach (var button in projectFileButtonList)
{
if (button.Text == "BOM_scrub")
{
// Found Project File now select it
action.DoubleClick(button);
action.Perform();
break;
}
}
// Select Upload BOM Button
IList<IWebElement> uploadBomBtn = driver.FindElements(By.ClassName("se-custom-main-button"));
foreach (var element in uploadBomBtn )
{
try
{
action.DoubleClick(element);
action.Perform();
}
catch
{
}
/*
if (element.Text == "Upload BOM")
{
int i = 0;
while (i == 0)
{
try
{
action.DoubleClick(element);
action.Perform();
break;
}
catch
{
}
}
}
*/
}
Don't use driver.findElement(-s) with dynamic components.
StaleElementReferenceException occurs, as you're trying to perform an action against element, which has already been detached from DOM.
You have to use explicit waits mechanism (a combination of WebDriverWait + ExpectedConditions), which automatically refreshes element's state, and returns its valid representation, when specified condition is met.
Related
So, i am trying to use Selenium to scrape this webpage that updates every couple seconds. I am looking to click on the most recent link which, in this case, is the link by Edward Researcher. This list will update multiple times and get updated with new links stacking at the top.
My current code basically takes the top most link of the entire page when I want it to take links that are shown on the page starting from the top
allLinks = driver.FindElements(By.XPath("/html/body/div[6]//a")).ToHashSet();
This will get all the clickable links going top down using a tags to grab the links. My problem is trying to only get links from one place on the webpage and grab that link everytime it updates. Not really sure how to do that since the code and xPath changes with every added link. Any help would be greatly appreciated
The bottom most mark in red is the current link that I inspected that is topmost but when the page gets updated with new Links the two top red marks are where they will be updated in another DIV.
The Code that I have a t the moment works to grab the first link on the page but I would prefer to start at the Logged hits section as shown in the first image. Here is the code that makes it work:
public static void searchAllLinks()
{
//HitForker is labelled as '0'
//PandaCrazy tab is labellled as '1'
//Start index at 1
int listIndex = 1;
//Different numbers need for the first run down the list
bool firstRun = true;
//HashSet to store all IWebElements found on page at runtime
HashSet<IWebElement> allLinks;
//Get browser tabs open at current time
var browserTabs = driver.WindowHandles;
//Switch to HF
driver.SwitchTo().Window(driver.WindowHandles[0]);
//Grab links from page starting at Logged Hits Most recent Div
allLinks = driver.FindElements(By.XPath("/html/body/div[6]//a")).ToHashSet();
//Loop through all links in hash
//Hashset contains every link on the page. Only need the middle link to access the content
foreach(IWebElement value in allLinks)
{
if(firstRun == true)
{
//Second link in the hash
if(listIndex == 2)
{
value.Click();
firstRun = false;
listIndex = 0;
whatToClick(value);
}
}
//When linkIndex is 5 then click the value and reset to 0
if(listIndex == 5)
{
value.Click();
whatToClick(value);
listIndex = 0;
}
listIndex++;
}
}
//Method to find which webpage opened on successfull click
public static void whatToClick(IWebElement currentLink)
{
//Grabs the browser handles open
var browserTabs = driver.WindowHandles;
//When the link is clicked on switch to that tab
driver.SwitchTo().Window(driver.WindowHandles[2]);
//2 options
//Hit is not available then add to PC
//Hit is available then accept
try
{
if (driver.FindElement(By.XPath("/html/body/div[3]/div[2]/div/div/div/div[2]/p/span/span/button[2]")).Displayed == true)
{
driver.FindElement(By.XPath("/html/body/div[3]/div[2]/div/div/div/div[2]/p/span/span/button[2]")).Click();
driver.Close();
}
else
{
driver.Close();
driver.SwitchTo().Window(driver.WindowHandles[0]);
}
//Switch Back to the HF
driver.SwitchTo().Window(driver.WindowHandles[0]);
//Catch exception to catch if the hit cannot be accepted
} catch (OpenQA.Selenium.NoSuchElementException e )
{
if (driver.FindElement(By.XPath("/html/body/div[2]/div[1]/nav/div/div[1]/div/div[3]/span/span/button")).Displayed == true)
{
driver.FindElement(By.XPath("/html/body/div[2]/div[1]/nav/div/div[1]/div/div[3]/span/span/button")).Click();
driver.Close();
}
driver.SwitchTo().Window(driver.WindowHandles[0]);
}
catch (OpenQA.Selenium.NoSuchWindowException w)
{
Console.WriteLine("Window Not open");
}
}
Please click the element using relative xpath as follows
driver.FindElement(By.Xpath("//div[#id='log_table']/div[0]/div/div[1]/span[1]//a")).Click()
The above xpath will focus on the first of the table and accesses the hyperlink of that.
I have a Responsive website where it contains 36 buttons .At first glance we can only able to see 12 buttons and I need to validate their text values which I know how to do that .But problem is I need to click next button until it is not visible. So how can I do it with selenium in c#.
Also while clicking next we need to store text for the button in a list or array so i can add all the lists in a new list and do comparison.
Look at below code which i tried
while (respRightNav.Displayed)
{
IList<IWebElement> sportsBtn = Driver.FindElements(By.CssSelector(".sports-buttons-responsive>ul li[class='sport-button']"));
foreach (var item in sportsBtn)
{
string btnText = item.Text.Replace(System.Environment.NewLine, "");
if (btnText == btnText.ToUpper())
{
Console.WriteLine("Sports Button : " + btnText + " is In Upper Case");
}
else
{
Assert.Fail("Sports Button Text is not Upper Case for : " + btnText);
}
}
respRightNav.Click();
if (respRightNav.Displayed.Equals(false))
{
break;
}
Now above is working but i'm stuck at when Next button is not available I still need to run above foreach loop for getting text ( if (respRightNav.Displayed.Equals(false))). means i need to run Foreach loop
Any help would be appreciated.
You could check the ".Displayed" value with a while loop:
IWebElement button;
while((button = driver.FindElementByXPath("//input[.='Next']")).Displayed) {
button.Click();
...
}
Or with FindElementsByXPath in a single call matching the visible element:
ReadOnlyCollection<IWebElement> buttons;
while((buttons = driver.FindElementsByXPath("//input[.='Next' and not(contains(#class,'none'))]")).Count > 0) {
buttons[0].Click();
...
}
Ok want to write a testmonkey for our web-application. This monkey should log in and randomly start entering values and clicking webelements.
So far, I have been able to log in, find the webelements (filter them) and then select one at random and Click it. I run into the StaleElementReferenceException after a random number of clicks though. All the solutions for this I was able to find suggest I wait for the element, or I find it again. Unfortunately I do not have a locator or path for the element, I only have the element itself.
My code looks like this:
public void Start()
{
List<IWebElement> elements = _seleniumAdapter.AllElementsOnPage();
Debug.WriteLine("Elementcount: " + elements.Count);
IWebElement element = elements[_random.Next(elements.Count)];
Debug.WriteLine(element.TagName + "," + element.Text);
element.Click();
if (element.GetAttribute("type").Contains("text"))
{
Debug.WriteLine("text!");
element.SendKeys(randomLetter());
}
Start();
}
Is there any way I can get around the StaleElementReference? AllElementsOnPage finds the elements By.CssSelector("*") and then filters the list before returning it, so that I only have clickable elements left.
Elements in our app do not have an id most of the time, so I can not use that. Nor have I been able to find any attribute that I can use as a unique selector.
I am stumped.
Store the index which _random.Next has generated for you in a variable. For Example i.
Then make changes in your code to catch the StaleElementReferenceException. In Catch block you can again populate your list and find the element with the same index which _random.Next has generated for you.
Try to click it again in the catch block. If it is still cannot be clicked , catch it again in the second catch block and log the attributes of this element and move on to next elements. Afterwards you can inspect that what was actually wrong with this element. See the the changed code below.
public void Start()
{
List<IWebElement> elements = _seleniumAdapter.AllElementsOnPage();
Debug.WriteLine("Elementcount: " + elements.Count);
int i = _random.Next(elements.Count);
IWebElement element = elements[i];
Debug.WriteLine(element.TagName + "," + element.Text);
try
{
element.Click();
}
catch(StaleElementReferenceException ex)
{
elements = _seleniumAdapter.AllElementsOnPage();
element = elements[i];
element.Click()
}
catch(Exception ex)
{
Debug.WriteLine(ex.Message);
element = null;
}
if(element != null)
{
if (element.GetAttribute("type").Contains("text"))
{
Debug.WriteLine("text!");
element.SendKeys(randomLetter());
}
Start();
}
}
This approach is performance intensive, but it will get things done . I hope.
I am just finishing a test script and accessing a fairly dynamic page. The page in question, has an element appear (generally a radio button or tick-box), which is only present if certain criteria in previous pages are met. So, my test will be accessing this page irrelevant of previous criteria and I want to hit the "continue" element at the bottom of the page whilst handling these elements "IF" they appear. I have a few method sto click elements by ID, and so far have the following code:
// Selects the "Confirm" button
IWebElement radioOption = mWebDriver.FindElement(By.Id("Radio_Button_Id"));
if (radioOption.Displayed)
{
this.ClickElementById("Radio_Button_Id");
// Clicks CONTINUE
this.ClickElementById("CONTINUE");
}
else
{
// Selects CONTINUE
this.ClickElementById("CONTINUE");
}
I am trying in this code to handle that if the radio button appears, select it then select the continue button. Also, if the radio button does not appear, ignore it and select the continue button. Any help with this would be much appreciated.
Try something like this:
//Displayed
public static bool IsElementDisplayed(this IWebDriver driver, By element)
{
IReadOnlyCollection<IWebElement> elements = driver.FindElements(element);
if (elements.Count > 0)
{
return elements.ElementAt(0).Displayed;
}
return false;
}
//Enabled
public static bool IsElementEnabled(this IWebDriver driver, By element)
{
IReadOnlyCollection<IWebElement> elements = driver.FindElements(element);
if (elements.Count > 0)
{
return elements.ElementAt(0).Enabled;
}
return false;
}
You'll not get any exception and then the test can continue.
You said that you were getting NoSuchElementExceptions. radioOption.Displayed tests to see if the element is visible on the page, but it will throw an error if the element doesn't even exist. (An element can be present, but invisible)
To test to see if an element is present, you need to do mWebDriver.FindElements (note the S). This will return a List<WebElement> of all of the elements that match your selector, and if it can't find any, it will return a list of size 0 (and not throw an error).
That way, your if statement will be if (radioOptions.size()!=0), and will check to see if the element exists (not if its visible).
I've also used this as a way to test if the element is present and get a handle on the element if it is present:
namespace SeleniumExtensions
{
public static class WebDriverExtensions
{
public static bool TryFindElement(this IWebDriver driver, By by, out IWebElement element)
{
try
{
element = driver.FindElement(by);
return true;
}
catch (NoSuchElementException)
{
element = null;
return false;
}
}
public static bool IsElementEnabled(this IWebDriver driver, By by)
{
IWebElement element = null;
if (driver.TryFindElement(by, out element))
{
return element.Enabled;
}
return false;
}
}
}
This allows for code like:
using SeleniumExtensions;
// ...
IWebElement element = null;
if (driver.TryFindElement(By.Id("item-01"), out element)
{
// use element
}
else
{
// element is null
}
Or:
if (driver.IsElementEnabled(By.Id("item-01"))
{
// item is enabled
}
I need some help because I keep getting a StaleElementReference when I try to parse a list of a tags to click.
What I have done is on page land I iterate through the page and generate an object List<> with with all the a tags
private List<IWebElement> _pageLinks;
public List<IWebElement> pageLinks
{
get
{
if (_pageLinks == null)
{
_pageLinks = InfoDriver.FindElements(By.TagName("a")).ToList();
}
return _pageLinks;
}
}
Then I want to parse this list, and click each one and then go back to the page it was referenced from.
private static SeleniumInformation si = new SeleniumInformation(ffDriver);
si.pageLinks.ForEach(i =>
{
i.Click();
System.Threading.Thread.Sleep(1000);
ffDriver.Navigate().Back();
});
What happens is that after the first click it goes to the new page and then goes back to the starting page but it can't get the next link. I've tried setting it to a static element, setting a backing field so that it checks to see if there is data there already however it appears that on click the IwebElement looses the list and it doesn't rebuild the list either so I get a StaleElementReference exception not handled and element not found in cache.
Is this a bug in Selenium with the IWebElement class or am I doing something wrong? Any help would be greatly appreciated.
This is the expected behavior. You left the page the element was on. When you navigated back, it is a new page and that element is no longer on it.
To work around this I would suggest passing around Bys instead, if you can. Assuming your anchorlinks all have unique hrefs, you could instead generate a list as follows (java code, but should translate to c#):
private static List<By> getLinks(WebDriver driver)
{
List<By> anchorLinkBys = new ArrayList<By>();
List<WebElement> elements = driver.findElements(By.tagName("a"));
for(WebElement e : elements)
{
anchorLinkBys.add(By.cssSelector("a[href=\"" + e.getAttribute("href") + "\"]"));
//could also use another attribute such as id.
}
return anchorLinkBys;
}
I don't know the makeup of your page so I don't know if it is possible to generate By's dynamically that uniquely identify the elements you want. For example if all the elements have the same parent, you could use the css level 3 selector nth-child(n). Hopefully you get some ideas from the above code.
private void YourTest()
{
IWebDriver browserDriver = new FirefoxDriver();
browserDriver.Navigate().GoToUrl(pageUrl);
int linkCount= browserDriver.FindElements(By.TagName("a")).Count;
for (int i = 0; i <= linkCount-1; i++ )
{
List<IWebElement> linksToClick = browserDriver.FindElements(By.TagName("a")).ToList();
linksToClick[i].Click();
System.Threading.Thread.Sleep(4000);
if(some boolean check)
{
//Do something here for validation
}
browserDriver.Navigate().Back();
}
broswerDriver.Quit();
}