selenium_shots 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc CHANGED
@@ -3,52 +3,15 @@
3
3
  http://www.seleniumshots.com
4
4
  Selenium Shots is an Integration Testing Service that transparently distributes your integration tests across multiple operating systems with different versions of all major browsers AND captures a screen shot. This eliminates the need to have multiple vm's on your computer or the need for multiple machines on your test to test your web application. Running your tests remotely will dramatically speed up in-browser web testing and leave more time to and create a slide show available to confirm visuals making it easy for you to improve your web application.
5
5
 
6
- == Install
7
-
8
- === Rails 3
9
-
10
- Add this to your Gemfile:
11
-
12
- group :development do
13
- gem 'selenium_shots'
14
- end
15
-
16
- === Rails 2
17
-
18
- To install add teh following to config/environment.rb:
19
-
20
- config.gem 'selenium_shots'
21
-
22
- == Configure
23
-
24
- In configure/selenium_shots.yml you will need to define the application
25
- * api_key
26
- * mode
27
- * default_browser_url
28
- * application_name
29
- * local_browser
30
- * browsers
31
-
32
- == Creating Tests
33
-
34
-
35
- class MyTest < SeleniumShots
36
-
37
- @group = "MyTestGroup"
38
-
39
- selenium_shot "Should run my test and pass." do
40
- @name = "My Test Name"
41
- browser.open "/my_site"
42
- browser.type "search[query]", "Cats"
43
- browser.click "find"
44
- end
45
- end
46
-
47
- == Selenium Setup
48
- Download the latest version of selenium grid from http://seleniumhq.org/download/
49
-
50
- ===Using a Custom Selenium Server
51
- `ant launch-remote-control -DcustomRemoteControl=/path/to/your/customer/selenium-server.jar`
6
+ == Note on Patches/Pull Requests
7
+
8
+ * Fork the project.
9
+ * Make your feature addition or bug fix.
10
+ * Add tests for it. This is important so I don't break it in a
11
+ future version unintentionally.
12
+ * Commit, do not mess with rakefile, version, or history.
13
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
14
+ * Send me a pull request. Bonus points for topic branches.
52
15
 
53
16
  == Copyright
54
17
 
@@ -0,0 +1,54 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require 'fileutils'
5
+
6
+ GEM_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
7
+ SELENIUM_SERVER = File.join(GEM_ROOT, 'vendor', 'selenium-server-1.0.2-SNAPSHOT-standalone.jar')
8
+
9
+
10
+ module SeleniumShots
11
+ module Server
12
+ class << self
13
+ def pid_file
14
+ '/tmp/selenium_shots.pid'
15
+ end
16
+
17
+ def start_process
18
+ if File.exists?(pid_file)
19
+ puts "the selenium shots server is running...."
20
+ else
21
+ pipe = IO.popen("java -jar #{SELENIUM_SERVER}")
22
+ File.open(pid_file, 'w') {|f| f.write(pipe.pid) }
23
+ end
24
+ end
25
+
26
+ def stop_process
27
+ if File.exists?(pid_file)
28
+ process_id = File.open(pid_file,'r').readline
29
+ Process.kill 9, process_id.to_i
30
+ FileUtils.rm(pid_file)
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+
37
+ help = <<EOF
38
+ selenium_shots_local_server {start|stop}
39
+ EOF
40
+
41
+ args = ARGV.dup
42
+ ARGV.clear
43
+
44
+ command = args.shift.strip rescue help
45
+
46
+ case command
47
+ when "start"
48
+ SeleniumShots::Server.start_process
49
+ when "stop":
50
+ SeleniumShots::Server.stop_process
51
+ else
52
+ puts help
53
+ end
54
+
@@ -1,5 +1,5 @@
1
1
  require 'rubygems'
2
- require 'rest_client'
2
+ #require 'rest_client'
3
3
  require 'uri'
4
4
  require 'time'
5
5
 
@@ -7,11 +7,16 @@ class SeleniumShots::Client
7
7
 
8
8
  attr_reader :host, :api_key
9
9
 
10
- def initialize(api_key, host='www.seleniumshots.com')
10
+ def initialize(api_key, host='seleniumshots.heroku.com')
11
11
  @api_key = api_key
12
12
  @host = host
13
13
  end
14
14
 
15
+ def list
16
+ #get list app from selenium_shots
17
+ []
18
+ end
19
+
15
20
  ############
16
21
  def resource(uri)
17
22
  RestClient::Resource.new("http://#{host}", api_key)[uri]
@@ -1,52 +1,50 @@
1
1
  module SeleniumShots
2
- module Command
3
- class InvalidCommand < RuntimeError; end
4
- class CommandFailed < RuntimeError; end
2
+ module Command
3
+ class InvalidCommand < RuntimeError; end
4
+ class CommandFailed < RuntimeError; end
5
5
 
6
- class << self
7
- def run(command, args)
8
- run_internal(command, args)
9
- rescue InvalidCommand
10
- display "Unknown command. Run 'selenium_shots help' for usage information."
11
- rescue RestClient::Unauthorized
12
- display "Authentication failure. For more information you can go to http://www.seleniumshots.com"
13
- end
6
+ class << self
7
+ def run(command, args)
8
+ run_internal(command, args)
9
+ rescue InvalidCommand
10
+ display "Unknown command. Run 'selenium_shots help' for usage information."
11
+ end
14
12
 
15
- def run_internal(command, args)
16
- namespace, command = parse(command)
17
- require "#{namespace}"
18
- klass = SeleniumShots::Command.const_get(namespace.capitalize).new(args)
19
- raise InvalidCommand unless klass.respond_to?(command)
20
- klass.send(command)
21
- end
13
+ def run_internal(command, args)
14
+ namespace, command = parse(command)
15
+ require "#{namespace}"
16
+ klass = SeleniumShots::Command.const_get(namespace.capitalize).new(args)
17
+ raise InvalidCommand unless klass.respond_to?(command)
18
+ klass.send(command)
19
+ end
22
20
 
23
- def display(msg)
24
- puts(msg)
25
- end
21
+ def display(msg)
22
+ puts(msg)
23
+ end
26
24
 
27
- def parse(command)
28
- parts = command.split(':')
29
- case parts.size
30
- when 1
31
- if namespaces.include? command
32
- return command, 'index'
33
- else
34
- return 'app', command
35
- end
36
- when 2
37
- raise InvalidCommand unless namespaces.include? parts[0]
38
- return parts
39
- else
40
- raise InvalidCommand
41
- end
42
- end
25
+ def parse(command)
26
+ parts = command.split(':')
27
+ case parts.size
28
+ when 1
29
+ if namespaces.include? command
30
+ return command, 'index'
31
+ else
32
+ return 'app', command
33
+ end
34
+ when 2
35
+ raise InvalidCommand unless namespaces.include? parts[0]
36
+ return parts
37
+ else
38
+ raise InvalidCommand
39
+ end
40
+ end
43
41
 
44
- def namespaces
45
- @@namespaces ||= Dir["#{File.dirname(__FILE__)}/commands/*"].map do |namespace|
46
- namespace.gsub(/.*\//, '').gsub(/\.rb/, '')
47
- end
48
- end
49
- end
50
- end
42
+ def namespaces
43
+ @@namespaces ||= Dir["#{File.dirname(__FILE__)}/commands/*"].map do |namespace|
44
+ namespace.gsub(/.*\//, '').gsub(/\.rb/, '')
45
+ end
46
+ end
47
+ end
48
+ end
51
49
  end
52
50
 
@@ -1,16 +1,23 @@
1
1
  module SeleniumShots::Command
2
- class App < Base
2
+ class App < Base
3
3
  def create
4
- name = args.shift.downcase.strip rescue nil
4
+ name = args.shift.downcase.strip rescue nil
5
5
  if name
6
- selenium_shots_api_key
7
- if make_config_file(name, @api_key) == "y"
8
- display "Created #{name}\nYou can configurate selenium shots on config/selenium_shots.yml"
9
- end
6
+ api_key ||= SeleniumShots::Command.run_internal('auth:api_key', args)
7
+ display "Created #{name}" if make_config_file(name, api_key) == "y"
10
8
  else
11
9
  display "You need specify a name for your app. Run 'selenium_shots help' for usage information"
12
10
  end
13
11
  end
14
- end
12
+
13
+ def list
14
+ list = selenium_shots.list
15
+ if list.size > 0
16
+ display list.join("\n")
17
+ else
18
+ display "You have no apps."
19
+ end
20
+ end
21
+ end
15
22
  end
16
23
 
@@ -1,127 +1,127 @@
1
1
  module SeleniumShots::Command
2
- class Auth < Base
3
- attr_accessor :api_key_hash
2
+ class Auth < Base
3
+ attr_accessor :api_key_hash
4
4
 
5
- def client
6
- @client ||= init_selenium_shots
7
- end
5
+ def client
6
+ @client ||= init_selenium_shots
7
+ end
8
8
 
9
- def init_selenium_shots
10
- SeleniumShots::Client.new(api_key)
11
- end
9
+ def init_selenium_shots
10
+ SeleniumShots::Client.new(api_key)
11
+ end
12
12
 
13
- def api_key
13
+ def api_key
14
14
  get_api_key
15
- end
15
+ end
16
16
 
17
17
  def get_api_key_from_host
18
- RestClient.post 'http://www.seleniumshots.com/selenium_tests/get_api_key', :user_session => { :email => @api_key_hash[0],
19
- :password => @api_key_hash[1]}
20
- end
21
-
22
- def api_key_file
23
- "#{home_directory}/.selenium_shots/api_key"
24
- end
25
-
26
- def get_api_key
27
- return if @api_key_hash
28
- unless @api_key_hash = read_api_key
29
- @api_key_hash = ask_for_api_key
30
- save_api_key
31
- end
32
- @api_key_hash
33
- end
34
-
35
- def read_api_key
36
- if File.exists? api_key_file
37
- return File.read(api_key_file).split("\n")
38
- end
39
- end
40
-
41
- def echo_off
42
- system "stty -echo"
43
- end
44
-
45
- def echo_on
46
- system "stty echo"
47
- end
48
-
49
- def ask_for_api_key
50
- puts "Enter your SeleniumShots Account"
51
-
52
- print "Email: "
53
- user = ask
54
-
55
- print "Password: "
56
- password = running_on_windows? ? ask_for_password_on_windows : ask_for_password
57
-
58
- [ user, password ]
59
- end
60
-
61
- def ask_for_password_on_windows
62
- require "Win32API"
63
- char = nil
64
- password = ''
65
-
66
- while char = Win32API.new("crtdll", "_getch", [ ], "L").Call do
67
- break if char == 10 || char == 13 # received carriage return or newline
68
- if char == 127 || char == 8 # backspace and delete
69
- password.slice!(-1, 1)
70
- else
71
- password << char.chr
72
- end
73
- end
74
- puts
75
- return password
76
- end
77
-
78
- def ask_for_password
79
- echo_off
80
- password = ask
81
- puts
82
- echo_on
83
- return password
84
- end
85
-
86
- def save_api_key
87
- begin
18
+ RestClient.post 'http://seleniumshots.heroku.com/selenium_tests/get_api_key', :user_session => { :login => @api_key_hash[0],
19
+ :password => @api_key_hash[1]}
20
+ end
21
+
22
+ def api_key_file
23
+ "#{home_directory}/.selenium_shots/api_key"
24
+ end
25
+
26
+ def get_api_key
27
+ return if @api_key_hash
28
+ unless @api_key_hash = read_api_key
29
+ @api_key_hash = ask_for_api_key
30
+ save_api_key
31
+ end
32
+ @api_key_hash
33
+ end
34
+
35
+ def read_api_key
36
+ if File.exists? api_key_file
37
+ return File.read(api_key_file).split("\n")
38
+ end
39
+ end
40
+
41
+ def echo_off
42
+ system "stty -echo"
43
+ end
44
+
45
+ def echo_on
46
+ system "stty echo"
47
+ end
48
+
49
+ def ask_for_api_key
50
+ puts "Enter your SeleniumShots Account"
51
+
52
+ print "Login: "
53
+ user = ask
54
+
55
+ print "Password: "
56
+ password = running_on_windows? ? ask_for_password_on_windows : ask_for_password
57
+
58
+ [ user, password ]
59
+ end
60
+
61
+ def ask_for_password_on_windows
62
+ require "Win32API"
63
+ char = nil
64
+ password = ''
65
+
66
+ while char = Win32API.new("crtdll", "_getch", [ ], "L").Call do
67
+ break if char == 10 || char == 13 # received carriage return or newline
68
+ if char == 127 || char == 8 # backspace and delete
69
+ password.slice!(-1, 1)
70
+ else
71
+ password << char.chr
72
+ end
73
+ end
74
+ puts
75
+ return password
76
+ end
77
+
78
+ def ask_for_password
79
+ echo_off
80
+ password = ask
81
+ puts
82
+ echo_on
83
+ return password
84
+ end
85
+
86
+ def save_api_key
87
+ begin
88
88
  @api_key_hash = get_api_key_from_host
89
- write_api_key
90
- rescue RestClient::Unauthorized => e
91
- delete_api_key
92
- raise e unless retry_login?
93
- display "\nAuthentication failed"
94
- @api_key_hash = ask_for_api_key
95
- @client = init_selenium_shots
96
- retry
97
- rescue Exception => e
98
- delete_api_key
99
- raise e
100
- end
101
- end
102
-
103
- def retry_login?
104
- @login_attempts ||= 0
105
- @login_attempts += 1
106
- @login_attempts < 3
107
- end
108
-
109
- def write_api_key
110
- FileUtils.mkdir_p(File.dirname(api_key_file))
111
- File.open(api_key_file, 'w') do |f|
112
- f.puts self.api_key_hash
113
- end
114
- set_api_key_permissions
115
- end
116
-
117
- def set_api_key_permissions
118
- FileUtils.chmod 0700, File.dirname(api_key_file)
119
- FileUtils.chmod 0600, api_key_file
120
- end
121
-
122
- def delete_api_key
123
- FileUtils.rm_f(api_key_file)
124
- end
125
- end
89
+ write_api_key
90
+ rescue RestClient::Unauthorized => e
91
+ delete_api_key
92
+ raise e unless retry_login?
93
+ display "\nAuthentication failed"
94
+ @api_key_hash = ask_for_api_key
95
+ @client = init_selenium_shots
96
+ retry
97
+ rescue Exception => e
98
+ delete_api_key
99
+ raise e
100
+ end
101
+ end
102
+
103
+ def retry_login?
104
+ @login_attempts ||= 0
105
+ @login_attempts += 1
106
+ @login_attempts < 3
107
+ end
108
+
109
+ def write_api_key
110
+ FileUtils.mkdir_p(File.dirname(api_key_file))
111
+ File.open(api_key_file, 'w') do |f|
112
+ f.puts self.api_key_hash
113
+ end
114
+ set_api_key_permissions
115
+ end
116
+
117
+ def set_api_key_permissions
118
+ FileUtils.chmod 0700, File.dirname(api_key_file)
119
+ FileUtils.chmod 0600, api_key_file
120
+ end
121
+
122
+ def delete_api_key
123
+ FileUtils.rm_f(api_key_file)
124
+ end
125
+ end
126
126
  end
127
127
 
@@ -1,39 +1,35 @@
1
1
  require 'fileutils'
2
2
 
3
3
  module SeleniumShots::Command
4
- class Base
5
- attr_accessor :args
6
- def initialize(args)
7
- @args = args
8
- end
9
-
10
- def selenium_shots
11
- @selenium_shots ||= SeleniumShots::Command.run_internal('auth:client', args)
12
- end
4
+ class Base
5
+ attr_accessor :args
6
+ def initialize(args)
7
+ @args = args
8
+ end
13
9
 
14
- def selenium_shots_api_key
15
- @api_key ||= SeleniumShots::Command.run_internal('auth:api_key', args)
16
- end
10
+ def selenium_shots
11
+ @selenium_shots ||= SeleniumShots::Command.run_internal('auth:client', args)
12
+ end
17
13
 
18
- def display(msg, newline=true)
19
- newline ? puts(msg) : print(msg)
20
- end
14
+ def display(msg, newline=true)
15
+ newline ? puts(msg) : print(msg)
16
+ end
21
17
 
22
- def ask
23
- gets.strip
24
- end
18
+ def ask
19
+ gets.strip
20
+ end
25
21
 
26
- def shell(cmd)
27
- `#{cmd}`
28
- end
22
+ def shell(cmd)
23
+ `#{cmd}`
24
+ end
29
25
 
30
- def home_directory
31
- running_on_windows? ? ENV['USERPROFILE'] : ENV['HOME']
32
- end
26
+ def home_directory
27
+ running_on_windows? ? ENV['USERPROFILE'] : ENV['HOME']
28
+ end
33
29
 
34
- def running_on_windows?
35
- RUBY_PLATFORM =~ /mswin32/
36
- end
30
+ def running_on_windows?
31
+ RUBY_PLATFORM =~ /mswin32/
32
+ end
37
33
 
38
34
  def config_file
39
35
  'config/selenium_shots.yml'
@@ -42,8 +38,8 @@ module SeleniumShots::Command
42
38
 
43
39
  def ask_for_config_file
44
40
  if File.exists?(config_file)
45
- print "The file config/selenium_shots.yml exists, do you want overwrite this? (y/n): "
46
- ask
41
+ print "The config file exists, do you want overwrite this? (y/n): "
42
+ ask
47
43
  else
48
44
  "y"
49
45
  end
@@ -53,23 +49,28 @@ module SeleniumShots::Command
53
49
  overwrite_or_create_file = ask_for_config_file
54
50
  if overwrite_or_create_file == "y"
55
51
  config_file_hash = <<EOFILE
52
+ #remote way
56
53
  api_key: "#{api_key}"
57
54
  mode: "remote" # "local" for run test locally
58
55
  default_browser_url: "http://www.myapp.com"
59
56
  application_name: "#{name}"
60
- local_browser: "firefox"
61
57
  browsers:
62
- - IE8 on XP
63
- - Firefox3.6 on XP
58
+ - IE8 on XP #browser for remote way
59
+ - Firefox3.6 on XP #browser for remote way
60
+ # - "*firefox3" #browser for local way
64
61
  EOFILE
65
- File.open(config_file, 'w') do |f|
66
- f.puts config_file_hash
67
- end
62
+ File.open(config_file, 'w') do |f|
63
+ f.puts config_file_hash
64
+ end
68
65
  end
69
66
  overwrite_or_create_file
70
67
  end
71
68
 
72
- end
69
+ def inside_rails_app?
70
+ File.exists?('config/environment.rb')
71
+ end
72
+
73
+ end
73
74
 
74
75
  end
75
76
 
@@ -1,16 +1,15 @@
1
1
  module SeleniumShots::Command
2
- class Help < Base
3
- def index
4
- display usage
5
- end
2
+ class Help < Base
3
+ def index
4
+ display usage
5
+ end
6
6
 
7
- def usage
8
- usage = <<EOTXT
7
+ def usage
8
+ usage = <<EOTXT
9
9
  === General Commands
10
10
 
11
- help # show this usage
12
- create [name] # create file config for your app
13
-
11
+ help # show this usage
12
+ create [name] # create file config for your app
14
13
  === Example story:
15
14
 
16
15
  rails myapp
@@ -18,7 +17,7 @@ module SeleniumShots::Command
18
17
  (...make edits...)
19
18
  selenium_shots create example_one
20
19
  EOTXT
21
- end
22
- end
20
+ end
21
+ end
23
22
  end
24
23
 
@@ -1,9 +1,9 @@
1
1
  module SeleniumShots; end
2
2
 
3
- $LOAD_PATH.unshift(File.dirname(__FILE__))
4
3
  $LOAD_PATH.unshift(File.dirname(__FILE__) + '/commands')
5
4
 
5
+ require "rubygems" #take this later
6
+ require "base"
6
7
  require "client"
7
8
  require "command"
8
- require "base"
9
9
 
@@ -1,26 +1,25 @@
1
1
  require "test/unit"
2
2
  require "rubygems"
3
3
  require "selenium/client"
4
- require "selenium-webdriver"
5
4
  require 'active_support'
6
5
  require 'active_support/test_case'
7
6
  require 'ostruct'
8
7
 
9
- #load config
10
- ASSET_ROOT = File.expand_path((defined?(Rails) && Rails.root.to_s.length > 0) ? Rails.root : ".") unless defined?(ASSET_ROOT)
11
8
 
12
- SeleniumConfig = OpenStruct.new(YAML.load_file(ASSET_ROOT + '/config/selenium_shots.yml'))
9
+ #load config
10
+ SeleniumConfig = OpenStruct.new(YAML.load_file("#{RAILS_ROOT}/config/selenium_shots.yml"))
13
11
  #
12
+ ENV["RAILS_ENV"] = "test"
14
13
 
15
14
  #activeresource models
16
15
  class SeleniumTest < ActiveResource::Base
17
- self.site = "http://seleniumshots.com"
16
+ self.site = "http://seleniumshots.heroku.com"
18
17
  self.user = SeleniumConfig.api_key
19
18
  end
20
19
 
21
20
  class SeleniumShots < ActionController::IntegrationTest
22
21
 
23
- attr_reader :driver, :agent, :take_screenshot
22
+ attr_reader :browser, :agent
24
23
  cattr_accessor :expected_test_count
25
24
 
26
25
  if SeleniumConfig.mode == "remote"
@@ -38,35 +37,36 @@ class SeleniumShots < ActionController::IntegrationTest
38
37
  "/tmp/selenium_shots.pid"
39
38
  end
40
39
 
41
- def local_browsers
42
- ["firefox", "ie", "chrome"]
40
+ def setup
41
+ if(not self.class.expected_test_count)
42
+ self.class.expected_test_count = (self.class.instance_methods.reject{|method| method[0..3] != 'test'}).length
43
+ if !File.exists?(pid_file) && SeleniumConfig.mode == "local"
44
+ IO.popen("selenium_shots_local_server start 2>&1")
45
+ sleep(2)
46
+ end
47
+ end
43
48
  end
44
49
 
45
- def selected_browsers
46
- if SeleniumConfig.mode == "remote"
47
- SeleniumConfig.browsers
48
- else
49
- if defined?(SeleniumConfig.local_browser)
50
- [SeleniumConfig.local_browser]
51
- else
52
- [local_browsers.first]
53
- end
50
+ def teardown
51
+ if((self.class.expected_test_count-=1) == 0)
52
+ if File.exists?(pid_file) && SeleniumConfig.mode == "local"
53
+ IO.popen("selenium_shots_local_server stop 2>&1")
54
+ end
54
55
  end
55
56
  end
56
-
57
- def self.core_test(description, take_screenshot = true, &block)
57
+
58
+ def self.selenium_shot(description, &block)
59
+ @@description = description
58
60
  @@group = (@group || "Default")
59
61
  test_name = "test_#{description.gsub(/\s+/,'_')}".to_sym
60
62
  defined = instance_method(test_name) rescue false
61
63
  raise "#{test_name} is already defined in #{self}" if defined
62
64
  if block_given?
63
- define_method(test_name) do
64
- @description = description
65
- @take_screenshot = take_screenshot
66
- run_in_all_browsers do
67
- instance_eval &block
68
- end
69
- end
65
+ define_method(test_name) do
66
+ run_in_all_browsers do |browser|
67
+ instance_eval &block
68
+ end
69
+ end
70
70
  else
71
71
  define_method(test_name) do
72
72
  flunk "No implementation provided for #{name}"
@@ -74,107 +74,57 @@ class SeleniumShots < ActionController::IntegrationTest
74
74
  end
75
75
  end
76
76
 
77
- def self.selenium_test(description, &block)
78
- core_test(description, nil, &block)
79
- end
80
-
81
- def self.selenium_shot(description, &block)
82
- core_test(description, true, &block)
83
- end
84
-
85
77
  def run_in_all_browsers(&block)
86
- @error = nil
87
- browsers = (@selected_browser || selected_browsers)
88
- browsers.each do |browser_spec|
78
+ SeleniumConfig.browsers.each do |browser_spec|
89
79
  begin
90
- run_webdriver(browser_spec, block)
91
- rescue => error
92
- @driver.quit if @driver
80
+ run_browser(browser_spec, block)
81
+ @error = nil
82
+ rescue => error
93
83
  @error = error.message
94
- if @error.match(/Failed to start new browser session/) && SeleniumConfig.mode == "local"
95
- @tmp_browsers ||= local_browsers
96
- @tmp_browsers.delete(browser_spec)
97
- @selected_browser = [@tmp_browsers.shift]
98
- unless @selected_browser.empty?
99
- puts "The browser #{browser_spec} is not available, selenium_shots going to try with #{@selected_browser} browser"
100
- run_in_all_browsers(&block)
101
- end
102
- end
103
84
  end
85
+ assert @error.nil?, "Expected zero failures or errors, but got #{@error}\n"
104
86
  end
105
- assert @error.nil?, "Expected zero failures or errors, but got #{@error}\n"
106
87
  end
107
-
108
- def run_webdriver(browser_spec, block)
109
-
110
- client = Selenium::WebDriver::Remote::Http::Default.new
111
- client.timeout = 20 # seconds
112
-
113
- if SeleniumConfig.mode == "local"
114
- if /(firefox)/i.match(browser_spec)
115
- profile = Selenium::WebDriver::Firefox::Profile.new
116
- profile.native_events = false
117
- @driver = Selenium::WebDriver.for(:firefox, :profile => profile, :http_client => client)
118
- elsif /(chrome)/i.match(browser_spec)
119
- @driver = Selenium::WebDriver.for(:chrome, :http_client => client)
120
- elsif /(ie)/i.match(browser_spec)
121
- @driver = Selenium::WebDriver.for(:ie, :http_client => client)
122
- end
123
- else
124
- caps = nil
125
- if /(firefox)/i.match(browser_spec)
126
- caps = WebDriver::Remote::Capabilities.firefox
127
- elsif /(chrome)/i.match(browser_spec)
128
- caps = WebDriver::Remote::Capabilities.chrome
129
- elsif /(ie)/i.match(browser_spec)
130
- caps = WebDriver::Remote::Capabilities.internet_explorer
131
- elsif /(safari)/i.match(browser_spec)
132
- caps = WebDriver::Remote::Capabilities.safari
133
- elsif /(htmlunit)/i.match(browser_spec)
134
- caps = WebDriver::Remote::Capabilities.htmlunit
135
- caps.javascript_enabled = true
136
- end
137
-
138
- @driver = Selenium::WebDriver.for(:remote, :desired_capabilities => caps, :http_client => client) if caps
139
- end
140
-
141
- @driver.manage.timeouts.implicit_wait = 2 #seconds
142
- @driver.navigate.to SeleniumConfig.default_browser_url
143
88
 
89
+ def run_browser(browser_spec, block)
90
+ @browser = Selenium::Client::Driver.new(
91
+ :host => HOST,
92
+ :port => PORT,
93
+ :browser => browser_spec,
94
+ :url => SeleniumConfig.default_browser_url,
95
+ :timeout_in_second => 120)
96
+ @browser.start_new_browser_session
144
97
  begin
145
- block.call
146
- rescue => error
147
- @error = error.message
148
- puts error.message
149
- puts error.backtrace
98
+ block.call(@browser)
150
99
  ensure
151
100
  save_test({:selenium_test_group_name => @@group, :selenium_test_name => @name,
152
- :description => @description}) if SeleniumConfig.mode == "remote"
153
- @driver.quit
154
- end
101
+ :description => @@description}) if SeleniumConfig.mode == "remote"
102
+ @browser.close_current_browser_session
103
+ end
155
104
  end
156
105
 
157
106
  def capture_screenshot_on(src)
158
107
  browser.window_focus
159
108
  browser.window_maximize
160
109
  sleep(2)
161
- if @driver.browser.to_s.match(/XP/)
162
- @driver.capture_entire_page_screenshot("#{PICS_WINDOWS_PATH}\\#{src}", "background=#FFFFFF")
163
- elsif @driver.browser.to_s.match(/SnowLeopard/)
164
- @driver.capture_entire_page_screenshot("#{PICS_MACOS_PATH}/#{src}", "background=#FFFFFF")
165
- elsif @driver.browser.to_s.match(/Linux/)
166
- @driver.capture_entire_page_screenshot("#{PICS_LINUX_PATH}/#{src}", "background=#FFFFFF")
110
+ if browser.browser_string.match(/XP/)
111
+ browser.capture_entire_page_screenshot("#{PICS_WINDOWS_PATH}\\#{src}", "background=#FFFFFF")
112
+ elsif browser.browser_string.match(/SnowLeopard/)
113
+ browser.capture_entire_page_screenshot("#{PICS_MACOS_PATH}/#{src}", "background=#FFFFFF")
114
+ elsif browser.browser_string.match(/Linux/)
115
+ browser.capture_entire_page_screenshot("#{PICS_LINUX_PATH}/#{src}", "background=#FFFFFF")
167
116
  end
168
117
  end
169
118
 
170
119
  def save_test(params)
171
120
  src = "#{SeleniumConfig.application_name}_#{params[:selenium_test_group_name]}_#{params[:selenium_test_name]}_" +
172
- "#{@driver.browser.to_s.gsub(/\s+/,"_").downcase}.png"
121
+ "#{browser.browser_string.gsub(/\s+/,"_").downcase}.png"
173
122
 
174
123
  capture_screenshot_on(src)
175
124
 
176
125
  SeleniumTest.create(:selenium_test_name => params[:selenium_test_name], :description => params[:description],
177
- :url => @driver.current_url, :error_message => @error, :is_error => !@error.nil?, :environment => @driver.browser.to_s,
126
+ :url => browser.location, :error_message => @error, :is_error => !@error.nil?, :environment => browser.browser_string,
178
127
  :selenium_test_group_name => params[:selenium_test_group_name], :application_name => SeleniumConfig.application_name)
179
128
  end
180
129
  end
130
+
metadata CHANGED
@@ -4,20 +4,19 @@ version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
6
  - 0
7
- - 0
8
7
  - 1
9
- version: 0.0.1
8
+ - 0
9
+ version: 0.1.0
10
10
  platform: ruby
11
11
  authors:
12
12
  - Kyle J. Ginavan
13
13
  - Mauro Torres
14
- - Mike Hemesath
15
14
  autorequire:
16
15
  bindir: bin
17
16
  cert_chain: []
18
17
 
19
- date: 2010-12-21 00:00:00 -06:00
20
- default_executable: selenium_shots
18
+ date: 2010-04-13 00:00:00 -05:00
19
+ default_executable:
21
20
  dependencies:
22
21
  - !ruby/object:Gem::Dependency
23
22
  name: thoughtbot-shoulda
@@ -32,51 +31,24 @@ dependencies:
32
31
  type: :development
33
32
  version_requirements: *id001
34
33
  - !ruby/object:Gem::Dependency
35
- name: rspec
34
+ name: selenium-client
36
35
  prerelease: false
37
36
  requirement: &id002 !ruby/object:Gem::Requirement
38
- requirements:
39
- - - "="
40
- - !ruby/object:Gem::Version
41
- segments:
42
- - 1
43
- - 1
44
- - 12
45
- version: 1.1.12
46
- type: :development
47
- version_requirements: *id002
48
- - !ruby/object:Gem::Dependency
49
- name: selenium-webdriver
50
- prerelease: false
51
- requirement: &id003 !ruby/object:Gem::Requirement
52
37
  requirements:
53
38
  - - ">="
54
39
  - !ruby/object:Gem::Version
55
40
  segments:
56
- - 0
57
41
  - 1
58
- - 0
59
- version: 0.1.0
60
- type: :runtime
61
- version_requirements: *id003
62
- - !ruby/object:Gem::Dependency
63
- name: rest-client
64
- prerelease: false
65
- requirement: &id004 !ruby/object:Gem::Requirement
66
- requirements:
67
- - - ">="
68
- - !ruby/object:Gem::Version
69
- segments:
70
- - 0
71
- - 8
72
42
  - 2
73
- version: 0.8.2
43
+ - 18
44
+ version: 1.2.18
74
45
  type: :runtime
75
- version_requirements: *id004
46
+ version_requirements: *id002
76
47
  description: Selenium Shots is an Integration Testing Service that transparently distributes your integration tests across multiple operating systems with different versions of all major browsers AND captures a screen shot
77
48
  email: kyle@4rockets.com
78
49
  executables:
79
50
  - selenium_shots
51
+ - selenium_shots_local_server
80
52
  extensions: []
81
53
 
82
54
  extra_rdoc_files:
@@ -93,6 +65,7 @@ files:
93
65
  - lib/selenium_shots/cli/commands/help.rb
94
66
  - lib/selenium_shots/cli/init.rb
95
67
  - lib/selenium_shots/test_selenium_shots.rb
68
+ - vendor/selenium-server-1.0.2-SNAPSHOT-standalone.jar
96
69
  - LICENSE
97
70
  - LICENSE.orig
98
71
  - README.rdoc
@@ -127,11 +100,5 @@ signing_key:
127
100
  specification_version: 3
128
101
  summary: Integration Tests made easy
129
102
  test_files:
130
- - spec/base.rb
131
- - spec/commands/app_spec.rb
132
- - spec/commands/auth_spec.rb
133
- - spec/commands/base_spec.rb
134
- - spec/commands/server_spec.rb
135
103
  - test/helper.rb
136
104
  - test/test_selenium_shots.rb
137
- - examples/google.rb
data/examples/google.rb DELETED
@@ -1,14 +0,0 @@
1
- require File.expand_path(File.dirname(__FILE__) + "/../config/environment")
2
-
3
- class Google < SeleniumShots
4
-
5
- @group = "Google"
6
-
7
- selenium_shot "should search on google" do
8
- @name = "Google search"
9
- element = driver.find_element(:name, 'q')
10
- element.send_keys "Hello WebDriver!"
11
- element.submit
12
- end
13
- end
14
-
data/spec/base.rb DELETED
@@ -1,14 +0,0 @@
1
- require 'rubygems'
2
- require 'spec'
3
- require 'fileutils'
4
-
5
- require File.dirname(__FILE__) + '/../lib/selenium_shots/cli/init'
6
-
7
- %w(app auth base server).each { |c| require c }
8
-
9
- def prepare_command(klass)
10
- command = klass.new([])
11
- command.stub!(:display)
12
- command
13
- end
14
-
@@ -1,26 +0,0 @@
1
- require File.dirname(__FILE__) + '/../base'
2
-
3
- module SeleniumShots::Command
4
- describe App do
5
- before do
6
- @cli = prepare_command(App)
7
- @auth = prepare_command(Auth)
8
- end
9
-
10
- it "creates with a name" do
11
- @cli.stub!(:args).and_return([ 'myapp' ])
12
- @cli.stub!(:selenium_shots_api_key).and_return("api_key")
13
- @cli.should_receive(:make_config_file)
14
- @cli.create
15
- end
16
-
17
- it "cant creates app without a name" do
18
- @cli.stub!(:args).and_return([ nil ])
19
- @cli.stub!(:selenium_shots_api_key)
20
- @cli.should_not_receive(:make_config_file)
21
- @cli.create
22
- end
23
-
24
- end
25
- end
26
-
@@ -1,83 +0,0 @@
1
- require File.dirname(__FILE__) + '/../base'
2
-
3
- module SeleniumShots::Command
4
- describe Auth do
5
- before do
6
- @cli = prepare_command(Auth)
7
- end
8
-
9
- it "reads api key from the api keys file" do
10
- sandbox = "/tmp/cli_spec_#{Process.pid}"
11
- File.open(sandbox, "w") { |f| f.write "api_key" }
12
- @cli.stub!(:api_key_file).and_return(sandbox)
13
- @cli.read_api_key.should == %w(api_key)
14
- end
15
-
16
- it "takes the apikey from the file" do
17
- @cli.stub!(:read_api_key).and_return(%w(api_key))
18
- @cli.api_key.should == %w(api_key)
19
- end
20
-
21
- it "asks for api_key when the file doesn't exist" do
22
- sandbox = "/tmp/cli_spec_#{Process.pid}"
23
- FileUtils.rm_rf(sandbox)
24
- @cli.stub!(:api_key_file).and_return(sandbox)
25
- @cli.should_receive(:ask_for_api_key).and_return(['u', 'p'])
26
- @cli.should_receive(:save_api_key)
27
- @cli.get_api_key.should == [ 'u', 'p' ]
28
- end
29
-
30
- it "writes the api_key to a file" do
31
- sandbox = "/tmp/cli_spec_#{Process.pid}"
32
- FileUtils.rm_rf(sandbox)
33
- @cli.stub!(:api_key_file).and_return(sandbox)
34
- @cli.stub!(:api_key_hash).and_return(['api_key'])
35
- @cli.should_receive(:set_api_key_permissions)
36
- @cli.write_api_key
37
- File.read(sandbox).should == "api_key\n"
38
- end
39
-
40
- it "sets ~/.selenium_shots/api_key to be readable only by the user" do
41
- sandbox = "/tmp/cli_spec_#{Process.pid}"
42
- FileUtils.rm_rf(sandbox)
43
- FileUtils.mkdir_p(sandbox)
44
- fname = "#{sandbox}/file"
45
- system "touch #{fname}"
46
- @cli.stub!(:api_key_file).and_return(fname)
47
- @cli.set_api_key_permissions
48
- File.stat(sandbox).mode.should == 040700
49
- File.stat(fname).mode.should == 0100600
50
- end
51
-
52
- it "writes api_key when the account is ok" do
53
- @cli.stub!(:api_key)
54
- @cli.should_receive(:write_api_key)
55
- @cli.should_receive(:get_api_key_from_host).and_return("api_key")
56
- @cli.save_api_key
57
- end
58
-
59
- it "save_api_key deletes the api_key when the resquest api_key is unauthorized" do
60
- @cli.stub!(:write_api_key)
61
- @cli.stub!(:retry_login?).and_return(false)
62
- @cli.should_receive(:get_api_key_from_host).and_raise(RestClient::Unauthorized)
63
- @cli.should_receive(:delete_api_key)
64
- lambda { @cli.save_api_key }.should raise_error(RestClient::Unauthorized)
65
- end
66
-
67
-
68
- it "asks for login again when not authorized, for three times" do
69
- @cli.stub!(:read_api_key)
70
- @cli.stub!(:write_api_key)
71
- @cli.stub!(:delete_api_key)
72
- @cli.should_receive(:get_api_key_from_host).exactly(3).times.and_raise(RestClient::Unauthorized)
73
- @cli.should_receive(:ask_for_api_key).exactly(4).times
74
- lambda { @cli.save_api_key }.should raise_error(RestClient::Unauthorized)
75
- end
76
-
77
- it "deletes the api_key file" do
78
- FileUtils.should_receive(:rm_f).with(@cli.api_key_file)
79
- @cli.delete_api_key
80
- end
81
- end
82
- end
83
-
@@ -1,38 +0,0 @@
1
- require File.dirname(__FILE__) + '/../base'
2
-
3
- module SeleniumShots::Command
4
- describe Base do
5
- before do
6
- @args = [1, 2]
7
- @base = Base.new(@args)
8
- @base.stub!(:display)
9
- end
10
-
11
- it "initializes the selenium_shots client with the Auth command" do
12
- SeleniumShots::Command.should_receive(:run_internal).with('auth:client', @args)
13
- @base.selenium_shots
14
- end
15
-
16
- it "creates or overwrite the selenium_shots yml file" do
17
- sandbox = "/tmp/cli_spec_selenium_shots"
18
- @base.stub!(:config_file).and_return(sandbox)
19
- @base.should_receive(:ask_for_config_file).and_return("y")
20
- @base.make_config_file("myapp", "api_key")
21
- File.exists?(sandbox) == true
22
- end
23
-
24
- it "not overwrite the selenium_shots yml file" do
25
- sandbox = "/tmp/cli_spec_selenium_shots"
26
- @base.stub!(:config_file).and_return(sandbox)
27
- @base.should_receive(:ask_for_config_file).and_return("n")
28
- @base.make_config_file("myapp", "api_key")
29
- File.exists?(sandbox) == false
30
- end
31
-
32
- it "return the config file name" do
33
- @base.config_file.should == 'config/selenium_shots.yml'
34
- end
35
-
36
- end
37
- end
38
-
@@ -1,22 +0,0 @@
1
- require File.dirname(__FILE__) + '/../base'
2
-
3
- module SeleniumShots::Command
4
- describe Server do
5
- before do
6
- @cli = prepare_command(Server)
7
- end
8
-
9
- it "run local instance of selenium server" do
10
- @cli.start
11
- File.exists?("/tmp/selenium_shots.pid") == true
12
- end
13
-
14
- it "stop local instance of selenium server" do
15
- @cli.stop
16
- File.exists?("/tmp/selenium_shots.pid") == false
17
- end
18
-
19
-
20
- end
21
- end
22
-