selenium-connect 3.1.2 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG.md +8 -0
- data/README.md +3 -2
- data/lib/sauce/sauce_facade.rb +73 -0
- data/lib/selenium_connect/configuration.rb +1 -1
- data/lib/selenium_connect/job.rb +39 -47
- data/lib/selenium_connect.rb +3 -1
- data/selenium-connect.gemspec +2 -2
- data/spec/integration/lib/selenium_connect/runners/sauce_spec.rb +5 -5
- data/spec/support/example.yaml +1 -0
- data/spec/unit/lib/sauce/sauce_facade_spec.rb +55 -0
- data/spec/unit/lib/selenium_connect/job_spec.rb +2 -1
- data/spec/unit/lib/selenium_connect_spec.rb +1 -0
- metadata +7 -4
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,11 @@
|
|
1
|
+
#3.2.0 (2013-07-15)
|
2
|
+
added better error handling to the sauce api calls and improved naming convention for job assets
|
3
|
+
|
4
|
+
- Bumped version to 3.2.0 to prepare for release.
|
5
|
+
- cleaned up the way the job log was saving
|
6
|
+
- added one more error check around the sauce data
|
7
|
+
- refactored the log saving and sauce api access, now can configure the api time out and the requests will poll when trying to fetch the data from the server
|
8
|
+
|
1
9
|
#3.1.2 (2013-07-14)
|
2
10
|
removed references to curb
|
3
11
|
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
#selenium-connect 3.
|
1
|
+
#selenium-connect 3.2.0 (2013-07-15)
|
2
2
|
|
3
3
|
[](http://badge.fury.io/rb/selenium-connect) [](https://travis-ci.org/arrgyle/selenium-connect) [](https://codeclimate.com/github/arrgyle/selenium-connect) [](https://coveralls.io/r/arrgyle/selenium-connect?branch=develop)
|
4
4
|
|
@@ -90,6 +90,7 @@ sauce_username: 'test_user_name'
|
|
90
90
|
sauce_api_key:
|
91
91
|
browser_version:
|
92
92
|
description: #sauce job/test description
|
93
|
+
api_timeout: #how many seconds we should try to get the assets (default 10)
|
93
94
|
```
|
94
95
|
|
95
96
|
You can pass parameters into the new config object like:
|
@@ -124,7 +125,7 @@ report = job.finish failed: true, failshot: true
|
|
124
125
|
The `report` is simply a container for arbitrary data. Right now we are passing back the sauce details. Here is an example of `report.data` for a failed job:
|
125
126
|
|
126
127
|
```
|
127
|
-
{:
|
128
|
+
{:assets=>{:server_log=>"failed_serverlog_failing_sauce_job_3ee1fddf4032476fa3f9de94298766ae.log", :job_data_log=>"failed_saucejob_failing_sauce_job_3ee1fddf4032476fa3f9de94298766ae.log"}, :sauce_data=>{:id=>"3ee1fddf4032476fa3f9de94298766ae", :"custom-data"=>nil, :owner=>"testing_arrgyle", :status=>"in progress", :error=>nil, :name=>"failing_sauce_job", :browser=>"iexplore", :browser_version=>"7.0.5730.13", :os=>"Windows 2003", :creation_time=>1373916900, :start_time=>1373916901, :end_time=>0, :video_url=>"http://saucelabs.com/jobs/3ee1fddf4032476fa3f9de94298766ae/video.flv", :log_url=>"http://saucelabs.com/jobs/3ee1fddf4032476fa3f9de94298766ae/selenium-server.log", :public=>nil, :tags=>[], :passed=>false}}
|
128
129
|
```
|
129
130
|
|
130
131
|
## Contribution Guidelines
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# Encoding: utf-8
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
require 'sauce_whisk'
|
5
|
+
require 'rest_client'
|
6
|
+
require 'sauce/client'
|
7
|
+
|
8
|
+
module Sauce
|
9
|
+
# wrapps the sauce whick api accessor with sane error handling
|
10
|
+
class SauceFacade
|
11
|
+
|
12
|
+
attr_accessor :job_id, :timeout
|
13
|
+
|
14
|
+
def initialize(timeout = 10)
|
15
|
+
@timeout = timeout ||= 10
|
16
|
+
end
|
17
|
+
|
18
|
+
def fail_job
|
19
|
+
requires_job_id
|
20
|
+
SauceWhisk::Jobs.fail_job @job_id
|
21
|
+
end
|
22
|
+
|
23
|
+
def pass_job
|
24
|
+
requires_job_id
|
25
|
+
SauceWhisk::Jobs.pass_job @job_id
|
26
|
+
end
|
27
|
+
|
28
|
+
def fetch_last_screenshot
|
29
|
+
requires_job_id
|
30
|
+
polling_api_request @timeout do
|
31
|
+
SauceWhisk::Jobs.fetch_asset @job_id, 'final_screenshot.png'
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def fetch_server_log
|
36
|
+
requires_job_id
|
37
|
+
polling_api_request @timeout do
|
38
|
+
SauceWhisk::Jobs.fetch_asset @job_id, 'selenium-server.log'
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def fetch_job_data
|
43
|
+
requires_job_id
|
44
|
+
# TODO let's switch this over to use whisk as well
|
45
|
+
# This is used because it's easy to get all the data out of the job
|
46
|
+
begin
|
47
|
+
job = Sauce::Job.find @job_id
|
48
|
+
JSON.parse job.to_json
|
49
|
+
rescue StandardError => e
|
50
|
+
puts "An error occured while fetching the job data: #{e.message}"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def requires_job_id
|
57
|
+
raise ArgumentError, "#{caller[0][/`.*'/][1..-2]} requires that the job_id be set in this object." unless @job_id
|
58
|
+
end
|
59
|
+
|
60
|
+
def polling_api_request(timeout)
|
61
|
+
begin
|
62
|
+
sleep 1
|
63
|
+
yield
|
64
|
+
rescue RestClient::Exception => e
|
65
|
+
if timeout > 0
|
66
|
+
polling_api_request(timeout - 1) { yield }
|
67
|
+
else
|
68
|
+
puts "Request timed out after #{timeout} with: #{e.message}"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
data/lib/selenium_connect/job.rb
CHANGED
@@ -1,26 +1,27 @@
|
|
1
1
|
# Encoding: utf-8
|
2
|
+
|
2
3
|
require 'selenium_connect/runner'
|
3
4
|
require 'sauce/client'
|
4
5
|
require 'rest_client'
|
5
6
|
require 'selenium-webdriver'
|
6
7
|
require 'json'
|
7
8
|
require 'sauce_whisk'
|
8
|
-
|
9
9
|
# selenium connect
|
10
10
|
class SeleniumConnect
|
11
11
|
# encapsulates the creation of a driver and a run
|
12
12
|
class Job
|
13
13
|
|
14
|
-
def initialize(config, report_factory)
|
14
|
+
def initialize(config, report_factory, sauce_facade)
|
15
15
|
@config = config
|
16
16
|
@report_factory = report_factory
|
17
|
+
@sauce_facade = sauce_facade
|
17
18
|
end
|
18
19
|
|
19
20
|
# Creates and returns the driver, using options passed in
|
20
21
|
def start(opts = {})
|
21
|
-
|
22
|
+
@job_name = slugify_name opts[:name] if opts.has_key? :name
|
22
23
|
# TODO this could be refactored out into an options parser of sorts
|
23
|
-
@config.description =
|
24
|
+
@config.description = @job_name ||= 'unnamed_job'
|
24
25
|
@driver = Runner.new(@config).driver
|
25
26
|
end
|
26
27
|
|
@@ -30,63 +31,54 @@ class SeleniumConnect
|
|
30
31
|
# extracted from the earlier main finish
|
31
32
|
begin
|
32
33
|
@driver.quit
|
33
|
-
data = {}
|
34
|
-
if @config.host == 'saucelabs'
|
35
|
-
job_id = @driver.session_id
|
36
|
-
if opts.has_key?(:failed) && opts[:failed]
|
37
|
-
fail_job job_id
|
38
|
-
if opts.has_key?(:failshot) && opts[:failshot]
|
39
|
-
data[:failshot] = save_last_screenshot job_id
|
40
|
-
end
|
41
|
-
end
|
42
|
-
if opts.has_key?(:passed) && opts[:passed]
|
43
|
-
pass_job job_id
|
44
|
-
end
|
45
|
-
data.merge! fetch_logs(job_id)
|
46
|
-
end
|
34
|
+
@data = { assets: {} }
|
35
|
+
process_sauce_logs(opts) if @config.host == 'saucelabs'
|
47
36
|
# rubocop:disable HandleExceptions
|
48
37
|
rescue Selenium::WebDriver::Error::WebDriverError
|
49
38
|
# rubocop:enable HandleExceptions
|
50
39
|
end
|
51
|
-
|
52
|
-
@report_factory.build :job, report_data
|
40
|
+
@report_factory.build :job, @data
|
53
41
|
end
|
54
42
|
|
55
43
|
private
|
56
44
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
filename
|
68
|
-
rescue RestClient::ResourceNotFound
|
69
|
-
puts 'Unable to download image!'
|
45
|
+
def process_sauce_logs(opts = {})
|
46
|
+
job_id = @driver.session_id
|
47
|
+
@sauce_facade.job_id = job_id
|
48
|
+
if opts.has_key?(:failed) && opts[:failed]
|
49
|
+
status = 'failed'
|
50
|
+
@sauce_facade.fail_job
|
51
|
+
if opts.has_key?(:failshot) && opts[:failshot]
|
52
|
+
screenshot = @sauce_facade.fetch_last_screenshot
|
53
|
+
@data[:assets][:failshot] = save_asset("#{status}_failshot_#{@job_name}_#{job_id}.png", screenshot) if screenshot
|
54
|
+
end
|
70
55
|
end
|
71
|
-
|
56
|
+
if opts.has_key?(:passed) && opts[:passed]
|
57
|
+
status = 'passed'
|
58
|
+
@sauce_facade.pass_job
|
59
|
+
end
|
60
|
+
server_log = @sauce_facade.fetch_server_log
|
61
|
+
@data[:assets][:server_log] = save_asset("#{status}_serverlog_#{@job_name}_#{job_id}.log", server_log) if server_log
|
72
62
|
|
73
|
-
|
74
|
-
|
63
|
+
job_data = @sauce_facade.fetch_job_data
|
64
|
+
@data[:sauce_data] = job_data if job_data
|
65
|
+
|
66
|
+
job_data_log_file = "#{status}_saucejob_#{@job_name}_#{job_id}.log"
|
67
|
+
@data[:assets][:job_data_log] = job_data_log_file
|
68
|
+
@data = symbolize_keys @data
|
69
|
+
save_asset(job_data_log_file, @data)
|
75
70
|
end
|
76
71
|
|
77
|
-
def
|
78
|
-
|
72
|
+
def save_asset(filename, asset)
|
73
|
+
if @config.log
|
74
|
+
asset_file = File.join(Dir.getwd, @config.log, filename)
|
75
|
+
File.open(asset_file, 'w') { |f| f.write asset }
|
76
|
+
filename
|
77
|
+
end
|
79
78
|
end
|
80
79
|
|
81
|
-
def
|
82
|
-
|
83
|
-
# Seemingly need to wait slightly for the images to be processed
|
84
|
-
sleep(2)
|
85
|
-
filename = "sauce_job_#{job_id}.log"
|
86
|
-
server_log = SauceWhisk::Jobs.fetch_asset job_id, 'selenium-server.log'
|
87
|
-
log_file = File.join(Dir.getwd, @config.log, filename) if @config.log
|
88
|
-
File.open(log_file, 'w') { |f| f.write server_log }
|
89
|
-
{ server_log: filename, sauce_data: JSON.parse(sauce_job.to_json) }
|
80
|
+
def slugify_name(name)
|
81
|
+
name.downcase.strip.gsub(' ', '_').gsub(/[^\w-]/, '')
|
90
82
|
end
|
91
83
|
|
92
84
|
# TODO this should be pulled out into a generic report... or something
|
data/lib/selenium_connect.rb
CHANGED
@@ -3,6 +3,7 @@
|
|
3
3
|
require 'selenium_connect/job'
|
4
4
|
require 'selenium_connect/server'
|
5
5
|
require 'selenium_connect/configuration'
|
6
|
+
require 'sauce/sauce_facade'
|
6
7
|
require 'selenium_connect/report/report_factory'
|
7
8
|
|
8
9
|
# Selenium Connect main module
|
@@ -24,7 +25,8 @@ class SeleniumConnect
|
|
24
25
|
end
|
25
26
|
|
26
27
|
def create_job(opts = {})
|
27
|
-
|
28
|
+
sauce_facade = Sauce::SauceFacade.new @config.api_timeout
|
29
|
+
SeleniumConnect::Job.new @config, @report_factory, sauce_facade
|
28
30
|
end
|
29
31
|
|
30
32
|
def finish
|
data/selenium-connect.gemspec
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = 'selenium-connect'
|
3
|
-
s.version = '3.
|
3
|
+
s.version = '3.2.0'
|
4
4
|
s.platform = Gem::Platform::RUBY
|
5
5
|
s.authors = ['Dave Haeffner', 'Jason Fox']
|
6
6
|
s.email = ['dave@arrgyle.com', 'jason@arrgyle.com']
|
7
7
|
s.homepage = 'https://github.com/arrgyle/selenium-connect'
|
8
8
|
s.summary = 'A stupid simple way to run your Selenium tests on localhost, against a Selenium Grid, or in the cloud (e.g. SauceLabs).'
|
9
|
-
s.description = '
|
9
|
+
s.description = 'added better error handling to the sauce api calls and improved naming convention for job assets'
|
10
10
|
s.license = 'MIT'
|
11
11
|
|
12
12
|
s.files = `git ls-files`.split($/)
|
@@ -26,10 +26,10 @@ describe 'Sauce Labs', selenium: true do
|
|
26
26
|
execute_simple_test driver
|
27
27
|
report = job.finish passed: true
|
28
28
|
sauce_id = report.data[:sauce_data][:id]
|
29
|
-
report.data[:sauce_data][:name].should be ==
|
29
|
+
report.data[:sauce_data][:name].should be == 'successful_sauce_job'
|
30
30
|
report.data[:sauce_data][:passed].should be_true
|
31
|
-
report.data[:server_log].should be == "
|
32
|
-
File.exist?(File.join(Dir.pwd, 'build', 'tmp', "
|
31
|
+
report.data[:assets][:server_log].should be == "passed_serverlog_successful_sauce_job_#{sauce_id}.log"
|
32
|
+
File.exist?(File.join(Dir.pwd, 'build', 'tmp', "passed_serverlog_successful_sauce_job_#{sauce_id}.log")).should be_true
|
33
33
|
end
|
34
34
|
|
35
35
|
it 'should mark a sauce job as failed' do
|
@@ -56,8 +56,8 @@ describe 'Sauce Labs', selenium: true do
|
|
56
56
|
end
|
57
57
|
sauce_id = report.data[:sauce_data][:id]
|
58
58
|
report.data[:sauce_data][:passed].should be false
|
59
|
-
report.data[:failshot].should be == "
|
60
|
-
File.exist?(File.join(Dir.pwd, 'build', 'tmp', "
|
59
|
+
report.data[:assets][:failshot].should be == "failed_failshot_failshot_#{sauce_id}.png"
|
60
|
+
File.exist?(File.join(Dir.pwd, 'build', 'tmp', "failed_failshot_failshot_#{sauce_id}.png")).should be_true
|
61
61
|
end
|
62
62
|
|
63
63
|
after(:each) do
|
data/spec/support/example.yaml
CHANGED
@@ -0,0 +1,55 @@
|
|
1
|
+
# Encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
require 'sauce/sauce_facade'
|
5
|
+
|
6
|
+
describe Sauce::SauceFacade do
|
7
|
+
|
8
|
+
before(:each) do
|
9
|
+
@facade = Sauce::SauceFacade.new
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'should be initialized' do
|
13
|
+
@facade.should be_an_instance_of Sauce::SauceFacade
|
14
|
+
@facade.timeout.should be == 10
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'can be initialized with an optional time out value' do
|
18
|
+
facade = Sauce::SauceFacade.new 30
|
19
|
+
facade.timeout.should be == 30
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'should respond to fail_job' do
|
23
|
+
@facade.should respond_to :fail_job
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'should respond to pass_job' do
|
27
|
+
@facade.should respond_to :pass_job
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'should respond to fetch_last_screenshot' do
|
31
|
+
@facade.should respond_to :fetch_last_screenshot
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'should respond to fetch_server_log' do
|
35
|
+
@facade.should respond_to :fetch_server_log
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'should respoind to fetch_job_data' do
|
39
|
+
@facade.should respond_to :fetch_job_data
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'should respond to job_id' do
|
43
|
+
@facade.should respond_to :job_id
|
44
|
+
@facade.should respond_to :job_id=
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'should raise and exception if a function is called that requires an id but it is not set' do
|
48
|
+
[:fail_job, :pass_job, :fetch_last_screenshot, :fetch_server_log, :fetch_job_data].each do |method|
|
49
|
+
expect do
|
50
|
+
@facade.send method
|
51
|
+
end.to raise_error ArgumentError, "#{method.to_s} requires that the job_id be set in this object."
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
@@ -8,9 +8,10 @@ describe SeleniumConnect::Job do
|
|
8
8
|
before(:each) do
|
9
9
|
config = double 'SeleniumConnect::Configuration'
|
10
10
|
report_factory = double 'SeleniumConnect::Report::ReportFactory'
|
11
|
+
sauce_facade = double 'Sauce::SauceFacade'
|
11
12
|
@report = double 'SeleniumConnect::Report::JobReport'
|
12
13
|
allow(report_factory).to receive(:build).and_return(@report)
|
13
|
-
@job = SeleniumConnect::Job.new config, report_factory
|
14
|
+
@job = SeleniumConnect::Job.new config, report_factory, sauce_facade
|
14
15
|
end
|
15
16
|
|
16
17
|
it 'should be initialized' do
|
@@ -9,6 +9,7 @@ describe SeleniumConnect do
|
|
9
9
|
@config = double 'SeleniumConnect::Configuration'
|
10
10
|
allow(@config).to receive(:is_a?).and_return(true)
|
11
11
|
allow(@config).to receive(:host).and_return(false)
|
12
|
+
allow(@config).to receive(:api_timeout).and_return(10)
|
12
13
|
|
13
14
|
report_factory = double 'SeleniumConnect::Report::ReportFactory'
|
14
15
|
@report = double 'SeleniumConnect::Report::MainReport'
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: selenium-connect
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.
|
4
|
+
version: 3.2.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2013-07-
|
13
|
+
date: 2013-07-15 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: selenium-webdriver
|
@@ -140,7 +140,8 @@ dependencies:
|
|
140
140
|
- - ~>
|
141
141
|
- !ruby/object:Gem::Version
|
142
142
|
version: 0.6.7
|
143
|
-
description:
|
143
|
+
description: added better error handling to the sauce api calls and improved naming
|
144
|
+
convention for job assets
|
144
145
|
email:
|
145
146
|
- dave@arrgyle.com
|
146
147
|
- jason@arrgyle.com
|
@@ -163,6 +164,7 @@ files:
|
|
163
164
|
- bin/chromedriver
|
164
165
|
- bin/phantomjs
|
165
166
|
- bin/selenium-server-standalone-2.33.0.jar
|
167
|
+
- lib/sauce/sauce_facade.rb
|
166
168
|
- lib/selenium-connect.rb
|
167
169
|
- lib/selenium_connect.rb
|
168
170
|
- lib/selenium_connect/configuration.rb
|
@@ -190,6 +192,7 @@ files:
|
|
190
192
|
- spec/support/example.yaml
|
191
193
|
- spec/support/example_page_object.rb
|
192
194
|
- spec/support/integration_helper.rb
|
195
|
+
- spec/unit/lib/sauce/sauce_facade_spec.rb
|
193
196
|
- spec/unit/lib/selenium-connect_spec.rb
|
194
197
|
- spec/unit/lib/selenium_connect/configuration_spec.rb
|
195
198
|
- spec/unit/lib/selenium_connect/job_spec.rb
|
@@ -219,7 +222,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
219
222
|
version: '0'
|
220
223
|
segments:
|
221
224
|
- 0
|
222
|
-
hash: -
|
225
|
+
hash: -3833046545710782296
|
223
226
|
requirements: []
|
224
227
|
rubyforge_project:
|
225
228
|
rubygems_version: 1.8.25
|