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

Commit

Permalink
feat(locators): add by.exactRepeater
Browse files Browse the repository at this point in the history
  • Loading branch information
sjelin committed Feb 7, 2015
1 parent 4045386 commit 324f69d
Show file tree
Hide file tree
Showing 3 changed files with 153 additions and 81 deletions.
76 changes: 58 additions & 18 deletions lib/clientsidescripts.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,13 +93,23 @@ functions.findBindings = function(binding, exactMatch, using, rootSelector) {
* Returns an array of all the elements in one segment for ng-repeat-start.
*
* @param {string} repeater The text of the repeater, e.g. 'cat in cats'.
* @param {boolean} exact Whether the repeater needs to be matched exactly
* @param {number} index The row index.
* @param {Element} using The scope of the search.
*
* @return {Array.<Element>} The row of the repeater, or an array of elements
* in the first row in the case of ng-repeat-start.
*/
functions.findRepeaterRows = function(repeater, index, using) {
functions.findRepeaterRows = function(repeater, exact, index, using) {
function repeaterMatch(ngRepeat, repeater, exact) {
if (exact) {
return ngRepeat.split(' track by ')[0].split(' as ')[0].split('|')[0].
trim() == repeater;
} else {
return ngRepeat.indexOf(repeater) != -1;
}
}

using = using || document;

var prefixes = ['ng-', 'ng_', 'data-ng-', 'x-ng-', 'ng\\:'];
Expand All @@ -109,7 +119,7 @@ functions.findBindings = function(binding, exactMatch, using, rootSelector) {
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) {
if (repeaterMatch(repeatElems[i].getAttribute(attr), repeater, exact)) {
rows.push(repeatElems[i]);
}
}
Expand All @@ -122,11 +132,11 @@ functions.findBindings = function(binding, exactMatch, using, rootSelector) {
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) {
if (repeaterMatch(repeatElems[i].getAttribute(attr), repeater, exact)) {
var elem = repeatElems[i];
var row = [];
while (elem.nodeType != 8 ||
elem.nodeValue.indexOf(repeater) == -1) {
!repeaterMatch(elem.nodeValue, repeater, exact)) {
if (elem.nodeType == 1) {
row.push(elem);
}
Expand All @@ -144,11 +154,21 @@ functions.findBindings = function(binding, exactMatch, using, rootSelector) {
* Find all rows of an ng-repeat.
*
* @param {string} repeater The text of the repeater, e.g. 'cat in cats'.
* @param {boolean} exact Whether the repeater needs to be matched exactly
* @param {Element} using The scope of the search.
*
* @return {Array.<Element>} All rows of the repeater.
*/
functions.findAllRepeaterRows = function(repeater, using) {
functions.findAllRepeaterRows = function(repeater, exact, using) {
function repeaterMatch(ngRepeat, repeater, exact) {
if (exact) {
return ngRepeat.split(' track by ')[0].split(' as ')[0].split('|')[0].
trim() == repeater;
} else {
return ngRepeat.indexOf(repeater) != -1;
}
}

using = using || document;

var rows = [];
Expand All @@ -158,7 +178,7 @@ functions.findBindings = function(binding, exactMatch, using, rootSelector) {
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) {
if (repeaterMatch(repeatElems[i].getAttribute(attr), repeater, exact)) {
rows.push(repeatElems[i]);
}
}
Expand All @@ -168,10 +188,10 @@ functions.findBindings = function(binding, exactMatch, using, rootSelector) {
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) {
if (repeaterMatch(repeatElems[i].getAttribute(attr), repeater, exact)) {
var elem = repeatElems[i];
while (elem.nodeType != 8 ||
elem.nodeValue.indexOf(repeater) == -1) {
!repeaterMatch(elem.nodeValue, repeater, exact)) {
if (elem.nodeType == 1) {
rows.push(elem);
}
Expand All @@ -187,14 +207,24 @@ functions.findBindings = function(binding, exactMatch, using, rootSelector) {
* Find an element within an ng-repeat by its row and column.
*
* @param {string} repeater The text of the repeater, e.g. 'cat in cats'.
* @param {boolean} exact Whether the repeater needs to be matched exactly
* @param {number} index The row index.
* @param {string} binding The column binding, e.g. '{{cat.name}}'.
* @param {Element} using The scope of the search.
* @param {string} rootSelector The selector to use for the root app element.
*
* @return {Array.<Element>} The element in an array.
*/
functions.findRepeaterElement = function(repeater, index, binding, using, rootSelector) {
functions.findRepeaterElement = function(repeater, exact, index, binding, using, rootSelector) {
function repeaterMatch(ngRepeat, repeater, exact) {
if (exact) {
return ngRepeat.split(' track by ')[0].split(' as ')[0].split('|')[0].
trim() == repeater;
} else {
return ngRepeat.indexOf(repeater) != -1;
}
}

var matches = [];
var root = document.querySelector(rootSelector || 'body');
using = using || document;
Expand All @@ -206,7 +236,7 @@ functions.findRepeaterElement = function(repeater, index, binding, using, rootSe
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) {
if (repeaterMatch(repeatElems[i].getAttribute(attr), repeater, exact)) {
rows.push(repeatElems[i]);
}
}
Expand All @@ -219,11 +249,11 @@ functions.findRepeaterElement = function(repeater, index, binding, using, rootSe
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) {
if (repeaterMatch(repeatElems[i].getAttribute(attr), repeater, exact)) {
var elem = repeatElems[i];
var row = [];
while (elem.nodeType != 8 ||
(elem.nodeValue && elem.nodeValue.indexOf(repeater) == -1)) {
while (elem.nodeType != 8 || (elem.nodeValue &&
!repeaterMatch(elem.nodeValue, repeater, exact))) {
if (elem.nodeType == 1) {
row.push(elem);
}
Expand Down Expand Up @@ -285,13 +315,23 @@ functions.findRepeaterElement = function(repeater, index, binding, using, rootSe
* Find the elements in a column of an ng-repeat.
*
* @param {string} repeater The text of the repeater, e.g. 'cat in cats'.
* @param {boolean} exact Whether the repeater needs to be matched exactly
* @param {string} binding The column binding, e.g. '{{cat.name}}'.
* @param {Element} using The scope of the search.
* @param {string} rootSelector The selector to use for the root app element.
*
* @return {Array.<Element>} The elements in the column.
*/
functions.findRepeaterColumn = function(repeater, binding, using, rootSelector) {
functions.findRepeaterColumn = function(repeater, exact, binding, using, rootSelector) {
function repeaterMatch(ngRepeat, repeater, exact) {
if (exact) {
return ngRepeat.split(' track by ')[0].split(' as ')[0].split('|')[0].
trim() == repeater;
} else {
return ngRepeat.indexOf(repeater) != -1;
}
}

var matches = [];
var root = document.querySelector(rootSelector || 'body');
using = using || document;
Expand All @@ -303,7 +343,7 @@ functions.findRepeaterColumn = function(repeater, binding, using, rootSelector)
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) {
if (repeaterMatch(repeatElems[i].getAttribute(attr), repeater, exact)) {
rows.push(repeatElems[i]);
}
}
Expand All @@ -316,11 +356,11 @@ functions.findRepeaterColumn = function(repeater, binding, using, rootSelector)
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) {
if (repeaterMatch(repeatElems[i].getAttribute(attr), repeater, exact)) {
var elem = repeatElems[i];
var row = [];
while (elem.nodeType != 8 ||
(elem.nodeValue && elem.nodeValue.indexOf(repeater) == -1)) {
while (elem.nodeType != 8 || (elem.nodeValue &&
!repeaterMatch(elem.nodeValue, repeater, exact))) {
if (elem.nodeType == 1) {
row.push(elem);
}
Expand Down
153 changes: 90 additions & 63 deletions lib/locators.js
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,74 @@ ProtractorBy.prototype.partialButtonText = function(searchText) {
};
};

// Generate either by.repeater or by.exactRepeater
function byRepeaterInner(exact) {
var name = 'by.' + (exact ? 'exactR' : 'r') + 'epeater';
return function(repeatDescriptor) {
return {
findElementsOverride: function(driver, using, rootSelector) {
return driver.findElements(
webdriver.By.js(clientSideScripts.findAllRepeaterRows,
repeatDescriptor, exact, using, rootSelector));
},
toString: function toString() {
return name + '("' + repeatDescriptor + '")';
},
row: function(index) {
return {
findElementsOverride: function(driver, using, rootSelector) {
return driver.findElements(
webdriver.By.js(clientSideScripts.findRepeaterRows,
repeatDescriptor, exact, index, using, rootSelector));
},
toString: function toString() {
return name + '(' + repeatDescriptor + '").row("' + index + '")"';
},
column: function(binding) {
return {
findElementsOverride: function(driver, using, rootSelector) {
return driver.findElements(
webdriver.By.js(clientSideScripts.findRepeaterElement,
repeatDescriptor, exact, index, binding, using,
rootSelector));
},
toString: function toString() {
return name + '("' + repeatDescriptor + '").row("' + index +
'").column("' + binding + '")';
}
};
}
};
},
column: function(binding) {
return {
findElementsOverride: function(driver, using, rootSelector) {
return driver.findElements(
webdriver.By.js(clientSideScripts.findRepeaterColumn,
repeatDescriptor, exact, binding, using, rootSelector));
},
toString: function toString() {
return name + '("' + repeatDescriptor + '").column("' +
binding + '")';
},
row: function(index) {
return {
findElementsOverride: function(driver, using, rootSelector) {
return driver.findElements(
webdriver.By.js(clientSideScripts.findRepeaterElement,
repeatDescriptor, exact, index, binding, using, rootSelector));
},
toString: function toString() {
return name + '("' + repeatDescriptor + '").column("' +
binding + '").row("' + index + '")';
}
};
}
};
}
};
};
}

/**
* Find elements inside an ng-repeat.
Expand Down Expand Up @@ -279,70 +347,29 @@ ProtractorBy.prototype.partialButtonText = function(searchText) {
* // all top level elements repeated by the repeater. For 2 books divs
* // resolves to an array of 4 elements.
* var divs = element.all(by.repeater('book in library'));
*
* @param {string} repeatDescriptor
* @return {{findElementsOverride: findElementsOverride, toString: Function|string}}
*/
ProtractorBy.prototype.repeater = function(repeatDescriptor) {
return {
findElementsOverride: function(driver, using, rootSelector) {
return driver.findElements(
webdriver.By.js(clientSideScripts.findAllRepeaterRows,
repeatDescriptor, using, rootSelector));
},
toString: function toString() {
return 'by.repeater("' + repeatDescriptor + '")';
},
row: function(index) {
return {
findElementsOverride: function(driver, using, rootSelector) {
return driver.findElements(
webdriver.By.js(clientSideScripts.findRepeaterRows,
repeatDescriptor, index, using, rootSelector));
},
toString: function toString() {
return 'by.repeater(' + repeatDescriptor + '").row("' + index + '")"';
},
column: function(binding) {
return {
findElementsOverride: function(driver, using, rootSelector) {
return driver.findElements(
webdriver.By.js(clientSideScripts.findRepeaterElement,
repeatDescriptor, index, binding, using, rootSelector));
},
toString: function toString() {
return 'by.repeater("' + repeatDescriptor + '").row("' + index +
'").column("' + binding + '")';
}
};
}
};
},
column: function(binding) {
return {
findElementsOverride: function(driver, using, rootSelector) {
return driver.findElements(
webdriver.By.js(clientSideScripts.findRepeaterColumn,
repeatDescriptor, binding, using, rootSelector));
},
toString: function toString() {
return 'by.repeater("' + repeatDescriptor + '").column("' +
binding + '")';
},
row: function(index) {
return {
findElementsOverride: function(driver, using, rootSelector) {
return driver.findElements(
webdriver.By.js(clientSideScripts.findRepeaterElement,
repeatDescriptor, index, binding, using, rootSelector));
},
toString: function toString() {
return 'by.repeater("' + repeatDescriptor + '").column("' +
binding + '").row("' + index + '")';
}
};
}
};
}
};
};
ProtractorBy.prototype.repeater = byRepeaterInner(false);

/**
* Find an element by exact repeater.
*
* @view
* <li ng-repeat="person in peopleWithRedHair"></li>
* <li ng-repeat="car in cars | orderBy:year"></li>
*
* @example
* expect(element(by.exactRepeater('person in peopleWithRedHair')).isPresent()).toBe(true);
* expect(element(by.exactRepeater('person in people')).isPresent()).toBe(false);
* expect(element(by.exactRepeater('car in cars')).isPresent()).toBe(true);
*
* @param {string} repeatDescriptor
* @return {{findElementsOverride: findElementsOverride, toString: Function|string}}
*/
ProtractorBy.prototype.exactRepeater = byRepeaterInner(true);


/**
* Find elements by CSS which contain a certain string.
Expand Down
5 changes: 5 additions & 0 deletions spec/basic/locators_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,11 @@ describe('locators', function() {
toBe(false);
});

it('should have by.exactRepeater working', function() {
expect(element(by.exactRepeater('day in d')).isPresent()).toBe(false);
expect(element(by.exactRepeater('day in days')).isPresent()).toBe(true);
});

describe('repeaters using ng-repeat-start and ng-repeat-end', function() {
it('should return all elements when unmodified', function() {
var all =
Expand Down

0 comments on commit 324f69d

Please sign in to comment.