cucumber 3.1.2 → 5.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (115) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +287 -14
  3. data/CONTRIBUTING.md +11 -25
  4. data/README.md +4 -5
  5. data/bin/cucumber +1 -1
  6. data/lib/autotest/cucumber_mixin.rb +46 -53
  7. data/lib/cucumber.rb +1 -1
  8. data/lib/cucumber/cli/configuration.rb +5 -5
  9. data/lib/cucumber/cli/main.rb +12 -12
  10. data/lib/cucumber/cli/options.rb +97 -76
  11. data/lib/cucumber/cli/profile_loader.rb +49 -26
  12. data/lib/cucumber/configuration.rb +44 -29
  13. data/lib/cucumber/constantize.rb +2 -5
  14. data/lib/cucumber/deprecate.rb +31 -7
  15. data/lib/cucumber/errors.rb +5 -7
  16. data/lib/cucumber/events.rb +13 -6
  17. data/lib/cucumber/events/envelope.rb +9 -0
  18. data/lib/cucumber/events/gherkin_source_parsed.rb +11 -0
  19. data/lib/cucumber/events/hook_test_step_created.rb +13 -0
  20. data/lib/cucumber/events/step_activated.rb +2 -1
  21. data/lib/cucumber/events/test_case_created.rb +13 -0
  22. data/lib/cucumber/events/test_case_ready.rb +12 -0
  23. data/lib/cucumber/events/test_step_created.rb +13 -0
  24. data/lib/cucumber/events/undefined_parameter_type.rb +10 -0
  25. data/lib/cucumber/file_specs.rb +6 -6
  26. data/lib/cucumber/filters.rb +1 -0
  27. data/lib/cucumber/filters/activate_steps.rb +5 -3
  28. data/lib/cucumber/filters/broadcast_test_case_ready_event.rb +12 -0
  29. data/lib/cucumber/filters/prepare_world.rb +5 -9
  30. data/lib/cucumber/filters/quit.rb +1 -3
  31. data/lib/cucumber/filters/tag_limits/verifier.rb +2 -4
  32. data/lib/cucumber/formatter/ansicolor.rb +40 -45
  33. data/lib/cucumber/formatter/ast_lookup.rb +163 -0
  34. data/lib/cucumber/formatter/backtrace_filter.rb +9 -8
  35. data/lib/cucumber/formatter/console.rb +58 -66
  36. data/lib/cucumber/formatter/console_counts.rb +4 -9
  37. data/lib/cucumber/formatter/console_issues.rb +6 -3
  38. data/lib/cucumber/formatter/duration.rb +1 -1
  39. data/lib/cucumber/formatter/duration_extractor.rb +3 -1
  40. data/lib/cucumber/formatter/errors.rb +6 -0
  41. data/lib/cucumber/formatter/fanout.rb +2 -0
  42. data/lib/cucumber/formatter/html.rb +11 -598
  43. data/lib/cucumber/formatter/http_io.rb +147 -0
  44. data/lib/cucumber/formatter/ignore_missing_messages.rb +1 -1
  45. data/lib/cucumber/formatter/interceptor.rb +11 -30
  46. data/lib/cucumber/formatter/io.rb +55 -13
  47. data/lib/cucumber/formatter/json.rb +102 -110
  48. data/lib/cucumber/formatter/junit.rb +55 -55
  49. data/lib/cucumber/formatter/message.rb +22 -0
  50. data/lib/cucumber/formatter/message_builder.rb +255 -0
  51. data/lib/cucumber/formatter/pretty.rb +359 -153
  52. data/lib/cucumber/formatter/progress.rb +30 -32
  53. data/lib/cucumber/formatter/publish_banner_printer.rb +77 -0
  54. data/lib/cucumber/formatter/query/hook_by_test_step.rb +31 -0
  55. data/lib/cucumber/formatter/query/pickle_by_test.rb +26 -0
  56. data/lib/cucumber/formatter/query/pickle_step_by_test_step.rb +26 -0
  57. data/lib/cucumber/formatter/query/step_definitions_by_test_step.rb +40 -0
  58. data/lib/cucumber/formatter/query/test_case_started_by_test_case.rb +40 -0
  59. data/lib/cucumber/formatter/rerun.rb +22 -4
  60. data/lib/cucumber/formatter/stepdefs.rb +1 -2
  61. data/lib/cucumber/formatter/steps.rb +3 -4
  62. data/lib/cucumber/formatter/summary.rb +16 -8
  63. data/lib/cucumber/formatter/unicode.rb +15 -17
  64. data/lib/cucumber/formatter/url_reporter.rb +17 -0
  65. data/lib/cucumber/formatter/usage.rb +11 -10
  66. data/lib/cucumber/gherkin/data_table_parser.rb +17 -6
  67. data/lib/cucumber/gherkin/formatter/ansi_escapes.rb +13 -17
  68. data/lib/cucumber/gherkin/formatter/escaping.rb +2 -2
  69. data/lib/cucumber/gherkin/steps_parser.rb +17 -8
  70. data/lib/cucumber/glue/hook.rb +34 -11
  71. data/lib/cucumber/glue/invoke_in_world.rb +13 -18
  72. data/lib/cucumber/glue/proto_world.rb +42 -33
  73. data/lib/cucumber/glue/registry_and_more.rb +42 -12
  74. data/lib/cucumber/glue/snippet.rb +23 -22
  75. data/lib/cucumber/glue/step_definition.rb +42 -19
  76. data/lib/cucumber/glue/world_factory.rb +1 -1
  77. data/lib/cucumber/hooks.rb +11 -11
  78. data/lib/cucumber/multiline_argument.rb +4 -6
  79. data/lib/cucumber/multiline_argument/data_table.rb +97 -64
  80. data/lib/cucumber/multiline_argument/data_table/diff_matrices.rb +2 -2
  81. data/lib/cucumber/multiline_argument/doc_string.rb +1 -1
  82. data/lib/cucumber/platform.rb +3 -3
  83. data/lib/cucumber/rake/task.rb +16 -18
  84. data/lib/cucumber/rspec/disable_option_parser.rb +9 -8
  85. data/lib/cucumber/rspec/doubles.rb +3 -5
  86. data/lib/cucumber/running_test_case.rb +2 -53
  87. data/lib/cucumber/runtime.rb +41 -58
  88. data/lib/cucumber/runtime/after_hooks.rb +8 -4
  89. data/lib/cucumber/runtime/before_hooks.rb +8 -4
  90. data/lib/cucumber/runtime/for_programming_languages.rb +4 -2
  91. data/lib/cucumber/runtime/step_hooks.rb +6 -2
  92. data/lib/cucumber/runtime/support_code.rb +13 -15
  93. data/lib/cucumber/runtime/user_interface.rb +6 -16
  94. data/lib/cucumber/step_definition_light.rb +4 -3
  95. data/lib/cucumber/step_definitions.rb +2 -2
  96. data/lib/cucumber/step_match.rb +12 -11
  97. data/lib/cucumber/step_match_search.rb +2 -1
  98. data/lib/cucumber/term/ansicolor.rb +9 -9
  99. data/lib/cucumber/term/banner.rb +56 -0
  100. data/lib/cucumber/version +1 -1
  101. metadata +254 -83
  102. data/lib/cucumber/events/gherkin_source_parsed.rb~ +0 -14
  103. data/lib/cucumber/formatter/ast_lookup.rb~ +0 -9
  104. data/lib/cucumber/formatter/cucumber.css +0 -286
  105. data/lib/cucumber/formatter/cucumber.sass +0 -247
  106. data/lib/cucumber/formatter/hook_query_visitor.rb +0 -42
  107. data/lib/cucumber/formatter/html_builder.rb +0 -121
  108. data/lib/cucumber/formatter/inline-js.js +0 -30
  109. data/lib/cucumber/formatter/jquery-min.js +0 -154
  110. data/lib/cucumber/formatter/json_pretty.rb +0 -11
  111. data/lib/cucumber/formatter/legacy_api/adapter.rb +0 -1028
  112. data/lib/cucumber/formatter/legacy_api/ast.rb +0 -394
  113. data/lib/cucumber/formatter/legacy_api/results.rb +0 -50
  114. data/lib/cucumber/formatter/legacy_api/runtime_facade.rb +0 -32
  115. data/lib/cucumber/step_argument.rb +0 -25
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'cucumber/core/events'
4
+
5
+ module Cucumber
6
+ module Events
7
+ # Event fired when a TestStep is created from a PickleStep
8
+ class TestStepCreated < Core::Event.new(:test_step, :pickle_step)
9
+ attr_reader :test_step
10
+ attr_reader :pickle_step
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,10 @@
1
+ require 'cucumber/core/events'
2
+
3
+ module Cucumber
4
+ module Events
5
+ class UndefinedParameterType < Core::Event.new(:type_name, :expression)
6
+ attr_reader :type_name
7
+ attr_reader :expression
8
+ end
9
+ end
10
+ end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'cucumber'
4
- require 'cucumber/core/ast/location'
4
+ require 'cucumber/core/test/location'
5
5
 
6
6
  module Cucumber
7
7
  class FileSpecs
@@ -9,7 +9,7 @@ module Cucumber
9
9
 
10
10
  def initialize(file_specs)
11
11
  Cucumber.logger.debug("Features:\n")
12
- @file_specs = file_specs.map { |s| FileSpec.new(s) }
12
+ @file_specs = file_specs.map { |spec| FileSpec.new(spec) }
13
13
  Cucumber.logger.debug("\n")
14
14
  end
15
15
 
@@ -22,8 +22,8 @@ module Cucumber
22
22
  end
23
23
 
24
24
  class FileSpec
25
- def initialize(s)
26
- @file, @lines = *FILE_COLON_LINE_PATTERN.match(s).captures
25
+ def initialize(spec)
26
+ @file, @lines = *FILE_COLON_LINE_PATTERN.match(spec).captures
27
27
  Cucumber.logger.debug(" * #{@file}\n")
28
28
  @lines = String(@lines).split(':').map { |line| Integer(line) }
29
29
  end
@@ -31,8 +31,8 @@ module Cucumber
31
31
  attr_reader :file
32
32
 
33
33
  def locations
34
- return [Core::Ast::Location.new(@file)] if @lines.empty?
35
- @lines.map { |line| Core::Ast::Location.new(@file, line) }
34
+ return [Core::Test::Location.new(@file)] if @lines.empty?
35
+ @lines.map { |line| Core::Test::Location.new(@file, line) }
36
36
  end
37
37
  end
38
38
  end
@@ -11,3 +11,4 @@ require 'cucumber/filters/quit'
11
11
  require 'cucumber/filters/randomizer'
12
12
  require 'cucumber/filters/retry'
13
13
  require 'cucumber/filters/tag_limits'
14
+ require 'cucumber/filters/broadcast_test_case_ready_event'
@@ -26,7 +26,7 @@ module Cucumber
26
26
  private
27
27
 
28
28
  def new_test_steps
29
- @original_test_case.test_steps.map(&self.method(:attempt_to_activate))
29
+ @original_test_case.test_steps.map(&method(:attempt_to_activate))
30
30
  end
31
31
 
32
32
  def attempt_to_activate(test_step)
@@ -39,12 +39,14 @@ module Cucumber
39
39
 
40
40
  class FindMatch
41
41
  def initialize(step_match_search, configuration, test_step)
42
- @step_match_search, @configuration, @test_step = step_match_search, configuration, test_step
42
+ @step_match_search = step_match_search
43
+ @configuration = configuration
44
+ @test_step = test_step
43
45
  end
44
46
 
45
47
  def result
46
48
  begin
47
- return NoStepMatch.new(test_step.source.last, test_step.text) unless matches.any?
49
+ return NoStepMatch.new(test_step, test_step.text) unless matches.any?
48
50
  rescue Cucumber::Ambiguous => e
49
51
  return AmbiguousStepMatch.new(e)
50
52
  end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cucumber
4
+ module Filters
5
+ class BroadcastTestCaseReadyEvent < Core::Filter.new(:config)
6
+ def test_case(test_case)
7
+ config.notify :test_case_ready, test_case
8
+ test_case.describe_to(receiver)
9
+ end
10
+ end
11
+ end
12
+ end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'cucumber/core/filter'
4
- require 'cucumber/core/ast/location'
4
+ require 'cucumber/core/test/location'
5
5
  require 'cucumber/running_test_case'
6
6
 
7
7
  module Cucumber
@@ -13,23 +13,19 @@ module Cucumber
13
13
 
14
14
  class CaseFilter
15
15
  def initialize(runtime, original_test_case)
16
- @runtime, @original_test_case = runtime, original_test_case
16
+ @runtime = runtime
17
+ @original_test_case = original_test_case
17
18
  end
18
19
 
19
20
  def test_case
20
- init_scenario = Cucumber::Hooks.around_hook(@original_test_case.source) do |continue|
21
+ init_scenario = Cucumber::Hooks.around_hook do |continue|
21
22
  @runtime.begin_scenario(scenario)
22
23
  continue.call
23
24
  @runtime.end_scenario(scenario)
24
25
  end
25
26
  around_hooks = [init_scenario] + @original_test_case.around_hooks
26
27
 
27
- empty_hook = proc {} # no op - legacy format adapter expects a before hooks
28
- empty_hook_location = Cucumber::Core::Ast::Location.from_source_location(*empty_hook.source_location)
29
- default_hook = Cucumber::Hooks.before_hook(@original_test_case.source, empty_hook_location, &empty_hook)
30
- steps = [default_hook] + @original_test_case.test_steps
31
-
32
- @original_test_case.with_around_hooks(around_hooks).with_steps(steps)
28
+ @original_test_case.with_around_hooks(around_hooks).with_steps(@original_test_case.test_steps)
33
29
  end
34
30
 
35
31
  private
@@ -8,9 +8,7 @@ module Cucumber
8
8
  end
9
9
 
10
10
  def test_case(test_case)
11
- unless Cucumber.wants_to_quit
12
- test_case.describe_to @receiver
13
- end
11
+ test_case.describe_to @receiver unless Cucumber.wants_to_quit
14
12
  self
15
13
  end
16
14
 
@@ -17,10 +17,8 @@ module Cucumber
17
17
 
18
18
  def collect_breaches(test_case_index)
19
19
  tag_limits.reduce([]) do |breaches, (tag_name, limit)|
20
- breaches.tap do |breaches|
21
- if test_case_index.count_by_tag_name(tag_name) > limit
22
- breaches << Breach.new(tag_name, limit, test_case_index.locations_of_tag_name(tag_name))
23
- end
20
+ breaches.tap do |breach|
21
+ breach << Breach.new(tag_name, limit, test_case_index.locations_of_tag_name(tag_name)) if test_case_index.count_by_tag_name(tag_name) > limit
24
22
  end
25
23
  end
26
24
  end
@@ -55,20 +55,18 @@ module Cucumber
55
55
  include Cucumber::Term::ANSIColor
56
56
 
57
57
  ALIASES = Hash.new do |h, k|
58
- if k.to_s =~ /(.*)_param/
59
- h[$1] + ',bold'
60
- end
61
- end.merge({
62
- 'undefined' => 'yellow',
63
- 'pending' => 'yellow',
64
- 'flaky' => 'yellow',
65
- 'failed' => 'red',
66
- 'passed' => 'green',
67
- 'outline' => 'cyan',
68
- 'skipped' => 'cyan',
69
- 'comment' => 'grey',
70
- 'tag' => 'cyan'
71
- })
58
+ h[Regexp.last_match(1)] + ',bold' if k.to_s =~ /(.*)_param/
59
+ end.merge(
60
+ 'undefined' => 'yellow',
61
+ 'pending' => 'yellow',
62
+ 'flaky' => 'yellow',
63
+ 'failed' => 'red',
64
+ 'passed' => 'green',
65
+ 'outline' => 'cyan',
66
+ 'skipped' => 'cyan',
67
+ 'comment' => 'grey',
68
+ 'tag' => 'cyan'
69
+ )
72
70
 
73
71
  if ENV['CUCUMBER_COLORS'] # Example: export CUCUMBER_COLORS="passed=red:failed=yellow"
74
72
  ENV['CUCUMBER_COLORS'].split(':').each do |pair|
@@ -89,45 +87,42 @@ module Cucumber
89
87
  # red(bold(string, &proc)) + red
90
88
  # end
91
89
  ALIASES.each_key do |method_name|
92
- unless method_name =~ /.*_param/
93
- code = <<-EOF
90
+ next if method_name =~ /.*_param/
91
+ code = <<-COLOR
94
92
  def #{method_name}(string=nil, &proc)
95
- #{ALIASES[method_name].split(",").join("(") + "(string, &proc" + ")" * ALIASES[method_name].split(",").length}
93
+ #{ALIASES[method_name].split(',').join('(') + '(string, &proc' + ')' * ALIASES[method_name].split(',').length}
96
94
  end
97
95
  # This resets the colour to the non-param colour
98
96
  def #{method_name}_param(string=nil, &proc)
99
- #{ALIASES[method_name + '_param'].split(",").join("(") + "(string, &proc" + ")" * ALIASES[method_name + '_param'].split(",").length} + #{ALIASES[method_name].split(",").join(' + ')}
97
+ #{ALIASES[method_name + '_param'].split(',').join('(') + '(string, &proc' + ')' * ALIASES[method_name + '_param'].split(',').length} + #{ALIASES[method_name].split(',').join(' + ')}
100
98
  end
101
- EOF
102
- eval(code)
103
- end
99
+ COLOR
100
+ eval(code) # rubocop:disable Security/Eval
104
101
  end
105
102
 
106
103
  def self.define_grey #:nodoc:
107
- begin
108
- gem 'genki-ruby-terminfo'
109
- require 'terminfo'
110
- case TermInfo.default_object.tigetnum('colors')
111
- when 0
112
- raise "Your terminal doesn't support colours."
113
- when 1
114
- ::Cucumber::Term::ANSIColor.coloring = false
115
- alias grey white
116
- when 2..8
117
- alias grey white
118
- else
119
- define_real_grey
120
- end
121
- rescue Exception => e
122
- if e.class.name == 'TermInfo::TermInfoError'
123
- STDERR.puts '*** WARNING ***'
124
- STDERR.puts "You have the genki-ruby-terminfo gem installed, but you haven't set your TERM variable."
125
- STDERR.puts 'Try setting it to TERM=xterm-256color to get grey colour in output.'
126
- STDERR.puts "\n"
127
- alias grey white
128
- else
129
- define_real_grey
130
- end
104
+ gem 'genki-ruby-terminfo'
105
+ require 'terminfo'
106
+ case TermInfo.default_object.tigetnum('colors')
107
+ when 0
108
+ raise "Your terminal doesn't support colours."
109
+ when 1
110
+ ::Cucumber::Term::ANSIColor.coloring = false
111
+ alias_method :grey, :white
112
+ when 2..8
113
+ alias_method :grey, :white # rubocop:disable Lint/DuplicateMethods
114
+ else
115
+ define_real_grey
116
+ end
117
+ rescue Exception => e # rubocop:disable Lint/RescueException
118
+ if e.class.name == 'TermInfo::TermInfoError'
119
+ STDERR.puts '*** WARNING ***'
120
+ STDERR.puts "You have the genki-ruby-terminfo gem installed, but you haven't set your TERM variable."
121
+ STDERR.puts 'Try setting it to TERM=xterm-256color to get grey colour in output.'
122
+ STDERR.puts "\n"
123
+ alias_method :grey, :white
124
+ else
125
+ define_real_grey
131
126
  end
132
127
  end
133
128
 
@@ -0,0 +1,163 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cucumber
4
+ module Formatter
5
+ class AstLookup
6
+ def initialize(config)
7
+ @gherkin_documents = {}
8
+ @test_case_lookups = {}
9
+ @test_step_lookups = {}
10
+ @step_keyword_lookups = {}
11
+ config.on_event :gherkin_source_parsed, &method(:on_gherkin_source_parsed)
12
+ end
13
+
14
+ def on_gherkin_source_parsed(event)
15
+ @gherkin_documents[event.gherkin_document.uri] = event.gherkin_document
16
+ end
17
+
18
+ def gherkin_document(uri)
19
+ @gherkin_documents[uri]
20
+ end
21
+
22
+ def scenario_source(test_case)
23
+ uri = test_case.location.file
24
+ @test_case_lookups[uri] ||= TestCaseLookupBuilder.new(gherkin_document(uri)).lookup_hash
25
+ @test_case_lookups[uri][test_case.location.lines.max]
26
+ end
27
+
28
+ def step_source(test_step)
29
+ uri = test_step.location.file
30
+ @test_step_lookups[uri] ||= TestStepLookupBuilder.new(gherkin_document(uri)).lookup_hash
31
+ @test_step_lookups[uri][test_step.location.lines.min]
32
+ end
33
+
34
+ def snippet_step_keyword(test_step)
35
+ uri = test_step.location.file
36
+ document = gherkin_document(uri)
37
+ dialect = ::Gherkin::Dialect.for(document.feature.language)
38
+ given_when_then_keywords = [dialect.given_keywords, dialect.when_keywords, dialect.then_keywords].flatten.uniq.reject { |kw| kw == '* ' }
39
+ keyword_lookup = step_keyword_lookup(uri)
40
+ keyword = nil
41
+ node = keyword_lookup[test_step.location.lines.min]
42
+ while keyword.nil?
43
+ if given_when_then_keywords.include?(node.keyword)
44
+ keyword = node.keyword
45
+ break
46
+ end
47
+ break if node.previous_node.nil?
48
+ node = node.previous_node
49
+ end
50
+ keyword = dialect.given_keywords.reject { |kw| kw == '* ' }[0] if keyword.nil?
51
+ keyword = Cucumber::Gherkin::I18n.code_keyword_for(keyword)
52
+ keyword
53
+ end
54
+
55
+ ScenarioSource = Struct.new(:type, :scenario)
56
+
57
+ ScenarioOutlineSource = Struct.new(:type, :scenario_outline, :examples, :row)
58
+
59
+ StepSource = Struct.new(:type, :step)
60
+
61
+ private
62
+
63
+ def step_keyword_lookup(uri)
64
+ @step_keyword_lookups[uri] ||= KeywordLookupBuilder.new(gherkin_document(uri)).lookup_hash
65
+ end
66
+
67
+ class TestCaseLookupBuilder
68
+ attr_reader :lookup_hash
69
+
70
+ def initialize(gherkin_document)
71
+ @lookup_hash = {}
72
+ process_scenario_container(gherkin_document.feature)
73
+ end
74
+
75
+ private
76
+
77
+ def process_scenario_container(container)
78
+ container.children.each do |child|
79
+ if child.respond_to?(:rule) && child.rule
80
+ process_scenario_container(child.rule)
81
+ elsif child.respond_to?(:scenario) && child.scenario
82
+ process_scenario(child)
83
+ end
84
+ end
85
+ end
86
+
87
+ def process_scenario(child)
88
+ if child.scenario.examples.empty?
89
+ @lookup_hash[child.scenario.location.line] = ScenarioSource.new(:Scenario, child.scenario)
90
+ else
91
+ child.scenario.examples.each do |examples|
92
+ examples.table_body.each do |row|
93
+ @lookup_hash[row.location.line] = ScenarioOutlineSource.new(:ScenarioOutline, child.scenario, examples, row)
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
99
+
100
+ class TestStepLookupBuilder
101
+ attr_reader :lookup_hash
102
+
103
+ def initialize(gherkin_document)
104
+ @lookup_hash = {}
105
+ process_scenario_container(gherkin_document.feature)
106
+ end
107
+
108
+ private
109
+
110
+ def process_scenario_container(container)
111
+ container.children.each do |child|
112
+ if child.respond_to?(:rule) && child.rule
113
+ process_scenario_container(child.rule)
114
+ elsif child.respond_to?(:scenario) && child.scenario
115
+ child.scenario.steps.each do |step|
116
+ @lookup_hash[step.location.line] = StepSource.new(:Step, step)
117
+ end
118
+ elsif !child.background.nil?
119
+ child.background.steps.each do |step|
120
+ @lookup_hash[step.location.line] = StepSource.new(:Step, step)
121
+ end
122
+ end
123
+ end
124
+ end
125
+ end
126
+
127
+ KeywordSearchNode = Struct.new(:keyword, :previous_node)
128
+
129
+ class KeywordLookupBuilder
130
+ attr_reader :lookup_hash
131
+
132
+ def initialize(gherkin_document)
133
+ @lookup_hash = {}
134
+ process_scenario_container(gherkin_document.feature, nil)
135
+ end
136
+
137
+ private
138
+
139
+ def process_scenario_container(container, original_previous_node)
140
+ container.children.each do |child|
141
+ previous_node = original_previous_node
142
+ if child.respond_to?(:rule) && child.rule
143
+ process_scenario_container(child.rule, original_previous_node)
144
+ elsif child.respond_to?(:scenario) && child.scenario
145
+ child.scenario.steps.each do |step|
146
+ node = KeywordSearchNode.new(step.keyword, previous_node)
147
+ @lookup_hash[step.location.line] = node
148
+ previous_node = node
149
+ end
150
+ elsif child.respond_to?(:background) && child.background
151
+ child.background.steps.each do |step|
152
+ node = KeywordSearchNode.new(step.keyword, previous_node)
153
+ @lookup_hash[step.location.line] = node
154
+ previous_node = node
155
+ original_previous_node = previous_node
156
+ end
157
+ end
158
+ end
159
+ end
160
+ end
161
+ end
162
+ end
163
+ end
@@ -4,22 +4,23 @@ require 'cucumber/platform'
4
4
 
5
5
  module Cucumber
6
6
  module Formatter
7
- @backtrace_filters = %w(
7
+ @backtrace_filters = %w[
8
8
  /vendor/rails
9
9
  lib/cucumber
10
10
  bin/cucumber:
11
11
  lib/rspec
12
12
  gems/
13
+ site_ruby/
13
14
  minitest
14
15
  test/unit
15
16
  .gem/ruby
16
- lib/ruby/
17
17
  bin/bundle
18
- )
18
+ ]
19
19
 
20
- if ::Cucumber::JRUBY
21
- @backtrace_filters << 'org/jruby/'
22
- end
20
+ @backtrace_filters << RbConfig::CONFIG['rubyarchdir'] if RbConfig::CONFIG['rubyarchdir']
21
+ @backtrace_filters << RbConfig::CONFIG['rubylibdir'] if RbConfig::CONFIG['rubylibdir']
22
+
23
+ @backtrace_filters << 'org/jruby/' if ::Cucumber::JRUBY
23
24
 
24
25
  BACKTRACE_FILTER_PATTERNS = Regexp.new(@backtrace_filters.join('|'))
25
26
 
@@ -31,7 +32,7 @@ module Cucumber
31
32
  def exception
32
33
  return @exception if ::Cucumber.use_full_backtrace
33
34
 
34
- pwd_pattern = /#{::Regexp.escape(::Dir.pwd)}\//m
35
+ pwd_pattern = /#{::Regexp.escape(::Dir.pwd)}\//m # rubocop:disable Style/RegexpLiteral
35
36
  backtrace = @exception.backtrace.map { |line| line.gsub(pwd_pattern, './') }
36
37
 
37
38
  filtered = (backtrace || []).reject do |line|
@@ -41,7 +42,7 @@ module Cucumber
41
42
  if ::ENV['CUCUMBER_TRUNCATE_OUTPUT']
42
43
  # Strip off file locations
43
44
  filtered = filtered.map do |line|
44
- line =~ /(.*):in `/ ? $1 : line
45
+ line =~ /(.*):in `/ ? Regexp.last_match(1) : line
45
46
  end
46
47
  end
47
48