mirror of
https://github.com/teamcapybara/capybara.git
synced 2022-11-09 12:08:07 -05:00
Add initial Element#shadow_root support
This commit is contained in:
parent
780d578592
commit
836a4167a2
10 changed files with 95 additions and 1 deletions
|
@ -9,6 +9,7 @@ Release date: unreleased
|
|||
|
||||
* [Beta] CSP nonces inserted into animation disabler additions - Issue #2542
|
||||
* Support `<base>` element in rack-test driver - ISsue #2544
|
||||
* [Beta] `Element#shadow_root` support. Requires selenium-webdriver 4.1+. Only currently supported with Chrome when using the selenium driver. Note: only CSS can be used to find elements inside the shadow dom so you won't be able to use most Capybara helper methods (`fill_in`, `click_link`, `find_field`, etc) since those locators are built using XPath. Stick to `find(:css, ...)` and direct interaction methods.
|
||||
|
||||
### Fixed
|
||||
|
||||
|
|
|
@ -125,6 +125,10 @@ module Capybara
|
|||
raise NotSupportedByDriverError, 'Capybara::Driver::Node#trigger'
|
||||
end
|
||||
|
||||
def shadow_root
|
||||
raise NotSupportedByDriverError, 'Capybara::Driver::Node#shadow_root'
|
||||
end
|
||||
|
||||
def inspect
|
||||
%(#<#{self.class} tag="#{tag_name}" path="#{path}">)
|
||||
rescue NotSupportedByDriverError
|
||||
|
|
|
@ -472,6 +472,17 @@ module Capybara
|
|||
self
|
||||
end
|
||||
|
||||
##
|
||||
#
|
||||
# Return the shadow_root for the current element
|
||||
#
|
||||
# @return [Capybara::Node::Element] The shadow root
|
||||
|
||||
def shadow_root
|
||||
root = synchronize { base.shadow_root }
|
||||
root && Capybara::Node::Element.new(self.session, root, nil, nil)
|
||||
end
|
||||
|
||||
##
|
||||
#
|
||||
# Execute the given JS in the context of the element not returning a result. This is useful for scripts that return
|
||||
|
|
|
@ -473,12 +473,16 @@ private
|
|||
end
|
||||
|
||||
def unwrap_script_result(arg)
|
||||
# TODO - move into the case when we drop support for Selenium < 4.1
|
||||
element_types = [Selenium::WebDriver::Element]
|
||||
element_types.push(Selenium::WebDriver::ShadowRoot) if defined?(Selenium::WebDriver::ShadowRoot)
|
||||
|
||||
case arg
|
||||
when Array
|
||||
arg.map { |arr| unwrap_script_result(arr) }
|
||||
when Hash
|
||||
arg.transform_values! { |value| unwrap_script_result(value) }
|
||||
when Selenium::WebDriver::Element
|
||||
when *element_types
|
||||
build_node(arg)
|
||||
else
|
||||
arg
|
||||
|
|
|
@ -219,6 +219,13 @@ class Capybara::Selenium::Node < Capybara::Driver::Node
|
|||
native.rect
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
protected
|
||||
|
||||
def scroll_if_needed
|
||||
|
|
|
@ -1176,6 +1176,29 @@ Capybara::SpecHelper.spec 'node' do
|
|||
end
|
||||
end
|
||||
|
||||
describe '#shadow_root', requires: %i[js] do
|
||||
it 'should get the shadow root' do
|
||||
@session.visit('/with_shadow')
|
||||
expect do
|
||||
shadow_root = @session.find(:css, '#shadow_host').shadow_root
|
||||
expect(shadow_root).not_to be_nil
|
||||
end.not_to raise_error
|
||||
end
|
||||
|
||||
it 'should find elements inside the shadow dom using CSS' do
|
||||
@session.visit('/with_shadow')
|
||||
shadow_root = @session.find(:css, '#shadow_host').shadow_root
|
||||
expect(shadow_root).to have_css('#shadow_content', text: 'some text')
|
||||
end
|
||||
|
||||
it 'should find nested shadow roots' do
|
||||
@session.visit('/with_shadow')
|
||||
shadow_root = @session.find(:css, '#shadow_host').shadow_root
|
||||
nested_shadow_root = shadow_root.find(:css, '#nested_shadow_host').shadow_root
|
||||
expect(nested_shadow_root).to have_css('#nested_shadow_content', text: 'nested text')
|
||||
end
|
||||
end
|
||||
|
||||
describe '#reload', requires: [:js] do
|
||||
it 'should reload elements found via ancestor with CSS' do
|
||||
@session.visit('/with_js')
|
||||
|
|
29
lib/capybara/spec/views/with_shadow.erb
Normal file
29
lib/capybara/spec/views/with_shadow.erb
Normal file
|
@ -0,0 +1,29 @@
|
|||
<!DOCTYPE html>
|
||||
<%# Borrowed from Titus Fortner %>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<title>Shadow DOM</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="no_host"></div>
|
||||
<div id="shadow_host"></div>
|
||||
<a href="scroll.html">scroll.html</a>
|
||||
<script>
|
||||
let shadowRoot = document.getElementById('shadow_host').attachShadow({mode: 'open'});
|
||||
shadowRoot.innerHTML = `
|
||||
<span class="wrapper" id="shadow_content"><span class="info">some text</span></span>
|
||||
<div id="nested_shadow_host"></div>
|
||||
<a href="scroll.html">scroll.html</a>
|
||||
<input type="text" />
|
||||
<input type="checkbox" />
|
||||
<input type="file" />
|
||||
`;
|
||||
|
||||
let nestedShadowRoot = shadowRoot.getElementById('nested_shadow_host').attachShadow({mode: 'open'});
|
||||
nestedShadowRoot.innerHTML = `
|
||||
<div id="nested_shadow_content"><div>nested text</div></div>
|
||||
`;
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -72,6 +72,10 @@ Capybara::SpecHelper.run_specs TestSessions::SeleniumFirefox, 'selenium', capyba
|
|||
when 'Capybara::Session selenium #accept_alert should handle the alert if the page changes',
|
||||
'Capybara::Session selenium #accept_alert with an asynchronous alert should accept the alert'
|
||||
skip 'No clue what Firefox is doing here - works fine on MacOS locally'
|
||||
when 'Capybara::Session selenium node #shadow_root should get the shadow root',
|
||||
'Capybara::Session selenium node #shadow_root should find elements inside the shadow dom using CSS',
|
||||
'Capybara::Session selenium node #shadow_root should find nested shadow roots'
|
||||
pending "Firefox doesn't yet have W3C shadow root support"
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -84,6 +84,10 @@ Capybara::SpecHelper.run_specs TestSessions::Safari, SAFARI_DRIVER.to_s, capybar
|
|||
when 'Capybara::Session selenium_safari #go_back should fetch a response from the driver from the previous page',
|
||||
'Capybara::Session selenium_safari #go_forward should fetch a response from the driver from the previous page'
|
||||
skip 'safaridriver loses the ability to find elements in the document after `go_back`'
|
||||
when 'Capybara::Session selenium node #shadow_root should get the shadow root',
|
||||
'Capybara::Session selenium node #shadow_root should find elements inside the shadow dom using CSS',
|
||||
'Capybara::Session selenium node #shadow_root should find nested shadow roots'
|
||||
pending "Safari doesn't yet have W3C shadow root support"
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -280,6 +280,13 @@ RSpec.shared_examples 'Capybara::Session' do |session, mode|
|
|||
expect(element).to eq session.find(:id, 'form_title')
|
||||
end
|
||||
|
||||
it 'returns a shadow root' do
|
||||
session.visit('/with_shadow')
|
||||
shadow = session.find(:css, '#shadow_host')
|
||||
element = session.evaluate_script("arguments[0].shadowRoot", shadow)
|
||||
expect(element).to be_instance_of(Capybara::Node::Element)
|
||||
end
|
||||
|
||||
it 'can return arrays of nested elements' do
|
||||
session.visit('/form')
|
||||
elements = session.evaluate_script('document.querySelectorAll("#form_city option")')
|
||||
|
|
Loading…
Add table
Reference in a new issue