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


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)

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

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

#disabled?Boolean

Returns:

  • (Boolean)

190
191
192
193
194
195
# File 'lib/capybara/selenium/node.rb', line 190

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)

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

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


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

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)

176
177
178
# File 'lib/capybara/selenium/node.rb', line 176

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

#hoverObject


159
160
161
# File 'lib/capybara/selenium/node.rb', line 159

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

#multiple?Boolean

Returns:

  • (Boolean)

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

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

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

Returns:

  • (Boolean)

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

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


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

def path
  driver.evaluate_script GET_XPATH_SCRIPT, self
end

#readonly?Boolean

Returns:

  • (Boolean)

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

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

#rectObject


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

def rect
  native.rect
end

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


129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
# 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)
    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


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)

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

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

#send_keys(*args) ⇒ Object


155
156
157
# File 'lib/capybara/selenium/node.rb', line 155

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


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

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

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

#tag_nameObject


180
181
182
# File 'lib/capybara/selenium/node.rb', line 180

def tag_name
  @tag_name ||= native.tag_name.downcase
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)

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

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