When Selenium stops chromedriver, it sends SIGTERM and polls until the process exits or a timeout passes, and only then sends SIGKILL.
class Selenium::WebDriver::ChildProcess
def poll_for_exit
end_time = Time.now + timeout
sleep POLL_INTERVAL until exited? || Time.now > end_time
end
end
Under Timecop.freeze, Time.now never reaches end_time. So a chromedriver that doesn't react to SIGTERM (e.g. a hung browser) is never killed, and the test process hangs until your CI job times out.
Use this monkey patch to fix the polling mechanism whenever browsers are stopped inside a frozen example:
# spec/support/selenium_child_process.rb
# Selenium waits for its driver processes to exit on the wall clock, which stands still while Timecop freezes the
# time. A chromedriver that doesn't exit would hang the run instead of being killed after the timeout.
module SeleniumChildProcessRealTime
def poll_for_exit(timeout)
Timecop.return { super }
end
end
Selenium::WebDriver::ChildProcess.prepend(SeleniumChildProcessRealTime)
Posted by Michael Leimstädtner to makandra dev (2026-11-05 13:21)