From 18cb9a6b5487b144efdd362ec2eee5e88ee9e61e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Rasmusson?= Date: Sun, 29 Jun 2014 20:02:55 +0200 Subject: [PATCH 1/4] Regression test for scenario outlines in the JSON formatter --- features/json_formatter.feature | 82 +++++++++++++++++++++ features/step_definitions/cucumber_steps.rb | 6 +- 2 files changed, 86 insertions(+), 2 deletions(-) diff --git a/features/json_formatter.feature b/features/json_formatter.feature index f722dd422f..b4c4ed8bf0 100644 --- a/features/json_formatter.feature +++ b/features/json_formatter.feature @@ -72,6 +72,19 @@ Feature: JSON output formatter And I print from step definition """ + And a file named "features/outline.feature" with: + """ + Feature: An outline feature + + Scenario Outline: outline + Given a step + + Examples: examples + | type | + | passing | + | failing | + + """ # Need to investigate why this won't pass in-process. error_message doesn't get det? @spawn @@ -405,4 +418,73 @@ Feature: JSON output formatter } ] + """ + @spawn + Scenario: scenario outline + When I run `cucumber --format json features/outline.feature` + Then it should fail with JSON: + """ + [ + { + "uri": "features/outline.feature", + "id": "an-outline-feature", + "keyword": "Feature", + "name": "An outline feature", + "line": 1, + "description": "", + "elements": [ + { + "id": "an-outline-feature;outline", + "keyword": "Scenario Outline", + "name": "outline", + "line": 3, + "description": "", + "type": "scenario_outline", + "steps": [ + { + "keyword": "Given ", + "name": "a step", + "line": 4, + "match": { + "location": "features/step_definitions/steps.rb:1" + } + } + ], + "examples": [ + { + "keyword": "Examples", + "name": "examples", + "line": 6, + "description": "", + "id": "an-outline-feature;outline;examples", + "rows": [ + { + "cells": [ + "type" + ], + "line": 7, + "id": "an-outline-feature;outline;examples;1" + }, + { + "cells": [ + "passing" + ], + "line": 8, + "id": "an-outline-feature;outline;examples;2" + }, + { + "cells": [ + "failing" + ], + "line": 9, + "id": "an-outline-feature;outline;examples;3" + } + ] + } + ] + } + ] + } + ] + """ diff --git a/features/step_definitions/cucumber_steps.rb b/features/step_definitions/cucumber_steps.rb index 6941ade74b..22f701d0d7 100644 --- a/features/step_definitions/cucumber_steps.rb +++ b/features/step_definitions/cucumber_steps.rb @@ -17,8 +17,10 @@ actual.each do |feature| feature['elements'].each do |scenario| scenario['steps'].each do |step| - step['result']['duration'].should be >= 0 - step['result']['duration'] = 1 + if step['result'] + step['result']['duration'].should be >= 0 + step['result']['duration'] = 1 + end end end end From 02eb585ea9e12cfe1f3aefc3fe2f879658fd6fe7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Rasmusson?= Date: Sun, 29 Jun 2014 20:08:19 +0200 Subject: [PATCH 2/4] Test for the new outline expand feature for the JSON formatter --- features/json_formatter.feature | 64 +++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/features/json_formatter.feature b/features/json_formatter.feature index b4c4ed8bf0..ae3ac0f222 100644 --- a/features/json_formatter.feature +++ b/features/json_formatter.feature @@ -487,4 +487,68 @@ Feature: JSON output formatter } ] + """ + @spawn + Scenario: scenario outline expanded + When I run `cucumber --expand --format json features/outline.feature` + Then it should fail with JSON: + """ + [ + { + "uri": "features/outline.feature", + "id": "an-outline-feature", + "keyword": "Feature", + "name": "An outline feature", + "line": 1, + "description": "", + "elements": [ + { + "id": "an-outline-feature;outline;examples;2", + "keyword": "Scenario Outline", + "name": "outline", + "line": 8, + "description": "", + "type": "scenario", + "steps": [ + { + "keyword": "Given ", + "name": "a passing step", + "line": 4, + "match": { + "location": "features/step_definitions/steps.rb:1" + }, + "result": { + "status": "passed", + "duration": 1 + } + } + ] + }, + { + "id": "an-outline-feature;outline;examples;3", + "keyword": "Scenario Outline", + "name": "outline", + "line": 9, + "description": "", + "type": "scenario", + "steps": [ + { + "keyword": "Given ", + "name": "a failing step", + "line": 4, + "match": { + "location": "features/step_definitions/steps.rb:5" + }, + "result": { + "status": "failed", + "error_message" : " (RuntimeError)\n./features/step_definitions/steps.rb:6:in `/a failing step/'\nfeatures/outline.feature:4:in `Given a step'", + "duration": 1 + } + } + ] + } + ] + } + ] + """ From 1740b65ebf3eb9fe8f96de4e9b60f2ac478f1e76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Rasmusson?= Date: Sun, 29 Jun 2014 20:17:58 +0200 Subject: [PATCH 3/4] Add new outline expand feature to the JSON formatter --- .../formatter/gherkin_formatter_adapter.rb | 82 +++++++++++++++++-- lib/cucumber/formatter/json.rb | 2 +- 2 files changed, 77 insertions(+), 7 deletions(-) diff --git a/lib/cucumber/formatter/gherkin_formatter_adapter.rb b/lib/cucumber/formatter/gherkin_formatter_adapter.rb index ba116ad294..05f51a9690 100644 --- a/lib/cucumber/formatter/gherkin_formatter_adapter.rb +++ b/lib/cucumber/formatter/gherkin_formatter_adapter.rb @@ -6,9 +6,10 @@ module Formatter # Adapts Cucumber formatter events to Gherkin formatter events # This class will disappear when Cucumber is based on Gherkin's model. class GherkinFormatterAdapter - def initialize(gherkin_formatter, print_empty_match) + def initialize(gherkin_formatter, print_empty_match, options) @gf = gherkin_formatter @print_empty_match = print_empty_match + @options = options end def before_feature(feature) @@ -28,14 +29,42 @@ def before_feature_element(feature_element) @gf.scenario(feature_element.gherkin_statement) when Ast::ScenarioOutline @outline = true - @gf.scenario_outline(feature_element.gherkin_statement) + if @options[:expand] + @current_scenario_hash = to_hash(feature_element.gherkin_statement) + else + @gf.scenario_outline(feature_element.gherkin_statement) + end else raise "Bad type: #{feature_element.class}" end end + def scenario_name(keyword, name, file_colon_line, source_indent) + if @outline and @options[:expand] + @example_row = @example_row ? @example_row + 1 : 0 + if in_instantiated_scenario? + example_row_hash = @current_example_rows[@example_row].to_hash + scenario = Gherkin::Formatter::Model::Scenario.new( + @current_scenario_hash['comments'], + @current_scenario_hash['tags'], + @current_scenario_hash['keyword'], + @current_scenario_hash['name'], + @current_scenario_hash['description'], + example_row_hash['line'], + example_row_hash['id']) + @gf.scenario(scenario) + end + end + end + def before_step(step) - @gf.step(step.gherkin_statement) + unless @outline and @options[:expand] + @gf.step(step.gherkin_statement) + else + if in_instantiated_scenario? + @current_step_hash = to_hash(step.gherkin_statement) + end + end if @print_empty_match if(@outline) match = Gherkin::Formatter::Model::Match.new(step.gherkin_statement.outline_args, nil) @@ -55,23 +84,50 @@ def before_step_result(keyword, step_match, multiline_arg, status, exception, so # Trick the formatter to believe that's what was printed previously so we get arg highlights on #result @gf.instance_variable_set('@match', match) else - @gf.match(match) + unless @outline and @options[:expand] + @gf.match(match) + end end error_message = exception ? "#{exception.message} (#{exception.class})\n#{exception.backtrace.join("\n")}" : nil unless @outline @gf.result(Gherkin::Formatter::Model::Result.new(status, nil, error_message)) + else + if @options[:expand] and in_instantiated_scenario? + @current_match = match + @current_result = Gherkin::Formatter::Model::Result.new(status, nil, error_message) + end + end + end + + def step_name(keyword, step_match, status, source_indent, background, file_colon_line) + if @outline and @options[:expand] and in_instantiated_scenario? + @gf.step(Gherkin::Formatter::Model::Step.new( + @current_step_hash['comments'], + @current_step_hash['keyword'], + step_match.format_args(), + @current_step_hash['line'], + @current_step_hash['rows'], + @current_step_hash['doc_string'])) + @gf.match(@current_match) + @gf.result(@current_result) end end def before_examples(examples) - @gf.examples(examples.gherkin_statement) + unless @options[:expand] + @gf.examples(examples.gherkin_statement) + else + @current_example_rows = to_hash(examples.gherkin_statement)['rows'] + end end #used for capturing duration def after_step(step) step_finish = (Time.now - @step_time) - @gf.append_duration(step_finish) + unless @outline and @options[:expand] and not in_instantiated_scenario? + @gf.append_duration(step_finish) + end end def after_feature(feature) @@ -93,6 +149,20 @@ def embed(file, mime_type, label) def puts(message) @gf.write(message) end + + private + + def in_instantiated_scenario? + @example_row > 0 + end + + def to_hash(gherkin_statement) + if defined?(JRUBY_VERSION) + gherkin_statement.toMap() + else + gherkin_statement.to_hash + end + end end end end diff --git a/lib/cucumber/formatter/json.rb b/lib/cucumber/formatter/json.rb index bdc7124395..a2e720ec4d 100644 --- a/lib/cucumber/formatter/json.rb +++ b/lib/cucumber/formatter/json.rb @@ -11,7 +11,7 @@ class Json < GherkinFormatterAdapter def initialize(runtime, io, options) @io = ensure_io(io, "json") - super(Gherkin::Formatter::JSONFormatter.new(@io), false) + super(Gherkin::Formatter::JSONFormatter.new(@io), false, options) end end end From 3a4f8f69444fe94c0216bfb444ea000cfe4db7e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Rasmusson?= Date: Mon, 30 Jun 2014 16:08:31 +0200 Subject: [PATCH 4/4] Handle more than one Examples table in a Scenario Outline --- features/json_formatter.feature | 65 ++++++++++++++++--- .../formatter/gherkin_formatter_adapter.rb | 43 ++++++------ 2 files changed, 80 insertions(+), 28 deletions(-) diff --git a/features/json_formatter.feature b/features/json_formatter.feature index ae3ac0f222..e9fbb25d77 100644 --- a/features/json_formatter.feature +++ b/features/json_formatter.feature @@ -79,11 +79,15 @@ Feature: JSON output formatter Scenario Outline: outline Given a step - Examples: examples + Examples: examples1 | type | | passing | | failing | + Examples: examples2 + | type | + | passing | + """ # Need to investigate why this won't pass in-process. error_message doesn't get det? @@ -453,31 +457,54 @@ Feature: JSON output formatter "examples": [ { "keyword": "Examples", - "name": "examples", + "name": "examples1", "line": 6, "description": "", - "id": "an-outline-feature;outline;examples", + "id": "an-outline-feature;outline;examples1", "rows": [ { "cells": [ "type" ], "line": 7, - "id": "an-outline-feature;outline;examples;1" + "id": "an-outline-feature;outline;examples1;1" }, { "cells": [ "passing" ], "line": 8, - "id": "an-outline-feature;outline;examples;2" + "id": "an-outline-feature;outline;examples1;2" }, { "cells": [ "failing" ], "line": 9, - "id": "an-outline-feature;outline;examples;3" + "id": "an-outline-feature;outline;examples1;3" + } + ] + }, + { + "keyword": "Examples", + "name": "examples2", + "line": 11, + "description": "", + "id": "an-outline-feature;outline;examples2", + "rows": [ + { + "cells": [ + "type" + ], + "line": 12, + "id": "an-outline-feature;outline;examples2;1" + }, + { + "cells": [ + "passing" + ], + "line": 13, + "id": "an-outline-feature;outline;examples2;2" } ] } @@ -503,7 +530,7 @@ Feature: JSON output formatter "description": "", "elements": [ { - "id": "an-outline-feature;outline;examples;2", + "id": "an-outline-feature;outline;examples1;2", "keyword": "Scenario Outline", "name": "outline", "line": 8, @@ -525,7 +552,7 @@ Feature: JSON output formatter ] }, { - "id": "an-outline-feature;outline;examples;3", + "id": "an-outline-feature;outline;examples1;3", "keyword": "Scenario Outline", "name": "outline", "line": 9, @@ -546,6 +573,28 @@ Feature: JSON output formatter } } ] + }, + { + "id": "an-outline-feature;outline;examples2;2", + "keyword": "Scenario Outline", + "name": "outline", + "line": 13, + "description": "", + "type": "scenario", + "steps": [ + { + "keyword": "Given ", + "name": "a passing step", + "line": 4, + "match": { + "location": "features/step_definitions/steps.rb:1" + }, + "result": { + "status": "passed", + "duration": 1 + } + } + ] } ] } diff --git a/lib/cucumber/formatter/gherkin_formatter_adapter.rb b/lib/cucumber/formatter/gherkin_formatter_adapter.rb index 05f51a9690..15b165c7ee 100644 --- a/lib/cucumber/formatter/gherkin_formatter_adapter.rb +++ b/lib/cucumber/formatter/gherkin_formatter_adapter.rb @@ -30,6 +30,7 @@ def before_feature_element(feature_element) when Ast::ScenarioOutline @outline = true if @options[:expand] + @in_instantiated_scenario = false @current_scenario_hash = to_hash(feature_element.gherkin_statement) else @gf.scenario_outline(feature_element.gherkin_statement) @@ -41,19 +42,23 @@ def before_feature_element(feature_element) def scenario_name(keyword, name, file_colon_line, source_indent) if @outline and @options[:expand] - @example_row = @example_row ? @example_row + 1 : 0 - if in_instantiated_scenario? - example_row_hash = @current_example_rows[@example_row].to_hash - scenario = Gherkin::Formatter::Model::Scenario.new( - @current_scenario_hash['comments'], - @current_scenario_hash['tags'], - @current_scenario_hash['keyword'], - @current_scenario_hash['name'], - @current_scenario_hash['description'], - example_row_hash['line'], - example_row_hash['id']) - @gf.scenario(scenario) + return if not @in_instantiated_scenario + if @new_example_table + @example_row = 1 + @new_example_table = false + else + @example_row += 1 end + example_row_hash = @current_example_rows[@example_row].to_hash + scenario = Gherkin::Formatter::Model::Scenario.new( + @current_scenario_hash['comments'], + @current_scenario_hash['tags'], + @current_scenario_hash['keyword'], + @current_scenario_hash['name'], + @current_scenario_hash['description'], + example_row_hash['line'], + example_row_hash['id']) + @gf.scenario(scenario) end end @@ -61,7 +66,7 @@ def before_step(step) unless @outline and @options[:expand] @gf.step(step.gherkin_statement) else - if in_instantiated_scenario? + if @in_instantiated_scenario @current_step_hash = to_hash(step.gherkin_statement) end end @@ -93,7 +98,7 @@ def before_step_result(keyword, step_match, multiline_arg, status, exception, so unless @outline @gf.result(Gherkin::Formatter::Model::Result.new(status, nil, error_message)) else - if @options[:expand] and in_instantiated_scenario? + if @options[:expand] and @in_instantiated_scenario @current_match = match @current_result = Gherkin::Formatter::Model::Result.new(status, nil, error_message) end @@ -101,7 +106,7 @@ def before_step_result(keyword, step_match, multiline_arg, status, exception, so end def step_name(keyword, step_match, status, source_indent, background, file_colon_line) - if @outline and @options[:expand] and in_instantiated_scenario? + if @outline and @options[:expand] and @in_instantiated_scenario @gf.step(Gherkin::Formatter::Model::Step.new( @current_step_hash['comments'], @current_step_hash['keyword'], @@ -118,6 +123,8 @@ def before_examples(examples) unless @options[:expand] @gf.examples(examples.gherkin_statement) else + @in_instantiated_scenario = true + @new_example_table = true @current_example_rows = to_hash(examples.gherkin_statement)['rows'] end end @@ -125,7 +132,7 @@ def before_examples(examples) #used for capturing duration def after_step(step) step_finish = (Time.now - @step_time) - unless @outline and @options[:expand] and not in_instantiated_scenario? + unless @outline and @options[:expand] and not @in_instantiated_scenario @gf.append_duration(step_finish) end end @@ -152,10 +159,6 @@ def puts(message) private - def in_instantiated_scenario? - @example_row > 0 - end - def to_hash(gherkin_statement) if defined?(JRUBY_VERSION) gherkin_statement.toMap()