| 8 | | CONFIG_PATH = RAILS_ROOT + '/config/selenium_rc.yml' |
| 9 | | DEFAULT_CONFIG = { |
| 10 | | :app => { |
| 11 | | :host => 'localhost', |
| 12 | | :binding => '127.0.0.1', |
| 13 | | :port => 4545, |
| 14 | | :environment => 'test', |
| 15 | | }.freeze, |
| 16 | | :seleniumrc => { |
| 17 | | :host => 'localhost', |
| 18 | | :port => 4444, |
| 19 | | :browsers => %w[ *firefox ].freeze |
| 20 | | }.freeze |
| 21 | | }.freeze |
| 22 | | |
| 23 | | CONFIG = Module.new.module_eval do |
| 24 | | if File.exist?(CONFIG_PATH) |
| 25 | | yaml = File.open(CONFIG_PATH) {|f| |
| 26 | | ERB.new(f.read).result(binding) |
| 27 | | } |
| 28 | | user_config = YAML.load(yaml).symbolize_keys |
| 29 | | DEFAULT_CONFIG.merge \ |
| 30 | | :app => user_config[:app].symbolize_keys, |
| 31 | | :seleniumrc => user_config[:seleniumrc].symbolize_keys |
| 32 | | else |
| 33 | | DEFAULT_CONFIG |
| 34 | | end |
| 35 | | end |
| 36 | | |
| 37 | | module Example |
| 38 | | class SeleniumRcExampleGroup < Spec::Rails::Example::RailsExampleGroup |
| 39 | | before(:all) do |
| 40 | | create_server |
| 41 | | create_proxy |
| 42 | | end |
| 43 | | after(:all) do |
| 44 | | stop_selenium |
| 45 | | stop_server |
| 46 | | Process.waitall |
| 47 | | end |
| 48 | | |
| 49 | | private |
| 50 | | def create_server |
| 51 | | @server_pid = Process.fork do |
| 52 | | config = CONFIG[:app] |
| 53 | | host, port, env = config[:binding], config[:port], config[:environment] |
| 54 | | server = File.join(RAILS_ROOT, *%w[ script server ]) |
| 55 | | exec server, *%W[-b #{host} -p #{port} -e #{env}] |
| 56 | | end |
| 57 | | catch(:server_connected) { |
| 58 | | 5.times do |
| 59 | | throw :server_connected if server_connectable? |
| 60 | | sleep 1 |
| 61 | | end |
| 62 | | raise "can't connect to rails server" |
| 63 | | } |
| 64 | | end |
| 65 | | def create_proxy |
| 66 | | rc_config = CONFIG[:seleniumrc] |
| 67 | | host, port, browser = rc_config[:host], rc_config[:port], rc_config[:browsers].first |
| 68 | | app_url = "http://#{CONFIG[:app][:host]}:#{CONFIG[:app][:port]}" |
| 69 | | @@selenium = Selenium::SeleniumDriver.new(host, port, browser, app_url) |
| 70 | | @@selenium.start |
| 71 | | @@selenium.set_context("test_new") |
| 72 | | end |
| 73 | | |
| 74 | | def stop_selenium |
| 75 | | @@selenium.stop if @@selenium rescue nil |
| 76 | | end |
| 77 | | def stop_server |
| 78 | | Process.kill :INT, @server_pid if @server_pid rescue nil |
| 79 | | end |
| 80 | | |
| 81 | | private |
| 82 | | def server_connectable? |
| 83 | | host = CONFIG[:app][:host] |
| 84 | | TCPSocket.open(CONFIG[:app][:host], CONFIG[:app][:port]) { |
| 85 | | return true |
| 86 | | } |
| 87 | | rescue Errno::ECONNREFUSED |
| 88 | | return false |
| 89 | | end |
| 90 | | |
| 91 | | class << self |
| 92 | | alias senario_body it |
| 93 | | def senario(user_name, description, &block) |
| 94 | | user_creation = lambda { User.new(user_name, class_variable_get(:@@selenium)) } |
| 95 | | senario_body("%s %s" % [user_name, description]) do |
| 96 | | user = user_creation.call |
| 97 | | (class << self; self end).module_eval do |
| 98 | | define_method(:he){user} |
| 99 | | define_method(:she){user} |
| 100 | | define_method(:ve){user} |
| 101 | | define_method(:it){user.last_return_value} |
| 102 | | end |
| 103 | | instance_eval(&block) |
| 104 | | end |
| 105 | | end |
| 106 | | end |
| 107 | | Spec::Example::ExampleGroupFactory.register(:selenium, self) |
| 108 | | end |
| 109 | | |
| 110 | | module SeleniumDriverExtension |
| 111 | | def self.extended(driver) |
| 112 | | class << driver |
| 113 | | %w[ |
| 114 | | get_string get_string_array |
| 115 | | get_number get_number_array |
| 116 | | get_boolean get_boolean_array |
| 117 | | ].each do |original_name| |
| 118 | | eval <<-"EOS" |
| 119 | | def #{original_name}_with_return_value_recording(*args) |
| 120 | | @last_return_value = #{original_name}_without_return_value_recording(*args) |
| 121 | | end |
| 122 | | alias_method_chain :#{original_name}, :return_value_recording |
| 123 | | EOS |
| 124 | | end |
| 125 | | def last_return_value |
| 126 | | @last_return_value |
| 127 | | end |
| 128 | | end |
| 129 | | end |
| 130 | | end |
| 131 | | |
| 132 | | class User |
| 133 | | def initialize(name, proxy) |
| 134 | | @name = name |
| 135 | | @selenium = proxy |
| 136 | | @selenium.extend(SeleniumDriverExtension) |
| 137 | | end |
| 138 | | def selenium; @selenium end |
| 139 | | def method_missing(msg, *args) |
| 140 | | if selenium.respond_to?(msg) |
| 141 | | return selenium.__send__(msg, *args) |
| 142 | | elsif m = /\Ahas_(\w+)\??\Z/.match(msg.to_s) and selenium.respond_to?("get_#{m[1]}") |
| 143 | | if args.empty? |
| 144 | | return !!selenium.__send__("get_#{m[1]}") |
| 145 | | else |
| 146 | | value = selenium.__send__("get_#{m[1]}") |
| 147 | | return value == args.first |
| 148 | | end |
| 149 | | elsif m = /\A(\w+)=\Z/.match(msg.to_s) and selenium.respond_to?("set_#{m[1]}") |
| 150 | | return selenium.__send__("set_#{m[1]}", *args) |
| 151 | | elsif m = /\A(\w+)\??\Z/.match(msg.to_s) and selenium.respond_to?("get_#{m[1]}") |
| 152 | | return selenium.__send__("get_#{m[1]}", *args) |
| 153 | | elsif m = /\A([\w^_]+)e?s(_\w+)?\Z/.match(msg.to_s) and |
| 154 | | selenium.respond_to?("#{m[1]}#{m[2]}") then |
| 155 | | return selenium.__send__("#{m[1]}#{m[2]}", *args) |
| 156 | | end |
| 157 | | |
| 158 | | return super |
| 159 | | end |
| 160 | | |
| 161 | | def opens_and_waits(url, timeout) |
| 162 | | open(url) |
| 163 | | wait_for_page_to_load(timeout) |
| 164 | | end |
| 165 | | def clicks_and_waits(locator, timeout = 3000) |
| 166 | | click(locator) |
| 167 | | wait_for_page_to_load(timeout) |
| 168 | | end |
| 169 | | |
| 170 | | # by http://twitter.com/nazoking/statuses/83616812 |
| 171 | | def waits_for_ajax_loaded(timeout = 3000) |
| 172 | | wait_for_condition(<<-JAVASCRIPT, timeout) |
| 173 | | selenium.page().currentWindow.Ajax.activeRequestCount==0 |
| 174 | | JAVASCRIPT |
| 175 | | end |
| 176 | | end |
| 177 | | end |
| | 6 | VERSION = "$Revision$" |