Selenium 等待文档准备好

有人能告诉我如何让硒等到页面完全加载的时候吗?我想要一些通用的东西,我知道我可以配置 WebDriverwait 并调用类似于“ find”的东西来让它等待,但是我没有走那么远。我只需要测试页面是否成功加载,然后转到下一个页面进行测试。

我在.net 中找到了一些东西,但是在 java 中无法工作..。

IWait<IWebDriver> wait = new OpenQA.Selenium.Support.UI.WebDriverWait(driver, TimeSpan.FromSeconds(30.00));
wait.Until(driver1 => ((IJavaScriptExecutor)driver).ExecuteScript("return document.readyState").Equals("complete"));

大家有什么想法吗?

408674 次浏览

这是您给出的示例的 Java 版本:

void waitForLoad(WebDriver driver) {
new WebDriverWait(driver, 30).until((ExpectedCondition<Boolean>) wd ->
((JavascriptExecutor) wd).executeScript("return document.readyState").equals("complete"));
}

例如 c # :

public static void WaitForLoad(IWebDriver driver, int timeoutSec = 15)
{
IJavaScriptExecutor js = (IJavaScriptExecutor)driver;
WebDriverWait wait = new WebDriverWait(driver, new TimeSpan(0, 0, timeoutSec));
wait.Until(wd => js.ExecuteScript("return document.readyState").ToString() == "complete");
}

PHP 示例:

final public function waitUntilDomReadyState(RemoteWebDriver $webDriver): void
{
$webDriver->wait()->until(function () {
return $webDriver->executeScript('return document.readyState') === 'complete';
});
}

试试这个代码:

  driver.manage().timeouts().pageLoadTimeout(10, TimeUnit.SECONDS);

上面的代码将等待页面加载长达10秒。如果页面加载超过时间,它将抛出 TimeoutException。您可以捕获异常并完成自己的需求。我不确定它是否在引发异常后退出页面加载。我还没试过这个代码。我想试试。

这是一个隐式等待。如果您设置了这一次,它将拥有作用域,直到 Web 驱动程序实例销毁。

有关更多信息,请参见 WebDriver.Timeouts的文件

您建议的解决方案只需等待 DOM readyState发出 complete信号。但是在默认情况下,Selenium 尝试通过 driver.get()element.click()方法等待页面加载中的那些(以及稍微多一点)。他们已经被阻塞,他们等待页面完全加载,这些应该可以正常工作。

很明显,问题在于通过 AJAX 请求和运行脚本进行重定向—— Selenium 无法捕获这些请求,它不会等到它们完成。另外,你不能可靠地通过 readyState捕获它们-它等待一点,这可能是有用的,但它会信号 complete远在所有的 AJAX 内容被下载。

没有一个通用的解决方案可以在任何地方适用于每个人,这就是为什么它很难,每个人都使用一些不同的东西。

通常的规则是依赖 WebDriver 完成他的部分,然后使用隐式等待,然后使用显式等待您希望在页面上断言的元素,但是还有更多的技术可以完成。您应该在您的测试页面上选择一个(或多个组合)在您的情况下最有效的方法。

有关更多信息,请参见我的两个答案:

public void waitForPageToLoad()
{
(new WebDriverWait(driver, DEFAULT_WAIT_TIME)).until(new ExpectedCondition<Boolean>() {
public Boolean apply(WebDriver d) {
return (((org.openqa.selenium.JavascriptExecutor) driver).executeScript("return document.readyState").equals("complete"));
}
});//Here DEFAULT_WAIT_TIME is a integer correspond to wait time in seconds

您可以编写一些逻辑来处理这个问题。我写了一个返回 WebElement的方法,这个方法将被调用三次,或者你可以增加时间并为 WebElement添加一个 null 检查。这里有一个例子

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

下面是我在 Python 中尝试的一个完全通用的解决方案:

首先,一个通用的“ wait”函数(如果你愿意,可以使用 WebDriverwait,我觉得它们很丑陋) :

def wait_for(condition_function):
start_time = time.time()
while time.time() < start_time + 3:
if condition_function():
return True
else:
time.sleep(0.1)
raise Exception('Timeout waiting for {}'.format(condition_function.__name__))

接下来,解决方案依赖于这样一个事实,即 selenium 为页面上的所有元素(包括顶级 <html>元素)记录一个(内部) id-number。当页面刷新或加载时,它将获得一个具有新 ID 的新 html 元素。

因此,假设你想点击一个带有文本“ my link”的链接,例如:

old_page = browser.find_element_by_tag_name('html')


browser.find_element_by_link_text('my link').click()


def page_has_loaded():
new_page = browser.find_element_by_tag_name('html')
return new_page.id != old_page.id


wait_for(page_has_loaded)

对于更多 Python 的、可重用的、通用的助手,您可以创建一个上下文管理器:

from contextlib import contextmanager


@contextmanager
def wait_for_page_load(browser):
old_page = browser.find_element_by_tag_name('html')


yield


def page_has_loaded():
new_page = browser.find_element_by_tag_name('html')
return new_page.id != old_page.id


wait_for(page_has_loaded)

然后你就可以把它用在几乎所有的硒相互作用中:

with wait_for_page_load(browser):
browser.find_element_by_link_text('my link').click()

我觉得那是防弹的,你觉得呢?

更多信息在 关于它的博客文章在这里

在 Ruby 中也有类似的东西:

wait = Selenium::WebDriver::Wait.new(:timeout => 10)
wait.until { @driver.execute_script('return document.readyState').eql?('complete') }

对于初始页面加载,我注意到“最大化”浏览器窗口实际上等待页面加载完成(包括源)

更换:

AppDriver.Navigate().GoToUrl(url);

配合:

public void OpenURL(IWebDriver AppDriver, string Url)
{
try
{
AppDriver.Navigate().GoToUrl(Url);
AppDriver.Manage().Window.Maximize();
AppDriver.SwitchTo().ActiveElement();
}
catch (Exception e)
{
Console.WriteLine("ERR: {0}; {1}", e.TargetSite, e.Message);
throw;
}
}

使用:

OpenURL(myDriver, myUrl);

这将加载页面,等待,直到完成,最大化和关注它。我不知道为什么是这样,但它的工作原理。

如果你想等待页面加载后点击下一步或任何其他网页导航触发器然后“导航()”,本戴尔的答案(在这个线程)将做的工作。

我也有过类似的问题。我需要等到文档准备好,但也要等到所有 Ajax 调用完成。第二种情况被证明是难以检测的。最后,我检查了活动的 Ajax 调用,它起作用了。

Javascript:

return (document.readyState == 'complete' && jQuery.active == 0)

完整 C # 方法:

private void WaitUntilDocumentIsReady(TimeSpan timeout)
{
var javaScriptExecutor = WebDriver as IJavaScriptExecutor;
var wait = new WebDriverWait(WebDriver, timeout);


// Check if document is ready
Func<IWebDriver, bool> readyCondition = webDriver => javaScriptExecutor
.ExecuteScript("return (document.readyState == 'complete' && jQuery.active == 0)");
wait.Until(readyCondition);
}

看看 挂毯网络框架。你可以在那里 下载源代码

其思想是通过 body 的 html 属性来表示页面已经准备好了。你可以用这个想法忽略复杂的起诉案件。

<html>
<head>
</head>
<body data-page-initialized="false">
<p>Write you page here</p>


<script>
$(document).ready(function () {
$(document.body).attr('data-page-initialized', 'true');
});
</script>
</body>
</html>

然后创建 Selenium 网络驱动程序的扩展(根据挂毯框架)

public static void WaitForPageToLoad(this IWebDriver driver, int timeout = 15000)
{
//wait a bit for the page to start loading
Thread.Sleep(100);


//// In a limited number of cases, a "page" is an container error page or raw HTML content
// that does not include the body element and data-page-initialized element. In those cases,
// there will never be page initialization in the Tapestry sense and we return immediately.
if (!driver.ElementIsDisplayed("/html/body[@data-page-initialized]"))
{
return;
}


Stopwatch stopwatch = Stopwatch.StartNew();


int sleepTime = 20;


while(true)
{
if (driver.ElementIsDisplayed("/html/body[@data-page-initialized='true']"))
{
return;
}


if (stopwatch.ElapsedMilliseconds > 30000)
{
throw new Exception("Page did not finish initializing after 30 seconds.");
}


Thread.Sleep(sleepTime);
sleepTime *= 2; // geometric row of sleep time
}
}

使用扩展 ElementIsDisplay作者是阿利斯特 · 斯科特

public static bool ElementIsDisplayed(this IWebDriver driver, string xpath)
{
try
{
return driver.FindElement(By.XPath(xpath)).Displayed;
}
catch(NoSuchElementException)
{
return false;
}
}

最后创建测试:

driver.Url = this.GetAbsoluteUrl("/Account/Login");
driver.WaitForPageToLoad();

在重新加载页面之前,线程可以一直处于休眠状态。这不是最好的解决方案,因为您需要估计加载页面需要多长时间。

driver.get(homeUrl);
Thread.sleep(5000);
driver.findElement(By.xpath("Your_Xpath_here")).sendKeys(userName);
driver.findElement(By.xpath("Your_Xpath_here")).sendKeys(passWord);
driver.findElement(By.xpath("Your_Xpath_here")).click();
WebDriverWait wait = new WebDriverWait(dr, 30);
wait.until(ExpectedConditions.jsReturnsValue("return document.readyState==\"complete\";"));

Ben Dryer 的答案没有在我的机器上编译("The method until(Predicate<WebDriver>) is ambiguous for the type WebDriverWait")。

工作 Java8版本:

Predicate<WebDriver> pageLoaded = wd -> ((JavascriptExecutor) wd).executeScript(
"return document.readyState").equals("complete");
new FluentWait<WebDriver>(driver).until(pageLoaded);

Java7版本:

Predicate<WebDriver> pageLoaded = new Predicate<WebDriver>() {


@Override
public boolean apply(WebDriver input) {
return ((JavascriptExecutor) input).executeScript("return document.readyState").equals("complete");
}


};
new FluentWait<WebDriver>(driver).until(pageLoaded);

我执行了一个 javascript 代码来检查文档是否准备好了。为具有客户端呈现的站点调试硒测试节省了大量时间。

public static boolean waitUntilDOMIsReady(WebDriver driver) {
def maxSeconds = DEFAULT_WAIT_SECONDS * 10
for (count in 1..maxSeconds) {
Thread.sleep(100)
def ready = isDOMReady(driver);
if (ready) {
break;
}
}

}

public static boolean isDOMReady(WebDriver driver){
return driver.executeScript("return document.readyState");
}

我试过这个代码,它对我很有用。每次我移动到另一个页面时,我都会调用这个函数

public static void waitForPageToBeReady()
{
JavascriptExecutor js = (JavascriptExecutor)driver;


//This loop will rotate for 100 times to check If page Is ready after every 1 second.
//You can replace your if you wants to Increase or decrease wait time.
for (int i=0; i<400; i++)
{
try
{
Thread.sleep(1000);
}catch (InterruptedException e) {}
//To check page ready state.


if (js.executeScript("return document.readyState").toString().equals("complete"))
{
break;
}
}
}

在 Nodejs 你可以通过承诺得到它..。

如果您编写此代码,则可以确保当您到达然后..。

driver.get('www.sidanmor.com').then(()=> {
// here the page is fully loaded!!!
// do your stuff...
}).catch(console.log.bind(console));

如果您编写此代码,您将导航,硒将等待3秒..。

driver.get('www.sidanmor.com');
driver.sleep(3000);
// you can't be sure that the page is fully loaded!!!
// do your stuff... hope it will be OK...

来自 Selenium 文档:

This. get (url)→ Thable

计划命令导航到给定的 URL。

返回将在文档具有 装完了时解决的承诺。

Selenium 文档(Nodejs)

你在用 Angular 吗?如果你是有可能的,网络驱动程序不承认异步调用已经完成。

我建议看看 Paul Hammants ngWebDriver。 方法 wait ForAngularRequestsToFinish ()可能会派上用场。

public boolean waitForElement(String zoneName, String element, int index, int timeout) {
WebDriverWait wait = new WebDriverWait(appiumDriver, timeout/1000);
wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath(element)));
return true;
}

对于 C # NUnit,您需要将 WebDriver 转换为 JSExecter,然后执行脚本以检查 document.ready 状态是否已完成。参考以下代码:

 public static void WaitForLoad(IWebDriver driver)
{
IJavaScriptExecutor js = (IJavaScriptExecutor)driver;
int timeoutSec = 15;
WebDriverWait wait = new WebDriverWait(driver, new TimeSpan(0, 0, timeoutSec));
wait.Until(wd => js.ExecuteScript("return document.readyState").ToString() == "complete");
}

这将一直等到满足条件或超时为止。

等待 document.ready 事件并不是解决这个问题的全部方法,因为这段代码仍然处于竞争状态: 有时这段代码在处理 click 事件之前被激发,所以这段代码直接返回,因为浏览器还没有开始加载新页面。

经过一番搜索,我发现了一个关于 服从测试的山羊的帖子,其中有一个解决这个问题的办法。该解决方案的 c # 代码是这样的:

 IWebElement page = null;
...
public void WaitForPageLoad()
{
if (page != null)
{
var waitForCurrentPageToStale = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
waitForCurrentPageToStale.Until(ExpectedConditions.StalenessOf(page));
}


var waitForDocumentReady = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
waitForDocumentReady.Until((wdriver) => (driver as IJavaScriptExecutor).ExecuteScript("return document.readyState").Equals("complete"));


page = driver.FindElement(By.TagName("html"));


}

` 我直接在 driver.Navate.gotourl 之后触发这个方法,以便它尽快获得页面的引用。好好享受吧!

就像 Rubanov 为 C # 写的,我为 Java 写的,它是:

    public void waitForPageLoaded() {
ExpectedCondition<Boolean> expectation = new
ExpectedCondition<Boolean>() {
public Boolean apply(WebDriver driver) {
return (((JavascriptExecutor) driver).executeScript("return document.readyState").toString().equals("complete")&&((Boolean)((JavascriptExecutor)driver).executeScript("return jQuery.active == 0")));
}
};
try {
Thread.sleep(100);
WebDriverWait waitForLoad = new WebDriverWait(driver, 30);
waitForLoad.until(expectation);
} catch (Throwable error) {
Assert.fail("Timeout waiting for Page Load Request to complete.");
}
}

在 Java 中,它会像下面这样:-

  private static boolean isloadComplete(WebDriver driver)
{
return ((JavascriptExecutor) driver).executeScript("return document.readyState").equals("loaded")
|| ((JavascriptExecutor) driver).executeScript("return document.readyState").equals("complete");
}

通常情况下,当 selenium 通过点击或提交或获取方法打开一个新的页面时,它会等待,不知道页面已经加载,但问题是,当页面有一个 xhr 调用(ajax)时,它永远不会等待 xhr 被加载,所以创建一个新的方法来监视 xhr 并等待它们将是好的。

public boolean waitForJSandJQueryToLoad() {
WebDriverWait wait = new WebDriverWait(webDriver, 30);
// wait for jQuery to load
ExpectedCondition<Boolean> jQueryLoad = new ExpectedCondition<Boolean>() {
@Override
public Boolean apply(WebDriver driver) {
try {
Long r = (Long)((JavascriptExecutor)driver).executeScript("return $.active");
return r == 0;
} catch (Exception e) {
LOG.info("no jquery present");
return true;
}
}
};


// wait for Javascript to load
ExpectedCondition<Boolean> jsLoad = new ExpectedCondition<Boolean>() {
@Override
public Boolean apply(WebDriver driver) {
return ((JavascriptExecutor)driver).executeScript("return document.readyState")
.toString().equals("complete");
}
};


return wait.until(jQueryLoad) && wait.until(jsLoad);
}

if $.active == 0所以没有活动的 xhrs 调用(只能用于 jQuery)。 对于 javascript ajax 调用,必须在项目中创建一个变量并进行模拟。

下面的代码应该可以正常工作:

WebDriverWait wait = new WebDriverWait(driver, 10);
wait.until(ExpectedConditions.presenceOfAllElementsLocated(By.xpath("//*")));

我检查页面加载完成,工作在 Selenium 3.14.0

    public static void UntilPageLoadComplete(IWebDriver driver, long timeoutInSeconds)
{
Until(driver, (d) =>
{
Boolean isPageLoaded = (Boolean)((IJavaScriptExecutor)driver).ExecuteScript("return document.readyState").Equals("complete");
if (!isPageLoaded) Console.WriteLine("Document is loading");
return isPageLoaded;
}, timeoutInSeconds);
}


public static void Until(IWebDriver driver, Func<IWebDriver, Boolean> waitCondition, long timeoutInSeconds)
{
WebDriverWait webDriverWait = new WebDriverWait(driver, TimeSpan.FromSeconds(timeoutInSeconds));
webDriverWait.Timeout = TimeSpan.FromSeconds(timeoutInSeconds);
try
{
webDriverWait.Until(waitCondition);
}
catch (Exception e)
{
Console.WriteLine(e);
}
}

对于那些需要等待特定元素出现的人来说

public static void WaitForElement(IWebDriver driver, By element)
{
WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(20));
wait.Until(ExpectedConditions.ElementIsVisible(element));
}

然后,如果您想等待,例如,如果类 = “ error-message”存在于 DOM 中,您只需要:

WaitForElement(driver, By.ClassName("error-message"));

对于身份证,它将是

WaitForElement(driver, By.Id("yourid"));

如果您的页面或网络连接速度很慢,那么上述方法很可能都不起作用。我都试过了,唯一对我有用的就是等待页面上最后一个可见的元素。以必应网页为例。他们在主搜索按钮旁边放置了一个 CAMERA 图标(通过图像搜索按钮) ,这个按钮只有在整个页面加载后才能看到。如果每个人都这样做了,那么我们所要做的就是像上面的例子一样使用一个显式的 wait。