2

I need to implement & modify my current method to do both matters:

  • Waiting for element of being visible - (which is currently implemented but it still uses ExpectedCondition which is obsolete and might be changed)
  • Waiting for element of being clickable - (When my test are running, it's face a problems with "loading circles", I'm pressing the button and before window is loaded there is loader which takes for 1 to 4 seconds and then it disappears. My goal is to force Selenium to wait "XXX" time and when loading will finish then continue with process.

Current code:

public static IWebElement WaitForElementVisible(this IWebDriver driver, By by, int timeoutInSeconds = 6)
    {
        IWebElement element;
        driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(0);
        try
        {
            WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(timeoutInSeconds));
            element = wait.Until(ExpectedConditions.ElementIsVisible(by));
            driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(Configuration.ElementTimeout);
            return element;

        }
        catch (WebDriverTimeoutException e)
        {
            Console.WriteLine(e.ToString());
        }
        driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(Configuration.ElementTimeout);
        return null;
    }

Based on different topics I've started writing something but unfortunately it's just copy...

I would prefer to do one valuable method which goal would be to check both things. Could you please give me any advice's/ hints what could be added to that specific method?

//Edit

I'm adding how that loader code looks like:

    <div class="Loader__background" style="display: block; position: absolute; top: 0px; left: 0px; width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.5); z-index: 10;">
   <div class="Loader__foreground" style="display: table; width: 100%; height: 100%; text-align: center; z-index: 20; color: white;">
      <div class="Loader__message" style="display: table-cell; vertical-align: middle;">
         <div mode="indeterminate" value="0" min="0" max="100" style="position: relative; display: inline-block; width: 280px; height: 280px;">
            <div style="width: 280px; height: 280px; display: inline-block; transition: transform 10s linear 0ms; transform: rotate(1800deg);">
               <svg viewBox="0 0 280 280" style="width: 280px; height: 280px; position: relative;">
                  <circle cx="140" cy="140" r="137.5" fill="none" stroke-width="5" stroke-miterlimit="20" style="stroke: rgb(0, 188, 212); stroke-linecap: round; transition: all 750ms ease-in-out 0ms; stroke-dasharray: 604.757, 863.938; stroke-dashoffset: -259.181;"></circle>
               </svg>
            </div>
         </div>
      </div>
   </div>
</div>

I've also transferred @sers Java advice into C#:

public static IWebElement WaitForElementClickable(this IWebDriver driver, By by, int timeoutInSeconds)
    {
        new WebDriverWait(driver, TimeSpan.FromSeconds(timeoutInSeconds)).Until(d =>
        {
            Boolean ajaxComplete;
            Boolean jsReady;
            Boolean loaderHidden = false;

            IJavaScriptExecutor js = (IJavaScriptExecutor)d;
            jsReady = (Boolean)js.ExecuteScript("return (document.readyState == \"complete\" || document.readyState == \"interactive\")"); ;

            try
            {
                ajaxComplete = (Boolean)js.ExecuteScript("var result = true; try { result = (typeof jQuery != 'undefined') ? jQuery.active == 0 : true } catch (e) {}; return result;");
            }
            catch (Exception)
            {
                ajaxComplete = true;
            }
            try
            {
                loaderHidden = !d.FindElement(by).Displayed;
            }
            catch (Exception) { }

            return ajaxComplete && jsReady && loaderHidden;
        });
    }
Ixi11
  • 219
  • 2
  • 14

2 Answers2

1

The ExpectedConditions in .NET binding is obsolete, however it was moved to DotNetSeleniumExtras

'ExpectedConditions' is obsolete: 'The ExpectedConditions implementation in the .NET bindings is deprecated and will be removed in a future release. This portion of the code has been migrated to the DotNetSeleniumExtras repository on GitHub (https://github.com/DotNetSeleniumTools/DotNetSeleniumExtras)'

Use ExpectedConditions from there, it will dismiss the warning

using WaitHelpers = SeleniumExtras.WaitHelpers;

wait.Until(WaitHelpers.ExpectedConditions.ElementIsVisible(by));

You can also use ExpectedConditions to wait for the element to be clickable

IWebElement webElement = wait.Until(WaitHelpers.ExpectedConditions.ElementToBeClickable(by));
webElement.Click();

Another option is to wait for the loader to appear and then disappear and then continue

wait.Until(WaitHelpers.ExpectedConditions.ElementIsVisible(by));   
wait.Until(WaitHelpers.ExpectedConditions.InvisibilityOfElementLocated(by));
Guy
  • 46,488
  • 10
  • 44
  • 88
  • Where I'm supposed to add: using WaitHelpers = SeleniumExtras.WaitHelpers? IDE is not compiling + I cannot add anything in order to detect it – Ixi11 Aug 23 '18 at 10:38
  • @Ixi11 Its 'using', put it in the top with the rest of the 'using'. – Guy Aug 23 '18 at 10:50
  • I removed DotNetSeleniumExtras after updating to Selenium 4: https://stackoverflow.com/a/69128074/8963597 Then how? – Brian Hong Mar 22 '22 at 21:23
1

As I understood your element is visible and clickable when loading element on the screen but overlaying you element, also maybe you need to wait javascript to complete to click successfully.

You need get "loading circles" locator. For that open chrome devtools trigger "loading circles" to appear and press F8(pause) then you can find html of loading element.

Wait until loading element is disappeared:

var wait = new WebDriverWait(Driver.Instance, TimeSpan.FromSeconds(8));
wait.until(ExpectedConditions.invisibilityOfElementLocated(loadingElementLocator);

Also you can check if javascript is complete :

IJavaScriptExecutor js = (IJavaScriptExecutor)driver;
bool jsLoaded = (bool)js.ExecuteScript("return (document.readyState == \"complete\" || document.readyState == \"interactive\")");

Here Java example:

new WebDriverWait(driver, timeoutSec).until(d ->
{
    boolean ajaxComplete;
    boolean jsReady;
    boolean loaderHidden = false;

    JavascriptExecutor js = (JavascriptExecutor) d;
    jsReady = (boolean) js.executeScript("return (document.readyState == \"complete\" || document.readyState == \"interactive\")");;

    try {
        ajaxComplete = (boolean) js.executeScript("var result = true; try { result = (typeof jQuery != 'undefined') ? jQuery.active == 0 : true } catch (e) {}; return result;");
    } catch (Exception ignored) {
        ajaxComplete = true;
    }

    try {
        loaderHidden = !d.findElement(loadElementLocator).isDisplayed();
    } catch (Exception ignored) {}

    return ajaxComplete && jsReady && loaderHidden;
});

Here your updated code:

public static void WaitForLoading(IWebDriver driver, int timeoutInSeconds)
    {
        new WebDriverWait(driver, TimeSpan.FromSeconds(timeoutInSeconds)).Until(d =>
        {
            Boolean ajaxComplete;
            Boolean jsReady;
            Boolean loaderHidden = false;

            IJavaScriptExecutor js = (IJavaScriptExecutor)d;
            jsReady = (Boolean)js.ExecuteScript("return (document.readyState == \"complete\" || document.readyState == \"interactive\")"); ;

            try
            {
                ajaxComplete = (Boolean)js.ExecuteScript("var result = true; try { result = (typeof jQuery != 'undefined') ? jQuery.active == 0 : true } catch (e) {}; return result;");
            }
            catch (Exception)
            {
                ajaxComplete = true;
            }
            try
            {
                loaderHidden = !d.FindElement(By.ClassName("Loader__background")).Displayed;
            }
            catch (Exception) { }

            return ajaxComplete && jsReady && loaderHidden;
        });
    }

How to use:

WaitForLoading(driver, 10);
myButton.Click();
Sers
  • 12,047
  • 2
  • 12
  • 31
  • 1
    I've updated my post with both a) "circle load" DOM b) code witch was transferred from JAVA to C#. I think you may have right with that js locator however still I don't really understand what I'm doing. Where I'm supposed to set that circle loader inside the code? – Ixi11 Aug 23 '18 at 10:02
  • Also compiler is arguing that my method does not return anything – Ixi11 Aug 23 '18 at 10:06
  • I added locator of loader: `By.ClassName("Loader__background")`, try it – Sers Aug 23 '18 at 10:06
  • Make method void and call before you click, use it as wait loading to complete. Also if loading appears every where you can implement `WebDriverEventListener` listener and call this method inside `beforeFindBy`. – Sers Aug 23 '18 at 11:10