selenium_shots 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc CHANGED
@@ -3,16 +3,6 @@
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
- == 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.
15
-
16
6
  == Copyright
17
7
 
18
8
  Copyright (c) 2010 Kyle Ginavan. See LICENSE for details.
@@ -3,52 +3,14 @@
3
3
  require 'rubygems'
4
4
  require 'fileutils'
5
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')
6
+ $LOAD_PATH.unshift(File.dirname(__FILE__) + '/../lib/selenium_shots/cli/')
8
7
 
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
8
+ require 'init'
40
9
 
41
10
  args = ARGV.dup
42
11
  ARGV.clear
43
12
 
44
- command = args.shift.strip rescue help
13
+ command = args.shift.strip rescue 'help'
45
14
 
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
15
+ SeleniumShots::Command.run("server:#{command}", args)
54
16
 
@@ -0,0 +1,15 @@
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 = "selenium shots"
9
+ browser.open "/"
10
+ browser.type "q", "Selenium Shots"
11
+ browser.click "btnG"
12
+ end
13
+
14
+ end
15
+
@@ -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
 
@@ -12,11 +12,6 @@ class SeleniumShots::Client
12
12
  @host = host
13
13
  end
14
14
 
15
- def list
16
- #get list app from selenium_shots
17
- []
18
- end
19
-
20
15
  ############
21
16
  def resource(uri)
22
17
  RestClient::Resource.new("http://#{host}", api_key)[uri]
@@ -8,6 +8,8 @@ module SeleniumShots
8
8
  run_internal(command, args)
9
9
  rescue InvalidCommand
10
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"
11
13
  end
12
14
 
13
15
  def run_internal(command, args)
@@ -1,23 +1,16 @@
1
1
  module SeleniumShots::Command
2
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
- api_key ||= SeleniumShots::Command.run_internal('auth:api_key', args)
7
- display "Created #{name}" if make_config_file(name, api_key) == "y"
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
8
10
  else
9
11
  display "You need specify a name for your app. Run 'selenium_shots help' for usage information"
10
12
  end
11
13
  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
14
  end
22
15
  end
23
16
 
@@ -15,7 +15,7 @@ module SeleniumShots::Command
15
15
  end
16
16
 
17
17
  def get_api_key_from_host
18
- RestClient.post 'http://seleniumshots.heroku.com/selenium_tests/get_api_key', :user_session => { :login => @api_key_hash[0],
18
+ RestClient.post 'http://seleniumshots.heroku.com/selenium_tests/get_api_key', :user_session => { :email => @api_key_hash[0],
19
19
  :password => @api_key_hash[1]}
20
20
  end
21
21
 
@@ -49,7 +49,7 @@ module SeleniumShots::Command
49
49
  def ask_for_api_key
50
50
  puts "Enter your SeleniumShots Account"
51
51
 
52
- print "Login: "
52
+ print "Email: "
53
53
  user = ask
54
54
 
55
55
  print "Password: "
@@ -11,6 +11,10 @@ module SeleniumShots::Command
11
11
  @selenium_shots ||= SeleniumShots::Command.run_internal('auth:client', args)
12
12
  end
13
13
 
14
+ def selenium_shots_api_key
15
+ @api_key ||= SeleniumShots::Command.run_internal('auth:api_key', args)
16
+ end
17
+
14
18
  def display(msg, newline=true)
15
19
  newline ? puts(msg) : print(msg)
16
20
  end
@@ -38,7 +42,7 @@ module SeleniumShots::Command
38
42
 
39
43
  def ask_for_config_file
40
44
  if File.exists?(config_file)
41
- print "The config file exists, do you want overwrite this? (y/n): "
45
+ print "The file config/selenium_shots.yml exists, do you want overwrite this? (y/n): "
42
46
  ask
43
47
  else
44
48
  "y"
@@ -49,15 +53,14 @@ module SeleniumShots::Command
49
53
  overwrite_or_create_file = ask_for_config_file
50
54
  if overwrite_or_create_file == "y"
51
55
  config_file_hash = <<EOFILE
52
- #remote way
53
56
  api_key: "#{api_key}"
54
57
  mode: "remote" # "local" for run test locally
55
58
  default_browser_url: "http://www.myapp.com"
56
59
  application_name: "#{name}"
60
+ local_browser: "*firefox3"
57
61
  browsers:
58
- - IE8 on XP #browser for remote way
59
- - Firefox3.6 on XP #browser for remote way
60
- # - "*firefox3" #browser for local way
62
+ - IE8 on XP
63
+ - Firefox3.6 on XP
61
64
  EOFILE
62
65
  File.open(config_file, 'w') do |f|
63
66
  f.puts config_file_hash
@@ -66,10 +69,6 @@ EOFILE
66
69
  overwrite_or_create_file
67
70
  end
68
71
 
69
- def inside_rails_app?
70
- File.exists?('config/environment.rb')
71
- end
72
-
73
72
  end
74
73
 
75
74
  end
@@ -8,8 +8,9 @@ module SeleniumShots::Command
8
8
  usage = <<EOTXT
9
9
  === General Commands
10
10
 
11
- help # show this usage
12
- create [name] # create file config for your app
11
+ help # show this usage
12
+ create [name] # create file config for your app
13
+
13
14
  === Example story:
14
15
 
15
16
  rails myapp
@@ -0,0 +1,34 @@
1
+ module SeleniumShots::Command
2
+ class Server < Base
3
+
4
+ GEM_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '/../../../../'))
5
+ SELENIUM_SERVER = File.join(GEM_ROOT, 'vendor', 'selenium-server-1.0.2-SNAPSHOT-standalone.jar')
6
+
7
+ def pid_file
8
+ '/tmp/selenium_shots.pid'
9
+ end
10
+
11
+ def start
12
+ if File.exists?(pid_file)
13
+ puts "the selenium shots server is running...."
14
+ else
15
+ pipe = IO.popen("java -jar #{SELENIUM_SERVER}")
16
+ File.open(pid_file, 'w') {|f| f.write(pipe.pid) }
17
+ end
18
+ end
19
+
20
+ def stop
21
+ if File.exists?(pid_file)
22
+ process_id = File.open(pid_file,'r').readline
23
+ Process.kill 9, process_id.to_i
24
+ FileUtils.rm(pid_file)
25
+ end
26
+ end
27
+
28
+ def help
29
+ puts "selenium_shots_local_server {start|stop}"
30
+ end
31
+
32
+ end
33
+ end
34
+
@@ -1,9 +1,9 @@
1
1
  module SeleniumShots; end
2
2
 
3
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
3
4
  $LOAD_PATH.unshift(File.dirname(__FILE__) + '/commands')
4
5
 
5
- require "rubygems" #take this later
6
- require "base"
7
6
  require "client"
8
7
  require "command"
8
+ require "base"
9
9
 
@@ -5,11 +5,9 @@ require 'active_support'
5
5
  require 'active_support/test_case'
6
6
  require 'ostruct'
7
7
 
8
-
9
8
  #load config
10
9
  SeleniumConfig = OpenStruct.new(YAML.load_file("#{RAILS_ROOT}/config/selenium_shots.yml"))
11
10
  #
12
- ENV["RAILS_ENV"] = "test"
13
11
 
14
12
  #activeresource models
15
13
  class SeleniumTest < ActiveResource::Base
@@ -37,6 +35,22 @@ class SeleniumShots < ActionController::IntegrationTest
37
35
  "/tmp/selenium_shots.pid"
38
36
  end
39
37
 
38
+ def local_browsers
39
+ ["*firefox3", "*iexplore", "*safari"]
40
+ end
41
+
42
+ def selected_browsers
43
+ if SeleniumConfig.mode == "remote"
44
+ SeleniumConfig.browsers
45
+ else
46
+ if defined?(SeleniumConfig.local_browser)
47
+ [SeleniumConfig.local_browser]
48
+ else
49
+ [local_browsers.first]
50
+ end
51
+ end
52
+ end
53
+
40
54
  def setup
41
55
  if(not self.class.expected_test_count)
42
56
  self.class.expected_test_count = (self.class.instance_methods.reject{|method| method[0..3] != 'test'}).length
@@ -75,14 +89,24 @@ class SeleniumShots < ActionController::IntegrationTest
75
89
  end
76
90
 
77
91
  def run_in_all_browsers(&block)
78
- SeleniumConfig.browsers.each do |browser_spec|
92
+ browsers = (@selected_browser || selected_browsers)
93
+ browsers.each do |browser_spec|
79
94
  begin
80
95
  run_browser(browser_spec, block)
81
96
  @error = nil
82
- rescue => error
97
+ rescue => error
83
98
  @error = error.message
99
+ if @error.match(/Failed to start new browser session/) && SeleniumConfig.mode == "local"
100
+ @tmp_browsers ||= local_browsers
101
+ @tmp_browsers.delete(browser_spec)
102
+ @selected_browser = [@tmp_browsers.shift]
103
+ unless @selected_browser.empty?
104
+ puts "The browser #{browser_spec} is not available, selenium_shots going to try with #{@selected_browser} browser"
105
+ run_in_all_browsers(&block)
106
+ end
107
+ end
84
108
  end
85
- assert @error.nil?, "Expected zero failures or errors, but got #{@error}\n"
109
+ assert @error.nil?, "Expected zero failures or errors, but got #{@error}\n"
86
110
  end
87
111
  end
88
112
 
data/spec/base.rb ADDED
@@ -0,0 +1,14 @@
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
+
@@ -0,0 +1,26 @@
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
+
@@ -0,0 +1,83 @@
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
+
@@ -0,0 +1,38 @@
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
+
@@ -0,0 +1,22 @@
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
+
metadata CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
6
  - 0
7
- - 1
7
+ - 2
8
8
  - 0
9
- version: 0.1.0
9
+ version: 0.2.0
10
10
  platform: ruby
11
11
  authors:
12
12
  - Kyle J. Ginavan
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-04-13 00:00:00 -05:00
18
+ date: 2010-04-20 00:00:00 -05:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -44,6 +44,20 @@ dependencies:
44
44
  version: 1.2.18
45
45
  type: :runtime
46
46
  version_requirements: *id002
47
+ - !ruby/object:Gem::Dependency
48
+ name: rest-client
49
+ prerelease: false
50
+ requirement: &id003 !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ segments:
55
+ - 0
56
+ - 8
57
+ - 2
58
+ version: 0.8.2
59
+ type: :runtime
60
+ version_requirements: *id003
47
61
  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
48
62
  email: kyle@4rockets.com
49
63
  executables:
@@ -63,6 +77,7 @@ files:
63
77
  - lib/selenium_shots/cli/commands/auth.rb
64
78
  - lib/selenium_shots/cli/commands/base.rb
65
79
  - lib/selenium_shots/cli/commands/help.rb
80
+ - lib/selenium_shots/cli/commands/server.rb
66
81
  - lib/selenium_shots/cli/init.rb
67
82
  - lib/selenium_shots/test_selenium_shots.rb
68
83
  - vendor/selenium-server-1.0.2-SNAPSHOT-standalone.jar
@@ -100,5 +115,11 @@ signing_key:
100
115
  specification_version: 3
101
116
  summary: Integration Tests made easy
102
117
  test_files:
118
+ - spec/base.rb
119
+ - spec/commands/app_spec.rb
120
+ - spec/commands/auth_spec.rb
121
+ - spec/commands/base_spec.rb
122
+ - spec/commands/server_spec.rb
103
123
  - test/helper.rb
104
124
  - test/test_selenium_shots.rb
125
+ - examples/google.rb