Значение индекса изменяется после getText() внутри цикла for - PROTRACTOR
В приведенном ниже коде я использую повторитель для получения значений из ng-repeat и получаю столбец по cat.name, который дает мне массив всех имен cat. Так что я делаю цикл for, чтобы добраться до определенного имени, скажем Мяу, я хочу сохранить значение индекса, чтобы я мог проверить, равна ли соответствующая строка возрасту кошки 10. 2 console.log Индекс внутри цикла for приводит к различным значениям, первое находится в диапазоне от 0 до 10 (учитывая длину массива как 10), а следующее всегда приводит к 10. Он записывает "10" 10 раз. Теперь я не могу получить индекс для проверки соответствующей строки, поскольку мне всегда 10. Может ли кто-нибудь исправить меня, если я ошибаюсь, а также ответить мне измененным кодом. Я думаю, что я ошибаюсь в цепочке обещаний
element(by.repeater(cat in cats).column(cat.name)).then(function(fields) {
for (var i in fields) { // promise returns an array fields
console.log(i); // values range from 0 to length of the fields(say 10)
fields[i].getText().then(function(fieldValue) {
console.log(i); // values are always 10 (i.e the length of the array)
if(fieldValue === 'Meow') {
var catAge= element(by.repeater('cat in cats').row(i)).element(by.model('cat.age')); // row(i) always gives the last value
expect(catAge.getAttribute('value')).toBe('10');
}
})
}
});
1 ответ
Проблема возникает из фрагмента ниже, первый console.log() выполняется синхронно, но второй console.log() будет выполняться асинхронно, потому что он внутри getText().then()
, как мы знаем, все Protractor API является асинхронным и вернуть обещание.
for (var i in fields) {
console.log(i);
fields[i].getText().then(function(fieldValue) {
console.log(i);
Таким образом, фактическая процедура исполнения выше for
Цикл должен быть таким:
when i=1
first console.log() execute done, it print out 1
fields[i].getText() execute done, it return a Promise,
and push the promise into Protractor control flow,
actually getText() not to read the text from page,
because it's execute async.
when i=2
first console.log() execute done, it print out 2
fields[i].getText() execute done, it return a Promise,
and push the promise into Protractor control flow,
actually getText() not to read the text from page,
because it's execute async.
....
when i=10
the loop end,
you get 1, 2 .. 9, 10 printed out
Protractor control flow get a promise queue
Now Protractor control flow start to execute the promise queue
Protractor push out the fist promise in queue
Important!!! the i=10 now
fields[i].getText() will be fields[10].getText()
so you will get fields[10].getText() for 10 times
Вариант 1) использовать закрытие javascript, как сказано в комментарии Jamines, небольшие изменения в вашем текущем коде
element(by.repeater(cat in cats).column(cat.name)).then(function(fields) {
for (var i in fields) {
console.log(i);
(function(index){ // change part
fields[index].getText().then(function(fieldValue) {
console.log(index);
if(fieldValue === 'Meow') {
var catAge= element(by.repeater('cat in cats').row(index)).element(by.model('cat.age'));
expect(catAge.getAttribute('value')).toBe('10');
}
})
})(i); // change part
}
});
Вариант 2 использования транспортира filter()
element(by.repeater(cat in cats).column(cat.name)).then(function(fields) {
for (var i in fields) {
console.log(i);
var matchedIndex = -1;
fields
.filter(function(item, index){
if( matchedIndex > -1) {
// means had found the matched one, directly return false to
// skip read text from page to reduce exection time
return false;
}
else {
return item.getText().then(function(name){
if(name === 'Meow') {
matchedIndex = index;
return true;
}
return false;
})
}
})
.then(function(){
console.log('matchedIndex: ' + matchedIndex);
var catAge= element(by.repeater('cat in cats').row(matchedIndex)).element(by.model('cat.age'));
return expect(catAge.getAttribute('value')).toBe('10');
});
}
});