selenium_shots 0.0.0 → 0.0.1
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/README.rdoc +46 -9
- data/bin/selenium_shots +1 -1
- data/examples/google.rb +14 -0
- data/lib/selenium_shots/cli/client.rb +1 -18
- data/lib/selenium_shots/cli/command.rb +44 -42
- data/lib/selenium_shots/cli/commands/app.rb +11 -15
- data/lib/selenium_shots/cli/commands/auth.rb +117 -117
- data/lib/selenium_shots/cli/commands/base.rb +50 -36
- data/lib/selenium_shots/cli/commands/help.rb +11 -10
- data/lib/selenium_shots/cli/init.rb +2 -2
- data/lib/selenium_shots/test_selenium_shots.rb +125 -49
- data/spec/base.rb +14 -0
- data/spec/commands/app_spec.rb +26 -0
- data/spec/commands/auth_spec.rb +83 -0
- data/spec/commands/base_spec.rb +38 -0
- data/spec/commands/server_spec.rb +22 -0
- metadata +25 -18
    
        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 | 
            -
            ==  | 
| 7 | 
            -
             | 
| 8 | 
            -
             | 
| 9 | 
            -
             | 
| 10 | 
            -
             | 
| 11 | 
            -
             | 
| 12 | 
            -
             | 
| 13 | 
            -
             | 
| 14 | 
            -
             | 
| 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 " | 
| 14 | 
            +
              puts "app rails not found!, you need stay inside on the root of one rails app"
         | 
| 15 15 | 
             
            end
         | 
| 16 16 |  | 
    
        data/examples/google.rb
    ADDED
    
    | @@ -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. | 
| 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 | 
            -
             | 
| 3 | 
            -
             | 
| 4 | 
            -
             | 
| 2 | 
            +
              module Command
         | 
| 3 | 
            +
                class InvalidCommand < RuntimeError; end
         | 
| 4 | 
            +
                class CommandFailed  < RuntimeError; end
         | 
| 5 5 |  | 
| 6 | 
            -
             | 
| 7 | 
            -
             | 
| 8 | 
            -
             | 
| 9 | 
            -
             | 
| 10 | 
            -
             | 
| 11 | 
            -
             | 
| 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 | 
            -
             | 
| 14 | 
            -
             | 
| 15 | 
            -
             | 
| 16 | 
            -
             | 
| 17 | 
            -
             | 
| 18 | 
            -
             | 
| 19 | 
            -
             | 
| 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 | 
            -
             | 
| 22 | 
            -
             | 
| 23 | 
            -
             | 
| 23 | 
            +
                  def display(msg)
         | 
| 24 | 
            +
                    puts(msg)
         | 
| 25 | 
            +
                  end
         | 
| 24 26 |  | 
| 25 | 
            -
             | 
| 26 | 
            -
             | 
| 27 | 
            -
             | 
| 28 | 
            -
             | 
| 29 | 
            -
             | 
| 30 | 
            -
             | 
| 31 | 
            -
             | 
| 32 | 
            -
             | 
| 33 | 
            -
             | 
| 34 | 
            -
             | 
| 35 | 
            -
             | 
| 36 | 
            -
             | 
| 37 | 
            -
             | 
| 38 | 
            -
             | 
| 39 | 
            -
             | 
| 40 | 
            -
             | 
| 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 | 
            -
             | 
| 43 | 
            -
             | 
| 44 | 
            -
             | 
| 45 | 
            -
             | 
| 46 | 
            -
             | 
| 47 | 
            -
             | 
| 48 | 
            -
             | 
| 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 | 
            -
             | 
| 2 | 
            +
              class App < Base
         | 
| 3 3 | 
             
                def create
         | 
| 4 | 
            -
             | 
| 5 | 
            -
                   | 
| 6 | 
            -
             | 
| 7 | 
            -
             | 
| 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 | 
            -
             | 
| 3 | 
            -
             | 
| 2 | 
            +
              class Auth < Base
         | 
| 3 | 
            +
                attr_accessor :api_key_hash
         | 
| 4 4 |  | 
| 5 | 
            -
             | 
| 6 | 
            -
             | 
| 7 | 
            -
             | 
| 5 | 
            +
                def client
         | 
| 6 | 
            +
                  @client ||= init_selenium_shots
         | 
| 7 | 
            +
                end
         | 
| 8 8 |  | 
| 9 | 
            -
             | 
| 10 | 
            -
             | 
| 11 | 
            -
             | 
| 9 | 
            +
                def init_selenium_shots
         | 
| 10 | 
            +
                  SeleniumShots::Client.new(api_key)
         | 
| 11 | 
            +
                end
         | 
| 12 12 |  | 
| 13 | 
            -
             | 
| 13 | 
            +
                def api_key
         | 
| 14 14 | 
             
                  get_api_key
         | 
| 15 | 
            -
             | 
| 15 | 
            +
                end
         | 
| 16 16 |  | 
| 17 17 | 
             
                def get_api_key_from_host
         | 
| 18 | 
            -
                  RestClient.post 'http://seleniumshots. | 
| 19 | 
            -
             | 
| 20 | 
            -
                end
         | 
| 21 | 
            -
             | 
| 22 | 
            -
             | 
| 23 | 
            -
             | 
| 24 | 
            -
             | 
| 25 | 
            -
             | 
| 26 | 
            -
             | 
| 27 | 
            -
             | 
| 28 | 
            -
             | 
| 29 | 
            -
             | 
| 30 | 
            -
             | 
| 31 | 
            -
             | 
| 32 | 
            -
             | 
| 33 | 
            -
             | 
| 34 | 
            -
             | 
| 35 | 
            -
             | 
| 36 | 
            -
             | 
| 37 | 
            -
             | 
| 38 | 
            -
             | 
| 39 | 
            -
             | 
| 40 | 
            -
             | 
| 41 | 
            -
             | 
| 42 | 
            -
             | 
| 43 | 
            -
             | 
| 44 | 
            -
             | 
| 45 | 
            -
             | 
| 46 | 
            -
             | 
| 47 | 
            -
             | 
| 48 | 
            -
             | 
| 49 | 
            -
             | 
| 50 | 
            -
             | 
| 51 | 
            -
             | 
| 52 | 
            -
             | 
| 53 | 
            -
             | 
| 54 | 
            -
             | 
| 55 | 
            -
             | 
| 56 | 
            -
             | 
| 57 | 
            -
             | 
| 58 | 
            -
             | 
| 59 | 
            -
             | 
| 60 | 
            -
             | 
| 61 | 
            -
             | 
| 62 | 
            -
             | 
| 63 | 
            -
             | 
| 64 | 
            -
             | 
| 65 | 
            -
             | 
| 66 | 
            -
             | 
| 67 | 
            -
             | 
| 68 | 
            -
             | 
| 69 | 
            -
             | 
| 70 | 
            -
             | 
| 71 | 
            -
             | 
| 72 | 
            -
             | 
| 73 | 
            -
             | 
| 74 | 
            -
             | 
| 75 | 
            -
             | 
| 76 | 
            -
             | 
| 77 | 
            -
             | 
| 78 | 
            -
             | 
| 79 | 
            -
             | 
| 80 | 
            -
             | 
| 81 | 
            -
             | 
| 82 | 
            -
             | 
| 83 | 
            -
             | 
| 84 | 
            -
             | 
| 85 | 
            -
             | 
| 86 | 
            -
             | 
| 87 | 
            -
             | 
| 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 | 
            -
             | 
| 90 | 
            -
             | 
| 91 | 
            -
             | 
| 92 | 
            -
             | 
| 93 | 
            -
             | 
| 94 | 
            -
             | 
| 95 | 
            -
             | 
| 96 | 
            -
             | 
| 97 | 
            -
             | 
| 98 | 
            -
             | 
| 99 | 
            -
             | 
| 100 | 
            -
             | 
| 101 | 
            -
             | 
| 102 | 
            -
             | 
| 103 | 
            -
             | 
| 104 | 
            -
             | 
| 105 | 
            -
             | 
| 106 | 
            -
             | 
| 107 | 
            -
             | 
| 108 | 
            -
             | 
| 109 | 
            -
             | 
| 110 | 
            -
             | 
| 111 | 
            -
             | 
| 112 | 
            -
             | 
| 113 | 
            -
             | 
| 114 | 
            -
             | 
| 115 | 
            -
             | 
| 116 | 
            -
             | 
| 117 | 
            -
             | 
| 118 | 
            -
             | 
| 119 | 
            -
             | 
| 120 | 
            -
             | 
| 121 | 
            -
             | 
| 122 | 
            -
             | 
| 123 | 
            -
             | 
| 124 | 
            -
             | 
| 125 | 
            -
             | 
| 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 | 
            -
             | 
| 5 | 
            -
             | 
| 6 | 
            -
             | 
| 7 | 
            -
             | 
| 8 | 
            -
             | 
| 4 | 
            +
              class Base
         | 
| 5 | 
            +
                attr_accessor :args
         | 
| 6 | 
            +
                def initialize(args)
         | 
| 7 | 
            +
                  @args = args
         | 
| 8 | 
            +
                end
         | 
| 9 9 |  | 
| 10 | 
            -
             | 
| 11 | 
            -
             | 
| 12 | 
            -
             | 
| 10 | 
            +
                def selenium_shots
         | 
| 11 | 
            +
                  @selenium_shots ||= SeleniumShots::Command.run_internal('auth:client', args)
         | 
| 12 | 
            +
                end
         | 
| 13 13 |  | 
| 14 | 
            -
             | 
| 15 | 
            -
             | 
| 16 | 
            -
             | 
| 14 | 
            +
                def selenium_shots_api_key
         | 
| 15 | 
            +
                  @api_key ||= SeleniumShots::Command.run_internal('auth:api_key', args)
         | 
| 16 | 
            +
                end
         | 
| 17 17 |  | 
| 18 | 
            -
             | 
| 19 | 
            -
             | 
| 20 | 
            -
             | 
| 18 | 
            +
                def display(msg, newline=true)
         | 
| 19 | 
            +
                  newline ? puts(msg) : print(msg)
         | 
| 20 | 
            +
                end
         | 
| 21 21 |  | 
| 22 | 
            -
             | 
| 23 | 
            -
             | 
| 24 | 
            -
             | 
| 22 | 
            +
                def ask
         | 
| 23 | 
            +
                  gets.strip
         | 
| 24 | 
            +
                end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                def shell(cmd)
         | 
| 27 | 
            +
                  `#{cmd}`
         | 
| 28 | 
            +
                end
         | 
| 25 29 |  | 
| 26 | 
            -
             | 
| 27 | 
            -
             | 
| 28 | 
            -
             | 
| 30 | 
            +
                def home_directory
         | 
| 31 | 
            +
                  running_on_windows? ? ENV['USERPROFILE'] : ENV['HOME']
         | 
| 32 | 
            +
                end
         | 
| 29 33 |  | 
| 30 | 
            -
             | 
| 31 | 
            -
             | 
| 32 | 
            -
             | 
| 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 | 
            -
             | 
| 40 | 
            -
                   | 
| 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 | 
            -
             | 
| 43 | 
            -
             | 
| 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 | 
            -
             | 
| 50 | 
            -
             | 
| 51 | 
            -
             | 
| 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 | 
            -
             | 
| 72 | 
            +
              end
         | 
| 59 73 |  | 
| 60 74 | 
             
            end
         | 
| 61 75 |  | 
| @@ -1,15 +1,16 @@ | |
| 1 1 | 
             
            module SeleniumShots::Command
         | 
| 2 | 
            -
             | 
| 3 | 
            -
             | 
| 4 | 
            -
             | 
| 5 | 
            -
             | 
| 2 | 
            +
              class Help < Base
         | 
| 3 | 
            +
                def index
         | 
| 4 | 
            +
                  display usage
         | 
| 5 | 
            +
                end
         | 
| 6 6 |  | 
| 7 | 
            -
             | 
| 8 | 
            -
             | 
| 7 | 
            +
                def usage
         | 
| 8 | 
            +
                  usage = <<EOTXT
         | 
| 9 9 | 
             
            === General Commands
         | 
| 10 10 |  | 
| 11 | 
            -
             help | 
| 12 | 
            -
             create [name] | 
| 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 | 
            -
             | 
| 21 | 
            -
             | 
| 21 | 
            +
                end
         | 
| 22 | 
            +
              end
         | 
| 22 23 | 
             
            end
         | 
| 23 24 |  | 
| @@ -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 | 
            -
             | 
| 11 | 
            -
             | 
| 12 | 
            -
             | 
| 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. | 
| 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 | 
            -
             | 
| 23 | 
            +
              attr_reader :driver, :agent, :take_screenshot
         | 
| 24 | 
            +
              cattr_accessor :expected_test_count
         | 
| 24 25 |  | 
| 25 | 
            -
               | 
| 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  | 
| 28 | 
            -
                 | 
| 29 | 
            -
             | 
| 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 | 
| 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  | 
| 44 | 
            -
                 | 
| 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  | 
| 55 | 
            -
                 | 
| 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  | 
| 61 | 
            -
                 | 
| 62 | 
            -
                 | 
| 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 | 
            -
             | 
| 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 | 
            -
             | 
| 140 | 
            +
                
         | 
| 141 | 
            +
                @driver.manage.timeouts.implicit_wait = 2 #seconds
         | 
| 142 | 
            +
                @driver.navigate.to SeleniumConfig.default_browser_url
         | 
| 73 143 |  | 
| 74 | 
            -
             | 
| 75 | 
            -
             | 
| 76 | 
            -
             | 
| 77 | 
            -
             | 
| 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. | 
| 85 | 
            -
                   | 
| 86 | 
            -
                elsif browser. | 
| 87 | 
            -
                   | 
| 88 | 
            -
                elsif browser. | 
| 89 | 
            -
                   | 
| 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 | 
            -
             | 
| 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 =>  | 
| 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 | 
            -
              -  | 
| 9 | 
            -
              version: 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- | 
| 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:  | 
| 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 | 
            -
                    -  | 
| 42 | 
            -
                    -  | 
| 43 | 
            -
                    -  | 
| 44 | 
            -
                    version:  | 
| 45 | 
            -
              type: : | 
| 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:  | 
| 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 | 
            -
                    -  | 
| 57 | 
            -
                    -  | 
| 58 | 
            -
                    version: 0. | 
| 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:  | 
| 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 | 
            -
                    -  | 
| 70 | 
            +
                    - 0
         | 
| 71 | 
            +
                    - 8
         | 
| 70 72 | 
             
                    - 2
         | 
| 71 | 
            -
                     | 
| 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
         |