1

<md-datepicker ng-model="mc.date.from" required="" md-val="">
  <span class="input-group date" style="width:144px">
    <input size="16" type="text"
           class="form-control"
           autocomplete="off">
    <span class="input-group-btn">
    <button class="btn btn-default" tabindex="-1" >
      <i class="glyphicon glyphicon-calendar"></i>
    </button>
    </span>
  </span>
</md-datepicker>

I have an AngularJs component that contains an input of type text. I have used the following code to enter a date. It fails most of the times when I run the test headless.

WebElement fromDate = driver.findElement(
    By.tagName("md-datepicker"))
    .findElement(By.tagName("input"));

if (fromDate.getAttribute("value").length() > 0) {
    fromDate.clear();
}
fromDate.sendKeys(startDate);

There are a few other inputs before the datepicker that I fill in. But when the test reaches datepciker, it fails because it can not find it.

How can to fix this issue?

Update

I have used this method right before the above code.

public static void waitUntilVisible(By locator) {
    final long startTime = System.currentTimeMillis();
    final Duration duration = Duration.ofSeconds(2);
    Wait<WebDriver> wait = new FluentWait<WebDriver>(driver)
        .pollingEvery(duration)
        .ignoring(StaleElementReferenceException.class);

    while ((System.currentTimeMillis() - startTime) < 91000) {
        try {
            wait.until(ExpectedConditions.presenceOfElementLocated(locator));
            break;
        } catch (StaleElementReferenceException e) {
            log.info("", e);
        }
    }
}
mahan
  • 12,366
  • 5
  • 48
  • 83

4 Answers4

2

As the <input> element is an Angular element so you have to induce WebDriverWait for the desired element to be clickable and you can use either of the following solutions:

  • cssSelector:

    WebElement elem = new WebDriverWait(driver, 20).until(ExpectedConditions.elementToBeClickable(By.cssSelector("md-datepicker[ng-model$='from']>span.input-group.date>input.form-control")));
    elem.click();
    elem.clear();
    elem.sendKeys(startDate);
    
  • xpath:

    WebElement elem = new WebDriverWait(driver, 20).until(ExpectedConditions.elementToBeClickable(By.xpath("//md-datepicker[contains(@ng-model,'from')]/span[@class='input-group date']/input[@class='form-control']")));
    elem.click();
    elem.clear();
    elem.sendKeys(startDate);
    

Update

As per your question update the function waitUntilVisible() looks like a pure overhead to me where you are implementing FluentWait for presenceOfElementLocated() ignoring StaleElementReferenceException, which could have been easily implemented through the tailor made WebDriverWait with ExpectedConditions as elementToBeClickable().

undetected Selenium
  • 183,867
  • 41
  • 278
  • 352
  • Thanks for the answer. What if I omit `span.input-group.date` ? Should I have to wait whenever I have an angularjs element? – mahan Oct 29 '18 at 11:25
  • What is the impact of WebDriverWait on Angular anything special, I believe it only helps in synchronization, till the element is clickable .... Do we have to do anything special to handle angular elements here ? – Amit Jain Oct 29 '18 at 11:26
  • @mahan - Angular works on promises, if promise is not resolved elements may not load, so a good synchronization is required for angular elements just like we do for ajax elements. But I am not sure how good webdriver waits will help for angular elements, if you have more angular elements in your application usage of `ng-webdriver ` is recommended – Amit Jain Oct 29 '18 at 11:28
  • @mahan As you mentioned _...there are a few other inputs before the datepicker that I fill in..._ so for an exact match the _Ancestor_ node i.e. ``, _Descendent_ node i.e. `` and next _Descendent_ node i.e. `` was considered while constructing the [Locator Strategies](https://stackoverflow.com/questions/48369043/official-locator-strategies-for-the-webdriver/48376890#48376890). However if omitting `span.input-group.date` still identifies the desired element uniquely that would be the most optimum way. – undetected Selenium Oct 29 '18 at 11:53
  • 1
    @AmitJain _WebDriverWait_ is the **sole** way to handle [Angular](https://angular.io/) elements which helps in synchronization whether you wait for element presence/visibility/interactability. Promises are handled internally through _Selenium's Java client_ which OP is using, so considering _Promise_ and _ng-webdriver_ is _out of scope_ for this question. – undetected Selenium Oct 29 '18 at 11:59
  • @DebanjanB I have updated the code. I do wait for the element. – mahan Oct 30 '18 at 08:06
  • @DebanjanB You just marked an another question of mine as duplicate of this. I have explained that this approach does not work but again you marked it dup. Did you read the question? – mahan Nov 06 '18 at 12:03
  • @mahan Once you raise a question before raising the same question again you need to work on the well researched answers provided by the contributors and bring the discussion to a _Closure_ (accepting an answer incase your question is solved) or else ask for more help. Raising the same question again and again doesn't helps the community. – undetected Selenium Nov 06 '18 at 12:07
0

You can try wait visibility of element:

WebElement fromDate =
                new WebDriverWait(driver, 10)
                .until(ExpectedConditions.visibilityOfElementLocated(By.cssSelector("md-datepicker input")));
Sers
  • 12,047
  • 2
  • 12
  • 31
0

You can try using cssSelector, using xpath is not recommended for headless execution

WebElement fromDate = driver.findElement(
    By.cssSelector(".input-group.date"))
    .findElement(By.cssSelector(".form-control"));
if (fromDate.getAttribute("value").length() > 0) {
    fromDate.clear();
}
fromDate.sendKeys(startDate);
Amit Jain
  • 4,389
  • 2
  • 18
  • 21
  • I have more than one input. Therefore, I am using `md-datepicker` – mahan Oct 29 '18 at 11:28
  • I have worked on many datepickers, you will find difference in css classed for different input or date texts, so you can differentiate themI believe selenium does not works with any ng elements, it will not recognize them with stability. – Amit Jain Oct 29 '18 at 11:32
  • I agree with you. But the input's parents do not have a unique identifier. – mahan Oct 29 '18 at 11:34
0

I could only solve this problem with a try a catch block where I catch StaleElementReferenceException.

   WebElement input;
    try {
        input = driver.findElement(
            By.tagName("md-datepicker"))
            .findElement(By.tagName("input"));

    } catch(StaleElementReferenceException e) {
        input = driver.findElement(By.xpath("//md-datepicker/span/input"));
    }

    if (input.getAttribute("value").length() > 0) {
        input.clear();
    }

As I have stated in the question, I use waitUntilVisible method to wait for the presence of the input.


I asked this question on 29th October 2018. At the time, I was using selenium version 3.14.0. But this approach selects the input neither should you use selenium version 3.141.0.


Update 30 september 2021

Wait until element to be clickable.

    public WebElement getElementWhenClickable(By selector) {
        return new WebDriverWait(driver, 60)
                .ignoring(StaleElementReferenceException.class)
                .until(ExpectedConditions.elementToBeClickable(selector));
    }

Find its nearest unique parent.

WebElement parent = getElementWhenClickable(By.cssSelector("#parent"))

Find the input

WebElement input = parent.findElement(By.cssSelector("md-datepicker[ng-model=\"mc.date.from\"] input"))

mahan
  • 12,366
  • 5
  • 48
  • 83