bauxite 0.6.5 → 0.6.6

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.
@@ -53,6 +53,10 @@ module Bauxite
53
53
  # Number of seconds to wait before issuing a timeout error. This
54
54
  # timeout applies only to Selectors.
55
55
  #
56
+ # [-o, \--open_timeout SECONDS]
57
+ # Number of seconds to wait before issuing a timeout error. This
58
+ # timeout applies only to page loading times (i.e. Net::HTTP).
59
+ #
56
60
  # [-d, \--debug]
57
61
  # If an error occurs, break into the debug console. This mode is very
58
62
  # useful to try different selector combinations and debug
@@ -84,6 +88,8 @@ module Bauxite
84
88
  # To see a complete list of the available loggers execute:
85
89
  # bauxite -h
86
90
  #
91
+ # Note that multiple instances of this option can be specified.
92
+ #
87
93
  # [-L, \--logger-option OPTION]
88
94
  # A <tt>name=value</tt> pair of options that are directly forwarded to
89
95
  # the logger. This option argument can appear multiple times in the
@@ -106,12 +112,34 @@ module Bauxite
106
112
  # To see a complete list of the available selectors execute:
107
113
  # bauxite -h
108
114
  #
115
+ # [-c, \--capture]
116
+ # If the test fails, capture a screenshot in the +output+ directory.
117
+ # See the \--output option below.
118
+ #
119
+ # [\--csv-summary FILE]
120
+ # Output a single-line CSV summary ideal to feed into the Plot Jenkins
121
+ # plugin. If +FILE+ is not specified, defaults to +summary.csv+
122
+ #
123
+ # [\--output DIR]
124
+ # Output directory for generated artifacts. Defaults to the current
125
+ # working directory.
126
+ #
109
127
  # [-u, \--url URL]
110
128
  # This option is an alias of:
111
129
  # -p remote -P url=URL
112
130
  # If +URL+ is not present <tt>http://localhost:4444/wd/hub</tt> will be
113
131
  # assumed.
114
132
  #
133
+ # [\--jenkins DIR]
134
+ # Configures default options for Jenkins integration. This option is an
135
+ # alias of:
136
+ # -p remote -P url=http://localhost:4444/wd/hub -l echo -l html
137
+ # -L html_package=true -c --csv-summary
138
+ # --output DIR
139
+ # Note that any of the options above can be overridden by specifying the
140
+ # option after the <tt>--jenkins</tt> option. For example:
141
+ # --jenkins -u selenium.my-organization.com:4445
142
+ #
115
143
  # [-e, \--extension DIR]
116
144
  # Loads every Ruby file (*.rb) inside +DIR+ (and subdirectories). This
117
145
  # option can be used to load custom Bauxite extensions (e.g. Actions,
@@ -147,7 +175,7 @@ module Bauxite
147
175
  class Application
148
176
  def self.start #:nodoc:
149
177
  options = {
150
- :logger => (ENV['TERM'] == 'xterm') ? 'xterm' : 'terminal',
178
+ :logger => [],
151
179
  :verbose => false,
152
180
  :break => false,
153
181
  :driver => 'firefox',
@@ -160,7 +188,9 @@ module Bauxite
160
188
  :logger_opt => {},
161
189
  :extensions => []
162
190
  }
163
-
191
+
192
+ default_logger = (ENV['TERM'] == 'xterm') ? 'xterm' : 'terminal'
193
+
164
194
  OptionParser.new do |opts|
165
195
  opts.banner = "Usage: bauxite [options] [files...]"
166
196
 
@@ -173,13 +203,19 @@ module Bauxite
173
203
  end
174
204
  self
175
205
  end
176
- def opts.multi(*args)
206
+ def opts.hash(*args)
177
207
  on(*args[1..-1]) do |v|
178
208
  n,v = v.split('=', 2)
179
209
  @o[args[0]][n.to_sym] = v || true
180
210
  end
181
211
  self
182
212
  end
213
+ def opts.multi(*args)
214
+ on(*args[1..-1]) do |v|
215
+ @o[args[0]] << v
216
+ end
217
+ self
218
+ end
183
219
  opts.o = options
184
220
 
185
221
  opts.separator ""
@@ -191,23 +227,36 @@ module Bauxite
191
227
  .single(:debug , "-d", "--debug", "Break to debug on error. "+
192
228
  "Start the debug console if no input files given.")
193
229
  .single(:driver , "-p", "--provider PROVIDER" , "Driver provider")
194
- .multi( :driver_opt , "-P", "--provider-option OPTION", "Provider options (name=value)")
195
- .single(:logger , "-l", "--logger LOGGER" , "Logger type ('#{options[:logger]}')")
196
- .multi( :logger_opt , "-L", "--logger-option OPTION" , "Logger options (name=value)")
230
+ .hash( :driver_opt , "-P", "--provider-option OPTION", "Provider options (name=value)")
231
+ .multi( :logger , "-l", "--logger LOGGER" , "Logger type ('#{default_logger}')")
232
+ .hash( :logger_opt , "-L", "--logger-option OPTION" , "Logger options (name=value)")
197
233
  .single(:reset , "-r", "--reset" , "Reset driver between tests")
198
234
  .single(:wait , "-w", "--wait" , "Wait for ENTER before exiting")
199
235
  .single(:selector , "-s", "--selector SELECTOR" , "Default selector ('#{options[:selector]}')")
200
236
  .single(:capture , "-c", "--capture" , "If the test fails, capture a screenshot")
201
- .single(:csv , "--csv-summary FILE" , "Output a single-line CSV summary")
202
237
  .single(:output , "--output DIR" , "Output directory for generated artifacts")
203
- opts.on("-u", "--url [URL]", "Configure the remote provider listening in the given url.") do |v|
238
+ .multi( :extensions , "-e", "--extension DIR" , "Load extensions from DIR")
239
+
240
+ opts.on("-u", "--url [URL]", "Configure the remote provider listening in the given url") do |v|
204
241
  v = 'localhost:4444' unless v
205
242
  v = 'http://'+v unless v.match /^https?:\/\//
206
243
  v = v + '/wd/hub' unless v.end_with? '/wd/hub'
207
244
  options[:driver] = 'remote'
208
245
  options[:driver_opt][:url] = v
209
246
  end
210
- opts.on("-e", "--extension DIR", "Load extensions from DIR") { |v| options[:extensions] << v }
247
+
248
+ opts.on("--csv-summary [FILE]", "Output a single-line CSV summary") { |v| options[:csv] = v || 'summary.csv' }
249
+
250
+ opts.on("--jenkins DIR", "Configure default options for Jenkins integration") do |v|
251
+ options[:driver] = 'remote'
252
+ options[:driver_opt][:url] = 'http://localhost:4444/wd/hub'
253
+ options[:logger] = ['echo', 'html']
254
+ options[:output] = v
255
+ options[:logger_opt][:html_package] = true
256
+ options[:capture] = true
257
+ options[:csv] = 'summary.csv'
258
+ end
259
+
211
260
  opts.on("--version", "Show version") do
212
261
  puts "bauxite #{Bauxite::VERSION}"
213
262
  exit
@@ -232,7 +281,9 @@ module Bauxite
232
281
 
233
282
  opts.separator ""
234
283
  end.parse!
235
-
284
+
285
+ options[:logger] << default_logger unless options[:logger].size > 0
286
+
236
287
  ctx = nil
237
288
  begin
238
289
  ctx = Context.new(options)
@@ -273,7 +324,7 @@ module Bauxite
273
324
  ok = ctx.tests.inject(0) { |s,t| s + (t[:status] == 'OK' ? 1 : 0) }
274
325
  failed = total - ok
275
326
  time = ctx.tests.inject(0) { |s,t| s + t[:time] }
276
- File.open(csv_file, 'w') do |f|
327
+ File.open(ctx.output_path(csv_file), 'w') do |f|
277
328
  f.write "Total,OK,Failed,Time\n#{total},#{ok},#{failed},#{time}\n"
278
329
  end
279
330
  end
@@ -23,6 +23,7 @@
23
23
  require 'selenium-webdriver'
24
24
  require 'readline'
25
25
  require 'csv'
26
+ require 'pathname'
26
27
 
27
28
  # Load dependencies and extensions without leaking dir into the global scope
28
29
  lambda do
@@ -177,7 +178,12 @@ module Bauxite
177
178
  # # and clicks the "Search" button.
178
179
  #
179
180
  def start(actions = [])
180
- _load_driver
181
+ begin
182
+ _load_driver
183
+ rescue StandardError => e
184
+ print_error(e)
185
+ raise
186
+ end
181
187
  return unless actions.size > 0
182
188
  begin
183
189
  actions.each do |action|
@@ -420,7 +426,13 @@ module Bauxite
420
426
  # Constructs a Logger instance using +name+ as a hint for the logger
421
427
  # type.
422
428
  #
423
- def self.load_logger(name, options)
429
+ def self.load_logger(loggers, options)
430
+ if loggers.is_a? Array
431
+ return Loggers::CompositeLogger.new(options, loggers)
432
+ end
433
+
434
+ name = loggers
435
+
424
436
  log_name = (name || 'null').downcase
425
437
 
426
438
  class_name = "#{log_name.capitalize}Logger"
@@ -555,6 +567,29 @@ module Bauxite
555
567
  end
556
568
  end
557
569
 
570
+ # Returns the output path for +path+ accounting for the
571
+ # <tt>__OUTPUT__</tt> variable.
572
+ #
573
+ # For example:
574
+ # # assuming --output /mnt/disk
575
+ #
576
+ # ctx.output_path '/tmp/myfile.txt'
577
+ # # => returns '/tmp/myfile.txt'
578
+ #
579
+ # ctx.output_path 'myfile.txt'
580
+ # # => returns '/mnt/disk/myfile.txt'
581
+ #
582
+ def output_path(path)
583
+ unless Pathname.new(path).absolute?
584
+ output = @variables['__OUTPUT__']
585
+ if output
586
+ Dir.mkdir output unless Dir.exists? output
587
+ path = File.join(output, path)
588
+ end
589
+ end
590
+ path
591
+ end
592
+
558
593
  # ======================================================================= #
559
594
  # :section: Metadata
560
595
  # ======================================================================= #
@@ -24,19 +24,23 @@
24
24
  #
25
25
  # This composite logger forwards logging calls to each of its children.
26
26
  #
27
- # Set the +:loggers+ option to a comma-separated list of logger names
27
+ # Composite logger options include:
28
+ # [<tt>loggers</tt>] A comma-separated list of logger names.
28
29
  #
29
30
  class Bauxite::Loggers::CompositeLogger
30
31
 
31
32
  # Constructs a new composite logger instance.
32
- def initialize(options)
33
- unless options[:loggers]
34
- raise ArgumentError, "Missing required logger option 'loggers'. "+
35
- "The value of this option is a comma-separated list of valid loggers. "+
36
- "For example loggers=xterm,file."
33
+ def initialize(options, loggers = nil)
34
+ unless loggers
35
+ unless options[:loggers]
36
+ raise ArgumentError, "Missing required logger option 'loggers'. "+
37
+ "The value of this option is a comma-separated list of valid loggers. "+
38
+ "For example loggers=xterm,file."
39
+ end
40
+ loggers = options[:loggers].split(',')
37
41
  end
38
42
 
39
- @loggers = options[:loggers].split(',').map do |l|
43
+ @loggers = loggers.map do |l|
40
44
  Bauxite::Context::load_logger(l, options)
41
45
  end
42
46
  end
@@ -25,6 +25,10 @@
25
25
  # This logger outputs the raw action text for every action executed to
26
26
  # the file specified in the +file+ logger option.
27
27
  #
28
+ # File logger options include:
29
+ # [<tt>file</tt>] Output file name.
30
+ #
31
+ #
28
32
  class Bauxite::Loggers::FileLogger < Bauxite::Loggers::NullLogger
29
33
 
30
34
  # Constructs a new echo logger instance.
@@ -34,12 +38,17 @@ class Bauxite::Loggers::FileLogger < Bauxite::Loggers::NullLogger
34
38
  unless @file and @file != ''
35
39
  raise ArgumentError, "FileLogger configuration error: Undefined 'file' option."
36
40
  end
37
- File.open(@file, "w") {} # truncate file
41
+ @lines = []
42
+ end
43
+
44
+ # Completes the log execution.
45
+ def finalize(ctx)
46
+ File.open(ctx.output_path(@file), 'w') { |f| f.write(@lines.join("\n")) }
38
47
  end
39
48
 
40
49
  # Echoes the raw action text.
41
50
  def log_cmd(action)
42
- File.open(@file, 'a+') { |f| f.write(action.text+"\n") }
51
+ @lines << action.text
43
52
  yield
44
53
  end
45
54
  end
@@ -20,12 +20,20 @@
20
20
  # SOFTWARE.
21
21
  #++
22
22
 
23
- # Echo logger.
23
+ require 'base64'
24
+
25
+ # Html logger.
24
26
  #
25
- # This logger outputs the raw action text for every action executed.
27
+ # This logger creates an HTML report of the test execution, linking to the
28
+ # captures taken, if any.
26
29
  #
27
- # Note that this logger does not include execution status information
28
- # (i.e. action succeeded, failed or was skipped).
30
+ # Html logger options include:
31
+ # [<tt>html</tt>] Name of the outpus HTML report file. If not present, defaults
32
+ # to "test.html".
33
+ # [<tt>html_package</tt>] If set, embed captures into the HTML report file
34
+ # using the data URI scheme (base64 encoded). The
35
+ # captures embedded into the report are deleted from
36
+ # the filesystem.
29
37
  #
30
38
  class Bauxite::Loggers::HtmlLogger < Bauxite::Loggers::NullLogger
31
39
 
@@ -35,6 +43,7 @@ class Bauxite::Loggers::HtmlLogger < Bauxite::Loggers::NullLogger
35
43
  super(options)
36
44
  @data = []
37
45
  @file = options[:html] || 'test.html'
46
+ @imgs = []
38
47
  end
39
48
 
40
49
  # Logs the specified string.
@@ -158,12 +167,9 @@ class Bauxite::Loggers::HtmlLogger < Bauxite::Loggers::NullLogger
158
167
  html << "
159
168
  </body>
160
169
  </html>"
161
- file = @file
162
- if output != ''
163
- file = File.join(output, file)
164
- Dir.mkdir output unless Dir.exists? output
165
- end
170
+ file = ctx.output_path(@file)
166
171
  File.open(file, 'w') { |f| f.write html }
172
+ File.delete(*@imgs) if @imgs.size > 0
167
173
  end
168
174
 
169
175
  private
@@ -171,7 +177,13 @@ private
171
177
  "\n"+depth.times.inject('') { |s,i| s + "\t" } + s
172
178
  end
173
179
  def _img(output, path)
174
- path = path[output.size+1..-1] unless output == ''
175
- "<img src='#{path}'/>"
180
+ if @options[:html_package]
181
+ content = Base64.encode64(File.open(path, 'r') { |f| f.read })
182
+ @imgs << path unless @imgs.include? path
183
+ "<img src='data:image/png;base64,#{content}'/>"
184
+ else
185
+ path = path[output.size+1..-1] unless output == ''
186
+ "<img src='#{path}'/>"
187
+ end
176
188
  end
177
189
  end
@@ -27,6 +27,14 @@ require_relative 'terminal'
27
27
  # This logger outputs colorized lines using xterm (VT100/2) escape
28
28
  # sequences.
29
29
  #
30
+ # XTerm logger options include:
31
+ # [<tt>nc</tt>, <tt>color</tt>] If set to 'no', prints text without using
32
+ # colors. Note that positional escape sequences
33
+ # will still be used to show progress. To disable
34
+ # escape sequences completely, consider using the
35
+ # Bauxite::Loggers::TerminalLogger logger
36
+ # instead.
37
+ #
30
38
  class Bauxite::Loggers::XtermLogger < Bauxite::Loggers::TerminalLogger
31
39
 
32
40
  protected
data/lib/bauxite.rb CHANGED
@@ -22,7 +22,7 @@
22
22
 
23
23
  #--
24
24
  module Bauxite
25
- VERSION = "0.6.5"
25
+ VERSION = "0.6.6"
26
26
  end
27
27
  #++
28
28
 
data/test/asserth.bxt CHANGED
@@ -1,2 +1,2 @@
1
1
  open "https://www.ruby-lang.org"
2
- asserth "content-type=html" "content-length=^\d+$"
2
+ asserth "content-type=html" "status=^\d+ .*$"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bauxite
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.5
4
+ version: 0.6.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Patricio Zavolinsky
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-02-06 00:00:00.000000000 Z
11
+ date: 2014-02-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: selenium-webdriver