Removed superfluous 'tracks' directory at the root of the repository.

Testing commits to github.
This commit is contained in:
bsag 2008-05-20 21:28:26 +01:00
parent 6a42901514
commit 4cbf5a34d3
2269 changed files with 0 additions and 0 deletions

View file

@ -0,0 +1,210 @@
require File.dirname(__FILE__) + '/paths'
require File.dirname(__FILE__) + '/../selenium_on_rails_config'
require 'net/http'
require 'tempfile'
def c(var, default = nil) SeleniumOnRailsConfig.get var, default end
def c_b(var, default = nil) SeleniumOnRailsConfig.get(var, default) { yield } end
BROWSERS = c :browsers, {}
REUSE_EXISTING_SERVER = c :reuse_existing_server, true
START_SERVER = c :start_server, false #TODO can't get it to work reliably on Windows, perhaps it's just on my computer, but I leave it off by default for now
HOST = c :host, 'localhost'
PORTS = c(:port_start, 3000)..c(:port_end, 3005)
TEST_RUNNER_URL = c :test_runner_url, '/selenium/TestRunner.html'
MAX_BROWSER_DURATION = c :max_browser_duration, 2*60
MULTI_WINDOW = c :multi_window, false
SERVER_COMMAND = c_b :server_command do
server_path = File.expand_path(File.dirname(__FILE__) + '/../../../../../script/server')
if RUBY_PLATFORM =~ /mswin/
"ruby #{server_path} -p %d -e test > NUL 2>&1"
else
# don't use redirects to /dev/nul since it makes the fork return wrong pid
# see UnixSubProcess
"#{server_path} -p %d -e test"
end
end
module SeleniumOnRails
class AcceptanceTestRunner
include SeleniumOnRails::Paths
def run
raise 'no browser specified, edit/create config.yml' if BROWSERS.empty?
start_server
has_error = false
begin
BROWSERS.each_pair do |browser, path|
log_file = start_browser browser, path
wait_for_completion log_file
stop_browser
result = YAML::load_file log_file
print_result result
has_error ||= result['numTestFailures'].to_i > 0
File.delete log_file unless has_error
end
rescue
stop_server
raise
end
stop_server
raise 'Test failures' if has_error
end
private
def start_server
PORTS.each do |p|
@port = p
case server_check
when :success
return if REUSE_EXISTING_SERVER
next
when Fixnum
next
when :no_response
next unless START_SERVER
do_start_server
return
end
end
raise START_SERVER ? 'failed to start server': 'failed to find existing server, run script/server -e test'
end
def do_start_server
puts 'Starting server'
@server = start_subprocess(format(SERVER_COMMAND, @port))
while true
print '.'
r = server_check
if r == :success
puts
return
end
raise "server returned error: #{r}" if r.instance_of? Fixnum
sleep 3
end
end
def server_check
begin
res = Net::HTTP.get_response HOST, TEST_RUNNER_URL, @port
return :success if (200..399).include? res.code.to_i
return res.code.to_i
rescue Errno::ECONNREFUSED
return :no_response
end
end
def stop_server
return unless defined? @server
puts
@server.stop 'server'
end
def start_browser browser, path
puts
puts "Starting #{browser}"
log = log_file browser
command = "\"#{path}\" \"http://#{HOST}:#{@port}#{TEST_RUNNER_URL}?test=tests&auto=true&resultsUrl=postResults/#{log}&multiWindow=#{MULTI_WINDOW}\""
@browser = start_subprocess command
log_path log
end
def stop_browser
@browser.stop 'browser'
end
def start_subprocess command
if RUBY_PLATFORM =~ /mswin/
SeleniumOnRails::AcceptanceTestRunner::Win32SubProcess.new command
elsif RUBY_PLATFORM =~ /darwin/i && command =~ /safari/i
SeleniumOnRails::AcceptanceTestRunner::SafariSubProcess.new command
else
SeleniumOnRails::AcceptanceTestRunner::UnixSubProcess.new command
end
end
def log_file browser
(0..100).each do |i|
name = browser + (i==0 ? '' : "(#{i})") + '.yml'
return name unless File.exist?(log_path(name))
end
raise 'there are way too many files in the log directory...'
end
def wait_for_completion log_file
duration = 0
while true
raise 'browser takes too long' if duration > MAX_BROWSER_DURATION
print '.'
break if File.exist? log_file
sleep 5
duration += 5
end
puts
end
def print_result result
puts "Finished in #{result['totalTime']} seconds."
puts
puts "#{result['numTestPasses']} tests passed, #{result['numTestFailures']} tests failed"
puts "(Results stored in '#{result['resultDir']}')" if result['resultDir']
end
end
end
class SeleniumOnRails::AcceptanceTestRunner::SubProcess
def stop what
begin
puts "Stopping #{what} (pid=#{@pid}) ..."
Process.kill 9, @pid
rescue Errno::EPERM #such as the process is already closed (tabbed browser)
end
end
end
class SeleniumOnRails::AcceptanceTestRunner::Win32SubProcess < SeleniumOnRails::AcceptanceTestRunner::SubProcess
def initialize command
require 'win32/open3' #win32-open3 http://raa.ruby-lang.org/project/win32-open3/
puts command
input, output, error, @pid = Open4.popen4 command, 't', true
end
end
class SeleniumOnRails::AcceptanceTestRunner::UnixSubProcess < SeleniumOnRails::AcceptanceTestRunner::SubProcess
def initialize command
puts command
@pid = fork do
# Since we can't use shell redirects without screwing
# up the pid, we'll reopen stdin and stdout instead
# to get the same effect.
[STDOUT,STDERR].each {|f| f.reopen '/dev/null', 'w' }
exec command
end
end
end
# The path to Safari should look like this: /Applications/Safari.app/Contents/MacOS/Safari
class SeleniumOnRails::AcceptanceTestRunner::SafariSubProcess < SeleniumOnRails::AcceptanceTestRunner::UnixSubProcess
def initialize command
f = File.open(Tempfile.new('selenium-on-rails').path, 'w')
f.puts <<-HTML
<html>
<head>
<script type="text/javascript" charset="utf-8">
window.location.href = #{command.split.last};
</script>
</head>
<body></body>
</html>
HTML
f.close
super "#{command.split.first} #{f.path}"
end
end

View file

@ -0,0 +1,54 @@
require 'active_record/fixtures'
module SeleniumOnRails::FixtureLoader
include SeleniumOnRails::Paths
def available_fixtures
fixtures = {}
path = fixtures_path + '/'
files = Dir["#{path}**/*.{yml,csv}"]
files.each do |file|
rel_path = file.sub(path, '')
next if skip_file? rel_path
fixture_set = File.dirname(rel_path)
fixture_set = '' if fixture_set == '.'
fixture = rel_path.sub /\.[^.]*$/, ''
fixtures[fixture_set] ||= []
fixtures[fixture_set] << fixture
end
fixtures
end
def load_fixtures fixtures_param
available = nil
fixtures = fixtures_param.split(/\s*,\s*/).collect do |f|
fixture_set = File.dirname f
fixture_set = '' if fixture_set == '.'
fixture = File.basename f
if fixture == 'all'
available ||= available_fixtures
available[fixture_set]
else
f
end
end
fixtures.flatten!
fixtures.reject! {|f| f.blank? }
if fixtures.any?
Fixtures.create_fixtures fixtures_path, fixtures
end
fixtures
end
def clear_tables tables
table_names = tables.split /\s*,\s*/
connection = ActiveRecord::Base.connection
table_names.each do |table|
connection.execute "DELETE FROM #{table}"
end
table_names
end
end

View file

@ -0,0 +1,38 @@
# Provides partials support to test cases so they can include other partial test
# cases.
#
# The partial's commands are returned as html table rows.
module SeleniumOnRails::PartialsSupport
include SeleniumOnRails::Paths
# Overrides where the partial is searched for, and returns only the command table rows.
def render_partial partial_path = default_template_name, object = nil, local_assigns = nil, status = nil
pattern = partial_pattern partial_path
filename = Dir[pattern].first
raise "Partial '#{partial_path}' cannot be found! (Looking for file: '#{pattern}')" unless filename
partial = render :file => filename, :use_full_path => false, :locals => local_assigns
extract_commands_from_partial partial
end
# Extracts the commands from a partial. The partial must contain a html table
# and the first row is ignored since it cannot contain a command.
def extract_commands_from_partial partial
partial = partial.match(/.*<table>.*?<tr>.*?<\/tr>(.*?)<\/table>/im)[1]
raise "Partial '#{name}' doesn't contain any table" unless partial
partial
end
private
# Generates the file pattern from the provided partial path.
# The starting _ and file extension don't have too be provided.
def partial_pattern partial_path
path = partial_path.split '/'
filename = path.delete_at(-1)
filename = '_' + filename unless filename.starts_with? '_'
filename << '.*' unless filename.include? '.'
pattern = selenium_tests_path + '/'
pattern << path.join('/') + '/' if path
pattern << filename
end
end

View file

@ -0,0 +1,61 @@
module SeleniumOnRails
module Paths
def selenium_path
@@selenium_path ||= find_selenium_path
@@selenium_path
end
def selenium_tests_path
File.expand_path(File.join(RAILS_ROOT, 'test/selenium'))
end
def view_path view
File.expand_path(File.dirname(__FILE__) + '/../views/' + view)
end
# Returns the path to the layout template. The path is relative in relation
# to the app/views/ directory since Rails doesn't support absolute paths
# to layout templates.
def layout_path
rails_root = Pathname.new File.expand_path(File.join(RAILS_ROOT, 'app/views'))
view_path = Pathname.new view_path('layout')
view_path.relative_path_from(rails_root).to_s
end
def fixtures_path
File.expand_path File.join(RAILS_ROOT, 'test/fixtures')
end
def log_path log_file
File.expand_path(File.dirname(__FILE__) + '/../../log/' + File.basename(log_file))
end
def skip_file? file
file.split('/').each do |f|
return true if f.upcase == 'CVS' or f.starts_with?('.') or f.ends_with?('~') or f.starts_with?('_')
end
false
end
private
def find_selenium_path
sel_dirs = SeleniumOnRailsConfig.get :selenium_path do
ds = [File.expand_path(File.join(RAILS_ROOT, 'vendor/selenium')),
File.expand_path(File.join(RAILS_ROOT, 'vendor/selenium-core'))]
gems = Gem.source_index.find_name 'selenium', nil
ds << gems.last.full_gem_path unless gems.empty?
ds
end
sel_dirs.to_a.each do |seleniumdir|
['', 'core', 'selenium', 'javascript'].each do |subdir|
path = File.join seleniumdir, subdir
return path if File.exist?(File.join(path, 'TestRunner.html'))
end
end
raise 'Could not find Selenium Core installation'
end
end
end

View file

@ -0,0 +1,17 @@
module SeleniumOnRails::Renderer
include SeleniumOnRails::Paths
include SeleniumHelper
def render_test_case filename
@template.extend SeleniumOnRails::PartialsSupport
@page_title = test_case_name filename
output = render_to_string :file => filename
layout = (output =~ /<html>/i ? false : layout_path)
render :text => output, :layout => layout
headers['Cache-control'] = 'no-cache'
headers['Pragma'] = 'no-cache'
headers['Expires'] = '-1'
end
end

View file

@ -0,0 +1,35 @@
# Renders Selenium test templates in a fashion analogous to +rxml+ and
# +rjs+ templates.
#
# setup
# open :controller => 'customer', :action => 'list'
# assert_title 'Customers'
#
# See SeleniumOnRails::TestBuilder for a list of available commands.
class SeleniumOnRails::RSelenese < SeleniumOnRails::TestBuilder
end
ActionView::Base.register_template_handler 'rsel', SeleniumOnRails::RSelenese
class SeleniumOnRails::RSelenese < SeleniumOnRails::TestBuilder
attr_accessor :view
# Create a new RSelenese renderer bound to _view_.
def initialize view
super view
@view = view
end
# Render _template_ using _local_assigns_.
def render template, local_assigns
title = (@view.assigns['page_title'] or local_assigns['page_title'])
table(title) do
test = self #to enable test.command
assign_locals_code = ''
local_assigns.each_key {|key| assign_locals_code << "#{key} = local_assigns[#{key.inspect}];"}
eval assign_locals_code + "\n" + template
end
end
end

View file

@ -0,0 +1,81 @@
class SeleniumOnRails::Selenese
end
ActionView::Base.register_template_handler 'sel', SeleniumOnRails::Selenese
class SeleniumOnRails::Selenese
def initialize view
@view = view
end
def render template, local_assigns
name = (@view.assigns['page_title'] or local_assigns['page_title'])
lines = template.strip.split "\n"
html = ''
html << extract_comments(lines)
html << extract_commands(lines, name)
html << extract_comments(lines)
raise 'You cannot have comments in the middle of commands!' if next_line lines, :any
html
end
private
def next_line lines, expects
while lines.any?
l = lines.shift.strip
next if (l.empty? and expects != :comment)
comment = (l =~ /^\|.*\|$/).nil?
if (comment and expects == :command) or (!comment and expects == :comment)
lines.unshift l
return nil
end
return l
end
end
def extract_comments lines
comments = ''
while (line = next_line lines, :comment)
comments << line + "\n"
end
if defined? RedCloth
comments = RedCloth.new(comments).to_html
end
comments += "\n" unless comments.empty?
comments
end
def extract_commands lines, name
html = "<table>\n<tr><th colspan=\"3\">#{name}</th></tr>\n"
while (line = next_line lines, :command)
line = line[1..-2] #remove starting and ending |
cells = line.split '|'
if cells.first == 'includePartial'
html << include_partial(cells[1..-1])
next
end
raise 'There might only be a maximum of three cells!' if cells.length > 3
html << '<tr>'
(1..3).each do
cell = cells.shift
cell = (cell ? CGI.escapeHTML(cell.strip) : '&nbsp;')
html << "<td>#{cell}</td>"
end
html << "</tr>\n"
end
html << "</table>\n"
end
def include_partial params
partial = params.shift
locals = {}
params.each do |assignment|
next if assignment.empty?
_, var, value = assignment.split(/^([a-z_][a-zA-Z0-9_]*)\s*=\s*(.*)$/)
raise "Invalid format '#{assignment}'. Should be '|includePartial|partial|var1=value|var2=value|." unless var
locals[var.to_sym] = value or ''
end
@view.render :partial => partial, :locals => locals
end
end

View file

@ -0,0 +1,51 @@
module SeleniumOnRails::SuiteRenderer
def test_suite_name path
return 'All test cases' if [nil, '/'].include? path_to_relative_url(path)
File.split(path)[-1].humanize
end
def test_suites path
suites = []
parent_path = File.join(File.split(path).slice(0..-2)) #all but last
parent_path = path_to_relative_url parent_path
suites << ['..', parent_path] unless parent_path.nil?
visit_all_tests path, '', Proc.new {|n, p| suites << [n,path_to_relative_url(p)]}, nil
suites
end
def test_cases path
tests = []
visit_all_tests path, '', nil, Proc.new {|n, p| tests << [n,p]}
tests
end
def link_to_test_case suite_name, filename
name = suite_name + test_case_name(filename)
link_to name, :action => :test_file, :testname => path_to_relative_url(filename).sub(/^\//,'')
end
private
def path_to_relative_url path
slt = @controller.selenium_tests_path
return nil unless path.index slt
path.sub slt, ''
end
def visit_all_tests path, suite_name, suite_consumer, test_consumer
dirs = [] #add dirs to an array in order for files to be processed before dirs
Dir.entries(path).sort.each do |e|
next if skip_file?(e) or ['.','..'].include?(e)
filename = File.join path, e
if File.directory? filename
dirs << [filename, "#{suite_name}#{e.humanize}."]
suite_consumer.call("#{suite_name}#{e.humanize}", filename) if suite_consumer
else
test_consumer.call(suite_name, filename) if test_consumer
end
end
#recurse through dirs
dirs.each {|p, n| visit_all_tests p, n, suite_consumer, test_consumer }
end
end

View file

@ -0,0 +1,92 @@
# Builds Selenium test table using a high-level Ruby interface. Normally
# invoked through SeleniumOnRails::RSelenese.
#
# See SeleniumOnRails::TestBuilderActions for the available actions and
# SeleniumOnRails::TestBuilderAccessors for the available checks.
#
# For more information on the commands supported by TestBuilder, see the
# Selenium Commands Documentation at
# http://release.openqa.org/selenium-core/nightly/reference.html.
class SeleniumOnRails::TestBuilder
include SeleniumOnRails::TestBuilderActions
include SeleniumOnRails::TestBuilderAccessors
# Convert _str_ to a Selenium command name.
def self.selenize str
str.camelize.gsub(/^[A-Z]/) {|s| s.downcase }
end
# Prepends _pattern_ with 'exact:' if it would be considered containing
# string-match pattern otherwise.
def exactize pattern
pattern.include?(':') ? "exact:#{pattern}" : pattern
end
# Create a new TestBuilder for _view_.
def initialize view
@view = view
@output = ''
@xml = Builder::XmlMarkup.new :indent => 2, :target => @output
end
# Add a new table of tests, and return the HTML.
def table title
@xml.table do
@xml.tr do @xml.th(title, :colspan => 3) end
yield self
end
end
# Add a new test command using _cmd_, _target_ and _value_.
def command cmd, target=nil, value=nil
@xml.tr do
_tdata cmd
_tdata target
_tdata value
end
end
# :nodoc
alias_method :command_verbatim, :command
# Same as _command_ but add _AndWait_ to the name of _cmd_.
def command_and_wait cmd, target=nil, value=nil
command_verbatim cmd.to_s + 'AndWait', target, value
end
# Re routes commands in the provided block to #command_and_wait instead of
# #command.
def make_command_waiting
self.class.send :alias_method, :command, :command_and_wait
yield
self.class.send :alias_method, :command, :command_verbatim
end
protected
# If _url_ is a string, return unchanged. Otherwise, pass it to
# ActionView#UrlHelper#url_for.
def url_arg url
if url.instance_of?(String) then url else exactize(@view.url_for(url)) end
end
# If _arg_ is an array formats _arg_ to a textual representation.
# Otherwise return unchanged.
def collection_arg arg
if arg.is_a? Array
arg.collect {|e| e.gsub(/[\\,]/) {|s| "\\#{s}" } }.join(',')
else
arg
end
end
private
# Output a single TD element.
def _tdata value
if value
@xml.td(value.to_s)
else
@xml.td do @xml.target! << '&nbsp;' end
end
end
end

View file

@ -0,0 +1,575 @@
# The accessors available for SeleniumOnRails::TestBuilder tests.
#
# For each +store_foo+ there's +assert_foo+, +assert_not_foo+, +verify_foo+,
# +verify_not_foo+, +wait_for_foo+, +wait_for_not_foo+.
module SeleniumOnRails::TestBuilderAccessors
# Has an alert occurred?
#
# Related Assertions, automatically generated:
# * +assert_alert_present+
# * +assert_alert_not_present+
# * +verify_alert_present+
# * +verify_alert_not_present+
# * +wait_for_alert_present+
# * +wait_for_alert_not_present+
def store_alert_present variable_name
command 'storeAlertPresent', variable_name
end
# Has a prompt occurred?
#
# Related Assertions, automatically generated:
# * +assert_prompt_present+
# * +assert_prompt_not_present+
# * +verify_prompt_present+
# * +verify_prompt_not_present+
# * +wait_for_prompt_present+
# * +wait_for_prompt_not_present+
def store_prompt_present variable_name
command 'storePromptPresent', variable_name
end
# Has <tt>confirm()</tt> been called?
#
# Related Assertions, automatically generated:
# * +assert_confirmation_present+
# * +assert_confirmation_not_present+
# * +verify_confirmation_present+
# * +verify_confirmation_not_present+
# * +wait_for_confirmation_present+
# * +wait_for_confirmation_not_present+
def store_confirmation_present variable_name
command 'storeConfirmationPresent', variable_name
end
# Retrieves the message of a JavaScript alert generated during the previous
# action, or fail if there were no alerts.
#
# Getting an alert has the same effect as manually clicking OK. If an alert
# is generated but you do not get/verify it, the next Selenium action will
# fail.
#
# NOTE: under Selenium, JavaScript alerts will NOT pop up a visible alert
# dialog.
#
# NOTE: Selenium does NOT support JavaScript alerts that are generated in a
# page's <tt>onload()</tt> event handler. In this case a visible dialog WILL be
# generated and Selenium will hang until someone manually clicks OK.
#
# Related Assertions, automatically generated:
# * <tt>assert_alert(pattern)</tt>
# * <tt>assert_not_alert(pattern)</tt>
# * <tt>verify_alert_present(pattern)</tt>
# * <tt>verify_not_alert(pattern)</tt>
# * <tt>wait_for_alert(pattern)</tt>
# * <tt>wait_for_not_alert(pattern)</tt>
def store_alert variable_name
command 'storeAlert', variable_name
end
# Retrieves the message of a JavaScript confirmation dialog generated during
# the previous action.
#
# By default, the confirm function will return +true+, having the same effect
# as manually clicking OK. This can be changed by prior execution of the
# +choose_cancel_on_next_confirmation+ command. If a confirmation is
# generated but you do not get/verify it, the next Selenium action will fail.
#
# NOTE: under Selenium, JavaScript confirmations will NOT pop up a visible
# dialog.
#
# NOTE: Selenium does NOT support JavaScript confirmations that are generated
# in a page's <tt>onload()</tt> event handler. In this case a visible dialog WILL be
# generated and Selenium will hang until you manually click OK.
#
# Related Assertions, automatically generated:
# * <tt>assert_confirmation(pattern)</tt>
# * <tt>assert_not_confirmation(pattern)</tt>
# * <tt>verify_confirmation_present(pattern)</tt>
# * <tt>verify_not_confirmation(pattern)</tt>
# * <tt>wait_for_confirmation(pattern)</tt>
# * <tt>wait_for_not_confirmation(pattern)</tt>
def store_confirmation variable_name
command 'storeConfirmation', variable_name
end
# Retrieves the message of a JavaScript question prompt dialog generated
# during the previous action.
#
# Successful handling of the prompt requires prior execution of the
# +answer_on_next_prompt+ command. If a prompt is generated but you do not
# get/verify it, the next Selenium action will fail.
#
# NOTE: under Selenium, JavaScript prompts will NOT pop up a visible dialog.
#
# NOTE: Selenium does NOT support JavaScript prompts that are generated in a
# page's <tt>onload()</tt> event handler. In this case a visible dialog WILL be
# generated and Selenium will hang until someone manually clicks OK.
#
# Related Assertions, automatically generated:
# * <tt>assert_prompt(pattern)</tt>
# * <tt>assert_not_prompt(pattern)</tt>
# * <tt>verify_prompt_present(pattern)</tt>
# * <tt>verify_not_prompt(pattern)</tt>
# * <tt>wait_for_prompt(pattern)</tt>
# * <tt>wait_for_not_prompt(pattern)</tt>
def store_prompt variable_name
command 'storePrompt', variable_name
end
# Gets the absolute URL of the current page.
#
# Related Assertions, automatically generated:
# * <tt>assert_absolute_location(pattern)</tt>
# * <tt>assert_not_absolute_location(pattern)</tt>
# * <tt>verify_absolute_location_present(pattern)</tt>
# * <tt>verify_not_absolute_location(pattern)</tt>
# * <tt>wait_for_absolute_location(pattern)</tt>
# * <tt>wait_for_not_absolute_location(pattern)</tt>
def store_absolute_location variable_name
command 'storeAbsoluteLocation', variable_name
end
# Verify the location of the current page ends with the expected location.
# If an URL querystring is provided, this is checked as well.
#
# Related Assertions, automatically generated:
# * <tt>assert_location(pattern)</tt>
# * <tt>assert_not_location(pattern)</tt>
# * <tt>verify_location_present(pattern)</tt>
# * <tt>verify_not_location(pattern)</tt>
# * <tt>wait_for_location(pattern)</tt>
# * <tt>wait_for_not_location(pattern)</tt>
def store_location expected_location, variable_name
command 'storeLocation', expected_location, variable_name
end
# Gets the title of the current page.
#
# Related Assertions, automatically generated:
# * <tt>assert_title(pattern)</tt>
# * <tt>assert_not_title(pattern)</tt>
# * <tt>verify_title_present(pattern)</tt>
# * <tt>verify_not_title(pattern)</tt>
# * <tt>wait_for_title(pattern)</tt>
# * <tt>wait_for_not_title(pattern)</tt>
def store_title variable_name
command 'storeTitle', variable_name
end
# Gets the entire text of the page.
#
# Related Assertions, automatically generated:
# * <tt>assert_body_text(pattern)</tt>
# * <tt>assert_not_body_text(pattern)</tt>
# * <tt>verify_body_text_present(pattern)</tt>
# * <tt>verify_not_body_text(pattern)</tt>
# * <tt>wait_for_body_text(pattern)</tt>
# * <tt>wait_for_not_body_text(pattern)</tt>
def store_body_text variable_name
command 'storeBodyText', variable_name
end
# Gets the (whitespace-trimmed) value of an input field (or anything else
# with a value parameter). For checkbox/radio elements, the value will be
# "on" or "off" depending on whether the element is checked or not.
#
# Related Assertions, automatically generated:
# * <tt>assert_value(locator, pattern)</tt>
# * <tt>assert_not_value(locator, pattern)</tt>
# * <tt>verify_value_present(locator, pattern)</tt>
# * <tt>verify_not_value(locator, pattern)</tt>
# * <tt>wait_for_value(locator, pattern)</tt>
# * <tt>wait_for_not_value(locator, pattern)</tt>
def store_value locator, variable_name
command 'storeValue', locator, variable_name
end
# Gets the text of an element. This works for any element that contains text.
# This command uses either the +textContent+ (Mozilla-like browsers) or the
# +innerText+ (IE-like browsers) of the element, which is the rendered text
# shown to the user.
#
# Related Assertions, automatically generated:
# * <tt>assert_text(locator, pattern)</tt>
# * <tt>assert_not_text(locator, pattern)</tt>
# * <tt>verify_text_present(locator, pattern)</tt>
# * <tt>verify_not_text(locator, pattern)</tt>
# * <tt>wait_for_text(locator, pattern)</tt>
# * <tt>wait_for_not_text(locator, pattern)</tt>
def store_text locator, variable_name
command 'storeText', locator, variable_name
end
# Gets the result of evaluating the specified JavaScript snippet. The snippet
# may have multiple lines, but only the result of the last line will be
# returned.
#
# Note that, by default, the snippet will run in the context of the
# "selenium" object itself, so +this+ will refer to the Selenium object, and
# +window+ will refer to the top-level runner test window, not the window of
# your application.
#
# If you need a reference to the window of your application, you can refer to
# <tt>this.browserbot.getCurrentWindow()</tt> and if you need to use a locator to
# refer to a single element in your application page, you can use
# <tt>this.page().findElement("foo")</tt> where <tt>"foo"</tt> is your locator.
#
# Related Assertions, automatically generated:
# * <tt>assert_eval(script, pattern)</tt>
# * <tt>assert_not_eval(script, pattern)</tt>
# * <tt>verify_eval_present(script, pattern)</tt>
# * <tt>verify_not_eval(script, pattern)</tt>
# * <tt>wait_for_eval(script, pattern)</tt>
# * <tt>wait_for_not_eval(script, pattern)</tt>
def store_eval script, variable_name
command 'storeEval', script, variable_name
end
# Gets whether a toggle-button (checkbox/radio) is checked. Fails if the
# specified element doesn't exist or isn't a toggle-button.
#
# Related Assertions, automatically generated:
# * <tt>assert_checked(locator, pattern)</tt>
# * <tt>assert_not_checked(locator, pattern)</tt>
# * <tt>verify_checked_present(locator, pattern)</tt>
# * <tt>verify_not_checked(locator, pattern)</tt>
# * <tt>wait_for_checked(locator, pattern)</tt>
# * <tt>wait_for_not_checked(locator, pattern)</tt>
def store_checked locator, variable_name
command 'storeChecked', locator, variable_name
end
# Gets the text from a cell of a table.
#
# Related Assertions, automatically generated:
# * <tt>assert_table(locator, row, column, pattern)</tt>
# * <tt>assert_not_table(locator, row, column, pattern)</tt>
# * <tt>verify_table_present(locator, row, column, pattern)</tt>
# * <tt>verify_not_table(locator, row, column, pattern)</tt>
# * <tt>wait_for_table(locator, row, column, pattern)</tt>
# * <tt>wait_for_not_table(locator, row, column, pattern)</tt>
def store_table locator, row, column, variable_name
command 'storeTable', "#{locator}.#{row}.#{column}", variable_name
end
# Verifies that the selected option of a drop-down satisfies the
# +option_locator+.
#
# +option_locator+ is typically just an option label (e.g. "John Smith").
#
# See the +select+ command for more information about option locators.
#
# NOTE: +store_selected+ is currently not supported by Selenium Core.
#
# Related Assertions, automatically generated:
# * <tt>assert_selected(locator, option_locator)</tt>
# * <tt>assert_not_selected(locator, option_locator)</tt>
# * <tt>verify_selected_present(locator, option_locator)</tt>
# * <tt>verify_not_selected(locator, option_locator)</tt>
# * <tt>wait_for_selected(locator, option_locator)</tt>
# * <tt>wait_for_not_selected(locator, option_locator)</tt>
def store_selected locator, option_locator, variable_name
raise 'Not supported in Selenium Core at the moment'
end
# Gets all option labels for selected options in the specified select or
# multi-select element.
#
# The +pattern+ for the automatically generated assertions can either take an
# array or a pattern.
# assert_selected_options 'fruits', ['apple', 'pear']
# assert_selected_options 'fruits', 'a*,p*'
#
# Related Assertions, automatically generated:
# * <tt>assert_selected_options(locator, pattern)</tt>
# * <tt>assert_not_selected_options(locator, pattern)</tt>
# * <tt>verify_selected_options_present(locator, pattern)</tt>
# * <tt>verify_not_selected_options(locator, pattern)</tt>
# * <tt>wait_for_selected_options(locator, pattern)</tt>
# * <tt>wait_for_not_selected_options(locator, pattern)</tt>
def store_selected_options locator, variable_name
command 'storeSelectedOptions', locator, variable_name
end
# Gets all option labels in the specified select drop-down.
#
# The +pattern+ for the automatically generated assertions can either take an
# array or a pattern.
# assert_select_options 'fruits', ['apple', 'pear']
# assert_select_options 'fruits', 'a*,p*'
#
# Related Assertions, automatically generated:
# * <tt>assert_select_options(locator, pattern)</tt>
# * <tt>assert_not_select_options(locator, pattern)</tt>
# * <tt>verify_select_options_present(locator, pattern)</tt>
# * <tt>verify_not_select_options(locator, pattern)</tt>
# * <tt>wait_for_select_options(locator, pattern)</tt>
# * <tt>wait_for_not_select_options(locator, pattern)</tt>
def store_select_options locator, variable_name
command 'storeSelectOptions', locator, variable_name
end
# Gets the value of an element attribute.
#
# Related Assertions, automatically generated:
# * <tt>assert_attribute(locator, attribute_name, pattern)</tt>
# * <tt>assert_not_attribute(locator, attribute_name, pattern)</tt>
# * <tt>verify_attribute_present(locator, attribute_name, pattern)</tt>
# * <tt>verify_not_attribute(locator, attribute_name, pattern)</tt>
# * <tt>wait_for_attribute(locator, attribute_name, pattern)</tt>
# * <tt>wait_for_not_attribute(locator, attribute_name, pattern)</tt>
def store_attribute locator, attribute_name, variable_name
command 'storeAttribute', "#{locator}@#{attribute_name}", variable_name
end
# Verifies that the specified text pattern appears somewhere on the rendered
# page shown to the user.
#
# Related Assertions, automatically generated:
# * <tt>assert_text_present(pattern)</tt>
# * <tt>assert_text_not_present(pattern)</tt>
# * <tt>verify_text_present(pattern)</tt>
# * <tt>verify_text_not_present(pattern)</tt>
# * <tt>wait_for_text_present(pattern)</tt>
# * <tt>wait_for_text_not_present(pattern)</tt>
def store_text_present pattern, variable_name
command 'storeTextPresent', pattern, variable_name
end
# Verifies that the specified element is somewhere on the page.
#
# Related Assertions, automatically generated:
# * <tt>assert_element_present(locator)</tt>
# * <tt>assert_element_not_present(locator)</tt>
# * <tt>verify_element_present(locator)</tt>
# * <tt>verify_element_not_present(locator)</tt>
# * <tt>wait_for_element_present(locator)</tt>
# * <tt>wait_for_element_not_present(locator)</tt>
def store_element_present locator, variable_name
command 'storeElementPresent', locator, variable_name
end
# Determines if the specified element is visible. An element can be rendered
# invisible by setting the CSS "visibility" property to "hidden", or the
# "display" property to "none", either for the element itself or one if its
# ancestors. This method will fail if the element is not present.
#
# Related Assertions, automatically generated:
# * <tt>assert_visible(locator)</tt>
# * <tt>assert_not_visible(locator)</tt>
# * <tt>verify_visible(locator)</tt>
# * <tt>verify_not_visible(locator)</tt>
# * <tt>wait_for_visible(locator)</tt>
# * <tt>wait_for_not_visible(locator)</tt>
def store_visible locator, variable_name
command 'storeVisible', locator, variable_name
end
# Determines whether the specified input element is editable, i.e. hasn't
# been disabled. This method will fail if the specified element isn't an
# input element.
#
# Related Assertions, automatically generated:
# * <tt>assert_editable(locator)</tt>
# * <tt>assert_not_editable(locator)</tt>
# * <tt>verify_editable(locator)</tt>
# * <tt>verify_not_editable(locator)</tt>
# * <tt>wait_for_editable(locator)</tt>
# * <tt>wait_for_not_editable(locator)</tt>
def store_editable locator, variable_name
command 'storeEditable', locator, variable_name
end
# Returns the IDs of all buttons on the page.
#
# If a given button has no ID, it will appear as "" in this array.
#
# The +pattern+ for the automatically generated assertions can either take an
# array or a pattern.
# assert_all_buttons ['but1', 'but2']
# assert_all_buttons 'but?,but?*'
#
# Related Assertions, automatically generated:
# * <tt>assert_all_buttons(pattern)</tt>
# * <tt>assert_not_all_buttons(pattern)</tt>
# * <tt>verify_all_buttons(pattern)</tt>
# * <tt>verify_not_all_buttons(pattern)</tt>
# * <tt>wait_for_all_buttons(pattern)</tt>
# * <tt>wait_for_not_all_buttons(pattern)</tt>
def store_all_buttons variable_name
command 'storeAllButtons', variable_name
end
# Returns the IDs of all links on the page.
#
# If a given link has no ID, it will appear as "" in this array.
#
# The +pattern+ for the automatically generated assertions can either take an
# array or a pattern.
# assert_all_links ['link1', 'link2']
# assert_all_links 'link?,link?*'
#
# Related Assertions, automatically generated:
# * <tt>assert_all_links(pattern)</tt>
# * <tt>assert_not_all_links(pattern)</tt>
# * <tt>verify_all_links(pattern)</tt>
# * <tt>verify_not_all_links(pattern)</tt>
# * <tt>wait_for_all_links(pattern)</tt>
# * <tt>wait_for_not_all_links(pattern)</tt>
def store_all_links variable_name
command 'storeAllLinks', variable_name
end
# Returns the IDs of all input fields on the page.
#
# If a given field has no ID, it will appear as "" in this array.
#
# The +pattern+ for the automatically generated assertions can either take an
# array or a pattern.
# assert_all_fields ['field1', 'field2']
# assert_all_fields 'field?,field?*'
#
# Related Assertions, automatically generated:
# * <tt>assert_all_fields(pattern)</tt>
# * <tt>assert_not_all_fields(pattern)</tt>
# * <tt>verify_all_fields(pattern)</tt>
# * <tt>verify_not_all_fields(pattern)</tt>
# * <tt>wait_for_all_fields(pattern)</tt>
# * <tt>wait_for_not_all_fields(pattern)</tt>
def store_all_fields variable_name
command 'storeAllFields', variable_name
end
# Returns the entire HTML source between the opening and closing "html" tags.
#
# Related Assertions, automatically generated:
# * <tt>assert_html_source(pattern)</tt>
# * <tt>assert_not_html_source(pattern)</tt>
# * <tt>verify_html_source(pattern)</tt>
# * <tt>verify_not_html_source(pattern)</tt>
# * <tt>wait_for_html_source(pattern)</tt>
# * <tt>wait_for_not_html_source(pattern)</tt>
def store_html_source variable_name
command 'storeHtmlSource', variable_name
end
# Returns the specified expression.
#
# This is useful because of JavaScript preprocessing.
#
# Related Assertions, automatically generated:
# * <tt>assert_expression(expression, pattern)</tt>
# * <tt>assert_not_expression(expression, pattern)</tt>
# * <tt>verify_expression(expression, pattern)</tt>
# * <tt>verify_not_expression(expression, pattern)</tt>
# * <tt>wait_for_expression(expression, pattern)</tt>
# * <tt>wait_for_not_expression(expression, pattern)</tt>
def store_expression expression, variable_name
command 'storeExpression', expression, variable_name
end
private
# Generates all assertions for the accessors.
def self.generate_methods
public_instance_methods.each do |method|
case method
when 'store_alert_present',
'store_prompt_present',
'store_confirmation_present'
each_assertion method do |assertion_method, command_name|
define_method assertion_method do
command command_name
end
end
when 'store_alert',
'store_confirmation',
'store_prompt',
'store_title',
'store_body_text',
'store_text_present',
'store_element_present',
'store_visible',
'store_editable',
'store_html_source'
each_assertion method do |assertion_method, command_name|
define_method assertion_method do |pattern|
command command_name, pattern
end
end
when 'store_value',
'store_text',
'store_eval',
'store_checked',
'store_selected',
'store_expression'
each_assertion method do |assertion_method, command_name|
define_method assertion_method do |arg1, arg2|
command command_name, arg1, arg2
end
end
when 'store_all_buttons',
'store_all_links',
'store_all_fields'
each_assertion method do |assertion_method, command_name|
define_method assertion_method do |pattern|
command command_name, collection_arg(pattern)
end
end
when 'store_select_options',
'store_selected_options'
each_assertion method do |assertion_method, command_name|
define_method assertion_method do |locator, pattern|
command command_name, locator, collection_arg(pattern)
end
end
when 'store_attribute'
each_assertion method do |assertion_method, command_name|
define_method assertion_method do |locator, attribute_name, pattern|
command command_name, "#{locator}@#{attribute_name}", pattern
end
end
when 'store_table'
each_assertion method do |assertion_method, command_name|
define_method assertion_method do |locator, row, column, pattern|
command command_name, "#{locator}.#{row}.#{column}", pattern
end
end
when 'store_absolute_location',
'store_location'
each_assertion method do |assertion_method, command_name|
define_method assertion_method do |pattern|
if method == 'store_absolute_location' and pattern.is_a? Hash
pattern[:only_path] = false
end
command command_name, url_arg(pattern)
end
end
when /^store_/
raise 'internal error'
end
end
end
# Generates all the assertions needed given a +store_method+.
def self.each_assertion store_method
before_negation = nil
after_negation = store_method.split('_')[1..-1] #throw away 'store'
if after_negation.last == 'present'
before_negation, after_negation = after_negation, after_negation.pop
end
['assert', 'verify', ['wait','for']].each do |action|
[nil, 'not'].each do |negation|
name = [action, before_negation, negation, after_negation].flatten.reject{|a|a.nil?}
method_name = name.join '_'
command = name.inject(name.shift.clone) {|n, p| n << p.capitalize}
yield method_name, command
end
end
end
generate_methods
end

View file

@ -0,0 +1,286 @@
# The actions available for SeleniumOnRails::TestBuilder tests.
#
# For each action +foo+ there's also an action +foo_and_wait+.
module SeleniumOnRails::TestBuilderActions
# Tell Selenium on Rails to clear the session and load any fixtures. DO
# NOT CALL THIS AGAINST NON-TEST DATABASES.
# The supported +options+ are <code>:keep_session</code>,
# <code>:fixtures</code> and <code>:clear_tables</code>
# setup
# setup :keep_session
# setup :fixtures => :all
# setup :keep_session, :fixtures => [:foo, :bar]
# setup :clear_tables => [:foo, :bar]
def setup options = {}
options = {options => nil} unless options.is_a? Hash
opts = {:controller => 'selenium', :action => 'setup'}
opts[:keep_session] = true if options.has_key? :keep_session
[:fixtures, :clear_tables].each do |key|
if (f = options[key])
f = [f] unless f.is_a? Array
opts[key] = f.join ','
end
end
open opts
end
# Includes a partial.
# The path is relative to the Selenium tests root. The starting _ and the file
# extension don't have to be specified.
# #include test/selenium/_partial.*
# include_partial 'partial'
# #include test/selenium/suite/_partial.*
# include_partial 'suite/partial'
# #include test/selenium/suite/_partial.* and provide local assigns
# include_partial 'suite/partial', :foo => bar
def include_partial path, local_assigns = {}
partial = @view.render :partial => path, :locals => local_assigns
@output << partial
end
# Clicks on a link, button, checkbox or radio button. If the click action
# causes a new page to load (like a link usually does), call
# +wait_for_page_to_load+.
def click locator
command 'click', locator
end
# Explicitly simulate an event (e.g. <tt>"focus"</tt>, <tt>"blur"</tt>), to
# trigger the corresponding <tt>"on_event_"</tt> handler.
def fire_event locator, event_name
command 'fireEvent', locator, event_name
end
# Simulates a user pressing and releasing a key.
#
# +keycode+ is the numeric keycode of the key to be pressed, normally the
# ASCII value of that key.
def key_press locator, keycode
command 'keyPress', locator, keycode
end
# Simulates a user pressing a key (without releasing it yet).
#
# +keycode+ is the numeric keycode of the key to be pressed, normally the
# ASCII value of that key.
def key_down locator, keycode
command 'keyDown', locator, keycode
end
# Simulates a user releasing a key.
#
# +keycode+ is the numeric keycode of the key to be released, normally the
# ASCII value of that key.
def key_up locator, keycode
command 'keyUp', locator, keycode
end
# Simulates a user hovering a mouse over the specified element.
def mouse_over locator
command 'mouseOver', locator
end
# Simulates a user pressing the mouse button (without releasing it yet) on the
# specified element.
def mouse_down locator
command 'mouseDown', locator
end
# Sets the value of an input field, as though you typed it in.
#
# Can also be used to set the value of combo boxes, check boxes, etc. In these
# cases, +value+ should be the value of the option selected, not the visible
# text.
def type locator, value
command 'type', locator, value
end
# Check a toggle-button (checkbox/radio).
def check locator
command 'check', locator
end
# Uncheck a toggle-button (checkbox/radio).
def uncheck locator
command 'uncheck', locator
end
# Select an option from a drop-down using an option locator.
#
# Option locators provide different ways of specifying options of an HTML
# Select element (e.g. for selecting a specific option, or for asserting that
# the selected option satisfies a specification). There are several forms of
# Select Option Locator.
#
# * label=labelPattern
# matches options based on their labels, i.e. the visible text. (This is the
# default.)
# label=regexp:^[Oo]ther
# * value=valuePattern
# matches options based on their values.
# value=other
# * id=id
# matches options based on their ids.
# id=option1
# * index=index
# matches an option based on its index (offset from zero).
# index=2
#
# If no option locator prefix is provided, the default behaviour is to match
# on label.
def select locator, option_locator
command 'select', locator, option_locator
end
# Add a selection to the set of selected options in a multi-select element
# using an option locator.
#
# See the <tt>#select</tt> command for more information about option locators.
def add_selection locator, option_locator
command 'addSelection', locator, option_locator
end
# Remove a selection from the set of selected options in a multi-select
# element using an option locator.
#
# See the +select+ command for more information about option locators.
def remove_selection locator, option_locator
command 'removeSelection', locator, option_locator
end
# Submit the specified form. This is particularly useful for forms without
# submit buttons, e.g. single-input "Search" forms.
def submit locator
command 'submit', locator
end
# Opens an URL in the test frame. This accepts both relative and absolute
# URLs. The <tt>open</tt> command waits for the page to load before
# proceeding, i.e. you don't have to call +wait_for_page_to_load+.
#
# Note: The URL must be on the same domain as the runner HTML due to security
# restrictions in the browser (Same Origin Policy).
def open url
command 'open', url_arg(url)
end
# Selects a popup window; once a popup window has been selected, all commands
# go to that window. To select the main window again, use +nil+ as the target.
def select_window window_id
command 'selectWindow', window_id||'null'
end
# Waits for a popup window to appear and load up.
#
# The +timeout+ is specified in milliseconds.
def wait_for_popup window_id, timeout
command 'waitForPopUp', window_id||'null', timeout
end
# By default, Selenium's overridden <tt>window.confirm()</tt> function will return
# +true+, as if the user had manually clicked OK. After running this command,
# the next call to <tt>confirm()</tt> will return +false+, as if the user had clicked
# Cancel.
def choose_cancel_on_next_confirmation
command 'chooseCancelOnNextConfirmation'
end
# Instructs Selenium to return the specified answer string in response to the
# next JavaScript prompt (<tt>window.prompt()</tt>).
def answer_on_next_prompt answer
command 'answerOnNextPrompt', answer
end
# Simulates the user clicking the "back" button on their browser.
def go_back
command 'goBack'
end
# Simulates the user clicking the "Refresh" button on their browser.
def refresh
command 'refresh'
end
# Simulates the user clicking the "close" button in the titlebar of a popup
# window or tab.
def close
command 'close'
end
# Writes a message to the status bar and adds a note to the browser-side log.
#
# +context+ is the message sent to the browser.
#
# +log_level_threshold+ can be +nil+, <tt>:debug</tt>, <tt>:info</tt>,
# <tt>:warn</tt> or <tt>:error</tt>.
def set_context context, log_level_threshold = nil
if log_level_threshold
command 'setContext', context, log_level_threshold.to_s
else
command 'setContext', context
end
end
# Runs the specified JavaScript snippet repeatedly until it evaluates to
# +true+. The snippet may have multiple lines, but only the result of the last
# line will be considered.
#
# Note that, by default, the snippet will be run in the runner's test window,
# not in the window of your application. To get the window of your
# application, you can use the JavaScript snippet
# <tt>selenium.browserbot.getCurrentWindow()</tt>, and then run your
# JavaScript in there.
#
# +timeout+ is specified in milliseconds.
def wait_for_condition script, timeout
command 'waitForCondition', script, timeout
end
# Specifies the amount of time that Selenium will wait for actions to
# complete.
#
# Actions that require waiting include +open+ and the <tt>wait_for*</tt>
# actions.
#
# The default timeout is 30 seconds.
#
# +timeout+ is specified in milliseconds.
def set_timeout timeout
command 'setTimeout', timeout
end
# Waits for a new page to load.
#
# You can use this command instead of the +and_wait+ suffixes,
# +click_and_wait+, +select_and_wait+, +type_and_wait+ etc. (which are only
# available in the JS API).
#
# Selenium constantly keeps track of new pages loading, and sets a
# +newPageLoaded+ flag when it first notices a page load. Running any other
# Selenium command after turns the flag to +false+. Hence, if you want to wait
# for a page to load, you must wait immediately after a Selenium command that
# caused a page-load.
#
# +timeout+ is specified in milliseconds.
def wait_for_page_to_load timeout
command 'waitForPageToLoad', timeout
end
private
# Generates the corresponding +_and_wait+ for each action.
def self.generate_and_wait_actions
public_instance_methods.each do |method|
define_method method + '_and_wait' do |*args|
make_command_waiting do
send method, *args
end
end
end
end
generate_and_wait_actions
end