Automated Testing Frames in Selenium WebDriver
Testing frames (including iframes) in Selenium WebDriver
You can find more Selenium examples like this one in this eBook: Selenium WebDriver Recipes in Ruby. This is also included in my “How to in Selenium WebDriver” series.
Frames are deprecated in HTML5. However, iFrames (inline frames) are still widely used in many web apps, in particular, large enterprise apps such as Microsoft SharePoint and Dynamic 365.
From my memory, over 50% client web apps I worked on used iFrames. So, a few years ago, I was shock to know that so-called popular Cypress did not support Frames, and others. How could a test automation framework/tool be so crippled? Especially, W3C standard-based Selenium WebDriver is so much better. (many people used Selenium WebDriver wrongly, see my other articles for more)
This tutorial will show you multiple ways to test web pages with frames in Selenium WebDriver.
Table of Contents:
· iFrames vs Frames — What’s the Difference?
· Steps to Automated Testing page operations in a Frame
· How to Switch to a Frame?
∘ Identify Frame by ‘ID’
∘ Identify a frame by Name does not work
∘ Identify Frame by finding the frame element
∘ Find Frame by Index
· Leave the frame after done
iFrames vs Frames — What’s the Difference?
Frames (<frame>
) are used in conjunction with Framesets. A frameset splits the view of a browser into sections (frames). And each frame contains an independent page.
HTML Source:
<frameset rows="100,*" frameborder="0" border="0" framespacing="0">
<frame name="topNav" id="topNavId" src="top_nav.html">
<frameset cols="200,*" frameborder="0" border="0" framespacing="0">
<frame name="menu" id="menu_frame" src="menu_1.html" scrolling="auto" noresize>
<frame name="content" src="content.html">
</frameset>
</frameset>
iFrames are used to embed a document within the current HTML document (hence ‘inline’ in the name), without being in a frameset. In short, it’s like another page inside the current page.
In Test Automation, there is no difference in testing Frames or iFrames. In this article, I’ll refer to both as ‘frames’.
Steps to Automated Testing page operations in a Frame
To interact with elements inside a frame, the automation driver performs these steps:
1. Identify a frame
2. Switch to a frame
3. Drive the elements inside the frame
4. Switch back the main page
How to Switch to a Frame?
In Selenium WebDriver, we use the switch_to
command to focus on a specific frame. There are multiple ways to identify a frame (then we can switch to it) :
by ‘ID’ attribute
by Selenium WebElement
by index
Identify Frame by ‘ID’
This is the most intuitive method but only works if the ID attribute is present.
The HTML fragment:
<iframe id="Frame1" frameborder="1" name="first-frame">
...
</iframe>
The test step below switches the focus to it.
driver.switch_to.frame("Frame1")
After this line, you can start interacting with the contents inside the frame, Frame1
. In the above example, that is the Username and Password form.
Below is the complete test case:
it "Testing iFrame by ID" do
driver.navigate.to(site_url + "/iframe.html")
driver.find_element(:name, "user").send_keys("agileway")
# switch to the sign in frame
driver.switch_to.frame("Frame1")
driver.find_element(:name, "username").send_keys("tester")
driver.find_element(:name, "password").send_keys("TestWise")
driver.find_element(:id, "loginBtn").click
sleep 0.5
expect(driver.page_source).to include("Signed in")
# return to main document
driver.switch_to.default_content()
driver.find_element(:id, "accept_terms").click
end
Note: If there are multiple frames that share the same ID, then the driver will switch to the first frame that matches the ID.
Identifying a frame by Name does not work
According to the Selenium Documentation, you should be able to use the name attribute directly. i.e. driver.switch_to.frame(“first-frame”).
However, I could not get this to work in Selenium 4— if you could, please let me know how! 😁
Identify Frame by finding the frame element
A more flexible way to identify a frame is to actually finding the frame element. This means you may use any one or combination of Selenium’s locators, including:
name
xpath
css_selector
tag_name
and more!
I prefer to use XPath on the frame’s source (src
) attribute, as source must be present. The source is often unique on a page, but this is not guaranteed.
The HTML fragment:
<iframe frameborder="1" src="login_iframe.html">
...
</iframe>
The test step below switches the focus to the frame using the source:
el=driver.find_element(:xpath, "//iframe[@src='login_iframe.html']")
driver.switch_to.frame(el)
By passing in the WebElement, the focus is switching to the frame, and you can start interacting with the elements inside it.
Below is the complete test case:
it "Testing iFrames by element" do
driver.navigate.to(site_url + "/iframe.html")
driver.find_element(:name, "user").send_keys("agileway")
# switch to the sign in frame
elem_frame = driver.find_element(:xpath, "//iframe[@src='login_iframe.html']")
driver.switch_to.frame(elem_frame)
driver.find_element(:name, "username").send_keys("tester")
driver.find_element(:name, "password").send_keys("TestWise")
driver.find_element(:id, "loginBtn").click
sleep 0.5
expect(driver.page_source).to include("Signed in")
# return to main document
driver.switch_to.default_content()
driver.find_element(:id, "accept_terms").click
end
Find Frame by Index
We can also use the index of the frames to get a specific frame. A webpage can contain multiple frames.
To list all the frames on a web page, run window.frames
in your browser’s console.
You can copy the index from the console (the purple number at the start of the line) to get the index value, starting with 0
.
The HTML fragment for the above screen:
<iframe src="login_iframe.html">
...
</iframe><iframe src="account_details_iframe.html">
...
</iframe>
The test step below switches the focus to the first frame:
driver.switch_to.frame(0)
The full test case to interact with elements from both frames is below:
sit "Testing iFrames by index" do
driver.navigate.to(site_url + "/iframes.html")
# switch to first frame
driver.switch_to.frame(0)
driver.find_element(:name, "username").send_keys("agileway")
# return to main document
driver.switch_to.default_content() # switch to second frame
driver.switch_to.frame(1)
driver.find_element(:id, "radio_male").click
end
Tip: I don’t recommending using indexes, as test script might break after the structure of the page is changed.
Leave the frame after done
In the above test cases, you may notice that after completing operations inside the frames, there is another switch_to
but to the default_content()
rather than a specific frame.
default_content
returns the driver to the main page before we start any switching. It is good practice to do this after the end of each test case. This can help the driver avoid getting stuck in a frame across test cases, especially there are multiple frames.
I heard a few people saying automated testing Microsoft Dynamic 365 or Guidewire was not possible. Wrong! It is more challenging, as there are multi-levels are frames are used. It was frustrating and time-consuming when got lost in frames. With TestWise tools’s unique debugging feature: attaching selected test steps against the current browser, it’s a lot easier.
A rule of thumb, returning to the main (or parent) document before entering another frame at the same level.