From ff99f3d56b7a1d86fc7875f89b435901811b6f1a Mon Sep 17 00:00:00 2001 From: Lucas Guedes Date: Mon, 28 Sep 2026 17:38:18 -0300 Subject: [PATCH] Ignore ancestor opacity for open popovers and modal dialogs --- lib/capybara/cuprite/javascripts/index.js | 14 +++++++++++- spec/features/session_spec.rb | 28 +++++++++++++++++++++++ spec/support/views/visible.erb | 7 ++++++ 3 files changed, 48 insertions(+), 1 deletion(-) diff --git a/lib/capybara/cuprite/javascripts/index.js b/lib/capybara/cuprite/javascripts/index.js index 092c0226..b0733b09 100644 --- a/lib/capybara/cuprite/javascripts/index.js +++ b/lib/capybara/cuprite/javascripts/index.js @@ -77,15 +77,27 @@ class Cuprite { } } + let ancestorOpacityApplies = true; + while (node) { if (node instanceof ShadowRoot) { node = node.host; } else { style = window.getComputedStyle(node); - if (style.display === "none" || style.visibility === "hidden" || parseFloat(style.opacity) === 0) { + if (style.display === "none" || style.visibility === "hidden" || (ancestorOpacityApplies && parseFloat(style.opacity) === 0)) { return false; } + // A node matching :modal or :popover-open is a top-layer element, so + // an ancestor's opacity no longer applies to it once shown. + if (ancestorOpacityApplies) { + try { + if (node.matches(":modal") || node.matches(":popover-open")) { + ancestorOpacityApplies = false; + } + } catch (e) {} + } + let parent = node.parentElement; if (parent && parent.tagName === "DETAILS" && !parent.open) { // In a closed
only the first (and its subtree) is rendered. diff --git a/spec/features/session_spec.rb b/spec/features/session_spec.rb index 4ce33090..752d2c78 100644 --- a/spec/features/session_spec.rb +++ b/spec/features/session_spec.rb @@ -410,6 +410,34 @@ it "element with all children hidden returns empty text" do expect(@session.find(:css, "div").text).to eq("") end + + it "considers a shown popover to be visible past opacity: 0" do + popover = @session.find(:css, "#popover_in_transparent", visible: false) + expect(popover.visible?).to be false + + popover.execute_script("this.showPopover()") + + expect(popover.visible?).to be true + end + + it "considers a shown modal dialog to be visible past opacity: 0" do + dialog = @session.find(:css, "#dialog_in_transparent", visible: false) + expect(dialog.visible?).to be false + + dialog.execute_script("this.showModal()") + expect(dialog.visible?).to be true + + dialog.execute_script("this.close(); this.show()") + expect(dialog.visible?).to be false + end + + it "considers a shown popover to not be visible past display: none" do + popover = @session.find(:css, "#popover_in_display_none", visible: false) + + popover.execute_script("this.showPopover()") + + expect(popover.visible?).to be false + end end describe "Node#checked?" do diff --git a/spec/support/views/visible.erb b/spec/support/views/visible.erb index 0209ddc7..8733bae3 100644 --- a/spec/support/views/visible.erb +++ b/spec/support/views/visible.erb @@ -15,5 +15,12 @@ Display None Hidden +
+
Popover In Transparent
+ Dialog In Transparent +
+
+
Popover In Display None
+