Class: Cucumber::Formatter::SlimJSON
- Inherits:
-
Object
- Object
- Cucumber::Formatter::SlimJSON
- Includes:
- Console, FileUtils, Io
- Defined in:
- lib/cukable/slim_json_formatter.rb
Overview
FitNesse SliM JSON output formatter for Cucumber
Instance Method Summary collapse
-
#after_background(background) ⇒ Object
Called after a
Background:block. -
#after_examples(examples) ⇒ Object
Called after an
Examples:section. -
#after_feature(feature) ⇒ Object
Called after each
.featureis run. -
#after_step_result(keyword, step_match, multiline_arg, status, exception, source_indent, background) ⇒ Object
Called after a step has executed, and we have a result.
-
#after_table_row(table_row) ⇒ Object
Called after a table row is done being read.
-
#background_name(keyword, name, file_colon_line, source_indent) ⇒ Object
Called when a
Background:line is read. -
#backtrace(exception) ⇒ Object
Return an exception message and backtrace.
-
#before_background(background) ⇒ Object
Called before a
Background:block. -
#before_examples(examples) ⇒ Object
Called before an
Examples:section in a Scenario Outline. -
#before_feature(feature) ⇒ Object
Called before each
.featureis run. -
#before_multiline_arg(multiline_arg) ⇒ Object
Start a new multiline arg (such as a table or Py-string).
-
#before_step(step) ⇒ Object
Called after a step has been executed, but before any output from that step has been done.
-
#before_step_result(keyword, step_match, multiline_arg, status, exception, source_indent, background) ⇒ Object
Called before any output from a step result.
-
#before_table_row(table_row) ⇒ Object
Called before a table row is read.
-
#examples_name(keyword, name) ⇒ Object
Called when the
Examples:line is read. -
#feature_name(keyword, name) ⇒ Object
Called when
Feature: <name>is read. -
#initialize(step_mother, path_or_io, options) ⇒ SlimJSON
constructor
Create a new SlimJSON formatter, with the provided
path_or_io(as given by the--outoption) and any additional options passed to cucumber. -
#py_string(string) ⇒ Object
Called when a multi-line string argument is read.
-
#sanitize(text) ⇒ Object
Return
textwith any HTML-specific characters sanitized. -
#scenario_name(keyword, name, file_colon_line, source_indent) ⇒ Object
Called when
Scenario: <name>is read. -
#section_message(keyword, name, file_colon_line = '') ⇒ Object
Return a string suitable for use as a section heading for "Feature:", "Scenario:" or "Scenario Outline:" output rows.
-
#source_message(file_colon_line) ⇒ Object
Return a string for outputting the source filename and line number.
-
#status_map(status) ⇒ Object
Map Cucumber status strings to FitNesse status strings.
-
#table_cell_value(value, status) ⇒ Object
Called when a table cell value is read.
-
#tag_name(tag_name) ⇒ Object
Called when a tag name is found.
Constructor Details
#initialize(step_mother, path_or_io, options) ⇒ SlimJSON
Create a new SlimJSON formatter, with the provided path_or_io (as
given by the --out option) and any additional options passed to
cucumber.
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
# File 'lib/cukable/slim_json_formatter.rb', line 20
def initialize(step_mother, path_or_io, options)
@step_mother = step_mother
# Output directory
@out_dir = path_or_io
ensure_dir(@out_dir, "FitNesse")
# There should be no IO until we get a feature, and
# create the output directory in before_feature
@io = nil
# Cache of data lines to write
@data = []
# Multi-line output (must be cached and printed after the step that
# precedes it)
@multiline = []
# Expected/actual, to support table diffs
@expected_row = []
@actual_row = []
# Not in background until we actually find one
@in_background = false
end
|
Instance Method Details
#after_background(background) ⇒ Object
Called after a Background: block.
93 94 95 |
# File 'lib/cukable/slim_json_formatter.rb', line 93
def after_background(background)
@in_background = false
end
|
#after_examples(examples) ⇒ Object
Called after an Examples: section. Outputs anything accumulated in
@multiline, and empties it.
202 203 204 205 206 207 208 |
# File 'lib/cukable/slim_json_formatter.rb', line 202
def after_examples(examples)
# Output any multiline args that followed this step
@multiline.each do |row|
@data << row
end
@multiline = []
end
|
#after_feature(feature) ⇒ Object
Called after each .feature is run. Write all @data to the JSON
file, then closes the output file.
58 59 60 61 62 |
# File 'lib/cukable/slim_json_formatter.rb', line 58
def after_feature(feature)
@io.puts JSON.pretty_generate(@data)
@io.flush
@io.close
end
|
#after_step_result(keyword, step_match, multiline_arg, status, exception, source_indent, background) ⇒ Object
Called after a step has executed, and we have a result. Generates a
single row of output in @data, including the status of the completed
step, along with one row for each line accumulated in a multi-line
argument (@multiline) if any were provided. Resets @multiline when
done.
267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 |
# File 'lib/cukable/slim_json_formatter.rb', line 267
def after_step_result(keyword, step_match, multiline_arg, status,
exception, source_indent, background)
return if @hide_this_step
# One-line message to print
message = ''
# A bit of a hack here to support Scenario Outlines
# Apparently, at the time of calling after_step_result, a StepMatch in
# a Scenario Outline has a `name` attribute (and no arguments to
# format, because they don't get pattern-matched until the Examples:
# section that fills them in), whereas a StepMatch in a normal scenario
# has no value set for its `name` attribute, and *does* have arguments
# to format. This behavior is exploited here for the sake of replacing
# the `<...>` angle brackets that appear in Scenario Outline steps.
#
# In other words, if `step_match` has a `name`, assume it's in a
# Scenario Outline, and replace the angle brackets (so the bracketed
# parameter can be displayed in an HTML page)
if step_match.name
step_name = keyword + step_match.name.gsub('<', '<').gsub('>', '>')
# Otherwise, wrap the arguments in bold tags
else
step_name = keyword + step_match.format_args("<b>%s</b>")
end
# Output the step name with appropriate colorization
stat = status_map(status)
message = "#{stat}:#{step_name}"
# Add the source file and line number where this step was defined
message += source_message(step_match.file_colon_line)
# Include additional info for undefined and failed steps
if status == :undefined
message += "<br/>(Undefined Step)"
elsif status == :failed && exception
message += backtrace(exception)
end
# Output the final message for this step
@data << [message]
# Output any multiline args that followed this step
@multiline.each do |row|
@data << row
end
@multiline = []
end
|
#after_table_row(table_row) ⇒ Object
Called after a table row is done being read. Appends @table_row
to @multiline, which will be output in after_step_result.
There is some special handling here for handling table diffs; when doing a table diff, and a row doesn't match, two rows are generated. These need to be merged into a single row in the JSON output, to maintain the 1:1 mapping between FitNesse table and the returned results.
129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 |
# File 'lib/cukable/slim_json_formatter.rb', line 129
def after_table_row(table_row)
return if @hide_this_step
# If we have an @expected_row and @actual_row at this point,
# merge them into a single row and append to @multiline_arg
if !@expected_row.empty? && !@actual_row.empty?
cell_diff = []
@expected_row.zip(@actual_row) do |expect, actual|
expect.gsub!(/^ignore:/, '')
actual.gsub!(/^error:/, '')
# If we got what we wanted in this cell, consider it passed
if actual == expect
cell_diff << "pass:#{actual}"
# Otherwise, show expected vs. actual as a failure
else
cell_diff << "fail:Expected: '#{expect}'<br/>Actual: '#{actual}'"
end
end
@multiline << ["report: "] + cell_diff
# Reset for upcoming rows
@expected_row = []
@actual_row = []
end
# Row with all cells having status == :comment (ignore)?
# This row was part of a table diff, and contains the values
# that were expected to be in the row.
if @table_row.all? { |cell| cell =~ /^ignore:/ }
@expected_row = @table_row
# Row with all cells having status == :undefined (error)?
# This row was part of a table diff, and contains the values
# that actually appeared in the row.
elsif @table_row.all? { |cell| cell =~ /^error:/ }
@actual_row = @table_row
# For any other row, append to multiline normally
else
# If an exception occurred in a table row, put the exception
# message in the first cell (which is normally empty). This
# allows us to show the exception without adding extra rows
# (which messes up the original table's formatting)
if table_row.exception
@multiline << ["fail:#{backtrace(table_row.exception)}"] + @table_row
# Otherwise, output an empty report: cell in the first column
else
@multiline << ["report: "] + @table_row
end
end
end
|
#background_name(keyword, name, file_colon_line, source_indent) ⇒ Object
Called when a Background: line is read. Generates a single row of
output in @data with the Background: line.
87 88 89 |
# File 'lib/cukable/slim_json_formatter.rb', line 87
def background_name(keyword, name, file_colon_line, source_indent)
@data << [section_message(keyword, name, file_colon_line)]
end
|
#backtrace(exception) ⇒ Object
Return an exception message and backtrace
355 356 357 358 359 360 361 |
# File 'lib/cukable/slim_json_formatter.rb', line 355
def backtrace(exception)
message = "<br/>" + sanitize(exception.message) + "<br/>"
message += exception.backtrace.collect { |line|
sanitize(line)
}.join("<br/>")
return message
end
|
#before_background(background) ⇒ Object
Called before a Background: block.
80 81 82 |
# File 'lib/cukable/slim_json_formatter.rb', line 80
def before_background(background)
@in_background = true
end
|
#before_examples(examples) ⇒ Object
Called before an Examples: section in a Scenario Outline. An
Examples: table works similarly to a multiline argument, except there
is no associated step to output them. The after_table_row method
will still accumulate the table rows, but we need to rely on
after_examples to output them. Thus, we will be accumulating these
rows in the multi-purpose @multiline variable, initialized here.
188 189 190 |
# File 'lib/cukable/slim_json_formatter.rb', line 188
def before_examples(examples)
@multiline = []
end
|
#before_feature(feature) ⇒ Object
Called before each .feature is run. Creates a new output file for the
results in @out_dir, and empties @data.
47 48 49 50 51 52 53 |
# File 'lib/cukable/slim_json_formatter.rb', line 47
def before_feature(feature)
file = File.join(@out_dir, "#{feature.file}.json")
dir = File.dirname(file)
mkdir_p(dir) unless File.directory?(dir)
@io = ensure_file(file, "FitNesse")
@data = []
end
|
#before_multiline_arg(multiline_arg) ⇒ Object
Start a new multiline arg (such as a table or Py-string). Initializes
@multiline and related arrays.
100 101 102 103 104 |
# File 'lib/cukable/slim_json_formatter.rb', line 100
def before_multiline_arg(multiline_arg)
@multiline = []
@expected_row = []
@actual_row = []
end
|
#before_step(step) ⇒ Object
Called after a step has been executed, but before any output from that step has been done.
213 214 215 |
# File 'lib/cukable/slim_json_formatter.rb', line 213
def before_step(step)
@current_step = step
end
|
#before_step_result(keyword, step_match, multiline_arg, status, exception, source_indent, background) ⇒ Object
Called before any output from a step result. To avoid redundant output,
we want to show the results of Background steps only once, within the
Background section (unless a background step somehow failed when it
was executed at the top of a Scenario). Here, background is true if
the step was defined in the Background section, and @in_background
is true if we are actually inside the Background section during
execution. In short, if a step was defined in the Background section,
but we are not within the Background section now, we want to hide
the step's output.
252 253 254 255 256 257 258 259 |
# File 'lib/cukable/slim_json_formatter.rb', line 252
def before_step_result(keyword, step_match, multiline_arg, status,
exception, source_indent, background)
if status != :failed && @in_background ^ background
@hide_this_step = true
else
@hide_this_step = false
end
end
|
#before_table_row(table_row) ⇒ Object
Called before a table row is read. Starts a new @table_row.
108 109 110 |
# File 'lib/cukable/slim_json_formatter.rb', line 108
def before_table_row(table_row)
@table_row = []
end
|
#examples_name(keyword, name) ⇒ Object
Called when the Examples: line is read. Outputs the Examples: line
to @data.
195 196 197 |
# File 'lib/cukable/slim_json_formatter.rb', line 195
def examples_name(keyword, name)
@data << ["report:#{keyword}: #{name}"]
end
|
#feature_name(keyword, name) ⇒ Object
Called when Feature: <name> is read. Generates a single row of output
in @data with the feature name.
67 68 69 |
# File 'lib/cukable/slim_json_formatter.rb', line 67
def feature_name(keyword, name)
@data << [section_message(keyword, name)]
end
|
#py_string(string) ⇒ Object
Called when a multi-line string argument is read. Generates a row of
output for each line in the multi-line string (including the """
opening and closing lines), colored based on the status of the current
step. The output is accumulated in @multiline, for output in
after_step_result.
223 224 225 226 227 228 229 230 231 |
# File 'lib/cukable/slim_json_formatter.rb', line 223
def py_string(string)
return if @hide_this_step
status = status_map(@current_step.status)
@multiline << [status + ':"""']
string.split("\n").each do |line|
@multiline << ["#{status}:#{line}"]
end
@multiline << [status + ':"""']
end
|
#sanitize(text) ⇒ Object
Return text with any HTML-specific characters sanitized
336 337 338 |
# File 'lib/cukable/slim_json_formatter.rb', line 336
def sanitize(text)
text.gsub('<', '<').gsub('>', '>')
end
|
#scenario_name(keyword, name, file_colon_line, source_indent) ⇒ Object
Called when Scenario: <name> is read. Generates a single row of
output in @data with the scenario name.
74 75 76 |
# File 'lib/cukable/slim_json_formatter.rb', line 74
def scenario_name(keyword, name, file_colon_line, source_indent)
@data << [section_message(keyword, name, file_colon_line)]
end
|
#section_message(keyword, name, file_colon_line = '') ⇒ Object
Return a string suitable for use as a section heading for "Feature:", "Scenario:" or "Scenario Outline:" output rows.
349 350 351 |
# File 'lib/cukable/slim_json_formatter.rb', line 349
def section_message(keyword, name, file_colon_line='')
"report:#{keyword}: #{name}" + source_message(file_colon_line)
end
|
#source_message(file_colon_line) ⇒ Object
Return a string for outputting the source filename and line number
342 343 344 |
# File 'lib/cukable/slim_json_formatter.rb', line 342
def source_message(file_colon_line)
return " <span class=\"source_file\">" + file_colon_line + '</span>'
end
|
#status_map(status) ⇒ Object
Map Cucumber status strings to FitNesse status strings
322 323 324 325 326 327 328 329 330 331 332 |
# File 'lib/cukable/slim_json_formatter.rb', line 322
def status_map(status)
case status
when nil then 'pass'
when :passed then 'pass'
when :failed then 'fail'
when :undefined then 'error'
when :skipped then 'ignore'
when :comment then 'ignore'
else 'pass'
end
end
|
#table_cell_value(value, status) ⇒ Object
Called when a table cell value is read. Appends to @table_row.
114 115 116 117 118 |
# File 'lib/cukable/slim_json_formatter.rb', line 114
def table_cell_value(value, status)
return if @hide_this_step
stat = status_map(status)
@table_row << "#{stat}:#{value}"
end
|
#tag_name(tag_name) ⇒ Object
Called when a tag name is found. Generates a single row of output in
@data with the tag name. (Note that this will only work properly if
there is only one tag per line; otherwise, too many lines may be
output.)
238 239 240 |
# File 'lib/cukable/slim_json_formatter.rb', line 238
def tag_name(tag_name)
@data << ["ignore:#{tag_name}"]
end
|