selenium_shots 0.0.0 → 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc CHANGED
@@ -3,15 +3,52 @@
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.
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`
15
52
 
16
53
  == Copyright
17
54
 
data/bin/selenium_shots CHANGED
@@ -11,6 +11,6 @@ command = args.shift.strip rescue 'help'
11
11
  if File.exists?('config/environment.rb')
12
12
  SeleniumShots::Command.run(command, args)
13
13
  else
14
- puts "No rails app found!, you need stay inside on the root of one rails app"
14
+ puts "app rails not found!, you need stay inside on the root of one rails app"
15
15
  end
16
16
 
@@ -0,0 +1,14 @@
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
+
@@ -1,5 +1,4 @@
1
1
  require 'rubygems'
2
- require 'rexml/document'
3
2
  require 'rest_client'
4
3
  require 'uri'
5
4
  require 'time'
@@ -8,16 +7,11 @@ class SeleniumShots::Client
8
7
 
9
8
  attr_reader :host, :api_key
10
9
 
11
- def initialize(api_key, host='seleniumshots.heroku.com')
10
+ def initialize(api_key, host='www.seleniumshots.com')
12
11
  @api_key = api_key
13
12
  @host = host
14
13
  end
15
14
 
16
- def list
17
- #get list app from selenium_shots
18
- []
19
- end
20
-
21
15
  ############
22
16
  def resource(uri)
23
17
  RestClient::Resource.new("http://#{host}", api_key)[uri]
@@ -38,16 +32,5 @@ class SeleniumShots::Client
38
32
  def delete(uri)
39
33
  resource(uri).delete
40
34
  end
41
-
42
- def xml(raw) # :nodoc:
43
- REXML::Document.new(raw)
44
- end
45
-
46
- def escape(value) # :nodoc:
47
- escaped = URI.escape(value.to_s, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]"))
48
- escaped.gsub('.', '%2E') # not covered by the previous URI.escape
49
- end
50
-
51
-
52
35
  end
53
36
 
@@ -1,50 +1,52 @@
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
- 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
+ rescue RestClient::Unauthorized
12
+ display "Authentication failure. For more information you can go to http://www.seleniumshots.com"
13
+ end
12
14
 
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
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
20
22
 
21
- def display(msg)
22
- puts(msg)
23
- end
23
+ def display(msg)
24
+ puts(msg)
25
+ end
24
26
 
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
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
41
43
 
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
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
49
51
  end
50
52
 
@@ -1,20 +1,16 @@
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
5
- api_key ||= SeleniumShots::Command.run_internal('auth:api_key', args)
6
- make_config_file(name, api_key)
7
- display "Created #{name}"
4
+ name = args.shift.downcase.strip rescue nil
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
10
+ else
11
+ display "You need specify a name for your app. Run 'selenium_shots help' for usage information"
12
+ end
8
13
  end
9
-
10
- def list
11
- list = selenium_shots.list
12
- if list.size > 0
13
- display list.join("\n")
14
- else
15
- display "You have no apps."
16
- end
17
- end
18
- end
14
+ end
19
15
  end
20
16
 
@@ -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://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
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
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,61 +1,75 @@
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
4
+ class Base
5
+ attr_accessor :args
6
+ def initialize(args)
7
+ @args = args
8
+ end
9
9
 
10
- def selenium_shots
11
- @selenium_shots ||= SeleniumShots::Command.run_internal('auth:client', args)
12
- end
10
+ def selenium_shots
11
+ @selenium_shots ||= SeleniumShots::Command.run_internal('auth:client', args)
12
+ end
13
13
 
14
- def display(msg, newline=true)
15
- newline ? puts(msg) : print(msg)
16
- end
14
+ def selenium_shots_api_key
15
+ @api_key ||= SeleniumShots::Command.run_internal('auth:api_key', args)
16
+ end
17
17
 
18
- def ask
19
- gets.strip
20
- end
18
+ def display(msg, newline=true)
19
+ newline ? puts(msg) : print(msg)
20
+ end
21
21
 
22
- def shell(cmd)
23
- `#{cmd}`
24
- end
22
+ def ask
23
+ gets.strip
24
+ end
25
+
26
+ def shell(cmd)
27
+ `#{cmd}`
28
+ end
25
29
 
26
- def home_directory
27
- running_on_windows? ? ENV['USERPROFILE'] : ENV['HOME']
28
- end
30
+ def home_directory
31
+ running_on_windows? ? ENV['USERPROFILE'] : ENV['HOME']
32
+ end
29
33
 
30
- def running_on_windows?
31
- RUBY_PLATFORM =~ /mswin32/
32
- end
34
+ def running_on_windows?
35
+ RUBY_PLATFORM =~ /mswin32/
36
+ end
33
37
 
34
38
  def config_file
35
39
  'config/selenium_shots.yml'
36
40
  end
37
41
 
42
+
43
+ def ask_for_config_file
44
+ if File.exists?(config_file)
45
+ print "The file config/selenium_shots.yml exists, do you want overwrite this? (y/n): "
46
+ ask
47
+ else
48
+ "y"
49
+ end
50
+ end
51
+
38
52
  def make_config_file(name, api_key)
39
- FileUtils.rm_f(config_file) if File.exists?(config_file)
40
- config_file_hash = <<EOFILE
53
+ overwrite_or_create_file = ask_for_config_file
54
+ if overwrite_or_create_file == "y"
55
+ config_file_hash = <<EOFILE
41
56
  api_key: "#{api_key}"
42
- hub_url: 'url'
43
- hub_port: 'port'
44
- default_browser_url: 'default url'
57
+ mode: "remote" # "local" for run test locally
58
+ default_browser_url: "http://www.myapp.com"
45
59
  application_name: "#{name}"
60
+ local_browser: "firefox"
46
61
  browsers:
47
62
  - IE8 on XP
63
+ - Firefox3.6 on XP
48
64
  EOFILE
49
- File.open(config_file, 'w') do |f|
50
- f.puts config_file_hash
51
- end
52
- end
53
-
54
- def inside_rails_app?
55
- File.exists?('config/environment.rb')
65
+ File.open(config_file, 'w') do |f|
66
+ f.puts config_file_hash
67
+ end
56
68
  end
69
+ overwrite_or_create_file
70
+ end
57
71
 
58
- end
72
+ end
59
73
 
60
74
  end
61
75
 
@@ -1,15 +1,16 @@
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
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
@@ -17,7 +18,7 @@ module SeleniumShots::Command
17
18
  (...make edits...)
18
19
  selenium_shots create example_one
19
20
  EOTXT
20
- end
21
- end
21
+ end
22
+ end
22
23
  end
23
24
 
@@ -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
 
@@ -1,38 +1,72 @@
1
1
  require "test/unit"
2
2
  require "rubygems"
3
3
  require "selenium/client"
4
+ require "selenium-webdriver"
4
5
  require 'active_support'
5
6
  require 'active_support/test_case'
6
7
  require 'ostruct'
7
8
 
8
-
9
9
  #load config
10
- SeleniumConfig = OpenStruct.new(YAML.load_file("#{RAILS_ROOT}/config/selenium_shots.yml"))
11
- #
12
- PICS_WINDOWS_PATH = "Z:"
13
- PICS_LINUX_PATH = ''
14
- PICS_MACOS_PATH = ''
10
+ ASSET_ROOT = File.expand_path((defined?(Rails) && Rails.root.to_s.length > 0) ? Rails.root : ".") unless defined?(ASSET_ROOT)
11
+
12
+ SeleniumConfig = OpenStruct.new(YAML.load_file(ASSET_ROOT + '/config/selenium_shots.yml'))
15
13
  #
14
+
16
15
  #activeresource models
17
16
  class SeleniumTest < ActiveResource::Base
18
- self.site = "http://seleniumshots.heroku.com"
17
+ self.site = "http://seleniumshots.com"
19
18
  self.user = SeleniumConfig.api_key
20
19
  end
21
20
 
21
+ class SeleniumShots < ActionController::IntegrationTest
22
22
 
23
- class ActiveSupport::TestCase
23
+ attr_reader :driver, :agent, :take_screenshot
24
+ cattr_accessor :expected_test_count
24
25
 
25
- attr_reader :browser, :agent
26
+ if SeleniumConfig.mode == "remote"
27
+ PICS_WINDOWS_PATH = "Z:"
28
+ PICS_LINUX_PATH = ''
29
+ PICS_MACOS_PATH = ''
30
+ HOST = "staging.advisorshq.com"
31
+ PORT = "8888"
32
+ else
33
+ HOST = "127.0.0.1"
34
+ PORT = "4444"
35
+ end
26
36
 
27
- def self.selenium_shot(description, &block)
28
- #set vars
29
- @description = description
37
+ def pid_file
38
+ "/tmp/selenium_shots.pid"
39
+ end
40
+
41
+ def local_browsers
42
+ ["firefox", "ie", "chrome"]
43
+ end
30
44
 
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
54
+ end
55
+ end
56
+
57
+ def self.core_test(description, take_screenshot = true, &block)
58
+ @@group = (@group || "Default")
31
59
  test_name = "test_#{description.gsub(/\s+/,'_')}".to_sym
32
60
  defined = instance_method(test_name) rescue false
33
61
  raise "#{test_name} is already defined in #{self}" if defined
34
62
  if block_given?
35
- define_method(test_name, &block)
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
36
70
  else
37
71
  define_method(test_name) do
38
72
  flunk "No implementation provided for #{name}"
@@ -40,65 +74,107 @@ class ActiveSupport::TestCase
40
74
  end
41
75
  end
42
76
 
43
- def select_browser(browser, url = nil)
44
- @browser = Selenium::Client::Driver.new \
45
- :host => SeleniumConfig.hub_url,
46
- :port => SeleniumConfig.hub_port,
47
- :browser => browser,
48
- :url => url || SeleniumConfig.default_browser_url,
49
- :timeout_in_second => 60,
50
- :highlight_located_element => true
51
- @browser.start_new_browser_session
77
+ def self.selenium_test(description, &block)
78
+ core_test(description, nil, &block)
52
79
  end
53
80
 
54
- def hover_click(locator)
55
- browser.mouse_over locator
56
- browser.click locator
57
- browser.focus locator
81
+ def self.selenium_shot(description, &block)
82
+ core_test(description, true, &block)
58
83
  end
59
84
 
60
- def open_and_wait(url)
61
- browser.open url
62
- browser.wait_for_page_to_load "30000"
85
+ def run_in_all_browsers(&block)
86
+ @error = nil
87
+ browsers = (@selected_browser || selected_browsers)
88
+ browsers.each do |browser_spec|
89
+ begin
90
+ run_webdriver(browser_spec, block)
91
+ rescue => error
92
+ @driver.quit if @driver
93
+ @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
+ end
104
+ end
105
+ assert @error.nil?, "Expected zero failures or errors, but got #{@error}\n"
63
106
  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
64
137
 
65
- def run_test(&proc)
66
- begin
67
- proc.call
68
- @error = nil
69
- rescue => e
70
- @error = e.message
138
+ @driver = Selenium::WebDriver.for(:remote, :desired_capabilities => caps, :http_client => client) if caps
71
139
  end
72
- end
140
+
141
+ @driver.manage.timeouts.implicit_wait = 2 #seconds
142
+ @driver.navigate.to SeleniumConfig.default_browser_url
73
143
 
74
- def teardown
75
- save_test ({:selenium_test_group_name => @group, :selenium_test_name => @name,
76
- :description => @description})
77
- browser.close_current_browser_session
144
+ begin
145
+ block.call
146
+ rescue => error
147
+ @error = error.message
148
+ puts error.message
149
+ puts error.backtrace
150
+ ensure
151
+ save_test({:selenium_test_group_name => @@group, :selenium_test_name => @name,
152
+ :description => @description}) if SeleniumConfig.mode == "remote"
153
+ @driver.quit
154
+ end
78
155
  end
79
156
 
80
157
  def capture_screenshot_on(src)
81
158
  browser.window_focus
82
159
  browser.window_maximize
83
160
  sleep(2)
84
- if browser.browser_string.match(/XP/)
85
- browser.capture_entire_page_screenshot("#{PICS_WINDOWS_PATH}\\#{src}", "background=#FFFFFF")
86
- elsif browser.browser_string.match(/SnowLeopard/)
87
- browser.capture_entire_page_screenshot("#{PICS_MACOS_PATH}/#{src}", "background=#FFFFFF")
88
- elsif browser.browser_string.match(/Linux/)
89
- browser.capture_entire_page_screenshot("#{PICS_LINUX_PATH}/#{src}", "background=#FFFFFF")
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")
90
167
  end
91
168
  end
92
169
 
93
170
  def save_test(params)
94
171
  src = "#{SeleniumConfig.application_name}_#{params[:selenium_test_group_name]}_#{params[:selenium_test_name]}_" +
95
- "#{browser.browser_string.gsub(/\s+/,"_").downcase}.png"
172
+ "#{@driver.browser.to_s.gsub(/\s+/,"_").downcase}.png"
96
173
 
97
174
  capture_screenshot_on(src)
98
175
 
99
176
  SeleniumTest.create(:selenium_test_name => params[:selenium_test_name], :description => params[:description],
100
- :url => browser.location, :error_message => @error, :is_error => !@error.nil?, :environment => browser.browser_string,
177
+ :url => @driver.current_url, :error_message => @error, :is_error => !@error.nil?, :environment => @driver.browser.to_s,
101
178
  :selenium_test_group_name => params[:selenium_test_group_name], :application_name => SeleniumConfig.application_name)
102
179
  end
103
180
  end
104
-
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
@@ -5,17 +5,18 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 0
8
- - 0
9
- version: 0.0.0
8
+ - 1
9
+ version: 0.0.1
10
10
  platform: ruby
11
11
  authors:
12
12
  - Kyle J. Ginavan
13
13
  - Mauro Torres
14
+ - Mike Hemesath
14
15
  autorequire:
15
16
  bindir: bin
16
17
  cert_chain: []
17
18
 
18
- date: 2010-04-08 00:00:00 -05:00
19
+ date: 2010-12-21 00:00:00 -06:00
19
20
  default_executable: selenium_shots
20
21
  dependencies:
21
22
  - !ruby/object:Gem::Dependency
@@ -31,21 +32,21 @@ dependencies:
31
32
  type: :development
32
33
  version_requirements: *id001
33
34
  - !ruby/object:Gem::Dependency
34
- name: rest-client
35
+ name: rspec
35
36
  prerelease: false
36
37
  requirement: &id002 !ruby/object:Gem::Requirement
37
38
  requirements:
38
- - - ">="
39
+ - - "="
39
40
  - !ruby/object:Gem::Version
40
41
  segments:
41
- - 0
42
- - 8
43
- - 2
44
- version: 0.8.2
45
- type: :runtime
42
+ - 1
43
+ - 1
44
+ - 12
45
+ version: 1.1.12
46
+ type: :development
46
47
  version_requirements: *id002
47
48
  - !ruby/object:Gem::Dependency
48
- name: launchy
49
+ name: selenium-webdriver
49
50
  prerelease: false
50
51
  requirement: &id003 !ruby/object:Gem::Requirement
51
52
  requirements:
@@ -53,23 +54,23 @@ dependencies:
53
54
  - !ruby/object:Gem::Version
54
55
  segments:
55
56
  - 0
56
- - 3
57
- - 2
58
- version: 0.3.2
57
+ - 1
58
+ - 0
59
+ version: 0.1.0
59
60
  type: :runtime
60
61
  version_requirements: *id003
61
62
  - !ruby/object:Gem::Dependency
62
- name: selenium-client
63
+ name: rest-client
63
64
  prerelease: false
64
65
  requirement: &id004 !ruby/object:Gem::Requirement
65
66
  requirements:
66
67
  - - ">="
67
68
  - !ruby/object:Gem::Version
68
69
  segments:
69
- - 1
70
+ - 0
71
+ - 8
70
72
  - 2
71
- - 18
72
- version: 1.2.18
73
+ version: 0.8.2
73
74
  type: :runtime
74
75
  version_requirements: *id004
75
76
  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
@@ -126,5 +127,11 @@ signing_key:
126
127
  specification_version: 3
127
128
  summary: Integration Tests made easy
128
129
  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
129
135
  - test/helper.rb
130
136
  - test/test_selenium_shots.rb
137
+ - examples/google.rb