Automated Testing Frames in Selenium WebDriver
How to test content inside Frames and iFrames in Selenium WebDriver
Frames (and iFrames) are widely considered to be bad practice. To the point where frames have been deprecated in HTML5. iFrames themselves still are supported but they carry some performance and accessibility concerns. Despite this, iFrames are still widely used, especially in enterprise apps. For example, Microsoft dynamic 365 uses iFrames heavily.
This tutorial will show you multiple ways to get past these frames and test the content inside them in Selenium WebDriver.
iFrames vs Frames — What’s the Difference?
Frames (<frame>
) are used in conjunction with Framesets, so I’ll explain framesets first to understand frames. 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>
iFrame stands for ‘inline frame’. iFrames are used to embed a document within the current HTML document (hence ‘inline’ in the name). iFrames do not have to be in a frameset. In short, it’s like another page inside the current page.
In the context of automated testing, Frames and iFrames are the same. In this article, I’ll refer to both as ‘frames’’.
Steps to Automated Testing page operations in a Frame
Selenium Driver only can find elements in the current page or frame. To interact with elements inside a frame, the driver must first focus on that frame.
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). Here are some common methods:
- Frame by ‘ID’
- Frame by Selenium WebElement
- Frame 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 frameborder="1" id="Frame1" 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 testcase:
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.
Can you identify a frame directly by Name?
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.1.0 — if you could, please let me know how! 😁
Besides directly calling ID, there are slightly more complex but more powerful ways of identifying frames.
Identify Frame by finding the WebElement first/by Source
We can use Selenium’s find_element
feature to get a WebElement, then use this element for identifying the frame to switch to. This is very flexible, as it means you can find the frame using your preferred method, including:
name
xpath
css_selector
- and more!
I prefer to use XPath on the frame’s source (src
) attribute. The frame’s source must be present, even if an ID or name is not. 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:
elem_frame = driver.find_element(:xpath, "//iframe[@src='login_iframe.html']")driver.switch_to.frame(elem_frame)
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 testcase:
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
Frame by Index
We can also use the index of the frames to identify specific frames. A webpage can contain multiple frames.
To list all the frames, run window.frames
in your browser’s console. This will list all the frames on the current page. See the below screenshot for example output and where to find the frames’ indices.
You can copy the index from the console (the purple number at the start of the line) to get the index value.
The HTML fragment for the above screencap:
<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 testcase to interact with elements from both frames is below:
it "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
How to Leave a Frame
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.
In the case of the index example with multiple frames, you should be returning to the main (or parent) content before entering another frame at the same level.