Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge pull request #1235 from abotalov/add_methods_for_windows

New API for working with windows (switching/finding/closing/opening/etc....)
  • Loading branch information...
commit de8b1a0de23a6ddc9c4011678c63553ce37c42bd 2 parents 3c5dd37 + 573668c
Thomas Walpole authored
Showing with 1,207 additions and 103 deletions.
  1. +6 −1 .travis.yml
  2. +15 −0 README.md
  3. +2 −2 capybara.gemspec
  4. +3 −0  lib/capybara.rb
  5. +37 −1 lib/capybara/driver/base.rb
  6. +8 −4 lib/capybara/node/base.rb
  7. +37 −0 lib/capybara/rspec/matchers.rb
  8. +74 −12 lib/capybara/selenium/driver.rb
  9. +166 −10 lib/capybara/session.rb
  10. +84 −0 lib/capybara/spec/session/window/become_closed_spec.rb
  11. +25 −0 lib/capybara/spec/session/window/current_window_spec.rb
  12. +28 −0 lib/capybara/spec/session/window/open_new_window_spec.rb
  13. +114 −0 lib/capybara/spec/session/window/switch_to_window_spec.rb
  14. +83 −0 lib/capybara/spec/session/window/window_opened_by_spec.rb
  15. +141 −0 lib/capybara/spec/session/window/window_spec.rb
  16. +31 −0 lib/capybara/spec/session/window/windows_spec.rb
  17. +188 −0 lib/capybara/spec/session/window/within_window_spec.rb
  18. +0 −45 lib/capybara/spec/session/within_window_spec.rb
  19. +2 −1  lib/capybara/spec/spec_helper.rb
  20. +1 −1  lib/capybara/spec/views/popup_one.erb
  21. +1 −1  lib/capybara/spec/views/popup_two.erb
  22. +38 −0 lib/capybara/spec/views/with_windows.erb
  23. +0 −25 lib/capybara/spec/views/within_popups.erb
  24. +123 −0 lib/capybara/window.rb
7 .travis.yml
View
@@ -25,7 +25,12 @@ before_install:
- sudo apt-get update
- sudo apt-get install google-chrome-stable --force-yes
- sudo chmod 1777 /dev/shm
+ - sudo apt-get install awesome -y
before_script:
- - sh -e /etc/init.d/xvfb start
- export DISPLAY=:99.0
+ - sh -e /etc/init.d/xvfb start
+ - sleep 1
+ - mkdir -p ~/.config/awesome
+ - echo "require('awful'); tags = {}; tags[1] = awful.tag({'name'})" > ~/.config/awesome/rc.lua
+ - "awesome &"
script: "bundle exec rake travis"
15 README.md
View
@@ -467,6 +467,21 @@ within_table('Employee') do
end
```
+### Working with windows
+
+Capybara provides some methods to ease finding and switching windows:
+
+```ruby
+facebook_window = window_opened_by do
+ click_button 'Like'
+end
+within_window facebook_window do
+ find('#login_email').set('a@example.com')
+ find('#login_password').set('qwerty')
+ click_button 'Submit'
+end
+```
+
### Scripting
In drivers which support it, you can easily execute JavaScript:
4 capybara.gemspec
View
@@ -37,13 +37,13 @@ Gem::Specification.new do |s|
s.add_development_dependency("cucumber", [">= 0.10.5"])
s.add_development_dependency("rake")
s.add_development_dependency("pry")
-
+
if RUBY_ENGINE == 'rbx' then
s.add_development_dependency("racc")
s.add_development_dependency("json")
s.add_development_dependency("rubysl")
end
-
+
if File.exist?("gem-private_key.pem")
s.signing_key = 'gem-private_key.pem'
end
3  lib/capybara.rb
View
@@ -13,6 +13,8 @@ class FileNotFound < CapybaraError; end
class UnselectNotAllowed < CapybaraError; end
class NotSupportedByDriverError < CapybaraError; end
class InfiniteRedirectError < CapybaraError; end
+ class ScopeError < CapybaraError; end
+ class WindowError < CapybaraError; end
class << self
attr_accessor :asset_host, :app_host, :run_server, :default_host, :always_include_port
@@ -316,6 +318,7 @@ module Selenium; end
require 'capybara/helpers'
require 'capybara/session'
require 'capybara/dsl'
+ require 'capybara/window'
require 'capybara/server'
require 'capybara/selector'
require 'capybara/query'
38 lib/capybara/driver/base.rb
View
@@ -51,10 +51,46 @@ def within_frame(frame_handle)
raise Capybara::NotSupportedByDriverError, 'Capybara::Driver::Base#within_frame'
end
- def within_window(handle)
+ def current_window_handle
+ raise Capybara::NotSupportedByDriverError, 'Capybara::Driver::Base#current_window_handle'
+ end
+
+ def window_size(handle)
+ raise Capybara::NotSupportedByDriverError, 'Capybara::Driver::Base#window_size'
+ end
+
+ def resize_window_to(handle, width, height)
+ raise Capybara::NotSupportedByDriverError, 'Capybara::Driver::Base#resize_window_to'
+ end
+
+ def maximize_window(handle)
+ raise Capybara::NotSupportedByDriverError, 'Capybara::Driver::Base#maximize_current_window'
+ end
+
+ def close_window(handle)
+ raise Capybara::NotSupportedByDriverError, 'Capybara::Driver::Base#close_window'
+ end
+
+ def window_handles
+ raise Capybara::NotSupportedByDriverError, 'Capybara::Driver::Base#window_handles'
+ end
+
+ def open_new_window
+ raise Capybara::NotSupportedByDriverError, 'Capybara::Driver::Base#open_new_window'
+ end
+
+ def switch_to_window(handle)
+ raise Capybara::NotSupportedByDriverError, 'Capybara::Driver::Base#switch_to_window'
+ end
+
+ def within_window(locator)
raise Capybara::NotSupportedByDriverError, 'Capybara::Driver::Base#within_window'
end
+ def no_such_window_error
+ raise Capybara::NotSupportedByDriverError, 'Capybara::Driver::Base#no_such_window_error'
+ end
+
def invalid_element_errors
[]
end
12 lib/capybara/node/base.rb
View
@@ -67,10 +67,13 @@ def reload
# Capybara will raise `Capybara::FrozenInTime`.
#
# @param [Integer] seconds Number of seconds to retry this block
+ # @param options [Hash]
+ # @option options [Array<Exception>] :errors (driver.invalid_element_errors +
+ # [Capybara::ElementNotFound]) exception types that cause the block to be rerun
# @return [Object] The result of the given block
# @raise [Capybara::FrozenInTime] If the return value of `Time.now` appears stuck
#
- def synchronize(seconds=Capybara.default_wait_time)
+ def synchronize(seconds=Capybara.default_wait_time, options = {})
start_time = Time.now
if session.synchronized
@@ -82,7 +85,7 @@ def synchronize(seconds=Capybara.default_wait_time)
rescue => e
session.raise_server_error!
raise e unless driver.wait?
- raise e unless catch_error?(e)
+ raise e unless catch_error?(e, options[:errors])
raise e if (Time.now - start_time) >= seconds
sleep(0.05)
raise Capybara::FrozenInTime, "time appears to be frozen, Capybara does not work with libraries which freeze time, consider using time travelling instead" if Time.now == start_time
@@ -96,8 +99,9 @@ def synchronize(seconds=Capybara.default_wait_time)
protected
- def catch_error?(error)
- (driver.invalid_element_errors + [Capybara::ElementNotFound]).any? do |type|
+ def catch_error?(error, errors = nil)
+ errors ||= (driver.invalid_element_errors + [Capybara::ElementNotFound])
+ errors.any? do |type|
error.is_a?(type)
end
end
37 lib/capybara/rspec/matchers.rb
View
@@ -109,6 +109,33 @@ def description
end
end
+ class BecomeClosed
+ def initialize(options)
+ @wait_time = Capybara::Query.new(options).wait
+ end
+
+ def matches?(window)
+ @window = window
+ start_time = Time.now
+ while window.exists? && (Time.now - start_time) < @wait_time
+ sleep 0.05
+ end
+ window.closed?
+ end
+
+ def failure_message
+ "expected #{@window.inspect} to become closed after #{@wait_time} seconds"
+ end
+
+ def failure_message_when_negated
+ "expected #{@window.inspect} not to become closed after #{@wait_time} seconds"
+ end
+
+ # RSpec 2 compatibility:
+ alias_method :failure_message_for_should, :failure_message
+ alias_method :failure_message_for_should_not, :failure_message_when_negated
+ end
+
def have_selector(*args)
HaveSelector.new(*args)
end
@@ -157,5 +184,15 @@ def have_select(locator, options={})
def have_table(locator, options={})
HaveSelector.new(:table, locator, options)
end
+
+ ##
+ # Wait for window to become closed.
+ # @example
+ # expect(window).to become_closed(wait: 0.8)
+ # @param options [Hash] optional param
+ # @option options [Numeric] :wait (Capybara.default_wait_time) wait time
+ def become_closed(options = {})
+ BecomeClosed.new(options)
+ end
end
end
86 lib/capybara/selenium/driver.rb
View
@@ -126,24 +126,69 @@ def within_frame(frame_handle)
@frame_handles[browser.window_handle].each { |fh| browser.switch_to.frame(fh) }
end
- def find_window( selector )
+ def current_window_handle
+ browser.window_handle
+ end
+
+ def window_size(handle)
+ within_given_window(handle) do
+ size = browser.manage.window.size
+ [size.width, size.height]
+ end
+ end
+
+ def resize_window_to(handle, width, height)
+ within_given_window(handle) do
+ browser.manage.window.resize_to(width, height)
+ end
+ end
+
+ def maximize_window(handle)
+ within_given_window(handle) do
+ browser.manage.window.maximize
+ end
+ sleep 0.1 # work around for https://code.google.com/p/selenium/issues/detail?id=7405
+ end
+
+ def close_window(handle)
+ within_given_window(handle) do
+ browser.close
+ end
+ end
+
+ def window_handles
+ browser.window_handles
+ end
+
+ def open_new_window
+ browser.execute_script('window.open();')
+ end
+
+ def switch_to_window(handle)
+ browser.switch_to.window handle
+ end
+
+ # @api private
+ def find_window(locator)
+ handles = browser.window_handles
+ return locator if handles.include? locator
+
original_handle = browser.window_handle
- browser.window_handles.each do |handle|
- browser.switch_to.window handle
- if( selector == browser.execute_script("return window.name") ||
- browser.title.include?(selector) ||
- browser.current_url.include?(selector) ||
- (selector == handle) )
- browser.switch_to.window original_handle
+ handles.each do |handle|
+ switch_to_window(handle)
+ if (locator == browser.execute_script("return window.name") ||
+ browser.title.include?(locator) ||
+ browser.current_url.include?(locator))
+ switch_to_window(original_handle)
return handle
end
end
- raise Capybara::ElementNotFound, "Could not find a window identified by #{selector}"
+ raise Capybara::ElementNotFound, "Could not find a window identified by #{locator}"
end
- def within_window(selector, &blk)
- handle = find_window( selector )
- browser.switch_to.window(handle, &blk)
+ def within_window(locator)
+ handle = find_window(locator)
+ browser.switch_to.window(handle) { yield }
end
def quit
@@ -158,4 +203,21 @@ def invalid_element_errors
[Selenium::WebDriver::Error::StaleElementReferenceError, Selenium::WebDriver::Error::UnhandledError, Selenium::WebDriver::Error::ElementNotVisibleError]
end
+ def no_such_window_error
+ Selenium::WebDriver::Error::NoSuchWindowError
+ end
+
+ private
+
+ def within_given_window(handle)
+ original_handle = self.current_window_handle
+ if handle == original_handle
+ yield
+ else
+ switch_to_window(handle)
+ result = yield
+ switch_to_window(original_handle)
+ result
+ end
+ end
end
176 lib/capybara/session.rb
View
@@ -40,7 +40,8 @@ class Session
SESSION_METHODS = [
:body, :html, :source, :current_url, :current_host, :current_path,
:execute_script, :evaluate_script, :visit, :go_back, :go_forward,
- :within, :within_fieldset, :within_table, :within_frame, :within_window,
+ :within, :within_fieldset, :within_table, :within_frame, :current_window,
+ :windows, :open_new_window, :switch_to_window, :within_window, :window_opened_by,
:save_page, :save_and_open_page, :save_screenshot,
:save_and_open_screenshot, :reset_session!, :response_headers,
:status_code, :title, :has_title?, :has_no_title?, :current_scope
@@ -327,17 +328,172 @@ def within_frame(frame_handle)
end
##
+ # @return [Capybara::Window] current window
#
- # Execute the given block within the given window. Only works on
- # some drivers (e.g. Selenium)
+ def current_window
+ Window.new(self, driver.current_window_handle)
+ end
+
+ ##
+ # Get all opened windows.
+ # The order of windows in returned array is not defined.
+ # The driver may sort windows by their creation time but it's not required.
#
- # @param [String] handle of the window
+ # @return [Array<Capybara::Window>] an array of all windows
#
- def within_window(handle, &blk)
- scopes.push(nil)
- driver.within_window(handle, &blk)
- ensure
- scopes.pop
+ def windows
+ driver.window_handles.map do |handle|
+ Window.new(self, handle)
+ end
+ end
+
+ ##
+ # Open new window.
+ # Current window doesn't change as the result of this call.
+ # It should be switched to explicitly.
+ #
+ # @return [Capybara::Window] window that has been opened
+ #
+ def open_new_window
+ window_opened_by do
+ driver.open_new_window
+ end
+ end
+
+ ##
+ # @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' }
+ # @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
+ #
+ def switch_to_window(window = nil)
+ block_given = block_given?
+ if window && block_given
+ raise ArgumentError, "`switch_to_window` can take either a block or a window, not both"
+ elsif !window && !block_given
+ raise ArgumentError, "`switch_to_window`: either window or block should be provided"
+ elsif scopes.size > 1
+ raise Capybara::ScopeError, "`switch_to_window` is not supposed to be invoked from "\
+ "`within`'s, `within_frame`'s' or `within_window`'s' block."
+ end
+
+ if window
+ driver.switch_to_window(window.handle)
+ window
+ else
+ 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
+ end
+ end
+
+ ##
+ # This method does the following:
+ #
+ # 1. Switches to the given window (it can be located by window instance/lambda/string).
+ # 2. Executes the given block (within window located at previous step).
+ # 3. Switches back (this step will be invoked even if exception will happen at second step)
+ #
+ # @overload within_window(window) { do_something }
+ # @param window [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.
+ # @example
+ # within_window(->{ page.title == 'Page title' }) { click_button 'Submit' }
+ # @raise [Capybara::WindowError] if no window matching lambda was found
+ # @overload within_window(string) { do_something }
+ # @deprecated Pass window or lambda instead
+ # @param [String] handle, name, url or title of the window
+ #
+ # @raise [Capybara::ScopeError] if this method is invoked inside `within`,
+ # `within_frame` or `within_window` methods
+ # @return value returned by the block
+ #
+ def within_window(window_or_handle)
+ if window_or_handle.instance_of?(Capybara::Window)
+ original = current_window
+ switch_to_window(window_or_handle) unless original == window_or_handle
+ scopes << nil
+ begin
+ yield
+ ensure
+ @scopes.pop
+ 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
+ yield
+ ensure
+ @scopes.pop
+ switch_to_window(original)
+ end
+ else
+ offending_line = caller.first
+ file_line = offending_line.match(/^(.+?):(\d+)/)[0]
+ warn "DEPRECATION WARNING: Passing string argument to #within_window is deprecated. "\
+ "Pass window object or lambda. (called from #{file_line})"
+ begin
+ scopes << nil
+ driver.within_window(window_or_handle) { yield }
+ ensure
+ @scopes.pop
+ end
+ end
+ end
+
+ ##
+ # Get the window that has been opened by the passed block.
+ # It will wait for it to be opened (in the same way as other Capybara methods wait).
+ # It's better to use this method than `windows.last`
+ # {https://dvcs.w3.org/hg/webdriver/raw-file/default/webdriver-spec.html#h_note_10 as order of windows isn't defined in some drivers}
+ #
+ # @param options [Hash]
+ # @option options [Numeric] :wait (Capybara.default_wait_time) wait time
+ # @return [Capybara::Window] the window that has been opened within a block
+ # @raise [Capybara::WindowError] if block passed to window hasn't opened window
+ # or opened more than one window
+ #
+ def window_opened_by(options = {}, &block)
+ old_handles = driver.window_handles
+ block.call
+
+ wait_time = Capybara::Query.new(options).wait
+ document.synchronize(wait_time, errors: [Capybara::WindowError]) do
+ opened_handles = (driver.window_handles - old_handles)
+ if opened_handles.size != 1
+ raise Capybara::WindowError, "block passed to #window_opened_by "\
+ "opened #{opened_handles.size} windows instead of 1"
+ end
+ Window.new(self, opened_handles.first)
+ end
end
##
@@ -479,7 +635,7 @@ def default_path(extension)
end
def scopes
- @scopes ||= [document]
+ @scopes ||= [nil]
end
end
end
84 lib/capybara/spec/session/window/become_closed_spec.rb
View
@@ -0,0 +1,84 @@
+Capybara::SpecHelper.spec '#become_closed', requires: [:windows, :js] do
+ before(:each) do
+ @window = @session.current_window
+ @session.visit('/with_windows')
+ @other_window = @session.window_opened_by do
+ @session.find(:css, '#openWindow').click
+ end
+ end
+
+ after(:each) do
+ @session.document.synchronize(3, errors: [Capybara::CapybaraError]) do
+ raise Capybara::CapybaraError if @session.windows.size != 1
+ end
+ @session.switch_to_window(@window)
+ end
+
+ context 'with :wait option' do
+ it 'should wait if value of :wait is more than timeout' do
+ @session.within_window @other_window do
+ @session.execute_script('setTimeout(function(){ window.close(); }, 500);')
+ end
+ Capybara.using_wait_time 0.1 do
+ expect(@other_window).to become_closed(wait: 0.7)
+ end
+ end
+
+ it 'should raise error if value of :wait is less than timeout' do
+ @session.within_window @other_window do
+ @session.execute_script('setTimeout(function(){ window.close(); }, 700);')
+ end
+ Capybara.using_wait_time 2 do
+ expect do
+ expect(@other_window).to become_closed(wait: 0.4)
+ end.to raise_error(RSpec::Expectations::ExpectationNotMetError, /\Aexpected #<Window @handle=".+"> to become closed after 0.4 seconds\Z/)
+ end
+ end
+ end
+
+ context 'without :wait option' do
+ it 'should wait if value of default_wait_time is more than timeout' do
+ @session.within_window @other_window do
+ @session.execute_script('setTimeout(function(){ window.close(); }, 500);')
+ end
+ Capybara.using_wait_time 0.7 do
+ expect(@other_window).to become_closed
+ end
+ end
+
+ it 'should raise error if value of default_wait_time is less than timeout' do
+ @session.within_window @other_window do
+ @session.execute_script('setTimeout(function(){ window.close(); }, 900);')
+ end
+ Capybara.using_wait_time 0.4 do
+ expect do
+ expect(@other_window).to become_closed
+ end.to raise_error(RSpec::Expectations::ExpectationNotMetError, /\Aexpected #<Window @handle=".+"> to become closed after 0.4 seconds\Z/)
+ end
+ end
+ end
+
+ context 'with not_to' do
+ it 'should raise error if default_wait_time is more than timeout' do
+ @session.within_window @other_window do
+ @session.execute_script('setTimeout(function(){ window.close(); }, 700);')
+ end
+ Capybara.using_wait_time 0.4 do
+ expect do
+ expect(@other_window).not_to become_closed
+ end
+ end
+ end
+
+ it 'should raise error if default_wait_time is more than timeout' do
+ @session.within_window @other_window do
+ @session.execute_script('setTimeout(function(){ window.close(); }, 700);')
+ end
+ Capybara.using_wait_time 1.1 do
+ expect do
+ expect(@other_window).not_to become_closed
+ end.to raise_error(RSpec::Expectations::ExpectationNotMetError, /\Aexpected #<Window @handle=".+"> not to become closed after 1.1 seconds\Z/)
+ end
+ end
+ end
+end
25 lib/capybara/spec/session/window/current_window_spec.rb
View
@@ -0,0 +1,25 @@
+Capybara::SpecHelper.spec '#current_window', requires: [:windows] do
+ before(:each) do
+ @window = @session.current_window
+ @session.visit('/with_windows')
+ end
+ after(:each) do
+ (@session.windows - [@window]).each do |w|
+ @session.switch_to_window w
+ w.close
+ end
+ @session.switch_to_window(@window)
+ end
+
+ it 'should return window' do
+ expect(@session.current_window).to be_instance_of(Capybara::Window)
+ end
+
+ it "should be modified by switching to another window" do
+ window = @session.window_opened_by { @session.find(:css, '#openWindow').click }
+
+ expect do
+ @session.switch_to_window(window)
+ end.to change { @session.current_window }.from(@window).to(window)
+ end
+end
28 lib/capybara/spec/session/window/open_new_window_spec.rb
View
@@ -0,0 +1,28 @@
+Capybara::SpecHelper.spec '#open_new_window', requires: [:windows] do
+ before(:each) do
+ @window = @session.current_window
+ @session.visit('/with_windows')
+ end
+ after(:each) do
+ (@session.windows - [@window]).each do |w|
+ @session.switch_to_window w
+ w.close
+ end
+ @session.switch_to_window(@window)
+ end
+
+ it 'should open new window with blank url and title' do
+ window = @session.open_new_window
+ @session.switch_to_window(window)
+ expect(['', 'about:blank']).to include(@session.title)
+ expect(@session.current_url).to eq('about:blank')
+ end
+
+ it 'should open window with changeable content' do
+ window = @session.open_new_window
+ @session.within_window window do
+ @session.visit '/with_html'
+ expect(@session).to have_css('#first')
+ end
+ end
+end
114 lib/capybara/spec/session/window/switch_to_window_spec.rb
View
@@ -0,0 +1,114 @@
+Capybara::SpecHelper.spec '#switch_to_window', requires: [:windows] do
+ before(:each) do
+ @window = @session.current_window
+ @session.visit('/with_windows')
+ end
+ after(:each) do
+ (@session.windows - [@window]).each do |w|
+ @session.switch_to_window w
+ w.close
+ end
+ @session.switch_to_window(@window)
+ end
+
+ it "should raise error when invoked without args" do
+ expect do
+ @session.switch_to_window
+ end.to raise_error(ArgumentError, "`switch_to_window`: either window or block should be provided")
+ end
+
+ it "should raise error when invoked with window and block" do
+ expect do
+ @session.switch_to_window(@window) { @session.title == 'Title of the first popup' }
+ end.to raise_error(ArgumentError, "`switch_to_window` can take either a block or a window, not both")
+ end
+
+ context "with an instance of Capybara::Window" do
+ it "should be able to switch to window" do
+ window = @session.open_new_window
+ expect(@session.title).to eq('With Windows')
+ @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
+ before(:each) do
+ @session.find(:css, '#openTwoWindows').click
+ end
+
+ it "should be able to switch to current window" do
+ @session.switch_to_window { @session.title == 'With Windows' }
+ expect(@session).to have_css('#openTwoWindows')
+ end
+
+ it "should find the div in another window" do
+ @session.switch_to_window { @session.title == 'Title of popup two' }
+ expect(@session).to have_css('#divInPopupTwo')
+ end
+
+ it "should be able to switch multiple times" do
+ @session.switch_to_window { @session.title == 'Title of the first popup' }
+ expect(@session).to have_css('#divInPopupOne')
+ @session.switch_to_window { @session.title == 'Title of popup two' }
+ expect(@session).to have_css('#divInPopupTwo')
+ end
+
+ it "should return window" do
+ window = @session.switch_to_window { @session.title == 'Title of popup two' }
+ expect((@session.windows - [@window])).to include(window)
+ end
+
+ it "should raise error when invoked inside `within` as it's nonsense" do
+ expect do
+ @session.within(:css, '#doesNotOpenWindows') do
+ @session.switch_to_window { @session.title == 'With Windows' }
+ 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
+
+ it "should raise error when invoked inside `within_frame` as it's nonsense" do
+ expect do
+ @session.within_frame('frameOne') do
+ @session.switch_to_window { @session.title == 'With Windows' }
+ 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
+
+ it "should raise error when invoked inside `within_window` as it's nonsense" do
+ window = (@session.windows - [@window]).first
+ expect do
+ @session.within_window window do
+ @session.switch_to_window { @session.title == 'With Windows' }
+ 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
+
+ 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
83 lib/capybara/spec/session/window/window_opened_by_spec.rb
View
@@ -0,0 +1,83 @@
+Capybara::SpecHelper.spec '#window_opened_by', requires: [:windows] do
+ before(:each) do
+ @window = @session.current_window
+ @session.visit('/with_windows')
+ end
+ after(:each) do
+ (@session.windows - [@window]).each do |w|
+ @session.switch_to_window w
+ w.close
+ end
+ @session.switch_to_window(@window)
+ end
+
+ let(:zero_windows_message) { "block passed to #window_opened_by opened 0 windows instead of 1" }
+ let(:two_windows_message) { "block passed to #window_opened_by opened 2 windows instead of 1" }
+
+ context 'with :wait option' 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.4) do
+ @session.find(:css, '#openWindowWithTimeout').click
+ end
+ end.to raise_error(Capybara::WindowError, zero_windows_message)
+ end
+ @session.document.synchronize(2, errors: [Capybara::CapybaraError]) do
+ raise Capybara::CapybaraError if @session.windows.size != 2
+ end
+ end
+
+ 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: 1.5) do
+ @session.find(:css, '#openWindowWithTimeout').click
+ end
+ expect(window).to be_instance_of(Capybara::Window)
+ end
+ end
+ end
+
+ context 'without :wait option' do
+ it 'should raise error if default_wait_time is less than timeout' do
+ Capybara.using_wait_time 0.4 do
+ expect do
+ @session.window_opened_by do
+ @session.find(:css, '#openWindowWithTimeout').click
+ end
+ end.to raise_error(Capybara::WindowError, zero_windows_message)
+ end
+ @session.document.synchronize(2, errors: [Capybara::CapybaraError]) do
+ raise Capybara::CapybaraError if @session.windows.size != 2
+ end
+ end
+
+ it 'should find window if default_wait_time is more than timeout' do
+ Capybara.using_wait_time 1.5 do
+ window = @session.window_opened_by do
+ @session.find(:css, '#openWindowWithTimeout').click
+ end
+ expect(window).to be_instance_of(Capybara::Window)
+ end
+ end
+ end
+
+ it 'should raise error when two windows have been opened by block' do
+ expect do
+ @session.window_opened_by do
+ @session.find(:css, '#openTwoWindows').click
+ end
+ end.to raise_error(Capybara::WindowError, two_windows_message)
+ @session.document.synchronize(2, errors: [Capybara::CapybaraError]) do
+ raise Capybara::CapybaraError if @session.windows.size != 3
+ end
+ end
+
+ it 'should raise error when no windows were opened by block' do
+ expect do
+ @session.window_opened_by do
+ @session.find(:css, '#doesNotOpenWindows').click
+ end
+ end.to raise_error(Capybara::WindowError, zero_windows_message)
+ end
+end
141 lib/capybara/spec/session/window/window_spec.rb
View
@@ -0,0 +1,141 @@
+Capybara::SpecHelper.spec Capybara::Window, requires: [:windows] do
+ before(:each) do
+ @window = @session.current_window
+ @session.visit('/with_windows')
+ end
+ after(:each) do
+ (@session.windows - [@window]).each do |w|
+ @session.switch_to_window w
+ w.close
+ end
+ @session.switch_to_window(@window)
+ end
+
+ describe '#exists?' do
+ before(:each) do
+ @other_window = @session.window_opened_by do
+ @session.find(:css, '#openWindow').click
+ end
+ end
+
+ it "should become false after window was closed" do
+ expect do
+ @session.switch_to_window @other_window
+ @other_window.close
+ end.to change { @other_window.exists? }.from(true).to(false)
+ end
+ end
+
+ describe '#closed?' do
+ it "should become true after window was closed" do
+ @other_window = @session.window_opened_by do
+ @session.find(:css, '#openWindow').click
+ end
+ expect do
+ @session.switch_to_window @other_window
+ @other_window.close
+ end.to change { @other_window.closed? }.from(false).to(true)
+ end
+ end
+
+ describe '#current?' do
+ before(:each) do
+ @other_window = @session.window_opened_by do
+ @session.find(:css, '#openWindow').click
+ end
+ end
+
+ it 'should become true after switching to window' do
+ expect do
+ @session.switch_to_window(@other_window)
+ end.to change { @other_window.current? }.from(false).to(true)
+ end
+
+ it 'should return false if window is closed' do
+ @session.switch_to_window(@other_window)
+ @other_window.close
+ expect(@other_window.current?).to eq(false)
+ end
+ end
+
+ describe '#close' do
+ before(:each) do
+ @other_window = @session.window_opened_by do
+ @session.find(:css, '#openWindow').click
+ end
+ end
+
+ it 'should switch to original window if invoked not for current window' do
+ expect(@session.windows.size).to eq(2)
+ expect(@session.current_window).to eq(@window)
+ @other_window.close
+ expect(@session.windows.size).to eq(1)
+ expect(@session.current_window).to eq(@window)
+ end
+
+ it 'should make subsequent invocations of other methods raise no_such_window_error if invoked for current window' do
+ @session.switch_to_window(@other_window)
+ expect(@session.current_window).to eq(@other_window)
+ @other_window.close
+ expect do
+ @session.find(:css, '#some_id')
+ end.to raise_error(@session.driver.no_such_window_error)
+ @session.switch_to_window(@window)
+ end
+ end
+
+ describe '#size' do
+ it 'should return size of whole window', requires: [:js] do
+ expect(@session.current_window.size).to eq @session.evaluate_script("[window.outerWidth, window.outerHeight];")
+ end
+
+ it 'should switch to original window if invoked not for current window' do
+ @other_window = @session.window_opened_by do
+ @session.find(:css, '#openWindow').click
+ end
+ size =
+ @session.within_window @other_window do
+ @session.evaluate_script("[window.outerWidth, window.outerHeight];")
+ end
+ expect(@other_window.size).to eq(size)
+ expect(@session.current_window).to eq(@window)
+ end
+ end
+
+ describe '#resize_to' do
+ it 'should be able to resize window', requires: [:js] do
+ width, height = @session.evaluate_script("[window.outerWidth, window.outerHeight];")
+ @session.current_window.resize_to(width-10, height-10)
+ expect(@session.evaluate_script("[window.outerWidth, window.outerHeight];")).to eq([width-10, height-10])
+ end
+
+ it 'should switch to original window if invoked not for current window' do
+ @other_window = @session.window_opened_by do
+ @session.find(:css, '#openWindow').click
+ end
+ @other_window.resize_to(400, 300)
+ expect(@session.current_window).to eq(@window)
+ expect(@other_window.size).to eq([400, 300])
+ end
+ end
+
+ describe '#maximize' do
+ it 'should be able to maximize window', requires: [:js] do
+ screen_width, screen_height = @session.evaluate_script("[window.screen.availWidth, window.screen.availHeight];")
+ window = @session.current_window
+ window.resize_to(screen_width-100, screen_height-100)
+ expect(@session.evaluate_script("[window.outerWidth, window.outerHeight];")).to eq([screen_width-100, screen_height-100])
+ window.maximize
+ expect(@session.evaluate_script("[window.outerWidth, window.outerHeight];")).to eq([screen_width, screen_height])
+ end
+
+ it 'should switch to original window if invoked not for current window' do
+ @other_window = @session.window_opened_by do
+ @session.find(:css, '#openWindow').click
+ end
+ @other_window.maximize
+ expect(@session.current_window).to eq(@window)
+ expect(@other_window.size).to eq(@session.evaluate_script("[window.screen.availWidth, window.screen.availHeight];"))
+ end
+ end
+end
31 lib/capybara/spec/session/window/windows_spec.rb
View
@@ -0,0 +1,31 @@
+Capybara::SpecHelper.spec '#windows', requires: [:windows] do
+ before(:each) do
+ @window = @session.current_window
+ @session.visit('/with_windows')
+ @session.find(:css, '#openTwoWindows').click
+
+ @session.document.synchronize(3, errors: [Capybara::CapybaraError]) do
+ raise Capybara::CapybaraError if @session.windows.size != 3
+ end
+ end
+ after(:each) do
+ (@session.windows - [@window]).each do |w|
+ @session.switch_to_window w
+ w.close
+ end
+ @session.switch_to_window(@window)
+ end
+
+ it 'should return objects of Capybara::Window class' do
+ expect(@session.windows.map { |window| window.instance_of?(Capybara::Window) }).to eq([true] * 3)
+ end
+
+ it 'should switchable windows' do
+ titles = @session.windows.map do |window|
+ @session.within_window(window) { @session.title }
+ end
+ expect(titles).to match_array([
+ 'With Windows', 'Title of the first popup', 'Title of popup two'
+ ])
+ end
+end
188 lib/capybara/spec/session/window/within_window_spec.rb
View
@@ -0,0 +1,188 @@
+Capybara::SpecHelper.spec '#within_window', requires: [:windows] do
+ before(:each) do
+ @window = @session.current_window
+ @session.visit('/with_windows')
+ @session.find(:css, '#openTwoWindows').click
+
+ @session.document.synchronize(3, errors: [Capybara::CapybaraError]) do
+ raise Capybara::CapybaraError if @session.windows.size != 3
+ end
+ end
+ after(:each) do
+ (@session.windows - [@window]).each do |w|
+ @session.switch_to_window w
+ w.close
+ end
+ @session.switch_to_window(@window)
+ end
+
+ context "with an instance of Capybara::Window" do
+ it "should not invoke driver#switch_to_window when given current window" do
+ # switch_to_window is invoked in after hook
+ expect(@session.driver).to receive(:switch_to_window).exactly(3).times.and_call_original
+ @session.within_window @window do
+ expect(@session.title).to eq('With Windows')
+ end
+ end
+
+ it "should be able to switch to another window" do
+ window = (@session.windows - [@window]).first
+ expect(@session.driver).to receive(:switch_to_window).exactly(5).times.and_call_original
+ @session.within_window window do
+ expect(['Title of the first popup', 'Title of popup two']).to include(@session.title)
+ end
+ expect(@session.title).to eq('With Windows')
+ end
+
+ it "returns value from the block" do
+ window = (@session.windows - [@window]).first
+ value = @session.within_window window do
+ 43252003274489856000
+ end
+ expect(value).to eq(43252003274489856000)
+ end
+
+ it "should switch back if exception was raised inside block" do
+ window = (@session.windows - [@window]).first
+ expect do
+ @session.within_window(window) do
+ @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 leave correct scopes after execution in case of error" do
+ window = (@session.windows - [@window]).first
+ expect do
+ @session.within 'html' do
+ @session.within_window(window) {}
+ end
+ end.to raise_error(Capybara::ScopeError)
+ 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
+ other_window = (@session.windows - [@window]).first
+ @session.within_window other_window do
+ other_window.close
+ end
+ expect do
+ @session.within_window(other_window) 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
+
+ context "with lambda" do
+ it "should find the div in another window" do
+ @session.within_window(->{ @session.title == 'Title of the first popup'}) do
+ expect(@session).to have_css('#divInPopupOne')
+ end
+ end
+
+ it "should find divs in both windows" do
+ @session.within_window(->{ @session.title == 'Title of popup two'}) do
+ expect(@session).to have_css('#divInPopupTwo')
+ end
+ @session.within_window(->{ @session.title == 'Title of the first popup'}) do
+ expect(@session).to have_css('#divInPopupOne')
+ end
+ expect(@session.title).to eq('With Windows')
+ end
+
+ it "should raise error if window wasn't found" do
+ expect do
+ @session.within_window(->{ @session.title == 'Invalid title'}) 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
+ value = @session.within_window(->{ @session.title == 'Title of popup two'}) do
+ 42
+ end
+ expect(value).to eq(42)
+ end
+
+ it "should switch back if exception was raised inside block" do
+ expect do
+ @session.within_window(->{ @session.title == 'Title of popup two'}) do
+ raise 'some error'
+ end
+ end.to raise_error(StandardError, 'some error')
+ expect(@session.current_window).to eq(@window)
+ expect(@session.send(:scopes)).to eq([nil])
+ end
+ end
+
+ context "with string" 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__}:138)").and_call_original
+ @session.within_window('firstPopup') {}
+ end
+
+ it "should find window by handle" do
+ window = (@session.windows - [@window]).first
+ @session.within_window window.handle do
+ expect(['Title of the first popup', 'Title of popup two']).to include(@session.title)
+ end
+ end
+
+ it "should find the div in firstPopup" do
+ @session.within_window("firstPopup") do
+ expect(@session.find("//*[@id='divInPopupOne']").text).to eq 'This is the text of divInPopupOne'
+ end
+ end
+ it "should find the div in secondPopup" do
+ @session.within_window("secondPopup") do
+ expect(@session.find("//*[@id='divInPopupTwo']").text).to eq 'This is the text of divInPopupTwo'
+ end
+ end
+ it "should find the divs in both popups" do
+ @session.within_window("secondPopup") do
+ expect(@session.find("//*[@id='divInPopupTwo']").text).to eq 'This is the text of divInPopupTwo'
+ end
+ @session.within_window("firstPopup") do
+ expect(@session.find("//*[@id='divInPopupOne']").text).to eq 'This is the text of divInPopupOne'
+ end
+ end
+ it "should find the div in the main window after finding a div in a popup" do
+ @session.within_window("secondPopup") do
+ expect(@session.find("//*[@id='divInPopupTwo']").text).to eq 'This is the text of divInPopupTwo'
+ end
+ expect(@session.find("//*[@id='doesNotOpenWindows']").text).to eq 'Does not open windows'
+ end
+ it "should reset scope when switching windows" do
+ @session.within(:css, '#doesNotOpenWindows') do
+ @session.within_window("secondPopup") do
+ expect(@session.find("//*[@id='divInPopupTwo']").text).to eq 'This is the text of divInPopupTwo'
+ end
+ end
+ end
+ it "should switch back if exception was raised inside block" do
+ expect do
+ @session.within_window('secondPopup') do
+ raise 'some error'
+ end
+ end.to raise_error(StandardError, 'some error')
+ expect(@session.current_window).to eq(@window)
+ end
+ end
+end
45 lib/capybara/spec/session/within_window_spec.rb
View
@@ -1,45 +0,0 @@
-Capybara::SpecHelper.spec '#within_window', :requires => [:windows] do
- before(:each) do
- @session.visit('/within_popups')
- end
- after(:each) do
- @session.within_window("firstPopup") do
- @session.evaluate_script('window.close()')
- end
- @session.within_window("secondPopup") do
- @session.evaluate_script('window.close()')
- end
- end
-
- it "should find the div in firstPopup" do
- @session.within_window("firstPopup") do
- expect(@session.find("//*[@id='divInPopupOne']").text).to eql 'This is the text of divInPopupOne'
- end
- end
- it "should find the div in secondPopup" do
- @session.within_window("secondPopup") do
- expect(@session.find("//*[@id='divInPopupTwo']").text).to eql 'This is the text of divInPopupTwo'
- end
- end
- it "should find the divs in both popups" do
- @session.within_window("secondPopup") do
- expect(@session.find("//*[@id='divInPopupTwo']").text).to eql 'This is the text of divInPopupTwo'
- end
- @session.within_window("firstPopup") do
- expect(@session.find("//*[@id='divInPopupOne']").text).to eql 'This is the text of divInPopupOne'
- end
- end
- it "should find the div in the main window after finding a div in a popup" do
- @session.within_window("secondPopup") do
- expect(@session.find("//*[@id='divInPopupTwo']").text).to eql 'This is the text of divInPopupTwo'
- end
- expect(@session.find("//*[@id='divInMainWindow']").text).to eql 'This is the text for divInMainWindow'
- end
- it "should reset scope when switching windows" do
- @session.within(:css, '#divInMainWindow') do
- @session.within_window("secondPopup") do
- expect(@session.find("//*[@id='divInPopupTwo']").text).to eql 'This is the text of divInPopupTwo'
- end
- end
- end
-end
3  lib/capybara/spec/spec_helper.rb
View
@@ -54,6 +54,7 @@ def run_specs(session, name, options={})
specs = @specs
RSpec.describe Capybara::Session, name, options do
include Capybara::SpecHelper
+ include Capybara::RSpecMatchers
before do
@session = session
end
@@ -92,4 +93,4 @@ def extract_results(session)
end
end
-Dir[File.dirname(__FILE__)+'/session/*'].each { |group| require group }
+Dir[File.dirname(__FILE__) + "/session/**/*.rb"].each { |file| require_relative file }
2  lib/capybara/spec/views/popup_one.erb
View
@@ -1,6 +1,6 @@
<html>
<head>
- <title>This is the title of the first popup</title>
+ <title>Title of the first popup</title>
</head>
<body>
<div id="divInPopupOne">This is the text of divInPopupOne</div>
2  lib/capybara/spec/views/popup_two.erb
View
@@ -1,6 +1,6 @@
<html>
<head>
- <title>This is the title of popup two</title>
+ <title>Title of popup two</title>
</head>
<body>
<div id="divInPopupTwo">This is the text of divInPopupTwo</div>
38 lib/capybara/spec/views/with_windows.erb
View
@@ -0,0 +1,38 @@
+<html>
+ <head>
+ <title>With Windows</title>
+ <script src="/jquery.js" type="text/javascript" charset="utf-8"></script>
+ <script language="javascript" type="text/javascript">
+ $(document).ready(function(){
+ $('#openWindow').click(function(){
+ window.open('/popup_one', 'firstPopup');
+ return false;
+ });
+
+ $('#openWindowWithTimeout').click(function(){
+ setTimeout(function(){
+ window.open('/popup_one', 'firstPopup');
+ }, 600);
+ return false;
+ });
+
+ $('#openTwoWindows').click(function() {
+ window.open('/popup_one', 'firstPopup');
+ window.open('/popup_two', 'secondPopup');
+ return false;
+ });
+ });
+ </script>
+ </head>
+ <body>
+ <button id="openWindow">Open new window</button>
+
+ <button id="openWindowWithTimeout">Open new window with timeout</button>
+
+ <button id="openTwoWindows">Open two windows</button>
+
+ <button id="doesNotOpenWindows">Does not open windows</button>
+
+ <iframe src="/frame_one" id="frameOne"></iframe>
+ </body>
+</html>
25 lib/capybara/spec/views/within_popups.erb
View
@@ -1,25 +0,0 @@
-<html>
- <head>
- <title>With Popups</title>
- <script language="javascript" type="text/javascript">
- <!--
-
- function popItUp(name, url) {
- newwindow=window.open(url,name,'height=200,width=150');
- if (window.focus) { newwindow.focus() }
- return false;
- }
-
- function init() {
- popItUp('firstPopup', '/popup_one');
- popItUp('secondPopup', '/popup_two');
- }
- window.onload = init;
-
- // -->
- </script>
- </head>
- <body>
- <div id="divInMainWindow">This is the text for divInMainWindow</div>
- </body>
-</html>
123 lib/capybara/window.rb
View
@@ -0,0 +1,123 @@
+module Capybara
+ ##
+ # The Window class represents a browser window.
+ #
+ # You can get an instance of the class by calling either of:
+ #
+ # * {Capybara::Session#windows}
+ # * {Capybara::Session#current_window}
+ # * {Capybara::Session#window_opened_by}
+ # * {Capybara::Session#switch_to_window}
+ #
+ # Note that some drivers (e.g. Selenium) support getting size of/resizing/closing only
+ # current window. So if you invoke such method for:
+ #
+ # * window that is current, Capybara will make 2 Selenium method invocations
+ # (get handle of current window + get size/resize/close).
+ # * window that is not current, Capybara will make 4 Selenium method invocations
+ # (get handle of current window + switch to given handle + get size/resize/close + switch to original handle)
+ #
+ class Window
+ # @return [String] a string that uniquely identifies window within session
+ attr_reader :handle
+
+ # @return [Capybara::Session] session that this window belongs to
+ attr_reader :session
+
+ # @api private
+ def initialize(session, handle)
+ @session = session
+ @driver = session.driver
+ @handle = handle
+ end
+
+ ##
+ # @return [Boolean] whether the window is not closed
+ def exists?
+ @driver.window_handles.include?(@handle)
+ end
+
+ ##
+ # @return [Boolean] whether the window is closed
+ def closed?
+ !exists?
+ end
+
+ ##
+ # @return [Boolean] whether this window is the window in which commands are being executed
+ def current?
+ @driver.current_window_handle == @handle
+ rescue @driver.no_such_window_error
+ false
+ end
+
+ ##
+ # Close window.
+ #
+ # If this method was called for window that is current, then after calling this method
+ # future invocations of other Capybara methods should raise
+ # `session.driver.no_such_window_error` until another window will be switched to.
+ #
+ # @!macro about_current
+ # If this method was called for window that is not current, then after calling this method
+ # current window shouldn remain the same as it was before calling this method.
+ #
+ def close
+ @driver.close_window(handle)
+ end
+
+ ##
+ # Get window size.
+ #
+ # @macro about_current
+ # @return [Array<(Fixnum, Fixnum)>] an array with width and height
+ #
+ def size
+ @driver.window_size(handle)
+ end
+
+ ##
+ # Resize window.
+ #
+ # @macro about_current
+ # @param width [String] the new window width in pixels
+ # @param height [String] the new window height in pixels
+ #
+ def resize_to(width, height)
+ @driver.resize_window_to(handle, width, height)
+ end
+
+ ##
+ # Maximize window.
+ #
+ # If a particular driver (e.g. headless driver) doesn't have concept of maximizing it
+ # may not support this method.
+ #
+ # @macro about_current
+ #
+ def maximize
+ @driver.maximize_window(handle)
+ end
+
+ def eql?(other)
+ other.kind_of?(self.class) && @session == other.session && @handle == other.handle
+ end
+ alias_method :==, :eql?
+
+ def hash
+ @session.hash ^ @handle.hash
+ end
+
+ def inspect
+ "#<Window @handle=#{@handle.inspect}>"
+ end
+
+ private
+
+ def raise_unless_current(what)
+ unless current?
+ raise Capybara::WindowError, "#{what} not current window is not possible."
+ end
+ end
+ end
+end
Please sign in to comment.
Something went wrong with that request. Please try again.