Skip to content
This repository was archived by the owner on Jul 29, 2024. It is now read-only.

Commit c892c2a

Browse files
committed
feat(protractor): implement reduce and filter for ElementArrayFinder
See #877
1 parent 1848111 commit c892c2a

File tree

2 files changed

+117
-0
lines changed

2 files changed

+117
-0
lines changed

lib/protractor.js

+86
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,92 @@ var buildElementHelper = function(ptor) {
327327
});
328328
};
329329

330+
/**
331+
* Apply a filter function to each element found using the locator. Returns
332+
* promise of a new array with all elements that pass the filter function. The
333+
* filter function receives the ElementFinder as the first argument
334+
* and the index as a second arg.
335+
*
336+
* @alias element.all(locator).filter(filterFn)
337+
* @view
338+
* <ul class="items">
339+
* <li class="one">First</li>
340+
* <li class="two">Second</li>
341+
* <li class="three">Third</li>
342+
* </ul>
343+
*
344+
* @example
345+
* element.all(by.css('.items li')).filter(function(elem, index) {
346+
* return elem.getText().then(function(text) {
347+
* return text === 'Third';
348+
* });
349+
* }).then(function(filteredElements) {
350+
* filteredElements[0].click();
351+
* });
352+
*
353+
* @param {function(ElementFinder, number): webdriver.WebElement.Promise} filterFn
354+
* Filter function that will test if an element should be returned.
355+
* filterFn should return a promise that resolves to a boolean.
356+
* @return {!webdriver.promise.Promise} A promise that resolves to an array
357+
* of ElementFinders that satisfy the filter function.
358+
*/
359+
ElementArrayFinder.prototype.filter = function(filterFn) {
360+
return this.asElementFinders_().then(function(arr) {
361+
var list = [];
362+
arr.forEach(function(elementFinder, index) {
363+
filterFn(elementFinder, index).then(function(satisfies) {
364+
if (satisfies) {
365+
list.push(elementFinder);
366+
}
367+
});
368+
});
369+
return list;
370+
});
371+
};
372+
373+
/**
374+
* Apply a reduce function against an accumulator and every element found
375+
* using the locator (from left-to-right). The reduce function has to reduce
376+
* every element into a single value (the accumulator). Returns promise of
377+
* the accumulator. The reduce function receives the accumulator, current
378+
* ElementFinder, the index, and the entire array of ElementFinders,
379+
* respectively.
380+
*
381+
* @alias element.all(locator).reduce(reduceFn)
382+
* @view
383+
* <ul class="items">
384+
* <li class="one">First</li>
385+
* <li class="two">Second</li>
386+
* <li class="three">Third</li>
387+
* </ul>
388+
*
389+
* @example
390+
* var value = element.all(by.css('.items li')).reduce(function(acc, elem) {
391+
* return elem.getText().then(function(text) {
392+
* return acc + text + ' ';
393+
* });
394+
* });
395+
*
396+
* expect(value).toEqual('First Second Third ');
397+
*
398+
* @param {function(number, ElementFinder, number, Array.<ElementFinder>)}
399+
* reduceFn Reduce function that reduces every element into a single value.
400+
* @param {*} initialValue Initial value of the accumulator.
401+
* @return {!webdriver.promise.Promise} A promise that resolves to the final
402+
* value of the accumulator.
403+
*/
404+
ElementArrayFinder.prototype.reduce = function(reduceFn, initialValue) {
405+
var valuePromise = webdriver.promise.fulfilled(initialValue);
406+
return this.asElementFinders_().then(function(arr) {
407+
arr.forEach(function(elementFinder, index) {
408+
valuePromise = valuePromise.then(function(value) {
409+
return reduceFn(value, elementFinder, index, arr);
410+
});
411+
});
412+
return valuePromise;
413+
});
414+
};
415+
330416
/**
331417
* The ElementFinder can be treated as a WebElement for most purposes, in
332418
* particular, you may perform actions (i.e. click, getText) on them as you

spec/basic/elements_spec.js

+31
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,37 @@ describe('ElementFinder', function() {
231231
expect(labels).toEqual([1, 2, 3, 4, 5, 6, 7]);
232232
});
233233

234+
it('should filter elements', function() {
235+
browser.get('index.html#/form');
236+
var count = element.all(by.css('.menu li a')).filter(function(elem) {
237+
return elem.getText().then(function(text) {
238+
return text === 'bindings';
239+
});
240+
}).then(function(filteredElements) {
241+
return filteredElements.length;
242+
});
243+
244+
expect(count).toEqual(1);
245+
});
246+
247+
it('should reduce elements', function() {
248+
browser.get('index.html#/form');
249+
var value = element.all(by.css('.menu li a')).
250+
reduce(function(currentValue, elem, index, elemArr) {
251+
return elem.getText().then(function(text) {
252+
return currentValue + index + '/' + elemArr.length + ': ' + text + '\n';
253+
});
254+
}, '');
255+
256+
expect(value).toEqual('0/7: repeater\n' +
257+
'1/7: bindings\n' +
258+
'2/7: form\n' +
259+
'3/7: async\n' +
260+
'4/7: conflict\n' +
261+
'5/7: polling\n' +
262+
'6/7: animation\n');
263+
});
264+
234265
it('should export an isPresent helper', function() {
235266
browser.get('index.html#/form');
236267

0 commit comments

Comments
 (0)