2016-03-07 19:52:19 -05:00
# frozen_string_literal: true
2011-04-11 01:24:00 -04:00
class Capybara :: Selenium :: Node < Capybara :: Driver :: Node
2013-02-17 08:58:41 -05:00
def visible_text
2012-08-01 07:24:43 -04:00
# Selenium doesn't normalize Unicode whitespace.
Capybara :: Helpers . normalize_whitespace ( native . text )
2011-04-11 01:24:00 -04:00
end
2013-02-17 08:58:41 -05:00
def all_text
2017-03-07 19:32:02 -05:00
text = driver . execute_script ( " return arguments[0].textContent " , self )
2013-02-17 08:58:41 -05:00
Capybara :: Helpers . normalize_whitespace ( text )
end
2011-04-11 01:24:00 -04:00
def [] ( name )
2012-10-26 09:14:56 -04:00
native . attribute ( name . to_s )
2011-04-11 01:24:00 -04:00
rescue Selenium :: WebDriver :: Error :: WebDriverError
nil
end
def value
2016-06-11 15:09:23 -04:00
if tag_name == " select " and multiple?
2011-04-28 15:30:01 -04:00
native . find_elements ( :xpath , " .//option " ) . select { | n | n . selected? } . map { | n | n [ :value ] || n . text }
2011-04-11 01:24:00 -04:00
else
2011-04-28 15:30:01 -04:00
native [ :value ]
2011-04-11 01:24:00 -04:00
end
end
2015-04-13 00:25:13 -04:00
##
#
# Set the value of the form element to the given value.
#
# @param [String] value The new value
# @param [Hash{}] options Driver specific options for how to set the value
# @option options [Symbol,Array] :clear (nil) The method used to clear the previous value <br/>
# nil => clear via javascript <br/>
# :none => append the new value to the existing value <br/>
# :backspace => send backspace keystrokes to clear the field <br/>
# Array => an array of keys to send before the value being set, e.g. [[:command, 'a'], :backspace]
def set ( value , options = { } )
2012-10-26 09:14:56 -04:00
tag_name = self . tag_name
type = self [ :type ]
2016-06-11 15:09:23 -04:00
if ( Array === value ) && ! multiple?
2012-09-17 08:48:13 -04:00
raise ArgumentError . new " Value cannot be an Array when 'multiple' attribute is not present. Not a #{ value . class } "
end
2011-04-11 01:24:00 -04:00
if tag_name == 'input' and type == 'radio'
click
elsif tag_name == 'input' and type == 'checkbox'
click if value ^ native . attribute ( 'checked' ) . to_s . eql? ( " true " )
2011-11-15 11:05:18 -05:00
elsif tag_name == 'input' and type == 'file'
2012-09-17 08:48:13 -04:00
path_names = value . to_s . empty? ? [ ] : value
2016-10-18 14:09:26 -04:00
if driver . options [ :browser ] . to_s == " chrome "
native . send_keys ( Array ( path_names ) . join ( " \n " ) )
else
native . send_keys ( * path_names )
end
2011-04-11 01:24:00 -04:00
elsif tag_name == 'textarea' or tag_name == 'input'
2016-06-10 19:50:55 -04:00
if readonly?
2014-06-11 20:14:31 -04:00
warn " Attempt to set readonly element with value: #{ value } \n *This will raise an exception in a future version of Capybara "
elsif value . to_s . empty?
2013-11-10 08:10:56 -05:00
native . clear
else
2015-04-13 00:25:13 -04:00
if options [ :clear ] == :backspace
2015-04-07 20:26:02 -04:00
# Clear field by sending the correct number of backspace keys.
backspaces = [ :backspace ] * self . value . to_s . length
native . send_keys ( * ( backspaces + [ value . to_s ] ) )
2015-04-13 00:25:13 -04:00
elsif options [ :clear ] == :none
native . send_keys ( value . to_s )
elsif options [ :clear ] . is_a? Array
native . send_keys ( * options [ :clear ] , value . to_s )
2015-04-07 20:26:02 -04:00
else
# Clear field by JavaScript assignment of the value property.
# Script can change a readonly element which user input cannot, so
# don't execute if readonly.
2017-03-07 19:32:02 -05:00
driver . execute_script " arguments[0].value = '' " , self
2015-04-07 20:26:02 -04:00
native . send_keys ( value . to_s )
end
2013-11-10 08:10:56 -05:00
end
2012-12-14 19:06:50 -05:00
elsif native . attribute ( 'isContentEditable' )
#ensure we are focused on the element
2017-03-07 18:53:25 -05:00
native . click
2012-12-14 19:06:50 -05:00
script = <<-JS
var range = document . createRange ( ) ;
2015-10-08 14:07:00 -04:00
arguments [ 0 ] . focus ( ) ;
2012-12-14 19:06:50 -05:00
range . selectNodeContents ( arguments [ 0 ] ) ;
window . getSelection ( ) . addRange ( range ) ;
JS
2017-03-07 19:32:02 -05:00
driver . execute_script script , self
2017-03-07 18:53:25 -05:00
if ( driver . options [ :browser ] . to_s == " chrome " ) ||
( driver . options [ :browser ] . to_s == " firefox " && ! driver . marionette? )
# chromedriver raises a can't focus element if we use native.send_keys
# we've already focused it so just use action api
driver . browser . action . send_keys ( value . to_s ) . perform
else
# action api is really slow here just use native.send_keys
native . send_keys ( value . to_s )
end
2011-04-11 01:24:00 -04:00
end
end
def select_option
2016-06-10 20:22:17 -04:00
native . click unless selected? || disabled?
2011-04-11 01:24:00 -04:00
end
def unselect_option
if select_node [ 'multiple' ] != 'multiple' and select_node [ 'multiple' ] != 'true'
raise Capybara :: UnselectNotAllowed , " Cannot unselect option from single select box. "
end
2012-02-01 08:23:17 -05:00
native . click if selected?
2011-04-11 01:24:00 -04:00
end
def click
2012-02-01 08:23:17 -05:00
native . click
2011-04-11 01:24:00 -04:00
end
2015-04-13 12:24:13 -04:00
2013-05-10 12:55:17 -04:00
def right_click
driver . browser . action . context_click ( native ) . perform
end
2015-04-13 12:24:13 -04:00
2013-05-10 12:55:17 -04:00
def double_click
driver . browser . action . double_click ( native ) . perform
end
2015-04-13 12:24:13 -04:00
2015-01-23 15:23:57 -05:00
def send_keys ( * args )
native . send_keys ( * args )
end
2011-04-11 01:24:00 -04:00
2013-02-25 13:37:25 -05:00
def hover
driver . browser . action . move_to ( native ) . perform
end
2012-12-14 19:06:50 -05:00
2011-04-11 01:24:00 -04:00
def drag_to ( element )
2012-02-01 08:23:17 -05:00
driver . browser . action . drag_and_drop ( native , element . native ) . perform
2011-04-11 01:24:00 -04:00
end
def tag_name
2012-10-26 09:14:56 -04:00
native . tag_name . downcase
2011-04-11 01:24:00 -04:00
end
def visible?
displayed = native . displayed?
displayed and displayed != " false "
end
def selected?
selected = native . selected?
selected and selected != " false "
end
2016-06-11 15:09:23 -04:00
alias :checked? :selected?
2011-04-11 01:24:00 -04:00
2013-01-29 05:45:24 -05:00
def disabled?
2017-01-02 17:08:35 -05:00
# workaround for selenium-webdriver/geckodriver reporting elements as enabled when they are nested in disabling elements
2017-01-02 20:17:35 -05:00
if driver . marionette?
2017-01-02 17:08:35 -05:00
if %w( option optgroup ) . include? tag_name
2017-01-03 16:45:49 -05:00
! native . enabled? || find_xpath ( " parent::*[self::optgroup or self::select] " ) [ 0 ] . disabled?
2017-01-02 17:08:35 -05:00
else
2017-01-03 16:45:49 -05:00
! native . enabled? || ! find_xpath ( " parent::fieldset[@disabled] | ancestor::*[not(self::legend) or preceding-sibling::legend][parent::fieldset[@disabled]] " ) . empty?
2017-01-02 17:08:35 -05:00
end
else
! native . enabled?
end
2013-01-29 05:45:24 -05:00
end
2016-06-11 15:09:23 -04:00
def readonly?
readonly = self [ :readonly ]
readonly and readonly != " false "
end
def multiple?
multiple = self [ :multiple ]
multiple and multiple != " false "
end
2011-04-11 01:24:00 -04:00
2013-02-19 12:03:26 -05:00
def find_xpath ( locator )
2011-04-11 01:24:00 -04:00
native . find_elements ( :xpath , locator ) . map { | n | self . class . new ( driver , n ) }
end
2012-12-14 19:06:50 -05:00
2013-02-19 12:03:26 -05:00
def find_css ( locator )
native . find_elements ( :css , locator ) . map { | n | self . class . new ( driver , n ) }
end
2012-12-14 19:06:50 -05:00
2012-11-19 21:57:09 -05:00
def == ( other )
native == other . native
end
2015-08-09 12:55:56 -04:00
def path
2015-08-10 09:49:15 -04:00
path = find_xpath ( 'ancestor::*' ) . reverse
2015-08-09 12:55:56 -04:00
path . unshift self
result = [ ]
while node = path . shift
parent = path . first
if parent
siblings = parent . find_xpath ( node . tag_name )
if siblings . size == 1
result . unshift node . tag_name
else
index = siblings . index ( node )
result . unshift " #{ node . tag_name } [ #{ index + 1 } ] "
end
else
result . unshift node . tag_name
end
end
'/' + result . join ( '/' )
end
2011-04-11 01:24:00 -04:00
private
# a reference to the select node if this is an option node
def select_node
2013-02-19 12:03:26 -05:00
find_xpath ( './ancestor::select' ) . first
2011-04-11 01:24:00 -04:00
end
end