diff --git a/lib/capybara/cuprite/browser.rb b/lib/capybara/cuprite/browser.rb index 5aad56df..d7ce914e 100644 --- a/lib/capybara/cuprite/browser.rb +++ b/lib/capybara/cuprite/browser.rb @@ -210,6 +210,10 @@ def path(node) evaluate_on(node: node, expression: "_cuprite.path(this)") end + def obscured?(node) + evaluate_on(node: node, expression: "_cuprite.isObscured(this)") + end + def all_text(node) node.text end diff --git a/lib/capybara/cuprite/javascripts/index.js b/lib/capybara/cuprite/javascripts/index.js index 81bc8db8..74b96ae5 100644 --- a/lib/capybara/cuprite/javascripts/index.js +++ b/lib/capybara/cuprite/javascripts/index.js @@ -115,6 +115,35 @@ class Cuprite { return `//${selectors.join("/")}`; } + /** + * Returns true if the node is obscured in the viewport. + * + * @param {Element} node + * @return {boolean} true if the node is obscured, false otherwise + */ + isObscured(node) { + let win = window; + let rect = node.getBoundingClientRect(); + let px = rect.left + rect.width / 2; + let py = rect.top + rect.height / 2; + + while (win) { + let topNode = win.document.elementFromPoint(px, py); + + if (node !== topNode && !node.contains(topNode)) return true; + + node = win.frameElement; + if (!node) return false; + + rect = node.getBoundingClientRect(); + px = rect.left + px; + py = rect.top + py; + win = win.parent; + } + + return false; + } + set(node, value) { if (node.readOnly) return; diff --git a/lib/capybara/cuprite/node.rb b/lib/capybara/cuprite/node.rb index f7e68f44..d4f12395 100644 --- a/lib/capybara/cuprite/node.rb +++ b/lib/capybara/cuprite/node.rb @@ -213,6 +213,10 @@ def path command(:path) end + def obscured? + command(:obscured?) + end + def inspect %(#<#{self.class} @node=#{@node.inspect}>) end diff --git a/spec/features/session_spec.rb b/spec/features/session_spec.rb index ea3cd45d..96a5b784 100644 --- a/spec/features/session_spec.rb +++ b/spec/features/session_spec.rb @@ -314,6 +314,31 @@ end end + describe "Node#obscured?" do + context "when the element is not in the viewport of parent element" do + before do + @session.visit("/cuprite/scroll") + end + + it "is is a boolean" do + expect(@session.find_link("Link outside viewport")).to be_obscured + expect(@session.find_link("Below the fold")).to be_obscured + end + end + + context "when the element is only overlapped by descendants" do + before do + @session.visit("/with_html") + end + + # copied from https://github.com/teamcapybara/capybara/blob/master/lib/capybara/spec/session/node_spec.rb#L328 + # as this example is currently disabled on CI in the upstream suite + it "is not obscured" do + expect(@session.first(:css, "p:not(.para)")).not_to be_obscured + end + end + end + it "has no trouble clicking elements when the size of a document changes" do @session.visit("/cuprite/long_page") @session.find(:css, "#penultimate").click diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index ff85a004..e41daddd 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -37,7 +37,6 @@ module TestSessions RSpec.configure do |config| config.define_derived_metadata do |metadata| regexes = <<~REGEXP.split("\n").map { |s| Regexp.quote(s.strip) }.join("|") - node #obscured? node #drag_to should work with jsTree node #drag_to should drag and drop an object node #drag_to should drag and drop if scrolling is needed @@ -68,10 +67,6 @@ module TestSessions node #path reports when element in shadow dom node #shadow_root node #set should submit single text input forms if ended with - #all with obscured filter should only find nodes on top in the viewport when false - #all with obscured filter should not find nodes on top outside the viewport when false - #all with obscured filter should find top nodes outside the viewport when true - #all with obscured filter should only find non-top nodes when true #fill_in should fill in a color field #fill_in should handle carriage returns with line feeds in a textarea correctly #has_field with valid should be false if field is invalid