Code & QA

Switching Between tabs and frames

Switching to iframe

Many of modern web applications are designed with <iframe> nodes used on their pages’ HTML code. Usually <iframe> element may be considered as a web page inserted into parent web page. An <iframe> is a link to another XML document like a web page located somewhere in the internet. Parsing HTML code and coming across <iframe> tag a browser refers to the iframe link and loads its content. The content is displayed like a part of the HTML page but actually it is a separate document with its own header, body, scripts, etc.

If you take a look at the DOM structure of a page that contains an iframe you will never find inner content of the iframe. And you won’t be able to interact with it via the DOM. The only option available is interacting with the iframe as with a separate web element instance (in the scope of the Selenium WebDriver tool you can find only single instance of IWebElement interface).

Look at the code below. In the HTML you can see the structure of a page with iframe element inside which is referred to ‘inneriframe.htm’ document. But you cannot see HTML code of ‘inneriframe.htm’ page and therefore you cannot interact with its elements.

HTML code of the page:

<!DOCTYPE html>

<html lang=”en” xmlns=”http://www.w3.org/1999/xhtml”>

<head>

<title></title>

<link href=”style.css” rel=”Stylesheet” type=”text/css” />

</head>

<body style=”background-image:none; background-color:black; text-align: justify; color: white;”>

<p>I am iframe</p>

<p>And I have an iframe inside</p>

<iframe id=”inneriframe” src=”inneriframe.html” height=”100%” width=”100%” frameborder=”0″ scrolling=”no” style=” overflowy:visible;”></iframe>

</body>

</html>

clip_image002

Iframe with id=”inneriframe” is highlighted with blue on the picture

Assume you want to get ‘I am iframe in iframe‘ text element. Let’s look then inside the iframe to know what it consists from. You can do it with xPath tool in Mozilla Firefox browser.

clip_image004

But you can see only the code of the iframe, not of the entire page. To get know to which element the iframe belongs look at the list of available frames of the page. Pay attention that all frames are named with their CSS-selectors. Selected iframe is named ‘inneriframe’ but you can see that two other containers are present on the page as well – innerContent and Top Window.

clip_image006

The main document is named as Top window. innerContent is the iframe within which the innerframe is located.

clip_image008

Select iframe#innerContent window in the list – it will switch you to the parent iframe. Expand the <document> node and you will see the inner content of <iframe id=”innerContent” width=”100%” height=”100%” frameborder=”0″ style=”overflow-y:visible;” scrolling=”no” src=”iframe.html”/> element. Well, it contains the ‘inneriframe’ iframe.

Exploring the page and switching between frames you can see that its structure is the following:

<body>

<iframe id=”innerContent”>

<iframe id=”inneriframe”>

<p>I am iframe in iframe</p>

</iframe>

</iframe>

</body>

It means that you have to switch first from the top document to the ‘innerContent’ iframe and then to switch again to the next iframe ‘inneriframe’, and only after that you will be able to find <p> node.

Below I give the example how it looks in C# code with Selenium Webdriver tool and how I use switching between frames in my framework.

I added separate class for tests over Testing with Selenium application:

clip_image010

Assume I have to verify that the text inside the ‘inneriframe’ frame is ‘I am iframe in iframe’. Using pure IWebDriver API and my own report I can do it this way:

using System;

using NUnit.Framework;

using Microsoft.VisualStudio.TestTools.UnitTesting;

using OpenQA.Selenium;

using OpenQA.Selenium.Firefox;

using OpenQA.Selenium.IE;

using OpenQA.Selenium.Chrome;

using OpenQA.Selenium.Support.UI;

using System.Collections.Generic;

using Logger;

using System.Reflection;

using Mapping;

using System.Linq;

using System.Configuration;

using nUnit = NUnit.Framework;

namespace Tests

{

[TestFixture]

[TestClass]

public class TestingWithSelenium : BaseTest

{

[Test]

[TestMethod]

public void SwitchingBetweenIframes()

{

string expectedText = “I am iframe in iframe”;

var testScope = new Dictionary<string, string> { { “Open the application”, “start page is displayed” }, { “Click iFrame in the left-side menu”, “iframe page content is displayed” }, { “Verify expected text is displayed on the page”, “Text in the bottom part of content area is ” + expectedText } };

Report.StartTestCase(this.GetType().Name, MethodBase.GetCurrentMethod().Name, “Verification of iframe text”, testScope);

Report.RunStep(“Step 1: Open the application”);

driver.Navigate().GoToUrl(Globals.applicationURL);

var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(5));

wait.Until(ExpectedConditions.ElementIsVisible(By.Id(“innerContent”)));

Report.AddInfo(testScope.ElementAt(0).Key, testScope.ElementAt(0).Value, driver.TakeScreenshot(testScope.ElementAt(0).Value));

Report.RunStep(“Step 2: Click iFrame in the left-side menu”);

driver.FindElement(By.LinkText(“iFrame”)).Click();

wait.Until(x => { return driver.FindElements(By.XPath(“//iframe[@src=’iframe.html’]”)).Count > 0; });

Report.AddInfo(testScope.ElementAt(1).Key, testScope.ElementAt(1).Value, driver.TakeScreenshot(testScope.ElementAt(1).Value));

Report.RunStep(“Step 3: Verify expected text is displayed on the page”);

driver.SwitchTo().Frame(“innerContent”);

driver.SwitchTo().Frame(0);

NUnit.Framework.Assert.IsTrue(driver.FindElement(By.TagName(“p”)).Text == expectedText);

Report.AddInfo(testScope.ElementAt(2).Key, testScope.ElementAt(2).Value, driver.TakeScreenshot(testScope.ElementAt(2).Value));

}

}

}

Let’s explore the code above step by step.

Block #0:

string expectedText = “I am iframe in iframe”;

var testScope = new Dictionary<string, string> { { “Open the application”, “start page is displayed” }, { “Click iFrame in the left-side menu”, “iframe page content is displayed” }, { “Verify expected text is displayed on the page”, “Text in the bottom part of content area is ” + expectedText } };

Report.StartTestCase(this.GetType().Name, MethodBase.GetCurrentMethod().Name, “Verification of iframe text”, testScope);

This is a declaration of local variables expectedText – used for text verification and testScope – description of test steps and their expected results. testScope variable is not obligatory for a test but as far as it is used in reporting it is recommended to initialize the testScope. Method StartTestCase() initializes variables which collect information about current test case execution flow. The information will be used in summary report.

Block #1:

Report.RunStep(“Step 1: Open the application”);

driver.Navigate().GoToUrl(ConfigurationManager.AppSettings[“applicationURL”]);

var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(5));

wait.Until(ExpectedConditions.ElementIsVisible(By.Id(“innerContent”)));

Report.AddInfo(testScope.ElementAt(0).Key, testScope.ElementAt(0).Value, driver.TakeScreenshot(testScope.ElementAt(0).Value));

Method RunStep() adds information to the report that new step is started. Text “Step 1: Open the application” means nothing but message to the reader of the code about the step. It is also displayed in summary report as a tooltip of step description (becomes visible when the mouse cursor is hovered over the step description). Just for easier understanding what the next lines of code are supposed to do.

At the moment the browser is already invoked (it has been done by SetUpDriverInstance method which had been invoked from BaseTest.Setup method). driver.Navigate().GoToUrl() method opens the web address you pass as the parameter.

WebDriverWait wait is a variable that sets up time period during which driver will wait until definite event is occurred. It is known as WebDriver explicit wait (for more details please seehttp://docs.seleniumhq.org/docs/04_webdriver_advanced.jsp). In my case wait time is five seconds.

The wait waits until the driver finds on opened page an element with id attribute “innerContent”, or until five seconds pass.

AddInfo adds new line to the report. In the case this is general information about the current test step. It will be included to the report as a substep.

clip_image012

Block #2

Report.RunStep(“Step 2: Click iFrame in the left-side menu”);

driver.FindElement(By.LinkText(“iFrame”)).Click();

wait.Until(x => { return driver.FindElements(By.XPath(“//iframe[@src=’iframe.html’]”)).Count > 0; });

Report.AddInfo(testScope.ElementAt(1).Key, testScope.ElementAt(1).Value, driver.TakeScreenshot(testScope.ElementAt(1).Value));

The driver finds a link with text ‘iFrame’ and clicks on it.

Then it waits until number of elements found with XPath selector “//iframe[@src=’iframe.html’]” is more than zero – at least one is found. FindElements() is used here instead of FindElement() because it returns number of elements found with specified locator and never throws ElementNotFoundException.

Block #3

Report.RunStep(“Step 3: Verify expected text is displayed on the page”);

driver.SwitchTo().Frame(“innerContent”);

driver.SwitchTo().Frame(0);

nUnit.Assert.IsTrue(driver.FindElement(By.TagName(“p”)).Text == expectedText);

Report.AddInfo(testScope.ElementAt(2).Key, testScope.ElementAt(2).Value, driver.TakeScreenshot(testScope.ElementAt(2).Value));

For successful verification of the targeted text I need to dive into the iframes inside the page. As I said above the targeted element is located in a frame which is inside another frame. So, initially I have to switch to the first iframe element. The driver switches to the frame by its id – innerContent. Then I switch the driver to the next iframe. I do it in a different way – by position in the document – it is the first (actually, single) iframe on the page. Under term “page” I understand not the entire page displayed in browser window but innerContent iframe, which is separate HTML page inserted into main page. It contains one iframe element (“inneriframe”) which is a separate web page as well.

Ok, now the driver is in correct place and I can verify the targeted element.

Switch to tab/window

Below is the test which switches to another tab/window and verifies its text.

[Test][TestMethod]

public void SwitchingToNewTab()

{

string expectedText = “New tab is opened”;

string newTabTitle = “Testing with Selenium – new tab”;

var testScope = new Dictionary<string, string> { { “Open the application”, “start page is displayed” }, { “Click New tab item in the left-side menu”, “New tab or window is opened” }, { “Verify expected text is displayed in new tab”, string.Empty } };

Report.StartTestCase(this.GetType().Name, MethodBase.GetCurrentMethod().Name, “Verification of text in new tab”, testScope);

Report.RunStep(“Step 1: Open the application”);

driver.Navigate().GoToUrl(Globals.applicationURL);

var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(5));

wait.Until(ExpectedConditions.ElementIsVisible(By.Id(“innerContent”)));

Report.AddInfo(testScope.ElementAt(0).Key, testScope.ElementAt(0).Value, driver.TakeScreenshot(testScope.ElementAt(0).Value));

Report.RunStep(“Step 2: Click New tab item in the left-side menu”);

var numberOfTabs = driver.WindowHandles.Count; driver.FindElement(By.LinkText(“New tab”)).Click();

wait.Until(x => { return driver.WindowHandles.Count == numberOfTabs + 1; });

Report.AddInfo(testScope.ElementAt(1).Key, testScope.ElementAt(1).Value);

Report.RunStep(“Step 3: Verify expected text is displayed in new tab”);

foreach (var handle in driver.WindowHandles)

{

driver.SwitchTo().Window(handle);

if (driver.Title.Equals(newTabTitle))

break;

}

nUnit.Assert.IsTrue(driver.FindElement(By.TagName(“p”)).Text == expectedText);

Report.AddInfo(testScope.ElementAt(2).Key, testScope.ElementAt(2).Value, driver.TakeScreenshot(testScope.ElementAt(2).Value));

}

Block #0, #1:

The same as in example above

Block #2:

Report.RunStep(“Step 2: Click New tab item in the left-side menu”);

var numberOfTabs = driver.WindowHandles.Count;

driver.FindElement(By.LinkText(“New tab”)).Click();

wait.Until(x => { return driver.WindowHandles.Count == numberOfTabs + 1; });

Report.AddInfo(testScope.ElementAt(1).Key, testScope.ElementAt(1).Value);

numberOfTabs is the number of tabs or windows currently opened in the browser. This value is necessary for further comparison with actual number of opened tabs.

Then the driver clicks on the link with text ‘New tab’.

After the link is clicked the new tab should be opened. Method Until() of the wait variable waits when the number of currently opened tabs becomes the same as previously opened plus one.

Block #3:

Report.RunStep(“Step 3: Verify expected text is displayed in new tab”);

foreach (var handle in driver.WindowHandles)

{

driver.SwitchTo().Window(handle);

if (driver.Title.Equals(newTabTitle))

break;

}

nUnit.Assert.IsTrue(driver.FindElement(By.TagName(“p”)).Text == expectedText);

Report.AddInfo(testScope.ElementAt(2).Key, testScope.ElementAt(2).Value, driver.TakeScreenshot(testScope.ElementAt(2).Value));

I know that the title of newly opened tab must be ‘Testing with Selenium – new tab’ – the value of newTabTitle variable. So in a loop I go through all opened tabs and verify title of every tab. If one is equal to expected the loop is terminated.

Then the verification of targeted text is performed.

Summary

Switching IWebDriver between windows (tabs) is provided with IWebDriver.SwitchTo().Window(string handler) method. Common approach for performing it is following:

1. Find out and remember the number of currently opened windows (IWebDriver.WindowHandles.Count property)

2. Remember the handle of current window (IWebDriver.CurrentWindowHandle property)

3. Perform an action which opens/closes windows (tabs)

4. Wait until number of opened windows becomes equal to expected (WebDriverWait.Until(d => d.WindowHandles.Count == previousNumberOfOpenedWindows +/- n))

5. Switch IWebDriver instance to necessary window with IWebDriver.SwitchTo().Window(string handler) method. If you do not know exactly the handle of the target window, use a loop for it as it is shown above.

6. If you need to return back to initial window you can switch the driver by the handle which has been remembered in clause 2

Note: if a window is closed, obviously you cannot return back to inexistent window.

7. IWebDriver.Close() method closes only current tab/window – the window which the driver is switched on. After this action IWebDriver.CurrentWindowHandle becomes null and can’t be used until the driver is switched to any of opened windows explicitely.

Apart to IWebDriver.Close() the IWebDriver.Quit() method invokes IWebDriver.Dispose() method. IWebDriver.Dispose() method closes all browser windows and safely ends the session.

Switching between frames is provided with method IWebDriver.SwithchTo().Frame(). The method can take three types of parameter:

· string – it may be id or name attribute of iframe

assume there is an element <iframe id = ’iframe_id’ name = ’iframe_name’>. In that case the driver will be switched to the iframe either if you pass ‘iframe_id’ or ‘iframe_name’ string as a parameter:

driver.SwitchTo().Frame(“iframe_id”);

driver.SwitchTo().Frame(“iframe_name”);

· integer is the index of the iframe element among all iframes on the page. Passing 0 to the method switches the driver into the first iframe on the page.

· IWebElement. Iframe is a web element like any other elements described with HTML tags, hence it may be found with FindElement() method.

IWebElement frame = driver.FindElement(By.Id(“iframe_id”));

driver.SwitchTo().Frame(frame);

You can switch the driver only in one direction, into frames located on a page. But you can never switch it back into the upper(parent) frame from which you switched before. The only available option is to switch the driver to the very top level of entire document. Assume the structure of a page is following:

<body>

<iframe id=’frame_1’>

<iframe id=’frame_1_1’></iframe>

</iframe>

<iframe id=’frame_2’></iframe></iframe>

</body>

If you have to switch to ‘frame_1_1’ iframe you can do it in this way:

driver.SwitchTo().Frame(“frame_1”);

driver.SwitchTo().Frame(“frame_1_1”);

Then if you have to jump into the frame ‘frame_2’ you have to return back to the top level of the page first:

driver.SwitchTo().DefaultContent();

driver.SwitchTo().Frame(“frame_2”);

And again you have to switch to the top if you have to jump into any other frame because you cannot see neither ‘frame_1’ nor ‘frame_1_1’ from ‘frame_2’ iframe.

[row]
[column lg=”4″ md=”12″ sm=”12″ xs=”12″ ]
WebPage Class [/column]
[column lg=”4″ md=”12″ sm=”12″ xs=”12″ ]
Table Of Content
[/column]
[column lg=”4″ md=”12″ sm=”12″ xs=”12″ ]
WebElement
[/column]
[/row]

Leave a Reply