使用Protractor测试AngularJS测试失败,因为尽管等待elementToBeClickable,但元素不可点击......?

时间:2017-11-13 10:13:16

标签: angularjs protractor automated-tests wait

我在使用Protractor为AngularJS应用程序开发的测试套件中编写其中一个测试时遇到了一个奇怪的问题。

在最初开发测试时,我正在使用browser.pause()调用来确保在运行测试脚本时我手动告诉测试执行每一行。我现在很高兴我到目前为止所写的测试正确执行了。所有通过/失败都取决于他们的标准。

我现在想要删除对browser.pause()的调用,以便脚本可以自动运行每个测试,而无需任何用户交互。但是,因为应用程序是Angular,所以这样做会导致在执行引用它们的测试步骤之前定时/等待元素加载的一些问题 - 所以我目前正在进行每个测试,并试图确保它们是允许元素在与元素交互之前加载/等待元素所需的时间等等。

当我运行我的测试脚本时,它在第二次测试失败(导航到导出页面),我写了如下:

it('should navigate to the Export page', function() {
    browser.waitForAngularEnabled(false);
    browser.wait(EC.elementToBeClickable(exportMenuBtn), 25000).then(
        browser.actions().mouseMove(exportMenuBtn).perform().then(
            exportMenuBtn.click().then(
                function() {
                    console.log("Export page test completed ");
                    expect(browser.getCurrentUrl()).toBe(VM + '/#/export');
                }
            )
        )
    );
});

我在运行脚本的命令行中收到的错误消息显示:

  

失败:未知错误:元素...在点(40,301)处无法点击。其他元素将收到点击:

<div tabindex="-1" role="dialog" class="modal fade ng-isolate-scope ult-loading modal-message ng-animate in-remove in-remove-active" ng-class="{in: animate}" ng-style="{'z-index': 1050 + index*10, display: 'block'}" ng-click="close($event)" modal-window="" window-class="ult-loading modal-message" size="sm" index="0" animate="animate" style="z-index: 1050; display: block;; transition-property: opacity;transition-duration: 0.15s;">...</div>
  

(会话信息:chrome = 62.0.3202.89)         (驱动程序信息:chromedriver = 2.33.506120(e3e53437346286c0bc2d2dc9aa4915ba81d9023f),platform = Windows NT 10.0.16299 x86_64)

这表明我的测试脚本试图在其上调用exportMenuBtn.click()元素不是'可点击的'。但这没有意义,因为在我尝试点击它之前,我已使用行browser.wait(EC.elementToBeClickable(exportMenuBtn), 25000)等待exportMenuBtn元素可点击...

我已经尝试增加等待元素可点击的时间,但这似乎没有什么区别......

有谁可以解释为什么这个测试失败了?如何确保测试在尝试单击之前等待exportMenuBtn元素可点击?

修改

所以我尝试做了@Ernst Zwingli在他们的回答中提出的建议 - 添加了一个函数来关闭我的spec.js文件的对话框:

var pageLoaded = function() {
    var blockingElement = $('div[window-class="ult-loading modal-message"');
    return EC.invisibilityOf(blockingElement);
}
module.exports = new pageLoaded();

然后将测试脚本更新为:

it('should navigate to the Export page', function() {
    browser.waitForAngularEnabled(false);
    browser.wait(pageLoaded, 5000);
    exportMenuBtn.click();
    browser.wait(pageLoaded, 5000);
    browser.wait(EC.urlIs(VM + '/#/export'), 5000);
});

但我的测试脚本仍然失败,给出了相同的错误消息:

  

失败:未知错误:元素

<a href="#/export" target="_self">...</a> 
  

在点(40,301)处无法点击。

     

其他元素会收到点击:

<div tabindex="-1" role="dialog" class="modal fade ng-isolate-scope ult-loading modal-message ng-animate in-remove in-remove-active" ng-class="{in: animate}" ng-style="{'z-index': 1050 + index*10, display: 'block'}" ng-click="close($event)" modal-window="" window-class="ult-loading modal-message" size="sm" index="0" animate="animate" style="z-index: 1050; display: block;; transition-property: opacity;transition-duration: 0.15s;">...</div>
 (Session info: chrome=62.0.3202.89)
  (Driver info: chromedriver=2.33.506120 (e3e53437346286c0bc2d2dc9aa4915ba81d9023f),platform=Windows NT 10.0.16299 x86_64)

在Dialog / service.js中定义了导致该问题的对话框:

function wait(msg, progress, title) {
    // Create a wait dialog
    var dlg = dialogs.wait(
        title,
        msg,
        progress,
        {
            // Disable keyboard ESC to close dialogue instances
            keyboard: false,

            // lock the background window
            backdrop: "static",
            size: "sm",
            windowClass: "ult-loading modal-message",
            animation: false
        }
    );

    // Push the dialog into the pool
    dlgs.push(dlg);

    // Return the created dialog
    return dlg;
}

当手动运行测试/浏览到页面时,此对话框实际上永远不可见,因此它似乎是在浏览/加载新页面时运行的某种“后台任务”。我还应该注意,此对话框是在factory

中定义的
.factory('DialogMgr', function($rootScope, $document, dialogs) {
    ...
    function wait(...) {
        ...
    }
});

我想知道这是否可能与对话框中设置的'淡入淡出'动画有关?我该如何解决这个问题?

2 个答案:

答案 0 :(得分:1)

这是for 99% the same question as the one from November 10th

my answer there如何不满足您的问题?

我添加了(虽然已注释)行browser.wait(EC.invisibilityOf(blockingElement), 5000);作为您问题的答案。

此刻,元素isClickable并不自动表示,它也在前面,没有其他对象覆盖它。它只是意味着该对象可能会收到click(),因为......

  

元素可见并启用,以便您可以单击它

Read more about here。您甚至可以检查源代码。

<强>更新

根据您的评论,您正在加载页面时,会有一个对话框弹出窗口阻止输入。因此,您可以创建自己的ExpectedConditions - 函数,该函数将一直等到阻塞对话框消失。

这是基于这个假设构建的可重用函数,该阻塞元素将出现在许多地方:

var loaded = function(){
    var EC = protractor.ExpectedConditions;
    var blockerElm = $('div[window-class="ult-loading"');
    return EC.invisibilityOf(blockerElm);
}
module.exports = new loaded();

以下是在测试用例中调用它的方法:

var loaded = require(pathToLoaded.js);
it('should navigate to the Charts page', function() {
    console.log("Start Charts page test");
    browser.waitForAngularEnabled(false);
    browser.wait(loaded, 5000);
    chartsMenuBtn.click(); //works, if the element is defined as you said
    browser.wait(EC.urlIs(site + '/#/charts'), 5000);
})

答案 1 :(得分:0)

虽然我知道它不被认为是最佳做法,但实际上能够解决此问题的唯一方法是在调用{{{}之前插入browser.sleep()行。 1}}:

browser.click()

使用Ernst Zwingli在答案中提供的it('should navigate to the Export page', function() { browser.waitForAngularEnabled(false); browser.wait(pageLoaded, 5000); browser.sleep(500); exportMenuBtn.click(); browser.wait(EC.urlIs(site + '/#/export'), 5000); }); 函数:

pageLoaded()

如果没有调用var pageLoaded = function() { var blockingElement = $('div[window-class="ult-loading modal-message"'); //console.log("blockingElement: ", blockingElement); return EC.invisibilityOf(blockingElement); } module.exports = new pageLoaded(); ,页面就没有时间在已打开的对话框上完成动画。页面加载时在后台关闭 - 意味着对话框收到了点击,而不是我想要的菜单按钮。