Skip to content

Commit ecf5d7f

Browse files
authored
fix: raise ObsoleteNode error for disconnected nodes between lookup and usage (#239) (#322)
1 parent b64f03d commit ecf5d7f

4 files changed

Lines changed: 37 additions & 0 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
### Fixed
1111
- Ensure node has focus before setting value [#280]
12+
- Raise `ObsoleteNode` instead of silently acting on a node that got disconnected from the DOM between being found and being used [#239]
1213

1314
### Removed
1415

lib/capybara/cuprite/node.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ def initialize(driver, node)
1818
end
1919

2020
def command(name, *args)
21+
raise ObsoleteNode.new(self, nil) unless node.evaluate("this.isConnected")
22+
2123
browser.send(name, node, *args)
2224
rescue Ferrum::NodeNotFoundError => e
2325
raise ObsoleteNode.new(self, e.response)

spec/features/session_spec.rb

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,32 @@
4242
expect(@session.current_path).to eq("/")
4343
end
4444

45+
it "raises an error instead of filling in a stale node replaced right after being found" do
46+
@session.visit("/cuprite/with_js")
47+
node = @session.find(:css, "[name=draft]", visible: :all)
48+
49+
@session.execute_script(<<~JS)
50+
var wrapper = document.getElementById("draft-wrapper");
51+
setTimeout(function() { wrapper.innerHTML = '<textarea name="draft"></textarea>'; }, 0);
52+
JS
53+
54+
# native.set bypasses Capybara::Node::Element#reload, which would
55+
# otherwise silently re-find and retry against the live replacement.
56+
expect { node.native.set("World") }.to raise_error(Capybara::Cuprite::ObsoleteNode)
57+
end
58+
59+
it "raises an error instead of checking a stale checkbox replaced right after being found" do
60+
@session.visit("/cuprite/with_js")
61+
node = @session.find(:css, "[name=agree]", visible: :all)
62+
63+
@session.execute_script(<<~JS)
64+
var wrapper = document.getElementById("agree-wrapper");
65+
setTimeout(function() { wrapper.innerHTML = '<input type="checkbox" name="agree">'; }, 0);
66+
JS
67+
68+
expect { node.native.set(true) }.to raise_error(Capybara::Cuprite::ObsoleteNode)
69+
end
70+
4571
it "does not raise error when asserting svg elements with a count that is not what is in the dom" do
4672
@session.visit("/cuprite/with_js")
4773
expect { @session.has_css?("svg circle", count: 2) }.to_not raise_error

spec/support/views/with_js.erb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,5 +72,13 @@
7272
<p>
7373
<a href="#" id="open-twice">Open check twice</a>
7474
</p>
75+
76+
<div id="draft-wrapper">
77+
<textarea name="draft"></textarea>
78+
</div>
79+
80+
<div id="agree-wrapper">
81+
<input type="checkbox" name="agree">
82+
</div>
7583
</body>
7684
</html>

0 commit comments

Comments
 (0)