Skip to content

Commit 1578943

Browse files
committed
Add initial support for shadow_root
Following capybara's support teamcapybara/capybara#2546
1 parent c1c2e42 commit 1578943

File tree

4 files changed

+49
-12
lines changed

4 files changed

+49
-12
lines changed

lib/capybara/cuprite/javascripts/index.js

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -52,13 +52,17 @@ class Cuprite {
5252
if (this.isVisible(node)) {
5353
if (node.nodeName == "TEXTAREA") {
5454
return node.textContent;
55-
} else {
56-
if (node instanceof SVGElement) {
57-
return node.textContent;
58-
} else {
59-
return node.innerText;
60-
}
6155
}
56+
if (node instanceof SVGElement) {
57+
return node.textContent;
58+
}
59+
if (node instanceof ShadowRoot) {
60+
return Array.from(node.children)
61+
.map(child => this.visibleText(child))
62+
.filter(text => text)
63+
.join(" ");
64+
}
65+
return node.innerText;
6266
}
6367
}
6468

@@ -74,11 +78,15 @@ class Cuprite {
7478
}
7579

7680
while (node) {
77-
style = window.getComputedStyle(node);
78-
if (style.display === "none" || style.visibility === "hidden" || parseFloat(style.opacity) === 0) {
79-
return false;
81+
if (node instanceof ShadowRoot) {
82+
node = node.host;
83+
} else {
84+
style = window.getComputedStyle(node);
85+
if (style.display === "none" || style.visibility === "hidden" || parseFloat(style.opacity) === 0) {
86+
return false;
87+
}
88+
node = node.parentElement ?? (node.getRootNode() instanceof ShadowRoot && node.getRootNode());
8089
}
81-
node = node.parentElement;
8290
}
8391

8492
return true;
@@ -94,6 +102,10 @@ class Cuprite {
94102
}
95103

96104
path(node) {
105+
if (node.getRootNode && node.getRootNode() instanceof ShadowRoot) {
106+
return "(: Shadow DOM element - no XPath :)";
107+
};
108+
97109
let nodes = [node];
98110
let parent = node.parentNode;
99111
while (parent !== document && parent !== null) {
@@ -280,7 +292,7 @@ class Cuprite {
280292
x -= frameOffset.left;
281293
y -= frameOffset.top;
282294

283-
let element = document.elementFromPoint(x, y);
295+
let element = node.getRootNode().elementFromPoint(x, y);
284296

285297
let el = element;
286298
while (el) {

lib/capybara/cuprite/node.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,13 @@ def path
211211
command(:path)
212212
end
213213

214+
def shadow_root
215+
root = driver.evaluate_script <<~JS, self
216+
arguments[0].shadowRoot
217+
JS
218+
root && self.class.new(driver, root.node)
219+
end
220+
214221
def inspect
215222
%(#<#{self.class} @node=#{@node.inspect}>)
216223
end

spec/features/session_spec.rb

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,24 @@
314314
end
315315
end
316316

317+
describe "Node#shadow_root" do
318+
it "produces error messages when failing" do
319+
@session.visit("/with_shadow")
320+
shadow_root = @session.find(:css, "#shadow_host").shadow_root
321+
expect do
322+
expect(shadow_root).to have_css("#shadow_content", text: "Not in the document")
323+
end.to raise_error(/tag="#document-fragment"/)
324+
end
325+
326+
it "extends visibility check across shadow host boundary" do
327+
@session.visit("/with_shadow")
328+
shadow_root = @session.find(:css, "#shadow_host").shadow_root
329+
expect(shadow_root).to have_css("a")
330+
@session.execute_script %(document.getElementById("shadow_host").style.display = "none")
331+
expect(shadow_root).to_not have_css("a")
332+
end
333+
end
334+
317335
it "has no trouble clicking elements when the size of a document changes" do
318336
@session.visit("/cuprite/long_page")
319337
@session.find(:css, "#penultimate").click

spec/spec_helper.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,8 @@ module TestSessions
6666
node #visible? details non-summary descendants should be non-visible
6767
node #visible? works when details is toggled open and closed
6868
node #path reports when element in shadow dom
69-
node #shadow_root
7069
node #set should submit single text input forms if ended with
70+
node #shadow_root should produce error messages when failing
7171
#all with obscured filter should only find nodes on top in the viewport when false
7272
#all with obscured filter should not find nodes on top outside the viewport when false
7373
#all with obscured filter should find top nodes outside the viewport when true

0 commit comments

Comments
 (0)