How can I switch between two frames with Capybara

2019-02-16 08:59发布

问题:

I am trying do something inside 2 frames but error raises everytime when I try to switch between frames. For example:

# encoding: utf-8

require "capybara/dsl"

Capybara.run_server = false
Capybara.current_driver = :selenium
Capybara.app_host = 'https://hb.posted.co.rs/posted'

class Account
  include Capybara::DSL

  def check_balance
    visit('/')
    page.driver.browser.switch_to.frame 'main'
    fill_in 'korisnik', :with => 'foo'
    fill_in 'lozinka', :with => 'bar'
    click_button 'Potvrda unosa'

    page.driver.browser.switch_to.frame 'header'
    click_on 'Stanje' 
  end
end

account = Account.new
account.check_balance

Error is:

[remote server] file:///tmp/webdriver-profile20120810-9163-xy6dtm/extensions/fxdriver@googlecode.com/components/driver_component.js:6638:in `unknown': Unable to locate frame: main (Selenium::WebDriver::Error::NoSuchFrameError)

What is the problem? Maybe I am doing something wrong here?

If I change order of switching frames so try first to switch to 'header' then switch to 'main' frame then same error raises except that it says that this time there is no 'main' frame:

# encoding: utf-8

require "capybara/dsl"

Capybara.run_server = false
Capybara.current_driver = :selenium
Capybara.app_host = 'https://hb.posted.co.rs/posted'

class Account
  include Capybara::DSL

  def check_balance
    visit('/')
    page.driver.browser.switch_to.frame 'header'
    click_on 'Stanje' 

    page.driver.browser.switch_to.frame 'main'
    fill_in 'korisnik', :with => 'foo'
    fill_in 'lozinka', :with => 'bar'
    click_button 'Potvrda unosa'
  end
end

account = Account.new
account.check_balance

Error:

[remote server] file:///tmp/webdriver-profile20120810-9247-w3o5hj/extensions/fxdriver@googlecode.com/components/driver_component.js:6638:in `unknown': Unable to locate frame: main (Selenium::WebDriver::Error::NoSuchFrameError)

回答1:

Problem

The problem is that when you do page.driver.browser.switch_to.frame, it switches the page's context to the frame. All actions against the page are now actually against the frame. So when you are switching frames the second time, you are actually saying find the 'header' frame inside the 'main' frame (rather than, what I assume you want, the 'header' frame inside the main page).

Solution - Capybara within_frame (recommended):

When working inside a frame, you should use Capybara's within_frame method. You would want to do:

  def check_balance
    visit('/')

    within_frame('main'){
      fill_in 'korisnik', :with => 'foo'
      fill_in 'lozinka', :with => 'bar'
      click_button 'Potvrda unosa'
    }

    within_frame('header'){
      click_on 'Stanje' 
    }
  end

Solution - Selenium switch_to:

If you want to do the frame management yourself (ie not use Capybara's built-in method), you can switch the page's context back to the browser and then to the second frame. This would look like the following. Though I would suggest using the built-in Capybara method.

  def check_balance
    visit('/')
    page.driver.browser.switch_to.frame 'header'
    click_on 'Stanje' 

    #Switch page context back to the main browser
    page.driver.browser.switch_to.default_content

    page.driver.browser.switch_to.frame 'main'
    fill_in 'korisnik', :with => 'foo'
    fill_in 'lozinka', :with => 'bar'
    click_button 'Potvrda unosa'
  end


回答2:

I've found solution. within_frame works as expected:

# encoding: utf-8

require "capybara/dsl"

Capybara.run_server = false
Capybara.current_driver = :selenium
Capybara.app_host = 'https://hb.posted.co.rs/posted'
class Account
  include Capybara::DSL
  def check_balance
    visit('/')

    within_frame 'main' do
      fill_in 'korisnik', :with => 'foo'
      fill_in 'lozinka', :with => 'bar'
      click_button 'Potvrda unosa'
    end

    within_frame 'header' do
      click_on 'Stanje'
    end
  end
end

account = Account.new
account.check_balance

I've found source code for within_frame in file https://github.com/jnicklas/capybara/blob/master/lib/capybara/selenium/driver.rb. starting from line 81.

EDIT: While I wrote this answer, @JustinKo answered question, so both answers are correct but +1 and accepted answer for him.



标签: ruby capybara