With Capybara, how do I switch to the new window for links with "_blank" targets?
Asked Answered
E

15

85

Perhaps this isn't actually the issue I'm experiencing, but it seems that when I "click_link" a link with target="_blank", the session keeps the focus on the current window.

So I either want to be able to switch to the new window, or to ignore the _blank attribute - essentially, I just want it to actually go to the page indicated by the link so I can make sure it's the right page.

I use the webkit and selenium drivers.


I submitted my findings thus far below. A more thorough answer is much appreciated.

Also, this only works with selenium - the equivalent for the webkit driver (or pointing out where I could discover it myself) would be much appreciated.

Expurgatory answered 30/9, 2011 at 14:42 Comment(0)
O
98

Capybara >= 2.3 includes the new window management API. It can be used like:

new_window = window_opened_by { click_link 'Something' }
within_window new_window do
  # code
end
Otherworldly answered 2/8, 2014 at 9:17 Comment(1)
poltergeist 1.6 implements this API, so at this writing this is the way to go with poltergeist.Commandment
E
54

This solution only works for the Selenium driver

All open windows are stores in Selenium's

response.driver.browser.window_handles

Which seems to be an array. The last item is always the window that was most recently opened, meaning you can do the following to switch to it.

Within a block:

new_window=page.driver.browser.window_handles.last 
page.within_window new_window do
  #code
end

Simply refocus for current session:

session.driver.browser.switch_to.window(page.driver.browser.window_handles.last)

Referenced on the capybara issues page: https://github.com/jnicklas/capybara/issues/173

More details on Selenium's window switching capabilities: http://qastuffs.blogspot.com/2010/10/testing-pop-up-windows-using-selenium.html

Expurgatory answered 3/10, 2011 at 15:30 Comment(5)
This is great, but the second example is a bit misleading. There is no difference between session and page, so session.driver.browser and page.driver.browser actually refer to the same thing. That messed me up while trying to adapt this to a different context, accessing Capybara through a different library, and not using the DSL. I had to read the Capybara DSL code to figure out that to access 'page`, I just need to access the session.Housefather
@glyphgryph boy I love when solutions just work with a copy & paste, sans all the extra waste.Johann
It's not only for Selenium: within_window(page.driver.window_handles.last) works for me with capybara-webkit.Sumo
As mentioned below, Capybara has a new syntax for dealing with windows: github.com/jnicklas/capybara#working-with-windowsStrident
This method is deprecated in capybara DEPRECATION WARNING: Passing string argument to #within_window is deprecated.Galway
B
11

This is now working with Poltergeist. Although window_handles is still not implemented (you need a window name, i.e. via a JavaScript popup):

within_window 'other_window' do
  current_url.should match /example.com/
end

Edit: Per comment below, Poltergeist now implements window_handles since version 1.4.0.

Bcd answered 5/3, 2013 at 10:6 Comment(2)
I just tried page.within_window page.driver.browser.window_handles.last do ... and it appears to work as expected in Poltergeist. No need to give a window name. So this answer might need to be updated.Aurelie
Capybara 2.3 deprecates calling within_window with a String: DEPRECATION WARNING: Passing string argument to #within_window is deprecated. Pass window object or lambda. The method documented by Andrey Botalov fixes this deprecation.Commandment
G
11

Capybara provides some methods to ease finding and switching windows:

facebook_window = window_opened_by do
  click_button 'Like'
end
within_window facebook_window do
  find('#login_email').set('[email protected]')
  find('#login_password').set('qwerty')
  click_button 'Submit'
end

More details here: Capybara documentation

Galway answered 24/11, 2015 at 11:33 Comment(0)
E
9

I know this is old post, but for what its worth in capybara 2.4.4

within_window(switch_to_window(windows.last)) do 
    # in my case assert redirected url from a prior click action
    expect(current_url).to eq(redirect['url'])
end
Ernaernald answered 27/1, 2015 at 19:45 Comment(0)
C
7

Seems like it is not possible with capybara-webkit right now: https://github.com/thoughtbot/capybara-webkit/issues/271

:-(

At the same time https://github.com/thoughtbot/capybara-webkit/issues/129 claims it is possible to switch windows with within_window.

Also https://github.com/thoughtbot/capybara-webkit/issues/47 suggests that page.driver.browser.switch_to().window(page.driver.browser.window_handles.last) works. Ah well, on to code reading.

The code at https://github.com/thoughtbot/capybara-webkit/blob/master/lib/capybara/webkit/browser.rb at least has some references that suggest that the API that works for webdriver / firefox is also working for webkit.

Canzone answered 18/9, 2012 at 10:32 Comment(1)
See my comment on accepted answer: within_window(page.driver.window_handles.last) works for me with capybara-webkit.Sumo
Y
6

Now within_window implemented for capybara-webkit http://github.com/thoughtbot/capybara-webkit/pull/314 and here you can see how to use it http://github.com/mhoran/capybara-webkit-demo

Yarborough answered 13/1, 2013 at 12:53 Comment(0)
S
6

As of May 2014 the following code works on capybara-webkit

 within_window(page.driver.browser.window_handles.last) do
   expect(current_url).to eq('http://www.example.com/')
 end
Shanks answered 8/5, 2014 at 5:51 Comment(1)
page.driver.brower.window_handles.las is now deprecated. Use windows.last. See github.com/thoughtbot/capybara-webkit/issues/650Intense
M
6

To explicitly change window, you use switch_to_window

  def terms_of_use
    terms_window = window_opened_by do
      click_link(@terms_link)
    end
    switch_to_window(terms_window)
  end

An after that method browser will work in the new page, instead of wrap everything in a within_window block

Mallette answered 20/12, 2016 at 19:18 Comment(0)
M
5

This works for me in capybara-webkit:

within_window(windows.last) do
  # code here
end

(I'm using capybara 2.4.1 and capybara-webkit 1.3.0)

Manhood answered 1/1, 2015 at 18:17 Comment(0)
N
4

You can pass a name, url or title of the window also (But now its dipricated)

  let(:product) { create :product }

  it 'tests' do
    visit products_path
    click_link(product.id)

    within_window(product_path(product)) do
      expect(page).to have_content(product.title)
    end
  end

You can pass a labda or a proc also

within_window(->{ page.title == 'Page title' }) do
  click_button 'Submit'
end

wish it stretches the method usage to more clearly understaing

Norenenorfleet answered 31/8, 2015 at 14:40 Comment(0)
S
3

I had this issue when opening links in an gmail window: I fixed it like this:

Given /^(?:|I )click the "([^"]*)" link in email message$/ do |field|

  # var alllinks = document.getElementsByTagName("a");
  # for (alllinksi=0; alllinksi<alllinks.length; alllinksi++) {
  #   alllinks[alllinksi].removeAttribute("target");
  # }

  page.execute_script('var alllinks = document.getElementsByTagName("a"); for (alllinksi=0; alllinksi<alllinks.length; alllinksi++) { alllinks[alllinksi].removeAttribute("target"); }')

  within(:css, "div.msg") do
    click_link link_text
  end

end
Sidewalk answered 3/5, 2013 at 13:57 Comment(1)
BTW, instead of comments you could use heredoc syntax: page.execute_script <<-JS var alllinks = document.getElementsByTagName("a"); for (alllinksi=0; alllinksi<alllinks.length; alllinksi++) { alllinks[alllinksi].removeAttribute("target"); } JS Comments would only allow inline code, so make sure there are line breaks before and after "JS", but you get the idea. this way you don't have to manage code in two places and most editors will highlight them anyway.Doughy
O
3

The best idea is to update capybara to the latests version (2.4.1) and just use windows.last because page.driver.browser.window_handles is deprecated.

Opah answered 6/2, 2015 at 14:31 Comment(0)
B
0

The main implementation (window_opened_by) raises an error for me:

*** Capybara::WindowError Exception: block passed to #window_opened_by opened 0 windows instead of 1

So, I resolve it by this solution:

new_window = open_new_window

within_window new_window do
  visit(click_link 'Something')
end

page.driver.browser.window_handles
# => ["CDwindow-F7EF6D3C12B68D6B6A3DFC69C2790718", "CDwindow-9A026DEC65C3C031AF7D2BA12F28ADC7"]
Bedder answered 8/3, 2019 at 6:52 Comment(0)
H
0

I personally like the following approach since it works correctly regardless of JS being enabled or not.

My Spec:

  it "shows as expected" do
    visit my_path

    # ...test my non-JS stuff in the current tab

    switch_to_new_tab

    # ...test my non-JS stuff in the new tab
    
    # ...keep switching to new tabs as much as necessary
  end 

  # OR

  it "shows as expected", js: true do
    visit my_path

    # ...test my non-JS stuff in the current tab
    # ...also test my JS stuff in the current tab

    switch_to_new_tab

    # ...test my non-JS stuff in the new tab
    # ...also test my JS stuff in the new tab

    # ...keep switching to new tabs as much as necessary
  end 

Test helpers:

  def switch_to_new_tab
    current_browser = page.driver.browser

    if js_enabled?
      current_browser.switch_to.window(current_browser.window_handles.last)
    else
      visit current_browser.last_request.fullpath
    end
  end

  def js_enabled?
    Capybara.current_driver == Capybara.javascript_driver
  end
Haustellum answered 24/8, 2020 at 6:30 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.