使用量角器测试临时元素的内容
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);
我已对此进行了简化以使其更易于查看,我通常会将其放在一个方法中以使定位器动态化。
【讨论】:
以上是关于使用量角器测试临时元素的内容的主要内容,如果未能解决你的问题,请参考以下文章