我有一个用Selenium测试的Web应用程序。页面加载时会运行很多JavaScript
这段JavaScript代码编写得不是很好,但我无法改变任何东西。
因此,等待元素以findElement()
方法出现在DOM中不是一种选择
我想在Java中创建一个通用函数来等待页面加载,可能的解决方案是:
document.body.innerHTML
的结果存储在字符串变量body
中。body
变量与先前版本的body
进行比较。如果它们相同,则设置递增计数器notChangedCount
,否则将notChangedCount
设置为零。notChangedCount >= 10
然后退出循环,否则循环到第一步。你认为这是一个有效的解决方案吗?
答案 0 :(得分:62)
如果有人真正知道一般的,总是适用的答案,那么很久以前就会实现无处不在,并且会让我们的生活变得更加轻松。
你可以做很多事情,但每一件事都有问题:
正如Ashwin Prabhu所说,如果您对脚本有所了解,您可以在window
或document
等处观察其行为并跟踪其中的一些变量。但是,此解决方案并非如此适用于所有人,只能由您使用,并且只能在有限的页面上使用。
通过观察HTML代码及其是否已经有一段时间没有被更改的解决方案也不错(同样,有a method来直接获取原始的和未经编辑的HTML WebDriver
),但是:
setTimeout()
)并一次又一次地工作,并且每次运行时都可能更改HTML。说真的,每个“Web 2.0”页面都能做到。甚至Stack Overflow。你可以覆盖最常用的方法,并考虑使用它们完成的脚本,但......你无法确定。innerHTML
有趣的事情。有一些工具可以帮助您。即Progress Listeners和nsIWebProgressListener以及其他一些人。然而,浏览器对此的支持非常糟糕。 Firefox开始尝试从FF4开始支持它(仍在不断发展),IE在IE9中有基本支持。
我想我很快就会想出另一个有缺陷的解决方案。事实是 - 由于永久的脚本正在完成他们的工作,所以没有明确的答案何时说“现在页面已完成”。选择最适合你的那个,但要注意它的缺点。
答案 1 :(得分:28)
谢谢Ashwin!
在我的情况下,我应该等待某个元素中的jquery插件执行..特别是“qtip”
根据你的提示,它对我来说很有效:
wait.until( new Predicate<WebDriver>() {
public boolean apply(WebDriver driver) {
return ((JavascriptExecutor)driver).executeScript("return document.readyState").equals("complete");
}
}
);
注意:我正在使用Webdriver 2
答案 2 :(得分:24)
你需要等待Javascript和jQuery才能完成加载。
执行Javascript以检查jQuery.active
是否为0
且document.readyState
为complete
,这意味着JS和jQuery加载已完成。
public boolean waitForJStoLoad() {
WebDriverWait wait = new WebDriverWait(driver, 30);
// wait for jQuery to load
ExpectedCondition<Boolean> jQueryLoad = new ExpectedCondition<Boolean>() {
@Override
public Boolean apply(WebDriver driver) {
try {
return ((Long)executeJavaScript("return jQuery.active") == 0);
}
catch (Exception e) {
return true;
}
}
};
// wait for Javascript to load
ExpectedCondition<Boolean> jsLoad = new ExpectedCondition<Boolean>() {
@Override
public Boolean apply(WebDriver driver) {
return executeJavaScript("return document.readyState")
.toString().equals("complete");
}
};
return wait.until(jQueryLoad) && wait.until(jsLoad);
}
答案 3 :(得分:8)
JS库是否在窗口上定义/初始化任何众所周知的变量?
如果是这样,您可以等待变量出现。你可以使用
((JavascriptExecutor)driver).executeScript(String script, Object... args)
测试此条件(例如:window.SomeClass && window.SomeClass.variable != null
)并返回布尔值true
/ false
。
将其包裹在WebDriverWait
中,并等待脚本返回true
。
答案 4 :(得分:5)
如果你需要做的就是在尝试与元素交互之前等待页面上的html变得稳定,你可以定期轮询DOM并比较结果,如果在给定的轮询时间内DOM是相同的,你是金色的。这样的事情你在比较之前传递最长等待时间和页面轮询之间的时间。简单而有效。
public void waitForJavascript(int maxWaitMillis, int pollDelimiter) {
double startTime = System.currentTimeMillis();
while (System.currentTimeMillis() < startTime + maxWaitMillis) {
String prevState = webDriver.getPageSource();
Thread.sleep(pollDelimiter); // <-- would need to wrap in a try catch
if (prevState.equals(webDriver.getPageSource())) {
return;
}
}
}
答案 5 :(得分:3)
以下代码在我的情况下完美运行 - 我的页面包含复杂的java脚本
public void checkPageIsReady() {
JavascriptExecutor js = (JavascriptExecutor)driver;
//Initially bellow given if condition will check ready state of page.
if (js.executeScript("return document.readyState").toString().equals("complete")){
System.out.println("Page Is loaded.");
return;
}
//This loop will rotate for 25 times to check If page Is ready after every 1 second.
//You can replace your value with 25 If you wants to Increase or decrease wait time.
for (int i=0; i<25; i++){
try {
Thread.sleep(1000);
}catch (InterruptedException e) {}
//To check page ready state.
if (js.executeScript("return document.readyState").toString().equals("complete")){
break;
}
}
}
来源 - How To Wait For Page To Load/Ready In Selenium WebDriver
答案 6 :(得分:2)
我有同样的问题。 这个解决方案适用于WebDriverDoku:
WebDriverWait wait = new WebDriverWait(driver, 10);
WebElement element = wait.until(ExpectedConditions.elementToBeClickable(By.id("someid")));
答案 7 :(得分:1)
对于nodejs
Selenium库,我使用了以下代码段。在我的例子中,我正在寻找添加到窗口的两个对象,在这个例子中是<SOME PROPERTY>
,10000
是超时毫秒,<NEXT STEP HERE>
是在属性之后发生的事情。发现在窗户上。
driver.wait( driver => {
return driver.executeScript( 'if(window.hasOwnProperty(<SOME PROPERTY>) && window.hasOwnProperty(<SOME PROPERTY>)) return true;' ); }, 10000).then( ()=>{
<NEXT STEP HERE>
}).catch(err => {
console.log("looking for window properties", err);
});
答案 8 :(得分:1)
我让我的开发人员创建了一个JavaScript变量&#34; isProcessing&#34;我可以访问(在&#34; ae&#34;对象中)他们在事情开始运行时设置并在事情完成时清除。然后我在一个累加器中运行它,每100毫秒检查一次,直到它连续五次,总共500毫秒没有任何变化。如果30秒过去了,我会抛出异常,因为到那时应该发生了什么。这是在C#。
public static void WaitForDocumentReady(this IWebDriver driver)
{
Console.WriteLine("Waiting for five instances of document.readyState returning 'complete' at 100ms intervals.");
IJavaScriptExecutor jse = (IJavaScriptExecutor)driver;
int i = 0; // Count of (document.readyState === complete) && (ae.isProcessing === false)
int j = 0; // Count of iterations in the while() loop.
int k = 0; // Count of times i was reset to 0.
bool readyState = false;
while (i < 5)
{
System.Threading.Thread.Sleep(100);
readyState = (bool)jse.ExecuteScript("return ((document.readyState === 'complete') && (ae.isProcessing === false))");
if (readyState) { i++; }
else
{
i = 0;
k++;
}
j++;
if (j > 300) { throw new TimeoutException("Timeout waiting for document.readyState to be complete."); }
}
j *= 100;
Console.WriteLine("Waited " + j.ToString() + " milliseconds. There were " + k + " resets.");
}
答案 9 :(得分:1)
这对我来说很适合动态呈现的网站:
WebDriverWait wait = new WebDriverWait(driver, 50);
wait.until((ExpectedCondition<Boolean>) wd -> ((JavascriptExecutor) wd).executeScript("return document.readyState").equals("complete"));
try {
wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath("//*[contains(text(),'" + "This text will always fail :)" + "')]"))); // condition you are certain won't be true
}
catch (TimeoutException te) {
}
String script = "return document.getElementsByTagName(\"html\")[0].outerHTML;";
content = ((JavascriptExecutor) driver).executeScript(script).toString();
答案 10 :(得分:0)
这是我自己的代码:
Window.setTimeout仅在浏览器空闲时执行。
因此,如果浏览器中没有活动,则递归调用函数(42次)将花费100ms,如果浏览器忙于执行其他操作,则需要更多时间。
ExpectedCondition<Boolean> javascriptDone = new ExpectedCondition<Boolean>() {
public Boolean apply(WebDriver d) {
try{//window.setTimeout executes only when browser is idle,
//introduces needed wait time when javascript is running in browser
return ((Boolean) ((JavascriptExecutor) d).executeAsyncScript(
" var callback =arguments[arguments.length - 1]; " +
" var count=42; " +
" setTimeout( collect, 0);" +
" function collect() { " +
" if(count-->0) { "+
" setTimeout( collect, 0); " +
" } "+
" else {callback(" +
" true" +
" );}"+
" } "
));
}catch (Exception e) {
return Boolean.FALSE;
}
}
};
WebDriverWait w = new WebDriverWait(driver,timeOut);
w.until(javascriptDone);
w=null;
作为奖励,可以在document.readyState或jQuery Ajax调用上重置计数器,或者如果正在运行任何jQuery动画(仅当您的应用程序使用jQuery进行ajax调用时...)... ...
" function collect() { " +
" if(!((typeof jQuery === 'undefined') || ((jQuery.active === 0) && ($(\":animated\").length === 0))) && (document.readyState === 'complete')){" +
" count=42;" +
" setTimeout( collect, 0); " +
" }" +
" else if(count-->0) { "+
" setTimeout( collect, 0); " +
" } "+
...
编辑:我注意到如果加载新页面并且测试可能会停止响应,则executeAsyncScript不能正常工作,而是更好地使用它。
public static ExpectedCondition<Boolean> documentNotActive(final int counter){
return new ExpectedCondition<Boolean>() {
boolean resetCount=true;
@Override
public Boolean apply(WebDriver d) {
if(resetCount){
((JavascriptExecutor) d).executeScript(
" window.mssCount="+counter+";\r\n" +
" window.mssJSDelay=function mssJSDelay(){\r\n" +
" if((typeof jQuery != 'undefined') && (jQuery.active !== 0 || $(\":animated\").length !== 0))\r\n" +
" window.mssCount="+counter+";\r\n" +
" window.mssCount-->0 &&\r\n" +
" setTimeout(window.mssJSDelay,window.mssCount+1);\r\n" +
" }\r\n" +
" window.mssJSDelay();");
resetCount=false;
}
boolean ready=false;
try{
ready=-1==((Long) ((JavascriptExecutor) d).executeScript(
"if(typeof window.mssJSDelay!=\"function\"){\r\n" +
" window.mssCount="+counter+";\r\n" +
" window.mssJSDelay=function mssJSDelay(){\r\n" +
" if((typeof jQuery != 'undefined') && (jQuery.active !== 0 || $(\":animated\").length !== 0))\r\n" +
" window.mssCount="+counter+";\r\n" +
" window.mssCount-->0 &&\r\n" +
" setTimeout(window.mssJSDelay,window.mssCount+1);\r\n" +
" }\r\n" +
" window.mssJSDelay();\r\n" +
"}\r\n" +
"return window.mssCount;"));
}
catch (NoSuchWindowException a){
a.printStackTrace();
return true;
}
catch (Exception e) {
e.printStackTrace();
return false;
}
return ready;
}
@Override
public String toString() {
return String.format("Timeout waiting for documentNotActive script");
}
};
}
答案 11 :(得分:0)
你可以编写一些逻辑来处理这个问题。我编写了一个返回WebElement
的方法,这个方法将被调用三次,或者你可以增加时间并为WebElement
添加一个空检查。这是一个例子
public static void main(String[] args) {
WebDriver driver = new FirefoxDriver();
driver.get("https://www.crowdanalytix.com/#home");
WebElement webElement = getWebElement(driver, "homekkkkkkkkkkkk");
int i = 1;
while (webElement == null && i < 4) {
webElement = getWebElement(driver, "homessssssssssss");
System.out.println("calling");
i++;
}
System.out.println(webElement.getTagName());
System.out.println("End");
driver.close();
}
public static WebElement getWebElement(WebDriver driver, String id) {
WebElement myDynamicElement = null;
try {
myDynamicElement = (new WebDriverWait(driver, 10))
.until(ExpectedConditions.presenceOfElementLocated(By
.id(id)));
return myDynamicElement;
} catch (TimeoutException ex) {
return null;
}
}
答案 12 :(得分:0)
要正确完成,您需要处理异常。
以下是我等待iFrame的方法。这要求您的JUnit测试类将RemoteWebDriver的实例传递给页面对象:
public class IFrame1 extends LoadableComponent<IFrame1> {
private RemoteWebDriver driver;
@FindBy(id = "iFrame1TextFieldTestInputControlID" )
public WebElement iFrame1TextFieldInput;
@FindBy(id = "iFrame1TextFieldTestProcessButtonID" )
public WebElement copyButton;
public IFrame1( RemoteWebDriver drv ) {
super();
this.driver = drv;
this.driver.switchTo().defaultContent();
waitTimer(1, 1000);
this.driver.switchTo().frame("BodyFrame1");
LOGGER.info("IFrame1 constructor...");
}
@Override
protected void isLoaded() throws Error {
LOGGER.info("IFrame1.isLoaded()...");
PageFactory.initElements( driver, this );
try {
assertTrue( "Page visible title is not yet available.", driver
.findElementByCssSelector("body form#webDriverUnitiFrame1TestFormID h1")
.getText().equals("iFrame1 Test") );
} catch ( NoSuchElementException e) {
LOGGER.info("No such element." );
assertTrue("No such element.", false);
}
}
@Override
protected void load() {
LOGGER.info("IFrame1.load()...");
Wait<WebDriver> wait = new FluentWait<WebDriver>( driver )
.withTimeout(30, TimeUnit.SECONDS)
.pollingEvery(5, TimeUnit.SECONDS)
.ignoring( NoSuchElementException.class )
.ignoring( StaleElementReferenceException.class ) ;
wait.until( ExpectedConditions.presenceOfElementLocated(
By.cssSelector("body form#webDriverUnitiFrame1TestFormID h1") ) );
}
....
答案 13 :(得分:0)
在找到页面上的任何元素之前,可以使用两个条件来检查页面是否已加载:
WebDriverWait wait = new WebDriverWait(driver, 50);
使用下面的redayState将等到页面加载
wait.until((ExpectedCondition<Boolean>) wd ->
((JavascriptExecutor) wd).executeScript("return document.readyState").equals("complete"));
JQuery下面将等到数据未加载
int count =0;
if((Boolean) executor.executeScript("return window.jQuery != undefined")){
while(!(Boolean) executor.executeScript("return jQuery.active == 0")){
Thread.sleep(4000);
if(count>4)
break;
count++;
}
}
在这些JavaScriptCode尝试findOut webElement之后。
WebElement we = wait.until(ExpectedConditions.presenceOfElementLocated(by));
答案 14 :(得分:0)
我喜欢您轮询HTML直到稳定的想法。我可以将其添加到我自己的解决方案中。以下方法在C#中使用,并且需要jQuery。
我是SuccessFactors(SaaS)测试项目的开发人员,在这里我们对开发人员或网页背后DOM的特征完全没有影响。 SaaS产品可能每年可能会更改其基础DOM设计4次,因此该搜索将永久寻求使用Selenium进行测试的可靠,高性能的方法(包括在可能的情况下不使用Selenium进行测试!)
这是我用于“页面准备”的内容。目前,它可以在我所有的测试中使用。几年前,相同的方法也适用于大型内部Java Web应用程序,并且在我离开该项目时已有超过一年的时间了。
Driver
是与浏览器通信的WebDriver实例DefaultPageLoadTimeout
是超时值,以滴答为单位(每滴答100ns)public IWebDriver Driver { get; private set; }
// ...
const int GlobalPageLoadTimeOutSecs = 10;
static readonly TimeSpan DefaultPageLoadTimeout =
new TimeSpan((long) (10_000_000 * GlobalPageLoadTimeOutSecs));
Driver = new FirefoxDriver();
接下来,请注意方法PageReady
中的等待顺序(准备好硒文件,Ajax,动画),如果考虑一下,这是很有意义的:
像DOM比较方法之类的东西可以在1到2之间使用,以增加另一层鲁棒性。
public void PageReady()
{
DocumentReady();
AjaxReady();
AnimationsReady();
}
private void DocumentReady()
{
WaitForJavascript(script: "return document.readyState", result: "complete");
}
private void WaitForJavascript(string script, string result)
{
new WebDriverWait(Driver, DefaultPageLoadTimeout).Until(
d => ((IJavaScriptExecutor) d).ExecuteScript(script).Equals(result));
}
private void AjaxReady()
{
WaitForJavascript(script: "return jQuery.active.toString()", result: "0");
}
private void AnimationsReady()
{
WaitForJavascript(script: "return $(\"animated\").length.toString()", result: "0");
}
答案 15 :(得分:0)
不知道该怎么做,但就我而言,页面加载和渲染的末尾与Firefox选项卡中显示的FAVICON相匹配。
因此,如果我们可以在Web浏览器中获得收藏夹图像,则表明网页已完全加载。
但是如何执行....
答案 16 :(得分:0)
使用隐式等待对我有用。
driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(10);
答案 17 :(得分:-1)
这是我的方法:
new WebDriverWait(driver, 20).until(
ExpectedConditions.jsReturnsValue(
"return document.readyState === 'complete' ? true : false"));