polonium 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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