cucumber 3.2.0 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +192 -19
  3. data/CONTRIBUTING.md +2 -18
  4. data/README.md +4 -5
  5. data/bin/cucumber +1 -1
  6. data/lib/autotest/cucumber_mixin.rb +34 -39
  7. data/lib/cucumber/cli/configuration.rb +5 -5
  8. data/lib/cucumber/cli/main.rb +12 -12
  9. data/lib/cucumber/cli/options.rb +62 -71
  10. data/lib/cucumber/cli/profile_loader.rb +49 -26
  11. data/lib/cucumber/configuration.rb +31 -23
  12. data/lib/cucumber/constantize.rb +2 -5
  13. data/lib/cucumber/deprecate.rb +31 -7
  14. data/lib/cucumber/errors.rb +5 -7
  15. data/lib/cucumber/events/envelope.rb +9 -0
  16. data/lib/cucumber/events/gherkin_source_parsed.rb +11 -0
  17. data/lib/cucumber/events/hook_test_step_created.rb +13 -0
  18. data/lib/cucumber/events/step_activated.rb +2 -1
  19. data/lib/cucumber/events/test_case_created.rb +13 -0
  20. data/lib/cucumber/events/test_case_ready.rb +12 -0
  21. data/lib/cucumber/events/test_step_created.rb +13 -0
  22. data/lib/cucumber/events/undefined_parameter_type.rb +10 -0
  23. data/lib/cucumber/events.rb +13 -6
  24. data/lib/cucumber/file_specs.rb +6 -6
  25. data/lib/cucumber/filters/activate_steps.rb +5 -3
  26. data/lib/cucumber/filters/broadcast_test_case_ready_event.rb +12 -0
  27. data/lib/cucumber/filters/prepare_world.rb +5 -9
  28. data/lib/cucumber/filters/quit.rb +1 -3
  29. data/lib/cucumber/filters/tag_limits/verifier.rb +2 -4
  30. data/lib/cucumber/filters.rb +1 -0
  31. data/lib/cucumber/formatter/ansicolor.rb +40 -45
  32. data/lib/cucumber/formatter/ast_lookup.rb +165 -0
  33. data/lib/cucumber/formatter/backtrace_filter.rb +9 -8
  34. data/lib/cucumber/formatter/console.rb +58 -66
  35. data/lib/cucumber/formatter/console_counts.rb +4 -9
  36. data/lib/cucumber/formatter/console_issues.rb +6 -3
  37. data/lib/cucumber/formatter/duration.rb +1 -1
  38. data/lib/cucumber/formatter/duration_extractor.rb +3 -1
  39. data/lib/cucumber/formatter/errors.rb +6 -0
  40. data/lib/cucumber/formatter/fanout.rb +2 -0
  41. data/lib/cucumber/formatter/html.rb +11 -598
  42. data/lib/cucumber/formatter/http_io.rb +1 -1
  43. data/lib/cucumber/formatter/ignore_missing_messages.rb +1 -1
  44. data/lib/cucumber/formatter/interceptor.rb +8 -28
  45. data/lib/cucumber/formatter/io.rb +1 -1
  46. data/lib/cucumber/formatter/json.rb +101 -115
  47. data/lib/cucumber/formatter/junit.rb +56 -56
  48. data/lib/cucumber/formatter/message.rb +22 -0
  49. data/lib/cucumber/formatter/message_builder.rb +255 -0
  50. data/lib/cucumber/formatter/pretty.rb +359 -153
  51. data/lib/cucumber/formatter/progress.rb +30 -32
  52. data/lib/cucumber/formatter/query/hook_by_test_step.rb +31 -0
  53. data/lib/cucumber/formatter/query/pickle_by_test.rb +26 -0
  54. data/lib/cucumber/formatter/query/pickle_step_by_test_step.rb +26 -0
  55. data/lib/cucumber/formatter/query/step_definitions_by_test_step.rb +40 -0
  56. data/lib/cucumber/formatter/query/test_case_started_by_test_case.rb +40 -0
  57. data/lib/cucumber/formatter/rerun.rb +22 -4
  58. data/lib/cucumber/formatter/stepdefs.rb +1 -2
  59. data/lib/cucumber/formatter/steps.rb +2 -3
  60. data/lib/cucumber/formatter/summary.rb +16 -8
  61. data/lib/cucumber/formatter/unicode.rb +15 -17
  62. data/lib/cucumber/formatter/usage.rb +11 -10
  63. data/lib/cucumber/gherkin/data_table_parser.rb +17 -6
  64. data/lib/cucumber/gherkin/formatter/ansi_escapes.rb +13 -17
  65. data/lib/cucumber/gherkin/formatter/escaping.rb +2 -2
  66. data/lib/cucumber/gherkin/steps_parser.rb +17 -8
  67. data/lib/cucumber/glue/dsl.rb +1 -1
  68. data/lib/cucumber/glue/hook.rb +34 -11
  69. data/lib/cucumber/glue/invoke_in_world.rb +13 -18
  70. data/lib/cucumber/glue/proto_world.rb +42 -33
  71. data/lib/cucumber/glue/registry_and_more.rb +42 -12
  72. data/lib/cucumber/glue/snippet.rb +23 -22
  73. data/lib/cucumber/glue/step_definition.rb +42 -19
  74. data/lib/cucumber/glue/world_factory.rb +1 -1
  75. data/lib/cucumber/hooks.rb +11 -11
  76. data/lib/cucumber/multiline_argument/data_table/diff_matrices.rb +1 -1
  77. data/lib/cucumber/multiline_argument/data_table.rb +97 -64
  78. data/lib/cucumber/multiline_argument/doc_string.rb +1 -1
  79. data/lib/cucumber/multiline_argument.rb +4 -6
  80. data/lib/cucumber/platform.rb +3 -3
  81. data/lib/cucumber/rake/task.rb +16 -16
  82. data/lib/cucumber/rspec/disable_option_parser.rb +9 -8
  83. data/lib/cucumber/running_test_case.rb +2 -53
  84. data/lib/cucumber/runtime/after_hooks.rb +8 -4
  85. data/lib/cucumber/runtime/before_hooks.rb +8 -4
  86. data/lib/cucumber/runtime/for_programming_languages.rb +4 -2
  87. data/lib/cucumber/runtime/step_hooks.rb +6 -2
  88. data/lib/cucumber/runtime/support_code.rb +13 -15
  89. data/lib/cucumber/runtime/user_interface.rb +6 -16
  90. data/lib/cucumber/runtime.rb +34 -58
  91. data/lib/cucumber/step_definition_light.rb +4 -3
  92. data/lib/cucumber/step_definitions.rb +2 -2
  93. data/lib/cucumber/step_match.rb +12 -11
  94. data/lib/cucumber/step_match_search.rb +2 -1
  95. data/lib/cucumber/term/ansicolor.rb +9 -9
  96. data/lib/cucumber/version +1 -1
  97. data/lib/cucumber.rb +1 -1
  98. metadata +251 -81
  99. data/lib/cucumber/formatter/cucumber.css +0 -286
  100. data/lib/cucumber/formatter/cucumber.sass +0 -247
  101. data/lib/cucumber/formatter/hook_query_visitor.rb +0 -42
  102. data/lib/cucumber/formatter/html_builder.rb +0 -121
  103. data/lib/cucumber/formatter/inline-js.js +0 -30
  104. data/lib/cucumber/formatter/jquery-min.js +0 -154
  105. data/lib/cucumber/formatter/json_pretty.rb +0 -11
  106. data/lib/cucumber/formatter/legacy_api/adapter.rb +0 -1028
  107. data/lib/cucumber/formatter/legacy_api/ast.rb +0 -394
  108. data/lib/cucumber/formatter/legacy_api/results.rb +0 -50
  109. data/lib/cucumber/formatter/legacy_api/runtime_facade.rb +0 -32
  110. 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 step is created from a hook
8
+ class HookTestStepCreated < Core::Event.new(:test_step, :hook)
9
+ attr_reader :test_step
10
+ attr_reader :hook
11
+ end
12
+ end
13
+ end
@@ -18,7 +18,8 @@ module Cucumber
18
18
 
19
19
  # @private
20
20
  def initialize(test_step, step_match)
21
- @test_step, @step_match = test_step, step_match
21
+ @test_step = test_step
22
+ @step_match = step_match
22
23
  end
23
24
  end
24
25
  end
@@ -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 Test::Case is created from a Pickle
8
+ class TestCaseCreated < Core::Event.new(:test_case, :pickle)
9
+ attr_reader :test_case
10
+ attr_reader :pickle
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'cucumber/core/events'
4
+
5
+ module Cucumber
6
+ module Events
7
+ # Event fired when a Test::Case is ready to be ran (matching has been done, hooks added etc)
8
+ class TestCaseReady < Core::Event.new(:test_case)
9
+ attr_reader :test_case
10
+ end
11
+ end
12
+ end
@@ -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
@@ -24,15 +24,22 @@ module Cucumber
24
24
 
25
25
  def self.registry
26
26
  Core::Events.build_registry(
27
- TestCaseStarted,
27
+ GherkinSourceParsed,
28
+ GherkinSourceRead,
29
+ HookTestStepCreated,
30
+ StepActivated,
31
+ StepDefinitionRegistered,
32
+ TestCaseCreated,
28
33
  TestCaseFinished,
34
+ TestCaseStarted,
35
+ TestCaseReady,
36
+ TestRunFinished,
37
+ TestRunStarted,
38
+ TestStepCreated,
29
39
  TestStepFinished,
30
40
  TestStepStarted,
31
- StepDefinitionRegistered,
32
- StepActivated,
33
- TestRunFinished,
34
- GherkinSourceRead,
35
- TestRunStarted
41
+ Envelope,
42
+ UndefinedParameterType
36
43
  )
37
44
  end
38
45
  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
@@ -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
@@ -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'
@@ -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,165 @@
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
+ # rubocop:disable Metrics/PerceivedComplexity
140
+ def process_scenario_container(container, original_previous_node)
141
+ container.children.each do |child|
142
+ previous_node = original_previous_node
143
+ if child.respond_to?(:rule) && child.rule
144
+ process_scenario_container(child.rule, original_previous_node)
145
+ elsif child.respond_to?(:scenario) && child.scenario
146
+ child.scenario.steps.each do |step|
147
+ node = KeywordSearchNode.new(step.keyword, previous_node)
148
+ @lookup_hash[step.location.line] = node
149
+ previous_node = node
150
+ end
151
+ elsif child.respond_to?(:background) && child.background
152
+ child.background.steps.each do |step|
153
+ node = KeywordSearchNode.new(step.keyword, previous_node)
154
+ @lookup_hash[step.location.line] = node
155
+ previous_node = node
156
+ original_previous_node = previous_node
157
+ end
158
+ end
159
+ end
160
+ end
161
+ # rubocop:enable Metrics/PerceivedComplexity
162
+ end
163
+ end
164
+ end
165
+ 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