SeleniumConf is coming to Berlin this October (and the CFP is open NOW)! For details GO HERE x

3

How To Work with Frames

The Problem

On occasion you'll run into a relic of the front-end world -- frames. And when writing a test against them, you can easily get tripped if you're not paying attention.

A Solution

Rather than gnash your teeth when authoring your tests, you can easily work with the elements in a frame by telling Selenium to switch to that frame first. Then the rest of your test should be business as usual.

Let's dig in with some examples.

An Example

We'll first need to pull in our requisite libraries (selenium-webdriver to drive the browser, and rspec/expecations & RSpec::Matchers to perform our assertions) and wire up some simple setup, teardown, and run methods.

# filename: frames.rb

require 'selenium-webdriver'
require 'rspec/expectations'
include RSpec::Matchers

def setup
  @driver = Selenium::WebDriver.for :firefox
end

def teardown
  @driver.quit
end

def run
  setup
  yield
  teardown
end

Now onto our test. In it we'll step through an example of nested frames which can be found on the-internet.

run do
  @driver.get 'http://the-internet.herokuapp.com/nested_frames'
  @driver.switch_to.frame('frame-top')
  @driver.switch_to.frame('frame-middle')
  expect(@driver.find_element(id: 'content').text).to eql 'MIDDLE'
end

With Selenium's .switch_to.frame method we can easily switch to the frame we want. It accepts either an ID or name attribute. But in order to get the text of the middle frame (e.g., a frame nested within another frame), we need to switch to the parent frame (e.g., the top frame) and then switch to the child frame (e.g., the middle frame).

Once we've done that we're able to find the element we need, grab it's text, and assert that it's what we expect.

While this example helps illustrate the point of frame switching, it's not very practical.

A More Practical Example

Here is a more likely example you'll run into -- working with a WYSIWYG Editor like TinyMCE. You can see the page we're testing here.

# filename: frames.rb

run do
  @driver.get 'http://the-internet.herokuapp.com/tinymce'
  @driver.switch_to.frame('mce_0_ifr')
  editor = @driver.find_element(id: 'tinymce')
  before_text = editor.text
  editor.clear
  editor.send_keys 'Hello World!'
  after_text = editor.text
  expect(after_text).not_to eql before_text
end

Once the page loads we switch into the frame that contains TinyMCE and...

  • grab the original text and store it
  • clear and input new text
  • grab the new text value
  • assert that the original and new texts are not the same

Keep in mind that if we need to access a part of the page outside of the frame we are currently in we'll need to switch to it. Thankfully Selenium has method that enables us to quickly jump back to the top level of the page -- switch_to.default_content.

Here is what that looks like in practice.

  @driver.switch_to.default_content
  expect(@driver.find_element(css: 'h3').text).not_to be_empty

Expected Behavior

If we save the file and run it (e.g., ruby frames.rb from the command-line) here is what will happen:

Example 1

  • Open the browser
  • Visit the page
  • Switch to the nested frame
  • Grab the text from the frame and assert that Selenium is in the correct place
  • Close the browser

Example 2

  • Open the browser
  • Visit the page
  • Switch to the frame that contains the TinyMCE editor
  • Grab and clear the text in the editor
  • Input and grab new text in the edtitor
  • Assert that the original and new text entries don't match
  • Switch to the top level of the page
  • Grab the text from the top of the page and assert that it's not empty
  • Close the browser

Outro

Now you're ready to handily defeat frames when they cross your path.

Happy Testing!


Back to the archives