diff --git a/features/docs/formatters/json_formatter.feature b/features/docs/formatters/json_formatter.feature index b522a848f7..52f21d4d66 100644 --- a/features/docs/formatters/json_formatter.feature +++ b/features/docs/formatters/json_formatter.feature @@ -83,6 +83,17 @@ Feature: JSON output formatter Given I embed data directly """ + And a file named "features/out_scenario_out_scenario_outline.feature" with: + """ + Feature: + Scenario: + Given this step passes + Scenario Outline: + Given this step + Examples: + | status | + | passes | + """ # Need to investigate why this won't pass in-process. error_message doesn't get det? @spawn @@ -647,3 +658,39 @@ Feature: JSON output formatter ] """ + @spawn + Scenario: handle output from hooks + Given a file named "features/step_definitions/output_steps.rb" with: + """ + Before do + puts "Before hook 1" + embed "src", "mime_type", "label" + end + + Before do + puts "Before hook 2" + embed "src", "mime_type", "label" + end + + AfterStep do + puts "AfterStep hook 1" + embed "src", "mime_type", "label" + end + + AfterStep do + puts "AfterStep hook 2" + embed "src", "mime_type", "label" + end + + After do + puts "After hook 1" + embed "src", "mime_type", "label" + end + + After do + puts "After hook 2" + embed "src", "mime_type", "label" + end + """ + When I run `cucumber --format json features/out_scenario_out_scenario_outline.feature` + Then it should pass diff --git a/features/docs/output_from_hooks.feature b/features/docs/output_from_hooks.feature new file mode 100644 index 0000000000..74a105a439 --- /dev/null +++ b/features/docs/output_from_hooks.feature @@ -0,0 +1,128 @@ +Feature: Hook output feature + + Calls to puts and embed in hook, should be passed to the formatters. + + Background: + Given the standard step definitions + + Scenario: Output from hooks + Given a file named "features/test.feature" with: + """ + Feature: + Scenario: + Given this step passes + Scenario Outline: + Given this step + Examples: + | status | + | passes | + """ + And a file named "features/step_definitions/output_steps.rb" with: + """ + Before do + puts "Before hook 1" + embed "src", "mime_type", "label" + end + + Before do + puts "Before hook 2" + embed "src", "mime_type", "label" + end + + AfterStep do + puts "AfterStep hook 1" + embed "src", "mime_type", "label" + end + + AfterStep do + puts "AfterStep hook 2" + embed "src", "mime_type", "label" + end + + After do + puts "After hook 1" + embed "src", "mime_type", "label" + end + + After do + puts "After hook 2" + embed "src", "mime_type", "label" + end + """ + When I run `cucumber -f debug` + Then the stderr should not contain anything + Then it should pass with: + """ + before_features + before_feature + before_tags + after_tags + feature_name + before_feature_element + before_tags + after_tags + scenario_name + puts + embed + puts + embed + before_steps + before_step + before_step_result + step_name + after_step_result + after_step + puts + embed + puts + embed + after_steps + puts + embed + puts + embed + after_feature_element + before_feature_element + before_tags + after_tags + scenario_name + before_steps + before_step + before_step_result + step_name + after_step_result + after_step + after_steps + before_examples_array + before_examples + examples_name + before_outline_table + before_table_row + before_table_cell + table_cell_value + after_table_cell + after_table_row + puts + embed + puts + embed + before_table_row + before_table_cell + table_cell_value + after_table_cell + after_table_row + puts + embed + puts + embed + puts + embed + puts + embed + after_outline_table + after_examples + after_examples_array + after_feature_element + after_feature + after_features + """ diff --git a/lib/cucumber/formatter/debug.rb b/lib/cucumber/formatter/debug.rb index 0071d71a79..d477eeb342 100644 --- a/lib/cucumber/formatter/debug.rb +++ b/lib/cucumber/formatter/debug.rb @@ -24,6 +24,10 @@ def method_missing(name, *args) @indent += 2 if name.to_s =~ /^before/ end + def puts(*args) + print("puts") + end + private def print(text) diff --git a/lib/cucumber/formatter/gherkin_formatter_adapter.rb b/lib/cucumber/formatter/gherkin_formatter_adapter.rb index 1a7c48769e..99a0bd4819 100644 --- a/lib/cucumber/formatter/gherkin_formatter_adapter.rb +++ b/lib/cucumber/formatter/gherkin_formatter_adapter.rb @@ -10,6 +10,8 @@ def initialize(gherkin_formatter, print_empty_match, options) @gf = gherkin_formatter @print_empty_match = print_empty_match @options = options + @delayed_messages = [] + @delayed_embeddings = [] end def before_feature(feature) @@ -19,10 +21,12 @@ def before_feature(feature) def before_background(background) @outline = false + @before_steps = true @gf.background(background.gherkin_statement) end def before_feature_element(feature_element) + @before_steps = true case(feature_element) when Core::Ast::Scenario @outline = false @@ -65,6 +69,8 @@ def scenario_name(keyword, name, file_colon_line, source_indent) def before_step(step) unless @outline and @options[:expand] @gf.step(step.gherkin_statement) + pass_delayed_output + @before_steps = false else if @in_instantiated_scenario @current_step_hash = to_hash(step.gherkin_statement) @@ -114,6 +120,8 @@ def step_name(keyword, step_match, status, source_indent, background, file_colon @current_step_hash['line'], @current_step_hash['rows'], @current_step_hash['doc_string'])) + pass_delayed_output + @before_steps = false @gf.match(@current_match) @gf.result(@current_result) end @@ -159,11 +167,19 @@ def embed(file, mime_type, label) if defined?(JRUBY_VERSION) data = data.to_java_bytes end - @gf.embedding(mime_type, data) + unless @before_steps + @gf.embedding(mime_type, data) + else + @delayed_embeddings.push [mime_type, data] + end end def puts(message) - @gf.write(message) + unless @before_steps + @gf.write(message) + else + @delayed_messages.push message + end end private @@ -175,6 +191,13 @@ def to_hash(gherkin_statement) gherkin_statement.to_hash end end + + def pass_delayed_output + @delayed_messages.each { |message| @gf.write(message) } + @delayed_embeddings.each { |embed_data| @gf.embedding(embed_data[0], embed_data[1]) } + @delayed_messages = [] + @delayed_embeddings = [] + end end end end diff --git a/lib/cucumber/reports/legacy_formatter.rb b/lib/cucumber/reports/legacy_formatter.rb index 08b99875f2..a5f1e39d9b 100644 --- a/lib/cucumber/reports/legacy_formatter.rb +++ b/lib/cucumber/reports/legacy_formatter.rb @@ -220,29 +220,41 @@ def after_test_step(test_step, result) def after_test_case(*args) if current_test_step_source.step_result.nil? switch_step_container - @delayed_messages = [] - @delayed_embeddings = [] end + + # messages and embedding should already have been handled, but just in case... + @delayed_messages.each { |message| formatter.puts(message) } + @delayed_embeddings.each { |embedding| embedding.send_to_formatter(formatter) } + @delayed_messages = [] + @delayed_embeddings = [] + @child.after_test_case @previous_test_case_background = @current_test_case_background @previous_test_case_scenario_outline = current_test_step_source.scenario_outline end def before_hook(location, result) - @before_hook_results << Legacy::Ast::HookResult.new(LegacyResultBuilder.new(result)) + @before_hook_results << Legacy::Ast::HookResult.new(LegacyResultBuilder.new(result), @delayed_messages, @delayed_embeddings) + @delayed_messages = [] + @delayed_embeddings = [] end def after_hook(location, result) # if the scenario has no steps, we can hit this before we've created the scenario printer # ideally we should call switch_step_container in before_step_step switch_step_container if !@child - @child.after_hook Legacy::Ast::HookResult.new(LegacyResultBuilder.new(result)) + @child.after_hook Legacy::Ast::HookResult.new(LegacyResultBuilder.new(result), @delayed_messages, @delayed_embeddings) + @delayed_messages = [] + @delayed_embeddings = [] end def after_step_hook(hook, result) line = StepBacktraceLine.new(current_test_step_source.step) - @child.after_step_hook LegacyResultBuilder.new(result). - append_to_exception_backtrace(line) + @child.after_step_hook Legacy::Ast::HookResult.new(LegacyResultBuilder.new(result). + append_to_exception_backtrace(line), @delayed_messages, @delayed_embeddings) + @delayed_messages = [] + @delayed_embeddings = [] + end def background(node, *) @@ -417,7 +429,7 @@ def before end def after_step_hook(result) - result.describe_exception_to formatter + result.accept formatter end def step_invocation(step_invocation, source) @@ -485,7 +497,7 @@ def step_invocation(step_invocation, source) end def after_step_hook(result) - result.describe_exception_to formatter + result.accept formatter end def after_test_case(*args) @@ -726,7 +738,8 @@ class TableRowPrinterBase < Struct.new(:formatter, :node, :before_hook_results) include PrintsAfterHooks def after_step_hook(result) - @after_step_hook_result = result + @after_step_hook_result ||= [] + @after_step_hook_result.push result end def after_test_case(*args) @@ -791,7 +804,7 @@ def after formatter.after_table_cell(value) end formatter.after_table_row(legacy_table_row) - @after_step_hook_result.describe_exception_to formatter if @after_step_hook_result + @after_step_hook_result.each { |result| result.accept formatter } if @after_step_hook_result after_hook_results.accept(formatter) @done = true self @@ -826,7 +839,7 @@ def step_invocation(step_invocation, source) def after return if @done @child.after if @child - @after_step_hook_result.describe_exception_to formatter if @after_step_hook_result + @after_step_hook_result.each { |result| result.accept formatter } if @after_step_hook_result after_hook_results.accept(formatter) @done = true self @@ -1057,13 +1070,15 @@ def accept(formatter) end class HookResult - def initialize(result) - @result = result + def initialize(result, messages, embeddings) + @result, @messages, @embeddings = result, messages, embeddings @already_accepted = false end def accept(formatter) unless @already_accepted + @messages.each { |message| formatter.puts(message) } + @embeddings.each { |embedding| embedding.send_to_formatter(formatter) } @result.describe_exception_to(formatter) @already_accepted = true end