【问题标题】: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 过期才能继续测试?

【问题讨论】:

    标签: angularjs timeout protractor


    【解决方案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);
            

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

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2017-04-27
              • 1970-01-01
              • 2014-10-15
              • 2015-11-07
              • 2014-04-08
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多