cucumber 3.2.0 → 4.0.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 +166 -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 +3 -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 +54 -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 +221 -77
  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
@@ -1,13 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'cucumber/core/report/summary'
4
3
  require 'cucumber/formatter/backtrace_filter'
5
4
  require 'cucumber/formatter/console'
6
5
  require 'cucumber/formatter/console_counts'
7
6
  require 'cucumber/formatter/console_issues'
8
7
  require 'cucumber/formatter/io'
9
8
  require 'cucumber/formatter/duration_extractor'
10
- require 'cucumber/formatter/hook_query_visitor'
9
+ require 'cucumber/formatter/ast_lookup'
11
10
 
12
11
  module Cucumber
13
12
  module Formatter
@@ -15,56 +14,58 @@ module Cucumber
15
14
  class Progress
16
15
  include Console
17
16
  include Io
18
- attr_reader :runtime
19
- attr_reader :config, :summary
17
+ attr_reader :config, :current_feature_uri
18
+ private :config, :current_feature_uri
20
19
 
21
20
  def initialize(config)
22
- @config, @io = config, ensure_io(config.out_stream)
23
- @previous_step_keyword = nil
21
+ @config = config
22
+ @io = ensure_io(config.out_stream)
24
23
  @snippets_input = []
24
+ @undefined_parameter_types = []
25
25
  @total_duration = 0
26
- @summary = Cucumber::Core::Report::Summary.new(config.event_bus)
27
26
  @matches = {}
28
27
  @pending_step_matches = []
29
28
  @failed_results = []
30
- @failed_test_cases = []
31
29
  @passed_test_cases = []
30
+ @current_feature_uri = ''
31
+ @gherkin_documents = {}
32
+ @ast_lookup = AstLookup.new(config)
32
33
  @counts = ConsoleCounts.new(config)
33
- @issues = ConsoleIssues.new(config)
34
+ @issues = ConsoleIssues.new(config, @ast_lookup)
34
35
  config.on_event :step_activated, &method(:on_step_activated)
35
36
  config.on_event :test_case_started, &method(:on_test_case_started)
36
37
  config.on_event :test_step_finished, &method(:on_test_step_finished)
37
38
  config.on_event :test_case_finished, &method(:on_test_case_finished)
38
39
  config.on_event :test_run_finished, &method(:on_test_run_finished)
40
+ config.on_event :undefined_parameter_type, &method(:collect_undefined_parameter_type_names)
39
41
  end
40
42
 
41
43
  def on_step_activated(event)
42
- @matches[event.test_step.source] = event.step_match
44
+ @matches[event.test_step.to_s] = event.step_match
43
45
  end
44
46
 
45
- def on_test_case_started(_event)
47
+ def on_test_case_started(event)
46
48
  unless @profile_information_printed
47
49
  do_print_profile_information(config.profiles) unless config.skip_profile_information? || config.profiles.nil? || config.profiles.empty?
48
50
  @profile_information_printed = true
49
51
  end
50
- @previous_step_keyword = nil
52
+ @current_feature_uri = event.test_case.location.file
51
53
  end
52
54
 
53
55
  def on_test_step_finished(event)
54
56
  test_step = event.test_step
55
57
  result = event.result.with_filtered_backtrace(Cucumber::Formatter::BacktraceFilter)
56
- progress(result.to_sym) if !HookQueryVisitor.new(test_step).hook? || result.failed?
58
+ progress(result.to_sym) if !test_step.hook? || result.failed?
57
59
 
58
- return if HookQueryVisitor.new(test_step).hook?
59
- collect_snippet_data(test_step, result)
60
- @pending_step_matches << @matches[test_step.source] if result.pending?
60
+ return if test_step.hook?
61
+ collect_snippet_data(test_step, @ast_lookup) if result.undefined?
62
+ @pending_step_matches << @matches[test_step.to_s] if result.pending?
61
63
  @failed_results << result if result.failed?
62
64
  end
63
65
 
64
66
  def on_test_case_finished(event)
65
67
  test_case = event.test_case
66
68
  result = event.result.with_filtered_backtrace(Cucumber::Formatter::BacktraceFilter)
67
- @failed_test_cases << test_case if result.failed?
68
69
  @passed_test_cases << test_case if result.passed?
69
70
  @total_duration += DurationExtractor.new(result).result_duration
70
71
  end
@@ -77,28 +78,25 @@ module Cucumber
77
78
 
78
79
  private
79
80
 
81
+ def gherkin_document
82
+ @ast_lookup.gherkin_document(current_feature_uri)
83
+ end
84
+
80
85
  def print_summary
81
86
  print_elements(@pending_step_matches, :pending, 'steps')
82
87
  print_elements(@failed_results, :failed, 'steps')
83
88
  print_statistics(@total_duration, @config, @counts, @issues)
84
- snippet_text_proc = lambda do |step_keyword, step_name, multiline_arg|
85
- snippet_text(step_keyword, step_name, multiline_arg)
86
- end
87
- do_print_snippets(snippet_text_proc) if config.snippets? && summary.test_steps.total(:undefined) > 0
88
- return unless config.wip?
89
- messages = @passed_test_cases.map do |test_case|
90
- linebreaks("#{test_case.location.on_line(test_case.location.line)}:in `#{test_case.name}'", ENV['CUCUMBER_TRUNCATE_OUTPUT'].to_i)
91
- end
92
- do_print_passing_wip(messages)
89
+ print_snippets(config.to_hash)
90
+ print_passing_wip(config, @passed_test_cases, @ast_lookup)
93
91
  end
94
92
 
95
93
  CHARS = {
96
- :passed => '.',
97
- :failed => 'F',
98
- :undefined => 'U',
99
- :pending => 'P',
100
- :skipped => '-'
101
- }
94
+ passed: '.',
95
+ failed: 'F',
96
+ undefined: 'U',
97
+ pending: 'P',
98
+ skipped: '-'
99
+ }.freeze
102
100
 
103
101
  def progress(status)
104
102
  char = CHARS[status]
@@ -0,0 +1,31 @@
1
+ require 'cucumber/formatter/errors'
2
+
3
+ module Cucumber
4
+ module Formatter
5
+ module Query
6
+ class HookByTestStep
7
+ def initialize(config)
8
+ @hook_id_by_test_step_id = {}
9
+
10
+ config.on_event :test_step_created, &method(:on_test_step_created)
11
+ config.on_event :hook_test_step_created, &method(:on_hook_test_step_created)
12
+ end
13
+
14
+ def hook_id(test_step)
15
+ return @hook_id_by_test_step_id[test_step.id] if @hook_id_by_test_step_id.key?(test_step.id)
16
+ raise TestStepUnknownError, "No hook found for #{test_step.id} }. Known: #{@hook_id_by_test_step_id.keys}"
17
+ end
18
+
19
+ private
20
+
21
+ def on_test_step_created(event)
22
+ @hook_id_by_test_step_id[event.test_step.id] = nil
23
+ end
24
+
25
+ def on_hook_test_step_created(event)
26
+ @hook_id_by_test_step_id[event.test_step.id] = event.hook.id
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,26 @@
1
+ require 'cucumber/formatter/errors'
2
+
3
+ module Cucumber
4
+ module Formatter
5
+ module Query
6
+ class PickleByTest
7
+ def initialize(config)
8
+ @pickle_id_by_test_case_id = {}
9
+ config.on_event :test_case_created, &method(:on_test_case_created)
10
+ end
11
+
12
+ def pickle_id(test_case)
13
+ return @pickle_id_by_test_case_id[test_case.id] if @pickle_id_by_test_case_id.key?(test_case.id)
14
+
15
+ raise TestCaseUnknownError, "No pickle found for #{test_case.id} }. Known: #{@pickle_id_by_test_case_id.keys}"
16
+ end
17
+
18
+ private
19
+
20
+ def on_test_case_created(event)
21
+ @pickle_id_by_test_case_id[event.test_case.id] = event.pickle.id
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,26 @@
1
+ require 'cucumber/formatter/errors'
2
+
3
+ module Cucumber
4
+ module Formatter
5
+ module Query
6
+ class PickleStepByTestStep
7
+ def initialize(config)
8
+ @pickle_id_step_by_test_step_id = {}
9
+ config.on_event :test_step_created, &method(:on_test_step_created)
10
+ end
11
+
12
+ def pickle_step_id(test_step)
13
+ return @pickle_id_step_by_test_step_id[test_step.id] if @pickle_id_step_by_test_step_id.key?(test_step.id)
14
+
15
+ raise TestStepUnknownError, "No pickle step found for #{test_step.id} }. Known: #{@pickle_id_step_by_test_step_id.keys}"
16
+ end
17
+
18
+ private
19
+
20
+ def on_test_step_created(event)
21
+ @pickle_id_step_by_test_step_id[event.test_step.id] = event.pickle_step.id
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,40 @@
1
+ require 'cucumber/formatter/errors'
2
+
3
+ module Cucumber
4
+ module Formatter
5
+ module Query
6
+ class StepDefinitionsByTestStep
7
+ def initialize(config)
8
+ @step_definition_ids_by_test_step_id = {}
9
+ @step_match_arguments_by_test_step_id = {}
10
+
11
+ config.on_event :test_step_created, &method(:on_test_step_created)
12
+ config.on_event :step_activated, &method(:on_step_activated)
13
+ end
14
+
15
+ def step_definition_ids(test_step)
16
+ return @step_definition_ids_by_test_step_id[test_step.id] if @step_definition_ids_by_test_step_id.key?(test_step.id)
17
+
18
+ raise TestStepUnknownError, "No step definition found for #{test_step.id} }. Known: #{@step_definition_ids_by_test_step_id.keys}"
19
+ end
20
+
21
+ def step_match_arguments(test_step)
22
+ return @step_match_arguments_by_test_step_id[test_step.id] if @step_match_arguments_by_test_step_id.key?(test_step.id)
23
+
24
+ raise TestStepUnknownError, "No step match arguments found for #{test_step.id} }. Known: #{@step_match_arguments_by_test_step_id.keys}"
25
+ end
26
+
27
+ private
28
+
29
+ def on_test_step_created(event)
30
+ @step_definition_ids_by_test_step_id[event.test_step.id] = []
31
+ end
32
+
33
+ def on_step_activated(event)
34
+ @step_definition_ids_by_test_step_id[event.test_step.id] << event.step_match.step_definition.id
35
+ @step_match_arguments_by_test_step_id[event.test_step.id] = event.step_match.step_arguments
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,40 @@
1
+ require 'cucumber/formatter/errors'
2
+
3
+ module Cucumber
4
+ module Formatter
5
+ module Query
6
+ class TestCaseStartedByTestCase
7
+ def initialize(config)
8
+ @config = config
9
+ config.on_event :test_case_created, &method(:on_test_case_created)
10
+ config.on_event :test_case_started, &method(:on_test_case_started)
11
+
12
+ @attempts_by_test_case_id = {}
13
+ @test_case_started_id_by_test_case_id = {}
14
+ end
15
+
16
+ def attempt_by_test_case(test_case)
17
+ raise TestCaseUnknownError, "No test case found for #{test_case.id} }. Known: #{@attempts_by_test_case_id.keys}" unless @attempts_by_test_case_id.key?(test_case.id)
18
+ @attempts_by_test_case_id[test_case.id]
19
+ end
20
+
21
+ def test_case_started_id_by_test_case(test_case)
22
+ raise TestCaseUnknownError, "No test case found for #{test_case.id} }. Known: #{@test_case_started_id_by_test_case_id.keys}" unless @test_case_started_id_by_test_case_id.key?(test_case.id)
23
+ @test_case_started_id_by_test_case_id[test_case.id]
24
+ end
25
+
26
+ private
27
+
28
+ def on_test_case_created(event)
29
+ @attempts_by_test_case_id[event.test_case.id] = 0
30
+ @test_case_started_id_by_test_case_id[event.test_case.id] = nil
31
+ end
32
+
33
+ def on_test_case_started(event)
34
+ @attempts_by_test_case_id[event.test_case.id] += 1
35
+ @test_case_started_id_by_test_case_id[event.test_case.id] = @config.id_generator.new_id
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -7,17 +7,29 @@ module Cucumber
7
7
  class Rerun
8
8
  include Formatter::Io
9
9
 
10
- def initialize(config)
10
+ def initialize(config) # rubocop:disable Metrics/PerceivedComplexity
11
11
  @io = ensure_io(config.out_stream)
12
12
  @config = config
13
13
  @failures = {}
14
14
  config.on_event :test_case_finished do |event|
15
15
  test_case, result = *event.attributes
16
- next if result.ok?(@config.strict)
17
- @failures[test_case.location.file] ||= []
18
- @failures[test_case.location.file] << test_case.location.line
16
+ if @config.strict.strict?(:flaky)
17
+ next if result.ok?(@config.strict)
18
+ add_to_failures(test_case)
19
+ else
20
+ unless @latest_failed_test_case.nil?
21
+ if @latest_failed_test_case != test_case
22
+ add_to_failures(@latest_failed_test_case)
23
+ @latest_failed_test_case = nil
24
+ elsif result.ok?(@config.strict)
25
+ @latest_failed_test_case = nil
26
+ end
27
+ end
28
+ @latest_failed_test_case = test_case unless result.ok?(@config.strict)
29
+ end
19
30
  end
20
31
  config.on_event :test_run_finished do
32
+ add_to_failures(@latest_failed_test_case) unless @latest_failed_test_case.nil?
21
33
  next if @failures.empty?
22
34
  @io.print file_failures.join("\n")
23
35
  end
@@ -28,6 +40,12 @@ module Cucumber
28
40
  def file_failures
29
41
  @failures.map { |file, lines| [file, lines].join(':') }
30
42
  end
43
+
44
+ def add_to_failures(test_case)
45
+ location = test_case.location
46
+ @failures[location.file] ||= []
47
+ @failures[location.file] << location.lines.max unless @failures[location.file].include?(location.lines.max)
48
+ end
31
49
  end
32
50
  end
33
51
  end
@@ -5,8 +5,7 @@ require 'cucumber/formatter/usage'
5
5
  module Cucumber
6
6
  module Formatter
7
7
  class Stepdefs < Usage
8
- def print_steps(stepdef_key)
9
- end
8
+ def print_steps(stepdef_key); end
10
9
 
11
10
  def max_step_length
12
11
  0
@@ -35,15 +35,14 @@ module Cucumber
35
35
  end
36
36
 
37
37
  def collect_steps(runtime)
38
- runtime.step_definitions.inject({}) do |step_definitions, step_definition|
38
+ runtime.step_definitions.each_with_object({}) do |step_definition, step_definitions|
39
39
  step_definitions[step_definition.file] ||= []
40
40
  step_definitions[step_definition.file] << [step_definition.file_colon_line, step_definition.regexp_source]
41
- step_definitions
42
41
  end
43
42
  end
44
43
 
45
44
  def source_indent(sources)
46
- sources.map { |file_colon_line, regexp| regexp.size }.max + 1
45
+ sources.map { |_file_colon_line, regexp| regexp.size }.max + 1
47
46
  end
48
47
  end
49
48
  end
@@ -5,6 +5,7 @@ require 'cucumber/formatter/console'
5
5
  require 'cucumber/formatter/console_counts'
6
6
  require 'cucumber/formatter/console_issues'
7
7
  require 'cucumber/core/test/result'
8
+ require 'cucumber/formatter/ast_lookup'
8
9
 
9
10
  module Cucumber
10
11
  module Formatter
@@ -14,9 +15,11 @@ module Cucumber
14
15
  include Console
15
16
 
16
17
  def initialize(config)
17
- @config, @io = config, ensure_io(config.out_stream)
18
+ @config = config
19
+ @io = ensure_io(config.out_stream)
20
+ @ast_lookup = AstLookup.new(config)
18
21
  @counts = ConsoleCounts.new(@config)
19
- @issues = ConsoleIssues.new(@config)
22
+ @issues = ConsoleIssues.new(@config, @ast_lookup)
20
23
  @start_time = Time.now
21
24
 
22
25
  @config.on_event :test_case_started do |event|
@@ -28,7 +31,7 @@ module Cucumber
28
31
  print_result event.result
29
32
  end
30
33
 
31
- @config.on_event :test_run_finished do |event|
34
+ @config.on_event :test_run_finished do |_event|
32
35
  duration = Time.now - @start_time
33
36
  @io.puts
34
37
  print_statistics(duration, @config, @counts, @issues)
@@ -37,12 +40,17 @@ module Cucumber
37
40
 
38
41
  private
39
42
 
43
+ def gherkin_document(uri)
44
+ @ast_lookup.gherkin_document(uri)
45
+ end
46
+
40
47
  def print_feature(test_case)
41
- feature = test_case.feature
42
- return if @current_feature == feature
43
- @io.puts unless @current_feature.nil?
44
- @io.puts feature
45
- @current_feature = feature
48
+ uri = test_case.location.file
49
+ return if @current_feature_uri == uri
50
+ feature_name = gherkin_document(uri).feature.name
51
+ @io.puts unless @current_feature_uri.nil?
52
+ @io.puts feature_name
53
+ @current_feature_uri = uri
46
54
  end
47
55
 
48
56
  def print_test_case(test_case)
@@ -9,39 +9,37 @@ if Cucumber::WINDOWS
9
9
  if ENV['CUCUMBER_OUTPUT_ENCODING']
10
10
  Cucumber::CODEPAGE = ENV['CUCUMBER_OUTPUT_ENCODING']
11
11
  elsif `cmd /c chcp` =~ /(\d+)/
12
- if [65000, 65001].include? $1.to_i
13
- Cucumber::CODEPAGE = 'UTF-8'
12
+ if [65_000, 65_001].include? Regexp.last_match(1).to_i
13
+ Cucumber::CODEPAGE = 'UTF-8'.freeze
14
14
  ENV['ANSICON_API'] = 'ruby'
15
15
  else
16
- Cucumber::CODEPAGE = "cp#{$1.to_i}"
16
+ Cucumber::CODEPAGE = "cp#{Regexp.last_match(1).to_i}".freeze
17
17
  end
18
18
  else
19
- Cucumber::CODEPAGE = 'cp1252'
19
+ Cucumber::CODEPAGE = 'cp1252'.freeze
20
20
  STDERR.puts("WARNING: Couldn't detect your output codepage. Assuming it is 1252. You may have to chcp 1252 or SET CUCUMBER_OUTPUT_ENCODING=cp1252.")
21
21
  end
22
22
 
23
23
  module Cucumber
24
24
  # @private
25
25
  module WindowsOutput
26
- def self.extended(o)
27
- o.instance_eval do
28
- def cucumber_preprocess_output(*a)
29
- begin
30
- a.map { |arg| arg.to_s.encode(Encoding.default_external) }
31
- rescue Encoding::UndefinedConversionError => e
32
- STDERR.cucumber_puts("WARNING: #{e.message}")
33
- a
34
- end
26
+ def self.extended(output)
27
+ output.instance_eval do
28
+ def cucumber_preprocess_output(*out)
29
+ out.map { |arg| arg.to_s.encode(Encoding.default_external) }
30
+ rescue Encoding::UndefinedConversionError => e
31
+ STDERR.cucumber_puts("WARNING: #{e.message}")
32
+ out
35
33
  end
36
34
 
37
35
  alias cucumber_print print
38
- def print(*a)
39
- cucumber_print(*cucumber_preprocess_output(*a))
36
+ def print(*out)
37
+ cucumber_print(*cucumber_preprocess_output(*out))
40
38
  end
41
39
 
42
40
  alias cucumber_puts puts
43
- def puts(*a)
44
- cucumber_puts(*cucumber_preprocess_output(*a))
41
+ def puts(*out)
42
+ cucumber_puts(*cucumber_preprocess_output(*out))
45
43
  end
46
44
  end
47
45
  end
@@ -17,7 +17,7 @@ module Cucumber
17
17
  @matches = {}
18
18
  config.on_event :step_activated do |event|
19
19
  test_step, step_match = *event.attributes
20
- @matches[test_step.source] = step_match
20
+ @matches[test_step.to_s] = step_match
21
21
  end
22
22
  config.on_event :step_definition_registered, &method(:on_step_definition_registered)
23
23
  end
@@ -28,25 +28,26 @@ module Cucumber
28
28
  end
29
29
 
30
30
  def on_step_match(event)
31
- @matches[event.test_step.source] = event.step_match
31
+ @matches[event.test_step.to_s] = event.step_match
32
32
  super
33
33
  end
34
34
 
35
35
  def on_test_step_finished(event)
36
- return if HookQueryVisitor.new(event.test_step).hook?
36
+ return if event.test_step.hook?
37
37
 
38
38
  test_step = event.test_step
39
39
  result = event.result.with_filtered_backtrace(Cucumber::Formatter::BacktraceFilter)
40
- step_match = @matches[test_step.source]
40
+ step_match = @matches[test_step.to_s]
41
41
 
42
42
  unless step_match.nil?
43
43
  step_definition = step_match.step_definition
44
44
  stepdef_key = StepDefKey.new(step_definition.expression.to_s, step_definition.location)
45
45
  unless @stepdef_to_match[stepdef_key].map { |key| key[:location] }.include? test_step.location
46
46
  duration = DurationExtractor.new(result).result_duration
47
+ keyword = @ast_lookup.step_source(test_step).step.keyword
47
48
 
48
49
  @stepdef_to_match[stepdef_key] << {
49
- keyword: test_step.source.last.keyword,
50
+ keyword: keyword,
50
51
  step_match: step_match,
51
52
  status: result.to_sym,
52
53
  location: test_step.location,
@@ -64,9 +65,9 @@ module Cucumber
64
65
  aggregate_info
65
66
 
66
67
  keys = if config.dry_run?
67
- @stepdef_to_match.keys.sort { |a, b| a.regexp_source <=> b.regexp_source }
68
+ @stepdef_to_match.keys.sort_by(&:regexp_source)
68
69
  else
69
- @stepdef_to_match.keys.sort { |a, b| a.mean_duration <=> b.mean_duration }.reverse
70
+ @stepdef_to_match.keys.sort_by(&:mean_duration).reverse
70
71
  end
71
72
 
72
73
  keys.each do |stepdef_key|
@@ -83,7 +84,7 @@ module Cucumber
83
84
  end
84
85
 
85
86
  def print_step_definition(stepdef_key)
86
- @io.print format_string(format('%.7f', stepdef_key.mean_duration), :skipped) + ' ' unless config.dry_run?
87
+ @io.print format_string(format('%<duration>.7f', duration: stepdef_key.mean_duration), :skipped) + ' ' unless config.dry_run?
87
88
  @io.print format_string(stepdef_key.regexp_source, stepdef_key.status)
88
89
  if config.source?
89
90
  indent = max_length - stepdef_key.regexp_source.unpack('U*').length
@@ -96,7 +97,7 @@ module Cucumber
96
97
  def print_steps(stepdef_key)
97
98
  @stepdef_to_match[stepdef_key].each do |step|
98
99
  @io.print ' '
99
- @io.print format_string(format('%.7f', step[:duration]), :skipped) + ' ' unless config.dry_run?
100
+ @io.print format_string(format('%<duration>.7f', duration: step[:duration]), :skipped) + ' ' unless config.dry_run?
100
101
  @io.print format_step(step[:keyword], step[:step_match], step[:status], nil)
101
102
  if config.source?
102
103
  indent = max_length - (step[:keyword].unpack('U*').length + step[:step_match].format_args.unpack('U*').length)
@@ -135,7 +136,7 @@ module Cucumber
135
136
  end
136
137
 
137
138
  def worst_status(statuses)
138
- [:passed, :undefined, :pending, :skipped, :failed].find do |status|
139
+ %i[passed undefined pending skipped failed].find do |status|
139
140
  statuses.include?(status)
140
141
  end
141
142
  end
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'gherkin/token_scanner'
4
- require 'gherkin/parser'
3
+ require 'gherkin'
5
4
  require 'gherkin/dialect'
6
5
 
7
6
  module Cucumber
@@ -12,15 +11,27 @@ module Cucumber
12
11
  end
13
12
 
14
13
  def parse(text)
15
- token_scanner = ::Gherkin::TokenScanner.new(feature_header + text)
16
- parser = ::Gherkin::Parser.new
17
- gherkin_document = parser.parse(token_scanner)
14
+ gherkin_document = nil
15
+ messages = ::Gherkin.from_source('dummy', feature_header + text, gherkin_options)
18
16
 
19
- gherkin_document[:feature][:children][0][:steps][0][:argument][:rows].each do |row|
17
+ messages.each do |message|
18
+ gherkin_document = message.gherkin_document.to_hash unless message.gherkin_document.nil?
19
+ end
20
+
21
+ return if gherkin_document.nil?
22
+ gherkin_document[:feature][:children][0][:scenario][:steps][0][:data_table][:rows].each do |row|
20
23
  @builder.row(row[:cells].map { |cell| cell[:value] })
21
24
  end
22
25
  end
23
26
 
27
+ def gherkin_options
28
+ {
29
+ include_source: false,
30
+ include_gherkin_document: true,
31
+ include_pickles: false
32
+ }
33
+ end
34
+
24
35
  def feature_header
25
36
  dialect = ::Gherkin::Dialect.for('en')
26
37
  %(#{dialect.feature_keywords[0]}:
@@ -51,23 +51,21 @@ module Cucumber
51
51
  'white' => "\e[37m",
52
52
  'grey' => "\e[90m",
53
53
  'bold' => "\e[1m"
54
- }
54
+ }.freeze
55
55
 
56
56
  ALIASES = Hash.new do |h, k|
57
- if k.to_s =~ /(.*)_arg/
58
- h[$1] + ',bold'
59
- end
60
- end.merge({
61
- 'undefined' => 'yellow',
62
- 'pending' => 'yellow',
63
- 'executing' => 'grey',
64
- 'failed' => 'red',
65
- 'passed' => 'green',
66
- 'outline' => 'cyan',
67
- 'skipped' => 'cyan',
68
- 'comments' => 'grey',
69
- 'tag' => 'cyan'
70
- })
57
+ h[Regexp.last_match(1)] + ',bold' if k.to_s =~ /(.*)_arg/
58
+ end.merge(
59
+ 'undefined' => 'yellow',
60
+ 'pending' => 'yellow',
61
+ 'executing' => 'grey',
62
+ 'failed' => 'red',
63
+ 'passed' => 'green',
64
+ 'outline' => 'cyan',
65
+ 'skipped' => 'cyan',
66
+ 'comments' => 'grey',
67
+ 'tag' => 'cyan'
68
+ )
71
69
 
72
70
  if ENV['GHERKIN_COLORS'] # Example: export GHERKIN_COLORS="passed=red:failed=yellow"
73
71
  ENV['GHERKIN_COLORS'].split(':').each do |pair|
@@ -93,8 +91,6 @@ module Cucumber
93
91
  def up(n)
94
92
  "\e[#{n}A"
95
93
  end
96
-
97
- extend self
98
94
  end
99
95
  end
100
96
  end
@@ -10,8 +10,8 @@ module Cucumber
10
10
  # * \ becomes \\
11
11
  #
12
12
  # This is used in the pretty formatter.
13
- def escape_cell(s)
14
- s.gsub(/\\(?!\|)/, '\\\\\\\\').gsub(/\n/, '\\n').gsub(/\|/, '\\|')
13
+ def escape_cell(sym)
14
+ sym.gsub(/\\(?!\|)/, '\\\\\\\\').gsub(/\n/, '\\n').gsub(/\|/, '\\|')
15
15
  end
16
16
  end
17
17
  end