From d7a385978447a0bb633ccaeb3e652a0ddbe1b744 Mon Sep 17 00:00:00 2001 From: Hermann Mayer Date: Thu, 6 Jun 2019 21:09:32 +0200 Subject: [PATCH] Added support for multiple tag lines for features and scenarios. Signed-off-by: Hermann Mayer --- lib/gherkin_ruby/parser/gherkin.y | 13 +- lib/gherkin_ruby/parser/parser.rb | 243 ++++++++++++++++------------- test/gherkin/parser/parser_test.rb | 41 +++++ test/gherkin/parser_test.rb | 14 +- 4 files changed, 195 insertions(+), 116 deletions(-) diff --git a/lib/gherkin_ruby/parser/gherkin.y b/lib/gherkin_ruby/parser/gherkin.y index 3657789..ee7a473 100644 --- a/lib/gherkin_ruby/parser/gherkin.y +++ b/lib/gherkin_ruby/parser/gherkin.y @@ -17,7 +17,7 @@ rule Feature Scenarios { result = val[0]; result.scenarios = val[1] } | FeatureTags Feature { result = val[1]; result.tags = val[0] } - | FeatureTags Feature + | FeatureTags Feature Scenarios { result = val[1]; result.scenarios = val[2]; result.tags = val[0] } ; @@ -29,6 +29,8 @@ rule FeatureTags: Tags { result = val[0] } | Newline Tags { result = val[1] } + | FeatureTags Newline Tags { result += val[2] } + ; Feature: FeatureHeader { result = val[0] } @@ -84,9 +86,14 @@ rule Scenario: SCENARIO TEXT Newline Steps { result = AST::Scenario.new(val[1], val[3]); result.pos(filename, lineno - 1) } - | Tags Newline + | ScenarioTags SCENARIO TEXT Newline - Steps { result = AST::Scenario.new(val[3], val[5], val[0]); result.pos(filename, lineno - 2) } + Steps { result = AST::Scenario.new(val[2], val[4], val[0]); result.pos(filename, lineno - 2) } + ; + + ScenarioTags: + Tags Newline { result = val[0] } + | Tags Newline ScenarioTags { result += val[2] } ; Tags: diff --git a/lib/gherkin_ruby/parser/parser.rb b/lib/gherkin_ruby/parser/parser.rb index 515c289..642fced 100644 --- a/lib/gherkin_ruby/parser/parser.rb +++ b/lib/gherkin_ruby/parser/parser.rb @@ -1,6 +1,6 @@ # # DO NOT MODIFY!!!! -# This file is automatically generated by Racc 1.4.11 +# This file is automatically generated by Racc 1.4.15 # from Racc grammer file "". # @@ -12,7 +12,7 @@ module GherkinRuby class Parser < Racc::Parser -module_eval(<<'...end gherkin.y/module_eval...', 'gherkin.y', 104) +module_eval(<<'...end gherkin.y/module_eval...', 'gherkin.y', 111) def parse(input) @yydebug = true if ENV['DEBUG_RACC'] @@ -22,62 +22,64 @@ def parse(input) ##### State transition tables begin ### racc_action_table = [ - 18, 18, 20, 4, 26, 10, 18, 21, 14, 10, - 43, 36, 37, 38, 39, 40, 18, 18, 14, 10, - 45, 36, 37, 38, 39, 40, 18, 14, 10, 4, - 9, 36, 37, 38, 39, 40, 36, 37, 38, 39, - 40, 4, 9, 14, 10, 10, 18, 20, 32, 24, - 21, 4, 4, 29, 21, 4, 47, 18, 48, 4, - 27, 51, 11, 4, 18, 4, 18, 4 ] + 19, 4, 9, 19, 21, 10, 19, 10, 14, 10, + 46, 39, 40, 41, 42, 43, 19, 4, 9, 14, + 10, 39, 40, 41, 42, 43, 19, 14, 10, 14, + 10, 39, 40, 41, 42, 43, 4, 19, 21, 11, + 22, 10, 39, 40, 41, 42, 43, 19, 22, 25, + 4, 10, 27, 28, 30, 31, 22, 35, 4, 4, + 48, 22, 4, 51, 19, 52, 4, 4, 4, 19, + 19 ] racc_action_check = [ - 25, 5, 5, 15, 9, 5, 54, 15, 31, 31, - 25, 54, 54, 54, 54, 54, 44, 30, 12, 12, - 30, 44, 44, 44, 44, 44, 46, 2, 2, 3, - 3, 46, 46, 46, 46, 46, 23, 23, 23, 23, - 23, 0, 0, 16, 16, 0, 17, 17, 20, 7, - 6, 8, 29, 14, 19, 34, 35, 41, 42, 43, - 11, 45, 1, 48, 49, 51, 53, 24 ] + 26, 0, 0, 5, 5, 0, 47, 5, 2, 2, + 26, 47, 47, 47, 47, 47, 50, 3, 3, 12, + 12, 50, 50, 50, 50, 50, 55, 17, 17, 33, + 33, 55, 55, 55, 55, 55, 16, 18, 18, 1, + 16, 18, 24, 24, 24, 24, 24, 32, 6, 7, + 8, 32, 9, 11, 14, 15, 20, 21, 25, 30, + 31, 34, 37, 38, 44, 45, 46, 48, 52, 53, + 57 ] racc_action_pointer = [ - 39, 62, 22, 27, nil, -1, 44, 45, 49, -8, - nil, 60, 13, nil, 41, 1, 38, 44, nil, 48, - 36, nil, nil, 29, 65, -2, nil, nil, nil, 50, - 15, 3, nil, nil, 53, 44, nil, nil, nil, nil, - nil, 55, 46, 57, 14, 49, 24, nil, 61, 62, - nil, 63, nil, 64, 4, nil ] + -1, 39, 3, 15, nil, 1, 42, 45, 48, 40, + nil, 53, 14, nil, 42, 50, 34, 22, 35, nil, + 50, 45, nil, nil, 35, 56, -2, nil, nil, nil, + 57, 48, 45, 24, 55, nil, nil, 60, 51, nil, + nil, nil, nil, nil, 62, 53, 64, 4, 65, nil, + 14, nil, 66, 67, nil, 24, nil, 68, nil ] racc_action_default = [ - -35, -35, -1, -35, -5, -35, -7, -9, -11, -35, - -33, -35, -2, -29, -35, -35, -3, -35, -6, -8, - -35, -34, -10, -35, -35, -12, -14, 56, -30, -35, - -35, -4, -15, -18, -20, -35, -24, -25, -26, -27, - -28, -19, -13, -35, -35, -35, -21, -23, -35, -16, - -31, -35, -22, -17, -35, -32 ] + -38, -38, -1, -38, -5, -38, -7, -10, -12, -38, + -36, -38, -2, -30, -38, -38, -38, -3, -38, -6, + -8, -38, -37, -11, -38, -38, -13, -15, 59, -31, + -38, -38, -34, -4, -9, -16, -19, -21, -38, -25, + -26, -27, -28, -29, -20, -14, -38, -38, -38, -35, + -22, -24, -38, -17, -32, -38, -23, -18, -33 ] racc_goto_table = [ - 5, 28, 33, 17, 12, 6, 2, 23, 25, 16, - 19, 42, 22, 3, 1, 30, nil, nil, 31, nil, - 28, nil, nil, 50, 41, 52, nil, nil, nil, 44, - nil, nil, nil, 55, 46, nil, nil, nil, nil, nil, - nil, nil, nil, 49, nil, nil, nil, nil, 53, nil, - nil, 54 ] + 5, 36, 29, 18, 6, 12, 1, 2, 26, 20, + 17, 3, 23, 45, 24, 49, 32, nil, nil, nil, + 33, nil, 34, 29, 54, 44, nil, 56, nil, nil, + 47, nil, 58, nil, nil, nil, nil, 50, nil, nil, + nil, nil, nil, nil, nil, nil, 53, nil, 55, nil, + nil, nil, 57 ] racc_goto_check = [ - 5, 15, 12, 5, 3, 6, 2, 11, 5, 2, - 6, 10, 8, 4, 1, 5, nil, nil, 3, nil, - 15, nil, nil, 12, 5, 12, nil, nil, nil, 5, - nil, nil, nil, 12, 5, nil, nil, nil, nil, nil, - nil, nil, nil, 5, nil, nil, nil, nil, 5, nil, - nil, 5 ] + 5, 12, 15, 5, 6, 3, 1, 2, 5, 6, + 2, 4, 8, 10, 11, 16, 5, nil, nil, nil, + 3, nil, 6, 15, 12, 5, nil, 12, nil, nil, + 5, nil, 12, nil, nil, nil, nil, 5, nil, nil, + nil, nil, nil, nil, nil, nil, 5, nil, 5, nil, + nil, nil, 5 ] racc_goto_pointer = [ - nil, 14, 6, 2, 13, 0, 5, nil, 5, nil, - -14, 0, -21, nil, nil, -11 ] + nil, 6, 7, 3, 11, 0, 4, nil, 5, nil, + -13, 7, -23, nil, nil, -10, -17 ] racc_goto_default = [ - nil, nil, nil, nil, nil, nil, 15, 7, nil, 8, - nil, nil, nil, 34, 35, 13 ] + nil, nil, nil, nil, nil, nil, 16, 7, nil, 8, + nil, nil, nil, 37, 38, 13, 15 ] racc_reduce_table = [ 0, 0, :racc_error, @@ -89,36 +91,39 @@ def parse(input) 2, 18, :_reduce_none, 1, 17, :_reduce_7, 2, 17, :_reduce_8, - 1, 15, :_reduce_9, - 2, 15, :_reduce_10, - 1, 20, :_reduce_11, - 2, 20, :_reduce_12, - 3, 20, :_reduce_13, - 2, 22, :_reduce_14, - 3, 22, :_reduce_15, - 2, 23, :_reduce_16, - 3, 23, :_reduce_17, - 2, 21, :_reduce_18, - 2, 24, :_reduce_19, - 1, 25, :_reduce_20, - 2, 25, :_reduce_21, - 3, 25, :_reduce_22, - 2, 26, :_reduce_23, + 3, 17, :_reduce_9, + 1, 15, :_reduce_10, + 2, 15, :_reduce_11, + 1, 20, :_reduce_12, + 2, 20, :_reduce_13, + 3, 20, :_reduce_14, + 2, 22, :_reduce_15, + 3, 22, :_reduce_16, + 2, 23, :_reduce_17, + 3, 23, :_reduce_18, + 2, 21, :_reduce_19, + 2, 24, :_reduce_20, + 1, 25, :_reduce_21, + 2, 25, :_reduce_22, + 3, 25, :_reduce_23, + 2, 26, :_reduce_24, 1, 27, :_reduce_none, 1, 27, :_reduce_none, 1, 27, :_reduce_none, 1, 27, :_reduce_none, 1, 27, :_reduce_none, - 1, 16, :_reduce_29, - 2, 16, :_reduce_30, - 4, 28, :_reduce_31, - 6, 28, :_reduce_32, - 1, 19, :_reduce_33, - 2, 19, :_reduce_34 ] + 1, 16, :_reduce_30, + 2, 16, :_reduce_31, + 4, 28, :_reduce_32, + 5, 28, :_reduce_33, + 2, 29, :_reduce_34, + 3, 29, :_reduce_35, + 1, 19, :_reduce_36, + 2, 19, :_reduce_37 ] -racc_reduce_n = 35 +racc_reduce_n = 38 -racc_shift_n = 56 +racc_shift_n = 59 racc_token_table = { false => 0, @@ -184,7 +189,8 @@ def parse(input) "Steps", "Step", "Keyword", - "Scenario" ] + "Scenario", + "ScenarioTags" ] Racc_debug_parser = false @@ -238,28 +244,28 @@ def _reduce_8(val, _values, result) end .,., -module_eval(<<'.,.,', 'gherkin.y', 33) +module_eval(<<'.,.,', 'gherkin.y', 31) def _reduce_9(val, _values, result) - result = val[0] + result += val[2] result end .,., module_eval(<<'.,.,', 'gherkin.y', 35) def _reduce_10(val, _values, result) - result = val[0]; result.background = val[1] + result = val[0] result end .,., -module_eval(<<'.,.,', 'gherkin.y', 39) +module_eval(<<'.,.,', 'gherkin.y', 37) def _reduce_11(val, _values, result) - result = val[0] + result = val[0]; result.background = val[1] result end .,., -module_eval(<<'.,.,', 'gherkin.y', 40) +module_eval(<<'.,.,', 'gherkin.y', 41) def _reduce_12(val, _values, result) result = val[0] result @@ -268,82 +274,87 @@ def _reduce_12(val, _values, result) module_eval(<<'.,.,', 'gherkin.y', 42) def _reduce_13(val, _values, result) - result = val[0]; result.description = val[2] + result = val[0] result end .,., -module_eval(<<'.,.,', 'gherkin.y', 46) +module_eval(<<'.,.,', 'gherkin.y', 44) def _reduce_14(val, _values, result) - result = AST::Feature.new(val[1]); result.pos(filename, lineno) + result = val[0]; result.description = val[2] result end .,., -module_eval(<<'.,.,', 'gherkin.y', 47) +module_eval(<<'.,.,', 'gherkin.y', 48) def _reduce_15(val, _values, result) - result = AST::Feature.new(val[2]); result.pos(filename, lineno) + result = AST::Feature.new(val[1]); result.pos(filename, lineno) result end .,., -module_eval(<<'.,.,', 'gherkin.y', 51) +module_eval(<<'.,.,', 'gherkin.y', 49) def _reduce_16(val, _values, result) - result = val[0] + result = AST::Feature.new(val[2]); result.pos(filename, lineno) result end .,., -module_eval(<<'.,.,', 'gherkin.y', 52) +module_eval(<<'.,.,', 'gherkin.y', 53) def _reduce_17(val, _values, result) - result = val[0...-1].flatten + result = val[0] result end .,., -module_eval(<<'.,.,', 'gherkin.y', 57) +module_eval(<<'.,.,', 'gherkin.y', 54) def _reduce_18(val, _values, result) - result = val[0]; result.steps = val[1] + result = val[0...-1].flatten result end .,., -module_eval(<<'.,.,', 'gherkin.y', 61) +module_eval(<<'.,.,', 'gherkin.y', 59) def _reduce_19(val, _values, result) - result = AST::Background.new; result.pos(filename, lineno) + result = val[0]; result.steps = val[1] result end .,., -module_eval(<<'.,.,', 'gherkin.y', 65) +module_eval(<<'.,.,', 'gherkin.y', 63) def _reduce_20(val, _values, result) - result = [val[0]] + result = AST::Background.new; result.pos(filename, lineno) result end .,., -module_eval(<<'.,.,', 'gherkin.y', 66) +module_eval(<<'.,.,', 'gherkin.y', 67) def _reduce_21(val, _values, result) result = [val[0]] result end .,., -module_eval(<<'.,.,', 'gherkin.y', 67) +module_eval(<<'.,.,', 'gherkin.y', 68) def _reduce_22(val, _values, result) - val[2].unshift(val[0]); result = val[2] + result = [val[0]] result end .,., -module_eval(<<'.,.,', 'gherkin.y', 71) +module_eval(<<'.,.,', 'gherkin.y', 69) def _reduce_23(val, _values, result) - result = AST::Step.new(val[1], val[0]); result.pos(filename, lineno) + val[2].unshift(val[0]); result = val[2] result end .,., -# reduce 24 omitted +module_eval(<<'.,.,', 'gherkin.y', 73) + def _reduce_24(val, _values, result) + result = AST::Step.new(val[1], val[0]); result.pos(filename, lineno) + result + end +.,., # reduce 25 omitted @@ -353,43 +364,59 @@ def _reduce_23(val, _values, result) # reduce 28 omitted -module_eval(<<'.,.,', 'gherkin.y', 79) - def _reduce_29(val, _values, result) +# reduce 29 omitted + +module_eval(<<'.,.,', 'gherkin.y', 81) + def _reduce_30(val, _values, result) result = [val[0]] result end .,., -module_eval(<<'.,.,', 'gherkin.y', 80) - def _reduce_30(val, _values, result) +module_eval(<<'.,.,', 'gherkin.y', 82) + def _reduce_31(val, _values, result) result = val[0] << val[1] result end .,., -module_eval(<<'.,.,', 'gherkin.y', 85) - def _reduce_31(val, _values, result) +module_eval(<<'.,.,', 'gherkin.y', 87) + def _reduce_32(val, _values, result) result = AST::Scenario.new(val[1], val[3]); result.pos(filename, lineno - 1) result end .,., -module_eval(<<'.,.,', 'gherkin.y', 88) - def _reduce_32(val, _values, result) - result = AST::Scenario.new(val[3], val[5], val[0]); result.pos(filename, lineno - 2) +module_eval(<<'.,.,', 'gherkin.y', 90) + def _reduce_33(val, _values, result) + result = AST::Scenario.new(val[2], val[4], val[0]); result.pos(filename, lineno - 2) result end .,., -module_eval(<<'.,.,', 'gherkin.y', 92) - def _reduce_33(val, _values, result) +module_eval(<<'.,.,', 'gherkin.y', 94) + def _reduce_34(val, _values, result) + result = val[0] + result + end +.,., + +module_eval(<<'.,.,', 'gherkin.y', 95) + def _reduce_35(val, _values, result) + result += val[2] + result + end +.,., + +module_eval(<<'.,.,', 'gherkin.y', 99) + def _reduce_36(val, _values, result) result = [AST::Tag.new(val[0])] result end .,., -module_eval(<<'.,.,', 'gherkin.y', 93) - def _reduce_34(val, _values, result) +module_eval(<<'.,.,', 'gherkin.y', 100) + def _reduce_37(val, _values, result) result = val[0] << AST::Tag.new(val[1]) result end diff --git a/test/gherkin/parser/parser_test.rb b/test/gherkin/parser/parser_test.rb index 6f3d1eb..13857cc 100644 --- a/test/gherkin/parser/parser_test.rb +++ b/test/gherkin/parser/parser_test.rb @@ -34,6 +34,20 @@ def parse(input) feature.tags.last.name.must_equal "with-dash" end + it 'parses feature with tags on multiple lines' do + feature = parse(""" +@wip +@with-dash +@third @fourth +Feature: Do something +""") + feature.name.must_equal "Do something" + feature.tags[0].name.must_equal "wip" + feature.tags[1].name.must_equal "with-dash" + feature.tags[2].name.must_equal "third" + feature.tags[3].name.must_equal "fourth" + end + it 'parses feature with tagsi event without newline at start' do feature = parse( "@wip\nFeature: Do something" @@ -127,6 +141,33 @@ def parse(input) last_scenario.tags[1].name.must_equal "wip" last_scenario.tags[2].name.must_equal "with-vcr" end + + it 'parses feature with scenarios with tags on multiple lines' do + feature = parse(""" +Feature: Do something + + Scenario: Foo bar baz + Given blah foo bar + Then something else + + @javascript @wip + @with-vcr + @forelast + @last + Scenario: Foo bar baz blah + Given blah foo bar + Then something else +""") + scenarios = feature.scenarios + + last_scenario = scenarios.last + + last_scenario.tags[0].name.must_equal "javascript" + last_scenario.tags[1].name.must_equal "wip" + last_scenario.tags[2].name.must_equal "with-vcr" + last_scenario.tags[3].name.must_equal "forelast" + last_scenario.tags[4].name.must_equal "last" + end end end end diff --git a/test/gherkin/parser_test.rb b/test/gherkin/parser_test.rb index e26f0a5..3c20eab 100644 --- a/test/gherkin/parser_test.rb +++ b/test/gherkin/parser_test.rb @@ -20,6 +20,8 @@ module GherkinRuby Then something cooler happens @javascript @wip #@destroy + @test-1 + @test-2 # @test-disabled Scenario: something else happens Given foo Then bar @@ -60,18 +62,20 @@ module GherkinRuby last_scenario = @result.scenarios.last last_scenario.must_be_kind_of AST::Scenario - last_scenario.line.must_equal 18 + last_scenario.line.must_equal 20 last_scenario.name.must_equal 'something else happens' - last_scenario.tags.first.name.must_equal 'javascript' - last_scenario.tags.last.name.must_equal 'wip' + last_scenario.tags.at(0).name.must_equal 'javascript' + last_scenario.tags.at(1).name.must_equal 'wip' + last_scenario.tags.at(2).name.must_equal 'test-1' + last_scenario.tags.at(3).name.must_equal 'test-2' last_scenario.steps.first.keyword.must_equal 'Given' last_scenario.steps.first.name.must_equal 'foo' - last_scenario.steps.first.line.must_equal 19 + last_scenario.steps.first.line.must_equal 21 last_scenario.steps.last.keyword.must_equal 'Then' last_scenario.steps.last.name.must_equal 'bar' - last_scenario.steps.last.line.must_equal 20 + last_scenario.steps.last.line.must_equal 22 end end end