使用量角器测试临时元素的内容

Posted

技术标签:

【中文标题】使用量角器测试临时元素的内容【英文标题】:Testing the contents of a temporary element with protractor 【发布时间】:2014-09-23 15:00:23 【问题描述】:

我正在尝试使用量角器测试我网站上的登录页面。

如果您登录不正确,该站点会显示一条“toast”消息,该消息会弹出 5 秒钟,然后消失(使用 $timeout)。

我正在使用以下测试:

  describe('[login]', ()->
    it('should show a toast with an error if the password is wrong', ()->

      username = element(select.model("user.username"))
      password = element(select.model("user.password"))
      loginButton = $('button[type=\'submit\']')
      toast = $('.toaster')

      # Verify that the toast isn't visible yet
      expect(toast.isDisplayed()).toBe(false)

      username.sendKeys("admin")
      password.sendKeys("wrongpassword")
      loginButton.click().then(()->
        # Verify that toast appears and contains an error
        toastMessage = $('.toast-message')
        expect(toast.isDisplayed()).toBe(true)
        expect(toastMessage.getText()).toBe("Invalid password")
      )
    )
  )

相关标记(玉)如下:

.toaster(ng-show="messages.length")
  .toast-message(ng-repeat="message in messages") message.body

问题是toastMessage 测试失败(它找不到元素)。它似乎在等待 toast 消失并然后运行测试。

我也尝试将toastMessage 测试放在then() 回调之外(我认为这几乎是多余的),但我得到了完全相同的行为。

我最好的猜测是量角器看到有一个 $timeout 正在运行,并在运行下一个测试之前等待它完成(参考 protractor control flow)。我将如何解决这个问题并确保测试在 超时期间运行?

更新:

按照下面的建议,我使用browser.wait() 等待 toast 可见,然后在 promise 解决后尝试运行测试。没用。

console.log "clicking button"
loginButton.click()

browser.wait((()-> toast.isDisplayed()),20000, "never visible").then(()->
  console.log "looking for message"
  toastMessage = $('.toaster')
  expect(toastMessage.getText()).toBe("Invalid password")
)

console.log 语句让我看看发生了什么。这是一系列事件,[] 是我在浏览器中看到的。

clicking button
[toast appears]
[5 sec pass]
[toast disappears]
looking for message
[test fails]

为了更清楚地了解烤面包机的情况:我有一个服务,它基本上包含一系列消息。 toast 指令始终在页面上(模板是上面的玉),并监视 toast 服务中的消息。如果有新消息,它会运行以下代码:

scope.messages.push(newMessage)
# set a timeout to remove it afterwards.
$timeout(
()-> 
  scope.messages.splice(0,1) 
, 
  5000
)

这会将消息推送到作用域上的消息数组中 5 秒,这就是让 toast 出现的原因(通过ng-show="messages.length")。

为什么量角器要等 toast 的 $timeout 过期才能继续测试?

【问题讨论】:

【参考方案1】:

我使用下面的代码块解决了这个问题。我有一个来自第 3 方节点包 (ng-notifications-bar) 的通知栏,它使用 $timeout 而不是 $interval,但需要期望错误文本是某个值。我使用了一个简短的 sleep() 来允许出现通知栏动画,将 ignoreSynchronization 切换为 true,这样 Protractor 就不会等待 $timeout 结束,设置我的 expect(),然后将 ignoreSynchronization 切换回 false,这样 Protractor 就可以以常规的 AngularJS 节奏继续测试。我知道睡眠并不理想,但它们很短。

browser.sleep(500);
browser.ignoreSynchronization = true;
expect(page.notification.getText()).toContain('The card was declined.');
browser.sleep(500);
browser.ignoreSynchronization = false;

【讨论】:

【参考方案2】:

原来这是known behaviour for protractor。我认为这应该是一个错误,但目前问题已关闭。

解决方法是使用$interval 而不是$timeout,将第三个参数设置为1,这样它只会被调用一次。

【讨论】:

【参考方案3】:
you should wait for your toast displayed then do other steps

browser.wait(function() 
    return $('.toaster').isDisplayed();
, 20000);

【讨论】:

抱歉,您介意把这个更清楚吗?我应该把测试放在哪里来检查消息? 我阅读了browser.wait 的文档并弄清楚了这一点,但这对结果没有影响。请参阅上面的更新。【参考方案4】:

如果有人仍然感兴趣,这段代码对我有用,无需修改 $timeout 或 $interval 或 Toast。这个想法是使用 click() 和 wait() 的承诺来打开和关闭同步。单击任何内容以到达带有 toast 消息的页面,然后立即关闭同步,等待 toast 消息,然后将其关闭,然后重新打开同步(在 promise 内)。

element(by.id('createFoo')).click().then(function () 
    browser.wait(EC.stalenessOf(element(by.id('createFoo'))), TIMEOUT);
    browser.ignoreSynchronization = true;
    browser.wait(EC.visibilityOf(element(by.id('toastClose'))), TIMEOUT).then(function () 
      element(by.id('toastClose')).click();
      browser.ignoreSynchronization = false;
   )
);

【讨论】:

【参考方案5】:

我希望这可以帮助那些在量角器、茉莉花、角度和 ngToast 方面遇到问题的人。

我创建了一个 CommonPage 来处理每个页面中的 Toast,而无需重复代码。 例如:

var CommonPage = require('./pages/common-page');
var commonPage = new CommonPage();
decribe('Test toast', function()
    it('should add new product', function () 
            browser.setLocation("/products/new").then(function () 

                element(by.model("product.name")).sendKeys("Some name");    
                var btnSave = element(by.css("div.head a.btn-save"));
                browser.wait(EC.elementToBeClickable(btnSave, 5000));
                btnSave.click().then(function () 

                    // this function use a callback to notify
                    // me when Toast appears
                    commonPage.successAlert(function (toast) 
                        expect(toast.isDisplayed()).toBe(true);
                    );

                );
            );
        )
);

这是我的 CommonPage:

var _toastAlert = function (type, cb) 
    var toast = null;

    switch (type) 
        case "success":
            toast = $('ul.ng-toast__list div.alert-success');
            break;
        case "danger":
            toast = $('ul.ng-toast__list div.alert-danger');
            break;
    

    if (!toast) 
        throw new Error("Unable to determine the correct toast's type");
    

    browser.ignoreSynchronization = true;
    browser.sleep(500);
    browser.wait(EC.presenceOf(toast), 10000).then(function () 
        cb(toast);
        toast.click();
        browser.ignoreSynchronization = false;
    )




var CommonPage = function ()     
    this.successAlert = function (cb) 
        _toastAlert("success", cb);
    ;
    this.dangerAlert = function(cb) 
        _toastAlert("danger", cb);
    


module.exports = CommonPage;

【讨论】:

【参考方案6】:

Chris-Traynor 的回答对我有用,但我有更新。

ignoreSynchronization 现在已弃用。 对于那些使用角度和量角器来测试的人来说,下面的内容对我来说很好。

$(locators.button).click();
await browser.waitForAngularEnabled(false);
const isDisplayed = await $(locators.notification).isPresent();
await browser.waitForAngularEnabled(true);
expect(isDisplayed).toEqual(true);

我已对此进行了简化以使其更易于查看,我通常会将其放在一个方法中以使定位器动态化。

【讨论】:

以上是关于使用量角器测试临时元素的内容的主要内容,如果未能解决你的问题,请参考以下文章

量角器超时等待存在的元素

量角器无法通过绑定$ ctrl找到元素

量角器获取 DOM 元素并使用它们的属性

在量角器中单击if元素是否可单击

删除 NG-repeat 量角器中的记录

如何获取量角器中根 div 元素内存在的子 div 元素的数量