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

时间:2014-07-31 15:17:51

标签: angularjs timeout protractor

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

如果您登录不正确,该网站会显示一条“吐司”消息,该消息会弹出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测试失败(找不到元素)。它似乎在等待吐司消失,然后运行测试。

我也尝试将toastMessage测试置于then()回调之外(我认为这无论多余都是多余的),但我得到完全相同的行为。

我最好的猜测是量角器看到有一个$timeout正在运行,并在运行下一个测试之前等待它完成(ref 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指令始终在页面上(模板是上面的jade),并在toast服务中监视消息。如果有新消息,则运行以下代码:

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

这会将消息推送到示波器上的messages数组中5秒钟,这就是吐司出现的原因(通过ng-show="messages.length")。

在进行测试之前,为什么量角器等待吐司的$timeout到期?

6 个答案:

答案 0 :(得分:14)

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

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

答案 1 :(得分:6)

事实证明这是known behaviour for protractor。我认为这应该是一个错误,但目前问题已经结束。

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

答案 2 :(得分:2)

you should wait for your toast displayed then do other steps

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

答案 3 :(得分:2)

如果有人仍然感兴趣,这段代码适用于我,没有黑客超时$ timeout或$ interval或Toast。我们的想法是使用click()和wait()的promise来打开和关闭同步。点击任意进入带有toast消息的页面,然后立即关闭同步,等待toast消息,然后关闭它然后再重新开启同步(INSIDE the 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;
   })
});

答案 4 :(得分:0)

我希望这可以帮助谁在量角器,茉莉,棱角分明和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;

答案 5 :(得分:0)

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

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

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

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