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

邮差的信 提交于 2019-11-28 20:42:04

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

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

Also, see this answer

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

Ben Williams

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

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).

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
    }
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!