1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
|
# frozen_string_literal: true
module Capybara
##
# The {Window} class represents a browser window.
#
# You can get an instance of the class by calling any 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
# {Capybara::Driver::Base#no_such_window_error 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 should 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<(Integer, Integer)>] an array with width and height
#
def size
@driver.window_size(handle)
end
##
# Resize window.
#
# @macro about_current
# @param width [Integer] the new window width in pixels
# @param height [Integer] the new window height in pixels
#
def resize_to(width, height)
wait_for_stable_size { @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
wait_for_stable_size { @driver.maximize_window(handle) }
end
##
# Fullscreen window.
#
# If a particular driver doesn't have concept of fullscreen it may not support this method.
#
# @macro about_current
#
def fullscreen
@driver.fullscreen_window(handle)
end
def eql?(other)
other.is_a?(self.class) && @session == other.session && @handle == other.handle
end
alias_method :==, :eql?
def hash
[@session, @handle].hash
end
def inspect
"#<Window @handle=#{@handle.inspect}>"
end
private
def wait_for_stable_size(seconds = session.config.default_max_wait_time)
res = yield if block_given?
timer = Capybara::Helpers.timer(expire_in: seconds)
loop do
prev_size = size
sleep 0.025
return res if prev_size == size
break if timer.expired?
end
raise Capybara::WindowError, "Window size not stable within #{seconds} seconds."
end
end
end
|