iFrame in Selenium C#

An iFrame is an embedded document that is inside another HTML document. You can normally see an iframe in the html tree under or in a <iframe> tag. Usually the first sign there is an iframe in your AUT is when selenium is not detecting a web element and you don’t know why. They are a bit tricky because you will need to instruct selenium to switch to it in order to access its web elements. Then you’ll need to switch back if you need to access elements outside of that iframe. There are some web applications that make matters worse when their iframes do not have id properties or generate a random one. But we’ll talk about that later. In Selenium C#, in order to handle these frames there are several methods we can use with IWebDriver.SwitchTo().

  • SwitchTo().DefaultContent() – Selects either the first frame on the page or the main document when a page contains iFrames.
  • SwitchTo().Frame(int frameIndex) – Select a frame by its (zero-based) index.
  • SwitchTo().Frame(IWebElement frameElement) – Select a frame using its previously located OpenQA.Selenium.IWebElement.
  • SwitchTo().Frame(string frameName) – Select a frame by its name or ID.
  • SwitchTo().ParentFrame() – Select the parent frame of the currently selected frame.

More details can be found in the interface ITargetLocator of Selenium.

Switch to a frame by Index

Before we can learn to switch to an iframe by index, we first need to know how many and which iframe to switch to. This approach can also help you if the iframe id/name is not given or if they are auto generated values.

 

List<IWebElement> frames = new List<IWebElement>(driver.FindElements(By.TagName("iframe")));
Console.WriteLine("Number of Frames: " + frames.Count);
for (int i = 0; i < frames.Count; i++)
{
Console.WriteLine("frame[" + i + "]: " + frames[i].GetAttribute("id").ToString());
}

Now that we know which iframe we want, we can then simply pass the index.

driver.SwitchTo().Frame(0);

Switch to a frame by IWebElement

IWebElement iframe = driver.FindElement(By.Id("iframe1"));
driver.SwitchTo().Frame(iframe);

 

Alternately we can use the first or last syntax from the List we created above.

</pre>
<pre>List<IWebElement> frames = new List<IWebElement>(driver.FindElements(By.TagName("iframe")));
driver.SwitchTo().Frame(frames.First());
driver.SwitchTo().Frame(frames.Last())

 

 

 Switch to a frame by Name of ID

driver.SwitchTo().Frame(("iframe1");

Switch back to the default document or the parent frame

In order to access the elements in the default document you’ll need to switch back out if you are currently in an iframe.

driver.SwitchTo().DefaultContent();

 

If the AUT has nested iframes, you can switch back out to the parent frame.

driver.SwitchTo().ParentFrame();

Selenium: Wait for Page to load

Handling the page loading times for web automation can be really frustrating. Especially when you think your scripts are all 100% working until you run one and it fails on a NoSuchElement exception. A lot of web applications now are very ajax heavy meaning even when selenium detects that your element is present  and loaded there are still parts of the application loading. You can test this by executing a screenshot after your findElement code and you’ll see that the page is not totally loaded. You can find numerous solutions online but several doesn’t quite do the trick. There is one however that worked for my case and it can be found at Brantley’s blog.

The method works because it checks for three things. First, it checks if the normal load page is complete. Then it will wait for if any ajax requests to load and lastly it will check again for the normal page to complete. The code used on my case can be found below. All credit belongs to the original poster in the link above, the code has been tweaked a little to work on my particular project .

To call the whole thing which is located in a class called Wait.


Wait.WaitForReady(driver, TimeSpan.FromSeconds(120));

In the Wait class, a wait until is called.

public void WaitForReady(IWebDriver driver, TimeSpan seconds)
{
    WebDriverWait wait = new WebDriverWait(driver, seconds);
    wait.Until(d =>
    {
        return WaitForPageLoad(driver, seconds);
    });
}

A Method that will do three checks.

public static bool WaitForPageLoad(IWebDriver driver, TimeSpan waitTime)
        {
            WaitForDocumentReady(driver, waitTime);
            bool ajaxReady = WaitForAjaxReady(driver, waitTime);
            WaitForDocumentReady(driver, waitTime);
            return ajaxReady;
        }

The method for page load.

private static void WaitForDocumentReady(IWebDriver driver, TimeSpan waitTime)
        {
            var wait = new WebDriverWait(driver, waitTime);
            var javascript = driver as IJavaScriptExecutor;
            if (javascript == null)
                throw new ArgumentException("driver", "Driver must support javascript execution");

            wait.Until((d) =>
            {
                try
                {
                    string readyState = javascript.ExecuteScript(
                        "if (document.readyState) return document.readyState;").ToString();
                    return readyState.ToLower() == "complete";
                }
                catch (InvalidOperationException e)
                {
                    return e.Message.ToLower().Contains("unable to get browser");
                }
                catch (WebDriverException e)
                {
                    return e.Message.ToLower().Contains("unable to connect");
                }
                catch (Exception)
                {
                    return false;
                }
            });
        }

A method that will wait for (if any) Ajax requests.

private static bool WaitForAjaxReady(IWebDriver driver, TimeSpan waitTime)
        {
            System.Threading.Thread.Sleep(1000);
            WebDriverWait wait = new WebDriverWait(driver, waitTime);
            return wait.Until<bool>((d) =>
            {
                return driver.FindElements(By.CssSelector(".waiting, .tb-loading")).Count == 0;
            });
        }

This greatly helped in script stability for my c# projects. I managed to also write this in java which I will do a later post on.