Class: Capybara::Selenium::Node

Inherits:
Driver::Node show all
Includes:
Find, Scroll
Defined in:
lib/capybara/selenium/node.rb,
lib/capybara/selenium/extensions/html5_drag.rb,
lib/capybara/selenium/extensions/modifier_keys_stack.rb,
lib/capybara/selenium/extensions/file_input_click_emulation.rb

Direct Known Subclasses

ChromeNode, EdgeNode, FirefoxNode, IENode, SafariNode

Defined Under Namespace

Modules: FileInputClickEmulation, Html5Drag Classes: ModifierKeysStack

Instance Attribute Summary

Attributes inherited from Driver::Node

#driver, #initial_cache, #native

Instance Method Summary collapse

Methods included from Scroll

#scroll_by, #scroll_to

Methods included from Find

#find_css, #find_xpath

Methods inherited from Driver::Node

#==, #initialize, #inspect, #scroll_by, #scroll_to, #trigger

Constructor Details

This class inherits a constructor from Capybara::Driver::Node

Instance Method Details

#[](name) ⇒ Object


25
26
27
28
29
# File 'lib/capybara/selenium/node.rb', line 25

def [](name)
  native.attribute(name.to_s)
rescue Selenium::WebDriver::Error::WebDriverError
  nil
end

#all_textObject


16
17
18
19
20
21
22
23
# File 'lib/capybara/selenium/node.rb', line 16

def all_text
  text = driver.evaluate_script('arguments[0].textContent', self) || ''
  text.gsub(/[\u200b\u200e\u200f]/, '')
      .gsub(/[\ \n\f\t\v\u2028\u2029]+/, ' ')
      .gsub(/\A[[:space:]&&[^\u00a0]]+/, '')
      .gsub(/[[:space:]&&[^\u00a0]]+\z/, '')
      .tr("\u00a0", ' ')
end

#click(keys = [], **options) ⇒ Object


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
# File 'lib/capybara/selenium/node.rb', line 108

def click(keys = [], **options)
  click_options = ClickOptions.new(keys, options)
  return native.click if click_options.empty?

  perform_with_options(click_options) do |action|
    target = click_options.coords? ? nil : native
    if click_options.delay.zero?
      action.click(target)
    else
      action.click_and_hold(target)
      if w3c?
        action.pause(action.pointer_inputs.first, click_options.delay)
      else
        action.pause(click_options.delay)
      end
      action.release
    end
  end
rescue StandardError => e
  if e.is_a?(::Selenium::WebDriver::Error::ElementClickInterceptedError) ||
     e.message.include?('Other element would receive the click')
    scroll_to_center
  end

  raise e
end

#content_editable?Boolean

Returns:

  • (Boolean)

203
204
205
# File 'lib/capybara/selenium/node.rb', line 203

def content_editable?
  native.attribute('isContentEditable') == 'true'
end

#disabled?Boolean

Returns:

  • (Boolean)

196
197
198
199
200
201
# File 'lib/capybara/selenium/node.rb', line 196

def disabled?
  return true unless native.enabled?

  # WebDriver only defines `disabled?` for form controls but fieldset makes sense too
  find_xpath('self::fieldset/ancestor-or-self::fieldset[@disabled]').any?
end

#double_click(keys = [], **options) ⇒ Object

Raises:

  • (ArgumentError)

152
153
154
155
156
157
158
159
# File 'lib/capybara/selenium/node.rb', line 152

def double_click(keys = [], **options)
  click_options = ClickOptions.new(keys, options)
  raise ArgumentError, "double_click doesn't support a delay option" unless click_options.delay.zero?

  perform_with_options(click_options) do |action|
    click_options.coords? ? action.double_click : action.double_click(native)
  end
end

#drag_to(element, drop_modifiers: []) ⇒ Object


169
170
171
172
173
174
175
176
177
178
179
180
# File 'lib/capybara/selenium/node.rb', line 169

def drag_to(element, drop_modifiers: [], **)
  drop_modifiers = Array(drop_modifiers)
  # Due to W3C spec compliance - The Actions API no longer scrolls to elements when necessary
  # which means Seleniums `drag_and_drop` is now broken - do it manually
  scroll_if_needed { browser_action.click_and_hold(native).perform }
  # element.scroll_if_needed { browser_action.move_to(element.native).release.perform }
  element.scroll_if_needed do
    keys_down = modifiers_down(browser_action, drop_modifiers)
    keys_up = modifiers_up(keys_down.move_to(element.native).release, drop_modifiers)
    keys_up.perform
  end
end

#drop(*_) ⇒ Object

Raises:

  • (NotImplementedError)

182
183
184
# File 'lib/capybara/selenium/node.rb', line 182

def drop(*_)
  raise NotImplementedError, 'Out of browser drop emulation is not implemented for the current browser'
end

#hoverObject


165
166
167
# File 'lib/capybara/selenium/node.rb', line 165

def hover
  scroll_if_needed { browser_action.move_to(native).perform }
end

#multiple?Boolean

Returns:

  • (Boolean)

192
# File 'lib/capybara/selenium/node.rb', line 192

def multiple?; boolean_attr(self[:multiple]); end

#obscured?(x: nil, y: nil) ⇒ Boolean

Returns:

  • (Boolean)

211
212
213
214
215
216
# File 'lib/capybara/selenium/node.rb', line 211

def obscured?(x: nil, y: nil)
  res = driver.evaluate_script(OBSCURED_OR_OFFSET_SCRIPT, self, x, y)
  return true if res == true

  driver.frame_obscured_at?(x: res['x'], y: res['y'])
end

#pathObject


207
208
209
# File 'lib/capybara/selenium/node.rb', line 207

def path
  driver.evaluate_script GET_XPATH_SCRIPT, self
end

#readonly?Boolean

Returns:

  • (Boolean)

191
# File 'lib/capybara/selenium/node.rb', line 191

def readonly?; boolean_attr(self[:readonly]); end

#rectObject


218
219
220
# File 'lib/capybara/selenium/node.rb', line 218

def rect
  native.rect
end

#right_click(keys = [], **options) ⇒ Object


135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
# File 'lib/capybara/selenium/node.rb', line 135

def right_click(keys = [], **options)
  click_options = ClickOptions.new(keys, options)
  perform_with_options(click_options) do |action|
    target = click_options.coords? ? nil : native
    if click_options.delay.zero?
      action.context_click(target)
    elsif w3c?
      action.move_to(target) if target
      action.pointer_down(:right)
            .pause(action.pointer_inputs.first, click_options.delay)
            .pointer_up(:right)
    else
      raise ArgumentError, 'Delay is not supported when right clicking with legacy (non-w3c) selenium driver'
    end
  end
end

#select_optionObject


98
99
100
# File 'lib/capybara/selenium/node.rb', line 98

def select_option
  click unless selected? || disabled?
end

#selected?Boolean Also known as: checked?

Returns:

  • (Boolean)

193
# File 'lib/capybara/selenium/node.rb', line 193

def selected?; boolean_attr(native.selected?); end

#send_keys(*args) ⇒ Object


161
162
163
# File 'lib/capybara/selenium/node.rb', line 161

def send_keys(*args)
  native.send_keys(*args)
end

#set(value, **options) ⇒ Object

Set the value of the form element to the given value.

Parameters:

  • value (String)

    The new value

  • options (Hash{})

    Driver specific options for how to set the value

Options Hash (**options):

  • :clear (Symbol, Array) — default: nil

    The method used to clear the previous value
    nil => clear via javascript
    :none => append the new value to the existing value
    :backspace => send backspace keystrokes to clear the field
    Array => an array of keys to send before the value being set, e.g. [[:command, 'a'], :backspace]

  • :rapid (Boolean) — default: nil

    Whether setting text inputs should use a faster "rapid" mode
    nil => Text inputs with length greater than 30 characters will be set using a faster driver script mode
    true => Rapid mode will be used regardless of input length
    false => Sends keys via conventional mode. This may be required to avoid losing key-presses if you have certain Javascript interactions on form inputs


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
# File 'lib/capybara/selenium/node.rb', line 61

def set(value, **options)
  if value.is_a?(Array) && !multiple?
    raise ArgumentError, "Value cannot be an Array when 'multiple' attribute is not present. Not a #{value.class}"
  end

  tag_name, type = attrs(:tagName, :type).map { |val| val&.downcase }
  @tag_name ||= tag_name

  case tag_name
  when 'input'
    case type
    when 'radio'
      click
    when 'checkbox'
      click if value ^ checked?
    when 'file'
      set_file(value)
    when 'date'
      set_date(value)
    when 'time'
      set_time(value)
    when 'datetime-local'
      set_datetime_local(value)
    when 'color'
      set_color(value)
    when 'range'
      set_range(value)
    else
      set_text(value, **options)
    end
  when 'textarea'
    set_text(value, **options)
  else
    set_content_editable(value)
  end
end

#shadow_rootObject


222
223
224
225
226
227
# File 'lib/capybara/selenium/node.rb', line 222

def shadow_root
  raise_error 'You must be using Selenium 4.1+ for shadow_root support' unless native.respond_to? :shadow_root

  root = native.shadow_root
  root && build_node(native.shadow_root)
end

#style(styles) ⇒ Object


39
40
41
42
43
# File 'lib/capybara/selenium/node.rb', line 39

def style(styles)
  styles.each_with_object({}) do |style, result|
    result[style] = native.css_value(style)
  end
end

#tag_nameObject


186
187
188
# File 'lib/capybara/selenium/node.rb', line 186

def tag_name
  @tag_name ||= native.tag_name.downcase
end

#unselect_optionObject


102
103
104
105
106
# File 'lib/capybara/selenium/node.rb', line 102

def unselect_option
  raise Capybara::UnselectNotAllowed, 'Cannot unselect option from single select box.' unless select_node.multiple?

  click if selected?
end

#valueObject


31
32
33
34
35
36
37
# File 'lib/capybara/selenium/node.rb', line 31

def value
  if tag_name == 'select' && multiple?
    native.find_elements(:css, 'option:checked').map { |el| el[:value] || el.text }
  else
    native[:value]
  end
end

#visible?Boolean

Returns:

  • (Boolean)

190
# File 'lib/capybara/selenium/node.rb', line 190

def visible?; boolean_attr(native.displayed?); end

#visible_textObject


12
13
14
# File 'lib/capybara/selenium/node.rb', line 12

def visible_text
  native.text
end