Skip to content

Commit 2aa1abc

Browse files
committed
Refactor how we patch node classes to support driver with browser: :remote
1 parent 14f4690 commit 2aa1abc

File tree

3 files changed

+133
-22
lines changed

3 files changed

+133
-22
lines changed

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@ All notable changes to this project will be documented in this file.
22

33
This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
44

5+
# Unreleased
6+
7+
- Add support for drivers with `browser: :remote`.
8+
59

610
# 2.2.0
711

lib/capybara-lockstep/capybara_ext.rb

+29-22
Original file line numberDiff line numberDiff line change
@@ -3,36 +3,46 @@
33
module Capybara
44
module Lockstep
55
module SynchronizeMacros
6+
def self.extended(by)
7+
by.instance_eval do
8+
prepend(@synchronize_before_module = Module.new)
9+
prepend(@synchronize_after_module = Module.new)
10+
prepend(@unsynchronize_after_module = Module.new)
11+
end
12+
end
613

714
def synchronize_before(meth, lazy:)
8-
mod = Module.new do
15+
@synchronize_before_module.module_eval do
916
define_method meth do |*args, &block|
10-
Lockstep.auto_synchronize(lazy: lazy, log: "Synchronizing before ##{meth}")
17+
@synchronize_before_count ||= 0
18+
@synchronize_before_count += 1
19+
Lockstep.auto_synchronize(lazy: lazy, log: "Synchronizing before ##{meth}") if @synchronize_before_count == 1
1120
super(*args, &block)
21+
ensure
22+
@synchronize_before_count -= 1
1223
end
1324

1425
ruby2_keywords meth
1526
end
16-
17-
prepend(mod)
1827
end
1928

2029
def synchronize_after(meth)
21-
mod = Module.new do
30+
@synchronize_after_module.module_eval do
2231
define_method meth do |*args, &block|
32+
@synchronize_after_count ||= 0
33+
@synchronize_after_count += 1
2334
super(*args, &block)
2435
ensure
25-
Lockstep.auto_synchronize
36+
Lockstep.auto_synchronize(log: "Synchronizing after ##{meth}") if @synchronize_after_count == 1
37+
@synchronize_after_count -= 1
2638
end
2739

2840
ruby2_keywords meth
2941
end
30-
31-
prepend(mod)
3242
end
3343

3444
def unsynchronize_after(meth)
35-
mod = Module.new do
45+
@unsynchronize_after_module.module_eval do
3646
define_method meth do |*args, &block|
3747
super(*args, &block)
3848
ensure
@@ -41,10 +51,7 @@ def unsynchronize_after(meth)
4151

4252
ruby2_keywords meth
4353
end
44-
45-
prepend(mod)
4654
end
47-
4855
end
4956
end
5057
end
@@ -158,23 +165,23 @@ def synchronize_around_script_method(meth)
158165
synchronize_around_script_method :evaluate_async_script
159166
end
160167

161-
# Capybara 3 has driver-specific Node classes which sometimes
162-
# super to Capybara::Selenium::Node, but not always.
163-
node_classes = [
168+
# In Capybara 3 there are the specialized classes for nodes for most browers.
169+
# We need to patch relevant methods on all of these.
170+
driver_specific_node_classes = [
164171
(Capybara::Selenium::ChromeNode if defined?(Capybara::Selenium::ChromeNode)),
165172
(Capybara::Selenium::FirefoxNode if defined?(Capybara::Selenium::FirefoxNode)),
166173
(Capybara::Selenium::SafariNode if defined?(Capybara::Selenium::SafariNode)),
167174
(Capybara::Selenium::EdgeNode if defined?(Capybara::Selenium::EdgeNode)),
168175
(Capybara::Selenium::IENode if defined?(Capybara::Selenium::IENode)),
169-
].compact
176+
].compact.freeze
170177

171-
if node_classes.empty?
172-
# Capybara 2 has no driver-specific Node implementations,
173-
# so we patch the shared base class.
174-
node_classes = [Capybara::Selenium::Node]
175-
end
178+
# For other browsers (like the :remote browser) we instead get a generic node class.
179+
# This is also the case for Capybara 2.
180+
generic_node_classes = [
181+
Capybara::Selenium::Node,
182+
].freeze
176183

177-
node_classes.each do |node_class|
184+
[*driver_specific_node_classes, *generic_node_classes].each do |node_class|
178185
node_class.class_eval do
179186
extend Capybara::Lockstep::SynchronizeMacros
180187

spec/capybara_ext_spec.rb

+100
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
describe Capybara::Lockstep::SynchronizeMacros do
2+
after do
3+
# called after each example, messes with our expectations
4+
allow(Capybara).to receive(:reset_sessions!)
5+
end
6+
7+
let(:example_class) do
8+
Class.new do
9+
def do_something
10+
end
11+
12+
def call_do_something
13+
do_something
14+
end
15+
end
16+
end
17+
18+
describe 'synchronize_before' do
19+
let(:patched_class) do
20+
Class.new(example_class) do
21+
extend Capybara::Lockstep::SynchronizeMacros
22+
23+
synchronize_before :call_do_something, lazy: false
24+
end
25+
end
26+
27+
let(:patched_sub_class) do
28+
Class.new(patched_class) do
29+
extend Capybara::Lockstep::SynchronizeMacros
30+
31+
synchronize_before :call_do_something, lazy: false
32+
end
33+
end
34+
35+
it 'runs auto_synchronize before the method' do
36+
object = patched_class.new
37+
expect(Capybara::Lockstep).to receive(:auto_synchronize).exactly(:once).ordered
38+
expect(object).to receive(:do_something).ordered
39+
object.call_do_something
40+
end
41+
42+
it 'runs it only once, even if we patch multiple classes in the class hierarchy' do
43+
object = patched_sub_class.new
44+
expect(Capybara::Lockstep).to receive(:auto_synchronize).exactly(:once).ordered
45+
expect(object).to receive(:do_something).ordered
46+
object.call_do_something
47+
end
48+
end
49+
50+
describe 'synchronize_after' do
51+
let(:patched_class) do
52+
Class.new(example_class) do
53+
extend Capybara::Lockstep::SynchronizeMacros
54+
55+
synchronize_after :call_do_something
56+
end
57+
end
58+
59+
let(:patched_sub_class) do
60+
Class.new(patched_class) do
61+
extend Capybara::Lockstep::SynchronizeMacros
62+
63+
synchronize_after :call_do_something
64+
end
65+
end
66+
67+
it 'runs auto_synchronize before the method' do
68+
object = patched_class.new
69+
expect(object).to receive(:do_something).ordered
70+
expect(Capybara::Lockstep).to receive(:auto_synchronize).exactly(:once).ordered
71+
object.call_do_something
72+
end
73+
74+
it 'runs it only once, even if we patch multiple classes in the class hierarchy' do
75+
object = patched_sub_class.new
76+
expect(object).to receive(:do_something).ordered
77+
expect(Capybara::Lockstep).to receive(:auto_synchronize).exactly(:once).ordered
78+
object.call_do_something
79+
end
80+
end
81+
82+
describe 'synchronize_before and synchronize_after' do
83+
let(:patched_class) do
84+
Class.new(example_class) do
85+
extend Capybara::Lockstep::SynchronizeMacros
86+
87+
synchronize_before :call_do_something, lazy: false
88+
synchronize_after :call_do_something
89+
end
90+
end
91+
92+
it 'runs auto_synchronize before and after the method' do
93+
object = patched_class.new
94+
expect(Capybara::Lockstep).to receive(:auto_synchronize).exactly(:once).ordered
95+
expect(object).to receive(:do_something).ordered
96+
expect(Capybara::Lockstep).to receive(:auto_synchronize).exactly(:once).ordered
97+
object.call_do_something
98+
end
99+
end
100+
end

0 commit comments

Comments
 (0)