lib/capybara/webmock.rb in capybara-webmock-0.4.4 vs lib/capybara/webmock.rb in capybara-webmock-0.5.0

- old
+ new

@@ -1,30 +1,63 @@ +require 'open3' +require 'fileutils' +require 'socket' require 'capybara' require 'selenium-webdriver' require 'capybara/webmock/version' require 'capybara/webmock/proxy' +require 'capybara/webmock/proxied_request' module Capybara module Webmock class << self + attr_accessor :port_number, :pid_file, :kill_timeout, :start_timeout - attr_accessor :port_number - def start - log_file = File.join('log', 'test.log') - gem_path = File.dirname(__FILE__) - proxy_file = File.join(gem_path, 'webmock', 'config.ru') - IO.popen("PROXY_PORT_NUMBER=#{port_number} rackup #{proxy_file} >> #{log_file} 2>&1") + if @pid.nil? + kill_old_process + gem_path = File.dirname(__FILE__) + proxy_file = File.join(gem_path, 'webmock', 'config.ru') + stdin, stdout, wait_thr = Open3.popen2e({ "PROXY_PORT_NUMBER" => port_number.to_s }, "rackup", proxy_file) + stdin.close + @stdout = stdout + @pid = wait_thr[:pid] + write_pid_file + wait_for_proxy_start + end + + reset end - def stop - if File.exist?(Capybara::Webmock::Proxy::PID_FILE) - rack_pid = File.read(Capybara::Webmock::Proxy::PID_FILE).to_i - Process.kill('HUP', rack_pid) + def reset + get_output_nonblocking + @output_buf = "" + end + + def proxied_requests + @output_buf += get_output_nonblocking + + matches = @output_buf.sub(/\n[^\n]+\z/, '').split("\n").map do |line| + match = /\A(.+) -> (.+)\Z/.match(line) + next nil unless match + match.captures end + + matches.compact.map{ |raw_referrer, raw_uri| ProxiedRequest.new(raw_referrer, raw_uri) } end + def stop + return if @pid.nil? + + @stdout.close + kill_process(@pid) + remove_pid_file + + @pid = nil + @stdout = nil + end + def firefox_profile proxy_host = '127.0.0.1' profile = ::Selenium::WebDriver::Firefox::Profile.new profile["network.proxy.type"] = 1 profile["network.proxy.http"] = proxy_host @@ -33,29 +66,106 @@ profile["network.proxy.ssl_port"] = port_number profile end def chrome_options - ::Selenium::WebDriver::Chrome::Options.new.tap do |options| - options.add_argument("--proxy-server=127.0.0.1:#{port_number}") - end + { args: "proxy-server=127.0.0.1:#{port_number}" } end def phantomjs_options ["--proxy=127.0.0.1:#{port_number}"] end + + private + + def wait_for_proxy_start + connected = false + (1..start_timeout).each do + begin + Socket.tcp("127.0.0.1", port_number, connect_timeout: 1) {} + connected = true + break + rescue => e + sleep 1 + end + end + + unless connected + raise "Unable to connect to capybara-webmock proxy on #{port_number}" + end + end + + def get_output_nonblocking + buf = "" + + while true + begin + output = @stdout.read_nonblock(1024) + break if output == "" + buf += output + rescue IO::WaitReadable + break + end + end + + buf + end + + def kill_old_process + return unless File.exists?(pid_file) + old_pid = File.read(pid_file).to_i + kill_process(old_pid) if old_pid > 1 + remove_pid_file + end + + def kill_process(pid) + Process.kill('HUP', pid) if process_alive?(pid) + + (1..kill_timeout).each do + sleep(1) if process_alive?(pid) + end + + Process.kill('KILL', pid) if process_alive?(pid) + + (1..kill_timeout).each do + sleep(1) if process_alive?(pid) + end + + if process_alive?(pid) + raise "Unable to kill capybara-webmock process with PID #{pid}" + end + end + + def process_alive?(pid) + !!Process.kill(0, pid) rescue false + end + + def write_pid_file + raise "Pid file #{pid_file} already exists" if File.exists?(pid_file) + FileUtils.mkdir_p(File.dirname(pid_file)) + File.write(pid_file, @pid.to_s) + end + + def remove_pid_file + File.delete(pid_file) if File.exists?(pid_file) + end end end end Capybara::Webmock.port_number ||= 9292 +Capybara::Webmock.pid_file ||= File.join('tmp', 'pids', 'capybara_webmock_proxy.pid') +Capybara::Webmock.kill_timeout ||= 5 +Capybara::Webmock.start_timeout ||= 30 Capybara.register_driver :capybara_webmock do |app| Capybara::Selenium::Driver.new(app, browser: :firefox, profile: Capybara::Webmock.firefox_profile) end Capybara.register_driver :capybara_webmock_chrome do |app| - Capybara::Selenium::Driver.new(app, browser: :chrome, options: Capybara::Webmock.chrome_options) + Capybara::Selenium::Driver.new(app, browser: :chrome, desired_capabilities: { + chromeOptions: Capybara::Webmock.chrome_options + }) end Capybara.register_driver :capybara_webmock_poltergeist do |app| Capybara::Poltergeist::Driver.new(app, phantomjs_options: Capybara::Webmock.phantomjs_options) end