Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Include the overlapping xpath for ClickeFailed from obscured elements #534

Closed
wants to merge 3 commits into from

2 participants

@jferris
Admin

This makes it easier to debug failed clicks because of modals, etc.

Example message:

Failed to click element /html/body/div[@id='one'] because of overlapping
element /html/body/div[@id='two'] at position 199, 199
src/capybara.js
@@ -93,8 +93,8 @@ Capybara = {
return this.nodes[index].hasAttribute(name);
},
- path: function(index) {
- return "/" + this.getXPathNode(this.nodes[index]).join("/");
+ path: function(node) {
@mhoran Collaborator
mhoran added a note

Looks like there are no tests for http://git.io/RlZPiQ, which is an upstream Capybara method.

@jferris Admin
jferris added a note

Good catch. I added specs for that and fixed them.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@mhoran
Collaborator

This is great. I had a minor comment about the changes made to path.

@jferris
Admin

@halogenandtoast reported this error when using this branch:

Capybara::Webkit::InvalidResponseError: 'undefined' is not an object
from /Users/halogenandtoast/.gem/ruby/1.9.3/bundler/gems/capybara-webkit-bb8260c3961d/lib/capybara/webkit/browser.rb:215:in `check'
@jferris
Admin

@halogenandtoast reported this error when using this branch:

Capybara::Webkit::InvalidResponseError: 'undefined' is not an object
from /Users/halogenandtoast/.gem/ruby/1.9.3/bundler/gems/capybara-webkit-bb8260c3961d/lib/capybara/webkit/browser.rb:215:in `check'
@mhoran
Collaborator

I get that error as well. I've fixed the Travis failures on master, so you should be able to merge that in to see the error on Travis. The issue is on line 146 and 147 of capybara.js, which should be using pathForNode instead of path. Otherwise, I think this is good to merge.

@mhoran
Collaborator

With the above changes, rspec ./spec/integration/session_spec.rb:441 still fails. I believe we need to check that there is a nodeAtPosition before trying to find the pathToNode on line 147 of capybara.js.

jferris added some commits
@jferris jferris Include the overlapping xpath for ClickeFailed from obscured elements
This makes it easier to debug failed clicks because of modals, etc.

Example message:

    Failed to click element /html/body/div[@id='one'] because of overlapping
    element /html/body/div[@id='two'] at position 199, 199
ff14220
@jferris jferris Fix Node#path
* Also adds missing specs for capybara's Node#path
6da6743
@jferris jferris Fix failures from invisible nodes, path/pathForNode mixups a7766f1
@jferris jferris closed this
@jferris
Admin

I rebased on master, fixed the path/pathForNode issues, fixed the missing object for invisible elements, and everything is passing. This is merged.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Jul 12, 2013
  1. @jferris

    Include the overlapping xpath for ClickeFailed from obscured elements

    jferris authored
    This makes it easier to debug failed clicks because of modals, etc.
    
    Example message:
    
        Failed to click element /html/body/div[@id='one'] because of overlapping
        element /html/body/div[@id='two'] at position 199, 199
  2. @jferris

    Fix Node#path

    jferris authored
    * Also adds missing specs for capybara's Node#path
  3. @jferris
This page is out of date. Refresh to see the latest.
View
19 spec/driver_spec.rb
@@ -2303,6 +2303,25 @@ def log
end
end
+ context 'path' do
+ let(:driver) do
+ driver_for_html(<<-HTML)
+ <html>
+ <body>
+ <div></div>
+ <div><div><span>hello</span></div></div>
+ </body>
+ </html>
+ HTML
+ end
+
+ it 'returns an xpath for the current node' do
+ visit('/')
+ path = driver.find_xpath('//span').first.path
+ driver.find_xpath(path).first.text.should == 'hello'
+ end
+ end
+
context "version" do
let(:driver) do
driver_for_html(<<-HTML)
View
10 spec/integration/session_spec.rb
@@ -414,7 +414,10 @@ module TestSessions
lambda {
subject.find(:css, '#one').click
- }.should raise_error(Capybara::Webkit::ClickFailed, /\[@id='one'\] at position/)
+ }.should raise_error(
+ Capybara::Webkit::ClickFailed,
+ /Failed.*\[@id='one'\].*overlapping.*\[@id='two'\].*at position/
+ )
end
it 'raises an error if a checkbox is obscured when checked' do
@@ -441,7 +444,10 @@ module TestSessions
begin
subject.visit('/')
subject.execute_script "document.getElementById('foo').style.display = 'none'"
- lambda { subject.click_link "Click Me" }.should raise_error(Capybara::Webkit::ClickFailed, /\[@id='foo'\] at unknown/)
+ lambda { subject.click_link "Click Me" }.should raise_error(
+ Capybara::Webkit::ClickFailed,
+ /\[@id='foo'\]/
+ )
ensure
Capybara.ignore_hidden_elements = ignore_hidden_elements
end
View
73 src/capybara.js
@@ -94,7 +94,11 @@ Capybara = {
},
path: function(index) {
- return "/" + this.getXPathNode(this.nodes[index]).join("/");
+ return this.pathForNode(this.nodes[index]);
+ },
+
+ pathForNode: function(node) {
+ return "/" + this.getXPathNode(node).join("/");
},
getXPathNode: function(node, path) {
@@ -133,17 +137,38 @@ Capybara = {
return this.nodes[index].submit();
},
- clickTest: function(node, pos) {
- var el = document.elementFromPoint(pos.relativeX, pos.relativeY);
-
- while (el) {
- if (el === node)
- return CapybaraInvocation.clickTest(node, pos.absoluteX, pos.absoluteY);
- else
- el = el.parentNode;
+ expectNodeAtPosition: function(node, pos) {
+ var nodeAtPosition =
+ document.elementFromPoint(pos.relativeX, pos.relativeY);
+ var overlappingPath;
+
+ if (nodeAtPosition)
+ overlappingPath = this.pathForNode(nodeAtPosition)
+
+ if (!this.isNodeOrChildAtPosition(node, pos, nodeAtPosition))
+ throw new Capybara.ClickFailed(
+ this.pathForNode(node),
+ overlappingPath,
+ pos
+ );
+ },
+
+ isNodeOrChildAtPosition: function(expectedNode, pos, currentNode) {
+ if (currentNode == expectedNode) {
+ return CapybaraInvocation.clickTest(
+ expectedNode,
+ pos.absoluteX,
+ pos.absoluteY
+ );
+ } else if (currentNode) {
+ return this.isNodeOrChildAtPosition(
+ expectedNode,
+ pos,
+ currentNode.parentNode
+ );
+ } else {
+ return false;
}
-
- return false;
},
clickPosition: function(node) {
@@ -155,18 +180,16 @@ Capybara = {
if (rect.width > 0 && rect.height > 0)
return CapybaraInvocation.clickPosition(node, rect.left, rect.top, rect.width, rect.height);
}
+
+ throw new Capybara.UnpositionedElement(this.pathForNode(node));
},
click: function (index, action) {
var node = this.nodes[index];
node.scrollIntoViewIfNeeded();
-
var pos = this.clickPosition(node);
-
- if (pos && this.clickTest(node, pos))
- action(pos.absoluteX, pos.absoluteY);
- else
- throw new Capybara.ClickFailed(this.path(index), pos);
+ this.expectNodeAtPosition(node, pos);
+ action(pos.absoluteX, pos.absoluteY);
},
leftClick: function (index) {
@@ -187,8 +210,7 @@ Capybara = {
node.scrollIntoViewIfNeeded();
var pos = this.clickPosition(node);
- if (pos)
- CapybaraInvocation.hover(pos.absoluteX, pos.absoluteY);
+ CapybaraInvocation.hover(pos.absoluteX, pos.absoluteY);
},
trigger: function (index, eventName) {
@@ -341,9 +363,11 @@ Capybara = {
}
};
-Capybara.ClickFailed = function(path, position) {
+Capybara.ClickFailed = function(expectedPath, actualPath, position) {
this.name = 'Capybara.ClickFailed';
- this.message = 'Failed to click element ' + path;
+ this.message = 'Failed to click element ' + expectedPath;
+ if (actualPath)
+ this.message += ' because of overlapping element ' + actualPath;
if (position)
this.message += ' at position ' + position["absoluteX"] + ', ' + position["absoluteY"];
else
@@ -351,3 +375,10 @@ Capybara.ClickFailed = function(path, position) {
};
Capybara.ClickFailed.prototype = new Error();
Capybara.ClickFailed.prototype.constructor = Capybara.ClickFailed;
+
+Capybara.UnpositionedElement = function(path) {
+ this.name = 'Capybara.ClickFailed';
+ this.message = 'Failed to find position for element ' + path;
+};
+Capybara.UnpositionedElement.prototype = new Error();
+Capybara.UnpositionedElement.prototype.constructor = Capybara.UnpositionedElement;
Something went wrong with that request. Please try again.