2013-10-20 17:29:22 +00:00
require " uri "
2011-04-11 05:24:00 +00:00
class Capybara :: Selenium :: Driver < Capybara :: Driver :: Base
2011-02-22 14:53:14 +00:00
DEFAULT_OPTIONS = {
:browser = > :firefox
}
2012-02-01 13:23:17 +00:00
SPECIAL_OPTIONS = [ :browser ]
2011-02-22 14:53:14 +00:00
2012-07-13 11:29:02 +00:00
attr_reader :app , :options
2009-11-07 15:30:16 +00:00
2010-08-30 07:19:44 +00:00
def browser
unless @browser
2011-02-22 14:53:14 +00:00
@browser = Selenium :: WebDriver . for ( options [ :browser ] , options . reject { | key , val | SPECIAL_OPTIONS . include? ( key ) } )
2011-05-13 12:22:50 +00:00
main = Process . pid
2009-11-14 09:44:46 +00:00
at_exit do
2011-08-22 08:15:03 +00:00
# Store the exit status of the test run since it goes away after calling the at_exit proc...
@exit_status = $! . status if $! . is_a? ( SystemExit )
2011-05-13 12:22:50 +00:00
quit if Process . pid == main
2011-08-22 08:15:03 +00:00
exit @exit_status if @exit_status # Force exit with stored status
2009-11-14 09:44:46 +00:00
end
end
2010-08-30 07:19:44 +00:00
@browser
2009-11-07 15:30:16 +00:00
end
2010-09-07 16:35:55 +00:00
def initialize ( app , options = { } )
2013-03-18 22:43:48 +00:00
begin
require 'selenium-webdriver'
rescue LoadError = > e
if e . message =~ / 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. "
else
raise e
end
end
2009-11-07 15:30:16 +00:00
@app = app
2012-01-08 14:01:46 +00:00
@browser = nil
2012-01-08 14:02:24 +00:00
@exit_status = nil
2013-02-22 20:12:09 +00:00
@frame_handles = { }
2011-02-22 14:53:14 +00:00
@options = DEFAULT_OPTIONS . merge ( options )
2009-11-07 15:30:16 +00:00
end
2009-11-14 09:44:46 +00:00
2009-11-07 15:30:16 +00:00
def visit ( path )
2012-07-13 11:29:02 +00:00
browser . navigate . to ( path )
2009-11-07 15:30:16 +00:00
end
2009-11-14 09:44:46 +00:00
2013-10-05 18:23:51 +00:00
def go_back
browser . navigate . back
end
def go_forward
browser . navigate . forward
end
2012-09-10 01:05:17 +00:00
def html
2010-01-01 19:13:54 +00:00
browser . page_source
2009-11-07 15:30:16 +00:00
end
2009-11-14 09:44:46 +00:00
2013-02-06 19:36:55 +00:00
def title
browser . title
end
2013-02-24 17:54:39 +00:00
2009-12-16 14:16:52 +00:00
def current_url
2010-01-01 19:13:54 +00:00
browser . current_url
2009-12-16 14:16:52 +00:00
end
2013-02-19 17:03:26 +00:00
def find_xpath ( selector )
browser . find_elements ( :xpath , selector ) . map { | node | Capybara :: Selenium :: Node . new ( self , node ) }
end
2013-02-24 17:54:39 +00:00
2013-02-19 17:03:26 +00:00
def find_css ( selector )
browser . find_elements ( :css , selector ) . map { | node | Capybara :: Selenium :: Node . new ( self , node ) }
2009-11-07 15:30:16 +00:00
end
2013-02-24 17:54:39 +00:00
2009-12-12 20:46:08 +00:00
def wait? ; true ; end
2012-07-13 11:29:02 +00:00
def needs_server? ; true ; end
2009-11-07 15:30:16 +00:00
2010-06-29 20:41:33 +00:00
def execute_script ( script )
browser . execute_script script
end
2009-12-11 21:41:12 +00:00
def evaluate_script ( script )
2010-01-01 19:13:54 +00:00
browser . execute_script " return #{ script } "
end
2010-01-01 21:46:05 +00:00
2012-07-10 04:50:15 +00:00
def save_screenshot ( path , options = { } )
browser . save_screenshot ( path )
end
2010-07-29 13:25:45 +00:00
def reset!
2010-09-07 16:35:09 +00:00
# Use instance variable directly so we avoid starting the browser just to reset the session
2011-04-07 15:08:32 +00:00
if @browser
2014-06-03 19:18:14 +00:00
begin
begin @browser . manage . delete_all_cookies
rescue Selenium :: WebDriver :: Error :: UnhandledError
# delete_all_cookies fails when we've previously gone
# to about:blank, so we rescue this error and do nothing
# instead.
end
@browser . navigate . to ( " about:blank " )
rescue Selenium :: WebDriver :: Error :: UnhandledAlertError
# 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
begin
@browser . switch_to . alert . accept
rescue Selenium :: WebDriver :: Error :: NoAlertPresentError
# The alert is now gone - nothing to do
end
# try cleaning up the browser again
retry
2011-04-10 17:25:39 +00:00
end
2011-04-07 15:08:32 +00:00
end
2010-01-23 11:58:30 +00:00
end
2013-01-08 08:40:12 +00:00
##
#
2015-02-26 04:16:38 +00:00
# Webdriver supports frame name, id, index(zero-based) or {Capybara::Node::Element} to find iframe
2013-01-08 08:40:12 +00:00
#
# @overload within_frame(index)
# @param [Integer] index index of a frame
# @overload within_frame(name_or_id)
# @param [String] name_or_id name or id of a frame
# @overload within_frame(element)
# @param [Capybara::Node::Base] a_node frame element
#
def within_frame ( frame_handle )
frame_handle = frame_handle . native if frame_handle . is_a? ( Capybara :: Node :: Base )
2014-07-23 16:46:12 +00:00
if ! browser . switch_to . respond_to? ( :parent_frame )
# Selenium Webdriver < 2.43 doesnt support moving back to the parent
@frame_handles [ browser . window_handle ] || = [ ]
@frame_handles [ browser . window_handle ] << frame_handle
end
2015-02-06 15:13:41 +00:00
browser . switch_to . frame ( frame_handle )
2010-04-28 23:03:13 +00:00
yield
2012-04-25 09:43:56 +00:00
ensure
2014-07-23 16:46:12 +00:00
if browser . switch_to . respond_to? ( :parent_frame )
browser . switch_to . parent_frame
else
# There doesnt appear to be any way in Selenium Webdriver < 2.43 to move back to a parent frame
# other than going back to the root and then reiterating down
@frame_handles [ browser . window_handle ] . pop
browser . switch_to . default_content
@frame_handles [ browser . window_handle ] . each { | fh | browser . switch_to . frame ( fh ) }
end
2010-01-12 19:40:10 +00:00
end
2010-04-28 23:03:13 +00:00
2014-04-08 21:28:16 +00:00
def current_window_handle
browser . window_handle
end
2014-04-24 22:38:42 +00:00
def window_size ( handle )
within_given_window ( handle ) do
size = browser . manage . window . size
[ size . width , size . height ]
end
2014-04-08 21:28:16 +00:00
end
2014-04-24 22:38:42 +00:00
def resize_window_to ( handle , width , height )
within_given_window ( handle ) do
browser . manage . window . resize_to ( width , height )
end
2014-04-08 21:28:16 +00:00
end
2014-04-24 22:38:42 +00:00
def maximize_window ( handle )
within_given_window ( handle ) do
browser . manage . window . maximize
end
2014-05-28 19:34:31 +00:00
sleep 0 . 1 # work around for https://code.google.com/p/selenium/issues/detail?id=7405
2014-04-10 07:20:27 +00:00
end
2014-04-24 22:38:42 +00:00
def close_window ( handle )
within_given_window ( handle ) do
browser . close
end
2014-04-08 21:28:16 +00:00
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
2011-02-22 14:53:14 +00:00
original_handle = browser . window_handle
2014-04-08 21:28:16 +00:00
handles . each do | handle |
2014-04-24 22:38:42 +00:00
switch_to_window ( handle )
2014-04-08 21:28:16 +00:00
if ( locator == browser . execute_script ( " return window.name " ) ||
browser . title . include? ( locator ) ||
browser . current_url . include? ( locator ) )
2014-04-24 22:38:42 +00:00
switch_to_window ( original_handle )
2011-02-22 14:53:14 +00:00
return handle
end
end
2014-04-08 21:28:16 +00:00
raise Capybara :: ElementNotFound , " Could not find a window identified by #{ locator } "
2011-02-22 14:53:14 +00:00
end
2014-04-08 21:28:16 +00:00
def within_window ( locator )
handle = find_window ( locator )
browser . switch_to . window ( handle ) { yield }
2010-08-27 19:00:08 +00:00
end
2013-04-01 22:41:55 +00:00
def accept_modal ( type , options = { } , & blk )
2014-07-03 02:10:30 +00:00
yield if block_given?
2014-06-03 19:18:14 +00:00
modal = find_modal ( options )
2014-06-27 19:37:33 +00:00
modal . send_keys options [ :with ] if options [ :with ]
2013-04-01 22:41:55 +00:00
message = modal . text
modal . accept
message
end
2014-06-03 19:18:14 +00:00
def dismiss_modal ( type , options = { } , & blk )
2014-07-03 02:10:30 +00:00
yield if block_given?
2014-06-03 19:18:14 +00:00
modal = find_modal ( options )
2013-04-01 22:41:55 +00:00
message = modal . text
modal . dismiss
message
end
2011-05-13 12:22:50 +00:00
def quit
2013-05-23 11:06:05 +00:00
@browser . quit if @browser
2011-05-13 12:22:50 +00:00
rescue Errno :: ECONNREFUSED
# Browser must have already gone
2013-11-22 23:59:51 +00:00
ensure
@browser = nil
2011-05-13 12:22:50 +00:00
end
2011-07-13 13:39:17 +00:00
def invalid_element_errors
2015-05-21 18:00:36 +00:00
[ Selenium :: WebDriver :: Error :: StaleElementReferenceError ,
Selenium :: WebDriver :: Error :: UnhandledError ,
Selenium :: WebDriver :: Error :: ElementNotVisibleError ,
Selenium :: WebDriver :: Error :: InvalidSelectorError ] # Work around a race condition that can occur with chromedriver and #go_back/#go_forward
2011-07-13 13:39:17 +00:00
end
2013-02-24 17:54:39 +00:00
2014-04-08 21:28:16 +00:00
def no_such_window_error
Selenium :: WebDriver :: Error :: NoSuchWindowError
end
2014-04-24 22:38:42 +00:00
2014-09-19 21:18:50 +00:00
def browser_initialized?
! @browser . nil?
end
2014-04-24 22:38:42 +00:00
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
2013-04-01 22:41:55 +00:00
2014-06-03 19:18:14 +00:00
def find_modal ( options = { } )
# 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 (
2015-04-14 20:56:30 +00:00
timeout : ( options [ :wait ] || Capybara . default_max_wait_time ) ,
2014-06-03 19:18:14 +00:00
ignore : Selenium :: WebDriver :: Error :: NoAlertPresentError )
begin
2015-02-06 15:13:41 +00:00
wait . until do
2014-06-03 19:18:14 +00:00
alert = @browser . switch_to . alert
regexp = options [ :text ] . is_a? ( Regexp ) ? options [ :text ] : Regexp . escape ( options [ :text ] . to_s )
alert . text . match ( regexp ) ? alert : nil
end
rescue Selenium :: WebDriver :: Error :: TimeOutError
raise Capybara :: ModalNotFound . new ( " Unable to find modal dialog #{ " with #{ options [ :text ] } " if options [ :text ] } " )
end
2013-04-01 22:41:55 +00:00
end
2009-11-07 15:30:16 +00:00
end