From 6a73a25c61a72ef991a604eadae010c90a157266 Mon Sep 17 00:00:00 2001 From: Julie Date: Sun, 22 Dec 2013 16:41:17 -0800 Subject: [PATCH] feat(by.repeat) by.repeat support for multi ng-repeat Make by.repeat (and its column and row friends) work with ng-repeat-start and ng-repeat-end elements. Closes #366. Closes #182. --- lib/clientsidescripts.js | 148 ++++++++++++++++++++++++++++---- spec/basic/findelements_spec.js | 57 ++++++++++-- testapp/repeater/repeater.html | 7 ++ 3 files changed, 187 insertions(+), 25 deletions(-) diff --git a/lib/clientsidescripts.js b/lib/clientsidescripts.js index 1de79c0ca..647b51cda 100644 --- a/lib/clientsidescripts.js +++ b/lib/clientsidescripts.js @@ -53,23 +53,24 @@ clientSideScripts.findBindings = function() { }; /** - * Find an array of elements matching a row within an ng-repeat. Always returns - * an array of only one element. + * Find an array of elements matching a row within an ng-repeat. + * Always returns an array of only one element for plain old ng-repeat. + * Returns an array of all the elements in one segment for ng-repeat-start. * * arguments[0] {Element} The scope of the search. * arguments[1] {string} The text of the repeater, e.g. 'cat in cats'. * arguments[2] {number} The row index. * - * @return {Array.} An array of a single element, the row of the - * repeater. + * @return {Array.} The row of the repeater, or an array of elements + * in the first row in the case of ng-repeat-start. */ clientSideScripts.findRepeaterRows = function() { var using = arguments[0] || document; var repeater = arguments[1]; var index = arguments[2]; - var rows = []; var prefixes = ['ng-', 'ng_', 'data-ng-', 'x-ng-', 'ng\\:']; + var rows = []; for (var p = 0; p < prefixes.length; ++p) { var attr = prefixes[p] + 'repeat'; var repeatElems = using.querySelectorAll('[' + attr + ']'); @@ -80,7 +81,29 @@ clientSideScripts.findBindings = function() { } } } - return [rows[index]]; + // multiRows is an array of arrays, where each inner array contains + // one row of elements. + var multiRows = []; + for (var p = 0; p < prefixes.length; ++p) { + var attr = prefixes[p] + 'repeat-start'; + var repeatElems = using.querySelectorAll('[' + attr + ']'); + attr = attr.replace(/\\/g, ''); + for (var i = 0; i < repeatElems.length; ++i) { + if (repeatElems[i].getAttribute(attr).indexOf(repeater) != -1) { + var elem = repeatElems[i]; + var row = []; + while (elem.nodeType != 8 || + elem.nodeValue.indexOf(repeater) == -1) { + if (elem.nodeType == 1) { + row.push(elem); + } + elem = elem.nextSibling; + } + multiRows.push(row); + } + } + } + return [rows[index]].concat(multiRows[index]); }; /** @@ -107,6 +130,22 @@ clientSideScripts.findBindings = function() { } } } + for (var p = 0; p < prefixes.length; ++p) { + var attr = prefixes[p] + 'repeat-start'; + var repeatElems = using.querySelectorAll('[' + attr + ']'); + attr = attr.replace(/\\/g, ''); + for (var i = 0; i < repeatElems.length; ++i) { + if (repeatElems[i].getAttribute(attr).indexOf(repeater) != -1) { + var elem = repeatElems[i]; + var row = []; + while (elem.nodeType != 8 || + elem.nodeValue.indexOf(repeater) == -1) { + rows.push(elem); + elem = elem.nextSibling; + } + } + } + } return rows; }; @@ -139,14 +178,51 @@ clientSideScripts.findRepeaterElement = function() { } } } + // multiRows is an array of arrays, where each inner array contains + // one row of elements. + var multiRows = []; + for (var p = 0; p < prefixes.length; ++p) { + var attr = prefixes[p] + 'repeat-start'; + var repeatElems = using.querySelectorAll('[' + attr + ']'); + attr = attr.replace(/\\/g, ''); + for (var i = 0; i < repeatElems.length; ++i) { + if (repeatElems[i].getAttribute(attr).indexOf(repeater) != -1) { + var elem = repeatElems[i]; + var row = []; + while (elem.nodeType != 8 || + (elem.nodeValue && elem.nodeValue.indexOf(repeater) == -1)) { + if (elem.nodeType == 1) { + row.push(elem); + } + elem = elem.nextSibling; + } + multiRows.push(row); + } + } + } var row = rows[index]; + var multiRow = multiRows[index]; var bindings = []; - if (row.className.indexOf('ng-binding') != -1) { - bindings.push(row); + if (row) { + if (row.className.indexOf('ng-binding') != -1) { + bindings.push(row); + } + var childBindings = row.getElementsByClassName('ng-binding'); + for (var i = 0; i < childBindings.length; ++i) { + bindings.push(childBindings[i]); + } } - var childBindings = row.getElementsByClassName('ng-binding'); - for (var i = 0; i < childBindings.length; ++i) { - bindings.push(childBindings[i]); + if (multiRow) { + for (var i = 0; i < multiRow.length; ++i) { + rowElem = multiRow[i]; + if (rowElem.className.indexOf('ng-binding') != -1) { + bindings.push(rowElem); + } + var childBindings = rowElem.getElementsByClassName('ng-binding'); + for (var j = 0; j < childBindings.length; ++j) { + bindings.push(childBindings[j]); + } + } } for (var i = 0; i < bindings.length; ++i) { var dataBinding = angular.element(bindings[i]).data('$binding'); @@ -187,8 +263,30 @@ clientSideScripts.findRepeaterColumn = function() { } } } + // multiRows is an array of arrays, where each inner array contains + // one row of elements. + var multiRows = []; + for (var p = 0; p < prefixes.length; ++p) { + var attr = prefixes[p] + 'repeat-start'; + var repeatElems = using.querySelectorAll('[' + attr + ']'); + attr = attr.replace(/\\/g, ''); + for (var i = 0; i < repeatElems.length; ++i) { + if (repeatElems[i].getAttribute(attr).indexOf(repeater) != -1) { + var elem = repeatElems[i]; + var row = []; + while (elem.nodeType != 8 || + (elem.nodeValue && elem.nodeValue.indexOf(repeater) == -1)) { + if (elem.nodeType == 1) { + row.push(elem); + } + elem = elem.nextSibling; + } + multiRows.push(row); + } + } + } + var bindings = []; for (var i = 0; i < rows.length; ++i) { - var bindings = []; if (rows[i].className.indexOf('ng-binding') != -1) { bindings.push(rows[i]); } @@ -196,13 +294,25 @@ clientSideScripts.findRepeaterColumn = function() { for (var k = 0; k < childBindings.length; ++k) { bindings.push(childBindings[k]); } - for (var j = 0; j < bindings.length; ++j) { - var dataBinding = angular.element(bindings[j]).data('$binding'); - if(dataBinding) { - var bindingName = dataBinding.exp || dataBinding[0].exp || dataBinding; - if (bindingName.indexOf(binding) != -1) { - matches.push(bindings[j]); - } + } + for (var i = 0; i < multiRows.length; ++i) { + for (var j = 0; j < multiRows[i].length; ++j) { + var elem = multiRows[i][j]; + if (elem.className.indexOf('ng-binding') != -1) { + bindings.push(elem); + } + var childBindings = elem.getElementsByClassName('ng-binding'); + for (var k = 0; k < childBindings.length; ++k) { + bindings.push(childBindings[k]); + } + } + } + for (var j = 0; j < bindings.length; ++j) { + var dataBinding = angular.element(bindings[j]).data('$binding'); + if (dataBinding) { + var bindingName = dataBinding.exp || dataBinding[0].exp || dataBinding; + if (bindingName.indexOf(binding) != -1) { + matches.push(bindings[j]); } } } diff --git a/spec/basic/findelements_spec.js b/spec/basic/findelements_spec.js index 4650f02e0..7f01e42af 100644 --- a/spec/basic/findelements_spec.js +++ b/spec/basic/findelements_spec.js @@ -127,8 +127,6 @@ describe('locators', function() { expect(arr[2].getText()).toBe('blue'); }); }); - - }); describe('by repeater', function() { @@ -153,7 +151,7 @@ describe('locators', function() { it('should return all rows when unmodified', function() { var all = - browser.findElements(by.repeater('allinfo in days')); + element.all(by.repeater('allinfo in days')); all.then(function(arr) { expect(arr.length).toEqual(5); expect(arr[0].getText()).toEqual('M Monday'); @@ -163,7 +161,7 @@ describe('locators', function() { }); it('should return a single column', function() { - var initials = browser.findElements( + var initials = element.all( by.repeater('allinfo in days').column('initial')); initials.then(function(arr) { expect(arr.length).toEqual(5); @@ -172,7 +170,7 @@ describe('locators', function() { expect(arr[2].getText()).toEqual('W'); }); - var names = browser.findElements( + var names = element.all( by.repeater('allinfo in days').column('name')); names.then(function(arr) { expect(arr.length).toEqual(5); @@ -253,7 +251,54 @@ describe('locators', function() { // There are only 5 rows, so the 6th row is not present. expect(element(by.repeater('allinfo in days').row(5)).isPresent()). toBe(false); - }) + }); + + describe('repeaters using ng-repeat-start and ng-repeat-end', function() { + it('should return all elements when unmodified', function() { + var all = + element.all(by.repeater('bloop in days')); + + all.then(function(arr) { + expect(arr.length).toEqual(3 * 5); + expect(arr[0].getText()).toEqual('M'); + expect(arr[1].getText()).toEqual('-'); + expect(arr[2].getText()).toEqual('Monday'); + expect(arr[3].getText()).toEqual('T'); + expect(arr[4].getText()).toEqual('-'); + expect(arr[5].getText()).toEqual('Tuesday'); + }); + }); + + it('should return a group of elements for a row', function() { + var firstRow = element.all(by.repeater('bloop in days').row(0)); + + firstRow.then(function(arr) { + expect(arr.length).toEqual(3); + expect(arr[0].getText()).toEqual('M'); + expect(arr[1].getText()).toEqual('-'); + expect(arr[2].getText()).toEqual('Monday'); + }); + }); + + it('should return a group of elements for a column', function() { + var nameColumn = element.all( + by.repeater('bloop in days').column('name')); + + nameColumn.then(function(arr) { + expect(arr.length).toEqual(5); + expect(arr[0].getText()).toEqual('Monday'); + expect(arr[1].getText()).toEqual('Tuesday'); + }); + }); + + it('should find an individual element', function() { + browser.debugger(); + var firstInitial = element( + by.repeater('bloop in days').row(0).column('bloop.initial')); + + expect(firstInitial.getText()).toEqual('M'); + }); + }); }); it('should determine if an element is present', function() { diff --git a/testapp/repeater/repeater.html b/testapp/repeater/repeater.html index 6cd014d17..caf0c3d23 100644 --- a/testapp/repeater/repeater.html +++ b/testapp/repeater/repeater.html @@ -8,3 +8,10 @@
  • {{bar.initial}}
  • {{foo.initial}}
  • {{qux.initial}}
+
+ {{bloop.initial}} +
+- +
+ {{bloop.name}} +