diff --git a/lib/capybara/session.rb b/lib/capybara/session.rb index 3e19885e..df7a724d 100644 --- a/lib/capybara/session.rb +++ b/lib/capybara/session.rb @@ -363,14 +363,15 @@ module Capybara ## # @overload switch_to_window(&block) # Switches to the first window for which given block returns a value other than false or nil. + # If window that matches block can't be found, the window will be switched back and `WindowError` will be raised. # @example # window = switch_to_window { title == 'Page title' } - # @return [Capybara::Window] window that has been switched to # @raise [Capybara::WindowError] if no window matches given block # @overload switch_to_window(window) # @param window [Capybara::Window] window that should be switched to # @raise [Capybara::Driver::Base#no_such_window_error] if unexistent (e.g. closed) window was passed # + # @return [Capybara::Window] window that has been switched to # @raise [Capybara::ScopeError] if this method is invoked inside `within`, # `within_frame` or `within_window` methods # @raise [ArgumentError] if both or neither arguments were provided @@ -388,14 +389,23 @@ module Capybara if window driver.switch_to_window(window.handle) + window else - driver.window_handles.each do |handle| - driver.switch_to_window handle - if yield - return Window.new(self, handle) + original_window_handle = driver.current_window_handle + begin + driver.window_handles.each do |handle| + driver.switch_to_window handle + if yield + return Window.new(self, handle) + end end + rescue => e + driver.switch_to_window(original_window_handle) + raise e + else + driver.switch_to_window(original_window_handle) + raise Capybara::WindowError, "Could not find a window matching block/lambda" end - raise Capybara::WindowError, "Could not find a window matching block/lambda" end end @@ -409,6 +419,7 @@ module Capybara # @overload within_window(window) { do_something } # @param [Capybara::Window] instance of Capybara::Window class # that will be switched to + # @raise [driver#no_such_window_error] if unexistent (e.g. closed) window was passed # @overload within_window(proc_or_lambda) { do_something } # @param lambda [Proc] lambda. First window for which lambda # returns a value other than false or nil will be switched to. @@ -421,7 +432,6 @@ module Capybara # # @raise [Capybara::ScopeError] if this method is invoked inside `within`, # `within_frame` or `within_window` methods - # @raise [driver#no_such_window_error] if unexistent (e.g. closed) window was passed # @return value returned by the block # def within_window(window_or_handle) @@ -429,20 +439,20 @@ module Capybara original = current_window begin switch_to_window(window_or_handle) unless original == window_or_handle - scopes.push(nil) + scopes << nil yield ensure - scopes.pop if scopes.last.nil? # It will be not nil if closed window has been passed + @scopes = [nil] switch_to_window(original) unless original == window_or_handle end elsif window_or_handle.is_a?(Proc) original = current_window + switch_to_window { window_or_handle.call } + scopes << nil begin - switch_to_window { window_or_handle.call } - scopes.push(nil) yield ensure - scopes.pop if scopes.last.nil? + @scopes = [nil] switch_to_window(original) end else @@ -451,10 +461,10 @@ module Capybara warn "DEPRECATION WARNING: Passing string argument to #within_window is deprecated. "\ "Pass window object or lambda. (called from #{file_line})" begin - scopes.push(nil) + scopes << nil driver.within_window(window_or_handle) { yield } ensure - scopes.pop + @scopes = [nil] end end end @@ -625,7 +635,7 @@ module Capybara end def scopes - @scopes ||= [document] + @scopes ||= [nil] end end end diff --git a/lib/capybara/spec/session/window/switch_to_window_spec.rb b/lib/capybara/spec/session/window/switch_to_window_spec.rb index 7c8d05b8..a5855223 100644 --- a/lib/capybara/spec/session/window/switch_to_window_spec.rb +++ b/lib/capybara/spec/session/window/switch_to_window_spec.rb @@ -30,6 +30,17 @@ Capybara::SpecHelper.spec '#switch_to_window', requires: [:windows] do @session.switch_to_window(window) expect(['', 'about:blank']).to include(@session.title) end + + it "should raise error when closed window is passed" do + original_window = @session.current_window + new_window = @session.open_new_window + @session.switch_to_window(new_window) + new_window.close + @session.switch_to_window(original_window) + expect do + @session.switch_to_window(new_window) + end.to raise_error(@session.driver.no_such_window_error) + end end context "with block" do @@ -83,11 +94,21 @@ Capybara::SpecHelper.spec '#switch_to_window', requires: [:windows] do end end.to raise_error(Capybara::ScopeError, "`switch_to_window` is not supposed to be invoked from `within`'s, `within_frame`'s' or `within_window`'s' block.") end - end - it "should raise error if window matching block wasn't found" do - expect do - @session.switch_to_window { @session.title == 'A title' } - end.to raise_error(Capybara::WindowError, "Could not find a window matching block/lambda") + it "should raise error if window matching block wasn't found" do + original = @session.current_window + expect do + @session.switch_to_window { @session.title == 'A title' } + end.to raise_error(Capybara::WindowError, "Could not find a window matching block/lambda") + expect(@session.current_window).to eq(original) + end + + it "should switch to original window if error is raised inside block" do + original = @session.switch_to_window(@session.windows[1]) + expect do + @session.switch_to_window { raise 'error' } + end.to raise_error(StandardError, 'error') + expect(@session.current_window).to eq(original) + end end end diff --git a/lib/capybara/spec/session/window/window_opened_by_spec.rb b/lib/capybara/spec/session/window/window_opened_by_spec.rb index 517998b7..693d1f9d 100644 --- a/lib/capybara/spec/session/window/window_opened_by_spec.rb +++ b/lib/capybara/spec/session/window/window_opened_by_spec.rb @@ -18,7 +18,7 @@ Capybara::SpecHelper.spec '#window_opened_by', requires: [:windows] do it 'should raise error if value of :wait is less than timeout' do Capybara.using_wait_time 1 do expect do - @session.window_opened_by(wait: 0.3) do + @session.window_opened_by(wait: 0.4) do @session.find(:css, '#openWindowWithTimeout').click end end.to raise_error(Capybara::WindowError, zero_windows_message) @@ -27,7 +27,7 @@ Capybara::SpecHelper.spec '#window_opened_by', requires: [:windows] do it 'should find window if value of :wait is more than timeout' do Capybara.using_wait_time 0.1 do - window = @session.window_opened_by(wait: 0.9) do + window = @session.window_opened_by(wait: 1) do @session.find(:css, '#openWindowWithTimeout').click end expect(window).to be_instance_of(Capybara::Window) @@ -37,7 +37,7 @@ Capybara::SpecHelper.spec '#window_opened_by', requires: [:windows] do context 'without :wait option' do it 'should raise error if default_wait_time is less than timeout' do - Capybara.using_wait_time 0.2 do + Capybara.using_wait_time 0.4 do expect do @session.window_opened_by do @session.find(:css, '#openWindowWithTimeout').click @@ -47,7 +47,7 @@ Capybara::SpecHelper.spec '#window_opened_by', requires: [:windows] do end it 'should find window if default_wait_time is more than timeout' do - Capybara.using_wait_time 0.9 do + Capybara.using_wait_time 1 do window = @session.window_opened_by do @session.find(:css, '#openWindowWithTimeout').click end diff --git a/lib/capybara/spec/session/window/window_spec.rb b/lib/capybara/spec/session/window/window_spec.rb index da814d02..2e854d80 100644 --- a/lib/capybara/spec/session/window/window_spec.rb +++ b/lib/capybara/spec/session/window/window_spec.rb @@ -54,7 +54,7 @@ Capybara::SpecHelper.spec Capybara::Window, requires: [:windows] do it 'should return false if window is closed' do @session.switch_to_window(@other_window) @other_window.close - expect(@other_window.current?).to be_false + expect(@other_window.current?).to eq(false) end end diff --git a/lib/capybara/spec/session/window/within_window_spec.rb b/lib/capybara/spec/session/window/within_window_spec.rb index 4df0ad5f..3b8cac9a 100644 --- a/lib/capybara/spec/session/window/within_window_spec.rb +++ b/lib/capybara/spec/session/window/within_window_spec.rb @@ -42,11 +42,14 @@ Capybara::SpecHelper.spec '#within_window', requires: [:windows] do window = (@session.windows - [@window]).first expect do @session.within_window(window) do - raise 'some error' + @session.within 'html' do + raise 'some error' + end end end.to raise_error(StandardError, 'some error') expect(@session.current_window).to eq(@window) expect(@session).to have_css('#doesNotOpenWindows') + expect(@session.send(:scopes)).to eq([nil]) end it 'should raise error if closed window was passed' do @@ -59,7 +62,9 @@ Capybara::SpecHelper.spec '#within_window', requires: [:windows] do raise 'should not be invoked' end end.to raise_error(@session.driver.no_such_window_error) + expect(@session.current_window).to eq(@window) expect(@session).to have_css('#doesNotOpenWindows') + expect(@session.send(:scopes)).to eq([nil]) end end @@ -86,7 +91,9 @@ Capybara::SpecHelper.spec '#within_window', requires: [:windows] do expect(@session).to have_css('#divInPopupOne') end end.to raise_error(Capybara::WindowError, "Could not find a window matching block/lambda") + expect(@session.current_window).to eq(@window) expect(@session).to have_css('#doesNotOpenWindows') + expect(@session.send(:scopes)).to eq([nil]) end it "returns value from the block" do @@ -103,6 +110,7 @@ Capybara::SpecHelper.spec '#within_window', requires: [:windows] do end end.to raise_error(StandardError, 'some error') expect(@session.current_window).to eq(@window) + expect(@session.send(:scopes)).to eq([nil]) end end @@ -110,7 +118,7 @@ Capybara::SpecHelper.spec '#within_window', requires: [:windows] do it "should warn" do expect(@session).to receive(:warn).with("DEPRECATION WARNING: Passing string argument "\ "to #within_window is deprecated. Pass window object or lambda. "\ - "(called from #{__FILE__}:114)").and_call_original + "(called from #{__FILE__}:122)").and_call_original @session.within_window('firstPopup') {} end diff --git a/lib/capybara/spec/views/with_windows.erb b/lib/capybara/spec/views/with_windows.erb index 3c5e6564..a9c410d7 100644 --- a/lib/capybara/spec/views/with_windows.erb +++ b/lib/capybara/spec/views/with_windows.erb @@ -12,7 +12,7 @@ $('#openWindowWithTimeout').click(function(){ setTimeout(function(){ window.open('/popup_one', 'firstPopup'); - }, 500); + }, 600); return false; });