Upgrade Selenium on Rails to r140

This commit is contained in:
Eric Allen 2009-12-14 11:51:36 -05:00
parent 156862200b
commit 40074c71ad
117 changed files with 16789 additions and 8867 deletions

View file

@ -1,214 +1,214 @@
$: << 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
$: << 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.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)
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,57 +1,57 @@
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
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,36 +1,36 @@
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
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,60 +1,61 @@
require 'selenium_on_rails_config'
module SeleniumOnRails
module Paths
attr_accessor :config
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
'layout.rhtml'
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 = @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
require 'selenium_on_rails_config'
module SeleniumOnRails
module Paths
def selenium_path
@@selenium_path ||= find_selenium_path
@@selenium_path
end
def selenium_tests_path
return SeleniumOnRailsConfig.get("selenium_tests_path") if SeleniumOnRailsConfig.get("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
'layout.rhtml'
end
def fixtures_path
return SeleniumOnRailsConfig.get("fixtures_path") if SeleniumOnRailsConfig.get("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
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,20 +1,20 @@
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
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

@ -10,7 +10,7 @@ class SeleniumOnRails::RSelenese < SeleniumOnRails::TestBuilder
end
ActionView::Template.register_template_handler 'rsel', SeleniumOnRails::RSelenese
class SeleniumOnRails::RSelenese < SeleniumOnRails::TestBuilder
class SeleniumOnRails::RSelenese
attr_accessor :view
def initialize view
@ -18,19 +18,27 @@ class SeleniumOnRails::RSelenese < SeleniumOnRails::TestBuilder
@view = view
end
def render template, 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.source
end
evaluator = Evaluator.new(@view)
evaluator.run_script title, assign_locals_code_from(local_assigns) + "\n" + template.source, local_assigns
end
def assign_locals_code_from(local_assigns)
return local_assigns.keys.collect {|key| "#{key} = local_assigns[#{key.inspect}];"}.join
end
def self.call(template)
"#{name}.new(self).render(template, local_assigns)"
end
class Evaluator < SeleniumOnRails::TestBuilder
def run_script(title, script, local_assigns)
table(title) do
test = self #to enable test.command
eval script
end
end
end
end

View file

@ -1,87 +1,87 @@
require 'selenium_on_rails/partials_support'
class SeleniumOnRails::Selenese
end
ActionView::Template.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.source.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 self.call(template)
"#{name}.new(self).render(template, local_assigns)"
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
require 'selenium_on_rails/partials_support'
class SeleniumOnRails::Selenese
end
ActionView::Template.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.source.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 self.call(template)
"#{name}.new(self).render(template, local_assigns)"
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

@ -1,56 +1,56 @@
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
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,116 +1,116 @@
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
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