class Capybara::Playwright::Node
Selector and checking methods are derived from twapole/apparition Action methods (click, select_option
, …) uses playwright.
ref:
selenium: https://github.com/teamcapybara/capybara/blob/master/lib/capybara/selenium/node.rb apparition: https://github.com/twalpole/apparition/blob/master/lib/capybara/apparition/node.rb
Constants
- SCROLL_POSITIONS
Public Class Methods
new(driver, page, element)
click to toggle source
Calls superclass method
# File lib/capybara/playwright/node.rb, line 67 def initialize(driver, page, element) super(driver, element) @page = page @element = element end
Public Instance Methods
==(other)
click to toggle source
# File lib/capybara/playwright/node.rb, line 898 def ==(other) return false unless other.is_a?(Node) @element.evaluate('(self, other) => self == other', arg: other.element) end
[](name)
click to toggle source
# File lib/capybara/playwright/node.rb, line 137 def [](name) assert_element_not_stale property(name) || attribute(name) end
all_text()
click to toggle source
# File lib/capybara/playwright/node.rb, line 104 def all_text assert_element_not_stale text = @element.text_content text.to_s.gsub(/[\u200b\u200e\u200f]/, '') .gsub(/[\ \n\f\t\v\u2028\u2029]+/, ' ') .gsub(/\A[[:space:]&&[^\u00a0]]+/, '') .gsub(/[[:space:]&&[^\u00a0]]+\z/, '') .tr("\u00a0", ' ') end
checked?()
click to toggle source
# File lib/capybara/playwright/node.rb, line 812 def checked? assert_element_not_stale @element.evaluate('el => !!el.checked') end
click(keys = [], **options)
click to toggle source
# File lib/capybara/playwright/node.rb, line 343 def click(keys = [], **options) click_options = ClickOptions.new(@element, keys, options, capybara_default_wait_time) @element.click(**click_options.as_params) end
disabled?()
click to toggle source
# File lib/capybara/playwright/node.rb, line 824 def disabled? @element.evaluate(<<~JAVASCRIPT) function(el) { const xpath = 'parent::optgroup[@disabled] | \ ancestor::select[@disabled] | \ parent::fieldset[@disabled] | \ ancestor::*[not(self::legend) or preceding-sibling::legend][parent::fieldset[@disabled]]'; return el.disabled || document.evaluate(xpath, el, null, XPathResult.BOOLEAN_TYPE, null).booleanValue } JAVASCRIPT end
double_click(keys = [], **options)
click to toggle source
# File lib/capybara/playwright/node.rb, line 355 def double_click(keys = [], **options) click_options = ClickOptions.new(@element, keys, options, capybara_default_wait_time) @element.dblclick(**click_options.as_params) end
drag_to(element, **options)
click to toggle source
# File lib/capybara/playwright/node.rb, line 613 def drag_to(element, **options) DragTo.new(@page, @element, element.element, options).execute end
drop(*args)
click to toggle source
# File lib/capybara/playwright/node.rb, line 687 def drop(*args) raise NotImplementedError end
find_css(query, **options)
click to toggle source
# File lib/capybara/playwright/node.rb, line 912 def find_css(query, **options) assert_element_not_stale @element.query_selector_all(query).map do |el| Node.new(@driver, @page, el) end end
find_xpath(query, **options)
click to toggle source
# File lib/capybara/playwright/node.rb, line 904 def find_xpath(query, **options) assert_element_not_stale @element.query_selector_all("xpath=#{query}").map do |el| Node.new(@driver, @page, el) end end
hover()
click to toggle source
# File lib/capybara/playwright/node.rb, line 609 def hover @element.hover(timeout: capybara_default_wait_time) end
inspect()
click to toggle source
# File lib/capybara/playwright/node.rb, line 894 def inspect %(#<#{self.class} tag="#{tag_name}" path="#{path}">) end
multiple?()
click to toggle source
# File lib/capybara/playwright/node.rb, line 840 def multiple? @element.evaluate('el => el.multiple') end
obscured?()
click to toggle source
# File lib/capybara/playwright/node.rb, line 808 def obscured? @element.capybara_obscured? end
path()
click to toggle source
# File lib/capybara/playwright/node.rb, line 856 def path assert_element_not_stale @element.evaluate(<<~JAVASCRIPT) (el) => { var xml = document; var xpath = ''; var pos, tempitem2; if (el.getRootNode && el.getRootNode() instanceof ShadowRoot) { return "(: Shadow DOM element - no XPath :)"; }; while(el !== xml.documentElement) { pos = 0; tempitem2 = el; while(tempitem2) { if (tempitem2.nodeType === 1 && tempitem2.nodeName === el.nodeName) { // If it is ELEMENT_NODE of the same name pos += 1; } tempitem2 = tempitem2.previousSibling; } if (el.namespaceURI != xml.documentElement.namespaceURI) { xpath = "*[local-name()='"+el.nodeName+"' and namespace-uri()='"+(el.namespaceURI===null?'':el.namespaceURI)+"']["+pos+']'+'/'+xpath; } else { xpath = el.nodeName.toUpperCase()+"["+pos+"]/"+xpath; } el = el.parentNode; } xpath = '/'+xml.documentElement.nodeName.toUpperCase()+'/'+xpath; xpath = xpath.replace(/\\/$/, ''); return xpath; } JAVASCRIPT end
readonly?()
click to toggle source
# File lib/capybara/playwright/node.rb, line 836 def readonly? !@element.editable? end
rect()
click to toggle source
# File lib/capybara/playwright/node.rb, line 844 def rect assert_element_not_stale @element.evaluate(<<~JAVASCRIPT) function(el){ const rects = [...el.getClientRects()] const rect = rects.find(r => (r.height && r.width)) || el.getBoundingClientRect(); return rect.toJSON(); } JAVASCRIPT end
right_click(keys = [], **options)
click to toggle source
# File lib/capybara/playwright/node.rb, line 348 def right_click(keys = [], **options) click_options = ClickOptions.new(@element, keys, options, capybara_default_wait_time) params = click_options.as_params params[:button] = 'right' @element.click(**params) end
scroll_by(x, y)
click to toggle source
# File lib/capybara/playwright/node.rb, line 691 def scroll_by(x, y) js = <<~JAVASCRIPT (el, [x, y]) => { if (el.scrollBy){ el.scrollBy(x, y); } else { el.scrollTop = el.scrollTop + y; el.scrollLeft = el.scrollLeft + x; } } JAVASCRIPT @element.evaluate(js, arg: [x, y]) end
scroll_to(element, location, position = nil)
click to toggle source
# File lib/capybara/playwright/node.rb, line 706 def scroll_to(element, location, position = nil) # location, element = element, nil if element.is_a? Symbol if element.is_a? Capybara::Playwright::Node scroll_element_to_location(element, location) elsif location.is_a? Symbol scroll_to_location(location) else scroll_to_coords(*position) end self end
select_option()
click to toggle source
# File lib/capybara/playwright/node.rb, line 316 def select_option return false if disabled? select_element = parent_select_element if select_element.evaluate('el => el.multiple') selected_options = select_element.query_selector_all('option:checked') selected_options << @element select_element.select_option(element: selected_options, timeout: capybara_default_wait_time) else select_element.select_option(element: @element, timeout: capybara_default_wait_time) end end
selected?()
click to toggle source
# File lib/capybara/playwright/node.rb, line 818 def selected? assert_element_not_stale @element.evaluate('el => !!el.selected') end
send_keys(*args)
click to toggle source
# File lib/capybara/playwright/node.rb, line 439 def send_keys(*args) SendKeys.new(@element, args).execute end
set(value, **options)
click to toggle source
@param value [String, Array] Array is only allowed if node has 'multiple' attribute @param options [Hash] Driver
specific options for how to set a value on a node
# File lib/capybara/playwright/node.rb, line 174 def set(value, **options) settable_class = case tag_name when 'input' case attribute('type') when 'radio' RadioButton when 'checkbox' Checkbox when 'file' FileUpload when 'date' DateInput when 'time' TimeInput when 'datetime-local' DateTimeInput when 'color' JSValueInput when 'range' JSValueInput else TextInput end when 'textarea' TextInput else if @element.editable? TextInput else raise NotSupportedByDriverError end end settable_class.new(@element, capybara_default_wait_time).set(value, **options) rescue ::Playwright::TimeoutError => err raise NotActionableError.new(err) end
style(styles)
click to toggle source
# File lib/capybara/playwright/node.rb, line 166 def style(styles) # Capybara provides default implementation. # ref: https://github.com/teamcapybara/capybara/blob/f7ab0b5cd5da86185816c2d5c30d58145fe654ed/lib/capybara/node/element.rb#L92 raise NotImplementedError end
tag_name()
click to toggle source
# File lib/capybara/playwright/node.rb, line 770 def tag_name @tag_name ||= @element.evaluate('e => e.tagName.toLowerCase()') end
trigger(event)
click to toggle source
# File lib/capybara/playwright/node.rb, line 890 def trigger(event) @element.dispatch_event(event) end
unselect_option()
click to toggle source
# File lib/capybara/playwright/node.rb, line 329 def unselect_option if parent_select_element.evaluate('el => el.multiple') return false if disabled? @element.evaluate('el => el.selected = false') else raise Capybara::UnselectNotAllowed, 'Cannot unselect option from single select box.' end end
value()
click to toggle source
# File lib/capybara/playwright/node.rb, line 152 def value assert_element_not_stale # ref: https://github.com/teamcapybara/capybara/blob/f7ab0b5cd5da86185816c2d5c30d58145fe654ed/lib/capybara/selenium/node.rb#L31 # ref: https://github.com/twalpole/apparition/blob/11aca464b38b77585191b7e302be2e062bdd369d/lib/capybara/apparition/node.rb#L728 if tag_name == 'select' && @element.evaluate('el => el.multiple') @element.query_selector_all('option:checked').map do |option| option.evaluate('el => el.value') end else @element.evaluate('el => el.value') end end
visible?()
click to toggle source
# File lib/capybara/playwright/node.rb, line 774 def visible? assert_element_not_stale # if an area element, check visibility of relevant image @element.evaluate(<<~JAVASCRIPT) function(el) { if (el.tagName == 'AREA'){ const map_name = document.evaluate('./ancestor::map/@name', el, null, XPathResult.STRING_TYPE, null).stringValue; el = document.querySelector(`img[usemap='#${map_name}']`); if (!el){ return false; } } var forced_visible = false; while (el) { const style = window.getComputedStyle(el); if (style.visibility == 'visible') forced_visible = true; if ((style.display == 'none') || ((style.visibility == 'hidden') && !forced_visible) || (parseFloat(style.opacity) == 0)) { return false; } var parent = el.parentElement; if (parent && (parent.tagName == 'DETAILS') && !parent.open && (el.tagName != 'SUMMARY')) { return false; } el = parent; } return true; } JAVASCRIPT end
visible_text()
click to toggle source
# File lib/capybara/playwright/node.rb, line 115 def visible_text assert_element_not_stale return '' unless visible? text = @element.evaluate(<<~JAVASCRIPT) function(el){ if (el.nodeName == 'TEXTAREA'){ return el.textContent; } else if (el instanceof SVGElement) { return el.textContent; } else { return el.innerText; } } JAVASCRIPT text.to_s.gsub(/\A[[:space:]&&[^\u00a0]]+/, '') .gsub(/[[:space:]&&[^\u00a0]]+\z/, '') .gsub(/\n+/, "\n") .tr("\u00a0", ' ') end
Protected Instance Methods
element()
click to toggle source
# File lib/capybara/playwright/node.rb, line 73 def element @element end
Private Instance Methods
assert_element_not_stale()
click to toggle source
# File lib/capybara/playwright/node.rb, line 77 def assert_element_not_stale # Playwright checks the staled state only when # actionable methods. (click, select_option, hover, ...) # Capybara expects stale checking also when getting inner text, and so on. @element.enabled? rescue ::Playwright::Error => err case err.message when /Element is not attached to the DOM/ raise StaleReferenceError.new(err) when /Cannot find context with specified id/ raise StaleReferenceError.new(err) when /Unable to adopt element handle from a different document/ # for WebKit. raise StaleReferenceError.new(err) when /error in channel "content::page": exception while running method "adoptNode"/ # for Firefox raise StaleReferenceError.new(err) else raise end end
attribute(name)
click to toggle source
# File lib/capybara/playwright/node.rb, line 148 def attribute(name) @element.get_attribute(name) end
capybara_default_wait_time()
click to toggle source
# File lib/capybara/playwright/node.rb, line 97 def capybara_default_wait_time Capybara.default_max_wait_time * 1100 # with 10% buffer for allowing overhead. end
parent_select_element()
click to toggle source
# File lib/capybara/playwright/node.rb, line 339 def parent_select_element @element.query_selector('xpath=ancestor::select') end
property(name)
click to toggle source
# File lib/capybara/playwright/node.rb, line 143 def property(name) value = @element.get_property(name) value.evaluate("value => ['object', 'function'].includes(typeof value) ? null : value") end
scroll_element_to_location(element, location)
click to toggle source
# File lib/capybara/playwright/node.rb, line 719 def scroll_element_to_location(element, location) scroll_opts = case location when :top 'true' when :bottom 'false' when :center "{behavior: 'instant', block: 'center'}" else raise ArgumentError, "Invalid scroll_to location: #{location}" end element.native.evaluate("(el) => { el.scrollIntoView(#{scroll_opts}) }") end
scroll_to_coords(x, y)
click to toggle source
# File lib/capybara/playwright/node.rb, line 755 def scroll_to_coords(x, y) js = <<~JAVASCRIPT (el, [x, y]) => { if (el.scrollTo){ el.scrollTo(x, y); } else { el.scrollTop = y; el.scrollLeft = x; } } JAVASCRIPT @element.evaluate(js, arg: [x, y]) end
scroll_to_location(location)
click to toggle source
# File lib/capybara/playwright/node.rb, line 741 def scroll_to_location(location) position = SCROLL_POSITIONS[location] @element.evaluate(<<~JAVASCRIPT) (el) => { if (el.scrollTo){ el.scrollTo(0, #{position}); } else { el.scrollTop = #{position}; } } JAVASCRIPT end