Protractor: Избегайте ожидания $ timeout () s?

Созданный на 16 окт. 2013  ·  59Комментарии  ·  Источник: angular/protractor

В нашем приложении мы отображаем "всплывающие" сообщения для нашего пользователя при нажатии определенных кнопок. Эти всплывающие сообщения видны только около 20 секунд (после чего $ timeout удаляет их).

Проблема в том, что Protractor, кажется, ждет, пока не завершится $ timeout, прежде чем обрабатывать .findElement () и .expect (). Однако, когда тайм-аут $ истек, сообщение пропадает, и элемент не найден.

Любые предложения о том, как обойти / решить это? (Нам _ нужно_ утверждать, что пользователю отображается правильное «всплывающее» сообщение)

благодаря

question

Самый полезный комментарий

Планирует ли команда Angular вновь открыть эту проблему, потому что с ней сталкивается все больше и больше людей. Это не профессионально, просто притвориться, что нет никаких проблем, потому что вы можете заменить $ timeout на $ interval, потому что код не должен быть написан для соответствия тестам - тесты должны быть написаны в тестовом коде.

Все 59 Комментарий

Вы можете использовать ptor.ignoreSynchronization , но это может вызвать нестабильность в другом месте вашего теста. В качестве альтернативы, в Angular 1.2rc3 теперь есть служба interval , которую Protractor не будет ждать (см. Https://github.com/angular/angular.js/commit/2b5ce84fca7b41fca24707e163ec6af84bc12e83). Вы можете установить интервал, который будет повторяться 1 раз.

Привет, @drhumlen , у нас была такая же проблема, и мы пришли к выводу, что всплывающее окно определенно должно пройти модульное тестирование. Итак, мы смоделировали нашу систему уведомлений и ожидаем, что макет был вызван (в конечном итоге с некоторой важной информацией). Мы также протестировали уведомление, чтобы убедиться, что ничего не сломается.
Надеюсь, что это поможет.

$interval сделали свое дело.

  $interval(function() {
    // ...
  }, duration, 1);

Теперь он отлично работает. Но мы должны быть очень добросовестными и убедиться, что всплывающее окно удаляется каким-либо другим способом после каждого теста (в нашем случае, нажав кнопку «x»):

  afterEach(function() {
    removeLogMessagesFromDOM();
  });

@mackwic : я полагаю, что можно протестировать его с помощью модульного теста для многих приложений, но в нашем случае сообщения об ошибках генерируются серверной частью, поэтому мы должны тестировать их в e2e.

Спасибо @juliemr

Кажется странным, что транспортир ждет $timeout s. Большую часть времени пользователю придется бороться с этим?

Также столкнулся с этой проблемой и решил ее с помощью $interval . С небольшим поворотом вы можете использовать его как $timeout .

Я хотел протестировать метод сохранения и убедиться, что после сохранения отображается уведомление об успешном выполнении. Это уведомление было скрыто службой $ timeout. Транспортир ждал таймаута $ и проверял, отображается ли уведомление после того, как оно уже было скрыто.

До:

$timeout(function() {
    //hide notification
}, 3000);

После:

var intervalCanceller = $interval(function() {
    //hide notification
    $interval.cancel(intervalCanceller);
}, 3000);

Это не считается актуальной проблемой? Я сам столкнулся с этим. Я действительно не вижу, что использование $interval вместо $timeout - это решение, а скорее обходной путь.

Я также не могу заставить это работать, даже если я перейду на $interval с $timeout .

У меня есть тост, который исчезает по истечении времени ожидания. Это код, который я использую для его тестирования:

username = element(select.model("user.username"))
password = element(select.model("user.password"))
loginButton = $('button[type=\'submit\']')
toast = $('.toaster')
# Check the toaster isn't displayed initially
expect(toast.isDisplayed()).toBe(false)  # This passes
username.sendKeys("redders")
password.sendKeys("wrongpassword")
loginButton.click()
toastMessage = $('toast-message')
# Check the toaster is now displayed
expect(toast.isDisplayed()).toBe(true)   # This passes
expect(toastMessage.getText()).toBe("Invalid password")  # This fails

Соответствующая разметка здесь (нефрит)

.toaster(ng-show="messages.length")
  .toast-message(ng-repeat="message in messages") {{message.body}}

Ошибка в том, что toastMessage = $('toast-message') не соответствует ни одному элементу. Это не потому, что он вызывается в начале, когда .toast-message не существует?

Эта проблема уже исправлена? Потому что использование интервала вместо тайм-аута - это больше, чем решение.

У меня такая же проблема и browser.ignoreSynchronization = true; не помогает. Я могу заменить код на $ interval, но тесты e2e должны проверять реальный код, а не принятый для них код.

@ vytautas-pranskunas- похоже, что у вас другая проблема, если ignoreSynchronization не работает. Если вы можете привести воспроизводимый пример, откройте новую проблему.

Я выяснил, когда ignoreSynchronization не работает:
У меня есть служба, которая отображает сообщения об ошибках. Одноразовое сообщение остается на экране в течение пяти секунд после исчезновения. Этот процесс контролируется $ timeout.

У меня два сценария - в первом ignoreSynchronization работает нормально, во втором - нет

1) Я знаю, что сообщения должны появляться сразу после того, как пользователь нажимает кнопку, и я делаю expec(message).tobe(...) в этом случае ignoreSynchronization выполняет свою работу.

2) Я знаю, что сообщение может появиться через любое время (зависит от времени ответа сервера), и моя дальнейшая логика должна выполняться только после того, как оно появится, поэтому в этом случае я должен использовать browser.wait(by.(....).isPresent(), n) здесь ignoreSynchronization перестает работать, и поток следует (я покажу вывод browser.wait console.log во время ожидания)

waits
waits
waits (element appears)
browser waits freezes for 5 seconds
after element disappears it continue
wait
wait
wait

Итак, как вы можете видеть, он никогда не видит присутствующий элемент, потому что ожидание зависает при контакте с $ timeout

У меня действительно не так много времени, чтобы сделать это воспроизводимым в плункере, но если вы попытаетесь, это будет здорово, потому что этот сценарий должен быть довольно распространенным.

Thnaks

Ваш журнал действительно не очень полезен без контекста кода, который его создал. Не могли бы вы поделиться этим?

Я не могу поделиться своим кодом из-за конфиденциальности, но я могу показать псевдокод, чтобы понять
(не писать html-код, потому что есть кнопка и ng-repeat для отображения сообщений)

constructor(){
$scope.$on('globalMessageAdded', () => {
            $scope.globalMessages = this.getMessages();
        });
}

callServer(){
//interval here acts like server delay
   $interval(()=> {
     $rootScope.messages.push('test messages');
      this.$rootScope.$broadcast('globalMessageAdded');
   }, 3000, 1);
}

getMessages = () => {
        var newMessages = <ISpaGlobalMessage[]>[];
        _.forEach(this.$rootScope.globalMessages, item => {
            newMessages.push(item);
//because of this timeout browser.wait stops waitng for 5 seconds
            this.$timeout(() => {
                this.remove(item);
                this.$rootScope.$broadcast('globalMessageRemoved');
            }, 5000);
        });

        return newMessages;
    }

и транспортир

element('button').click();
browser.ignoreSynchronization = true; //tried with and without it
browser.wait(() => element(by.css('.alert-success')).isPresent(), 10000000);
browser.ignoreSynchronization = false;

other code that should be executed after successful message

browser.wait никогда не находит этот элемент, потому что, пока элемент присутствует, browse.wait не запускает проверки.

Есть ли прогресс или мысли по этому поводу?

Почему "browser.ignoreSynchronization = false;" не может быть установлен сразу после browser.wait ()?

Мой код

        var el = element(by.css('.popup-title.ng-binding'));
        browser.ignoreSynchronization = true; 
        browser.wait(function (){
            return el.isPresent();
        }, 10000);
        var popupMsg = el.getText();
        expect(popupMsg).toEqual('something...');
        browser.ignoreSynchronization = false; // not work, if take out this statement, it works again

Меня укусила та же проблема. Модуль тостов, который мы используем, является общим компонентом для npm, поэтому мы не можем легко изменить его на использование $ interval. Я также не понимаю, почему транспортир должен ждать одного, а не другого. В идеале это можно было бы настраивать самостоятельно, поэтому это не влияет на другие ожидания синхронизации.

У нас есть эта проблема в angular -ui / bootstrap # 3982, и мы решили не менять ее на $ interval, поэтому мы пришли сюда, чтобы узнать, есть ли что-нибудь, что можно сделать, чтобы исправить проблему.

Большое спасибо.

На этой неделе я столкнулся с этой проблемой, используя компонент всплывающих уведомлений.

Использование $interval вместо $timeout не является правдоподобным решением. И browser.ignoreSynchronization = true у меня тоже не работает.

Какие-нибудь мысли или идеи?

Планирует ли команда Angular вновь открыть эту проблему, потому что с ней сталкивается все больше и больше людей. Это не профессионально, просто притвориться, что нет никаких проблем, потому что вы можете заменить $ timeout на $ interval, потому что код не должен быть написан для соответствия тестам - тесты должны быть написаны в тестовом коде.

+1 Нам тоже нужен фикс.

Согласовано. Тест должен быть исправлен. +1

+1, тоже столкнулся с этой проблемой. Может быть, должен быть способ сказать, что тайм-ауты транспортира не следует ждать.

ignoreSynchronisation у меня не работает, я думаю, что игнорирует слишком много вещей. Нам нужен способ игнорировать синхронизацию тайм-аутов или иметь более сложный API, например browser.waitForTimeouts(); .

+1. нужно исправить. почему мы должны использовать уродливые обходные пути с интервалом.

+1

+1, мне очень сложно обрабатывать нестандартные тесты .. нужно исправить это

+1 согласен с @ vytautas-pranskunas - нам не следует требовать изменения кода, который мы хотим протестировать, только для того, чтобы сам тест работал ....

+1

+1

+1

+1

+1

+1

+1

+1

+1

+1

Я думаю, что многие люди могут неправильно использовать browser.ignoreSynchronization = true . Пожалуйста, поправьте меня, если я ошибаюсь, но мне, если вы напишете:

browser.ignoreSynchronization = true;
expect( elem.getText() ).toEqual( "foo" );
browser.ignoreSynchronization = false;

Тогда это не сработает: это все еще чистый JavaScript, и три строки будут читаться синхронно. Таким образом, к тому времени, когда getText и expect разрешатся, синхронизация уже будет включена. Вы можете обойти это, дождавшись, пока транспортир разрешит последнее обещание и вернет синхронизацию в свой обратный вызов then .

Один из способов, которым команда Protractor может помочь людям, может заключаться в предоставлении доступа к методам, которые влияют на синхронизацию только после завершения буфера обещаний. Например:

browser.setIgnoreSynchronization( true );
expect( elem.getText() ).toEqual( "foo" );
browser.setIgnoreSynchronization( false );

В любом случае, я также столкнулся со многими проблемами из-за того, что Protractor ожидал таймаутов $: в больших приложениях слишком много побочных эффектов, будь то всплывающие подсказки, модальные окна, анимация и т. Д. Мы решили глобально отключить синхронизацию с помощью browser.ignoreSynchronization = true; и напишите тесты так, как пользователь тестирует продукт: дождитесь, пока элемент станет видимым, если вы хотите щелкнуть по нему, и т. Д. Напишите себе несколько вспомогательных методов, и все станет проще простого. У нас это работает очень хорошо, и тесты запускаются намного быстрее.

+1 с использованием $ interval - это взлом, а не решение

+1 эта ошибка причинила мне столько страданий. Давайте начнем диалог о том, как это исправить. У меня есть несколько десятков модулей bower и npm angular, которые используют $ timeout в сотнях (!) Мест, и я использую его в своем коде 45 раз. Изменение моего кода возможно (и разумно), однако изменение стороннего кода совершенно исключено с логистической точки зрения: связываться с вышестоящими поставщиками и пытаться мотивировать их или локально разветвлять сторонние модули angular локально и поддерживать параллельный поток патчей - это кошмарный сценарий неприемлемо, если ошибка связана с Protractor и $ timeout, а не с внешними модулями.

Пожалуйста, давайте вместе поработаем над этим.

+1

Эта ошибка создает проблемы для тестирования e2e для приведенного ниже примера сценария,

1) Заполните поля и отправьте форму, которая будет транслировать несколько событий, например
* ссылка на сообщение о состоянии, которая прикреплена с таймаутом $ и видна всего несколько секунд
* новое сообщение добавляется в список сообщений в другом виджете
2) Затем, когда вы нажимаете ссылку сообщения о состоянии, оно должно открывать всплывающее сообщение с подробностями.

Поэтому, когда мы пытаемся автоматизировать сценарий, мы можем отправить форму. Но когда мы пытаемся щелкнуть ссылку сообщения о состоянии, нам нужно, чтобы browser.ignoreSynchronization = true; потому что ссылка привязана к $ timeout.

Ссылка активна, но всплывающее сообщение не работает.

Когда я проверил Stackoverflow и некоторые ответы Google, я узнал, что это из-за тайм-аута $. Итак, я проверил, удалив $ timeout для элемента, теперь он работает правильно.

Поэтому я прошу каждый раз, когда у нас не может быть элемента без тайм-аута $. Поэтому, пожалуйста, рассмотрите приведенный выше сценарий и используйте новую функцию, в которой транспортир может игнорировать ожидание тайм-аута $.

Столкнувшись с той же проблемой, пришлось использовать window.setTimeout и window.clearTimeout, чтобы избежать ее. Не очень угловатый способ и может мешать дайджесту, но $ interval и игнорирование синхронизации - не решение

Столкнувшись с той же проблемой, ответит ли на это кто-нибудь из команды транспортиров?

+1

Это сводило меня с ума (здесь Protractor noob), пока я не пришел к грязному решению, как показано ниже:

beforeEach(function () { browser.ignoreSynchronization = true; });
afterEach(function () { browser.restart(); });

Работает нормально и не слишком медленно в безголовом браузере.

+1

Также здесь возникает проблема. Мои тесты проходили гладко, пока я не посетил страницу, которая делает несколько запросов API и открывает некоторые веб-сокеты. В этот момент Транспортир каждый раз выходит из строя. Я не нашел хорошего решения.

Мне любопытно @floribon о ваших предлагаемых вспомогательных методах, если вы отключите browser.ignoreSynchronization = true; глобально. Прямо сейчас я использую объекты страницы, которые обычно выглядят так:

describe('something', function() {
  it('should do....', function() {
    var fooPage = new FooPage();
    fooPage.visit();
    var barPage = fooPage.clickCreate();  // does some API call, then redirects to barPage
    // at this point, enough things are happening that it is impossible to interact with barPage.
    // Protractor locks up.
    // barPage makes other API call & opens WebSocket
    barPage.clickBaz();  // nope.  will timeout every time.
  });
});

@benjaminapetersen, мои помощники выглядят так:

btn1.click();         // Triggers many things and eventually displays page2
wait.forURL('page2'); // wait for page2 to be loaded before moving on
$('.btn1').click();   // this does some async job and eventually displays btn2
wait.forElement('.btn2');
$('.btn2').click();

Например, возврат wait.forElement

browser.wait(() => elem.isPresent().then(p => p, () => {}), timeout, 'Not found');
// browser.wait(function() { return elem.isPresent().then(function(p) { return p;} , function() {}); }, timeout, 'Not found');

Идея состоит в том, чтобы имитировать реальное поведение пользователя: я нажимаю на элемент, жду, пока не появится другой, затем нажимаю на него и т. Д. Таким образом не нужно ждать тайм-аута $, это быстрее и безопаснее.

Ключ здесь - игнорировать ошибки внутреннего обещания и дать ему еще несколько попыток, пока не истечет время ожидания.

PS: Вы также можете использовать ExpectedConditions для этого, но у меня было несколько проблем с ними, когда angular перестраивал узел DOM (например, ng-if conditions):

browser.wait(browser.ExpectedConditions.precenseOf(elem), timeout, 'Not found');

@floribon классно , спасибо!

@juliemr может помочь мне, пожалуйста, я поймаю сообщения об ошибках тоста в транспортире. Пожалуйста, ответьте на мой почтовый идентификатор anjali. [email protected]

@juliemr Использование службы $ interval для вызова длительных HTTP-запросов не работает. Транспортир все еще получает время ожидания.
Вот мой угловой код
this. $ interval (() => this. $ http.get(this.prefix (url), config), 0, 1)
транспортир все еще ожидает завершения задачи $ http.
Я использую транспортир 5.x и angualrjs 1.6x.
Не могли бы вы помочь мне?

+1, что-то нужно добавить, чтобы справиться с этим.

+1

+1

+1

+1

+1

+1

Эта спецификация сработала для меня. Большое спасибо! и приветствуют @floribon за намек на асинхронность выполнения.

    // screenshot-spec.js

    // a close button that appears on the md-toast template
    const closeToastButton = $('[data-automation="toast-close"]')
    const cond = protractor.ExpectedConditions
    function waitToastShow() {
        return browser.wait(cond.elementToBeClickable(closeToastButton), 5000)
    }
    function waitToastHide() {
        return browser.wait(cond.invisibilityOf(closeToastButton), 5000)
    }

    screenshot = name => browser.takeScreenshot().then(/* save fn */)

    describe('a suite ... ', () => {
        it('takes screenshots of an md-toast once shown and after hidden', function () {
            // ... actions that launch an md-toast using $mdToast.show({ ... })
            browser.ignoreSynchronization = true
            waitToastShow().then(() => {
                screenshot('toast-showing.png')
                waitToastHide().then(() => {
                    screenshot('toast-hidden.png')
                    browser.ignoreSynchronization = false;
                })
            })
        });
    }

ребята, как я могу добавить тайм-аут для определенного теста
`
it ('1 - нужно войти в систему с кодом регистрации, отправленным на электронную почту', function (done) {

// setTimeout(function () {
            flow.execute(browser.params.getLastEmail)
                .then(function (email) {
                    expect(email.subject)
                        .toEqual('[email protected] submitted feedback');
                    expect(email.headers.to)
                        .toEqual('[email protected]');
                    expect(email.html.includes('User feedback details: accountId: 12345, related To: dashboard, description: ' + D.feedbackMsg + ''))
                        .toEqual(true);
                    console.log(email.html);
                    // done();
                });


    });`
Была ли эта страница полезной?
0 / 5 - 0 рейтинги