Skip to content

Commit 2cc6976

Browse files
authored
Add initial support for shadow_root (#234)
Following capybara's support teamcapybara/capybara#2546
1 parent 49e2c4f commit 2cc6976

4 files changed

Lines changed: 49 additions & 12 deletions

File tree

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) {
@@ -306,7 +318,7 @@ class Cuprite {
306318
x -= frameOffset.left;
307319
y -= frameOffset.top;
308320

309-
let element = document.elementFromPoint(x, y);
321+
let element = node.getRootNode().elementFromPoint(x, y);
310322

311323
let el = element;
312324
while (el) {

lib/capybara/cuprite/node.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,13 @@ def obscured?
229229
command(:obscured?)
230230
end
231231

232+
def shadow_root
233+
root = driver.evaluate_script <<~JS, self
234+
arguments[0].shadowRoot
235+
JS
236+
root && self.class.new(driver, root.node)
237+
end
238+
232239
def inspect
233240
%(#<#{self.class} @node=#{@node.inspect}>)
234241
end

spec/features/session_spec.rb

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -446,6 +446,24 @@
446446
end
447447
end
448448

449+
describe "Node#shadow_root" do
450+
it "produces error messages when failing" do
451+
@session.visit("/with_shadow")
452+
shadow_root = @session.find(:css, "#shadow_host").shadow_root
453+
expect do
454+
expect(shadow_root).to have_css("#shadow_content", text: "Not in the document")
455+
end.to raise_error(/tag="#document-fragment"/)
456+
end
457+
458+
it "extends visibility check across shadow host boundary" do
459+
@session.visit("/with_shadow")
460+
shadow_root = @session.find(:css, "#shadow_host").shadow_root
461+
expect(shadow_root).to have_css("a")
462+
@session.execute_script %(document.getElementById("shadow_host").style.display = "none")
463+
expect(shadow_root).to_not have_css("a")
464+
end
465+
end
466+
449467
it "has no trouble clicking elements when the size of a document changes" do
450468
@session.visit("/cuprite/long_page")
451469
@session.find(:css, "#penultimate").click

spec/spec_helper.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,8 @@ module TestSessions
5959
node #visible? details non-summary descendants should be non-visible
6060
node #visible? works when details is toggled open and closed
6161
node #path reports when element in shadow dom
62-
node #shadow_root
6362
node #set should submit single text input forms if ended with
63+
node #shadow_root should produce error messages when failing
6464
#fill_in should fill in a color field
6565
#fill_in should handle carriage returns with line feeds in a textarea correctly
6666
#fill_in should fill in a textarea in a reasonable time by default

0 commit comments

Comments
 (0)