Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

search an element in a repeater containing specific text using protractor

Tags:

protractor

How could I search an element from a repeater containing a specific text ?

I tried things like that :

element(by.repeater('item in array')).all(by.cssContainingText('.xyz','my item title')); // only gets the first element

I could search by myself using .then after element.all but I'm wondering if it exists something simpler like cssContainingText but for repeaters :

element(by.repeaterContainingText('item in array','my item title'))

or a element chaining like that :

element.all(by.repeater('item in array')).element(by.cssContainingText('.xyz','my item title'));

A solution with filter (but very slow)

element.all(by.repeater('item in array')).filter(function(elem){
    return elem.getText().then(function(text){
        return text.indexOf('my item title') > -1;
    });
}).then(function(filteredElements) {
    return filteredElements[0];
})
like image 322
sylvain Avatar asked Jul 30 '14 15:07

sylvain


2 Answers

Protractor allows adding locators.. I am using the following implementation (pure javascript) successfully in a project with 20 tests or so.

Here is a solution using lodash and jquery. Below there is also one in pure javascript.

https://gist.github.com/GuyMograbi/7a5f5e580bcf8d7da58a

by.addLocator('text',

/**
 *
 * @param {string} text - will be lowercased
 * @param {string} selector - to get list of children
 * @param {null|object} parent - protractor will provide this..
 */
function(text, selector, parent) {
    return _.filter($(parent || 'body').find(selector), function(e){
        return $(e).is(':visible') && $(e).text().toLowerCase().trim() === text.toLowerCase().trim();
    });
});

and use it with

return $('table').all(by.text('my text', 'tr')).first().getText().then(function(text){...})

or

return element(by.text('my text', 'tr')).getText().then(function(text){...})

Don't forget the following about protractor and locators

I just spent 2 hours breaking my brains as to why my locator does not work. please remember the following:

  • protractor 'ignores' hidden elements, however your locator does not do so by default.. so if you write a custom locator, in order for it to work as expected in angular you must filter out hidden elements
  • if you need to debug, I found it most useful using getOuterHtml().then..

Pure JavaScript

This is how the same will look without jquery and lodash

by.addLocator('text',

/**
 *
 * @param text - will be lowercased
 * @param selector - to get list of children
 * @param parent - protractor will provide this..
 */
function(text, selector, _parent) {

    return Array.prototype.filter.call( (_parent || document).querySelectorAll(selector), function(e){
        return e && !!(e.offsetWidth || e.offsetHeight || e.getClientRects().length) && e.textContent && e.textContent.toLowerCase().trim() === text.toLowerCase().trim();

    });
});
    • the isVisible part was eventually taken from jquery sources. see answer at: https://stackoverflow.com/a/33456469/1068746

Important note - or - why this should be the right answer

Using filter you get a promise. using this solution you get an element. So instead of having a getByText(..).then(function(e){ e.click() }) you will have getByText(...).click() - sweeet!

Limitations and possible enhancements

in your case, the repeater locator cannot be used which is a bummer, unless you modify the location instead of text to be textInRepeater and then have '[ng-repeat=' + repeater + ']' somehow added in the code.

Another thing you might want to enhance which is very useful. Sometime you want to get an element which CONTAINS text, or possibly contains a child with text, but you actually want to select the parent.

like image 115
guy mograbi Avatar answered Oct 12 '22 12:10

guy mograbi


Yes you can chain elements in Protractor.

element(locator1).element(locator2);

protractor-Locators.md

like image 32
Gamebreaker Avatar answered Oct 12 '22 13:10

Gamebreaker