Class: Capybara::Dommy::Node
- Inherits:
-
Capybara::Driver::Node
- Object
- Capybara::Driver::Node
- Capybara::Dommy::Node
- Defined in:
- lib/capybara/dommy/node.rb
Overview
Wraps a Dommy::Element as a Capybara driver node. Modeled on Capybara::RackTest::Node: field setting mutates the Dommy::Element in the live document, while link clicks and form submission delegate to the dommy-rack session (which re-reads the same document at submit time).
Constant Summary collapse
- OPTION_OWNER_XPATH =
"./parent::*[self::optgroup | self::select | self::datalist]"- DISABLED_BY_FIELDSET_XPATH =
"./parent::fieldset[./@disabled] | " \ "./ancestor::*[(not(./self::legend) or ./preceding-sibling::legend)][./parent::fieldset[./@disabled]]"
- DISABLEABLE_ELEMENTS =
disabledonly applies to form-associated elements; on anything else (e.g. a link that incorrectly carries the attribute) it has no effect. %w[button fieldset input optgroup option select textarea].freeze
- NON_READONLY_TYPES =
readonly does not apply to these input types, so they are never readonly even if the attribute is present (matches RackTest).
%w[hidden range color checkbox radio file submit image reset button].freeze
Instance Method Summary collapse
-
#==(other) ⇒ Object
Identity matters to Capybara (e.g. the focused: filter compares a candidate against session.active_element).
- #[](name) ⇒ Object
- #all_text ⇒ Object
- #checked? ⇒ Boolean
-
#click(_keys = [], **_options) ⇒ Object
--- Interaction ---.
- #disabled? ⇒ Boolean
-
#find_css(locator, **_options) ⇒ Object
--- Scoped queries (for
within) ---. - #find_xpath(locator, **_options) ⇒ Object
-
#hover ⇒ Object
Move the (virtual) pointer over this element: :hover rules and
matches(":hover")then apply to it and its ancestors. - #path ⇒ Object
- #readonly? ⇒ Boolean
- #select_option ⇒ Object
- #selected? ⇒ Boolean
-
#send_keys(*args) ⇒ Object
Keyboard input without a JS engine: maintains a caret over the field's value and applies printable keys plus the position/modifier keys Capybara's non-JS send_keys specs use.
- #set(value, **_options) ⇒ Object
-
#style(styles) ⇒ Object
Computed styles for Capybara's matches_style? / style: filters, served by Dommy's CSS cascade (values come back in the computed serialization, e.g. colors as rgb()).
- #tag_name ⇒ Object
- #unselect_option ⇒ Object
- #value ⇒ Object
- #visible? ⇒ Boolean
- #visible_text ⇒ Object
Instance Method Details
#==(other) ⇒ Object
Identity matters to Capybara (e.g. the focused: filter compares a candidate against session.active_element). Dommy's wrapper cache hands out one wrapper per DOM node, so native equality is node identity.
230 231 232 |
# File 'lib/capybara/dommy/node.rb', line 230 def ==(other) other.is_a?(Node) && native == other.native end |
#[](name) ⇒ Object
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
# File 'lib/capybara/dommy/node.rb', line 19 def [](name) key = name.to_s # Capybara's validation_message filter reads the constraint-validation # DOM property, which never appears as a markup attribute. if key == "validationMessage" && native.respond_to?(:validation_message) return native. end # `checked` / `selected` reflect the live IDL property (current state), # not the content attribute (which is the *default*) — as WebDriver's # getAttribute does for these boolean attributes. if key == "checked" && native.respond_to?(:checked) return native.checked ? "true" : nil end if key == "selected" && native.respond_to?(:selected) return native.selected ? "true" : nil end native.get_attribute(key) end |
#all_text ⇒ Object
50 51 52 |
# File 'lib/capybara/dommy/node.rb', line 50 def all_text text_extractor.all_text(native) end |
#checked? ⇒ Boolean
62 63 64 |
# File 'lib/capybara/dommy/node.rb', line 62 def checked? native.respond_to?(:checked) && native.checked end |
#click(_keys = [], **_options) ⇒ Object
--- Interaction ---
144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 |
# File 'lib/capybara/dommy/node.rb', line 144 def click(_keys = [], **) return js_click if driver.javascript? if link? click_link_node elsif submits? submit_owning_form elsif checkable? set(!checked?) elsif tag_name == "label" click_label elsif (details = native.closest("details")) toggle_details(details) end end |
#disabled? ⇒ Boolean
74 75 76 77 78 79 80 81 82 83 84 |
# File 'lib/capybara/dommy/node.rb', line 74 def disabled? return false unless DISABLEABLE_ELEMENTS.include?(tag_name) return true if native.has_attribute?("disabled") if %w[option optgroup].include?(tag_name) owner = native.xpath(OPTION_OWNER_XPATH).first owner ? self.class.new(driver, owner).disabled? : false else !native.xpath(DISABLED_BY_FIELDSET_XPATH).empty? end end |
#find_css(locator, **_options) ⇒ Object
--- Scoped queries (for within) ---
204 205 206 |
# File 'lib/capybara/dommy/node.rb', line 204 def find_css(locator, **) native.query_selector_all(locator).map { |element| self.class.new(driver, element) } end |
#find_xpath(locator, **_options) ⇒ Object
208 209 210 |
# File 'lib/capybara/dommy/node.rb', line 208 def find_xpath(locator, **) native.xpath(locator).map { |element| self.class.new(driver, element) } end |
#hover ⇒ Object
Move the (virtual) pointer over this element: :hover rules and
matches(":hover") then apply to it and its ancestors. No
mouseover/mouseout events are dispatched (nothing observes them
without JavaScript).
197 198 199 200 |
# File 'lib/capybara/dommy/node.rb', line 197 def hover native.owner_document.__internal_set_hovered_element__(native) nil end |
#path ⇒ Object
96 97 98 99 100 101 102 103 |
# File 'lib/capybara/dommy/node.rb', line 96 def path # Capybara's documented placeholder: a shadow tree has no XPath. if native.respond_to?(:get_root_node) && native.get_root_node.is_a?(::Dommy::ShadowRoot) return "(: Shadow DOM element - no XPath :)" end native.path end |
#readonly? ⇒ Boolean
90 91 92 93 94 |
# File 'lib/capybara/dommy/node.rb', line 90 def readonly? return false if input_field? && NON_READONLY_TYPES.include?(field_type) native.has_attribute?("readonly") end |
#select_option ⇒ Object
176 177 178 179 180 181 182 183 |
# File 'lib/capybara/dommy/node.rb', line 176 def select_option return if disabled? select_el = select_node deselect_all(select_el) unless select_el&.multiple native.selected = true notify_select_changed(select_el) end |
#selected? ⇒ Boolean
66 67 68 |
# File 'lib/capybara/dommy/node.rb', line 66 def selected? native.respond_to?(:selected) && native.selected end |
#send_keys(*args) ⇒ Object
Keyboard input without a JS engine: maintains a caret over the field's value and applies printable keys plus the position/modifier keys Capybara's non-JS send_keys specs use. Key events are not dispatched (nothing here can observe them without JavaScript).
109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 |
# File 'lib/capybara/dommy/node.rb', line 109 def send_keys(*args) # Under JavaScript, dispatch real keyboard events (keydown/keypress/ # input/keyup with browser default actions) through the dommy driver, # so keyboard handlers (arrow navigation, Enter selection) run. # Key chords ([:shift, "o"]) are not supported on this path. return driver.rack_session.send_keys_to(native, *args) if driver.javascript? return unless native.respond_to?(:value=) state = {chars: native.value.to_s.chars, caret: native.value.to_s.length, shift: false} args.each do |arg| if arg.is_a?(Array) # A chord like [:shift, 'o'] holds its modifiers only for the # duration of the array. held = state[:shift] arg.each { |key| apply_key(state, key) } state[:shift] = held else apply_key(state, arg) end end native.focus if native.respond_to?(:focus) native.value = state[:chars].join end |
#set(value, **_options) ⇒ Object
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 |
# File 'lib/capybara/dommy/node.rb', line 160 def set(value, **) return if disabled? || readonly? if radio? set_radio elsif checkbox? set_checkbox(value) elsif range? set_range(value) elsif file? set_file(value) elsif input_field? || textarea? set_text_value(value) end end |
#style(styles) ⇒ Object
Computed styles for Capybara's matches_style? / style: filters, served by Dommy's CSS cascade (values come back in the computed serialization, e.g. colors as rgb()).
137 138 139 140 |
# File 'lib/capybara/dommy/node.rb', line 137 def style(styles) computed = ::Dommy::Internal::CSS::Cascade.computed_style(native) Array(styles).flatten.to_h { |name| [name.to_s, computed[name.to_s].to_s] } end |
#tag_name ⇒ Object
15 16 17 |
# File 'lib/capybara/dommy/node.rb', line 15 def tag_name native.tag_name.downcase end |
#unselect_option ⇒ Object
185 186 187 188 189 190 191 |
# File 'lib/capybara/dommy/node.rb', line 185 def unselect_option unless select_node&.multiple raise Capybara::UnselectNotAllowed, "Cannot unselect option from a non-multiple select box" end native.selected = false end |
#value ⇒ Object
40 41 42 43 44 45 46 47 48 |
# File 'lib/capybara/dommy/node.rb', line 40 def value if select? native.multiple ? native..map(&:value) : native.value elsif checkable? native.has_attribute?("value") ? native.get_attribute("value") : "on" elsif native.respond_to?(:value) native.value end end |
#visible? ⇒ Boolean
58 59 60 |
# File 'lib/capybara/dommy/node.rb', line 58 def visible? driver.visible?(native) end |
#visible_text ⇒ Object
54 55 56 |
# File 'lib/capybara/dommy/node.rb', line 54 def visible_text text_extractor.visible_text(native) end |