在条件评估为真时获取元素(扩展 ElementArrayFinder)

IT技术 javascript selenium selenium-webdriver iteration protractor
2021-03-15 12:45:09

我们有一个表示为ul->li列表的菜单(简化):

<ul class="dropdown-menu" role="menu">
    <li ng-repeat="filterItem in filterCtrl.filterPanelCfg track by filterItem.name"
        ng-class="{'divider': filterItem.isDivider}" class="ng-scope">
        <a href="" class="ng-binding"> Menu Item 1</a>
    </li>
    ...
    <li ng-repeat="filterItem in filterCtrl.filterPanelCfg track by filterItem.name"
        ng-class="{'divider': filterItem.isDivider}" class="ng-scope">
        <a href="" class="ng-binding"> Menu Item 2</a>
    </li>
</ul>

在位置 N 的某处有一个分隔符,可以通过评估 filterItem.isDivider或检查a链接的文本来识别(在分隔符的情况下,它是空的)。

现在,目标是获取位于分隔线之前的所有菜单项。你会如何处理这个问题?


我目前的方法相当通用 - 扩展ElementArrayFinder和添加takewhile()功能(受 Python 的启发itertools.takewhile())。这是我实现它的方式(基于filter()):

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_);
};

而且,这是我如何使用它:

this.getInclusionFilters = function () {
    return element.all(by.css("ul.dropdown-menu li")).takewhile(function (inclusionFilter) {
        return inclusionFilter.evaluate("!filterItem.isDivider");
    });
};

但是,测试只是挂起,直到呼叫jasmine.DEFAULT_TIMEOUT_INTERVAL到达takewhile()

如果我将console.logs 放入循环和之后,我可以看到它正确地将元素推送到分隔符之前,并在到达分隔符时停止。我可能在这里遗漏了一些东西。

使用量角器 2.2.0。


另外,如果我把问题复杂化了,请告诉我。

2个回答

也许我遗漏了一些东西,但是你不能ul li a在他们从 getText() 给你一些东西的时候遍历元素,并将它们存储到某个数组,或者直接在那个循环中对它们做一些事情吗?

var i = 0;
var el = element.all(by.css('ul li a'));
var tableItems = [];
(function loop() {
    el.get(i).getText().then(function(text){
        if(text){
            tableItems.push(el.get(i));
            i+=1;
            loop();
        }
    });
}());
鉴于现在我可以从元素查找器数组中创建一个ElementArrayFinder实例,此选项成为我的首选。再次感谢!
2021-05-10 12:45:09
感谢您的选择!我不满意的是,在这种情况下,结果我会得到一个元素数组而不是一个 ElementArrayFinder,这使得以后使用不方便 - 例如,我将无法调用getText()它并获取一系列文本,或使用map()each()或量角器中可用的其他功能语法......但是,总的来说,它可以完成工作,我同意。
2021-05-12 12:45:09

takewhile()一旦我删除了protractor.promise = require("q");from ,实际上对我有用onPrepare()- 这是在那里替换的protractor.promiseq以便能够使用类似spread()函数的语法糖显然,使用q代替是不安全protractor.promise

我现在要做的就是将其添加到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_);
};

用法非常类似于filter()

element.all(by.css("ul li a")).takewhile(function (elm) {
    return elm.getText().then(function (text) {
        return text;
    });
});

仅供参考,建议制作takewhile()内置.