【问题标题】:Testing AngularJS with Protractor- test fails because element is not clickable despite waiting for elementToBeClickable...?使用量角器测试 AngularJS - 测试失败,因为元素不可点击,尽管等待 elementToBeClickable...?
【发布时间】:2017-11-13 10:13:15
【问题描述】:

我在使用 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(...) {
        ...
    }
});

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

【问题讨论】:

    标签: angularjs protractor automated-tests wait


    【解决方案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。您甚至可以检查源代码。

    更新

    鉴于您的 cmets,您在页面加载时有一个对话框弹出阻止输入。因此,您可以创建自己的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);
    })
    

    【讨论】:

    • 您从哪里获得blockingElement?控制台中的错误似乎表明它是一个对话框,此时将接收点击,但在执行测试期间没有显示对话框(即,没有执行的行导航到显示对话框的页面)。 ..
    • 错误提示:Other element would receive the click: &lt;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;"&gt;...&lt;/div&gt;,但是这些页面上没有显示对话框...
    • 刚刚意识到错误消息中显示的代码部分在我的 OP 中未正确显示-因此已重新格式化。
    • 您提供的 Protractor API 的链接正是我在编写代码时一直遵循的
    • 在另一个问题上,您在此处发布了您在此处引用的答案,显示了一个对话框 - 我还没有设法让您的答案与它一起工作,但在我解决后会重新访问这个测试的问题。正如我在这个问题中提到的,最初编写测试时,我启用了browser.pause(); 行,并且当我手动运行测试时(必须告诉脚本在每行执行时继续),它们都通过了正确。自从删除对browser.pause(); 的调用后,我才遇到这个问题 - 所以这似乎是一个时间问题......
    【解决方案2】:

    虽然我知道这不被视为“最佳实践”,但我真正能够解决此问题的唯一方法是在调用 browser.click() 之前插入 browser.sleep() 行:

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

    使用 Ernst Zwingli 在他们的回答中提供的 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();
    

    如果没有调用browser.sleep(500);,页面没有时间在页面加载时在后台打开和关闭的对话框上完成动画 - 这意味着对话框收到了点击,而不是菜单我本来打算的按钮。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-03-17
      • 1970-01-01
      • 2017-12-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-11-19
      • 1970-01-01
      相关资源
      最近更新 更多