我有一个Selenium测试套件,它针对许多Web应用程序运行Selenium集成测试,一些是用Angular 2+编写的,一些是用AngularJS编写的。
我们使用ExpectedCondition
和WebDriverWait
定制private static ExpectedCondition<Boolean> angularIsFinished() {
return new ExpectedCondition<Boolean>() {
public Boolean apply(final WebDriver driver) {
Object result = null;
while(result == null || result.toString().equals("undefined")) {
result = ((JavascriptExecutor)driver).executeScript("return typeof angular;");
try {
Thread.sleep(200L);
} catch (final InterruptedException ex) {
logger.error("Error while trying to sleep", ex);
}
}
final String script = " var el = document.querySelector(\"body\");\n" +
" var callback = arguments[arguments.length - 1];\n" +
" angular.element(el).injector().get('$browser').notifyWhenNoOutstandingRequests(callback);";
((JavascriptExecutor)driver).executeAsyncScript(script);
return true;
}
public String toString() {
return "Wait for AngularJS";
}
};
}
,我们用它来等待AngularJS应用程序完成加载,以避免等待任意时间:
return typeof angular;
然而,undefined
将永远返回notifyWhenNoOutstandingRequests为Angular 2+应用程序。是否有类似于AngularJS的This question的方法,您可以使用它来确定Angular 2+应用程序何时完成加载?
NgZone
提到使用JavascriptExecutor
作为可能的解决方案,但是如何通过qazxswpoi执行的脚本来处理?
你可以通过调用例如document.querySelector('app-root')
?或任意组件选择器......
或者如何调用document.readyState
?它应该在完全加载的wep页面之后有结果'complete'
,并且如果网页基于角度无关紧要。
感谢@Ardesco's answer,我能够使用window.getAllAngularTestabilities
函数做类似于Protractor的工作。这是我运行的脚本,用于确定Angular 2+页面是否加载:
var testability = window.getAllAngularTestabilities()[0];
var callback = arguments[arguments.length - 1];
testability.whenStable(callback);
以下是完整的ExpectedCondition
看起来适用于AngularJS和Angular 2+:
private static ExpectedCondition<Boolean> angularIsFinished() {
return new ExpectedCondition<Boolean>() {
public Boolean apply(final WebDriver driver) {
Object result = null;
boolean isAngular2Plus = false;
while(result == null || result.toString().equals("undefined")) {
result = ((JavascriptExecutor)driver).executeScript("return typeof angular;");
if (result == null || result.toString().equals("undefined")) {
result = ((JavascriptExecutor)driver).executeScript("return typeof window.getAngularTestability;");
if (result != null && !result.toString().equals("undefined")) {
isAngular2Plus = true;
}
}
try {
Thread.sleep(200L);
} catch (final InterruptedException ex) {
logger.error("Error while trying to sleep", ex);
}
}
final String script;
if (isAngular2Plus) {
script =" var testability = window.getAllAngularTestabilities()[0];\n" +
" var callback = arguments[arguments.length - 1];\n" +
" testability.whenStable(callback);";
} else {
script =" var el = document.querySelector(\"body\");\n" +
" var callback = arguments[arguments.length - 1];\n" +
" angular.element(el).injector().get('$browser').notifyWhenNoOutstandingRequests(callback);";
}
((JavascriptExecutor) driver).executeAsyncScript(script);
return true;
}
public String toString() {
return "Wait for AngularJS";
}
};
}
看看Protractor code,我想出了两个可能的解决方案:
首先,我们有一个选项,我们找到可测试性列表,然后为所有这些添加回调,然后等待其中一个将该站点标记为可测试(这意味着您的脚本将在任何一个可测试性之后继续已经变得可以测试了,它不会等待所有这些都变得可测试了。
private static ExpectedCondition angular2IsTestable() {
return (ExpectedCondition<Boolean>) driver -> {
JavascriptExecutor jsexec = ((JavascriptExecutor) driver);
Object result = jsexec.executeAsyncScript("window.seleniumCallback = arguments[arguments.length -1];\n" +
"if (window.getAllAngularTestabilities()) {\n" +
" window.getAllAngularTestabilities().forEach(function (testability) {\n" +
" testability.whenStable(window.seleniumCallback(true))\n" +
" }\n" +
" );\n" +
"} else {\n" +
" window.seleniumCallback(false)\n" +
"}"
);
return Boolean.parseBoolean(result.toString());
};
}
第二个选项是专门检查角度根元素可测试性状态:
private static ExpectedCondition angular2ElementIsTestable(final WebElement element) {
return (ExpectedCondition<Boolean>) driver -> {
JavascriptExecutor jsexec = ((JavascriptExecutor) driver);
Object result = jsexec.executeAsyncScript(
"window.seleniumCallback = arguments[arguments.length -1];\n" +
"var element = arguments[0];\n" +
"if (window.getAngularTestability && window.getAngularTestability(element)) {\n" +
" window.getAngularTestability(element).whenStable(window.seleniumCallback(true));\n" +
"} else {\n" +
" window.seleniumCallback(false)\n" +
"}"
, element);
return Boolean.parseBoolean(result.toString());
};
}
如果您想测试站点的特定区域,第二个选项更具针对性,因此更可靠。
第三种选择是写一些更复杂的东西来跟踪所有可测试性的状态,然后只有当所有这些都变为真时才触发真正的回调。我还没有这方面的实现。