Тесты E2E с несколькими страницами с информацией из браузера

Я пишу тест E2E с транспортиром. Мне нужно было получить информацию из браузера и выполнить шаг несколько раз.

Я тестирую один экран, который начнется, когда

  • Пользователь нажимает кнопку "Пуск"
  • приземляется на новой странице
  • Ниже описан рабочий процесс, когда счетчик передается как аргумент
  • id Идентификатор html не изменяется. значение изменяется при повторном запросе после отправки текущей формы.
for(i = 0 ; i < count ; i++){
   console.log("counter is "+i);
   element(by('id')).evaluate('value').then(function(v) {
    // do some action on UI based on v
    element(by('id1')).sendKeys(v+v);
    // submit etc., 
    // some angular code runs in the frontend.
   }
   // need to wait since webdriver jumps to the next one without this completing
}

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

Любые предложения оценены.

Никогда не используйте инструкции оператора протрансформатора внутри цикла: простая причина в том, что API-интерфейс webdriverJS (транспортир) является асинхронным. Операторы элементов возвращают обещание, и это обещание находится в неразрешенном состоянии, а код ниже операторов продолжает выполняться. Это приводит к непредсказуемым результатам. Следовательно, рекомендуется использовать рекурсивные функции вместо циклов.

источник: http://engineering.wingify.com/posts/angularapp-e2e-testing-with-protractor/

Изменить: обновленный вопрос с деталями рабочего процесса.

Ответ 1

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

Причина в том, что первые асинхронные вызовы выполняются после последней итерации цикла, когда i уже равен count. Таким образом, это затрудняет разрыв цикла и отслеживание значения i.

На пути решения проблемы следует использовать рекурсивную функцию:

var count = 3;
var results = [];

function iterate(i, n) {
  if(i < n) {
    console.log(`counter is ${i}`);

    browser.refresh();
    return element(by.id('h-top-questions')).getText().then(function(text) {
      results.push(`${i}:${text}`);
      return iterate(i + 1, n);
    });
  }
}

iterate(0, count).then(function(){
  console.log("done!", results);
});

Но лучшим способом было бы итератировать с promise.map в массиве размером до количества итераций:

var count = 3;

protractor.promise.map(Array(count).fill(0), function(v, i) {
  console.log(`counter is ${i}`);

  browser.refresh();
  return element(by.id('h-top-questions')).getText().then(function(text) {
    return `${i}:${text}`;
  });
}).then(function(results){
  console.log("done!", results);
});

Вы также можете использовать цикл. Сначала вам нужно будет использовать оператор let, чтобы получить значение i в асинхронной функции (ES6). Затем вызовите все синхронный код с помощью browser.call, чтобы синхронизировать выполнение:

var count = 3;
var results = [];

for(let i = 0 ; i < count ; i++){
  browser.call(function(){
    console.log(`counter is ${i}`);

    browser.refresh();
    element(by.id('h-top-questions')).getText().then(function(text) {
      results.push(`${i}:${text}`);
    });
  });
}

browser.call(function() {
  console.log("done!", results);
});

Ответ 2

Зацикливание в транспортире работает следующим образом

describe('Describe something', function() {
    var testParams = [1,2,3,4,5,6,7,8,9,10];
    beforeEach( function() {
        // ...
   });

for (var i = 0; i < testParams.length; i++) {
  (function (testSpec) {
    it('should do something', function() {
        // inside loop
    });

  })(testParams[i]);

};
});

Изменить: я мог бы неправильно понимать ваш вопрос, но мне кажется, что вы хотите выполнить все (динамические подсчеты) на странице, прежде чем перейти к следующему?

it('should clear old inspections', function() {  
                  inspectieModuleInspectieFixture.getRemoveInspectionButton().count().then(function (value) {
                        if(value == 0){
                            console.log('--- no inspections to remove ---');
                        }
                        for(var i = 0; i < value; i++){
                            //global.waitForClickable(inspectieModuleInspectieFixture.getRemoveInspectionButtonList(i+1));
                            inspectieModuleInspectieFixture.getRemoveInspectionButtonList(i+1).click();
                            console.log('iteration '+i + 'count '+value )
                        };
                    });
                    global.wait(5000);

            }); */

это подсчитывает элементы на странице, а затем выполняет действие для количества найденных элементов

В приведенном выше примере я использую контейнеры для хранения своих элементов, поэтому мой код остается читаемым (т.е. inspectieModuleInspectieFixture.getRemoveInspectionButton() содержит $( ". elementSelectorExample" )

Также есть комментарий "global.waitForClickable", который добавляется к "модулю времени", который я создал, который расширяет функциональность "wait", в этом случае он ждет, пока элемент станет допустимым/кликабельным.

Это легко отражается, возможно, примерно так:

    waitForElementNoDisplay: function(element){
    return browser.wait(function() {
        return element.isDisplayed().then(function(present) {
            return !present;
        })
    });
},

это заставит protractor WAIT пока элемент больше не будет отображаться. (Дисплей: нет)

Ответ 3

Если вам нужно выполнить какое-либо действие для каждого элемента, это правда, что лучше не использовать циклы. Используйте .map() или .each() или .filter() вместо

Все еще не совсем уверен, что вам делать, но вот пример того, как я делаю подобные задачи, когда вам нужно сделать количество действий в зависимости от данных со страницы:

class SomePage {

    typeValueForEachElement(elements) {
        elements.each((elem, index)=> {
            elem.getAttribute('value').then(value=> {
                elem.sendKeys(value + value)
                elem.submit()
            })
        })

    }
}

new SomePage().typeValueForEachElement($$('your locator here'))

Вот ссылка на api, которая может помочь

http://www.protractortest.org/#/api?view=ElementArrayFinder.prototype.map http://www.protractortest.org/#/api?view=ElementArrayFinder.prototype.reduce http://www.protractortest.org/#/api?view=ElementArrayFinder.prototype.each http://www.protractortest.org/#/api?view=ElementArrayFinder.prototype.filter