我有以下代码段。
this.clickButtonText = function (buttonText, attempts, defer) {
var me = this;
if (attempts == null) {
attempts = 3;
}
if (defer == null) {
defer = protractor.promise.defer();
}
browser.driver.findElements(by.tagName('button')).then(function (buttons) {
buttons.forEach(function (button) {
button.getText().then(
function (text) {
console.log('button_loop:' + text);
if (text == buttonText) {
defer.fulfill(button.click());
console.log('RESOLVED!');
return defer.promise;
}
},
function (err) {
console.log("ERROR::" + err);
if (attempts > 0) {
return me.clickButtonText(buttonText, attempts - 1, defer);
} else {
throw err;
}
}
);
});
});
return defer.promise;
};
我的代码不时到达'ERROR :: StaleElementReferenceError:陈旧元素引用:元素没有附加到页面文档'行,所以我需要再次尝试并使用“尝试 - 1”参数。这是预期的行为。 但是一旦它达到“已解决!”一行,它就会继续迭代,所以我看到这样的smth:
button_loop:wrong_label_1
button_loop:CORRECT_LABEL
RESOLVED!
button_loop:wrong_label_2
button_loop:wrong_label_3
button_loop:wrong_label_4
问题是:如何打破循环/承诺并在 console.log('RESOLVED!'); 行之后从函数返回?
答案 0 :(得分:0)
除了抛出异常之外,没有办法停止或中断forEach()循环。如果你需要这样的行为,forEach()方法是错误的工具,而是使用普通循环。
消息来源:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach
答案 1 :(得分:0)
出于好奇,你想要完成什么?对我而言,您似乎想要根据它的文本点击一个按钮,这样您就可以遍历页面上的所有按钮,这些按钮受到一个尝试号码的限制,直到您找到该文本的匹配项。
看起来您在非角度页面上使用量角器,如果您在规范文件中使用browser.ignoreSynchronization = true;
或在conf.js
文件中使用onPrepare块,这样会更容易量角器API有两个元素定位器,可以轻松实现这一点。
this.clickButtonText = function(buttonText) {
return element.all(by.cssContainingText('button',buttonText)).get(0).click();
};
或强>
this.clickButtonText = function(buttonText) {
return element.all(by.buttonText(buttonText)).get(0).click();
};
如果还有其他原因想要遍历按钮,我可以编写一个更复杂的解释,使用bluebird循环遍历这些元素。它是一个非常有用的解决承诺的库。
答案 2 :(得分:0)
您通过创建额外的延迟对象使自己变得更加困难。如果点击失败,您可以使用承诺本身重试操作。
var clickOrRetry = function(element, attempts) {
attempts = attempts === undefined ? 3 : attempts;
return element.click().then(function() {}, function(err) {
if (attempts > 0) {
return clickOrRetry(element, attempts - 1);
} else {
throw new Error('I failed to click it -- ' + err);
}
});
};
return browser.driver.findElements(by.tagName('button')).then(function(buttons) {
return buttons.forEach(function(button) {
return clickOrRetry(button);
});
});
答案 3 :(得分:0)
一种方法是建立(在每次“尝试”)一个承诺链,该承诺链在失败时继续但在成功时跳到最后。这样的链条将具有一般形式......
return initialPromise.catch(...).catch(...).catch(...)...;
...并且使用javascript数组方法.reduce()
以编程方式构建很简单。
在实践中,代码将变得笨重:
button.getText()
,然后执行相关的测试以匹配文本,但仍然不太笨重。
据我所知,你想要这样的东西:
this.clickButtonText = function (buttonText, attempts) {
var me = this;
if(attempts === undefined) {
attempts = 3;
}
return browser.driver.findElements(by.tagName('button')).then(function(buttons) {
return buttons.reduce(function(promise, button) {
return promise.catch(function(error) {
return button.getText().then(function(text) {
if(text === buttonText) {
return button.click(); // if/when this happens, the rest of the catch chain (including its terminal catch) will be bypassed, and whatever is returned by `button.click()` will be delivered.
} else {
throw error; //rethrow the "no match" error
}
});
});
}, Promise.reject(new Error('no match'))).catch(function(err) {
if (attempts > 0) {
return me.clickButtonText(buttonText, attempts - 1); // retry
} else {
throw err; //rethrow whatever error brought you to this catch; probably a "no match" but potentially an error thrown by `button.getText()`.
}
});
});
};
注意:
catch(... retry ...)
块移动到最后一个catch。这比button.getText().then(onSucccess, onError)
结构更有意义,这会导致第一次失败时重试与buttonText匹配;这对我来说似乎不对。browser.driver.findElements()
引发的错误被捕获(重试),尽管这可能是过度杀伤。如果browser.driver.findElements()
失败一次,它可能会再次失败。.reduce()
过程构建的捕获链的3x级联来实现。但是你会看到更大的内存峰值。