Class: Capybara::Selenium::Node

Inherits:
Driver::Node show all
Includes:
Node::WhitespaceNormalizer, 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

Constant Summary

Constants included from Node::WhitespaceNormalizer

Node::WhitespaceNormalizer::BREAKING_SPACES, Node::WhitespaceNormalizer::EMPTY_LINES, Node::WhitespaceNormalizer::LEADING_SPACES, Node::WhitespaceNormalizer::LEFT_TO_RIGHT_MARK, Node::WhitespaceNormalizer::LINE_SEPERATOR, Node::WhitespaceNormalizer::NON_BREAKING_SPACE, Node::WhitespaceNormalizer::PARAGRAPH_SEPERATOR, Node::WhitespaceNormalizer::REMOVED_CHARACTERS, Node::WhitespaceNormalizer::RIGHT_TO_LEFT_MARK, Node::WhitespaceNormalizer::SQUEEZED_SPACES, Node::WhitespaceNormalizer::TRAILING_SPACES, Node::WhitespaceNormalizer::ZERO_WIDTH_SPACE

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 included from Node::WhitespaceNormalizer

#normalize_spacing, #normalize_visible_spacing

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



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

def all_text
  text = driver.evaluate_script('arguments[0].textContent', self) || ''
  normalize_spacing(text)
end

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



106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
# File 'lib/capybara/selenium/node.rb', line 106

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)


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

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

#disabled?Boolean

Returns:

  • (Boolean)


193
194
195
196
197
198
# File 'lib/capybara/selenium/node.rb', line 193

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)


144
145
146
147
148
149
150
151
# File 'lib/capybara/selenium/node.rb', line 144

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



161
162
163
164
165
166
167
168
169
170
171
172
# File 'lib/capybara/selenium/node.rb', line 161

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)


174
175
176
# File 'lib/capybara/selenium/node.rb', line 174

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

#hoverObject



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

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

#multiple?Boolean

Returns:

  • (Boolean)


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

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

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

Returns:

  • (Boolean)


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

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



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

def path
  driver.evaluate_script GET_XPATH_SCRIPT, self
end

#readonly?Boolean

Returns:

  • (Boolean)


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

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

#rectObject



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

def rect
  native.rect
end

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



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

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)
    else
      action.move_to(target) if target
      action.pointer_down(:right).then do |act|
        action_pause(act, click_options.delay)
      end.pointer_up(:right)
    end
  end
end

#select_optionObject



96
97
98
# File 'lib/capybara/selenium/node.rb', line 96

def select_option
  click unless selected? || disabled?
end

#selected?Boolean Also known as: checked?

Returns:

  • (Boolean)


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

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

#send_keys(*args) ⇒ Object



153
154
155
# File 'lib/capybara/selenium/node.rb', line 153

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



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

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



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

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

#style(styles) ⇒ Object



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

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

#tag_nameObject



178
179
180
181
182
183
184
185
# File 'lib/capybara/selenium/node.rb', line 178

def tag_name
  @tag_name ||=
    if native.respond_to? :tag_name
      native.tag_name.downcase
    else
      shadow_root? ? 'ShadowRoot' : 'Unknown'
    end
end

#unselect_optionObject



100
101
102
103
104
# File 'lib/capybara/selenium/node.rb', line 100

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)


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

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

#visible_textObject

Raises:

  • (NotImplementedError)


14
15
16
17
18
# File 'lib/capybara/selenium/node.rb', line 14

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

  native.text
end