Skip to content

Commit bff3d8f

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

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;
@@ -95,6 +103,10 @@ class Cuprite {
95103
}
96104

97105
path(node) {
106+
if (node.getRootNode && node.getRootNode() instanceof ShadowRoot) {
107+
return "(: Shadow DOM element - no XPath :)";
108+
};
109+
98110
let nodes = [node];
99111
let parent = node.parentNode;
100112
while (parent !== document && parent !== null) {
@@ -275,7 +287,7 @@ class Cuprite {
275287
x -= frameOffset.left;
276288
y -= frameOffset.top;
277289

278-
let element = document.elementFromPoint(x, y);
290+
let element = node.getRootNode().elementFromPoint(x, y);
279291

280292
let el = element;
281293
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
@@ -316,6 +316,24 @@
316316
end
317317
end
318318

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

spec/spec_helper.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ module TestSessions
6464
node #visible? details non-summary descendants should be non-visible
6565
node #visible? works when details is toggled open and closed
6666
node #path reports when element in shadow dom
67-
node #shadow_root
67+
node #shadow_root should produce error messages when failing
6868
#all with obscured filter should only find nodes on top in the viewport when false
6969
#all with obscured filter should not find nodes on top outside the viewport when false
7070
#all with obscured filter should find top nodes outside the viewport when true

0 commit comments

Comments
 (0)