selenium_shots 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 Kyle J. Ginavan
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/LICENSE.orig ADDED
@@ -0,0 +1,24 @@
1
+ <<<<<<< HEAD
2
+ Copyright (c) 2010 Kyle Ginavan
3
+ =======
4
+ Copyright (c) 2010 Kyle J. Ginavan
5
+ >>>>>>> 469e6dd32e742be5b03b6d01b441316b7e4c6bbc
6
+
7
+ Permission is hereby granted, free of charge, to any person obtaining
8
+ a copy of this software and associated documentation files (the
9
+ "Software"), to deal in the Software without restriction, including
10
+ without limitation the rights to use, copy, modify, merge, publish,
11
+ distribute, sublicense, and/or sell copies of the Software, and to
12
+ permit persons to whom the Software is furnished to do so, subject to
13
+ the following conditions:
14
+
15
+ The above copyright notice and this permission notice shall be
16
+ included in all copies or substantial portions of the Software.
17
+
18
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,18 @@
1
+ = selenium_shots
2
+
3
+ http://www.seleniumshots.com
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
+
6
+ == Note on Patches/Pull Requests
7
+
8
+ * Fork the project.
9
+ * Make your feature addition or bug fix.
10
+ * Add tests for it. This is important so I don't break it in a
11
+ future version unintentionally.
12
+ * Commit, do not mess with rakefile, version, or history.
13
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
14
+ * Send me a pull request. Bonus points for topic branches.
15
+
16
+ == Copyright
17
+
18
+ Copyright (c) 2010 Kyle Ginavan. See LICENSE for details.
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH.unshift(File.dirname(__FILE__) + '/../lib/selenium_shots/cli/')
4
+
5
+ require 'init'
6
+
7
+ args = ARGV.dup
8
+ ARGV.clear
9
+ command = args.shift.strip rescue 'help'
10
+
11
+ if File.exists?('config/environment.rb')
12
+ SeleniumShots::Command.run(command, args)
13
+ else
14
+ puts "No rails app found!, you need stay inside on the root of one rails app"
15
+ end
16
+
@@ -0,0 +1,2 @@
1
+ require "selenium_shots/test_selenium_shots"
2
+
@@ -0,0 +1,53 @@
1
+ require 'rubygems'
2
+ require 'rexml/document'
3
+ require 'rest_client'
4
+ require 'uri'
5
+ require 'time'
6
+
7
+ class SeleniumShots::Client
8
+
9
+ attr_reader :host, :api_key
10
+
11
+ def initialize(api_key, host='seleniumshots.heroku.com')
12
+ @api_key = api_key
13
+ @host = host
14
+ end
15
+
16
+ def list
17
+ #get list app from selenium_shots
18
+ []
19
+ end
20
+
21
+ ############
22
+ def resource(uri)
23
+ RestClient::Resource.new("http://#{host}", api_key)[uri]
24
+ end
25
+
26
+ def get(uri)
27
+ resource(uri).get
28
+ end
29
+
30
+ def post(uri)
31
+ resource(uri).post
32
+ end
33
+
34
+ def put(uri)
35
+ resource(uri).put
36
+ end
37
+
38
+ def delete(uri)
39
+ resource(uri).delete
40
+ 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
+ end
53
+
@@ -0,0 +1,50 @@
1
+ module SeleniumShots
2
+ module Command
3
+ class InvalidCommand < RuntimeError; end
4
+ class CommandFailed < RuntimeError; end
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
12
+
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
20
+
21
+ def display(msg)
22
+ puts(msg)
23
+ end
24
+
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
41
+
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
49
+ end
50
+
@@ -0,0 +1,20 @@
1
+ module SeleniumShots::Command
2
+ class App < Base
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}"
8
+ 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
19
+ end
20
+
@@ -0,0 +1,127 @@
1
+ module SeleniumShots::Command
2
+ class Auth < Base
3
+ attr_accessor :api_key_hash
4
+
5
+ def client
6
+ @client ||= init_selenium_shots
7
+ end
8
+
9
+ def init_selenium_shots
10
+ SeleniumShots::Client.new(api_key)
11
+ end
12
+
13
+ def api_key
14
+ get_api_key
15
+ end
16
+
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
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
126
+ end
127
+
@@ -0,0 +1,61 @@
1
+ require 'fileutils'
2
+
3
+ module SeleniumShots::Command
4
+ class Base
5
+ attr_accessor :args
6
+ def initialize(args)
7
+ @args = args
8
+ end
9
+
10
+ def selenium_shots
11
+ @selenium_shots ||= SeleniumShots::Command.run_internal('auth:client', args)
12
+ end
13
+
14
+ def display(msg, newline=true)
15
+ newline ? puts(msg) : print(msg)
16
+ end
17
+
18
+ def ask
19
+ gets.strip
20
+ end
21
+
22
+ def shell(cmd)
23
+ `#{cmd}`
24
+ end
25
+
26
+ def home_directory
27
+ running_on_windows? ? ENV['USERPROFILE'] : ENV['HOME']
28
+ end
29
+
30
+ def running_on_windows?
31
+ RUBY_PLATFORM =~ /mswin32/
32
+ end
33
+
34
+ def config_file
35
+ 'config/selenium_shots.yml'
36
+ end
37
+
38
+ def make_config_file(name, api_key)
39
+ FileUtils.rm_f(config_file) if File.exists?(config_file)
40
+ config_file_hash = <<EOFILE
41
+ api_key: "#{api_key}"
42
+ hub_url: 'url'
43
+ hub_port: 'port'
44
+ default_browser_url: 'default url'
45
+ application_name: "#{name}"
46
+ browsers:
47
+ - IE8 on XP
48
+ 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')
56
+ end
57
+
58
+ end
59
+
60
+ end
61
+
@@ -0,0 +1,23 @@
1
+ module SeleniumShots::Command
2
+ class Help < Base
3
+ def index
4
+ display usage
5
+ end
6
+
7
+ def usage
8
+ usage = <<EOTXT
9
+ === General Commands
10
+
11
+ help # show this usage
12
+ create [name] # create file config for your app
13
+ === Example story:
14
+
15
+ rails myapp
16
+ cd myapp
17
+ (...make edits...)
18
+ selenium_shots create example_one
19
+ EOTXT
20
+ end
21
+ end
22
+ end
23
+
@@ -0,0 +1,9 @@
1
+ module SeleniumShots; end
2
+
3
+ $LOAD_PATH.unshift(File.dirname(__FILE__) + '/commands')
4
+
5
+ require "rubygems" #take this later
6
+ require "base"
7
+ require "client"
8
+ require "command"
9
+
@@ -0,0 +1,104 @@
1
+ require "test/unit"
2
+ require "rubygems"
3
+ require "selenium/client"
4
+ require 'active_support'
5
+ require 'active_support/test_case'
6
+ require 'ostruct'
7
+
8
+
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 = ''
15
+ #
16
+ #activeresource models
17
+ class SeleniumTest < ActiveResource::Base
18
+ self.site = "http://seleniumshots.heroku.com"
19
+ self.user = SeleniumConfig.api_key
20
+ end
21
+
22
+
23
+ class ActiveSupport::TestCase
24
+
25
+ attr_reader :browser, :agent
26
+
27
+ def self.selenium_shot(description, &block)
28
+ #set vars
29
+ @description = description
30
+
31
+ test_name = "test_#{description.gsub(/\s+/,'_')}".to_sym
32
+ defined = instance_method(test_name) rescue false
33
+ raise "#{test_name} is already defined in #{self}" if defined
34
+ if block_given?
35
+ define_method(test_name, &block)
36
+ else
37
+ define_method(test_name) do
38
+ flunk "No implementation provided for #{name}"
39
+ end
40
+ end
41
+ end
42
+
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
52
+ end
53
+
54
+ def hover_click(locator)
55
+ browser.mouse_over locator
56
+ browser.click locator
57
+ browser.focus locator
58
+ end
59
+
60
+ def open_and_wait(url)
61
+ browser.open url
62
+ browser.wait_for_page_to_load "30000"
63
+ end
64
+
65
+ def run_test(&proc)
66
+ begin
67
+ proc.call
68
+ @error = nil
69
+ rescue => e
70
+ @error = e.message
71
+ end
72
+ end
73
+
74
+ def teardown
75
+ save_test ({:selenium_test_group_name => @group, :selenium_test_name => @name,
76
+ :description => @description})
77
+ browser.close_current_browser_session
78
+ end
79
+
80
+ def capture_screenshot_on(src)
81
+ browser.window_focus
82
+ browser.window_maximize
83
+ 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")
90
+ end
91
+ end
92
+
93
+ def save_test(params)
94
+ src = "#{SeleniumConfig.application_name}_#{params[:selenium_test_group_name]}_#{params[:selenium_test_name]}_" +
95
+ "#{browser.browser_string.gsub(/\s+/,"_").downcase}.png"
96
+
97
+ capture_screenshot_on(src)
98
+
99
+ 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,
101
+ :selenium_test_group_name => params[:selenium_test_group_name], :application_name => SeleniumConfig.application_name)
102
+ end
103
+ end
104
+
data/test/helper.rb ADDED
@@ -0,0 +1,10 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require 'shoulda'
4
+
5
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
6
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
7
+ require 'selenium_shots'
8
+
9
+ class Test::Unit::TestCase
10
+ end
@@ -0,0 +1,7 @@
1
+ require 'helper'
2
+
3
+ class TestSeleniumShots < Test::Unit::TestCase
4
+ should "probably rename this file and start testing for real" do
5
+ flunk "hey buddy, you should probably rename this file and start testing for real"
6
+ end
7
+ end
metadata ADDED
@@ -0,0 +1,130 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: selenium_shots
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 0
8
+ - 0
9
+ version: 0.0.0
10
+ platform: ruby
11
+ authors:
12
+ - Kyle J. Ginavan
13
+ - Mauro Torres
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-04-08 00:00:00 -05:00
19
+ default_executable: selenium_shots
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: thoughtbot-shoulda
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ segments:
29
+ - 0
30
+ version: "0"
31
+ type: :development
32
+ version_requirements: *id001
33
+ - !ruby/object:Gem::Dependency
34
+ name: rest-client
35
+ prerelease: false
36
+ requirement: &id002 !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ segments:
41
+ - 0
42
+ - 8
43
+ - 2
44
+ version: 0.8.2
45
+ type: :runtime
46
+ version_requirements: *id002
47
+ - !ruby/object:Gem::Dependency
48
+ name: launchy
49
+ prerelease: false
50
+ requirement: &id003 !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ segments:
55
+ - 0
56
+ - 3
57
+ - 2
58
+ version: 0.3.2
59
+ type: :runtime
60
+ version_requirements: *id003
61
+ - !ruby/object:Gem::Dependency
62
+ name: selenium-client
63
+ prerelease: false
64
+ requirement: &id004 !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ segments:
69
+ - 1
70
+ - 2
71
+ - 18
72
+ version: 1.2.18
73
+ type: :runtime
74
+ version_requirements: *id004
75
+ 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
76
+ email: kyle@4rockets.com
77
+ executables:
78
+ - selenium_shots
79
+ extensions: []
80
+
81
+ extra_rdoc_files:
82
+ - LICENSE
83
+ - LICENSE.orig
84
+ - README.rdoc
85
+ files:
86
+ - lib/selenium_shots.rb
87
+ - lib/selenium_shots/cli/client.rb
88
+ - lib/selenium_shots/cli/command.rb
89
+ - lib/selenium_shots/cli/commands/app.rb
90
+ - lib/selenium_shots/cli/commands/auth.rb
91
+ - lib/selenium_shots/cli/commands/base.rb
92
+ - lib/selenium_shots/cli/commands/help.rb
93
+ - lib/selenium_shots/cli/init.rb
94
+ - lib/selenium_shots/test_selenium_shots.rb
95
+ - LICENSE
96
+ - LICENSE.orig
97
+ - README.rdoc
98
+ has_rdoc: true
99
+ homepage: http://github.com/kylejginavan/selenium_shots
100
+ licenses: []
101
+
102
+ post_install_message:
103
+ rdoc_options:
104
+ - --charset=UTF-8
105
+ require_paths:
106
+ - lib
107
+ required_ruby_version: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - ">="
110
+ - !ruby/object:Gem::Version
111
+ segments:
112
+ - 0
113
+ version: "0"
114
+ required_rubygems_version: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - ">="
117
+ - !ruby/object:Gem::Version
118
+ segments:
119
+ - 0
120
+ version: "0"
121
+ requirements: []
122
+
123
+ rubyforge_project:
124
+ rubygems_version: 1.3.6
125
+ signing_key:
126
+ specification_version: 3
127
+ summary: Integration Tests made easy
128
+ test_files:
129
+ - test/helper.rb
130
+ - test/test_selenium_shots.rb