我正在尝试使用量角器测试我网站上的登录页面。
如果您登录不正确,该网站会显示一条“吐司”消息,该消息会弹出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
到期?
答案 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);
我已对此进行了简化以使其更易于查看,我通常将其放在使定位器动态化的方法中。