Update selenium on rails using 'official' git repo

git://github.com/paytonrules/selenium-on-rails.git
This commit is contained in:
Reinier Balt 2008-12-02 10:05:41 +01:00
parent 198f3240b8
commit 9b504b3e47
159 changed files with 16409 additions and 11794 deletions

View file

@ -1,210 +1,214 @@
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
$: << File.expand_path(File.dirname(__FILE__) + "/")
$: << File.expand_path(File.dirname(__FILE__) + "/../")
require 'paths'
require 'net/http'
require 'tempfile'
def c(var, default = nil) SeleniumOnRailsConfig.new.get var, default end
def c_b(var, default = nil) SeleniumOnRailsConfig.new.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)
BASE_URL_PATH = c :base_url_path, '/'
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} webrick -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} webrick -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}"
base_url = "http://#{HOST}:#{@port}#{BASE_URL_PATH}"
log = log_file browser
command = "\"#{path}\" \"http://#{HOST}:#{@port}#{TEST_RUNNER_URL}?test=tests&auto=true&baseUrl=#{base_url}&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
FileUtils.mkdir_p(log_path(''))
(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

@ -1,54 +1,57 @@
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
require 'test/unit'
require 'active_record'
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.reset_cache # in case they've already been loaded and things have changed
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

@ -1,38 +1,36 @@
# 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
require 'selenium_on_rails/paths'
module SeleniumOnRails::PartialsSupport
include SeleniumOnRails::Paths
# Overrides where the partial is searched for, and returns only the command table rows.
def render_partial(options)
pattern = partial_pattern options[:partial]
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 => options[:locals]
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

@ -1,5 +1,9 @@
require 'selenium_on_rails_config'
module SeleniumOnRails
module Paths
attr_accessor :config
def selenium_path
@@selenium_path ||= find_selenium_path
@@selenium_path
@ -13,8 +17,11 @@ module SeleniumOnRails
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
'/layout.rhtml'
'layout.rhtml'
end
def fixtures_path
@ -32,25 +39,22 @@ module SeleniumOnRails
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
private ###############################################
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'
def find_selenium_path
sel_dirs = @config.get :selenium_path do
File.expand_path(File.dirname(__FILE__) + '/../../selenium-core')
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

@ -1,17 +1,20 @@
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
module SeleniumOnRails::Renderer
include SeleniumOnRails::Paths
def render_test_case filename
@template.extend SeleniumOnRails::PartialsSupport
@page_title = test_case_name filename
output = render_to_string :file => filename, :locals => {"page_title" => @page_title}
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
def test_case_name filename
File.basename(filename).sub(/\..*/,'').humanize
end
end

View file

@ -13,23 +13,24 @@ ActionView::Template.register_template_handler 'rsel', SeleniumOnRails::RSelenes
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
title = @view.assigns['page_title']
def render template, local_assigns
title = (@view.assigns['page_title'] or local_assigns['page_title'])
table(title) do
test = self #to enable test.command
eval template.source
assign_locals_code = ''
local_assigns.each_key {|key| assign_locals_code << "#{key} = local_assigns[#{key.inspect}];"}
eval assign_locals_code + "\n" + template.source
end
end
def compilable?
false
def self.call(template)
"#{name}.new(self).render(template, local_assigns)"
end
end
end

View file

@ -1,16 +1,18 @@
require 'selenium_on_rails/partials_support'
class SeleniumOnRails::Selenese
end
ActionView::Template.register_template_handler 'sel', SeleniumOnRails::Selenese
class SeleniumOnRails::Selenese
class SeleniumOnRails::Selenese
def initialize view
@view = view
end
def render template
name = @view.assigns['page_title']
lines = template.strip.split "\n"
def render template, local_assigns = {}
name = (@view.assigns['page_title'] or local_assigns['page_title'])
lines = template.source.strip.split "\n"
html = ''
html << extract_comments(lines)
html << extract_commands(lines, name)
@ -19,10 +21,6 @@ class SeleniumOnRails::Selenese
html
end
def compilable?
false
end
private
def next_line lines, expects
while lines.any?
@ -36,6 +34,10 @@ class SeleniumOnRails::Selenese
return l
end
end
def self.call(template)
"#{name}.new(self).render(template, local_assigns)"
end
def extract_comments lines
comments = ''

View file

@ -1,51 +1,56 @@
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
require 'selenium_on_rails'
module SeleniumOnRails
module 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
end

View file

@ -1,92 +1,116 @@
# 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
require 'selenium_on_rails/test_builder_actions'
require 'selenium_on_rails/test_builder_accessors'
# Create test_builder_user_actions.rb to support actions included
# in selenium-core's user-extensions.js
#
# See test_builder_user_actions.rb.example for examples matching
# selenium-core's user-extensions.js.sample
module SeleniumOnRails::TestBuilderUserActions
end
require 'selenium_on_rails/test_builder_user_actions' if File.exist?(File.expand_path(File.join(File.dirname(__FILE__), 'test_builder_user_actions.rb')))
# Create test_builder_user_accessors.rb to support accessors
# included in selenium-core's user-extensions.js
#
# See test_builder_user_accessors.rb.example for examples matching
# selenium-core's user-extensions.js.sample
module SeleniumOnRails::TestBuilderUserAccessors
end
require 'selenium_on_rails/test_builder_user_accessors' if File.exist?(File.expand_path(File.join(File.dirname(__FILE__), 'test_builder_user_accessors.rb')))
# 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
include SeleniumOnRails::TestBuilderUserActions
include SeleniumOnRails::TestBuilderUserAccessors
# 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

@ -1,286 +1,515 @@
# 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
# 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
# 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_at locator, coord_string
command 'clickAt', locator, coord_string
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
# Simulates the user pressing the alt key and hold it down until do_alt_up()
# is called or a new page is loaded.
def alt_key_down
command 'altKeyDown'
end
# Simulates the user releasing the alt key.
def alt_key_up
command 'altKeyUp'
end
# Halt the currently running test, and wait for the user to press the Continue
# button. This command is useful for debugging, but be careful when using it,
# because it will force automated tests to hang until a user intervenes manually.
#
# NOTE: <tt>break</tt> is a reserved word in Ruby, so we have to simulate
# Selenium core's <tt>break()</tt> with <tt>brake()</tt>
def brake
command 'break'
end
# Simulates the user pressing the alt key and hold it down until do_control_up()
# is called or a new page is loaded.
def control_key_down
command 'controlKeyDown'
end
# Simulates the user releasing the control key.
def control_key_up
command 'controlKeyUp'
end
# Create a new cookie whose path and domain are same with those of current page
# under test, unless you specified a path for this cookie explicitly.
#
# Arguments:
# * <tt>name_value_pair</tt> - name and value of the cookie in a format "name=value"
# * <tt>options_string</tt> - options for the cookie. Currently supported options
# include 'path' and 'max_age'. The options_string's format is
# <tt>"path=/path/, max_age=60"</tt>. The order of options are irrelevant, the
# unit of the value of 'max_age' is second.
def create_cookie name_value_pair, options_string
command 'createCookie', name_value_pair, options_string
end
# Delete a named cookie with specified path.
def delete_cookie name, path
command 'deleteCookie', name, path
end
# Double clicks on a link, button, checkbox or radio button. If the double click action
# causes a new page to load (like a link usually does), call <tt>wait_for_page_to_load</tt>.
def double_click locator
command 'doubleClick', locator
end
# Doubleclicks on a link, button, checkbox or radio button. If the action causes a new page
# to load (like a link usually does), call <tt>wait_for_page_to_load</tt>.
def double_click_at locator, coord_string
command 'doubleClickAt', locator, coord_string
end
# Drags an element a certain distance and then drops it.
def drag_and_drop locator, movements_string
command 'dragAndDrop', locator, movements_string
end
# Drags an element and drops it on another element.
def drag_and_drop_to_object locator_of_object_to_be_dragged, locator_of_drag_destination_object
command 'dragAndDropToObject', locator_of_object_to_be_dragged, locator_of_drag_destination_object
end
# Prints the specified message into the third table cell in your Selenese
# tables.
# Useful for debugging.
def echo message
command 'echo', message
end
# Briefly changes the backgroundColor of the specified element yellow.
# Useful for debugging.
def highlight locator
command 'highlight', locator
end
# Press the meta key and hold it down until <tt>doMetaUp()</tt> is called or
# a new page is loaded.
def meta_key_down
command 'metaKeyDown'
end
# Release the meta key.
def meta_key_up
command 'metaKeyUp'
end
# Simulates a user pressing the mouse button (without releasing it yet) on the specified
# element.
def mouse_down_at locator, coord_string
command 'mouseDownAt', locator, coord_string
end
# Simulates a user moving the mouse.
def mouse_move locator
command 'mouseMove', locator
end
# Simulates a user moving the mouse relative to the specified element.
def mouse_move_at locator, coord_string
command 'mouseMoveAt', locator, coord_string
end
# Simulates the user moving the mouse off the specified element.
def mouse_out locator
command 'mouseOut', locator
end
# Simulates the user releasing the mouse button on the specified element.
def mouse_up locator
command 'mouseUp', locator
end
# Simulates a user pressing the mouse button (without releasing it yet) on the
# specified element.
def mouse_up_at locator, coord_string
command 'mouseUpAt', locator, coord_string
end
# Opens a popup window (if a window with that ID isn't already open). After opening the
# window, you'll need to select it using the <tt>select_window</tt> command.
#
# This command can also be a useful workaround for bug SEL-339. In some cases, Selenium
# will be unable to intercept a call to window.open (if the call occurs during or before
# the "onLoad" event, for example). In those cases, you can force Selenium to notice the
# open window's name by using the Selenium openWindow command, using an empty (blank) url,
# like this: <tt>open_window("", "myFunnyWindow")</tt>.
def open_window url, window_id
command 'openWindow', url, window_id
end
# Wait for the specified amount of time (in milliseconds).
def pause wait_time
command 'pause', wait_time
end
# Unselects all of the selected options in a multi-select element.
def remove_all_selections locator
command 'removeAllSelections', locator
end
# Selects a frame within the current window. (You may invoke this command multiple times
# to select nested frames.) To select the parent frame, use "relative=parent" as a
# locator; to select the top frame, use "relative=top".
#
# You may also use a DOM expression to identify the frame you want directly, like this:
# <tt>dom=frames["main"].frames["subframe"]</tt>
def select_frame locator
command 'selectFrame', locator
end
# Moves the text cursor to the specified position in the given input element or textarea.
# This method will fail if the specified element isn't an input element or textarea.
def set_cursor_position locator, position
command 'setCursorPosition', locator, position
end
# Configure the number of pixels between "mousemove" events during dragAndDrop commands
# (default=10).
# Setting this value to 0 means that we'll send a "mousemove" event to every single pixel
# in between the start location and the end location; that can be very slow, and may
# cause some browsers to force the JavaScript to timeout.
#
# If the mouse speed is greater than the distance between the two dragged objects, we'll
# just send one "mousemove" at the start location and then one final one at the end location.
def set_mouse_speed pixels
command 'setMouseSpeed', pixels
end
# Press the shift key and hold it down until <tt>doShiftUp()</tt> is called or a new page
# is loaded.
def shift_key_down
command 'shiftKeyDown'
end
# Release the shift key.
def shift_key_up
command 'shiftKeyUp'
end
# This command is a synonym for <tt>store_expression</tt>.
def store expression, variable_name
command 'store', expression, variable_name
end
# Simulates keystroke events on the specified element, as though you typed the value
# key-by-key.
#
# This is a convenience method for calling <tt>key_down</tt>, <tt>key_up</tt>,
# <tt>key_press</tt> for every character in the specified string; this is useful for
# dynamic UI widgets (like auto-completing combo boxes) that require explicit key events.
#
# Unlike the simple "type" command, which forces the specified value into the page directly,
# this command may or may not have any visible effect, even in cases where typing keys would
# normally have a visible effect. For example, if you use "<tt>type_keys</tt>" on a form
# element, you may or may not see the results of what you typed in the field.
#
# In some cases, you may need to use the simple "type" command to set the value of the field
# and then the "<tt>type_keys</tt>" command to send the keystroke events corresponding to
# what you just typed.
def type_keys locator, value
command 'typeKeys', locator, value
end
# Gives focus to a window.
def window_focus window_name
command 'windowFocus', window_name
end
# Resize window to take up the entire screen.
def window_maximize window_name
command 'windowMaximize', window_name
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

View file

@ -0,0 +1,91 @@
# Mirrors the accessors specified in user-extensions.js from the selenium-core
module SeleniumOnRails::TestBuilderUserAccessors
# Return the length of text of a specified element.
#
# Related Assertions, automatically generated:
# * <tt>assert_text_length(locator, variable)</tt>
# * <tt>assert_not_text_length(locator, length)</tt>
# * <tt>verify_text_length(locator, length)</tt>
# * <tt>verify_not_text_length(locator, length)</tt>
# * <tt>wait_for_text_length(locator, length)</tt>
# * <tt>wait_for_not_text_length(locator, length)</tt>
def store_text_length locator, variable_name
command 'storeTextLength', locator, variable_name
end
# Checks if value entered more than once in textbox.
#
# Related Assertions, automatically generated:
# * <tt>assert_not_text_length(locator, text)</tt>
# * <tt>verify_text_length(locator, text)</tt>
# * <tt>verify_not_text_length(locator, text)</tt>
# * <tt>wait_for_text_length(locator, text)</tt>
# * <tt>wait_for_not_text_length(locator, text)</tt>
def assert_value_repeated locator, text
command 'assertValueRepeated', locator, text
end
private
def self.generate_methods
public_instance_methods.each do |method|
case method
when 'store_text_length'
each_assertion method do |assertion_method, command_name|
define_method assertion_method do |arg1, arg2|
command command_name, arg1, arg2
end
end
when 'assert_value_repeated'
each_check method do |check_method, command_name|
define_method check_method do |arg1, arg2|
command command_name, arg1, arg2
end
end
else
raise "Internal error: Don't know how to process user accessor: #{method}"
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
def self.each_check assert_method
before_negation = nil
after_negation = assert_method.split('_')[1..-1] #throw away 'assert'
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|
unless (action == 'assert' && negation.nil?)
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
end
generate_methods
end

View file

@ -0,0 +1,24 @@
# Mirrors the actions specified in user-extensions.js from the selenium-core
module SeleniumOnRails::TestBuilderUserActions
# Types the text twice into a text box.
def type_repeated locator, text
command 'typeRepeated', locator, text
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