polonium 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. data/CHANGES +10 -0
  2. data/README +38 -0
  3. data/Rakefile +68 -0
  4. data/lib/polonium.rb +28 -0
  5. data/lib/polonium/configuration.rb +271 -0
  6. data/lib/polonium/driver.rb +155 -0
  7. data/lib/polonium/dsl/selenium_dsl.rb +33 -0
  8. data/lib/polonium/dsl/test_unit_dsl.rb +58 -0
  9. data/lib/polonium/element.rb +207 -0
  10. data/lib/polonium/extensions/module.rb +8 -0
  11. data/lib/polonium/extensions/testrunnermediator.rb +22 -0
  12. data/lib/polonium/mongrel_selenium_server_runner.rb +37 -0
  13. data/lib/polonium/page.rb +84 -0
  14. data/lib/polonium/selenium_helper.rb +5 -0
  15. data/lib/polonium/server_runner.rb +33 -0
  16. data/lib/polonium/tasks/selenium_test_task.rb +21 -0
  17. data/lib/polonium/test_case.rb +93 -0
  18. data/lib/polonium/wait_for.rb +40 -0
  19. data/lib/polonium/webrick_selenium_server_runner.rb +33 -0
  20. data/spec/polonium/extensions/module_spec.rb +29 -0
  21. data/spec/polonium/extensions/testrunnermediator_spec.rb +19 -0
  22. data/spec/polonium/mongrel_selenium_server_runner_spec.rb +35 -0
  23. data/spec/polonium/selenium_configuration_spec.rb +359 -0
  24. data/spec/polonium/selenium_driver_spec.rb +104 -0
  25. data/spec/polonium/selenium_element_spec.rb +551 -0
  26. data/spec/polonium/selenium_page_spec.rb +249 -0
  27. data/spec/polonium/selenium_server_runner_spec.rb +42 -0
  28. data/spec/polonium/selenium_test_case_class_method_spec.rb +41 -0
  29. data/spec/polonium/selenium_test_case_spec.rb +870 -0
  30. data/spec/polonium/selenium_test_case_spec_helper.rb +16 -0
  31. data/spec/polonium/webrick_selenium_server_runner_spec.rb +117 -0
  32. data/spec/spec_helper.rb +63 -0
  33. data/spec/spec_suite.rb +6 -0
  34. metadata +82 -0
data/CHANGES ADDED
@@ -0,0 +1,10 @@
1
+ * Renamed to Polonium
2
+ * Methods renamed to match Test::Unit assertions
3
+ * Page and Element delegate to Driver [Paired with Jeff Whitmore]
4
+
5
+ 0.0.2
6
+ * Supports multiple sessions by instantiating multiple SeleniumDrivers
7
+ * Added SeleniumDriver which holds most of the polling methods
8
+
9
+ 0.0.1
10
+ * Initial Release. More documentation to come.
data/README ADDED
@@ -0,0 +1,38 @@
1
+ == Introduction
2
+
3
+ Welcome to Polonium!
4
+
5
+ This plugin is designed to let you use Polonium to write Selenium tests in Ruby, using a simple series of
6
+ Rake tasks.
7
+
8
+ == Installation
9
+
10
+ The current version of this plugin can be found at: http://rubyforge.org/var/svn/pivotalrb/polonium/trunk
11
+
12
+ You may install the plugin with the following command:
13
+
14
+ script/plugin install svn://rubyforge.org/var/svn/pivotalrb/polonium/trunk
15
+
16
+ == Usage
17
+ The polonium plugin assumes you have a test/selenium directory with a selenium_suite.rb file in
18
+ it. A sample selenium_suite.rb and selenium_helper.rb can be copied into your test/selenium directory.
19
+
20
+ You'll get the following tasks:
21
+ - selenium:test - starts a Selenium RC server, runs selenium tests, and shuts the server down
22
+ when they're done
23
+
24
+ and lower level tasks as well:
25
+ - selenium:server starts up a Selenium RC server
26
+ - selenium:test_with_server_started runs Selenium tests off of an existing server
27
+
28
+ == Future Enhancements
29
+
30
+ There are a few things we'd like to improve, but we wanted to get this out now. Check the tracker in the pivotal.rb RubyForge project for details.
31
+
32
+ == License
33
+
34
+ Polonium is distributed under the MIT license. Copyright © 2007 Pivotal Labs, Inc.
35
+
36
+ == Contributing
37
+
38
+ Contributions to this plugin are welcome. Contributions should be accompanied by tests. See http://pivotalrb.rubyforge.org for more details.
data/Rakefile ADDED
@@ -0,0 +1,68 @@
1
+ require "rake"
2
+ require 'rake/gempackagetask'
3
+ require 'rake/contrib/rubyforgepublisher'
4
+ require 'rake/clean'
5
+ require 'rake/testtask'
6
+ require 'rake/rdoctask'
7
+
8
+ desc "Runs the Rspec suite"
9
+ task(:default) do
10
+ run_suite
11
+ end
12
+
13
+ desc "Runs the Rspec suite"
14
+ task(:spec) do
15
+ run_suite
16
+ end
17
+
18
+ desc "Copies the trunk to a tag with the name of the current release"
19
+ task(:tag_release) do
20
+ tag_release
21
+ end
22
+
23
+ def run_suite
24
+ dir = File.dirname(__FILE__)
25
+ system("ruby #{dir}/spec/spec_suite.rb") || raise("Example Suite failed")
26
+ end
27
+
28
+ PKG_NAME = "polonium"
29
+ PKG_VERSION = "0.1.0"
30
+ PKG_FILES = FileList[
31
+ '[A-Z]*',
32
+ '*.rb',
33
+ 'lib/**/*.rb',
34
+ 'spec/**/*.rb'
35
+ ]
36
+
37
+ spec = Gem::Specification.new do |s|
38
+ s.name = PKG_NAME
39
+ s.version = PKG_VERSION
40
+ s.summary = "Selenium RC with enhanced assertions that also runs your rails app."
41
+ s.test_files = "spec/spec_suite.rb"
42
+ s.description = s.summary
43
+
44
+ s.files = PKG_FILES.to_a
45
+ s.require_path = 'lib'
46
+
47
+ s.has_rdoc = true
48
+ s.extra_rdoc_files = [ "README", "CHANGES" ]
49
+ s.rdoc_options = ["--main", "README", "--inline-source", "--line-numbers"]
50
+
51
+ s.test_files = Dir.glob('spec/*_spec.rb')
52
+ s.require_path = 'lib'
53
+ s.author = "Pivotal Labs"
54
+ s.email = "opensource@pivotallabs.com"
55
+ s.homepage = "http://pivotallabs.com"
56
+ s.rubyforge_project = "pivotalrb"
57
+ end
58
+
59
+ Rake::GemPackageTask.new(spec) do |pkg|
60
+ pkg.need_zip = true
61
+ pkg.need_tar = true
62
+ end
63
+
64
+ def tag_release
65
+ dashed_version = PKG_VERSION.gsub('.', '-')
66
+ svn_user_prefix = "#{ENV['SVN_USER']}@" if ENV['SVN_USER']
67
+ `svn cp svn+ssh://#{svn_user_prefix}rubyforge.org/var/svn/pivotalrb/polonium/trunk svn+ssh://#{svn_user_prefix}rubyforge.org/var/svn/pivotalrb/polonium/tags/REL-#{dashed_version} -m 'Version #{PKG_VERSION}'`
68
+ end
data/lib/polonium.rb ADDED
@@ -0,0 +1,28 @@
1
+ require 'socket'
2
+ require 'logger'
3
+ require "stringio"
4
+
5
+ require "active_record"
6
+
7
+ require 'net/http'
8
+ require 'test/unit'
9
+ require 'test/unit/testresult'
10
+ require 'test/unit/ui/testrunnermediator'
11
+
12
+ require "selenium"
13
+ require "polonium/extensions/module"
14
+ require "polonium/extensions/testrunnermediator"
15
+ require "polonium/wait_for"
16
+ require "polonium/driver"
17
+ require "polonium/server_runner"
18
+ require "polonium/mongrel_selenium_server_runner"
19
+ require "polonium/webrick_selenium_server_runner"
20
+ require "polonium/dsl/selenium_dsl"
21
+ require "polonium/dsl/test_unit_dsl"
22
+ require "polonium/configuration"
23
+ require "polonium/page"
24
+ require "polonium/element"
25
+ require "polonium/test_case"
26
+ require "polonium/tasks/selenium_test_task"
27
+
28
+ require 'webrick_server' if self.class.const_defined? :RAILS_ROOT
@@ -0,0 +1,271 @@
1
+ module Polonium
2
+ # The configuration interface. This Configuration acts as a singleton to a SeleniumContext.
3
+ # You can access the Configuration object by calling
4
+ # Polonium::Configuration.instance
5
+ class Configuration
6
+ module BrowserMode
7
+ Suite = "suite" unless const_defined? :Suite
8
+ Test = "test" unless const_defined? :Test
9
+ end
10
+ FIREFOX = "firefox" unless const_defined? :FIREFOX
11
+ IEXPLORE = "iexplore" unless const_defined? :IEXPLORE
12
+
13
+ class << self
14
+ # The instance of the Singleton Configuration. On its initial call, the initial configuration is set.
15
+ # The initial configuration is based on Environment variables and defaults.
16
+ # The environment variables are:
17
+ # * RAILS_ENV - The Rails environment (defaults: test)
18
+ # * selenium_server_host - The host name for the Selenium RC server (default: localhost)
19
+ # * selenium_server_port - The port for the Selenium RC server (default: 4444)
20
+ # * webrick_host - The host name that the application server will start under (default: localhost)
21
+ # * webrick_port - The port that the application server will start under (default: 4000)
22
+ # * app_server_engine - The type of server the application will be run with (webrick or mongrel)
23
+ # * internal_app_server_host - The host name for the Application server that the Browser will access (default: localhost)
24
+ # * internal_app_server_host - The port for the Application server that the Browser will access (default: 4000)
25
+ # * keep_browser_open_on_failure - If there is a failure in the test suite, keep the browser window open (default: false)
26
+ # * verify_remote_app_server_is_running - Raise an exception if the Application Server is not running (default: true)
27
+ def instance
28
+ return @instance if @instance
29
+ @instance = new
30
+ @instance.env = ENV
31
+
32
+ @instance.browser = FIREFOX
33
+ @instance.selenium_server_host = "localhost" # address of selenium RC server (java)
34
+ @instance.selenium_server_port = 4444
35
+ @instance.app_server_engine = :webrick
36
+ @instance.internal_app_server_host = "0.0.0.0" # internal address of app server (webrick or mongrel)
37
+ @instance.internal_app_server_port = 4000
38
+ @instance.external_app_server_host = "localhost" # external address of app server (webrick or mongrel)
39
+ @instance.external_app_server_port = 4000
40
+ @instance.server_engine = :webrick
41
+ @instance.keep_browser_open_on_failure = false
42
+ @instance.browser_mode = BrowserMode::Suite
43
+ @instance.verify_remote_app_server_is_running = true
44
+
45
+ establish_environment
46
+ @instance
47
+ end
48
+ attr_writer :instance
49
+
50
+ private
51
+ def establish_environment
52
+ @instance.rails_env = env['RAILS_ENV'] if env.include?('RAILS_ENV')
53
+ @instance.rails_root = Object.const_get(:RAILS_ROOT) if Object.const_defined?(:RAILS_ROOT)
54
+ [
55
+ 'selenium_server_host',
56
+ 'selenium_server_port',
57
+ 'internal_app_server_port',
58
+ 'internal_app_server_host',
59
+ 'app_server_engine',
60
+ 'external_app_server_host',
61
+ 'external_app_server_port'
62
+ ].each do |env_key|
63
+ if env.include?(env_key)
64
+ @instance.send("#{env_key}=", env[env_key])
65
+ end
66
+ end
67
+ [
68
+ 'keep_browser_open_on_failure',
69
+ 'verify_remote_app_server_is_running'
70
+ ].each do |env_key|
71
+ if env.include?(env_key)
72
+ @instance.send("#{env_key}=", env[env_key].to_s != false.to_s)
73
+ end
74
+ end
75
+ @instance.browser = env['browser'] if env.include?('browser')
76
+ end
77
+
78
+ def env
79
+ @instance.env
80
+ end
81
+ end
82
+
83
+ attr_accessor :configuration,
84
+ :env,
85
+ :rails_env,
86
+ :rails_root,
87
+ :browser,
88
+ :driver,
89
+ :browser_mode,
90
+ :selenium_server_host,
91
+ :selenium_server_port,
92
+ :app_server_engine,
93
+ :internal_app_server_host,
94
+ :internal_app_server_port,
95
+ :external_app_server_host,
96
+ :external_app_server_port,
97
+ :server_engine,
98
+ :keep_browser_open_on_failure,
99
+ :verify_remote_app_server_is_running,
100
+ :app_server_initialization
101
+
102
+ def initialize
103
+ self.verify_remote_app_server_is_running = true
104
+ @after_driver_started_listeners = []
105
+ @app_server_initialization = proc {}
106
+ @failure_has_occurred = false
107
+ end
108
+
109
+ # A callback hook that gets run after the Selenese Interpreter is started.
110
+ def after_driver_started(&block)
111
+ @after_driver_started_listeners << block
112
+ end
113
+
114
+ # Notify all after_driver_started callbacks.
115
+ def notify_after_driver_started(driver)
116
+ for listener in @after_driver_started_listeners
117
+ listener.call(driver)
118
+ end
119
+ end
120
+
121
+ # The browser formatted for the Selenese driver.
122
+ def formatted_browser
123
+ return "*#{@browser}"
124
+ end
125
+
126
+ # Has a failure occurred in the tests?
127
+ def failure_has_occurred?
128
+ @failure_has_occurred = true
129
+ end
130
+
131
+ # The http host name and port to be entered into the browser address bar
132
+ def browser_url
133
+ "http://#{external_app_server_host}:#{external_app_server_port}"
134
+ end
135
+
136
+ # The root directory (public) of the Rails application
137
+ def server_root
138
+ File.expand_path("#{rails_root}/public/")
139
+ end
140
+
141
+ # Sets the Test Suite to open a new browser instance for each TestCase
142
+ def test_browser_mode
143
+ @browser_mode = Configuration::BrowserMode::Test
144
+ end
145
+
146
+ # Are we going to open a new browser instance for each TestCase?
147
+ def test_browser_mode?
148
+ @browser_mode == Configuration::BrowserMode::Test
149
+ end
150
+
151
+ # Sets the Test Suite to use one browser instance
152
+ def suite_browser_mode
153
+ @browser_mode = Configuration::BrowserMode::Suite
154
+ end
155
+
156
+ # Does the Test Suite to use one browser instance?
157
+ def suite_browser_mode?
158
+ @browser_mode == Configuration::BrowserMode::Suite
159
+ end
160
+
161
+ # The Driver object, which sublcasses the Driver provided by the Selenium RC (http://openqa.org/selenium-rc/) project.
162
+ def driver
163
+ return nil unless suite_browser_mode?
164
+ @driver ||= create_and_initialize_driver
165
+ end
166
+
167
+ def stop_driver_if_necessary(suite_passed) # nodoc
168
+ failure_has_occurred unless suite_passed
169
+ if @driver && stop_driver?(suite_passed)
170
+ @driver.stop
171
+ @driver = nil
172
+ end
173
+ end
174
+
175
+ def stop_driver?(passed) # nodoc
176
+ return true if passed
177
+ return !keep_browser_open_on_failure
178
+ end
179
+
180
+ def create_and_initialize_driver # nodoc
181
+ driver = create_driver
182
+ driver.start
183
+ notify_after_driver_started(driver)
184
+ driver
185
+ end
186
+
187
+ def create_driver # nodoc
188
+ return ::Polonium::Driver.new(
189
+ selenium_server_host,
190
+ selenium_server_port,
191
+ formatted_browser,
192
+ browser_url,
193
+ 15000
194
+ )
195
+ end
196
+
197
+ def create_server_runner # nodoc
198
+ case @app_server_engine.to_sym
199
+ when :mongrel
200
+ create_mongrel_runner
201
+ when :webrick
202
+ create_webrick_runner
203
+ else
204
+ raise "Invalid server type: #{selenium_configuration.app_server_type}"
205
+ end
206
+ end
207
+
208
+ def create_webrick_runner # nodoc
209
+ require 'webrick_server'
210
+ runner = WebrickSeleniumServerRunner.new
211
+ runner.configuration = self
212
+ runner.thread_class = Thread
213
+ runner.socket = Socket
214
+ runner.dispatch_servlet = DispatchServlet
215
+ runner.environment_path = File.expand_path("#{@rails_root}/config/environment")
216
+ runner
217
+ end
218
+
219
+ def create_webrick_server # nodoc
220
+ WEBrick::HTTPServer.new({
221
+ :Port => @internal_app_server_port,
222
+ :BindAddress => @internal_app_server_host,
223
+ :ServerType => WEBrick::SimpleServer,
224
+ :MimeTypes => WEBrick::HTTPUtils::DefaultMimeTypes,
225
+ :Logger => new_logger,
226
+ :AccessLog => []
227
+ })
228
+ end
229
+
230
+ def new_logger
231
+ Logger.new(StringIO.new)
232
+ end
233
+
234
+ def create_mongrel_runner # nodoc
235
+ runner = MongrelSeleniumServerRunner.new
236
+ runner.configuration = self
237
+ runner.thread_class = Thread
238
+ runner
239
+ end
240
+
241
+ def create_mongrel_configurator # nodoc
242
+ dir = File.dirname(__FILE__)
243
+ require 'mongrel/rails'
244
+ settings = {
245
+ :host => internal_app_server_host,
246
+ :port => internal_app_server_port,
247
+ :cwd => @rails_root,
248
+ :log_file => "#{@rails_root}/log/mongrel.log",
249
+ :pid_file => "#{@rails_root}/log/mongrel.pid",
250
+ :environment => @rails_env,
251
+ :docroot => "#{@rails_root}/public",
252
+ :mime_map => nil,
253
+ :daemon => false,
254
+ :debug => false,
255
+ :includes => ["mongrel"],
256
+ :config_script => nil
257
+ }
258
+
259
+ configurator = Mongrel::Rails::RailsConfigurator.new(settings) do
260
+ log "Starting Mongrel in #{defaults[:environment]} mode at #{defaults[:host]}:#{defaults[:port]}"
261
+ end
262
+ configurator
263
+ end
264
+
265
+ protected
266
+ # Sets the failure state to true
267
+ def failure_has_occurred
268
+ @failure_has_occurred = true
269
+ end
270
+ end
271
+ end
@@ -0,0 +1,155 @@
1
+ module Polonium
2
+ class Driver < ::Selenium::SeleniumDriver
3
+ include WaitFor
4
+ attr_reader :server_host, :server_port
5
+
6
+ def browser_start_command
7
+ @browserStartCommand
8
+ end
9
+
10
+ def browser_url
11
+ @browserURL
12
+ end
13
+
14
+ def timeout_in_milliseconds
15
+ @timeout
16
+ end
17
+
18
+ def insert_javascript_file(uri)
19
+ js = <<-USEREXTENSIONS
20
+ var headTag = document.getElementsByTagName("head").item(0);
21
+ var scriptTag = document.createElement("script");
22
+ scriptTag.src = "#{uri}";
23
+ headTag.appendChild( scriptTag );
24
+ USEREXTENSIONS
25
+ get_eval(js)
26
+ end
27
+
28
+ def insert_user_extensions
29
+ insert_javascript_file("/selenium/user-extensions.js")
30
+ end
31
+
32
+ def element(locator)
33
+ Element.new(self, locator)
34
+ end
35
+
36
+ def page
37
+ Page.new(self)
38
+ end
39
+
40
+ #--------- Commands
41
+ alias_method :confirm, :get_confirmation
42
+
43
+ # Type text into a page element
44
+ def type(locator, value)
45
+ wait_for_is_element_present(locator)
46
+ super
47
+ end
48
+
49
+ # Reload the current page that the browser is on.
50
+ def reload
51
+ get_eval("selenium.browserbot.getCurrentWindow().location.reload()")
52
+ end
53
+
54
+ def click(locator)
55
+ wait_for_is_element_present(locator)
56
+ super
57
+ end
58
+ alias_method :wait_for_and_click, :click
59
+
60
+ def select(select_locator, option_locator)
61
+ wait_for_is_element_present(select_locator)
62
+ super
63
+ end
64
+
65
+ # Click a link and wait for the page to load.
66
+ def click_and_wait(locator, wait_for = default_timeout)
67
+ click locator
68
+ wait_for_page_to_load(wait_for)
69
+ end
70
+ alias_method :click_and_wait_for_page_to_load, :click_and_wait
71
+
72
+ # Click the back button and wait for the page to load.
73
+ def go_back_and_wait
74
+ go_back
75
+ wait_for_page_to_load
76
+ end
77
+
78
+ # Open the home page of the Application and wait for the page to load.
79
+ def open(url)
80
+ super
81
+ wait_for_page_to_load
82
+ end
83
+ alias_method :open_and_wait, :open
84
+
85
+ # Get the inner html of the located element.
86
+ def get_inner_html(locator)
87
+ get_eval(inner_html_js(locator))
88
+ end
89
+
90
+ # Does the element at locator contain the text?
91
+ def element_contains_text(locator, text)
92
+ is_element_present(locator) && element(locator).contains?(text)
93
+ end
94
+
95
+ # Does the element at locator not contain the text?
96
+ def element_does_not_contain_text(locator, text)
97
+ return true unless is_element_present(locator)
98
+ return !element(locator).contains?(text)
99
+ end
100
+
101
+ #----- Waiting for conditions
102
+ def wait_for_is_element_present(locator, params={})
103
+ params = {
104
+ :message => "Expected element '#{locator}' to be present, but it was not"
105
+ }.merge(params)
106
+ wait_for(params) do
107
+ is_element_present(locator)
108
+ end
109
+ end
110
+
111
+ def wait_for_is_element_not_present(locator, params={})
112
+ params = {
113
+ :message => "Expected element '#{locator}' to be absent, but it was not"
114
+ }.merge(params)
115
+ wait_for(:message => params[:message]) do
116
+ !is_element_present(locator)
117
+ end
118
+ end
119
+
120
+ def wait_for_page_to_load(timeout=default_timeout)
121
+ super
122
+ if get_title.include?("Exception caught")
123
+ flunk "We got a new page, but it was an application exception page.\n\n#{get_html_source}"
124
+ end
125
+ end
126
+
127
+ def wait_for_element_to_contain(locator, text, message=nil, timeout=default_wait_for_time)
128
+ wait_for(:message => message, :timeout => timeout) do
129
+ element_contains_text(locator, text)
130
+ end
131
+ end
132
+ alias_method :wait_for_element_to_contain_text, :wait_for_element_to_contain
133
+
134
+ # Open the log window on the browser. This is useful to diagnose issues with Selenium Core.
135
+ def show_log(log_level = "debug")
136
+ get_eval "LOG.setLogLevelThreshold('#{log_level}')"
137
+ end
138
+
139
+ # Slow down each Selenese step after this method is called.
140
+ def slow_mode
141
+ get_eval "slowMode = true"
142
+ get_eval 'window.document.getElementsByName("FASTMODE")[0].checked = true'
143
+ end
144
+
145
+ # Speeds up each Selenese step to normal speed after this method is called.
146
+ def fast_mode
147
+ get_eval "slowMode = false"
148
+ get_eval 'window.document.getElementsByName("FASTMODE")[0].checked = false'
149
+ end
150
+
151
+ def inner_html_js(locator)
152
+ %Q|this.page().findElement("#{locator}").innerHTML|
153
+ end
154
+ end
155
+ end