Search code examples
javascriptangularjspromiseeachweb-testing

How to break when found the item?


I have a web test code and I don't want to iterate over the rest after i found the item. This code iterate to all item and the finally return. How to resolve this problem, because in the each I don't break!

isItemPresent: function (name) {
            var def = Q.defer();
            Trace.add("Checking the " + name + " inside the menu");
            var count = 0, total = 0, stop = false;
            var listObj = element(by.id(this.MENU_ID)).all(by.tagName("div"));

            listObj.count().then(function (nr) {
                total = nr;
                listObj.each(func);
            });
            var _this = this;          
            var func = function (element) {

                element.getAttribute('class').then(function (classes) {                  
                    count++;
                    if (classes.indexOf(name) !== -1 && !stop) {
                        stop = true;                      
                        element.getAttribute('id').then(function (value) {
                            _this._isItemVisible('', value).then(function (opt) {
                                value = value.match(/(\d+)/g);                            
                                if (opt.success) {
                                   // console.log('------- BREAK --------');
                                    def.resolve({success: true, msg: {index: value[0]}});
                                    Trace.add("Menu item: " + name + " was found in the main menu.");                                  
                                } else {
                                    def.resolve({success: false, msg: {index: value[0]}});
                                    Trace.add("Menu item: " + name + " was not found in the main menu.");
                                }
                            });
                        });
                    } else if (count === total && stop === true) {
                        def.resolve({success: false, msg: {index: 0}});
      Trace.add("Menu item: " + name + " was not found in the main menu.");
                        stop = true;
                    }
                });
            };
            return def.promise;
        };

Solution

  • Various solutions are available, none of which is particularly obvious. This is the best presentation/discussion that I know of

    You might like to consider the very concise Bluebird solution given in this answer, however, sticking with Q, here's a solution based on the "loop" solution in the same answer.

    isItemPresent: function (name) {
        var _this = this,
            listObj = element(by.id(this.MENU_ID)).all(by.tagName("div")),
            list = [];
    
        //Map listObj to the Array `list`, providing a convenient .reduce() method.
        //If listObj has a `.toArray()` method or `.map()` method, then use that in preference.
        listObj.each(function(element) {
            list.push(element);
        });
    
        // To make func() useful it must return a promise :
        // * fulfilled for success - forcing a "break"
        // * rejected for failure - allowing "continue"
        function func(element) {
            return element.getAttribute('class').then(function(classes) {
                if (classes.indexOf(name) === -1) {
                    return Q.reject('class "' + name + '" not found');//"continue"
                } else {
                    return element.getAttribute('id').then(function(value) {
                        _this._isItemVisible('', value).then(function(opt) {
                            if(opt.success) {
                                return Q(value.match(/(\d+)/g)[0]);//"break".
                            } else {
                                return Q.reject('class "' + name + '" not visible');//"continue"
                            }
                        });
                    });
                }
            });
        };
    
        // Master routine
        return list.reduce(function(previous, element) {
            return previous.catch(function(error) {
                console.log(error || 'list length is zero'); //optional
                return func(element);
            });
        }, Q.reject(null)).catch(function(error) {
            return Q.reject(-1);//for example
        });
    };
    

    Explanation

    The master routine builds a .catch() chain seeded with a rejected promise and :

    • as long as func() continues to return a rejected promise, the catch(...) callback ensures that func() is called again for the next element.

    • if func() never hits opt.success, the master routine returns the last rejected promise returned to it (or the seed promise if list has zero length).

    • if/when func() hits opt.success, a promise fulfilled with the required value is returned and the rest of the master routine's promise chain is effectively skipped as there are no success handlers to cause it to do otherwise, and the master routine returns the fulfilled promise that was returned to it.

    The final .catch() is just an example. You may want to do something different - whatever is most appropriate for handling failure wherever isItemPresent(name) is called.