Тестирование содержимого временного элемента с помощью транспортира
Я пытаюсь проверить страницу входа на моем сайте с помощью транспортира.
Если вы входите в систему неправильно, на сайте отображается сообщение "тост", которое появляется в течение 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 поток управления транспортиром). Как мне обойти это и убедиться, что тест выполняется во время таймаута?
Update:
Следуя приведенному ниже предложению, я использовал browser.wait()
, чтобы ждать, пока тост станет видимым, а затем попытался запустить тест, когда обещание было разрешено. Это не сработало.
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]
Для дополнительной ясности в том, что происходит с тостером: у меня есть служба, которая по существу содержит массив сообщений. Тост-директива всегда находится на странице (шаблон - нефрит выше) и следит за сообщениями в службе тостов. Если появилось новое сообщение, он запускает следующий код:
scope.messages.push(newMessage)
# set a timeout to remove it afterwards.
$timeout(
()->
scope.messages.splice(0,1)
,
5000
)
Это подталкивает сообщение в массив сообщений в области 5 секунд, что и делает тост (через ng-show="messages.length"
).
Почему переносчик ждет, пока истечет срок тоста $timeout
, прежде чем перейти к испытаниям?
Ответы
Ответ 1
Оказывается, что это известное поведение для транспортира. Я думаю, что это будет ошибка, но на данный момент проблема закрыта.
Обходной путь заключается в использовании $interval
вместо $timeout
, установив третий аргумент в 1, чтобы он вызывался только один раз.
Ответ 2
Я взломал это, используя нижеследующий блок кода. У меня была панель уведомлений от стороннего пакета node (ng-notifications-bar), который использовал $timeout вместо $interval, но нужно было ожидать, что текст ошибки будет определенным значением. Я использую короткий сон(), чтобы появилась анимация панели уведомлений, переключилась ignoreSynchronization на true, поэтому Protractor не дождался окончания $timeout, установил my 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;
Ответ 3
you should wait for your toast displayed then do other steps
browser.wait(function() {
return $('.toaster').isDisplayed();
}, 20000);
Ответ 4
Если кому-то все еще интересно, этот код работает для меня без хаков до $timeout или $interval или Toast. Идея состоит в том, чтобы использовать promises щелчка() и wait() для включения и выключения синхронизации. Нажмите все, чтобы перейти на страницу с помощью сообщения тоста, и немедленно отключите синхронизацию, дождитесь сообщения с тостом, затем отпустите его, а затем снова включите синхронизацию (ВСТАВЬТЕ обещание).
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
Надеюсь, это может помочь тем, у кого есть проблемы с транспортиром, жасмином, angular и 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 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;