Xotabu4
Xotabu4

Reputation: 3091

How to extend ElementFinder object in ProtractorJS?

Durring some experiments with protractorJS i noticed that there is no easy way to extend (inherit) ElementFinder object from protractor to add own functions.

For example, i want to create object Checkbox, that would have additional method - check() - should switch checkbox depending on result of isSelected().

I come up with the code -

var ElementFinder = require('protractor/lib/element.js').ElementFinder;
var ElementArrayFinder = require('protractor/lib/element.js').ElementArrayFinder;

class CheckBox extends ElementFinder {
    constructor(loc) {
      var getWebElements = function () {
        var ptor = browser;
        var locator = loc;
        return ptor.waitForAngular().then(function() {
          if (locator.findElementsOverride) {
            return locator.findElementsOverride(ptor.driver, null, ptor.rootEl);
          } else {
            return ptor.driver.findElements(locator);
          }
        });
      }
  var ArrayFinderFull = new ElementArrayFinder(browser, getWebElements, loc);
  super(browser, ArrayFinderFull);
}

    check() {
    return this.isSelected().then(selected => selected? this.click() : null)
    }
}

But getWebElements is copy-paste from protractor/element.js - https://github.com/angular/protractor/blob/3.1.0/lib/element.js#L131

This copy-paste flustrating me. I think there should be more proper way to extend ElementFinder.

Does anyone inherited ElementFinder in protractorJS?

Upvotes: 6

Views: 1555

Answers (2)

Xotabu4
Xotabu4

Reputation: 3091

Now it is much simplier to extend ElementFinder, i calling this - page fragments.

I even created lib to solve this issue (PRs welcome!) - https://github.com/Xotabu4/protractor-element-extend

For now it only works with ElementFinder, but i want to be able to extend ElementArrayFinders as well (planned for 2.0.0 version)

UPDATE

Support for ElementArrayFinder inheritance is added.

Upvotes: 2

alecxe
alecxe

Reputation: 474003

I'm not sure this would help, but here is something we did recently to have a takewhile() method available on an ElementArrayFinder. We've put the following into onPrepare():

protractor.ElementArrayFinder.prototype.takewhile = function(whileFn) {
    var self = this;
    var getWebElements = function() {
        return self.getWebElements().then(function(parentWebElements) {
            var list = [];
            parentWebElements.forEach(function(parentWebElement, index) {
                var elementFinder =
                    protractor.ElementFinder.fromWebElement_(self.ptor_, parentWebElement, self.locator_);

                list.push(whileFn(elementFinder, index));
            });
            return protractor.promise.all(list).then(function(resolvedList) {
                var filteredElementList = [];
                for (var index = 0; index < resolvedList.length; index++) {
                    if (!resolvedList[index]) {
                        break;
                    }
                    filteredElementList.push(parentWebElements[index])
                }
                return filteredElementList;
            });
        });
    };
    return new protractor.ElementArrayFinder(this.ptor_, getWebElements, this.locator_);
};

And now we can use takewhile on the result of element.all():

element.all(by.repeater("row in rows")).takewhile(function (elm) {
    return elm.getText().then(function (text) {
        return some_condition_to_be_true;
    });
});

Upvotes: 5

Related Questions