How to get webDriver to wait for page to load (C# Selenium project)
Asked Answered
L

10

32

I've started a Selenium project in C#. Trying to wait for page to finish loading up and only afterwards proceed to next action.

My code looks like this:

 loginPage.GoToLoginPage();
        loginPage.LoginAs(TestCase.Username, TestCase.Password);
        loginPage.SelectRole(TestCase.Orgunit);
        loginPage.AcceptRole();

inside loginPage.SelectRole(TestCase.Orgunit):

 RoleHierachyLabel = CommonsBasePage.Driver.FindElement(By.XPath("//span[contains(text(), " + role + ")]"));
 RoleHierachyLabel.Click();
 RoleLoginButton.Click();

I search for element RoleHierachyLabel. I've been trying to use multiple ways to wait for page to load or search for an element property allowing for some timeout:

1. _browserInstance.Manage().Timeouts().ImplicitlyWait(TimeSpan.FromSeconds(5));

2. public static bool WaitUntilElementIsPresent(RemoteWebDriver driver, By by, int timeout = 5)
    {
        for (var i = 0; i < timeout; i++)
        {
            if (driver.ElementExists(by)) return true;
        }
        return false;
    }

How would you tackle this obstacle?

Lifer answered 4/4, 2017 at 9:27 Comment(1)
Possible duplicate of Wait for page load in SeleniumSheepfold
L
48

I've been searching for alternatives and I've settled for the following versions. All use explicit wait with a defined timeout and are based on element properties in the first case and on element staleness in the second case.

First choice would be checking element properties until a timeout is reached. I've arrived to the following properties that confirm it is available on the page:

Existence - An expectation for checking that an element is present on the DOM of a page. This does not necessarily mean that the element is visible.

//this will not wait for page to load
Assert.True(Driver.FindElement(By elementLocator).Enabled)

//this will search for the element until a timeout is reached
public static IWebElement WaitUntilElementExists(By elementLocator, int timeout = 10)
    {
        try
        {
            var wait = new WebDriverWait(Driver, TimeSpan.FromSeconds(timeout));
            return wait.Until(ExpectedConditions.ElementExists(elementLocator));
        }
        catch (NoSuchElementException)
        {
            Console.WriteLine("Element with locator: '" + elementLocator + "' was not found in current context page.");
            throw;
        }
    }

Visibility - An expectation for checking that an element is present on the DOM of a page and visible. Visibility means that the element is not only displayed but also has a height and width that is greater than 0.

//this will not wait for page to load
Assert.True(Driver.FindElement(By elementLocator).Displayed)

//this will search for the element until a timeout is reached
public static IWebElement WaitUntilElementVisible(By elementLocator, int timeout = 10)
    {
        try
        {
            var wait = new WebDriverWait(Driver, TimeSpan.FromSeconds(timeout));
            return wait.Until(ExpectedConditions.ElementIsVisible(elementLocator));
        }
        catch (NoSuchElementException)
        {
            Console.WriteLine("Element with locator: '" + elementLocator + "' was not found.");
            throw;
        }
    }

Clickable - An expectation for checking an element is visible and enabled such that you can click it.

//this will not wait for page to load
//both properties need to be true in order for element to be clickable
Assert.True(Driver.FindElement(By elementLocator).Enabled)
Assert.True(Driver.FindElement(By elementLocator).Displayed)

//this will search for the element until a timeout is reached
public static IWebElement WaitUntilElementClickable(By elementLocator, int timeout = 10)
    {
        try
        {
            var wait = new WebDriverWait(Driver, TimeSpan.FromSeconds(timeout));
            return wait.Until(ExpectedConditions.ElementToBeClickable(elementLocator));
        }
        catch (NoSuchElementException)
        {
            Console.WriteLine("Element with locator: '" + elementLocator + "' was not found in current context page.");
            throw;
        }
    }

Second choice applies when the trigger object, for example a menu item, is no longer attached to the DOM after it is clicked. This is ususally the case when click action on the element will trigger a redirect to another page. In this case it's usefull to check StalenessOf(element) where element is the item that was clicked to trigger the redirect to the new page.

public static void ClickAndWaitForPageToLoad(By elementLocator, int timeout = 10)
    {
        try
        {
            var wait = new WebDriverWait(Driver, TimeSpan.FromSeconds(timeout));
            var element = Driver.FindElement(elementLocator);
            element.Click();
            wait.Until(ExpectedConditions.StalenessOf(element));
        }
        catch (NoSuchElementException)
        {
            Console.WriteLine("Element with locator: '" + elementLocator + "' was not found in current context page.");
            throw;
        }
    }
Lifer answered 21/7, 2017 at 9:29 Comment(5)
Excellent and thorough answer! Is there a way to wait for a popup window to load ?Arleyne
Thank you @Lars! Pop-up interaction could be treated in some different ways: first thought would be using the WindowHandles to find your pop-up and with driver.SwitchTo() interact with it.Lifer
On no longer wanting to depend on ExpectedConditions in my C# Selenium WebDriver tests ontestautomation.com/…Oilbird
ExpectedConditions is obsolete. Could use lambda instead: Wait.Until(x => x.FindElement(By.Id("xxx"))); Wait.Until() ignores ‵NoSuchElementException` by default.Wooton
ExpectedConditions being obsolete, you can use lambda as suggested above, or use DotNetSeleniumExtras as described in this post: #49866834 tldr; Install-Package DotNetSeleniumExtras.WaitHelpers -Version 3.11.0Zwickau
R
9

driver.Manage().Timeouts().PageLoad = TimeSpan.FromSeconds(5);

Also, see this answer

Refugee answered 21/6, 2017 at 20:51 Comment(2)
you should explain what this does exactly. For example: how is this different than ImplicitWait?Saltant
Not always useful; you might not want the user to expect the pages to stop loading fully only because a timeout interval was set. Plus, you would not have the time to research the expected load times across various machines, browsers and internet connection constraints.Nightdress
S
3

I usually use an explicit wait for this, and wait until an elements is visible, then proceed to the next action. This should look like this:

WebDriverWait waitForElement = new WebDriverWait(driver, TimeSpan.FromSeconds(5));
waitForElement.Until(ExpectedConditions.ElementIsVisible(By.Id("yourIDHere")));

More on Explicit waits here: Explicit waits Selenium C# and here WebDriver Explicit waits

Sinistrality answered 4/4, 2017 at 9:47 Comment(1)
The ExpectedConditions implementation in the .NET bindings is deprecated and will be removed in a future release.Obsequent
U
3

Just had the same problem. With the folowing Method I wait for the page to load fully, not relying on causing the page load by JavaScript, a click or an action on an input element.

private void WaitForPageToLoad(Action doing)
{
    IWebElement oldPage = _driver.FindElement(By.TagName("html"));
    doing();
    WebDriverWait wait = new WebDriverWait(_driver, new TimeSpan(0, 0, Timeout));
    try
    {
        wait.Until(driver => ExpectedConditions.StalenessOf(oldPage)(_driver) &&
            ((IJavaScriptExecutor)driver).ExecuteScript("return document.readyState").Equals("complete"));
    }
    catch (Exception pageLoadWaitError)
    {
        throw new TimeoutException("Timeout during page load", pageLoadWaitError);
    }
}

called like following

WaitForPageToLoad(() => _driver.FindElement(By.Id("button1")).Click());
Uam answered 27/11, 2020 at 9:32 Comment(0)
B
1

I did this to address this type of issue. It's a combination of timers and loops that are looking for a specific element until it timesout after a certain number of milliseconds.

private IWebElement FindElementById(string id, int timeout = 1000)
{
    IWebElement element = null;

    var s = new Stopwatch();
    s.Start();

    while (s.Elapsed < TimeSpan.FromMilliseconds(timeout))
    {
        try
        {
            element = _driver.FindElementById(id);
            break;
        }
        catch (NoSuchElementException)
        {
        }
    }

    s.Stop();
    return element;
}

I also made one for element enabled

private IWebElement ElementEnabled(IWebElement element, int timeout = 1000)
{
    var s = new Stopwatch();
    s.Start();

    while (s.Elapsed < TimeSpan.FromMilliseconds(timeout))
    {
        if (element.Enabled)
        {
            return element;
        }
    }

    s.Stop();
    return null;
}
Bannock answered 28/8, 2018 at 0:20 Comment(3)
Depending on your system setup, you might need to increase the available ports or reduce the timeout. What I did instead to prevent port saturation is by adding a Thread.Sleep(200); at the end of inside loops so it doesn't make so many calls as fast as it can and use up all the ports. This means you'll be checking for your elements 5 times a second instead of something like 30 times a second.Bannock
@StephenRauch: Looking good! IWebElement will return an object, how to you go by obtaining its path? For assertion purposes element.getAbsoluteXpath() element.getRelativeXpath() could be a good set of candidates.Lifer
@clau84 I'm not sure what you mean because when you are calling element.getAbsoluteXpath() you already have the element and it is not null. That means you already grabbed that IWebElement through some means of Id, classname, xpath, etc.Bannock
S
1

As said in Wait for page load in Selenium:

In general, with Selenium 2.0 the web driver should only return control to the calling code once it has determined that the page has loaded. If it does not, you can call waitforelemement, which cycles round calling findelement until it is found or times out (time out can be set).

Sheepfold answered 28/4, 2019 at 5:15 Comment(0)
G
0

Because of its simplicity I like this solution. Also it has the benefit of avoiding excessive waits and taking the guess work out of what might be the upper wait limit:

    public bool WaitToLoad(By by)
    {
        int i = 0;
        while (i < 600)
        {
            i++;
            Thread.Sleep(100); // sleep 100 ms
            try
            {
                driver.FindElement(by);
                break;
            }
            catch { }
        }
        if (i == 600) return false; // page load failed in 1 min
        else return true;
    }

Which can be modified to also include a "timer" if monitoring page load latency is needed:

    public int WaitToLoad(By by)
    {
        int i = 0;
        while (i < 600)
        {
            i++;
            Thread.Sleep(100); // sleep 100 ms
            try
            {
                driver.FindElement(by);
                break;
            }
            catch { }
        }
        return i; // page load latency in 1/10 secs
    }
Griseldagriseldis answered 22/6, 2019 at 0:17 Comment(1)
Why was this downvoted? Even though Thread.Sleep and polling isn't the greatest way to accomplish the task, this seems like a reasonable option...Banksia
A
0

You just need to import SeleniumExtras package via NuGet, then using it like the following :

var e = new WebDriverWait(driver, new TimeSpan(0, 0, 60)).Until(SeleniumExtras.WaitHelpers.ExpectedConditions.ElementIsVisible(By.Id("--id")));
Aweless answered 4/8, 2021 at 20:37 Comment(0)
S
0

This answer is for those who wants the simple solution they can understand. It is working for me.

private void WaitForLoad(IWebDriver driver, string findBy, string value)
{
    bool load=false;
    while (!load)
    {
        try
        {
            Thread.Sleep(1000);
            if(findBy == "ID")
            {
                driver.FindElement(By.Id(value));
            }
            else
            {
                driver.FindElement(By.Name(value));
            }
            load = true;
        }
        catch (Exception ex)
        {
        }
    }
}

Note: you can change Sleep Time as per your requirement.

You can call this function like this:

WaitForLoad(driver, "NAME", "emailOrUsername");

IWebElement uname = driver.FindElement(By.Name("emailOrUsername"));
Smithery answered 16/3, 2023 at 9:29 Comment(0)
M
-4

All anwsers without any sence here. Page could contain all controls. But you change something and data in controls changing and page goes in reload state. So if you continue working with this page you get a bunch of errors. Really I don't see better solution then Thread.Sleep()

Mortification answered 6/7, 2021 at 12:18 Comment(1)
Then you are not validating the right attributes, find then the attributes that change only when your action is performed.Lifer

© 2022 - 2024 — McMap. All rights reserved.