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


27
28
29
30
31
# File 'lib/capybara/selenium/node.rb', line 27

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

#all_textObject


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

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
# 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)
      action_pause(action, click_options.delay)
      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)

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

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

#disabled?Boolean

Returns:

  • (Boolean)

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

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)

148
149
150
151
152
153
154
155
# File 'lib/capybara/selenium/node.rb', line 148

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


165
166
167
168
169
170
171
172
173
174
175
176
# File 'lib/capybara/selenium/node.rb', line 165

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)

178
179
180
# File 'lib/capybara/selenium/node.rb', line 178

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

#hoverObject


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

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

#multiple?Boolean

Returns:

  • (Boolean)

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

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

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

Returns:

  • (Boolean)

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

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


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

def path
  driver.evaluate_script GET_XPATH_SCRIPT, self
end

#readonly?Boolean

Returns:

  • (Boolean)

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

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

#rectObject


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

def rect
  native.rect
end

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


131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
# File 'lib/capybara/selenium/node.rb', line 131

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).then do |act|
        action_pause(act, click_options.delay)
      end.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)

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

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

#send_keys(*args) ⇒ Object


157
158
159
# File 'lib/capybara/selenium/node.rb', line 157

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


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

def shadow_root
  raise '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


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

def style(styles)
  styles.to_h { |style| [style, native.css_value(style)] }
end

#tag_nameObject


182
183
184
185
186
187
188
189
# File 'lib/capybara/selenium/node.rb', line 182

def tag_name
  @tag_name ||=
    if native.respond_to? :tag_name
      native.tag_name.downcase
    else
      shadow_root? ? 'ShadowRoot' : 'Unknown'
    end
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


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

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)

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

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

#visible_textObject

Raises:

  • (NotImplementedError)

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

def visible_text
  raise NotImplementedError, 'Getting visible text is not currently supported directly on shadow roots' if shadow_root?

  native.text
end