Selenium - 从 angularjs 组件中选择一个输入

Posted

技术标签:

【中文标题】Selenium - 从 angularjs 组件中选择一个输入【英文标题】:Selenium - select an input from an angularjs component 【发布时间】:2019-04-02 07:24:13 【问题描述】:

<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>

我有一个 AngularJs 组件,其中包含 input 类型的 text。我使用以下代码输入了date。当我无头运行测试时,它大多数时候都会失败。

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

if (fromDate.getAttribute("value").length() > 0) 
    fromDate.clear();

fromDate.sendKeys(startDate);

在我填写的datepicker之前还有几个inputs。但是当测试到datepciker时,因为找不到它而失败。

如何解决这个问题?

更新

我在上面的代码之前就使用过这个方法。

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);
        
    

【问题讨论】:

【参考方案1】:

由于&lt;input&gt; 元素是Angular 元素,因此您必须诱导WebDriverWait 以使所需的元素可点击,您可以使用以下任一方法解决方案:

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);

更新

根据您的问题更新函数waitUntilVisible() 对我来说看起来像是一个纯粹的开销,您正在为presenceOfElementLocated() 实现 FluentWait 忽略 StaleElementReferenceException,这可能是通过定制的 WebDriverWait 轻松实现,ExpectedConditionselementToBeClickable()

【讨论】:

感谢您的回答。如果我省略 span.input-group.date 怎么办?每当我有一个 angularjs 元素时,我是否必须等待? WebDriverWait 对 Angular 有什么特别的影响,我相信它只有助于同步,直到元素可点击....我们是否必须做一些特别的事情来处理 Angular 元素? @mahan - Angular 对 Promise 起作用,如果 Promise 未解决,元素可能无法加载,因此 Angular 元素需要良好的同步,就像我们对 ajax 元素所做的一样。但我不确定 webdriver 等待对角度元素有多大帮助,如果您的应用程序中有更多角度元素,建议使用 ng-webdriver @mahan 正如你提到的 ...在我填写的日期选择器之前还有一些其他输入... 所以为了完全匹配 Ancestor 节点即&lt;md-datepicker&gt;Descendent 节点即&lt;span&gt; 和下一个Descendent 节点即&lt;input&gt; 在构造Locator Strategies 时被考虑。但是,如果省略 span.input-group.date 仍然唯一标识所需元素,这将是最佳方式。 @AmitJain WebDriverWait 是处理Angular 元素的唯一 方式,无论您等待元素存在/可见性/交互性,它都有助于同步。 Promise 是通过 OP 正在使用的 Selenium 的 Java 客户端 在内部处理的,因此考虑到 Promiseng-webdriver超出范围 i> 这个问题。【参考方案2】:

您可以尝试等待元素的可见性:

WebElement fromDate =
                new WebDriverWait(driver, 10)
                .until(ExpectedConditions.visibilityOfElementLocated(By.cssSelector("md-datepicker input")));

【讨论】:

【参考方案3】:

您可以尝试使用cssSelector,不建议使用xpath 执行headless

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);

【讨论】:

我有多个输入。因此,我使用md-datepicker 我在许多日期选择器上工作过,您会发现针对不同输入或日期文本分类的 css 存在差异,因此您可以区分它们我相信 selenium 不适用于任何 ng 元素,它无法识别它们稳定性。 我同意你的看法。但输入的父母没有唯一标识符。【参考方案4】:

我只能通过尝试捕获 StaleElementReferenceException 的 catch 块来解决这个问题。

   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();
    

正如我在问题中所说,我使用waitUntilVisible 方法来等待输入的存在。


我在 2018 年 10 月 29 日问了这个问题。当时我使用的是 selenium 版本3.14.0。但是这种方法选择输入也不应该使用硒版本3.141.0


2021 年 9 月 30 日更新

等到元素可点击。

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

找到它最近的唯一父代。

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

找到输入

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

【讨论】:

以上是关于Selenium - 从 angularjs 组件中选择一个输入的主要内容,如果未能解决你的问题,请参考以下文章

AngularJS,从指令迁移到组件

从控制器获取数据到组件 AngularJs

AngularJS:从列表中动态添加组件

AngularJs从组件向控制器添加方法

组件不会从父angularjs接收更新

无法从 Angularjs 组件访问 Popover 模板上的变量