sauce-test 0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +57 -0
- data/TODO.rdoc +3 -0
- data/bin/sauce_test +4 -0
- data/examples/google_search/Rakefile +9 -0
- data/examples/google_search/config/sauce.yml +3 -0
- data/examples/google_search/helpers/google_test_helper.rb +21 -0
- data/examples/google_search/spec/selenium/google_spec.rb +37 -0
- data/examples/google_search/spec/selenium/r.html +97 -0
- data/examples/google_search/spec/spec_helper.rb +4 -0
- data/lib/sauce-test/collection.rb +104 -0
- data/lib/sauce-test/helpers/sauce_test_options.rb +77 -0
- data/lib/sauce-test/rake/tasks.rb +35 -0
- data/lib/sauce-test/rspec/rake_tasks.rb +14 -0
- data/lib/sauce-test/rspec/runner.rb +49 -0
- data/lib/sauce-test/rspec/sauce_formatter.rb +76 -0
- data/lib/sauce-test/runner.rb +32 -0
- data/lib/sauce-test/runner/options.rb +116 -0
- data/lib/sauce-test/runner/report.rb +65 -0
- data/lib/sauce-test/selenium/client_driver_extension.rb +35 -0
- data/lib/sauce-test/templates/collection_report.erb +57 -0
- data/lib/sauce-test/templates/img/saucelabs.jpg +0 -0
- data/lib/sauce-test/templates/test_report.erb +81 -0
- data/lib/sauce-test/testunit/rake_tasks.rb +14 -0
- data/lib/sauce-test/testunit/runner.rb +15 -0
- data/lib/sauce-test/version.rb +13 -0
- metadata +110 -0
data/README.rdoc
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
= RSpec
|
2
|
+
|
3
|
+
* http://rspec.info
|
4
|
+
* http://rubyforge.org/projects/rspec
|
5
|
+
* http://github.com/dchelimsky/rspec/wikis
|
6
|
+
* mailto:rspec-users@rubyforge.org
|
7
|
+
* mailto:rspec-devel@rubyforge.org
|
8
|
+
|
9
|
+
== DESCRIPTION:
|
10
|
+
|
11
|
+
SauceTest lets you run your Selenium tests on Sauce Labs cloud unchanged.
|
12
|
+
|
13
|
+
sauce_test is command line utility you can use
|
14
|
+
or create rake task and run your tests in parallel
|
15
|
+
|
16
|
+
== SYNOPSIS:
|
17
|
+
|
18
|
+
1. create new rake task, should look like that:
|
19
|
+
|
20
|
+
only 'files' field is mandatory
|
21
|
+
|
22
|
+
require 'rubygems'
|
23
|
+
require 'rake'
|
24
|
+
require 'sauce-test/rake/tasks'
|
25
|
+
|
26
|
+
SauceTest::Rake::RSpecTask.new do |t|
|
27
|
+
t.files = 'spec/**/*_spec.rb' # Dir.glob pattern
|
28
|
+
|
29
|
+
t.browsers = [{'os' => "Linux", 'name' => "firefox", 'version' => 3},
|
30
|
+
{'os' => "Windows 2003", 'name' => "iexplore", 'version' => 7}]
|
31
|
+
|
32
|
+
t.workers = 4 #workers per browser
|
33
|
+
|
34
|
+
t.report = 'path_to/report'
|
35
|
+
t.config = 'config/sauce.yml'
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
|
40
|
+
|
41
|
+
|
42
|
+
2. create sauce.yml file in config dir.
|
43
|
+
if you like to use different dir/file make sure that SAUCE_CONFIG env variable contains path to config file. .
|
44
|
+
example sauce.yml
|
45
|
+
|
46
|
+
login: user
|
47
|
+
key: ff487724-2sfe-4a50-8365-sc750f2d875f
|
48
|
+
timeout: 180 #optional but advised to be at least 90 sec
|
49
|
+
|
50
|
+
|
51
|
+
3. rake sauce:spec
|
52
|
+
|
53
|
+
== INSTALL:
|
54
|
+
|
55
|
+
[sudo] gem install rspec
|
56
|
+
|
57
|
+
|
data/TODO.rdoc
ADDED
data/bin/sauce_test
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
class GooglePage < Selenium::Client::Driver
|
2
|
+
|
3
|
+
def initialize()
|
4
|
+
super( :host => "localhost",
|
5
|
+
:port => 4444,
|
6
|
+
:browser => '*chrome',
|
7
|
+
:url => "http://www.google.com",
|
8
|
+
:timeout_in_second => 60 )
|
9
|
+
end
|
10
|
+
|
11
|
+
|
12
|
+
def search_for(search_str)
|
13
|
+
self.type "q", search_str
|
14
|
+
self.click "btnG", :wait_for => :page
|
15
|
+
end
|
16
|
+
|
17
|
+
def query_str
|
18
|
+
self.value("q")
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
|
3
|
+
describe "Google Search" do
|
4
|
+
|
5
|
+
before(:all) do
|
6
|
+
@page = GooglePage.new
|
7
|
+
end
|
8
|
+
|
9
|
+
before(:each) do
|
10
|
+
@page.start
|
11
|
+
end
|
12
|
+
|
13
|
+
append_after(:each) do
|
14
|
+
@page.stop
|
15
|
+
end
|
16
|
+
|
17
|
+
it "can find Selenium" do
|
18
|
+
@page.open "/"
|
19
|
+
@page.title.should eql("Google")
|
20
|
+
@page.search_for "Selenium"
|
21
|
+
@page.query_str.should eql("Selenium")
|
22
|
+
@page.title.should eql("Selenium - Google Search")
|
23
|
+
@page.text?("seniumhq.org").should be_true
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should return Sauce Labs website as top result" do
|
27
|
+
@page.open "/"
|
28
|
+
@page.title.should eql("Google")
|
29
|
+
@page.search_for "Sauce Labs"
|
30
|
+
@page.query_str.should eql("Sauce Labs")
|
31
|
+
@page.title.should eql("Sauce Labs - Google Search")
|
32
|
+
@page.text?("saucelabs.com").should be_true
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
|
37
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
<!DOCTYPE html
|
2
|
+
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
3
|
+
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
4
|
+
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
5
|
+
<head>
|
6
|
+
<title>SauceTest Report</title>
|
7
|
+
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
8
|
+
<meta http-equiv="Expires" content="-1" />
|
9
|
+
<meta http-equiv="Pragma" content="no-cache" />
|
10
|
+
</head>
|
11
|
+
<body>
|
12
|
+
|
13
|
+
|
14
|
+
|
15
|
+
<div class="sauce-report">
|
16
|
+
|
17
|
+
<div id="sauce-header">
|
18
|
+
<div id="summary">
|
19
|
+
<p id="file">/home/bart/Projects/sauce-test/examples/google_search/spec/selenium/google_spec.rb</p>
|
20
|
+
<p id="browser">
|
21
|
+
googlechrome 1 on Windows 2003
|
22
|
+
</p>
|
23
|
+
<p id="totals">
|
24
|
+
3 examples
|
25
|
+
, 1 FAILED
|
26
|
+
, 1 pending
|
27
|
+
</p>
|
28
|
+
<p id="duration">Duration: 42.071908 sec</p>
|
29
|
+
</div>
|
30
|
+
</div>
|
31
|
+
|
32
|
+
<div class="results">
|
33
|
+
<div class=example_group">
|
34
|
+
|
35
|
+
|
36
|
+
|
37
|
+
<dl>
|
38
|
+
<dt>Google Search</dt>
|
39
|
+
|
40
|
+
|
41
|
+
|
42
|
+
|
43
|
+
<dd class="failed">
|
44
|
+
<span class="failed_name">can find Selenium
|
45
|
+
<a class="job_link" href="http://saucelabs.com/jobs/dcd89827c758495ca782bcf1c66a1e05">(Sauce Labs Job)</a></span>
|
46
|
+
<div class="failure">
|
47
|
+
<div class="message"><pre>
|
48
|
+
expected true, got false
|
49
|
+
</pre></div>
|
50
|
+
<div class="backtrace"><pre>
|
51
|
+
/home/bart/Projects/sauce-test/examples/google_search/spec/selenium/google_spec.rb:23
|
52
|
+
</pre></div>
|
53
|
+
</div>
|
54
|
+
</dd>
|
55
|
+
|
56
|
+
|
57
|
+
|
58
|
+
|
59
|
+
|
60
|
+
|
61
|
+
|
62
|
+
|
63
|
+
|
64
|
+
|
65
|
+
|
66
|
+
<dd class="passed">
|
67
|
+
<span class="passed_name">should return Sauce Labs website as top result
|
68
|
+
<a class="job_link" href="http://saucelabs.com/jobs/d7e4b4e1f0ce6fe4985a4896ac70d025">(Sauce Labs Job)</a></span>
|
69
|
+
</dd>
|
70
|
+
|
71
|
+
|
72
|
+
|
73
|
+
|
74
|
+
|
75
|
+
|
76
|
+
|
77
|
+
|
78
|
+
|
79
|
+
|
80
|
+
|
81
|
+
<dd class="pending">
|
82
|
+
<span class="pending_name">is pend PENDING: blabla</span>
|
83
|
+
</dd>
|
84
|
+
|
85
|
+
|
86
|
+
|
87
|
+
|
88
|
+
</dl>
|
89
|
+
|
90
|
+
|
91
|
+
|
92
|
+
</div>
|
93
|
+
</div>
|
94
|
+
|
95
|
+
|
96
|
+
</body>
|
97
|
+
</html>
|
@@ -0,0 +1,104 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'stringio'
|
3
|
+
require 'thread'
|
4
|
+
require 'erb'
|
5
|
+
|
6
|
+
require 'sauce-test/runner'
|
7
|
+
|
8
|
+
module SauceTest
|
9
|
+
module Collection
|
10
|
+
|
11
|
+
class << self
|
12
|
+
include ERB::Util
|
13
|
+
|
14
|
+
def run(files, browsers, workers, config, report, type)
|
15
|
+
bin_path = File.expand_path(File.dirname(__FILE__) + '/../../bin')
|
16
|
+
ENV['PATH'] = bin_path + ':' + ENV['PATH']
|
17
|
+
test_array = []
|
18
|
+
test_array_mutex = Mutex.new
|
19
|
+
@test_result = []
|
20
|
+
test_result_mutex = Mutex.new
|
21
|
+
|
22
|
+
Dir.glob(files) do |file|
|
23
|
+
sauce_test = 'sauce_test -l -t ' + type.to_s + ' ' + file
|
24
|
+
result = `#{sauce_test}`
|
25
|
+
|
26
|
+
groups = YAML::load(StringIO.new result)['groups']
|
27
|
+
|
28
|
+
groups.each do |group|
|
29
|
+
group['examples'].each do |example|
|
30
|
+
test_array << [file,
|
31
|
+
group['name'],
|
32
|
+
example['name'],
|
33
|
+
example['full_name']]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
browser_threads = []
|
39
|
+
worker_threads = []
|
40
|
+
|
41
|
+
browsers.each do |b|
|
42
|
+
browser_threads << Thread.new(b) do |browser|
|
43
|
+
test_count = 0
|
44
|
+
test_count_mutex = Mutex.new
|
45
|
+
(1..workers).each do
|
46
|
+
worker_threads << Thread.new do
|
47
|
+
loop do
|
48
|
+
test = nil
|
49
|
+
test_count_mutex.synchronize do
|
50
|
+
test_array_mutex.synchronize{test = test_array[test_count]}
|
51
|
+
test_count += 1
|
52
|
+
end
|
53
|
+
break unless test
|
54
|
+
|
55
|
+
file = test[0]
|
56
|
+
group = test[1]
|
57
|
+
example = test[3]
|
58
|
+
report_file = '/' + file + '/' + example + '/' + browser['name'] +
|
59
|
+
'/' + browser['version'].to_s + '/' + browser['os'] + '/report.html'
|
60
|
+
|
61
|
+
sauce_test = 'sauce_test -t ' + type.to_s + ' ' +
|
62
|
+
'-c ' + config + ' ' +
|
63
|
+
'-r "' + report + report_file + '" ' +
|
64
|
+
'-b ' + browser['name'] + ' ' +
|
65
|
+
'-v ' + browser['version'].to_s + ' ' +
|
66
|
+
'-o "' + browser['os'] + '" ' +
|
67
|
+
'-e "' + example + '" ' + file
|
68
|
+
|
69
|
+
result = `#{sauce_test}`
|
70
|
+
|
71
|
+
test_result_mutex.synchronize do
|
72
|
+
example = test[2]
|
73
|
+
status = YAML::load(StringIO.new result)['status']
|
74
|
+
duration = YAML::load(StringIO.new result)['duration']
|
75
|
+
job_id = YAML::load(StringIO.new result)['groups'][0]['examples'][0]['job_id']
|
76
|
+
@test_result << [file,
|
77
|
+
group,
|
78
|
+
example,
|
79
|
+
browser,
|
80
|
+
status,
|
81
|
+
duration,
|
82
|
+
job_id,
|
83
|
+
report_file]
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
worker_threads.each{|t| t.join}
|
92
|
+
browser_threads.each{|t| t.join}
|
93
|
+
|
94
|
+
template_file = File.dirname(__FILE__) + '/templates/collection_report.erb'
|
95
|
+
template_str = IO.readlines(template_file).join
|
96
|
+
template = ERB.new(template_str)
|
97
|
+
File.new(report + '/report.html', 'w') << template.result(binding)
|
98
|
+
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module SauceTest
|
4
|
+
module Helpers
|
5
|
+
|
6
|
+
class SauceTestOptions
|
7
|
+
|
8
|
+
attr_reader :user,
|
9
|
+
:key,
|
10
|
+
:timeout,
|
11
|
+
:browser
|
12
|
+
|
13
|
+
attr_accessor :job_name
|
14
|
+
|
15
|
+
def initialize(browser = nil)
|
16
|
+
set_browser(browser || "firefox")
|
17
|
+
end
|
18
|
+
|
19
|
+
def parse_config(config_file)
|
20
|
+
config = YAML::load_file config_file
|
21
|
+
@user = config['login']
|
22
|
+
@key = config['key']
|
23
|
+
@timeout = config['timeout'] || 180
|
24
|
+
|
25
|
+
if(@user and @key) then true
|
26
|
+
else false end
|
27
|
+
end
|
28
|
+
|
29
|
+
def browser_json
|
30
|
+
%W({ "username": "#{@user}",) +
|
31
|
+
%W("access-key": "#{@key}",) +
|
32
|
+
%W("job-name": "#{@job_name}",) +
|
33
|
+
%W("os": "#{@browser['os']}",) +
|
34
|
+
%W("browser": "#{@browser['name']}",) +
|
35
|
+
%W("browser-major-version": "#{@browser['version']}" })
|
36
|
+
end
|
37
|
+
|
38
|
+
def set_browser_version(version)
|
39
|
+
@browser['version'] = version
|
40
|
+
end
|
41
|
+
|
42
|
+
def set_os(os)
|
43
|
+
@browser['os'] = os
|
44
|
+
end
|
45
|
+
|
46
|
+
protected
|
47
|
+
|
48
|
+
def set_browser(name)
|
49
|
+
@browser = {}
|
50
|
+
case name
|
51
|
+
when "firefox", "chrome"
|
52
|
+
@browser['name'] = "firefox"
|
53
|
+
@browser['version'] = 3
|
54
|
+
@browser['os'] = "Linux"
|
55
|
+
when "iehta", "iexplore"
|
56
|
+
@browser['name'] = "iexplore"
|
57
|
+
@browser['version'] = 7
|
58
|
+
@browser['os'] = "Windows 2003"
|
59
|
+
when "safari"
|
60
|
+
@browser['name'] = name
|
61
|
+
@browser['version'] = 4
|
62
|
+
@browser['os'] = "Windows 2003"
|
63
|
+
when "opera"
|
64
|
+
@browser['name'] = name
|
65
|
+
@browser['version'] = 9
|
66
|
+
@browser['os'] = "Windows 2003"
|
67
|
+
when "googlechrome"
|
68
|
+
@browser['name'] = name
|
69
|
+
@browser['version'] = 1
|
70
|
+
@browser['os'] = "Windows 2003"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'sauce-test/collection'
|
2
|
+
|
3
|
+
module SauceTest
|
4
|
+
module Rake
|
5
|
+
|
6
|
+
class BaseTask
|
7
|
+
|
8
|
+
attr_accessor :files, :browsers, :report, :workers, :config
|
9
|
+
|
10
|
+
def initialize(name)
|
11
|
+
@name = name
|
12
|
+
@browsers = [{'os' => "Linux", 'name' => "firefox", 'version' => 3},
|
13
|
+
{'os' => "Windows 2003", 'name' => "iexplore", 'version' => 7}]
|
14
|
+
@workers = 1
|
15
|
+
@config = 'config/sauce.yml'
|
16
|
+
@report = 'reports/sauce_test'
|
17
|
+
|
18
|
+
yield self if block_given?
|
19
|
+
|
20
|
+
define_task
|
21
|
+
end
|
22
|
+
|
23
|
+
def define_task
|
24
|
+
task @name do
|
25
|
+
SauceTest::Collection.run(@files, @browsers, @workers, @config, @report, @type)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
require 'sauce-test/rspec/rake_tasks'
|
35
|
+
require 'sauce-test/testunit/rake_tasks'
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'stringio'
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'spec'
|
5
|
+
|
6
|
+
require 'sauce-test/rspec/sauce_formatter'
|
7
|
+
require 'sauce-test/selenium/client_driver_extension'
|
8
|
+
|
9
|
+
module SauceTest
|
10
|
+
module RSpec
|
11
|
+
|
12
|
+
module Runner
|
13
|
+
|
14
|
+
class << self
|
15
|
+
def run(options)
|
16
|
+
@options = options
|
17
|
+
rspec_init
|
18
|
+
Spec::Runner.run
|
19
|
+
report = SauceTest::RSpec::Runner::SauceFormatter.report
|
20
|
+
report.test_file = @options.test_file
|
21
|
+
report
|
22
|
+
end
|
23
|
+
|
24
|
+
def rspec_init
|
25
|
+
out_str = StringIO.new
|
26
|
+
err_str = StringIO.new
|
27
|
+
spec_opt = Spec::Runner::Options.new(@err_str, @out_str)
|
28
|
+
|
29
|
+
if @options.list
|
30
|
+
spec_opt.dry_run = true
|
31
|
+
else
|
32
|
+
SauceTest::RSpec::Runner::SauceFormatter.set_sauce_test_options @options.sauce_options
|
33
|
+
end
|
34
|
+
|
35
|
+
if @options.example
|
36
|
+
spec_opt.parse_example @options.example
|
37
|
+
end
|
38
|
+
|
39
|
+
spec_opt.parse_format SauceTest::RSpec::Runner::SauceFormatter.to_s
|
40
|
+
spec_opt.files << @options.test_file
|
41
|
+
|
42
|
+
Spec::Runner.use spec_opt
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'spec/runner/formatter/base_formatter'
|
2
|
+
|
3
|
+
module SauceTest
|
4
|
+
module RSpec
|
5
|
+
module Runner
|
6
|
+
|
7
|
+
class SauceFormatter < Spec::Runner::Formatter::BaseFormatter
|
8
|
+
|
9
|
+
def initialize(options, output)
|
10
|
+
@dry_run = !!options.dry_run
|
11
|
+
@selenium = Selenium::Client::Driver
|
12
|
+
@selenium.set_sauce_options(@@sauce_options) unless @dry_run
|
13
|
+
@@report = SauceTest::Runner::Report.new
|
14
|
+
@@report.browser = @@sauce_options.browser unless @dry_run
|
15
|
+
end
|
16
|
+
|
17
|
+
def example_group_started(group_proxy)
|
18
|
+
@group = group_proxy.description
|
19
|
+
end
|
20
|
+
|
21
|
+
def example_started(example_proxy)
|
22
|
+
example = example_proxy.description
|
23
|
+
if @dry_run
|
24
|
+
@@report.add_example(@group,
|
25
|
+
{'name' => example,
|
26
|
+
'status' => 'passed'})
|
27
|
+
else
|
28
|
+
@@sauce_options.job_name = @group + ' ' + example unless @dry_run
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def example_pending(example_proxy, message)
|
33
|
+
@@report.add_example(@group,
|
34
|
+
{'name' => example_proxy.description,
|
35
|
+
'status' => 'pending',
|
36
|
+
'message' => message})
|
37
|
+
end
|
38
|
+
|
39
|
+
def example_passed(example_proxy)
|
40
|
+
@@report.add_example(@group,
|
41
|
+
{'name' => example_proxy.description,
|
42
|
+
'status' => 'passed',
|
43
|
+
'job_id' => @selenium.last_session_id }
|
44
|
+
) unless @dry_run
|
45
|
+
end
|
46
|
+
|
47
|
+
def example_failed(example_proxy, counter, failure)
|
48
|
+
failure.exception.backtrace[0] =~ /(.*):(\d+)/
|
49
|
+
file = $1
|
50
|
+
line = $2.to_i
|
51
|
+
@@report.add_example(@group,
|
52
|
+
{'name' => example_proxy.description,
|
53
|
+
'status' => 'failed',
|
54
|
+
'job_id' => @selenium.last_session_id,
|
55
|
+
'message' => failure.exception.message,
|
56
|
+
'file' => file,
|
57
|
+
'line' => line})
|
58
|
+
end
|
59
|
+
|
60
|
+
def dump_summary(duration, example_count, failure_count, pending_count)
|
61
|
+
@@report.duration = duration
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.report
|
65
|
+
@@report
|
66
|
+
end
|
67
|
+
|
68
|
+
def self.set_sauce_test_options(options)
|
69
|
+
@@sauce_options = options.clone
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'fileutils'
|
3
|
+
|
4
|
+
require 'sauce-test/runner/options'
|
5
|
+
require 'sauce-test/runner/report'
|
6
|
+
require 'sauce-test/rspec/runner'
|
7
|
+
require 'sauce-test/testunit/runner'
|
8
|
+
|
9
|
+
module SauceTest
|
10
|
+
module Runner
|
11
|
+
|
12
|
+
class << self
|
13
|
+
def run(opts = nil)
|
14
|
+
options = opts || SauceTest::Runner::Options.new
|
15
|
+
|
16
|
+
case options.test_type
|
17
|
+
when :unit then runner = SauceTest::TestUnit::Runner
|
18
|
+
when :spec then runner = SauceTest::RSpec::Runner
|
19
|
+
end
|
20
|
+
|
21
|
+
report = runner.run(options)
|
22
|
+
YAML.dump(report.to_hash, $stdout)
|
23
|
+
|
24
|
+
if report_name = options.report_file and !options.list
|
25
|
+
FileUtils.mkdir_p File.dirname(report_name)
|
26
|
+
File.new(report_name, 'w') << report.to_html
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
|
3
|
+
require 'sauce-test/version'
|
4
|
+
require 'sauce-test/helpers/sauce_test_options'
|
5
|
+
|
6
|
+
module SauceTest
|
7
|
+
module Runner
|
8
|
+
|
9
|
+
class Options
|
10
|
+
|
11
|
+
attr_reader :list,
|
12
|
+
:test_type,
|
13
|
+
:example,
|
14
|
+
:report_file,
|
15
|
+
:test_file,
|
16
|
+
:sauce_options
|
17
|
+
|
18
|
+
def initialize(opt_array = ARGV)
|
19
|
+
@browser = {}
|
20
|
+
|
21
|
+
@opt_parser = OptionParser.new
|
22
|
+
|
23
|
+
@opt_parser.banner = "Usage: sauce_test [options] FILE"
|
24
|
+
|
25
|
+
@opt_parser.on(*OPTIONS[:list]) {|list| @list = true}
|
26
|
+
@opt_parser.on(*OPTIONS[:type]) {|type| parse_type type}
|
27
|
+
@opt_parser.on(*OPTIONS[:example]) {|example| @example = example}
|
28
|
+
@opt_parser.on(*OPTIONS[:report]) {|report| @report_file = File.expand_path report}
|
29
|
+
@opt_parser.on(*OPTIONS[:browser]) {|browser| @browser['name'] = browser}
|
30
|
+
@opt_parser.on(*OPTIONS[:version]) {|version| @browser['version'] = version}
|
31
|
+
@opt_parser.on(*OPTIONS[:os]) {|os| @browser['os'] = os}
|
32
|
+
@opt_parser.on(*OPTIONS[:config]) {|config| parse_config(config)}
|
33
|
+
@opt_parser.on(*OPTIONS[:help]) {show_help}
|
34
|
+
|
35
|
+
@opt_parser.separator EXAMPLES
|
36
|
+
|
37
|
+
opt_rest = @opt_parser.parse(opt_array)
|
38
|
+
|
39
|
+
create_sauce_options unless list
|
40
|
+
|
41
|
+
parse_test_file(opt_rest[0])
|
42
|
+
end
|
43
|
+
|
44
|
+
protected
|
45
|
+
|
46
|
+
def create_sauce_options
|
47
|
+
@sauce_options = SauceTest::Helpers::SauceTestOptions.new(@browser['name'])
|
48
|
+
parse_config(DEFAULT_CFG) unless @config_file
|
49
|
+
show_help unless @sauce_options.parse_config(@config_file)
|
50
|
+
@sauce_options.set_os @browser['os'] if @browser['os']
|
51
|
+
@sauce_options.set_browser_version @browser['version'] if @browser['version']
|
52
|
+
end
|
53
|
+
|
54
|
+
def show_help
|
55
|
+
puts SauceTest::VERSION::SUMMARY
|
56
|
+
puts @opt_parser
|
57
|
+
exit
|
58
|
+
end
|
59
|
+
|
60
|
+
def parse_config(path)
|
61
|
+
@config_file = File.expand_path path
|
62
|
+
show_help unless File.file?(@config_file)
|
63
|
+
end
|
64
|
+
|
65
|
+
def parse_test_file(path)
|
66
|
+
if path and File.file?(path)
|
67
|
+
then @test_file = File.expand_path path
|
68
|
+
else show_help end
|
69
|
+
|
70
|
+
unless @test_type
|
71
|
+
case File.basename @test_file
|
72
|
+
when /_test\.rb/ then @test_type = :unit
|
73
|
+
when /_spec\.rb/ then @test_type = :spec
|
74
|
+
else show_help
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def parse_type(type_str)
|
80
|
+
case type_str
|
81
|
+
when 'unit', 'u' then @test_type = :unit
|
82
|
+
when 'spec', 's'then @test_type = :spec
|
83
|
+
else show_help
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
DEFAULT_CFG = './config/sauce.yml'
|
88
|
+
|
89
|
+
OPTIONS = {
|
90
|
+
:example => ["-e", "--example NAME", "Run single test case. Use -l to list NAMEs"],
|
91
|
+
:list => ["-l", "--list", "List all tests"],
|
92
|
+
:type => ["-t", "--type TYPE", "Specify type of test file(optional)",
|
93
|
+
"spec|s :RSpec",
|
94
|
+
"unit|u :Test::Unit"],
|
95
|
+
:report => ["-r", "--report PATH", "HTML report directory path"],
|
96
|
+
:config => ["-c", "--config PATH", "config file path(#{DEFAULT_CFG} by default)"],
|
97
|
+
:browser => ["-b", "--browser STRING", "Sauce Labs JSON web browser string(firefox by default)"],
|
98
|
+
:os => ["-o", "--os STRING", "Sauce Labs JSON operating system string(optional)"],
|
99
|
+
:version => ["-v", "--version NUM", "Sauce Labs JSON browser version number(optional)"],
|
100
|
+
:help => ["-h", "--help", "You're looking at it"]
|
101
|
+
}
|
102
|
+
|
103
|
+
EXAMPLES = [ "\nUsage Examples:\n",
|
104
|
+
" sauce_test example_test.rb",
|
105
|
+
' sauce_test -r "./reports/" example_spec.rb',
|
106
|
+
" sauce_test -l example_test.rb",
|
107
|
+
' sauce_test -b "iexplore" example_test.rb',
|
108
|
+
' sauce_test -b "firefox" -v 2 -o "Windows 2003" example_spec.rb',
|
109
|
+
' sauce_test -t unit -e "test_something(ExampleTest)" example_test.rb',
|
110
|
+
' sauce_test -t s -e "ExampleTest can do something" example_spec.rb']
|
111
|
+
|
112
|
+
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'erb'
|
2
|
+
|
3
|
+
module SauceTest
|
4
|
+
module Runner
|
5
|
+
|
6
|
+
class Report
|
7
|
+
|
8
|
+
include ERB::Util
|
9
|
+
|
10
|
+
attr_accessor :test_file, :browser, :duration
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
@groups = []
|
14
|
+
@status = 'passed'
|
15
|
+
@count = {'total' => 0,
|
16
|
+
'pending' => 0,
|
17
|
+
'failed' => 0}
|
18
|
+
end
|
19
|
+
|
20
|
+
def add_example(group_name, descripton)
|
21
|
+
descripton['full_name'] = group_name + ' ' + descripton['name']
|
22
|
+
@count['total'] += 1
|
23
|
+
status = descripton['status']
|
24
|
+
@count['pending'] += 1 if status == 'pending'
|
25
|
+
@count['failed'] += 1 if status == 'failed'
|
26
|
+
|
27
|
+
group = nil
|
28
|
+
@groups.each do |g|
|
29
|
+
if g['name'] == group_name
|
30
|
+
group = g
|
31
|
+
g['examples'] << descripton
|
32
|
+
g['status'] = status if status != 'passed' and g['status'] != 'failed'
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
unless group
|
37
|
+
group = {'name' => group_name,
|
38
|
+
'status' => status,
|
39
|
+
'examples' => [descripton]}
|
40
|
+
@groups << group
|
41
|
+
end
|
42
|
+
|
43
|
+
@status = status if status != 'passed' and @status != 'failed'
|
44
|
+
end
|
45
|
+
|
46
|
+
def to_hash
|
47
|
+
{'file' => test_file,
|
48
|
+
'browser' => browser,
|
49
|
+
'duration' => duration,
|
50
|
+
'status' => @status,
|
51
|
+
'count' => @count,
|
52
|
+
'groups' => @groups}
|
53
|
+
end
|
54
|
+
|
55
|
+
def to_html
|
56
|
+
template_file = File.dirname(__FILE__) + '/../templates/test_report.erb'
|
57
|
+
template_str = IO.readlines(template_file).join
|
58
|
+
template = ERB.new(template_str)
|
59
|
+
template.result(binding)
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require "selenium/client"
|
3
|
+
|
4
|
+
module Selenium
|
5
|
+
module Client
|
6
|
+
|
7
|
+
class Driver
|
8
|
+
|
9
|
+
def start_new_browser_session(options={})
|
10
|
+
sauce_init
|
11
|
+
super(options)
|
12
|
+
@@last_session_id = @session_id
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.set_sauce_options(sauce_options)
|
16
|
+
@@sauce_options = sauce_options
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.last_session_id
|
20
|
+
@@last_session_id
|
21
|
+
end
|
22
|
+
|
23
|
+
protected
|
24
|
+
|
25
|
+
def sauce_init
|
26
|
+
@host = "saucelabs.com"
|
27
|
+
@port = 4444
|
28
|
+
@browser_string = @@sauce_options.browser_json
|
29
|
+
@default_timeout_in_seconds = @@sauce_options.timeout
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
<!DOCTYPE html
|
2
|
+
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
3
|
+
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
4
|
+
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
5
|
+
<head>
|
6
|
+
<title>SauceTest Report</title>
|
7
|
+
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
8
|
+
<meta http-equiv="Expires" content="-1" />
|
9
|
+
<meta http-equiv="Pragma" content="no-cache" />
|
10
|
+
</head>
|
11
|
+
<body>
|
12
|
+
|
13
|
+
|
14
|
+
|
15
|
+
<div class="sauce-report">
|
16
|
+
|
17
|
+
<div class="results">
|
18
|
+
|
19
|
+
|
20
|
+
<% @test_result.each do |result| %>
|
21
|
+
|
22
|
+
<div>
|
23
|
+
<div>
|
24
|
+
File: <a href="<%=u(result[7])%>"><%=result[0]%></a>
|
25
|
+
</div>
|
26
|
+
|
27
|
+
<div>
|
28
|
+
Test: <%=result[1] + ' ' + result[2]%>
|
29
|
+
</div>
|
30
|
+
|
31
|
+
<div>
|
32
|
+
Browser: <%=result[3]['name'] + ' ' + result[3]['version'].to_s + ' on ' + result[3]['os']%>
|
33
|
+
</div>
|
34
|
+
|
35
|
+
<div>
|
36
|
+
Status: <%=result[4]%>
|
37
|
+
</div>
|
38
|
+
|
39
|
+
|
40
|
+
<div>
|
41
|
+
Duration: <%=result[5]%> sec
|
42
|
+
</div>
|
43
|
+
|
44
|
+
<div>
|
45
|
+
<a href="http://saucelabs.com/jobs/<%=result[6]%>">Sauce Labs Report</a>
|
46
|
+
</div>
|
47
|
+
|
48
|
+
</br>
|
49
|
+
</div>
|
50
|
+
|
51
|
+
<% end %>
|
52
|
+
|
53
|
+
</div>
|
54
|
+
|
55
|
+
|
56
|
+
</body>
|
57
|
+
</html>
|
Binary file
|
@@ -0,0 +1,81 @@
|
|
1
|
+
<!DOCTYPE html
|
2
|
+
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
3
|
+
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
4
|
+
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
5
|
+
<head>
|
6
|
+
<title>SauceTest Report</title>
|
7
|
+
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
8
|
+
<meta http-equiv="Expires" content="-1" />
|
9
|
+
<meta http-equiv="Pragma" content="no-cache" />
|
10
|
+
</head>
|
11
|
+
<body>
|
12
|
+
|
13
|
+
|
14
|
+
|
15
|
+
<div class="sauce-report">
|
16
|
+
|
17
|
+
<div id="sauce-header">
|
18
|
+
<div id="summary">
|
19
|
+
<p id="file"><%= test_file %></p>
|
20
|
+
<p id="browser">
|
21
|
+
<%= browser['name'] + ' ' + browser['version'].to_s + ' on ' + browser['os'] %>
|
22
|
+
</p>
|
23
|
+
<p id="totals">
|
24
|
+
<%= @count['total'].to_s + ' examples' %>
|
25
|
+
<%= ', ' + @count['failed'].to_s + ' FAILED' %>
|
26
|
+
<%= ', ' + @count['pending'].to_s + ' pending' %>
|
27
|
+
</p>
|
28
|
+
<p id="duration">Duration: <%= duration %> sec</p>
|
29
|
+
</div>
|
30
|
+
</div>
|
31
|
+
|
32
|
+
<div class="results">
|
33
|
+
<div class=example_group">
|
34
|
+
|
35
|
+
<% @groups.each do |g| %>
|
36
|
+
|
37
|
+
<dl>
|
38
|
+
<dt><%= h(g['name']) %></dt>
|
39
|
+
|
40
|
+
<% g['examples'].each do |e| %>
|
41
|
+
|
42
|
+
<% if e['status'] == 'failed' %>
|
43
|
+
<dd class="failed">
|
44
|
+
<span class="failed_name"><%= h(e['name']) %>
|
45
|
+
<a class="job_link" href="<%= 'http://saucelabs.com/jobs/' + e['job_id'] %>">(Sauce Labs Job)</a></span>
|
46
|
+
<div class="failure">
|
47
|
+
<div class="message"><pre>
|
48
|
+
<%= h(e['message']) %>
|
49
|
+
</pre></div>
|
50
|
+
<div class="backtrace"><pre>
|
51
|
+
<%= e['file'] + ':' + e['line'].to_s %>
|
52
|
+
</pre></div>
|
53
|
+
</div>
|
54
|
+
</dd>
|
55
|
+
<% end %>
|
56
|
+
|
57
|
+
<% if e['status'] == 'passed' %>
|
58
|
+
<dd class="passed">
|
59
|
+
<span class="passed_name"><%= h(e['name']) %>
|
60
|
+
<a class="job_link" href="<%= 'http://saucelabs.com/jobs/' + e['job_id'] %>">(Sauce Labs Job)</a></span>
|
61
|
+
</dd>
|
62
|
+
<% end %>
|
63
|
+
|
64
|
+
<% if e['status'] == 'pending' %>
|
65
|
+
<dd class="pending">
|
66
|
+
<span class="pending_name"><%= h(e['name'] + ' PENDING: ' + e['message']) %></span>
|
67
|
+
</dd>
|
68
|
+
<% end %>
|
69
|
+
|
70
|
+
<% end %>
|
71
|
+
|
72
|
+
</dl>
|
73
|
+
|
74
|
+
<% end %>
|
75
|
+
|
76
|
+
</div>
|
77
|
+
</div>
|
78
|
+
|
79
|
+
|
80
|
+
</body>
|
81
|
+
</html>
|
metadata
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: sauce-test
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: "0.1"
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Bart Wadolowski
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-05-25 00:00:00 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: rspec
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "0"
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: selenium-client
|
27
|
+
type: :runtime
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: "0"
|
34
|
+
version:
|
35
|
+
description:
|
36
|
+
email: bart@saucelabs.com
|
37
|
+
executables:
|
38
|
+
- sauce_test
|
39
|
+
extensions: []
|
40
|
+
|
41
|
+
extra_rdoc_files: []
|
42
|
+
|
43
|
+
files:
|
44
|
+
- bin/sauce_test
|
45
|
+
- lib/sauce-test
|
46
|
+
- lib/sauce-test/rake
|
47
|
+
- lib/sauce-test/rake/tasks.rb
|
48
|
+
- lib/sauce-test/runner.rb
|
49
|
+
- lib/sauce-test/rspec
|
50
|
+
- lib/sauce-test/rspec/runner.rb
|
51
|
+
- lib/sauce-test/rspec/sauce_formatter.rb
|
52
|
+
- lib/sauce-test/rspec/rake_tasks.rb
|
53
|
+
- lib/sauce-test/templates
|
54
|
+
- lib/sauce-test/templates/test_report.erb
|
55
|
+
- lib/sauce-test/templates/collection_report.erb
|
56
|
+
- lib/sauce-test/templates/img
|
57
|
+
- lib/sauce-test/templates/img/saucelabs.jpg
|
58
|
+
- lib/sauce-test/collection.rb
|
59
|
+
- lib/sauce-test/selenium
|
60
|
+
- lib/sauce-test/selenium/client_driver_extension.rb
|
61
|
+
- lib/sauce-test/version.rb
|
62
|
+
- lib/sauce-test/runner
|
63
|
+
- lib/sauce-test/runner/report.rb
|
64
|
+
- lib/sauce-test/runner/options.rb
|
65
|
+
- lib/sauce-test/testunit
|
66
|
+
- lib/sauce-test/testunit/runner.rb
|
67
|
+
- lib/sauce-test/testunit/rake_tasks.rb
|
68
|
+
- lib/sauce-test/helpers
|
69
|
+
- lib/sauce-test/helpers/sauce_test_options.rb
|
70
|
+
- examples/google_search
|
71
|
+
- examples/google_search/spec
|
72
|
+
- examples/google_search/spec/selenium
|
73
|
+
- examples/google_search/spec/selenium/google_spec.rb
|
74
|
+
- examples/google_search/spec/selenium/r.html
|
75
|
+
- examples/google_search/spec/spec_helper.rb
|
76
|
+
- examples/google_search/Rakefile
|
77
|
+
- examples/google_search/config
|
78
|
+
- examples/google_search/config/sauce.yml
|
79
|
+
- examples/google_search/helpers
|
80
|
+
- examples/google_search/helpers/google_test_helper.rb
|
81
|
+
- README.rdoc
|
82
|
+
- TODO.rdoc
|
83
|
+
has_rdoc: true
|
84
|
+
homepage: http://www.saucelabs.com
|
85
|
+
post_install_message:
|
86
|
+
rdoc_options: []
|
87
|
+
|
88
|
+
require_paths:
|
89
|
+
- lib
|
90
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
91
|
+
requirements:
|
92
|
+
- - ">="
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: "0"
|
95
|
+
version:
|
96
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
97
|
+
requirements:
|
98
|
+
- - ">="
|
99
|
+
- !ruby/object:Gem::Version
|
100
|
+
version: "0"
|
101
|
+
version:
|
102
|
+
requirements: []
|
103
|
+
|
104
|
+
rubyforge_project: sauce-test
|
105
|
+
rubygems_version: 1.3.1
|
106
|
+
signing_key:
|
107
|
+
specification_version: 2
|
108
|
+
summary: Run your Selenium tests on Sauce Labs cloud
|
109
|
+
test_files: []
|
110
|
+
|