Skip to content

Commit 3162afe

Browse files
Merge pull request rails#49908 from mattbrictson/bugs/chromedriver-race-cond
Preload Selenium driver_path before parallelizing system tests
2 parents d53ef2c + e7d743b commit 3162afe

File tree

3 files changed

+36
-11
lines changed

3 files changed

+36
-11
lines changed

actionpack/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
* Fix a race condition that could cause a `Text file busy - chromedriver`
2+
error with parallel system tests
3+
4+
*Matt Brictson*
5+
16
* Add `racc` as a dependency since it will become a bundled gem in Ruby 3.4.0
27

38
*Hartley McGuire*

actionpack/lib/action_dispatch/system_testing/browser.rb

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,14 @@ def configure
2626
yield options if block_given? && options
2727
end
2828

29-
# driver_path can be configured as a proc. Running this proc early allows
30-
# us to only update the webdriver once and avoid race conditions when
31-
# using parallel tests.
29+
# driver_path is lazily initialized by default. Eagerly set it to
30+
# avoid race conditions when using parallel tests.
3231
def preload
3332
case type
3433
when :chrome
35-
::Selenium::WebDriver::Chrome::Service.driver_path.try(:call)
34+
resolve_driver_path(::Selenium::WebDriver::Chrome)
3635
when :firefox
37-
::Selenium::WebDriver::Firefox::Service.driver_path.try(:call)
36+
resolve_driver_path(::Selenium::WebDriver::Firefox)
3837
end
3938
end
4039

@@ -70,6 +69,13 @@ def set_headless_firefox_browser_options
7069
capabilities.add_argument("-headless")
7170
end
7271
end
72+
73+
def resolve_driver_path(namespace)
74+
namespace::Service.driver_path = ::Selenium::WebDriver::DriverFinder.path(
75+
options || namespace::Options.new,
76+
namespace::Service
77+
)
78+
end
7379
end
7480
end
7581
end

actionpack/test/dispatch/system_testing/driver_test.rb

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -148,15 +148,29 @@ class DriverTest < ActiveSupport::TestCase
148148
end
149149
end
150150

151-
test "preloads browser's driver_path" do
152-
called = false
153-
151+
test "preloads browser's driver_path with DriverFinder if a path isn't already specified" do
154152
original_driver_path = ::Selenium::WebDriver::Chrome::Service.driver_path
155-
::Selenium::WebDriver::Chrome::Service.driver_path = -> { called = true }
153+
::Selenium::WebDriver::Chrome::Service.driver_path = nil
156154

157-
ActionDispatch::SystemTesting::Driver.new(:selenium, screen_size: [1400, 1400], using: :chrome)
155+
# Our stub must return a path to a real executable, otherwise an internal Selenium assertion will fail.
156+
found_executable = RbConfig.ruby
157+
::Selenium::WebDriver::SeleniumManager.stub(:driver_path, found_executable) do
158+
ActionDispatch::SystemTesting::Driver.new(:selenium, screen_size: [1400, 1400], using: :chrome)
159+
end
158160

159-
assert called
161+
assert_equal found_executable, ::Selenium::WebDriver::Chrome::Service.driver_path
162+
ensure
163+
::Selenium::WebDriver::Chrome::Service.driver_path = original_driver_path
164+
end
165+
166+
test "does not overwrite existing driver_path during preload" do
167+
original_driver_path = ::Selenium::WebDriver::Chrome::Service.driver_path
168+
# The driver_path must point to a real executable, otherwise an internal Selenium assertion will fail.
169+
::Selenium::WebDriver::Chrome::Service.driver_path = RbConfig.ruby
170+
171+
assert_no_changes -> { ::Selenium::WebDriver::Chrome::Service.driver_path } do
172+
ActionDispatch::SystemTesting::Driver.new(:selenium, screen_size: [1400, 1400], using: :chrome)
173+
end
160174
ensure
161175
::Selenium::WebDriver::Chrome::Service.driver_path = original_driver_path
162176
end

0 commit comments

Comments
 (0)