browser_shooter 0.2.3 → 0.3.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.
@@ -0,0 +1,65 @@
1
+ module BrowserShooter
2
+ class Base
3
+ attr_reader :opts
4
+
5
+ def initialize( opts )
6
+ @opts = opts
7
+ end
8
+
9
+ def run
10
+ BrowserShooter::Logger.verbose = opts[:verbose]
11
+ BrowserShooter::Logger.log( "Starting script running with version #{BrowserShooter::VERSION}..." )
12
+
13
+ config = BrowserShooter::Configurator.new( opts )
14
+ suites = config.suites
15
+
16
+ suites.each do |suite|
17
+ suite.tests.each do |test|
18
+ suite.browsers.each do |browser|
19
+ BrowserShooter::Base.run_test(
20
+ suite,
21
+ test,
22
+ browser,
23
+ config["output_path"]
24
+ )
25
+ end
26
+ end
27
+ end
28
+
29
+ BrowserShooter::Logger.log( "... script running ended." )
30
+ BrowserShooter::Logger.log( "Logs and Shots are in: #{config["output_path"]}", true )
31
+ BrowserShooter::Logger.log( "BYE!" )
32
+ end
33
+
34
+ def self.run_test( suite, test, browser, output_path )
35
+ BrowserShooter::Logger.log( "Executing #{suite.name} | #{test.name} | #{browser.name}", true )
36
+ output_path = "#{output_path}/#{suite.name}/#{test.name}/#{browser.name}"
37
+
38
+ driver = nil
39
+
40
+ begin
41
+ driver =
42
+ Selenium::WebDriver.for(
43
+ :remote,
44
+ :url => browser.url,
45
+ :desired_capabilities => browser.type.to_sym
46
+ )
47
+
48
+ logs =
49
+ BrowserShooter::Commander.script(
50
+ test.commands,
51
+ driver,
52
+ output_path
53
+ )
54
+
55
+ BrowserShooter::LogExporter.export(
56
+ logs,
57
+ "#{output_path}/logs"
58
+ )
59
+
60
+ ensure
61
+ driver.quit if driver
62
+ end
63
+ end
64
+ end
65
+ end
@@ -1,141 +1,143 @@
1
- class BrowserShooter
2
- module Commander
3
- def self.execute( command, client, shots_path )
4
- BrowserShooter::Logger.log "command: #{command}"
5
-
6
- if( command.split[0].strip == "shot" )
7
- sufix = command.split[1] ? command.split[1].strip : nil
8
-
9
- BrowserShooter::Commander.shot(
10
- client,
11
- shots_path,
12
- sufix
13
- )
1
+ module BrowserShooter::Commander
2
+
3
+ def self.script( commands, driver, output_path )
4
+ test_result =
5
+ commands.map do |command|
6
+ command_result =
7
+ BrowserShooter::Commander.wrapper_execute(
8
+ command.strip,
9
+ driver,
10
+ output_path
11
+ )
14
12
 
15
- elsif( command.split[0].strip == "shot_system" )
16
- sufix = command.split[1] ? command.split[1].strip : nil
13
+ BrowserShooter::Logger.command_result( command_result )
17
14
 
18
- BrowserShooter::Commander.shot_system(
19
- client,
20
- shots_path,
21
- sufix
22
- )
15
+ command_result
16
+ end
23
17
 
24
- elsif( command.split[0].strip == "pause" )
25
- BrowserShooter::Commander.pause( command.split[1].strip.to_i )
18
+ BrowserShooter::Logger.test_result( test_result )
26
19
 
27
- elsif( command.split[0].strip == "wait_for_element" )
28
- params = command.match /wait_for_element "(.*)"\s?,\s?(\d*)/
20
+ test_result
21
+ end
29
22
 
30
- BrowserShooter::Commander.wait_for_element(
31
- client,
32
- params[1],
33
- params[2].to_i
34
- )
23
+ def self.execute( command, driver, output_path )
24
+ BrowserShooter::Logger.log "command: #{command}"
35
25
 
36
- elsif( command.split[0].strip == "type" )
37
- params = command.match /type "(.*)"\s?,\s?"(.*)"/
26
+ if( command.split[0].strip == "shot" )
27
+ sufix = command.split[1] ? command.split[1].strip : nil
38
28
 
39
- BrowserShooter::Commander.type(
40
- client,
41
- params[1],
42
- params[2]
43
- )
29
+ BrowserShooter::Commander.shot(
30
+ driver,
31
+ output_path,
32
+ sufix
33
+ )
44
34
 
45
- elsif( command.split[0].strip == "click" )
46
- params = command.match /click "(.*)"/
47
- BrowserShooter::Commander.click(
48
- client,
49
- params[1]
50
- )
35
+ elsif( command.split[0].strip == "pause" )
36
+ BrowserShooter::Commander.pause( command.split[1].strip.to_i )
51
37
 
52
- else
53
- eval "client.#{command}"
38
+ elsif( command.split[0].strip == "wait_for_element" )
39
+ params = command.match /wait_for_element "(.*)"\s?,\s?(\d*)/
54
40
 
55
- end
56
- end
41
+ BrowserShooter::Commander.wait_for_element(
42
+ driver,
43
+ params[1],
44
+ params[2].to_i
45
+ )
57
46
 
58
- def self.wrapper_execute( command, client, shots_path )
59
- result = {
60
- :time => Time.now.to_i,
61
- :command => command
62
- }
63
-
64
- begin
65
- message =
66
- BrowserShooter::Commander.execute(
67
- command,
68
- client,
69
- shots_path
70
- )
47
+ elsif( command.split[0].strip == "type" )
48
+ params = command.match /type "(.*)"\s?,\s?"(.*)"/
71
49
 
72
- result.merge!(
73
- :success => true,
74
- :message => message
75
- )
50
+ BrowserShooter::Commander.type(
51
+ driver,
52
+ params[1],
53
+ params[2]
54
+ )
76
55
 
77
- rescue Exception => e
78
- BrowserShooter::Logger.log "ERROR: #{e.message}"
56
+ elsif( command.split[0].strip == "click" )
57
+ params = command.match /click "(.*)"/
58
+ BrowserShooter::Commander.click(
59
+ driver,
60
+ params[1]
61
+ )
62
+
63
+ else
64
+ eval "driver.#{command}"
65
+
66
+ end
67
+ end
79
68
 
80
- result.merge!(
81
- :success => false,
82
- :message => e.message
69
+ def self.wrapper_execute( command, driver, output_path )
70
+ result = {
71
+ :time => Time.now.to_i,
72
+ :command => command
73
+ }
74
+
75
+ begin
76
+ message =
77
+ BrowserShooter::Commander.execute(
78
+ command,
79
+ driver,
80
+ output_path
83
81
  )
84
82
 
85
- end
86
83
 
87
- return result
88
- end
84
+ result.merge!(
85
+ :success => true,
86
+ :message => message
87
+ )
89
88
 
90
- def self.shot( client, path, sufix = nil )
91
- sufix = timestamp unless sufix
92
- path = "#{path}_#{sufix}.png"
89
+ rescue Exception => e
90
+ BrowserShooter::Logger.log "ERROR: #{e.message}"
93
91
 
94
- BrowserShooter::Logger.log "shooting in '#{path}'"
95
- client.save_screenshot path
92
+ # puts "XXX: Exception"
93
+ # puts e.backtrace.join( "\n" )
94
+
95
+ result.merge!(
96
+ :success => false,
97
+ :message => e.message
98
+ )
96
99
 
97
- return path
98
100
  end
99
101
 
100
- # FIXME: Not supported in WebDriver
101
- # def self.shot_system( client, path, sufix = timestamp )
102
- # sufix = timestamp unless sufix
103
- # path = "#{path}_#{sufix}.system.png"
102
+ return result
103
+ end
104
104
 
105
- # BrowserShooter::Logger.log "shooting system in '#{path}'"
105
+ def self.shot( driver, output_path, sufix = nil )
106
+ sufix = timestamp unless sufix
107
+ shot_path = "#{output_path}/shots/#{sufix}.png"
106
108
 
107
- # File.open( path, "wb" ) do |f|
108
- # f.write( Base64.decode64( client.capture_screenshot_to_string ) )
109
- # end
109
+ BrowserShooter::Logger.log "shooting in '#{shot_path}'"
110
110
 
111
- # return path
112
- # end
111
+ FileUtils.mkdir_p( File.dirname( shot_path ) )
112
+ driver.save_screenshot( shot_path )
113
113
 
114
- def self.wait_for_element( client, css_selector, timeout )
115
- wait = Selenium::WebDriver::Wait.new( :timeout => timeout )
114
+ return shot_path
115
+ end
116
116
 
117
- wait.until do
118
- client.find_element( "css", css_selector )
119
- end
120
- end
117
+ def self.wait_for_element( driver, css_selector, timeout )
118
+ wait = Selenium::WebDriver::Wait.new( :timeout => timeout )
121
119
 
122
- def self.click( client, css_selector )
123
- client.find_element( "css", css_selector ).click
120
+ wait.until do
121
+ driver.find_element( "css", css_selector )
124
122
  end
123
+ end
125
124
 
126
- def self.type( client, css_selector, text )
127
- client.find_element( "css", css_selector ).send_keys( text )
128
- end
125
+ def self.click( driver, css_selector )
126
+ driver.find_element( "css", css_selector ).click
127
+ end
129
128
 
130
- def self.pause( seconds )
131
- BrowserShooter::Logger.log "pausing #{seconds} seconds"
132
- Kernel.sleep seconds
129
+ def self.type( driver, css_selector, text )
130
+ driver.find_element( "css", css_selector ).send_keys( text )
131
+ end
133
132
 
134
- return "#{seconds} seconds later..."
135
- end
133
+ def self.pause( seconds )
134
+ BrowserShooter::Logger.log "pausing #{seconds} seconds"
135
+ Kernel.sleep seconds
136
136
 
137
- def self.timestamp
138
- Time.now.to_i
139
- end
137
+ return "#{seconds} seconds later..."
138
+ end
139
+
140
+ def self.timestamp
141
+ Time.now.to_i
140
142
  end
141
- end
143
+ end
@@ -1,5 +1,80 @@
1
- class BrowserShooter
2
- module Configurator
1
+ module BrowserShooter
2
+ class Configurator
3
+ attr_reader :suites
4
+
5
+ def initialize( opts )
6
+ @config = BrowserShooter::Configurator.load_config( opts[:config_file] )
7
+ models = BrowserShooter::Configurator.build_models( @config )
8
+ @suites = BrowserShooter::Configurator.filter_suites( models, opts )
9
+ end
10
+
11
+ def [](value)
12
+ @config[value]
13
+ end
14
+
15
+ def self.filter_suites( models, opts )
16
+ suites = []
17
+
18
+ if( opts[:suite] )
19
+ suite = models[:suites].select{ |e| e.name == opts[:suite] }.first
20
+ raise ArgumentError, "Not suite found '#{opts[:suite]}'" if suite.nil?
21
+
22
+ suites = [suite]
23
+
24
+ elsif( opts[:test] && opts[:browsers] )
25
+ test = models[:tests].select{ |e| e.name == opts[:test] }.first
26
+ raise ArgumentError, "Not test found '#{opts[:test]}'" if test.nil?
27
+
28
+ browsers = models[:browsers].select{ |e| opts[:browsers].include? e.name }
29
+ raise ArgumentError, "Not browsers found '#{opts[:browsers].join( "," )}'" if browsers.empty?
30
+
31
+ suite = BrowserShooter::Models::Suite.new( "anonymous", [test], browsers )
32
+ suites = [suite]
33
+
34
+ elsif( opts[:test] )
35
+ test = models[:tests].select{ |e| e.name == opts[:test] }.first
36
+ raise ArgumentError, "Not test found '#{opts[:test]}'" if test.nil?
37
+
38
+ browsers = models[:browsers]
39
+ suite = BrowserShooter::Models::Suite.new( "anonymous", [test], browsers )
40
+ suites = [suite]
41
+
42
+ else
43
+ suites = models[:suites]
44
+
45
+ end
46
+
47
+ suites
48
+ end
49
+
50
+ def self.build_models( config )
51
+ tests =
52
+ config["tests"].map do |name, commands|
53
+ test_commands = commands.split( "\n" )
54
+
55
+ BrowserShooter::Models::Test.new( name, test_commands )
56
+ end
57
+
58
+ browsers =
59
+ config["browsers"].map do |name, opts|
60
+ BrowserShooter::Models::Browser.new( name, opts["url"], opts["type"] )
61
+ end
62
+
63
+ suites =
64
+ config["suites"].map do |name, opts|
65
+ suite_tests = tests.select{ |e| opts["tests"].include? e.name }
66
+ suite_browsers = browsers.select{ |e| opts["browsers"].include? e.name }
67
+
68
+ BrowserShooter::Models::Suite.new( name, suite_tests, suite_browsers )
69
+ end
70
+
71
+ {
72
+ :tests => tests,
73
+ :browsers => browsers,
74
+ :suites => suites
75
+ }
76
+ end
77
+
3
78
  def self.load_config( config_file_path )
4
79
  config = {
5
80
  "output_path" => "~/browser_shooter",
@@ -17,10 +92,6 @@ class BrowserShooter
17
92
  output_path = File.expand_path( "#{output_path}/#{timestamp}" )
18
93
  BrowserShooter::Logger.log( "output_path: #{output_path}" )
19
94
 
20
- FileUtils.mkdir_p( output_path )
21
- FileUtils.mkdir( "#{output_path}/shots" )
22
- FileUtils.mkdir( "#{output_path}/logs" )
23
-
24
95
  output_path
25
96
  end
26
97
 
@@ -1,26 +1,20 @@
1
- class BrowserShooter
1
+ module BrowserShooter
2
2
  module LogExporter
3
- def self.export( logs, path, format )
4
- BrowserShooter::Logger.log "Exporting '#{format}' logs to #{path}"
5
- send(:"export_to_#{format}", logs, path )
6
- end
3
+ def self.export( logs, logs_path, format = "csv" )
4
+ logs_path = File.expand_path( "#{logs_path}/log.#{format}" )
5
+ BrowserShooter::Logger.log "Exporting '#{format}' logs to #{logs_path}"
6
+ FileUtils.mkdir_p( File.dirname( logs_path ) )
7
7
 
8
- def self.export_to_json( logs, path )
9
- File.open( "#{path}/logs.json", "w" ) do |f|
10
- f.write JSON.pretty_generate( logs )
11
- end
8
+ send(:"export_to_#{format}", logs, logs_path )
12
9
  end
13
10
 
14
11
  def self.export_to_csv( logs, path )
15
- logs.each do |script_name, results|
16
- _path = File.expand_path "#{path}/#{script_name}.csv"
17
-
18
- File.open( _path, "w" ) do |f|
19
- f.puts results.first.keys.join( " | " )
12
+ File.open( path, "w" ) do |f|
13
+ f.puts "time | success | command | message"
20
14
 
21
- results.each do |result|
22
- f.puts result.values.join( " | " )
23
- end
15
+ logs.each do |result|
16
+ line = "#{result[:time]} | #{result[:success]} | #{result[:command]} | #{result[:message]}".gsub( "\n", " - " )
17
+ f.puts line
24
18
  end
25
19
  end
26
20
  end
@@ -1,7 +1,24 @@
1
- class BrowserShooter
1
+ module BrowserShooter
2
2
  module Logger
3
- def self.log( message )
4
- puts "[BrowserShooter #{Time.now.strftime( "%F %T" )}] #{message}"
3
+ extend self
4
+
5
+ attr_accessor :verbose
6
+
7
+ def log( message, force = verbose )
8
+ if force
9
+ Kernel.puts "[BrowserShooter #{Time.now.strftime( "%F %T" )}] #{message}"
10
+ end
5
11
  end
12
+
13
+ def command_result( command_result )
14
+ Kernel.print "." if command_result[:success]
15
+ Kernel.print "F" if !command_result[:success]
16
+ end
17
+
18
+ def test_result( test_result )
19
+ Kernel.puts " (success)" if test_result.all? { |e| e[:success] }
20
+ Kernel.puts " (fail)" if !test_result.all? { |e| e[:success] }
21
+ end
22
+
6
23
  end
7
24
  end