Class: Capybara::Selenium::Driver
Relationships & Source Files | |
Namespace Children | |
Modules:
| |
Super Chains via Extension / Inclusion / Inheritance | |
Class Chain:
self,
Driver::Base
|
|
Instance Chain:
self,
Find ,
Driver::Base
|
|
Inherits: |
Capybara::Driver::Base
|
Defined in: | lib/capybara/selenium/driver.rb |
Constant Summary
-
CAPS_VERSION =
# File 'lib/capybara/selenium/driver.rb', line 15Gem::Requirement.new('< 4.8.0')
-
DEFAULT_OPTIONS =
# File 'lib/capybara/selenium/driver.rb', line 9{ browser: :firefox, clear_local_storage: nil, clear_session_storage: nil }.freeze
-
SPECIAL_OPTIONS =
# File 'lib/capybara/selenium/driver.rb', line 14%i[browser clear_local_storage clear_session_storage timeout native_displayed].freeze
Class Attribute Summary
- .selenium_webdriver_version readonly
- .specializations readonly
Class Method Summary
Instance Attribute Summary
- #app readonly
- #needs_server? ⇒ Boolean readonly
- #options readonly
- #wait? ⇒ Boolean readonly
Driver::Base
- Inherited
Instance Method Summary
- #accept_modal(_type, **options)
- #active_element
- #browser
- #close_window(handle)
- #current_url
- #current_window_handle
- #dismiss_modal(_type, **options)
- #evaluate_async_script(script, *args)
- #evaluate_script(script, *args)
- #execute_script(script, *args)
- #frame_obscured_at?(x:, y:) ⇒ Boolean
- #fullscreen_window(handle)
- #go_back
- #go_forward
- #html
- #invalid_element_errors
- #maximize_window(handle)
- #no_such_window_error
- #open_new_window(kind = :tab)
- #quit
- #refresh
- #reset!
- #resize_window_to(handle, width, height)
- #save_screenshot(path, **options)
- #send_keys(*args)
- #switch_to_frame(frame)
- #switch_to_window(handle)
- #title
- #visit(path)
- #window_handles
- #window_size(handle)
- #accept_unhandled_reset_alert private
- #bridge private
- #build_node(native_node, initial_cache = {}) private
- #clear_browser_state private
- #clear_browser_state_errors private
- #clear_local_storage private
- #clear_session_storage private
- #clear_storage private
- #delete_all_cookies private
- #find_context private
- #find_modal(text: nil, **options) private
- #find_modal_errors private
- #modal_error private
- #native_active_element private
- #native_args(args) private
- #navigate_with_accept(url) private
- #reset_browser_state private
- #setup_exit_handler private
- #silenced_unknown_error_message?(msg) ⇒ Boolean private
- #silenced_unknown_error_messages private
- #specialize_driver private
- #unhandled_alert_errors private
- #unwrap_script_result(arg) private
- #wait_for_empty_page(timer) private
- #within_given_window(handle) private
Find
- Included
#find_css, #find_xpath, #build_hints_js, #es_context, #filter_by_text, #find_by, #gather_hints, #is_displayed_atom |
Driver::Base
- Inherited
#accept_modal | Execute the block, and then accept the modal opened. |
#active_element, #close_window, #current_url, #current_window_handle, | |
#dismiss_modal | Execute the block, and then dismiss the modal opened. |
#evaluate_async_script, #evaluate_script, #execute_script, #find_css, #find_xpath, #frame_title, #frame_url, #fullscreen_window, #go_back, #go_forward, #html, #invalid_element_errors, #maximize_window, #no_such_window_error, #open_new_window, #refresh, #reset!, #resize_window_to, #response_headers, #save_screenshot, #send_keys, #session_options, #status_code, #switch_to_frame, #switch_to_window, #visit, #window_handles, #window_size |
Constructor Details
.new(app, **options) ⇒ Driver
# File 'lib/capybara/selenium/driver.rb', line 83
def initialize(app, ** ) super() self.class.load_selenium @app = app @browser = nil @exit_status = nil @frame_handles = Hash.new { |hash, handle| hash[handle] = [] } @options = DEFAULT_OPTIONS.merge( ) @node_class = ::Capybara::Selenium::Node end
Class Attribute Details
.selenium_webdriver_version (readonly)
[ GitHub ]# File 'lib/capybara/selenium/driver.rb', line 20
attr_reader :selenium_webdriver_version
.specializations (readonly)
[ GitHub ]# File 'lib/capybara/selenium/driver.rb', line 55
attr_reader :specializations
Class Method Details
.load_selenium
[ GitHub ]# File 'lib/capybara/selenium/driver.rb', line 22
def load_selenium require 'selenium-webdriver' require 'capybara/selenium/patches/atoms' require 'capybara/selenium/patches/is_displayed' # Look up the version of `selenium-webdriver` to # see if it's a version we support. # # By default, we use Gem.loaded_specs to determine # the version number. However, in some cases, such # as when loading `selenium-webdriver` outside of # Rubygems, we fall back to referencing # Selenium::WebDriver::VERSION. Ideally we'd # use the constant in all cases, but earlier versions # of `selenium-webdriver` didn't provide the constant. @selenium_webdriver_version = if Gem.loaded_specs['selenium-webdriver'] Gem.loaded_specs['selenium-webdriver'].version else Gem::Version.new(Selenium::WebDriver::VERSION) end unless Gem::Requirement.new('>= 4.8').satisfied_by? @selenium_webdriver_version warn "Warning: You're using an unsupported version of selenium-webdriver, please upgrade to 4.8+." end @selenium_webdriver_version rescue LoadError => e raise e unless e. .include?('selenium-webdriver') raise LoadError, "Capybara's selenium driver is unable to load `selenium-webdriver`, please install the gem and add `gem 'selenium-webdriver'` to your Gemfile if you are using bundler." end
.register_specialization(browser_name, specialization)
[ GitHub ]# File 'lib/capybara/selenium/driver.rb', line 57
def register_specialization(browser_name, specialization) @specializations ||= {} @specializations[browser_name] = specialization end
Instance Attribute Details
#app (readonly)
[ GitHub ]# File 'lib/capybara/selenium/driver.rb', line 17
attr_reader :app, :
#needs_server? ⇒ Boolean
(readonly)
[ GitHub ]
# File 'lib/capybara/selenium/driver.rb', line 125
def needs_server?; true; end
#options (readonly)
[ GitHub ]# File 'lib/capybara/selenium/driver.rb', line 17
attr_reader :app, :
#wait? ⇒ Boolean
(readonly)
[ GitHub ]
# File 'lib/capybara/selenium/driver.rb', line 124
def wait?; true; end
Instance Method Details
#accept_modal(_type, **options)
[ GitHub ]# File 'lib/capybara/selenium/driver.rb', line 264
def accept_modal(_type, ** ) yield if block_given? modal = find_modal(** ) modal.send_keys [:with] if [:with] = modal.text modal.accept end
#accept_unhandled_reset_alert (private)
[ GitHub ]# File 'lib/capybara/selenium/driver.rb', line 502
def accept_unhandled_reset_alert @browser.switch_to.alert.accept sleep 0.25 # allow time for the modal to be handled rescue modal_error # The alert is now gone. # If navigation has not occurred attempt again and accept alert # since FF may have dismissed the alert at first attempt. navigate_with_accept('about:blank') if current_url != 'about:blank' end
#active_element
[ GitHub ]# File 'lib/capybara/selenium/driver.rb', line 142
def active_element build_node(native_active_element) end
#bridge (private)
[ GitHub ]# File 'lib/capybara/selenium/driver.rb', line 463
def bridge browser.send(:bridge) end
#browser
[ GitHub ]# File 'lib/capybara/selenium/driver.rb', line 63
def browser unless @browser [:http_client] ||= begin require 'capybara/selenium/patches/persistent_client' if [:timeout] ::Capybara::Selenium::PersistentClient.new(read_timeout: [:timeout]) else ::Capybara::Selenium::PersistentClient.new end end = .except(*SPECIAL_OPTIONS) @browser = Selenium::WebDriver.for( [:browser], ) specialize_driver setup_exit_handler end @browser end
#build_node(native_node, initial_cache = {}) (private)
[ GitHub ]#clear_browser_state (private)
[ GitHub ]# File 'lib/capybara/selenium/driver.rb', line 327
def clear_browser_state clear_storage rescue *clear_browser_state_errors # delete_all_cookies fails when we've previously gone # to about:blank, so we rescue this error and do nothing # instead. end
#clear_browser_state_errors (private)
[ GitHub ]# File 'lib/capybara/selenium/driver.rb', line 336
def clear_browser_state_errors @clear_browser_state_errors ||= [Selenium::WebDriver::Error::UnknownError] end
#clear_local_storage (private)
[ GitHub ]# File 'lib/capybara/selenium/driver.rb', line 369
def clear_local_storage if @browser.respond_to? :local_storage @browser.local_storage.clear else begin @browser&.execute_script('window.localStorage.clear()') rescue # rubocop:disable Style/RescueStandardError unless [:clear_local_storage].nil? warn 'localStorage clear requested but is not supported by this driver' end end end end
#clear_session_storage (private)
[ GitHub ]# File 'lib/capybara/selenium/driver.rb', line 355
def clear_session_storage if @browser.respond_to? :session_storage @browser.session_storage.clear else begin @browser&.execute_script('window.sessionStorage.clear()') rescue # rubocop:disable Style/RescueStandardError unless [:clear_session_storage].nil? warn 'sessionStorage clear requested but is not supported by this driver' end end end end
#clear_storage (private)
[ GitHub ]# File 'lib/capybara/selenium/driver.rb', line 348
def clear_storage clear_session_storage unless [:clear_session_storage] == false clear_local_storage unless [:clear_local_storage] == false rescue Selenium::WebDriver::Error::JavascriptError, Selenium::WebDriver::Error::WebDriverError # session/local storage may not be available if on non-http pages (e.g. about:blank) end
#close_window(handle)
# File 'lib/capybara/selenium/driver.rb', line 235
def close_window(handle) raise ArgumentError, 'Not allowed to close the primary window' if handle == window_handles.first within_given_window(handle) do browser.close end end
#current_url
[ GitHub ]# File 'lib/capybara/selenium/driver.rb', line 120
def current_url browser.current_url end
#current_window_handle
[ GitHub ]# File 'lib/capybara/selenium/driver.rb', line 205
def current_window_handle browser.window_handle end
#delete_all_cookies (private)
[ GitHub ]# File 'lib/capybara/selenium/driver.rb', line 344
def @browser.manage. end
#dismiss_modal(_type, **options)
[ GitHub ]# File 'lib/capybara/selenium/driver.rb', line 275
def dismiss_modal(_type, ** ) yield if block_given? modal = find_modal(** ) = modal.text modal.dismiss end
#evaluate_async_script(script, *args)
[ GitHub ]# File 'lib/capybara/selenium/driver.rb', line 136
def evaluate_async_script(script, *args) browser.manage.timeouts.script_timeout = Capybara.default_max_wait_time result = browser.execute_async_script(script, *native_args(args)) unwrap_script_result(result) end
#evaluate_script(script, *args)
[ GitHub ]# File 'lib/capybara/selenium/driver.rb', line 131
def evaluate_script(script, *args) result = execute_script("return #{script}", *args) unwrap_script_result(result) end
#execute_script(script, *args)
[ GitHub ]# File 'lib/capybara/selenium/driver.rb', line 127
def execute_script(script, *args) browser.execute_script(script, *native_args(args)) end
#find_context (private)
[ GitHub ]# File 'lib/capybara/selenium/driver.rb', line 455
def find_context browser end
#find_modal(text: nil, **options) (private)
[ GitHub ]# File 'lib/capybara/selenium/driver.rb', line 407
def find_modal(text: nil, ** ) # Selenium has its own built in wait (2 seconds)for a modal to show up, so this wait is really the minimum time # Actual wait time may be longer than specified wait = Selenium::WebDriver::Wait.new( timeout: .fetch(:wait, .default_max_wait_time) || 0, ignore: modal_error ) begin wait.until do alert = @browser.switch_to.alert regexp = text.is_a?(Regexp) ? text : Regexp.new(Regexp.escape(text.to_s)) matched = alert.text.match?(regexp) unless matched raise Capybara::ModalNotFound, "Unable to find modal dialog with #{text} - found '#{alert.text}' instead." end alert end rescue *find_modal_errors raise Capybara::ModalNotFound, "Unable to find modal dialog#{" with #{text}" if text}" end end
#find_modal_errors (private)
[ GitHub ]# File 'lib/capybara/selenium/driver.rb', line 430
def find_modal_errors @find_modal_errors ||= [Selenium::WebDriver::Error::TimeoutError] end
#frame_obscured_at?(x:, y:) ⇒ Boolean
# File 'lib/capybara/selenium/driver.rb', line 178
def frame_obscured_at?(x:, y:) frame = @frame_handles[current_window_handle].last return false unless frame switch_to_frame(:parent) begin frame.base.obscured?(x: x, y: y) ensure switch_to_frame(frame) end end
#fullscreen_window(handle)
[ GitHub ]# File 'lib/capybara/selenium/driver.rb', line 229
def fullscreen_window(handle) within_given_window(handle) do browser.manage.window.full_screen end end
#go_back
[ GitHub ]# File 'lib/capybara/selenium/driver.rb', line 102
def go_back browser.navigate.back end
#go_forward
[ GitHub ]# File 'lib/capybara/selenium/driver.rb', line 106
def go_forward browser.navigate.forward end
#html
[ GitHub ]#invalid_element_errors
[ GitHub ]# File 'lib/capybara/selenium/driver.rb', line 297
def invalid_element_errors @invalid_element_errors ||= [ ::Selenium::WebDriver::Error::StaleElementReferenceError, ::Selenium::WebDriver::Error::ElementNotInteractableError, ::Selenium::WebDriver::Error::InvalidSelectorError, # Work around chromedriver go_back/go_forward race condition ::Selenium::WebDriver::Error::ElementClickInterceptedError, ::Selenium::WebDriver::Error::NoSuchElementError, # IE ::Selenium::WebDriver::Error::InvalidArgumentError # IE ].tap do |errors| if defined?(::Selenium::WebDriver::Error::DetachedShadowRootError) errors.push(::Selenium::WebDriver::Error::DetachedShadowRootError) end end end
#maximize_window(handle)
[ GitHub ]# File 'lib/capybara/selenium/driver.rb', line 222
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
#modal_error (private)
[ GitHub ]# File 'lib/capybara/selenium/driver.rb', line 391
def modal_error Selenium::WebDriver::Error::NoSuchAlertError end
#native_active_element (private)
[ GitHub ]# File 'lib/capybara/selenium/driver.rb', line 323
def native_active_element browser.switch_to.active_element end
#native_args(args) (private)
[ GitHub ]#no_such_window_error
[ GitHub ]# File 'lib/capybara/selenium/driver.rb', line 313
def no_such_window_error Selenium::WebDriver::Error::NoSuchWindowError end
#open_new_window(kind = :tab)
[ GitHub ]# File 'lib/capybara/selenium/driver.rb', line 247
def open_new_window(kind = :tab) if browser.switch_to.respond_to?(:new_window) handle = current_window_handle browser.switch_to.new_window(kind) switch_to_window(handle) else browser.manage.new_window(kind) end rescue NoMethodError, Selenium::WebDriver::Error::WebDriverError # If not supported by the driver or browser default to using JS browser.execute_script('window.open();') end
#quit
[ GitHub ]# File 'lib/capybara/selenium/driver.rb', line 283
def quit @browser&.quit rescue Selenium::WebDriver::Error::SessionNotCreatedError, Errno::ECONNREFUSED, Selenium::WebDriver::Error::InvalidSessionIdError # Browser must have already gone rescue Selenium::WebDriver::Error::UnknownError => e unless (e. ) # Most likely already gone # probably already gone but not sure - so warn warn "Ignoring Selenium UnknownError during driver quit: #{e.}" end ensure @browser = nil end
#refresh
[ GitHub ]# File 'lib/capybara/selenium/driver.rb', line 98
def refresh browser.navigate.refresh end
#reset!
[ GitHub ]# File 'lib/capybara/selenium/driver.rb', line 155
def reset! # Use instance variable directly so we avoid starting the browser just to reset the session return unless @browser navigated = false timer = Capybara::Helpers.timer(expire_in: 10) begin # Only trigger a navigation if we haven't done it already, otherwise it # can trigger an endless series of unload modals reset_browser_state unless navigated navigated = true # Ensure the page is empty and trigger an UnhandledAlertError for any modals that appear during unload wait_for_empty_page(timer) rescue *unhandled_alert_errors # This error is thrown if an unhandled alert is on the page # Firefox appears to automatically dismiss this alert, chrome does not # We'll try to accept it accept_unhandled_reset_alert # try cleaning up the browser again retry end end
#reset_browser_state (private)
[ GitHub ]# File 'lib/capybara/selenium/driver.rb', line 484
def reset_browser_state clear_browser_state @browser.navigate.to('about:blank') end
#resize_window_to(handle, width, height)
[ GitHub ]# File 'lib/capybara/selenium/driver.rb', line 216
def resize_window_to(handle, width, height) within_given_window(handle) do browser.manage.window.resize_to(width, height) end end
#save_screenshot(path, **options)
[ GitHub ]# File 'lib/capybara/selenium/driver.rb', line 151
def save_screenshot(path, ** ) browser.save_screenshot(path, ** ) end
#send_keys(*args)
[ GitHub ]# File 'lib/capybara/selenium/driver.rb', line 146
def send_keys(*args) # Should this call the specialized nodes rather than native??? native_active_element.send_keys(*args) end
#setup_exit_handler (private)
[ GitHub ]# File 'lib/capybara/selenium/driver.rb', line 474
def setup_exit_handler main = Process.pid at_exit do # Store the exit status of the test run since it goes away after calling the at_exit proc... @exit_status = $ERROR_INFO.status if $ERROR_INFO.is_a?(SystemExit) quit if Process.pid == main exit @exit_status if @exit_status # Force exit with stored status end end
#silenced_unknown_error_message?(msg) ⇒ Boolean
(private)
# File 'lib/capybara/selenium/driver.rb', line 434
def (msg) .any? { |regex| msg.match? regex } end
#silenced_unknown_error_messages (private)
[ GitHub ]# File 'lib/capybara/selenium/driver.rb', line 438
def [/Error communicating with the remote browser/] end
#specialize_driver (private)
[ GitHub ]# File 'lib/capybara/selenium/driver.rb', line 467
def specialize_driver browser_type = browser.browser Capybara::Selenium::Driver.specializations.select { |k, _v| k === browser_type }.each_value do |specialization| # rubocop:disable Style/CaseEquality extend specialization end end
#switch_to_frame(frame)
[ GitHub ]# File 'lib/capybara/selenium/driver.rb', line 190
def switch_to_frame(frame) handles = @frame_handles[current_window_handle] case frame when :top handles.clear browser.switch_to.default_content when :parent handles.pop browser.switch_to.parent_frame else handles << frame browser.switch_to.frame(frame.native) end end
#switch_to_window(handle)
[ GitHub ]# File 'lib/capybara/selenium/driver.rb', line 260
def switch_to_window(handle) browser.switch_to.window handle end
#title
[ GitHub ]# File 'lib/capybara/selenium/driver.rb', line 116
def title browser.title end
#unhandled_alert_errors (private)
[ GitHub ]# File 'lib/capybara/selenium/driver.rb', line 340
def unhandled_alert_errors @unhandled_alert_errors ||= [Selenium::WebDriver::Error::UnexpectedAlertOpenError] end
#unwrap_script_result(arg) (private)
[ GitHub ]# File 'lib/capybara/selenium/driver.rb', line 442
def unwrap_script_result(arg) case arg when Array arg.map { |arr| unwrap_script_result(arr) } when Hash arg.transform_values! { |value| unwrap_script_result(value) } when Selenium::WebDriver::Element, Selenium::WebDriver::ShadowRoot build_node(arg) else arg end end
#visit(path)
[ GitHub ]# File 'lib/capybara/selenium/driver.rb', line 94
def visit(path) browser.navigate.to(path) end
#wait_for_empty_page(timer) (private)
[ GitHub ]# File 'lib/capybara/selenium/driver.rb', line 489
def wait_for_empty_page(timer) until find_xpath('/html/body/*').empty? raise Capybara::ExpectationNotMet, 'Timed out waiting for Selenium session reset' if timer.expired? sleep 0.01 # It has been observed that it is possible that asynchronous JS code in # the application under test can navigate the browser away from about:blank # if the timing is just right. Ensure we are still at about:blank... @browser.navigate.to('about:blank') unless current_url == 'about:blank' end end
#window_handles
[ GitHub ]# File 'lib/capybara/selenium/driver.rb', line 243
def window_handles browser.window_handles end
#window_size(handle)
[ GitHub ]# File 'lib/capybara/selenium/driver.rb', line 209
def window_size(handle) within_given_window(handle) do size = browser.manage.window.size [size.width, size.height] end end
#within_given_window(handle) (private)
[ GitHub ]# File 'lib/capybara/selenium/driver.rb', line 395
def within_given_window(handle) original_handle = current_window_handle if handle == original_handle yield else switch_to_window(handle) result = yield switch_to_window(original_handle) result end end