From c892c2a1a773cc24cc6565efe2db892776143104 Mon Sep 17 00:00:00 2001 From: Hank Duan Date: Thu, 19 Jun 2014 10:11:33 -0700 Subject: [PATCH] feat(protractor): implement reduce and filter for ElementArrayFinder See https://github.com/angular/protractor/issues/877 --- lib/protractor.js | 86 +++++++++++++++++++++++++++++++++++++ spec/basic/elements_spec.js | 31 +++++++++++++ 2 files changed, 117 insertions(+) diff --git a/lib/protractor.js b/lib/protractor.js index f6f745563..2ce52faa9 100644 --- a/lib/protractor.js +++ b/lib/protractor.js @@ -327,6 +327,92 @@ var buildElementHelper = function(ptor) { }); }; + /** + * Apply a filter function to each element found using the locator. Returns + * promise of a new array with all elements that pass the filter function. The + * filter function receives the ElementFinder as the first argument + * and the index as a second arg. + * + * @alias element.all(locator).filter(filterFn) + * @view + * + * + * @example + * element.all(by.css('.items li')).filter(function(elem, index) { + * return elem.getText().then(function(text) { + * return text === 'Third'; + * }); + * }).then(function(filteredElements) { + * filteredElements[0].click(); + * }); + * + * @param {function(ElementFinder, number): webdriver.WebElement.Promise} filterFn + * Filter function that will test if an element should be returned. + * filterFn should return a promise that resolves to a boolean. + * @return {!webdriver.promise.Promise} A promise that resolves to an array + * of ElementFinders that satisfy the filter function. + */ + ElementArrayFinder.prototype.filter = function(filterFn) { + return this.asElementFinders_().then(function(arr) { + var list = []; + arr.forEach(function(elementFinder, index) { + filterFn(elementFinder, index).then(function(satisfies) { + if (satisfies) { + list.push(elementFinder); + } + }); + }); + return list; + }); + }; + + /** + * Apply a reduce function against an accumulator and every element found + * using the locator (from left-to-right). The reduce function has to reduce + * every element into a single value (the accumulator). Returns promise of + * the accumulator. The reduce function receives the accumulator, current + * ElementFinder, the index, and the entire array of ElementFinders, + * respectively. + * + * @alias element.all(locator).reduce(reduceFn) + * @view + * + * + * @example + * var value = element.all(by.css('.items li')).reduce(function(acc, elem) { + * return elem.getText().then(function(text) { + * return acc + text + ' '; + * }); + * }); + * + * expect(value).toEqual('First Second Third '); + * + * @param {function(number, ElementFinder, number, Array.)} + * reduceFn Reduce function that reduces every element into a single value. + * @param {*} initialValue Initial value of the accumulator. + * @return {!webdriver.promise.Promise} A promise that resolves to the final + * value of the accumulator. + */ + ElementArrayFinder.prototype.reduce = function(reduceFn, initialValue) { + var valuePromise = webdriver.promise.fulfilled(initialValue); + return this.asElementFinders_().then(function(arr) { + arr.forEach(function(elementFinder, index) { + valuePromise = valuePromise.then(function(value) { + return reduceFn(value, elementFinder, index, arr); + }); + }); + return valuePromise; + }); + }; + /** * The ElementFinder can be treated as a WebElement for most purposes, in * particular, you may perform actions (i.e. click, getText) on them as you diff --git a/spec/basic/elements_spec.js b/spec/basic/elements_spec.js index 1341181ee..9808b3469 100644 --- a/spec/basic/elements_spec.js +++ b/spec/basic/elements_spec.js @@ -231,6 +231,37 @@ describe('ElementFinder', function() { expect(labels).toEqual([1, 2, 3, 4, 5, 6, 7]); }); + it('should filter elements', function() { + browser.get('index.html#/form'); + var count = element.all(by.css('.menu li a')).filter(function(elem) { + return elem.getText().then(function(text) { + return text === 'bindings'; + }); + }).then(function(filteredElements) { + return filteredElements.length; + }); + + expect(count).toEqual(1); + }); + + it('should reduce elements', function() { + browser.get('index.html#/form'); + var value = element.all(by.css('.menu li a')). + reduce(function(currentValue, elem, index, elemArr) { + return elem.getText().then(function(text) { + return currentValue + index + '/' + elemArr.length + ': ' + text + '\n'; + }); + }, ''); + + expect(value).toEqual('0/7: repeater\n' + + '1/7: bindings\n' + + '2/7: form\n' + + '3/7: async\n' + + '4/7: conflict\n' + + '5/7: polling\n' + + '6/7: animation\n'); + }); + it('should export an isPresent helper', function() { browser.get('index.html#/form');