3

Hopefully someone can help on this issue...

I keep receiving the above error message (see title) when interacting with cascading drop-downs. The only rudimentary fix I have successfully employed is "Thread.Sleep"... See code extract below:

Note I am passing the following parameters:

attribute: ID

attrval: e.g. ID123456 (ID of the drop-down)

parameter: Car (drop-down value we are wanting to select)

IWebElement element = findMyElement(attribute, attrval);
SelectElement selectElement = new SelectElement(element);
selectElement.SelectByText(parameter);
// dirty code - needs to be re-written
Thread.Sleep(500);
if (new SelectElement(findMyElement(attribute, attrval)).SelectedOption.Text.Equals(parameter))
{
    return "pass";
}

Note2: findMyElement is a custom method (here is an extract):

public static IWebElement findMyElement(string attribute, string attrval)
        {
            WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(30));
            switch (attribute.ToLower())
            {
                case "id":
                    wait.Until(ExpectedConditions.ElementExists(By.Id(attrval)));
                    wait.Until(ExpectedConditions.ElementIsVisible(By.Id(attrval)));
                    return driver.FindElement(By.Id(attrval));

As I have stated in my code comments is there anyway I can avoid using Thread.Sleep as I am aware that this is not a recommended approach.

Thanks in advance :)

Saifur
  • 16,081
  • 6
  • 49
  • 73
David
  • 58
  • 1
  • 6
  • What line throws the exception? What occurs in your application when selecting the dropdown? Does the page change at all? Any new elements added? Any new requests sent off? – Arran Dec 09 '14 at 12:09
  • Exception is thrown here (without thread.sleep) if (new SelectElement(findMyElement(attribute, attrval)).SelectedOption.Text.Equals(parameter)) – David Dec 09 '14 at 12:14
  • Regarding the page itself as it is a cascading drop-down the selectable options within the drop-down will update e.g. if I select car in the first drop-down the second will ask the colour. Note in the background the page does a post back when a value in the first drop-down has been selected – David Dec 09 '14 at 12:34
  • Can you provide your html if possible and are you navigating back and forth with different actions before performing this action? – Saifur Dec 09 '14 at 13:36
  • On the front end aspx the following call is made when the first drop-down value has been selected: OnDropDownSelectedIndexChanged="..._DropDownSelectedIndexChanged" this in turn locates the second drop-down and rebinds the data – David Dec 09 '14 at 13:43
  • This exception mostly happens when `DOM` is refreshed somehow. I usually solve the issue with finding the element on the fly. Some ajax call also may refresh the `DOM` hard to say – Saifur Dec 09 '14 at 13:45
  • Note: no other actions have been carried out on this page – David Dec 09 '14 at 13:45
  • I would suggest you to do same action without using your custom `findMyElement()` function and observe what happens – Saifur Dec 09 '14 at 13:46
  • So only replace this line: if (new SelectElement(findMyElement(attribute, attrval)).SelectedOption.Text.Equals(parameter)) with: if (new SelectElement(driver.FindElement(By.Id(attrval))).SelectedOption.Text.Equals(parameter)) ??? – David Dec 09 '14 at 13:57
  • No both `findelements()` before and inside the `if` – Saifur Dec 09 '14 at 14:00
  • Type explicitly. something like `By byId = By.id("Your ID") IWebElement element = driver.findElement(byId) SelectElement selectElement = new SelectElement(element); selectElement.SelectByText("My Text"); // dirty code - needs to be re-written Thread.Sleep(500); if (new SelectElement(driver.findElement(byId)).SelectedOption.Text.Equals("My second Text")) { return "pass"; }` – Saifur Dec 09 '14 at 14:04
  • Code used: WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(30)); wait.Until(ExpectedConditions.ElementExists(By.Id(attrval))); wait.Until(ExpectedConditions.ElementIsVisible(By.Id(attrval))); IWebElement element = driver.FindElement(By.Id(attrval)); SelectElement selectElement = new SelectElement(element); selectElement.SelectByText(parameter); – David Dec 09 '14 at 14:10
  • wait.Until(ExpectedConditions.ElementExists(By.Id(attrval))); wait.Until(ExpectedConditions.ElementIsVisible(By.Id(attrval))); if (new SelectElement(driver.FindElement(By.Id(attrval))).SelectedOption.Text.Equals(parameter)) { return "pass"; } – David Dec 09 '14 at 14:10
  • Still doesn't work - i.e. same error message – David Dec 09 '14 at 14:11
  • Cant say anything without seeing html – Saifur Dec 09 '14 at 14:12
  • Here is the asp.net markup: – David Dec 09 '14 at 14:32
  • Here is the HTML (via UI) – David Dec 09 '14 at 14:37
  • Please Select – David Dec 09 '14 at 14:38
  • To be honest with you there is no perfect solution to this problem. At least not that I know. We have almost similar situation. Only resolution I know is trap that exception and wait until the element is present. See [this](http://stackoverflow.com/questions/4846454/selenium-webdriver-staleelementreferenceexception) for a possible solution – Saifur Dec 09 '14 at 14:50

1 Answers1

1

It looks as if the first drop-down element gets unloaded from the DOM when the postback starts and reloads to the DOM once the postback finishes.

The StaleElementReferenceException is thrown when your code is trying to touch this element during the postback. The Sleep() call works by halting your code from touching this element for a period that coincidentally is long enough for the postback to complete.

The ideal solution is to identify when the postback has completed.

What predicate will only return true once the the postback is complete? Replace the Sleep() with a wait for that predicate to return true.

e.g. If the second drop-down only appears on the postback, replace Thread.Sleep(500); with:

Func<IWebDriver, bool> predicate = (x) =>
{
    try
    {
        IWebElement elementThatOnlyAppearsOnPostback = findMyElement(attribute, attrval);
        return true;
    }
    catch (NoSuchElementException)
    {
        return false;
    }
};
WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(30)); // or whatever timeout you want to set
wait.Until(predicate);

If the second drop-down is already there, but only becomes populated with options on the postback, switch the predicate to:

Func<IWebDriver, bool> predicate = (x) =>
{
    SelectElement secondDropDown = new SelectElement(findMyElement(attribute, attrval));
    return (secondDropDown.Options.Count > 0);
}
John O.
  • 708
  • 5
  • 9