module RAutomation class UnknownElementException < RuntimeError end class UnknownWindowException < UnknownElementException end class UnknownButtonException < UnknownElementException end class UnknownTextFieldException < UnknownElementException end class Window include Adapter::Helper extend ElementCollections has_many :windows, :buttons, :text_fields class << self # @param locators (see #initialize) # @return [Windows] all windows. def windows(locators = {}) Windows.new(nil, locators) end end # Retrieves all windows with similar locators to the current window. # @param locators (see #initialize) # @return [Windows] all windows matching current window's _locators_ if no # explicit locators specified or windows matching the specified _locators_. def windows(locators = @window.locators) Windows.new(nil, locators) end # Currently used {Adapter}. attr_reader :adapter # Creates the window object. # # Possible window _locators_ may depend of the used platform and adapter, but # following examples will use :title, :class and :hwnd. # # @example Use window with some title: # RAutomation::Window.new(:title => "some title") # # @example Use window with Regexp title: # RAutomation::Window.new(:title => /some title/i) # # @example Use window with handle (hwnd): # RAutomation::Window.new(:hwnd => 123456) # # @example Use multiple locators, every locator will be matched (AND-ed) to the window: # RAutomation::Window.new(:title => "some title", :class => "IEFrame") # # @note Refer to all possible _locators_ in each {Adapter} documentation. # # _locators_ may also include a key called :adapter to change default adapter, # which is dependent of the platform, to automate windows and their controls. # # It is also possible to change the default adapter by using environment variable called # __RAUTOMATION_ADAPTER__ # # @note This constructor doesn't check for window's existance. # @note Only visible windows are supported. # @note If given _locators_ include :hwnd then every other possible _locator_ is ignored. # @param [Hash] locators locators for the window. def initialize(locators) @adapter = locators.delete(:adapter) || ENV["RAUTOMATION_ADAPTER"] && ENV["RAUTOMATION_ADAPTER"].to_sym || default_adapter @window = Adapter.const_get(normalize(@adapter)).const_get(:Window).new(self, locators) end class << self # Timeout for waiting until object exists. If the timeout exceeds then an {WaitHelper::TimeoutError} is raised. @@wait_timeout = 60 # Change the timeout to wait before {WaitHelper::TimeoutError} is raised. # @param [Fixnum] timeout in seconds. def wait_timeout=(timeout) @@wait_timeout = timeout end # Retrieve current timeout in seconds to wait before {WaitHelper::TimeoutError} is raised. # @return [Fixnum] timeout in seconds def wait_timeout @@wait_timeout end end # @return [Fixnum] handle of the window which is used internally for other methods. # @raise [UnknownWindowException] if the window doesn't exist. def hwnd wait_until_present @window.hwnd end # @return [Fixnum] process identifier (PID) of the window. # @raise [UnknownWindowException] if the window doesn't exist. def pid wait_until_present @window.pid end # @return [String] title of the window. # @raise [UnknownWindowException] if the window doesn't exist. def title wait_until_present @window.title end # @return [Array<String>] all {Window} class names in a sorted array. # @raise [UnknownWindowException] if the window doesn't exist. def class_names wait_until_present @window.class_names end # Activates the Window, e.g. brings it to the top of other windows. def activate @window.activate end # Checks if the window is active, e.g. on the top of other windows. # @return [Boolean] true if the window is active, false otherwise. def active? @window.active? end # Returns visible text of the Window. # @return [String] visible text of the window. # @raise [UnknownWindowException] if the window doesn't exist. def text wait_until_present @window.text end # Checks if the window exists (does have to be visible). # @return [Boolean] true if the window exists, false otherwise. def exists? @window.exists? end alias_method :exist?, :exists? # Checks if window is visible. # @note Window is also visible, if it is behind other windows or minimized. # @return [Boolean] true if window is visible, false otherwise. # @raise [UnknownWindowException] if the window doesn't exist. def visible? wait_until_exists @window.visible? end # Checks if the window exists and is visible. # @return [Boolean] true if window exists and is visible, false otherwise def present? exists? && visible? end # Maximizes the window. # @raise [UnknownWindowException] if the window doesn't exist. def maximize wait_until_present @window.maximize end # Minimizes the window. # @raise [UnknownWindowException] if the window doesn't exist. def minimize wait_until_present @window.minimize end # Checks if window is minimized. # @return [Boolean] true if window is minimized, false otherwise. # @raise [UnknownWindowException] if the window doesn't exist. def minimized? wait_until_present @window.minimized? end # Restores the window size and position. # @note If the window is minimized, makes it visible again. # @raise [UnknownWindowException] if the window doesn't exist. def restore wait_until_present @window.restore end # Sends keyboard keys to the window. Refer to specific {Adapter} documentation for all possible values. # @raise [UnknownWindowException] if the window doesn't exist. def send_keys(*keys) wait_until_present @window.send_keys(keys) end # Closes the window if it exists. def close return unless @window.exists? @window.close end # Retrieves {Button} on the window. # @note Refer to specific {Adapter} documentation for possible _locator_ parameters. # @param [Hash] locators for the {Button}. # @raise [UnknownWindowException] if the window doesn't exist. def button(locators={}) wait_until_present Button.new(@window, locators) end # Retrieves {TextField} on the window. # @note Refer to specific {Adapter} documentation for possible _locators_ parameters. # @raise [UnknownWindowException] if the window doesn't exist. def text_field(locators={}) wait_until_present TextField.new(@window, locators) end # Allows to execute specific {Adapter} methods not part of the public API. def method_missing(name, *args) @window.send(name, *args) end def wait_until_present WaitHelper.wait_until {present?} rescue WaitHelper::TimeoutError raise UnknownWindowException, "Window with locator #{@window.locators.inspect} doesn't exist or is not visible!" end def wait_until_exists WaitHelper.wait_until {exists?} rescue WaitHelper::TimeoutError raise UnknownWindowException, "Window with locator #{@window.locators.inspect} doesn't exist!" end private def normalize adapter adapter.to_s.split("_").map {|word| word.capitalize}.join end end end