0
<div id="crm" class="row gridrow clickable ng-scope" ng-repeat="customer in customerList" ng-click="gotoRecord(customer.id)">
     <i class="col m1 s1 tiny fa fa-male"></i>
     <div class="col m3 s11 ng-binding"> Allard</div>
     <div class="col m2 s12 ng-binding"></div>
</div>

I have this snippet of HTML, it's one row displaying as a result of a search action for a Customer with nameCustomer 'Allard'. I want to click on this customer to continue to the next page but most of the time this results in a StaleElementException.

I tried it two different ways, using Protractor and without Protractor.

First way:

IWebElement elem = driver.FindElement(By.XPath("//*[contains(text(),'" + nameCustomer + "')]//parent::div[contains(@id,'crm')]"));
        ExplicitWait.WaitAndClick(driver, elem);

Second way:

var customers = driver.FindElements(NgBy.Repeater("customer in customerList"));
        foreach (var customer in customers)
        {
            if (elem.Text.Equals(nameCustomer))
            {
                elem.Click();
            }
        }
Frank
  • 831
  • 1
  • 11
  • 23
  • 1. IS there are any events that happen prior appearing of this table? i.e. table is hidden on the page or maybe it's prefilled with some other data? 2. Have you tried more precise xpath? Your xpath simply tries to find smthn that matches text on the page – Mikhail Jun 05 '17 at 18:55
  • 1.initialy the table is filled with the first 20 customers in the database, after searching they are replaced by the customers wich are fullfilling the search criteria. 2. The text i.e. the customername is the only property which uniquely identifies the tablerow. – Frank Jun 05 '17 at 19:28
  • 1. And I assume that Allard is one of those first 20 customers? 2. //div[@id='crm']/div[text()='Allard'] - or if you're 100% sure that element is unique and case above is true, then there is no need for it. – Mikhail Jun 05 '17 at 20:27
  • 1. You are right, the customer I'm looking for is on the first page and already present at the moment I'm starting the search action but it's on another position in comparison with his position after the search action. When the same test is looking for a customer not appearing at the first page it never fails (I tried more then 30 times). The answer below by @Tyler Nielsen is a perfect explanation of this behavior. – Frank Jun 06 '17 at 07:17

1 Answers1

1

The problem (I think)

With StaleReferenceExceptions, an IWebElement that was previously created is no longer attached to the DOM (you probably already know this). What's most likely happening is this:
1: You click search.
2: Selenium executes driver.FindElement(...) and finds a matching element.
3: Then the search function finishes and the DOM updates. The old IWebElement found previously is gone.
4: Then Selenium tries to click on the element (which no longer exists, causing the StaleElementException. There is an element that matches the one that was there before, but it's not the same element in Selenium's eyes.)

Your statement that this happens "most of the time" makes me suspect this is the case even more, because the exception would depend on the order of events, which would vary depending on the relative speeds of Selenium vs. the web-page.

How to resolve (if this is your problem)

You need to find something on the page that will indicate to Selenium that the search action is done. This is where the creativity of writing GUI automation scripts comes in. If there is something on the page that you know will change as a result of the load, craft an explicit wait to ensure that is complete. Maybe there is a loading bar that shows up, or a message that appears when the search is done. You could grab an element that doesn't match your search before clicking search, then do an explicit wait to make sure it disappears before going on to look for the result you do expect to be there.

It would look something like this below.

# Search action performed before this.
WebDriverWait wait= new WebDriverWait(driver, TimeSpan.FromSeconds(secondsToWait));
wait.Until(ExpectedConditions.InvisibilityOfElementLocated(By.XPath( expressionForElementThatWillDissapear )));


IWebElement elem = driver.FindElement(By.XPath("//*[contains(text(),'" + nameCustomer + "')]//parent::div[contains(@id,'crm')]"));
            ExplicitWait.WaitAndClick(driver, elem);

I'd recommend making the method to implement the explicit wait above. These situations will come up often.

Community
  • 1
  • 1
Tyler Nielsen
  • 605
  • 1
  • 7
  • 21
  • You are right, you exactly describe the reason of my problem. I solved it by a combination of waiting till a message present before starting the search function is Invisible and waiting till the search result is Visible. – Frank Jun 06 '17 at 09:14